†ô!	÷tñ�Ëj-è¶sŠ�†]îø™¹ašÙý_Îõ:  ÓŠÿ˜IX«@ÞüÈ#I½CH9Í™�ñó£áŒIª[¿ŠCûÖ|îñ3BNmB¦Ë¨bÐ>!%¨²7ísß ˜-ÒcI0hèAE%Äç"äK~ÈO+�‚<**%ãŽÇÒñ^½ft‡rç¹ÞOD�Œ>(ör'™,þ~í³,uU”«hªFµ@¶²76€³ƒk‹ÿi¢j©È…Û&öjm°Ùakm]zJÊé™;hR41aŠ~Ü?:ü¸Oît£Eƒ]…
Õ&0ã…ìà
:hG}Ý+jÙÆñ
%!3á&Ìç¸9¿@lõ7¥paºòø<@hÎ\ºÑ&B•†^IîZ�L š�N�Ã8?Ô zÈÃ5¯Óö½S:d±¶„MãÒxô½kŽÄ[€Ñ@.‡vÀÿÑ½»3¤¾V.@ ¯‚éˆ±®‚)ÎäGõOÞYØ$i¼¥uèŒm½�§L÷˜—mÿÁJ˜<3}òˆŠ×;×“·�Ä·Ï�z²Ì°p{Y0fM"¼·h›ØÉgdNòdá@”t¾ÞÖ|RÔÙh0EY µ÷ÅWa” š�èþjUÈ´|©C#—gV�!Þ8™ÛùðŒâ®ÔgW#jŒ¬Z¤]tUÿÓX5ñKÉp>åIW»ô ÑÅ»Ÿ+ÊÈ}­L� c]Â'Íõóm`àù¼†_Ú~3dT	UýA�ôÙçïeŠõe2µ3¥Ø}-|ã´LžWš•=Ñ]'xÆÃ_õ ^høÕ”L¤ð²áAyÖA
ï%äbd‡œZ�®xøÉA]F’8ÙÛ‹ÏðKÉwe¨!
Ð¿>ùaì�Ñ4°`Ý,BåX1ÃYüìk�ä¹’f§jÇ%Îƒ"ÇydÇ‰.`VF‘Ví¿µê½Êp3ï?‰ÍX×¨Çðiz¾#\2þ;¿	â®ÂFþãkõyÒ\¯HoôÿŠ§á¼“Üf¡‘{ƒ%Ö«�_‚:"±acO´—ªÒ�¢U›^Z”ï\¥ác~}–¿P‹›Dï==ÀéÖÖØ×ôxs9F7:s½M-Û|ÍÎR‡oæ«Aág8Ù€³shKÜr2ø3n4ªÎuêÙj›â`.G-õùé*
ùì¹†W"¨5‹¨®íØâ™tè±MO=ÉoFA:"ò`²*¶®ù‘¾Ž2Ëb¹±M[íªKM<|cUkà…VÆ�¨
¹Ú'«œ±ºñ—3Kw˜œ¸.Ï:ÞóÞËÂì:D'×?üHœˆXÊŽQ©ê@øz’q<ú:ò<¡S¼(ô W!mašA¼I—Ü�ÊYmv<™zM·ˆwêÜD€ú[©RIßéw‹[j?	�¶ý!(ê†ŒÅ
ÁòÙûú•‡«SÖ¿`ºq­˜s‚3ñ�
“|n ×ÖS#Êw8‚JÌ5)¢ùò]%¼IÔ7‘9šü¦¥œî¼Év.®ÀàÍìO«¤yü˜ï1ÊŠÖ,ÉŒÌº9–²ÔQ¹j:4�,ã“=A}hÌgL0Lÿ(eº$Ÿ[hvm‡€õím,ËK‰÷[U–Q“±5ÛÚ¢¸î“k�XØq•ž8é	.%¨[iÍCÛò®ÿæÌ×wÇÁZui$´�€´ê€å¢	ã"Jc`�»ñuIh¼ôÍ=ßPÖc%’àr, 2À(Ú2S€Ðx×4»Ì!ìþ^fUûZFf)¹È(Få1\Õ|šª	¡ÙXŒMÏaÐ}`A'ð¥*­…²Õ@+¤g½à=ìÅIÉ­ó¢zaÝrÁëf|¿�/¹üEcR`�”i…‡Wv°®êØÒò¡×ú3+«ý/®Þôg }�ôú£IS
áµS=U[™üìfl¹Îl66˜÷e{§ƒŒôr—-¬Ó~e‹dªofz˜á	bª'ˆ4Áéó®åá¸9œ…§6ž'Œ¿âû›c_Vk®7À\‰àï[�—Æé†0O¢Ñ6rá|veÔð—®Eÿ�6÷BR‰Žø ßyÚ†P-‹-ÙÅú:¡ò}„ÖÁ³¶B²™Æ‡7	r>)Ç(U!pƒ¿&«ÐhPéÉdï¶+ÛòîØ|™¡ëP�yP½=ºi_áSjˆvX�0�^‚‰DÆª•È '7·rU»“ÃK¼„Û“‰‰#¨�J¬é 3†ñae
ˆ÷Ûxvô³ÏÊS1]�:%¯@eŒ½¾_eœ™+oÂn›4ià4TM«A’ê¦“°¼O±)§Á�©›”a'!š-H¬f˜�ÂééFWþRoËsMïZˆ»¤ôHid@çùZï/¤ÃŒ/7Í¯"(úá„“®úí~ß-oä¢;¬†!Jƒe’¤½#cšZÁ¡”¦¬�ÍJäMø…¥ï<TnÕ–c6Ð#ßü”ò>@Å^‡VH½qÔXf­^KDHmÑDchô8Æ‹Ñ}&½Þ~Ü
T"Âæ �QºžÛGÛs•¹{†¹vÞaçž$®«Ëà	O¨[Wq¿é•Øtà ÀÕ±²ÔA=Ã¬·Á¬iŒ~¢xõ7k•èƒëÆÈ7I‡Êt¤FM7wÊ{zÛIz³|¢$wÚÕŒü©®tåÓwWh5þ‘º$Âñ}°òÃùÝQ }mWm0ßr�]¶ºµZ—$lR^ˆ™Öh°G±ÐÔ×9i¯fÛ™¡ärV7ÏI/ö–EÐ²ÿ6Ø2:§³Y¦h±I¬bÿ…Pˆ_«0CÔõè§£'ø“¿û÷÷5û6-¸,³ÑT¤O›Š¡”A TÛ7—×•ªõZ°6¶KÝk\ƒšP%Êk°ï|Õya8<GNsÍü\¬žvOmgú>=ãdŸäLaIža÷_Íý�¹F_L²W�˜ËîšK³ye°‹ôãZgŒv—�%íŠ¢f>—°ýý‘Ùƒª�žgv£d‹c©— ˆ©Ìïd¿‰õ^æ!ùo§»àp6Í•ûØÒ„Y\"|ä's`##ë²ÊKÛÓÑýTšÜHP¸TBvžx€5ÚxDFÏû:+sçÓ"ˆ�¿çÝñ_X�*‘Ê÷5"^s�cgð®›±6½|÷I‡+…}˜~ô=Û++¸Ø®§ëi`LØòóÊ¼b~O=QÜ¬Dž¢‚Ì²
EÞè¥öªðnWÿ_¤�45J&êNDÞ’ÙIò“³"SoS¤�§¦ÃñíyHé¦ˆå÷Ï�µžéƒÂ
&.p'p…˜H20 ©ÇØƒÜ­5÷î`×!ør�+ÞpHö–¼%ur¤>åßx±•è²rOá¯øâêë5ötè¹$_gË=Vr‡ÑTD›hfßÖQðµÈçHó!†'¤íËêb‰å�ŽÐö£³™­PÑ¡4µì}$š¬x�‚ò·©
hK>�ôm;Ž²\c`éAeª‡»æÂCßPÂHÁ²ƒÊÐä÷õçÖdy»ZïHs!EZ—{«‘¦„2@O,èÍ§,í×AÌ+'LOWRßpŽðÔmM¼ö6��¢%Q×¨oÄ„ä4ûpÃDÎ6!&Ü3˜‘¹­A"�h³P1³Ã›¾�/TB4ß‰áÅ‹x�ô}H-üçY%3hš"Ë³ÚÓH‚úþ~ð0ÐÊçãr/±û’õbÚVÏh—WFÙ™c& ÊâÚ˜7X'[Žè|‡øÍ-az�«K œr2XM±¾TQCš[eß’¿]†qjPõhÿDýªàòö]í—Aü�¸6ýÊ–’,"ñÕL4­±œ ‹ÈúÄQõI©cÞ}ßè<<‚RZå(<Œtø:®MéÊð8\»¸Ž%‰H1˜•`tÕÿµ¥Ëkâ9‹"Ÿ+I˜RH:,¦ÐÆÝãšÇÀï¡ÈÎîøÚcÌó<#GØæB3ÅÌ,»Ñ°‚üúr:?BÑðtä•Žüð'ZÇÀ¯�»©!H(t<ˆúR¼¸¹ö˜Š¿_¹@6tk¾•áZn”i¹oW–É"p“tD�°e Ê<‹ÌÑÿBµ*¹Ë†å/'4-üÕ«$¢9 	€·˜Õ®?�Q´¥LÔ—4ä5¶œ¼(î¦ŠRÀ7ü’ê(¾Qè¶i/ªkÿì‡°Ë^n‹ø´Ëê�/Ò6H—ïV´%©°�‹ž$7¯n'{+¯Ž¬Ÿˆ‡Oï?§eCYÙíô[yUêÉ¼ÞM™yŸ¡Šiú©ä¢ÇˆÌ÷¥àÌAàŽYï^EA½OÀ ‘^–}Ž@•ŒÙæ£jƒeåbPÿ½�šñ(ðÝ³°½Ï<Š”1�&Ægæ-žåû\u™�lV‡%cdx%ÐÕUq$VÀaï;ÈVŒë<÷Rtä•Žüð'ZÇÀ¯�»©!H(t<ˆúR¼¸¹ö˜Š¿_¹@6tk¾•áZn”i¹oWvO„OÇ�¢°m®~<·×fÀ0Ë^xÔE_ãÅË†îà÷D_&Ù¯··q Û¬œ;ÆÆÔ–L´�ö÷Z1^ yÔ”ÇÂº³vši,AÛaüÇÚ(ÕH;#<"3Xâ¬ÊÉ:rsoKã´'½“»Û®]ÉV‹5‚öd‚Ñ]*[ÅØ«(Íè–…OEÌü½~%—#Gª½=²+D<âvD:–NàÝ›gÍ6Ù@ÕpÍ~ác�Ê öÉe¿ïÚãšîBò«ú¦ÀŠù\ªvÒ*¨E;Þ/Ø¼�ºù™Kˆ^ÏÙsZ¹rï@ì:+þÉ‰ƒ"c	D)¤^½Ð—Ï¬·™ƒ|Ù�¡%Øh·W/üÖ£Zê†’`wÒo² 2‹i‰Nú#0ÑÙÍî±æ�Åä¤�ÀÖ.Y/
€¯ÃÙ’¬¼�wSÎ!×Šñy‹Ü�Áuÿ³HtJÃ,?
5�9~¯pUH&nÏN;õÐ¤ÆîJ6ñÛAòÞ¸g¼–ªI¾ÖÛ4Ì¦¥šÌva`©ÀWáOÈŠÀkU?ýç¦ö·vÒ*¨E;Þ/Ø¼�ºù™KJz6-t“ñ†ÌÅ+1©‹Ó”s¢�|ÕúiŽ(5ÏB¤0ŸÂË-•äÂb3.‡•êx“Ñ�Ôx¶5™¤Ö€Q¸%ŽlDÇ¹¶,�ŠEõ½„‰ŒKáÀüÝ[-\IÙ!Å'Ò³õ©Âºv”€ØW"°‡Ü¤�°¢þ@¡j{kJiÇ–;l"?uÁåA3^º!½’ÁÙ}uØVÚ’Þ
*›gXõŽæ·˜é€¢=5—¶ì‡Õ5ðeþWqJ ™@lºUª›lÜÞW8¹¬­@¼çƒåC¿Û��¸Áç«Èñ®£Š^Üö´µ"²ÛÑiq.r¾ˆÒN——šçmetn‡—ee¦îó5I)¿Ä€ÍOXnûÓîó5I)¿Ä€ÍOXnûÓîó5I)¿Ä€ÍOXnûÓîó5I)¿Ä€ÍOXnûÓÙ?|	Öô­îÊ© «afunctions
	 */

	/**
	 * "Magic" method to automatically apply the required check to any of the
	 * static methods simply by adding '_required' or '_empty' to the end of the
	 * method's name, depending on if you need the field to be submitted or not.
	 *
	 * This is retained for backwards compatibility, but the validation options
	 * are now the recommended way of indicating that a field is required.
	 *
	 * @internal
	 * @param string $name Function name
	 * @param array $arguments Function arguments
	 * @return mixed|string
	 */
	public static function __callStatic( $name, $arguments ) {
		if ( preg_match( '/_required$/', $name ) ) {
			if ( $arguments[0] === null || $arguments[0] === '' ) {
				return 'This field is required';
			}

			return call_user_func_array( 
				__NAMESPACE__.'\Validate::'.str_replace( '_required', '', $name ),
				$arguments
			);
		}
	}


	/**
	 * Extend the options from the user function and the validation function
	 * with core defaults.
	 *
	 *  @internal
	 */
	public static function _extend( $userOpts, $prop, $fnOpts ) {
		$cfg = array(
			'message'  => 'Input not valid',
			'required' => false,
			'empty'    => true,
			'optional' => true
		);

		if ( ! is_array( $userOpts ) ) {
			if ( $prop ) {
				$cfg[ $prop ] = $userOpts;
			}

			// Not an array, but the non-array case has been handled above, so
			// just an empty array
			$userOpts = array();
		}

		// Merge into a single array - first array gets priority if there is a
		// key clash, so user first, then function commands and finally the
		// global options 
		$cfg = $userOpts + $fnOpts + $cfg;

		return $cfg;
	}


	/**
	 * Perform common validation using the configuration parameters
	 *
	 *  @internal
	 */
	public static function _common( $val, $opts ) {
		$optional = $opts->optional();
		$empty    = $opts->allowEmpty();

		// Error state tests
		if ( ! $optional && $val === null  ) {
			// Value must be given
			return false;
		}
		else if ( $empty === false && $val === '' ) {
			// Value must not be empty
			return false;
		}
		
		// Validation passed states
		if ( $optional && $val === null ) {
			return true;
		}
		else if ( $empty === true && $val === '' ) {
			return true;
		}

		// Have the specific validation function perform its tests
		return null;
	}

	/**
	 * Convert the old style validation parameters into ValidateOptions
	 *
	 *  @internal
	 */
	public static function _commonLegacy( $cfg ) {
		$opts = new ValidateOptions();

		if ( is_array( $cfg ) ) {
			// `required` is a legacy shortcut for optional=false, empty=false
			if ( isset( $cfg['required'] ) ) {
				$opts->optional( false );
				$opts->allowEmpty( false );
			}

			if ( isset( $cfg['empty'] ) ) {
				$opts->allowEmpty( $cfg['empty'] );
			}

			if ( isset( $cfg['message'] ) ) {
				$opts->message( $cfg['message'] );
			}

			if ( isset( $cfg['optional'] ) ) {
				$opts->optional( $cfg['optional'] );
			}
		}

		return $opts;
	}



	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * Basic validation
	 */

	/**
	 * No validation - all inputs are valid.
	 *  @return callable Validation function
	 */
	public static function none() {
		return function ( $val, $data, $field, $host ) {
			return true;
		};
	}


	/**
	 * Basic validation - this is used to perform the validation provided by the
	 * validation options only. If the validation options pass (e.g. `required`,
	 * `empty` and `optional`) then the validation will pass regardless of the
	 * actual value.
	 *
	 * Note that there are two helper short-cut methods that provide the same
	 * function as this method, but are slightly shorter:
	 *
	 * ```
	 * // Required:
	 * Validate::required()
	 *
	 * // is the same as
	 * Validate::basic( $val, $data, array(
	 *   "required" => true
	 * );
	 * ```
	 *
	 * ```
	 * // Optional, but not empty if given:
	 * Validate::notEmpty()
	 *
	 * // is the same as
	 * Validate::basic( $val, $data, array(
	 *   "empty" => false
	 * );
	 * ```
	 *
	 * @param string $val The value to check for validity
	 * @param string[] $data The full data set submitted
	 * @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 * @param array $host Host information
	 * @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	static function basic( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );

		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			return $common === false ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Required field - there must be a value and it must be a non-empty value
	 *
	 * This is a helper short-cut method which is the same as:
	 * 
	 * ```
	 * Validate::basic( $val, $data, array(
	 *   "required" => true
	 * );
	 * ```
	 * 
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 * @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	static function required( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		$opts->allowEmpty( false );
		$opts->optional( false );

		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			return $common === false ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Optional field, but if given there must be a non-empty value
	 *
	 * This is a helper short-cut method which is the same as:
	 * 
	 * ```
	 * Validate::basic( $val, $data, array(
	 *   "empty" => false
	 * );
	 * ```
	 *
	 *  @param ValidateOptions $cfg Validation options
	 *  @return callable Validation function
	 */
	static function notEmpty( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		$opts->allowEmpty( false );

		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			return $common === false ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Validate an input as a boolean value.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function boolean( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );

		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
	
			if ( filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === null ) {
				return $opts->message();
			}
			return true;
		};
	}



	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * Number validation methods
	 */

	/**
	 * Check that any input is numeric.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. Additional options:
	 *     * `decimal`: is available to indicate what character should be used
	 *       as the decimal
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function numeric ( $decimal=".", $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );

		return function ( $val, $data, $field, $host ) use ( $opts, $decimal ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
	
			if ( $decimal !== '.' ) {
				$val = str_replace( $decimal, '.', $val );
			}
	
			return ! is_numeric( $val ) ?
				$opts->message() :
				true;
		};
	}

	/**
	 * Check for a numeric input and that it is greater than a given value.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. Additional options:
	 *     * `min`: indicate the minimum value. If only the default validation
	 *       options are required, this parameter can be given as an integer
	 *       value, which will be used as the minimum value.
	 *     * `decimal`: is available to indicate what character should be used
	 *       as the decimal
	 *    separator (default '.').
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function minNum ( $min, $decimal=".", $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );

		return function ( $val, $data, $field, $host ) use ( $opts, $min, $decimal ) {
			$common = Validate::_common( $val, $opts );
			
			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
			
			$fn = Validate::numeric( $decimal, $opts );
			$numeric = $fn( $val, $data, $field, $host );

			if ( $numeric !== true ) {
				return $numeric;
			}
	
			if ( $decimal !== '.' ) {
				$val = str_replace( $decimal, '.', $val );
			}
	
			return $val < $min ?
				$opts->message() :
				true;
		};
	}

	/**
	 * Check for a numeric input and that it is less than a given value.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options.
	 *     * `max`: indicate the maximum value. If only the default validation
	 *       options are required, this parameter can be given as an integer
	 *       value, which will be used as the maximum value.
	 *     * `decimal`: is available to indicate what character should be used
	 *       as the decimal
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function maxNum ( $max, $decimal=".", $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );

		return function ( $val, $data, $field, $host ) use ( $opts, $max, $decimal ) {
			$common = Validate::_common( $val, $opts );
			
			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
			
			$fn = Validate::numeric( $decimal, $opts );
			$numeric = $fn( $val, $data, $field, $host );

			if ( $numeric !== true ) {
				return $numeric;
			}
	
			if ( $decimal !== '.' ) {
				$val = str_replace( $decimal, '.', $val );
			}
	
			return $val > $max ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Check for a numeric input and that it is both greater and smaller than
	 * given numbers.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. Additional options:
	 *     * `min`: indicate the minimum value.
	 *     * `max`: indicate the maximum value.
	 *     * `decimal`: is available to indicate what character should be used
	 *       as the decimal
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function minMaxNum ( $min, $max, $decimal='.', $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts, $min, $max, $decimal ) {
			$common = Validate::_common( $val, $opts );
			
			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
			
			$fn = Validate::numeric( $decimal, $opts );
			$numeric = $fn( $val, $data, $field, $host );

			if ( $numeric !== true ) {
				return $numeric;
			}

			$fn = Validate::minNum( $min, $decimal, $opts );
			$numeric = $fn( $val, $data, $field, $host );

			if ( $numeric !== true ) {
				return $numeric;
			}
			
			$fn = Validate::maxNum( $max, $decimal, $opts );
			$numeric = $fn( $val, $data, $field, $host );

			if ( $numeric !== true ) {
				return $numeric;
			}

			return true;
		};
	}



	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * String validation methods
	 */

	/**
	 * Validate an input as an e-mail address.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function email( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return filter_var($val, FILTER_VALIDATE_EMAIL) !== false ?
				true :
				$opts->message();
		};
	}


	/**
	 * Validate a string has a minimum length.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional option of
	 *    `min` is available for this method to indicate the minimum string
	 *    length. If only the default validation options are required, this
	 *    parameter can be given as an integer value, which will be used as the
	 *    minimum string length.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function minLen( $min, $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $min, $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			$strlen = is_callable('mb_strlen') ?
				'mb_strlen' :
				'strlen';

			return $strlen( $val ) < $min ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Validate a string does not exceed a maximum length.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional option of
	 *    `max` is available for this method to indicate the maximum string
	 *    length. If only the default validation options are required, this
	 *    parameter can be given as an integer value, which will be used as the
	 *    maximum string length.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function maxLen( $max, $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $max, $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			$strlen = is_callable('mb_strlen') ?
				'mb_strlen' :
				'strlen';

			return $strlen( $val ) > $max ?
				$opts->message() :
				true;
		};
	}

	/**
	 * Require a string with a certain minimum or maximum number of characters.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `min` and `max` are available for this method to indicate the minimum
	 *    and maximum string lengths, respectively.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function minMaxLen( $min, $max, $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts, $min, $max ) {
			$common = Validate::_common( $val, $opts );
			
			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}
			
			$fn = Validate::minLen( $min, $opts );
			$res = $fn( $val, $data, $field, $host );

			if ( $res !== true ) {
				return $res;
			}
			
			$fn = Validate::maxLen( $max, $opts );
			$res = $fn( $val, $data, $field, $host );

			if ( $res !== true ) {
				return $res;
			}

			return true;
		};
	}


	/**
	 * Validate as an IP address.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function ip( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return filter_var($val, FILTER_VALIDATE_IP) !== false ?
				true :
				$opts->message();
		};
	}


	/**
	 * Validate as an URL address.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array $opts Validation options. No additional options are
	 *    available or required for this validation method.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function url( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return filter_var($val, FILTER_VALIDATE_URL) !== false ?
				true :
				$opts->message();
		};
	}


	/**
	 * Check if string could contain an XSS attack string
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `db` - database connection object, `table` - database table to use and
	 *    `column` - the column to check this value against as value, are also
	 *    available. These options are not required and if not given are
	 *    automatically derived from the Editor and Field instances.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function xss ( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return $field->xssSafety( $val ) != $val ?
				$opts->message() :
				true;
		};
	}


	/**
	 * Confirm that the value submitted is in a list of allowable values
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `db` - database connection object, `table` - database table to use and
	 *    `column` - the column to check this value against as value, are also
	 *    available. These options are not required and if not given are
	 *    automatically derived from the Editor and Field instances.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function values( $values, $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $values, $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return in_array($val, $values) ?
				true :
				$opts->message();
		};
	}


	/**
	 * Check if there are any tags in the submitted value
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `db` - database connection object, `table` - database table to use and
	 *    `column` - the column to check this value against as value, are also
	 *    available. These options are not required and if not given are
	 *    automatically derived from the Editor and Field instances.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function noTags ( $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			return strip_tags( $val ) != $val ?
				$opts->message() :
				true;
		};
	}



	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * Date validation methods
	 */

	/**
	 * Check that a valid date input is given
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param array|string $opts If given as a string, then $opts is the date
	 *    format to check the validity of. If given as an array, then the
	 *    date format is in the 'format' parameter, and the return error
	 *    message in the 'message' parameter.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function dateFormat( $format, $cfg=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $format, $opts ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			$formatCreate = substr($format, 0, 1) !== '!' ?
				'!'.$format :
				$format;

			$date = \DateTime::createFromFormat( $formatCreate, $val );

			return $date && $date->format( $format ) == $val ?
				true :
				$opts->message();
		};
	}


	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * Database validation methods
	 */

	/**
	 * Check that the given value is unique in the database
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `db` - database connection object, `table` - database table to use and
	 *    `column` - the column to check this value against as value, are also
	 *    available. These options are not required and if not given are
	 *    automatically derived from the Editor and Field instances.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function unique( $cfg=null, $column=null, $table=null, $db=null ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			$editor = $host['editor'];

			if ( ! $db ) {
				$db = $host['db'];
			}

			if ( ! $table ) {
				$table = $editor->table(); // Returns an array, but `select` will take an array
			}

			if ( ! $column ) {
				$column = $field->dbField();
			}

			$query = $db
				->query( 'select', $table )
				->get( $column )
				->where( $column, $val );

			// If doing an edit, then we need to also discount the current row,
			// since it is of course already validly unique
			if ( $host['action'] === 'edit' ) {
				$cond = $editor->pkeyToArray( $host['id'], true );
				$query->where( $cond, null, '!=' );
			}

			$res = $query->exec();

			return $res->count() === 0 ?
				true :
				$opts->message(); 
		};
	}

	/**
	 * Check that the given value is a value that is available in a database -
	 * i.e. a join primary key. This will attempt to automatically use the table
	 * name and value column from the field's `options` method (under the
	 * assumption that it will typically be used with a joined field), but the
	 * table and field can also be specified via the options.
	 *
	 *  @param string $val The value to check for validity
	 *  @param string[] $data The full data set submitted
	 *  @param int|array $opts Validation options. The additional options of
	 *    `db` - database connection object, `table` - database table to use and
	 *    `column` - the column to check this value against as value, are also
	 *    available. These options are not required and if not given are
	 *    automatically derived from the Editor and Field instances.
	 *  @param array $host Host information
	 *  @return string|true true if the value is valid, a string with an error
	 *    message otherwise.
	 */
	public static function dbValues( $cfg=null, $column=null, $table=null, $db=null, $values=array() ) {
		$opts = ValidateOptions::select( $cfg );
		
		return function ( $val, $data, $field, $host ) use ( $opts, $column, $table, $db, $values ) {
			$common = Validate::_common( $val, $opts );

			if ( $common !== null ) {
				return $common === false ?
					$opts->message() :
					$common;
			}

			// Allow local values to be defined - for example null
			if ( in_array($val, $values) ) {
				return true;
			}

			$editor = $host['editor'];
			$options = $field->options();

			if ( ! $db ) {
				$db = $host['db'];
			}

			if ( ! $table ) {
				$table = $options->table(); // Returns an array, but `select` will take an array
			}

			if ( ! $column ) {
				$column = $options->value();
			}

			if ( ! $table ) {
				throw new \Exception('Table for database value check is not defined for field '.$field->name());
			}

			if ( ! $column ) {
				throw new \Exception('Value column for database value check is not defined for field '.$field->name());
			}

			// Try / catch in case the submitted value can't be represented as the
			// database type (e.g. an empty string as an integer)
			try {
				$count = $db
					->query( 'select', $table )
					->get( $column )
					->where( $column, $val )
					->exec()
					->count();

				return $count === 0 ?
					$opts->message() :
					true;
			}
			catch (\Exception $e) {
				return $opts->message();
			}
		};
	}
	
	

	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* File validation methods
	*/
	static function fileExtensions ( $extensions, $msg="This file type cannot be uploaded." ) {
		return function ( $file ) use ( $extensions, $msg ) {
			$extn = pathinfo($file['name'], PATHINFO_EXTENSION);

			for ( $i=0, $ien=count($extensions) ; $i<$ien ; $i++ ) {
				if ( strtolower( $extn ) === strtolower( $extensions[$i] ) ) {
					return true;
				}
			}

			return $msg;
		};
	}

	static function fileSize ( $size, $msg="Uploaded file is too large." ) {
		return function ( $file ) use ( $size, $msg ) {
			return $file['size'] > $size ?
				$msg :
				true;
		};
	}



	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Mjoin validation methods
	*/
	static function mjoinMinCount ( $count, $msg="Too few items." ) {
		return function ( $editor, $action, $values ) use ( $count, $msg ) {
			if ( $action === 'create' || $action === 'edit' ) {
				return count($values) < $count ?
					$msg :
					true;
			}
			return true;
		};
	}

	static function mjoinMaxCount ( $count, $msg="Too many items." ) {
		return function ( $editor, $action, $values ) use ( $count, $msg ) {
			if ( $action === 'create' || $action === 'edit' ) {
				return count($values) > $count ?
					$msg :
					true;
			}
			return true;
		};
	}


	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	 * Internal functions
	 * These legacy methods are for backwards compatibility with the old way of
	 * using the validation methods. They basically do argument swapping.
	 */

	/**
	 * @internal
	 */
	static function noneLegacy( $legacyOpts ) {
		return Validate::none();
	}
	
	/**
	 * @internal
	 */
	static function basicLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array() );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::required( $opts );
	}
	
	/**
	 * @internal
	 */
	static function requiredLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This field is required."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::required( $opts );
	}
	
	/**
	 * @internal
	 */
	static function notEmptyLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This field is required."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::notEmpty( $opts );
	}
	
	/**
	 * @internal
	 */
	static function booleanLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Please enter true or false."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::notEmpty( $opts );
	}
	
	/**
	 * @internal
	 */
	static function numericLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This input must be given as a number."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return isset( $legacyOpts['decimal'] ) ?
			Validate::numeric( $legacyOpts['decimal'], $opts ) :
			Validate::numeric( '.', $opts );
	}
	
	/**
	 * @internal
	 */
	static function minNumLegacy( $legacyOpts ) {
		$min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Number is too small, must be ".$min." or larger."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return isset( $legacyOpts['decimal'] ) ?
			Validate::minNum( $min, $legacyOpts['decimal'], $opts ) :
			Validate::minNum( $min, '.', $opts );
	}
	
	/**
	 * @internal
	 */
	static function maxNumLegacy( $legacyOpts ) {
		$max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Number is too large, must be ".$max." or smaller."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return isset( $legacyOpts['decimal'] ) ?
			Validate::maxNum( $max, $legacyOpts['decimal'], $opts ) :
			Validate::maxNum( $max, '.', $opts );
	}
	
	/**
	 * @internal
	 */
	static function minMaxNumLegacy( $legacyOpts ) {
		$min = $legacyOpts['min'];
		$max = $legacyOpts['max'];
		$cfg = Validate::_extend( $legacyOpts, null, array() );
		$opts = Validate::_commonLegacy( $cfg );

		return isset( $legacyOpts['decimal'] ) ?
			Validate::minMaxNum( $min, $max, $legacyOpts['decimal'], $opts ) :
			Validate::minMaxNum( $min, $max, '.', $opts );
	}
	
	/**
	 * @internal
	 */
	static function emailLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Please enter a valid e-mail address."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::email( $opts );
	}
	
	/**
	 * @internal
	 */
	static function minLenLegacy( $legacyOpts ) {
		$min = is_array($legacyOpts) ? $legacyOpts['min'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "The input is too short. ".$min." characters required."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::minLen( $min, $opts );
	}
	
	/**
	 * @internal
	 */
	static function maxLenLegacy( $legacyOpts ) {
		$max = is_array($legacyOpts) ? $legacyOpts['max'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "The input is too long. ".$max." characters maximum."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::maxLen( $max, $opts );
	}
	
	/**
	 * @internal
	 */
	static function minMaxLenLegacy( $legacyOpts ) {
		$min = $legacyOpts['min'];
		$max = $legacyOpts['max'];
		$cfg = Validate::_extend( $legacyOpts, null, array() );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::minMaxLen( $min, $max, $opts );
	}
	
	/**
	 * @internal
	 */
	static function ipLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Please enter a valid IP address."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::ip( $opts );
	}
	
	/**
	 * @internal
	 */
	static function urlLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Please enter a valid URL."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::url( $opts );
	}
	
	/**
	 * @internal
	 */
	static function xssLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This field contains potentially unsafe data."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::xss( $opts );
	}
	
	/**
	 * @internal
	 */
	static function valuesLegacy( $legacyOpts ) {
		$values = isset($legacyOpts['valid']) ? $legacyOpts['valid'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This value is not valid."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::values( $values, $opts );
	}
	
	/**
	 * @internal
	 */
	static function noTagsLegacy( $legacyOpts ) {
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This field may not contain HTML."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::noTags( $opts );
	}
	
	/**
	 * @internal
	 */
	static function dateFormatLegacy( $legacyOpts ) {
		$format = is_array($legacyOpts) ? $legacyOpts['format'] : $legacyOpts;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "Date is not in the expected format."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::dateFormat( $format, $opts );
	}
	
	/**
	 * @internal
	 */
	static function uniqueLegacy( $legacyOpts ) {
		$table =  isset( $legacyOpts['table'] )  ? $legacyOpts['table']  : null;
		$column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
		$db =     isset( $legacyOpts['db'] )     ? $legacyOpts['db']     : null;
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This field must have a unique value."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::unique( $opts, $column, $table, $db );
	}
	
	/**
	 * @internal
	 */
	static function dbValuesLegacy( $legacyOpts ) {
		$table =  isset( $legacyOpts['table'] )  ? $legacyOpts['table']  : null;
		$column = isset( $legacyOpts['column'] ) ? $legacyOpts['column'] : null;
		$db =     isset( $legacyOpts['db'] )     ? $legacyOpts['db']     : null;
		$values = isset( $legacyOpts['values'] ) ? $legacyOpts['values'] : array();
		$cfg = Validate::_extend( $legacyOpts, null, array(
			'message' => "This value is not valid."
		) );
		$opts = Validate::_commonLegacy( $cfg );

		return Validate::dbValues( $opts, $column, $table, $db, $values );
	}
}

