11716 lines
202 KiB
JavaScript
11716 lines
202 KiB
JavaScript
( 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;
|
|
|
|
};
|
|
|
|
window.wp = window.wp || {};
|
|
window.wp.hooks = new EventManager();
|
|
|
|
} )( window );
|
|
|
|
|
|
var acf;
|
|
|
|
(function($){
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
};
|
|
|
|
|
|
acf = {
|
|
|
|
// vars
|
|
l10n: {},
|
|
o: {},
|
|
|
|
|
|
/*
|
|
* update
|
|
*
|
|
* This function will update a value found in acf.o
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param k (string) the key
|
|
* @param v (mixed) the value
|
|
* @return n/a
|
|
*/
|
|
|
|
update: function( k, v ){
|
|
|
|
this.o[ k ] = v;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get
|
|
*
|
|
* This function will return a value found in acf.o
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param k (string) the key
|
|
* @return v (mixed) the value
|
|
*/
|
|
|
|
get: function( k ){
|
|
|
|
if( typeof this.o[ k ] !== 'undefined' ) {
|
|
|
|
return this.o[ k ];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* _e
|
|
*
|
|
* This functiln will return a string found in acf.l10n
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param k1 (string) the first key to look for
|
|
* @param k2 (string) the second key to look for
|
|
* @return string (string)
|
|
*/
|
|
|
|
_e: function( k1, k2 ){
|
|
|
|
// defaults
|
|
k2 = k2 || false;
|
|
|
|
|
|
// get context
|
|
var string = this.l10n[ k1 ] || '';
|
|
|
|
|
|
// get string
|
|
if( k2 ) {
|
|
|
|
string = string[ k2 ] || '';
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return string;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add_action
|
|
*
|
|
* This function uses wp.hooks to mimics WP add_action
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
add_action: function() {
|
|
|
|
// vars
|
|
var a = arguments[0].split(' '),
|
|
l = a.length;
|
|
|
|
|
|
// loop
|
|
for( var i = 0; i < l; i++) {
|
|
|
|
/*
|
|
// allow for special actions
|
|
if( a[i].indexOf('initialize') !== -1 ) {
|
|
|
|
a.push( a[i].replace('initialize', 'ready') );
|
|
a.push( a[i].replace('initialize', 'append') );
|
|
l = a.length;
|
|
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + a[i];
|
|
|
|
|
|
// add
|
|
wp.hooks.addAction.apply(this, arguments);
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove_action
|
|
*
|
|
* This function uses wp.hooks to mimics WP remove_action
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
remove_action: function() {
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + arguments[0];
|
|
|
|
wp.hooks.removeAction.apply(this, arguments);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* do_action
|
|
*
|
|
* This function uses wp.hooks to mimics WP do_action
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
do_action: function() { //console.log('acf.do_action(%o)', arguments);
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + arguments[0];
|
|
|
|
wp.hooks.doAction.apply(this, arguments);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add_filter
|
|
*
|
|
* This function uses wp.hooks to mimics WP add_filter
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
add_filter: function() {
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + arguments[0];
|
|
|
|
wp.hooks.addFilter.apply(this, arguments);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove_filter
|
|
*
|
|
* This function uses wp.hooks to mimics WP remove_filter
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
remove_filter: function() {
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + arguments[0];
|
|
|
|
wp.hooks.removeFilter.apply(this, arguments);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* apply_filters
|
|
*
|
|
* This function uses wp.hooks to mimics WP apply_filters
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param
|
|
* @return
|
|
*/
|
|
|
|
apply_filters: function() { //console.log('acf.apply_filters(%o)', arguments);
|
|
|
|
// prefix action
|
|
arguments[0] = 'acf/' + arguments[0];
|
|
|
|
return wp.hooks.applyFilters.apply(this, arguments);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_selector
|
|
*
|
|
* This function will return a valid selector for finding a field object
|
|
*
|
|
* @type function
|
|
* @date 15/01/2015
|
|
* @since 5.1.5
|
|
*
|
|
* @param s (string)
|
|
* @return (string)
|
|
*/
|
|
|
|
get_selector: function( s ) {
|
|
|
|
// defaults
|
|
s = s || '';
|
|
|
|
|
|
// vars
|
|
var selector = '.acf-field';
|
|
|
|
|
|
// compatibility with object
|
|
if( $.isPlainObject(s) ) {
|
|
|
|
if( $.isEmptyObject(s) ) {
|
|
|
|
s = '';
|
|
|
|
} else {
|
|
|
|
for( k in s ) { s = s[k]; break; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// search
|
|
if( s ) {
|
|
|
|
// append
|
|
selector += '-' + s;
|
|
|
|
|
|
// replace underscores (split/join replaces all and is faster than regex!)
|
|
selector = selector.split('_').join('-');
|
|
|
|
|
|
// remove potential double up
|
|
selector = selector.split('field-field-').join('field-');
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return selector;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_fields
|
|
*
|
|
* This function will return a jQuery selection of fields
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param args (object)
|
|
* @param $el (jQuery) element to look within
|
|
* @param all (boolean) return all fields or allow filtering (for repeater)
|
|
* @return $fields (jQuery)
|
|
*/
|
|
|
|
get_fields: function( s, $el, all ){
|
|
|
|
// debug
|
|
//console.log( 'acf.get_fields(%o, %o, %o)', args, $el, all );
|
|
//console.time("acf.get_fields");
|
|
|
|
|
|
// defaults
|
|
s = s || '';
|
|
$el = $el || false;
|
|
all = all || false;
|
|
|
|
|
|
// vars
|
|
var selector = this.get_selector(s);
|
|
|
|
|
|
// get child fields
|
|
var $fields = $( selector, $el );
|
|
|
|
|
|
// append context to fields if also matches selector.
|
|
// * Required for field group 'change_filed_type' append $tr to work
|
|
if( $el !== false ) {
|
|
|
|
$el.each(function(){
|
|
|
|
if( $(this).is(selector) ) {
|
|
|
|
$fields = $fields.add( $(this) );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// filter out fields
|
|
if( !all ) {
|
|
|
|
$fields = acf.apply_filters('get_fields', $fields);
|
|
|
|
}
|
|
|
|
|
|
//console.log('get_fields(%o, %o, %o) %o', s, $el, all, $fields);
|
|
//console.log('acf.get_fields(%o):', this.get_selector(s) );
|
|
//console.timeEnd("acf.get_fields");
|
|
|
|
|
|
// return
|
|
return $fields;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_field
|
|
*
|
|
* This function will return a jQuery selection based on a field key
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param field_key (string)
|
|
* @param $el (jQuery) element to look within
|
|
* @return $field (jQuery)
|
|
*/
|
|
|
|
get_field: function( s, $el ){
|
|
|
|
// defaults
|
|
s = s || '';
|
|
$el = $el || false;
|
|
|
|
|
|
// get fields
|
|
var $fields = this.get_fields(s, $el, true);
|
|
|
|
|
|
// check if exists
|
|
if( $fields.exists() ) {
|
|
|
|
return $fields.first();
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_closest_field
|
|
*
|
|
* This function will return the closest parent field
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery) element to start from
|
|
* @param args (object)
|
|
* @return $field (jQuery)
|
|
*/
|
|
|
|
get_closest_field : function( $el, s ){
|
|
|
|
// defaults
|
|
s = s || '';
|
|
|
|
|
|
// return
|
|
return $el.closest( this.get_selector(s) );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_field_wrap
|
|
*
|
|
* This function will return the closest parent field
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery) element to start from
|
|
* @return $field (jQuery)
|
|
*/
|
|
|
|
get_field_wrap: function( $el ){
|
|
|
|
return $el.closest( this.get_selector() );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_field_key
|
|
*
|
|
* This function will return the field's key
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $field (jQuery)
|
|
* @return (string)
|
|
*/
|
|
|
|
get_field_key: function( $field ){
|
|
|
|
return $field.data('key');
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_field_type
|
|
*
|
|
* This function will return the field's type
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $field (jQuery)
|
|
* @return (string)
|
|
*/
|
|
|
|
get_field_type: function( $field ){
|
|
|
|
return $field.data('type');
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_data
|
|
*
|
|
* This function will return attribute data for a given elemnt
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @param name (mixed)
|
|
* @return (mixed)
|
|
*/
|
|
|
|
get_data: function( $el, name ){
|
|
|
|
//console.log('get_data(%o, %o)', name, $el);
|
|
|
|
|
|
// get all datas
|
|
if( typeof name === 'undefined' ) {
|
|
|
|
return $el.data();
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return $el.data(name);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_uniqid
|
|
*
|
|
* This function will return a unique string ID
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param prefix (string)
|
|
* @param more_entropy (boolean)
|
|
* @return (string)
|
|
*/
|
|
|
|
get_uniqid : function( prefix, more_entropy ){
|
|
|
|
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + revised by: Kankrelune (http://www.webfaktory.info/)
|
|
// % note 1: Uses an internal counter (in php_js global) to avoid collision
|
|
// * example 1: uniqid();
|
|
// * returns 1: 'a30285b160c14'
|
|
// * example 2: uniqid('foo');
|
|
// * returns 2: 'fooa30285b1cd361'
|
|
// * example 3: uniqid('bar', true);
|
|
// * returns 3: 'bara20285b23dfd1.31879087'
|
|
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;
|
|
};
|
|
|
|
// BEGIN REDUNDANT
|
|
if (!this.php_js) {
|
|
this.php_js = {};
|
|
}
|
|
// END REDUNDANT
|
|
if (!this.php_js.uniqidSeed) { // init seed with big random int
|
|
this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
|
|
}
|
|
this.php_js.uniqidSeed++;
|
|
|
|
retId = prefix; // start with prefix, add current milliseconds hex string
|
|
retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
|
|
retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
|
|
if (more_entropy) {
|
|
// for more entropy we add a float lower to 10
|
|
retId += (Math.random() * 10).toFixed(8).toString();
|
|
}
|
|
|
|
return retId;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* serialize_form
|
|
*
|
|
* This function will create an object of data containing all form inputs within an element
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery selection)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
serialize_form : function( $el ){
|
|
|
|
// vars
|
|
var data = {},
|
|
names = {};
|
|
|
|
|
|
// selector
|
|
$selector = $el.find('select, textarea, input');
|
|
|
|
|
|
// populate data
|
|
$.each( $selector.serializeArray(), function( i, pair ) {
|
|
|
|
// initiate name
|
|
if( pair.name.slice(-2) === '[]' ) {
|
|
|
|
// remove []
|
|
pair.name = pair.name.replace('[]', '');
|
|
|
|
|
|
// initiate counter
|
|
if( typeof names[ pair.name ] === 'undefined'){
|
|
|
|
names[ pair.name ] = -1;
|
|
}
|
|
|
|
|
|
// increase counter
|
|
names[ pair.name ]++;
|
|
|
|
|
|
// add key
|
|
pair.name += '[' + names[ pair.name ] +']';
|
|
}
|
|
|
|
|
|
// append to data
|
|
data[ pair.name ] = pair.value;
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
serialize: function( $el ){
|
|
|
|
return this.serialize_form( $el );
|
|
|
|
},
|
|
|
|
/*
|
|
* disable_form
|
|
*
|
|
* This function will disable all inputs within an element
|
|
*
|
|
* @type function
|
|
* @date 22/09/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @return na
|
|
*/
|
|
|
|
disable_form: function( $el, context ) {
|
|
|
|
// defaults
|
|
context = context || '';
|
|
|
|
|
|
// loop
|
|
$el.find('select, textarea, input').each(function(){
|
|
|
|
acf.disable( $(this), context );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* disable
|
|
*
|
|
* This function will disable an input
|
|
*
|
|
* @type function
|
|
* @date 22/09/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
disable: function( $input, context ){
|
|
|
|
// defaults
|
|
context = context || '';
|
|
|
|
|
|
// bail early if is .acf-disabled
|
|
if( $input.hasClass('acf-disabled') ) return false;
|
|
|
|
|
|
// context
|
|
if( context ) {
|
|
|
|
// vars
|
|
var attr = $input.attr('data-disabled'),
|
|
disabled = attr ? attr.split(',') : [],
|
|
i = disabled.indexOf(context);
|
|
|
|
|
|
// bail early if already disabled
|
|
if( i >= 0 ) return false;
|
|
|
|
|
|
// append context
|
|
disabled.push( context );
|
|
|
|
|
|
// join
|
|
attr = disabled.join(',');
|
|
|
|
|
|
// update context
|
|
$input.attr('data-disabled', attr);
|
|
|
|
}
|
|
|
|
|
|
// disable input
|
|
$input.prop('disabled', true);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* enable_form
|
|
*
|
|
* This function will enable all inputs within an element
|
|
*
|
|
* @type function
|
|
* @date 22/09/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @return na
|
|
*/
|
|
|
|
enable_form: function( $el, context ) {
|
|
|
|
// defaults
|
|
context = context || '';
|
|
|
|
|
|
// loop
|
|
$el.find('select, textarea, input').each(function(){
|
|
|
|
acf.enable( $(this), context );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* enable
|
|
*
|
|
* This function will enable an input
|
|
*
|
|
* @type function
|
|
* @date 22/09/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
enable: function( $input, context ){
|
|
|
|
// defaults
|
|
context = context || '';
|
|
|
|
|
|
// bail early if is .acf-disabled
|
|
if( $input.hasClass('acf-disabled') ) return false;
|
|
|
|
|
|
// context
|
|
if( context ) {
|
|
|
|
// vars
|
|
var attr = $input.attr('data-disabled'),
|
|
disabled = attr ? attr.split(',') : [],
|
|
i = disabled.indexOf(context);
|
|
|
|
|
|
// bail early if no content or context does not match
|
|
if( i < 0 ) return false;
|
|
|
|
|
|
// delete
|
|
disabled.splice(i, 1);
|
|
|
|
|
|
// update attr
|
|
attr = disabled.join(',');
|
|
|
|
|
|
// update context
|
|
$input.attr('data-disabled', attr);
|
|
|
|
|
|
// bail early if other disableds exist
|
|
if( attr ) return false;
|
|
|
|
}
|
|
|
|
|
|
// enable input
|
|
$input.prop('disabled', false);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove_tr
|
|
*
|
|
* This function will remove a tr element with animation
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $tr (jQuery selection)
|
|
* @param callback (function) runs on complete
|
|
* @return n/a
|
|
*/
|
|
|
|
remove_tr : function( $tr, callback ){
|
|
|
|
// vars
|
|
var height = $tr.height(),
|
|
children = $tr.children().length;
|
|
|
|
|
|
// add class
|
|
$tr.addClass('acf-remove-element');
|
|
|
|
|
|
// after animation
|
|
setTimeout(function(){
|
|
|
|
// remove class
|
|
$tr.removeClass('acf-remove-element');
|
|
|
|
|
|
// vars
|
|
$tr.html('<td style="padding:0; height:' + height + 'px" colspan="' + children + '"></td>');
|
|
|
|
|
|
$tr.children('td').animate({ height : 0}, 250, function(){
|
|
|
|
$tr.remove();
|
|
|
|
if( typeof(callback) == 'function' ) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}, 250);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove_el
|
|
*
|
|
* This function will remove an element with animation
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery selection)
|
|
* @param callback (function) runs on complete
|
|
* @param end_height (int)
|
|
* @return n/a
|
|
*/
|
|
|
|
remove_el : function( $el, callback, end_height ){
|
|
|
|
// defaults
|
|
end_height = end_height || 0;
|
|
|
|
|
|
// set layout
|
|
$el.css({
|
|
height : $el.height(),
|
|
width : $el.width(),
|
|
position : 'absolute',
|
|
//padding : 0
|
|
});
|
|
|
|
|
|
// wrap field
|
|
$el.wrap( '<div class="acf-temp-wrap" style="height:' + $el.outerHeight(true) + 'px"></div>' );
|
|
|
|
|
|
// fade $el
|
|
$el.animate({ opacity : 0 }, 250);
|
|
|
|
|
|
// remove
|
|
$el.parent('.acf-temp-wrap').animate({ height : end_height }, 250, function(){
|
|
|
|
$(this).remove();
|
|
|
|
if( typeof(callback) == 'function' ) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* isset
|
|
*
|
|
* This function will return true if an object key exists
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param (object)
|
|
* @param key1 (string)
|
|
* @param key2 (string)
|
|
* @param ...
|
|
* @return (boolean)
|
|
*/
|
|
|
|
isset : function(){
|
|
|
|
var a = arguments,
|
|
l = a.length,
|
|
c = null,
|
|
undef;
|
|
|
|
if (l === 0) {
|
|
throw new Error('Empty isset');
|
|
}
|
|
|
|
c = a[0];
|
|
|
|
for (i = 1; i < l; i++) {
|
|
|
|
if (a[i] === undef || c[ a[i] ] === undef) {
|
|
return false;
|
|
}
|
|
|
|
c = c[ a[i] ];
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* maybe_get
|
|
*
|
|
* This function will attempt to return a value and return null if not possible
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param obj (object) the array to look within
|
|
* @param key (key) the array key to look for. Nested values may be found using '/'
|
|
* @param value (mixed) the value returned if not found
|
|
* @return (mixed)
|
|
*/
|
|
|
|
maybe_get: function( obj, key, value ){
|
|
|
|
// default
|
|
if( typeof value == 'undefined' ) value = null;
|
|
|
|
|
|
// convert type to string and split
|
|
keys = String(key).split('.');
|
|
|
|
|
|
// loop through keys
|
|
for( var i in keys ) {
|
|
|
|
// vars
|
|
var key = keys[i];
|
|
|
|
|
|
// bail ealry if not set
|
|
if( typeof obj[ key ] === 'undefined' ) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
// update obj
|
|
obj = obj[ key ];
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return obj;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* open_popup
|
|
*
|
|
* This function will create and open a popup modal
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param args (object)
|
|
* @return n/a
|
|
*/
|
|
|
|
open_popup : function( args ){
|
|
|
|
// vars
|
|
$popup = $('body > #acf-popup');
|
|
|
|
|
|
// already exists?
|
|
if( $popup.exists() ) {
|
|
|
|
return update_popup(args);
|
|
|
|
}
|
|
|
|
|
|
// template
|
|
var tmpl = [
|
|
'<div id="acf-popup">',
|
|
'<div class="acf-popup-box acf-box">',
|
|
'<div class="title"><h3></h3><a href="#" class="acf-icon -cancel grey acf-close-popup"></a></div>',
|
|
'<div class="inner"></div>',
|
|
'<div class="loading"><i class="acf-loading"></i></div>',
|
|
'</div>',
|
|
'<div class="bg"></div>',
|
|
'</div>'
|
|
].join('');
|
|
|
|
|
|
// append
|
|
$('body').append( tmpl );
|
|
|
|
|
|
$('#acf-popup').on('click', '.bg, .acf-close-popup', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
acf.close_popup();
|
|
|
|
});
|
|
|
|
|
|
// update
|
|
return this.update_popup(args);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* update_popup
|
|
*
|
|
* This function will update the content within a popup modal
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param args (object)
|
|
* @return n/a
|
|
*/
|
|
|
|
update_popup : function( args ){
|
|
|
|
// vars
|
|
$popup = $('#acf-popup');
|
|
|
|
|
|
// validate
|
|
if( !$popup.exists() )
|
|
{
|
|
return false
|
|
}
|
|
|
|
|
|
// defaults
|
|
args = $.extend({}, {
|
|
title : '',
|
|
content : '',
|
|
width : 0,
|
|
height : 0,
|
|
loading : false
|
|
}, args);
|
|
|
|
|
|
if( args.title ) {
|
|
|
|
$popup.find('.title h3').html( args.title );
|
|
|
|
}
|
|
|
|
if( args.content ) {
|
|
|
|
$inner = $popup.find('.inner:first');
|
|
|
|
$inner.html( args.content );
|
|
|
|
acf.do_action('append', $inner);
|
|
|
|
// update height
|
|
$inner.attr('style', 'position: relative;');
|
|
args.height = $inner.outerHeight();
|
|
$inner.removeAttr('style');
|
|
|
|
}
|
|
|
|
if( args.width ) {
|
|
|
|
$popup.find('.acf-popup-box').css({
|
|
'width' : args.width,
|
|
'margin-left' : 0 - (args.width / 2),
|
|
});
|
|
|
|
}
|
|
|
|
if( args.height ) {
|
|
|
|
// add h3 height (44)
|
|
args.height += 44;
|
|
|
|
$popup.find('.acf-popup-box').css({
|
|
'height' : args.height,
|
|
'margin-top' : 0 - (args.height / 2),
|
|
});
|
|
|
|
}
|
|
|
|
|
|
if( args.loading ) {
|
|
|
|
$popup.find('.loading').show();
|
|
|
|
} else {
|
|
|
|
$popup.find('.loading').hide();
|
|
|
|
}
|
|
|
|
return $popup;
|
|
},
|
|
|
|
|
|
/*
|
|
* close_popup
|
|
*
|
|
* This function will close and remove a popup modal
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
close_popup : function(){
|
|
|
|
// vars
|
|
$popup = $('#acf-popup');
|
|
|
|
|
|
// already exists?
|
|
if( $popup.exists() )
|
|
{
|
|
$popup.remove();
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* update_user_setting
|
|
*
|
|
* This function will send an AJAX request to update a user setting
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
update_user_setting : function( name, value ) {
|
|
|
|
// ajax
|
|
$.ajax({
|
|
url : acf.get('ajaxurl'),
|
|
dataType : 'html',
|
|
type : 'post',
|
|
data : acf.prepare_for_ajax({
|
|
'action' : 'acf/update_user_setting',
|
|
'name' : name,
|
|
'value' : value
|
|
})
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* prepare_for_ajax
|
|
*
|
|
* This function will prepare data for an AJAX request
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param args (object)
|
|
* @return args
|
|
*/
|
|
|
|
prepare_for_ajax : function( args ) {
|
|
|
|
// vars
|
|
args.nonce = acf.get('nonce');
|
|
args.post_id = acf.get('post_id');
|
|
|
|
|
|
// filter for 3rd party customization
|
|
args = acf.apply_filters('prepare_for_ajax', args);
|
|
|
|
|
|
// return
|
|
return args;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* is_ajax_success
|
|
*
|
|
* This function will return true for a successful WP AJAX response
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param json (object)
|
|
* @return (boolean)
|
|
*/
|
|
|
|
is_ajax_success : function( json ) {
|
|
|
|
if( json && json.success ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_ajax_message
|
|
*
|
|
* This function will return an object containing error/message information
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param json (object)
|
|
* @return (boolean)
|
|
*/
|
|
|
|
get_ajax_message: function( json ) {
|
|
|
|
// vars
|
|
var message = {
|
|
text: '',
|
|
type: 'error'
|
|
};
|
|
|
|
|
|
// bail early if no json
|
|
if( !json ) {
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
// PHP error (too may themes will have warnings / errors. Don't show these in ACF taxonomy popup)
|
|
/*
|
|
if( typeof json === 'string' ) {
|
|
|
|
message.text = json;
|
|
return message;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
// success
|
|
if( json.success ) {
|
|
|
|
message.type = 'success';
|
|
|
|
}
|
|
|
|
|
|
// message
|
|
if( json.data && json.data.message ) {
|
|
|
|
message.text = json.data.message;
|
|
|
|
}
|
|
|
|
|
|
// error
|
|
if( json.data && json.data.error ) {
|
|
|
|
message.text = json.data.error;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return message;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* is_in_view
|
|
*
|
|
* This function will return true if a jQuery element is visible in browser
|
|
*
|
|
* @type function
|
|
* @date 8/09/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $el (jQuery)
|
|
* @return (boolean)
|
|
*/
|
|
|
|
is_in_view: function( $el ) {
|
|
|
|
// vars
|
|
var elemTop = $el.offset().top,
|
|
elemBottom = elemTop + $el.height();
|
|
|
|
|
|
// bail early if hidden
|
|
if( elemTop === elemBottom ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// more vars
|
|
var docViewTop = $(window).scrollTop(),
|
|
docViewBottom = docViewTop + $(window).height();
|
|
|
|
|
|
// return
|
|
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* val
|
|
*
|
|
* This function will update an elements value and trigger the change event if different
|
|
*
|
|
* @type function
|
|
* @date 16/10/2014
|
|
* @since 5.0.9
|
|
*
|
|
* @param $el (jQuery)
|
|
* @param val (mixed)
|
|
* @return n/a
|
|
*/
|
|
|
|
val: function( $el, val ){
|
|
|
|
// vars
|
|
var orig = $el.val();
|
|
|
|
|
|
// update value
|
|
$el.val( val );
|
|
|
|
|
|
// trigger change
|
|
if( val != orig ) {
|
|
|
|
$el.trigger('change');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* str_replace
|
|
*
|
|
* This function will perform a str replace similar to php function str_replace
|
|
*
|
|
* @type function
|
|
* @date 1/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $search (string)
|
|
* @param $replace (string)
|
|
* @param $subject (string)
|
|
* @return (string)
|
|
*/
|
|
|
|
str_replace: function( search, replace, subject ) {
|
|
|
|
return subject.split(search).join(replace);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* str_sanitize
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 4/06/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
str_sanitize: function( string ) {
|
|
|
|
// vars
|
|
var string2 = '',
|
|
replace = {
|
|
'æ': 'a',
|
|
'å': 'a',
|
|
'á': 'a',
|
|
'ä': 'a',
|
|
'č': 'c',
|
|
'ď': 'd',
|
|
'è': 'e',
|
|
'é': 'e',
|
|
'ě': 'e',
|
|
'ë': 'e',
|
|
'í': 'i',
|
|
'ĺ': 'l',
|
|
'ľ': 'l',
|
|
'ň': 'n',
|
|
'ø': 'o',
|
|
'ó': 'o',
|
|
'ô': 'o',
|
|
'ő': 'o',
|
|
'ö': 'o',
|
|
'ŕ': 'r',
|
|
'š': 's',
|
|
'ť': 't',
|
|
'ú': 'u',
|
|
'ů': 'u',
|
|
'ű': 'u',
|
|
'ü': 'u',
|
|
'ý': 'y',
|
|
'ř': 'r',
|
|
'ž': 'z',
|
|
' ': '_',
|
|
'\'': '',
|
|
'?': '',
|
|
'/': '',
|
|
'\\': '',
|
|
'.': '',
|
|
',': '',
|
|
'>': '',
|
|
'<': '',
|
|
'"': '',
|
|
'[': '',
|
|
']': '',
|
|
'|': '',
|
|
'{': '',
|
|
'}': '',
|
|
'(': '',
|
|
')': ''
|
|
};
|
|
|
|
|
|
// lowercase
|
|
string = string.toLowerCase();
|
|
|
|
|
|
// loop through characters
|
|
for( i = 0; i < string.length; i++ ) {
|
|
|
|
// character
|
|
var c = string.charAt(i);
|
|
|
|
|
|
// override c with replacement
|
|
if( typeof replace[c] !== 'undefined' ) {
|
|
|
|
c = replace[c];
|
|
|
|
}
|
|
|
|
|
|
// append
|
|
string2 += c;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return string2;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render_select
|
|
*
|
|
* This function will update a select field with new choices
|
|
*
|
|
* @type function
|
|
* @date 8/04/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $select
|
|
* @param choices
|
|
* @return n/a
|
|
*/
|
|
|
|
render_select: function( $select, choices ){
|
|
|
|
// vars
|
|
var value = $select.val();
|
|
|
|
|
|
// clear choices
|
|
$select.html('');
|
|
|
|
|
|
// bail early if no choices
|
|
if( !choices ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// populate choices
|
|
$.each(choices, function( i, item ){
|
|
|
|
// vars
|
|
var $optgroup = $select;
|
|
|
|
|
|
// add group
|
|
if( item.group ) {
|
|
|
|
$optgroup = $select.find('optgroup[label="' + item.group + '"]');
|
|
|
|
if( !$optgroup.exists() ) {
|
|
|
|
$optgroup = $('<optgroup label="' + item.group + '"></optgroup>');
|
|
|
|
$select.append( $optgroup );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// append select
|
|
$optgroup.append( '<option value="' + item.value + '">' + item.label + '</option>' );
|
|
|
|
|
|
// selectedIndex
|
|
if( value == item.value ) {
|
|
|
|
$select.prop('selectedIndex', i);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* duplicate
|
|
*
|
|
* This function will duplicate and return an element
|
|
*
|
|
* @type function
|
|
* @date 22/08/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $el (jQuery) object to be duplicated
|
|
* @param attr (string) attrbute name where $el id can be found
|
|
* @return $el2 (jQuery)
|
|
*/
|
|
|
|
duplicate: function( args ){
|
|
|
|
//console.time('duplicate');
|
|
|
|
|
|
// backwards compatibility
|
|
// - array of settings added in v5.4.6
|
|
if( typeof args.length !== 'undefined' ) args = { $el: args };
|
|
|
|
|
|
// defaults
|
|
args = acf.parse_args(args, {
|
|
$el: false,
|
|
search: '',
|
|
replace: '',
|
|
before: function( $el ){},
|
|
after: function( $el, $el2 ){},
|
|
append: function( $el, $el2 ){ $el.after( $el2 ); }
|
|
});
|
|
|
|
|
|
// vars
|
|
var $el = args.$el,
|
|
$el2;
|
|
|
|
|
|
// search
|
|
if( !args.search ) args.search = $el.attr('data-id');
|
|
|
|
|
|
// replace
|
|
if( !args.replace ) args.replace = acf.get_uniqid();
|
|
|
|
|
|
// before
|
|
// - allow acf to modify DOM
|
|
// - fixes bug where select field option is not selected
|
|
args.before.apply( this, [$el] );
|
|
acf.do_action('before_duplicate', $el);
|
|
|
|
|
|
// clone
|
|
var $el2 = $el.clone();
|
|
|
|
|
|
// remove acf-clone (may be a clone)
|
|
$el2.removeClass('acf-clone');
|
|
|
|
|
|
// remove JS functionality
|
|
acf.do_action('remove', $el2);
|
|
|
|
|
|
// find / replace
|
|
if( args.search ) {
|
|
|
|
// replace data
|
|
$el2.attr('data-id', args.replace);
|
|
|
|
|
|
// replace ids
|
|
$el2.find('[id*="' + args.search + '"]').each(function(){
|
|
|
|
$(this).attr('id', $(this).attr('id').replace(args.search, args.replace) );
|
|
|
|
});
|
|
|
|
|
|
// replace names
|
|
$el2.find('[name*="' + args.search + '"]').each(function(){
|
|
|
|
$(this).attr('name', $(this).attr('name').replace(args.search, args.replace) );
|
|
|
|
});
|
|
|
|
|
|
// replace label for
|
|
$el2.find('label[for*="' + args.search + '"]').each(function(){
|
|
|
|
$(this).attr('for', $(this).attr('for').replace(args.search, args.replace) );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// remove ui-sortable
|
|
$el2.find('.ui-sortable').removeClass('ui-sortable');
|
|
|
|
|
|
// after
|
|
// - allow acf to modify DOM
|
|
acf.do_action('after_duplicate', $el, $el2 );
|
|
args.after.apply( this, [$el, $el2] );
|
|
|
|
|
|
// append
|
|
args.append.apply( this, [$el, $el2] );
|
|
|
|
|
|
// add JS functionality
|
|
// - allow element to be moved into a visible position before fire action
|
|
setTimeout(function(){
|
|
|
|
acf.do_action('append', $el2);
|
|
|
|
}, 1);
|
|
|
|
|
|
//console.timeEnd('duplicate');
|
|
|
|
|
|
// return
|
|
return $el2;
|
|
|
|
},
|
|
|
|
decode: function( string ){
|
|
|
|
return $('<textarea/>').html( string ).text();
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* parse_args
|
|
*
|
|
* This function will merge together defaults and args much like the WP wp_parse_args function
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param args (object)
|
|
* @param defaults (object)
|
|
* @return args
|
|
*/
|
|
|
|
parse_args: function( args, defaults ) {
|
|
|
|
return $.extend({}, defaults, args);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* enqueue_script
|
|
*
|
|
* This function will append a script to the page
|
|
*
|
|
* @source https://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/
|
|
* @type function
|
|
* @date 27/08/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param url (string)
|
|
* @param callback (function)
|
|
* @return na
|
|
*/
|
|
|
|
enqueue_script: function( url, callback ) {
|
|
|
|
// vars
|
|
var script = document.createElement('script');
|
|
|
|
|
|
// atts
|
|
script.type = "text/javascript";
|
|
script.src = url;
|
|
script.async = true;
|
|
|
|
|
|
// ie
|
|
if( script.readyState ) {
|
|
|
|
script.onreadystatechange = function(){
|
|
|
|
if( script.readyState == 'loaded' || script.readyState == 'complete' ){
|
|
|
|
script.onreadystatechange = null;
|
|
callback();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// normal browsers
|
|
} else {
|
|
|
|
script.onload = function(){
|
|
callback();
|
|
};
|
|
|
|
}
|
|
|
|
|
|
// append
|
|
document.body.appendChild(script);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/*
|
|
* 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 = {
|
|
|
|
// vars
|
|
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,
|
|
event = name.substr(0,name.indexOf(' ')),
|
|
selector = name.substr(name.indexOf(' ')+1);
|
|
|
|
|
|
// add event
|
|
$(document).on(event, selector, function( e ){
|
|
|
|
// append $el to event object
|
|
e.$el = $(this);
|
|
|
|
|
|
// event
|
|
if( typeof model.event === 'function' ) {
|
|
|
|
e = model.event( e );
|
|
|
|
}
|
|
|
|
|
|
// callback
|
|
model[ callback ].apply(model, [e]);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
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({
|
|
|
|
// vars
|
|
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 ){
|
|
|
|
// append $el to event object
|
|
e.$el = $(this);
|
|
e.$field = acf.get_closest_field(e.$el, model.type);
|
|
|
|
|
|
// focus
|
|
model.set('$field', e.$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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* field
|
|
*
|
|
* This model fires actions and filters for registered fields
|
|
*
|
|
* @type function
|
|
* @date 21/02/2014
|
|
* @since 3.5.1
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
acf.fields = acf.model.extend({
|
|
|
|
actions: {
|
|
'prepare' : '_prepare',
|
|
'prepare_field' : '_prepare_field',
|
|
'ready' : '_ready',
|
|
'ready_field' : '_ready_field',
|
|
'append' : '_append',
|
|
'append_field' : '_append_field',
|
|
'load' : '_load',
|
|
'load_field' : '_load_field',
|
|
'remove' : '_remove',
|
|
'remove_field' : '_remove_field',
|
|
'sortstart' : '_sortstart',
|
|
'sortstart_field' : '_sortstart_field',
|
|
'sortstop' : '_sortstop',
|
|
'sortstop_field' : '_sortstop_field',
|
|
'show' : '_show',
|
|
'show_field' : '_show_field',
|
|
'hide' : '_hide',
|
|
'hide_field' : '_hide_field',
|
|
},
|
|
|
|
// prepare
|
|
_prepare: function( $el ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('prepare_field', $(this));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_prepare_field: function( $el ){
|
|
|
|
acf.do_action('prepare_field/type=' + $el.data('type'), $el);
|
|
|
|
},
|
|
|
|
// ready
|
|
_ready: function( $el ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('ready_field', $(this));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_ready_field: function( $el ){
|
|
|
|
acf.do_action('ready_field/type=' + $el.data('type'), $el);
|
|
|
|
},
|
|
|
|
// append
|
|
_append: function( $el ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('append_field', $(this));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_append_field: function( $el ){
|
|
|
|
acf.do_action('append_field/type=' + $el.data('type'), $el);
|
|
|
|
},
|
|
|
|
// load
|
|
_load: function( $el ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('load_field', $(this));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_load_field: function( $el ){
|
|
|
|
acf.do_action('load_field/type=' + $el.data('type'), $el);
|
|
|
|
},
|
|
|
|
// remove
|
|
_remove: function( $el ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('remove_field', $(this));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_remove_field: function( $el ){
|
|
|
|
acf.do_action('remove_field/type=' + $el.data('type'), $el);
|
|
|
|
},
|
|
|
|
// sortstart
|
|
_sortstart: function( $el, $placeholder ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('sortstart_field', $(this), $placeholder);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_sortstart_field: function( $el, $placeholder ){
|
|
|
|
acf.do_action('sortstart_field/type=' + $el.data('type'), $el, $placeholder);
|
|
|
|
},
|
|
|
|
// sortstop
|
|
_sortstop: function( $el, $placeholder ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('sortstop_field', $(this), $placeholder);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_sortstop_field: function( $el, $placeholder ){
|
|
|
|
acf.do_action('sortstop_field/type=' + $el.data('type'), $el, $placeholder);
|
|
|
|
},
|
|
|
|
|
|
// hide
|
|
_hide: function( $el, context ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('hide_field', $(this), context);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_hide_field: function( $el, context ){
|
|
|
|
acf.do_action('hide_field/type=' + $el.data('type'), $el, context);
|
|
|
|
},
|
|
|
|
// show
|
|
_show: function( $el, context ){
|
|
|
|
acf.get_fields('', $el).each(function(){
|
|
|
|
acf.do_action('show_field', $(this), context);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_show_field: function( $el, context ){
|
|
|
|
acf.do_action('show_field/type=' + $el.data('type'), $el, context);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* ready
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 19/02/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
$(document).ready(function(){
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('ready', $('body'));
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* load
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 19/02/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
$(window).on('load', function(){
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('load', $('body'));
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* layout
|
|
*
|
|
* This model handles the width layout for fields
|
|
*
|
|
* @type function
|
|
* @date 21/02/2014
|
|
* @since 3.5.1
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
acf.layout = acf.model.extend({
|
|
|
|
active: 0,
|
|
|
|
actions: {
|
|
'refresh': 'refresh',
|
|
},
|
|
|
|
refresh: function( $el ){
|
|
|
|
//console.log('acf.layout.refresh', $el);
|
|
|
|
// defaults
|
|
$el = $el || false;
|
|
|
|
|
|
// if is '.acf-fields'
|
|
if( $el && $el.is('.acf-fields') ) {
|
|
|
|
$el = $el.parent();
|
|
|
|
}
|
|
|
|
|
|
// loop over visible fields
|
|
$('.acf-fields:visible', $el).each(function(){
|
|
|
|
// vars
|
|
var $els = $(),
|
|
top = 0,
|
|
height = 0,
|
|
cell = -1;
|
|
|
|
|
|
// get fields
|
|
var $fields = $(this).children('.acf-field[data-width]:visible');
|
|
|
|
|
|
// bail early if no fields
|
|
if( !$fields.exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// reset fields
|
|
$fields.removeClass('acf-r0 acf-c0').css({'min-height': 0});
|
|
|
|
|
|
$fields.each(function( i ){
|
|
|
|
// vars
|
|
var $el = $(this),
|
|
this_top = $el.position().top;
|
|
|
|
|
|
// set top
|
|
if( i == 0 ) {
|
|
|
|
top = this_top;
|
|
|
|
}
|
|
|
|
|
|
// detect new row
|
|
if( this_top != top ) {
|
|
|
|
// set previous heights
|
|
$els.css({'min-height': (height+1)+'px'});
|
|
|
|
// reset
|
|
$els = $();
|
|
top = $el.position().top; // don't use variable as this value may have changed due to min-height css
|
|
height = 0;
|
|
cell = -1;
|
|
|
|
}
|
|
|
|
|
|
// increase
|
|
cell++;
|
|
|
|
// set height
|
|
height = ($el.outerHeight() > height) ? $el.outerHeight() : height;
|
|
|
|
// append
|
|
$els = $els.add( $el );
|
|
|
|
// add classes
|
|
if( this_top == 0 ) {
|
|
|
|
$el.addClass('acf-r0');
|
|
|
|
} else if( cell == 0 ) {
|
|
|
|
$el.addClass('acf-c0');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// clean up
|
|
if( $els.exists() ) {
|
|
|
|
$els.css({'min-height': (height+1)+'px'});
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
//console.timeEnd('acf.width.render');
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* Force revisions
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 19/02/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
$(document).on('change', '.acf-field input, .acf-field textarea, .acf-field select', function(){
|
|
|
|
// preview hack
|
|
if( $('#acf-form-data input[name="_acfchanged"]').exists() ) {
|
|
|
|
$('#acf-form-data input[name="_acfchanged"]').val(1);
|
|
|
|
}
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('change', $(this));
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* preventDefault helper
|
|
*
|
|
* This function will prevent default of any link with an href of #
|
|
*
|
|
* @type function
|
|
* @date 24/07/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
$(document).on('click', '.acf-field a[href="#"]', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* unload
|
|
*
|
|
* This model handles the unload prompt
|
|
*
|
|
* @type function
|
|
* @date 21/02/2014
|
|
* @since 3.5.1
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
acf.unload = acf.model.extend({
|
|
|
|
active: 1,
|
|
changed: 0,
|
|
|
|
filters: {
|
|
'validation_complete': 'validation_complete'
|
|
},
|
|
|
|
actions: {
|
|
'change': 'on',
|
|
'submit': 'off'
|
|
},
|
|
|
|
events: {
|
|
'submit form': 'off',
|
|
},
|
|
|
|
validation_complete: function( json, $form ){
|
|
|
|
if( json && json.errors ) {
|
|
|
|
this.on();
|
|
|
|
}
|
|
|
|
// return
|
|
return json;
|
|
|
|
},
|
|
|
|
on: function(){
|
|
|
|
// bail ealry if already changed (or not active)
|
|
if( this.changed || !this.active ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// update
|
|
this.changed = 1;
|
|
|
|
|
|
// add event
|
|
$(window).on('beforeunload', this.unload);
|
|
|
|
},
|
|
|
|
off: function(){
|
|
|
|
// update
|
|
this.changed = 0;
|
|
|
|
|
|
// remove event
|
|
$(window).off('beforeunload', this.unload);
|
|
|
|
},
|
|
|
|
unload: function(){
|
|
|
|
// alert string
|
|
return acf._e('unload');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
acf.tooltip = acf.model.extend({
|
|
|
|
$el: null,
|
|
|
|
events: {
|
|
'mouseenter .acf-js-tooltip': 'on',
|
|
'mouseleave .acf-js-tooltip': 'off',
|
|
},
|
|
|
|
on: function( e ){
|
|
|
|
//console.log('on');
|
|
|
|
// vars
|
|
var title = e.$el.attr('title');
|
|
|
|
|
|
// hide empty titles
|
|
if( !title ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// $t
|
|
this.$el = $('<div class="acf-tooltip">' + title + '</div>');
|
|
|
|
|
|
// append
|
|
$('body').append( this.$el );
|
|
|
|
|
|
// position
|
|
var tolerance = 10;
|
|
target_w = e.$el.outerWidth(),
|
|
target_h = e.$el.outerHeight(),
|
|
target_t = e.$el.offset().top,
|
|
target_l = e.$el.offset().left,
|
|
tooltip_w = this.$el.outerWidth(),
|
|
tooltip_h = this.$el.outerHeight();
|
|
|
|
|
|
// calculate top
|
|
var top = target_t - tooltip_h,
|
|
left = target_l + (target_w / 2) - (tooltip_w / 2);
|
|
|
|
|
|
// too far left
|
|
if( left < tolerance ) {
|
|
|
|
this.$el.addClass('right');
|
|
|
|
left = target_l + target_w;
|
|
top = target_t + (target_h / 2) - (tooltip_h / 2);
|
|
|
|
|
|
// too far right
|
|
} else if( (left + tooltip_w + tolerance) > $(window).width() ) {
|
|
|
|
this.$el.addClass('left');
|
|
|
|
left = target_l - tooltip_w;
|
|
top = target_t + (target_h / 2) - (tooltip_h / 2);
|
|
|
|
|
|
// too far top
|
|
} else if( top - $(window).scrollTop() < tolerance ) {
|
|
|
|
this.$el.addClass('bottom');
|
|
|
|
top = target_t + target_h;
|
|
|
|
} else {
|
|
|
|
this.$el.addClass('top');
|
|
|
|
}
|
|
|
|
|
|
// update css
|
|
this.$el.css({ 'top': top, 'left': left });
|
|
|
|
|
|
// avoid double title
|
|
e.$el.data('title', title);
|
|
e.$el.attr('title', '');
|
|
|
|
},
|
|
|
|
off: function( e ){
|
|
|
|
//console.log('off');
|
|
|
|
// bail early if no $el
|
|
if( !this.$el ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// replace title
|
|
e.$el.attr('title', e.$el.data('title'));
|
|
|
|
|
|
// remove tooltip
|
|
this.$el.remove();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
acf.postbox = acf.model.extend({
|
|
|
|
events: {
|
|
'mouseenter .acf-postbox .handlediv': 'on',
|
|
'mouseleave .acf-postbox .handlediv': 'off',
|
|
},
|
|
|
|
on: function( e ){
|
|
|
|
e.$el.siblings('.hndle').addClass('hover');
|
|
|
|
},
|
|
|
|
off: function( e ){
|
|
|
|
e.$el.siblings('.hndle').removeClass('hover');
|
|
|
|
},
|
|
|
|
render: function( args ){
|
|
|
|
// defaults
|
|
args = $.extend({}, {
|
|
id: '',
|
|
key: '',
|
|
style: 'default',
|
|
label: 'top',
|
|
edit_url: '',
|
|
edit_title: '',
|
|
visibility: true
|
|
}, args);
|
|
|
|
|
|
// vars
|
|
var $postbox = $('#' + args.id),
|
|
$toggle = $('#' + args.id + '-hide'),
|
|
$label = $toggle.parent();
|
|
|
|
|
|
|
|
// add class
|
|
$postbox.addClass('acf-postbox');
|
|
$label.addClass('acf-postbox-toggle');
|
|
|
|
|
|
// remove class
|
|
$postbox.removeClass('hide-if-js');
|
|
$label.removeClass('hide-if-js');
|
|
|
|
|
|
// field group style
|
|
if( args.style !== 'default' ) {
|
|
|
|
$postbox.addClass( args.style );
|
|
|
|
}
|
|
|
|
|
|
// .inside class
|
|
$postbox.children('.inside').addClass('acf-fields').addClass('-' + args.label);
|
|
|
|
|
|
// visibility
|
|
if( args.visibility ) {
|
|
|
|
$toggle.prop('checked', true);
|
|
|
|
} else {
|
|
|
|
$postbox.addClass('acf-hidden');
|
|
$label.addClass('acf-hidden');
|
|
|
|
}
|
|
|
|
|
|
// edit_url
|
|
if( args.edit_url ) {
|
|
|
|
$postbox.children('.hndle').append('<a href="' + args.edit_url + '" class="dashicons dashicons-admin-generic acf-hndle-cog acf-js-tooltip" title="' + args.edit_title + '"></a>');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* Sortable
|
|
*
|
|
* These functions will hook into the start and stop of a jQuery sortable event and modify the item and placeholder
|
|
*
|
|
* @type function
|
|
* @date 12/11/2013
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
acf.add_action('sortstart', function( $item, $placeholder ){
|
|
|
|
// if $item is a tr, apply some css to the elements
|
|
if( $item.is('tr') ) {
|
|
|
|
// temp set as relative to find widths
|
|
$item.css('position', 'relative');
|
|
|
|
|
|
// set widths for td children
|
|
$item.children().each(function(){
|
|
|
|
$(this).width($(this).width());
|
|
|
|
});
|
|
|
|
|
|
// revert position css
|
|
$item.css('position', 'absolute');
|
|
|
|
|
|
// add markup to the placeholder
|
|
$placeholder.html('<td style="height:' + $item.height() + 'px; padding:0;" colspan="' + $item.children('td').length + '"></td>');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
* before & after duplicate
|
|
*
|
|
* This function will modify the DOM before it is cloned. Primarily fixes a cloning issue with select elements
|
|
*
|
|
* @type function
|
|
* @date 16/05/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
acf.add_action('before_duplicate', function( $orig ){
|
|
|
|
// add 'selected' class
|
|
$orig.find('select option:selected').addClass('selected');
|
|
|
|
});
|
|
|
|
acf.add_action('after_duplicate', function( $orig, $duplicate ){
|
|
|
|
// set select values
|
|
$duplicate.find('select').each(function(){
|
|
|
|
// vars
|
|
var $select = $(this);
|
|
|
|
|
|
// bail early if is 'Stylized UI'
|
|
//if( $select.data('ui') ) return;
|
|
|
|
|
|
// vars
|
|
var val = [];
|
|
|
|
|
|
// loop
|
|
$select.find('option.selected').each(function(){
|
|
|
|
val.push( $(this).val() );
|
|
|
|
});
|
|
|
|
|
|
// set val
|
|
$select.val( val );
|
|
|
|
});
|
|
|
|
|
|
// remove 'selected' class
|
|
$orig.find('select option.selected').removeClass('selected');
|
|
$duplicate.find('select option.selected').removeClass('selected');
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
|
|
|
|
console.time("acf_test_ready");
|
|
console.time("acf_test_load");
|
|
|
|
acf.add_action('ready', function(){
|
|
|
|
console.timeEnd("acf_test_ready");
|
|
|
|
}, 999);
|
|
|
|
acf.add_action('load', function(){
|
|
|
|
console.timeEnd("acf_test_load");
|
|
|
|
}, 999);
|
|
*/
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.ajax = acf.model.extend({
|
|
|
|
active: false,
|
|
actions: {
|
|
'ready': 'ready'
|
|
},
|
|
events: {
|
|
'change #page_template': '_change_template',
|
|
'change #parent_id': '_change_parent',
|
|
'change #post-formats-select input': '_change_format',
|
|
'change .categorychecklist input': '_change_term',
|
|
'change .acf-taxonomy-field[data-save="1"] input': '_change_term',
|
|
'change .acf-taxonomy-field[data-save="1"] select': '_change_term'
|
|
},
|
|
o: {
|
|
//'post_id': 0,
|
|
//'page_template': 0,
|
|
//'page_parent': 0,
|
|
//'page_type': 0,
|
|
//'post_format': 0,
|
|
//'post_taxonomy': 0,
|
|
},
|
|
xhr: null,
|
|
|
|
update: function( k, v ){
|
|
|
|
this.o[ k ] = v;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
get: function( k ){
|
|
|
|
return this.o[ k ] || null;
|
|
|
|
},
|
|
|
|
ready: function(){
|
|
|
|
// update post_id
|
|
this.update('post_id', acf.get('post_id'));
|
|
|
|
|
|
// active
|
|
this.active = true;
|
|
|
|
},
|
|
|
|
/*
|
|
timeout: null,
|
|
maybe_fetch: function(){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// abort timeout
|
|
if( this.timeout ) {
|
|
|
|
clearTimeout( this.timeout );
|
|
|
|
}
|
|
|
|
|
|
// fetch
|
|
this.timeout = setTimeout(function(){
|
|
|
|
self.fetch();
|
|
|
|
}, 100);
|
|
|
|
},
|
|
*/
|
|
|
|
fetch: function(){
|
|
|
|
// bail early if not active
|
|
if( !this.active ) return;
|
|
|
|
|
|
// bail early if no ajax
|
|
if( !acf.get('ajax') ) return;
|
|
|
|
|
|
// abort XHR if is already loading AJAX data
|
|
if( this.xhr ) {
|
|
|
|
this.xhr.abort();
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var self = this,
|
|
data = this.o;
|
|
|
|
|
|
// add action url
|
|
data.action = 'acf/post/get_field_groups';
|
|
|
|
|
|
// add ignore
|
|
data.exists = [];
|
|
|
|
$('.acf-postbox').not('.acf-hidden').each(function(){
|
|
|
|
data.exists.push( $(this).attr('id').substr(4) );
|
|
|
|
});
|
|
|
|
|
|
// ajax
|
|
this.xhr = $.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
data: acf.prepare_for_ajax( data ),
|
|
type: 'post',
|
|
dataType: 'json',
|
|
|
|
success: function( json ){
|
|
|
|
if( acf.is_ajax_success( json ) ) {
|
|
|
|
self.render( json.data );
|
|
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
render: function( json ){
|
|
|
|
// hide
|
|
$('.acf-postbox').addClass('acf-hidden');
|
|
$('.acf-postbox-toggle').addClass('acf-hidden');
|
|
|
|
|
|
// reset style
|
|
$('#acf-style').html('');
|
|
|
|
|
|
// show the new postboxes
|
|
$.each(json, function( k, field_group ){
|
|
|
|
// vars
|
|
var $postbox = $('#acf-' + field_group.key),
|
|
$toggle = $('#acf-' + field_group.key + '-hide'),
|
|
$label = $toggle.parent();
|
|
|
|
|
|
// show
|
|
// use show() to force display when postbox has been hidden by 'Show on screen' toggle
|
|
$postbox.removeClass('acf-hidden hide-if-js').show();
|
|
$label.removeClass('acf-hidden hide-if-js').show();
|
|
$toggle.prop('checked', true);
|
|
|
|
|
|
// replace HTML if needed
|
|
var $replace = $postbox.find('.acf-replace-with-fields');
|
|
|
|
if( $replace.exists() ) {
|
|
|
|
$replace.replaceWith( field_group.html );
|
|
|
|
acf.do_action('append', $postbox);
|
|
|
|
}
|
|
|
|
|
|
// update style if needed
|
|
if( k === 0 ) {
|
|
|
|
$('#acf-style').html( field_group.style );
|
|
|
|
}
|
|
|
|
|
|
// enable inputs
|
|
$postbox.find('.acf-hidden-by-postbox').prop('disabled', false);
|
|
|
|
});
|
|
|
|
|
|
// disable inputs
|
|
$('.acf-postbox.acf-hidden').find('select, textarea, input').not(':disabled').each(function(){
|
|
|
|
$(this).addClass('acf-hidden-by-postbox').prop('disabled', true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
sync_taxonomy_terms: function(){
|
|
|
|
// vars
|
|
var values = [''];
|
|
|
|
|
|
// loop over term lists
|
|
$('.categorychecklist, .acf-taxonomy-field').each(function(){
|
|
|
|
// vars
|
|
var $el = $(this),
|
|
$checkbox = $el.find('input[type="checkbox"]').not(':disabled'),
|
|
$radio = $el.find('input[type="radio"]').not(':disabled'),
|
|
$select = $el.find('select').not(':disabled'),
|
|
$hidden = $el.find('input[type="hidden"]').not(':disabled');
|
|
|
|
|
|
// bail early if not a field which saves taxonomy terms to post
|
|
if( $el.is('.acf-taxonomy-field') && $el.attr('data-save') != '1' ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// bail early if in attachment
|
|
if( $el.closest('.media-frame').exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// checkbox
|
|
if( $checkbox.exists() ) {
|
|
|
|
$checkbox.filter(':checked').each(function(){
|
|
|
|
values.push( $(this).val() );
|
|
|
|
});
|
|
|
|
} else if( $radio.exists() ) {
|
|
|
|
$radio.filter(':checked').each(function(){
|
|
|
|
values.push( $(this).val() );
|
|
|
|
});
|
|
|
|
} else if( $select.exists() ) {
|
|
|
|
$select.find('option:selected').each(function(){
|
|
|
|
values.push( $(this).val() );
|
|
|
|
});
|
|
|
|
} else if( $hidden.exists() ) {
|
|
|
|
$hidden.each(function(){
|
|
|
|
// ignor blank values
|
|
if( ! $(this).val() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
values.push( $(this).val() );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// filter duplicates
|
|
values = values.filter (function (v, i, a) { return a.indexOf (v) == i });
|
|
|
|
|
|
// update screen
|
|
this.update( 'post_taxonomy', values ).fetch();
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* events
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 29/09/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
_change_template: function( e ){
|
|
|
|
// vars
|
|
var page_template = e.$el.val();
|
|
|
|
|
|
// update & fetch
|
|
this.update('page_template', page_template).fetch();
|
|
|
|
},
|
|
|
|
_change_parent: function( e ){
|
|
|
|
// vars
|
|
var page_type = 'parent',
|
|
page_parent = 0;
|
|
|
|
|
|
// if is child
|
|
if( e.$el.val() != "" ) {
|
|
|
|
page_type = 'child';
|
|
page_parent = e.$el.val();
|
|
|
|
}
|
|
|
|
// update & fetch
|
|
this.update('page_type', page_type).update('page_parent', page_parent).fetch();
|
|
|
|
},
|
|
|
|
_change_format: function( e ){
|
|
|
|
// vars
|
|
var post_format = e.$el.val();
|
|
|
|
|
|
// default
|
|
if( post_format == '0' ) {
|
|
|
|
post_format = 'standard';
|
|
|
|
}
|
|
|
|
|
|
// update & fetch
|
|
this.update('post_format', post_format).fetch();
|
|
|
|
},
|
|
|
|
_change_term: function( e ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// bail early if within media popup
|
|
if( e.$el.closest('.media-frame').exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// set timeout to fix issue with chrome which does not register the change has yet happened
|
|
setTimeout(function(){
|
|
|
|
self.sync_taxonomy_terms();
|
|
|
|
}, 1);
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.checkbox = acf.field.extend({
|
|
|
|
type: 'checkbox',
|
|
|
|
events: {
|
|
'change input': 'change'
|
|
},
|
|
|
|
change: function( e ){
|
|
|
|
// vars
|
|
var $ul = e.$el.closest('ul'),
|
|
$inputs = $ul.find('input[name]'),
|
|
checked = e.$el.is(':checked');
|
|
|
|
|
|
// is toggle?
|
|
if( e.$el.hasClass('acf-checkbox-toggle') ) {
|
|
|
|
// toggle all
|
|
$inputs.prop('checked', checked);
|
|
|
|
|
|
// return
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// bail early if no toggle
|
|
if( !$ul.find('.acf-checkbox-toggle').exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// determine if all inputs are checked
|
|
var checked = ( $inputs.not(':checked').length == 0 );
|
|
|
|
|
|
// update toggle
|
|
$ul.find('.acf-checkbox-toggle').prop('checked', checked);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.color_picker = acf.field.extend({
|
|
|
|
type: 'color_picker',
|
|
$input: null,
|
|
$hidden: null,
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
this.$input = this.$field.find('input[type="text"]');
|
|
this.$hidden = this.$field.find('input[type="hidden"]');
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// reference
|
|
var $input = this.$input,
|
|
$hidden = this.$hidden;
|
|
|
|
|
|
// trigger change function
|
|
var change_hidden = function(){
|
|
|
|
// timeout is required to ensure the $input val is correct
|
|
setTimeout(function(){
|
|
|
|
acf.val( $hidden, $input.val() );
|
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
|
|
// args
|
|
var args = {
|
|
|
|
defaultColor: false,
|
|
palettes: true,
|
|
hide: true,
|
|
change: change_hidden,
|
|
clear: change_hidden
|
|
|
|
}
|
|
|
|
|
|
// filter
|
|
var args = acf.apply_filters('color_picker_args', args, this.$field);
|
|
|
|
|
|
// iris
|
|
this.$input.wpColorPicker(args);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.conditional_logic = acf.model.extend({
|
|
|
|
actions: {
|
|
'prepare 20': 'render',
|
|
'append 20': 'render'
|
|
},
|
|
|
|
events: {
|
|
'change .acf-field input': 'change',
|
|
'change .acf-field textarea': 'change',
|
|
'change .acf-field select': 'change'
|
|
},
|
|
|
|
items: {},
|
|
triggers: {},
|
|
|
|
|
|
/*
|
|
* add
|
|
*
|
|
* This function will add a set of conditional logic rules
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param target (string) target field key
|
|
* @param groups (array) rule groups
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
add: function( target, groups ){
|
|
|
|
// debug
|
|
//console.log( 'conditional_logic.add(%o, %o)', target, groups );
|
|
|
|
|
|
// populate triggers
|
|
for( var i in groups ) {
|
|
|
|
// vars
|
|
var group = groups[i];
|
|
|
|
for( var k in group ) {
|
|
|
|
// vars
|
|
var rule = group[k],
|
|
trigger = rule.field,
|
|
triggers = this.triggers[ trigger ] || {};
|
|
|
|
|
|
// append trigger (sub field will simply override)
|
|
triggers[ target ] = target;
|
|
|
|
|
|
// update
|
|
this.triggers[ trigger ] = triggers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// append items
|
|
this.items[ target ] = groups;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render
|
|
*
|
|
* This function will render all fields
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
render: function( $el ){
|
|
|
|
// debug
|
|
//console.log('conditional_logic.render(%o)', $el);
|
|
|
|
|
|
// defaults
|
|
$el = $el || false;
|
|
|
|
|
|
// get targets
|
|
var $targets = acf.get_fields( '', $el, true );
|
|
|
|
|
|
// render fields
|
|
this.render_fields( $targets );
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('refresh', $el);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* change
|
|
*
|
|
* This function is called when an input is changed and will render any fields which are considered targets of this trigger
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
change: function( e ){
|
|
|
|
// debug
|
|
//console.log( 'conditional_logic.change(%o)', $input );
|
|
|
|
|
|
// vars
|
|
var $input = e.$el,
|
|
$field = acf.get_field_wrap( $input ),
|
|
key = $field.data('key');
|
|
|
|
|
|
// bail early if this field does not trigger any actions
|
|
if( typeof this.triggers[key] === 'undefined' ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
$parent = $field.parent();
|
|
|
|
|
|
// update visibility
|
|
for( var i in this.triggers[ key ] ) {
|
|
|
|
// get the target key
|
|
var target_key = this.triggers[ key ][ i ];
|
|
|
|
|
|
// get targets
|
|
var $targets = acf.get_fields(target_key, $parent, true);
|
|
|
|
|
|
// render
|
|
this.render_fields( $targets );
|
|
|
|
}
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('refresh', $parent);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render_fields
|
|
*
|
|
* This function will render a selection of fields
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
render_fields: function( $targets ) {
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// loop over targets and render them
|
|
$targets.each(function(){
|
|
|
|
self.render_field( $(this) );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render_field
|
|
*
|
|
* This function will render a field
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
render_field : function( $target ){
|
|
|
|
// vars
|
|
var key = $target.data('key');
|
|
|
|
|
|
// bail early if this field does not contain any conditional logic
|
|
if( typeof this.items[ key ] === 'undefined' ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var visibility = false;
|
|
|
|
|
|
// debug
|
|
//console.log( 'conditional_logic.render_field(%o)', $field );
|
|
|
|
|
|
// get conditional logic
|
|
var groups = this.items[ key ];
|
|
|
|
|
|
// calculate visibility
|
|
for( var i in groups ) {
|
|
|
|
// vars
|
|
var group = groups[i],
|
|
match_group = true;
|
|
|
|
for( var k in group ) {
|
|
|
|
// vars
|
|
var rule = group[k];
|
|
|
|
|
|
// get trigger for rule
|
|
var $trigger = this.get_trigger( $target, rule.field );
|
|
|
|
|
|
// break if rule did not validate
|
|
if( !this.calculate(rule, $trigger, $target) ) {
|
|
|
|
match_group = false;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// set visibility if rule group did validate
|
|
if( match_group ) {
|
|
|
|
visibility = true;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// hide / show field
|
|
if( visibility ) {
|
|
|
|
this.show_field( $target );
|
|
|
|
} else {
|
|
|
|
this.hide_field( $target );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* show_field
|
|
*
|
|
* This function will show a field
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
show_field: function( $field ){
|
|
|
|
// debug
|
|
//console.log('show_field(%o)', $field);
|
|
|
|
|
|
// bail early if field is already visible
|
|
// Note: Do not bail early! Instead, allow show_field to run on already visible fields.
|
|
// This fixes an issue where showing a repeater field would enable sub field inputs which
|
|
// should remain hidden due to another conditiona logic rule
|
|
/*
|
|
if( !$field.hasClass('hidden-by-conditional-logic') ) {
|
|
|
|
return;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
// remove class
|
|
$field.removeClass( 'hidden-by-conditional-logic' );
|
|
|
|
|
|
// clean up incorrectly hidden inputs
|
|
// case: Select2 is added after conditioan logic hides the select input.
|
|
$field.find('.acf-clhi.acf-disabled').removeClass('acf-clhi');
|
|
|
|
|
|
// remove "disabled"
|
|
// ignore inputs which have a class of 'acf-disabled'. These inputs are disabled for life
|
|
// ignore inputs which are hidden by conditiona logic of a sub field
|
|
$field.find('.acf-clhi').not('.hidden-by-conditional-logic .acf-clhi').removeClass('acf-clhi').prop('disabled', false);
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('show_field', $field, 'conditional_logic' );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* hide_field
|
|
*
|
|
* This function will hide a field
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
hide_field : function( $field ){
|
|
|
|
// debug
|
|
//console.log('hide_field(%o)', $field);
|
|
|
|
|
|
// bail early if field is already hidden
|
|
/*
|
|
if( $field.hasClass('hidden-by-conditional-logic') ) {
|
|
|
|
return;
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
// add class
|
|
$field.addClass( 'hidden-by-conditional-logic' );
|
|
|
|
|
|
// add "disabled"
|
|
$field.find('input, textarea, select').not('.acf-disabled').addClass('acf-clhi').prop('disabled', true);
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('hide_field', $field, 'conditional_logic' );
|
|
|
|
},
|
|
|
|
/*
|
|
* get_trigger
|
|
*
|
|
* This function will return the relevant $trigger for a $target
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
get_trigger: function( $target, key ){
|
|
|
|
// vars
|
|
var selector = acf.get_selector( key );
|
|
|
|
|
|
// find sibling $trigger
|
|
var $trigger = $target.siblings( selector );
|
|
|
|
|
|
// parent trigger
|
|
if( !$trigger.exists() ) {
|
|
|
|
// vars
|
|
var parent = acf.get_selector();
|
|
|
|
|
|
// loop through parent fields and review their siblings too
|
|
$target.parents( parent ).each(function(){
|
|
|
|
// find sibling $trigger
|
|
$trigger = $(this).siblings( selector );
|
|
|
|
|
|
// bail early if $trigger is found
|
|
if( $trigger.exists() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// bail early if no $trigger is found
|
|
if( !$trigger.exists() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return $trigger;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* calculate
|
|
*
|
|
* This function will calculate if a rule matches based on the $trigger
|
|
*
|
|
* @type function
|
|
* @date 22/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
calculate : function( rule, $trigger, $target ){
|
|
|
|
// bail early if $trigger could not be found
|
|
if( !$trigger || !$target ) return false;
|
|
|
|
|
|
// debug
|
|
//console.log( 'calculate(%o, %o, %o)', rule, $trigger, $target);
|
|
|
|
|
|
// vars
|
|
var match = false,
|
|
type = $trigger.data('type');
|
|
|
|
|
|
// input with :checked
|
|
if( type == 'true_false' || type == 'checkbox' || type == 'radio' ) {
|
|
|
|
match = this.calculate_checkbox( rule, $trigger );
|
|
|
|
|
|
} else if( type == 'select' ) {
|
|
|
|
match = this.calculate_select( rule, $trigger );
|
|
|
|
}
|
|
|
|
|
|
// reverse if 'not equal to'
|
|
if( rule.operator === "!=" ) {
|
|
|
|
match = !match;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return match;
|
|
|
|
},
|
|
|
|
calculate_checkbox: function( rule, $trigger ){
|
|
|
|
// look for selected input
|
|
var match = $trigger.find('input[value="' + rule.value + '"]:checked').exists();
|
|
|
|
|
|
// override for "allow null"
|
|
if( rule.value === '' && !$trigger.find('input:checked').exists() ) {
|
|
|
|
match = true;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return match;
|
|
|
|
},
|
|
|
|
|
|
calculate_select: function( rule, $trigger ){
|
|
|
|
// vars
|
|
var $select = $trigger.find('select'),
|
|
val = $select.val();
|
|
|
|
|
|
// check for no value
|
|
if( !val && !$.isNumeric(val) ) {
|
|
|
|
val = '';
|
|
|
|
}
|
|
|
|
|
|
// convert to array
|
|
if( !$.isArray(val) ) {
|
|
|
|
val = [ val ];
|
|
|
|
}
|
|
|
|
|
|
// calc
|
|
match = ($.inArray(rule.value, val) > -1);
|
|
|
|
|
|
// return
|
|
return match;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
/*
|
|
* acf.datepicker
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 16/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
acf.datepicker = acf.model.extend({
|
|
|
|
actions: {
|
|
'ready 1': 'ready',
|
|
},
|
|
|
|
ready: function(){
|
|
|
|
// vars
|
|
var locale = acf.get('locale'),
|
|
rtl = acf.get('rtl')
|
|
l10n = acf._e('date_picker');
|
|
|
|
|
|
// bail ealry if no l10n (fiedl groups admin page)
|
|
if( !l10n ) return;
|
|
|
|
|
|
// rtl
|
|
l10n.isRTL = rtl;
|
|
|
|
|
|
// append
|
|
$.datepicker.regional[ locale ] = l10n;
|
|
$.datepicker.setDefaults(l10n);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init
|
|
*
|
|
* This function will initialize JS
|
|
*
|
|
* @type function
|
|
* @date 2/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $input (jQuery selector)
|
|
* @param args (object)
|
|
* @return n/a
|
|
*/
|
|
|
|
init: function( $input, args ){
|
|
|
|
// defaults
|
|
args = args || {};
|
|
|
|
|
|
// add date picker
|
|
$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" />');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init
|
|
*
|
|
* This function will remove JS
|
|
*
|
|
* @type function
|
|
* @date 2/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $input (jQuery selector)
|
|
* @return n/a
|
|
*/
|
|
|
|
destroy: function( $input ){
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
});
|
|
|
|
acf.fields.date_picker = acf.field.extend({
|
|
|
|
type: 'date_picker',
|
|
$el: null,
|
|
$input: null,
|
|
$hidden: null,
|
|
|
|
o: {},
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'blur input[type="text"]': 'blur',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-date-picker');
|
|
this.$input = this.$el.find('input[type="text"]');
|
|
this.$hidden = this.$el.find('input[type="hidden"]');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// create options
|
|
var args = {
|
|
dateFormat: this.o.date_format,
|
|
altField: this.$hidden,
|
|
altFormat: 'yymmdd',
|
|
changeYear: true,
|
|
yearRange: "-100:+100",
|
|
changeMonth: true,
|
|
showButtonPanel: true,
|
|
firstDay: this.o.first_day
|
|
};
|
|
|
|
|
|
// filter for 3rd party customization
|
|
args = acf.apply_filters('date_picker_args', args, this.$field);
|
|
|
|
|
|
// add date picker
|
|
acf.datepicker.init( this.$input, args );
|
|
|
|
},
|
|
|
|
blur: function(){
|
|
|
|
if( !this.$input.val() ) {
|
|
|
|
this.$hidden.val('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
/*
|
|
* acf.datepicker
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 16/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
acf.datetimepicker = acf.model.extend({
|
|
|
|
actions: {
|
|
'ready 1': 'ready',
|
|
},
|
|
|
|
ready: function(){
|
|
|
|
// vars
|
|
var locale = acf.get('locale'),
|
|
rtl = acf.get('rtl')
|
|
l10n = acf._e('date_time_picker');
|
|
|
|
|
|
// bail ealry if no l10n (fiedl groups admin page)
|
|
if( !l10n ) return;
|
|
|
|
|
|
// rtl
|
|
l10n.isRTL = rtl;
|
|
|
|
|
|
// append
|
|
$.timepicker.regional[ locale ] = l10n;
|
|
$.timepicker.setDefaults(l10n);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init
|
|
*
|
|
* This function will initialize JS
|
|
*
|
|
* @type function
|
|
* @date 2/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $input (jQuery selector)
|
|
* @param args (object)
|
|
* @return n/a
|
|
*/
|
|
|
|
init: function( $input, args ){
|
|
|
|
// defaults
|
|
args = args || {};
|
|
|
|
|
|
// add date picker
|
|
$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" />');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init
|
|
*
|
|
* This function will remove JS
|
|
*
|
|
* @type function
|
|
* @date 2/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $input (jQuery selector)
|
|
* @return n/a
|
|
*/
|
|
|
|
destroy: function( $input ){
|
|
|
|
// do nothing
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
acf.fields.date_time_picker = acf.field.extend({
|
|
|
|
type: 'date_time_picker',
|
|
$el: null,
|
|
$input: null,
|
|
$hidden: null,
|
|
|
|
o: {},
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'blur input[type="text"]': 'blur',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-date-time-picker');
|
|
this.$input = this.$el.find('input[type="text"]');
|
|
this.$hidden = this.$el.find('input[type="hidden"]');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// create options
|
|
var args = {
|
|
dateFormat: this.o.date_format,
|
|
timeFormat: this.o.time_format,
|
|
altField: this.$hidden,
|
|
altFieldTimeOnly: false,
|
|
altFormat: 'yy-mm-dd',
|
|
altTimeFormat: 'HH:mm:ss',
|
|
changeYear: true,
|
|
yearRange: "-100:+100",
|
|
changeMonth: true,
|
|
showButtonPanel: true,
|
|
firstDay: this.o.first_day,
|
|
controlType: 'select',
|
|
oneLine: true,
|
|
|
|
};
|
|
|
|
|
|
// filter for 3rd party customization
|
|
args = acf.apply_filters('date_time_picker_args', args, this.$field);
|
|
|
|
|
|
// add date time picker
|
|
acf.datetimepicker.init( this.$input, args );
|
|
|
|
},
|
|
|
|
blur: function(){
|
|
|
|
if( !this.$input.val() ) {
|
|
|
|
this.$hidden.val('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.file = acf.field.extend({
|
|
|
|
type: 'file',
|
|
$el: null,
|
|
$input: null,
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'click a[data-name="add"]': 'add',
|
|
'click a[data-name="edit"]': 'edit',
|
|
'click a[data-name="remove"]': 'remove',
|
|
'change input[type="file"]': 'change'
|
|
},
|
|
|
|
|
|
/*
|
|
* focus
|
|
*
|
|
* This function will setup variables when focused on a field
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-file-uploader');
|
|
this.$input = this.$el.find('input[type="hidden"]');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* initialize
|
|
*
|
|
* This function is used to setup basic upload form attributes
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
initialize: function(){
|
|
|
|
// add attribute to form
|
|
if( this.o.uploader == 'basic' ) {
|
|
|
|
this.$el.closest('form').attr('enctype', 'multipart/form-data');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* prepare
|
|
*
|
|
* This function will prepare an object of attachment data
|
|
* selecting a library image vs embed an image via url return different data
|
|
* this function will keep the 2 consistent
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param attachment (object)
|
|
* @return data (object)
|
|
*/
|
|
|
|
prepare: function( attachment ) {
|
|
|
|
// defaults
|
|
attachment = attachment || {};
|
|
|
|
|
|
// bail ealry if already valid
|
|
if( attachment._valid ) return attachment;
|
|
|
|
|
|
// vars
|
|
var data = {
|
|
url: '',
|
|
alt: '',
|
|
title: '',
|
|
filename: '',
|
|
filesize: '',
|
|
icon: '/wp-includes/images/media/default.png'
|
|
};
|
|
|
|
|
|
// wp image
|
|
if( attachment.id ) {
|
|
|
|
// update data
|
|
data = attachment.attributes;
|
|
|
|
}
|
|
|
|
|
|
// valid
|
|
data._valid = true;
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render
|
|
*
|
|
* This function will render the UI
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param attachment (obj)
|
|
* @return n/a
|
|
*/
|
|
|
|
render: function( data ){
|
|
|
|
// prepare
|
|
data = this.prepare(data);
|
|
|
|
|
|
// update els
|
|
this.$el.find('img').attr({
|
|
src: data.icon,
|
|
alt: data.alt,
|
|
title: data.title
|
|
});
|
|
this.$el.find('[data-name="title"]').text( data.title );
|
|
this.$el.find('[data-name="filename"]').text( data.filename ).attr( 'href', data.url );
|
|
this.$el.find('[data-name="filesize"]').text( data.filesize );
|
|
|
|
|
|
// vars
|
|
var val = '';
|
|
|
|
|
|
// WP attachment
|
|
if( data.id ) {
|
|
|
|
val = data.id;
|
|
|
|
}
|
|
|
|
|
|
// update val
|
|
acf.val( this.$input, val );
|
|
|
|
|
|
// update class
|
|
if( val ) {
|
|
|
|
this.$el.addClass('has-value');
|
|
|
|
} else {
|
|
|
|
this.$el.removeClass('has-value');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
add: function() {
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// get repeater
|
|
var $repeater = acf.get_closest_field( $field, 'repeater' );
|
|
|
|
|
|
// popup
|
|
var frame = acf.media.popup({
|
|
|
|
title: acf._e('file', 'select'),
|
|
mode: 'select',
|
|
type: '',
|
|
field: $field.data('key'),
|
|
multiple: $repeater.exists(),
|
|
library: this.o.library,
|
|
mime_types: this.o.mime_types,
|
|
|
|
select: function( attachment, i ) {
|
|
|
|
// select / add another image field?
|
|
if( i > 0 ) {
|
|
|
|
// vars
|
|
var key = $field.data('key'),
|
|
$tr = $field.closest('.acf-row');
|
|
|
|
|
|
// reset field
|
|
$field = false;
|
|
|
|
|
|
// find next image field
|
|
$tr.nextAll('.acf-row:visible').each(function(){
|
|
|
|
// get next $field
|
|
$field = acf.get_field( key, $(this) );
|
|
|
|
|
|
// bail early if $next was not found
|
|
if( !$field ) return;
|
|
|
|
|
|
// bail early if next file uploader has value
|
|
if( $field.find('.acf-file-uploader.has-value').exists() ) {
|
|
|
|
$field = false;
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// end loop if $next is found
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
// add extra row if next is not found
|
|
if( !$field ) {
|
|
|
|
$tr = acf.fields.repeater.doFocus( $repeater ).add();
|
|
|
|
|
|
// bail early if no $tr (maximum rows hit)
|
|
if( !$tr ) return false;
|
|
|
|
|
|
// get next $field
|
|
$field = acf.get_field( key, $tr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// render
|
|
self.set('$field', $field).render( attachment );
|
|
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* edit
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
edit: function() {
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// vars
|
|
var val = this.$input.val();
|
|
|
|
|
|
// bail early if no val
|
|
if( !val ) return;
|
|
|
|
|
|
// popup
|
|
var frame = acf.media.popup({
|
|
|
|
title: acf._e('file', 'edit'),
|
|
button: acf._e('file', 'update'),
|
|
mode: 'edit',
|
|
attachment: val,
|
|
|
|
select: function( attachment, i ) {
|
|
|
|
// render
|
|
self.set('$field', $field).render( attachment );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
remove: function() {
|
|
|
|
// vars
|
|
var attachment = {};
|
|
|
|
|
|
// add file to field
|
|
this.render( attachment );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* change
|
|
*
|
|
* This function will update the hidden input when selecting a basic file to clear validation errors
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
change: function( e ){
|
|
|
|
this.$input.val( e.$el.val() );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.google_map = acf.field.extend({
|
|
|
|
type: 'google_map',
|
|
url: '',
|
|
$el: null,
|
|
$search: null,
|
|
|
|
timeout: null,
|
|
status : '', // '', 'loading', 'ready'
|
|
geocoder : false,
|
|
map : false,
|
|
maps : {},
|
|
$pending: $(),
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize',
|
|
'show': 'show'
|
|
},
|
|
|
|
events: {
|
|
'click a[data-name="clear"]': '_clear',
|
|
'click a[data-name="locate"]': '_locate',
|
|
'click a[data-name="search"]': '_search',
|
|
'keydown .search': '_keydown',
|
|
'keyup .search': '_keyup',
|
|
'focus .search': '_focus',
|
|
'blur .search': '_blur',
|
|
//'paste .search': '_paste',
|
|
'mousedown .acf-google-map': '_mousedown',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-google-map');
|
|
this.$search = this.$el.find('.search');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
this.o.id = this.$el.attr('id');
|
|
|
|
|
|
// get map
|
|
if( this.maps[ this.o.id ] ) {
|
|
|
|
this.map = this.maps[ this.o.id ];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* is_ready
|
|
*
|
|
* This function will ensure google API is available and return a boolean for the current status
|
|
*
|
|
* @type function
|
|
* @date 19/11/2014
|
|
* @since 5.0.9
|
|
*
|
|
* @param n/a
|
|
* @return (boolean)
|
|
*/
|
|
|
|
is_ready: function(){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// ready
|
|
if( this.status == 'ready' ) return true;
|
|
|
|
|
|
// loading
|
|
if( this.status == 'loading' ) return false;
|
|
|
|
|
|
// check exists (optimal)
|
|
if( acf.isset(window, 'google', 'maps', 'places') ) {
|
|
|
|
this.status = 'ready';
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
// check exists (ok)
|
|
if( acf.isset(window, 'google', 'maps') ) {
|
|
|
|
this.status = 'ready';
|
|
|
|
}
|
|
|
|
|
|
// attempt load google.maps.places
|
|
if( this.url ) {
|
|
|
|
// set status
|
|
this.status = 'loading';
|
|
|
|
|
|
// enqueue
|
|
acf.enqueue_script(this.url, function(){
|
|
|
|
// set status
|
|
self.status = 'ready';
|
|
|
|
|
|
// initialize pending
|
|
self.initialize_pending();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// ready
|
|
if( this.status == 'ready' ) return true;
|
|
|
|
|
|
// return
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* initialize_pending
|
|
*
|
|
* This function will initialize pending fields
|
|
*
|
|
* @type function
|
|
* @date 27/08/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
initialize_pending: function(){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
this.$pending.each(function(){
|
|
|
|
self.set('$field', $(this)).initialize();
|
|
|
|
});
|
|
|
|
|
|
// reset
|
|
this.$pending = $();
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* actions
|
|
*
|
|
* these functions are fired for this fields actions
|
|
*
|
|
* @type function
|
|
* @date 17/09/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param (mixed)
|
|
* @return n/a
|
|
*/
|
|
|
|
initialize: function(){
|
|
|
|
// add to pending
|
|
if( !this.is_ready() ) {
|
|
|
|
this.$pending = this.$pending.add( this.$field );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// load geocode
|
|
if( !this.geocoder ) {
|
|
|
|
this.geocoder = new google.maps.Geocoder();
|
|
|
|
}
|
|
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field,
|
|
$el = this.$el,
|
|
$search = this.$search;
|
|
|
|
|
|
// input value may be cached by browser, so update the search input to match
|
|
$search.val( this.$el.find('.input-address').val() );
|
|
|
|
|
|
// map
|
|
var map_args = acf.apply_filters('google_map_args', {
|
|
|
|
zoom: parseInt(this.o.zoom),
|
|
center: new google.maps.LatLng(this.o.lat, this.o.lng),
|
|
mapTypeId: google.maps.MapTypeId.ROADMAP
|
|
|
|
}, this.$field);
|
|
|
|
|
|
// create map
|
|
this.map = new google.maps.Map( this.$el.find('.canvas')[0], map_args);
|
|
|
|
|
|
// search
|
|
if( acf.isset(window, 'google', 'maps', 'places', 'Autocomplete') ) {
|
|
|
|
// vars
|
|
var autocomplete = new google.maps.places.Autocomplete( this.$search[0] );
|
|
|
|
|
|
// bind
|
|
autocomplete.bindTo('bounds', this.map);
|
|
|
|
|
|
// event
|
|
google.maps.event.addListener(autocomplete, 'place_changed', function( e ) {
|
|
|
|
// vars
|
|
var place = this.getPlace();
|
|
|
|
|
|
// search
|
|
self.search( place );
|
|
|
|
});
|
|
|
|
|
|
// append
|
|
this.map.autocomplete = autocomplete;
|
|
|
|
}
|
|
|
|
|
|
// marker
|
|
var marker_args = acf.apply_filters('google_map_marker_args', {
|
|
|
|
draggable: true,
|
|
raiseOnDrag: true,
|
|
map: this.map
|
|
|
|
}, this.$field);
|
|
|
|
|
|
// add marker
|
|
this.map.marker = new google.maps.Marker( marker_args );
|
|
|
|
|
|
// add references
|
|
this.map.$el = $el;
|
|
this.map.$field = $field;
|
|
|
|
|
|
// value exists?
|
|
var lat = $el.find('.input-lat').val(),
|
|
lng = $el.find('.input-lng').val();
|
|
|
|
if( lat && lng ) {
|
|
|
|
this.update(lat, lng).center();
|
|
|
|
}
|
|
|
|
|
|
// events
|
|
google.maps.event.addListener( this.map.marker, 'dragend', function(){
|
|
|
|
// vars
|
|
var position = this.map.marker.getPosition(),
|
|
lat = position.lat(),
|
|
lng = position.lng();
|
|
|
|
self.update( lat, lng ).sync();
|
|
|
|
});
|
|
|
|
|
|
google.maps.event.addListener( this.map, 'click', function( e ) {
|
|
|
|
// vars
|
|
var lat = e.latLng.lat(),
|
|
lng = e.latLng.lng();
|
|
|
|
|
|
self.update( lat, lng ).sync();
|
|
|
|
});
|
|
|
|
|
|
// add to maps
|
|
this.maps[ this.o.id ] = this.map;
|
|
|
|
},
|
|
|
|
search: function( place ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var address = this.$search.val();
|
|
|
|
|
|
// bail ealry if no address
|
|
if( !address ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// update input
|
|
this.$el.find('.input-address').val( address );
|
|
|
|
|
|
// is lat lng?
|
|
var latLng = address.split(',');
|
|
|
|
if( latLng.length == 2 ) {
|
|
|
|
var lat = latLng[0],
|
|
lng = latLng[1];
|
|
|
|
|
|
if( $.isNumeric(lat) && $.isNumeric(lng) ) {
|
|
|
|
// parse
|
|
lat = parseFloat(lat);
|
|
lng = parseFloat(lng);
|
|
|
|
self.update( lat, lng ).center();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// if place exists
|
|
if( place && place.geometry ) {
|
|
|
|
var lat = place.geometry.location.lat(),
|
|
lng = place.geometry.location.lng();
|
|
|
|
|
|
// update
|
|
self.update( lat, lng ).center();
|
|
|
|
|
|
// bail early
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// add class
|
|
this.$el.addClass('-loading');
|
|
|
|
self.geocoder.geocode({ 'address' : address }, function( results, status ){
|
|
|
|
// remove class
|
|
self.$el.removeClass('-loading');
|
|
|
|
|
|
// validate
|
|
if( status != google.maps.GeocoderStatus.OK ) {
|
|
|
|
console.log('Geocoder failed due to: ' + status);
|
|
return;
|
|
|
|
} else if( !results[0] ) {
|
|
|
|
console.log('No results found');
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// get place
|
|
place = results[0];
|
|
|
|
var lat = place.geometry.location.lat(),
|
|
lng = place.geometry.location.lng();
|
|
|
|
|
|
self.update( lat, lng ).center();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
update: function( lat, lng ){
|
|
|
|
// vars
|
|
var latlng = new google.maps.LatLng( lat, lng );
|
|
|
|
|
|
// update inputs
|
|
acf.val( this.$el.find('.input-lat'), lat );
|
|
acf.val( this.$el.find('.input-lng'), lng );
|
|
|
|
|
|
// update marker
|
|
this.map.marker.setPosition( latlng );
|
|
|
|
|
|
// show marker
|
|
this.map.marker.setVisible( true );
|
|
|
|
|
|
// update class
|
|
this.$el.addClass('-value');
|
|
|
|
|
|
// validation
|
|
this.$field.removeClass('error');
|
|
|
|
|
|
// action
|
|
acf.do_action('google_map_change', latlng, this.map, this.$field);
|
|
|
|
|
|
// blur input
|
|
this.$search.blur();
|
|
|
|
|
|
// return for chaining
|
|
return this;
|
|
|
|
},
|
|
|
|
center: function(){
|
|
|
|
// vars
|
|
var position = this.map.marker.getPosition(),
|
|
lat = this.o.lat,
|
|
lng = this.o.lng;
|
|
|
|
|
|
// if marker exists, center on the marker
|
|
if( position ) {
|
|
|
|
lat = position.lat();
|
|
lng = position.lng();
|
|
|
|
}
|
|
|
|
|
|
var latlng = new google.maps.LatLng( lat, lng );
|
|
|
|
|
|
// set center of map
|
|
this.map.setCenter( latlng );
|
|
|
|
},
|
|
|
|
sync: function(){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var position = this.map.marker.getPosition(),
|
|
latlng = new google.maps.LatLng( position.lat(), position.lng() );
|
|
|
|
|
|
// add class
|
|
this.$el.addClass('-loading');
|
|
|
|
|
|
// load
|
|
this.geocoder.geocode({ 'latLng' : latlng }, function( results, status ){
|
|
|
|
// remove class
|
|
self.$el.removeClass('-loading');
|
|
|
|
|
|
// validate
|
|
if( status != google.maps.GeocoderStatus.OK ) {
|
|
|
|
console.log('Geocoder failed due to: ' + status);
|
|
return;
|
|
|
|
} else if( !results[0] ) {
|
|
|
|
console.log('No results found');
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// get location
|
|
var location = results[0];
|
|
|
|
|
|
// update title
|
|
self.$search.val( location.formatted_address );
|
|
|
|
|
|
// update input
|
|
acf.val( self.$el.find('.input-address'), location.formatted_address );
|
|
|
|
});
|
|
|
|
|
|
// return for chaining
|
|
return this;
|
|
|
|
},
|
|
|
|
refresh: function(){
|
|
|
|
// bail early if not ready
|
|
if( !this.is_ready() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// trigger resize on map
|
|
google.maps.event.trigger(this.map, 'resize');
|
|
|
|
|
|
|
|
// center map
|
|
this.center();
|
|
|
|
},
|
|
|
|
show: function(){
|
|
|
|
// vars
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// center map when it is shown (by a tab / collapsed row)
|
|
// - use delay to avoid rendering issues with browsers (ensures div is visible)
|
|
setTimeout(function(){
|
|
|
|
self.set('$field', $field).refresh();
|
|
|
|
}, 10);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* events
|
|
*
|
|
* these functions are fired for this fields events
|
|
*
|
|
* @type function
|
|
* @date 17/09/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param e
|
|
* @return n/a
|
|
*/
|
|
|
|
_clear: function( e ){ // console.log('_clear');
|
|
|
|
// remove Class
|
|
this.$el.removeClass('-value -loading -search');
|
|
|
|
|
|
// clear search
|
|
this.$search.val('');
|
|
|
|
|
|
// clear inputs
|
|
acf.val( this.$el.find('.input-address'), '' );
|
|
acf.val( this.$el.find('.input-lat'), '' );
|
|
acf.val( this.$el.find('.input-lng'), '' );
|
|
|
|
|
|
// hide marker
|
|
this.map.marker.setVisible( false );
|
|
|
|
},
|
|
|
|
_locate: function( e ){ // console.log('_locate');
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// Try HTML5 geolocation
|
|
if( !navigator.geolocation ) {
|
|
|
|
alert( acf._e('google_map', 'browser_support') );
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
// add class
|
|
this.$el.addClass('-loading');
|
|
|
|
|
|
// load
|
|
navigator.geolocation.getCurrentPosition(function(position){
|
|
|
|
// remove class
|
|
self.$el.removeClass('-loading');
|
|
|
|
|
|
// vars
|
|
var lat = position.coords.latitude,
|
|
lng = position.coords.longitude;
|
|
|
|
self.update( lat, lng ).sync().center();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_search: function( e ){ // console.log('_search');
|
|
|
|
this.search();
|
|
|
|
},
|
|
|
|
_focus: function( e ){ // console.log('_focus');
|
|
|
|
// remove class
|
|
this.$el.removeClass('-value');
|
|
|
|
|
|
// toggle -search class
|
|
this._keyup();
|
|
|
|
},
|
|
|
|
_blur: function( e ){ // console.log('_blur');
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var val = this.$el.find('.input-address').val();
|
|
|
|
|
|
// bail early if no val
|
|
if( !val ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// revert search to hidden input value
|
|
this.timeout = setTimeout(function(){
|
|
|
|
self.$el.addClass('-value');
|
|
self.$search.val( val );
|
|
|
|
}, 100);
|
|
|
|
},
|
|
|
|
/*
|
|
_paste: function( e ){ console.log('_paste');
|
|
|
|
// reference
|
|
var $search = this.$search;
|
|
|
|
|
|
// blur search
|
|
$search.blur();
|
|
|
|
|
|
// clear timeout
|
|
this._mousedown(e);
|
|
|
|
|
|
// focus on input
|
|
setTimeout(function(){
|
|
|
|
$search.focus();
|
|
|
|
}, 1);
|
|
},
|
|
*/
|
|
|
|
_keydown: function( e ){ // console.log('_keydown');
|
|
|
|
// prevent form from submitting
|
|
if( e.which == 13 ) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_keyup: function( e ){ // console.log('_keyup');
|
|
|
|
// vars
|
|
var val = this.$search.val();
|
|
|
|
|
|
// toggle class
|
|
if( val ) {
|
|
|
|
this.$el.addClass('-search');
|
|
|
|
} else {
|
|
|
|
this.$el.removeClass('-search');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_mousedown: function( e ){ // console.log('_mousedown');
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// clear timeout in 1ms (_mousedown will run before _blur)
|
|
setTimeout(function(){
|
|
|
|
clearTimeout( self.timeout );
|
|
|
|
}, 1);
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.image = acf.field.extend({
|
|
|
|
type: 'image',
|
|
$el: null,
|
|
$input: null,
|
|
$img: null,
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'click a[data-name="add"]': 'add',
|
|
'click a[data-name="edit"]': 'edit',
|
|
'click a[data-name="remove"]': 'remove',
|
|
'change input[type="file"]': 'change'
|
|
},
|
|
|
|
|
|
/*
|
|
* focus
|
|
*
|
|
* This function will setup variables when focused on a field
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
focus: function(){
|
|
|
|
// vars
|
|
this.$el = this.$field.find('.acf-image-uploader');
|
|
this.$input = this.$el.find('input[type="hidden"]');
|
|
this.$img = this.$el.find('img');
|
|
|
|
|
|
// options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* initialize
|
|
*
|
|
* This function is used to setup basic upload form attributes
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
initialize: function(){
|
|
|
|
// add attribute to form
|
|
if( this.o.uploader == 'basic' ) {
|
|
|
|
this.$el.closest('form').attr('enctype', 'multipart/form-data');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* prepare
|
|
*
|
|
* This function will prepare an object of attachment data
|
|
* selecting a library image vs embed an image via url return different data
|
|
* this function will keep the 2 consistent
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param attachment (object)
|
|
* @return data (object)
|
|
*/
|
|
|
|
prepare: function( attachment ) {
|
|
|
|
// defaults
|
|
attachment = attachment || {};
|
|
|
|
|
|
// bail ealry if already valid
|
|
if( attachment._valid ) return attachment;
|
|
|
|
|
|
// vars
|
|
var data = {
|
|
url: '',
|
|
alt: '',
|
|
title: '',
|
|
caption: '',
|
|
description: '',
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
|
|
|
|
// wp image
|
|
if( attachment.id ) {
|
|
|
|
// update data
|
|
data = attachment.attributes;
|
|
|
|
|
|
// maybe get preview size
|
|
data.url = acf.maybe_get(data, 'sizes.'+this.o.preview_size+'.url', data.url);
|
|
|
|
}
|
|
|
|
|
|
// valid
|
|
data._valid = true;
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* render
|
|
*
|
|
* This function will render the UI
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param attachment (obj)
|
|
* @return n/a
|
|
*/
|
|
|
|
render: function( data ){
|
|
|
|
// prepare
|
|
data = this.prepare(data);
|
|
|
|
|
|
// update image
|
|
this.$img.attr({
|
|
src: data.url,
|
|
alt: data.alt,
|
|
title: data.title
|
|
});
|
|
|
|
|
|
// vars
|
|
var val = '';
|
|
|
|
|
|
// WP attachment
|
|
if( data.id ) {
|
|
|
|
val = data.id;
|
|
|
|
}
|
|
|
|
|
|
// update val
|
|
acf.val( this.$input, val );
|
|
|
|
|
|
// update class
|
|
if( val ) {
|
|
|
|
this.$el.addClass('has-value');
|
|
|
|
} else {
|
|
|
|
this.$el.removeClass('has-value');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
add: function() {
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// get repeater
|
|
var $repeater = acf.get_closest_field( this.$field, 'repeater' );
|
|
|
|
|
|
// popup
|
|
var frame = acf.media.popup({
|
|
|
|
title: acf._e('image', 'select'),
|
|
mode: 'select',
|
|
type: 'image',
|
|
field: $field.data('key'),
|
|
multiple: $repeater.exists(),
|
|
library: this.o.library,
|
|
mime_types: this.o.mime_types,
|
|
|
|
select: function( attachment, i ) {
|
|
|
|
// select / add another image field?
|
|
if( i > 0 ) {
|
|
|
|
// vars
|
|
var key = $field.data('key'),
|
|
$tr = $field.closest('.acf-row');
|
|
|
|
|
|
// reset field
|
|
$field = false;
|
|
|
|
|
|
// find next image field
|
|
$tr.nextAll('.acf-row:visible').each(function(){
|
|
|
|
// get next $field
|
|
$field = acf.get_field( key, $(this) );
|
|
|
|
|
|
// bail early if $next was not found
|
|
if( !$field ) return;
|
|
|
|
|
|
// bail early if next file uploader has value
|
|
if( $field.find('.acf-image-uploader.has-value').exists() ) {
|
|
|
|
$field = false;
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// end loop if $next is found
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
// add extra row if next is not found
|
|
if( !$field ) {
|
|
|
|
$tr = acf.fields.repeater.doFocus( $repeater ).add();
|
|
|
|
|
|
// bail early if no $tr (maximum rows hit)
|
|
if( !$tr ) return false;
|
|
|
|
|
|
// get next $field
|
|
$field = acf.get_field( key, $tr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// render
|
|
self.set('$field', $field).render( attachment );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* edit
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
edit: function() {
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// vars
|
|
var val = this.$input.val();
|
|
|
|
|
|
// bail early if no val
|
|
if( !val ) return;
|
|
|
|
|
|
// popup
|
|
var frame = acf.media.popup({
|
|
|
|
title: acf._e('image', 'edit'),
|
|
button: acf._e('image', 'update'),
|
|
mode: 'edit',
|
|
attachment: val,
|
|
|
|
select: function( attachment, i ) {
|
|
|
|
// render
|
|
self.set('$field', $field).render( attachment );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove
|
|
*
|
|
* event listener
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
remove: function() {
|
|
|
|
// vars
|
|
var attachment = {};
|
|
|
|
|
|
// add file to field
|
|
this.render( attachment );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* change
|
|
*
|
|
* This function will update the hidden input when selecting a basic file to clear validation errors
|
|
*
|
|
* @type function
|
|
* @date 12/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param e (event)
|
|
* @return n/a
|
|
*/
|
|
|
|
change: function( e ){
|
|
|
|
this.$input.val( e.$el.val() );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.media = acf.model.extend({
|
|
|
|
frames: [],
|
|
mime_types: {},
|
|
|
|
actions: {
|
|
'ready': 'ready'
|
|
},
|
|
|
|
|
|
/*
|
|
* frame
|
|
*
|
|
* This function will return the current frame
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.2
|
|
*
|
|
* @param n/a
|
|
* @return frame (object)
|
|
*/
|
|
|
|
frame: function(){
|
|
|
|
// vars
|
|
var i = this.frames.length - 1;
|
|
|
|
|
|
// bail early if no index
|
|
if( i < 0 ) return false;
|
|
|
|
|
|
// return
|
|
return this.frames[ i ];
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* destroy
|
|
*
|
|
* this function will destroy a frame
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @return frame (object)
|
|
* @return n/a
|
|
*/
|
|
|
|
destroy: function( frame ) {
|
|
|
|
// detach
|
|
frame.detach();
|
|
frame.dispose();
|
|
|
|
|
|
// remove frame
|
|
frame = null;
|
|
this.frames.pop();
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* popup
|
|
*
|
|
* This function will create a wp media popup frame
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param args (object)
|
|
* @return frame (object)
|
|
*/
|
|
|
|
popup: function( args ) {
|
|
|
|
// vars
|
|
var post_id = acf.get('post_id'),
|
|
frame = false;
|
|
|
|
|
|
// validate post_id
|
|
if( !$.isNumeric(post_id) ) post_id = 0;
|
|
|
|
|
|
// settings
|
|
var settings = acf.parse_args( args, {
|
|
mode: 'select', // 'select', 'edit'
|
|
title: '', // 'Upload Image'
|
|
button: '', // 'Select Image'
|
|
type: '', // 'image', ''
|
|
field: '', // 'field_123'
|
|
mime_types: '', // 'pdf, etc'
|
|
library: 'all', // 'all', 'uploadedTo'
|
|
multiple: false, // false, true, 'add'
|
|
attachment: 0, // the attachment to edit
|
|
post_id: post_id, // the post being edited
|
|
select: function(){}
|
|
});
|
|
|
|
|
|
// id changed to attributes
|
|
if( settings.id ) settings.attachment = settings.id;
|
|
|
|
|
|
// create frame
|
|
var frame = this.new_media_frame( settings );
|
|
|
|
|
|
// append
|
|
this.frames.push( frame );
|
|
|
|
|
|
// open popup (allow frame customization before opening)
|
|
setTimeout(function(){
|
|
|
|
frame.open();
|
|
|
|
}, 1);
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* _get_media_frame_settings
|
|
*
|
|
* This function will return an object containing frame settings
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param frame (object)
|
|
* @param settings (object)
|
|
* @return frame (object)
|
|
*/
|
|
|
|
_get_media_frame_settings: function( frame, settings ){
|
|
|
|
// select
|
|
if( settings.mode === 'select' ) {
|
|
|
|
frame = this._get_select_frame_settings( frame, settings );
|
|
|
|
// edit
|
|
} else if( settings.mode === 'edit' ) {
|
|
|
|
frame = this._get_edit_frame_settings( frame, settings );
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
_get_select_frame_settings: function( frame, settings ){
|
|
|
|
// type
|
|
if( settings.type ) {
|
|
|
|
frame.library.type = settings.type;
|
|
|
|
}
|
|
|
|
|
|
// library
|
|
if( settings.library === 'uploadedTo' ) {
|
|
|
|
frame.library.uploadedTo = settings.post_id;
|
|
|
|
}
|
|
|
|
|
|
// button
|
|
frame._button = acf._e('media', 'select');
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
_get_edit_frame_settings: function( frame, settings ){
|
|
|
|
// post__in
|
|
frame.library.post__in = [ settings.attachment ];
|
|
|
|
|
|
// button
|
|
frame._button = acf._e('media', 'update');
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* _add_media_frame_events
|
|
*
|
|
* This function will add events to the frame object
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
_add_media_frame_events: function( frame, settings ){
|
|
|
|
// log events
|
|
/*
|
|
frame.on('all', function( e ) {
|
|
|
|
console.log( 'frame all: %o', e );
|
|
|
|
});
|
|
*/
|
|
|
|
|
|
// add class
|
|
frame.on('open',function() {
|
|
|
|
// add class
|
|
this.$el.closest('.media-modal').addClass('acf-media-modal -' +settings.mode );
|
|
|
|
}, frame);
|
|
|
|
|
|
// edit image view
|
|
// source: media-views.js:2410 editImageContent()
|
|
frame.on('content:render:edit-image', function(){
|
|
|
|
var image = this.state().get('image'),
|
|
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 );
|
|
|
|
|
|
// select image
|
|
frame.on('select', function() {
|
|
|
|
// get selected images
|
|
var state = frame.state(),
|
|
image = state.get('image'),
|
|
selection = state.get('selection');
|
|
|
|
|
|
// if editing image
|
|
if( image ) {
|
|
|
|
settings.select.apply( frame, [image, 0] );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// if selecting images
|
|
if( selection ) {
|
|
|
|
// vars
|
|
var i = 0;
|
|
|
|
|
|
// loop
|
|
selection.each(function( attachment ){
|
|
|
|
settings.select.apply( frame, [attachment, i] );
|
|
|
|
i++;
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// close popup
|
|
frame.on('close',function(){
|
|
|
|
setTimeout(function(){
|
|
|
|
acf.media.destroy( frame );
|
|
|
|
}, 500);
|
|
|
|
});
|
|
|
|
|
|
// select
|
|
if( settings.mode === 'select' ) {
|
|
|
|
frame = this._add_select_frame_events( frame, settings );
|
|
|
|
// edit
|
|
} else if( settings.mode === 'edit' ) {
|
|
|
|
frame = this._add_edit_frame_events( frame, settings );
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
_add_select_frame_events: function( frame, settings ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// 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 = settings.field;
|
|
|
|
|
|
// remove acf_field so future Uploaders won't inherit
|
|
frame.on('open', function(){
|
|
|
|
delete _wpPluploadSettings.defaults.multipart_params._acfuploader;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// modify DOM
|
|
frame.on('content:activate:browse', function(){
|
|
|
|
// populate above vars making sure to allow for failure
|
|
try {
|
|
|
|
var toolbar = frame.content.get().toolbar,
|
|
filters = toolbar.get('filters'),
|
|
search = toolbar.get('search');
|
|
|
|
} catch(e) {
|
|
|
|
// one of the objects was 'undefined'... perhaps the frame open is Upload Files
|
|
// console.log( 'error %o', e );
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// image
|
|
if( settings.type == 'image' ) {
|
|
|
|
// update all
|
|
filters.filters.all.text = acf._e('image', 'all');
|
|
|
|
|
|
// remove some filters
|
|
delete filters.filters.audio;
|
|
delete filters.filters.video;
|
|
|
|
|
|
// update all filters to show images
|
|
$.each( filters.filters, function( k, filter ){
|
|
|
|
if( filter.props.type === null ) {
|
|
|
|
filter.props.type = 'image';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// custom mime types
|
|
if( settings.mime_types ) {
|
|
|
|
// explode
|
|
var extra_types = settings.mime_types.split(' ').join('').split('.').join('').split(',');
|
|
|
|
|
|
// loop through mime_types
|
|
$.each( extra_types, function( i, type ){
|
|
|
|
// find mime
|
|
$.each( self.mime_types, function( t, mime ){
|
|
|
|
// continue if key does not match
|
|
if( t.indexOf(type) === -1 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// create new filter
|
|
var filter = {
|
|
text: type,
|
|
props: {
|
|
status: null,
|
|
type: mime,
|
|
uploadedTo: null,
|
|
orderby: 'date',
|
|
order: 'DESC'
|
|
},
|
|
priority: 20
|
|
};
|
|
|
|
|
|
// append filter
|
|
filters.filters[ mime ] = filter;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// uploaded to post
|
|
if( settings.library == 'uploadedTo' ) {
|
|
|
|
// remove some filters
|
|
delete filters.filters.unattached;
|
|
delete filters.filters.uploaded;
|
|
|
|
|
|
// add 'uploadedTo' text
|
|
filters.$el.parent().append('<span class="acf-uploadedTo">' + acf._e('image', 'uploadedTo') + '</span>');
|
|
|
|
|
|
// add uploadedTo to filters
|
|
$.each( filters.filters, function( k, filter ){
|
|
|
|
filter.props.uploadedTo = settings.post_id;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// add _acfuploader to filters
|
|
$.each( filters.filters, function( k, filter ){
|
|
|
|
filter.props._acfuploader = settings.field;
|
|
|
|
});
|
|
|
|
|
|
// add _acfuplaoder to search
|
|
search.model.attributes._acfuploader = settings.field;
|
|
|
|
|
|
// render
|
|
if( typeof filters.refresh === 'function' ) {
|
|
|
|
filters.refresh();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
_add_edit_frame_events: function( frame, settings ){
|
|
|
|
// 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(),
|
|
selection = state.get('selection'),
|
|
attachment = wp.media.attachment( settings.attachment );
|
|
|
|
|
|
selection.add( attachment );
|
|
|
|
}, frame);
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* new_media_frame
|
|
*
|
|
* this function will create a new media frame
|
|
*
|
|
* @type function
|
|
* @date 11/04/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param settings (object)
|
|
* @return frame (object)
|
|
*/
|
|
|
|
new_media_frame: function( settings ){
|
|
|
|
// vars
|
|
var attributes = {
|
|
title: settings.title,
|
|
multiple: settings.multiple,
|
|
library: {},
|
|
states: [],
|
|
};
|
|
|
|
|
|
// get options
|
|
attributes = this._get_media_frame_settings( attributes, settings );
|
|
|
|
|
|
// create query
|
|
var Query = wp.media.query( attributes.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( acf.isset(Query, 'mirroring', 'args') ) {
|
|
|
|
Query.mirroring.args._acfuploader = settings.field;
|
|
|
|
}
|
|
|
|
|
|
// add states
|
|
attributes.states = [
|
|
|
|
// main state
|
|
new wp.media.controller.Library({
|
|
library: Query,
|
|
multiple: attributes.multiple,
|
|
title: attributes.title,
|
|
priority: 20,
|
|
filterable: 'all',
|
|
editable: true,
|
|
|
|
// If the user isn't allowed to edit fields,
|
|
// can they still edit it locally?
|
|
allowLocalEdits: true,
|
|
})
|
|
|
|
];
|
|
|
|
|
|
// edit image functionality (added in WP 3.9)
|
|
if( acf.isset(wp, 'media', 'controller', 'EditImage') ) {
|
|
|
|
attributes.states.push( new wp.media.controller.EditImage() );
|
|
|
|
}
|
|
|
|
|
|
// create frame
|
|
var frame = wp.media( attributes );
|
|
|
|
|
|
// add args reference
|
|
frame.acf = settings;
|
|
|
|
|
|
// add events
|
|
frame = this._add_media_frame_events( frame, settings );
|
|
|
|
|
|
// return
|
|
return frame;
|
|
|
|
},
|
|
|
|
ready: function(){
|
|
|
|
// vars
|
|
var version = acf.get('wp_version'),
|
|
browser = acf.get('browser'),
|
|
post_id = acf.get('post_id');
|
|
|
|
|
|
// update wp.media
|
|
if( acf.isset(window,'wp','media','view','settings','post') && $.isNumeric(post_id) ) {
|
|
|
|
wp.media.view.settings.post.id = post_id;
|
|
|
|
}
|
|
|
|
|
|
// append browser
|
|
if( browser ) {
|
|
|
|
$('body').addClass('browser-' + browser );
|
|
|
|
}
|
|
|
|
|
|
// append version
|
|
if( version ) {
|
|
|
|
// ensure is string
|
|
version = version + '';
|
|
|
|
|
|
// use only major version
|
|
major = version.substr(0,1);
|
|
|
|
|
|
// add body class
|
|
$('body').addClass('major-' + major);
|
|
|
|
}
|
|
|
|
|
|
// customize wp.media views
|
|
if( acf.isset(window, 'wp', 'media', 'view') ) {
|
|
|
|
//this.customize_Attachments();
|
|
//this.customize_Query();
|
|
//this.add_AcfEmbed();
|
|
this.customize_Attachment();
|
|
this.customize_AttachmentFiltersAll();
|
|
this.customize_AttachmentCompat();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
add_AcfEmbed: function(){
|
|
|
|
//test urls
|
|
//(image) jpg: http://www.ml24.net/img/ml24_design_process_scion_frs_3d_rendering.jpg
|
|
//(image) svg: http://kompozer.net/images/svg/Mozilla_Firefox.svg
|
|
//(file) pdf: http://martinfowler.com/ieeeSoftware/whenType.pdf
|
|
//(video) mp4: https://videos.files.wordpress.com/kUJmAcSf/bbb_sunflower_1080p_30fps_normal_hd.mp4
|
|
|
|
|
|
|
|
// add view
|
|
wp.media.view.AcfEmbed = wp.media.view.Embed.extend({
|
|
|
|
initialize: function() {
|
|
|
|
// set attachments
|
|
this.model.props.attributes = this.controller.acf.attachment || {};
|
|
|
|
|
|
// refresh
|
|
wp.media.view.Embed.prototype.initialize.apply( this, arguments );
|
|
|
|
},
|
|
|
|
refresh: function() {
|
|
|
|
// vars
|
|
var attachment = acf.parse_args(this.model.props.attributes, {
|
|
url: '',
|
|
filename: '',
|
|
title: '',
|
|
caption: '',
|
|
alt: '',
|
|
description: '',
|
|
type: '',
|
|
ext: ''
|
|
});
|
|
|
|
|
|
// update attachment
|
|
if( attachment.url ) {
|
|
|
|
// filename
|
|
attachment.filename = attachment.url.split('/').pop().split('?')[0];
|
|
|
|
|
|
// update
|
|
attachment.ext = attachment.filename.split('.').pop();
|
|
attachment.type = /(jpe?g|png|gif|svg)/i.test(attachment.ext) ? 'image': 'file';
|
|
|
|
}
|
|
|
|
|
|
// auto generate title
|
|
if( attachment.filename && !attachment.title ) {
|
|
|
|
// replace
|
|
attachment.title = attachment.filename.split('-').join(' ').split('_').join(' ');
|
|
|
|
|
|
// uppercase first word
|
|
attachment.title = attachment.title.charAt(0).toUpperCase() + attachment.title.slice(1);
|
|
|
|
|
|
// remove extension
|
|
attachment.title = attachment.title.replace('.'+attachment.ext, '');
|
|
|
|
|
|
// update model
|
|
this.model.props.attributes.title = attachment.title;
|
|
|
|
}
|
|
|
|
|
|
// save somee extra data
|
|
this.model.props.attributes.filename = attachment.filename;
|
|
this.model.props.attributes.type = attachment.type;
|
|
|
|
|
|
// always show image view
|
|
// avoid this.model.set() to prevent listeners updating view
|
|
this.model.attributes.type = 'image';
|
|
|
|
|
|
// refresh
|
|
wp.media.view.Embed.prototype.refresh.apply( this, arguments );
|
|
|
|
|
|
// append title
|
|
this.$el.find('.setting.caption').before([
|
|
'<label class="setting title">',
|
|
'<span>Title</span>',
|
|
'<input type="text" data-setting="title" value="' + attachment.title + '">',
|
|
'</label>'
|
|
].join(''));
|
|
|
|
|
|
// append description
|
|
this.$el.find('.setting.alt-text').after([
|
|
'<label class="setting description">',
|
|
'<span>Description</span>',
|
|
'<textarea type="text" data-setting="description">' + attachment.description + '</textarea>',
|
|
'</label>'
|
|
].join(''));
|
|
|
|
|
|
// hide alt
|
|
if( attachment.type !== 'image' ) {
|
|
|
|
this.$el.find('.setting.alt-text').hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
*/
|
|
/*
|
|
|
|
customize_Attachments: function(){
|
|
|
|
// vars
|
|
var Attachments = wp.media.model.Attachments;
|
|
|
|
|
|
wp.media.model.Attachments = Attachments.extend({
|
|
|
|
initialize: function( models, options ){
|
|
|
|
// console.log('My Attachments initialize: %o %o %o', this, models, options);
|
|
|
|
// return
|
|
return Attachments.prototype.initialize.apply( this, arguments );
|
|
|
|
},
|
|
|
|
sync: function( method, model, options ) {
|
|
|
|
// console.log('My Attachments sync: %o %o %o %o', this, method, model, options);
|
|
|
|
|
|
// return
|
|
return Attachments.prototype.sync.apply( this, arguments );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
customize_Query: function(){
|
|
|
|
// console.log('customize Query!');
|
|
|
|
// vars
|
|
var Query = wp.media.model.Query;
|
|
|
|
|
|
wp.media.model.Query = {};
|
|
|
|
},
|
|
*/
|
|
|
|
customize_Attachment: function(){
|
|
|
|
// vars
|
|
var AttachmentLibrary = wp.media.view.Attachment.Library;
|
|
|
|
|
|
// extend
|
|
wp.media.view.Attachment.Library = AttachmentLibrary.extend({
|
|
|
|
render: function() {
|
|
|
|
// vars
|
|
var frame = acf.media.frame(),
|
|
errors = acf.maybe_get(this, 'model.attributes.acf_errors');
|
|
|
|
|
|
// add class
|
|
// also make sure frame exists to prevent this logic running on a WP popup (such as feature image)
|
|
if( frame && errors ) {
|
|
|
|
this.$el.addClass('acf-disabled');
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
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 = acf.media.frame(),
|
|
errors = acf.maybe_get(this, 'model.attributes.acf_errors'),
|
|
$sidebar = this.controller.$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.maybe_get(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._e('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
|
|
AttachmentLibrary.prototype.toggleSelection.apply( this, arguments );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
customize_AttachmentFiltersAll: function(){
|
|
|
|
// add function refresh
|
|
wp.media.view.AttachmentFilters.All.prototype.refresh = 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() );
|
|
|
|
};
|
|
|
|
},
|
|
|
|
customize_AttachmentCompat: function(){
|
|
|
|
// vars
|
|
var AttachmentCompat = wp.media.view.AttachmentCompat;
|
|
|
|
|
|
// extend
|
|
wp.media.view.AttachmentCompat = AttachmentCompat.extend({
|
|
|
|
render: function() {
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// validate
|
|
if( this.ignore_render ) return this;
|
|
|
|
|
|
// add button
|
|
setTimeout(function(){
|
|
|
|
// vars
|
|
var $media_model = self.$el.closest('.media-modal');
|
|
|
|
|
|
// does button already exist?
|
|
if( $media_model.find('.media-frame-router .acf-expand-details').exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// create button
|
|
var $a = $([
|
|
'<a href="#" class="acf-expand-details">',
|
|
'<span class="is-closed"><span class="acf-icon -left small grey"></span>' + acf._e('expand_details') + '</span>',
|
|
'<span class="is-open"><span class="acf-icon -right small grey"></span>' + acf._e('collapse_details') + '</span>',
|
|
'</a>'
|
|
].join(''));
|
|
|
|
|
|
// add events
|
|
$a.on('click', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
if( $media_model.hasClass('acf-expanded') ) {
|
|
|
|
$media_model.removeClass('acf-expanded');
|
|
|
|
} else {
|
|
|
|
$media_model.addClass('acf-expanded');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// append
|
|
$media_model.find('.media-frame-router').append( $a );
|
|
|
|
|
|
}, 0);
|
|
|
|
|
|
// setup fields
|
|
// The clearTimout is needed to prevent many setup functions from running at the same time
|
|
clearTimeout( acf.media.render_timout );
|
|
acf.media.render_timout = setTimeout(function(){
|
|
|
|
acf.do_action('append', self.$el);
|
|
|
|
}, 50);
|
|
|
|
|
|
// return
|
|
return AttachmentCompat.prototype.render.apply( this, arguments );
|
|
|
|
},
|
|
|
|
|
|
dispose: function() {
|
|
|
|
// remove
|
|
acf.do_action('remove', this.$el);
|
|
|
|
|
|
// return
|
|
return AttachmentCompat.prototype.dispose.apply( this, arguments );
|
|
|
|
},
|
|
|
|
|
|
save: function( e ) {
|
|
|
|
if( e ) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
|
|
// serialize form
|
|
var data = acf.serialize_form(this.$el);
|
|
|
|
|
|
// ignore render
|
|
this.ignore_render = true;
|
|
|
|
|
|
// save
|
|
this.model.saveCompat( data );
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.oembed = {
|
|
|
|
search : function( $el ){
|
|
|
|
// vars
|
|
var s = $el.find('[data-name="search-input"]').val();
|
|
|
|
|
|
// fix missing 'http://' - causes the oembed code to error and fail
|
|
if( s.substr(0, 4) != 'http' )
|
|
{
|
|
s = 'http://' + s;
|
|
$el.find('[data-name="search-input"]').val( s );
|
|
}
|
|
|
|
|
|
// show loading
|
|
$el.addClass('is-loading');
|
|
|
|
|
|
// AJAX data
|
|
var ajax_data = acf.prepare_for_ajax({
|
|
'action' : 'acf/fields/oembed/search',
|
|
's' : s,
|
|
'width' : acf.get_data($el, 'width'),
|
|
'height' : acf.get_data($el, 'height')
|
|
});
|
|
|
|
|
|
// abort XHR if this field is already loading AJAX data
|
|
if( $el.data('xhr') )
|
|
{
|
|
$el.data('xhr').abort();
|
|
}
|
|
|
|
|
|
// get HTML
|
|
var xhr = $.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
data: ajax_data,
|
|
type: 'post',
|
|
dataType: 'html',
|
|
success: function( html ){
|
|
|
|
$el.removeClass('is-loading');
|
|
|
|
|
|
// update from json
|
|
acf.fields.oembed.search_success( $el, s, html );
|
|
|
|
|
|
// no results?
|
|
if( !html )
|
|
{
|
|
acf.fields.oembed.search_error( $el );
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
|
|
// update el data
|
|
$el.data('xhr', xhr);
|
|
|
|
},
|
|
|
|
search_success : function( $el, s, html ){
|
|
|
|
$el.removeClass('has-error').addClass('has-value');
|
|
|
|
$el.find('[data-name="value-input"]').val( s );
|
|
$el.find('[data-name="value-title"]').html( s );
|
|
$el.find('[data-name="value-embed"]').html( html );
|
|
|
|
},
|
|
|
|
search_error : function( $el ){
|
|
|
|
// update class
|
|
$el.removeClass('has-value').addClass('has-error');
|
|
|
|
},
|
|
|
|
clear : function( $el ){
|
|
|
|
// update class
|
|
$el.removeClass('has-error has-value');
|
|
|
|
|
|
// clear search
|
|
$el.find('[data-name="search-input"]').val('');
|
|
|
|
|
|
// clear inputs
|
|
$el.find('[data-name="value-input"]').val( '' );
|
|
$el.find('[data-name="value-title"]').html( '' );
|
|
$el.find('[data-name="value-embed"]').html( '' );
|
|
|
|
},
|
|
|
|
edit : function( $el ){
|
|
|
|
// update class
|
|
$el.addClass('is-editing');
|
|
|
|
|
|
// set url and focus
|
|
var url = $el.find('[data-name="value-title"]').text();
|
|
|
|
$el.find('[data-name="search-input"]').val( url ).focus()
|
|
|
|
},
|
|
|
|
blur : function( $el ){
|
|
|
|
$el.removeClass('is-editing');
|
|
|
|
|
|
// set url and focus
|
|
var old_url = $el.find('[data-name="value-title"]').text(),
|
|
new_url = $el.find('[data-name="search-input"]').val(),
|
|
embed = $el.find('[data-name="value-embed"]').html();
|
|
|
|
|
|
// bail early if no valu
|
|
if( !new_url ) {
|
|
|
|
this.clear( $el );
|
|
return;
|
|
}
|
|
|
|
|
|
// bail early if no change
|
|
if( new_url == old_url ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.search( $el );
|
|
|
|
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Events
|
|
*
|
|
* jQuery events for this field
|
|
*
|
|
* @type function
|
|
* @date 1/03/2011
|
|
*
|
|
* @param N/A
|
|
* @return N/A
|
|
*/
|
|
|
|
$(document).on('click', '.acf-oembed [data-name="search-button"]', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
acf.fields.oembed.search( $(this).closest('.acf-oembed') );
|
|
|
|
$(this).blur();
|
|
|
|
});
|
|
|
|
$(document).on('click', '.acf-oembed [data-name="clear-button"]', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
acf.fields.oembed.clear( $(this).closest('.acf-oembed') );
|
|
|
|
$(this).blur();
|
|
|
|
});
|
|
|
|
$(document).on('click', '.acf-oembed [data-name="value-title"]', function( e ){
|
|
|
|
e.preventDefault();
|
|
|
|
acf.fields.oembed.edit( $(this).closest('.acf-oembed') );
|
|
|
|
});
|
|
|
|
$(document).on('keypress', '.acf-oembed [data-name="search-input"]', function( e ){
|
|
|
|
// don't submit form
|
|
if( e.which == 13 )
|
|
{
|
|
e.preventDefault();
|
|
}
|
|
|
|
});
|
|
|
|
|
|
$(document).on('keyup', '.acf-oembed [data-name="search-input"]', function( e ){
|
|
|
|
// bail early if no value
|
|
if( ! $(this).val() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// bail early for directional controls
|
|
if( ! e.which ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
acf.fields.oembed.search( $(this).closest('.acf-oembed') );
|
|
|
|
});
|
|
|
|
$(document).on('blur', '.acf-oembed [data-name="search-input"]', function(e){
|
|
|
|
acf.fields.oembed.blur( $(this).closest('.acf-oembed') );
|
|
|
|
});
|
|
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.radio = acf.field.extend({
|
|
|
|
type: 'radio',
|
|
|
|
$ul: null,
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'click input[type="radio"]': 'click',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// focus on $select
|
|
this.$ul = this.$field.find('.acf-radio-list');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$ul );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* initialize
|
|
*
|
|
* This function will fix a bug found in Chrome.
|
|
* A radio input (for a given name) may only have 1 selected value. When used within a fc layout
|
|
* multiple times (clone field), the selected value (default value) will not be checked.
|
|
* This simply re-checks it.
|
|
*
|
|
* @type function
|
|
* @date 30/08/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
initialize: function(){
|
|
|
|
// find selected input and check it
|
|
this.$ul.find('.selected input').prop('checked', true);
|
|
|
|
},
|
|
|
|
click: function(e){
|
|
|
|
// vars
|
|
var $radio = e.$el,
|
|
$label = $radio.parent('label'),
|
|
selected = $label.hasClass('selected'),
|
|
val = $radio.val();
|
|
|
|
|
|
// remove previous selected
|
|
this.$ul.find('.selected').removeClass('selected');
|
|
|
|
|
|
// add active class
|
|
$label.addClass('selected');
|
|
|
|
|
|
// allow null
|
|
if( this.o.allow_null && selected ) {
|
|
|
|
// unselect
|
|
e.$el.prop('checked', false);
|
|
$label.removeClass('selected');
|
|
val = false;
|
|
|
|
|
|
// trigger change
|
|
e.$el.trigger('change');
|
|
|
|
}
|
|
|
|
|
|
// other
|
|
if( this.o.other_choice ) {
|
|
|
|
// vars
|
|
var $other = this.$ul.find('input[type="text"]');
|
|
|
|
|
|
// show
|
|
if( val === 'other' ) {
|
|
|
|
$other.prop('disabled', false).attr('name', $radio.attr('name'));
|
|
|
|
// hide
|
|
} else {
|
|
|
|
$other.prop('disabled', true).attr('name', '');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.relationship = acf.field.extend({
|
|
|
|
type: 'relationship',
|
|
|
|
$el: null,
|
|
$input: null,
|
|
$filters: null,
|
|
$choices: null,
|
|
$values: null,
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'keypress [data-filter]': 'submit_filter',
|
|
'change [data-filter]': 'change_filter',
|
|
'keyup [data-filter]': 'change_filter',
|
|
'click .choices .acf-rel-item': 'add_item',
|
|
'click [data-name="remove_item"]': 'remove_item'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-relationship');
|
|
this.$input = this.$el.find('.acf-hidden input');
|
|
this.$choices = this.$el.find('.choices'),
|
|
this.$values = this.$el.find('.values');
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field,
|
|
$el = this.$el,
|
|
$input = this.$input;
|
|
|
|
|
|
// right sortable
|
|
this.$values.children('.list').sortable({
|
|
items: 'li',
|
|
forceHelperSize: true,
|
|
forcePlaceholderSize: true,
|
|
scroll: true,
|
|
update: function(){
|
|
|
|
$input.trigger('change');
|
|
|
|
}
|
|
});
|
|
|
|
|
|
this.$choices.children('.list').scrollTop(0).on('scroll', function(e){
|
|
|
|
// bail early if no more results
|
|
if( $el.hasClass('is-loading') || $el.hasClass('is-empty') ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// Scrolled to bottom
|
|
if( Math.ceil( $(this).scrollTop() ) + $(this).innerHeight() >= $(this).get(0).scrollHeight ) {
|
|
|
|
// get paged
|
|
var paged = $el.data('paged') || 1;
|
|
|
|
|
|
// update paged
|
|
$el.data('paged', (paged+1) );
|
|
|
|
|
|
// fetch
|
|
self.set('$field', $field).fetch();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
// scroll event
|
|
var maybe_fetch = function( e ){
|
|
console.log('scroll');
|
|
// remove listener
|
|
$(window).off('scroll', maybe_fetch);
|
|
|
|
|
|
// is field in view
|
|
if( acf.is_in_view($field) ) {
|
|
|
|
// fetch
|
|
self.doFocus($field);
|
|
self.fetch();
|
|
|
|
|
|
// return
|
|
return;
|
|
}
|
|
|
|
|
|
// add listener
|
|
setTimeout(function(){
|
|
|
|
$(window).on('scroll', maybe_fetch);
|
|
|
|
}, 500);
|
|
|
|
};
|
|
*/
|
|
|
|
|
|
// fetch
|
|
this.fetch();
|
|
|
|
},
|
|
|
|
/*
|
|
show: function(){
|
|
|
|
console.log('show field: %o', this.o.xhr);
|
|
|
|
// bail ealry if already loaded
|
|
if( typeof this.o.xhr !== 'undefined' ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// is field in view
|
|
if( acf.is_in_view(this.$field) ) {
|
|
|
|
// fetch
|
|
this.fetch();
|
|
|
|
}
|
|
|
|
},
|
|
*/
|
|
|
|
maybe_fetch: function(){
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// abort timeout
|
|
if( this.o.timeout ) {
|
|
|
|
clearTimeout( this.o.timeout );
|
|
|
|
}
|
|
|
|
|
|
// fetch
|
|
var timeout = setTimeout(function(){
|
|
|
|
self.doFocus($field);
|
|
self.fetch();
|
|
|
|
}, 300);
|
|
|
|
this.$el.data('timeout', timeout);
|
|
|
|
},
|
|
|
|
fetch: function(){
|
|
|
|
// reference
|
|
var self = this,
|
|
$field = this.$field;
|
|
|
|
|
|
// add class
|
|
this.$el.addClass('is-loading');
|
|
|
|
|
|
// abort XHR if this field is already loading AJAX data
|
|
if( this.o.xhr ) {
|
|
|
|
this.o.xhr.abort();
|
|
this.o.xhr = false;
|
|
|
|
}
|
|
|
|
|
|
// add to this.o
|
|
this.o.action = 'acf/fields/relationship/query';
|
|
this.o.field_key = $field.data('key');
|
|
this.o.post_id = acf.get('post_id');
|
|
|
|
|
|
// ready for ajax
|
|
var ajax_data = acf.prepare_for_ajax( this.o );
|
|
|
|
|
|
// clear html if is new query
|
|
if( ajax_data.paged == 1 ) {
|
|
|
|
this.$choices.children('.list').html('')
|
|
|
|
}
|
|
|
|
|
|
// add message
|
|
this.$choices.find('ul:last').append('<p><i class="acf-loading"></i> ' + acf._e('relationship', 'loading') + '</p>');
|
|
|
|
|
|
// get results
|
|
var xhr = $.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
dataType: 'json',
|
|
type: 'post',
|
|
data: ajax_data,
|
|
success: function( json ){
|
|
|
|
self.set('$field', $field).render( json );
|
|
|
|
}
|
|
});
|
|
|
|
|
|
// update el data
|
|
this.$el.data('xhr', xhr);
|
|
|
|
},
|
|
|
|
render: function( json ){
|
|
|
|
// remove loading class
|
|
this.$el.removeClass('is-loading is-empty');
|
|
|
|
|
|
// remove p tag
|
|
this.$choices.find('p').remove();
|
|
|
|
|
|
// no results?
|
|
if( !json || !json.results || !json.results.length ) {
|
|
|
|
// add class
|
|
this.$el.addClass('is-empty');
|
|
|
|
|
|
// add message
|
|
if( this.o.paged == 1 ) {
|
|
|
|
this.$choices.children('.list').append('<p>' + acf._e('relationship', 'empty') + '</p>');
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// get new results
|
|
var $new = $( this.walker(json.results) );
|
|
|
|
|
|
// apply .disabled to left li's
|
|
this.$values.find('.acf-rel-item').each(function(){
|
|
|
|
$new.find('.acf-rel-item[data-id="' + $(this).data('id') + '"]').addClass('disabled');
|
|
|
|
});
|
|
|
|
|
|
// underline search match
|
|
if( this.o.s ) {
|
|
|
|
var s = this.o.s;
|
|
|
|
$new.find('.acf-rel-item').each(function(){
|
|
|
|
// vars
|
|
var find = $(this).text(),
|
|
replace = find.replace( new RegExp('(' + s + ')', 'gi'), '<b>$1</b>');
|
|
|
|
$(this).html( $(this).html().replace(find, replace) );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// append
|
|
this.$choices.children('.list').append( $new );
|
|
|
|
|
|
// merge together groups
|
|
var label = '',
|
|
$list = null;
|
|
|
|
this.$choices.find('.acf-rel-label').each(function(){
|
|
|
|
if( $(this).text() == label ) {
|
|
|
|
$list.append( $(this).siblings('ul').html() );
|
|
|
|
$(this).parent().remove();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// update vars
|
|
label = $(this).text();
|
|
$list = $(this).siblings('ul');
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
walker: function( data ){
|
|
|
|
// vars
|
|
var s = '';
|
|
|
|
|
|
// loop through data
|
|
if( $.isArray(data) ) {
|
|
|
|
for( var k in data ) {
|
|
|
|
s += this.walker( data[ k ] );
|
|
|
|
}
|
|
|
|
} else if( $.isPlainObject(data) ) {
|
|
|
|
// optgroup
|
|
if( data.children !== undefined ) {
|
|
|
|
s += '<li><span class="acf-rel-label">' + data.text + '</span><ul class="acf-bl">';
|
|
|
|
s += this.walker( data.children );
|
|
|
|
s += '</ul></li>';
|
|
|
|
} else {
|
|
|
|
s += '<li><span class="acf-rel-item" data-id="' + data.id + '">' + data.text + '</span></li>';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return s;
|
|
|
|
},
|
|
|
|
submit_filter: function( e ){
|
|
|
|
// don't submit form
|
|
if( e.which == 13 ) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
change_filter: function( e ){
|
|
|
|
// vars
|
|
var val = e.$el.val(),
|
|
filter = e.$el.data('filter');
|
|
|
|
|
|
// Bail early if filter has not changed
|
|
if( this.$el.data(filter) == val ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// update attr
|
|
this.$el.data(filter, val);
|
|
|
|
|
|
// reset paged
|
|
this.$el.data('paged', 1);
|
|
|
|
|
|
// fetch
|
|
if( e.$el.is('select') ) {
|
|
|
|
this.fetch();
|
|
|
|
// search must go through timeout
|
|
} else {
|
|
|
|
this.maybe_fetch();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
add_item: function( e ){
|
|
|
|
// max posts
|
|
if( this.o.max > 0 ) {
|
|
|
|
if( this.$values.find('.acf-rel-item').length >= this.o.max ) {
|
|
|
|
alert( acf._e('relationship', 'max').replace('{max}', this.o.max) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// can be added?
|
|
if( e.$el.hasClass('disabled') ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// disable
|
|
e.$el.addClass('disabled');
|
|
|
|
|
|
// template
|
|
var html = [
|
|
'<li>',
|
|
'<input type="hidden" name="' + this.$input.attr('name') + '[]" value="' + e.$el.data('id') + '" />',
|
|
'<span data-id="' + e.$el.data('id') + '" class="acf-rel-item">' + e.$el.html(),
|
|
'<a href="#" class="acf-icon -minus small dark" data-name="remove_item"></a>',
|
|
'</span>',
|
|
'</li>'].join('');
|
|
|
|
|
|
// add new li
|
|
this.$values.children('.list').append( html )
|
|
|
|
|
|
// trigger change on new_li
|
|
this.$input.trigger('change');
|
|
|
|
|
|
// validation
|
|
acf.validation.remove_error( this.$field );
|
|
|
|
},
|
|
|
|
remove_item : function( e ){
|
|
|
|
// vars
|
|
var $span = e.$el.parent(),
|
|
id = $span.data('id');
|
|
|
|
|
|
// remove
|
|
$span.parent('li').remove();
|
|
|
|
|
|
// show
|
|
this.$choices.find('.acf-rel-item[data-id="' + id + '"]').removeClass('disabled');
|
|
|
|
|
|
// trigger change on new_li
|
|
this.$input.trigger('change');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
/*
|
|
* acf.select2
|
|
*
|
|
* all logic to create select2 instances
|
|
*
|
|
* @type function
|
|
* @date 16/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
acf.select2 = acf.model.extend({
|
|
|
|
// vars
|
|
version: 0,
|
|
|
|
|
|
// actions
|
|
actions: {
|
|
'ready 1': 'ready',
|
|
},
|
|
|
|
|
|
/*
|
|
* ready
|
|
*
|
|
* This function will setup vars
|
|
*
|
|
* @type function
|
|
* @date 21/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
ready: function(){
|
|
|
|
// determine Select2 version
|
|
if( acf.maybe_get(window, 'Select2') ) {
|
|
|
|
this.version = 3;
|
|
|
|
this.l10n_v3();
|
|
|
|
} else if( acf.maybe_get(window, 'jQuery.fn.select2.amd') ) {
|
|
|
|
this.version = 4;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* l10n_v3
|
|
*
|
|
* This function will set l10n for Select2 v3
|
|
*
|
|
* @type function
|
|
* @date 21/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
l10n_v3: function(){
|
|
|
|
// vars
|
|
var locale = acf.get('locale'),
|
|
rtl = acf.get('rtl')
|
|
l10n = acf._e('select');
|
|
|
|
|
|
// bail ealry if no l10n
|
|
if( !l10n ) return;
|
|
|
|
|
|
// vars
|
|
var l10n_functions = {
|
|
formatMatches: function( matches ) {
|
|
|
|
if ( 1 === matches ) {
|
|
return l10n.matches_1;
|
|
}
|
|
|
|
return l10n.matches_n.replace('%d', matches);
|
|
},
|
|
formatNoMatches: function() {
|
|
return l10n.matches_0;
|
|
},
|
|
formatAjaxError: function() {
|
|
return l10n.load_fail;
|
|
},
|
|
formatInputTooShort: function( input, min ) {
|
|
var number = min - input.length;
|
|
|
|
if ( 1 === number ) {
|
|
return l10n.input_too_short_1;
|
|
}
|
|
|
|
return l10n.input_too_short_n.replace( '%d', number );
|
|
},
|
|
formatInputTooLong: function( input, max ) {
|
|
var number = input.length - max;
|
|
|
|
if ( 1 === number ) {
|
|
return l10n.input_too_long_1;
|
|
}
|
|
|
|
return l10n.input_too_long_n.replace( '%d', number );
|
|
},
|
|
formatSelectionTooBig: function( limit ) {
|
|
if ( 1 === limit ) {
|
|
return l10n.selection_too_long_1;
|
|
}
|
|
|
|
return l10n.selection_too_long_n.replace( '%d', limit );
|
|
},
|
|
formatLoadMore: function() {
|
|
return l10n.load_more;
|
|
},
|
|
formatSearching: function() {
|
|
return l10n.searching;
|
|
}
|
|
};
|
|
|
|
|
|
// ensure locales exists
|
|
// older versions of Select2 did not have a locale storage
|
|
$.fn.select2.locales = acf.maybe_get(window, 'jQuery.fn.select2.locales', {});
|
|
|
|
|
|
// append
|
|
$.fn.select2.locales[ locale ] = l10n_functions;
|
|
$.extend($.fn.select2.defaults, l10n_functions);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init
|
|
*
|
|
* This function will initialize a Select2 instance
|
|
*
|
|
* @type function
|
|
* @date 21/06/2016
|
|
* @since 5.3.8
|
|
*
|
|
* @param $select (jQuery object)
|
|
* @param args (object)
|
|
* @return (mixed)
|
|
*/
|
|
|
|
init: function( $select, args ){
|
|
|
|
// bail early if no version found
|
|
if( !this.version ) return;
|
|
|
|
|
|
// defaults
|
|
args = $.extend({
|
|
allow_null: false,
|
|
placeholder: '',
|
|
multiple: false,
|
|
ajax: false,
|
|
ajax_action: '',
|
|
}, args);
|
|
|
|
|
|
// v3
|
|
if( this.version == 3 ) {
|
|
|
|
return this.init_v3( $select, args );
|
|
|
|
// v4
|
|
} else if( this.version == 4 ) {
|
|
|
|
return this.init_v4( $select, args );
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_data
|
|
*
|
|
* This function will look at a $select element and return an object choices
|
|
*
|
|
* @type function
|
|
* @date 24/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $select (jQuery)
|
|
* @return (array)
|
|
*/
|
|
|
|
get_data: function( $select, data ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// defaults
|
|
data = data || [];
|
|
|
|
|
|
// loop over children
|
|
$select.children().each(function(){
|
|
|
|
// vars
|
|
var $el = $(this);
|
|
|
|
|
|
// optgroup
|
|
if( $el.is('optgroup') ) {
|
|
|
|
data.push({
|
|
'text': $el.attr('label'),
|
|
'children': self.get_data( $el )
|
|
});
|
|
|
|
// option
|
|
} else {
|
|
|
|
data.push({
|
|
'id': $el.attr('value'),
|
|
'text': $el.text()
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* decode_data
|
|
*
|
|
* This function will take an array of choices and decode the text
|
|
* Changes '&' to '&' which fixes a bug (in Select2 v3 )when searching for '&'
|
|
*
|
|
* @type function
|
|
* @date 24/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $select (jQuery)
|
|
* @return (array)
|
|
*/
|
|
|
|
decode_data: function( data ) {
|
|
|
|
// bail ealry if no data
|
|
if( !data ) return [];
|
|
|
|
|
|
//loop
|
|
$.each(data, function(k, v){
|
|
|
|
// text
|
|
data[ k ].text = acf.decode( v.text );
|
|
|
|
|
|
// children
|
|
if( typeof v.children !== 'undefined' ) {
|
|
|
|
data[ k ].children = acf.select2.decode_data(v.children);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* count_data
|
|
*
|
|
* This function will take an array of choices and return the count
|
|
*
|
|
* @type function
|
|
* @date 24/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param data (array)
|
|
* @return (int)
|
|
*/
|
|
|
|
count_data: function( data ) {
|
|
|
|
// vars
|
|
var i = 0;
|
|
|
|
|
|
// bail ealry if no data
|
|
if( !data ) return i;
|
|
|
|
|
|
//loop
|
|
$.each(data, function(k, v){
|
|
|
|
// increase
|
|
i++;
|
|
|
|
|
|
// children
|
|
if( typeof v.children !== 'undefined' ) {
|
|
|
|
i += v.children.length;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return i;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_ajax_data
|
|
*
|
|
* This function will return an array of data to send via AJAX
|
|
*
|
|
* @type function
|
|
* @date 19/07/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
get_ajax_data: function( args, params ){
|
|
|
|
// vars
|
|
var data = acf.prepare_for_ajax({
|
|
action: args.ajax_action,
|
|
field_key: args.key,
|
|
s: params.term,
|
|
paged: params.page
|
|
});
|
|
|
|
|
|
// filter
|
|
data = acf.apply_filters( 'select2_ajax_data', data, args, params );
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_ajax_results
|
|
*
|
|
* This function will return a valid AJAX response
|
|
*
|
|
* @type function
|
|
* @date 19/07/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
get_ajax_results: function( data, params ){
|
|
|
|
// vars
|
|
var valid = {
|
|
results: []
|
|
};
|
|
|
|
|
|
// bail early if no data
|
|
if( !data ) {
|
|
|
|
data = valid;
|
|
|
|
}
|
|
|
|
|
|
// allow for an array of choices
|
|
if( typeof data.results == 'undefined' ) {
|
|
|
|
valid.results = data;
|
|
|
|
data = valid;
|
|
|
|
}
|
|
|
|
|
|
// decode
|
|
data.results = this.decode_data(data.results);
|
|
|
|
|
|
// filter
|
|
data = acf.apply_filters( 'select2_ajax_results', data, params );
|
|
|
|
|
|
// return
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* get_value
|
|
*
|
|
* This function will return the selected options in a Select2 format
|
|
*
|
|
* @type function
|
|
* @date 5/01/2016
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
get_value: function( $select ){
|
|
|
|
// vars
|
|
var val = [],
|
|
$selected = $select.find('option:selected');
|
|
|
|
|
|
// bail early if no selected
|
|
if( !$selected.exists() ) return val;
|
|
|
|
|
|
// sort
|
|
$selected = $selected.sort(function(a, b) {
|
|
|
|
return +a.getAttribute('data-i') - +b.getAttribute('data-i');
|
|
|
|
});
|
|
|
|
|
|
// loop
|
|
$selected.each(function(){
|
|
|
|
// vars
|
|
var $el = $(this);
|
|
|
|
|
|
// append
|
|
val.push({
|
|
'id': $el.attr('value'),
|
|
'text': $el.text()
|
|
});
|
|
|
|
});
|
|
|
|
|
|
// return
|
|
return val;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* init_v3
|
|
*
|
|
* This function will create a new Select2 for v3
|
|
*
|
|
* @type function
|
|
* @date 24/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $select (jQuery)
|
|
* @return args (object)
|
|
*/
|
|
|
|
init_v3: function( $select, args ){
|
|
|
|
// vars
|
|
var $input = $select.siblings('input');
|
|
|
|
|
|
// bail early if no input
|
|
if( !$input.exists() ) return;
|
|
|
|
|
|
// select2 args
|
|
var select2_args = {
|
|
width: '100%',
|
|
containerCssClass: '-acf',
|
|
allowClear: args.allow_null,
|
|
placeholder: args.placeholder,
|
|
multiple: args.multiple,
|
|
separator: '||',
|
|
data: [],
|
|
escapeMarkup: function( m ){ return m; },
|
|
formatResult: function( result, container, query, escapeMarkup ){
|
|
|
|
// run default formatResult
|
|
var text = $.fn.select2.defaults.formatResult( result, container, query, escapeMarkup );
|
|
|
|
|
|
// append description
|
|
if( result.description ) {
|
|
|
|
text += ' <span class="select2-result-description">' + result.description + '</span>';
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return text;
|
|
|
|
}
|
|
};
|
|
|
|
|
|
// value
|
|
var value = this.get_value( $select );
|
|
|
|
|
|
// multiple
|
|
if( args.multiple ) {
|
|
|
|
// vars
|
|
var name = $select.attr('name');
|
|
|
|
|
|
// add hidden input to each multiple selection
|
|
select2_args.formatSelection = function( object, $div ){
|
|
|
|
// vars
|
|
var html = '<input type="hidden" class="select2-search-choice-hidden" name="' + name + '" value="' + object.id + '"' + ($input.prop('disabled') ? 'disabled="disabled"' : '') + ' />';
|
|
|
|
|
|
// append input
|
|
$div.parent().append(html);
|
|
|
|
|
|
// return
|
|
return object.text;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// change array to single object
|
|
value = acf.maybe_get(value, 0, '');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove the blank option as we have a clear all button!
|
|
if( args.allow_null ) {
|
|
|
|
$select.find('option[value=""]').remove();
|
|
|
|
}
|
|
|
|
|
|
// get data
|
|
select2_args.data = this.get_data( $select );
|
|
|
|
|
|
// initial selection
|
|
select2_args.initSelection = function( element, callback ) {
|
|
|
|
callback( value );
|
|
|
|
};
|
|
|
|
|
|
// ajax
|
|
if( args.ajax ) {
|
|
|
|
select2_args.ajax = {
|
|
url: acf.get('ajaxurl'),
|
|
dataType: 'json',
|
|
type: 'post',
|
|
cache: false,
|
|
quietMillis: 250,
|
|
data: function( term, page ) {
|
|
|
|
// vars
|
|
var params = { 'term': term, 'page': page };
|
|
|
|
|
|
// return
|
|
return acf.select2.get_ajax_data(args, params);
|
|
|
|
},
|
|
results: function( data, page ){
|
|
|
|
// vars
|
|
var params = { 'page': page };
|
|
|
|
|
|
// merge together groups
|
|
setTimeout(function(){
|
|
|
|
acf.select2.merge_results_v3();
|
|
|
|
}, 1);
|
|
|
|
|
|
// return
|
|
return acf.select2.get_ajax_results(data, params);
|
|
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
|
|
// attachment z-index fix
|
|
select2_args.dropdownCss = {
|
|
'z-index' : '999999999'
|
|
};
|
|
|
|
|
|
// append args
|
|
select2_args.acf = args;
|
|
|
|
|
|
// filter for 3rd party customization
|
|
select2_args = acf.apply_filters( 'select2_args', select2_args, $select, args );
|
|
|
|
|
|
// add select2
|
|
$input.select2( select2_args );
|
|
|
|
|
|
// vars
|
|
var $container = $input.select2('container');
|
|
|
|
|
|
// reorder DOM
|
|
// - this order is very important so don't change it
|
|
// - $select goes first so the input can override it. Fixes issue where conditional logic will enable the select
|
|
// - $input goes second to reset the input data
|
|
// - container goes last to allow multiple hidden inputs to override $input
|
|
$container.before( $select );
|
|
$container.before( $input );
|
|
|
|
|
|
// multiple
|
|
if( args.multiple ) {
|
|
|
|
// sortable
|
|
$container.find('ul.select2-choices').sortable({
|
|
start: function() {
|
|
$input.select2("onSortStart");
|
|
},
|
|
stop: function() {
|
|
$input.select2("onSortEnd");
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
|
|
// disbale select
|
|
$select.prop('disabled', true).addClass('acf-disabled acf-hidden');
|
|
|
|
|
|
// update select value
|
|
// this fixes a bug where select2 appears blank after duplicating a post_object field (field settings).
|
|
// the $select is disabled, so setting the value won't cause any issues (this is what select2 v4 does anyway).
|
|
$input.on('change', function(e) {
|
|
|
|
// add new data
|
|
if( e.added ) {
|
|
|
|
$select.append('<option value="' + e.added.id + '">' + e.added.text + '</option>');
|
|
|
|
}
|
|
|
|
|
|
// update val
|
|
$select.val( e.val );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* merge_results_v3
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 20/07/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
merge_results_v3: function(){
|
|
|
|
// vars
|
|
var label = '',
|
|
$list = null;
|
|
|
|
|
|
// loop
|
|
$('#select2-drop .select2-result-with-children').each(function(){
|
|
|
|
// vars
|
|
var $label = $(this).children('.select2-result-label'),
|
|
$ul = $(this).children('.select2-result-sub');
|
|
|
|
|
|
// append group to previous
|
|
if( $label.text() == label ) {
|
|
|
|
$list.append( $ul.children() );
|
|
|
|
$(this).remove();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// update vars
|
|
label = $label.text();
|
|
$list = $ul;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
init_v4: function( $select, args ){
|
|
|
|
// vars
|
|
var $input = $select.siblings('input');
|
|
|
|
|
|
// bail early if no input
|
|
if( !$input.exists() ) return;
|
|
|
|
|
|
// select2 args
|
|
var select2_args = {
|
|
width: '100%',
|
|
allowClear: args.allow_null,
|
|
placeholder: args.placeholder,
|
|
multiple: args.multiple,
|
|
separator: '||',
|
|
data: [],
|
|
escapeMarkup: function( m ){ return m; },
|
|
/*
|
|
sorter: function (data) { console.log('sorter %o', data);
|
|
return data;
|
|
},
|
|
*/
|
|
};
|
|
|
|
|
|
// value
|
|
var value = this.get_value( $select );
|
|
|
|
|
|
// multiple
|
|
if( args.multiple ) {
|
|
|
|
/*
|
|
// vars
|
|
var name = $select.attr('name');
|
|
|
|
|
|
// add hidden input to each multiple selection
|
|
select2_args.templateSelection = function( selection ){
|
|
|
|
return selection.text + '<input type="hidden" class="select2-search-choice-hidden" name="' + name + '" value="' + selection.id + '" />';
|
|
|
|
}
|
|
*/
|
|
|
|
} else {
|
|
|
|
// change array to single object
|
|
value = acf.maybe_get(value, 0, '');
|
|
|
|
}
|
|
|
|
|
|
// remove the blank option as we have a clear all button!
|
|
if( args.allow_null ) {
|
|
|
|
$select.find('option[value=""]').remove();
|
|
|
|
}
|
|
|
|
|
|
// get data
|
|
select2_args.data = this.get_data( $select );
|
|
|
|
|
|
// initial selection
|
|
/*
|
|
select2_args.initSelection = function( element, callback ) {
|
|
|
|
callback( value );
|
|
|
|
};
|
|
*/
|
|
|
|
|
|
// remove conflicting atts
|
|
if( !args.ajax ) {
|
|
|
|
$select.removeData('ajax');
|
|
$select.removeAttr('data-ajax');
|
|
|
|
} else {
|
|
|
|
select2_args.ajax = {
|
|
url: acf.get('ajaxurl'),
|
|
delay: 250,
|
|
dataType: 'json',
|
|
type: 'post',
|
|
cache: false,
|
|
data: function( params ) {
|
|
|
|
// return
|
|
return acf.select2.get_ajax_data(args, params);
|
|
|
|
},
|
|
processResults: function( data, params ){
|
|
|
|
// vars
|
|
var results = acf.select2.get_ajax_results(data, params);
|
|
|
|
|
|
// change to more
|
|
if( results.more ) {
|
|
|
|
results.pagination = { more: true };
|
|
|
|
}
|
|
|
|
|
|
// merge together groups
|
|
setTimeout(function(){
|
|
|
|
acf.select2.merge_results_v4();
|
|
|
|
}, 1);
|
|
|
|
|
|
// return
|
|
return results
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// multiple
|
|
/*
|
|
if( args.multiple ) {
|
|
|
|
|
|
|
|
$select.on('select2:select', function( e ){
|
|
|
|
console.log( 'select2:select %o &o', $(this), e );
|
|
|
|
// vars
|
|
var $option = $(e.params.data.element);
|
|
|
|
|
|
// move option to begining of select
|
|
//$(this).prepend( $option );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// reorder DOM
|
|
// - no need to reorder, the select field is needed to $_POST values
|
|
|
|
|
|
// filter for 3rd party customization
|
|
select2_args = acf.apply_filters( 'select2_args', select2_args, $select, args );
|
|
|
|
|
|
// add select2
|
|
//console.log( '%o %o ', $select, select2_args );
|
|
var $container = $select.select2( select2_args );
|
|
|
|
|
|
// add class
|
|
$container.addClass('-acf');
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* merge_results_v4
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 20/07/2016
|
|
* @since 5.4.0
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
merge_results_v4: function(){
|
|
|
|
// vars
|
|
var $prev_options = null,
|
|
$prev_group = null;
|
|
|
|
|
|
// loop
|
|
$('.select2-results__option[role="group"]').each(function(){
|
|
|
|
// vars
|
|
var $options = $(this).children('ul'),
|
|
$group = $(this).children('strong');
|
|
|
|
|
|
// compare to previous
|
|
if( $prev_group !== null && $group.text() == $prev_group.text() ) {
|
|
|
|
$prev_options.append( $options.children() );
|
|
|
|
$(this).remove();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// update vars
|
|
$prev_options = $options;
|
|
$prev_group = $group;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* destroy
|
|
*
|
|
* This function will destroy a Select2
|
|
*
|
|
* @type function
|
|
* @date 24/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
destroy: function( $select ){
|
|
|
|
// remove select2 container
|
|
$select.siblings('.select2-container').remove();
|
|
|
|
|
|
// show input so that select2 can correctly render visible select2 container
|
|
$select.siblings('input').show();
|
|
|
|
|
|
// enable select
|
|
$select.prop('disabled', false).removeClass('acf-disabled acf-hidden');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* depreciated
|
|
*
|
|
* These functions have moved since v5.3.3
|
|
*
|
|
* @type function
|
|
* @date 11/12/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
acf.add_select2 = function( $select, args ) {
|
|
|
|
acf.select2.init( $select, args );
|
|
|
|
}
|
|
|
|
acf.remove_select2 = function( $select ) {
|
|
|
|
acf.select2.destroy( $select );
|
|
|
|
}
|
|
|
|
|
|
// select
|
|
acf.fields.select = acf.field.extend({
|
|
|
|
type: 'select',
|
|
|
|
$select: null,
|
|
|
|
actions: {
|
|
'ready': 'render',
|
|
'append': 'render',
|
|
'remove': 'remove'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// focus on $select
|
|
this.$select = this.$field.find('select');
|
|
|
|
|
|
// bail early if no select field
|
|
if( !this.$select.exists() ) return;
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$select );
|
|
|
|
|
|
// customize o
|
|
this.o = acf.parse_args(this.o, {
|
|
'ajax_action': 'acf/fields/'+this.type+'/query',
|
|
'key': this.$field.data('key')
|
|
});
|
|
|
|
},
|
|
|
|
render: function(){
|
|
|
|
// validate ui
|
|
if( !this.$select.exists() || !this.o.ui ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
acf.select2.init( this.$select, this.o );
|
|
|
|
},
|
|
|
|
remove: function(){
|
|
|
|
// validate ui
|
|
if( !this.$select.exists() || !this.o.ui ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// remove select2
|
|
acf.select2.destroy( this.$select );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// user
|
|
acf.fields.user = acf.fields.select.extend({
|
|
|
|
type: 'user',
|
|
|
|
});
|
|
|
|
|
|
// post_object
|
|
acf.fields.post_object = acf.fields.select.extend({
|
|
|
|
type: 'post_object',
|
|
|
|
});
|
|
|
|
|
|
// page_link
|
|
acf.fields.page_link = acf.fields.select.extend({
|
|
|
|
type: 'page_link',
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.tab = acf.field.extend({
|
|
|
|
type: 'tab',
|
|
$el: null,
|
|
$wrap: null,
|
|
|
|
actions: {
|
|
'prepare': 'initialize',
|
|
'append': 'initialize',
|
|
'hide': 'hide',
|
|
'show': 'show'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-tab');
|
|
|
|
|
|
// get options
|
|
this.o = this.$el.data();
|
|
this.o.key = this.$field.data('key');
|
|
this.o.text = this.$el.text();
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// bail early if is td
|
|
if( this.$field.is('td') ) return;
|
|
|
|
|
|
// add tab
|
|
tab_manager.add_tab( this.$field, this.o );
|
|
|
|
},
|
|
|
|
hide: function( $field, context ){
|
|
|
|
// bail early if not conditional logic
|
|
if( context != 'conditional_logic' ) return;
|
|
|
|
|
|
// vars
|
|
var key = $field.data('key'),
|
|
$group = $field.prevAll('.acf-tab-wrap'),
|
|
$a = $group.find('a[data-key="' + key + '"]'),
|
|
$li = $a.parent();
|
|
|
|
|
|
// bail early if $group does not exist (clone field)
|
|
if( !$group.exists() ) return;
|
|
|
|
|
|
// hide li
|
|
$li.addClass('hidden-by-conditional-logic');
|
|
|
|
|
|
// set timout to allow proceeding fields to hide first
|
|
// without this, the tab field will hide all fields, regarless of if that field has it's own conditional logic rules
|
|
setTimeout(function(){
|
|
|
|
// if this tab field was hidden by conditional_logic, disable it's children to prevent validation
|
|
$field.nextUntil('.acf-field-tab', '.acf-field').each(function(){
|
|
|
|
// bail ealry if already hidden
|
|
if( $(this).hasClass('hidden-by-conditional-logic') ) return;
|
|
|
|
|
|
// hide field
|
|
acf.conditional_logic.hide_field( $(this) );
|
|
|
|
|
|
// add parent reference
|
|
$(this).addClass('-hbcl-' + key);
|
|
|
|
});
|
|
|
|
|
|
// select other tab if active
|
|
if( $li.hasClass('active') ) {
|
|
|
|
$group.find('li:not(.hidden-by-conditional-logic):first a').trigger('click');
|
|
|
|
}
|
|
|
|
}, 0);
|
|
|
|
},
|
|
|
|
show: function( $field, context ){
|
|
|
|
// bail early if not conditional logic
|
|
if( context != 'conditional_logic' ) return;
|
|
|
|
// vars
|
|
var key = $field.data('key'),
|
|
$group = $field.prevAll('.acf-tab-wrap'),
|
|
$a = $group.find('a[data-key="' + key + '"]'),
|
|
$li = $a.parent();
|
|
|
|
|
|
// bail early if $group does not exist (clone field)
|
|
if( !$group.exists() ) return;
|
|
|
|
|
|
// show li
|
|
$li.removeClass('hidden-by-conditional-logic');
|
|
|
|
|
|
// set timout to allow proceeding fields to hide first
|
|
// without this, the tab field will hide all fields, regarless of if that field has it's own conditional logic rules
|
|
setTimeout(function(){
|
|
|
|
// if this tab field was shown by conditional_logic, enable it's children to allow validation
|
|
$field.siblings('.acf-field.-hbcl-' + key).each(function(){
|
|
|
|
// show field
|
|
acf.conditional_logic.show_field( $(this) );
|
|
|
|
|
|
// remove parent reference
|
|
$(this).removeClass('-hbcl-' + key);
|
|
|
|
});
|
|
|
|
|
|
// select tab if no other active
|
|
var $active = $li.siblings('.active');
|
|
if( !$active.exists() || $active.hasClass('hidden-by-conditional-logic') ) {
|
|
|
|
$a.trigger('click');
|
|
|
|
}
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* tab_manager
|
|
*
|
|
* This model will handle adding tabs and groups
|
|
*
|
|
* @type function
|
|
* @date 25/11/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
var tab_manager = acf.model.extend({
|
|
|
|
actions: {
|
|
'prepare 15': 'render',
|
|
'append 15': 'render',
|
|
'refresh 15': 'render'
|
|
},
|
|
|
|
events: {
|
|
'click .acf-tab-button': '_click'
|
|
},
|
|
|
|
|
|
render: function( $el ){
|
|
|
|
// find visible tab wraps
|
|
$('.acf-tab-wrap', $el).each(function(){
|
|
|
|
// vars
|
|
var $group = $(this),
|
|
$wrap = $group.parent();
|
|
|
|
|
|
// trigger click
|
|
if( !$group.find('li.active').exists() ) {
|
|
|
|
$group.find('li:not(.hidden-by-conditional-logic):first a').trigger('click');
|
|
|
|
}
|
|
|
|
|
|
if( $wrap.hasClass('-sidebar') ) {
|
|
|
|
// vars
|
|
var attribute = $wrap.is('td') ? 'height' : 'min-height';
|
|
|
|
|
|
// find height (minus 1 for border-bottom)
|
|
var height = $group.position().top + $group.children('ul').outerHeight(true) - 1;
|
|
|
|
|
|
// add css
|
|
$wrap.css(attribute, height);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
add_group: function( $field, settings ){
|
|
|
|
// vars
|
|
var $wrap = $field.parent(),
|
|
html = '';
|
|
|
|
|
|
// add sidebar to wrap
|
|
if( $wrap.hasClass('acf-fields') && settings.placement == 'left' ) {
|
|
|
|
$wrap.addClass('-sidebar');
|
|
|
|
// can't have side tab without sidebar
|
|
} else {
|
|
|
|
settings.placement = 'top';
|
|
|
|
}
|
|
|
|
|
|
// generate html
|
|
if( $wrap.is('tbody') ) {
|
|
|
|
html = '<tr class="acf-tab-wrap"><td colspan="2"><ul class="acf-hl acf-tab-group"></ul></td></tr>';
|
|
|
|
} else {
|
|
|
|
html = '<div class="acf-tab-wrap -' + settings.placement + '"><ul class="acf-hl acf-tab-group"></ul></div>';
|
|
|
|
}
|
|
|
|
|
|
// save
|
|
$group = $(html);
|
|
|
|
|
|
// append
|
|
$field.before( $group );
|
|
|
|
|
|
// return
|
|
return $group;
|
|
},
|
|
|
|
add_tab: function( $field, settings ){ //console.log('add_tab(%o, %o)', $field, settings);
|
|
|
|
// vars
|
|
var $group = $field.siblings('.acf-tab-wrap').last();
|
|
|
|
|
|
// add tab group if no group exists
|
|
if( !$group.exists() ) {
|
|
|
|
$group = this.add_group( $field, settings );
|
|
|
|
// add tab group if is endpoint
|
|
} else if( settings.endpoint ) {
|
|
|
|
$group = this.add_group( $field, settings );
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var $li = $('<li><a class="acf-tab-button" href="#" data-key="' + settings.key + '">' + settings.text + '</a></li>');
|
|
|
|
|
|
// hide li
|
|
if( settings.text === '' ) $li.hide();
|
|
|
|
|
|
// add tab
|
|
$group.find('ul').append( $li );
|
|
|
|
|
|
// conditional logic
|
|
if( $field.hasClass('hidden-by-conditional-logic') ) {
|
|
|
|
$li.addClass('hidden-by-conditional-logic');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_click: function( e ){
|
|
|
|
// prevent default
|
|
e.preventDefault();
|
|
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var $a = e.$el,
|
|
$group = $a.closest('.acf-tab-wrap'),
|
|
show = $a.data('key'),
|
|
current = '';
|
|
|
|
|
|
// add and remove classes
|
|
$a.parent().addClass('active').siblings().removeClass('active');
|
|
|
|
|
|
// loop over all fields until you hit another group
|
|
$group.nextUntil('.acf-tab-wrap', '.acf-field').each(function(){
|
|
|
|
// vars
|
|
var $field = $(this);
|
|
|
|
|
|
// set current
|
|
if( $field.data('type') == 'tab' ) {
|
|
|
|
current = $field.data('key');
|
|
|
|
// bail early if endpoint is found
|
|
if( $field.hasClass('endpoint') ) {
|
|
|
|
// stop loop - current tab group is complete
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// show
|
|
if( current === show ) {
|
|
|
|
// only show if hidden
|
|
if( $field.hasClass('hidden-by-tab') ) {
|
|
|
|
$field.removeClass('hidden-by-tab');
|
|
|
|
acf.do_action('show_field', $(this), 'tab');
|
|
|
|
}
|
|
|
|
// hide
|
|
} else {
|
|
|
|
// only hide if not hidden
|
|
if( !$field.hasClass('hidden-by-tab') ) {
|
|
|
|
$field.addClass('hidden-by-tab');
|
|
|
|
acf.do_action('hide_field', $(this), 'tab');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('refresh', $group.parent() );
|
|
|
|
|
|
// blur
|
|
$a.trigger('blur');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* tab_validation
|
|
*
|
|
* This model will handle validation of fields within a tab group
|
|
*
|
|
* @type function
|
|
* @date 25/11/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
var tab_validation = acf.model.extend({
|
|
|
|
active: 1,
|
|
|
|
actions: {
|
|
'add_field_error': 'add_field_error'
|
|
},
|
|
|
|
add_field_error: function( $field ){
|
|
|
|
// bail early if already focused
|
|
if( !this.active ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// bail early if not hidden by tab
|
|
if( !$field.hasClass('hidden-by-tab') ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var $tab = $field.prevAll('.acf-field-tab:first'),
|
|
$group = $field.prevAll('.acf-tab-wrap:first');
|
|
|
|
|
|
// focus
|
|
$group.find('a[data-key="' + $tab.data('key') + '"]').trigger('click');
|
|
|
|
|
|
// disable functionality for 1sec (allow next validation to work)
|
|
this.active = 0;
|
|
|
|
setTimeout(function(){
|
|
|
|
self.active = 1;
|
|
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.time_picker = acf.field.extend({
|
|
|
|
type: 'time_picker',
|
|
$el: null,
|
|
$input: null,
|
|
$hidden: null,
|
|
|
|
o: {},
|
|
|
|
actions: {
|
|
'ready': 'initialize',
|
|
'append': 'initialize'
|
|
},
|
|
|
|
events: {
|
|
'blur input[type="text"]': 'blur',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.acf-time-picker');
|
|
this.$input = this.$el.find('input[type="text"]');
|
|
this.$hidden = this.$el.find('input[type="hidden"]');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// create options
|
|
var args = {
|
|
timeFormat: this.o.time_format,
|
|
altField: this.$hidden,
|
|
altFieldTimeOnly: false,
|
|
altTimeFormat: 'HH:mm:ss',
|
|
showButtonPanel: true,
|
|
controlType: 'select',
|
|
oneLine: true,
|
|
closeText: acf._e('date_time_picker', 'selectText')
|
|
};
|
|
|
|
|
|
// add custom 'Close = Select' functionality
|
|
args.onClose = function( value, instance ){
|
|
|
|
// vars
|
|
var $div = instance.dpDiv,
|
|
$close = $div.find('.ui-datepicker-close');
|
|
|
|
|
|
// if clicking close button
|
|
if( !value && $close.is(':hover') ) {
|
|
|
|
// attempt to find new value
|
|
value = acf.maybe_get(instance, 'settings.timepicker.formattedTime');
|
|
|
|
|
|
// bail early if no value
|
|
if( !value ) return;
|
|
|
|
|
|
// update value
|
|
$.datepicker._setTime(instance);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
// filter for 3rd party customization
|
|
args = acf.apply_filters('time_picker_args', args, this.$field);
|
|
|
|
|
|
// add date picker
|
|
this.$input.addClass('active').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" />');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
blur: function(){
|
|
|
|
if( !this.$input.val() ) {
|
|
|
|
this.$hidden.val('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
// taxonomy
|
|
acf.fields.taxonomy = acf.field.extend({
|
|
|
|
type: 'taxonomy',
|
|
$el: null,
|
|
|
|
actions: {
|
|
'ready': 'render',
|
|
'append': 'render',
|
|
'remove': 'remove'
|
|
},
|
|
events: {
|
|
'click a[data-name="add"]': 'add_term',
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// $el
|
|
this.$el = this.$field.find('.acf-taxonomy-field');
|
|
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
|
|
|
|
// extra
|
|
this.o.key = this.$field.data('key');
|
|
|
|
},
|
|
|
|
render: function(){
|
|
|
|
// attempt select2
|
|
var $select = this.$field.find('select');
|
|
|
|
|
|
// bail early if no select field
|
|
if( !$select.exists() ) return;
|
|
|
|
|
|
// select2 options
|
|
var args = acf.get_data( $select );
|
|
|
|
|
|
// customize args
|
|
args = acf.parse_args(args, {
|
|
'pagination': true,
|
|
'ajax_action': 'acf/fields/taxonomy/query',
|
|
'key': this.o.key
|
|
});
|
|
|
|
|
|
// add select2
|
|
acf.select2.init( $select, args );
|
|
|
|
},
|
|
|
|
remove: function(){
|
|
|
|
// attempt select2
|
|
var $select = this.$field.find('select');
|
|
|
|
|
|
// validate ui
|
|
if( !$select.exists() ) return false;
|
|
|
|
|
|
// remove select2
|
|
acf.select2.destroy( $select );
|
|
|
|
},
|
|
|
|
add_term: function( e ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// open popup
|
|
acf.open_popup({
|
|
title: e.$el.attr('title') || e.$el.data('title'),
|
|
loading: true,
|
|
height: 220
|
|
});
|
|
|
|
|
|
|
|
// AJAX data
|
|
var ajax_data = acf.prepare_for_ajax({
|
|
action: 'acf/fields/taxonomy/add_term',
|
|
field_key: this.o.key
|
|
});
|
|
|
|
|
|
|
|
// get HTML
|
|
$.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
data: ajax_data,
|
|
type: 'post',
|
|
dataType: 'html',
|
|
success: function(html){
|
|
|
|
self.add_term_confirm( html );
|
|
|
|
}
|
|
});
|
|
|
|
|
|
},
|
|
|
|
add_term_confirm: function( html ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// update popup
|
|
acf.update_popup({
|
|
content : html
|
|
});
|
|
|
|
|
|
// focus
|
|
$('#acf-popup input[name="term_name"]').focus();
|
|
|
|
|
|
// events
|
|
$('#acf-popup form').on('submit', function( e ){
|
|
|
|
// prevent default
|
|
e.preventDefault();
|
|
|
|
|
|
// submit
|
|
self.add_term_submit( $(this ));
|
|
|
|
});
|
|
|
|
},
|
|
|
|
add_term_submit: function( $form ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// vars
|
|
var $submit = $form.find('.acf-submit'),
|
|
$name = $form.find('input[name="term_name"]'),
|
|
$parent = $form.find('select[name="term_parent"]');
|
|
|
|
|
|
// basic validation
|
|
if( $name.val() === '' ) {
|
|
|
|
$name.focus();
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// show loading
|
|
$submit.find('button').attr('disabled', 'disabled');
|
|
$submit.find('.acf-spinner').addClass('is-active');
|
|
|
|
|
|
// vars
|
|
var ajax_data = acf.prepare_for_ajax({
|
|
action: 'acf/fields/taxonomy/add_term',
|
|
field_key: this.o.key,
|
|
term_name: $name.val(),
|
|
term_parent: $parent.exists() ? $parent.val() : 0
|
|
});
|
|
|
|
|
|
// save term
|
|
$.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
data: ajax_data,
|
|
type: 'post',
|
|
dataType: 'json',
|
|
success: function( json ){
|
|
|
|
// vars
|
|
var message = acf.get_ajax_message(json);
|
|
|
|
|
|
// success
|
|
if( acf.is_ajax_success(json) ) {
|
|
|
|
// clear name
|
|
$name.val('');
|
|
|
|
|
|
// update term lists
|
|
self.append_new_term( json.data );
|
|
|
|
}
|
|
|
|
|
|
// message
|
|
if( message.text ) {
|
|
|
|
$submit.find('span').html( message.text );
|
|
|
|
}
|
|
|
|
},
|
|
complete: function(){
|
|
|
|
// reset button
|
|
$submit.find('button').removeAttr('disabled');
|
|
|
|
|
|
// hide loading
|
|
$submit.find('.acf-spinner').removeClass('is-active');
|
|
|
|
|
|
// remove message
|
|
$submit.find('span').delay(1500).fadeOut(250, function(){
|
|
|
|
$(this).html('');
|
|
$(this).show();
|
|
|
|
});
|
|
|
|
|
|
// focus
|
|
$name.focus();
|
|
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
append_new_term: function( term ){
|
|
|
|
// vars
|
|
var item = {
|
|
id: term.term_id,
|
|
text: term.term_label
|
|
};
|
|
|
|
|
|
// append to all taxonomy lists
|
|
$('.acf-taxonomy-field[data-taxonomy="' + this.o.taxonomy + '"]').each(function(){
|
|
|
|
// vars
|
|
var type = $(this).data('type');
|
|
|
|
|
|
// bail early if not checkbox/radio
|
|
if( type == 'radio' || type == 'checkbox' ) {
|
|
|
|
// allow
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var $hidden = $(this).children('input[type="hidden"]'),
|
|
$ul = $(this).find('ul:first'),
|
|
name = $hidden.attr('name');
|
|
|
|
|
|
// allow multiple selection
|
|
if( type == 'checkbox' ) {
|
|
|
|
name += '[]';
|
|
|
|
}
|
|
|
|
|
|
// create new li
|
|
var $li = $([
|
|
'<li data-id="' + term.term_id + '">',
|
|
'<label>',
|
|
'<input type="' + type + '" value="' + term.term_id + '" name="' + name + '" /> ',
|
|
'<span>' + term.term_label + '</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 );
|
|
|
|
});
|
|
|
|
|
|
// append to select
|
|
$('#acf-popup #term_parent').each(function(){
|
|
|
|
// vars
|
|
var $option = $('<option value="' + term.term_id + '">' + term.term_label + '</option>');
|
|
|
|
if( term.term_parent ) {
|
|
|
|
$(this).children('option[value="' + term.term_parent + '"]').after( $option );
|
|
|
|
} else {
|
|
|
|
$(this).append( $option );
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
// set value
|
|
switch( this.o.type ) {
|
|
|
|
// select
|
|
case 'select':
|
|
|
|
this.$el.children('input').select2('data', item);
|
|
break;
|
|
|
|
case 'multi_select':
|
|
|
|
// vars
|
|
var $input = this.$el.children('input'),
|
|
value = $input.select2('data') || [];
|
|
|
|
|
|
// append
|
|
value.push( item );
|
|
|
|
|
|
// update
|
|
$input.select2('data', value);
|
|
break;
|
|
|
|
case 'checkbox':
|
|
case 'radio':
|
|
|
|
// scroll to view
|
|
var $holder = this.$el.find('.categorychecklist-holder'),
|
|
$li = $holder.find('li[data-id="' + term.term_id + '"]'),
|
|
offet = $holder.get(0).scrollTop + ( $li.offset().top - $holder.offset().top );
|
|
|
|
|
|
// check input
|
|
$li.find('input').prop('checked', true);
|
|
|
|
|
|
// scroll to bottom
|
|
$holder.animate({scrollTop: offet}, '250');
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.url = acf.field.extend({
|
|
|
|
type: 'url',
|
|
$input: null,
|
|
|
|
actions: {
|
|
'ready': 'render',
|
|
'append': 'render'
|
|
|
|
},
|
|
|
|
events: {
|
|
'keyup input[type="url"]': 'render'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
this.$input = this.$field.find('input[type="url"]');
|
|
|
|
},
|
|
|
|
is_valid: function(){
|
|
|
|
// vars
|
|
var val = this.$input.val();
|
|
|
|
|
|
if( val.indexOf('://') !== -1 ) {
|
|
|
|
// url
|
|
|
|
} else if( val.indexOf('//') === 0 ) {
|
|
|
|
// protocol relative url
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return true;
|
|
|
|
},
|
|
|
|
render: function(){
|
|
|
|
// add class
|
|
if( this.is_valid() ) {
|
|
|
|
this.$input.parent().addClass('valid');
|
|
|
|
// remove class
|
|
} else {
|
|
|
|
this.$input.parent().removeClass('valid');
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.validation = acf.model.extend({
|
|
|
|
actions: {
|
|
'ready': 'ready',
|
|
'append': 'ready'
|
|
},
|
|
|
|
filters: {
|
|
'validation_complete': 'validation_complete'
|
|
},
|
|
|
|
events: {
|
|
'click #save-post': 'click_ignore',
|
|
'click [type="submit"]': 'click_publish',
|
|
'submit form': 'submit_form',
|
|
'click .acf-error-message a': 'click_message'
|
|
},
|
|
|
|
|
|
// vars
|
|
active: 1,
|
|
ignore: 0,
|
|
busy: 0,
|
|
valid: true,
|
|
errors: [],
|
|
|
|
|
|
// classes
|
|
error_class: 'acf-error',
|
|
message_class: 'acf-error-message',
|
|
|
|
|
|
// el
|
|
$trigger: null,
|
|
|
|
|
|
/*
|
|
* ready
|
|
*
|
|
* This function will add 'non bubbling' events
|
|
*
|
|
* @type function
|
|
* @date 26/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
ready: function( $el ){
|
|
|
|
// reference
|
|
$el.find('.acf-field input').filter('[type="number"], [type="email"], [type="url"]').on('invalid', function( e ){
|
|
|
|
// prvent defual
|
|
// fixes chrome bug where 'hidden-by-tab' field throws focus error
|
|
e.preventDefault();
|
|
|
|
|
|
// append to errors
|
|
acf.validation.errors.push({
|
|
input: $(this).attr('name'),
|
|
message: e.target.validationMessage
|
|
});
|
|
|
|
|
|
// run validation
|
|
acf.validation.fetch( $(this).closest('form') );
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* validation_complete
|
|
*
|
|
* This function will modify the JSON response and add local 'invalid' errors
|
|
*
|
|
* @type function
|
|
* @date 26/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
validation_complete: function( json, $form ) {
|
|
|
|
// bail early if no local errors
|
|
if( !this.errors.length ) return json;
|
|
|
|
|
|
// set valid
|
|
json.valid = 0;
|
|
|
|
|
|
// require array
|
|
json.errors = json.errors || [];
|
|
|
|
|
|
// vars
|
|
var inputs = [];
|
|
|
|
|
|
// populate inputs
|
|
if( json.errors.length ) {
|
|
|
|
for( i in json.errors ) {
|
|
|
|
inputs.push( json.errors[ i ].input );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// append
|
|
if( this.errors.length ) {
|
|
|
|
for( i in this.errors ) {
|
|
|
|
// vars
|
|
var error = this.errors[ i ];
|
|
|
|
|
|
// bail ealry if alreay exists
|
|
if( $.inArray(error.input, inputs) !== -1 ) continue;
|
|
|
|
|
|
// append
|
|
json.errors.push( error );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// reset
|
|
this.errors = [];
|
|
|
|
|
|
// return
|
|
return json;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* click_message
|
|
*
|
|
* This function will dismiss the validation message
|
|
*
|
|
* @type function
|
|
* @date 26/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
click_message: function( e ) {
|
|
|
|
e.preventDefault();
|
|
|
|
acf.remove_el( e.$el.parent() );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* click_ignore
|
|
*
|
|
* This event is trigered via submit butons which ignore validation
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
click_ignore: function( e ) {
|
|
|
|
this.ignore = 1;
|
|
this.$trigger = e.$el;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* click_publish
|
|
*
|
|
* This event is trigered via submit butons which trigger validation
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
click_publish: function( e ) {
|
|
|
|
this.$trigger = e.$el;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* submit_form
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
submit_form: function( e ){
|
|
|
|
// bail early if not active
|
|
if( !this.active ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
// ignore validation (only ignore once)
|
|
if( this.ignore ) {
|
|
|
|
this.ignore = 0;
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
// bail early if this form does not contain ACF data
|
|
if( !e.$el.find('#acf-form-data').exists() ) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
// bail early if is preview
|
|
var $preview = e.$el.find('#wp-preview');
|
|
if( $preview.exists() && $preview.val() ) {
|
|
|
|
// WP will lock form, unlock it
|
|
this.toggle( e.$el, 'unlock' );
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
// prevent default
|
|
e.preventDefault();
|
|
|
|
|
|
// run validation
|
|
this.fetch( e.$el );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* lock
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 7/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
toggle: function( $form, state ){
|
|
|
|
// defaults
|
|
state = state || 'unlock';
|
|
|
|
|
|
// debug
|
|
//console.log('toggle %o, %o %o', this.$trigger, $form, state);
|
|
|
|
// vars
|
|
var $submit = null,
|
|
$spinner = null,
|
|
$parent = $('#submitdiv');
|
|
|
|
|
|
// 3rd party publish box
|
|
if( !$parent.exists() ) {
|
|
|
|
$parent = $('#submitpost');
|
|
|
|
}
|
|
|
|
|
|
// term, user
|
|
if( !$parent.exists() ) {
|
|
|
|
$parent = $form.find('p.submit').last();
|
|
|
|
}
|
|
|
|
|
|
// front end form
|
|
if( !$parent.exists() ) {
|
|
|
|
$parent = $form.find('.acf-form-submit');
|
|
|
|
}
|
|
|
|
|
|
// default
|
|
if( !$parent.exists() ) {
|
|
|
|
$parent = $form;
|
|
|
|
}
|
|
|
|
|
|
// find elements
|
|
// note: media edit page does not use .button, this is why we need to look for generic input[type="submit"]
|
|
$submit = $parent.find('input[type="submit"], .button');
|
|
$spinner = $parent.find('.spinner, .acf-spinner');
|
|
|
|
|
|
// hide all spinners (hides the preview spinner)
|
|
this.hide_spinner( $spinner );
|
|
|
|
|
|
// unlock
|
|
if( state == 'unlock' ) {
|
|
|
|
this.enable_submit( $submit );
|
|
|
|
// lock
|
|
} else if( state == 'lock' ) {
|
|
|
|
// show only last spinner (allow all spinners to be hidden - preview spinner + submit spinner)
|
|
this.disable_submit( $submit );
|
|
this.show_spinner( $spinner.last() );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* fetch
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
fetch: function( $form ){
|
|
|
|
// bail aelry if already busy
|
|
if( this.busy ) return false;
|
|
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// action for 3rd party
|
|
acf.do_action('validation_begin');
|
|
|
|
|
|
// vars
|
|
var data = acf.serialize_form($form);
|
|
|
|
|
|
// append AJAX action
|
|
data.action = 'acf/validate_save_post';
|
|
|
|
|
|
// prepare
|
|
data = acf.prepare_for_ajax(data);
|
|
|
|
|
|
// set busy
|
|
this.busy = 1;
|
|
|
|
|
|
// lock form
|
|
this.toggle( $form, 'lock' );
|
|
|
|
|
|
// ajax
|
|
$.ajax({
|
|
url: acf.get('ajaxurl'),
|
|
data: data,
|
|
type: 'post',
|
|
dataType: 'json',
|
|
success: function( json ){
|
|
|
|
// bail early if not json success
|
|
if( !acf.is_ajax_success(json) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
self.fetch_success( $form, json.data );
|
|
|
|
},
|
|
complete: function(){
|
|
|
|
self.fetch_complete( $form );
|
|
|
|
}
|
|
});
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* fetch_complete
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
fetch_complete: function( $form ){
|
|
|
|
// set busy
|
|
this.busy = 0;
|
|
|
|
|
|
// unlock so WP can publish form
|
|
this.toggle( $form, 'unlock' );
|
|
|
|
|
|
// bail early if validationw as not valid
|
|
if( !this.valid ) return;
|
|
|
|
|
|
|
|
|
|
// update ignore (allow form submit to not run validation)
|
|
this.ignore = 1;
|
|
|
|
|
|
// remove previous error message
|
|
var $message = $form.children('.acf-error-message');
|
|
|
|
if( $message.exists() ) {
|
|
|
|
$message.addClass('-success');
|
|
$message.children('p').html( acf._e('validation_successful') );
|
|
|
|
|
|
// remove message
|
|
setTimeout(function(){
|
|
|
|
acf.remove_el( $message );
|
|
|
|
}, 2000);
|
|
|
|
}
|
|
|
|
|
|
// remove hidden postboxes (this will stop them from being posted to save)
|
|
$form.find('.acf-postbox.acf-hidden').remove();
|
|
|
|
|
|
// action for 3rd party customization
|
|
acf.do_action('submit', $form);
|
|
|
|
|
|
// submit form again
|
|
if( this.$trigger ) {
|
|
|
|
this.$trigger.click();
|
|
|
|
} else {
|
|
|
|
$form.submit();
|
|
|
|
}
|
|
|
|
|
|
// lock form
|
|
this.toggle( $form, 'lock' );
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* fetch_success
|
|
*
|
|
* description
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
fetch_success: function( $form, json ){
|
|
|
|
// filter for 3rd party customization
|
|
json = acf.apply_filters('validation_complete', json, $form);
|
|
|
|
|
|
// validate json
|
|
if( !json || json.valid || !json.errors ) {
|
|
|
|
// set valid (allows fetch_complete to run)
|
|
this.valid = true;
|
|
|
|
|
|
// action for 3rd party
|
|
acf.do_action('validation_success');
|
|
|
|
|
|
// end function
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// action for 3rd party
|
|
acf.do_action('validation_failure');
|
|
|
|
|
|
// set valid (prevents fetch_complete from runing)
|
|
this.valid = false;
|
|
|
|
|
|
// reset trigger
|
|
this.$trigger = null;
|
|
|
|
|
|
// vars
|
|
var $scrollTo = null,
|
|
count = 0,
|
|
message = acf._e('validation_failed');
|
|
|
|
|
|
// show field error messages
|
|
if( json.errors && json.errors.length > 0 ) {
|
|
|
|
for( var i in json.errors ) {
|
|
|
|
// get error
|
|
var error = json.errors[ i ];
|
|
|
|
|
|
// is error for a specific field?
|
|
if( !error.input ) {
|
|
|
|
// update message
|
|
message += '. ' + error.message;
|
|
|
|
|
|
// ignore following functionality
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
// get input
|
|
var $input = $form.find('[name="' + error.input + '"]').first();
|
|
|
|
|
|
// if $_POST value was an array, this $input may not exist
|
|
if( !$input.exists() ) {
|
|
|
|
$input = $form.find('[name^="' + error.input + '"]').first();
|
|
|
|
}
|
|
|
|
|
|
// bail early if input doesn't exist
|
|
if( !$input.exists() ) continue;
|
|
|
|
|
|
// increase
|
|
count++;
|
|
|
|
|
|
// now get field
|
|
var $field = acf.get_field_wrap( $input );
|
|
|
|
|
|
// add error
|
|
this.add_error( $field, error.message );
|
|
|
|
|
|
// set $scrollTo
|
|
if( $scrollTo === null ) {
|
|
|
|
$scrollTo = $field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// message
|
|
if( count == 1 ) {
|
|
|
|
message += '. ' + acf._e('validation_failed_1');
|
|
|
|
} else if( count > 1 ) {
|
|
|
|
message += '. ' + acf._e('validation_failed_2').replace('%d', count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// get $message
|
|
var $message = $form.children('.acf-error-message');
|
|
|
|
if( !$message.exists() ) {
|
|
|
|
$message = $('<div class="acf-error-message"><p></p><a href="#" class="acf-icon -cancel small"></a></div>');
|
|
|
|
$form.prepend( $message );
|
|
|
|
}
|
|
|
|
|
|
// update message
|
|
$message.children('p').html( message );
|
|
|
|
|
|
// if no $scrollTo, set to message
|
|
if( $scrollTo === null ) {
|
|
|
|
$scrollTo = $message;
|
|
|
|
}
|
|
|
|
|
|
// timeout avoids flicker jump
|
|
setTimeout(function(){
|
|
|
|
$("html, body").animate({ scrollTop: $scrollTo.offset().top - ( $(window).height() / 2 ) }, 500);
|
|
|
|
}, 1);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add_error
|
|
*
|
|
* This function will add error markup to a field
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $field (jQuery)
|
|
* @param message (string)
|
|
* @return n/a
|
|
*/
|
|
|
|
add_error: function( $field, message ){
|
|
|
|
// reference
|
|
var self = this;
|
|
|
|
|
|
// add class
|
|
$field.addClass(this.error_class);
|
|
|
|
|
|
// add message
|
|
if( message !== undefined ) {
|
|
|
|
$field.children('.acf-input').children('.' + this.message_class).remove();
|
|
$field.children('.acf-input').prepend('<div class="' + this.message_class + '"><p>' + message + '</p></div>');
|
|
|
|
}
|
|
|
|
|
|
// add event
|
|
var event = function(){
|
|
|
|
// remove error
|
|
self.remove_error( $field );
|
|
|
|
|
|
// remove self
|
|
$field.off('focus change', 'input, textarea, select', event);
|
|
|
|
}
|
|
|
|
$field.on('focus change', 'input, textarea, select', event);
|
|
|
|
|
|
// hook for 3rd party customization
|
|
acf.do_action('add_field_error', $field);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* remove_error
|
|
*
|
|
* This function will remove error markup from a field
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $field (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
remove_error: function( $field ){
|
|
|
|
// var
|
|
$message = $field.children('.acf-input').children('.' + this.message_class);
|
|
|
|
|
|
// remove class
|
|
$field.removeClass(this.error_class);
|
|
|
|
|
|
// remove message
|
|
setTimeout(function(){
|
|
|
|
acf.remove_el( $message );
|
|
|
|
}, 250);
|
|
|
|
|
|
// hook for 3rd party customization
|
|
acf.do_action('remove_field_error', $field);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* add_warning
|
|
*
|
|
* This functino will add and auto remove an error message to a field
|
|
*
|
|
* @type function
|
|
* @date 4/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $field (jQuery)
|
|
* @param message (string)
|
|
* @return n/a
|
|
*/
|
|
|
|
add_warning: function( $field, message ){
|
|
|
|
this.add_error( $field, message );
|
|
|
|
setTimeout(function(){
|
|
|
|
acf.validation.remove_error( $field )
|
|
|
|
}, 1000);
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* show_spinner
|
|
*
|
|
* This function will show a spinner element. Logic changed in WP 4.2
|
|
*
|
|
* @type function
|
|
* @date 3/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $spinner (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
show_spinner: function( $spinner ){
|
|
|
|
// bail early if no spinner
|
|
if( !$spinner.exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var wp_version = acf.get('wp_version');
|
|
|
|
|
|
// show
|
|
if( parseFloat(wp_version) >= 4.2 ) {
|
|
|
|
$spinner.addClass('is-active');
|
|
|
|
} else {
|
|
|
|
$spinner.css('display', 'inline-block');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* hide_spinner
|
|
*
|
|
* This function will hide a spinner element. Logic changed in WP 4.2
|
|
*
|
|
* @type function
|
|
* @date 3/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $spinner (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
hide_spinner: function( $spinner ){
|
|
|
|
// bail early if no spinner
|
|
if( !$spinner.exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// vars
|
|
var wp_version = acf.get('wp_version');
|
|
|
|
|
|
// hide
|
|
if( parseFloat(wp_version) >= 4.2 ) {
|
|
|
|
$spinner.removeClass('is-active');
|
|
|
|
} else {
|
|
|
|
$spinner.css('display', 'none');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* disable_submit
|
|
*
|
|
* This function will disable the $trigger is possible
|
|
*
|
|
* @type function
|
|
* @date 3/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $spinner (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
disable_submit: function( $submit ){
|
|
|
|
// bail early if no submit
|
|
if( !$submit.exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// add class
|
|
$submit.addClass('disabled button-disabled button-primary-disabled');
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* enable_submit
|
|
*
|
|
* This function will enable the $trigger is possible
|
|
*
|
|
* @type function
|
|
* @date 3/05/2015
|
|
* @since 5.2.3
|
|
*
|
|
* @param $spinner (jQuery)
|
|
* @return n/a
|
|
*/
|
|
|
|
enable_submit: function( $submit ){
|
|
|
|
// bail early if no submit
|
|
if( !$submit.exists() ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// remove class
|
|
$submit.removeClass('disabled button-disabled button-primary-disabled');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
(function($){
|
|
|
|
acf.fields.wysiwyg = acf.field.extend({
|
|
|
|
type: 'wysiwyg',
|
|
$el: null,
|
|
$textarea: null,
|
|
toolbars: {},
|
|
|
|
actions: {
|
|
'load': 'initialize',
|
|
'append': 'initialize',
|
|
'remove': 'disable',
|
|
'sortstart': 'disable',
|
|
'sortstop': 'enable'
|
|
},
|
|
|
|
focus: function(){
|
|
|
|
// get elements
|
|
this.$el = this.$field.find('.wp-editor-wrap').last();
|
|
this.$textarea = this.$el.find('textarea');
|
|
|
|
// get options
|
|
this.o = acf.get_data( this.$el );
|
|
this.o.id = this.$textarea.attr('id');
|
|
|
|
},
|
|
|
|
initialize: function(){
|
|
|
|
// bail early if no tinyMCEPreInit (needed by both tinymce and quicktags)
|
|
if( typeof tinyMCEPreInit === 'undefined' ) return;
|
|
|
|
|
|
// generate new id
|
|
var old_id = this.o.id,
|
|
new_id = acf.get_uniqid('acf-editor-'),
|
|
html = this.$el.outerHTML();
|
|
|
|
|
|
// replace
|
|
html = acf.str_replace( old_id, new_id, html );
|
|
|
|
|
|
// swap
|
|
this.$el.replaceWith( html );
|
|
|
|
|
|
// update id
|
|
this.o.id = new_id
|
|
|
|
|
|
// initialize
|
|
this.initialize_tinymce();
|
|
this.initialize_quicktags();
|
|
|
|
},
|
|
|
|
initialize_tinymce: function(){
|
|
|
|
// bail early if no tinymce
|
|
if( typeof tinymce === 'undefined' ) return;
|
|
|
|
|
|
// vars
|
|
var mceInit = this.get_mceInit();
|
|
|
|
|
|
// append
|
|
tinyMCEPreInit.mceInit[ mceInit.id ] = mceInit;
|
|
|
|
|
|
// bail early if not visual active
|
|
if( !this.$el.hasClass('tmce-active') ) return;
|
|
|
|
|
|
// initialize
|
|
try {
|
|
|
|
tinymce.init( mceInit );
|
|
|
|
} catch(e){}
|
|
|
|
},
|
|
|
|
initialize_quicktags: function(){
|
|
|
|
// bail early if no quicktags
|
|
if( typeof quicktags === 'undefined' ) return;
|
|
|
|
|
|
// vars
|
|
var qtInit = this.get_qtInit();
|
|
|
|
|
|
// append
|
|
tinyMCEPreInit.qtInit[ qtInit.id ] = qtInit;
|
|
|
|
|
|
// initialize
|
|
try {
|
|
|
|
var qtag = quicktags( qtInit );
|
|
|
|
this._buttonsInit( qtag );
|
|
|
|
} catch(e){}
|
|
|
|
},
|
|
|
|
get_mceInit : function(){
|
|
|
|
// reference
|
|
var $field = this.$field;
|
|
|
|
|
|
// vars
|
|
var toolbar = this.get_toolbar( this.o.toolbar ),
|
|
mceInit = $.extend({}, tinyMCEPreInit.mceInit.acf_content);
|
|
|
|
|
|
// selector
|
|
mceInit.selector = '#' + this.o.id;
|
|
|
|
|
|
// id
|
|
mceInit.id = this.o.id; // tinymce v4
|
|
mceInit.elements = this.o.id; // tinymce v3
|
|
|
|
|
|
// toolbar
|
|
if( toolbar ) {
|
|
|
|
var k = (tinymce.majorVersion < 4) ? 'theme_advanced_buttons' : 'toolbar';
|
|
|
|
for( var i = 1; i < 5; i++ ) {
|
|
|
|
mceInit[ k + i ] = acf.isset(toolbar, i) ? toolbar[i] : '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// events
|
|
if( tinymce.majorVersion < 4 ) {
|
|
|
|
mceInit.setup = function( ed ){
|
|
|
|
ed.onInit.add(function(ed, event) {
|
|
|
|
// focus
|
|
$(ed.getBody()).on('focus', function(){
|
|
|
|
acf.validation.remove_error( $field );
|
|
|
|
});
|
|
|
|
$(ed.getBody()).on('blur', function(){
|
|
|
|
// update the hidden textarea
|
|
// - This fixes a bug when adding a taxonomy term as the form is not posted and the hidden textarea is never populated!
|
|
|
|
// save to textarea
|
|
ed.save();
|
|
|
|
|
|
// trigger change on textarea
|
|
$field.find('textarea').trigger('change');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
mceInit.setup = function( ed ){
|
|
|
|
ed.on('focus', function(e) {
|
|
|
|
acf.validation.remove_error( $field );
|
|
|
|
});
|
|
|
|
ed.on('change', function(e) {
|
|
|
|
// save to textarea
|
|
ed.save();
|
|
|
|
|
|
$field.find('textarea').trigger('change');
|
|
|
|
});
|
|
|
|
/*
|
|
ed.on('blur', function(e) {
|
|
|
|
// update the hidden textarea
|
|
// - This fixes a but when adding a taxonomy term as the form is not posted and the hidden textarea is never populated!
|
|
|
|
// save to textarea
|
|
ed.save();
|
|
|
|
|
|
// trigger change on textarea
|
|
$field.find('textarea').trigger('change');
|
|
|
|
});
|
|
*/
|
|
|
|
/*
|
|
ed.on('ResizeEditor', function(e) {
|
|
// console.log(e);
|
|
});
|
|
*/
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
// disable wp_autoresize_on (no solution yet for fixed toolbar)
|
|
mceInit.wp_autoresize_on = false;
|
|
|
|
|
|
// hook for 3rd party customization
|
|
mceInit = acf.apply_filters('wysiwyg_tinymce_settings', mceInit, mceInit.id);
|
|
|
|
|
|
// return
|
|
return mceInit;
|
|
|
|
},
|
|
|
|
get_qtInit : function(){
|
|
|
|
// vars
|
|
var qtInit = $.extend({}, tinyMCEPreInit.qtInit.acf_content);
|
|
|
|
|
|
// id
|
|
qtInit.id = this.o.id;
|
|
|
|
|
|
// hook for 3rd party customization
|
|
qtInit = acf.apply_filters('wysiwyg_quicktags_settings', qtInit, qtInit.id);
|
|
|
|
|
|
// return
|
|
return qtInit;
|
|
|
|
},
|
|
|
|
/*
|
|
* disable
|
|
*
|
|
* This function will disable the tinymce for a given field
|
|
* Note: txtarea_el is different from $textarea.val() and is the value that you see, not the value that you save.
|
|
* this allows text like <--more--> to wok instead of showing as an image when the tinymce is removed
|
|
*
|
|
* @type function
|
|
* @date 1/08/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param n/a
|
|
* @return n/a
|
|
*/
|
|
|
|
disable: function(){
|
|
|
|
try {
|
|
|
|
// vars
|
|
var ed = tinyMCE.get( this.o.id )
|
|
|
|
|
|
// save
|
|
ed.save();
|
|
|
|
|
|
// destroy editor
|
|
ed.destroy();
|
|
|
|
} catch(e) {}
|
|
|
|
},
|
|
|
|
enable: function(){
|
|
|
|
// bail early if html mode
|
|
if( this.$el.hasClass('tmce-active') && acf.isset(window,'switchEditors') ) {
|
|
|
|
switchEditors.go( this.o.id, 'tmce');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
get_toolbar : function( name ){
|
|
|
|
// bail early if toolbar doesn't exist
|
|
if( typeof this.toolbars[ name ] !== 'undefined' ) {
|
|
|
|
return this.toolbars[ name ];
|
|
|
|
}
|
|
|
|
|
|
// return
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
/*
|
|
* _buttonsInit
|
|
*
|
|
* This function will add the quicktags HTML to a WYSIWYG field. Normaly, this is added via quicktags on document ready,
|
|
* however, there is no support for 'append'. Source: wp-includes/js/quicktags.js:245
|
|
*
|
|
* @type function
|
|
* @date 1/08/2014
|
|
* @since 5.0.0
|
|
*
|
|
* @param ed (object) quicktag object
|
|
* @return n/a
|
|
*/
|
|
|
|
_buttonsInit: function( ed ) {
|
|
var 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 = '';
|
|
|
|
// 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 === inst ) {
|
|
theButtons[id] = edButtons[i];
|
|
|
|
if ( edButtons[i].html ) {
|
|
html += edButtons[i].html(name + '_');
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( use && use.indexOf(',fullscreen,') !== -1 ) {
|
|
theButtons.fullscreen = new qt.FullscreenButton();
|
|
html += theButtons.fullscreen.html(name + '_');
|
|
}
|
|
|
|
|
|
if ( 'rtl' === document.getElementsByTagName('html')[0].dir ) {
|
|
theButtons.textdirection = new qt.TextDirectionButton();
|
|
html += theButtons.textdirection.html(name + '_');
|
|
}
|
|
|
|
ed.toolbar.innerHTML = html;
|
|
ed.theButtons = theButtons;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
/*
|
|
* wysiwyg_manager
|
|
*
|
|
* This model will handle validation of fields within a tab group
|
|
*
|
|
* @type function
|
|
* @date 25/11/2015
|
|
* @since 5.3.2
|
|
*
|
|
* @param $post_id (int)
|
|
* @return $post_id (int)
|
|
*/
|
|
|
|
var acf_content = acf.model.extend({
|
|
|
|
$div: null,
|
|
|
|
actions: {
|
|
'ready': 'ready'
|
|
},
|
|
|
|
ready: function(){
|
|
|
|
// vars
|
|
this.$div = $('#acf-hidden-wp-editor');
|
|
|
|
|
|
// bail early if doesn't exist
|
|
if( !this.$div.exists() ) return;
|
|
|
|
|
|
// move to footer
|
|
this.$div.appendTo('body');
|
|
|
|
|
|
// bail early if no tinymce
|
|
if( typeof tinymce === 'undefined' ) 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);
|
|
|
|
// @codekit-prepend "../js/event-manager.js";
|
|
// @codekit-prepend "../js/acf.js";
|
|
// @codekit-prepend "../js/acf-ajax.js";
|
|
// @codekit-prepend "../js/acf-checkbox.js";
|
|
// @codekit-prepend "../js/acf-color-picker.js";
|
|
// @codekit-prepend "../js/acf-conditional-logic.js";
|
|
// @codekit-prepend "../js/acf-date-picker.js";
|
|
// @codekit-prepend "../js/acf-date-time-picker.js";
|
|
// @codekit-prepend "../js/acf-file.js";
|
|
// @codekit-prepend "../js/acf-google-map.js";
|
|
// @codekit-prepend "../js/acf-image.js";
|
|
// @codekit-prepend "../js/acf-media.js";
|
|
// @codekit-prepend "../js/acf-oembed.js";
|
|
// @codekit-prepend "../js/acf-radio.js";
|
|
// @codekit-prepend "../js/acf-relationship.js";
|
|
// @codekit-prepend "../js/acf-select.js";
|
|
// @codekit-prepend "../js/acf-tab.js";
|
|
// @codekit-prepend "../js/acf-time-picker.js";
|
|
// @codekit-prepend "../js/acf-taxonomy.js";
|
|
// @codekit-prepend "../js/acf-url.js";
|
|
// @codekit-prepend "../js/acf-validation.js";
|
|
// @codekit-prepend "../js/acf-wysiwyg.js";
|
|
|