balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

redux-vendors.js (46241B)


      1 // jscs:disable
      2 // jshint ignore: start
      3 
      4 /*!
      5  * jQuery Cookie Plugin v1.4.1
      6  * https://github.com/carhartl/jquery-cookie
      7  *
      8  * Copyright 2006, 2014 Klaus Hartl
      9  * Released under the MIT license
     10  */
     11 (function( factory ) {
     12 	if ( typeof define === 'function' && define.amd ) {
     13 		// AMD (Register as an anonymous module)
     14 		define( ['jquery'], factory );
     15 	} else if ( typeof exports === 'object' ) {
     16 		// Node/CommonJS
     17 		module.exports = factory( require( 'jquery' ) );
     18 	} else {
     19 		// Browser globals
     20 		factory( jQuery );
     21 	}
     22 }( function( $ ) {
     23 
     24 	var pluses = /\+/g;
     25 
     26 	function encode( s ) {
     27 		return config.raw ? s : encodeURIComponent( s );
     28 	}
     29 
     30 	function decode( s ) {
     31 		return config.raw ? s : decodeURIComponent( s );
     32 	}
     33 
     34 	function stringifyCookieValue( value ) {
     35 		return encode( config.json ? JSON.stringify( value ) : String( value ) );
     36 	}
     37 
     38 	function parseCookieValue( s ) {
     39 		if ( s.indexOf( '"' ) === 0 ) {
     40 			// This is a quoted cookie as according to RFC2068, unescape...
     41 			s = s.slice( 1, - 1 ).replace( /\\"/g, '"' ).replace( /\\\\/g, '\\' );
     42 		}
     43 
     44 		try {
     45 			// Replace server-side written pluses with spaces.
     46 			// If we can't decode the cookie, ignore it, it's unusable.
     47 			// If we can't parse the cookie, ignore it, it's unusable.
     48 			s = decodeURIComponent( s.replace( pluses, ' ' ) );
     49 			return config.json ? JSON.parse( s ) : s;
     50 		} catch ( e ) {
     51 		}
     52 	}
     53 
     54 	function read( s, converter ) {
     55 		var value = config.raw ? s : parseCookieValue( s );
     56 		return $.isFunction( converter ) ? converter( value ) : value;
     57 	}
     58 
     59 	var config = $.cookie = function( key, value, options ) {
     60 
     61 		// Write
     62 
     63 		if ( arguments.length > 1 && ! $.isFunction( value ) ) {
     64 			options = $.extend( {}, config.defaults, options );
     65 
     66 			if ( typeof options.expires === 'number' ) {
     67 				var days = options.expires, t = options.expires = new Date();
     68 				t.setMilliseconds( t.getMilliseconds() + days * 864e+5 );
     69 			}
     70 
     71 			return (document.cookie = [encode( key ), '=', stringifyCookieValue( value ), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
     72 				options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : ''].join( '' ));
     73 		}
     74 
     75 		// Read
     76 
     77 		var result = key ? undefined : {}, // To prevent the for loop in the first place assign an empty array
     78 		    // in case there are no cookies at all. Also prevents odd result when
     79 		    // calling $.cookie().
     80 		    cookies = document.cookie ? document.cookie.split( '; ' ) : [], i = 0, l = cookies.length;
     81 
     82 		for ( ; i < l; i ++ ) {
     83 			var parts = cookies[i].split( '=' ), name = decode( parts.shift() ), cookie = parts.join( '=' );
     84 
     85 			if ( key === name ) {
     86 				// If second argument (value) is a function it's a converter...
     87 				result = read( cookie, value );
     88 				break;
     89 			}
     90 
     91 			// Prevent storing a cookie that we couldn't decode.
     92 			if ( ! key && (cookie = read( cookie )) !== undefined ) {
     93 				result[name] = cookie;
     94 			}
     95 		}
     96 
     97 		return result;
     98 	};
     99 
    100 	config.defaults = {};
    101 
    102 	$.removeCookie = function( key, options ) {
    103 		// Must not alter options, thus extending a fresh object...
    104 		$.cookie( key, '', $.extend( {}, options, {expires: - 1} ) );
    105 		return ! $.cookie( key );
    106 	};
    107 
    108 } ));
    109 
    110 // jscs:disable
    111 // jshint ignore: start
    112 
    113 /********************************************************************
    114  * Limit the characters that may be entered in a text field
    115  * Common options: alphanumeric, alphabetic or numeric
    116  * Kevin Sheedy, 2012
    117  * http://github.com/KevinSheedy/jquery.alphanum
    118  *********************************************************************/
    119 
    120 (function( $ ) {
    121 	// API ///////////////////////////////////////////////////////////////////
    122 	$.fn.alphanum = function( settings ) {
    123 
    124 		var combinedSettings = getCombinedSettingsAlphaNum( settings );
    125 
    126 		var $collection = this;
    127 
    128 		setupEventHandlers( $collection, trimAlphaNum, combinedSettings );
    129 
    130 		return this;
    131 	};
    132 
    133 	$.fn.alpha = function( settings ) {
    134 
    135 		var defaultAlphaSettings = getCombinedSettingsAlphaNum( "alpha" );
    136 		var combinedSettings     = getCombinedSettingsAlphaNum( settings, defaultAlphaSettings );
    137 
    138 		var $collection = this;
    139 
    140 		setupEventHandlers( $collection, trimAlphaNum, combinedSettings );
    141 
    142 		return this;
    143 	};
    144 
    145 	$.fn.numeric = function( settings ) {
    146 
    147 		var combinedSettings = getCombinedSettingsNum( settings );
    148 		var $collection      = this;
    149 
    150 		setupEventHandlers( $collection, trimNum, combinedSettings );
    151 
    152 		$collection.on(
    153 			'blur',
    154 			function() {
    155 				numericField_Blur( this, settings );
    156 			}
    157 		);
    158 
    159 		return this;
    160 	};
    161 
    162 	// End of API /////////////////////////////////////////////////////////////
    163 
    164 	// Start Settings ////////////////////////////////////////////////////////
    165 
    166 	var DEFAULT_SETTINGS_ALPHANUM = {
    167 		allow: '',   // Allow extra characters
    168 		disallow: '',   // Disallow extra characters
    169 		allowSpace: true, // Allow the space character
    170 		allowNumeric: true, // Allow digits 0-9
    171 		allowUpper: true, // Allow upper case characters
    172 		allowLower: true, // Allow lower case characters
    173 		allowCaseless: true, // Allow characters that don't have both upper & lower variants - eg Arabic or Chinese
    174 		allowLatin: true, // a-z A-Z
    175 		allowOtherCharSets: true, // eg �, �, Arabic, Chinese etc
    176 		maxLength: NaN   // eg Max Length
    177 	}
    178 
    179 	var DEFAULT_SETTINGS_NUM = {
    180 		allowPlus: false, // Allow the + sign
    181 		allowMinus: true,  // Allow the - sign
    182 		allowThouSep: true,  // Allow the thousands separator, default is the comma eg 12,000
    183 		allowDecSep: true,  // Allow the decimal separator, default is the fullstop eg 3.141
    184 		allowLeadingSpaces: false, maxDigits: NaN,   // The max number of digits
    185 		maxDecimalPlaces: NaN,   // The max number of decimal places
    186 		maxPreDecimalPlaces: NaN,   // The max number digits before the decimal point
    187 		max: NaN,   // The max numeric value allowed
    188 		min: NaN    // The min numeric value allowed
    189 	}
    190 
    191 	// Some pre-defined groups of settings for convenience
    192 	var CONVENIENCE_SETTINGS_ALPHANUM = {
    193 		"alpha": {
    194 			allowNumeric: false
    195 		}, "upper": {
    196 			allowNumeric: false, allowUpper: true, allowLower: false, allowCaseless: true
    197 		}, "lower": {
    198 			allowNumeric: false, allowUpper: false, allowLower: true, allowCaseless: true
    199 		}
    200 	};
    201 
    202 	// Some pre-defined groups of settings for convenience
    203 	var CONVENIENCE_SETTINGS_NUMERIC = {
    204 		"integer": {
    205 			allowPlus: false, allowMinus: true, allowThouSep: false, allowDecSep: false
    206 		}, "positiveInteger": {
    207 			allowPlus: false, allowMinus: false, allowThouSep: false, allowDecSep: false
    208 		}
    209 	};
    210 
    211 	var BLACKLIST   = getBlacklistAscii() + getBlacklistNonAscii();
    212 	var THOU_SEP    = ",";
    213 	var DEC_SEP     = ".";
    214 	var DIGITS      = getDigitsMap();
    215 	var LATIN_CHARS = getLatinCharsSet();
    216 
    217 	// Return the blacklisted special chars that are encodable using 7-bit ascii
    218 	function getBlacklistAscii() {
    219 		var blacklist = '!@#$%^&*()+=[]\\\';,/{}|":<>?~`.-_';
    220 		blacklist += " "; // 'Space' is on the blacklist but can be enabled using the 'allowSpace' config entry
    221 		return blacklist;
    222 	}
    223 
    224 	// Return the blacklisted special chars that are NOT encodable using 7-bit ascii
    225 	// We want this .js file to be encoded using 7-bit ascii so it can reach the widest possible audience
    226 	// Higher order chars must be escaped eg "\xAC"
    227 	// Not too worried about comments containing higher order characters for now (let's wait and see if it becomes a problem)
    228 	function getBlacklistNonAscii() {
    229 		var blacklist = "\xAC"     // �
    230 			+ "\u20AC"   // �
    231 			+ "\xA3"     // �
    232 			+ "\xA6"     // �
    233 		;
    234 		return blacklist;
    235 	}
    236 
    237 	// End Settings ////////////////////////////////////////////////////////
    238 
    239 	// Implementation details go here ////////////////////////////////////////////////////////
    240 
    241 	function setupEventHandlers( $textboxes, trimFunction, settings ) {
    242 
    243 		$textboxes.each( function() {
    244 
    245 			var $textbox = $( this );
    246 
    247 			$textbox.on( "keyup change paste", function( e ) {
    248 
    249 				var pastedText = "";
    250 
    251 				if ( e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData ) pastedText = e.originalEvent.clipboardData.getData( "text/plain" )
    252 
    253 				// setTimeout is necessary for handling the 'paste' event
    254 				setTimeout( function() {
    255 					trimTextbox( $textbox, trimFunction, settings, pastedText );
    256 				}, 0 );
    257 			} );
    258 
    259 			$textbox.on( "keypress", function( e ) {
    260 
    261 				// Determine which key is pressed.
    262 				// If it's a control key, then allow the event's default action to occur eg backspace, tab
    263 				var charCode = ! e.charCode ? e.which : e.charCode;
    264 				if ( isControlKey( charCode ) || e.ctrlKey || e.metaKey ) // cmd on MacOS
    265 					return;
    266 
    267 				var newChar = String.fromCharCode( charCode );
    268 
    269 				// Determine if some text was selected / highlighted when the key was pressed
    270 				var selectionObject = $textbox.selection();
    271 				var start           = selectionObject.start;
    272 				var end             = selectionObject.end;
    273 
    274 				var textBeforeKeypress = $textbox.val();
    275 
    276 				// The new char may be inserted:
    277 				//  1) At the start
    278 				//  2) In the middle
    279 				//  3) At the end
    280 				//  4) User highlights some text and then presses a key which would replace the highlighted text
    281 				//
    282 				// Here we build the string that would result after the keypress.
    283 				// If the resulting string is invalid, we cancel the event.
    284 				// Unfortunately, it isn't enough to just check if the new char is valid because some chars
    285 				// are position sensitive eg the decimal point '.'' or the minus sign '-'' are only valid in certain positions.
    286 				var potentialTextAfterKeypress = textBeforeKeypress.substring( 0, start ) + newChar + textBeforeKeypress.substring( end );
    287 				var validatedText              = trimFunction( potentialTextAfterKeypress, settings );
    288 
    289 				// If the keypress would cause the textbox to contain invalid characters, then cancel the keypress event
    290 				if ( validatedText != potentialTextAfterKeypress ) e.preventDefault();
    291 			} );
    292 		} );
    293 
    294 	}
    295 
    296 	// Ensure the text is a valid number when focus leaves the textbox
    297 	// This catches the case where a user enters '-' or '.' without entering any digits
    298 	function numericField_Blur( inputBox, settings ) {
    299 		var fieldValueNumeric = parseFloat( $( inputBox ).val() );
    300 		var $inputBox         = $( inputBox );
    301 
    302 		if ( isNaN( fieldValueNumeric ) ) {
    303 			$inputBox.val( "" );
    304 			return;
    305 		}
    306 
    307 		if ( isNumeric( settings.min ) && fieldValueNumeric < settings.min ) $inputBox.val( "" );
    308 
    309 		if ( isNumeric( settings.max ) && fieldValueNumeric > settings.max ) $inputBox.val( "" );
    310 	}
    311 
    312 	function isNumeric( value ) {
    313 		return ! isNaN( value );
    314 	}
    315 
    316 	function isControlKey( charCode ) {
    317 
    318 		if ( charCode >= 32 ) return false;
    319 		if ( charCode == 10 ) return false;
    320 		if ( charCode == 13 ) return false;
    321 
    322 		return true;
    323 	}
    324 
    325 	// One way to prevent a character being entered is to cancel the keypress event.
    326 	// However, this gets messy when you have to deal with things like copy paste which isn't a keypress.
    327 	// Which event gets fired first, keypress or keyup? What about IE6 etc etc?
    328 	// Instead, it's easier to allow the 'bad' character to be entered and then to delete it immediately after.
    329 
    330 	function trimTextbox( $textBox, trimFunction, settings, pastedText ) {
    331 
    332 		var inputString = $textBox.val();
    333 
    334 		if ( inputString == "" && pastedText.length > 0 ) inputString = pastedText;
    335 
    336 		var outputString = trimFunction( inputString, settings );
    337 
    338 		if ( inputString == outputString ) return;
    339 
    340 		var caretPos = $textBox.alphanum_caret();
    341 
    342 		$textBox.val( outputString );
    343 
    344 		//Reset the caret position
    345 		if ( inputString.length == (outputString.length + 1) ) $textBox.alphanum_caret( caretPos - 1 ); else $textBox.alphanum_caret( caretPos );
    346 	}
    347 
    348 	function getCombinedSettingsAlphaNum( settings, defaultSettings ) {
    349 		if ( typeof defaultSettings == "undefined" ) defaultSettings = DEFAULT_SETTINGS_ALPHANUM;
    350 		var userSettings, combinedSettings = {};
    351 		if ( typeof settings === "string" ) userSettings = CONVENIENCE_SETTINGS_ALPHANUM[settings]; else if ( typeof settings == "undefined" ) userSettings = {}; else userSettings = settings;
    352 
    353 		$.extend( combinedSettings, defaultSettings, userSettings );
    354 
    355 		if ( typeof combinedSettings.blacklist == 'undefined' ) combinedSettings.blacklistSet = getBlacklistSet( combinedSettings.allow, combinedSettings.disallow );
    356 
    357 		return combinedSettings;
    358 	}
    359 
    360 	function getCombinedSettingsNum( settings ) {
    361 		var userSettings, combinedSettings = {};
    362 		if ( typeof settings === "string" ) userSettings = CONVENIENCE_SETTINGS_NUMERIC[settings]; else if ( typeof settings == "undefined" ) userSettings = {}; else userSettings = settings;
    363 
    364 		$.extend( combinedSettings, DEFAULT_SETTINGS_NUM, userSettings );
    365 
    366 		return combinedSettings;
    367 	}
    368 
    369 	// This is the heart of the algorithm
    370 	function alphanum_allowChar( validatedStringFragment, Char, settings ) {
    371 
    372 		if ( settings.maxLength && validatedStringFragment.length >= settings.maxLength ) return false;
    373 
    374 		if ( settings.allow.indexOf( Char ) >= 0 ) return true;
    375 
    376 		if ( settings.allowSpace && (Char == " ") ) return true;
    377 
    378 		if ( settings.blacklistSet.contains( Char ) ) return false;
    379 
    380 		if ( ! settings.allowNumeric && DIGITS[Char] ) return false;
    381 
    382 		if ( ! settings.allowUpper && isUpper( Char ) ) return false;
    383 
    384 		if ( ! settings.allowLower && isLower( Char ) ) return false;
    385 
    386 		if ( ! settings.allowCaseless && isCaseless( Char ) ) return false;
    387 
    388 		if ( ! settings.allowLatin && LATIN_CHARS.contains( Char ) ) return false;
    389 
    390 		if ( ! settings.allowOtherCharSets ) {
    391 			if ( DIGITS[Char] || LATIN_CHARS.contains( Char ) ) return true; else return false;
    392 		}
    393 
    394 		return true;
    395 	}
    396 
    397 	function numeric_allowChar( validatedStringFragment, Char, settings ) {
    398 
    399 		if ( DIGITS[Char] ) {
    400 
    401 			if ( isMaxDigitsReached( validatedStringFragment, settings ) ) return false;
    402 
    403 			if ( isMaxPreDecimalsReached( validatedStringFragment, settings ) ) return false;
    404 
    405 			if ( isMaxDecimalsReached( validatedStringFragment, settings ) ) return false;
    406 
    407 			if ( isGreaterThanMax( validatedStringFragment + Char, settings ) ) return false;
    408 
    409 			if ( isLessThanMin( validatedStringFragment + Char, settings ) ) return false;
    410 
    411 			return true;
    412 		}
    413 
    414 		if ( settings.allowPlus && Char == '+' && validatedStringFragment == '' ) return true;
    415 
    416 		if ( settings.allowMinus && Char == '-' && validatedStringFragment == '' ) return true;
    417 
    418 		if ( Char == THOU_SEP && settings.allowThouSep && allowThouSep( validatedStringFragment, Char ) ) return true;
    419 
    420 		if ( Char == DEC_SEP ) {
    421 			// Only one decimal separator allowed
    422 			if ( validatedStringFragment.indexOf( DEC_SEP ) >= 0 ) return false;
    423 			if ( settings.allowDecSep ) return true;
    424 		}
    425 
    426 		return false;
    427 	}
    428 
    429 	function countDigits( string ) {
    430 
    431 		// Error handling, nulls etc
    432 		string = string + "";
    433 
    434 		// Count the digits
    435 		return string.replace( /[^0-9]/g, "" ).length;
    436 	}
    437 
    438 	function isMaxDigitsReached( string, settings ) {
    439 
    440 		var maxDigits = settings.maxDigits;
    441 
    442 		if ( maxDigits == "" || isNaN( maxDigits ) ) return false; // In this case, there is no maximum
    443 
    444 		var numDigits = countDigits( string );
    445 
    446 		if ( numDigits >= maxDigits ) return true;
    447 
    448 		return false;
    449 	}
    450 
    451 	function isMaxDecimalsReached( string, settings ) {
    452 
    453 		var maxDecimalPlaces = settings.maxDecimalPlaces;
    454 
    455 		if ( maxDecimalPlaces == "" || isNaN( maxDecimalPlaces ) ) return false; // In this case, there is no maximum
    456 
    457 		var indexOfDecimalPoint = string.indexOf( DEC_SEP );
    458 
    459 		if ( indexOfDecimalPoint == - 1 ) return false;
    460 
    461 		var decimalSubstring = string.substring( indexOfDecimalPoint );
    462 		var numDecimals      = countDigits( decimalSubstring );
    463 
    464 		if ( numDecimals >= maxDecimalPlaces ) return true;
    465 
    466 		return false;
    467 	}
    468 
    469 	function isMaxPreDecimalsReached( string, settings ) {
    470 
    471 		var maxPreDecimalPlaces = settings.maxPreDecimalPlaces;
    472 
    473 		if ( maxPreDecimalPlaces == "" || isNaN( maxPreDecimalPlaces ) ) return false; // In this case, there is no maximum
    474 
    475 		var indexOfDecimalPoint = string.indexOf( DEC_SEP );
    476 
    477 		if ( indexOfDecimalPoint >= 0 ) return false;
    478 
    479 		var numPreDecimalDigits = countDigits( string );
    480 
    481 		if ( numPreDecimalDigits >= maxPreDecimalPlaces ) return true;
    482 
    483 		return false;
    484 	}
    485 
    486 	function isGreaterThanMax( numericString, settings ) {
    487 
    488 		if ( ! settings.max || settings.max < 0 ) return false;
    489 
    490 		var outputNumber = parseFloat( numericString );
    491 		if ( outputNumber > settings.max ) return true;
    492 
    493 		return false;
    494 	}
    495 
    496 	function isLessThanMin( numericString, settings ) {
    497 
    498 		if ( ! settings.min || settings.min > 0 ) return false;
    499 
    500 		var outputNumber = parseFloat( numericString );
    501 		if ( outputNumber < settings.min ) return true;
    502 
    503 		return false;
    504 	}
    505 
    506 	/********************************
    507 	 * Trims a string according to the settings provided
    508 	 ********************************/
    509 	function trimAlphaNum( inputString, settings ) {
    510 
    511 		if ( typeof inputString != "string" ) return inputString;
    512 
    513 		var inChars  = inputString.split( "" );
    514 		var outChars = [];
    515 		var i        = 0;
    516 		var Char;
    517 
    518 		for ( i = 0; i < inChars.length; i ++ ) {
    519 			Char                        = inChars[i];
    520 			var validatedStringFragment = outChars.join( "" );
    521 			if ( alphanum_allowChar( validatedStringFragment, Char, settings ) ) outChars.push( Char );
    522 		}
    523 
    524 		return outChars.join( "" );
    525 	}
    526 
    527 	function trimNum( inputString, settings ) {
    528 		if ( typeof inputString != "string" ) return inputString;
    529 
    530 		var inChars  = inputString.split( "" );
    531 		var outChars = [];
    532 		var i        = 0;
    533 		var Char;
    534 
    535 		for ( i = 0; i < inChars.length; i ++ ) {
    536 			Char                        = inChars[i];
    537 			var validatedStringFragment = outChars.join( "" );
    538 			if ( numeric_allowChar( validatedStringFragment, Char, settings ) ) outChars.push( Char );
    539 		}
    540 
    541 		return outChars.join( "" );
    542 	}
    543 
    544 	function removeUpperCase( inputString ) {
    545 		var charArray   = inputString.split( '' );
    546 		var i           = 0;
    547 		var outputArray = [];
    548 		var Char;
    549 
    550 		for ( i = 0; i < charArray.length; i ++ ) {
    551 			Char = charArray[i];
    552 		}
    553 	}
    554 
    555 	function removeLowerCase( inputString ) {
    556 
    557 	}
    558 
    559 	function isUpper( Char ) {
    560 		var upper = Char.toUpperCase();
    561 		var lower = Char.toLowerCase();
    562 
    563 		if ( (Char == upper) && (upper != lower) ) return true; else return false;
    564 	}
    565 
    566 	function isLower( Char ) {
    567 		var upper = Char.toUpperCase();
    568 		var lower = Char.toLowerCase();
    569 
    570 		if ( (Char == lower) && (upper != lower) ) return true; else return false;
    571 	}
    572 
    573 	function isCaseless( Char ) {
    574 		if ( Char.toUpperCase() == Char.toLowerCase() ) return true; else return false;
    575 	}
    576 
    577 	function getBlacklistSet( allow, disallow ) {
    578 
    579 		var setOfBadChars  = new Set( BLACKLIST + disallow );
    580 		var setOfGoodChars = new Set( allow );
    581 
    582 		var blacklistSet = setOfBadChars.subtract( setOfGoodChars );
    583 
    584 		return blacklistSet;
    585 	}
    586 
    587 	function getDigitsMap() {
    588 		var array = "0123456789".split( "" );
    589 		var map   = {};
    590 		var i     = 0;
    591 		var digit;
    592 
    593 		for ( i = 0; i < array.length; i ++ ) {
    594 			digit      = array[i];
    595 			map[digit] = true;
    596 		}
    597 
    598 		return map;
    599 	}
    600 
    601 	function getLatinCharsSet() {
    602 		var lower = "abcdefghijklmnopqrstuvwxyz";
    603 		var upper = lower.toUpperCase();
    604 		var azAZ  = new Set( lower + upper );
    605 
    606 		return azAZ;
    607 	}
    608 
    609 	function allowThouSep( currentString, Char ) {
    610 
    611 		// Can't start with a THOU_SEP
    612 		if ( currentString.length == 0 ) return false;
    613 
    614 		// Can't have a THOU_SEP anywhere after a DEC_SEP
    615 		var posOfDecSep = currentString.indexOf( DEC_SEP );
    616 		if ( posOfDecSep >= 0 ) return false;
    617 
    618 		var posOfFirstThouSep = currentString.indexOf( THOU_SEP );
    619 
    620 		// Check if this is the first occurrence of a THOU_SEP
    621 		if ( posOfFirstThouSep < 0 ) return true;
    622 
    623 		var posOfLastThouSep      = currentString.lastIndexOf( THOU_SEP );
    624 		var charsSinceLastThouSep = currentString.length - posOfLastThouSep - 1;
    625 
    626 		// Check if there has been 3 digits since the last THOU_SEP
    627 		if ( charsSinceLastThouSep < 3 ) return false;
    628 
    629 		var digitsSinceFirstThouSep = countDigits( currentString.substring( posOfFirstThouSep ) );
    630 
    631 		// Check if there has been a multiple of 3 digits since the first THOU_SEP
    632 		if ( (digitsSinceFirstThouSep % 3) > 0 ) return false;
    633 
    634 		return true;
    635 	}
    636 
    637 	////////////////////////////////////////////////////////////////////////////////////
    638 	// Implementation of a Set
    639 	////////////////////////////////////////////////////////////////////////////////////
    640 	function Set( elems ) {
    641 		if ( typeof elems == "string" ) this.map = stringToMap( elems ); else this.map = {};
    642 	}
    643 
    644 	Set.prototype.add = function( set ) {
    645 
    646 		var newSet = this.clone();
    647 
    648 		for ( var key in set.map ) newSet.map[key] = true;
    649 
    650 		return newSet;
    651 	}
    652 
    653 	Set.prototype.subtract = function( set ) {
    654 
    655 		var newSet = this.clone();
    656 
    657 		for ( var key in set.map ) delete newSet.map[key];
    658 
    659 		return newSet;
    660 	}
    661 
    662 	Set.prototype.contains = function( key ) {
    663 		if ( this.map[key] ) return true; else return false;
    664 	}
    665 
    666 	Set.prototype.clone = function() {
    667 		var newSet = new Set();
    668 
    669 		for ( var key in this.map ) newSet.map[key] = true;
    670 
    671 		return newSet;
    672 	}
    673 
    674 	////////////////////////////////////////////////////////////////////////////////////
    675 
    676 	function stringToMap( string ) {
    677 		var map   = {};
    678 		var array = string.split( "" );
    679 		var i     = 0;
    680 		var Char;
    681 
    682 		for ( i = 0; i < array.length; i ++ ) {
    683 			Char      = array[i];
    684 			map[Char] = true;
    685 		}
    686 
    687 		return map;
    688 	}
    689 
    690 	// Backdoor for testing
    691 	$.fn.alphanum.backdoorAlphaNum = function( inputString, settings ) {
    692 		var combinedSettings = getCombinedSettingsAlphaNum( settings );
    693 
    694 		return trimAlphaNum( inputString, combinedSettings );
    695 	};
    696 
    697 	$.fn.alphanum.backdoorNumeric = function( inputString, settings ) {
    698 		var combinedSettings = getCombinedSettingsNum( settings );
    699 
    700 		return trimNum( inputString, combinedSettings );
    701 	};
    702 
    703 	$.fn.alphanum.setNumericSeparators = function( settings ) {
    704 
    705 		if ( settings.thousandsSeparator.length != 1 ) return;
    706 
    707 		if ( settings.decimalSeparator.length != 1 ) return;
    708 
    709 		THOU_SEP = settings.thousandsSeparator;
    710 		DEC_SEP  = settings.decimalSeparator;
    711 	}
    712 
    713 })( jQuery );
    714 
    715 //Include the 3rd party lib: jquery.caret.js
    716 
    717 // Set caret position easily in jQuery
    718 // Written by and Copyright of Luke Morton, 2011
    719 // Licensed under MIT
    720 (function( $ ) {
    721 	// Behind the scenes method deals with browser
    722 	// idiosyncrasies and such
    723 	function caretTo( el, index ) {
    724 		if ( el.createTextRange ) {
    725 			var range = el.createTextRange();
    726 			range.move( "character", index );
    727 			range.select();
    728 		} else if ( el.selectionStart != null ) {
    729 			el.focus();
    730 			el.setSelectionRange( index, index );
    731 		}
    732 	};
    733 
    734 	// Another behind the scenes that collects the
    735 	// current caret position for an element
    736 
    737 	// TODO: Get working with Opera
    738 	function caretPos( el ) {
    739 		if ( "selection" in document ) {
    740 			var range = el.createTextRange();
    741 			try {
    742 				range.setEndPoint( "EndToStart", document.selection.createRange() );
    743 			} catch ( e ) {
    744 				// Catch IE failure here, return 0 like
    745 				// other browsers
    746 				return 0;
    747 			}
    748 			return range.text.length;
    749 		} else if ( el.selectionStart != null ) {
    750 			return el.selectionStart;
    751 		}
    752 	};
    753 
    754 	// The following methods are queued under fx for more
    755 	// flexibility when combining with $.fn.delay() and
    756 	// jQuery effects.
    757 
    758 	// Set caret to a particular index
    759 	$.fn.alphanum_caret = function( index, offset ) {
    760 		if ( typeof (index) === "undefined" ) {
    761 			return caretPos( this.get( 0 ) );
    762 		}
    763 
    764 		return this.queue( function( next ) {
    765 			if ( isNaN( index ) ) {
    766 				var i = $( this ).val().indexOf( index );
    767 
    768 				if ( offset === true ) {
    769 					i += index.length;
    770 				} else if ( typeof (offset) !== "undefined" ) {
    771 					i += offset;
    772 				}
    773 
    774 				caretTo( this, i );
    775 			} else {
    776 				caretTo( this, index );
    777 			}
    778 
    779 			next();
    780 		} );
    781 	};
    782 }( jQuery ));
    783 
    784 /**********************************************************
    785  * Selection Library
    786  * Used to determine what text is highlighted in the textbox before a key is pressed.
    787  * http://donejs.com/docs.html#!jQuery.fn.selection
    788  * https://github.com/jupiterjs/jquerymx/blob/master/dom/selection/selection.js
    789  ***********************************************************/
    790 (function( $ ) {
    791 	var convertType          = function( type ) {
    792 		    return type.replace( /([a-z])([a-z]+)/gi, function( all, first, next ) {
    793 			    return first + next.toLowerCase();
    794 		    } ).replace( /_/g, "" );
    795 	    }, reverse           = function( type ) {
    796 		    return type.replace( /^([a-z]+)_TO_([a-z]+)/i, function( all, first, last ) {
    797 			    return last + "_TO_" + first;
    798 		    } );
    799 	    }, getWindow         = function( element ) {
    800 		    return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window;
    801 	    }, // A helper that uses range to abstract out getting the current start and endPos.
    802 	    getElementsSelection = function( el, win ) {
    803 		    var current                                                = $.Range.current( el ).clone(), entireElement = $.Range( el ).select( el );
    804 
    805 		    if ( ! current.overlaps( entireElement ) ) {
    806 			    return null;
    807 		    }
    808 		    // we need to check if it starts before our element ...
    809 		    if ( current.compare( "START_TO_START", entireElement ) < 1 ) {
    810 			    var startPos = 0;
    811 			    // we should move current ...
    812 			    current.move( "START_TO_START", entireElement );
    813 		    } else {
    814 			    var fromElementToCurrent = entireElement.clone();
    815 			    fromElementToCurrent.move( "END_TO_START", current );
    816 
    817 			    startPos = fromElementToCurrent.toString().length;
    818 		    }
    819 
    820 		    // now we need to make sure current isn't to the right of us ...
    821 		    var endPos;
    822 
    823 		    if ( current.compare( "END_TO_END", entireElement ) >= 0 ) {
    824 			    endPos = entireElement.toString().length;
    825 		    } else {
    826 			    endPos = startPos + current.toString().length;
    827 		    }
    828 
    829 		    return {
    830 			    start: startPos, end: endPos
    831 		    };
    832 	    }, getSelection      = function( el ) {
    833 		    // use selectionStart if we can.
    834 		    var win = getWindow( el );
    835 
    836 		    if ( el.selectionStart !== undefined ) {
    837 			    if ( document.activeElement && document.activeElement !== el && el.selectionStart === el.selectionEnd && el.selectionStart === 0 ) {
    838 				    return {start: el.value.length, end: el.value.length};
    839 			    }
    840 
    841 			    return {start: el.selectionStart, end: el.selectionEnd};
    842 		    } else if ( win.getSelection ) {
    843 			    return getElementsSelection( el, win );
    844 		    } else {
    845 			    try {
    846 				    //try 2 different methods that work differently
    847 				    // one should only work for input elements, but sometimes doesn't
    848 				    // I don't know why this is, or what to detect
    849 				    if ( el.nodeName.toLowerCase() === 'input' ) {
    850 					    var real = getWindow( el ).document.selection.createRange(), r = el.createTextRange();
    851 
    852 					    r.setEndPoint( "EndToStart", real );
    853 
    854 					    var start = r.text.length;
    855 
    856 					    return {
    857 						    start: start, end: start + real.text.length
    858 					    };
    859 				    } else {
    860 					    var res = getElementsSelection( el, win );
    861 					    if ( ! res ) {
    862 						    return res;
    863 					    }
    864 
    865 					    // we have to clean up for ie's textareas
    866 					    var current                                 = $.Range.current().clone(), r2 = current.clone().collapse().range,
    867 					        r3                                      = current.clone().collapse( false ).range;
    868 
    869 					    r2.moveStart( 'character', - 1 );
    870 					    r3.moveStart( 'character', - 1 );
    871 
    872 					    // if we aren't at the start, but previous is empty, we are at start of newline
    873 					    if ( res.startPos !== 0 && r2.text === "" ) {
    874 						    res.startPos += 2;
    875 					    }
    876 
    877 					    // do a similar thing for the end of the textarea
    878 					    if ( res.endPos !== 0 && r3.text === "" ) {
    879 						    res.endPos += 2;
    880 					    }
    881 
    882 					    return res;
    883 				    }
    884 			    } catch ( e ) {
    885 				    return {start: el.value.length, end: el.value.length};
    886 			    }
    887 		    }
    888 	    }, select            = function( el, start, end ) {
    889 		    var win = getWindow( el );
    890 
    891 		    if ( el.setSelectionRange ) {
    892 			    if ( end === undefined ) {
    893 				    el.focus();
    894 				    el.setSelectionRange( start, start );
    895 			    } else {
    896 				    el.select();
    897 				    el.selectionStart = start;
    898 				    el.selectionEnd   = end;
    899 			    }
    900 		    } else if ( el.createTextRange ) {
    901 			    //el.focus();
    902 			    var r = el.createTextRange();
    903 			    r.moveStart( 'character', start );
    904 			    end = end || start;
    905 			    r.moveEnd( 'character', end - el.value.length );
    906 
    907 			    r.select();
    908 		    } else if ( win.getSelection ) {
    909 			    var doc                                                 = win.document, sel = win.getSelection(), range = doc.createRange(),
    910 			        ranges                                              = [start, end !== undefined ? end : start];
    911 			    getCharElement( [el], ranges );
    912 			    range.setStart( ranges[0].el, ranges[0].count );
    913 			    range.setEnd( ranges[1].el, ranges[1].count );
    914 
    915 			    // removeAllRanges is suprisingly necessary for webkit ... BOOO!
    916 			    sel.removeAllRanges();
    917 			    sel.addRange( range );
    918 
    919 		    } else if ( win.document.body.createTextRange ) { //IE's weirdness
    920 			    var range = document.body.createTextRange();
    921 
    922 			    range.moveToElementText( el );
    923 			    range.collapse();
    924 			    range.moveStart( 'character', start );
    925 			    range.moveEnd( 'character', end !== undefined ? end : start );
    926 			    range.select();
    927 		    }
    928 	    }, /*
    929      * If one of the range values is within start and len, replace the range
    930      * value with the element and its offset.
    931      */
    932 	    replaceWithLess      = function( start, len, range, el ) {
    933 		    if ( typeof range[0] === 'number' && range[0] < len ) {
    934 			    range[0] = {
    935 				    el: el, count: range[0] - start
    936 			    };
    937 		    }
    938 		    if ( typeof range[1] === 'number' && range[1] <= len ) {
    939 			    range[1] = {
    940 				    el: el, count: range[1] - start
    941 			    };
    942 		    }
    943 	    }, getCharElement    = function( elems, range, len ) {
    944 		    var elem, start;
    945 
    946 		    len = len || 0;
    947 
    948 		    for ( var i = 0; elems[i]; i ++ ) {
    949 			    elem = elems[i];
    950 			    // Get the text from text nodes and CDATA nodes
    951 			    if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
    952 				    start = len;
    953 				    len += elem.nodeValue.length;
    954 				    //check if len is now greater than what's in counts
    955 				    replaceWithLess( start, len, range, elem );
    956 				    // Traverse everything else, except comment nodes
    957 			    } else if ( elem.nodeType !== 8 ) {
    958 				    len = getCharElement( elem.childNodes, range, len );
    959 			    }
    960 		    }
    961 
    962 		    return len;
    963 	    };
    964 
    965 	$.fn.selection = function( start, end ) {
    966 		if ( start !== undefined ) {
    967 			return this.each( function() {
    968 				select( this, start, end );
    969 			} );
    970 		} else {
    971 			return getSelection( this[0] );
    972 		}
    973 	};
    974 
    975 	// for testing
    976 	$.fn.selection.getCharElement = getCharElement;
    977 })( jQuery );
    978 
    979 // jscs:disable
    980 // jshint ignore: start
    981 
    982 /*
    983  * serializeForm
    984  * https://github.com/danheberden/serializeForm
    985  *
    986  * Copyright (c) 2012 Dan Heberden
    987  * Licensed under the MIT, GPL licenses.
    988  */
    989 (function( $ ) {
    990 	$.fn.serializeForm = function() {
    991 
    992 		// don't do anything if we didn't get any elements.
    993 		if ( this.length < 1 ) {
    994 			return false;
    995 		}
    996 
    997 		var data     = {};
    998 		var lookup   = data; // current reference of data.
    999 		var selector = ':input[type!="checkbox"][type!="radio"], input:checked';
   1000 		var parse    = function() {
   1001 
   1002 			// Ignore disabled elements.
   1003 			if ( this.disabled ) {
   1004 				return;
   1005 			}
   1006 
   1007 			// data[a][b] becomes [ data, a, b ].
   1008 			var named = this.name.replace( /\[([^\]]+)?\]/g, ',$1' ).split( ',' );
   1009 			var cap   = named.length - 1;
   1010 			var $el   = $( this );
   1011 
   1012 			// Ensure that only elements with valid `name` properties will be serialized.
   1013 			if ( named[0] ) {
   1014 				for ( var i = 0; i < cap; i ++ ) {
   1015 					// move down the tree - create objects or array if necessary.
   1016 					lookup = lookup[named[i]] = lookup[named[i]] || ((named[i + 1] === "" || named[i + 1] === '0') ? [] : {});
   1017 				}
   1018 
   1019 				// at the end, push or assign the value.
   1020 				if ( lookup.length !== undefined ) {
   1021 					lookup.push( $el.val() );
   1022 				} else {
   1023 					lookup[named[cap]] = $el.val();
   1024 				}
   1025 
   1026 				// assign the reference back to root.
   1027 				lookup = data;
   1028 			}
   1029 		};
   1030 
   1031 		// first, check for elements passed into this function.
   1032 		this.filter( selector ).each( parse );
   1033 
   1034 		// then parse possible child elements.
   1035 		this.find( selector ).each( parse );
   1036 
   1037 		// return data.
   1038 		return data;
   1039 	};
   1040 }( jQuery ));
   1041 
   1042 // jscs:disable
   1043 // jshint ignore: start
   1044 
   1045 /*
   1046 *	TypeWatch 3
   1047 *
   1048 *	Examples/Docs: github.com/dennyferra/TypeWatch
   1049 *
   1050 *  Dual licensed under the MIT and GPL licenses:
   1051 *  http://www.opensource.org/licenses/mit-license.php
   1052 *  http://www.gnu.org/licenses/gpl.html
   1053 */
   1054 
   1055 !function(root, factory) {
   1056 	if (typeof define === 'function' && define.amd) {
   1057 		define(['jquery'], factory);
   1058 	} else if (typeof exports === 'object') {
   1059 		factory(require('jquery'));
   1060 	} else {
   1061 		factory(root.jQuery);
   1062 	}
   1063 }(this, function($) {
   1064 	'use strict';
   1065 	$.fn.typeWatch = function(o) {
   1066 		// The default input types that are supported
   1067 		var _supportedInputTypes =
   1068 			    ['TEXT', 'TEXTAREA', 'PASSWORD', 'TEL', 'SEARCH', 'URL', 'EMAIL', 'DATETIME', 'DATE', 'MONTH', 'WEEK', 'TIME', 'DATETIME-LOCAL', 'NUMBER', 'RANGE', 'DIV'];
   1069 
   1070 		// Options
   1071 		var options = $.extend({
   1072 			wait: 750,
   1073 			callback: function() { },
   1074 			highlight: true,
   1075 			captureLength: 2,
   1076 			allowSubmit: false,
   1077 			inputTypes: _supportedInputTypes
   1078 		}, o);
   1079 
   1080 		function checkElement(timer, override) {
   1081 			var value = timer.type === 'DIV'
   1082 				? jQuery(timer.el).html()
   1083 				: jQuery(timer.el).val();
   1084 
   1085 			// If has capture length and has changed value
   1086 			// Or override and has capture length or allowSubmit option is true
   1087 			// Or capture length is zero and changed value
   1088 			if ((value.length >= options.captureLength && value != timer.text)
   1089 				|| (override && (value.length >= options.captureLength || options.allowSubmit))
   1090 				|| (value.length == 0 && timer.text))
   1091 			{
   1092 				timer.text = value;
   1093 				timer.cb.call(timer.el, value);
   1094 			}
   1095 		};
   1096 
   1097 		function watchElement(elem) {
   1098 			var elementType = (elem.type || elem.nodeName).toUpperCase();
   1099 			if (jQuery.inArray(elementType, options.inputTypes) >= 0) {
   1100 
   1101 				// Allocate timer element
   1102 				var timer = {
   1103 					timer: null,
   1104 					text: (elementType === 'DIV') ? jQuery(elem).html() : jQuery(elem).val(),
   1105 					cb: options.callback,
   1106 					el: elem,
   1107 					type: elementType,
   1108 					wait: options.wait
   1109 				};
   1110 
   1111 				// Set focus action (highlight)
   1112 				if (options.highlight && elementType !== 'DIV')
   1113 					jQuery(elem).focus(function() { this.select(); });
   1114 
   1115 				// Key watcher / clear and reset the timer
   1116 				var startWatch = function(evt) {
   1117 					var timerWait = timer.wait;
   1118 					var overrideBool = false;
   1119 					var evtElementType = elementType;
   1120 
   1121 					// If enter key is pressed and not a TEXTAREA or DIV
   1122 					if (typeof evt.keyCode != 'undefined' && evt.keyCode == 13
   1123 						&& evtElementType !== 'TEXTAREA' && elementType !== 'DIV') {
   1124 						timerWait = 1;
   1125 						overrideBool = true;
   1126 					}
   1127 
   1128 					var timerCallbackFx = function() {
   1129 						checkElement(timer, overrideBool)
   1130 					}
   1131 
   1132 					// Clear timer
   1133 					clearTimeout(timer.timer);
   1134 					timer.timer = setTimeout(timerCallbackFx, timerWait);
   1135 				};
   1136 
   1137 				jQuery(elem).on('keydown paste cut input', startWatch);
   1138 			}
   1139 		};
   1140 
   1141 		// Watch each element
   1142 		return this.each(function() {
   1143 			watchElement(this);
   1144 		});
   1145 	};
   1146 });
   1147 
   1148 // jscs:disable
   1149 // jshint ignore: start
   1150 
   1151 /* global console, jsonView */
   1152 
   1153 /*
   1154  * ViewJSON
   1155  * Version 1.0
   1156  * A Google Chrome extension to display JSON in a user-friendly format
   1157  *
   1158  * This is a chromeified version of the JSONView Firefox extension by Ben Hollis:
   1159  * http://jsonview.com
   1160  * http://code.google.com/p/jsonview
   1161  *
   1162  * Also based on the XMLTree Chrome extension by Moonty & alan.stroop
   1163  * https://chrome.google.com/extensions/detail/gbammbheopgpmaagmckhpjbfgdfkpadb
   1164  *
   1165  * port by Jamie Wilkinson (@jamiew) | http://jamiedubs.com | http://github.com/jamiew
   1166  * MIT license / copyfree (f) F.A.T. Lab http://fffff.at
   1167  * Speed Project Approved: 2h
   1168  */
   1169 
   1170 function collapse( evt ) {
   1171 	var collapser = evt.target;
   1172 	var target    = collapser.parentNode.getElementsByClassName( 'collapsible' );
   1173 	if ( ! target.length ) {
   1174 		return;
   1175 	}
   1176 	target = target[0];
   1177 	if ( target.style.display === 'none' ) {
   1178 		var ellipsis = target.parentNode.getElementsByClassName( 'ellipsis' )[0];
   1179 		target.parentNode.removeChild( ellipsis );
   1180 		target.style.display = '';
   1181 	} else {
   1182 		target.style.display = 'none';
   1183 		var ellipsis         = document.createElement( 'span' );
   1184 		ellipsis.className   = 'ellipsis';
   1185 		ellipsis.innerHTML   = ' &hellip; ';
   1186 		target.parentNode.insertBefore( ellipsis, target );
   1187 	}
   1188 	collapser.innerHTML = (collapser.innerHTML === '-') ? '+' : '-';
   1189 }
   1190 
   1191 function addCollapser( item ) {
   1192 	// This mainly filters out the root object (which shouldn't be collapsible).
   1193 	if ( item.nodeName !== 'LI' ) {
   1194 		return;
   1195 	}
   1196 	var collapser       = document.createElement( 'div' );
   1197 	collapser.className = 'collapser';
   1198 	collapser.innerHTML = '-';
   1199 	collapser.addEventListener( 'click', collapse, false );
   1200 	item.insertBefore( collapser, item.firstChild );
   1201 }
   1202 
   1203 function jsonView( id, target ) {
   1204 	this.debug = false;
   1205 	if ( id.indexOf( '#' ) !== - 1 ) {
   1206 		this.idType = 'id';
   1207 		this.id     = id.replace( '#', '' );
   1208 	} else if ( id.indexOf( '.' ) !== - 1 ) {
   1209 		this.idType = 'class';
   1210 		this.id     = id.replace( '.', '' );
   1211 	} else {
   1212 		if ( this.debug ) {
   1213 			console.log( 'Can\'t find that element' );
   1214 		}
   1215 		return;
   1216 	}
   1217 
   1218 	this.data = document.getElementById( this.id ).innerHTML;
   1219 	if ( typeof (target) !== undefined ) {
   1220 		if ( target.indexOf( '#' ) !== - 1 ) {
   1221 			this.targetType = 'id';
   1222 			this.target     = target.replace( '#', '' );
   1223 		} else if ( id.indexOf( '.' ) !== - 1 ) {
   1224 			this.targetType = 'class';
   1225 			this.target     = target.replace( '.', '' );
   1226 		} else {
   1227 			if ( this.debug ) {
   1228 				console.log( 'Can\'t find the target element' );
   1229 			}
   1230 			return;
   1231 		}
   1232 	}
   1233 	// Note: now using "*.json*" URI matching rather than these page regexes -- save CPU cycles!
   1234 	// var is_json = /^\s*(\{.*\})\s*$/.test(this.data);
   1235 	// var is_jsonp = /^.*\(\s*(\{.*\})\s*\)$/.test(this.data);
   1236 	// if(is_json || is_jsonp){
   1237 	// Our manifest specifies that we only do URLs matching '.json', so attempt to sanitize any HTML
   1238 	// added by Chrome's "text/plain" or "text/html" handlers.
   1239 	if ( /^\<pre.*\>(.*)\<\/pre\>$/.test( this.data ) ) {
   1240 		if ( this.debug ) {
   1241 			console.log( 'JSONView: data is wrapped in <pre>...</pre>, stripping HTML...' );
   1242 		}
   1243 		this.data = this.data.replace( /<(?:.|\s)*?>/g, '' ); // Aggressively strip HTML.
   1244 	}
   1245 	// Test if what remains is JSON or JSONp.
   1246 	var json_regex   = /^\s*([\[\{].*[\}\]])\s*$/; // Ghetto, but it works.
   1247 	var jsonp_regex  = /^[\s\u200B\uFEFF]*([\w$\[\]\.]+)[\s\u200B\uFEFF]*\([\s\u200B\uFEFF]*([\[{][\s\S]*[\]}])[\s\u200B\uFEFF]*\);?[\s\u200B\uFEFF]*$/;
   1248 	var jsonp_regex2 = /([\[\{][\s\S]*[\]\}])\)/; // more liberal support... this allows us to pass the jsonp.json & jsonp2.json tests.
   1249 	var is_json      = json_regex.test( this.data );
   1250 	var is_jsonp     = jsonp_regex.test( this.data );
   1251 	if ( this.debug ) {
   1252 		console.log( 'JSONView: is_json=' + is_json + ' is_jsonp=' + is_jsonp );
   1253 	}
   1254 	if ( is_json || is_jsonp ) {
   1255 		if ( this.debug ) {
   1256 			console.log( 'JSONView: sexytime!' );
   1257 		}
   1258 		// JSONFormatter json->HTML prototype straight from Firefox JSONView
   1259 		// For reference: http://code.google.com/p/jsonview.
   1260 		function JSONFormatter() {
   1261 			// No magic required.
   1262 		}
   1263 
   1264 		JSONFormatter.prototype = {
   1265 			htmlEncode: function( t ) {
   1266 				return t != null ? t.toString().replace( /&/g, '&amp;' ).replace( /"/g, '&quot;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' ) : '';
   1267 			}, decorateWithSpan: function( value, className ) {
   1268 				return '<span class="' + className + '">' + this.htmlEncode( value ) + '</span>';
   1269 			}, // Convert a basic JSON datatype (number, string, boolean, null, object, array) into an HTML fragment.
   1270 			valueToHTML: function( value ) {
   1271 				var valueType = typeof value;
   1272 				var output    = '';
   1273 				if ( value === null ) {
   1274 					output += this.decorateWithSpan( 'null', 'null' );
   1275 				} else if ( value && value.constructor === Array ) {
   1276 					output += this.arrayToHTML( value );
   1277 				} else if ( valueType === 'object' ) {
   1278 					output += this.objectToHTML( value );
   1279 				} else if ( valueType === 'number' ) {
   1280 					output += this.decorateWithSpan( value, 'num' );
   1281 				} else if ( valueType === 'string' ) {
   1282 					if ( /^(http|https):\/\/[^\s]+$/.test( value ) ) {
   1283 						output += '<a href="' + value + '">' + this.htmlEncode( value ) + '</a>';
   1284 					} else {
   1285 						output += this.decorateWithSpan( '"' + value + '"', 'string' );
   1286 					}
   1287 				} else if ( valueType === 'boolean' ) {
   1288 					output += this.decorateWithSpan( value, 'bool' );
   1289 				}
   1290 				return output;
   1291 			}, // Convert an array into an HTML fragment
   1292 			arrayToHTML: function( json ) {
   1293 				var output      = '[<ul class="array collapsible">';
   1294 				var hasContents = false;
   1295 				for ( var prop in json ) {
   1296 					hasContents = true;
   1297 					output     += '<li>';
   1298 					output     += this.valueToHTML( json[prop] );
   1299 					output     += '</li>';
   1300 				}
   1301 				output += '</ul>]';
   1302 				if ( ! hasContents ) {
   1303 					output = '[ ]';
   1304 				}
   1305 				return output;
   1306 			}, // Convert a JSON object to an HTML fragment
   1307 			objectToHTML: function( json ) {
   1308 				var output      = '{<ul class="obj collapsible">';
   1309 				var hasContents = false;
   1310 				for ( var prop in json ) {
   1311 					hasContents = true;
   1312 					output     += '<li>';
   1313 					output     += '<span class="prop">' + this.htmlEncode( prop ) + '</span>: ';
   1314 					output     += this.valueToHTML( json[prop] );
   1315 					output     += '</li>';
   1316 				}
   1317 				output += '</ul>}';
   1318 				if ( ! hasContents ) {
   1319 					output = '{ }';
   1320 				}
   1321 				return output;
   1322 			}, // Convert a whole JSON object into a formatted HTML document.
   1323 			jsonToHTML: function( json, callback, uri ) {
   1324 				var output = '';
   1325 				if ( callback ) {
   1326 					output += '<div class="callback">' + callback + ' (</div>';
   1327 					output += '<div id="json">';
   1328 				} else {
   1329 					output += '<div id="json">';
   1330 				}
   1331 				output += this.valueToHTML( json );
   1332 				output += '</div>';
   1333 				if ( callback ) {
   1334 					output += '<div class="callback">)</div>';
   1335 				}
   1336 				return this.toHTML( output, uri );
   1337 			}, // Produce an error document for when parsing fails.
   1338 			errorPage: function( error, data, uri ) {
   1339 				// var output = '<div id="error">' + this.stringbundle.GetStringFromName('errorParsing') + '</div>';
   1340 				// output += '<h1>' + this.stringbundle.GetStringFromName('docContents') + ':</h1>';.
   1341 				var output = '<div id="error">Error parsing JSON: ' + error.message + '</div>';
   1342 				output    += '<h1>' + error.stack + ':</h1>';
   1343 				output    += '<div id="json">' + this.htmlEncode( data ) + '</div>';
   1344 				return this.toHTML( output, uri + ' - Error' );
   1345 			}, // Wrap the HTML fragment in a full document. Used by jsonToHTML and errorPage.
   1346 			toHTML: function( content ) {
   1347 				return content;
   1348 			}
   1349 		};
   1350 		// Sanitize & output -- all magic from JSONView Firefox.
   1351 		this.jsonFormatter = new JSONFormatter();
   1352 		// This regex attempts to match a JSONP structure:
   1353 		// * Any amount of whitespace (including unicode nonbreaking spaces) between the start of the file and the callback name.
   1354 		// * Callback name (any valid JavaScript function name according to ECMA-262 Edition 3 spec).
   1355 		// * Any amount of whitespace (including unicode nonbreaking spaces).
   1356 		// * Open parentheses.
   1357 		// * Any amount of whitespace (including unicode nonbreaking spaces).
   1358 		// * Either { or [, the only two valid characters to start a JSON string.
   1359 		// * Any character, any number of times.
   1360 		// * Either } or ], the only two valid closing characters of a JSON string.
   1361 		// * Any amount of whitespace (including unicode nonbreaking spaces).
   1362 		// * A closing parenthesis, an optional semicolon, and any amount of whitespace (including unicode nonbreaking spaces) until the end of the file.
   1363 		// This will miss anything that has comments, or more than one callback, or requires modification before use.
   1364 		var outputDoc = '';
   1365 		// text = text.match(jsonp_regex)[1]; .
   1366 		var cleanData        = '', callback = '';
   1367 		var callback_results = jsonp_regex.exec( this.data );
   1368 		if ( callback_results && callback_results.length === 3 ) {
   1369 			if ( this.debug ) {
   1370 				console.log( 'THIS IS JSONp' );
   1371 			}
   1372 			callback  = callback_results[1];
   1373 			cleanData = callback_results[2];
   1374 		} else {
   1375 			if ( this.debug ) {
   1376 				console.log( 'Vanilla JSON' );
   1377 			}
   1378 			cleanData = this.data;
   1379 		}
   1380 		if ( this.debug ) {
   1381 			console.log( cleanData );
   1382 		}
   1383 		// Covert, and catch exceptions on failure.
   1384 		try {
   1385 			// var jsonObj = this.nativeJSON.decode(cleanData); .
   1386 			var jsonObj = JSON.parse( cleanData );
   1387 			if ( jsonObj ) {
   1388 				outputDoc = this.jsonFormatter.jsonToHTML( jsonObj, callback );
   1389 			} else {
   1390 				throw 'There was no object!';
   1391 			}
   1392 		} catch ( e ) {
   1393 			if ( this.debug ) {
   1394 				console.log( e );
   1395 			}
   1396 			outputDoc = this.jsonFormatter.errorPage( e, this.data );
   1397 		}
   1398 		var links = '<style type="text/css">.jsonViewOutput .prop{font-weight:700;}.jsonViewOutput .null{color:red;}.jsonViewOutput .string{color:green;}.jsonViewOutput .collapser{position:absolute;left:-1em;cursor:pointer;}.jsonViewOutput li{position:relative;}.jsonViewOutput li:after{content:\',\';}.jsonViewOutput li:last-child:after{content:\'\';}.jsonViewOutput #error{-moz-border-radius:8px;border:1px solid #970000;background-color:#F7E8E8;margin:.5em;padding:.5em;}.jsonViewOutput .errormessage{font-family:monospace;}.jsonViewOutput #json{font-family:monospace;font-size:1.1em;}.jsonViewOutput ul{list-style:none;margin:0 0 0 2em;padding:0;}.jsonViewOutput h1{font-size:1.2em;}.jsonViewOutput .callback + #json{padding-left:1em;}.jsonViewOutput .callback{font-family:monospace;color:#A52A2A;}.jsonViewOutput .bool,.jsonViewOutput .num{color:blue;}</style>';
   1399 		if ( this.targetType !== undefined ) {
   1400 			this.idType = this.targetType;
   1401 			this.id     = this.target;
   1402 		}
   1403 		var el;
   1404 		if ( this.idType === 'class' ) {
   1405 			el = document.getElementsByClassName( this.id );
   1406 			if ( el ) {
   1407 				el.className += el.className ? ' jsonViewOutput' : 'jsonViewOutput';
   1408 				el.innerHTML  = links + outputDoc;
   1409 			}
   1410 		} else if ( this.idType === 'id' ) {
   1411 			el = document.getElementById( this.id );
   1412 			if ( el ) {
   1413 				el.className += el.className ? ' jsonViewOutput' : 'jsonViewOutput';
   1414 				el.innerHTML  = links + outputDoc;
   1415 			}
   1416 			el.innerHTML = links + outputDoc;
   1417 		}
   1418 		var items = document.getElementsByClassName( 'collapsible' );
   1419 		var len   = items.length;
   1420 
   1421 		for ( var i = 0; i < len; i ++ ) {
   1422 			addCollapser( items[i].parentNode );
   1423 		}
   1424 	} else {
   1425 		// console.log("JSONView: this is not json, not formatting."); .
   1426 	}
   1427 }