( 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 '&amp;' 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";

