Last active
August 10, 2023 18:45
-
-
Save fedek6/df585348868dcaa7e8613c979bd7a040 to your computer and use it in GitHub Desktop.
act-input.js source (full documentation). https://www.advancedcustomfields.com/resources/javascript-api/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function($, undefined){ | |
/** | |
* acf | |
* | |
* description | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
// The global acf object | |
var acf = {}; | |
// Set as a browser global | |
window.acf = acf; | |
/** @var object Data sent from PHP */ | |
acf.data = {}; | |
/** | |
* get | |
* | |
* Gets a specific data value | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return mixed | |
*/ | |
acf.get = function( name ){ | |
return this.data[name] || null; | |
}; | |
/** | |
* has | |
* | |
* Returns `true` if the data exists and is not null | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return boolean | |
*/ | |
acf.has = function( name ){ | |
return this.get(name) !== null; | |
}; | |
/** | |
* set | |
* | |
* Sets a specific data value | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param mixed value | |
* @return this | |
*/ | |
acf.set = function( name, value ){ | |
this.data[ name ] = value; | |
return this; | |
}; | |
/** | |
* uniqueId | |
* | |
* Returns a unique ID | |
* | |
* @date 9/11/17 | |
* @since 5.6.3 | |
* | |
* @param string prefix Optional prefix. | |
* @return string | |
*/ | |
var idCounter = 0; | |
acf.uniqueId = function(prefix){ | |
var id = ++idCounter + ''; | |
return prefix ? prefix + id : id; | |
}; | |
/** | |
* acf.uniqueArray | |
* | |
* Returns a new array with only unique values | |
* Credit: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-an-array-remove-duplicates | |
* | |
* @date 23/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.uniqueArray = function( array ){ | |
function onlyUnique(value, index, self) { | |
return self.indexOf(value) === index; | |
} | |
return array.filter( onlyUnique ); | |
}; | |
/** | |
* uniqid | |
* | |
* Returns a unique ID (PHP version) | |
* | |
* @date 9/11/17 | |
* @since 5.6.3 | |
* @source http://locutus.io/php/misc/uniqid/ | |
* | |
* @param string prefix Optional prefix. | |
* @return string | |
*/ | |
var uniqidSeed = ''; | |
acf.uniqid = function(prefix, moreEntropy){ | |
// discuss at: http://locutus.io/php/uniqid/ | |
// original by: Kevin van Zonneveld (http://kvz.io) | |
// revised by: Kankrelune (http://www.webfaktory.info/) | |
// note 1: Uses an internal counter (in locutus global) to avoid collision | |
// example 1: var $id = uniqid() | |
// example 1: var $result = $id.length === 13 | |
// returns 1: true | |
// example 2: var $id = uniqid('foo') | |
// example 2: var $result = $id.length === (13 + 'foo'.length) | |
// returns 2: true | |
// example 3: var $id = uniqid('bar', true) | |
// example 3: var $result = $id.length === (23 + 'bar'.length) | |
// returns 3: true | |
if (typeof prefix === 'undefined') { | |
prefix = ''; | |
} | |
var retId; | |
var formatSeed = function(seed, reqWidth) { | |
seed = parseInt(seed, 10).toString(16); // to hex str | |
if (reqWidth < seed.length) { // so long we split | |
return seed.slice(seed.length - reqWidth); | |
} | |
if (reqWidth > seed.length) { // so short we pad | |
return Array(1 + (reqWidth - seed.length)).join('0') + seed; | |
} | |
return seed; | |
}; | |
if (!uniqidSeed) { // init seed with big random int | |
uniqidSeed = Math.floor(Math.random() * 0x75bcd15); | |
} | |
uniqidSeed++; | |
retId = prefix; // start with prefix, add current milliseconds hex string | |
retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8); | |
retId += formatSeed(uniqidSeed, 5); // add seed hex string | |
if (moreEntropy) { | |
// for more entropy we add a float lower to 10 | |
retId += (Math.random() * 10).toFixed(8).toString(); | |
} | |
return retId; | |
}; | |
/** | |
* strReplace | |
* | |
* Performs a string replace | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string search | |
* @param string replace | |
* @param string subject | |
* @return string | |
*/ | |
acf.strReplace = function( search, replace, subject ){ | |
return subject.split(search).join(replace); | |
}; | |
/** | |
* strCamelCase | |
* | |
* Converts a string into camelCase | |
* Thanks to https://stackoverflow.com/questions/2970525/converting-any-string-into-camel-case | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string str | |
* @return string | |
*/ | |
acf.strCamelCase = function( str ){ | |
// replace [_-] characters with space | |
str = str.replace(/[_-]/g, ' '); | |
// camelCase | |
str = str.replace(/(?:^\w|\b\w|\s+)/g, function(match, index) { | |
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces | |
return index == 0 ? match.toLowerCase() : match.toUpperCase(); | |
}); | |
// return | |
return str; | |
}; | |
/** | |
* strPascalCase | |
* | |
* Converts a string into PascalCase | |
* Thanks to https://stackoverflow.com/questions/1026069/how-do-i-make-the-first-letter-of-a-string-uppercase-in-javascript | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string str | |
* @return string | |
*/ | |
acf.strPascalCase = function( str ){ | |
var camel = acf.strCamelCase( str ); | |
return camel.charAt(0).toUpperCase() + camel.slice(1); | |
}; | |
/** | |
* acf.strSlugify | |
* | |
* Converts a string into a HTML class friendly slug | |
* | |
* @date 21/3/18 | |
* @since 5.6.9 | |
* | |
* @param string str | |
* @return string | |
*/ | |
acf.strSlugify = function( str ){ | |
return acf.strReplace( '_', '-', str.toLowerCase() ); | |
}; | |
acf.strSanitize = function( str ){ | |
// chars (https://jsperf.com/replace-foreign-characters) | |
var map = { | |
"À": "A", | |
"Á": "A", | |
"Â": "A", | |
"Ã": "A", | |
"Ä": "A", | |
"Å": "A", | |
"Æ": "AE", | |
"Ç": "C", | |
"È": "E", | |
"É": "E", | |
"Ê": "E", | |
"Ë": "E", | |
"Ì": "I", | |
"Í": "I", | |
"Î": "I", | |
"Ï": "I", | |
"Ð": "D", | |
"Ñ": "N", | |
"Ò": "O", | |
"Ó": "O", | |
"Ô": "O", | |
"Õ": "O", | |
"Ö": "O", | |
"Ø": "O", | |
"Ù": "U", | |
"Ú": "U", | |
"Û": "U", | |
"Ü": "U", | |
"Ý": "Y", | |
"ß": "s", | |
"à": "a", | |
"á": "a", | |
"â": "a", | |
"ã": "a", | |
"ä": "a", | |
"å": "a", | |
"æ": "ae", | |
"ç": "c", | |
"è": "e", | |
"é": "e", | |
"ê": "e", | |
"ë": "e", | |
"ì": "i", | |
"í": "i", | |
"î": "i", | |
"ï": "i", | |
"ñ": "n", | |
"ò": "o", | |
"ó": "o", | |
"ô": "o", | |
"õ": "o", | |
"ö": "o", | |
"ø": "o", | |
"ù": "u", | |
"ú": "u", | |
"û": "u", | |
"ü": "u", | |
"ý": "y", | |
"ÿ": "y", | |
"Ā": "A", | |
"ā": "a", | |
"Ă": "A", | |
"ă": "a", | |
"Ą": "A", | |
"ą": "a", | |
"Ć": "C", | |
"ć": "c", | |
"Ĉ": "C", | |
"ĉ": "c", | |
"Ċ": "C", | |
"ċ": "c", | |
"Č": "C", | |
"č": "c", | |
"Ď": "D", | |
"ď": "d", | |
"Đ": "D", | |
"đ": "d", | |
"Ē": "E", | |
"ē": "e", | |
"Ĕ": "E", | |
"ĕ": "e", | |
"Ė": "E", | |
"ė": "e", | |
"Ę": "E", | |
"ę": "e", | |
"Ě": "E", | |
"ě": "e", | |
"Ĝ": "G", | |
"ĝ": "g", | |
"Ğ": "G", | |
"ğ": "g", | |
"Ġ": "G", | |
"ġ": "g", | |
"Ģ": "G", | |
"ģ": "g", | |
"Ĥ": "H", | |
"ĥ": "h", | |
"Ħ": "H", | |
"ħ": "h", | |
"Ĩ": "I", | |
"ĩ": "i", | |
"Ī": "I", | |
"ī": "i", | |
"Ĭ": "I", | |
"ĭ": "i", | |
"Į": "I", | |
"į": "i", | |
"İ": "I", | |
"ı": "i", | |
"IJ": "IJ", | |
"ij": "ij", | |
"Ĵ": "J", | |
"ĵ": "j", | |
"Ķ": "K", | |
"ķ": "k", | |
"Ĺ": "L", | |
"ĺ": "l", | |
"Ļ": "L", | |
"ļ": "l", | |
"Ľ": "L", | |
"ľ": "l", | |
"Ŀ": "L", | |
"ŀ": "l", | |
"Ł": "l", | |
"ł": "l", | |
"Ń": "N", | |
"ń": "n", | |
"Ņ": "N", | |
"ņ": "n", | |
"Ň": "N", | |
"ň": "n", | |
"ʼn": "n", | |
"Ō": "O", | |
"ō": "o", | |
"Ŏ": "O", | |
"ŏ": "o", | |
"Ő": "O", | |
"ő": "o", | |
"Œ": "OE", | |
"œ": "oe", | |
"Ŕ": "R", | |
"ŕ": "r", | |
"Ŗ": "R", | |
"ŗ": "r", | |
"Ř": "R", | |
"ř": "r", | |
"Ś": "S", | |
"ś": "s", | |
"Ŝ": "S", | |
"ŝ": "s", | |
"Ş": "S", | |
"ş": "s", | |
"Š": "S", | |
"š": "s", | |
"Ţ": "T", | |
"ţ": "t", | |
"Ť": "T", | |
"ť": "t", | |
"Ŧ": "T", | |
"ŧ": "t", | |
"Ũ": "U", | |
"ũ": "u", | |
"Ū": "U", | |
"ū": "u", | |
"Ŭ": "U", | |
"ŭ": "u", | |
"Ů": "U", | |
"ů": "u", | |
"Ű": "U", | |
"ű": "u", | |
"Ų": "U", | |
"ų": "u", | |
"Ŵ": "W", | |
"ŵ": "w", | |
"Ŷ": "Y", | |
"ŷ": "y", | |
"Ÿ": "Y", | |
"Ź": "Z", | |
"ź": "z", | |
"Ż": "Z", | |
"ż": "z", | |
"Ž": "Z", | |
"ž": "z", | |
"ſ": "s", | |
"ƒ": "f", | |
"Ơ": "O", | |
"ơ": "o", | |
"Ư": "U", | |
"ư": "u", | |
"Ǎ": "A", | |
"ǎ": "a", | |
"Ǐ": "I", | |
"ǐ": "i", | |
"Ǒ": "O", | |
"ǒ": "o", | |
"Ǔ": "U", | |
"ǔ": "u", | |
"Ǖ": "U", | |
"ǖ": "u", | |
"Ǘ": "U", | |
"ǘ": "u", | |
"Ǚ": "U", | |
"ǚ": "u", | |
"Ǜ": "U", | |
"ǜ": "u", | |
"Ǻ": "A", | |
"ǻ": "a", | |
"Ǽ": "AE", | |
"ǽ": "ae", | |
"Ǿ": "O", | |
"ǿ": "o", | |
// extra | |
' ': '_', | |
"'": '', | |
'?': '', | |
'/': '', | |
'\\': '', | |
'.': '', | |
',': '', | |
'`': '', | |
'>': '', | |
'<': '', | |
'"': '', | |
'[': '', | |
']': '', | |
'|': '', | |
'{': '', | |
'}': '', | |
'(': '', | |
')': '' | |
}; | |
// vars | |
var nonWord = /\W/g; | |
var mapping = function (c) { | |
return (map[c] !== undefined) ? map[c] : c; | |
}; | |
// replace | |
str = str.replace(nonWord, mapping); | |
// lowercase | |
str = str.toLowerCase(); | |
// return | |
return str; | |
}; | |
/** | |
* acf.strMatch | |
* | |
* Returns the number of characters that match between two strings | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.strMatch = function( s1, s2 ){ | |
// vars | |
var val = 0; | |
var min = Math.min( s1.length, s2.length ); | |
// loop | |
for( var i = 0; i < min; i++ ) { | |
if( s1[i] !== s2[i] ) { | |
break; | |
} | |
val++; | |
} | |
// return | |
return val; | |
}; | |
/** | |
* acf.decode | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.decode = function( string ){ | |
return $('<textarea/>').html( string ).text(); | |
}; | |
/** | |
* acf.strEscape | |
* | |
* description | |
* | |
* @date 3/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.strEscape = function( string ){ | |
return $('<div>').text(string).html(); | |
}; | |
/** | |
* parseArgs | |
* | |
* Merges together defaults and args much like the WP wp_parse_args function | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object args | |
* @param object defaults | |
* @return object | |
*/ | |
acf.parseArgs = function( args, defaults ){ | |
if( typeof args !== 'object' ) args = {}; | |
if( typeof defaults !== 'object' ) defaults = {}; | |
return $.extend({}, defaults, args); | |
} | |
/** | |
* __ | |
* | |
* Retrieve the translation of $text. | |
* | |
* @date 16/4/18 | |
* @since 5.6.9 | |
* | |
* @param string text Text to translate. | |
* @return string Translated text. | |
*/ | |
if( window.acfL10n == undefined ) { | |
acfL10n = {}; | |
} | |
acf.__ = function( text ){ | |
return acfL10n[ text ] || text; | |
}; | |
/** | |
* _x | |
* | |
* Retrieve translated string with gettext context. | |
* | |
* @date 16/4/18 | |
* @since 5.6.9 | |
* | |
* @param string text Text to translate. | |
* @param string context Context information for the translators. | |
* @return string Translated text. | |
*/ | |
acf._x = function( text, context ){ | |
return acfL10n[ text + '.' + context ] || acfL10n[ text ] || text; | |
}; | |
/** | |
* _n | |
* | |
* Retrieve the plural or single form based on the amount. | |
* | |
* @date 16/4/18 | |
* @since 5.6.9 | |
* | |
* @param string single Single text to translate. | |
* @param string plural Plural text to translate. | |
* @param int number The number to compare against. | |
* @return string Translated text. | |
*/ | |
acf._n = function( single, plural, number ){ | |
if( number == 1 ) { | |
return acf.__(single); | |
} else { | |
return acf.__(plural); | |
} | |
}; | |
acf.isArray = function( a ){ | |
return Array.isArray(a); | |
}; | |
acf.isObject = function( a ){ | |
return ( typeof a === 'object' ); | |
} | |
/** | |
* serialize | |
* | |
* description | |
* | |
* @date 24/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var buildObject = function( obj, name, value ){ | |
// replace [] with placeholder | |
name = name.replace('[]', '[%%index%%]'); | |
// vars | |
var keys = name.match(/([^\[\]])+/g); | |
if( !keys ) return; | |
var length = keys.length; | |
var ref = obj; | |
// loop | |
for( var i = 0; i < length; i++ ) { | |
// vars | |
var key = String( keys[i] ); | |
// value | |
if( i == length - 1 ) { | |
// %%index%% | |
if( key === '%%index%%' ) { | |
ref.push( value ); | |
// default | |
} else { | |
ref[ key ] = value; | |
} | |
// path | |
} else { | |
// array | |
if( keys[i+1] === '%%index%%' ) { | |
if( !acf.isArray(ref[ key ]) ) { | |
ref[ key ] = []; | |
} | |
// object | |
} else { | |
if( !acf.isObject(ref[ key ]) ) { | |
ref[ key ] = {}; | |
} | |
} | |
// crawl | |
ref = ref[ key ]; | |
} | |
} | |
}; | |
acf.serialize = function( $el, prefix ){ | |
// vars | |
var obj = {}; | |
var inputs = acf.serializeArray( $el ); | |
// prefix | |
if( prefix !== undefined ) { | |
// filter and modify | |
inputs = inputs.filter(function( item ){ | |
return item.name.indexOf(prefix) === 0; | |
}).map(function( item ){ | |
item.name = item.name.slice(prefix.length); | |
return item; | |
}); | |
} | |
// loop | |
for( var i = 0; i < inputs.length; i++ ) { | |
buildObject( obj, inputs[i].name, inputs[i].value ); | |
} | |
// return | |
return obj; | |
}; | |
/** | |
* acf.serializeArray | |
* | |
* Similar to $.serializeArray() but works with a parent wrapping element. | |
* | |
* @date 19/8/18 | |
* @since 5.7.3 | |
* | |
* @param jQuery $el The element or form to serialize. | |
* @return array | |
*/ | |
acf.serializeArray = function( $el ){ | |
return $el.find('select, textarea, input').serializeArray(); | |
} | |
/** | |
* acf.serializeForAjax | |
* | |
* Returns an object containing name => value data ready to be encoded for Ajax. | |
* | |
* @date 17/12/18 | |
* @since 5.8.0 | |
* | |
* @param jQUery $el The element or form to serialize. | |
* @return object | |
*/ | |
acf.serializeForAjax = function( $el ){ | |
// vars | |
var data = {}; | |
var index = {}; | |
// Serialize inputs. | |
var inputs = acf.serializeArray( $el ); | |
// Loop over inputs and build data. | |
inputs.map(function( item ){ | |
// Append to array. | |
if( item.name.slice(-2) === '[]' ) { | |
data[ item.name ] = data[ item.name ] || []; | |
data[ item.name ].push( item.value ); | |
// Append | |
} else { | |
data[ item.name ] = item.value; | |
} | |
}); | |
// return | |
return data; | |
}; | |
/** | |
* addAction | |
* | |
* Wrapper for acf.hooks.addAction | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
/* | |
var prefixAction = function( action ){ | |
return 'acf_' + action; | |
} | |
*/ | |
acf.addAction = function( action, callback, priority, context ){ | |
//action = prefixAction(action); | |
acf.hooks.addAction.apply(this, arguments); | |
return this; | |
}; | |
/** | |
* removeAction | |
* | |
* Wrapper for acf.hooks.removeAction | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.removeAction = function( action, callback ){ | |
//action = prefixAction(action); | |
acf.hooks.removeAction.apply(this, arguments); | |
return this; | |
}; | |
/** | |
* doAction | |
* | |
* Wrapper for acf.hooks.doAction | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
var actionHistory = {}; | |
//var currentAction = false; | |
acf.doAction = function( action ){ | |
//action = prefixAction(action); | |
//currentAction = action; | |
actionHistory[ action ] = 1; | |
acf.hooks.doAction.apply(this, arguments); | |
actionHistory[ action ] = 0; | |
return this; | |
}; | |
/** | |
* doingAction | |
* | |
* Return true if doing action | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.doingAction = function( action ){ | |
//action = prefixAction(action); | |
return (actionHistory[ action ] === 1); | |
}; | |
/** | |
* didAction | |
* | |
* Wrapper for acf.hooks.doAction | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.didAction = function( action ){ | |
//action = prefixAction(action); | |
return (actionHistory[ action ] !== undefined); | |
}; | |
/** | |
* currentAction | |
* | |
* Wrapper for acf.hooks.doAction | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.currentAction = function(){ | |
for( var k in actionHistory ) { | |
if( actionHistory[k] ) { | |
return k; | |
} | |
} | |
return false; | |
}; | |
/** | |
* addFilter | |
* | |
* Wrapper for acf.hooks.addFilter | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.addFilter = function( action ){ | |
//action = prefixAction(action); | |
acf.hooks.addFilter.apply(this, arguments); | |
return this; | |
}; | |
/** | |
* removeFilter | |
* | |
* Wrapper for acf.hooks.removeFilter | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.removeFilter = function( action ){ | |
//action = prefixAction(action); | |
acf.hooks.removeFilter.apply(this, arguments); | |
return this; | |
}; | |
/** | |
* applyFilters | |
* | |
* Wrapper for acf.hooks.applyFilters | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return this | |
*/ | |
acf.applyFilters = function( action ){ | |
//action = prefixAction(action); | |
return acf.hooks.applyFilters.apply(this, arguments); | |
}; | |
/** | |
* getArgs | |
* | |
* description | |
* | |
* @date 15/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.arrayArgs = function( args ){ | |
return Array.prototype.slice.call( args ); | |
}; | |
/** | |
* extendArgs | |
* | |
* description | |
* | |
* @date 15/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
/* | |
acf.extendArgs = function( ){ | |
var args = Array.prototype.slice.call( arguments ); | |
var realArgs = args.shift(); | |
Array.prototype.push.call(arguments, 'bar') | |
return Array.prototype.push.apply( args, arguments ); | |
}; | |
*/ | |
// Preferences | |
// - use try/catch to avoid JS error if cookies are disabled on front-end form | |
try { | |
var preferences = JSON.parse(localStorage.getItem('acf')) || {}; | |
} catch(e) { | |
var preferences = {}; | |
} | |
/** | |
* getPreferenceName | |
* | |
* Gets the true preference name. | |
* Converts "this.thing" to "thing-123" if editing post 123. | |
* | |
* @date 11/11/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return string | |
*/ | |
var getPreferenceName = function( name ){ | |
if( name.substr(0, 5) === 'this.' ) { | |
name = name.substr(5) + '-' + acf.get('post_id'); | |
} | |
return name; | |
}; | |
/** | |
* acf.getPreference | |
* | |
* Gets a preference setting or null if not set. | |
* | |
* @date 11/11/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return mixed | |
*/ | |
acf.getPreference = function( name ){ | |
name = getPreferenceName( name ); | |
return preferences[ name ] || null; | |
} | |
/** | |
* acf.setPreference | |
* | |
* Sets a preference setting. | |
* | |
* @date 11/11/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param mixed value | |
* @return n/a | |
*/ | |
acf.setPreference = function( name, value ){ | |
name = getPreferenceName( name ); | |
if( value === null ) { | |
delete preferences[ name ]; | |
} else { | |
preferences[ name ] = value; | |
} | |
localStorage.setItem('acf', JSON.stringify(preferences)); | |
} | |
/** | |
* acf.removePreference | |
* | |
* Removes a preference setting. | |
* | |
* @date 11/11/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return n/a | |
*/ | |
acf.removePreference = function( name ){ | |
acf.setPreference(name, null); | |
}; | |
/** | |
* remove | |
* | |
* Removes an element with fade effect | |
* | |
* @date 1/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.remove = function( props ){ | |
// allow jQuery | |
if( props instanceof jQuery ) { | |
props = { | |
target: props | |
}; | |
} | |
// defaults | |
props = acf.parseArgs(props, { | |
target: false, | |
endHeight: 0, | |
complete: function(){} | |
}); | |
// action | |
acf.doAction('remove', props.target); | |
// tr | |
if( props.target.is('tr') ) { | |
removeTr( props ); | |
// div | |
} else { | |
removeDiv( props ); | |
} | |
}; | |
/** | |
* removeDiv | |
* | |
* description | |
* | |
* @date 16/2/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var removeDiv = function( props ){ | |
// vars | |
var $el = props.target; | |
var height = $el.height(); | |
var width = $el.width(); | |
var margin = $el.css('margin'); | |
var outerHeight = $el.outerHeight(true); | |
var style = $el.attr('style') + ''; // needed to copy | |
// wrap | |
$el.wrap('<div class="acf-temp-remove" style="height:' + outerHeight + 'px"></div>'); | |
var $wrap = $el.parent(); | |
// set pos | |
$el.css({ | |
height: height, | |
width: width, | |
margin: margin, | |
position: 'absolute' | |
}); | |
// fade wrap | |
setTimeout(function(){ | |
$wrap.css({ | |
opacity: 0, | |
height: props.endHeight | |
}); | |
}, 50); | |
// remove | |
setTimeout(function(){ | |
$el.attr('style', style); | |
$wrap.remove(); | |
props.complete(); | |
}, 301); | |
}; | |
/** | |
* removeTr | |
* | |
* description | |
* | |
* @date 16/2/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var removeTr = function( props ){ | |
// vars | |
var $tr = props.target; | |
var height = $tr.height(); | |
var children = $tr.children().length; | |
// create dummy td | |
var $td = $('<td class="acf-temp-remove" style="padding:0; height:' + height + 'px" colspan="' + children + '"></td>'); | |
// fade away tr | |
$tr.addClass('acf-remove-element'); | |
// update HTML after fade animation | |
setTimeout(function(){ | |
$tr.html( $td ); | |
}, 251); | |
// allow .acf-temp-remove to exist before changing CSS | |
setTimeout(function(){ | |
// remove class | |
$tr.removeClass('acf-remove-element'); | |
// collapse | |
$td.css({ | |
height: props.endHeight | |
}); | |
}, 300); | |
// remove | |
setTimeout(function(){ | |
$tr.remove(); | |
props.complete(); | |
}, 451); | |
}; | |
/** | |
* duplicate | |
* | |
* description | |
* | |
* @date 3/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.duplicate = function( args ){ | |
// allow jQuery | |
if( args instanceof jQuery ) { | |
args = { | |
target: args | |
}; | |
} | |
// vars | |
var timeout = 0; | |
// defaults | |
args = acf.parseArgs(args, { | |
target: false, | |
search: '', | |
replace: '', | |
before: function( $el ){}, | |
after: function( $el, $el2 ){}, | |
append: function( $el, $el2 ){ | |
$el.after( $el2 ); | |
timeout = 1; | |
} | |
}); | |
// compatibility | |
args.target = args.target || args.$el; | |
// vars | |
var $el = args.target; | |
// search | |
args.search = args.search || $el.attr('data-id'); | |
args.replace = args.replace || acf.uniqid(); | |
// before | |
// - allow acf to modify DOM | |
// - fixes bug where select field option is not selected | |
args.before( $el ); | |
acf.doAction('before_duplicate', $el); | |
// clone | |
var $el2 = $el.clone(); | |
// rename | |
acf.rename({ | |
target: $el2, | |
search: args.search, | |
replace: args.replace, | |
}); | |
// remove classes | |
$el2.removeClass('acf-clone'); | |
$el2.find('.ui-sortable').removeClass('ui-sortable'); | |
// after | |
// - allow acf to modify DOM | |
args.after( $el, $el2 ); | |
acf.doAction('after_duplicate', $el, $el2 ); | |
// append | |
args.append( $el, $el2 ); | |
/** | |
* Fires after an element has been duplicated and appended to the DOM. | |
* | |
* @date 30/10/19 | |
* @since 5.8.7 | |
* | |
* @param jQuery $el The original element. | |
* @param jQuery $el2 The duplicated element. | |
*/ | |
acf.doAction('duplicate', $el, $el2 ); | |
// append | |
// - allow element to be moved into a visible position before fire action | |
//var callback = function(){ | |
acf.doAction('append', $el2); | |
//}; | |
//if( timeout ) { | |
// setTimeout(callback, timeout); | |
//} else { | |
// callback(); | |
//} | |
// return | |
return $el2; | |
}; | |
/** | |
* rename | |
* | |
* description | |
* | |
* @date 7/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.rename = function( args ){ | |
// allow jQuery | |
if( args instanceof jQuery ) { | |
args = { | |
target: args | |
}; | |
} | |
// defaults | |
args = acf.parseArgs(args, { | |
target: false, | |
destructive: false, | |
search: '', | |
replace: '', | |
}); | |
// vars | |
var $el = args.target; | |
var search = args.search || $el.attr('data-id'); | |
var replace = args.replace || acf.uniqid('acf'); | |
var replaceAttr = function(i, value){ | |
return value.replace( search, replace ); | |
} | |
// replace (destructive) | |
if( args.destructive ) { | |
var html = $el.outerHTML(); | |
html = acf.strReplace( search, replace, html ); | |
$el.replaceWith( html ); | |
// replace | |
} else { | |
$el.attr('data-id', replace); | |
$el.find('[id*="' + search + '"]').attr('id', replaceAttr); | |
$el.find('[for*="' + search + '"]').attr('for', replaceAttr); | |
$el.find('[name*="' + search + '"]').attr('name', replaceAttr); | |
} | |
// return | |
return $el; | |
}; | |
/** | |
* acf.prepareForAjax | |
* | |
* description | |
* | |
* @date 4/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.prepareForAjax = function( data ){ | |
// required | |
data.nonce = acf.get('nonce'); | |
data.post_id = acf.get('post_id'); | |
// language | |
if( acf.has('language') ) { | |
data.lang = acf.get('language'); | |
} | |
// filter for 3rd party customization | |
data = acf.applyFilters('prepare_for_ajax', data); | |
// return | |
return data; | |
}; | |
/** | |
* acf.startButtonLoading | |
* | |
* description | |
* | |
* @date 5/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.startButtonLoading = function( $el ){ | |
$el.prop('disabled', true); | |
$el.after(' <i class="acf-loading"></i>'); | |
} | |
acf.stopButtonLoading = function( $el ){ | |
$el.prop('disabled', false); | |
$el.next('.acf-loading').remove(); | |
} | |
/** | |
* acf.showLoading | |
* | |
* description | |
* | |
* @date 12/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.showLoading = function( $el ){ | |
$el.append('<div class="acf-loading-overlay"><i class="acf-loading"></i></div>'); | |
}; | |
acf.hideLoading = function( $el ){ | |
$el.children('.acf-loading-overlay').remove(); | |
}; | |
/** | |
* acf.updateUserSetting | |
* | |
* description | |
* | |
* @date 5/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.updateUserSetting = function( name, value ){ | |
var ajaxData = { | |
action: 'acf/ajax/user_setting', | |
name: name, | |
value: value | |
}; | |
$.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax(ajaxData), | |
type: 'post', | |
dataType: 'html' | |
}); | |
}; | |
/** | |
* acf.val | |
* | |
* description | |
* | |
* @date 8/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.val = function( $input, value, silent ){ | |
// vars | |
var prevValue = $input.val(); | |
// bail if no change | |
if( value === prevValue ) { | |
return false | |
} | |
// update value | |
$input.val( value ); | |
// prevent select elements displaying blank value if option doesn't exist | |
if( $input.is('select') && $input.val() === null ) { | |
$input.val( prevValue ); | |
return false; | |
} | |
// update with trigger | |
if( silent !== true ) { | |
$input.trigger('change'); | |
} | |
// return | |
return true; | |
}; | |
/** | |
* acf.show | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.show = function( $el, lockKey ){ | |
// unlock | |
if( lockKey ) { | |
acf.unlock($el, 'hidden', lockKey); | |
} | |
// bail early if $el is still locked | |
if( acf.isLocked($el, 'hidden') ) { | |
//console.log( 'still locked', getLocks( $el, 'hidden' )); | |
return false; | |
} | |
// $el is hidden, remove class and return true due to change in visibility | |
if( $el.hasClass('acf-hidden') ) { | |
$el.removeClass('acf-hidden'); | |
return true; | |
// $el is visible, return false due to no change in visibility | |
} else { | |
return false; | |
} | |
}; | |
/** | |
* acf.hide | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.hide = function( $el, lockKey ){ | |
// lock | |
if( lockKey ) { | |
acf.lock($el, 'hidden', lockKey); | |
} | |
// $el is hidden, return false due to no change in visibility | |
if( $el.hasClass('acf-hidden') ) { | |
return false; | |
// $el is visible, add class and return true due to change in visibility | |
} else { | |
$el.addClass('acf-hidden'); | |
return true; | |
} | |
}; | |
/** | |
* acf.isHidden | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.isHidden = function( $el ){ | |
return $el.hasClass('acf-hidden'); | |
}; | |
/** | |
* acf.isVisible | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.isVisible = function( $el ){ | |
return !acf.isHidden( $el ); | |
}; | |
/** | |
* enable | |
* | |
* description | |
* | |
* @date 12/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var enable = function( $el, lockKey ){ | |
// check class. Allow .acf-disabled to overrule all JS | |
if( $el.hasClass('acf-disabled') ) { | |
return false; | |
} | |
// unlock | |
if( lockKey ) { | |
acf.unlock($el, 'disabled', lockKey); | |
} | |
// bail early if $el is still locked | |
if( acf.isLocked($el, 'disabled') ) { | |
return false; | |
} | |
// $el is disabled, remove prop and return true due to change | |
if( $el.prop('disabled') ) { | |
$el.prop('disabled', false); | |
return true; | |
// $el is enabled, return false due to no change | |
} else { | |
return false; | |
} | |
}; | |
/** | |
* acf.enable | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.enable = function( $el, lockKey ){ | |
// enable single input | |
if( $el.attr('name') ) { | |
return enable( $el, lockKey ); | |
} | |
// find and enable child inputs | |
// return true if any inputs have changed | |
var results = false; | |
$el.find('[name]').each(function(){ | |
var result = enable( $(this), lockKey ); | |
if( result ) { | |
results = true; | |
} | |
}); | |
return results; | |
}; | |
/** | |
* disable | |
* | |
* description | |
* | |
* @date 12/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var disable = function( $el, lockKey ){ | |
// lock | |
if( lockKey ) { | |
acf.lock($el, 'disabled', lockKey); | |
} | |
// $el is disabled, return false due to no change | |
if( $el.prop('disabled') ) { | |
return false; | |
// $el is enabled, add prop and return true due to change | |
} else { | |
$el.prop('disabled', true); | |
return true; | |
} | |
}; | |
/** | |
* acf.disable | |
* | |
* description | |
* | |
* @date 9/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.disable = function( $el, lockKey ){ | |
// disable single input | |
if( $el.attr('name') ) { | |
return disable( $el, lockKey ); | |
} | |
// find and enable child inputs | |
// return true if any inputs have changed | |
var results = false; | |
$el.find('[name]').each(function(){ | |
var result = disable( $(this), lockKey ); | |
if( result ) { | |
results = true; | |
} | |
}); | |
return results; | |
}; | |
/** | |
* acf.isset | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.isset = function( obj /*, level1, level2, ... */ ) { | |
for( var i = 1; i < arguments.length; i++ ) { | |
if( !obj || !obj.hasOwnProperty(arguments[i]) ) { | |
return false; | |
} | |
obj = obj[ arguments[i] ]; | |
} | |
return true; | |
}; | |
/** | |
* acf.isget | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.isget = function( obj /*, level1, level2, ... */ ) { | |
for( var i = 1; i < arguments.length; i++ ) { | |
if( !obj || !obj.hasOwnProperty(arguments[i]) ) { | |
return null; | |
} | |
obj = obj[ arguments[i] ]; | |
} | |
return obj; | |
}; | |
/** | |
* acf.getFileInputData | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getFileInputData = function( $input, callback ){ | |
// vars | |
var value = $input.val(); | |
// bail early if no value | |
if( !value ) { | |
return false; | |
} | |
// data | |
var data = { | |
url: value | |
}; | |
// modern browsers | |
var file = acf.isget( $input[0], 'files', 0); | |
if( file ){ | |
// update data | |
data.size = file.size; | |
data.type = file.type; | |
// image | |
if( file.type.indexOf('image') > -1 ) { | |
// vars | |
var windowURL = window.URL || window.webkitURL; | |
var img = new Image(); | |
img.onload = function() { | |
// update | |
data.width = this.width; | |
data.height = this.height; | |
callback( data ); | |
}; | |
img.src = windowURL.createObjectURL( file ); | |
} else { | |
callback( data ); | |
} | |
} else { | |
callback( data ); | |
} | |
}; | |
/** | |
* acf.isAjaxSuccess | |
* | |
* description | |
* | |
* @date 18/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.isAjaxSuccess = function( json ){ | |
return ( json && json.success ); | |
}; | |
/** | |
* acf.getAjaxMessage | |
* | |
* description | |
* | |
* @date 18/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getAjaxMessage = function( json ){ | |
return acf.isget( json, 'data', 'message' ); | |
}; | |
/** | |
* acf.getAjaxError | |
* | |
* description | |
* | |
* @date 18/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getAjaxError = function( json ){ | |
return acf.isget( json, 'data', 'error' ); | |
}; | |
/** | |
* acf.renderSelect | |
* | |
* Renders the innter html for a select field. | |
* | |
* @date 19/2/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $select The select element. | |
* @param array choices An array of choices. | |
* @return void | |
*/ | |
acf.renderSelect = function( $select, choices ){ | |
// vars | |
var value = $select.val(); | |
var values = []; | |
// callback | |
var crawl = function( items ){ | |
// vars | |
var itemsHtml = ''; | |
// loop | |
items.map(function( item ){ | |
// vars | |
var text = item.text || item.label || ''; | |
var id = item.id || item.value || ''; | |
// append | |
values.push(id); | |
// optgroup | |
if( item.children ) { | |
itemsHtml += '<optgroup label="' + acf.strEscape(text) + '">' + crawl( item.children ) + '</optgroup>'; | |
// option | |
} else { | |
itemsHtml += '<option value="' + id + '"' + (item.disabled ? ' disabled="disabled"' : '') + '>' + acf.strEscape(text) + '</option>'; | |
} | |
}); | |
// return | |
return itemsHtml; | |
}; | |
// update HTML | |
$select.html( crawl(choices) ); | |
// update value | |
if( values.indexOf(value) > -1 ){ | |
$select.val( value ); | |
} | |
// return selected value | |
return $select.val(); | |
}; | |
/** | |
* acf.lock | |
* | |
* Creates a "lock" on an element for a given type and key | |
* | |
* @date 22/2/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $el The element to lock. | |
* @param string type The type of lock such as "condition" or "visibility". | |
* @param string key The key that will be used to unlock. | |
* @return void | |
*/ | |
var getLocks = function( $el, type ){ | |
return $el.data('acf-lock-'+type) || []; | |
}; | |
var setLocks = function( $el, type, locks ){ | |
$el.data('acf-lock-'+type, locks); | |
} | |
acf.lock = function( $el, type, key ){ | |
var locks = getLocks( $el, type ); | |
var i = locks.indexOf(key); | |
if( i < 0 ) { | |
locks.push( key ); | |
setLocks( $el, type, locks ); | |
} | |
}; | |
/** | |
* acf.unlock | |
* | |
* Unlocks a "lock" on an element for a given type and key | |
* | |
* @date 22/2/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $el The element to lock. | |
* @param string type The type of lock such as "condition" or "visibility". | |
* @param string key The key that will be used to unlock. | |
* @return void | |
*/ | |
acf.unlock = function( $el, type, key ){ | |
var locks = getLocks( $el, type ); | |
var i = locks.indexOf(key); | |
if( i > -1 ) { | |
locks.splice(i, 1); | |
setLocks( $el, type, locks ); | |
} | |
// return true if is unlocked (no locks) | |
return (locks.length === 0); | |
}; | |
/** | |
* acf.isLocked | |
* | |
* Returns true if a lock exists for a given type | |
* | |
* @date 22/2/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $el The element to lock. | |
* @param string type The type of lock such as "condition" or "visibility". | |
* @return void | |
*/ | |
acf.isLocked = function( $el, type ){ | |
return ( getLocks( $el, type ).length > 0 ); | |
}; | |
/** | |
* acf.isGutenberg | |
* | |
* Returns true if the Gutenberg editor is being used. | |
* | |
* @date 14/11/18 | |
* @since 5.8.0 | |
* | |
* @param vois | |
* @return bool | |
*/ | |
acf.isGutenberg = function(){ | |
return ( window.wp && wp.data && wp.data.select && wp.data.select( 'core/editor' ) ); | |
}; | |
/** | |
* acf.objectToArray | |
* | |
* Returns an array of items from the given object. | |
* | |
* @date 20/11/18 | |
* @since 5.8.0 | |
* | |
* @param object obj The object of items. | |
* @return array | |
*/ | |
acf.objectToArray = function( obj ){ | |
return Object.keys( obj ).map(function( key ){ | |
return obj[key]; | |
}); | |
}; | |
/** | |
* acf.debounce | |
* | |
* 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. | |
* | |
* @date 28/8/19 | |
* @since 5.8.1 | |
* | |
* @param function callback The callback function. | |
* @return int wait The number of milliseconds to wait. | |
*/ | |
acf.debounce = function( callback, wait ){ | |
var timeout; | |
return function(){ | |
var context = this; | |
var args = arguments; | |
var later = function(){ | |
callback.apply( context, args ); | |
}; | |
clearTimeout( timeout ); | |
timeout = setTimeout( later, wait ); | |
}; | |
}; | |
/** | |
* acf.throttle | |
* | |
* Returns a throttled version of the passed function which will allow only one execution per `limit` time period. | |
* | |
* @date 28/8/19 | |
* @since 5.8.1 | |
* | |
* @param function callback The callback function. | |
* @return int wait The number of milliseconds to wait. | |
*/ | |
acf.throttle = function( callback, limit ){ | |
var busy = false; | |
return function(){ | |
if( busy ) return; | |
busy = true; | |
setTimeout(function(){ | |
busy = false; | |
}, limit); | |
callback.apply( this, arguments ); | |
}; | |
}; | |
/** | |
* acf.isInView | |
* | |
* Returns true if the given element is in view. | |
* | |
* @date 29/8/19 | |
* @since 5.8.1 | |
* | |
* @param elem el The dom element to inspect. | |
* @return bool | |
*/ | |
acf.isInView = function( el ){ | |
var rect = el.getBoundingClientRect(); | |
return ( | |
rect.top !== rect.bottom && | |
rect.top >= 0 && | |
rect.left >= 0 && | |
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && | |
rect.right <= (window.innerWidth || document.documentElement.clientWidth) | |
); | |
}; | |
/** | |
* acf.onceInView | |
* | |
* Watches for a dom element to become visible in the browser and then excecutes the passed callback. | |
* | |
* @date 28/8/19 | |
* @since 5.8.1 | |
* | |
* @param dom el The dom element to inspect. | |
* @param function callback The callback function. | |
*/ | |
acf.onceInView = (function() { | |
// Define list. | |
var items = []; | |
var id = 0; | |
// Define check function. | |
var check = function() { | |
items.forEach(function( item ){ | |
if( acf.isInView(item.el) ) { | |
item.callback.apply( this ); | |
pop( item.id ); | |
} | |
}); | |
}; | |
// And create a debounced version. | |
var debounced = acf.debounce( check, 300 ); | |
// Define add function. | |
var push = function( el, callback ) { | |
// Add event listener. | |
if( !items.length ) { | |
$(window).on( 'scroll resize', debounced ).on( 'acfrefresh orientationchange', check ); | |
} | |
// Append to list. | |
items.push({ id: id++, el: el, callback: callback }); | |
} | |
// Define remove function. | |
var pop = function( id ) { | |
// Remove from list. | |
items = items.filter(function(item) { | |
return (item.id !== id); | |
}); | |
// Clean up listener. | |
if( !items.length ) { | |
$(window).off( 'scroll resize', debounced ).off( 'acfrefresh orientationchange', check ); | |
} | |
} | |
// Define returned function. | |
return function( el, callback ){ | |
// Allow jQuery object. | |
if( el instanceof jQuery ) | |
el = el[0]; | |
// Execute callback if already in view or add to watch list. | |
if( acf.isInView(el) ) { | |
callback.apply( this ); | |
} else { | |
push( el, callback ); | |
} | |
} | |
})(); | |
/** | |
* acf.once | |
* | |
* Creates a function that is restricted to invoking `func` once. | |
* | |
* @date 2/9/19 | |
* @since 5.8.1 | |
* | |
* @param function func The function to restrict. | |
* @return function | |
*/ | |
acf.once = function( func ){ | |
var i = 0; | |
return function(){ | |
if( i++ > 0 ) { | |
return (func = undefined); | |
} | |
return func.apply(this, arguments); | |
} | |
} | |
/* | |
* exists | |
* | |
* This function will return true if a jQuery selection exists | |
* | |
* @type function | |
* @date 8/09/2014 | |
* @since 5.0.0 | |
* | |
* @param n/a | |
* @return (boolean) | |
*/ | |
$.fn.exists = function() { | |
return $(this).length>0; | |
}; | |
/* | |
* outerHTML | |
* | |
* This function will return a string containing the HTML of the selected element | |
* | |
* @type function | |
* @date 19/11/2013 | |
* @since 5.0.0 | |
* | |
* @param $.fn | |
* @return (string) | |
*/ | |
$.fn.outerHTML = function() { | |
return $(this).get(0).outerHTML; | |
}; | |
/* | |
* indexOf | |
* | |
* This function will provide compatibility for ie8 | |
* | |
* @type function | |
* @date 5/3/17 | |
* @since 5.5.10 | |
* | |
* @param n/a | |
* @return n/a | |
*/ | |
if( !Array.prototype.indexOf ) { | |
Array.prototype.indexOf = function(val) { | |
return $.inArray(val, this); | |
}; | |
} | |
// Set up actions from events | |
$(document).ready(function(){ | |
acf.doAction('ready'); | |
}); | |
$(window).on('load', function(){ | |
acf.doAction('load'); | |
}); | |
$(window).on('beforeunload', function(){ | |
acf.doAction('unload'); | |
}); | |
$(window).on('resize', function(){ | |
acf.doAction('resize'); | |
}); | |
$(document).on('sortstart', function( event, ui ) { | |
acf.doAction('sortstart', ui.item, ui.placeholder); | |
}); | |
$(document).on('sortstop', function( event, ui ) { | |
acf.doAction('sortstop', ui.item, ui.placeholder); | |
}); | |
})(jQuery); | |
( function( window, undefined ) { | |
"use strict"; | |
/** | |
* Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in | |
* that, lowest priority hooks are fired first. | |
*/ | |
var EventManager = function() { | |
/** | |
* Maintain a reference to the object scope so our public methods never get confusing. | |
*/ | |
var MethodsAvailable = { | |
removeFilter : removeFilter, | |
applyFilters : applyFilters, | |
addFilter : addFilter, | |
removeAction : removeAction, | |
doAction : doAction, | |
addAction : addAction, | |
storage : getStorage | |
}; | |
/** | |
* Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat" | |
* object literal such that looking up the hook utilizes the native object literal hash. | |
*/ | |
var STORAGE = { | |
actions : {}, | |
filters : {} | |
}; | |
function getStorage() { | |
return STORAGE; | |
}; | |
/** | |
* Adds an action to the event manager. | |
* | |
* @param action Must contain namespace.identifier | |
* @param callback Must be a valid callback function before this action is added | |
* @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook | |
* @param [context] Supply a value to be used for this | |
*/ | |
function addAction( action, callback, priority, context ) { | |
if( typeof action === 'string' && typeof callback === 'function' ) { | |
priority = parseInt( ( priority || 10 ), 10 ); | |
_addHook( 'actions', action, callback, priority, context ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is | |
* that the first argument must always be the action. | |
*/ | |
function doAction( /* action, arg1, arg2, ... */ ) { | |
var args = Array.prototype.slice.call( arguments ); | |
var action = args.shift(); | |
if( typeof action === 'string' ) { | |
_runHook( 'actions', action, args ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Removes the specified action if it contains a namespace.identifier & exists. | |
* | |
* @param action The action to remove | |
* @param [callback] Callback function to remove | |
*/ | |
function removeAction( action, callback ) { | |
if( typeof action === 'string' ) { | |
_removeHook( 'actions', action, callback ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Adds a filter to the event manager. | |
* | |
* @param filter Must contain namespace.identifier | |
* @param callback Must be a valid callback function before this action is added | |
* @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook | |
* @param [context] Supply a value to be used for this | |
*/ | |
function addFilter( filter, callback, priority, context ) { | |
if( typeof filter === 'string' && typeof callback === 'function' ) { | |
priority = parseInt( ( priority || 10 ), 10 ); | |
_addHook( 'filters', filter, callback, priority, context ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that | |
* the first argument must always be the filter. | |
*/ | |
function applyFilters( /* filter, filtered arg, arg2, ... */ ) { | |
var args = Array.prototype.slice.call( arguments ); | |
var filter = args.shift(); | |
if( typeof filter === 'string' ) { | |
return _runHook( 'filters', filter, args ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Removes the specified filter if it contains a namespace.identifier & exists. | |
* | |
* @param filter The action to remove | |
* @param [callback] Callback function to remove | |
*/ | |
function removeFilter( filter, callback ) { | |
if( typeof filter === 'string') { | |
_removeHook( 'filters', filter, callback ); | |
} | |
return MethodsAvailable; | |
} | |
/** | |
* Removes the specified hook by resetting the value of it. | |
* | |
* @param type Type of hook, either 'actions' or 'filters' | |
* @param hook The hook (namespace.identifier) to remove | |
* @private | |
*/ | |
function _removeHook( type, hook, callback, context ) { | |
if ( !STORAGE[ type ][ hook ] ) { | |
return; | |
} | |
if ( !callback ) { | |
STORAGE[ type ][ hook ] = []; | |
} else { | |
var handlers = STORAGE[ type ][ hook ]; | |
var i; | |
if ( !context ) { | |
for ( i = handlers.length; i--; ) { | |
if ( handlers[i].callback === callback ) { | |
handlers.splice( i, 1 ); | |
} | |
} | |
} | |
else { | |
for ( i = handlers.length; i--; ) { | |
var handler = handlers[i]; | |
if ( handler.callback === callback && handler.context === context) { | |
handlers.splice( i, 1 ); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Adds the hook to the appropriate storage container | |
* | |
* @param type 'actions' or 'filters' | |
* @param hook The hook (namespace.identifier) to add to our event manager | |
* @param callback The function that will be called when the hook is executed. | |
* @param priority The priority of this hook. Must be an integer. | |
* @param [context] A value to be used for this | |
* @private | |
*/ | |
function _addHook( type, hook, callback, priority, context ) { | |
var hookObject = { | |
callback : callback, | |
priority : priority, | |
context : context | |
}; | |
// Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19 | |
var hooks = STORAGE[ type ][ hook ]; | |
if( hooks ) { | |
hooks.push( hookObject ); | |
hooks = _hookInsertSort( hooks ); | |
} | |
else { | |
hooks = [ hookObject ]; | |
} | |
STORAGE[ type ][ hook ] = hooks; | |
} | |
/** | |
* Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster | |
* than bubble sort, etc: http://jsperf.com/javascript-sort | |
* | |
* @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on. | |
* @private | |
*/ | |
function _hookInsertSort( hooks ) { | |
var tmpHook, j, prevHook; | |
for( var i = 1, len = hooks.length; i < len; i++ ) { | |
tmpHook = hooks[ i ]; | |
j = i; | |
while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) { | |
hooks[ j ] = hooks[ j - 1 ]; | |
--j; | |
} | |
hooks[ j ] = tmpHook; | |
} | |
return hooks; | |
} | |
/** | |
* Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is. | |
* | |
* @param type 'actions' or 'filters' | |
* @param hook The hook ( namespace.identifier ) to be ran. | |
* @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter. | |
* @private | |
*/ | |
function _runHook( type, hook, args ) { | |
var handlers = STORAGE[ type ][ hook ]; | |
if ( !handlers ) { | |
return (type === 'filters') ? args[0] : false; | |
} | |
var i = 0, len = handlers.length; | |
if ( type === 'filters' ) { | |
for ( ; i < len; i++ ) { | |
args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args ); | |
} | |
} else { | |
for ( ; i < len; i++ ) { | |
handlers[ i ].callback.apply( handlers[ i ].context, args ); | |
} | |
} | |
return ( type === 'filters' ) ? args[ 0 ] : true; | |
} | |
// return all of the publicly available methods | |
return MethodsAvailable; | |
}; | |
// instantiate | |
acf.hooks = new EventManager(); | |
} )( window ); | |
(function($, undefined){ | |
// Cached regex to split keys for `addEvent`. | |
var delegateEventSplitter = /^(\S+)\s*(.*)$/; | |
/** | |
* extend | |
* | |
* Helper function to correctly set up the prototype chain for subclasses | |
* Heavily inspired by backbone.js | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object protoProps New properties for this object. | |
* @return function. | |
*/ | |
var extend = function( protoProps ) { | |
// vars | |
var Parent = this; | |
var Child; | |
// The constructor function for the new subclass is either defined by you | |
// (the "constructor" property in your `extend` definition), or defaulted | |
// by us to simply call the parent constructor. | |
if( protoProps && protoProps.hasOwnProperty('constructor') ) { | |
Child = protoProps.constructor; | |
} else { | |
Child = function(){ return Parent.apply(this, arguments); }; | |
} | |
// Add static properties to the constructor function, if supplied. | |
$.extend(Child, Parent); | |
// Set the prototype chain to inherit from `parent`, without calling | |
// `parent`'s constructor function and add the prototype properties. | |
Child.prototype = Object.create(Parent.prototype); | |
$.extend(Child.prototype, protoProps); | |
Child.prototype.constructor = Child; | |
// Set a convenience property in case the parent's prototype is needed later. | |
//Child.prototype.__parent__ = Parent.prototype; | |
// return | |
return Child; | |
}; | |
/** | |
* Model | |
* | |
* Base class for all inheritence | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object props | |
* @return function. | |
*/ | |
var Model = acf.Model = function(){ | |
// generate uique client id | |
this.cid = acf.uniqueId('acf'); | |
// set vars to avoid modifying prototype | |
this.data = $.extend(true, {}, this.data); | |
// pass props to setup function | |
this.setup.apply(this, arguments); | |
// store on element (allow this.setup to create this.$el) | |
if( this.$el && !this.$el.data('acf') ) { | |
this.$el.data('acf', this); | |
} | |
// initialize | |
var initialize = function(){ | |
this.initialize(); | |
this.addEvents(); | |
this.addActions(); | |
this.addFilters(); | |
}; | |
// initialize on action | |
if( this.wait && !acf.didAction(this.wait) ) { | |
this.addAction(this.wait, initialize); | |
// initialize now | |
} else { | |
initialize.apply(this); | |
} | |
}; | |
// Attach all inheritable methods to the Model prototype. | |
$.extend(Model.prototype, { | |
// Unique model id | |
id: '', | |
// Unique client id | |
cid: '', | |
// jQuery element | |
$el: null, | |
// Data specific to this instance | |
data: {}, | |
// toggle used when changing data | |
busy: false, | |
changed: false, | |
// Setup events hooks | |
events: {}, | |
actions: {}, | |
filters: {}, | |
// class used to avoid nested event triggers | |
eventScope: '', | |
// action to wait until initialize | |
wait: false, | |
// action priority default | |
priority: 10, | |
/** | |
* get | |
* | |
* Gets a specific data value | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return mixed | |
*/ | |
get: function( name ) { | |
return this.data[name]; | |
}, | |
/** | |
* has | |
* | |
* Returns `true` if the data exists and is not null | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @return boolean | |
*/ | |
has: function( name ) { | |
return this.get(name) != null; | |
}, | |
/** | |
* set | |
* | |
* Sets a specific data value | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param mixed value | |
* @return this | |
*/ | |
set: function( name, value, silent ) { | |
// bail if unchanged | |
var prevValue = this.get(name); | |
if( prevValue == value ) { | |
return this; | |
} | |
// set data | |
this.data[ name ] = value; | |
// trigger events | |
if( !silent ) { | |
this.changed = true; | |
this.trigger('changed:' + name, [value, prevValue]); | |
this.trigger('changed', [name, value, prevValue]); | |
} | |
// return | |
return this; | |
}, | |
/** | |
* inherit | |
* | |
* Inherits the data from a jQuery element | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param jQuery $el | |
* @return this | |
*/ | |
inherit: function( data ){ | |
// allow jQuery | |
if( data instanceof jQuery ) { | |
data = data.data(); | |
} | |
// extend | |
$.extend(this.data, data); | |
// return | |
return this; | |
}, | |
/** | |
* prop | |
* | |
* mimics the jQuery prop function | |
* | |
* @date 4/6/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
prop: function(){ | |
return this.$el.prop.apply(this.$el, arguments); | |
}, | |
/** | |
* setup | |
* | |
* Run during constructor function | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return n/a | |
*/ | |
setup: function( props ){ | |
$.extend(this, props); | |
}, | |
/** | |
* initialize | |
* | |
* Also run during constructor function | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param n/a | |
* @return n/a | |
*/ | |
initialize: function(){}, | |
/** | |
* addElements | |
* | |
* Adds multiple jQuery elements to this object | |
* | |
* @date 9/5/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
addElements: function( elements ){ | |
elements = elements || this.elements || null; | |
if( !elements || !Object.keys(elements).length ) return false; | |
for( var i in elements ) { | |
this.addElement( i, elements[i] ); | |
} | |
}, | |
/** | |
* addElement | |
* | |
* description | |
* | |
* @date 9/5/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
addElement: function( name, selector){ | |
this[ '$' + name ] = this.$( selector ); | |
}, | |
/** | |
* addEvents | |
* | |
* Adds multiple event handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object events {event1 : callback, event2 : callback, etc } | |
* @return n/a | |
*/ | |
addEvents: function( events ){ | |
events = events || this.events || null; | |
if( !events ) return false; | |
for( var key in events ) { | |
var match = key.match(delegateEventSplitter); | |
this.on(match[1], match[2], events[key]); | |
} | |
}, | |
/** | |
* removeEvents | |
* | |
* Removes multiple event handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object events {event1 : callback, event2 : callback, etc } | |
* @return n/a | |
*/ | |
removeEvents: function( events ){ | |
events = events || this.events || null; | |
if( !events ) return false; | |
for( var key in events ) { | |
var match = key.match(delegateEventSplitter); | |
this.off(match[1], match[2], events[key]); | |
} | |
}, | |
/** | |
* getEventTarget | |
* | |
* Returns a jQUery element to tigger an event on | |
* | |
* @date 5/6/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $el The default jQuery element. Optional. | |
* @param string event The event name. Optional. | |
* @return jQuery | |
*/ | |
getEventTarget: function( $el, event ){ | |
return $el || this.$el || $(document); | |
}, | |
/** | |
* validateEvent | |
* | |
* Returns true if the event target's closest $el is the same as this.$el | |
* Requires both this.el and this.$el to be defined | |
* | |
* @date 5/6/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
validateEvent: function( e ){ | |
if( this.eventScope ) { | |
return $( e.target ).closest( this.eventScope ).is( this.$el ); | |
} else { | |
return true; | |
} | |
}, | |
/** | |
* proxyEvent | |
* | |
* Returns a new event callback function scoped to this model | |
* | |
* @date 29/3/18 | |
* @since 5.6.9 | |
* | |
* @param function callback | |
* @return function | |
*/ | |
proxyEvent: function( callback ){ | |
return this.proxy(function(e){ | |
// validate | |
if( !this.validateEvent(e) ) { | |
return; | |
} | |
// construct args | |
var args = acf.arrayArgs( arguments ); | |
var extraArgs = args.slice(1); | |
var eventArgs = [ e, $(e.currentTarget) ].concat( extraArgs ); | |
// callback | |
callback.apply(this, eventArgs); | |
}); | |
}, | |
/** | |
* on | |
* | |
* Adds an event handler similar to jQuery | |
* Uses the instance 'cid' to namespace event | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
on: function( a1, a2, a3, a4 ){ | |
// vars | |
var $el, event, selector, callback, args; | |
// find args | |
if( a1 instanceof jQuery ) { | |
// 1. args( $el, event, selector, callback ) | |
if( a4 ) { | |
$el = a1; event = a2; selector = a3; callback = a4; | |
// 2. args( $el, event, callback ) | |
} else { | |
$el = a1; event = a2; callback = a3; | |
} | |
} else { | |
// 3. args( event, selector, callback ) | |
if( a3 ) { | |
event = a1; selector = a2; callback = a3; | |
// 4. args( event, callback ) | |
} else { | |
event = a1; callback = a2; | |
} | |
} | |
// element | |
$el = this.getEventTarget( $el ); | |
// modify callback | |
if( typeof callback === 'string' ) { | |
callback = this.proxyEvent( this[callback] ); | |
} | |
// modify event | |
event = event + '.' + this.cid; | |
// args | |
if( selector ) { | |
args = [ event, selector, callback ]; | |
} else { | |
args = [ event, callback ]; | |
} | |
// on() | |
$el.on.apply($el, args); | |
}, | |
/** | |
* off | |
* | |
* Removes an event handler similar to jQuery | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
off: function( a1, a2 ,a3 ){ | |
// vars | |
var $el, event, selector, args; | |
// find args | |
if( a1 instanceof jQuery ) { | |
// 1. args( $el, event, selector ) | |
if( a3 ) { | |
$el = a1; event = a2; selector = a3; | |
// 2. args( $el, event ) | |
} else { | |
$el = a1; event = a2; | |
} | |
} else { | |
// 3. args( event, selector ) | |
if( a2 ) { | |
event = a1; selector = a2; | |
// 4. args( event ) | |
} else { | |
event = a1; | |
} | |
} | |
// element | |
$el = this.getEventTarget( $el ); | |
// modify event | |
event = event + '.' + this.cid; | |
// args | |
if( selector ) { | |
args = [ event, selector ]; | |
} else { | |
args = [ event ]; | |
} | |
// off() | |
$el.off.apply($el, args); | |
}, | |
/** | |
* trigger | |
* | |
* Triggers an event similar to jQuery | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
trigger: function( name, args, bubbles ){ | |
var $el = this.getEventTarget(); | |
if( bubbles ) { | |
$el.trigger.apply( $el, arguments ); | |
} else { | |
$el.triggerHandler.apply( $el, arguments ); | |
} | |
return this; | |
}, | |
/** | |
* addActions | |
* | |
* Adds multiple action handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object actions {action1 : callback, action2 : callback, etc } | |
* @return n/a | |
*/ | |
addActions: function( actions ){ | |
actions = actions || this.actions || null; | |
if( !actions ) return false; | |
for( var i in actions ) { | |
this.addAction( i, actions[i] ); | |
} | |
}, | |
/** | |
* removeActions | |
* | |
* Removes multiple action handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object actions {action1 : callback, action2 : callback, etc } | |
* @return n/a | |
*/ | |
removeActions: function( actions ){ | |
actions = actions || this.actions || null; | |
if( !actions ) return false; | |
for( var i in actions ) { | |
this.removeAction( i, actions[i] ); | |
} | |
}, | |
/** | |
* addAction | |
* | |
* Adds an action using the wp.hooks library | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
addAction: function( name, callback, priority ){ | |
//console.log('addAction', name, priority); | |
// defaults | |
priority = priority || this.priority; | |
// modify callback | |
if( typeof callback === 'string' ) { | |
callback = this[ callback ]; | |
} | |
// add | |
acf.addAction(name, callback, priority, this); | |
}, | |
/** | |
* removeAction | |
* | |
* Remove an action using the wp.hooks library | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
removeAction: function( name, callback ){ | |
acf.removeAction(name, this[ callback ]); | |
}, | |
/** | |
* addFilters | |
* | |
* Adds multiple filter handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object filters {filter1 : callback, filter2 : callback, etc } | |
* @return n/a | |
*/ | |
addFilters: function( filters ){ | |
filters = filters || this.filters || null; | |
if( !filters ) return false; | |
for( var i in filters ) { | |
this.addFilter( i, filters[i] ); | |
} | |
}, | |
/** | |
* addFilter | |
* | |
* Adds a filter using the wp.hooks library | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
addFilter: function( name, callback, priority ){ | |
// defaults | |
priority = priority || this.priority; | |
// modify callback | |
if( typeof callback === 'string' ) { | |
callback = this[ callback ]; | |
} | |
// add | |
acf.addFilter(name, callback, priority, this); | |
}, | |
/** | |
* removeFilters | |
* | |
* Removes multiple filter handlers | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object filters {filter1 : callback, filter2 : callback, etc } | |
* @return n/a | |
*/ | |
removeFilters: function( filters ){ | |
filters = filters || this.filters || null; | |
if( !filters ) return false; | |
for( var i in filters ) { | |
this.removeFilter( i, filters[i] ); | |
} | |
}, | |
/** | |
* removeFilter | |
* | |
* Remove a filter using the wp.hooks library | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string name | |
* @param string callback | |
* @return n/a | |
*/ | |
removeFilter: function( name, callback ){ | |
acf.removeFilter(name, this[ callback ]); | |
}, | |
/** | |
* $ | |
* | |
* description | |
* | |
* @date 16/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
$: function( selector ){ | |
return this.$el.find( selector ); | |
}, | |
/** | |
* remove | |
* | |
* Removes the element and listenters | |
* | |
* @date 19/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
remove: function(){ | |
this.removeEvents(); | |
this.removeActions(); | |
this.removeFilters(); | |
this.$el.remove(); | |
}, | |
/** | |
* setTimeout | |
* | |
* description | |
* | |
* @date 16/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
setTimeout: function( callback, milliseconds ){ | |
return setTimeout( this.proxy(callback), milliseconds ); | |
}, | |
/** | |
* time | |
* | |
* used for debugging | |
* | |
* @date 7/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
time: function(){ | |
console.time( this.id || this.cid ); | |
}, | |
/** | |
* timeEnd | |
* | |
* used for debugging | |
* | |
* @date 7/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
timeEnd: function(){ | |
console.timeEnd( this.id || this.cid ); | |
}, | |
/** | |
* show | |
* | |
* description | |
* | |
* @date 15/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
show: function(){ | |
acf.show( this.$el ); | |
}, | |
/** | |
* hide | |
* | |
* description | |
* | |
* @date 15/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
hide: function(){ | |
acf.hide( this.$el ); | |
}, | |
/** | |
* proxy | |
* | |
* Returns a new function scoped to this model | |
* | |
* @date 29/3/18 | |
* @since 5.6.9 | |
* | |
* @param function callback | |
* @return function | |
*/ | |
proxy: function( callback ){ | |
return $.proxy( callback, this ); | |
} | |
}); | |
// Set up inheritance for the model | |
Model.extend = extend; | |
// Global model storage | |
acf.models = {}; | |
/** | |
* acf.getInstance | |
* | |
* This function will get an instance from an element | |
* | |
* @date 5/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getInstance = function( $el ){ | |
return $el.data('acf'); | |
}; | |
/** | |
* acf.getInstances | |
* | |
* This function will get an array of instances from multiple elements | |
* | |
* @date 5/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getInstances = function( $el ){ | |
var instances = []; | |
$el.each(function(){ | |
instances.push( acf.getInstance( $(this) ) ); | |
}); | |
return instances; | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
acf.models.Popup = acf.Model.extend({ | |
data: { | |
title: '', | |
content: '', | |
width: 0, | |
height: 0, | |
loading: false, | |
}, | |
events: { | |
'click [data-event="close"]': 'onClickClose', | |
'click .acf-close-popup': 'onClickClose', | |
}, | |
setup: function( props ){ | |
$.extend(this.data, props); | |
this.$el = $(this.tmpl()); | |
}, | |
initialize: function(){ | |
this.render(); | |
this.open(); | |
}, | |
tmpl: function(){ | |
return [ | |
'<div id="acf-popup">', | |
'<div class="acf-popup-box acf-box">', | |
'<div class="title"><h3></h3><a href="#" class="acf-icon -cancel grey" data-event="close"></a></div>', | |
'<div class="inner"></div>', | |
'<div class="loading"><i class="acf-loading"></i></div>', | |
'</div>', | |
'<div class="bg" data-event="close"></div>', | |
'</div>' | |
].join(''); | |
}, | |
render: function(){ | |
// vars | |
var title = this.get('title'); | |
var content = this.get('content'); | |
var loading = this.get('loading'); | |
var width = this.get('width'); | |
var height = this.get('height'); | |
// html | |
this.title( title ); | |
this.content( content ); | |
// width | |
if( width ) { | |
this.$('.acf-popup-box').css('width', width); | |
} | |
// height | |
if( height ) { | |
this.$('.acf-popup-box').css('min-height', height); | |
} | |
// loading | |
this.loading( loading ); | |
// action | |
acf.doAction('append', this.$el); | |
}, | |
update: function( props ){ | |
this.data = acf.parseArgs(props, this.data); | |
this.render(); | |
}, | |
title: function( title ){ | |
this.$('.title:first h3').html( title ); | |
}, | |
content: function( content ){ | |
this.$('.inner:first').html( content ); | |
}, | |
loading: function( show ){ | |
var $loading = this.$('.loading:first'); | |
show ? $loading.show() : $loading.hide(); | |
}, | |
open: function(){ | |
$('body').append( this.$el ); | |
}, | |
close: function(){ | |
this.remove(); | |
}, | |
onClickClose: function( e, $el ){ | |
e.preventDefault(); | |
this.close(); | |
} | |
}); | |
/** | |
* newPopup | |
* | |
* Creates a new Popup with the supplied props | |
* | |
* @date 17/12/17 | |
* @since 5.6.5 | |
* | |
* @param object props | |
* @return object | |
*/ | |
acf.newPopup = function( props ){ | |
return new acf.models.Popup( props ); | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
acf.unload = new acf.Model({ | |
wait: 'load', | |
active: true, | |
changed: false, | |
actions: { | |
'validation_failure': 'startListening', | |
'validation_success': 'stopListening' | |
}, | |
events: { | |
'change form .acf-field': 'startListening', | |
'submit form': 'stopListening' | |
}, | |
enable: function(){ | |
this.active = true; | |
}, | |
disable: function(){ | |
this.active = false; | |
}, | |
reset: function(){ | |
this.stopListening(); | |
}, | |
startListening: function(){ | |
// bail ealry if already changed, not active | |
if( this.changed || !this.active ) { | |
return; | |
} | |
// update | |
this.changed = true; | |
// add event | |
$(window).on('beforeunload', this.onUnload); | |
}, | |
stopListening: function(){ | |
// update | |
this.changed = false; | |
// remove event | |
$(window).off('beforeunload', this.onUnload); | |
}, | |
onUnload: function(){ | |
return acf.__('The changes you made will be lost if you navigate away from this page'); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var panel = new acf.Model({ | |
events: { | |
'click .acf-panel-title': 'onClick', | |
}, | |
onClick: function( e, $el ){ | |
e.preventDefault(); | |
this.toggle( $el.parent() ); | |
}, | |
isOpen: function( $el ) { | |
return $el.hasClass('-open'); | |
}, | |
toggle: function( $el ){ | |
this.isOpen($el) ? this.close( $el ) : this.open( $el ); | |
}, | |
open: function( $el ){ | |
$el.addClass('-open'); | |
$el.find('.acf-panel-title i').attr('class', 'dashicons dashicons-arrow-down'); | |
}, | |
close: function( $el ){ | |
$el.removeClass('-open'); | |
$el.find('.acf-panel-title i').attr('class', 'dashicons dashicons-arrow-right'); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var Notice = acf.Model.extend({ | |
data: { | |
text: '', | |
type: '', | |
timeout: 0, | |
dismiss: true, | |
target: false, | |
close: function(){} | |
}, | |
events: { | |
'click .acf-notice-dismiss': 'onClickClose', | |
}, | |
tmpl: function(){ | |
return '<div class="acf-notice"></div>'; | |
}, | |
setup: function( props ){ | |
$.extend(this.data, props); | |
this.$el = $(this.tmpl()); | |
}, | |
initialize: function(){ | |
// render | |
this.render(); | |
// show | |
this.show(); | |
}, | |
render: function(){ | |
// class | |
this.type( this.get('type') ); | |
// text | |
this.html( '<p>' + this.get('text') + '</p>' ); | |
// close | |
if( this.get('dismiss') ) { | |
this.$el.append('<a href="#" class="acf-notice-dismiss acf-icon -cancel small"></a>'); | |
this.$el.addClass('-dismiss'); | |
} | |
// timeout | |
var timeout = this.get('timeout'); | |
if( timeout ) { | |
this.away( timeout ); | |
} | |
}, | |
update: function( props ){ | |
// update | |
$.extend(this.data, props); | |
// re-initialize | |
this.initialize(); | |
// refresh events | |
this.removeEvents(); | |
this.addEvents(); | |
}, | |
show: function(){ | |
var $target = this.get('target'); | |
if( $target ) { | |
$target.prepend( this.$el ); | |
} | |
}, | |
hide: function(){ | |
this.$el.remove(); | |
}, | |
away: function( timeout ){ | |
this.setTimeout(function(){ | |
acf.remove( this.$el ); | |
}, timeout ); | |
}, | |
type: function( type ){ | |
// remove prev type | |
var prevType = this.get('type'); | |
if( prevType ) { | |
this.$el.removeClass('-' + prevType); | |
} | |
// add new type | |
this.$el.addClass('-' + type); | |
// backwards compatibility | |
if( type == 'error' ) { | |
this.$el.addClass('acf-error-message'); | |
} | |
}, | |
html: function( html ){ | |
this.$el.html( html ); | |
}, | |
text: function( text ){ | |
this.$('p').html( text ); | |
}, | |
onClickClose: function( e, $el ){ | |
e.preventDefault(); | |
this.get('close').apply(this, arguments); | |
this.remove(); | |
} | |
}); | |
acf.newNotice = function( props ){ | |
// ensure object | |
if( typeof props !== 'object' ) { | |
props = { text: props }; | |
} | |
// instantiate | |
return new Notice( props ); | |
}; | |
var noticeManager = new acf.Model({ | |
wait: 'prepare', | |
priority: 1, | |
initialize: function(){ | |
// vars | |
var $notice = $('.acf-admin-notice'); | |
// move to avoid WP flicker | |
if( $notice.length ) { | |
$('h1:first').after( $notice ); | |
} | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* postboxManager | |
* | |
* Manages postboxes on the screen. | |
* | |
* @date 25/5/19 | |
* @since 5.8.1 | |
* | |
* @param void | |
* @return void | |
*/ | |
var postboxManager = new acf.Model({ | |
wait: 'prepare', | |
priority: 1, | |
initialize: function(){ | |
(acf.get('postboxes') || []).map( acf.newPostbox ); | |
}, | |
}); | |
/** | |
* acf.getPostbox | |
* | |
* Returns a postbox instance. | |
* | |
* @date 23/9/18 | |
* @since 5.7.7 | |
* | |
* @param mixed $el Either a jQuery element or the postbox id. | |
* @return object | |
*/ | |
acf.getPostbox = function( $el ){ | |
// allow string parameter | |
if( typeof arguments[0] == 'string' ) { | |
$el = $('#' + arguments[0]); | |
} | |
// return instance | |
return acf.getInstance( $el ); | |
}; | |
/** | |
* acf.getPostboxes | |
* | |
* Returns an array of postbox instances. | |
* | |
* @date 23/9/18 | |
* @since 5.7.7 | |
* | |
* @param void | |
* @return array | |
*/ | |
acf.getPostboxes = function(){ | |
return acf.getInstances( $('.acf-postbox') ); | |
}; | |
/** | |
* acf.newPostbox | |
* | |
* Returns a new postbox instance for the given props. | |
* | |
* @date 20/9/18 | |
* @since 5.7.6 | |
* | |
* @param object props The postbox properties. | |
* @return object | |
*/ | |
acf.newPostbox = function( props ){ | |
return new acf.models.Postbox( props ); | |
}; | |
/** | |
* acf.models.Postbox | |
* | |
* The postbox model. | |
* | |
* @date 20/9/18 | |
* @since 5.7.6 | |
* | |
* @param void | |
* @return void | |
*/ | |
acf.models.Postbox = acf.Model.extend({ | |
data: { | |
id: '', | |
key: '', | |
style: 'default', | |
label: 'top', | |
edit: '' | |
}, | |
setup: function( props ){ | |
// compatibilty | |
if( props.editLink ) { | |
props.edit = props.editLink; | |
} | |
// extend data | |
$.extend(this.data, props); | |
// set $el | |
this.$el = this.$postbox(); | |
}, | |
$postbox: function(){ | |
return $('#' + this.get('id')); | |
}, | |
$hide: function(){ | |
return $('#' + this.get('id') + '-hide'); | |
}, | |
$hideLabel: function(){ | |
return this.$hide().parent(); | |
}, | |
$hndle: function(){ | |
return this.$('> .hndle'); | |
}, | |
$inside: function(){ | |
return this.$('> .inside'); | |
}, | |
isVisible: function(){ | |
return this.$el.hasClass('acf-hidden'); | |
}, | |
initialize: function(){ | |
// Add default class. | |
this.$el.addClass('acf-postbox'); | |
// Remove 'hide-if-js class. | |
// This class is added by WP to postboxes that are hidden via the "Screen Options" tab. | |
this.$el.removeClass('hide-if-js'); | |
// Add field group style class (ignore in block editor). | |
if( acf.get('editor') !== 'block' ) { | |
var style = this.get('style'); | |
if( style !== 'default' ) { | |
this.$el.addClass( style ); | |
} | |
} | |
// Add .inside class. | |
this.$inside().addClass('acf-fields').addClass('-' + this.get('label')); | |
// Append edit link. | |
var edit = this.get('edit'); | |
if( edit ) { | |
this.$hndle().append('<a href="' + edit + '" class="dashicons dashicons-admin-generic acf-hndle-cog acf-js-tooltip" title="' + acf.__('Edit field group') + '"></a>'); | |
} | |
// Show postbox. | |
this.show(); | |
}, | |
show: function(){ | |
// Show label. | |
this.$hideLabel().show(); | |
// toggle on checkbox | |
this.$hide().prop('checked', true); | |
// Show postbox | |
this.$el.show().removeClass('acf-hidden'); | |
// Do action. | |
acf.doAction('show_postbox', this); | |
}, | |
enable: function(){ | |
acf.enable( this.$el, 'postbox' ); | |
}, | |
showEnable: function(){ | |
this.enable(); | |
this.show(); | |
}, | |
hide: function(){ | |
// Hide label. | |
this.$hideLabel().hide(); | |
// Hide postbox | |
this.$el.hide().addClass('acf-hidden'); | |
// Do action. | |
acf.doAction('hide_postbox', this); | |
}, | |
disable: function(){ | |
acf.disable( this.$el, 'postbox' ); | |
}, | |
hideDisable: function(){ | |
this.disable(); | |
this.hide(); | |
}, | |
html: function( html ){ | |
// Update HTML. | |
this.$inside().html( html ); | |
// Do action. | |
acf.doAction('append', this.$el); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
acf.newTooltip = function( props ){ | |
// ensure object | |
if( typeof props !== 'object' ) { | |
props = { text: props }; | |
} | |
// confirmRemove | |
if( props.confirmRemove !== undefined ) { | |
props.textConfirm = acf.__('Remove'); | |
props.textCancel = acf.__('Cancel'); | |
return new TooltipConfirm( props ); | |
// confirm | |
} else if( props.confirm !== undefined ) { | |
return new TooltipConfirm( props ); | |
// default | |
} else { | |
return new Tooltip( props ); | |
} | |
}; | |
var Tooltip = acf.Model.extend({ | |
data: { | |
text: '', | |
timeout: 0, | |
target: null | |
}, | |
tmpl: function(){ | |
return '<div class="acf-tooltip"></div>'; | |
}, | |
setup: function( props ){ | |
$.extend(this.data, props); | |
this.$el = $(this.tmpl()); | |
}, | |
initialize: function(){ | |
// render | |
this.render(); | |
// append | |
this.show(); | |
// position | |
this.position(); | |
// timeout | |
var timeout = this.get('timeout'); | |
if( timeout ) { | |
setTimeout( $.proxy(this.fade, this), timeout ); | |
} | |
}, | |
update: function( props ){ | |
$.extend(this.data, props); | |
this.initialize(); | |
}, | |
render: function(){ | |
this.html( this.get('text') ); | |
}, | |
show: function(){ | |
$('body').append( this.$el ); | |
}, | |
hide: function(){ | |
this.$el.remove(); | |
}, | |
fade: function(){ | |
// add class | |
this.$el.addClass('acf-fade-up'); | |
// remove | |
this.setTimeout(function(){ | |
this.remove(); | |
}, 250); | |
}, | |
html: function( html ){ | |
this.$el.html( html ); | |
}, | |
position: function(){ | |
// vars | |
var $tooltip = this.$el; | |
var $target = this.get('target'); | |
if( !$target ) return; | |
// Reset position. | |
$tooltip.removeClass('right left bottom top').css({ top: 0, left: 0 }); | |
// Declare tollerance to edge of screen. | |
var tolerance = 10; | |
// Find target position. | |
var targetWidth = $target.outerWidth(); | |
var targetHeight = $target.outerHeight(); | |
var targetTop = $target.offset().top; | |
var targetLeft = $target.offset().left; | |
// Find tooltip position. | |
var tooltipWidth = $tooltip.outerWidth(); | |
var tooltipHeight = $tooltip.outerHeight(); | |
var tooltipTop = $tooltip.offset().top; // Should be 0, but WP media grid causes this to be 32 (toolbar padding). | |
// Assume default top alignment. | |
var top = targetTop - tooltipHeight - tooltipTop; | |
var left = targetLeft + (targetWidth / 2) - (tooltipWidth / 2); | |
// Check if too far left. | |
if( left < tolerance ) { | |
$tooltip.addClass('right'); | |
left = targetLeft + targetWidth; | |
top = targetTop + (targetHeight / 2) - (tooltipHeight / 2) - tooltipTop; | |
// Check if too far right. | |
} else if( (left + tooltipWidth + tolerance) > $(window).width() ) { | |
$tooltip.addClass('left'); | |
left = targetLeft - tooltipWidth; | |
top = targetTop + (targetHeight / 2) - (tooltipHeight / 2) - tooltipTop; | |
// Check if too far up. | |
} else if( top - $(window).scrollTop() < tolerance ) { | |
$tooltip.addClass('bottom'); | |
top = targetTop + targetHeight - tooltipTop; | |
// No colision with edges. | |
} else { | |
$tooltip.addClass('top'); | |
} | |
// update css | |
$tooltip.css({ 'top': top, 'left': left }); | |
} | |
}); | |
var TooltipConfirm = Tooltip.extend({ | |
data: { | |
text: '', | |
textConfirm: '', | |
textCancel: '', | |
target: null, | |
targetConfirm: true, | |
confirm: function(){}, | |
cancel: function(){}, | |
context: false | |
}, | |
events: { | |
'click [data-event="cancel"]': 'onCancel', | |
'click [data-event="confirm"]': 'onConfirm', | |
}, | |
addEvents: function(){ | |
// add events | |
acf.Model.prototype.addEvents.apply(this); | |
// vars | |
var $document = $(document); | |
var $target = this.get('target'); | |
// add global 'cancel' click event | |
// - use timeout to avoid the current 'click' event triggering the onCancel function | |
this.setTimeout(function(){ | |
this.on( $document, 'click', 'onCancel' ); | |
}); | |
// add target 'confirm' click event | |
// - allow setting to control this feature | |
if( this.get('targetConfirm') ) { | |
this.on( $target, 'click', 'onConfirm' ); | |
} | |
}, | |
removeEvents: function(){ | |
// remove events | |
acf.Model.prototype.removeEvents.apply(this); | |
// vars | |
var $document = $(document); | |
var $target = this.get('target'); | |
// remove custom events | |
this.off( $document, 'click' ); | |
this.off( $target, 'click' ); | |
}, | |
render: function(){ | |
// defaults | |
var text = this.get('text') || acf.__('Are you sure?'); | |
var textConfirm = this.get('textConfirm') || acf.__('Yes'); | |
var textCancel = this.get('textCancel') || acf.__('No'); | |
// html | |
var html = [ | |
text, | |
'<a href="#" data-event="confirm">' + textConfirm + '</a>', | |
'<a href="#" data-event="cancel">' + textCancel + '</a>' | |
].join(' '); | |
// html | |
this.html( html ); | |
// class | |
this.$el.addClass('-confirm'); | |
}, | |
onCancel: function( e, $el ){ | |
// prevent default | |
e.preventDefault(); | |
e.stopImmediatePropagation(); | |
// callback | |
var callback = this.get('cancel'); | |
var context = this.get('context') || this; | |
callback.apply( context, arguments ); | |
//remove | |
this.remove(); | |
}, | |
onConfirm: function( e, $el ){ | |
// prevent default | |
e.preventDefault(); | |
e.stopImmediatePropagation(); | |
// callback | |
var callback = this.get('confirm'); | |
var context = this.get('context') || this; | |
callback.apply( context, arguments ); | |
//remove | |
this.remove(); | |
} | |
}); | |
// storage | |
acf.models.Tooltip = Tooltip; | |
acf.models.TooltipConfirm = TooltipConfirm; | |
/** | |
* tooltipManager | |
* | |
* description | |
* | |
* @date 17/4/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var tooltipHoverHelper = new acf.Model({ | |
tooltip: false, | |
events: { | |
'mouseenter .acf-js-tooltip': 'showTitle', | |
'mouseup .acf-js-tooltip': 'hideTitle', | |
'mouseleave .acf-js-tooltip': 'hideTitle' | |
}, | |
showTitle: function( e, $el ){ | |
// vars | |
var title = $el.attr('title'); | |
// bail ealry if no title | |
if( !title ) { | |
return; | |
} | |
// clear title to avoid default browser tooltip | |
$el.attr('title', ''); | |
// create | |
if( !this.tooltip ) { | |
this.tooltip = acf.newTooltip({ | |
text: title, | |
target: $el | |
}); | |
// update | |
} else { | |
this.tooltip.update({ | |
text: title, | |
target: $el | |
}); | |
} | |
}, | |
hideTitle: function( e, $el ){ | |
// hide tooltip | |
this.tooltip.hide(); | |
// restore title | |
$el.attr('title', this.tooltip.get('text')); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
// vars | |
var storage = []; | |
/** | |
* acf.Field | |
* | |
* description | |
* | |
* @date 23/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.Field = acf.Model.extend({ | |
// field type | |
type: '', | |
// class used to avoid nested event triggers | |
eventScope: '.acf-field', | |
// initialize events on 'ready' | |
wait: 'ready', | |
/** | |
* setup | |
* | |
* Called during the constructor function to setup this field ready for initialization | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $field The field element. | |
* @return void | |
*/ | |
setup: function( $field ){ | |
// set $el | |
this.$el = $field; | |
// inherit $field data | |
this.inherit( $field ); | |
// inherit controll data | |
this.inherit( this.$control() ); | |
}, | |
/** | |
* val | |
* | |
* Sets or returns the field's value | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param mixed val Optional. The value to set | |
* @return mixed | |
*/ | |
val: function( val ){ | |
// Set. | |
if( val !== undefined ) { | |
return this.setValue( val ); | |
// Get. | |
} else { | |
return this.prop('disabled') ? null : this.getValue(); | |
} | |
}, | |
/** | |
* getValue | |
* | |
* returns the field's value | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return mixed | |
*/ | |
getValue: function(){ | |
return this.$input().val(); | |
}, | |
/** | |
* setValue | |
* | |
* sets the field's value and returns true if changed | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param mixed val | |
* @return boolean. True if changed. | |
*/ | |
setValue: function( val ){ | |
return acf.val( this.$input(), val ); | |
}, | |
/** | |
* __ | |
* | |
* i18n helper to be removed | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
__: function( string ){ | |
return acf._e( this.type, string ); | |
}, | |
/** | |
* $control | |
* | |
* returns the control jQuery element used for inheriting data. Uses this.control setting. | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return jQuery | |
*/ | |
$control: function(){ | |
return false; | |
}, | |
/** | |
* $input | |
* | |
* returns the input jQuery element used for saving values. Uses this.input setting. | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return jQuery | |
*/ | |
$input: function(){ | |
return this.$('[name]:first'); | |
}, | |
/** | |
* $inputWrap | |
* | |
* description | |
* | |
* @date 12/5/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
$inputWrap: function(){ | |
return this.$('.acf-input:first'); | |
}, | |
/** | |
* $inputWrap | |
* | |
* description | |
* | |
* @date 12/5/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
$labelWrap: function(){ | |
return this.$('.acf-label:first'); | |
}, | |
/** | |
* getInputName | |
* | |
* Returns the field's input name | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return string | |
*/ | |
getInputName: function(){ | |
return this.$input().attr('name') || ''; | |
}, | |
/** | |
* parent | |
* | |
* returns the field's parent field or false on failure. | |
* | |
* @date 8/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return object|false | |
*/ | |
parent: function() { | |
// vars | |
var parents = this.parents(); | |
// return | |
return parents.length ? parents[0] : false; | |
}, | |
/** | |
* parents | |
* | |
* description | |
* | |
* @date 9/7/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
parents: function(){ | |
// vars | |
var $parents = this.$el.parents('.acf-field'); | |
// convert | |
var parents = acf.getFields( $parents ); | |
// return | |
return parents; | |
}, | |
show: function( lockKey, context ){ | |
// show field and store result | |
var changed = acf.show( this.$el, lockKey ); | |
// do action if visibility has changed | |
if( changed ) { | |
this.prop('hidden', false); | |
acf.doAction('show_field', this, context); | |
} | |
// return | |
return changed; | |
}, | |
hide: function( lockKey, context ){ | |
// hide field and store result | |
var changed = acf.hide( this.$el, lockKey ); | |
// do action if visibility has changed | |
if( changed ) { | |
this.prop('hidden', true); | |
acf.doAction('hide_field', this, context); | |
} | |
// return | |
return changed; | |
}, | |
enable: function( lockKey, context ){ | |
// enable field and store result | |
var changed = acf.enable( this.$el, lockKey ); | |
// do action if disabled has changed | |
if( changed ) { | |
this.prop('disabled', false); | |
acf.doAction('enable_field', this, context); | |
} | |
// return | |
return changed; | |
}, | |
disable: function( lockKey, context ){ | |
// disabled field and store result | |
var changed = acf.disable( this.$el, lockKey ); | |
// do action if disabled has changed | |
if( changed ) { | |
this.prop('disabled', true); | |
acf.doAction('disable_field', this, context); | |
} | |
// return | |
return changed; | |
}, | |
showEnable: function( lockKey, context ){ | |
// enable | |
this.enable.apply(this, arguments); | |
// show and return true if changed | |
return this.show.apply(this, arguments); | |
}, | |
hideDisable: function( lockKey, context ){ | |
// disable | |
this.disable.apply(this, arguments); | |
// hide and return true if changed | |
return this.hide.apply(this, arguments); | |
}, | |
showNotice: function( props ){ | |
// ensure object | |
if( typeof props !== 'object' ) { | |
props = { text: props }; | |
} | |
// remove old notice | |
if( this.notice ) { | |
this.notice.remove(); | |
} | |
// create new notice | |
props.target = this.$inputWrap(); | |
this.notice = acf.newNotice( props ); | |
}, | |
removeNotice: function( timeout ){ | |
if( this.notice ) { | |
this.notice.away( timeout || 0 ); | |
this.notice = false; | |
} | |
}, | |
showError: function( message ){ | |
// add class | |
this.$el.addClass('acf-error'); | |
// add message | |
if( message !== undefined ) { | |
this.showNotice({ | |
text: message, | |
type: 'error', | |
dismiss: false | |
}); | |
} | |
// action | |
acf.doAction('invalid_field', this); | |
// add event | |
this.$el.one('focus change', 'input, select, textarea', $.proxy( this.removeError, this )); | |
}, | |
removeError: function(){ | |
// remove class | |
this.$el.removeClass('acf-error'); | |
// remove notice | |
this.removeNotice( 250 ); | |
// action | |
acf.doAction('valid_field', this); | |
}, | |
trigger: function( name, args, bubbles ){ | |
// allow some events to bubble | |
if( name == 'invalidField' ) { | |
bubbles = true; | |
} | |
// return | |
return acf.Model.prototype.trigger.apply(this, [name, args, bubbles]); | |
}, | |
}); | |
/** | |
* newField | |
* | |
* description | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.newField = function( $field ){ | |
// vars | |
var type = $field.data('type'); | |
var mid = modelId( type ); | |
var model = acf.models[ mid ] || acf.Field; | |
// instantiate | |
var field = new model( $field ); | |
// actions | |
acf.doAction('new_field', field); | |
// return | |
return field; | |
}; | |
/** | |
* mid | |
* | |
* Calculates the model ID for a field type | |
* | |
* @date 15/12/17 | |
* @since 5.6.5 | |
* | |
* @param string type | |
* @return string | |
*/ | |
var modelId = function( type ) { | |
return acf.strPascalCase( type || '' ) + 'Field'; | |
}; | |
/** | |
* registerFieldType | |
* | |
* description | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.registerFieldType = function( model ){ | |
// vars | |
var proto = model.prototype; | |
var type = proto.type; | |
var mid = modelId( type ); | |
// store model | |
acf.models[ mid ] = model; | |
// store reference | |
storage.push( type ); | |
}; | |
/** | |
* acf.getFieldType | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getFieldType = function( type ){ | |
var mid = modelId( type ); | |
return acf.models[ mid ] || false; | |
} | |
/** | |
* acf.getFieldTypes | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getFieldTypes = function( args ){ | |
// defaults | |
args = acf.parseArgs(args, { | |
category: '', | |
// hasValue: true | |
}); | |
// clonse available types | |
var types = []; | |
// loop | |
storage.map(function( type ){ | |
// vars | |
var model = acf.getFieldType(type); | |
var proto = model.prototype; | |
// check operator | |
if( args.category && proto.category !== args.category ) { | |
return; | |
} | |
// append | |
types.push( model ); | |
}); | |
// return | |
return types; | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* findFields | |
* | |
* Returns a jQuery selection object of acf fields. | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param object $args { | |
* Optional. Arguments to find fields. | |
* | |
* @type string key The field's key (data-attribute). | |
* @type string name The field's name (data-attribute). | |
* @type string type The field's type (data-attribute). | |
* @type string is jQuery selector to compare against. | |
* @type jQuery parent jQuery element to search within. | |
* @type jQuery sibling jQuery element to search alongside. | |
* @type limit int The number of fields to find. | |
* @type suppressFilters bool Whether to allow filters to add/remove results. Default behaviour will ignore clone fields. | |
* } | |
* @return jQuery | |
*/ | |
acf.findFields = function( args ){ | |
// vars | |
var selector = '.acf-field'; | |
var $fields = false; | |
// args | |
args = acf.parseArgs(args, { | |
key: '', | |
name: '', | |
type: '', | |
is: '', | |
parent: false, | |
sibling: false, | |
limit: false, | |
visible: false, | |
suppressFilters: false, | |
}); | |
// filter args | |
if( !args.suppressFilters ) { | |
args = acf.applyFilters('find_fields_args', args); | |
} | |
// key | |
if( args.key ) { | |
selector += '[data-key="' + args.key + '"]'; | |
} | |
// type | |
if( args.type ) { | |
selector += '[data-type="' + args.type + '"]'; | |
} | |
// name | |
if( args.name ) { | |
selector += '[data-name="' + args.name + '"]'; | |
} | |
// is | |
if( args.is ) { | |
selector += args.is; | |
} | |
// visibility | |
if( args.visible ) { | |
selector += ':visible'; | |
} | |
// query | |
if( args.parent ) { | |
$fields = args.parent.find( selector ); | |
} else if( args.sibling ) { | |
$fields = args.sibling.siblings( selector ); | |
} else { | |
$fields = $( selector ); | |
} | |
// filter | |
if( !args.suppressFilters ) { | |
$fields = $fields.not('.acf-clone .acf-field'); | |
$fields = acf.applyFilters('find_fields', $fields); | |
} | |
// limit | |
if( args.limit ) { | |
$fields = $fields.slice( 0, args.limit ); | |
} | |
// return | |
return $fields; | |
}; | |
/** | |
* findField | |
* | |
* Finds a specific field with jQuery | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param string key The field's key. | |
* @param jQuery $parent jQuery element to search within. | |
* @return jQuery | |
*/ | |
acf.findField = function( key, $parent ){ | |
return acf.findFields({ | |
key: key, | |
limit: 1, | |
parent: $parent, | |
suppressFilters: true | |
}); | |
}; | |
/** | |
* getField | |
* | |
* Returns a field instance | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param jQuery|string $field jQuery element or field key. | |
* @return object | |
*/ | |
acf.getField = function( $field ){ | |
// allow jQuery | |
if( $field instanceof jQuery ) { | |
// find fields | |
} else { | |
$field = acf.findField( $field ); | |
} | |
// instantiate | |
var field = $field.data('acf'); | |
if( !field ) { | |
field = acf.newField( $field ); | |
} | |
// return | |
return field; | |
}; | |
/** | |
* getFields | |
* | |
* Returns multiple field instances | |
* | |
* @date 14/12/17 | |
* @since 5.6.5 | |
* | |
* @param jQuery|object $fields jQuery elements or query args. | |
* @return array | |
*/ | |
acf.getFields = function( $fields ){ | |
// allow jQuery | |
if( $fields instanceof jQuery ) { | |
// find fields | |
} else { | |
$fields = acf.findFields( $fields ); | |
} | |
// loop | |
var fields = []; | |
$fields.each(function(){ | |
var field = acf.getField( $(this) ); | |
fields.push( field ); | |
}); | |
// return | |
return fields; | |
}; | |
/** | |
* findClosestField | |
* | |
* Returns the closest jQuery field element | |
* | |
* @date 9/4/18 | |
* @since 5.6.9 | |
* | |
* @param jQuery $el | |
* @return jQuery | |
*/ | |
acf.findClosestField = function( $el ){ | |
return $el.closest('.acf-field'); | |
}; | |
/** | |
* getClosestField | |
* | |
* Returns the closest field instance | |
* | |
* @date 22/1/18 | |
* @since 5.6.5 | |
* | |
* @param jQuery $el | |
* @return object | |
*/ | |
acf.getClosestField = function( $el ){ | |
var $field = acf.findClosestField( $el ); | |
return this.getField( $field ); | |
}; | |
/** | |
* addGlobalFieldAction | |
* | |
* Sets up callback logic for global field actions | |
* | |
* @date 15/6/18 | |
* @since 5.6.9 | |
* | |
* @param string action | |
* @return void | |
*/ | |
var addGlobalFieldAction = function( action ){ | |
// vars | |
var globalAction = action; | |
var pluralAction = action + '_fields'; // ready_fields | |
var singleAction = action + '_field'; // ready_field | |
// global action | |
var globalCallback = function( $el /*, arg1, arg2, etc*/ ){ | |
//console.log( action, arguments ); | |
// get args [$el, ...] | |
var args = acf.arrayArgs( arguments ); | |
var extraArgs = args.slice(1); | |
// find fields | |
var fields = acf.getFields({ parent: $el }); | |
// check | |
if( fields.length ) { | |
// pluralAction | |
var pluralArgs = [ pluralAction, fields ].concat( extraArgs ); | |
acf.doAction.apply(null, pluralArgs); | |
} | |
}; | |
// plural action | |
var pluralCallback = function( fields /*, arg1, arg2, etc*/ ){ | |
//console.log( pluralAction, arguments ); | |
// get args [fields, ...] | |
var args = acf.arrayArgs( arguments ); | |
var extraArgs = args.slice(1); | |
// loop | |
fields.map(function( field, i ){ | |
//setTimeout(function(){ | |
// singleAction | |
var singleArgs = [ singleAction, field ].concat( extraArgs ); | |
acf.doAction.apply(null, singleArgs); | |
//}, i * 100); | |
}); | |
}; | |
// add actions | |
acf.addAction(globalAction, globalCallback); | |
acf.addAction(pluralAction, pluralCallback); | |
// also add single action | |
addSingleFieldAction( action ); | |
} | |
/** | |
* addSingleFieldAction | |
* | |
* Sets up callback logic for single field actions | |
* | |
* @date 15/6/18 | |
* @since 5.6.9 | |
* | |
* @param string action | |
* @return void | |
*/ | |
var addSingleFieldAction = function( action ){ | |
// vars | |
var singleAction = action + '_field'; // ready_field | |
var singleEvent = action + 'Field'; // readyField | |
// single action | |
var singleCallback = function( field /*, arg1, arg2, etc*/ ){ | |
//console.log( singleAction, arguments ); | |
// get args [field, ...] | |
var args = acf.arrayArgs( arguments ); | |
var extraArgs = args.slice(1); | |
// action variations (ready_field/type=image) | |
var variations = ['type', 'name', 'key']; | |
variations.map(function( variation ){ | |
// vars | |
var prefix = '/' + variation + '=' + field.get(variation); | |
// singleAction | |
args = [ singleAction + prefix , field ].concat( extraArgs ); | |
acf.doAction.apply(null, args); | |
}); | |
// event | |
if( singleFieldEvents.indexOf(action) > -1 ) { | |
field.trigger(singleEvent, extraArgs); | |
} | |
}; | |
// add actions | |
acf.addAction(singleAction, singleCallback); | |
} | |
// vars | |
var globalFieldActions = [ 'prepare', 'ready', 'load', 'append', 'remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload' ]; | |
var singleFieldActions = [ 'valid', 'invalid', 'enable', 'disable', 'new' ]; | |
var singleFieldEvents = [ 'remove', 'unmount', 'remount', 'sortstart', 'sortstop', 'show', 'hide', 'unload', 'valid', 'invalid', 'enable', 'disable' ]; | |
// add | |
globalFieldActions.map( addGlobalFieldAction ); | |
singleFieldActions.map( addSingleFieldAction ); | |
/** | |
* fieldsEventManager | |
* | |
* Manages field actions and events | |
* | |
* @date 15/12/17 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @param void | |
*/ | |
var fieldsEventManager = new acf.Model({ | |
id: 'fieldsEventManager', | |
events: { | |
'click .acf-field a[href="#"]': 'onClick', | |
'change .acf-field': 'onChange' | |
}, | |
onClick: function( e ){ | |
// prevent default of any link with an href of # | |
e.preventDefault(); | |
}, | |
onChange: function(){ | |
// preview hack allows post to save with no title or content | |
$('#_acf_changed').val(1); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var i = 0; | |
var Field = acf.Field.extend({ | |
type: 'accordion', | |
wait: '', | |
$control: function(){ | |
return this.$('.acf-fields:first'); | |
}, | |
initialize: function(){ | |
// bail early if is cell | |
if( this.$el.is('td') ) return; | |
// enpoint | |
if( this.get('endpoint') ) { | |
return this.remove(); | |
} | |
// vars | |
var $field = this.$el; | |
var $label = this.$labelWrap() | |
var $input = this.$inputWrap(); | |
var $wrap = this.$control(); | |
var $instructions = $input.children('.description'); | |
// force description into label | |
if( $instructions.length ) { | |
$label.append( $instructions ); | |
} | |
// table | |
if( this.$el.is('tr') ) { | |
// vars | |
var $table = this.$el.closest('table'); | |
var $newLabel = $('<div class="acf-accordion-title"/>'); | |
var $newInput = $('<div class="acf-accordion-content"/>'); | |
var $newTable = $('<table class="' + $table.attr('class') + '"/>'); | |
var $newWrap = $('<tbody/>'); | |
// dom | |
$newLabel.append( $label.html() ); | |
$newTable.append( $newWrap ); | |
$newInput.append( $newTable ); | |
$input.append( $newLabel ); | |
$input.append( $newInput ); | |
// modify | |
$label.remove(); | |
$wrap.remove(); | |
$input.attr('colspan', 2); | |
// update vars | |
$label = $newLabel; | |
$input = $newInput; | |
$wrap = $newWrap; | |
} | |
// add classes | |
$field.addClass('acf-accordion'); | |
$label.addClass('acf-accordion-title'); | |
$input.addClass('acf-accordion-content'); | |
// index | |
i++; | |
// multi-expand | |
if( this.get('multi_expand') ) { | |
$field.attr('multi-expand', 1); | |
} | |
// open | |
var order = acf.getPreference('this.accordions') || []; | |
if( order[i-1] !== undefined ) { | |
this.set('open', order[i-1]); | |
} | |
if( this.get('open') ) { | |
$field.addClass('-open'); | |
$input.css('display', 'block'); // needed for accordion to close smoothly | |
} | |
// add icon | |
$label.prepend( accordionManager.iconHtml({ open: this.get('open') }) ); | |
// classes | |
// - remove 'inside' which is a #poststuff WP class | |
var $parent = $field.parent(); | |
$wrap.addClass( $parent.hasClass('-left') ? '-left' : '' ); | |
$wrap.addClass( $parent.hasClass('-clear') ? '-clear' : '' ); | |
// append | |
$wrap.append( $field.nextUntil('.acf-field-accordion', '.acf-field') ); | |
// clean up | |
$wrap.removeAttr('data-open data-multi_expand data-endpoint'); | |
}, | |
}); | |
acf.registerFieldType( Field ); | |
/** | |
* accordionManager | |
* | |
* Events manager for the acf accordion | |
* | |
* @date 14/2/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
var accordionManager = new acf.Model({ | |
actions: { | |
'unload': 'onUnload' | |
}, | |
events: { | |
'click .acf-accordion-title': 'onClick', | |
'invalidField .acf-accordion': 'onInvalidField' | |
}, | |
isOpen: function( $el ) { | |
return $el.hasClass('-open'); | |
}, | |
toggle: function( $el ){ | |
if( this.isOpen($el) ) { | |
this.close( $el ); | |
} else { | |
this.open( $el ); | |
} | |
}, | |
iconHtml: function( props ){ | |
// Determine icon. | |
//if( acf.isGutenberg() ) { | |
// var icon = props.open ? 'arrow-up-alt2' : 'arrow-down-alt2'; | |
//} else { | |
var icon = props.open ? 'arrow-down' : 'arrow-right'; | |
//} | |
// Return HTML. | |
return '<i class="acf-accordion-icon dashicons dashicons-' + icon + '"></i>'; | |
}, | |
open: function( $el ){ | |
// open | |
$el.find('.acf-accordion-content:first').slideDown().css('display', 'block'); | |
$el.find('.acf-accordion-icon:first').replaceWith( this.iconHtml({ open: true }) ); | |
$el.addClass('-open'); | |
// action | |
acf.doAction('show', $el); | |
// close siblings | |
if( !$el.attr('multi-expand') ) { | |
$el.siblings('.acf-accordion.-open').each(function(){ | |
accordionManager.close( $(this) ); | |
}); | |
} | |
}, | |
close: function( $el ){ | |
// close | |
$el.find('.acf-accordion-content:first').slideUp(); | |
$el.find('.acf-accordion-icon:first').replaceWith( this.iconHtml({ open: false }) ); | |
$el.removeClass('-open'); | |
// action | |
acf.doAction('hide', $el); | |
}, | |
onClick: function( e, $el ){ | |
// prevent Defailt | |
e.preventDefault(); | |
// open close | |
this.toggle( $el.parent() ); | |
}, | |
onInvalidField: function( e, $el ){ | |
// bail early if already focused | |
if( this.busy ) { | |
return; | |
} | |
// disable functionality for 1sec (allow next validation to work) | |
this.busy = true; | |
this.setTimeout(function(){ | |
this.busy = false; | |
}, 1000); | |
// open accordion | |
this.open( $el ); | |
}, | |
onUnload: function( e ){ | |
// vars | |
var order = []; | |
// loop | |
$('.acf-accordion').each(function(){ | |
var open = $(this).hasClass('-open') ? 1 : 0; | |
order.push(open); | |
}); | |
// set | |
if( order.length ) { | |
acf.setPreference('this.accordions', order); | |
} | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'button_group', | |
events: { | |
'click input[type="radio"]': 'onClick' | |
}, | |
$control: function(){ | |
return this.$('.acf-button-group'); | |
}, | |
$input: function(){ | |
return this.$('input:checked'); | |
}, | |
setValue: function( val ){ | |
this.$('input[value="' + val + '"]').prop('checked', true).trigger('change'); | |
}, | |
onClick: function( e, $el ){ | |
// vars | |
var $label = $el.parent('label'); | |
var selected = $label.hasClass('selected'); | |
// remove previous selected | |
this.$('.selected').removeClass('selected'); | |
// add active class | |
$label.addClass('selected'); | |
// allow null | |
if( this.get('allow_null') && selected ) { | |
$label.removeClass('selected'); | |
$el.prop('checked', false).trigger('change'); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'checkbox', | |
events: { | |
'change input': 'onChange', | |
'click .acf-add-checkbox': 'onClickAdd', | |
'click .acf-checkbox-toggle': 'onClickToggle', | |
'click .acf-checkbox-custom': 'onClickCustom' | |
}, | |
$control: function(){ | |
return this.$('.acf-checkbox-list'); | |
}, | |
$toggle: function(){ | |
return this.$('.acf-checkbox-toggle'); | |
}, | |
$input: function(){ | |
return this.$('input[type="hidden"]'); | |
}, | |
$inputs: function(){ | |
return this.$('input[type="checkbox"]').not('.acf-checkbox-toggle'); | |
}, | |
getValue: function(){ | |
var val = []; | |
this.$(':checked').each(function(){ | |
val.push( $(this).val() ); | |
}); | |
return val.length ? val : false; | |
}, | |
onChange: function( e, $el ){ | |
// Vars. | |
var checked = $el.prop('checked'); | |
var $label = $el.parent('label'); | |
var $toggle = this.$toggle(); | |
// Add or remove "selected" class. | |
if( checked ) { | |
$label.addClass('selected'); | |
} else { | |
$label.removeClass('selected'); | |
} | |
// Update toggle state if all inputs are checked. | |
if( $toggle.length ) { | |
var $inputs = this.$inputs(); | |
// all checked | |
if( $inputs.not(':checked').length == 0 ) { | |
$toggle.prop('checked', true); | |
} else { | |
$toggle.prop('checked', false); | |
} | |
} | |
}, | |
onClickAdd: function( e, $el ){ | |
var html = '<li><input class="acf-checkbox-custom" type="checkbox" checked="checked" /><input type="text" name="' + this.getInputName() + '[]" /></li>'; | |
$el.parent('li').before( html ); | |
}, | |
onClickToggle: function( e, $el ){ | |
// Vars. | |
var checked = $el.prop('checked'); | |
var $inputs = this.$('input[type="checkbox"]'); | |
var $labels = this.$('label'); | |
// Update "checked" state. | |
$inputs.prop('checked', checked); | |
// Add or remove "selected" class. | |
if( checked ) { | |
$labels.addClass('selected'); | |
} else { | |
$labels.removeClass('selected'); | |
} | |
}, | |
onClickCustom: function( e, $el ){ | |
var checked = $el.prop('checked'); | |
var $text = $el.next('input[type="text"]'); | |
// checked | |
if( checked ) { | |
$text.prop('disabled', false); | |
// not checked | |
} else { | |
$text.prop('disabled', true); | |
// remove | |
if( $text.val() == '' ) { | |
$el.parent('li').remove(); | |
} | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'color_picker', | |
wait: 'load', | |
$control: function(){ | |
return this.$('.acf-color-picker'); | |
}, | |
$input: function(){ | |
return this.$('input[type="hidden"]'); | |
}, | |
$inputText: function(){ | |
return this.$('input[type="text"]'); | |
}, | |
setValue: function( val ){ | |
// update input (with change) | |
acf.val( this.$input(), val ); | |
// update iris | |
this.$inputText().iris('color', val); | |
}, | |
initialize: function(){ | |
// vars | |
var $input = this.$input(); | |
var $inputText = this.$inputText(); | |
// event | |
var onChange = function( e ){ | |
// timeout is required to ensure the $input val is correct | |
setTimeout(function(){ | |
acf.val( $input, $inputText.val() ); | |
}, 1); | |
} | |
// args | |
var args = { | |
defaultColor: false, | |
palettes: true, | |
hide: true, | |
change: onChange, | |
clear: onChange | |
}; | |
// filter | |
var args = acf.applyFilters('color_picker_args', args, this); | |
// initialize | |
$inputText.wpColorPicker( args ); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'date_picker', | |
events: { | |
'blur input[type="text"]': 'onBlur' | |
}, | |
$control: function(){ | |
return this.$('.acf-date-picker'); | |
}, | |
$input: function(){ | |
return this.$('input[type="hidden"]'); | |
}, | |
$inputText: function(){ | |
return this.$('input[type="text"]'); | |
}, | |
initialize: function(){ | |
// save_format: compatibility with ACF < 5.0.0 | |
if( this.has('save_format') ) { | |
return this.initializeCompatibility(); | |
} | |
// vars | |
var $input = this.$input(); | |
var $inputText = this.$inputText(); | |
// args | |
var args = { | |
dateFormat: this.get('date_format'), | |
altField: $input, | |
altFormat: 'yymmdd', | |
changeYear: true, | |
yearRange: "-100:+100", | |
changeMonth: true, | |
showButtonPanel: true, | |
firstDay: this.get('first_day') | |
}; | |
// filter | |
args = acf.applyFilters('date_picker_args', args, this); | |
// add date picker | |
acf.newDatePicker( $inputText, args ); | |
// action | |
acf.doAction('date_picker_init', $inputText, args, this); | |
}, | |
initializeCompatibility: function(){ | |
// vars | |
var $input = this.$input(); | |
var $inputText = this.$inputText(); | |
// get and set value from alt field | |
$inputText.val( $input.val() ); | |
// args | |
var args = { | |
dateFormat: this.get('date_format'), | |
altField: $input, | |
altFormat: this.get('save_format'), | |
changeYear: true, | |
yearRange: "-100:+100", | |
changeMonth: true, | |
showButtonPanel: true, | |
firstDay: this.get('first_day') | |
}; | |
// filter for 3rd party customization | |
args = acf.applyFilters('date_picker_args', args, this); | |
// backup | |
var dateFormat = args.dateFormat; | |
// change args.dateFormat | |
args.dateFormat = this.get('save_format'); | |
// add date picker | |
acf.newDatePicker( $inputText, args ); | |
// now change the format back to how it should be. | |
$inputText.datepicker( 'option', 'dateFormat', dateFormat ); | |
// action for 3rd party customization | |
acf.doAction('date_picker_init', $inputText, args, this); | |
}, | |
onBlur: function(){ | |
if( !this.$inputText().val() ) { | |
acf.val( this.$input(), '' ); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
// manager | |
var datePickerManager = new acf.Model({ | |
priority: 5, | |
wait: 'ready', | |
initialize: function(){ | |
// vars | |
var locale = acf.get('locale'); | |
var rtl = acf.get('rtl'); | |
var l10n = acf.get('datePickerL10n'); | |
// bail ealry if no l10n | |
if( !l10n ) { | |
return false; | |
} | |
// bail ealry if no datepicker library | |
if( typeof $.datepicker === 'undefined' ) { | |
return false; | |
} | |
// rtl | |
l10n.isRTL = rtl; | |
// append | |
$.datepicker.regional[ locale ] = l10n; | |
$.datepicker.setDefaults(l10n); | |
} | |
}); | |
// add | |
acf.newDatePicker = function( $input, args ){ | |
// bail ealry if no datepicker library | |
if( typeof $.datepicker === 'undefined' ) { | |
return false; | |
} | |
// defaults | |
args = args || {}; | |
// initialize | |
$input.datepicker( args ); | |
// wrap the datepicker (only if it hasn't already been wrapped) | |
if( $('body > #ui-datepicker-div').exists() ) { | |
$('body > #ui-datepicker-div').wrap('<div class="acf-ui-datepicker" />'); | |
} | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.DatePickerField.extend({ | |
type: 'date_time_picker', | |
$control: function(){ | |
return this.$('.acf-date-time-picker'); | |
}, | |
initialize: function(){ | |
// vars | |
var $input = this.$input(); | |
var $inputText = this.$inputText(); | |
// args | |
var args = { | |
dateFormat: this.get('date_format'), | |
timeFormat: this.get('time_format'), | |
altField: $input, | |
altFieldTimeOnly: false, | |
altFormat: 'yy-mm-dd', | |
altTimeFormat: 'HH:mm:ss', | |
changeYear: true, | |
yearRange: "-100:+100", | |
changeMonth: true, | |
showButtonPanel: true, | |
firstDay: this.get('first_day'), | |
controlType: 'select', | |
oneLine: true | |
}; | |
// filter | |
args = acf.applyFilters('date_time_picker_args', args, this); | |
// add date time picker | |
acf.newDateTimePicker( $inputText, args ); | |
// action | |
acf.doAction('date_time_picker_init', $inputText, args, this); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
// manager | |
var dateTimePickerManager = new acf.Model({ | |
priority: 5, | |
wait: 'ready', | |
initialize: function(){ | |
// vars | |
var locale = acf.get('locale'); | |
var rtl = acf.get('rtl'); | |
var l10n = acf.get('dateTimePickerL10n'); | |
// bail ealry if no l10n | |
if( !l10n ) { | |
return false; | |
} | |
// bail ealry if no datepicker library | |
if( typeof $.timepicker === 'undefined' ) { | |
return false; | |
} | |
// rtl | |
l10n.isRTL = rtl; | |
// append | |
$.timepicker.regional[ locale ] = l10n; | |
$.timepicker.setDefaults(l10n); | |
} | |
}); | |
// add | |
acf.newDateTimePicker = function( $input, args ){ | |
// bail ealry if no datepicker library | |
if( typeof $.timepicker === 'undefined' ) { | |
return false; | |
} | |
// defaults | |
args = args || {}; | |
// initialize | |
$input.datetimepicker( args ); | |
// wrap the datepicker (only if it hasn't already been wrapped) | |
if( $('body > #ui-datepicker-div').exists() ) { | |
$('body > #ui-datepicker-div').wrap('<div class="acf-ui-datepicker" />'); | |
} | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'google_map', | |
map: false, | |
wait: 'load', | |
events: { | |
'click a[data-name="clear"]': 'onClickClear', | |
'click a[data-name="locate"]': 'onClickLocate', | |
'click a[data-name="search"]': 'onClickSearch', | |
'keydown .search': 'onKeydownSearch', | |
'keyup .search': 'onKeyupSearch', | |
'focus .search': 'onFocusSearch', | |
'blur .search': 'onBlurSearch', | |
'showField': 'onShow', | |
}, | |
$control: function(){ | |
return this.$('.acf-google-map'); | |
}, | |
$search: function(){ | |
return this.$('.search'); | |
}, | |
$canvas: function(){ | |
return this.$('.canvas'); | |
}, | |
setState: function( state ){ | |
// Remove previous state classes. | |
this.$control().removeClass( '-value -loading -searching' ); | |
// Determine auto state based of current value. | |
if( state === 'default' ) { | |
state = this.val() ? 'value' : ''; | |
} | |
// Update state class. | |
if( state ) { | |
this.$control().addClass( '-' + state ); | |
} | |
}, | |
getValue: function(){ | |
var val = this.$input().val(); | |
if( val ) { | |
return JSON.parse( val ) | |
} else { | |
return false; | |
} | |
}, | |
setValue: function( val, silent ){ | |
// Convert input value. | |
var valAttr = ''; | |
if( val ) { | |
valAttr = JSON.stringify( val ); | |
} | |
// Update input (with change). | |
acf.val( this.$input(), valAttr ); | |
// Bail early if silent update. | |
if( silent ) { | |
return; | |
} | |
// Render. | |
this.renderVal( val ); | |
/** | |
* Fires immediately after the value has changed. | |
* | |
* @date 12/02/2014 | |
* @since 5.0.0 | |
* | |
* @param object|string val The new value. | |
* @param object map The Google Map isntance. | |
* @param object field The field instance. | |
*/ | |
acf.doAction('google_map_change', val, this.map, this); | |
}, | |
renderVal: function( val ){ | |
// Value. | |
if( val ) { | |
this.setState( 'value' ); | |
this.$search().val( val.address ); | |
this.setPosition( val.lat, val.lng ); | |
// No value. | |
} else { | |
this.setState( '' ); | |
this.$search().val( '' ); | |
this.map.marker.setVisible( false ); | |
} | |
}, | |
newLatLng: function( lat, lng ){ | |
return new google.maps.LatLng( parseFloat(lat), parseFloat(lng) ); | |
}, | |
setPosition: function( lat, lng ){ | |
// Update marker position. | |
this.map.marker.setPosition({ | |
lat: parseFloat(lat), | |
lng: parseFloat(lng) | |
}); | |
// Show marker. | |
this.map.marker.setVisible( true ); | |
// Center map. | |
this.center(); | |
}, | |
center: function(){ | |
// Find marker position. | |
var position = this.map.marker.getPosition(); | |
if( position ) { | |
var lat = position.lat(); | |
var lng = position.lng(); | |
// Or find default settings. | |
} else { | |
var lat = this.get('lat'); | |
var lng = this.get('lng'); | |
} | |
// Center map. | |
this.map.setCenter({ | |
lat: parseFloat(lat), | |
lng: parseFloat(lng) | |
}); | |
}, | |
initialize: function(){ | |
// Ensure Google API is loaded and then initialize map. | |
withAPI( this.initializeMap.bind(this) ); | |
}, | |
initializeMap: function(){ | |
// Get value ignoring conditional logic status. | |
var val = this.getValue(); | |
// Construct default args. | |
var args = acf.parseArgs(val, { | |
zoom: this.get('zoom'), | |
lat: this.get('lat'), | |
lng: this.get('lng') | |
}); | |
// Create Map. | |
var mapArgs = { | |
scrollwheel: false, | |
zoom: parseInt( args.zoom ), | |
center: { | |
lat: parseFloat( args.lat ), | |
lng: parseFloat( args.lng ) | |
}, | |
mapTypeId: google.maps.MapTypeId.ROADMAP, | |
marker: { | |
draggable: true, | |
raiseOnDrag: true | |
}, | |
autocomplete: {} | |
}; | |
mapArgs = acf.applyFilters('google_map_args', mapArgs, this); | |
var map = new google.maps.Map( this.$canvas()[0], mapArgs ); | |
// Create Marker. | |
var markerArgs = acf.parseArgs(mapArgs.marker, { | |
draggable: true, | |
raiseOnDrag: true, | |
map: map | |
}); | |
markerArgs = acf.applyFilters('google_map_marker_args', markerArgs, this); | |
var marker = new google.maps.Marker( markerArgs ); | |
// Maybe Create Autocomplete. | |
var autocomplete = false; | |
if( acf.isset(google, 'maps', 'places', 'Autocomplete') ) { | |
var autocompleteArgs = mapArgs.autocomplete || {}; | |
autocompleteArgs = acf.applyFilters('google_map_autocomplete_args', autocompleteArgs, this); | |
autocomplete = new google.maps.places.Autocomplete( this.$search()[0], autocompleteArgs ); | |
autocomplete.bindTo('bounds', map); | |
} | |
// Add map events. | |
this.addMapEvents( this, map, marker, autocomplete ); | |
// Append references. | |
map.acf = this; | |
map.marker = marker; | |
map.autocomplete = autocomplete; | |
this.map = map; | |
// Set position. | |
if( val ) { | |
this.setPosition( val.lat, val.lng ); | |
} | |
/** | |
* Fires immediately after the Google Map has been initialized. | |
* | |
* @date 12/02/2014 | |
* @since 5.0.0 | |
* | |
* @param object map The Google Map isntance. | |
* @param object marker The Google Map marker isntance. | |
* @param object field The field instance. | |
*/ | |
acf.doAction('google_map_init', map, marker, this); | |
}, | |
addMapEvents: function( field, map, marker, autocomplete ){ | |
// Click map. | |
google.maps.event.addListener( map, 'click', function( e ) { | |
var lat = e.latLng.lat(); | |
var lng = e.latLng.lng(); | |
field.searchPosition( lat, lng ); | |
}); | |
// Drag marker. | |
google.maps.event.addListener( marker, 'dragend', function(){ | |
var lat = this.getPosition().lat(); | |
var lng = this.getPosition().lng(); | |
field.searchPosition( lat, lng ); | |
}); | |
// Autocomplete search. | |
if( autocomplete ) { | |
google.maps.event.addListener(autocomplete, 'place_changed', function() { | |
var place = this.getPlace(); | |
field.searchPlace( place ); | |
}); | |
} | |
// Detect zoom change. | |
google.maps.event.addListener( map, 'zoom_changed', function(){ | |
var val = field.val(); | |
if( val ) { | |
val.zoom = map.getZoom(); | |
field.setValue( val, true ); | |
} | |
}); | |
}, | |
searchPosition: function( lat, lng ){ | |
//console.log('searchPosition', lat, lng ); | |
// Start Loading. | |
this.setState( 'loading' ); | |
// Query Geocoder. | |
var latLng = { lat: lat, lng: lng }; | |
geocoder.geocode({ location: latLng }, function( results, status ){ | |
//console.log('searchPosition', arguments ); | |
// End Loading. | |
this.setState( '' ); | |
// Status failure. | |
if( status !== 'OK' ) { | |
this.showNotice({ | |
text: acf.__('Location not found: %s').replace('%s', status), | |
type: 'warning' | |
}); | |
// Success. | |
} else { | |
var val = this.parseResult( results[0] ); | |
// Update value. | |
this.val( val ); | |
} | |
}.bind( this )); | |
}, | |
searchPlace: function( place ){ | |
//console.log('searchPlace', place ); | |
// Ignore empty search. | |
if( !place || !place.name ) { | |
return; | |
} | |
// No geometry (Custom address search). | |
if( !place.geometry ) { | |
return this.searchAddress( place.name ); | |
} | |
// Parse place. | |
var val = this.parseResult( place ); | |
// Update value. | |
this.val( val ); | |
}, | |
searchAddress: function( address ){ | |
//console.log('searchAddress', address ); | |
// Bail early if no address. | |
if( !address ) { | |
return; | |
} | |
// Allow "lat,lng" search. | |
var latLng = address.split(','); | |
if( latLng.length == 2 ) { | |
var lat = parseFloat(latLng[0]); | |
var lng = parseFloat(latLng[1]); | |
if( lat && lng ) { | |
return this.searchPosition( lat, lng ); | |
} | |
} | |
// Start Loading. | |
this.setState( 'loading' ); | |
// Query Geocoder. | |
geocoder.geocode({ address: address }, function( results, status ){ | |
//console.log('searchPosition', arguments ); | |
// End Loading. | |
this.setState( '' ); | |
// Status failure. | |
if( status !== 'OK' ) { | |
this.showNotice({ | |
text: acf.__('Location not found: %s').replace('%s', status), | |
type: 'warning' | |
}); | |
// Success. | |
} else { | |
var val = this.parseResult( results[0] ); | |
// Override address data with parameter allowing custom address to be defined in search. | |
val.address = address; | |
// Update value. | |
this.val( val ); | |
} | |
}.bind( this )); | |
}, | |
searchLocation: function(){ | |
//console.log('searchLocation' ); | |
// Check HTML5 geolocation. | |
if( !navigator.geolocation ) { | |
return alert( acf.__('Sorry, this browser does not support geolocation') ); | |
} | |
// Start Loading. | |
this.setState( 'loading' ); | |
// Query Geolocation. | |
navigator.geolocation.getCurrentPosition( | |
// Success. | |
function( results ){ | |
// End Loading. | |
this.setState( '' ); | |
// Search position. | |
var lat = results.coords.latitude; | |
var lng = results.coords.longitude; | |
this.searchPosition( lat, lng ); | |
}.bind(this), | |
// Failure. | |
function( error ){ | |
this.setState( '' ); | |
}.bind(this) | |
); | |
}, | |
/** | |
* parseResult | |
* | |
* Returns location data for the given GeocoderResult object. | |
* | |
* @date 15/10/19 | |
* @since 5.8.6 | |
* | |
* @param object obj A GeocoderResult object. | |
* @return object | |
*/ | |
parseResult: function( obj ) { | |
// Construct basic data. | |
var result = { | |
address: obj.formatted_address, | |
lat: obj.geometry.location.lat(), | |
lng: obj.geometry.location.lng(), | |
}; | |
// Add zoom level. | |
result.zoom = this.map.getZoom(); | |
// Add place ID. | |
if( obj.place_id ) { | |
result.place_id = obj.place_id; | |
} | |
// Create search map for address component data. | |
var map = { | |
street_number: [ 'street_number' ], | |
street_name: [ 'street_address', 'route' ], | |
city: [ 'locality' ], | |
state: [ | |
'administrative_area_level_1', | |
'administrative_area_level_2', | |
'administrative_area_level_3', | |
'administrative_area_level_4', | |
'administrative_area_level_5' | |
], | |
post_code: [ 'postal_code' ], | |
country: [ 'country' ] | |
}; | |
// Loop over map. | |
for( var k in map ) { | |
var keywords = map[ k ]; | |
// Loop over address components. | |
for( var i = 0; i < obj.address_components.length; i++ ) { | |
var component = obj.address_components[ i ]; | |
var component_type = component.types[0]; | |
// Look for matching component type. | |
if( keywords.indexOf(component_type) !== -1 ) { | |
// Append to result. | |
result[ k ] = component.long_name; | |
// Append short version. | |
if( component.long_name !== component.short_name ) { | |
result[ k + '_short' ] = component.short_name; | |
} | |
} | |
} | |
} | |
/** | |
* Filters the parsed result. | |
* | |
* @date 18/10/19 | |
* @since 5.8.6 | |
* | |
* @param object result The parsed result value. | |
* @param object obj The GeocoderResult object. | |
*/ | |
return acf.applyFilters('google_map_result', result, obj, this.map, this); | |
}, | |
onClickClear: function(){ | |
this.val( false ); | |
}, | |
onClickLocate: function(){ | |
this.searchLocation(); | |
}, | |
onClickSearch: function(){ | |
this.searchAddress( this.$search().val() ); | |
}, | |
onFocusSearch: function( e, $el ){ | |
this.setState( 'searching' ); | |
}, | |
onBlurSearch: function( e, $el ){ | |
// Get saved address value. | |
var val = this.val(); | |
var address = val ? val.address : ''; | |
// Remove 'is-searching' if value has not changed. | |
if( $el.val() === address ) { | |
this.setState( 'default' ); | |
} | |
}, | |
onKeyupSearch: function( e, $el ){ | |
// Clear empty value. | |
if( !$el.val() ) { | |
this.val( false ); | |
} | |
}, | |
// Prevent form from submitting. | |
onKeydownSearch: function( e, $el ){ | |
if( e.which == 13 ) { | |
e.preventDefault(); | |
$el.blur(); | |
} | |
}, | |
// Center map once made visible. | |
onShow: function(){ | |
if( this.map ) { | |
this.setTimeout( this.center ); | |
} | |
}, | |
}); | |
acf.registerFieldType( Field ); | |
// Vars. | |
var loading = false; | |
var geocoder = false; | |
/** | |
* withAPI | |
* | |
* Loads the Google Maps API library and troggers callback. | |
* | |
* @date 28/3/19 | |
* @since 5.7.14 | |
* | |
* @param function callback The callback to excecute. | |
* @return void | |
*/ | |
function withAPI( callback ) { | |
// Check if geocoder exists. | |
if( geocoder ) { | |
return callback(); | |
} | |
// Check if geocoder API exists. | |
if( acf.isset(window, 'google', 'maps', 'Geocoder') ) { | |
geocoder = new google.maps.Geocoder(); | |
return callback(); | |
} | |
// Geocoder will need to be loaded. Hook callback to action. | |
acf.addAction( 'google_map_api_loaded', callback ); | |
// Bail early if already loading API. | |
if( loading ) { | |
return; | |
} | |
// load api | |
var url = acf.get('google_map_api'); | |
if( url ) { | |
// Set loading status. | |
loading = true; | |
// Load API | |
$.ajax({ | |
url: url, | |
dataType: 'script', | |
cache: true, | |
success: function(){ | |
geocoder = new google.maps.Geocoder(); | |
acf.doAction('google_map_api_loaded'); | |
} | |
}); | |
} | |
} | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'image', | |
$control: function(){ | |
return this.$('.acf-image-uploader'); | |
}, | |
$input: function(){ | |
return this.$('input[type="hidden"]'); | |
}, | |
events: { | |
'click a[data-name="add"]': 'onClickAdd', | |
'click a[data-name="edit"]': 'onClickEdit', | |
'click a[data-name="remove"]': 'onClickRemove', | |
'change input[type="file"]': 'onChange' | |
}, | |
initialize: function(){ | |
// add attribute to form | |
if( this.get('uploader') === 'basic' ) { | |
this.$el.closest('form').attr('enctype', 'multipart/form-data'); | |
} | |
}, | |
validateAttachment: function( attachment ){ | |
// defaults | |
attachment = attachment || {}; | |
// WP attachment | |
if( attachment.id !== undefined ) { | |
attachment = attachment.attributes; | |
} | |
// args | |
attachment = acf.parseArgs(attachment, { | |
url: '', | |
alt: '', | |
title: '', | |
caption: '', | |
description: '', | |
width: 0, | |
height: 0 | |
}); | |
// preview size | |
var url = acf.isget(attachment, 'sizes', this.get('preview_size'), 'url'); | |
if( url !== null ) { | |
attachment.url = url; | |
} | |
// return | |
return attachment; | |
}, | |
render: function( attachment ){ | |
// vars | |
attachment = this.validateAttachment( attachment ); | |
// update image | |
this.$('img').attr({ | |
src: attachment.url, | |
alt: attachment.alt, | |
title: attachment.title | |
}); | |
// vars | |
var val = attachment.id || ''; | |
// update val | |
this.val( val ); | |
// update class | |
if( val ) { | |
this.$control().addClass('has-value'); | |
} else { | |
this.$control().removeClass('has-value'); | |
} | |
}, | |
// create a new repeater row and render value | |
append: function( attachment, parent ){ | |
// create function to find next available field within parent | |
var getNext = function( field, parent ){ | |
// find existing file fields within parent | |
var fields = acf.getFields({ | |
key: field.get('key'), | |
parent: parent.$el | |
}); | |
// find the first field with no value | |
for( var i = 0; i < fields.length; i++ ) { | |
if( !fields[i].val() ) { | |
return fields[i]; | |
} | |
} | |
// return | |
return false; | |
} | |
// find existing file fields within parent | |
var field = getNext( this, parent ); | |
// add new row if no available field | |
if( !field ) { | |
parent.$('.acf-button:last').trigger('click'); | |
field = getNext( this, parent ); | |
} | |
// render | |
if( field ) { | |
field.render( attachment ); | |
} | |
}, | |
selectAttachment: function(){ | |
// vars | |
var parent = this.parent(); | |
var multiple = (parent && parent.get('type') === 'repeater'); | |
// new frame | |
var frame = acf.newMediaPopup({ | |
mode: 'select', | |
type: 'image', | |
title: acf.__('Select Image'), | |
field: this.get('key'), | |
multiple: multiple, | |
library: this.get('library'), | |
allowedTypes: this.get('mime_types'), | |
select: $.proxy(function( attachment, i ) { | |
if( i > 0 ) { | |
this.append( attachment, parent ); | |
} else { | |
this.render( attachment ); | |
} | |
}, this) | |
}); | |
}, | |
editAttachment: function(){ | |
// vars | |
var val = this.val(); | |
// bail early if no val | |
if( !val ) return; | |
// popup | |
var frame = acf.newMediaPopup({ | |
mode: 'edit', | |
title: acf.__('Edit Image'), | |
button: acf.__('Update Image'), | |
attachment: val, | |
field: this.get('key'), | |
select: $.proxy(function( attachment, i ) { | |
this.render( attachment ); | |
}, this) | |
}); | |
}, | |
removeAttachment: function(){ | |
this.render( false ); | |
}, | |
onClickAdd: function( e, $el ){ | |
this.selectAttachment(); | |
}, | |
onClickEdit: function( e, $el ){ | |
this.editAttachment(); | |
}, | |
onClickRemove: function( e, $el ){ | |
this.removeAttachment(); | |
}, | |
onChange: function( e, $el ){ | |
var $hiddenInput = this.$input(); | |
acf.getFileInputData($el, function( data ){ | |
$hiddenInput.val( $.param(data) ); | |
}); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.ImageField.extend({ | |
type: 'file', | |
$control: function(){ | |
return this.$('.acf-file-uploader'); | |
}, | |
$input: function(){ | |
return this.$('input[type="hidden"]'); | |
}, | |
validateAttachment: function( attachment ){ | |
// defaults | |
attachment = attachment || {}; | |
// WP attachment | |
if( attachment.id !== undefined ) { | |
attachment = attachment.attributes; | |
} | |
// args | |
attachment = acf.parseArgs(attachment, { | |
url: '', | |
alt: '', | |
title: '', | |
filename: '', | |
filesizeHumanReadable: '', | |
icon: '/wp-includes/images/media/default.png' | |
}); | |
// return | |
return attachment; | |
}, | |
render: function( attachment ){ | |
// vars | |
attachment = this.validateAttachment( attachment ); | |
// update image | |
this.$('img').attr({ | |
src: attachment.icon, | |
alt: attachment.alt, | |
title: attachment.title | |
}); | |
// update elements | |
this.$('[data-name="title"]').text( attachment.title ); | |
this.$('[data-name="filename"]').text( attachment.filename ).attr( 'href', attachment.url ); | |
this.$('[data-name="filesize"]').text( attachment.filesizeHumanReadable ); | |
// vars | |
var val = attachment.id || ''; | |
// update val | |
acf.val( this.$input(), val ); | |
// update class | |
if( val ) { | |
this.$control().addClass('has-value'); | |
} else { | |
this.$control().removeClass('has-value'); | |
} | |
}, | |
selectAttachment: function(){ | |
// vars | |
var parent = this.parent(); | |
var multiple = (parent && parent.get('type') === 'repeater'); | |
// new frame | |
var frame = acf.newMediaPopup({ | |
mode: 'select', | |
title: acf.__('Select File'), | |
field: this.get('key'), | |
multiple: multiple, | |
library: this.get('library'), | |
allowedTypes: this.get('mime_types'), | |
select: $.proxy(function( attachment, i ) { | |
if( i > 0 ) { | |
this.append( attachment, parent ); | |
} else { | |
this.render( attachment ); | |
} | |
}, this) | |
}); | |
}, | |
editAttachment: function(){ | |
// vars | |
var val = this.val(); | |
// bail early if no val | |
if( !val ) { | |
return false; | |
} | |
// popup | |
var frame = acf.newMediaPopup({ | |
mode: 'edit', | |
title: acf.__('Edit File'), | |
button: acf.__('Update File'), | |
attachment: val, | |
field: this.get('key'), | |
select: $.proxy(function( attachment, i ) { | |
this.render( attachment ); | |
}, this) | |
}); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'link', | |
events: { | |
'click a[data-name="add"]': 'onClickEdit', | |
'click a[data-name="edit"]': 'onClickEdit', | |
'click a[data-name="remove"]': 'onClickRemove', | |
'change .link-node': 'onChange', | |
}, | |
$control: function(){ | |
return this.$('.acf-link'); | |
}, | |
$node: function(){ | |
return this.$('.link-node'); | |
}, | |
getValue: function(){ | |
// vars | |
var $node = this.$node(); | |
// return false if empty | |
if( !$node.attr('href') ) { | |
return false; | |
} | |
// return | |
return { | |
title: $node.html(), | |
url: $node.attr('href'), | |
target: $node.attr('target') | |
}; | |
}, | |
setValue: function( val ){ | |
// default | |
val = acf.parseArgs(val, { | |
title: '', | |
url: '', | |
target: '' | |
}); | |
// vars | |
var $div = this.$control(); | |
var $node = this.$node(); | |
// remove class | |
$div.removeClass('-value -external'); | |
// add class | |
if( val.url ) $div.addClass('-value'); | |
if( val.target === '_blank' ) $div.addClass('-external'); | |
// update text | |
this.$('.link-title').html( val.title ); | |
this.$('.link-url').attr('href', val.url).html( val.url ); | |
// update node | |
$node.html(val.title); | |
$node.attr('href', val.url); | |
$node.attr('target', val.target); | |
// update inputs | |
this.$('.input-title').val( val.title ); | |
this.$('.input-target').val( val.target ); | |
this.$('.input-url').val( val.url ).trigger('change'); | |
}, | |
onClickEdit: function( e, $el ){ | |
acf.wpLink.open( this.$node() ); | |
}, | |
onClickRemove: function( e, $el ){ | |
this.setValue( false ); | |
}, | |
onChange: function( e, $el ){ | |
// get the changed value | |
var val = this.getValue(); | |
// update inputs | |
this.setValue(val); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
// manager | |
acf.wpLink = new acf.Model({ | |
getNodeValue: function(){ | |
var $node = this.get('node'); | |
return { | |
title: acf.decode( $node.html() ), | |
url: $node.attr('href'), | |
target: $node.attr('target') | |
}; | |
}, | |
setNodeValue: function( val ){ | |
var $node = this.get('node'); | |
$node.text( val.title ); | |
$node.attr('href', val.url); | |
$node.attr('target', val.target); | |
$node.trigger('change'); | |
}, | |
getInputValue: function(){ | |
return { | |
title: $('#wp-link-text').val(), | |
url: $('#wp-link-url').val(), | |
target: $('#wp-link-target').prop('checked') ? '_blank' : '' | |
}; | |
}, | |
setInputValue: function( val ){ | |
$('#wp-link-text').val( val.title ); | |
$('#wp-link-url').val( val.url ); | |
$('#wp-link-target').prop('checked', val.target === '_blank' ); | |
}, | |
open: function( $node ){ | |
// add events | |
this.on('wplink-open', 'onOpen'); | |
this.on('wplink-close', 'onClose'); | |
// set node | |
this.set('node', $node); | |
// create textarea | |
var $textarea = $('<textarea id="acf-link-textarea" style="display:none;"></textarea>'); | |
$('body').append( $textarea ); | |
// vars | |
var val = this.getNodeValue(); | |
// open popup | |
wpLink.open( 'acf-link-textarea', val.url, val.title, null ); | |
}, | |
onOpen: function(){ | |
// always show title (WP will hide title if empty) | |
$('#wp-link-wrap').addClass('has-text-field'); | |
// set inputs | |
var val = this.getNodeValue(); | |
this.setInputValue( val ); | |
}, | |
close: function(){ | |
wpLink.close(); | |
}, | |
onClose: function(){ | |
// bail early if no node | |
// needed due to WP triggering this event twice | |
if( !this.has('node') ) { | |
return false; | |
} | |
// remove events | |
this.off('wplink-open'); | |
this.off('wplink-close'); | |
// set value | |
var val = this.getInputValue(); | |
this.setNodeValue( val ); | |
// remove textarea | |
$('#acf-link-textarea').remove(); | |
// reset | |
this.set('node', null); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'oembed', | |
events: { | |
'click [data-name="clear-button"]': 'onClickClear', | |
'keypress .input-search': 'onKeypressSearch', | |
'keyup .input-search': 'onKeyupSearch', | |
'change .input-search': 'onChangeSearch' | |
}, | |
$control: function(){ | |
return this.$('.acf-oembed'); | |
}, | |
$input: function(){ | |
return this.$('.input-value'); | |
}, | |
$search: function(){ | |
return this.$('.input-search'); | |
}, | |
getValue: function(){ | |
return this.$input().val(); | |
}, | |
getSearchVal: function(){ | |
return this.$search().val(); | |
}, | |
setValue: function( val ){ | |
// class | |
if( val ) { | |
this.$control().addClass('has-value'); | |
} else { | |
this.$control().removeClass('has-value'); | |
} | |
acf.val( this.$input(), val ); | |
}, | |
showLoading: function( show ){ | |
acf.showLoading( this.$('.canvas') ); | |
}, | |
hideLoading: function(){ | |
acf.hideLoading( this.$('.canvas') ); | |
}, | |
maybeSearch: function(){ | |
// vars | |
var prevUrl = this.val(); | |
var url = this.getSearchVal(); | |
// no value | |
if( !url ) { | |
return this.clear(); | |
} | |
// fix missing 'http://' - causes the oembed code to error and fail | |
if( url.substr(0, 4) != 'http' ) { | |
url = 'http://' + url; | |
} | |
// bail early if no change | |
if( url === prevUrl ) return; | |
// clear existing timeout | |
var timeout = this.get('timeout'); | |
if( timeout ) { | |
clearTimeout( timeout ); | |
} | |
// set new timeout | |
var callback = $.proxy(this.search, this, url); | |
this.set('timeout', setTimeout(callback, 300)); | |
}, | |
search: function( url ){ | |
// ajax | |
var ajaxData = { | |
action: 'acf/fields/oembed/search', | |
s: url, | |
field_key: this.get('key') | |
}; | |
// clear existing timeout | |
var xhr = this.get('xhr'); | |
if( xhr ) { | |
xhr.abort(); | |
} | |
// loading | |
this.showLoading(); | |
// query | |
var xhr = $.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax(ajaxData), | |
type: 'post', | |
dataType: 'json', | |
context: this, | |
success: function( json ){ | |
// error | |
if( !json || !json.html ) { | |
json = { | |
url: false, | |
html: '' | |
} | |
} | |
// update vars | |
this.val( json.url ); | |
this.$('.canvas-media').html( json.html ); | |
}, | |
complete: function(){ | |
this.hideLoading(); | |
} | |
}); | |
this.set('xhr', xhr); | |
}, | |
clear: function(){ | |
this.val(''); | |
this.$search().val(''); | |
this.$('.canvas-media').html(''); | |
}, | |
onClickClear: function( e, $el ){ | |
this.clear(); | |
}, | |
onKeypressSearch: function( e, $el ){ | |
if( e.which == 13 ) { | |
e.preventDefault(); | |
this.maybeSearch(); | |
} | |
}, | |
onKeyupSearch: function( e, $el ){ | |
if( $el.val() ) { | |
this.maybeSearch(); | |
} | |
}, | |
onChangeSearch: function( e, $el ){ | |
this.maybeSearch(); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'radio', | |
events: { | |
'click input[type="radio"]': 'onClick', | |
}, | |
$control: function(){ | |
return this.$('.acf-radio-list'); | |
}, | |
$input: function(){ | |
return this.$('input:checked'); | |
}, | |
$inputText: function(){ | |
return this.$('input[type="text"]'); | |
}, | |
getValue: function(){ | |
var val = this.$input().val(); | |
if( val === 'other' && this.get('other_choice') ) { | |
val = this.$inputText().val(); | |
} | |
return val; | |
}, | |
onClick: function( e, $el ){ | |
// vars | |
var $label = $el.parent('label'); | |
var selected = $label.hasClass('selected'); | |
var val = $el.val(); | |
// remove previous selected | |
this.$('.selected').removeClass('selected'); | |
// add active class | |
$label.addClass('selected'); | |
// allow null | |
if( this.get('allow_null') && selected ) { | |
$label.removeClass('selected'); | |
$el.prop('checked', false).trigger('change'); | |
val = false; | |
} | |
// other | |
if( this.get('other_choice') ) { | |
// enable | |
if( val === 'other' ) { | |
this.$inputText().prop('disabled', false); | |
// disable | |
} else { | |
this.$inputText().prop('disabled', true); | |
} | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'range', | |
events: { | |
'input input[type="range"]': 'onChange', | |
'change input': 'onChange' | |
}, | |
$input: function(){ | |
return this.$('input[type="range"]'); | |
}, | |
$inputAlt: function(){ | |
return this.$('input[type="number"]'); | |
}, | |
setValue: function( val ){ | |
this.busy = true; | |
// Update range input (with change). | |
acf.val( this.$input(), val ); | |
// Update alt input (without change). | |
// Read in input value to inherit min/max validation. | |
acf.val( this.$inputAlt(), this.$input().val(), true ); | |
this.busy = false; | |
}, | |
onChange: function( e, $el ){ | |
if( !this.busy ) { | |
this.setValue( $el.val() ); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'relationship', | |
events: { | |
'keypress [data-filter]': 'onKeypressFilter', | |
'change [data-filter]': 'onChangeFilter', | |
'keyup [data-filter]': 'onChangeFilter', | |
'click .choices-list .acf-rel-item': 'onClickAdd', | |
'click [data-name="remove_item"]': 'onClickRemove', | |
}, | |
$control: function(){ | |
return this.$('.acf-relationship'); | |
}, | |
$list: function( list ) { | |
return this.$('.' + list + '-list'); | |
}, | |
$listItems: function( list ) { | |
return this.$list( list ).find('.acf-rel-item'); | |
}, | |
$listItem: function( list, id ) { | |
return this.$list( list ).find('.acf-rel-item[data-id="' + id + '"]'); | |
}, | |
getValue: function(){ | |
var val = []; | |
this.$listItems('values').each(function(){ | |
val.push( $(this).data('id') ); | |
}); | |
return val.length ? val : false; | |
}, | |
newChoice: function( props ){ | |
return [ | |
'<li>', | |
'<span data-id="' + props.id + '" class="acf-rel-item">' + props.text + '</span>', | |
'</li>' | |
].join(''); | |
}, | |
newValue: function( props ){ | |
return [ | |
'<li>', | |
'<input type="hidden" name="' + this.getInputName() + '[]" value="' + props.id + '" />', | |
'<span data-id="' + props.id + '" class="acf-rel-item">' + props.text, | |
'<a href="#" class="acf-icon -minus small dark" data-name="remove_item"></a>', | |
'</span>', | |
'</li>' | |
].join(''); | |
}, | |
initialize: function(){ | |
// Delay initialization until "interacted with" or "in view". | |
var delayed = this.proxy(acf.once(function(){ | |
// Add sortable. | |
this.$list('values').sortable({ | |
items: 'li', | |
forceHelperSize: true, | |
forcePlaceholderSize: true, | |
scroll: true, | |
update: this.proxy(function(){ | |
this.$input().trigger('change'); | |
}) | |
}); | |
// Avoid browser remembering old scroll position and add event. | |
this.$list('choices').scrollTop(0).on('scroll', this.proxy(this.onScrollChoices)); | |
// Fetch choices. | |
this.fetch(); | |
})); | |
// Bind "interacted with". | |
this.$el.one( 'mouseover', delayed ); | |
this.$el.one( 'focus', 'input', delayed ); | |
// Bind "in view". | |
acf.onceInView( this.$el, delayed ); | |
}, | |
onScrollChoices: function(e){ | |
// bail early if no more results | |
if( this.get('loading') || !this.get('more') ) { | |
return; | |
} | |
// Scrolled to bottom | |
var $list = this.$list('choices'); | |
var scrollTop = Math.ceil( $list.scrollTop() ); | |
var scrollHeight = Math.ceil( $list[0].scrollHeight ); | |
var innerHeight = Math.ceil( $list.innerHeight() ); | |
var paged = this.get('paged') || 1; | |
if( (scrollTop + innerHeight) >= scrollHeight ) { | |
// update paged | |
this.set('paged', (paged+1)); | |
// fetch | |
this.fetch(); | |
} | |
}, | |
onKeypressFilter: function( e, $el ){ | |
// don't submit form | |
if( e.which == 13 ) { | |
e.preventDefault(); | |
} | |
}, | |
onChangeFilter: function( e, $el ){ | |
// vars | |
var val = $el.val(); | |
var filter = $el.data('filter'); | |
// Bail early if filter has not changed | |
if( this.get(filter) === val ) { | |
return; | |
} | |
// update attr | |
this.set(filter, val); | |
// reset paged | |
this.set('paged', 1); | |
// fetch | |
if( $el.is('select') ) { | |
this.fetch(); | |
// search must go through timeout | |
} else { | |
this.maybeFetch(); | |
} | |
}, | |
onClickAdd: function( e, $el ){ | |
// vars | |
var val = this.val(); | |
var max = parseInt( this.get('max') ); | |
// can be added? | |
if( $el.hasClass('disabled') ) { | |
return false; | |
} | |
// validate | |
if( max > 0 && val && val.length >= max ) { | |
// add notice | |
this.showNotice({ | |
text: acf.__('Maximum values reached ( {max} values )').replace('{max}', max), | |
type: 'warning' | |
}); | |
return false; | |
} | |
// disable | |
$el.addClass('disabled'); | |
// add | |
var html = this.newValue({ | |
id: $el.data('id'), | |
text: $el.html() | |
}); | |
this.$list('values').append( html ) | |
// trigger change | |
this.$input().trigger('change'); | |
}, | |
onClickRemove: function( e, $el ){ | |
// Prevent default here because generic handler wont be triggered. | |
e.preventDefault(); | |
// vars | |
var $span = $el.parent(); | |
var $li = $span.parent(); | |
var id = $span.data('id'); | |
// remove value | |
$li.remove(); | |
// show choice | |
this.$listItem('choices', id).removeClass('disabled'); | |
// trigger change | |
this.$input().trigger('change'); | |
}, | |
maybeFetch: function(){ | |
// vars | |
var timeout = this.get('timeout'); | |
// abort timeout | |
if( timeout ) { | |
clearTimeout( timeout ); | |
} | |
// fetch | |
timeout = this.setTimeout(this.fetch, 300); | |
this.set('timeout', timeout); | |
}, | |
getAjaxData: function(){ | |
// load data based on element attributes | |
var ajaxData = this.$control().data(); | |
for( var name in ajaxData ) { | |
ajaxData[ name ] = this.get( name ); | |
} | |
// extra | |
ajaxData.action = 'acf/fields/relationship/query'; | |
ajaxData.field_key = this.get('key'); | |
// Filter. | |
ajaxData = acf.applyFilters( 'relationship_ajax_data', ajaxData, this ); | |
// return | |
return ajaxData; | |
}, | |
fetch: function(){ | |
// abort XHR if this field is already loading AJAX data | |
var xhr = this.get('xhr'); | |
if( xhr ) { | |
xhr.abort(); | |
} | |
// add to this.o | |
var ajaxData = this.getAjaxData(); | |
// clear html if is new query | |
var $choiceslist = this.$list( 'choices' ); | |
if( ajaxData.paged == 1 ) { | |
$choiceslist.html(''); | |
} | |
// loading | |
var $loading = $('<li><i class="acf-loading"></i> ' + acf.__('Loading') + '</li>'); | |
$choiceslist.append($loading); | |
this.set('loading', true); | |
// callback | |
var onComplete = function(){ | |
this.set('loading', false); | |
$loading.remove(); | |
}; | |
var onSuccess = function( json ){ | |
// no results | |
if( !json || !json.results || !json.results.length ) { | |
// prevent pagination | |
this.set('more', false); | |
// add message | |
if( this.get('paged') == 1 ) { | |
this.$list('choices').append('<li>' + acf.__('No matches found') + '</li>'); | |
} | |
// return | |
return; | |
} | |
// set more (allows pagination scroll) | |
this.set('more', json.more ); | |
// get new results | |
var html = this.walkChoices(json.results); | |
var $html = $( html ); | |
// apply .disabled to left li's | |
var val = this.val(); | |
if( val && val.length ) { | |
val.map(function( id ){ | |
$html.find('.acf-rel-item[data-id="' + id + '"]').addClass('disabled'); | |
}); | |
} | |
// append | |
$choiceslist.append( $html ); | |
// merge together groups | |
var $prevLabel = false; | |
var $prevList = false; | |
$choiceslist.find('.acf-rel-label').each(function(){ | |
var $label = $(this); | |
var $list = $label.siblings('ul'); | |
if( $prevLabel && $prevLabel.text() == $label.text() ) { | |
$prevList.append( $list.children() ); | |
$(this).parent().remove(); | |
return; | |
} | |
// update vars | |
$prevLabel = $label; | |
$prevList = $list; | |
}); | |
}; | |
// get results | |
var xhr = $.ajax({ | |
url: acf.get('ajaxurl'), | |
dataType: 'json', | |
type: 'post', | |
data: acf.prepareForAjax(ajaxData), | |
context: this, | |
success: onSuccess, | |
complete: onComplete | |
}); | |
// set | |
this.set('xhr', xhr); | |
}, | |
walkChoices: function( data ){ | |
// walker | |
var walk = function( data ){ | |
// vars | |
var html = ''; | |
// is array | |
if( $.isArray(data) ) { | |
data.map(function(item){ | |
html += walk( item ); | |
}); | |
// is item | |
} else if( $.isPlainObject(data) ) { | |
// group | |
if( data.children !== undefined ) { | |
html += '<li><span class="acf-rel-label">' + data.text + '</span><ul class="acf-bl">'; | |
html += walk( data.children ); | |
html += '</ul></li>'; | |
// single | |
} else { | |
html += '<li><span class="acf-rel-item" data-id="' + data.id + '">' + data.text + '</span></li>'; | |
} | |
} | |
// return | |
return html; | |
}; | |
return walk( data ); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'select', | |
select2: false, | |
wait: 'load', | |
events: { | |
'removeField': 'onRemove' | |
}, | |
$input: function(){ | |
return this.$('select'); | |
}, | |
initialize: function(){ | |
// vars | |
var $select = this.$input(); | |
// inherit data | |
this.inherit( $select ); | |
// select2 | |
if( this.get('ui') ) { | |
// populate ajax_data (allowing custom attribute to already exist) | |
var ajaxAction = this.get('ajax_action'); | |
if( !ajaxAction ) { | |
ajaxAction = 'acf/fields/' + this.get('type') + '/query'; | |
} | |
// select2 | |
this.select2 = acf.newSelect2($select, { | |
field: this, | |
ajax: this.get('ajax'), | |
multiple: this.get('multiple'), | |
placeholder: this.get('placeholder'), | |
allowNull: this.get('allow_null'), | |
ajaxAction: ajaxAction, | |
}); | |
} | |
}, | |
onRemove: function(){ | |
if( this.select2 ) { | |
this.select2.destroy(); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
// vars | |
var CONTEXT = 'tab'; | |
var Field = acf.Field.extend({ | |
type: 'tab', | |
wait: '', | |
tabs: false, | |
tab: false, | |
findFields: function(){ | |
return this.$el.nextUntil('.acf-field-tab', '.acf-field'); | |
}, | |
getFields: function(){ | |
return acf.getFields( this.findFields() ); | |
}, | |
findTabs: function(){ | |
return this.$el.prevAll('.acf-tab-wrap:first'); | |
}, | |
findTab: function(){ | |
return this.$('.acf-tab-button'); | |
}, | |
initialize: function(){ | |
// bail early if is td | |
if( this.$el.is('td') ) { | |
this.events = {}; | |
return false; | |
} | |
// vars | |
var $tabs = this.findTabs(); | |
var $tab = this.findTab(); | |
var settings = acf.parseArgs($tab.data(), { | |
endpoint: false, | |
placement: '', | |
before: this.$el | |
}); | |
// create wrap | |
if( !$tabs.length || settings.endpoint ) { | |
this.tabs = new Tabs( settings ); | |
} else { | |
this.tabs = $tabs.data('acf'); | |
} | |
// add tab | |
this.tab = this.tabs.addTab($tab, this); | |
}, | |
isActive: function(){ | |
return this.tab.isActive(); | |
}, | |
showFields: function(){ | |
// show fields | |
this.getFields().map(function( field ){ | |
field.show( this.cid, CONTEXT ); | |
field.hiddenByTab = false; | |
}, this); | |
}, | |
hideFields: function(){ | |
// hide fields | |
this.getFields().map(function( field ){ | |
field.hide( this.cid, CONTEXT ); | |
field.hiddenByTab = this.tab; | |
}, this); | |
}, | |
show: function( lockKey ){ | |
// show field and store result | |
var visible = acf.Field.prototype.show.apply(this, arguments); | |
// check if now visible | |
if( visible ) { | |
// show tab | |
this.tab.show(); | |
// check active tabs | |
this.tabs.refresh(); | |
} | |
// return | |
return visible; | |
}, | |
hide: function( lockKey ){ | |
// hide field and store result | |
var hidden = acf.Field.prototype.hide.apply(this, arguments); | |
// check if now hidden | |
if( hidden ) { | |
// hide tab | |
this.tab.hide(); | |
// reset tabs if this was active | |
if( this.isActive() ) { | |
this.tabs.reset(); | |
} | |
} | |
// return | |
return hidden; | |
}, | |
enable: function( lockKey ){ | |
// enable fields | |
this.getFields().map(function( field ){ | |
field.enable( CONTEXT ); | |
}); | |
}, | |
disable: function( lockKey ){ | |
// disable fields | |
this.getFields().map(function( field ){ | |
field.disable( CONTEXT ); | |
}); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
/** | |
* tabs | |
* | |
* description | |
* | |
* @date 8/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var i = 0; | |
var Tabs = acf.Model.extend({ | |
tabs: [], | |
active: false, | |
actions: { | |
'refresh': 'onRefresh' | |
}, | |
data: { | |
before: false, | |
placement: 'top', | |
index: 0, | |
initialized: false, | |
}, | |
setup: function( settings ){ | |
// data | |
$.extend(this.data, settings); | |
// define this prop to avoid scope issues | |
this.tabs = []; | |
this.active = false; | |
// vars | |
var placement = this.get('placement'); | |
var $before = this.get('before'); | |
var $parent = $before.parent(); | |
// add sidebar for left placement | |
if( placement == 'left' && $parent.hasClass('acf-fields') ) { | |
$parent.addClass('-sidebar'); | |
} | |
// create wrap | |
if( $before.is('tr') ) { | |
this.$el = $('<tr class="acf-tab-wrap"><td colspan="2"><ul class="acf-hl acf-tab-group"></ul></td></tr>'); | |
} else { | |
this.$el = $('<div class="acf-tab-wrap -' + placement + '"><ul class="acf-hl acf-tab-group"></ul></div>'); | |
} | |
// append | |
$before.before( this.$el ); | |
// set index | |
this.set('index', i, true); | |
i++; | |
}, | |
initializeTabs: function(){ | |
// find first visible tab | |
var tab = this.getVisible().shift(); | |
// remember previous tab state | |
var order = acf.getPreference('this.tabs') || []; | |
var groupIndex = this.get('index'); | |
var tabIndex = order[ groupIndex ]; | |
if( this.tabs[ tabIndex ] && this.tabs[ tabIndex ].isVisible() ) { | |
tab = this.tabs[ tabIndex ]; | |
} | |
// select | |
if( tab ) { | |
this.selectTab( tab ); | |
} else { | |
this.closeTabs(); | |
} | |
// set local variable used by tabsManager | |
this.set('initialized', true); | |
}, | |
getVisible: function(){ | |
return this.tabs.filter(function( tab ){ | |
return tab.isVisible(); | |
}); | |
}, | |
getActive: function(){ | |
return this.active; | |
}, | |
setActive: function( tab ){ | |
return this.active = tab; | |
}, | |
hasActive: function(){ | |
return (this.active !== false); | |
}, | |
isActive: function( tab ){ | |
var active = this.getActive(); | |
return (active && active.cid === tab.cid); | |
}, | |
closeActive: function(){ | |
if( this.hasActive() ) { | |
this.closeTab( this.getActive() ); | |
} | |
}, | |
openTab: function( tab ){ | |
// close existing tab | |
this.closeActive(); | |
// open | |
tab.open(); | |
// set active | |
this.setActive( tab ); | |
}, | |
closeTab: function( tab ){ | |
// close | |
tab.close(); | |
// set active | |
this.setActive( false ); | |
}, | |
closeTabs: function(){ | |
this.tabs.map( this.closeTab, this ); | |
}, | |
selectTab: function( tab ){ | |
// close other tabs | |
this.tabs.map(function( t ){ | |
if( tab.cid !== t.cid ) { | |
this.closeTab( t ); | |
} | |
}, this); | |
// open | |
this.openTab( tab ); | |
}, | |
addTab: function( $a, field ){ | |
// create <li> | |
var $li = $('<li></li>'); | |
// append <a> | |
$li.append( $a ); | |
// append | |
this.$('ul').append( $li ); | |
// initialize | |
var tab = new Tab({ | |
$el: $li, | |
field: field, | |
group: this, | |
}); | |
// store | |
this.tabs.push( tab ); | |
// return | |
return tab; | |
}, | |
reset: function(){ | |
// close existing tab | |
this.closeActive(); | |
// find and active a tab | |
return this.refresh(); | |
}, | |
refresh: function(){ | |
// bail early if active already exists | |
if( this.hasActive() ) { | |
return false; | |
} | |
// find next active tab | |
var tab = this.getVisible().shift(); | |
// open tab | |
if( tab ) { | |
this.openTab( tab ); | |
} | |
// return | |
return tab; | |
}, | |
onRefresh: function(){ | |
// only for left placements | |
if( this.get('placement') !== 'left' ) { | |
return; | |
} | |
// vars | |
var $parent = this.$el.parent(); | |
var $list = this.$el.children('ul'); | |
var attribute = $parent.is('td') ? 'height' : 'min-height'; | |
// find height (minus 1 for border-bottom) | |
var height = $list.position().top + $list.outerHeight(true) - 1; | |
// add css | |
$parent.css(attribute, height); | |
} | |
}); | |
var Tab = acf.Model.extend({ | |
group: false, | |
field: false, | |
events: { | |
'click a': 'onClick' | |
}, | |
index: function(){ | |
return this.$el.index(); | |
}, | |
isVisible: function(){ | |
return acf.isVisible( this.$el ); | |
}, | |
isActive: function(){ | |
return this.$el.hasClass('active'); | |
}, | |
open: function(){ | |
// add class | |
this.$el.addClass('active'); | |
// show field | |
this.field.showFields(); | |
}, | |
close: function(){ | |
// remove class | |
this.$el.removeClass('active'); | |
// hide field | |
this.field.hideFields(); | |
}, | |
onClick: function( e, $el ){ | |
// prevent default | |
e.preventDefault(); | |
// toggle | |
this.toggle(); | |
}, | |
toggle: function(){ | |
// bail early if already active | |
if( this.isActive() ) { | |
return; | |
} | |
// toggle this tab | |
this.group.openTab( this ); | |
} | |
}); | |
var tabsManager = new acf.Model({ | |
priority: 50, | |
actions: { | |
'prepare': 'render', | |
'append': 'render', | |
'unload': 'onUnload', | |
'invalid_field': 'onInvalidField' | |
}, | |
findTabs: function(){ | |
return $('.acf-tab-wrap'); | |
}, | |
getTabs: function(){ | |
return acf.getInstances( this.findTabs() ); | |
}, | |
render: function( $el ){ | |
this.getTabs().map(function( tabs ){ | |
if( !tabs.get('initialized') ) { | |
tabs.initializeTabs(); | |
} | |
}); | |
}, | |
onInvalidField: function( field ){ | |
// bail early if busy | |
if( this.busy ) { | |
return; | |
} | |
// ignore if not hidden by tab | |
if( !field.hiddenByTab ) { | |
return; | |
} | |
// toggle tab | |
field.hiddenByTab.toggle(); | |
// ignore other invalid fields | |
this.busy = true; | |
this.setTimeout(function(){ | |
this.busy = false; | |
}, 100); | |
}, | |
onUnload: function(){ | |
// vars | |
var order = []; | |
// loop | |
this.getTabs().map(function( group ){ | |
var active = group.hasActive() ? group.getActive().index() : 0; | |
order.push(active); | |
}); | |
// bail if no tabs | |
if( !order.length ) { | |
return; | |
} | |
// update | |
acf.setPreference('this.tabs', order); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.SelectField.extend({ | |
type: 'post_object', | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.SelectField.extend({ | |
type: 'page_link', | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.SelectField.extend({ | |
type: 'user', | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'taxonomy', | |
data: { | |
'ftype': 'select' | |
}, | |
select2: false, | |
wait: 'load', | |
events: { | |
'click a[data-name="add"]': 'onClickAdd', | |
'click input[type="radio"]': 'onClickRadio', | |
}, | |
$control: function(){ | |
return this.$('.acf-taxonomy-field'); | |
}, | |
$input: function(){ | |
return this.getRelatedPrototype().$input.apply(this, arguments); | |
}, | |
getRelatedType: function(){ | |
// vars | |
var fieldType = this.get('ftype'); | |
// normalize | |
if( fieldType == 'multi_select' ) { | |
fieldType = 'select'; | |
} | |
// return | |
return fieldType; | |
}, | |
getRelatedPrototype: function(){ | |
return acf.getFieldType( this.getRelatedType() ).prototype; | |
}, | |
getValue: function(){ | |
return this.getRelatedPrototype().getValue.apply(this, arguments); | |
}, | |
setValue: function(){ | |
return this.getRelatedPrototype().setValue.apply(this, arguments); | |
}, | |
initialize: function(){ | |
this.getRelatedPrototype().initialize.apply(this, arguments); | |
}, | |
onRemove: function(){ | |
if( this.select2 ) { | |
this.select2.destroy(); | |
} | |
}, | |
onClickAdd: function( e, $el ){ | |
// vars | |
var field = this; | |
var popup = false; | |
var $form = false; | |
var $name = false; | |
var $parent = false; | |
var $button = false; | |
var $message = false; | |
var notice = false; | |
// step 1. | |
var step1 = function(){ | |
// popup | |
popup = acf.newPopup({ | |
title: $el.attr('title'), | |
loading: true, | |
width: '300px' | |
}); | |
// ajax | |
var ajaxData = { | |
action: 'acf/fields/taxonomy/add_term', | |
field_key: field.get('key') | |
}; | |
// get HTML | |
$.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax(ajaxData), | |
type: 'post', | |
dataType: 'html', | |
success: step2 | |
}); | |
}; | |
// step 2. | |
var step2 = function( html ){ | |
// update popup | |
popup.loading(false); | |
popup.content(html); | |
// vars | |
$form = popup.$('form'); | |
$name = popup.$('input[name="term_name"]'); | |
$parent = popup.$('select[name="term_parent"]'); | |
$button = popup.$('.acf-submit-button'); | |
// focus | |
$name.focus(); | |
// submit form | |
popup.on('submit', 'form', step3); | |
}; | |
// step 3. | |
var step3 = function( e, $el ){ | |
// prevent | |
e.preventDefault(); | |
e.stopImmediatePropagation(); | |
// basic validation | |
if( $name.val() === '' ) { | |
$name.focus(); | |
return false; | |
} | |
// disable | |
acf.startButtonLoading( $button ); | |
// ajax | |
var ajaxData = { | |
action: 'acf/fields/taxonomy/add_term', | |
field_key: field.get('key'), | |
term_name: $name.val(), | |
term_parent: $parent.length ? $parent.val() : 0 | |
}; | |
$.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax(ajaxData), | |
type: 'post', | |
dataType: 'json', | |
success: step4 | |
}); | |
}; | |
// step 4. | |
var step4 = function( json ){ | |
// enable | |
acf.stopButtonLoading( $button ); | |
// remove prev notice | |
if( notice ) { | |
notice.remove(); | |
} | |
// success | |
if( acf.isAjaxSuccess(json) ) { | |
// clear name | |
$name.val(''); | |
// update term lists | |
step5( json.data ); | |
// notice | |
notice = acf.newNotice({ | |
type: 'success', | |
text: acf.getAjaxMessage(json), | |
target: $form, | |
timeout: 2000, | |
dismiss: false | |
}); | |
} else { | |
// notice | |
notice = acf.newNotice({ | |
type: 'error', | |
text: acf.getAjaxError(json), | |
target: $form, | |
timeout: 2000, | |
dismiss: false | |
}); | |
} | |
// focus | |
$name.focus(); | |
}; | |
// step 5. | |
var step5 = function( term ){ | |
// update parent dropdown | |
var $option = $('<option value="' + term.term_id + '">' + term.term_label + '</option>'); | |
if( term.term_parent ) { | |
$parent.children('option[value="' + term.term_parent + '"]').after( $option ); | |
} else { | |
$parent.append( $option ); | |
} | |
// add this new term to all taxonomy field | |
var fields = acf.getFields({ | |
type: 'taxonomy' | |
}); | |
fields.map(function( otherField ){ | |
if( otherField.get('taxonomy') == field.get('taxonomy') ) { | |
otherField.appendTerm( term ); | |
} | |
}); | |
// select | |
field.selectTerm( term.term_id ); | |
}; | |
// run | |
step1(); | |
}, | |
appendTerm: function( term ){ | |
if( this.getRelatedType() == 'select' ) { | |
this.appendTermSelect( term ); | |
} else { | |
this.appendTermCheckbox( term ); | |
} | |
}, | |
appendTermSelect: function( term ){ | |
this.select2.addOption({ | |
id: term.term_id, | |
text: term.term_label | |
}); | |
}, | |
appendTermCheckbox: function( term ){ | |
// vars | |
var name = this.$('[name]:first').attr('name'); | |
var $ul = this.$('ul:first'); | |
// allow multiple selection | |
if( this.getRelatedType() == 'checkbox' ) { | |
name += '[]'; | |
} | |
// create new li | |
var $li = $([ | |
'<li data-id="' + term.term_id + '">', | |
'<label>', | |
'<input type="' + this.get('ftype') + '" value="' + term.term_id + '" name="' + name + '" /> ', | |
'<span>' + term.term_name + '</span>', | |
'</label>', | |
'</li>' | |
].join('')); | |
// find parent | |
if( term.term_parent ) { | |
// vars | |
var $parent = $ul.find('li[data-id="' + term.term_parent + '"]'); | |
// update vars | |
$ul = $parent.children('ul'); | |
// create ul | |
if( !$ul.exists() ) { | |
$ul = $('<ul class="children acf-bl"></ul>'); | |
$parent.append( $ul ); | |
} | |
} | |
// append | |
$ul.append( $li ); | |
}, | |
selectTerm: function( id ){ | |
if( this.getRelatedType() == 'select' ) { | |
this.select2.selectOption( id ); | |
} else { | |
var $input = this.$('input[value="' + id + '"]'); | |
$input.prop('checked', true).trigger('change'); | |
} | |
}, | |
onClickRadio: function( e, $el ){ | |
// vars | |
var $label = $el.parent('label'); | |
var selected = $label.hasClass('selected'); | |
// remove previous selected | |
this.$('.selected').removeClass('selected'); | |
// add active class | |
$label.addClass('selected'); | |
// allow null | |
if( this.get('allow_null') && selected ) { | |
$label.removeClass('selected'); | |
$el.prop('checked', false).trigger('change'); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.models.DatePickerField.extend({ | |
type: 'time_picker', | |
$control: function(){ | |
return this.$('.acf-time-picker'); | |
}, | |
initialize: function(){ | |
// vars | |
var $input = this.$input(); | |
var $inputText = this.$inputText(); | |
// args | |
var args = { | |
timeFormat: this.get('time_format'), | |
altField: $input, | |
altFieldTimeOnly: false, | |
altTimeFormat: 'HH:mm:ss', | |
showButtonPanel: true, | |
controlType: 'select', | |
oneLine: true, | |
closeText: acf.get('dateTimePickerL10n').selectText, | |
timeOnly: true, | |
}; | |
// add custom 'Close = Select' functionality | |
args.onClose = function( value, dp_instance, t_instance ){ | |
// vars | |
var $close = dp_instance.dpDiv.find('.ui-datepicker-close'); | |
// if clicking close button | |
if( !value && $close.is(':hover') ) { | |
t_instance._updateDateTime(); | |
} | |
}; | |
// filter | |
args = acf.applyFilters('time_picker_args', args, this); | |
// add date time picker | |
acf.newTimePicker( $inputText, args ); | |
// action | |
acf.doAction('time_picker_init', $inputText, args, this); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
// add | |
acf.newTimePicker = function( $input, args ){ | |
// bail ealry if no datepicker library | |
if( typeof $.timepicker === 'undefined' ) { | |
return false; | |
} | |
// defaults | |
args = args || {}; | |
// initialize | |
$input.timepicker( args ); | |
// wrap the datepicker (only if it hasn't already been wrapped) | |
if( $('body > #ui-datepicker-div').exists() ) { | |
$('body > #ui-datepicker-div').wrap('<div class="acf-ui-datepicker" />'); | |
} | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'true_false', | |
events: { | |
'change .acf-switch-input': 'onChange', | |
'focus .acf-switch-input': 'onFocus', | |
'blur .acf-switch-input': 'onBlur', | |
'keypress .acf-switch-input': 'onKeypress' | |
}, | |
$input: function(){ | |
return this.$('input[type="checkbox"]'); | |
}, | |
$switch: function(){ | |
return this.$('.acf-switch'); | |
}, | |
getValue: function(){ | |
return this.$input().prop('checked') ? 1 : 0; | |
}, | |
initialize: function(){ | |
this.render(); | |
}, | |
render: function(){ | |
// vars | |
var $switch = this.$switch(); | |
// bail ealry if no $switch | |
if( !$switch.length ) return; | |
// vars | |
var $on = $switch.children('.acf-switch-on'); | |
var $off = $switch.children('.acf-switch-off'); | |
var width = Math.max( $on.width(), $off.width() ); | |
// bail ealry if no width | |
if( !width ) return; | |
// set widths | |
$on.css( 'min-width', width ); | |
$off.css( 'min-width', width ); | |
}, | |
switchOn: function() { | |
this.$input().prop('checked', true); | |
this.$switch().addClass('-on'); | |
}, | |
switchOff: function() { | |
this.$input().prop('checked', false); | |
this.$switch().removeClass('-on'); | |
}, | |
onChange: function( e, $el ){ | |
if( $el.prop('checked') ) { | |
this.switchOn(); | |
} else { | |
this.switchOff(); | |
} | |
}, | |
onFocus: function( e, $el ){ | |
this.$switch().addClass('-focus'); | |
}, | |
onBlur: function( e, $el ){ | |
this.$switch().removeClass('-focus'); | |
}, | |
onKeypress: function( e, $el ){ | |
// left | |
if( e.keyCode === 37 ) { | |
return this.switchOff(); | |
} | |
// right | |
if( e.keyCode === 39 ) { | |
return this.switchOn(); | |
} | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'url', | |
events: { | |
'keyup input[type="url"]': 'onkeyup' | |
}, | |
$control: function(){ | |
return this.$('.acf-input-wrap'); | |
}, | |
$input: function(){ | |
return this.$('input[type="url"]'); | |
}, | |
initialize: function(){ | |
this.render(); | |
}, | |
isValid: function(){ | |
// vars | |
var val = this.val(); | |
// bail early if no val | |
if( !val ) { | |
return false; | |
} | |
// url | |
if( val.indexOf('://') !== -1 ) { | |
return true; | |
} | |
// protocol relative url | |
if( val.indexOf('//') === 0 ) { | |
return true; | |
} | |
// return | |
return false; | |
}, | |
render: function(){ | |
// add class | |
if( this.isValid() ) { | |
this.$control().addClass('-valid'); | |
} else { | |
this.$control().removeClass('-valid'); | |
} | |
}, | |
onkeyup: function( e, $el ){ | |
this.render(); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
var Field = acf.Field.extend({ | |
type: 'wysiwyg', | |
wait: 'load', | |
events: { | |
'mousedown .acf-editor-wrap.delay': 'onMousedown', | |
'unmountField': 'disableEditor', | |
'remountField': 'enableEditor', | |
'removeField': 'disableEditor' | |
}, | |
$control: function(){ | |
return this.$('.acf-editor-wrap'); | |
}, | |
$input: function(){ | |
return this.$('textarea'); | |
}, | |
getMode: function(){ | |
return this.$control().hasClass('tmce-active') ? 'visual' : 'text'; | |
}, | |
initialize: function(){ | |
// initializeEditor if no delay | |
if( !this.$control().hasClass('delay') ) { | |
this.initializeEditor(); | |
} | |
}, | |
initializeEditor: function(){ | |
// vars | |
var $wrap = this.$control(); | |
var $textarea = this.$input(); | |
var args = { | |
tinymce: true, | |
quicktags: true, | |
toolbar: this.get('toolbar'), | |
mode: this.getMode(), | |
field: this | |
}; | |
// generate new id | |
var oldId = $textarea.attr('id'); | |
var newId = acf.uniqueId('acf-editor-'); | |
// store copy of textarea data | |
var data = $textarea.data(); | |
// rename | |
acf.rename({ | |
target: $wrap, | |
search: oldId, | |
replace: newId, | |
destructive: true | |
}); | |
// update id | |
this.set('id', newId, true); | |
// initialize | |
acf.tinymce.initialize( newId, args ); | |
// apply data to new textarea (acf.rename creates a new textarea element due to destructive mode) | |
// fixes bug where conditional logic "disabled" is lost during "screen_check" | |
this.$input().data(data); | |
}, | |
onMousedown: function( e ){ | |
// prevent default | |
e.preventDefault(); | |
// remove delay class | |
var $wrap = this.$control(); | |
$wrap.removeClass('delay'); | |
$wrap.find('.acf-editor-toolbar').remove(); | |
// initialize | |
this.initializeEditor(); | |
}, | |
enableEditor: function(){ | |
if( this.getMode() == 'visual' ) { | |
acf.tinymce.enable( this.get('id') ); | |
} | |
}, | |
disableEditor: function(){ | |
acf.tinymce.destroy( this.get('id') ); | |
} | |
}); | |
acf.registerFieldType( Field ); | |
})(jQuery); | |
(function($, undefined){ | |
// vars | |
var storage = []; | |
/** | |
* acf.Condition | |
* | |
* description | |
* | |
* @date 23/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.Condition = acf.Model.extend({ | |
type: '', // used for model name | |
operator: '==', // rule operator | |
label: '', // label shown when editing fields | |
choiceType: 'input', // input, select | |
fieldTypes: [], // auto connect this conditions with these field types | |
data: { | |
conditions: false, // the parent instance | |
field: false, // the field which we query against | |
rule: {} // the rule [field, operator, value] | |
}, | |
events: { | |
'change': 'change', | |
'keyup': 'change', | |
'enableField': 'change', | |
'disableField': 'change' | |
}, | |
setup: function( props ){ | |
$.extend(this.data, props); | |
}, | |
getEventTarget: function( $el, event ){ | |
return $el || this.get('field').$el; | |
}, | |
change: function( e, $el ){ | |
this.get('conditions').change( e ); | |
}, | |
match: function( rule, field ){ | |
return false; | |
}, | |
calculate: function(){ | |
return this.match( this.get('rule'), this.get('field') ); | |
}, | |
choices: function( field ){ | |
return '<input type="text" />'; | |
} | |
}); | |
/** | |
* acf.newCondition | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.newCondition = function( rule, conditions ){ | |
// currently setting up conditions for fieldX, this field is the 'target' | |
var target = conditions.get('field'); | |
// use the 'target' to find the 'trigger' field. | |
// - this field is used to setup the conditional logic events | |
var field = target.getField( rule.field ); | |
// bail ealry if no target or no field (possible if field doesn't exist due to HTML error) | |
if( !target || !field ) { | |
return false; | |
} | |
// vars | |
var args = { | |
rule: rule, | |
target: target, | |
conditions: conditions, | |
field: field | |
}; | |
// vars | |
var fieldType = field.get('type'); | |
var operator = rule.operator; | |
// get avaibale conditions | |
var conditionTypes = acf.getConditionTypes({ | |
fieldType: fieldType, | |
operator: operator, | |
}); | |
// instantiate | |
var model = conditionTypes[0] || acf.Condition; | |
// instantiate | |
var condition = new model( args ); | |
// return | |
return condition; | |
}; | |
/** | |
* mid | |
* | |
* Calculates the model ID for a field type | |
* | |
* @date 15/12/17 | |
* @since 5.6.5 | |
* | |
* @param string type | |
* @return string | |
*/ | |
var modelId = function( type ) { | |
return acf.strPascalCase( type || '' ) + 'Condition'; | |
}; | |
/** | |
* acf.registerConditionType | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.registerConditionType = function( model ){ | |
// vars | |
var proto = model.prototype; | |
var type = proto.type; | |
var mid = modelId( type ); | |
// store model | |
acf.models[ mid ] = model; | |
// store reference | |
storage.push( type ); | |
}; | |
/** | |
* acf.getConditionType | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getConditionType = function( type ){ | |
var mid = modelId( type ); | |
return acf.models[ mid ] || false; | |
} | |
/** | |
* acf.registerConditionForFieldType | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.registerConditionForFieldType = function( conditionType, fieldType ){ | |
// get model | |
var model = acf.getConditionType( conditionType ); | |
// append | |
if( model ) { | |
model.prototype.fieldTypes.push( fieldType ); | |
} | |
}; | |
/** | |
* acf.getConditionTypes | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getConditionTypes = function( args ){ | |
// defaults | |
args = acf.parseArgs(args, { | |
fieldType: '', | |
operator: '' | |
}); | |
// clonse available types | |
var types = []; | |
// loop | |
storage.map(function( type ){ | |
// vars | |
var model = acf.getConditionType(type); | |
var ProtoFieldTypes = model.prototype.fieldTypes; | |
var ProtoOperator = model.prototype.operator; | |
// check fieldType | |
if( args.fieldType && ProtoFieldTypes.indexOf( args.fieldType ) === -1 ) { | |
return; | |
} | |
// check operator | |
if( args.operator && ProtoOperator !== args.operator ) { | |
return; | |
} | |
// append | |
types.push( model ); | |
}); | |
// return | |
return types; | |
}; | |
})(jQuery); | |
(function($, undefined){ | |
// vars | |
var CONTEXT = 'conditional_logic'; | |
/** | |
* conditionsManager | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var conditionsManager = new acf.Model({ | |
id: 'conditionsManager', | |
priority: 20, // run actions later | |
actions: { | |
'new_field': 'onNewField', | |
}, | |
onNewField: function( field ){ | |
if( field.has('conditions') ) { | |
field.getConditions().render(); | |
} | |
}, | |
}); | |
/** | |
* acf.Field.prototype.getField | |
* | |
* Finds a field that is related to another field | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var getSiblingField = function( field, key ){ | |
// find sibling (very fast) | |
var fields = acf.getFields({ | |
key: key, | |
sibling: field.$el, | |
suppressFilters: true, | |
}); | |
// find sibling-children (fast) | |
// needed for group fields, accordions, etc | |
if( !fields.length ) { | |
fields = acf.getFields({ | |
key: key, | |
parent: field.$el.parent(), | |
suppressFilters: true, | |
}); | |
} | |
// return | |
if( fields.length ) { | |
return fields[0]; | |
} | |
return false; | |
}; | |
acf.Field.prototype.getField = function( key ){ | |
// get sibling field | |
var field = getSiblingField( this, key ); | |
// return early | |
if( field ) { | |
return field; | |
} | |
// move up through each parent and try again | |
var parents = this.parents(); | |
for( var i = 0; i < parents.length; i++ ) { | |
// get sibling field | |
field = getSiblingField( parents[i], key ); | |
// return early | |
if( field ) { | |
return field; | |
} | |
} | |
// return | |
return false; | |
}; | |
/** | |
* acf.Field.prototype.getConditions | |
* | |
* Returns the field's conditions instance | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.Field.prototype.getConditions = function(){ | |
// instantiate | |
if( !this.conditions ) { | |
this.conditions = new Conditions( this ); | |
} | |
// return | |
return this.conditions; | |
}; | |
/** | |
* Conditions | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var timeout = false; | |
var Conditions = acf.Model.extend({ | |
id: 'Conditions', | |
data: { | |
field: false, // The field with "data-conditions" (target). | |
timeStamp: false, // Reference used during "change" event. | |
groups: [], // The groups of condition instances. | |
}, | |
setup: function( field ){ | |
// data | |
this.data.field = field; | |
// vars | |
var conditions = field.get('conditions'); | |
// detect groups | |
if( conditions instanceof Array ) { | |
// detect groups | |
if( conditions[0] instanceof Array ) { | |
// loop | |
conditions.map(function(rules, i){ | |
this.addRules( rules, i ); | |
}, this); | |
// detect rules | |
} else { | |
this.addRules( conditions ); | |
} | |
// detect rule | |
} else { | |
this.addRule( conditions ); | |
} | |
}, | |
change: function( e ){ | |
// this function may be triggered multiple times per event due to multiple condition classes | |
// compare timestamp to allow only 1 trigger per event | |
if( this.get('timeStamp') === e.timeStamp ) { | |
return false; | |
} else { | |
this.set('timeStamp', e.timeStamp, true); | |
} | |
// render condition and store result | |
var changed = this.render(); | |
}, | |
render: function(){ | |
return this.calculate() ? this.show() : this.hide(); | |
}, | |
show: function(){ | |
return this.get('field').showEnable(this.cid, CONTEXT); | |
}, | |
hide: function(){ | |
return this.get('field').hideDisable(this.cid, CONTEXT); | |
}, | |
calculate: function(){ | |
// vars | |
var pass = false; | |
// loop | |
this.getGroups().map(function( group ){ | |
// igrnore this group if another group passed | |
if( pass ) return; | |
// find passed | |
var passed = group.filter(function(condition){ | |
return condition.calculate(); | |
}); | |
// if all conditions passed, update the global var | |
if( passed.length == group.length ) { | |
pass = true; | |
} | |
}); | |
return pass; | |
}, | |
hasGroups: function(){ | |
return this.data.groups != null; | |
}, | |
getGroups: function(){ | |
return this.data.groups; | |
}, | |
addGroup: function(){ | |
var group = []; | |
this.data.groups.push( group ); | |
return group; | |
}, | |
hasGroup: function( i ){ | |
return this.data.groups[i] != null; | |
}, | |
getGroup: function( i ){ | |
return this.data.groups[i]; | |
}, | |
removeGroup: function( i ){ | |
this.data.groups[i].delete; | |
return this; | |
}, | |
addRules: function( rules, group ){ | |
rules.map(function( rule ){ | |
this.addRule( rule, group ); | |
}, this); | |
}, | |
addRule: function( rule, group ){ | |
// defaults | |
group = group || 0; | |
// vars | |
var groupArray; | |
// get group | |
if( this.hasGroup(group) ) { | |
groupArray = this.getGroup(group); | |
} else { | |
groupArray = this.addGroup(); | |
} | |
// instantiate | |
var condition = acf.newCondition( rule, this ); | |
// bail ealry if condition failed (field did not exist) | |
if( !condition ) { | |
return false; | |
} | |
// add rule | |
groupArray.push(condition); | |
}, | |
hasRule: function(){ | |
}, | |
getRule: function( rule, group ){ | |
// defaults | |
rule = rule || 0; | |
group = group || 0; | |
return this.data.groups[ group ][ rule ]; | |
}, | |
removeRule: function(){ | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
var __ = acf.__; | |
var parseString = function( val ){ | |
return val ? '' + val : ''; | |
}; | |
var isEqualTo = function( v1, v2 ){ | |
return ( parseString(v1).toLowerCase() === parseString(v2).toLowerCase() ); | |
}; | |
var isEqualToNumber = function( v1, v2 ){ | |
return ( parseFloat(v1) === parseFloat(v2) ); | |
}; | |
var isGreaterThan = function( v1, v2 ){ | |
return ( parseFloat(v1) > parseFloat(v2) ); | |
}; | |
var isLessThan = function( v1, v2 ){ | |
return ( parseFloat(v1) < parseFloat(v2) ); | |
}; | |
var inArray = function( v1, array ){ | |
// cast all values as string | |
array = array.map(function(v2){ | |
return parseString(v2); | |
}); | |
return (array.indexOf( v1 ) > -1); | |
} | |
var containsString = function( haystack, needle ){ | |
return ( parseString(haystack).indexOf( parseString(needle) ) > -1 ); | |
}; | |
var matchesPattern = function( v1, pattern ){ | |
var regexp = new RegExp(parseString(pattern), 'gi'); | |
return parseString(v1).match( regexp ); | |
}; | |
/** | |
* hasValue | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var HasValue = acf.Condition.extend({ | |
type: 'hasValue', | |
operator: '!=empty', | |
label: __('Has any value'), | |
fieldTypes: [ 'text', 'textarea', 'number', 'range', 'email', 'url', 'password', 'image', 'file', 'wysiwyg', 'oembed', 'select', 'checkbox', 'radio', 'button_group', 'link', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user', 'google_map', 'date_picker', 'date_time_picker', 'time_picker', 'color_picker' ], | |
match: function( rule, field ){ | |
return (field.val() ? true : false); | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="text" disabled="" />'; | |
} | |
}); | |
acf.registerConditionType( HasValue ); | |
/** | |
* hasValue | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var HasNoValue = HasValue.extend({ | |
type: 'hasNoValue', | |
operator: '==empty', | |
label: __('Has no value'), | |
match: function( rule, field ){ | |
return !HasValue.prototype.match.apply(this, arguments); | |
} | |
}); | |
acf.registerConditionType( HasNoValue ); | |
/** | |
* EqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var EqualTo = acf.Condition.extend({ | |
type: 'equalTo', | |
operator: '==', | |
label: __('Value is equal to'), | |
fieldTypes: [ 'text', 'textarea', 'number', 'range', 'email', 'url', 'password' ], | |
match: function( rule, field ){ | |
if( $.isNumeric(rule.value) ) { | |
return isEqualToNumber( rule.value, field.val() ); | |
} else { | |
return isEqualTo( rule.value, field.val() ); | |
} | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="text" />'; | |
} | |
}); | |
acf.registerConditionType( EqualTo ); | |
/** | |
* NotEqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var NotEqualTo = EqualTo.extend({ | |
type: 'notEqualTo', | |
operator: '!=', | |
label: __('Value is not equal to'), | |
match: function( rule, field ){ | |
return !EqualTo.prototype.match.apply(this, arguments); | |
} | |
}); | |
acf.registerConditionType( NotEqualTo ); | |
/** | |
* PatternMatch | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var PatternMatch = acf.Condition.extend({ | |
type: 'patternMatch', | |
operator: '==pattern', | |
label: __('Value matches pattern'), | |
fieldTypes: [ 'text', 'textarea', 'email', 'url', 'password', 'wysiwyg' ], | |
match: function( rule, field ){ | |
return matchesPattern( field.val(), rule.value ); | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="text" placeholder="[a-z0-9]" />'; | |
} | |
}); | |
acf.registerConditionType( PatternMatch ); | |
/** | |
* Contains | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var Contains = acf.Condition.extend({ | |
type: 'contains', | |
operator: '==contains', | |
label: __('Value contains'), | |
fieldTypes: [ 'text', 'textarea', 'number', 'email', 'url', 'password', 'wysiwyg', 'oembed', 'select' ], | |
match: function( rule, field ){ | |
return containsString( field.val(), rule.value ); | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="text" />'; | |
} | |
}); | |
acf.registerConditionType( Contains ); | |
/** | |
* TrueFalseEqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var TrueFalseEqualTo = EqualTo.extend({ | |
type: 'trueFalseEqualTo', | |
choiceType: 'select', | |
fieldTypes: [ 'true_false' ], | |
choices: function( field ){ | |
return [ | |
{ | |
id: 1, | |
text: __('Checked') | |
} | |
]; | |
}, | |
}); | |
acf.registerConditionType( TrueFalseEqualTo ); | |
/** | |
* TrueFalseNotEqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var TrueFalseNotEqualTo = NotEqualTo.extend({ | |
type: 'trueFalseNotEqualTo', | |
choiceType: 'select', | |
fieldTypes: [ 'true_false' ], | |
choices: function( field ){ | |
return [ | |
{ | |
id: 1, | |
text: __('Checked') | |
} | |
]; | |
}, | |
}); | |
acf.registerConditionType( TrueFalseNotEqualTo ); | |
/** | |
* SelectEqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var SelectEqualTo = acf.Condition.extend({ | |
type: 'selectEqualTo', | |
operator: '==', | |
label: __('Value is equal to'), | |
fieldTypes: [ 'select', 'checkbox', 'radio', 'button_group' ], | |
match: function( rule, field ){ | |
var val = field.val(); | |
if( val instanceof Array ) { | |
return inArray( rule.value, val ); | |
} else { | |
return isEqualTo( rule.value, val ); | |
} | |
}, | |
choices: function( fieldObject ){ | |
// vars | |
var choices = []; | |
var lines = fieldObject.$setting('choices textarea').val().split("\n"); | |
// allow null | |
if( fieldObject.$input('allow_null').prop('checked') ) { | |
choices.push({ | |
id: '', | |
text: __('Null') | |
}); | |
} | |
// loop | |
lines.map(function( line ){ | |
// split | |
line = line.split(':'); | |
// default label to value | |
line[1] = line[1] || line[0]; | |
// append | |
choices.push({ | |
id: $.trim( line[0] ), | |
text: $.trim( line[1] ) | |
}); | |
}); | |
// return | |
return choices; | |
}, | |
}); | |
acf.registerConditionType( SelectEqualTo ); | |
/** | |
* SelectNotEqualTo | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var SelectNotEqualTo = SelectEqualTo.extend({ | |
type: 'selectNotEqualTo', | |
operator: '!=', | |
label: __('Value is not equal to'), | |
match: function( rule, field ){ | |
return !SelectEqualTo.prototype.match.apply(this, arguments); | |
} | |
}); | |
acf.registerConditionType( SelectNotEqualTo ); | |
/** | |
* GreaterThan | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var GreaterThan = acf.Condition.extend({ | |
type: 'greaterThan', | |
operator: '>', | |
label: __('Value is greater than'), | |
fieldTypes: [ 'number', 'range' ], | |
match: function( rule, field ){ | |
var val = field.val(); | |
if( val instanceof Array ) { | |
val = val.length; | |
} | |
return isGreaterThan( val, rule.value ); | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="number" />'; | |
} | |
}); | |
acf.registerConditionType( GreaterThan ); | |
/** | |
* LessThan | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var LessThan = GreaterThan.extend({ | |
type: 'lessThan', | |
operator: '<', | |
label: __('Value is less than'), | |
match: function( rule, field ){ | |
var val = field.val(); | |
if( val instanceof Array ) { | |
val = val.length; | |
} | |
return isLessThan( val, rule.value ); | |
}, | |
choices: function( fieldObject ){ | |
return '<input type="number" />'; | |
} | |
}); | |
acf.registerConditionType( LessThan ); | |
/** | |
* SelectedGreaterThan | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var SelectionGreaterThan = GreaterThan.extend({ | |
type: 'selectionGreaterThan', | |
label: __('Selection is greater than'), | |
fieldTypes: [ 'checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user' ], | |
}); | |
acf.registerConditionType( SelectionGreaterThan ); | |
/** | |
* SelectedGreaterThan | |
* | |
* description | |
* | |
* @date 1/2/18 | |
* @since 5.6.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var SelectionLessThan = LessThan.extend({ | |
type: 'selectionLessThan', | |
label: __('Selection is less than'), | |
fieldTypes: [ 'checkbox', 'select', 'post_object', 'page_link', 'relationship', 'taxonomy', 'user' ], | |
}); | |
acf.registerConditionType( SelectionLessThan ); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* acf.newMediaPopup | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.newMediaPopup = function( args ){ | |
// args | |
var popup = null; | |
var args = acf.parseArgs(args, { | |
mode: 'select', // 'select', 'edit' | |
title: '', // 'Upload Image' | |
button: '', // 'Select Image' | |
type: '', // 'image', '' | |
field: false, // field instance | |
allowedTypes: '', // '.jpg, .png, etc' | |
library: 'all', // 'all', 'uploadedTo' | |
multiple: false, // false, true, 'add' | |
attachment: 0, // the attachment to edit | |
autoOpen: true, // open the popup automatically | |
open: function(){}, // callback after close | |
select: function(){}, // callback after select | |
close: function(){} // callback after close | |
}); | |
// initialize | |
if( args.mode == 'edit' ) { | |
popup = new acf.models.EditMediaPopup( args ); | |
} else { | |
popup = new acf.models.SelectMediaPopup( args ); | |
} | |
// open popup (allow frame customization before opening) | |
if( args.autoOpen ) { | |
setTimeout(function(){ | |
popup.open(); | |
}, 1); | |
} | |
// action | |
acf.doAction('new_media_popup', popup); | |
// return | |
return popup; | |
}; | |
/** | |
* getPostID | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var getPostID = function() { | |
var postID = acf.get('post_id'); | |
return $.isNumeric(postID) ? postID : 0; | |
} | |
/** | |
* acf.getMimeTypes | |
* | |
* description | |
* | |
* @date 11/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.getMimeTypes = function(){ | |
return this.get('mimeTypes'); | |
}; | |
acf.getMimeType = function( name ){ | |
// vars | |
var allTypes = acf.getMimeTypes(); | |
// search | |
if( allTypes[name] !== undefined ) { | |
return allTypes[name]; | |
} | |
// some types contain a mixed key such as "jpg|jpeg|jpe" | |
for( var key in allTypes ) { | |
if( key.indexOf(name) !== -1 ) { | |
return allTypes[key]; | |
} | |
} | |
// return | |
return false; | |
}; | |
/** | |
* MediaPopup | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var MediaPopup = acf.Model.extend({ | |
id: 'MediaPopup', | |
data: {}, | |
defaults: {}, | |
frame: false, | |
setup: function( props ){ | |
$.extend(this.data, props); | |
}, | |
initialize: function(){ | |
// vars | |
var options = this.getFrameOptions(); | |
// add states | |
this.addFrameStates( options ); | |
// create frame | |
var frame = wp.media( options ); | |
// add args reference | |
frame.acf = this; | |
// add events | |
this.addFrameEvents( frame, options ); | |
// strore frame | |
this.frame = frame; | |
}, | |
open: function(){ | |
this.frame.open(); | |
}, | |
close: function(){ | |
this.frame.close(); | |
}, | |
remove: function(){ | |
this.frame.detach(); | |
this.frame.remove(); | |
}, | |
getFrameOptions: function(){ | |
// vars | |
var options = { | |
title: this.get('title'), | |
multiple: this.get('multiple'), | |
library: {}, | |
states: [] | |
}; | |
// type | |
if( this.get('type') ) { | |
options.library.type = this.get('type'); | |
} | |
// type | |
if( this.get('library') === 'uploadedTo' ) { | |
options.library.uploadedTo = getPostID(); | |
} | |
// attachment | |
if( this.get('attachment') ) { | |
options.library.post__in = [ this.get('attachment') ]; | |
} | |
// button | |
if( this.get('button') ) { | |
options.button = { | |
text: this.get('button') | |
}; | |
} | |
// return | |
return options; | |
}, | |
addFrameStates: function( options ){ | |
// create query | |
var Query = wp.media.query( options.library ); | |
// add _acfuploader | |
// this is super wack! | |
// if you add _acfuploader to the options.library args, new uploads will not be added to the library view. | |
// this has been traced back to the wp.media.model.Query initialize function (which can't be overriden) | |
// Adding any custom args will cause the Attahcments to not observe the uploader queue | |
// To bypass this security issue, we add in the args AFTER the Query has been initialized | |
// options.library._acfuploader = settings.field; | |
if( this.get('field') && acf.isset(Query, 'mirroring', 'args') ) { | |
Query.mirroring.args._acfuploader = this.get('field'); | |
} | |
// add states | |
options.states.push( | |
// main state | |
new wp.media.controller.Library({ | |
library: Query, | |
multiple: this.get('multiple'), | |
title: this.get('title'), | |
priority: 20, | |
filterable: 'all', | |
editable: true, | |
allowLocalEdits: true | |
}) | |
); | |
// edit image functionality (added in WP 3.9) | |
if( acf.isset(wp, 'media', 'controller', 'EditImage') ) { | |
options.states.push( new wp.media.controller.EditImage() ); | |
} | |
}, | |
addFrameEvents: function( frame, options ){ | |
// log all events | |
//frame.on('all', function( e ) { | |
// console.log( 'frame all: %o', e ); | |
//}); | |
// add class | |
frame.on('open',function() { | |
this.$el.closest('.media-modal').addClass('acf-media-modal -' + this.acf.get('mode') ); | |
}, frame); | |
// edit image view | |
// source: media-views.js:2410 editImageContent() | |
frame.on('content:render:edit-image', function(){ | |
var image = this.state().get('image'); | |
var view = new wp.media.view.EditImage({ model: image, controller: this }).render(); | |
this.content.set( view ); | |
// after creating the wrapper view, load the actual editor via an ajax call | |
view.loadEditor(); | |
}, frame); | |
// update toolbar button | |
//frame.on( 'toolbar:create:select', function( toolbar ) { | |
// toolbar.view = new wp.media.view.Toolbar.Select({ | |
// text: frame.options._button, | |
// controller: this | |
// }); | |
//}, frame ); | |
// on select | |
frame.on('select', function() { | |
// vars | |
var selection = frame.state().get('selection'); | |
// if selecting images | |
if( selection ) { | |
// loop | |
selection.each(function( attachment, i ){ | |
frame.acf.get('select').apply( frame.acf, [attachment, i] ); | |
}); | |
} | |
}); | |
// on close | |
frame.on('close',function(){ | |
// callback and remove | |
setTimeout(function(){ | |
frame.acf.get('close').apply( frame.acf ); | |
frame.acf.remove(); | |
}, 1); | |
}); | |
} | |
}); | |
/** | |
* acf.models.SelectMediaPopup | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.models.SelectMediaPopup = MediaPopup.extend({ | |
id: 'SelectMediaPopup', | |
setup: function( props ){ | |
// default button | |
if( !props.button ) { | |
props.button = acf._x('Select', 'verb'); | |
} | |
// parent | |
MediaPopup.prototype.setup.apply(this, arguments); | |
}, | |
addFrameEvents: function( frame, options ){ | |
// plupload | |
// adds _acfuploader param to validate uploads | |
if( acf.isset(_wpPluploadSettings, 'defaults', 'multipart_params') ) { | |
// add _acfuploader so that Uploader will inherit | |
_wpPluploadSettings.defaults.multipart_params._acfuploader = this.get('field'); | |
// remove acf_field so future Uploaders won't inherit | |
frame.on('open', function(){ | |
delete _wpPluploadSettings.defaults.multipart_params._acfuploader; | |
}); | |
} | |
// browse | |
frame.on('content:activate:browse', function(){ | |
// vars | |
var toolbar = false; | |
// populate above vars making sure to allow for failure | |
// perhaps toolbar does not exist because the frame open is Upload Files | |
try { | |
toolbar = frame.content.get().toolbar; | |
} catch(e) { | |
console.log(e); | |
return; | |
} | |
// callback | |
frame.acf.customizeFilters.apply(frame.acf, [toolbar]); | |
}); | |
// parent | |
MediaPopup.prototype.addFrameEvents.apply(this, arguments); | |
}, | |
customizeFilters: function( toolbar ){ | |
// vars | |
var filters = toolbar.get('filters'); | |
// image | |
if( this.get('type') == 'image' ) { | |
// update all | |
filters.filters.all.text = acf.__('All images'); | |
// remove some filters | |
delete filters.filters.audio; | |
delete filters.filters.video; | |
delete filters.filters.image; | |
// update all filters to show images | |
$.each(filters.filters, function( i, filter ){ | |
filter.props.type = filter.props.type || 'image'; | |
}); | |
} | |
// specific types | |
if( this.get('allowedTypes') ) { | |
// convert ".jpg, .png" into ["jpg", "png"] | |
var allowedTypes = this.get('allowedTypes').split(' ').join('').split('.').join('').split(','); | |
// loop | |
allowedTypes.map(function( name ){ | |
// get type | |
var mimeType = acf.getMimeType( name ); | |
// bail early if no type | |
if( !mimeType ) return; | |
// create new filter | |
var newFilter = { | |
text: mimeType, | |
props: { | |
status: null, | |
type: mimeType, | |
uploadedTo: null, | |
orderby: 'date', | |
order: 'DESC' | |
}, | |
priority: 20 | |
}; | |
// append | |
filters.filters[ mimeType ] = newFilter; | |
}); | |
} | |
// uploaded to post | |
if( this.get('library') === 'uploadedTo' ) { | |
// vars | |
var uploadedTo = this.frame.options.library.uploadedTo; | |
// remove some filters | |
delete filters.filters.unattached; | |
delete filters.filters.uploaded; | |
// add uploadedTo to filters | |
$.each(filters.filters, function( i, filter ){ | |
filter.text += ' (' + acf.__('Uploaded to this post') + ')'; | |
filter.props.uploadedTo = uploadedTo; | |
}); | |
} | |
// add _acfuploader to filters | |
var field = this.get('field'); | |
$.each(filters.filters, function( k, filter ){ | |
filter.props._acfuploader = field; | |
}); | |
// add _acfuplaoder to search | |
var search = toolbar.get('search'); | |
search.model.attributes._acfuploader = field; | |
// render (custom function added to prototype) | |
if( filters.renderFilters ) { | |
filters.renderFilters(); | |
} | |
} | |
}); | |
/** | |
* acf.models.EditMediaPopup | |
* | |
* description | |
* | |
* @date 10/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.models.EditMediaPopup = MediaPopup.extend({ | |
id: 'SelectMediaPopup', | |
setup: function( props ){ | |
// default button | |
if( !props.button ) { | |
props.button = acf._x('Update', 'verb'); | |
} | |
// parent | |
MediaPopup.prototype.setup.apply(this, arguments); | |
}, | |
addFrameEvents: function( frame, options ){ | |
// add class | |
frame.on('open',function() { | |
// add class | |
this.$el.closest('.media-modal').addClass('acf-expanded'); | |
// set to browse | |
if( this.content.mode() != 'browse' ) { | |
this.content.mode('browse'); | |
} | |
// set selection | |
var state = this.state(); | |
var selection = state.get('selection'); | |
var attachment = wp.media.attachment( frame.acf.get('attachment') ); | |
selection.add( attachment ); | |
}, frame); | |
// parent | |
MediaPopup.prototype.addFrameEvents.apply(this, arguments); | |
} | |
}); | |
/** | |
* customizePrototypes | |
* | |
* description | |
* | |
* @date 11/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var customizePrototypes = new acf.Model({ | |
id: 'customizePrototypes', | |
wait: 'ready', | |
initialize: function(){ | |
// bail early if no media views | |
if( !acf.isset(window, 'wp', 'media', 'view') ) { | |
return; | |
} | |
// fix bug where CPT without "editor" does not set post.id setting which then prevents uploadedTo from working | |
var postID = getPostID(); | |
if( postID && acf.isset(wp, 'media', 'view', 'settings', 'post') ) { | |
wp.media.view.settings.post.id = postID; | |
} | |
// customize | |
this.customizeAttachmentsButton(); | |
this.customizeAttachmentsRouter(); | |
this.customizeAttachmentFilters(); | |
this.customizeAttachmentCompat(); | |
this.customizeAttachmentLibrary(); | |
}, | |
customizeAttachmentsButton: function(){ | |
// validate | |
if( !acf.isset(wp, 'media', 'view', 'Button') ) { | |
return; | |
} | |
// Extend | |
var Button = wp.media.view.Button; | |
wp.media.view.Button = Button.extend({ | |
// Fix bug where "Select" button appears blank after editing an image. | |
// Do this by simplifying Button initialize function and avoid deleting this.options. | |
initialize: function() { | |
var options = _.defaults( this.options, this.defaults ); | |
this.model = new Backbone.Model( options ); | |
this.listenTo( this.model, 'change', this.render ); | |
} | |
}); | |
}, | |
customizeAttachmentsRouter: function(){ | |
// validate | |
if( !acf.isset(wp, 'media', 'view', 'Router') ) { | |
return; | |
} | |
// vars | |
var Parent = wp.media.view.Router; | |
// extend | |
wp.media.view.Router = Parent.extend({ | |
addExpand: function(){ | |
// vars | |
var $a = $([ | |
'<a href="#" class="acf-expand-details">', | |
'<span class="is-closed"><span class="acf-icon -left small grey"></span>' + acf.__('Expand Details') + '</span>', | |
'<span class="is-open"><span class="acf-icon -right small grey"></span>' + acf.__('Collapse Details') + '</span>', | |
'</a>' | |
].join('')); | |
// add events | |
$a.on('click', function( e ){ | |
e.preventDefault(); | |
var $div = $(this).closest('.media-modal'); | |
if( $div.hasClass('acf-expanded') ) { | |
$div.removeClass('acf-expanded'); | |
} else { | |
$div.addClass('acf-expanded'); | |
} | |
}); | |
// append | |
this.$el.append( $a ); | |
}, | |
initialize: function(){ | |
// initialize | |
Parent.prototype.initialize.apply( this, arguments ); | |
// add buttons | |
this.addExpand(); | |
// return | |
return this; | |
} | |
}); | |
}, | |
customizeAttachmentFilters: function(){ | |
// validate | |
if( !acf.isset(wp, 'media', 'view', 'AttachmentFilters', 'All') ) { | |
return; | |
} | |
// vars | |
var Parent = wp.media.view.AttachmentFilters.All; | |
// renderFilters | |
// copied from media-views.js:6939 | |
Parent.prototype.renderFilters = function(){ | |
// Build `<option>` elements. | |
this.$el.html( _.chain( this.filters ).map( function( filter, value ) { | |
return { | |
el: $( '<option></option>' ).val( value ).html( filter.text )[0], | |
priority: filter.priority || 50 | |
}; | |
}, this ).sortBy('priority').pluck('el').value() ); | |
}; | |
}, | |
customizeAttachmentCompat: function(){ | |
// validate | |
if( !acf.isset(wp, 'media', 'view', 'AttachmentCompat') ) { | |
return; | |
} | |
// vars | |
var AttachmentCompat = wp.media.view.AttachmentCompat; | |
var timeout = false; | |
// extend | |
wp.media.view.AttachmentCompat = AttachmentCompat.extend({ | |
render: function() { | |
// WP bug | |
// When multiple media frames exist on the same page (WP content, WYSIWYG, image, file ), | |
// WP creates multiple instances of this AttachmentCompat view. | |
// Each instance will attempt to render when a new modal is created. | |
// Use a property to avoid this and only render once per instance. | |
if( this.rendered ) { | |
return this; | |
} | |
// render HTML | |
AttachmentCompat.prototype.render.apply( this, arguments ); | |
// when uploading, render is called twice. | |
// ignore first render by checking for #acf-form-data element | |
if( !this.$('#acf-form-data').length ) { | |
return this; | |
} | |
// clear timeout | |
clearTimeout( timeout ); | |
// setTimeout | |
timeout = setTimeout($.proxy(function(){ | |
this.rendered = true; | |
acf.doAction('append', this.$el); | |
}, this), 50); | |
// return | |
return this; | |
}, | |
save: function( event ) { | |
var data = {}; | |
if ( event ) { | |
event.preventDefault(); | |
} | |
//_.each( this.$el.serializeArray(), function( pair ) { | |
// data[ pair.name ] = pair.value; | |
//}); | |
// Serialize data more thoroughly to allow chckbox inputs to save. | |
data = acf.serializeForAjax(this.$el); | |
this.controller.trigger( 'attachment:compat:waiting', ['waiting'] ); | |
this.model.saveCompat( data ).always( _.bind( this.postSave, this ) ); | |
} | |
}); | |
}, | |
customizeAttachmentLibrary: function(){ | |
// validate | |
if( !acf.isset(wp, 'media', 'view', 'Attachment', 'Library') ) { | |
return; | |
} | |
// vars | |
var AttachmentLibrary = wp.media.view.Attachment.Library; | |
// extend | |
wp.media.view.Attachment.Library = AttachmentLibrary.extend({ | |
render: function() { | |
// vars | |
var popup = acf.isget(this, 'controller', 'acf'); | |
var attributes = acf.isget(this, 'model', 'attributes'); | |
// check vars exist to avoid errors | |
if( popup && attributes ) { | |
// show errors | |
if( attributes.acf_errors ) { | |
this.$el.addClass('acf-disabled'); | |
} | |
// disable selected | |
var selected = popup.get('selected'); | |
if( selected && selected.indexOf(attributes.id) > -1 ) { | |
this.$el.addClass('acf-selected'); | |
} | |
} | |
// render | |
return AttachmentLibrary.prototype.render.apply( this, arguments ); | |
}, | |
/* | |
* toggleSelection | |
* | |
* This function is called before an attachment is selected | |
* A good place to check for errors and prevent the 'select' function from being fired | |
* | |
* @type function | |
* @date 29/09/2016 | |
* @since 5.4.0 | |
* | |
* @param options (object) | |
* @return n/a | |
*/ | |
toggleSelection: function( options ) { | |
// vars | |
// source: wp-includes/js/media-views.js:2880 | |
var collection = this.collection, | |
selection = this.options.selection, | |
model = this.model, | |
single = selection.single(); | |
// vars | |
var frame = this.controller; | |
var errors = acf.isget(this, 'model', 'attributes', 'acf_errors'); | |
var $sidebar = frame.$el.find('.media-frame-content .media-sidebar'); | |
// remove previous error | |
$sidebar.children('.acf-selection-error').remove(); | |
// show attachment details | |
$sidebar.children().removeClass('acf-hidden'); | |
// add message | |
if( frame && errors ) { | |
// vars | |
var filename = acf.isget(this, 'model', 'attributes', 'filename'); | |
// hide attachment details | |
// Gallery field continues to show previously selected attachment... | |
$sidebar.children().addClass('acf-hidden'); | |
// append message | |
$sidebar.prepend([ | |
'<div class="acf-selection-error">', | |
'<span class="selection-error-label">' + acf.__('Restricted') +'</span>', | |
'<span class="selection-error-filename">' + filename + '</span>', | |
'<span class="selection-error-message">' + errors + '</span>', | |
'</div>' | |
].join('')); | |
// reset selection (unselects all attachments) | |
selection.reset(); | |
// set single (attachment displayed in sidebar) | |
selection.single( model ); | |
// return and prevent 'select' form being fired | |
return; | |
} | |
// return | |
return AttachmentLibrary.prototype.toggleSelection.apply( this, arguments ); | |
} | |
}); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
acf.screen = new acf.Model({ | |
active: true, | |
xhr: false, | |
timeout: false, | |
wait: 'load', | |
events: { | |
'change #page_template': 'onChange', | |
'change #parent_id': 'onChange', | |
'change #post-formats-select': 'onChange', | |
'change .categorychecklist': 'onChange', | |
'change .tagsdiv': 'onChange', | |
'change .acf-taxonomy-field[data-save="1"]': 'onChange', | |
'change #product-type': 'onChange' | |
}, | |
isPost: function(){ | |
return acf.get('screen') === 'post'; | |
}, | |
isUser: function(){ | |
return acf.get('screen') === 'user'; | |
}, | |
isTaxonomy: function(){ | |
return acf.get('screen') === 'taxonomy'; | |
}, | |
isAttachment: function(){ | |
return acf.get('screen') === 'attachment'; | |
}, | |
isNavMenu: function(){ | |
return acf.get('screen') === 'nav_menu'; | |
}, | |
isWidget: function(){ | |
return acf.get('screen') === 'widget'; | |
}, | |
isComment: function(){ | |
return acf.get('screen') === 'comment'; | |
}, | |
getPageTemplate: function(){ | |
var $el = $('#page_template'); | |
return $el.length ? $el.val() : null; | |
}, | |
getPageParent: function( e, $el ){ | |
var $el = $('#parent_id'); | |
return $el.length ? $el.val() : null; | |
}, | |
getPageType: function( e, $el ){ | |
return this.getPageParent() ? 'child' : 'parent'; | |
}, | |
getPostType: function(){ | |
return $('#post_type').val(); | |
}, | |
getPostFormat: function( e, $el ){ | |
var $el = $('#post-formats-select input:checked'); | |
if( $el.length ) { | |
var val = $el.val(); | |
return (val == '0') ? 'standard' : val; | |
} | |
return null; | |
}, | |
getPostCoreTerms: function(){ | |
// vars | |
var terms = {}; | |
// serialize WP taxonomy postboxes | |
var data = acf.serialize( $('.categorydiv, .tagsdiv') ); | |
// use tax_input (tag, custom-taxonomy) when possible. | |
// this data is already formatted in taxonomy => [terms]. | |
if( data.tax_input ) { | |
terms = data.tax_input; | |
} | |
// append "category" which uses a different name | |
if( data.post_category ) { | |
terms.category = data.post_category; | |
} | |
// convert any string values (tags) into array format | |
for( var tax in terms ) { | |
if( !acf.isArray(terms[tax]) ) { | |
terms[tax] = terms[tax].split(/,[\s]?/); | |
} | |
} | |
// return | |
return terms; | |
}, | |
getPostTerms: function(){ | |
// Get core terms. | |
var terms = this.getPostCoreTerms(); | |
// loop over taxonomy fields and add their values | |
acf.getFields({type: 'taxonomy'}).map(function( field ){ | |
// ignore fields that don't save | |
if( !field.get('save') ) { | |
return; | |
} | |
// vars | |
var val = field.val(); | |
var tax = field.get('taxonomy'); | |
// check val | |
if( val ) { | |
// ensure terms exists | |
terms[ tax ] = terms[ tax ] || []; | |
// ensure val is an array | |
val = acf.isArray(val) ? val : [val]; | |
// append | |
terms[ tax ] = terms[ tax ].concat( val ); | |
} | |
}); | |
// add WC product type | |
if( (productType = this.getProductType()) !== null ) { | |
terms.product_type = [productType]; | |
} | |
// remove duplicate values | |
for( var tax in terms ) { | |
terms[tax] = acf.uniqueArray(terms[tax]); | |
} | |
// return | |
return terms; | |
}, | |
getProductType: function(){ | |
var $el = $('#product-type'); | |
return $el.length ? $el.val() : null; | |
}, | |
check: function(){ | |
// bail early if not for post | |
if( acf.get('screen') !== 'post' ) { | |
return; | |
} | |
// abort XHR if is already loading AJAX data | |
if( this.xhr ) { | |
this.xhr.abort(); | |
} | |
// vars | |
var ajaxData = acf.parseArgs(this.data, { | |
action: 'acf/ajax/check_screen', | |
screen: acf.get('screen'), | |
exists: [] | |
}); | |
// post id | |
if( this.isPost() ) { | |
ajaxData.post_id = acf.get('post_id'); | |
} | |
// post type | |
if( (postType = this.getPostType()) !== null ) { | |
ajaxData.post_type = postType; | |
} | |
// page template | |
if( (pageTemplate = this.getPageTemplate()) !== null ) { | |
ajaxData.page_template = pageTemplate; | |
} | |
// page parent | |
if( (pageParent = this.getPageParent()) !== null ) { | |
ajaxData.page_parent = pageParent; | |
} | |
// page type | |
if( (pageType = this.getPageType()) !== null ) { | |
ajaxData.page_type = pageType; | |
} | |
// post format | |
if( (postFormat = this.getPostFormat()) !== null ) { | |
ajaxData.post_format = postFormat; | |
} | |
// post terms | |
if( (postTerms = this.getPostTerms()) !== null ) { | |
ajaxData.post_terms = postTerms; | |
} | |
// add array of existing postboxes to increase performance and reduce JSON HTML | |
acf.getPostboxes().map(function( postbox ){ | |
ajaxData.exists.push( postbox.get('key') ); | |
}); | |
// filter | |
ajaxData = acf.applyFilters('check_screen_args', ajaxData); | |
// success | |
var onSuccess = function( json ){ | |
// Check success. | |
if( acf.isAjaxSuccess(json) ) { | |
// Render post screen. | |
if( acf.get('screen') == 'post' ) { | |
this.renderPostScreen( json.data ); | |
// Render user screen. | |
} else if( acf.get('screen') == 'user' ) { | |
this.renderUserScreen( json.data ); | |
} | |
} | |
// action | |
acf.doAction('check_screen_complete', json.data, ajaxData); | |
}; | |
// ajax | |
this.xhr = $.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax( ajaxData ), | |
type: 'post', | |
dataType: 'json', | |
context: this, | |
success: onSuccess | |
}); | |
}, | |
onChange: function( e, $el ){ | |
this.setTimeout(this.check, 1); | |
}, | |
renderPostScreen: function( data ){ | |
// Helper function to copy events | |
var copyEvents = function( $from, $to ){ | |
var events = $._data($from[0]).events; | |
for( var type in events ) { | |
for( var i = 0; i < events[type].length; i++ ) { | |
$to.on( type, events[type][i].handler ); | |
} | |
} | |
} | |
// Helper function to sort metabox. | |
var sortMetabox = function( id, ids ){ | |
// Find position of id within ids. | |
var index = ids.indexOf( id ); | |
// Bail early if index not found. | |
if( index == -1 ) { | |
return false; | |
} | |
// Loop over metaboxes behind (in reverse order). | |
for( var i = index-1; i >= 0; i-- ) { | |
if( $('#'+ids[i]).length ) { | |
return $('#'+ids[i]).after( $('#'+id) ); | |
} | |
} | |
// Loop over metaboxes infront. | |
for( var i = index+1; i < ids.length; i++ ) { | |
if( $('#'+ids[i]).length ) { | |
return $('#'+ids[i]).before( $('#'+id) ); | |
} | |
} | |
// Return false if not sorted. | |
return false; | |
}; | |
// Keep track of visible and hidden postboxes. | |
data.visible = []; | |
data.hidden = []; | |
// Show these postboxes. | |
data.results = data.results.map(function( result, i ){ | |
// vars | |
var postbox = acf.getPostbox( result.id ); | |
// Prevent "acf_after_title" position in Block Editor. | |
if( acf.isGutenberg() && result.position == "acf_after_title" ) { | |
result.position = 'normal'; | |
} | |
// Create postbox if doesn't exist. | |
if( !postbox ) { | |
// Create it. | |
var $postbox = $([ | |
'<div id="' + result.id + '" class="postbox">', | |
'<button type="button" class="handlediv" aria-expanded="false">', | |
'<span class="screen-reader-text">Toggle panel: ' + result.title + '</span>', | |
'<span class="toggle-indicator" aria-hidden="true"></span>', | |
'</button>', | |
'<h2 class="hndle ui-sortable-handle">', | |
'<span>' + result.title + '</span>', | |
'</h2>', | |
'<div class="inside">', | |
result.html, | |
'</div>', | |
'</div>' | |
].join('')); | |
// Create new hide toggle. | |
if( $('#adv-settings').length ) { | |
var $prefs = $('#adv-settings .metabox-prefs'); | |
var $label = $([ | |
'<label for="' + result.id + '-hide">', | |
'<input class="hide-postbox-tog" name="' + result.id + '-hide" type="checkbox" id="' + result.id + '-hide" value="' + result.id + '" checked="checked">', | |
' ' + result.title, | |
'</label>' | |
].join('')); | |
// Copy default WP events onto checkbox. | |
copyEvents( $prefs.find('input').first(), $label.find('input') ); | |
// Append hide label | |
$prefs.append( $label ); | |
} | |
// Copy default WP events onto metabox. | |
if( $('.postbox').length ) { | |
copyEvents( $('.postbox .handlediv').first(), $postbox.children('.handlediv') ); | |
copyEvents( $('.postbox .hndle').first(), $postbox.children('.hndle') ); | |
} | |
// Append metabox to the bottom of "side-sortables". | |
if( result.position === 'side' ) { | |
$('#' + result.position + '-sortables').append( $postbox ); | |
// Prepend metabox to the top of "normal-sortbables". | |
} else { | |
$('#' + result.position + '-sortables').prepend( $postbox ); | |
} | |
// Position metabox amongst existing ACF metaboxes within the same location. | |
var order = []; | |
data.results.map(function( _result ){ | |
if( result.position === _result.position && $('#' + result.position + '-sortables #' + _result.id).length ) { | |
order.push( _result.id ); | |
} | |
}); | |
sortMetabox(result.id, order) | |
// Check 'sorted' for user preference. | |
if( data.sorted ) { | |
// Loop over each position (acf_after_title, side, normal). | |
for( var position in data.sorted ) { | |
// Explode string into array of ids. | |
var order = data.sorted[position].split(','); | |
// Position metabox relative to order. | |
if( sortMetabox(result.id, order) ) { | |
break; | |
} | |
} | |
} | |
// Initalize it (modifies HTML). | |
postbox = acf.newPostbox( result ); | |
// Trigger action. | |
acf.doAction('append', $postbox); | |
acf.doAction('append_postbox', postbox); | |
} | |
// show postbox | |
postbox.showEnable(); | |
// append | |
data.visible.push( result.id ); | |
// Return result (may have changed). | |
return result; | |
}); | |
// Hide these postboxes. | |
acf.getPostboxes().map(function( postbox ){ | |
if( data.visible.indexOf( postbox.get('id') ) === -1 ) { | |
// Hide postbox. | |
postbox.hideDisable(); | |
// Append to data. | |
data.hidden.push( postbox.get('id') ); | |
} | |
}); | |
// Update style. | |
$('#acf-style').html( data.style ); | |
// Do action. | |
acf.doAction( 'refresh_post_screen', data ); | |
}, | |
renderUserScreen: function( json ){ | |
} | |
}); | |
/** | |
* gutenScreen | |
* | |
* Adds compatibility with the Gutenberg edit screen. | |
* | |
* @date 11/12/18 | |
* @since 5.8.0 | |
* | |
* @param void | |
* @return void | |
*/ | |
var gutenScreen = new acf.Model({ | |
// Keep a reference to the most recent post attributes. | |
postEdits: {}, | |
// Wait until load to avoid 'core' issues when loading taxonomies. | |
wait: 'load', | |
initialize: function(){ | |
// Bail early if not Gutenberg. | |
if( !acf.isGutenberg() ) { | |
return; | |
} | |
// Listen for changes (use debounced version as this can fires often). | |
wp.data.subscribe( acf.debounce(this.onChange).bind(this) ); | |
// Customize "acf.screen.get" functions. | |
acf.screen.getPageTemplate = this.getPageTemplate; | |
acf.screen.getPageParent = this.getPageParent; | |
acf.screen.getPostType = this.getPostType; | |
acf.screen.getPostFormat = this.getPostFormat; | |
acf.screen.getPostCoreTerms = this.getPostCoreTerms; | |
// Disable unload | |
acf.unload.disable(); | |
// Refresh metaboxes since WP 5.3. | |
var wpMinorVersion = parseFloat( acf.get('wp_version') ); | |
if( wpMinorVersion >= 5.3 ) { | |
this.addAction( 'refresh_post_screen', this.onRefreshPostScreen ); | |
} | |
}, | |
onChange: function(){ | |
// Determine attributes that can trigger a refresh. | |
var attributes = [ 'template', 'parent', 'format' ]; | |
// Append taxonomy attribute names to this list. | |
( wp.data.select( 'core' ).getTaxonomies() || [] ).map(function( taxonomy ){ | |
attributes.push( taxonomy.rest_base ); | |
}); | |
// Get relevant current post edits. | |
var _postEdits = wp.data.select( 'core/editor' ).getPostEdits(); | |
var postEdits = {}; | |
attributes.map(function( k ){ | |
if( _postEdits[k] !== undefined ) { | |
postEdits[k] = _postEdits[k]; | |
} | |
}); | |
// Detect change. | |
if( JSON.stringify(postEdits) !== JSON.stringify(this.postEdits) ) { | |
this.postEdits = postEdits; | |
// Check screen. | |
acf.screen.check(); | |
} | |
}, | |
getPageTemplate: function(){ | |
return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'template' ); | |
}, | |
getPageParent: function( e, $el ){ | |
return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'parent' ); | |
}, | |
getPostType: function(){ | |
return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'type' ); | |
}, | |
getPostFormat: function( e, $el ){ | |
return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'format' ); | |
}, | |
getPostCoreTerms: function(){ | |
// vars | |
var terms = {}; | |
// Loop over taxonomies. | |
var taxonomies = wp.data.select( 'core' ).getTaxonomies() || []; | |
taxonomies.map(function( taxonomy ){ | |
// Append selected taxonomies to terms object. | |
var postTerms = wp.data.select( 'core/editor' ).getEditedPostAttribute( taxonomy.rest_base ); | |
if( postTerms ) { | |
terms[ taxonomy.slug ] = postTerms; | |
} | |
}); | |
// return | |
return terms; | |
}, | |
/** | |
* onRefreshPostScreen | |
* | |
* Fires after the Post edit screen metaboxs are refreshed to update the Block Editor API state. | |
* | |
* @date 11/11/19 | |
* @since 5.8.7 | |
* | |
* @param object data The "check_screen" JSON response data. | |
* @return void | |
*/ | |
onRefreshPostScreen: function( data ) { | |
// Extract vars. | |
var select = wp.data.select( 'core/edit-post' ); | |
var dispatch = wp.data.dispatch( 'core/edit-post' ); | |
// Load current metabox locations and data. | |
var locations = {}; | |
select.getActiveMetaBoxLocations().map(function( location ){ | |
locations[ location ] = select.getMetaBoxesPerLocation( location ); | |
}); | |
// Generate flat array of existing ids. | |
var ids = []; | |
for( var k in locations ) { | |
locations[k].map(function( m ){ | |
ids.push( m.id ); | |
}); | |
} | |
// Append new ACF metaboxes (ignore those which already exist). | |
data.results.filter(function( r ){ | |
return ( ids.indexOf( r.id ) === -1 ); | |
}).map(function( result, i ){ | |
// Ensure location exists. | |
var location = result.position; | |
locations[ location ] = locations[ location ] || []; | |
// Append. | |
locations[ location ].push({ | |
id: result.id, | |
title: result.title | |
}); | |
}); | |
// Remove hidden ACF metaboxes. | |
for( var k in locations ) { | |
locations[k] = locations[k].filter(function( m ){ | |
return ( data.hidden.indexOf( m.id ) === -1 ); | |
}); | |
} | |
// Update state. | |
dispatch.setAvailableMetaBoxesPerLocation( locations ); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* acf.newSelect2 | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.newSelect2 = function( $select, props ){ | |
// defaults | |
props = acf.parseArgs(props, { | |
allowNull: false, | |
placeholder: '', | |
multiple: false, | |
field: false, | |
ajax: false, | |
ajaxAction: '', | |
ajaxData: function( data ){ return data; }, | |
ajaxResults: function( json ){ return json; }, | |
}); | |
// initialize | |
if( getVersion() == 4 ) { | |
var select2 = new Select2_4( $select, props ); | |
} else { | |
var select2 = new Select2_3( $select, props ); | |
} | |
// actions | |
acf.doAction('new_select2', select2); | |
// return | |
return select2; | |
}; | |
/** | |
* getVersion | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
function getVersion() { | |
// v4 | |
if( acf.isset(window, 'jQuery', 'fn', 'select2', 'amd') ) { | |
return 4; | |
} | |
// v3 | |
if( acf.isset(window, 'Select2') ) { | |
return 3; | |
} | |
// return | |
return false; | |
} | |
/** | |
* Select2 | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var Select2 = acf.Model.extend({ | |
setup: function( $select, props ){ | |
$.extend(this.data, props); | |
this.$el = $select; | |
}, | |
initialize: function(){ | |
}, | |
selectOption: function( value ){ | |
var $option = this.getOption( value ); | |
if( !$option.prop('selected') ) { | |
$option.prop('selected', true).trigger('change'); | |
} | |
}, | |
unselectOption: function( value ){ | |
var $option = this.getOption( value ); | |
if( $option.prop('selected') ) { | |
$option.prop('selected', false).trigger('change'); | |
} | |
}, | |
getOption: function( value ){ | |
return this.$('option[value="' + value + '"]'); | |
}, | |
addOption: function( option ){ | |
// defaults | |
option = acf.parseArgs(option, { | |
id: '', | |
text: '', | |
selected: false | |
}); | |
// vars | |
var $option = this.getOption( option.id ); | |
// append | |
if( !$option.length ) { | |
$option = $('<option></option>'); | |
$option.html( option.text ); | |
$option.attr('value', option.id); | |
$option.prop('selected', option.selected); | |
this.$el.append($option); | |
} | |
// chain | |
return $option; | |
}, | |
getValue: function(){ | |
// vars | |
var val = []; | |
var $options = this.$el.find('option:selected'); | |
// bail early if no selected | |
if( !$options.exists() ) { | |
return val; | |
} | |
// sort by attribute | |
$options = $options.sort(function(a, b) { | |
return +a.getAttribute('data-i') - +b.getAttribute('data-i'); | |
}); | |
// loop | |
$options.each(function(){ | |
var $el = $(this); | |
val.push({ | |
$el: $el, | |
id: $el.attr('value'), | |
text: $el.text(), | |
}); | |
}); | |
// return | |
return val; | |
}, | |
mergeOptions: function(){ | |
}, | |
getChoices: function(){ | |
// callback | |
var crawl = function( $parent ){ | |
// vars | |
var choices = []; | |
// loop | |
$parent.children().each(function(){ | |
// vars | |
var $child = $(this); | |
// optgroup | |
if( $child.is('optgroup') ) { | |
choices.push({ | |
text: $child.attr('label'), | |
children: crawl( $child ) | |
}); | |
// option | |
} else { | |
choices.push({ | |
id: $child.attr('value'), | |
text: $child.text() | |
}); | |
} | |
}); | |
// return | |
return choices; | |
}; | |
// crawl | |
return crawl( this.$el ); | |
}, | |
decodeChoices: function( choices ){ | |
// callback | |
var crawl = function( items ){ | |
items.map(function( item ){ | |
item.text = acf.decode( item.text ); | |
if( item.children ) { | |
item.children = crawl( item.children ); | |
} | |
return item; | |
}); | |
return items; | |
}; | |
// crawl | |
return crawl( choices ); | |
}, | |
getAjaxData: function( params ){ | |
// vars | |
var ajaxData = { | |
action: this.get('ajaxAction'), | |
s: params.term || '', | |
paged: params.page || 1 | |
}; | |
// field helper | |
var field = this.get('field'); | |
if( field ) { | |
ajaxData.field_key = field.get('key'); | |
} | |
// callback | |
var callback = this.get('ajaxData'); | |
if( callback ) { | |
ajaxData = callback.apply( this, [ajaxData, params] ); | |
} | |
// filter | |
ajaxData = acf.applyFilters( 'select2_ajax_data', ajaxData, this.data, this.$el, (field || false), this ); | |
// return | |
return acf.prepareForAjax(ajaxData); | |
}, | |
getAjaxResults: function( json, params ){ | |
// defaults | |
json = acf.parseArgs(json, { | |
results: false, | |
more: false, | |
}); | |
// decode | |
if( json.results ) { | |
json.results = this.decodeChoices(json.results); | |
} | |
// callback | |
var callback = this.get('ajaxResults'); | |
if( callback ) { | |
json = callback.apply( this, [json, params] ); | |
} | |
// filter | |
json = acf.applyFilters( 'select2_ajax_results', json, params, this ); | |
// return | |
return json; | |
}, | |
processAjaxResults: function( json, params ){ | |
// vars | |
var json = this.getAjaxResults( json, params ); | |
// change more to pagination | |
if( json.more ) { | |
json.pagination = { more: true }; | |
} | |
// merge together groups | |
setTimeout($.proxy(this.mergeOptions, this), 1); | |
// return | |
return json; | |
}, | |
destroy: function(){ | |
// destroy via api | |
if( this.$el.data('select2') ) { | |
this.$el.select2('destroy'); | |
} | |
// destory via HTML (duplicating HTML does not contain data) | |
this.$el.siblings('.select2-container').remove(); | |
} | |
}); | |
/** | |
* Select2_4 | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var Select2_4 = Select2.extend({ | |
initialize: function(){ | |
// vars | |
var $select = this.$el; | |
var options = { | |
width: '100%', | |
allowClear: this.get('allowNull'), | |
placeholder: this.get('placeholder'), | |
multiple: this.get('multiple'), | |
data: [], | |
escapeMarkup: function( m ){ return m; } | |
}; | |
// multiple | |
if( options.multiple ) { | |
// reorder options | |
this.getValue().map(function( item ){ | |
item.$el.detach().appendTo( $select ); | |
}); | |
} | |
// remove conflicting atts | |
$select.removeData('ajax'); | |
$select.removeAttr('data-ajax'); | |
// ajax | |
if( this.get('ajax') ) { | |
options.ajax = { | |
url: acf.get('ajaxurl'), | |
delay: 250, | |
dataType: 'json', | |
type: 'post', | |
cache: false, | |
data: $.proxy(this.getAjaxData, this), | |
processResults: $.proxy(this.processAjaxResults, this), | |
}; | |
} | |
// filter for 3rd party customization | |
//options = acf.applyFilters( 'select2_args', options, $select, this ); | |
var field = this.get('field'); | |
options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); | |
// add select2 | |
$select.select2( options ); | |
// get container (Select2 v4 does not return this from constructor) | |
var $container = $select.next('.select2-container'); | |
// multiple | |
if( options.multiple ) { | |
// vars | |
var $ul = $container.find('ul'); | |
// sortable | |
$ul.sortable({ | |
stop: function( e ) { | |
// loop | |
$ul.find('.select2-selection__choice').each(function() { | |
// vars | |
var $option = $( $(this).data('data').element ); | |
// detach and re-append to end | |
$option.detach().appendTo( $select ); | |
}); | |
// trigger change on input (JS error if trigger on select) | |
$select.trigger('change'); | |
} | |
}); | |
// on select, move to end | |
$select.on('select2:select', this.proxy(function( e ){ | |
this.getOption( e.params.data.id ).detach().appendTo( this.$el ); | |
})); | |
} | |
// add class | |
$container.addClass('-acf'); | |
// action for 3rd party customization | |
acf.doAction('select2_init', $select, options, this.data, (field || false), this); | |
}, | |
mergeOptions: function(){ | |
// vars | |
var $prevOptions = false; | |
var $prevGroup = false; | |
// loop | |
$('.select2-results__option[role="group"]').each(function(){ | |
// vars | |
var $options = $(this).children('ul'); | |
var $group = $(this).children('strong'); | |
// compare to previous | |
if( $prevGroup && $prevGroup.text() === $group.text() ) { | |
$prevOptions.append( $options.children() ); | |
$(this).remove(); | |
return; | |
} | |
// update vars | |
$prevOptions = $options; | |
$prevGroup = $group; | |
}); | |
}, | |
}); | |
/** | |
* Select2_3 | |
* | |
* description | |
* | |
* @date 13/1/18 | |
* @since 5.6.5 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var Select2_3 = Select2.extend({ | |
initialize: function(){ | |
// vars | |
var $select = this.$el; | |
var value = this.getValue(); | |
var multiple = this.get('multiple'); | |
var options = { | |
width: '100%', | |
allowClear: this.get('allowNull'), | |
placeholder: this.get('placeholder'), | |
separator: '||', | |
multiple: this.get('multiple'), | |
data: this.getChoices(), | |
escapeMarkup: function( m ){ return m; }, | |
dropdownCss: { | |
'z-index': '999999999' | |
}, | |
initSelection: function( element, callback ) { | |
if( multiple ) { | |
callback( value ); | |
} else { | |
callback( value.shift() ); | |
} | |
} | |
}; | |
// get hidden input | |
var $input = $select.siblings('input'); | |
if( !$input.length ) { | |
$input = $('<input type="hidden" />'); | |
$select.before( $input ); | |
} | |
// set input value | |
inputValue = value.map(function(item){ return item.id }).join('||'); | |
$input.val( inputValue ); | |
// multiple | |
if( options.multiple ) { | |
// reorder options | |
value.map(function( item ){ | |
item.$el.detach().appendTo( $select ); | |
}); | |
} | |
// remove blank option as we have a clear all button | |
if( options.allowClear ) { | |
options.data = options.data.filter(function(item){ | |
return item.id !== ''; | |
}); | |
} | |
// remove conflicting atts | |
$select.removeData('ajax'); | |
$select.removeAttr('data-ajax'); | |
// ajax | |
if( this.get('ajax') ) { | |
options.ajax = { | |
url: acf.get('ajaxurl'), | |
quietMillis: 250, | |
dataType: 'json', | |
type: 'post', | |
cache: false, | |
data: $.proxy(this.getAjaxData, this), | |
results: $.proxy(this.processAjaxResults, this), | |
}; | |
} | |
// filter for 3rd party customization | |
var field = this.get('field'); | |
options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); | |
// add select2 | |
$input.select2( options ); | |
// get container | |
var $container = $input.select2('container'); | |
// helper to find this select's option | |
var getOption = $.proxy(this.getOption, this); | |
// multiple | |
if( options.multiple ) { | |
// vars | |
var $ul = $container.find('ul'); | |
// sortable | |
$ul.sortable({ | |
stop: function() { | |
// loop | |
$ul.find('.select2-search-choice').each(function() { | |
// vars | |
var data = $(this).data('select2Data'); | |
var $option = getOption( data.id ); | |
// detach and re-append to end | |
$option.detach().appendTo( $select ); | |
}); | |
// trigger change on input (JS error if trigger on select) | |
$select.trigger('change'); | |
} | |
}); | |
} | |
// on select, create option and move to end | |
$input.on('select2-selecting', function( e ){ | |
// vars | |
var item = e.choice; | |
var $option = getOption( item.id ); | |
// create if doesn't exist | |
if( !$option.length ) { | |
$option = $('<option value="' + item.id + '">' + item.text + '</option>'); | |
} | |
// detach and re-append to end | |
$option.detach().appendTo( $select ); | |
}); | |
// add class | |
$container.addClass('-acf'); | |
// action for 3rd party customization | |
acf.doAction('select2_init', $select, options, this.data, (field || false), this); | |
// change | |
$input.on('change', function(){ | |
var val = $input.val(); | |
if( val.indexOf('||') ) { | |
val = val.split('||'); | |
} | |
$select.val( val ).trigger('change'); | |
}); | |
// hide select | |
$select.hide(); | |
}, | |
mergeOptions: function(){ | |
// vars | |
var $prevOptions = false; | |
var $prevGroup = false; | |
// loop | |
$('#select2-drop .select2-result-with-children').each(function(){ | |
// vars | |
var $options = $(this).children('ul'); | |
var $group = $(this).children('.select2-result-label'); | |
// compare to previous | |
if( $prevGroup && $prevGroup.text() === $group.text() ) { | |
$prevGroup.append( $options.children() ); | |
$(this).remove(); | |
return; | |
} | |
// update vars | |
$prevOptions = $options; | |
$prevGroup = $group; | |
}); | |
}, | |
getAjaxData: function( term, page ){ | |
// create Select2 v4 params | |
var params = { | |
term: term, | |
page: page | |
} | |
// return | |
return Select2.prototype.getAjaxData.apply(this, [params]); | |
}, | |
}); | |
// manager | |
var select2Manager = new acf.Model({ | |
priority: 5, | |
wait: 'prepare', | |
actions: { | |
'duplicate': 'onDuplicate' | |
}, | |
initialize: function(){ | |
// vars | |
var locale = acf.get('locale'); | |
var rtl = acf.get('rtl'); | |
var l10n = acf.get('select2L10n'); | |
var version = getVersion(); | |
// bail ealry if no l10n | |
if( !l10n ) { | |
return false; | |
} | |
// bail early if 'en' | |
if( locale.indexOf('en') === 0 ) { | |
return false; | |
} | |
// initialize | |
if( version == 4 ) { | |
this.addTranslations4(); | |
} else if( version == 3 ) { | |
this.addTranslations3(); | |
} | |
}, | |
addTranslations4: function(){ | |
// vars | |
var l10n = acf.get('select2L10n'); | |
var locale = acf.get('locale'); | |
// modify local to match html[lang] attribute (used by Select2) | |
locale = locale.replace('_', '-'); | |
// select2L10n | |
var select2L10n = { | |
errorLoading: function () { | |
return l10n.load_fail; | |
}, | |
inputTooLong: function (args) { | |
var overChars = args.input.length - args.maximum; | |
if( overChars > 1 ) { | |
return l10n.input_too_long_n.replace( '%d', overChars ); | |
} | |
return l10n.input_too_long_1; | |
}, | |
inputTooShort: function( args ){ | |
var remainingChars = args.minimum - args.input.length; | |
if( remainingChars > 1 ) { | |
return l10n.input_too_short_n.replace( '%d', remainingChars ); | |
} | |
return l10n.input_too_short_1; | |
}, | |
loadingMore: function () { | |
return l10n.load_more; | |
}, | |
maximumSelected: function( args ) { | |
var maximum = args.maximum; | |
if( maximum > 1 ) { | |
return l10n.selection_too_long_n.replace( '%d', maximum ); | |
} | |
return l10n.selection_too_long_1; | |
}, | |
noResults: function () { | |
return l10n.matches_0; | |
}, | |
searching: function () { | |
return l10n.searching; | |
} | |
}; | |
// append | |
jQuery.fn.select2.amd.define('select2/i18n/' + locale, [], function(){ | |
return select2L10n; | |
}); | |
}, | |
addTranslations3: function(){ | |
// vars | |
var l10n = acf.get('select2L10n'); | |
var locale = acf.get('locale'); | |
// modify local to match html[lang] attribute (used by Select2) | |
locale = locale.replace('_', '-'); | |
// select2L10n | |
var select2L10n = { | |
formatMatches: function( matches ) { | |
if( matches > 1 ) { | |
return l10n.matches_n.replace( '%d', matches ); | |
} | |
return l10n.matches_1; | |
}, | |
formatNoMatches: function() { | |
return l10n.matches_0; | |
}, | |
formatAjaxError: function() { | |
return l10n.load_fail; | |
}, | |
formatInputTooShort: function( input, min ) { | |
var remainingChars = min - input.length; | |
if( remainingChars > 1 ) { | |
return l10n.input_too_short_n.replace( '%d', remainingChars ); | |
} | |
return l10n.input_too_short_1; | |
}, | |
formatInputTooLong: function( input, max ) { | |
var overChars = input.length - max; | |
if( overChars > 1 ) { | |
return l10n.input_too_long_n.replace( '%d', overChars ); | |
} | |
return l10n.input_too_long_1; | |
}, | |
formatSelectionTooBig: function( maximum ) { | |
if( maximum > 1 ) { | |
return l10n.selection_too_long_n.replace( '%d', maximum ); | |
} | |
return l10n.selection_too_long_1; | |
}, | |
formatLoadMore: function() { | |
return l10n.load_more; | |
}, | |
formatSearching: function() { | |
return l10n.searching; | |
} | |
}; | |
// ensure locales exists | |
$.fn.select2.locales = $.fn.select2.locales || {}; | |
// append | |
$.fn.select2.locales[ locale ] = select2L10n; | |
$.extend($.fn.select2.defaults, select2L10n); | |
}, | |
onDuplicate: function( $el, $el2 ){ | |
$el2.find('.select2-container').remove(); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
acf.tinymce = { | |
/* | |
* defaults | |
* | |
* This function will return default mce and qt settings | |
* | |
* @type function | |
* @date 18/8/17 | |
* @since 5.6.0 | |
* | |
* @param $post_id (int) | |
* @return $post_id (int) | |
*/ | |
defaults: function(){ | |
// bail early if no tinyMCEPreInit | |
if( typeof tinyMCEPreInit === 'undefined' ) return false; | |
// vars | |
var defaults = { | |
tinymce: tinyMCEPreInit.mceInit.acf_content, | |
quicktags: tinyMCEPreInit.qtInit.acf_content | |
}; | |
// return | |
return defaults; | |
}, | |
/* | |
* initialize | |
* | |
* This function will initialize the tinymce and quicktags instances | |
* | |
* @type function | |
* @date 18/8/17 | |
* @since 5.6.0 | |
* | |
* @param $post_id (int) | |
* @return $post_id (int) | |
*/ | |
initialize: function( id, args ){ | |
// defaults | |
args = acf.parseArgs(args, { | |
tinymce: true, | |
quicktags: true, | |
toolbar: 'full', | |
mode: 'visual', // visual,text | |
field: false | |
}); | |
// tinymce | |
if( args.tinymce ) { | |
this.initializeTinymce( id, args ); | |
} | |
// quicktags | |
if( args.quicktags ) { | |
this.initializeQuicktags( id, args ); | |
} | |
}, | |
/* | |
* initializeTinymce | |
* | |
* This function will initialize the tinymce instance | |
* | |
* @type function | |
* @date 18/8/17 | |
* @since 5.6.0 | |
* | |
* @param $post_id (int) | |
* @return $post_id (int) | |
*/ | |
initializeTinymce: function( id, args ){ | |
// vars | |
var $textarea = $('#'+id); | |
var defaults = this.defaults(); | |
var toolbars = acf.get('toolbars'); | |
var field = args.field || false; | |
var $field = field.$el || false; | |
// bail early | |
if( typeof tinymce === 'undefined' ) return false; | |
if( !defaults ) return false; | |
// check if exists | |
if( tinymce.get(id) ) { | |
return this.enable( id ); | |
} | |
// settings | |
var init = $.extend( {}, defaults.tinymce, args.tinymce ); | |
init.id = id; | |
init.selector = '#' + id; | |
// toolbar | |
var toolbar = args.toolbar; | |
if( toolbar && toolbars && toolbars[toolbar] ) { | |
for( var i = 1; i <= 4; i++ ) { | |
init[ 'toolbar' + i ] = toolbars[toolbar][i] || ''; | |
} | |
} | |
// event | |
init.setup = function( ed ){ | |
ed.on('change', function(e) { | |
ed.save(); // save to textarea | |
$textarea.trigger('change'); | |
}); | |
// Fix bug where Gutenberg does not hear "mouseup" event and tries to select multiple blocks. | |
ed.on('mouseup', function(e) { | |
var event = new MouseEvent('mouseup'); | |
window.dispatchEvent(event); | |
}); | |
// Temporarily comment out. May not be necessary due to wysiwyg field actions. | |
//ed.on('unload', function(e) { | |
// acf.tinymce.remove( id ); | |
//}); | |
}; | |
// disable wp_autoresize_on (no solution yet for fixed toolbar) | |
init.wp_autoresize_on = false; | |
// Enable wpautop allowing value to save without <p> tags. | |
// Only if the "TinyMCE Advanced" plugin hasn't already set this functionality. | |
if( !init.tadv_noautop ) { | |
init.wpautop = true; | |
} | |
// hook for 3rd party customization | |
init = acf.applyFilters('wysiwyg_tinymce_settings', init, id, field); | |
// z-index fix (caused too many conflicts) | |
//if( acf.isset(tinymce,'ui','FloatPanel') ) { | |
// tinymce.ui.FloatPanel.zIndex = 900000; | |
//} | |
// store settings | |
tinyMCEPreInit.mceInit[ id ] = init; | |
// visual tab is active | |
if( args.mode == 'visual' ) { | |
// init | |
var result = tinymce.init( init ); | |
// get editor | |
var ed = tinymce.get( id ); | |
// validate | |
if( !ed ) { | |
return false; | |
} | |
// add reference | |
ed.acf = args.field; | |
// action | |
acf.doAction('wysiwyg_tinymce_init', ed, ed.id, init, field); | |
} | |
}, | |
/* | |
* initializeQuicktags | |
* | |
* This function will initialize the quicktags instance | |
* | |
* @type function | |
* @date 18/8/17 | |
* @since 5.6.0 | |
* | |
* @param $post_id (int) | |
* @return $post_id (int) | |
*/ | |
initializeQuicktags: function( id, args ){ | |
// vars | |
var defaults = this.defaults(); | |
// bail early | |
if( typeof quicktags === 'undefined' ) return false; | |
if( !defaults ) return false; | |
// settings | |
var init = $.extend( {}, defaults.quicktags, args.quicktags ); | |
init.id = id; | |
// filter | |
var field = args.field || false; | |
var $field = field.$el || false; | |
init = acf.applyFilters('wysiwyg_quicktags_settings', init, init.id, field); | |
// store settings | |
tinyMCEPreInit.qtInit[ id ] = init; | |
// init | |
var ed = quicktags( init ); | |
// validate | |
if( !ed ) { | |
return false; | |
} | |
// generate HTML | |
this.buildQuicktags( ed ); | |
// action for 3rd party customization | |
acf.doAction('wysiwyg_quicktags_init', ed, ed.id, init, field); | |
}, | |
/* | |
* buildQuicktags | |
* | |
* This function will build the quicktags HTML | |
* | |
* @type function | |
* @date 18/8/17 | |
* @since 5.6.0 | |
* | |
* @param $post_id (int) | |
* @return $post_id (int) | |
*/ | |
buildQuicktags: function( ed ){ | |
var canvas, name, settings, theButtons, html, ed, id, i, use, instanceId, | |
defaults = ',strong,em,link,block,del,ins,img,ul,ol,li,code,more,close,'; | |
canvas = ed.canvas; | |
name = ed.name; | |
settings = ed.settings; | |
html = ''; | |
theButtons = {}; | |
use = ''; | |
instanceId = ed.id; | |
// set buttons | |
if ( settings.buttons ) { | |
use = ','+settings.buttons+','; | |
} | |
for ( i in edButtons ) { | |
if ( ! edButtons[i] ) { | |
continue; | |
} | |
id = edButtons[i].id; | |
if ( use && defaults.indexOf( ',' + id + ',' ) !== -1 && use.indexOf( ',' + id + ',' ) === -1 ) { | |
continue; | |
} | |
if ( ! edButtons[i].instance || edButtons[i].instance === instanceId ) { | |
theButtons[id] = edButtons[i]; | |
if ( edButtons[i].html ) { | |
html += edButtons[i].html( name + '_' ); | |
} | |
} | |
} | |
if ( use && use.indexOf(',dfw,') !== -1 ) { | |
theButtons.dfw = new QTags.DFWButton(); | |
html += theButtons.dfw.html( name + '_' ); | |
} | |
if ( 'rtl' === document.getElementsByTagName( 'html' )[0].dir ) { | |
theButtons.textdirection = new QTags.TextDirectionButton(); | |
html += theButtons.textdirection.html( name + '_' ); | |
} | |
ed.toolbar.innerHTML = html; | |
ed.theButtons = theButtons; | |
if ( typeof jQuery !== 'undefined' ) { | |
jQuery( document ).triggerHandler( 'quicktags-init', [ ed ] ); | |
} | |
}, | |
disable: function( id ){ | |
this.destroyTinymce( id ); | |
}, | |
remove: function( id ){ | |
this.destroyTinymce( id ); | |
}, | |
destroy: function( id ){ | |
this.destroyTinymce( id ); | |
}, | |
destroyTinymce: function( id ){ | |
// bail early | |
if( typeof tinymce === 'undefined' ) return false; | |
// get editor | |
var ed = tinymce.get( id ); | |
// bail early if no editor | |
if( !ed ) return false; | |
// save | |
ed.save(); | |
// destroy editor | |
ed.destroy(); | |
// return | |
return true; | |
}, | |
enable: function( id ){ | |
this.enableTinymce( id ); | |
}, | |
enableTinymce: function( id ){ | |
// bail early | |
if( typeof switchEditors === 'undefined' ) return false; | |
// bail ealry if not initialized | |
if( typeof tinyMCEPreInit.mceInit[ id ] === 'undefined' ) return false; | |
// toggle | |
switchEditors.go( id, 'tmce'); | |
// return | |
return true; | |
} | |
}; | |
var editorManager = new acf.Model({ | |
// hook in before fieldsEventManager, conditions, etc | |
priority: 5, | |
actions: { | |
'prepare': 'onPrepare', | |
'ready': 'onReady', | |
}, | |
onPrepare: function(){ | |
// find hidden editor which may exist within a field | |
var $div = $('#acf-hidden-wp-editor'); | |
// move to footer | |
if( $div.exists() ) { | |
$div.appendTo('body'); | |
} | |
}, | |
onReady: function(){ | |
// Restore wp.editor functions used by tinymce removed in WP5. | |
if( acf.isset(window,'wp','oldEditor') ) { | |
wp.editor.autop = wp.oldEditor.autop; | |
wp.editor.removep = wp.oldEditor.removep; | |
} | |
// bail early if no tinymce | |
if( !acf.isset(window,'tinymce','on') ) return; | |
// restore default activeEditor | |
tinymce.on('AddEditor', function( data ){ | |
// vars | |
var editor = data.editor; | |
// bail early if not 'acf' | |
if( editor.id.substr(0, 3) !== 'acf' ) return; | |
// override if 'content' exists | |
editor = tinymce.editors.content || editor; | |
// update vars | |
tinymce.activeEditor = editor; | |
wpActiveEditor = editor.id; | |
}); | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* Validator | |
* | |
* The model for validating forms | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
var Validator = acf.Model.extend({ | |
/** @var string The model identifier. */ | |
id: 'Validator', | |
/** @var object The model data. */ | |
data: { | |
/** @var array The form errors. */ | |
errors: [], | |
/** @var object The form notice. */ | |
notice: null, | |
/** @var string The form status. loading, invalid, valid */ | |
status: '' | |
}, | |
/** @var object The model events. */ | |
events: { | |
'changed:status': 'onChangeStatus' | |
}, | |
/** | |
* addErrors | |
* | |
* Adds errors to the form. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param array errors An array of errors. | |
* @return void | |
*/ | |
addErrors: function( errors ){ | |
errors.map( this.addError, this ); | |
}, | |
/** | |
* addError | |
* | |
* Adds and error to the form. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object error An error object containing input and message. | |
* @return void | |
*/ | |
addError: function( error ){ | |
this.data.errors.push( error ); | |
}, | |
/** | |
* hasErrors | |
* | |
* Returns true if the form has errors. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return bool | |
*/ | |
hasErrors: function(){ | |
return this.data.errors.length; | |
}, | |
/** | |
* clearErrors | |
* | |
* Removes any errors. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
clearErrors: function(){ | |
return this.data.errors = []; | |
}, | |
/** | |
* getErrors | |
* | |
* Returns the forms errors. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return array | |
*/ | |
getErrors: function(){ | |
return this.data.errors; | |
}, | |
/** | |
* getFieldErrors | |
* | |
* Returns the forms field errors. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return array | |
*/ | |
getFieldErrors: function(){ | |
// vars | |
var errors = []; | |
var inputs = []; | |
// loop | |
this.getErrors().map(function(error){ | |
// bail early if global | |
if( !error.input ) return; | |
// update if exists | |
var i = inputs.indexOf(error.input); | |
if( i > -1 ) { | |
errors[ i ] = error; | |
// update | |
} else { | |
errors.push( error ); | |
inputs.push( error.input ); | |
} | |
}); | |
// return | |
return errors; | |
}, | |
/** | |
* getGlobalErrors | |
* | |
* Returns the forms global errors (errors without a specific input). | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return array | |
*/ | |
getGlobalErrors: function(){ | |
// return array of errors that contain no input | |
return this.getErrors().filter(function(error){ | |
return !error.input; | |
}); | |
}, | |
/** | |
* showErrors | |
* | |
* Displays all errors for this form. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
showErrors: function(){ | |
// bail early if no errors | |
if( !this.hasErrors() ) { | |
return; | |
} | |
// vars | |
var fieldErrors = this.getFieldErrors(); | |
var globalErrors = this.getGlobalErrors(); | |
// vars | |
var errorCount = 0; | |
var $scrollTo = false; | |
// loop | |
fieldErrors.map(function( error ){ | |
// get input | |
var $input = this.$('[name="' + error.input + '"]').first(); | |
// if $_POST value was an array, this $input may not exist | |
if( !$input.length ) { | |
$input = this.$('[name^="' + error.input + '"]').first(); | |
} | |
// bail early if input doesn't exist | |
if( !$input.length ) { | |
return; | |
} | |
// increase | |
errorCount++; | |
// get field | |
var field = acf.getClosestField( $input ); | |
// show error | |
field.showError( error.message ); | |
// set $scrollTo | |
if( !$scrollTo ) { | |
$scrollTo = field.$el; | |
} | |
}, this); | |
// errorMessage | |
var errorMessage = acf.__('Validation failed'); | |
globalErrors.map(function( error ){ | |
errorMessage += '. ' + error.message; | |
}); | |
if( errorCount == 1 ) { | |
errorMessage += '. ' + acf.__('1 field requires attention'); | |
} else if( errorCount > 1 ) { | |
errorMessage += '. ' + acf.__('%d fields require attention').replace('%d', errorCount); | |
} | |
// notice | |
if( this.has('notice') ) { | |
this.get('notice').update({ | |
type: 'error', | |
text: errorMessage | |
}); | |
} else { | |
var notice = acf.newNotice({ | |
type: 'error', | |
text: errorMessage, | |
target: this.$el | |
}); | |
this.set('notice', notice); | |
} | |
// if no $scrollTo, set to message | |
if( !$scrollTo ) { | |
$scrollTo = this.get('notice').$el; | |
} | |
// timeout | |
setTimeout(function(){ | |
$("html, body").animate({ scrollTop: $scrollTo.offset().top - ( $(window).height() / 2 ) }, 500); | |
}, 10); | |
}, | |
/** | |
* onChangeStatus | |
* | |
* Update the form class when changing the 'status' data | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The form element. | |
* @param string value The new status. | |
* @param string prevValue The old status. | |
* @return void | |
*/ | |
onChangeStatus: function( e, $el, value, prevValue ){ | |
this.$el.removeClass('is-'+prevValue).addClass('is-'+value); | |
}, | |
/** | |
* validate | |
* | |
* Vaildates the form via AJAX. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object args A list of settings to customize the validation process. | |
* @return bool True if the form is valid. | |
*/ | |
validate: function( args ){ | |
// default args | |
args = acf.parseArgs(args, { | |
// trigger event | |
event: false, | |
// reset the form after submit | |
reset: false, | |
// loading callback | |
loading: function(){}, | |
// complete callback | |
complete: function(){}, | |
// failure callback | |
failure: function(){}, | |
// success callback | |
success: function( $form ){ | |
$form.submit(); | |
} | |
}); | |
// return true if is valid - allows form submit | |
if( this.get('status') == 'valid' ) { | |
return true; | |
} | |
// return false if is currently validating - prevents form submit | |
if( this.get('status') == 'validating' ) { | |
return false; | |
} | |
// return true if no ACF fields exist (no need to validate) | |
if( !this.$('.acf-field').length ) { | |
return true; | |
} | |
// if event is provided, create a new success callback. | |
if( args.event ) { | |
var event = $.Event(null, args.event); | |
args.success = function(){ | |
acf.enableSubmit( $(event.target) ).trigger( event ); | |
} | |
} | |
// action for 3rd party | |
acf.doAction('validation_begin', this.$el); | |
// lock form | |
acf.lockForm( this.$el ); | |
// loading callback | |
args.loading( this.$el, this ); | |
// update status | |
this.set('status', 'validating'); | |
// success callback | |
var onSuccess = function( json ){ | |
// validate | |
if( !acf.isAjaxSuccess(json) ) { | |
return; | |
} | |
// filter | |
var data = acf.applyFilters('validation_complete', json.data, this.$el, this); | |
// add errors | |
if( !data.valid ) { | |
this.addErrors( data.errors ); | |
} | |
}; | |
// complete | |
var onComplete = function(){ | |
// unlock form | |
acf.unlockForm( this.$el ); | |
// failure | |
if( this.hasErrors() ) { | |
// update status | |
this.set('status', 'invalid'); | |
// action | |
acf.doAction('validation_failure', this.$el, this); | |
// display errors | |
this.showErrors(); | |
// failure callback | |
args.failure( this.$el, this ); | |
// success | |
} else { | |
// update status | |
this.set('status', 'valid'); | |
// remove previous error message | |
if( this.has('notice') ) { | |
this.get('notice').update({ | |
type: 'success', | |
text: acf.__('Validation successful'), | |
timeout: 1000 | |
}); | |
} | |
// action | |
acf.doAction('validation_success', this.$el, this); | |
acf.doAction('submit', this.$el); | |
// success callback (submit form) | |
args.success( this.$el, this ); | |
// lock form | |
acf.lockForm( this.$el ); | |
// reset | |
if( args.reset ) { | |
this.reset(); | |
} | |
} | |
// complete callback | |
args.complete( this.$el, this ); | |
// clear errors | |
this.clearErrors(); | |
}; | |
// serialize form data | |
var data = acf.serialize( this.$el ); | |
data.action = 'acf/validate_save_post'; | |
// ajax | |
$.ajax({ | |
url: acf.get('ajaxurl'), | |
data: acf.prepareForAjax(data), | |
type: 'post', | |
dataType: 'json', | |
context: this, | |
success: onSuccess, | |
complete: onComplete | |
}); | |
// return false to fail validation and allow AJAX | |
return false | |
}, | |
/** | |
* setup | |
* | |
* Called during the constructor function to setup this instance | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $form The form element. | |
* @return void | |
*/ | |
setup: function( $form ){ | |
// set $el | |
this.$el = $form; | |
}, | |
/** | |
* reset | |
* | |
* Rests the validation to be used again. | |
* | |
* @date 6/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
reset: function(){ | |
// reset data | |
this.set('errors', []); | |
this.set('notice', null); | |
this.set('status', ''); | |
// unlock form | |
acf.unlockForm( this.$el ); | |
} | |
}); | |
/** | |
* getValidator | |
* | |
* Returns the instance for a given form element. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $el The form element. | |
* @return object | |
*/ | |
var getValidator = function( $el ){ | |
// instantiate | |
var validator = $el.data('acf'); | |
if( !validator ) { | |
validator = new Validator( $el ); | |
} | |
// return | |
return validator; | |
}; | |
/** | |
* acf.validateForm | |
* | |
* A helper function for the Validator.validate() function. | |
* Returns true if form is valid, or fetches a validation request and returns false. | |
* | |
* @date 4/4/18 | |
* @since 5.6.9 | |
* | |
* @param object args A list of settings to customize the validation process. | |
* @return bool | |
*/ | |
acf.validateForm = function( args ){ | |
return getValidator( args.form ).validate( args ); | |
}; | |
/** | |
* acf.enableSubmit | |
* | |
* Enables a submit button and returns the element. | |
* | |
* @date 30/8/18 | |
* @since 5.7.4 | |
* | |
* @param jQuery $submit The submit button. | |
* @return jQuery | |
*/ | |
acf.enableSubmit = function( $submit ){ | |
return $submit.removeClass('disabled'); | |
}; | |
/** | |
* acf.disableSubmit | |
* | |
* Disables a submit button and returns the element. | |
* | |
* @date 30/8/18 | |
* @since 5.7.4 | |
* | |
* @param jQuery $submit The submit button. | |
* @return jQuery | |
*/ | |
acf.disableSubmit = function( $submit ){ | |
return $submit.addClass('disabled'); | |
}; | |
/** | |
* acf.showSpinner | |
* | |
* Shows the spinner element. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $spinner The spinner element. | |
* @return jQuery | |
*/ | |
acf.showSpinner = function( $spinner ){ | |
$spinner.addClass('is-active'); // add class (WP > 4.2) | |
$spinner.css('display', 'inline-block'); // css (WP < 4.2) | |
return $spinner; | |
}; | |
/** | |
* acf.hideSpinner | |
* | |
* Hides the spinner element. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $spinner The spinner element. | |
* @return jQuery | |
*/ | |
acf.hideSpinner = function( $spinner ){ | |
$spinner.removeClass('is-active'); // add class (WP > 4.2) | |
$spinner.css('display', 'none'); // css (WP < 4.2) | |
return $spinner; | |
}; | |
/** | |
* acf.lockForm | |
* | |
* Locks a form by disabeling its primary inputs and showing a spinner. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $form The form element. | |
* @return jQuery | |
*/ | |
acf.lockForm = function( $form ){ | |
// vars | |
var $wrap = findSubmitWrap( $form ); | |
var $submit = $wrap.find('.button, [type="submit"]'); | |
var $spinner = $wrap.find('.spinner, .acf-spinner'); | |
// hide all spinners (hides the preview spinner) | |
acf.hideSpinner( $spinner ); | |
// lock | |
acf.disableSubmit( $submit ); | |
acf.showSpinner( $spinner.last() ); | |
return $form; | |
}; | |
/** | |
* acf.unlockForm | |
* | |
* Unlocks a form by enabeling its primary inputs and hiding all spinners. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $form The form element. | |
* @return jQuery | |
*/ | |
acf.unlockForm = function( $form ){ | |
// vars | |
var $wrap = findSubmitWrap( $form ); | |
var $submit = $wrap.find('.button, [type="submit"]'); | |
var $spinner = $wrap.find('.spinner, .acf-spinner'); | |
// unlock | |
acf.enableSubmit( $submit ); | |
acf.hideSpinner( $spinner ); | |
return $form; | |
}; | |
/** | |
* findSubmitWrap | |
* | |
* An internal function to find the 'primary' form submit wrapping element. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $form The form element. | |
* @return jQuery | |
*/ | |
var findSubmitWrap = function( $form ){ | |
// default post submit div | |
var $wrap = $form.find('#submitdiv'); | |
if( $wrap.length ) { | |
return $wrap; | |
} | |
// 3rd party publish box | |
var $wrap = $form.find('#submitpost'); | |
if( $wrap.length ) { | |
return $wrap; | |
} | |
// term, user | |
var $wrap = $form.find('p.submit').last(); | |
if( $wrap.length ) { | |
return $wrap; | |
} | |
// front end form | |
var $wrap = $form.find('.acf-form-submit'); | |
if( $wrap.length ) { | |
return $wrap; | |
} | |
// default | |
return $form; | |
}; | |
/** | |
* acf.validation | |
* | |
* Global validation logic | |
* | |
* @date 4/4/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
acf.validation = new acf.Model({ | |
/** @var string The model identifier. */ | |
id: 'validation', | |
/** @var bool The active state. Set to false before 'prepare' to prevent validation. */ | |
active: true, | |
/** @var string The model initialize time. */ | |
wait: 'prepare', | |
/** @var object The model actions. */ | |
actions: { | |
'ready': 'addInputEvents', | |
'append': 'addInputEvents' | |
}, | |
/** @var object The model events. */ | |
events: { | |
'click input[type="submit"]': 'onClickSubmit', | |
'click button[type="submit"]': 'onClickSubmit', | |
//'click #editor .editor-post-publish-button': 'onClickSubmitGutenberg', | |
'click #save-post': 'onClickSave', | |
'submit form#post': 'onSubmitPost', | |
'submit form': 'onSubmit', | |
}, | |
/** | |
* initialize | |
* | |
* Called when initializing the model. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
initialize: function(){ | |
// check 'validation' setting | |
if( !acf.get('validation') ) { | |
this.active = false; | |
this.actions = {}; | |
this.events = {}; | |
} | |
}, | |
/** | |
* enable | |
* | |
* Enables validation. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
enable: function(){ | |
this.active = true; | |
}, | |
/** | |
* disable | |
* | |
* Disables validation. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param void | |
* @return void | |
*/ | |
disable: function(){ | |
this.active = false; | |
}, | |
/** | |
* reset | |
* | |
* Rests the form validation to be used again | |
* | |
* @date 6/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $form The form element. | |
* @return void | |
*/ | |
reset: function( $form ){ | |
getValidator( $form ).reset(); | |
}, | |
/** | |
* addInputEvents | |
* | |
* Adds 'invalid' event listeners to HTML inputs. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param jQuery $el The element being added / readied. | |
* @return void | |
*/ | |
addInputEvents: function( $el ){ | |
// Bug exists in Safari where custom "invalid" handeling prevents draft from saving. | |
if( acf.get('browser') === 'safari' ) | |
return; | |
// vars | |
var $inputs = $('.acf-field [name]', $el); | |
// check | |
if( $inputs.length ) { | |
this.on( $inputs, 'invalid', 'onInvalid' ); | |
} | |
}, | |
/** | |
* onInvalid | |
* | |
* Callback for the 'invalid' event. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onInvalid: function( e, $el ){ | |
// prevent default | |
// - prevents browser error message | |
// - also fixes chrome bug where 'hidden-by-tab' field throws focus error | |
e.preventDefault(); | |
// vars | |
var $form = $el.closest('form'); | |
// check form exists | |
if( $form.length ) { | |
// add error to validator | |
getValidator( $form ).addError({ | |
input: $el.attr('name'), | |
message: e.target.validationMessage | |
}); | |
// trigger submit on $form | |
// - allows for "save", "preview" and "publish" to work | |
$form.submit(); | |
} | |
}, | |
/** | |
* onClickSubmit | |
* | |
* Callback when clicking submit. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onClickSubmit: function( e, $el ){ | |
// store the "click event" for later use in this.onSubmit() | |
this.set('originalEvent', e); | |
}, | |
/** | |
* onClickSave | |
* | |
* Set ignore to true when saving a draft. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onClickSave: function( e, $el ) { | |
this.set('ignore', true); | |
}, | |
/** | |
* onClickSubmitGutenberg | |
* | |
* Custom validation event for the gutenberg editor. | |
* | |
* @date 29/10/18 | |
* @since 5.8.0 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onClickSubmitGutenberg: function( e, $el ){ | |
// validate | |
var valid = acf.validateForm({ | |
form: $('#editor'), | |
event: e, | |
reset: true, | |
failure: function( $form, validator ){ | |
var $notice = validator.get('notice').$el; | |
$notice.appendTo('.components-notice-list'); | |
$notice.find('.acf-notice-dismiss').removeClass('small'); | |
} | |
}); | |
// if not valid, stop event and allow validation to continue | |
if( !valid ) { | |
e.preventDefault(); | |
e.stopImmediatePropagation(); | |
} | |
}, | |
/** | |
* onSubmitPost | |
* | |
* Callback when the 'post' form is submit. | |
* | |
* @date 5/3/19 | |
* @since 5.7.13 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onSubmitPost: function( e, $el ) { | |
// Check if is preview. | |
if( $('input#wp-preview').val() === 'dopreview' ) { | |
// Ignore validation. | |
this.set('ignore', true); | |
// Unlock form to fix conflict with core "submit.edit-post" event causing all submit buttons to be disabled. | |
acf.unlockForm( $el ) | |
} | |
}, | |
/** | |
* onSubmit | |
* | |
* Callback when the form is submit. | |
* | |
* @date 4/9/18 | |
* @since 5.7.5 | |
* | |
* @param object e The event object. | |
* @param jQuery $el The input element. | |
* @return void | |
*/ | |
onSubmit: function( e, $el ){ | |
// Allow form to submit if... | |
if( | |
// Validation has been disabled. | |
!this.active | |
// Or this event is to be ignored. | |
|| this.get('ignore') | |
// Or this event has already been prevented. | |
|| e.isDefaultPrevented() | |
) { | |
// Return early and call reset function. | |
return this.allowSubmit(); | |
} | |
// Validate form. | |
var valid = acf.validateForm({ | |
form: $el, | |
event: this.get('originalEvent') | |
}); | |
// If not valid, stop event to prevent form submit. | |
if( !valid ) { | |
e.preventDefault(); | |
} | |
}, | |
/** | |
* allowSubmit | |
* | |
* Resets data during onSubmit when the form is allowed to submit. | |
* | |
* @date 5/3/19 | |
* @since 5.7.13 | |
* | |
* @param void | |
* @return void | |
*/ | |
allowSubmit: function(){ | |
// Reset "ignore" state. | |
this.set('ignore', false); | |
// Reset "originalEvent" object. | |
this.set('originalEvent', false); | |
// Return true | |
return true; | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* refreshHelper | |
* | |
* description | |
* | |
* @date 1/7/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var refreshHelper = new acf.Model({ | |
priority: 90, | |
initialize: function(){ | |
this.refresh = acf.debounce( this.refresh, 0 ); | |
}, | |
actions: { | |
'new_field': 'refresh', | |
'show_field': 'refresh', | |
'hide_field': 'refresh', | |
'remove_field': 'refresh', | |
'unmount_field': 'refresh', | |
'remount_field': 'refresh', | |
}, | |
refresh: function(){ | |
acf.doAction('refresh'); | |
$(window).trigger('acfrefresh'); | |
} | |
}); | |
/** | |
* mountHelper | |
* | |
* Adds compatiblity for the 'unmount' and 'remount' actions added in 5.8.0 | |
* | |
* @date 7/3/19 | |
* @since 5.7.14 | |
* | |
* @param void | |
* @return void | |
*/ | |
var mountHelper = new acf.Model({ | |
priority: 1, | |
actions: { | |
'sortstart': 'onSortstart', | |
'sortstop': 'onSortstop' | |
}, | |
onSortstart: function( $item ){ | |
acf.doAction('unmount', $item); | |
}, | |
onSortstop: function( $item ){ | |
acf.doAction('remount', $item); | |
} | |
}); | |
/** | |
* sortableHelper | |
* | |
* Adds compatibility for sorting a <tr> element | |
* | |
* @date 6/3/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
var sortableHelper = new acf.Model({ | |
actions: { | |
'sortstart': 'onSortstart' | |
}, | |
onSortstart: function( $item, $placeholder ){ | |
// if $item is a tr, apply some css to the elements | |
if( $item.is('tr') ) { | |
// replace $placeholder children with a single td | |
// fixes "width calculation issues" due to conditional logic hiding some children | |
$placeholder.html('<td style="padding:0;" colspan="' + $placeholder.children().length + '"></td>'); | |
// add helper class to remove absolute positioning | |
$item.addClass('acf-sortable-tr-helper'); | |
// set fixed widths for children | |
$item.children().each(function(){ | |
$(this).width( $(this).width() ); | |
}); | |
// mimic height | |
$placeholder.height( $item.height() + 'px' ); | |
// remove class | |
$item.removeClass('acf-sortable-tr-helper'); | |
} | |
} | |
}); | |
/** | |
* duplicateHelper | |
* | |
* Fixes browser bugs when duplicating an element | |
* | |
* @date 6/3/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
var duplicateHelper = new acf.Model({ | |
actions: { | |
'after_duplicate': 'onAfterDuplicate' | |
}, | |
onAfterDuplicate: function( $el, $el2 ){ | |
// get original values | |
var vals = []; | |
$el.find('select').each(function(i){ | |
vals.push( $(this).val() ); | |
}); | |
// set duplicate values | |
$el2.find('select').each(function(i){ | |
$(this).val( vals[i] ); | |
}); | |
} | |
}); | |
/** | |
* tableHelper | |
* | |
* description | |
* | |
* @date 6/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var tableHelper = new acf.Model({ | |
id: 'tableHelper', | |
priority: 20, | |
actions: { | |
'refresh': 'renderTables' | |
}, | |
renderTables: function( $el ){ | |
// loop | |
var self = this; | |
$('.acf-table:visible').each(function(){ | |
self.renderTable( $(this) ); | |
}); | |
}, | |
renderTable: function( $table ){ | |
// vars | |
var $ths = $table.find('> thead > tr:visible > th[data-key]'); | |
var $tds = $table.find('> tbody > tr:visible > td[data-key]'); | |
// bail early if no thead | |
if( !$ths.length || !$tds.length ) { | |
return false; | |
} | |
// visiblity | |
$ths.each(function( i ){ | |
// vars | |
var $th = $(this); | |
var key = $th.data('key'); | |
var $cells = $tds.filter('[data-key="' + key + '"]'); | |
var $hidden = $cells.filter('.acf-hidden'); | |
// always remove empty and allow cells to be hidden | |
$cells.removeClass('acf-empty'); | |
// hide $th if all cells are hidden | |
if( $cells.length === $hidden.length ) { | |
acf.hide( $th ); | |
// force all hidden cells to appear empty | |
} else { | |
acf.show( $th ); | |
$hidden.addClass('acf-empty'); | |
} | |
}); | |
// clear width | |
$ths.css('width', 'auto'); | |
// get visible | |
$ths = $ths.not('.acf-hidden'); | |
// vars | |
var availableWidth = 100; | |
var colspan = $ths.length; | |
// set custom widths first | |
var $fixedWidths = $ths.filter('[data-width]'); | |
$fixedWidths.each(function(){ | |
var width = $(this).data('width'); | |
$(this).css('width', width + '%'); | |
availableWidth -= width; | |
}); | |
// set auto widths | |
var $auoWidths = $ths.not('[data-width]'); | |
if( $auoWidths.length ) { | |
var width = availableWidth / $auoWidths.length; | |
$auoWidths.css('width', width + '%'); | |
availableWidth = 0; | |
} | |
// avoid stretching issue | |
if( availableWidth > 0 ) { | |
$ths.last().css('width', 'auto'); | |
} | |
// update colspan on collapsed | |
$tds.filter('.-collapsed-target').each(function(){ | |
// vars | |
var $td = $(this); | |
// check if collapsed | |
if( $td.parent().hasClass('-collapsed') ) { | |
$td.attr('colspan', $ths.length); | |
} else { | |
$td.removeAttr('colspan'); | |
} | |
}); | |
} | |
}); | |
/** | |
* fieldsHelper | |
* | |
* description | |
* | |
* @date 6/3/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var fieldsHelper = new acf.Model({ | |
id: 'fieldsHelper', | |
priority: 30, | |
actions: { | |
'refresh': 'renderGroups' | |
}, | |
renderGroups: function(){ | |
// loop | |
var self = this; | |
$('.acf-fields:visible').each(function(){ | |
self.renderGroup( $(this) ); | |
}); | |
}, | |
renderGroup: function( $el ){ | |
// vars | |
var top = 0; | |
var height = 0; | |
var $row = $(); | |
// get fields | |
var $fields = $el.children('.acf-field[data-width]:visible'); | |
// bail early if no fields | |
if( !$fields.length ) { | |
return false; | |
} | |
// bail ealry if is .-left | |
if( $el.hasClass('-left') ) { | |
$fields.removeAttr('data-width'); | |
$fields.css('width', 'auto'); | |
return false; | |
} | |
// reset fields | |
$fields.removeClass('-r0 -c0').css({'min-height': 0}); | |
// loop | |
$fields.each(function( i ){ | |
// vars | |
var $field = $(this); | |
var position = $field.position(); | |
var thisTop = Math.ceil( position.top ); | |
var thisLeft = Math.ceil( position.left ); | |
// detect change in row | |
if( $row.length && thisTop > top ) { | |
// set previous heights | |
$row.css({'min-height': height+'px'}); | |
// update position due to change in row above | |
position = $field.position(); | |
thisTop = Math.ceil( position.top ); | |
thisLeft = Math.ceil( position.left ); | |
// reset vars | |
top = 0; | |
height = 0; | |
$row = $(); | |
} | |
// rtl | |
if( acf.get('rtl') ) { | |
thisLeft = Math.ceil( $field.parent().width() - (position.left + $field.outerWidth()) ); | |
} | |
// add classes | |
if( thisTop == 0 ) { | |
$field.addClass('-r0'); | |
} else if( thisLeft == 0 ) { | |
$field.addClass('-c0'); | |
} | |
// get height after class change | |
// - add 1 for subpixel rendering | |
var thisHeight = Math.ceil( $field.outerHeight() ) + 1; | |
// set height | |
height = Math.max( height, thisHeight ); | |
// set y | |
top = Math.max( top, thisTop ); | |
// append | |
$row = $row.add( $field ); | |
}); | |
// clean up | |
if( $row.length ) { | |
$row.css({'min-height': height+'px'}); | |
} | |
} | |
}); | |
})(jQuery); | |
(function($, undefined){ | |
/** | |
* acf.newCompatibility | |
* | |
* Inserts a new __proto__ object compatibility layer | |
* | |
* @date 15/2/18 | |
* @since 5.6.9 | |
* | |
* @param object instance The object to modify. | |
* @param object compatibilty Optional. The compatibilty layer. | |
* @return object compatibilty | |
*/ | |
acf.newCompatibility = function( instance, compatibilty ){ | |
// defaults | |
compatibilty = compatibilty || {}; | |
// inherit __proto_- | |
compatibilty.__proto__ = instance.__proto__; | |
// inject | |
instance.__proto__ = compatibilty; | |
// reference | |
instance.compatibility = compatibilty; | |
// return | |
return compatibilty; | |
}; | |
/** | |
* acf.getCompatibility | |
* | |
* Returns the compatibility layer for a given instance | |
* | |
* @date 13/3/18 | |
* @since 5.6.9 | |
* | |
* @param object instance The object to look in. | |
* @return object|null compatibility The compatibility object or null on failure. | |
*/ | |
acf.getCompatibility = function( instance ) { | |
return instance.compatibility || null; | |
}; | |
/** | |
* acf (compatibility) | |
* | |
* Compatibility layer for the acf object | |
* | |
* @date 15/2/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
var _acf = acf.newCompatibility(acf, { | |
// storage | |
l10n: {}, | |
o: {}, | |
fields: {}, | |
// changed function names | |
update: acf.set, | |
add_action: acf.addAction, | |
remove_action: acf.removeAction, | |
do_action: acf.doAction, | |
add_filter: acf.addFilter, | |
remove_filter: acf.removeFilter, | |
apply_filters: acf.applyFilters, | |
parse_args: acf.parseArgs, | |
disable_el: acf.disable, | |
disable_form: acf.disable, | |
enable_el: acf.enable, | |
enable_form: acf.enable, | |
update_user_setting: acf.updateUserSetting, | |
prepare_for_ajax: acf.prepareForAjax, | |
is_ajax_success: acf.isAjaxSuccess, | |
remove_el: acf.remove, | |
remove_tr: acf.remove, | |
str_replace: acf.strReplace, | |
render_select: acf.renderSelect, | |
get_uniqid: acf.uniqid, | |
serialize_form: acf.serialize, | |
esc_html: acf.strEscape, | |
str_sanitize: acf.strSanitize, | |
}); | |
_acf._e = function( k1, k2 ){ | |
// defaults | |
k1 = k1 || ''; | |
k2 = k2 || ''; | |
// compability | |
var compatKey = k2 ? k1 + '.' + k2 : k1; | |
var compats = { | |
'image.select': 'Select Image', | |
'image.edit': 'Edit Image', | |
'image.update': 'Update Image' | |
}; | |
if( compats[compatKey] ) { | |
return acf.__(compats[compatKey]); | |
} | |
// try k1 | |
var string = this.l10n[ k1 ] || ''; | |
// try k2 | |
if( k2 ) { | |
string = string[ k2 ] || ''; | |
} | |
// return | |
return string; | |
}; | |
_acf.get_selector = function( s ) { | |
// vars | |
var selector = '.acf-field'; | |
// bail early if no search | |
if( !s ) { | |
return selector; | |
} | |
// compatibility with object | |
if( $.isPlainObject(s) ) { | |
if( $.isEmptyObject(s) ) { | |
return selector; | |
} else { | |
for( var k in s ) { s = s[k]; break; } | |
} | |
} | |
// append | |
selector += '-' + s; | |
// replace underscores (split/join replaces all and is faster than regex!) | |
selector = acf.strReplace('_', '-', selector); | |
// remove potential double up | |
selector = acf.strReplace('field-field-', 'field-', selector); | |
// return | |
return selector; | |
}; | |
_acf.get_fields = function( s, $el, all ){ | |
// args | |
var args = { | |
is: s || '', | |
parent: $el || false, | |
suppressFilters: all || false, | |
}; | |
// change 'field_123' to '.acf-field-123' | |
if( args.is ) { | |
args.is = this.get_selector( args.is ); | |
} | |
// return | |
return acf.findFields(args); | |
}; | |
_acf.get_field = function( s, $el ){ | |
// get fields | |
var $fields = this.get_fields.apply(this, arguments); | |
// return | |
if( $fields.length ) { | |
return $fields.first(); | |
} else { | |
return false; | |
} | |
}; | |
_acf.get_closest_field = function( $el, s ){ | |
return $el.closest( this.get_selector(s) ); | |
}; | |
_acf.get_field_wrap = function( $el ){ | |
return $el.closest( this.get_selector() ); | |
}; | |
_acf.get_field_key = function( $field ){ | |
return $field.data('key'); | |
}; | |
_acf.get_field_type = function( $field ){ | |
return $field.data('type'); | |
}; | |
_acf.get_data = function( $el, defaults ){ | |
return acf.parseArgs( $el.data(), defaults ); | |
}; | |
_acf.maybe_get = function( obj, key, value ){ | |
// default | |
if( value === undefined ) { | |
value = null; | |
} | |
// get keys | |
keys = String(key).split('.'); | |
// acf.isget | |
for( var i = 0; i < keys.length; i++ ) { | |
if( !obj.hasOwnProperty(keys[i]) ) { | |
return value; | |
} | |
obj = obj[ keys[i] ]; | |
} | |
return obj; | |
}; | |
/** | |
* hooks | |
* | |
* Modify add_action and add_filter functions to add compatibility with changed $field parameter | |
* Using the acf.add_action() or acf.add_filter() functions will interpret new field parameters as jQuery $field | |
* | |
* @date 12/5/18 | |
* @since 5.6.9 | |
* | |
* @param void | |
* @return void | |
*/ | |
var compatibleArgument = function( arg ){ | |
return ( arg instanceof acf.Field ) ? arg.$el : arg; | |
}; | |
var compatibleArguments = function( args ){ | |
return acf.arrayArgs( args ).map( compatibleArgument ); | |
} | |
var compatibleCallback = function( origCallback ){ | |
return function(){ | |
// convert to compatible arguments | |
if( arguments.length ) { | |
var args = compatibleArguments(arguments); | |
// add default argument for 'ready', 'append' and 'load' events | |
} else { | |
var args = [ $(document) ]; | |
} | |
// return | |
return origCallback.apply(this, args); | |
} | |
} | |
_acf.add_action = function( action, callback, priority, context ){ | |
// handle multiple actions | |
var actions = action.split(' '); | |
var length = actions.length; | |
if( length > 1 ) { | |
for( var i = 0; i < length; i++) { | |
action = actions[i]; | |
_acf.add_action.apply(this, arguments); | |
} | |
return this; | |
} | |
// single | |
var callback = compatibleCallback(callback); | |
return acf.addAction.apply(this, arguments); | |
}; | |
_acf.add_filter = function( action, callback, priority, context ){ | |
var callback = compatibleCallback(callback); | |
return acf.addFilter.apply(this, arguments); | |
}; | |
/* | |
* acf.model | |
* | |
* This model acts as a scafold for action.event driven modules | |
* | |
* @type object | |
* @date 8/09/2014 | |
* @since 5.0.0 | |
* | |
* @param (object) | |
* @return (object) | |
*/ | |
_acf.model = { | |
actions: {}, | |
filters: {}, | |
events: {}, | |
extend: function( args ){ | |
// extend | |
var model = $.extend( {}, this, args ); | |
// setup actions | |
$.each(model.actions, function( name, callback ){ | |
model._add_action( name, callback ); | |
}); | |
// setup filters | |
$.each(model.filters, function( name, callback ){ | |
model._add_filter( name, callback ); | |
}); | |
// setup events | |
$.each(model.events, function( name, callback ){ | |
model._add_event( name, callback ); | |
}); | |
// return | |
return model; | |
}, | |
_add_action: function( name, callback ) { | |
// split | |
var model = this, | |
data = name.split(' '); | |
// add missing priority | |
var name = data[0] || '', | |
priority = data[1] || 10; | |
// add action | |
acf.add_action(name, model[ callback ], priority, model); | |
}, | |
_add_filter: function( name, callback ) { | |
// split | |
var model = this, | |
data = name.split(' '); | |
// add missing priority | |
var name = data[0] || '', | |
priority = data[1] || 10; | |
// add action | |
acf.add_filter(name, model[ callback ], priority, model); | |
}, | |
_add_event: function( name, callback ) { | |
// vars | |
var model = this, | |
i = name.indexOf(' '), | |
event = (i > 0) ? name.substr(0,i) : name, | |
selector = (i > 0) ? name.substr(i+1) : ''; | |
// event | |
var fn = function( e ){ | |
// append $el to event object | |
e.$el = $(this); | |
// append $field to event object (used in field group) | |
if( acf.field_group ) { | |
e.$field = e.$el.closest('.acf-field-object'); | |
} | |
// event | |
if( typeof model.event === 'function' ) { | |
e = model.event( e ); | |
} | |
// callback | |
model[ callback ].apply(model, arguments); | |
}; | |
// add event | |
if( selector ) { | |
$(document).on(event, selector, fn); | |
} else { | |
$(document).on(event, fn); | |
} | |
}, | |
get: function( name, value ){ | |
// defaults | |
value = value || null; | |
// get | |
if( typeof this[ name ] !== 'undefined' ) { | |
value = this[ name ]; | |
} | |
// return | |
return value; | |
}, | |
set: function( name, value ){ | |
// set | |
this[ name ] = value; | |
// function for 3rd party | |
if( typeof this[ '_set_' + name ] === 'function' ) { | |
this[ '_set_' + name ].apply(this); | |
} | |
// return for chaining | |
return this; | |
} | |
}; | |
/* | |
* field | |
* | |
* This model sets up many of the field's interactions | |
* | |
* @type function | |
* @date 21/02/2014 | |
* @since 3.5.1 | |
* | |
* @param n/a | |
* @return n/a | |
*/ | |
_acf.field = acf.model.extend({ | |
type: '', | |
o: {}, | |
$field: null, | |
_add_action: function( name, callback ) { | |
// vars | |
var model = this; | |
// update name | |
name = name + '_field/type=' + model.type; | |
// add action | |
acf.add_action(name, function( $field ){ | |
// focus | |
model.set('$field', $field); | |
// callback | |
model[ callback ].apply(model, arguments); | |
}); | |
}, | |
_add_filter: function( name, callback ) { | |
// vars | |
var model = this; | |
// update name | |
name = name + '_field/type=' + model.type; | |
// add action | |
acf.add_filter(name, function( $field ){ | |
// focus | |
model.set('$field', $field); | |
// callback | |
model[ callback ].apply(model, arguments); | |
}); | |
}, | |
_add_event: function( name, callback ) { | |
// vars | |
var model = this, | |
event = name.substr(0,name.indexOf(' ')), | |
selector = name.substr(name.indexOf(' ')+1), | |
context = acf.get_selector(model.type); | |
// add event | |
$(document).on(event, context + ' ' + selector, function( e ){ | |
// vars | |
var $el = $(this); | |
var $field = acf.get_closest_field( $el, model.type ); | |
// bail early if no field | |
if( !$field.length ) return; | |
// focus | |
if( !$field.is(model.$field) ) { | |
model.set('$field', $field); | |
} | |
// append to event | |
e.$el = $el; | |
e.$field = $field; | |
// callback | |
model[ callback ].apply(model, [e]); | |
}); | |
}, | |
_set_$field: function(){ | |
// callback | |
if( typeof this.focus === 'function' ) { | |
this.focus(); | |
} | |
}, | |
// depreciated | |
doFocus: function( $field ){ | |
return this.set('$field', $field); | |
} | |
}); | |
/** | |
* validation | |
* | |
* description | |
* | |
* @date 15/2/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
var _validation = acf.newCompatibility(acf.validation, { | |
remove_error: function( $field ){ | |
acf.getField( $field ).removeError(); | |
}, | |
add_warning: function( $field, message ){ | |
acf.getField( $field ).showNotice({ | |
text: message, | |
type: 'warning', | |
timeout: 1000 | |
}); | |
}, | |
fetch: acf.validateForm, | |
enableSubmit: acf.enableSubmit, | |
disableSubmit: acf.disableSubmit, | |
showSpinner: acf.showSpinner, | |
hideSpinner: acf.hideSpinner, | |
unlockForm: acf.unlockForm, | |
lockForm: acf.lockForm | |
}); | |
/** | |
* tooltip | |
* | |
* description | |
* | |
* @date 15/2/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
_acf.tooltip = { | |
tooltip: function( text, $el ){ | |
var tooltip = acf.newTooltip({ | |
text: text, | |
target: $el | |
}); | |
// return | |
return tooltip.$el; | |
}, | |
temp: function( text, $el ){ | |
var tooltip = acf.newTooltip({ | |
text: text, | |
target: $el, | |
timeout: 250 | |
}); | |
}, | |
confirm: function( $el, callback, text, button_y, button_n ){ | |
var tooltip = acf.newTooltip({ | |
confirm: true, | |
text: text, | |
target: $el, | |
confirm: function(){ | |
callback(true); | |
}, | |
cancel: function(){ | |
callback(false); | |
} | |
}); | |
}, | |
confirm_remove: function( $el, callback ){ | |
var tooltip = acf.newTooltip({ | |
confirmRemove: true, | |
target: $el, | |
confirm: function(){ | |
callback(true); | |
}, | |
cancel: function(){ | |
callback(false); | |
} | |
}); | |
}, | |
}; | |
/** | |
* tooltip | |
* | |
* description | |
* | |
* @date 15/2/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
_acf.media = new acf.Model({ | |
activeFrame: false, | |
actions: { | |
'new_media_popup': 'onNewMediaPopup' | |
}, | |
frame: function(){ | |
return this.activeFrame; | |
}, | |
onNewMediaPopup: function( popup ){ | |
this.activeFrame = popup.frame; | |
}, | |
popup: function( props ){ | |
// update props | |
if( props.mime_types ) { | |
props.allowedTypes = props.mime_types; | |
} | |
if( props.id ) { | |
props.attachment = props.id; | |
} | |
// new | |
var popup = acf.newMediaPopup( props ); | |
// append | |
/* | |
if( props.selected ) { | |
popup.selected = props.selected; | |
} | |
*/ | |
// return | |
return popup.frame; | |
} | |
}); | |
/** | |
* Select2 | |
* | |
* description | |
* | |
* @date 11/6/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
_acf.select2 = { | |
init: function( $select, args, $field ){ | |
// compatible args | |
if( args.allow_null ) { | |
args.allowNull = args.allow_null; | |
} | |
if( args.ajax_action ) { | |
args.ajaxAction = args.ajax_action; | |
} | |
if( $field ) { | |
args.field = acf.getField($field); | |
} | |
// return | |
return acf.newSelect2( $select, args ); | |
}, | |
destroy: function( $select ){ | |
return acf.getInstance( $select ).destroy(); | |
}, | |
}; | |
/** | |
* postbox | |
* | |
* description | |
* | |
* @date 11/6/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
_acf.postbox = { | |
render: function( args ){ | |
// compatible args | |
if( args.edit_url ) { | |
args.editLink = args.edit_url; | |
} | |
if( args.edit_title ) { | |
args.editTitle = args.edit_title; | |
} | |
// return | |
return acf.newPostbox( args ); | |
} | |
}; | |
/** | |
* acf.screen | |
* | |
* description | |
* | |
* @date 11/6/18 | |
* @since 5.6.9 | |
* | |
* @param type $var Description. Default. | |
* @return type Description. | |
*/ | |
acf.newCompatibility(acf.screen, { | |
update: function(){ | |
return this.set.apply(this, arguments); | |
}, | |
fetch: acf.screen.check | |
}); | |
_acf.ajax = acf.screen; | |
})(jQuery); | |
// @codekit-prepend "_acf.js"; | |
// @codekit-prepend "_acf-hooks.js"; | |
// @codekit-prepend "_acf-model.js"; | |
// @codekit-prepend "_acf-popup.js"; | |
// @codekit-prepend "_acf-unload.js"; | |
// @codekit-prepend "_acf-panel.js"; | |
// @codekit-prepend "_acf-notice.js"; | |
// @codekit-prepend "_acf-postbox.js"; | |
// @codekit-prepend "_acf-tooltip.js"; | |
// @codekit-prepend "_acf-field.js"; | |
// @codekit-prepend "_acf-fields.js"; | |
// @codekit-prepend "_acf-field-accordion.js"; | |
// @codekit-prepend "_acf-field-button-group.js"; | |
// @codekit-prepend "_acf-field-checkbox.js"; | |
// @codekit-prepend "_acf-field-color-picker.js"; | |
// @codekit-prepend "_acf-field-date-picker.js"; | |
// @codekit-prepend "_acf-field-date-time-picker.js"; | |
// @codekit-prepend "_acf-field-google-map.js"; | |
// @codekit-prepend "_acf-field-image.js"; | |
// @codekit-prepend "_acf-field-file.js"; | |
// @codekit-prepend "_acf-field-link.js"; | |
// @codekit-prepend "_acf-field-oembed.js"; | |
// @codekit-prepend "_acf-field-radio.js"; | |
// @codekit-prepend "_acf-field-range.js"; | |
// @codekit-prepend "_acf-field-relationship.js"; | |
// @codekit-prepend "_acf-field-select.js"; | |
// @codekit-prepend "_acf-field-tab.js"; | |
// @codekit-prepend "_acf-field-post-object.js"; | |
// @codekit-prepend "_acf-field-page-link.js"; | |
// @codekit-prepend "_acf-field-user.js"; | |
// @codekit-prepend "_acf-field-taxonomy.js"; | |
// @codekit-prepend "_acf-field-time-picker.js"; | |
// @codekit-prepend "_acf-field-true-false.js"; | |
// @codekit-prepend "_acf-field-url.js"; | |
// @codekit-prepend "_acf-field-wysiwyg.js"; | |
// @codekit-prepend "_acf-condition.js"; | |
// @codekit-prepend "_acf-conditions.js"; | |
// @codekit-prepend "_acf-condition-types.js"; | |
// @codekit-prepend "_acf-media.js"; | |
// @codekit-prepend "_acf-screen.js"; | |
// @codekit-prepend "_acf-select2.js"; | |
// @codekit-prepend "_acf-tinymce.js"; | |
// @codekit-prepend "_acf-validation.js"; | |
// @codekit-prepend "_acf-helpers.js"; | |
// @codekit-prepend "_acf-compatibility"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment