PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
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.js
secure-custom-fields / assets / src / js Last commit date
bindings 6 months ago commands 1 week ago pro 1 week ago _acf-compatibility.js 1 year ago _acf-condition-types.js 7 months ago _acf-condition.js 1 year ago _acf-conditions.js 1 year ago _acf-field-accordion.js 6 months ago _acf-field-button-group.js 7 months ago _acf-field-checkbox.js 1 month ago _acf-field-color-picker.js 7 months ago _acf-field-date-picker.js 1 month ago _acf-field-date-time-picker.js 1 month ago _acf-field-file.js 1 month ago _acf-field-google-map.js 6 months ago _acf-field-icon-picker.js 1 month ago _acf-field-image.js 1 month ago _acf-field-link.js 1 year ago _acf-field-oembed.js 1 month ago _acf-field-page-link.js 1 year ago _acf-field-post-object.js 1 year ago _acf-field-radio.js 1 month ago _acf-field-range.js 1 year ago _acf-field-relationship.js 1 month ago _acf-field-select.js 1 month ago _acf-field-tab.js 3 weeks ago _acf-field-taxonomy.js 7 months ago _acf-field-time-picker.js 1 month ago _acf-field-true-false.js 1 month ago _acf-field-url.js 1 year ago _acf-field-user.js 1 year ago _acf-field-wysiwyg.js 1 month 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 7 months ago _acf-media.js 1 week ago _acf-modal.js 1 year ago _acf-model.js 1 year ago _acf-notice.js 7 months ago _acf-panel.js 1 year ago _acf-popup.js 7 months ago _acf-postbox.js 1 year ago _acf-screen.js 10 months ago _acf-select2.js 1 year ago _acf-tinymce.js 6 months ago _acf-tooltip.js 10 months ago _acf-unload.js 1 year ago _acf-validation.js 1 month ago _acf.js 7 months ago _browse-fields-modal.js 7 months ago _field-group-compatibility.js 1 year ago _field-group-conditions.js 1 year ago _field-group-field.js 3 months ago _field-group-fields.js 10 months ago _field-group-locations.js 1 year ago _field-group-settings.js 1 year ago _field-group.js 10 months 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.js
2844 lines
1 ( function ( $, undefined ) {
2 /**
3 * acf
4 *
5 * description
6 *
7 * @date 14/12/17
8 * @since ACF 5.6.5
9 *
10 * @param type $var Description. Default.
11 * @return type Description.
12 */
13
14 // The global acf object
15 var acf = {};
16
17 // Set as a browser global
18 window.acf = acf;
19
20 /** @var object Data sent from PHP */
21 acf.data = {};
22
23 /**
24 * get
25 *
26 * Gets a specific data value
27 *
28 * @date 14/12/17
29 * @since ACF 5.6.5
30 *
31 * @param string name
32 * @return mixed
33 */
34
35 acf.get = function ( name ) {
36 return this.data[ name ] || null;
37 };
38
39 /**
40 * has
41 *
42 * Returns `true` if the data exists and is not null
43 *
44 * @date 14/12/17
45 * @since ACF 5.6.5
46 *
47 * @param string name
48 * @return boolean
49 */
50
51 acf.has = function ( name ) {
52 return this.get( name ) !== null;
53 };
54
55 /**
56 * set
57 *
58 * Sets a specific data value
59 *
60 * @date 14/12/17
61 * @since ACF 5.6.5
62 *
63 * @param string name
64 * @param mixed value
65 * @return this
66 */
67
68 acf.set = function ( name, value ) {
69 this.data[ name ] = value;
70 return this;
71 };
72
73 /**
74 * uniqueId
75 *
76 * Returns a unique ID
77 *
78 * @date 9/11/17
79 * @since ACF 5.6.3
80 *
81 * @param string prefix Optional prefix.
82 * @return string
83 */
84
85 var idCounter = 0;
86 acf.uniqueId = function ( prefix ) {
87 var id = ++idCounter + '';
88 return prefix ? prefix + id : id;
89 };
90
91 /**
92 * acf.uniqueArray
93 *
94 * Returns a new array with only unique values
95 * Credit: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-an-array-remove-duplicates
96 *
97 * @date 23/3/18
98 * @since ACF 5.6.9
99 *
100 * @param type $var Description. Default.
101 * @return type Description.
102 */
103
104 acf.uniqueArray = function ( array ) {
105 function onlyUnique( value, index, self ) {
106 return self.indexOf( value ) === index;
107 }
108 return array.filter( onlyUnique );
109 };
110
111 /**
112 * uniqid
113 *
114 * Returns a unique ID (PHP version)
115 *
116 * @date 9/11/17
117 * @since ACF 5.6.3
118 * @source http://locutus.io/php/misc/uniqid/
119 *
120 * @param string prefix Optional prefix.
121 * @return string
122 */
123
124 var uniqidSeed = '';
125 acf.uniqid = function ( prefix, moreEntropy ) {
126 // discuss at: http://locutus.io/php/uniqid/
127 // original by: Kevin van Zonneveld (http://kvz.io)
128 // revised by: Kankrelune (http://www.webfaktory.info/)
129 // note 1: Uses an internal counter (in locutus global) to avoid collision
130 // example 1: var $id = uniqid()
131 // example 1: var $result = $id.length === 13
132 // returns 1: true
133 // example 2: var $id = uniqid('foo')
134 // example 2: var $result = $id.length === (13 + 'foo'.length)
135 // returns 2: true
136 // example 3: var $id = uniqid('bar', true)
137 // example 3: var $result = $id.length === (23 + 'bar'.length)
138 // returns 3: true
139 if ( typeof prefix === 'undefined' ) {
140 prefix = '';
141 }
142
143 var retId;
144 var formatSeed = function ( seed, reqWidth ) {
145 seed = parseInt( seed, 10 ).toString( 16 ); // to hex str
146 if ( reqWidth < seed.length ) {
147 // so long we split
148 return seed.slice( seed.length - reqWidth );
149 }
150 if ( reqWidth > seed.length ) {
151 // so short we pad
152 return (
153 Array( 1 + ( reqWidth - seed.length ) ).join( '0' ) + seed
154 );
155 }
156 return seed;
157 };
158
159 if ( ! uniqidSeed ) {
160 // init seed with big random int
161 uniqidSeed = Math.floor( Math.random() * 0x75bcd15 );
162 }
163 uniqidSeed++;
164
165 retId = prefix; // start with prefix, add current milliseconds hex string
166 retId += formatSeed( parseInt( new Date().getTime() / 1000, 10 ), 8 );
167 retId += formatSeed( uniqidSeed, 5 ); // add seed hex string
168 if ( moreEntropy ) {
169 // for more entropy we add a float lower to 10
170 retId += ( Math.random() * 10 ).toFixed( 8 ).toString();
171 }
172
173 return retId;
174 };
175
176 /**
177 * strReplace
178 *
179 * Performs a string replace
180 *
181 * @date 14/12/17
182 * @since ACF 5.6.5
183 *
184 * @param string search
185 * @param string replace
186 * @param string subject
187 * @return string
188 */
189
190 acf.strReplace = function ( search, replace, subject ) {
191 return subject.split( search ).join( replace );
192 };
193
194 /**
195 * strCamelCase
196 *
197 * Converts a string into camelCase
198 * Thanks to https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case
199 *
200 * @date 14/12/17
201 * @since ACF 5.6.5
202 *
203 * @param string str
204 * @return string
205 */
206
207 acf.strCamelCase = function ( str ) {
208 var matches = str.match( /([a-zA-Z0-9]+)/g );
209 return matches
210 ? matches
211 .map( function ( s, i ) {
212 var c = s.charAt( 0 );
213 return (
214 ( i === 0 ? c.toLowerCase() : c.toUpperCase() ) +
215 s.slice( 1 )
216 );
217 } )
218 .join( '' )
219 : '';
220 };
221
222 /**
223 * strPascalCase
224 *
225 * Converts a string into PascalCase
226 * Thanks to https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript
227 *
228 * @date 14/12/17
229 * @since ACF 5.6.5
230 *
231 * @param string str
232 * @return string
233 */
234
235 acf.strPascalCase = function ( str ) {
236 var camel = acf.strCamelCase( str );
237 return camel.charAt( 0 ).toUpperCase() + camel.slice( 1 );
238 };
239
240 /**
241 * acf.strSlugify
242 *
243 * Converts a string into a HTML class friendly slug
244 *
245 * @date 21/3/18
246 * @since ACF 5.6.9
247 *
248 * @param string str
249 * @return string
250 */
251
252 acf.strSlugify = function ( str ) {
253 return acf.strReplace( '_', '-', str.toLowerCase() );
254 };
255
256 acf.strSanitize = function ( str, toLowerCase = true ) {
257 // chars (https://jsperf.com/replace-foreign-characters)
258 var map = {
259 À: 'A',
260 Á: 'A',
261 Â: 'A',
262 Ã: 'A',
263 Ä: 'A',
264
265 : 'A',
266 Æ: 'AE',
267 Ç: 'C',
268 È: 'E',
269 É: 'E',
270 Ê: 'E',
271 Ë: 'E',
272 Ì: 'I',
273 Í: 'I',
274 Î: 'I',
275 Ï: 'I',
276 Ð: 'D',
277 Ñ: 'N',
278 Ò: 'O',
279 Ó: 'O',
280 Ô: 'O',
281 Õ: 'O',
282 Ö: 'O',
283 Ø: 'O',
284 Ù: 'U',
285 Ú: 'U',
286 Û: 'U',
287 Ü: 'U',
288 Ý: 'Y',
289 ß: 's',
290 à: 'a',
291 á: 'a',
292 â: 'a',
293 ã: 'a',
294 ä: 'a',
295 å: 'a',
296 æ: 'ae',
297 ç: 'c',
298 è: 'e',
299 é: 'e',
300 ê: 'e',
301 ë: 'e',
302 ì: 'i',
303 í: 'i',
304 î: 'i',
305 ï: 'i',
306 ñ: 'n',
307 ò: 'o',
308 ó: 'o',
309 ô: 'o',
310 õ: 'o',
311 ö: 'o',
312 ø: 'o',
313 ù: 'u',
314 ú: 'u',
315 û: 'u',
316 ü: 'u',
317 ý: 'y',
318 ÿ: 'y',
319 Ā: 'A',
320 ā: 'a',
321 Ă: 'A',
322 ă: 'a',
323 Ą: 'A',
324
325 : 'a',
326 Ć: 'C',
327 ć: 'c',
328 Ĉ: 'C',
329 ĉ: 'c',
330 Ċ: 'C',
331 ċ: 'c',
332 Č: 'C',
333 č: 'c',
334 Ď: 'D',
335 ď: 'd',
336 Đ: 'D',
337 đ: 'd',
338 Ē: 'E',
339 ē: 'e',
340 Ĕ: 'E',
341 ĕ: 'e',
342 Ė: 'E',
343 ė: 'e',
344 Ę: 'E',
345 ę: 'e',
346 Ě: 'E',
347 ě: 'e',
348 Ĝ: 'G',
349 ĝ: 'g',
350 Ğ: 'G',
351 ğ: 'g',
352 Ġ: 'G',
353 ġ: 'g',
354 Ģ: 'G',
355 ģ: 'g',
356 Ĥ: 'H',
357 ĥ: 'h',
358 Ħ: 'H',
359 ħ: 'h',
360 Ĩ: 'I',
361 ĩ: 'i',
362 Ī: 'I',
363 ī: 'i',
364 Ĭ: 'I',
365 ĭ: 'i',
366 Į: 'I',
367 į: 'i',
368 İ: 'I',
369 ı: 'i',
370 IJ: 'IJ',
371 ij: 'ij',
372 Ĵ: 'J',
373 ĵ: 'j',
374 Ķ: 'K',
375 ķ: 'k',
376 Ĺ: 'L',
377 ĺ: 'l',
378 Ļ: 'L',
379 ļ: 'l',
380 Ľ: 'L',
381 ľ: 'l',
382 Ŀ: 'L',
383 ŀ: 'l',
384 Ł: 'l',
385 ł: 'l',
386 Ń: 'N',
387 ń: 'n',
388
389 : 'N',
390 ņ: 'n',
391 Ň: 'N',
392 ň: 'n',
393 ʼn: 'n',
394 Ō: 'O',
395 ō: 'o',
396 Ŏ: 'O',
397 ŏ: 'o',
398 Ő: 'O',
399 ő: 'o',
400 Œ: 'OE',
401 œ: 'oe',
402 Ŕ: 'R',
403 ŕ: 'r',
404 Ŗ: 'R',
405 ŗ: 'r',
406 Ř: 'R',
407 ř: 'r',
408 Ś: 'S',
409 ś: 's',
410 Ŝ: 'S',
411 ŝ: 's',
412 Ş: 'S',
413 ş: 's',
414 Š: 'S',
415 š: 's',
416 Ţ: 'T',
417 ţ: 't',
418 Ť: 'T',
419 ť: 't',
420 Ŧ: 'T',
421 ŧ: 't',
422 Ũ: 'U',
423 ũ: 'u',
424 Ū: 'U',
425 ū: 'u',
426 Ŭ: 'U',
427 ŭ: 'u',
428 Ů: 'U',
429 ů: 'u',
430 Ű: 'U',
431 ű: 'u',
432 Ų: 'U',
433 ų: 'u',
434 Ŵ: 'W',
435 ŵ: 'w',
436 Ŷ: 'Y',
437 ŷ: 'y',
438 Ÿ: 'Y',
439 Ź: 'Z',
440 ź: 'z',
441 Ż: 'Z',
442 ż: 'z',
443 Ž: 'Z',
444 ž: 'z',
445 ſ: 's',
446 ƒ: 'f',
447 Ơ: 'O',
448 ơ: 'o',
449 Ư: 'U',
450 ư: 'u',
451 Ǎ: 'A',
452 ǎ: 'a',
453 Ǐ: 'I',
454 ǐ: 'i',
455 Ǒ: 'O',
456 ǒ: 'o',
457 Ǔ: 'U',
458 ǔ: 'u',
459 Ǖ: 'U',
460 ǖ: 'u',
461 Ǘ: 'U',
462 ǘ: 'u',
463 Ǚ: 'U',
464 ǚ: 'u',
465 Ǜ: 'U',
466 ǜ: 'u',
467 Ǻ: 'A',
468 ǻ: 'a',
469 Ǽ: 'AE',
470 ǽ: 'ae',
471 Ǿ: 'O',
472 ǿ: 'o',
473
474 // extra
475 ' ': '_',
476 "'": '',
477 '?': '',
478 '/': '',
479 '\\': '',
480 '.': '',
481 ',': '',
482 '`': '',
483 '>': '',
484 '<': '',
485 '"': '',
486 '[': '',
487 ']': '',
488 '|': '',
489 '{': '',
490 '}': '',
491 '(': '',
492 ')': '',
493 };
494
495 // vars
496 var nonWord = /\W/g;
497 var mapping = function ( c ) {
498 return map[ c ] !== undefined ? map[ c ] : c;
499 };
500
501 // replace
502 str = str.replace( nonWord, mapping );
503
504 // lowercase
505 if ( toLowerCase ) {
506 str = str.toLowerCase();
507 }
508
509 // return
510 return str;
511 };
512
513 /**
514 * acf.strMatch
515 *
516 * Returns the number of characters that match between two strings
517 *
518 * @date 1/2/18
519 * @since ACF 5.6.5
520 *
521 * @param type $var Description. Default.
522 * @return type Description.
523 */
524
525 acf.strMatch = function ( s1, s2 ) {
526 // vars
527 var val = 0;
528 var min = Math.min( s1.length, s2.length );
529
530 // loop
531 for ( var i = 0; i < min; i++ ) {
532 if ( s1[ i ] !== s2[ i ] ) {
533 break;
534 }
535 val++;
536 }
537
538 // return
539 return val;
540 };
541
542 /**
543 * Escapes HTML entities from a string.
544 *
545 * @date 08/06/2020
546 * @since ACF 5.9.0
547 *
548 * @param string string The input string.
549 * @return string
550 */
551 acf.strEscape = function ( string ) {
552 var htmlEscapes = {
553 '&': '&amp;',
554 '<': '&lt;',
555 '>': '&gt;',
556 '"': '&quot;',
557 "'": '&#39;',
558 };
559 return ( '' + string ).replace( /[&<>"']/g, function ( chr ) {
560 return htmlEscapes[ chr ];
561 } );
562 };
563
564 // Tests.
565 //console.log( acf.strEscape('Test 1') );
566 //console.log( acf.strEscape('Test & 1') );
567 //console.log( acf.strEscape('Test\'s &amp; 1') );
568 //console.log( acf.strEscape('<script>js</script>') );
569
570 /**
571 * Unescapes HTML entities from a string.
572 *
573 * @date 08/06/2020
574 * @since ACF 5.9.0
575 *
576 * @param string string The input string.
577 * @return string
578 */
579 acf.strUnescape = function ( string ) {
580 var htmlUnescapes = {
581 '&amp;': '&',
582 '&lt;': '<',
583 '&gt;': '>',
584 '&quot;': '"',
585 '&#39;': "'",
586 };
587 return ( '' + string ).replace(
588 /&amp;|&lt;|&gt;|&quot;|&#39;/g,
589 function ( entity ) {
590 return htmlUnescapes[ entity ];
591 }
592 );
593 };
594
595 // Tests.
596 //console.log( acf.strUnescape( acf.strEscape('Test 1') ) );
597 //console.log( acf.strUnescape( acf.strEscape('Test & 1') ) );
598 //console.log( acf.strUnescape( acf.strEscape('Test\'s &amp; 1') ) );
599 //console.log( acf.strUnescape( acf.strEscape('<script>js</script>') ) );
600
601 /**
602 * Escapes HTML entities from a string.
603 *
604 * @date 08/06/2020
605 * @since ACF 5.9.0
606 *
607 * @param string string The input string.
608 * @return string
609 */
610 acf.escAttr = acf.strEscape;
611
612 /**
613 * Encodes <script> tags for safe HTML output.
614 *
615 * @date 08/06/2020
616 * @since ACF 5.9.0
617 * @since ACF 6.4.3 - Use DOMPurify for better security.
618 *
619 * @param string string The input string.
620 * @return string
621 */
622
623 acf.escHtml = function ( string ) {
624 string = '' + string; // Convert to string if not already.
625 const DOMPurify = require( 'dompurify' );
626 if ( DOMPurify === undefined || ! DOMPurify.isSupported ) {
627 console.warn(
628 'ACF: DOMPurify not loaded or not supported. Falling back to basic HTML escaping for security.'
629 );
630 return acf.strEscape( string ); // Fallback to basic escaping.
631 }
632 const options = acf.applyFilters( 'esc_html_dompurify_config', {
633 USE_PROFILES: { html: true },
634 ADD_TAGS: [ 'audio', 'video' ],
635 ADD_ATTR: [ 'controls', 'loop', 'muted', 'preload' ],
636 FORBID_TAGS: [
637 'script',
638 'style',
639 'iframe',
640 'object',
641 'embed',
642 'base',
643 'meta',
644 'form',
645 ],
646 FORBID_ATTR: [
647 'style',
648 'srcset',
649 'action',
650 'background',
651 'dynsrc',
652 'lowsrc',
653 'on*',
654 ],
655 ALLOW_DATA_ATTR: false,
656 ALLOW_ARIA_ATTR: false,
657 } );
658 return DOMPurify.sanitize( string, options );
659 };
660
661 // Tests.
662 //console.log( acf.escHtml('<script>js</script>') );
663 //console.log( acf.escHtml( acf.strEscape('<script>js</script>') ) );
664 //console.log( acf.escHtml( '<script>js1</script><script>js2</script>' ) );
665
666 /**
667 * Encode a string potentially containing HTML into it's HTML entities equivalent.
668 *
669 * @since ACF 6.3.6
670 *
671 * @param {string} string String to encode.
672 * @return {string} The encoded string
673 */
674 acf.encode = function ( string ) {
675 return $( '<textarea/>' ).text( string ).html();
676 };
677
678 /**
679 * Decode a HTML encoded string into it's original form.
680 *
681 * @since ACF 5.6.5
682 *
683 * @param {string} string String to encode.
684 * @return {string} The encoded string
685 */
686 acf.decode = function ( string ) {
687 return $( '<textarea/>' ).html( string ).text();
688 };
689
690 /**
691 * parseArgs
692 *
693 * Merges together defaults and args much like the WP wp_parse_args function
694 *
695 * @date 14/12/17
696 * @since ACF 5.6.5
697 *
698 * @param object args
699 * @param object defaults
700 * @return object
701 */
702
703 acf.parseArgs = function ( args, defaults ) {
704 if ( typeof args !== 'object' ) args = {};
705 if ( typeof defaults !== 'object' ) defaults = {};
706 return $.extend( {}, defaults, args );
707 };
708
709 /**
710 * __
711 *
712 * Retrieve the translation of $text.
713 *
714 * @date 16/4/18
715 * @since ACF 5.6.9
716 *
717 * @param string text Text to translate.
718 * @return string Translated text.
719 */
720
721 // Make sure a global acfL10n object exists to prevent errors in other scopes
722 window.acfL10n = window.acfL10n || {};
723
724 acf.__ = function ( text ) {
725 return acfL10n[ text ] || text;
726 };
727
728 /**
729 * _x
730 *
731 * Retrieve translated string with gettext context.
732 *
733 * @date 16/4/18
734 * @since ACF 5.6.9
735 *
736 * @param string text Text to translate.
737 * @param string context Context information for the translators.
738 * @return string Translated text.
739 */
740
741 acf._x = function ( text, context ) {
742 return acfL10n[ text + '.' + context ] || acfL10n[ text ] || text;
743 };
744
745 /**
746 * _n
747 *
748 * Retrieve the plural or single form based on the amount.
749 *
750 * @date 16/4/18
751 * @since ACF 5.6.9
752 *
753 * @param string single Single text to translate.
754 * @param string plural Plural text to translate.
755 * @param int number The number to compare against.
756 * @return string Translated text.
757 */
758
759 acf._n = function ( single, plural, number ) {
760 if ( number == 1 ) {
761 return acf.__( single );
762 } else {
763 return acf.__( plural );
764 }
765 };
766
767 acf.isArray = function ( a ) {
768 return Array.isArray( a );
769 };
770
771 acf.isObject = function ( a ) {
772 return typeof a === 'object';
773 };
774
775 /**
776 * serialize
777 *
778 * description
779 *
780 * @date 24/12/17
781 * @since ACF 5.6.5
782 *
783 * @param type $var Description. Default.
784 * @return type Description.
785 */
786
787 var buildObject = function ( obj, name, value ) {
788 // replace [] with placeholder
789 name = name.replace( '[]', '[%%index%%]' );
790
791 // vars
792 var keys = name.match( /([^\[\]])+/g );
793 if ( ! keys ) return;
794 var length = keys.length;
795 var ref = obj;
796
797 // loop
798 for ( var i = 0; i < length; i++ ) {
799 // vars
800 var key = String( keys[ i ] );
801
802 // value
803 if ( i == length - 1 ) {
804 // %%index%%
805 if ( key === '%%index%%' ) {
806 ref.push( value );
807
808 // default
809 } else {
810 ref[ key ] = value;
811 }
812
813 // path
814 } else {
815 // array
816 if ( keys[ i + 1 ] === '%%index%%' ) {
817 if ( ! acf.isArray( ref[ key ] ) ) {
818 ref[ key ] = [];
819 }
820
821 // object
822 } else {
823 if ( ! acf.isObject( ref[ key ] ) ) {
824 ref[ key ] = {};
825 }
826 }
827
828 // crawl
829 ref = ref[ key ];
830 }
831 }
832 };
833
834 acf.serialize = function ( $el, prefix ) {
835 // vars
836 var obj = {};
837 var inputs = acf.serializeArray( $el );
838
839 // prefix
840 if ( prefix !== undefined ) {
841 // filter and modify
842 inputs = inputs
843 .filter( function ( item ) {
844 return item.name.indexOf( prefix ) === 0;
845 } )
846 .map( function ( item ) {
847 item.name = item.name.slice( prefix.length );
848 return item;
849 } );
850 }
851
852 // loop
853 for ( var i = 0; i < inputs.length; i++ ) {
854 buildObject( obj, inputs[ i ].name, inputs[ i ].value );
855 }
856
857 // return
858 return obj;
859 };
860
861 /**
862 * Check if an object has only numeric string keys.
863 * Used to detect objects that should be converted to arrays (e.g., checkbox values).
864 *
865 * Semantics:
866 * - Accepts base-10, non-negative integer strings composed of digits only (e.g. "0", "12").
867 * - Leading zeros are allowed (e.g. "0012") and treated as numeric by downstream logic.
868 * - Negative ("-1"), decimal ("1.0"), and non-numeric keys are rejected.
869 *
870 * @since SCF 6.6.0
871 * @private
872 *
873 * @param object obj The object to check
874 * @return boolean True if all keys are numeric strings
875 */
876 const hasOnlyNumericKeys = function ( obj ) {
877 const keys = Object.keys( obj );
878 if ( keys.length === 0 ) {
879 return false;
880 }
881
882 for ( let i = 0; i < keys.length; i++ ) {
883 if ( ! /^\d+$/.test( keys[ i ] ) ) {
884 return false;
885 }
886 }
887 return true;
888 };
889
890 /**
891 * Convert an object with numeric string keys to a numerically sorted array.
892 * Example: {"0": "one", "2": "three", "1": "two"} becomes ["one", "two", "three"].
893 *
894 * Notes on edge-cases:
895 * - Leading zeros (e.g. "00123") are supported; order is based on the numeric value
896 * but the original string key is used to read the value to avoid lookup mismatches.
897 * - Assumes {@link hasOnlyNumericKeys} has already gated out negatives/decimals.
898 *
899 * @since SCF 6.6.0
900 * @private
901 *
902 * @param object obj The object to convert
903 * @return array The numerically sorted array of values
904 */
905 const numericObjectToArray = function ( obj ) {
906 const arr = [];
907 // Pair each original key with its numeric value for stable lookup and sorting.
908 const entries = Object.keys( obj )
909 .map( function ( k ) {
910 return { k: k, n: parseInt( k, 10 ) };
911 } )
912 .sort( function ( a, b ) {
913 return a.n - b.n;
914 } );
915
916 for ( let i = 0; i < entries.length; i++ ) {
917 arr.push( obj[ entries[ i ].k ] );
918 }
919 return arr;
920 };
921
922 /**
923 * Check if a value looks like flexible content data.
924 * Flexible content objects contain rows where each row object has an 'acf_fc_layout' property.
925 * Keys for flexible rows are not guaranteed to be numeric: they are typically unique IDs
926 * (e.g. '69171156640b5') or strings like 'row-0'. Therefore, flexible content detection does
927 * not rely on numeric keys and is handled separately from numeric-keyed object normalization.
928 *
929 * @since SCF 6.6.0
930 *
931 * @param object value The value to check
932 * @return boolean True if this looks like flexible content data
933 */
934 acf.isFlexibleContentData = function ( value ) {
935 if ( ! acf.isObject( value ) ) {
936 return false;
937 }
938
939 var keys = Object.keys( value );
940 for ( var i = 0; i < keys.length; i++ ) {
941 var key = keys[ i ];
942 if ( key === 'acfcloneindex' ) {
943 continue;
944 }
945
946 var subvalue = value[ key ];
947 if ( acf.isObject( subvalue ) && subvalue.acf_fc_layout ) {
948 return true;
949 }
950 }
951 return false;
952 };
953
954 /**
955 * Normalizes flexible content data structure by converting objects to arrays.
956 * Private helper function.
957 *
958 * @since 6.6.0
959 *
960 * @param {Object} obj The object to normalize.
961 * @return {Object|Array} The normalized data.
962 */
963 const normalizeFlexibleContentData = function ( obj ) {
964 if ( ! acf.isObject( obj ) ) {
965 return obj;
966 }
967
968 let result = {};
969
970 for ( let key in obj ) {
971 if ( ! obj.hasOwnProperty( key ) ) {
972 continue;
973 }
974
975 var value = obj[ key ];
976
977 // Primitives pass through unchanged
978 if ( ! acf.isObject( value ) ) {
979 result[ key ] = value;
980 continue;
981 }
982
983 // Convert numeric-keyed objects to arrays (e.g., checkbox values)
984 if ( hasOnlyNumericKeys( value ) ) {
985 result[ key ] = numericObjectToArray( value );
986 continue;
987 } // Convert flexible content to arrays
988 if ( acf.isFlexibleContentData( value ) ) {
989 var arr = [];
990 var keys = Object.keys( value );
991
992 for ( var i = 0; i < keys.length; i++ ) {
993 var subkey = keys[ i ];
994 if ( subkey === 'acfcloneindex' ) {
995 continue;
996 }
997
998 var subvalue = value[ subkey ];
999 if ( acf.isObject( subvalue ) && subvalue.acf_fc_layout ) {
1000 arr.push( normalizeFlexibleContentData( subvalue ) );
1001 }
1002 }
1003
1004 result[ key ] = arr;
1005 } else {
1006 // Recursively process nested objects
1007 result[ key ] = normalizeFlexibleContentData( value );
1008 }
1009 }
1010
1011 return result;
1012 };
1013
1014 /**
1015 * Public API wrapper for normalizeFlexibleContentData.
1016 * Normalizes flexible content data structure by converting objects to arrays.
1017 *
1018 * @since 6.6.0
1019 *
1020 * @param {Object} obj The object to normalize.
1021 * @return {Object|Array} The normalized data.
1022 */
1023 acf.normalizeFlexibleContentData = function ( obj ) {
1024 return normalizeFlexibleContentData( obj );
1025 };
1026
1027 /**
1028 * acf.serializeArray
1029 *
1030 * Similar to $.serializeArray() but works with a parent wrapping element.
1031 *
1032 * @date 19/8/18
1033 * @since ACF 5.7.3
1034 *
1035 * @param jQuery $el The element or form to serialize.
1036 * @return array
1037 */
1038
1039 acf.serializeArray = function ( $el ) {
1040 return $el.find( 'select, textarea, input' ).serializeArray();
1041 };
1042
1043 /**
1044 * acf.serializeForAjax
1045 *
1046 * Returns an object containing name => value data ready to be encoded for Ajax.
1047 *
1048 * @date 17/12/18
1049 * @since ACF 5.8.0
1050 *
1051 * @param jQuery $el The element or form to serialize.
1052 * @return object
1053 */
1054 acf.serializeForAjax = function ( $el ) {
1055 // vars
1056 var data = {};
1057 var index = {};
1058
1059 // Serialize inputs.
1060 var inputs = acf.serializeArray( $el );
1061
1062 // Loop over inputs and build data.
1063 inputs.map( function ( item ) {
1064 // Append to array.
1065 if ( item.name.slice( -2 ) === '[]' ) {
1066 data[ item.name ] = data[ item.name ] || [];
1067 data[ item.name ].push( item.value );
1068 // Append
1069 } else {
1070 data[ item.name ] = item.value;
1071 }
1072 } );
1073
1074 // return
1075 return data;
1076 };
1077
1078 /**
1079 * addAction
1080 *
1081 * Wrapper for acf.hooks.addAction
1082 *
1083 * @date 14/12/17
1084 * @since ACF 5.6.5
1085 *
1086 * @param n/a
1087 * @return this
1088 */
1089
1090 /*
1091 var prefixAction = function( action ){
1092 return 'acf_' + action;
1093 }
1094 */
1095
1096 acf.addAction = function () {
1097 //action = prefixAction(action);
1098 acf.hooks.addAction.apply( this, arguments );
1099 return this;
1100 };
1101
1102 /**
1103 * removeAction
1104 *
1105 * Wrapper for acf.hooks.removeAction
1106 *
1107 * @date 14/12/17
1108 * @since ACF 5.6.5
1109 *
1110 * @param n/a
1111 * @return this
1112 */
1113
1114 acf.removeAction = function () {
1115 //action = prefixAction(action);
1116 acf.hooks.removeAction.apply( this, arguments );
1117 return this;
1118 };
1119
1120 /**
1121 * doAction
1122 *
1123 * Wrapper for acf.hooks.doAction
1124 *
1125 * @date 14/12/17
1126 * @since ACF 5.6.5
1127 *
1128 * @param n/a
1129 * @return this
1130 */
1131
1132 var actionHistory = {};
1133 //var currentAction = false;
1134 acf.doAction = function ( action ) {
1135 //action = prefixAction(action);
1136 //currentAction = action;
1137 actionHistory[ action ] = 1;
1138 acf.hooks.doAction.apply( this, arguments );
1139 actionHistory[ action ] = 0;
1140 return this;
1141 };
1142
1143 /**
1144 * doingAction
1145 *
1146 * Return true if doing action
1147 *
1148 * @date 14/12/17
1149 * @since ACF 5.6.5
1150 *
1151 * @param n/a
1152 * @return this
1153 */
1154
1155 acf.doingAction = function ( action ) {
1156 //action = prefixAction(action);
1157 return actionHistory[ action ] === 1;
1158 };
1159
1160 /**
1161 * didAction
1162 *
1163 * Wrapper for acf.hooks.doAction
1164 *
1165 * @date 14/12/17
1166 * @since ACF 5.6.5
1167 *
1168 * @param n/a
1169 * @return this
1170 */
1171
1172 acf.didAction = function ( action ) {
1173 //action = prefixAction(action);
1174 return actionHistory[ action ] !== undefined;
1175 };
1176
1177 /**
1178 * currentAction
1179 *
1180 * Wrapper for acf.hooks.doAction
1181 *
1182 * @date 14/12/17
1183 * @since ACF 5.6.5
1184 *
1185 * @param n/a
1186 * @return this
1187 */
1188
1189 acf.currentAction = function () {
1190 for ( var k in actionHistory ) {
1191 if ( actionHistory[ k ] ) {
1192 return k;
1193 }
1194 }
1195 return false;
1196 };
1197
1198 /**
1199 * addFilter
1200 *
1201 * Wrapper for acf.hooks.addFilter
1202 *
1203 * @date 14/12/17
1204 * @since ACF 5.6.5
1205 *
1206 * @param n/a
1207 * @return this
1208 */
1209
1210 acf.addFilter = function () {
1211 //action = prefixAction(action);
1212 acf.hooks.addFilter.apply( this, arguments );
1213 return this;
1214 };
1215
1216 /**
1217 * removeFilter
1218 *
1219 * Wrapper for acf.hooks.removeFilter
1220 *
1221 * @date 14/12/17
1222 * @since ACF 5.6.5
1223 *
1224 * @param n/a
1225 * @return this
1226 */
1227
1228 acf.removeFilter = function () {
1229 //action = prefixAction(action);
1230 acf.hooks.removeFilter.apply( this, arguments );
1231 return this;
1232 };
1233
1234 /**
1235 * applyFilters
1236 *
1237 * Wrapper for acf.hooks.applyFilters
1238 *
1239 * @date 14/12/17
1240 * @since ACF 5.6.5
1241 *
1242 * @param n/a
1243 * @return this
1244 */
1245
1246 acf.applyFilters = function () {
1247 //action = prefixAction(action);
1248 return acf.hooks.applyFilters.apply( this, arguments );
1249 };
1250
1251 /**
1252 * getArgs
1253 *
1254 * description
1255 *
1256 * @date 15/12/17
1257 * @since ACF 5.6.5
1258 *
1259 * @param type $var Description. Default.
1260 * @return type Description.
1261 */
1262
1263 acf.arrayArgs = function ( args ) {
1264 return Array.prototype.slice.call( args );
1265 };
1266
1267 /**
1268 * extendArgs
1269 *
1270 * description
1271 *
1272 * @date 15/12/17
1273 * @since ACF 5.6.5
1274 *
1275 * @param type $var Description. Default.
1276 * @return type Description.
1277 */
1278
1279 /*
1280 acf.extendArgs = function( ){
1281 var args = Array.prototype.slice.call( arguments );
1282 var realArgs = args.shift();
1283
1284 Array.prototype.push.call(arguments, 'bar')
1285 return Array.prototype.push.apply( args, arguments );
1286 };
1287 */
1288
1289 // Preferences
1290 // - use try/catch to avoid JS error if cookies are disabled on front-end form
1291 try {
1292 var preferences = JSON.parse( localStorage.getItem( 'acf' ) ) || {};
1293 } catch ( e ) {
1294 var preferences = {};
1295 }
1296
1297 /**
1298 * getPreferenceName
1299 *
1300 * Gets the true preference name.
1301 * Converts "this.thing" to "thing-123" if editing post 123.
1302 *
1303 * @date 11/11/17
1304 * @since ACF 5.6.5
1305 *
1306 * @param string name
1307 * @return string
1308 */
1309
1310 var getPreferenceName = function ( name ) {
1311 if ( name.substr( 0, 5 ) === 'this.' ) {
1312 name = name.substr( 5 ) + '-' + acf.get( 'post_id' );
1313 }
1314 return name;
1315 };
1316
1317 /**
1318 * acf.getPreference
1319 *
1320 * Gets a preference setting or null if not set.
1321 *
1322 * @date 11/11/17
1323 * @since ACF 5.6.5
1324 *
1325 * @param string name
1326 * @return mixed
1327 */
1328
1329 acf.getPreference = function ( name ) {
1330 name = getPreferenceName( name );
1331 return preferences[ name ] || null;
1332 };
1333
1334 /**
1335 * acf.setPreference
1336 *
1337 * Sets a preference setting.
1338 *
1339 * @date 11/11/17
1340 * @since ACF 5.6.5
1341 *
1342 * @param string name
1343 * @param mixed value
1344 * @return n/a
1345 */
1346
1347 acf.setPreference = function ( name, value ) {
1348 name = getPreferenceName( name );
1349 if ( value === null ) {
1350 delete preferences[ name ];
1351 } else {
1352 preferences[ name ] = value;
1353 }
1354 localStorage.setItem( 'acf', JSON.stringify( preferences ) );
1355 };
1356
1357 /**
1358 * acf.removePreference
1359 *
1360 * Removes a preference setting.
1361 *
1362 * @date 11/11/17
1363 * @since ACF 5.6.5
1364 *
1365 * @param string name
1366 * @return n/a
1367 */
1368
1369 acf.removePreference = function ( name ) {
1370 acf.setPreference( name, null );
1371 };
1372
1373 /**
1374 * remove
1375 *
1376 * Removes an element with fade effect
1377 *
1378 * @date 1/1/18
1379 * @since ACF 5.6.5
1380 *
1381 * @param type $var Description. Default.
1382 * @return type Description.
1383 */
1384
1385 acf.remove = function ( props ) {
1386 // allow jQuery
1387 if ( props instanceof jQuery ) {
1388 props = {
1389 target: props,
1390 };
1391 }
1392
1393 // defaults
1394 props = acf.parseArgs( props, {
1395 target: false,
1396 endHeight: 0,
1397 complete: function () {},
1398 } );
1399
1400 // action
1401 acf.doAction( 'remove', props.target );
1402
1403 // tr
1404 if ( props.target.is( 'tr' ) ) {
1405 removeTr( props );
1406
1407 // div
1408 } else {
1409 removeDiv( props );
1410 }
1411 };
1412
1413 /**
1414 * removeDiv
1415 *
1416 * description
1417 *
1418 * @date 16/2/18
1419 * @since ACF 5.6.9
1420 *
1421 * @param type $var Description. Default.
1422 * @return type Description.
1423 */
1424
1425 var removeDiv = function ( props ) {
1426 // vars
1427 var $el = props.target;
1428 var height = $el.height();
1429 var width = $el.width();
1430 var margin = $el.css( 'margin' );
1431 var outerHeight = $el.outerHeight( true );
1432 var style = $el.attr( 'style' ) + ''; // needed to copy
1433
1434 // wrap
1435 $el.wrap(
1436 '<div class="acf-temp-remove" style="height:' +
1437 outerHeight +
1438 'px"></div>'
1439 );
1440 var $wrap = $el.parent();
1441
1442 // set pos
1443 $el.css( {
1444 height: height,
1445 width: width,
1446 margin: margin,
1447 position: 'absolute',
1448 } );
1449
1450 // fade wrap
1451 setTimeout( function () {
1452 $wrap.css( {
1453 opacity: 0,
1454 height: props.endHeight,
1455 } );
1456 }, 50 );
1457
1458 // remove
1459 setTimeout( function () {
1460 $el.attr( 'style', style );
1461 $wrap.remove();
1462 props.complete();
1463 }, 301 );
1464 };
1465
1466 /**
1467 * removeTr
1468 *
1469 * description
1470 *
1471 * @date 16/2/18
1472 * @since ACF 5.6.9
1473 *
1474 * @param type $var Description. Default.
1475 * @return type Description.
1476 */
1477
1478 var removeTr = function ( props ) {
1479 // vars
1480 var $tr = props.target;
1481 var height = $tr.height();
1482 var children = $tr.children().length;
1483
1484 // create dummy td
1485 var $td = $(
1486 '<td class="acf-temp-remove" style="padding:0; height:' +
1487 height +
1488 'px" colspan="' +
1489 children +
1490 '"></td>'
1491 );
1492
1493 // fade away tr
1494 $tr.addClass( 'acf-remove-element' );
1495
1496 // update HTML after fade animation
1497 setTimeout( function () {
1498 $tr.html( $td );
1499 }, 251 );
1500
1501 // allow .acf-temp-remove to exist before changing CSS
1502 setTimeout( function () {
1503 // remove class
1504 $tr.removeClass( 'acf-remove-element' );
1505
1506 // collapse
1507 $td.css( {
1508 height: props.endHeight,
1509 } );
1510 }, 300 );
1511
1512 // remove
1513 setTimeout( function () {
1514 $tr.remove();
1515 props.complete();
1516 }, 451 );
1517 };
1518
1519 /**
1520 * duplicate
1521 *
1522 * description
1523 *
1524 * @date 3/1/18
1525 * @since ACF 5.6.5
1526 *
1527 * @param type $var Description. Default.
1528 * @return type Description.
1529 */
1530
1531 acf.duplicate = function ( args ) {
1532 // allow jQuery
1533 if ( args instanceof jQuery ) {
1534 args = {
1535 target: args,
1536 };
1537 }
1538
1539 // defaults
1540 args = acf.parseArgs( args, {
1541 target: false,
1542 search: '',
1543 replace: '',
1544 rename: true,
1545 before: function ( $el ) {},
1546 after: function ( $el, $el2 ) {},
1547 append: function ( $el, $el2 ) {
1548 $el.after( $el2 );
1549 },
1550 } );
1551
1552 // compatibility
1553 args.target = args.target || args.$el;
1554
1555 // vars
1556 var $el = args.target;
1557
1558 // search
1559 args.search = args.search || $el.attr( 'data-id' );
1560 args.replace = args.replace || acf.uniqid();
1561
1562 // before
1563 // - allow acf to modify DOM
1564 // - fixes bug where select field option is not selected
1565 args.before( $el );
1566 acf.doAction( 'before_duplicate', $el );
1567
1568 // clone
1569 var $el2 = $el.clone();
1570
1571 // rename
1572 if ( args.rename ) {
1573 acf.rename( {
1574 target: $el2,
1575 search: args.search,
1576 replace: args.replace,
1577 replacer:
1578 typeof args.rename === 'function' ? args.rename : null,
1579 } );
1580 }
1581
1582 // remove classes
1583 $el2.removeClass( 'acf-clone' );
1584 $el2.find( '.ui-sortable' ).removeClass( 'ui-sortable' );
1585
1586 // remove any initialised select2s prevent the duplicated object stealing the previous select2.
1587 $el2.find( '[data-select2-id]' ).removeAttr( 'data-select2-id' );
1588 $el2.find( '.select2' ).remove();
1589
1590 // subfield select2 renames happen after init and contain a duplicated ID. force change those IDs to prevent this.
1591 $el2.find( '.acf-is-subfields select[data-ui="1"]' ).each( function () {
1592 $( this ).prop(
1593 'id',
1594 $( this )
1595 .prop( 'id' )
1596 .replace(
1597 'acf_fields',
1598 acf.uniqid( 'duplicated_' ) + '_acf_fields'
1599 )
1600 );
1601 } );
1602
1603 // remove tab wrapper to ensure proper init
1604 $el2.find( '.acf-field-settings > .acf-tab-wrap' ).remove();
1605
1606 // after
1607 // - allow acf to modify DOM
1608 args.after( $el, $el2 );
1609 acf.doAction( 'after_duplicate', $el, $el2 );
1610
1611 // append
1612 args.append( $el, $el2 );
1613
1614 /**
1615 * Fires after an element has been duplicated and appended to the DOM.
1616 *
1617 * @date 30/10/19
1618 * @since ACF 5.8.7
1619 *
1620 * @param jQuery $el The original element.
1621 * @param jQuery $el2 The duplicated element.
1622 */
1623 acf.doAction( 'duplicate', $el, $el2 );
1624
1625 // append
1626 acf.doAction( 'append', $el2 );
1627
1628 // return
1629 return $el2;
1630 };
1631
1632 /**
1633 * rename
1634 *
1635 * description
1636 *
1637 * @date 7/1/18
1638 * @since ACF 5.6.5
1639 *
1640 * @param type $var Description. Default.
1641 * @return type Description.
1642 */
1643
1644 acf.rename = function ( args ) {
1645 // Allow jQuery param.
1646 if ( args instanceof jQuery ) {
1647 args = {
1648 target: args,
1649 };
1650 }
1651
1652 // Apply default args.
1653 args = acf.parseArgs( args, {
1654 target: false,
1655 destructive: false,
1656 search: '',
1657 replace: '',
1658 replacer: null,
1659 } );
1660
1661 // Extract args.
1662 var $el = args.target;
1663
1664 // Provide backup for empty args.
1665 if ( ! args.search ) {
1666 args.search = $el.attr( 'data-id' );
1667 }
1668 if ( ! args.replace ) {
1669 args.replace = acf.uniqid( 'acf' );
1670 }
1671 if ( ! args.replacer ) {
1672 args.replacer = function ( name, value, search, replace ) {
1673 return value.replace( search, replace );
1674 };
1675 }
1676
1677 // Callback function for jQuery replacing.
1678 var withReplacer = function ( name ) {
1679 return function ( i, value ) {
1680 return args.replacer( name, value, args.search, args.replace );
1681 };
1682 };
1683
1684 // Destructive Replace.
1685 if ( args.destructive ) {
1686 var html = acf.strReplace(
1687 args.search,
1688 args.replace,
1689 $el.outerHTML()
1690 );
1691 $el.replaceWith( html );
1692
1693 // Standard Replace.
1694 } else {
1695 $el.attr( 'data-id', args.replace );
1696 $el.find( '[id*="' + args.search + '"]' ).attr(
1697 'id',
1698 withReplacer( 'id' )
1699 );
1700 $el.find( '[for*="' + args.search + '"]' ).attr(
1701 'for',
1702 withReplacer( 'for' )
1703 );
1704 $el.find( '[name*="' + args.search + '"]' ).attr(
1705 'name',
1706 withReplacer( 'name' )
1707 );
1708 }
1709
1710 // return
1711 return $el;
1712 };
1713
1714 /**
1715 * Prepares AJAX data prior to being sent.
1716 *
1717 * @since ACF 5.6.5
1718 *
1719 * @param Object data The data to prepare
1720 * @param boolean use_global_nonce Should we ignore any nonce provided in the data object and force ACF's global nonce for this request
1721 * @return Object The prepared data.
1722 */
1723 acf.prepareForAjax = function ( data, use_global_nonce = false ) {
1724 // Set a default nonce if we don't have one already.
1725 if ( use_global_nonce || 'undefined' === typeof data.nonce ) {
1726 data.nonce = acf.get( 'nonce' );
1727 }
1728
1729 data.post_id = acf.get( 'post_id' );
1730
1731 if ( acf.has( 'language' ) ) {
1732 data.lang = acf.get( 'language' );
1733 }
1734
1735 // Filter for 3rd party customization.
1736 data = acf.applyFilters( 'prepare_for_ajax', data );
1737
1738 return data;
1739 };
1740
1741 /**
1742 * acf.startButtonLoading
1743 *
1744 * description
1745 *
1746 * @date 5/1/18
1747 * @since ACF 5.6.5
1748 *
1749 * @param type $var Description. Default.
1750 * @return type Description.
1751 */
1752
1753 acf.startButtonLoading = function ( $el ) {
1754 $el.prop( 'disabled', true );
1755 $el.after( ' <i class="acf-loading"></i>' );
1756 };
1757
1758 acf.stopButtonLoading = function ( $el ) {
1759 $el.prop( 'disabled', false );
1760 $el.next( '.acf-loading' ).remove();
1761 };
1762
1763 /**
1764 * acf.showLoading
1765 *
1766 * description
1767 *
1768 * @date 12/1/18
1769 * @since ACF 5.6.5
1770 *
1771 * @param type $var Description. Default.
1772 * @return type Description.
1773 */
1774
1775 acf.showLoading = function ( $el ) {
1776 $el.append(
1777 '<div class="acf-loading-overlay"><i class="acf-loading"></i></div>'
1778 );
1779 };
1780
1781 acf.hideLoading = function ( $el ) {
1782 $el.children( '.acf-loading-overlay' ).remove();
1783 };
1784
1785 /**
1786 * acf.updateUserSetting
1787 *
1788 * description
1789 *
1790 * @date 5/1/18
1791 * @since ACF 5.6.5
1792 *
1793 * @param type $var Description. Default.
1794 * @return type Description.
1795 */
1796
1797 acf.updateUserSetting = function ( name, value ) {
1798 var ajaxData = {
1799 action: 'acf/ajax/user_setting',
1800 name: name,
1801 value: value,
1802 };
1803
1804 $.ajax( {
1805 url: acf.get( 'ajaxurl' ),
1806 data: acf.prepareForAjax( ajaxData ),
1807 type: 'post',
1808 dataType: 'html',
1809 } );
1810 };
1811
1812 /**
1813 * acf.val
1814 *
1815 * description
1816 *
1817 * @date 8/1/18
1818 * @since ACF 5.6.5
1819 *
1820 * @param type $var Description. Default.
1821 * @return type Description.
1822 */
1823
1824 acf.val = function ( $input, value, silent ) {
1825 // vars
1826 var prevValue = $input.val();
1827
1828 // bail if no change
1829 if ( value === prevValue ) {
1830 return false;
1831 }
1832
1833 // update value
1834 $input.val( value );
1835
1836 // prevent select elements displaying blank value if option doesn't exist
1837 if ( $input.is( 'select' ) && $input.val() === null ) {
1838 $input.val( prevValue );
1839 return false;
1840 }
1841
1842 // update with trigger
1843 if ( silent !== true ) {
1844 $input.trigger( 'change' );
1845 }
1846
1847 // return
1848 return true;
1849 };
1850
1851 /**
1852 * acf.show
1853 *
1854 * description
1855 *
1856 * @date 9/2/18
1857 * @since ACF 5.6.5
1858 *
1859 * @param type $var Description. Default.
1860 * @return type Description.
1861 */
1862
1863 acf.show = function ( $el, lockKey ) {
1864 // unlock
1865 if ( lockKey ) {
1866 acf.unlock( $el, 'hidden', lockKey );
1867 }
1868
1869 // bail early if $el is still locked
1870 if ( acf.isLocked( $el, 'hidden' ) ) {
1871 //console.log( 'still locked', getLocks( $el, 'hidden' ));
1872 return false;
1873 }
1874
1875 // $el is hidden, remove class and return true due to change in visibility
1876 if ( $el.hasClass( 'acf-hidden' ) ) {
1877 $el.removeClass( 'acf-hidden' );
1878 return true;
1879
1880 // $el is visible, return false due to no change in visibility
1881 } else {
1882 return false;
1883 }
1884 };
1885
1886 /**
1887 * acf.hide
1888 *
1889 * description
1890 *
1891 * @date 9/2/18
1892 * @since ACF 5.6.5
1893 *
1894 * @param type $var Description. Default.
1895 * @return type Description.
1896 */
1897
1898 acf.hide = function ( $el, lockKey ) {
1899 // lock
1900 if ( lockKey ) {
1901 acf.lock( $el, 'hidden', lockKey );
1902 }
1903
1904 // $el is hidden, return false due to no change in visibility
1905 if ( $el.hasClass( 'acf-hidden' ) ) {
1906 return false;
1907
1908 // $el is visible, add class and return true due to change in visibility
1909 } else {
1910 $el.addClass( 'acf-hidden' );
1911 return true;
1912 }
1913 };
1914
1915 /**
1916 * acf.isHidden
1917 *
1918 * description
1919 *
1920 * @date 9/2/18
1921 * @since ACF 5.6.5
1922 *
1923 * @param type $var Description. Default.
1924 * @return type Description.
1925 */
1926
1927 acf.isHidden = function ( $el ) {
1928 return $el.hasClass( 'acf-hidden' );
1929 };
1930
1931 /**
1932 * acf.isVisible
1933 *
1934 * description
1935 *
1936 * @date 9/2/18
1937 * @since ACF 5.6.5
1938 *
1939 * @param type $var Description. Default.
1940 * @return type Description.
1941 */
1942
1943 acf.isVisible = function ( $el ) {
1944 return ! acf.isHidden( $el );
1945 };
1946
1947 /**
1948 * enable
1949 *
1950 * description
1951 *
1952 * @date 12/3/18
1953 * @since ACF 5.6.9
1954 *
1955 * @param type $var Description. Default.
1956 * @return type Description.
1957 */
1958
1959 var enable = function ( $el, lockKey ) {
1960 // check class. Allow .acf-disabled to overrule all JS
1961 if ( $el.hasClass( 'acf-disabled' ) ) {
1962 return false;
1963 }
1964
1965 // unlock
1966 if ( lockKey ) {
1967 acf.unlock( $el, 'disabled', lockKey );
1968 }
1969
1970 // bail early if $el is still locked
1971 if ( acf.isLocked( $el, 'disabled' ) ) {
1972 return false;
1973 }
1974
1975 // $el is disabled, remove prop and return true due to change
1976 if ( $el.prop( 'disabled' ) ) {
1977 $el.prop( 'disabled', false );
1978 return true;
1979
1980 // $el is enabled, return false due to no change
1981 } else {
1982 return false;
1983 }
1984 };
1985
1986 /**
1987 * acf.enable
1988 *
1989 * description
1990 *
1991 * @date 9/2/18
1992 * @since ACF 5.6.5
1993 *
1994 * @param type $var Description. Default.
1995 * @return type Description.
1996 */
1997
1998 acf.enable = function ( $el, lockKey ) {
1999 // enable single input
2000 if ( $el.attr( 'name' ) ) {
2001 return enable( $el, lockKey );
2002 }
2003
2004 // find and enable child inputs
2005 // return true if any inputs have changed
2006 var results = false;
2007 $el.find( '[name]' ).each( function () {
2008 var result = enable( $( this ), lockKey );
2009 if ( result ) {
2010 results = true;
2011 }
2012 } );
2013 return results;
2014 };
2015
2016 /**
2017 * disable
2018 *
2019 * description
2020 *
2021 * @date 12/3/18
2022 * @since ACF 5.6.9
2023 *
2024 * @param type $var Description. Default.
2025 * @return type Description.
2026 */
2027
2028 var disable = function ( $el, lockKey ) {
2029 // lock
2030 if ( lockKey ) {
2031 acf.lock( $el, 'disabled', lockKey );
2032 }
2033
2034 // $el is disabled, return false due to no change
2035 if ( $el.prop( 'disabled' ) ) {
2036 return false;
2037
2038 // $el is enabled, add prop and return true due to change
2039 } else {
2040 $el.prop( 'disabled', true );
2041 return true;
2042 }
2043 };
2044
2045 /**
2046 * acf.disable
2047 *
2048 * description
2049 *
2050 * @date 9/2/18
2051 * @since ACF 5.6.5
2052 *
2053 * @param type $var Description. Default.
2054 * @return type Description.
2055 */
2056
2057 acf.disable = function ( $el, lockKey ) {
2058 // disable single input
2059 if ( $el.attr( 'name' ) ) {
2060 return disable( $el, lockKey );
2061 }
2062
2063 // find and enable child inputs
2064 // return true if any inputs have changed
2065 var results = false;
2066 $el.find( '[name]' ).each( function () {
2067 var result = disable( $( this ), lockKey );
2068 if ( result ) {
2069 results = true;
2070 }
2071 } );
2072 return results;
2073 };
2074
2075 /**
2076 * acf.isset
2077 *
2078 * description
2079 *
2080 * @date 10/1/18
2081 * @since ACF 5.6.5
2082 *
2083 * @param type $var Description. Default.
2084 * @return type Description.
2085 */
2086
2087 acf.isset = function ( obj /*, level1, level2, ... */ ) {
2088 for ( var i = 1; i < arguments.length; i++ ) {
2089 if ( ! obj || ! obj.hasOwnProperty( arguments[ i ] ) ) {
2090 return false;
2091 }
2092 obj = obj[ arguments[ i ] ];
2093 }
2094 return true;
2095 };
2096
2097 /**
2098 * acf.isget
2099 *
2100 * description
2101 *
2102 * @date 10/1/18
2103 * @since ACF 5.6.5
2104 *
2105 * @param type $var Description. Default.
2106 * @return type Description.
2107 */
2108
2109 acf.isget = function ( obj /*, level1, level2, ... */ ) {
2110 for ( var i = 1; i < arguments.length; i++ ) {
2111 if ( ! obj || ! obj.hasOwnProperty( arguments[ i ] ) ) {
2112 return null;
2113 }
2114 obj = obj[ arguments[ i ] ];
2115 }
2116 return obj;
2117 };
2118
2119 /**
2120 * acf.getFileInputData
2121 *
2122 * description
2123 *
2124 * @date 10/1/18
2125 * @since ACF 5.6.5
2126 *
2127 * @param type $var Description. Default.
2128 * @return type Description.
2129 */
2130
2131 acf.getFileInputData = function ( $input, callback ) {
2132 // vars
2133 var value = $input.val();
2134
2135 // bail early if no value
2136 if ( ! value ) {
2137 return false;
2138 }
2139
2140 // data
2141 var data = {
2142 url: value,
2143 };
2144
2145 // modern browsers
2146 var file = $input[ 0 ].files.length
2147 ? acf.isget( $input[ 0 ].files, 0 )
2148 : false;
2149 if ( file ) {
2150 // update data
2151 data.size = file.size;
2152 data.type = file.type;
2153
2154 // image
2155 if ( file.type.indexOf( 'image' ) > -1 ) {
2156 // vars
2157 var windowURL = window.URL || window.webkitURL;
2158 var img = new Image();
2159
2160 img.onload = function () {
2161 // update
2162 data.width = this.width;
2163 data.height = this.height;
2164
2165 callback( data );
2166 };
2167 img.src = windowURL.createObjectURL( file );
2168 } else {
2169 callback( data );
2170 }
2171 } else {
2172 callback( data );
2173 }
2174 };
2175
2176 /**
2177 * acf.isAjaxSuccess
2178 *
2179 * description
2180 *
2181 * @date 18/1/18
2182 * @since ACF 5.6.5
2183 *
2184 * @param type $var Description. Default.
2185 * @return type Description.
2186 */
2187
2188 acf.isAjaxSuccess = function ( json ) {
2189 return json && json.success;
2190 };
2191
2192 /**
2193 * acf.getAjaxMessage
2194 *
2195 * description
2196 *
2197 * @date 18/1/18
2198 * @since ACF 5.6.5
2199 *
2200 * @param type $var Description. Default.
2201 * @return type Description.
2202 */
2203
2204 acf.getAjaxMessage = function ( json ) {
2205 return acf.isget( json, 'data', 'message' );
2206 };
2207
2208 /**
2209 * acf.getAjaxError
2210 *
2211 * description
2212 *
2213 * @date 18/1/18
2214 * @since ACF 5.6.5
2215 *
2216 * @param type $var Description. Default.
2217 * @return type Description.
2218 */
2219
2220 acf.getAjaxError = function ( json ) {
2221 return acf.isget( json, 'data', 'error' );
2222 };
2223
2224 /**
2225 * Returns the error message from an XHR object.
2226 *
2227 * @date 17/3/20
2228 * @since ACF 5.8.9
2229 *
2230 * @param object xhr The XHR object.
2231 * @return (string)
2232 */
2233 acf.getXhrError = function ( xhr ) {
2234 if ( xhr.responseJSON ) {
2235 // Responses via `return new WP_Error();`
2236 if ( xhr.responseJSON.message ) {
2237 return xhr.responseJSON.message;
2238 }
2239
2240 // Responses via `wp_send_json_error();`.
2241 if ( xhr.responseJSON.data && xhr.responseJSON.data.error ) {
2242 return xhr.responseJSON.data.error;
2243 }
2244 } else if ( xhr.statusText ) {
2245 return xhr.statusText;
2246 }
2247
2248 return '';
2249 };
2250
2251 /**
2252 * acf.renderSelect
2253 *
2254 * Renders the inner html for a select field.
2255 *
2256 * @date 19/2/18
2257 * @since ACF 5.6.9
2258 *
2259 * @param jQuery $select The select element.
2260 * @param array choices An array of choices.
2261 * @return void
2262 */
2263
2264 acf.renderSelect = function ( $select, choices ) {
2265 // vars
2266 var value = $select.val();
2267 var values = [];
2268
2269 // callback
2270 var crawl = function ( items ) {
2271 // vars
2272 var itemsHtml = '';
2273
2274 // loop
2275 items.map( function ( item ) {
2276 // vars
2277 var text = item.text || item.label || '';
2278 var id = item.id || item.value || '';
2279
2280 // append
2281 values.push( id );
2282
2283 // optgroup
2284 if ( item.children ) {
2285 itemsHtml +=
2286 '<optgroup label="' +
2287 acf.escAttr( text ) +
2288 '">' +
2289 crawl( item.children ) +
2290 '</optgroup>';
2291
2292 // option
2293 } else {
2294 itemsHtml +=
2295 '<option value="' +
2296 acf.escAttr( id ) +
2297 '"' +
2298 ( item.disabled ? ' disabled="disabled"' : '' ) +
2299 '>' +
2300 acf.strEscape( text ) +
2301 '</option>';
2302 }
2303 } );
2304 // return
2305 return itemsHtml;
2306 };
2307
2308 // update HTML
2309 $select.html( crawl( choices ) );
2310
2311 // update value
2312 if ( values.indexOf( value ) > -1 ) {
2313 $select.val( value );
2314 }
2315
2316 // return selected value
2317 return $select.val();
2318 };
2319
2320 /**
2321 * acf.lock
2322 *
2323 * Creates a "lock" on an element for a given type and key
2324 *
2325 * @date 22/2/18
2326 * @since ACF 5.6.9
2327 *
2328 * @param jQuery $el The element to lock.
2329 * @param string type The type of lock such as "condition" or "visibility".
2330 * @param string key The key that will be used to unlock.
2331 * @return void
2332 */
2333
2334 var getLocks = function ( $el, type ) {
2335 return $el.data( 'acf-lock-' + type ) || [];
2336 };
2337
2338 var setLocks = function ( $el, type, locks ) {
2339 $el.data( 'acf-lock-' + type, locks );
2340 };
2341
2342 acf.lock = function ( $el, type, key ) {
2343 var locks = getLocks( $el, type );
2344 var i = locks.indexOf( key );
2345 if ( i < 0 ) {
2346 locks.push( key );
2347 setLocks( $el, type, locks );
2348 }
2349 };
2350
2351 /**
2352 * acf.unlock
2353 *
2354 * Unlocks a "lock" on an element for a given type and key
2355 *
2356 * @date 22/2/18
2357 * @since ACF 5.6.9
2358 *
2359 * @param jQuery $el The element to lock.
2360 * @param string type The type of lock such as "condition" or "visibility".
2361 * @param string key The key that will be used to unlock.
2362 * @return void
2363 */
2364
2365 acf.unlock = function ( $el, type, key ) {
2366 var locks = getLocks( $el, type );
2367 var i = locks.indexOf( key );
2368 if ( i > -1 ) {
2369 locks.splice( i, 1 );
2370 setLocks( $el, type, locks );
2371 }
2372
2373 // return true if is unlocked (no locks)
2374 return locks.length === 0;
2375 };
2376
2377 /**
2378 * acf.isLocked
2379 *
2380 * Returns true if a lock exists for a given type
2381 *
2382 * @date 22/2/18
2383 * @since ACF 5.6.9
2384 *
2385 * @param jQuery $el The element to lock.
2386 * @param string type The type of lock such as "condition" or "visibility".
2387 * @return void
2388 */
2389
2390 acf.isLocked = function ( $el, type ) {
2391 return getLocks( $el, type ).length > 0;
2392 };
2393
2394 /**
2395 * acf.isGutenberg
2396 *
2397 * Returns true if the Gutenberg editor is being used.
2398 *
2399 * @since ACF 5.8.0
2400 *
2401 * @return bool
2402 */
2403 acf.isGutenberg = function () {
2404 return !! (
2405 window.wp &&
2406 wp.data &&
2407 wp.data.select &&
2408 wp.data.select( 'core/editor' )
2409 );
2410 };
2411
2412 /**
2413 * acf.isGutenbergPostEditor
2414 *
2415 * Returns true if the Gutenberg post editor is being used.
2416 *
2417 * @since ACF 6.2.2
2418 *
2419 * @return bool
2420 */
2421 acf.isGutenbergPostEditor = function () {
2422 return !! (
2423 window.wp &&
2424 wp.data &&
2425 wp.data.select &&
2426 wp.data.select( 'core/edit-post' )
2427 );
2428 };
2429
2430 /**
2431 * acf.objectToArray
2432 *
2433 * Returns an array of items from the given object.
2434 *
2435 * @date 20/11/18
2436 * @since ACF 5.8.0
2437 *
2438 * @param object obj The object of items.
2439 * @return array
2440 */
2441 acf.objectToArray = function ( obj ) {
2442 return Object.keys( obj ).map( function ( key ) {
2443 return obj[ key ];
2444 } );
2445 };
2446
2447 /**
2448 * acf.debounce
2449 *
2450 * Returns a debounced version of the passed function which will postpone its execution until after `wait` milliseconds have elapsed since the last time it was invoked.
2451 *
2452 * @date 28/8/19
2453 * @since ACF 5.8.1
2454 *
2455 * @param function callback The callback function.
2456 * @return int wait The number of milliseconds to wait.
2457 */
2458 acf.debounce = function ( callback, wait ) {
2459 var timeout;
2460 return function () {
2461 var context = this;
2462 var args = arguments;
2463 var later = function () {
2464 callback.apply( context, args );
2465 };
2466 clearTimeout( timeout );
2467 timeout = setTimeout( later, wait );
2468 };
2469 };
2470
2471 /**
2472 * acf.throttle
2473 *
2474 * Returns a throttled version of the passed function which will allow only one execution per `limit` time period.
2475 *
2476 * @date 28/8/19
2477 * @since ACF 5.8.1
2478 *
2479 * @param function callback The callback function.
2480 * @return int wait The number of milliseconds to wait.
2481 */
2482 acf.throttle = function ( callback, limit ) {
2483 var busy = false;
2484 return function () {
2485 if ( busy ) return;
2486 busy = true;
2487 setTimeout( function () {
2488 busy = false;
2489 }, limit );
2490 callback.apply( this, arguments );
2491 };
2492 };
2493
2494 /**
2495 * acf.isInView
2496 *
2497 * Returns true if the given element is in view.
2498 *
2499 * @date 29/8/19
2500 * @since ACF 5.8.1
2501 *
2502 * @param elem el The dom element to inspect.
2503 * @return bool
2504 */
2505 acf.isInView = function ( el ) {
2506 if ( el instanceof jQuery ) {
2507 el = el[ 0 ];
2508 }
2509 var rect = el.getBoundingClientRect();
2510 return (
2511 rect.top !== rect.bottom &&
2512 rect.top >= 0 &&
2513 rect.left >= 0 &&
2514 rect.bottom <=
2515 ( window.innerHeight ||
2516 document.documentElement.clientHeight ) &&
2517 rect.right <=
2518 ( window.innerWidth || document.documentElement.clientWidth )
2519 );
2520 };
2521
2522 /**
2523 * acf.onceInView
2524 *
2525 * Watches for a dom element to become visible in the browser and then executes the passed callback.
2526 *
2527 * @date 28/8/19
2528 * @since ACF 5.8.1
2529 *
2530 * @param dom el The dom element to inspect.
2531 * @param function callback The callback function.
2532 */
2533 acf.onceInView = ( function () {
2534 // Define list.
2535 var items = [];
2536 var id = 0;
2537
2538 // Define check function.
2539 var check = function () {
2540 items.forEach( function ( item ) {
2541 if ( acf.isInView( item.el ) ) {
2542 item.callback.apply( this );
2543 pop( item.id );
2544 }
2545 } );
2546 };
2547
2548 // And create a debounced version.
2549 var debounced = acf.debounce( check, 300 );
2550
2551 // Define add function.
2552 var push = function ( el, callback ) {
2553 // Add event listener.
2554 if ( ! items.length ) {
2555 $( window )
2556 .on( 'scroll resize', debounced )
2557 .on( 'acfrefresh orientationchange', check );
2558 }
2559
2560 // Append to list.
2561 items.push( { id: id++, el: el, callback: callback } );
2562 };
2563
2564 // Define remove function.
2565 var pop = function ( id ) {
2566 // Remove from list.
2567 items = items.filter( function ( item ) {
2568 return item.id !== id;
2569 } );
2570
2571 // Clean up listener.
2572 if ( ! items.length ) {
2573 $( window )
2574 .off( 'scroll resize', debounced )
2575 .off( 'acfrefresh orientationchange', check );
2576 }
2577 };
2578
2579 // Define returned function.
2580 return function ( el, callback ) {
2581 // Allow jQuery object.
2582 if ( el instanceof jQuery ) el = el[ 0 ];
2583
2584 // Execute callback if already in view or add to watch list.
2585 if ( acf.isInView( el ) ) {
2586 callback.apply( this );
2587 } else {
2588 push( el, callback );
2589 }
2590 };
2591 } )();
2592
2593 /**
2594 * acf.once
2595 *
2596 * Creates a function that is restricted to invoking `func` once.
2597 *
2598 * @date 2/9/19
2599 * @since ACF 5.8.1
2600 *
2601 * @param function func The function to restrict.
2602 * @return function
2603 */
2604 acf.once = function ( func ) {
2605 var i = 0;
2606 return function () {
2607 if ( i++ > 0 ) {
2608 return ( func = undefined );
2609 }
2610 return func.apply( this, arguments );
2611 };
2612 };
2613
2614 /**
2615 * Focuses attention to a specific element.
2616 *
2617 * @date 05/05/2020
2618 * @since ACF 5.9.0
2619 *
2620 * @param jQuery $el The jQuery element to focus.
2621 * @return void
2622 */
2623 acf.focusAttention = function ( $el ) {
2624 var wait = 1000;
2625
2626 // Apply class to focus attention.
2627 $el.addClass( 'acf-attention -focused' );
2628
2629 // Scroll to element if needed.
2630 var scrollTime = 500;
2631 if ( ! acf.isInView( $el ) ) {
2632 $( 'body, html' ).animate(
2633 {
2634 scrollTop: $el.offset().top - $( window ).height() / 2,
2635 },
2636 scrollTime
2637 );
2638 wait += scrollTime;
2639 }
2640
2641 // Remove class after $wait amount of time.
2642 var fadeTime = 250;
2643 setTimeout( function () {
2644 $el.removeClass( '-focused' );
2645 setTimeout( function () {
2646 $el.removeClass( 'acf-attention' );
2647 }, fadeTime );
2648 }, wait );
2649 };
2650
2651 /**
2652 * Description
2653 *
2654 * @date 05/05/2020
2655 * @since ACF 5.9.0
2656 *
2657 * @param type Var Description.
2658 * @return type Description.
2659 */
2660 acf.onFocus = function ( $el, callback ) {
2661 // Only run once per element.
2662 // if( $el.data('acf.onFocus') ) {
2663 // return false;
2664 // }
2665
2666 // Vars.
2667 var ignoreBlur = false;
2668 var focus = false;
2669
2670 // Functions.
2671 var onFocus = function () {
2672 ignoreBlur = true;
2673 setTimeout( function () {
2674 ignoreBlur = false;
2675 }, 1 );
2676 setFocus( true );
2677 };
2678 var onBlur = function () {
2679 if ( ! ignoreBlur ) {
2680 setFocus( false );
2681 }
2682 };
2683 var addEvents = function () {
2684 $( document ).on( 'click', onBlur );
2685 //$el.on('acfBlur', onBlur);
2686 $el.on( 'blur', 'input, select, textarea', onBlur );
2687 };
2688 var removeEvents = function () {
2689 $( document ).off( 'click', onBlur );
2690 //$el.off('acfBlur', onBlur);
2691 $el.off( 'blur', 'input, select, textarea', onBlur );
2692 };
2693 var setFocus = function ( value ) {
2694 if ( focus === value ) {
2695 return;
2696 }
2697 if ( value ) {
2698 addEvents();
2699 } else {
2700 removeEvents();
2701 }
2702 focus = value;
2703 callback( value );
2704 };
2705
2706 // Add events and set data.
2707 $el.on( 'click', onFocus );
2708 //$el.on('acfFocus', onFocus);
2709 $el.on( 'focus', 'input, select, textarea', onFocus );
2710 //$el.data('acf.onFocus', true);
2711 };
2712
2713 /**
2714 * Disable form submit buttons
2715 *
2716 * @since ACF 6.2.3
2717 *
2718 * @param event e
2719 * @returns void
2720 */
2721 acf.disableForm = function ( e ) {
2722 // Disable submit button.
2723 if ( e.submitter ) e.submitter.classList.add( 'disabled' );
2724 };
2725
2726 /*
2727 * exists
2728 *
2729 * This function will return true if a jQuery selection exists
2730 *
2731 * @type function
2732 * @date 8/09/2014
2733 * @since ACF 5.0.0
2734 *
2735 * @param n/a
2736 * @return (boolean)
2737 */
2738
2739 $.fn.exists = function () {
2740 return $( this ).length > 0;
2741 };
2742
2743 /*
2744 * outerHTML
2745 *
2746 * This function will return a string containing the HTML of the selected element
2747 *
2748 * @type function
2749 * @date 19/11/2013
2750 * @since ACF 5.0.0
2751 *
2752 * @param $.fn
2753 * @return (string)
2754 */
2755
2756 $.fn.outerHTML = function () {
2757 return $( this ).get( 0 ).outerHTML;
2758 };
2759
2760 /*
2761 * indexOf
2762 *
2763 * This function will provide compatibility for ie8
2764 *
2765 * @type function
2766 * @date 5/3/17
2767 * @since ACF 5.5.10
2768 *
2769 * @param n/a
2770 * @return n/a
2771 */
2772
2773 if ( ! Array.prototype.indexOf ) {
2774 Array.prototype.indexOf = function ( val ) {
2775 return $.inArray( val, this );
2776 };
2777 }
2778
2779 /**
2780 * Returns true if value is a number or a numeric string.
2781 *
2782 * @date 30/11/20
2783 * @since ACF 5.9.4
2784 * @link https://stackoverflow.com/questions/9716468/pure-javascript-a-function-like-jquerys-isnumeric/9716488#9716488
2785 *
2786 * @param mixed n The variable being evaluated.
2787 * @return bool.
2788 */
2789 acf.isNumeric = function ( n ) {
2790 return ! isNaN( parseFloat( n ) ) && isFinite( n );
2791 };
2792
2793 /**
2794 * Triggers a "refresh" action used by various Components to redraw the DOM.
2795 *
2796 * @date 26/05/2020
2797 * @since ACF 5.9.0
2798 *
2799 * @param void
2800 * @return void
2801 */
2802 acf.refresh = acf.debounce( function () {
2803 $( window ).trigger( 'acfrefresh' );
2804 acf.doAction( 'refresh' );
2805 }, 0 );
2806
2807 /**
2808 * Log something to console if we're in debug mode.
2809 *
2810 * @since ACF 6.3
2811 */
2812 acf.debug = function () {
2813 if ( acf.get( 'debug' ) ) console.log.apply( null, arguments );
2814 };
2815
2816 // Set up actions from events
2817 $( document ).ready( function () {
2818 acf.doAction( 'ready' );
2819 } );
2820
2821 $( window ).on( 'load', function () {
2822 // Use timeout to ensure action runs after Gutenberg has modified DOM elements during "DOMContentLoaded".
2823 setTimeout( function () {
2824 acf.doAction( 'load' );
2825 } );
2826 } );
2827
2828 $( window ).on( 'beforeunload', function () {
2829 acf.doAction( 'unload' );
2830 } );
2831
2832 $( window ).on( 'resize', function () {
2833 acf.doAction( 'resize' );
2834 } );
2835
2836 $( document ).on( 'sortstart', function ( event, ui ) {
2837 acf.doAction( 'sortstart', ui.item, ui.placeholder );
2838 } );
2839
2840 $( document ).on( 'sortstop', function ( event, ui ) {
2841 acf.doAction( 'sortstop', ui.item, ui.placeholder );
2842 } );
2843 } )( jQuery );
2844