text: /*!
   * jQuery JavaScript Library v2.2.3
   * http://jquery.com/
   *
   * Includes Sizzle.js
   * http://sizzlejs.com/
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license
   * http://jquery.org/license
   *
   * Date: 2016-04-05T19:26Z
   */
  
  (function( global, factory ) {
  
  	if ( typeof module === "object" && typeof module.exports === "object" ) {
  		// For CommonJS and CommonJS-like environments where a proper `window`
  		// is present, execute the factory and get jQuery.
  		// For environments that do not have a `window` with a `document`
  		// (such as Node.js), expose a factory as module.exports.
  		// This accentuates the need for the creation of a real `window`.
  		// e.g. var jQuery = require("jquery")(window);
  		// See ticket #14549 for more info.
  		module.exports = global.document ?
  			factory( global, true ) :
  			function( w ) {
  				if ( !w.document ) {
  					throw new Error( "jQuery requires a window with a document" );
  				}
  				return factory( w );
  			};
  	} else {
  		factory( global );
  	}
  
  // Pass this if window is not defined yet
  }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
  
  // Support: Firefox 18+
  // Can't be in strict mode, several libs including ASP.NET trace
  // the stack via arguments.caller.callee and Firefox dies if
  // you try to trace through "use strict" call chains. (#13335)
  //"use strict";
  var arr = [];
  
  var document = window.document;
  
  var slice = arr.slice;
  
  var concat = arr.concat;
  
  var push = arr.push;
  
  var indexOf = arr.indexOf;
  
  var class2type = {};
  
  var toString = class2type.toString;
  
  var hasOwn = class2type.hasOwnProperty;
  
  var support = {};
  
  
  
  var
  	version = "2.2.3",
  
  	// Define a local copy of jQuery
  	jQuery = function( selector, context ) {
  
  		// The jQuery object is actually just the init constructor 'enhanced'
  		// Need init if jQuery is called (just allow error to be thrown if not included)
  		return new jQuery.fn.init( selector, context );
  	},
  
  	// Support: Android<4.1
  	// Make sure we trim BOM and NBSP
  	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
  
  	// Matches dashed string for camelizing
  	rmsPrefix = /^-ms-/,
  	rdashAlpha = /-([\da-z])/gi,
  
  	// Used by jQuery.camelCase as callback to replace()
  	fcamelCase = function( all, letter ) {
  		return letter.toUpperCase();
  	};
  
  jQuery.fn = jQuery.prototype = {
  
  	// The current version of jQuery being used
  	jquery: version,
  
  	constructor: jQuery,
  
  	// Start with an empty selector
  	selector: "",
  
  	// The default length of a jQuery object is 0
  	length: 0,
  
  	toArray: function() {
  		return slice.call( this );
  	},
  
  	// Get the Nth element in the matched element set OR
  	// Get the whole matched element set as a clean array
  	get: function( num ) {
  		return num != null ?
  
  			// Return just the one element from the set
  			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
  
  			// Return all the elements in a clean array
  			slice.call( this );
  	},
  
  	// Take an array of elements and push it onto the stack
  	// (returning the new matched element set)
  	pushStack: function( elems ) {
  
  		// Build a new jQuery matched element set
  		var ret = jQuery.merge( this.constructor(), elems );
  
  		// Add the old object onto the stack (as a reference)
  		ret.prevObject = this;
  		ret.context = this.context;
  
  		// Return the newly-formed element set
  		return ret;
  	},
  
  	// Execute a callback for every element in the matched set.
  	each: function( callback ) {
  		return jQuery.each( this, callback );
  	},
  
  	map: function( callback ) {
  		return this.pushStack( jQuery.map( this, function( elem, i ) {
  			return callback.call( elem, i, elem );
  		} ) );
  	},
  
  	slice: function() {
  		return this.pushStack( slice.apply( this, arguments ) );
  	},
  
  	first: function() {
  		return this.eq( 0 );
  	},
  
  	last: function() {
  		return this.eq( -1 );
  	},
  
  	eq: function( i ) {
  		var len = this.length,
  			j = +i + ( i < 0 ? len : 0 );
  		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
  	},
  
  	end: function() {
  		return this.prevObject || this.constructor();
  	},
  
  	// For internal use only.
  	// Behaves like an Array's method, not like a jQuery method.
  	push: push,
  	sort: arr.sort,
  	splice: arr.splice
  };
  
  jQuery.extend = jQuery.fn.extend = function() {
  	var options, name, src, copy, copyIsArray, clone,
  		target = arguments[ 0 ] || {},
  		i = 1,
  		length = arguments.length,
  		deep = false;
  
  	// Handle a deep copy situation
  	if ( typeof target === "boolean" ) {
  		deep = target;
  
  		// Skip the boolean and the target
  		target = arguments[ i ] || {};
  		i++;
  	}
  
  	// Handle case when target is a string or something (possible in deep copy)
  	if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
  		target = {};
  	}
  
  	// Extend jQuery itself if only one argument is passed
  	if ( i === length ) {
  		target = this;
  		i--;
  	}
  
  	for ( ; i < length; i++ ) {
  
  		// Only deal with non-null/undefined values
  		if ( ( options = arguments[ i ] ) != null ) {
  
  			// Extend the base object
  			for ( name in options ) {
  				src = target[ name ];
  				copy = options[ name ];
  
  				// Prevent never-ending loop
  				if ( target === copy ) {
  					continue;
  				}
  
  				// Recurse if we're merging plain objects or arrays
  				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
  					( copyIsArray = jQuery.isArray( copy ) ) ) ) {
  
  					if ( copyIsArray ) {
  						copyIsArray = false;
  						clone = src && jQuery.isArray( src ) ? src : [];
  
  					} else {
  						clone = src && jQuery.isPlainObject( src ) ? src : {};
  					}
  
  					// Never move original objects, clone them
  					target[ name ] = jQuery.extend( deep, clone, copy );
  
  				// Don't bring in undefined values
  				} else if ( copy !== undefined ) {
  					target[ name ] = copy;
  				}
  			}
  		}
  	}
  
  	// Return the modified object
  	return target;
  };
  
  jQuery.extend( {
  
  	// Unique for each copy of jQuery on the page
  	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
  
  	// Assume jQuery is ready without the ready module
  	isReady: true,
  
  	error: function( msg ) {
  		throw new Error( msg );
  	},
  
  	noop: function() {},
  
  	isFunction: function( obj ) {
  		return jQuery.type( obj ) === "function";
  	},
  
  	isArray: Array.isArray,
  
  	isWindow: function( obj ) {
  		return obj != null && obj === obj.window;
  	},
  
  	isNumeric: function( obj ) {
  
  		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
  		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
  		// subtraction forces infinities to NaN
  		// adding 1 corrects loss of precision from parseFloat (#15100)
  		var realStringObj = obj && obj.toString();
  		return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
  	},
  
  	isPlainObject: function( obj ) {
  		var key;
  
  		// Not plain objects:
  		// - Any object or value whose internal [[Class]] property is not "[object Object]"
  		// - DOM nodes
  		// - window
  		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
  			return false;
  		}
  
  		// Not own constructor property must be Object
  		if ( obj.constructor &&
  				!hasOwn.call( obj, "constructor" ) &&
  				!hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) {
  			return false;
  		}
  
  		// Own properties are enumerated firstly, so to speed up,
  		// if last one is own, then all properties are own
  		for ( key in obj ) {}
  
  		return key === undefined || hasOwn.call( obj, key );
  	},
  
  	isEmptyObject: function( obj ) {
  		var name;
  		for ( name in obj ) {
  			return false;
  		}
  		return true;
  	},
  
  	type: function( obj ) {
  		if ( obj == null ) {
  			return obj + "";
  		}
  
  		// Support: Android<4.0, iOS<6 (functionish RegExp)
  		return typeof obj === "object" || typeof obj === "function" ?
  			class2type[ toString.call( obj ) ] || "object" :
  			typeof obj;
  	},
  
  	// Evaluates a script in a global context
  	globalEval: function( code ) {
  		var script,
  			indirect = eval;
  
  		code = jQuery.trim( code );
  
  		if ( code ) {
  
  			// If the code includes a valid, prologue position
  			// strict mode pragma, execute code by injecting a
  			// script tag into the document.
  			if ( code.indexOf( "use strict" ) === 1 ) {
  				script = document.createElement( "script" );
  				script.text = code;
  				document.head.appendChild( script ).parentNode.removeChild( script );
  			} else {
  
  				// Otherwise, avoid the DOM node creation, insertion
  				// and removal by using an indirect global eval
  
  				indirect( code );
  			}
  		}
  	},
  
  	// Convert dashed to camelCase; used by the css and data modules
  	// Support: IE9-11+
  	// Microsoft forgot to hump their vendor prefix (#9572)
  	camelCase: function( string ) {
  		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
  	},
  
  	nodeName: function( elem, name ) {
  		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
  	},
  
  	each: function( obj, callback ) {
  		var length, i = 0;
  
  		if ( isArrayLike( obj ) ) {
  			length = obj.length;
  			for ( ; i < length; i++ ) {
  				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
  					break;
  				}
  			}
  		} else {
  			for ( i in obj ) {
  				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
  					break;
  				}
  			}
  		}
  
  		return obj;
  	},
  
  	// Support: Android<4.1
  	trim: function( text ) {
  		return text == null ?
  			"" :
  			( text + "" ).replace( rtrim, "" );
  	},
  
  	// results is for internal usage only
  	makeArray: function( arr, results ) {
  		var ret = results || [];
  
  		if ( arr != null ) {
  			if ( isArrayLike( Object( arr ) ) ) {
  				jQuery.merge( ret,
  					typeof arr === "string" ?
  					[ arr ] : arr
  				);
  			} else {
  				push.call( ret, arr );
  			}
  		}
  
  		return ret;
  	},
  
  	inArray: function( elem, arr, i ) {
  		return arr == null ? -1 : indexOf.call( arr, elem, i );
  	},
  
  	merge: function( first, second ) {
  		var len = +second.length,
  			j = 0,
  			i = first.length;
  
  		for ( ; j < len; j++ ) {
  			first[ i++ ] = second[ j ];
  		}
  
  		first.length = i;
  
  		return first;
  	},
  
  	grep: function( elems, callback, invert ) {
  		var callbackInverse,
  			matches = [],
  			i = 0,
  			length = elems.length,
  			callbackExpect = !invert;
  
  		// Go through the array, only saving the items
  		// that pass the validator function
  		for ( ; i < length; i++ ) {
  			callbackInverse = !callback( elems[ i ], i );
  			if ( callbackInverse !== callbackExpect ) {
  				matches.push( elems[ i ] );
  			}
  		}
  
  		return matches;
  	},
  
  	// arg is for internal usage only
  	map: function( elems, callback, arg ) {
  		var length, value,
  			i = 0,
  			ret = [];
  
  		// Go through the array, translating each of the items to their new values
  		if ( isArrayLike( elems ) ) {
  			length = elems.length;
  			for ( ; i < length; i++ ) {
  				value = callback( elems[ i ], i, arg );
  
  				if ( value != null ) {
  					ret.push( value );
  				}
  			}
  
  		// Go through every key on the object,
  		} else {
  			for ( i in elems ) {
  				value = callback( elems[ i ], i, arg );
  
  				if ( value != null ) {
  					ret.push( value );
  				}
  			}
  		}
  
  		// Flatten any nested arrays
  		return concat.apply( [], ret );
  	},
  
  	// A global GUID counter for objects
  	guid: 1,
  
  	// Bind a function to a context, optionally partially applying any
  	// arguments.
  	proxy: function( fn, context ) {
  		var tmp, args, proxy;
  
  		if ( typeof context === "string" ) {
  			tmp = fn[ context ];
  			context = fn;
  			fn = tmp;
  		}
  
  		// Quick check to determine if target is callable, in the spec
  		// this throws a TypeError, but we will just return undefined.
  		if ( !jQuery.isFunction( fn ) ) {
  			return undefined;
  		}
  
  		// Simulated bind
  		args = slice.call( arguments, 2 );
  		proxy = function() {
  			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
  		};
  
  		// Set the guid of unique handler to the same of original handler, so it can be removed
  		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
  
  		return proxy;
  	},
  
  	now: Date.now,
  
  	// jQuery.support is not used in Core but other projects attach their
  	// properties to it so it needs to exist.
  	support: support
  } );
  
  // JSHint would error on this code due to the Symbol not being defined in ES5.
  // Defining this global in .jshintrc would create a danger of using the global
  // unguarded in another place, it seems safer to just disable JSHint for these
  // three lines.
  /* jshint ignore: start */
  if ( typeof Symbol === "function" ) {
  	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
  }
  /* jshint ignore: end */
  
  // Populate the class2type map
  jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
  function( i, name ) {
  	class2type[ "[object " + name + "]" ] = name.toLowerCase();
  } );
  
  function isArrayLike( obj ) {
  
  	// Support: iOS 8.2 (not reproducible in simulator)
  	// `in` check used to prevent JIT error (gh-2145)
  	// hasOwn isn't used here due to false negatives
  	// regarding Nodelist length in IE
  	var length = !!obj && "length" in obj && obj.length,
  		type = jQuery.type( obj );
  
  	if ( type === "function" || jQuery.isWindow( obj ) ) {
  		return false;
  	}
  
  	return type === "array" || length === 0 ||
  		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
  }
  var Sizzle =
  /*!
   * Sizzle CSS Selector Engine v2.2.1
   * http://sizzlejs.com/
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license
   * http://jquery.org/license
   *
   * Date: 2015-10-17
   */
  (function( window ) {
  
  var i,
  	support,
  	Expr,
  	getText,
  	isXML,
  	tokenize,
  	compile,
  	select,
  	outermostContext,
  	sortInput,
  	hasDuplicate,
  
  	// Local document vars
  	setDocument,
  	document,
  	docElem,
  	documentIsHTML,
  	rbuggyQSA,
  	rbuggyMatches,
  	matches,
  	contains,
  
  	// Instance-specific data
  	expando = "sizzle" + 1 * new Date(),
  	preferredDoc = window.document,
  	dirruns = 0,
  	done = 0,
  	classCache = createCache(),
  	tokenCache = createCache(),
  	compilerCache = createCache(),
  	sortOrder = function( a, b ) {
  		if ( a === b ) {
  			hasDuplicate = true;
  		}
  		return 0;
  	},
  
  	// General-purpose constants
  	MAX_NEGATIVE = 1 << 31,
  
  	// Instance methods
  	hasOwn = ({}).hasOwnProperty,
  	arr = [],
  	pop = arr.pop,
  	push_native = arr.push,
  	push = arr.push,
  	slice = arr.slice,
  	// Use a stripped-down indexOf as it's faster than native
  	// http://jsperf.com/thor-indexof-vs-for/5
  	indexOf = function( list, elem ) {
  		var i = 0,
  			len = list.length;
  		for ( ; i < len; i++ ) {
  			if ( list[i] === elem ) {
  				return i;
  			}
  		}
  		return -1;
  	},
  
  	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
  
  	// Regular expressions
  
  	// http://www.w3.org/TR/css3-selectors/#whitespace
  	whitespace = "[\\x20\\t\\r\\n\\f]",
  
  	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
  	identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
  
  	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
  	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
  		// Operator (capture 2)
  		"*([*^$|!~]?=)" + whitespace +
  		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
  		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
  		"*\\]",
  
  	pseudos = ":(" + identifier + ")(?:\\((" +
  		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
  		// 1. quoted (capture 3; capture 4 or capture 5)
  		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
  		// 2. simple (capture 6)
  		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
  		// 3. anything else (capture 2)
  		".*" +
  		")\\)|)",
  
  	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
  	rwhitespace = new RegExp( whitespace + "+", "g" ),
  	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
  
  	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
  	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
  
  	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
  
  	rpseudo = new RegExp( pseudos ),
  	ridentifier = new RegExp( "^" + identifier + "$" ),
  
  	matchExpr = {
  		"ID": new RegExp( "^#(" + identifier + ")" ),
  		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
  		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
  		"ATTR": new RegExp( "^" + attributes ),
  		"PSEUDO": new RegExp( "^" + pseudos ),
  		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
  			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
  			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
  		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
  		// For use in libraries implementing .is()
  		// We use this for POS matching in `select`
  		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
  			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
  	},
  
  	rinputs = /^(?:input|select|textarea|button)$/i,
  	rheader = /^h\d$/i,
  
  	rnative = /^[^{]+\{\s*\[native \w/,
  
  	// Easily-parseable/retrievable ID or TAG or CLASS selectors
  	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
  
  	rsibling = /[+~]/,
  	rescape = /'|\\/g,
  
  	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
  	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
  	funescape = function( _, escaped, escapedWhitespace ) {
  		var high = "0x" + escaped - 0x10000;
  		// NaN means non-codepoint
  		// Support: Firefox<24
  		// Workaround erroneous numeric interpretation of +"0x"
  		return high !== high || escapedWhitespace ?
  			escaped :
  			high < 0 ?
  				// BMP codepoint
  				String.fromCharCode( high + 0x10000 ) :
  				// Supplemental Plane codepoint (surrogate pair)
  				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
  	},
  
  	// Used for iframes
  	// See setDocument()
  	// Removing the function wrapper causes a "Permission Denied"
  	// error in IE
  	unloadHandler = function() {
  		setDocument();
  	};
  
  // Optimize for push.apply( _, NodeList )
  try {
  	push.apply(
  		(arr = slice.call( preferredDoc.childNodes )),
  		preferredDoc.childNodes
  	);
  	// Support: Android<4.0
  	// Detect silently failing push.apply
  	arr[ preferredDoc.childNodes.length ].nodeType;
  } catch ( e ) {
  	push = { apply: arr.length ?
  
  		// Leverage slice if possible
  		function( target, els ) {
  			push_native.apply( target, slice.call(els) );
  		} :
  
  		// Support: IE<9
  		// Otherwise append directly
  		function( target, els ) {
  			var j = target.length,
  				i = 0;
  			// Can't trust NodeList.length
  			while ( (target[j++] = els[i++]) ) {}
  			target.length = j - 1;
  		}
  	};
  }
  
  function Sizzle( selector, context, results, seed ) {
  	var m, i, elem, nid, nidselect, match, groups, newSelector,
  		newContext = context && context.ownerDocument,
  
  		// nodeType defaults to 9, since context defaults to document
  		nodeType = context ? context.nodeType : 9;
  
  	results = results || [];
  
  	// Return early from calls with invalid selector or context
  	if ( typeof selector !== "string" || !selector ||
  		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
  
  		return results;
  	}
  
  	// Try to shortcut find operations (as opposed to filters) in HTML documents
  	if ( !seed ) {
  
  		if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
  			setDocument( context );
  		}
  		context = context || document;
  
  		if ( documentIsHTML ) {
  
  			// If the selector is sufficiently simple, try using a "get*By*" DOM method
  			// (excepting DocumentFragment context, where the methods don't exist)
  			if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
  
  				// ID selector
  				if ( (m = match[1]) ) {
  
  					// Document context
  					if ( nodeType === 9 ) {
  						if ( (elem = context.getElementById( m )) ) {
  
  							// Support: IE, Opera, Webkit
  							// TODO: identify versions
  							// getElementById can match elements by name instead of ID
  							if ( elem.id === m ) {
  								results.push( elem );
  								return results;
  							}
  						} else {
  							return results;
  						}
  
  					// Element context
  					} else {
  
  						// Support: IE, Opera, Webkit
  						// TODO: identify versions
  						// getElementById can match elements by name instead of ID
  						if ( newContext && (elem = newContext.getElementById( m )) &&
  							contains( context, elem ) &&
  							elem.id === m ) {
  
  							results.push( elem );
  							return results;
  						}
  					}
  
  				// Type selector
  				} else if ( match[2] ) {
  					push.apply( results, context.getElementsByTagName( selector ) );
  					return results;
  
  				// Class selector
  				} else if ( (m = match[3]) && support.getElementsByClassName &&
  					context.getElementsByClassName ) {
  
  					push.apply( results, context.getElementsByClassName( m ) );
  					return results;
  				}
  			}
  
  			// Take advantage of querySelectorAll
  			if ( support.qsa &&
  				!compilerCache[ selector + " " ] &&
  				(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
  
  				if ( nodeType !== 1 ) {
  					newContext = context;
  					newSelector = selector;
  
  				// qSA looks outside Element context, which is not what we want
  				// Thanks to Andrew Dupont for this workaround technique
  				// Support: IE <=8
  				// Exclude object elements
  				} else if ( context.nodeName.toLowerCase() !== "object" ) {
  
  					// Capture the context ID, setting it first if necessary
  					if ( (nid = context.getAttribute( "id" )) ) {
  						nid = nid.replace( rescape, "\\$&" );
  					} else {
  						context.setAttribute( "id", (nid = expando) );
  					}
  
  					// Prefix every selector in the list
  					groups = tokenize( selector );
  					i = groups.length;
  					nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']";
  					while ( i-- ) {
  						groups[i] = nidselect + " " + toSelector( groups[i] );
  					}
  					newSelector = groups.join( "," );
  
  					// Expand context for sibling selectors
  					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
  						context;
  				}
  
  				if ( newSelector ) {
  					try {
  						push.apply( results,
  							newContext.querySelectorAll( newSelector )
  						);
  						return results;
  					} catch ( qsaError ) {
  					} finally {
  						if ( nid === expando ) {
  							context.removeAttribute( "id" );
  						}
  					}
  				}
  			}
  		}
  	}
  
  	// All others
  	return select( selector.replace( rtrim, "$1" ), context, results, seed );
  }
  
  /**
   * Create key-value caches of limited size
   * @returns {function(string, object)} Returns the Object data after storing it on itself with
   *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
   *	deleting the oldest entry
   */
  function createCache() {
  	var keys = [];
  
  	function cache( key, value ) {
  		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
  		if ( keys.push( key + " " ) > Expr.cacheLength ) {
  			// Only keep the most recent entries
  			delete cache[ keys.shift() ];
  		}
  		return (cache[ key + " " ] = value);
  	}
  	return cache;
  }
  
  /**
   * Mark a function for special use by Sizzle
   * @param {Function} fn The function to mark
   */
  function markFunction( fn ) {
  	fn[ expando ] = true;
  	return fn;
  }
  
  /**
   * Support testing using an element
   * @param {Function} fn Passed the created div and expects a boolean result
   */
  function assert( fn ) {
  	var div = document.createElement("div");
  
  	try {
  		return !!fn( div );
  	} catch (e) {
  		return false;
  	} finally {
  		// Remove from its parent by default
  		if ( div.parentNode ) {
  			div.parentNode.removeChild( div );
  		}
  		// release memory in IE
  		div = null;
  	}
  }
  
  /**
   * Adds the same handler for all of the specified attrs
   * @param {String} attrs Pipe-separated list of attributes
   * @param {Function} handler The method that will be applied
   */
  function addHandle( attrs, handler ) {
  	var arr = attrs.split("|"),
  		i = arr.length;
  
  	while ( i-- ) {
  		Expr.attrHandle[ arr[i] ] = handler;
  	}
  }
  
  /**
   * Checks document order of two siblings
   * @param {Element} a
   * @param {Element} b
   * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
   */
  function siblingCheck( a, b ) {
  	var cur = b && a,
  		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
  			( ~b.sourceIndex || MAX_NEGATIVE ) -
  			( ~a.sourceIndex || MAX_NEGATIVE );
  
  	// Use IE sourceIndex if available on both nodes
  	if ( diff ) {
  		return diff;
  	}
  
  	// Check if b follows a
  	if ( cur ) {
  		while ( (cur = cur.nextSibling) ) {
  			if ( cur === b ) {
  				return -1;
  			}
  		}
  	}
  
  	return a ? 1 : -1;
  }
  
  /**
   * Returns a function to use in pseudos for input types
   * @param {String} type
   */
  function createInputPseudo( type ) {
  	return function( elem ) {
  		var name = elem.nodeName.toLowerCase();
  		return name === "input" && elem.type === type;
  	};
  }
  
  /**
   * Returns a function to use in pseudos for buttons
   * @param {String} type
   */
  function createButtonPseudo( type ) {
  	return function( elem ) {
  		var name = elem.nodeName.toLowerCase();
  		return (name === "input" || name === "button") && elem.type === type;
  	};
  }
  
  /**
   * Returns a function to use in pseudos for positionals
   * @param {Function} fn
   */
  function createPositionalPseudo( fn ) {
  	return markFunction(function( argument ) {
  		argument = +argument;
  		return markFunction(function( seed, matches ) {
  			var j,
  				matchIndexes = fn( [], seed.length, argument ),
  				i = matchIndexes.length;
  
  			// Match elements found at the specified indexes
  			while ( i-- ) {
  				if ( seed[ (j = matchIndexes[i]) ] ) {
  					seed[j] = !(matches[j] = seed[j]);
  				}
  			}
  		});
  	});
  }
  
  /**
   * Checks a node for validity as a Sizzle context
   * @param {Element|Object=} context
   * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
   */
  function testContext( context ) {
  	return context && typeof context.getElementsByTagName !== "undefined" && context;
  }
  
  // Expose support vars for convenience
  support = Sizzle.support = {};
  
  /**
   * Detects XML nodes
   * @param {Element|Object} elem An element or a document
   * @returns {Boolean} True iff elem is a non-HTML XML node
   */
  isXML = Sizzle.isXML = function( elem ) {
  	// documentElement is verified for cases where it doesn't yet exist
  	// (such as loading iframes in IE - #4833)
  	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
  	return documentElement ? documentElement.nodeName !== "HTML" : false;
  };
  
  /**
   * Sets document-related variables once based on the current document
   * @param {Element|Object} [doc] An element or document object to use to set the document
   * @returns {Object} Returns the current document
   */
  setDocument = Sizzle.setDocument = function( node ) {
  	var hasCompare, parent,
  		doc = node ? node.ownerDocument || node : preferredDoc;
  
  	// Return early if doc is invalid or already selected
  	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
  		return document;
  	}
  
  	// Update global variables
  	document = doc;
  	docElem = document.documentElement;
  	documentIsHTML = !isXML( document );
  
  	// Support: IE 9-11, Edge
  	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
  	if ( (parent = document.defaultView) && parent.top !== parent ) {
  		// Support: IE 11
  		if ( parent.addEventListener ) {
  			parent.addEventListener( "unload", unloadHandler, false );
  
  		// Support: IE 9 - 10 only
  		} else if ( parent.attachEvent ) {
  			parent.attachEvent( "onunload", unloadHandler );
  		}
  	}
  
  	/* Attributes
  	---------------------------------------------------------------------- */
  
  	// Support: IE<8
  	// Verify that getAttribute really returns attributes and not properties
  	// (excepting IE8 booleans)
  	support.attributes = assert(function( div ) {
  		div.className = "i";
  		return !div.getAttribute("className");
  	});
  
  	/* getElement(s)By*
  	---------------------------------------------------------------------- */
  
  	// Check if getElementsByTagName("*") returns only elements
  	support.getElementsByTagName = assert(function( div ) {
  		div.appendChild( document.createComment("") );
  		return !div.getElementsByTagName("*").length;
  	});
  
  	// Support: IE<9
  	support.getElementsByClassName = rnative.test( document.getElementsByClassName );
  
  	// Support: IE<10
  	// Check if getElementById returns elements by name
  	// The broken getElementById methods don't pick up programatically-set names,
  	// so use a roundabout getElementsByName test
  	support.getById = assert(function( div ) {
  		docElem.appendChild( div ).id = expando;
  		return !document.getElementsByName || !document.getElementsByName( expando ).length;
  	});
  
  	// ID find and filter
  	if ( support.getById ) {
  		Expr.find["ID"] = function( id, context ) {
  			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
  				var m = context.getElementById( id );
  				return m ? [ m ] : [];
  			}
  		};
  		Expr.filter["ID"] = function( id ) {
  			var attrId = id.replace( runescape, funescape );
  			return function( elem ) {
  				return elem.getAttribute("id") === attrId;
  			};
  		};
  	} else {
  		// Support: IE6/7
  		// getElementById is not reliable as a find shortcut
  		delete Expr.find["ID"];
  
  		Expr.filter["ID"] =  function( id ) {
  			var attrId = id.replace( runescape, funescape );
  			return function( elem ) {
  				var node = typeof elem.getAttributeNode !== "undefined" &&
  					elem.getAttributeNode("id");
  				return node && node.value === attrId;
  			};
  		};
  	}
  
  	// Tag
  	Expr.find["TAG"] = support.getElementsByTagName ?
  		function( tag, context ) {
  			if ( typeof context.getElementsByTagName !== "undefined" ) {
  				return context.getElementsByTagName( tag );
  
  			// DocumentFragment nodes don't have gEBTN
  			} else if ( support.qsa ) {
  				return context.querySelectorAll( tag );
  			}
  		} :
  
  		function( tag, context ) {
  			var elem,
  				tmp = [],
  				i = 0,
  				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
  				results = context.getElementsByTagName( tag );
  
  			// Filter out possible comments
  			if ( tag === "*" ) {
  				while ( (elem = results[i++]) ) {
  					if ( elem.nodeType === 1 ) {
  						tmp.push( elem );
  					}
  				}
  
  				return tmp;
  			}
  			return results;
  		};
  
  	// Class
  	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
  		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
  			return context.getElementsByClassName( className );
  		}
  	};
  
  	/* QSA/matchesSelector
  	---------------------------------------------------------------------- */
  
  	// QSA and matchesSelector support
  
  	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
  	rbuggyMatches = [];
  
  	// qSa(:focus) reports false when true (Chrome 21)
  	// We allow this because of a bug in IE8/9 that throws an error
  	// whenever `document.activeElement` is accessed on an iframe
  	// So, we allow :focus to pass through QSA all the time to avoid the IE error
  	// See http://bugs.jquery.com/ticket/13378
  	rbuggyQSA = [];
  
  	if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
  		// Build QSA regex
  		// Regex strategy adopted from Diego Perini
  		assert(function( div ) {
  			// Select is set to empty string on purpose
  			// This is to test IE's treatment of not explicitly
  			// setting a boolean content attribute,
  			// since its presence should be enough
  			// http://bugs.jquery.com/ticket/12359
  			docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
  				"<select id='" + expando + "-\r\\' msallowcapture=''>" +
  				"<option selected=''></option></select>";
  
  			// Support: IE8, Opera 11-12.16
  			// Nothing should be selected when empty strings follow ^= or $= or *=
  			// The test attribute must be unknown in Opera but "safe" for WinRT
  			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
  			if ( div.querySelectorAll("[msallowcapture^='']").length ) {
  				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
  			}
  
  			// Support: IE8
  			// Boolean attributes and "value" are not treated correctly
  			if ( !div.querySelectorAll("[selected]").length ) {
  				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
  			}
  
  			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
  			if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
  				rbuggyQSA.push("~=");
  			}
  
  			// Webkit/Opera - :checked should return selected option elements
  			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
  			// IE8 throws error here and will not see later tests
  			if ( !div.querySelectorAll(":checked").length ) {
  				rbuggyQSA.push(":checked");
  			}
  
  			// Support: Safari 8+, iOS 8+
  			// https://bugs.webkit.org/show_bug.cgi?id=136851
  			// In-page `selector#id sibing-combinator selector` fails
  			if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
  				rbuggyQSA.push(".#.+[+~]");
  			}
  		});
  
  		assert(function( div ) {
  			// Support: Windows 8 Native Apps
  			// The type and name attributes are restricted during .innerHTML assignment
  			var input = document.createElement("input");
  			input.setAttribute( "type", "hidden" );
  			div.appendChild( input ).setAttribute( "name", "D" );
  
  			// Support: IE8
  			// Enforce case-sensitivity of name attribute
  			if ( div.querySelectorAll("[name=d]").length ) {
  				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
  			}
  
  			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
  			// IE8 throws error here and will not see later tests
  			if ( !div.querySelectorAll(":enabled").length ) {
  				rbuggyQSA.push( ":enabled", ":disabled" );
  			}
  
  			// Opera 10-11 does not throw on post-comma invalid pseudos
  			div.querySelectorAll("*,:x");
  			rbuggyQSA.push(",.*:");
  		});
  	}
  
  	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
  		docElem.webkitMatchesSelector ||
  		docElem.mozMatchesSelector ||
  		docElem.oMatchesSelector ||
  		docElem.msMatchesSelector) )) ) {
  
  		assert(function( div ) {
  			// Check to see if it's possible to do matchesSelector
  			// on a disconnected node (IE 9)
  			support.disconnectedMatch = matches.call( div, "div" );
  
  			// This should fail with an exception
  			// Gecko does not error, returns false instead
  			matches.call( div, "[s!='']:x" );
  			rbuggyMatches.push( "!=", pseudos );
  		});
  	}
  
  	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
  	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
  
  	/* Contains
  	---------------------------------------------------------------------- */
  	hasCompare = rnative.test( docElem.compareDocumentPosition );
  
  	// Element contains another
  	// Purposefully self-exclusive
  	// As in, an element does not contain itself
  	contains = hasCompare || rnative.test( docElem.contains ) ?
  		function( a, b ) {
  			var adown = a.nodeType === 9 ? a.documentElement : a,
  				bup = b && b.parentNode;
  			return a === bup || !!( bup && bup.nodeType === 1 && (
  				adown.contains ?
  					adown.contains( bup ) :
  					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
  			));
  		} :
  		function( a, b ) {
  			if ( b ) {
  				while ( (b = b.parentNode) ) {
  					if ( b === a ) {
  						return true;
  					}
  				}
  			}
  			return false;
  		};
  
  	/* Sorting
  	---------------------------------------------------------------------- */
  
  	// Document order sorting
  	sortOrder = hasCompare ?
  	function( a, b ) {
  
  		// Flag for duplicate removal
  		if ( a === b ) {
  			hasDuplicate = true;
  			return 0;
  		}
  
  		// Sort on method existence if only one input has compareDocumentPosition
  		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
  		if ( compare ) {
  			return compare;
  		}
  
  		// Calculate position if both inputs belong to the same document
  		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
  			a.compareDocumentPosition( b ) :
  
  			// Otherwise we know they are disconnected
  			1;
  
  		// Disconnected nodes
  		if ( compare & 1 ||
  			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
  
  			// Choose the first element that is related to our preferred document
  			if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
  				return -1;
  			}
  			if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
  				return 1;
  			}
  
  			// Maintain original order
  			return sortInput ?
  				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
  				0;
  		}
  
  		return compare & 4 ? -1 : 1;
  	} :
  	function( a, b ) {
  		// Exit early if the nodes are identical
  		if ( a === b ) {
  			hasDuplicate = true;
  			return 0;
  		}
  
  		var cur,
  			i = 0,
  			aup = a.parentNode,
  			bup = b.parentNode,
  			ap = [ a ],
  			bp = [ b ];
  
  		// Parentless nodes are either documents or disconnected
  		if ( !aup || !bup ) {
  			return a === document ? -1 :
  				b === document ? 1 :
  				aup ? -1 :
  				bup ? 1 :
  				sortInput ?
  				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
  				0;
  
  		// If the nodes are siblings, we can do a quick check
  		} else if ( aup === bup ) {
  			return siblingCheck( a, b );
  		}
  
  		// Otherwise we need full lists of their ancestors for comparison
  		cur = a;
  		while ( (cur = cur.parentNode) ) {
  			ap.unshift( cur );
  		}
  		cur = b;
  		while ( (cur = cur.parentNode) ) {
  			bp.unshift( cur );
  		}
  
  		// Walk down the tree looking for a discrepancy
  		while ( ap[i] === bp[i] ) {
  			i++;
  		}
  
  		return i ?
  			// Do a sibling check if the nodes have a common ancestor
  			siblingCheck( ap[i], bp[i] ) :
  
  			// Otherwise nodes in our document sort first
  			ap[i] === preferredDoc ? -1 :
  			bp[i] === preferredDoc ? 1 :
  			0;
  	};
  
  	return document;
  };
  
  Sizzle.matches = function( expr, elements ) {
  	return Sizzle( expr, null, null, elements );
  };
  
  Sizzle.matchesSelector = function( elem, expr ) {
  	// Set document vars if needed
  	if ( ( elem.ownerDocument || elem ) !== document ) {
  		setDocument( elem );
  	}
  
  	// Make sure that attribute selectors are quoted
  	expr = expr.replace( rattributeQuotes, "='$1']" );
  
  	if ( support.matchesSelector && documentIsHTML &&
  		!compilerCache[ expr + " " ] &&
  		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
  		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
  
  		try {
  			var ret = matches.call( elem, expr );
  
  			// IE 9's matchesSelector returns false on disconnected nodes
  			if ( ret || support.disconnectedMatch ||
  					// As well, disconnected nodes are said to be in a document
  					// fragment in IE 9
  					elem.document && elem.document.nodeType !== 11 ) {
  				return ret;
  			}
  		} catch (e) {}
  	}
  
  	return Sizzle( expr, document, null, [ elem ] ).length > 0;
  };
  
  Sizzle.contains = function( context, elem ) {
  	// Set document vars if needed
  	if ( ( context.ownerDocument || context ) !== document ) {
  		setDocument( context );
  	}
  	return contains( context, elem );
  };
  
  Sizzle.attr = function( elem, name ) {
  	// Set document vars if needed
  	if ( ( elem.ownerDocument || elem ) !== document ) {
  		setDocument( elem );
  	}
  
  	var fn = Expr.attrHandle[ name.toLowerCase() ],
  		// Don't get fooled by Object.prototype properties (jQuery #13807)
  		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
  			fn( elem, name, !documentIsHTML ) :
  			undefined;
  
  	return val !== undefined ?
  		val :
  		support.attributes || !documentIsHTML ?
  			elem.getAttribute( name ) :
  			(val = elem.getAttributeNode(name)) && val.specified ?
  				val.value :
  				null;
  };
  
  Sizzle.error = function( msg ) {
  	throw new Error( "Syntax error, unrecognized expression: " + msg );
  };
  
  /**
   * Document sorting and removing duplicates
   * @param {ArrayLike} results
   */
  Sizzle.uniqueSort = function( results ) {
  	var elem,
  		duplicates = [],
  		j = 0,
  		i = 0;
  
  	// Unless we *know* we can detect duplicates, assume their presence
  	hasDuplicate = !support.detectDuplicates;
  	sortInput = !support.sortStable && results.slice( 0 );
  	results.sort( sortOrder );
  
  	if ( hasDuplicate ) {
  		while ( (elem = results[i++]) ) {
  			if ( elem === results[ i ] ) {
  				j = duplicates.push( i );
  			}
  		}
  		while ( j-- ) {
  			results.splice( duplicates[ j ], 1 );
  		}
  	}
  
  	// Clear input after sorting to release objects
  	// See https://github.com/jquery/sizzle/pull/225
  	sortInput = null;
  
  	return results;
  };
  
  /**
   * Utility function for retrieving the text value of an array of DOM nodes
   * @param {Array|Element} elem
   */
  getText = Sizzle.getText = function( elem ) {
  	var node,
  		ret = "",
  		i = 0,
  		nodeType = elem.nodeType;
  
  	if ( !nodeType ) {
  		// If no nodeType, this is expected to be an array
  		while ( (node = elem[i++]) ) {
  			// Do not traverse comment nodes
  			ret += getText( node );
  		}
  	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
  		// Use textContent for elements
  		// innerText usage removed for consistency of new lines (jQuery #11153)
  		if ( typeof elem.textContent === "string" ) {
  			return elem.textContent;
  		} else {
  			// Traverse its children
  			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
  				ret += getText( elem );
  			}
  		}
  	} else if ( nodeType === 3 || nodeType === 4 ) {
  		return elem.nodeValue;
  	}
  	// Do not include comment or processing instruction nodes
  
  	return ret;
  };
  
  Expr = Sizzle.selectors = {
  
  	// Can be adjusted by the user
  	cacheLength: 50,
  
  	createPseudo: markFunction,
  
  	match: matchExpr,
  
  	attrHandle: {},
  
  	find: {},
  
  	relative: {
  		">": { dir: "parentNode", first: true },
  		" ": { dir: "parentNode" },
  		"+": { dir: "previousSibling", first: true },
  		"~": { dir: "previousSibling" }
  	},
  
  	preFilter: {
  		"ATTR": function( match ) {
  			match[1] = match[1].replace( runescape, funescape );
  
  			// Move the given value to match[3] whether quoted or unquoted
  			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
  
  			if ( match[2] === "~=" ) {
  				match[3] = " " + match[3] + " ";
  			}
  
  			return match.slice( 0, 4 );
  		},
  
  		"CHILD": function( match ) {
  			/* matches from matchExpr["CHILD"]
  				1 type (only|nth|...)
  				2 what (child|of-type)
  				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
  				4 xn-component of xn+y argument ([+-]?\d*n|)
  				5 sign of xn-component
  				6 x of xn-component
  				7 sign of y-component
  				8 y of y-component
  			*/
  			match[1] = match[1].toLowerCase();
  
  			if ( match[1].slice( 0, 3 ) === "nth" ) {
  				// nth-* requires argument
  				if ( !match[3] ) {
  					Sizzle.error( match[0] );
  				}
  
  				// numeric x and y parameters for Expr.filter.CHILD
  				// remember that false/true cast respectively to 0/1
  				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
  				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
  
  			// other types prohibit arguments
  			} else if ( match[3] ) {
  				Sizzle.error( match[0] );
  			}
  
  			return match;
  		},
  
  		"PSEUDO": function( match ) {
  			var excess,
  				unquoted = !match[6] && match[2];
  
  			if ( matchExpr["CHILD"].test( match[0] ) ) {
  				return null;
  			}
  
  			// Accept quoted arguments as-is
  			if ( match[3] ) {
  				match[2] = match[4] || match[5] || "";
  
  			// Strip excess characters from unquoted arguments
  			} else if ( unquoted && rpseudo.test( unquoted ) &&
  				// Get excess from tokenize (recursively)
  				(excess = tokenize( unquoted, true )) &&
  				// advance to the next closing parenthesis
  				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
  
  				// excess is a negative index
  				match[0] = match[0].slice( 0, excess );
  				match[2] = unquoted.slice( 0, excess );
  			}
  
  			// Return only captures needed by the pseudo filter method (type and argument)
  			return match.slice( 0, 3 );
  		}
  	},
  
  	filter: {
  
  		"TAG": function( nodeNameSelector ) {
  			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
  			return nodeNameSelector === "*" ?
  				function() { return true; } :
  				function( elem ) {
  					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
  				};
  		},
  
  		"CLASS": function( className ) {
  			var pattern = classCache[ className + " " ];
  
  			return pattern ||
  				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
  				classCache( className, function( elem ) {
  					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
  				});
  		},
  
  		"ATTR": function( name, operator, check ) {
  			return function( elem ) {
  				var result = Sizzle.attr( elem, name );
  
  				if ( result == null ) {
  					return operator === "!=";
  				}
  				if ( !operator ) {
  					return true;
  				}
  
  				result += "";
  
  				return operator === "=" ? result === check :
  					operator === "!=" ? result !== check :
  					operator === "^=" ? check && result.indexOf( check ) === 0 :
  					operator === "*=" ? check && result.indexOf( check ) > -1 :
  					operator === "$=" ? check && result.slice( -check.length ) === check :
  					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
  					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
  					false;
  			};
  		},
  
  		"CHILD": function( type, what, argument, first, last ) {
  			var simple = type.slice( 0, 3 ) !== "nth",
  				forward = type.slice( -4 ) !== "last",
  				ofType = what === "of-type";
  
  			return first === 1 && last === 0 ?
  
  				// Shortcut for :nth-*(n)
  				function( elem ) {
  					return !!elem.parentNode;
  				} :
  
  				function( elem, context, xml ) {
  					var cache, uniqueCache, outerCache, node, nodeIndex, start,
  						dir = simple !== forward ? "nextSibling" : "previousSibling",
  						parent = elem.parentNode,
  						name = ofType && elem.nodeName.toLowerCase(),
  						useCache = !xml && !ofType,
  						diff = false;
  
  					if ( parent ) {
  
  						// :(first|last|only)-(child|of-type)
  						if ( simple ) {
  							while ( dir ) {
  								node = elem;
  								while ( (node = node[ dir ]) ) {
  									if ( ofType ?
  										node.nodeName.toLowerCase() === name :
  										node.nodeType === 1 ) {
  
  										return false;
  									}
  								}
  								// Reverse direction for :only-* (if we haven't yet done so)
  								start = dir = type === "only" && !start && "nextSibling";
  							}
  							return true;
  						}
  
  						start = [ forward ? parent.firstChild : parent.lastChild ];
  
  						// non-xml :nth-child(...) stores cache data on `parent`
  						if ( forward && useCache ) {
  
  							// Seek `elem` from a previously-cached index
  
  							// ...in a gzip-friendly way
  							node = parent;
  							outerCache = node[ expando ] || (node[ expando ] = {});
  
  							// Support: IE <9 only
  							// Defend against cloned attroperties (jQuery gh-1709)
  							uniqueCache = outerCache[ node.uniqueID ] ||
  								(outerCache[ node.uniqueID ] = {});
  
  							cache = uniqueCache[ type ] || [];
  							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
  							diff = nodeIndex && cache[ 2 ];
  							node = nodeIndex && parent.childNodes[ nodeIndex ];
  
  							while ( (node = ++nodeIndex && node && node[ dir ] ||
  
  								// Fallback to seeking `elem` from the start
  								(diff = nodeIndex = 0) || start.pop()) ) {
  
  								// When found, cache indexes on `parent` and break
  								if ( node.nodeType === 1 && ++diff && node === elem ) {
  									uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
  									break;
  								}
  							}
  
  						} else {
  							// Use previously-cached element index if available
  							if ( useCache ) {
  								// ...in a gzip-friendly way
  								node = elem;
  								outerCache = node[ expando ] || (node[ expando ] = {});
  
  								// Support: IE <9 only
  								// Defend against cloned attroperties (jQuery gh-1709)
  								uniqueCache = outerCache[ node.uniqueID ] ||
  									(outerCache[ node.uniqueID ] = {});
  
  								cache = uniqueCache[ type ] || [];
  								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
  								diff = nodeIndex;
  							}
  
  							// xml :nth-child(...)
  							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
  							if ( diff === false ) {
  								// Use the same loop as above to seek `elem` from the start
  								while ( (node = ++nodeIndex && node && node[ dir ] ||
  									(diff = nodeIndex = 0) || start.pop()) ) {
  
  									if ( ( ofType ?
  										node.nodeName.toLowerCase() === name :
  										node.nodeType === 1 ) &&
  										++diff ) {
  
  										// Cache the index of each encountered element
  										if ( useCache ) {
  											outerCache = node[ expando ] || (node[ expando ] = {});
  
  											// Support: IE <9 only
  											// Defend against cloned attroperties (jQuery gh-1709)
  											uniqueCache = outerCache[ node.uniqueID ] ||
  												(outerCache[ node.uniqueID ] = {});
  
  											uniqueCache[ type ] = [ dirruns, diff ];
  										}
  
  										if ( node === elem ) {
  											break;
  										}
  									}
  								}
  							}
  						}
  
  						// Incorporate the offset, then check against cycle size
  						diff -= last;
  						return diff === first || ( diff % first === 0 && diff / first >= 0 );
  					}
  				};
  		},
  
  		"PSEUDO": function( pseudo, argument ) {
  			// pseudo-class names are case-insensitive
  			// http://www.w3.org/TR/selectors/#pseudo-classes
  			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
  			// Remember that setFilters inherits from pseudos
  			var args,
  				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
  					Sizzle.error( "unsupported pseudo: " + pseudo );
  
  			// The user may use createPseudo to indicate that
  			// arguments are needed to create the filter function
  			// just as Sizzle does
  			if ( fn[ expando ] ) {
  				return fn( argument );
  			}
  
  			// But maintain support for old signatures
  			if ( fn.length > 1 ) {
  				args = [ pseudo, pseudo, "", argument ];
  				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
  					markFunction(function( seed, matches ) {
  						var idx,
  							matched = fn( seed, argument ),
  							i = matched.length;
  						while ( i-- ) {
  							idx = indexOf( seed, matched[i] );
  							seed[ idx ] = !( matches[ idx ] = matched[i] );
  						}
  					}) :
  					function( elem ) {
  						return fn( elem, 0, args );
  					};
  			}
  
  			return fn;
  		}
  	},
  
  	pseudos: {
  		// Potentially complex pseudos
  		"not": markFunction(function( selector ) {
  			// Trim the selector passed to compile
  			// to avoid treating leading and trailing
  			// spaces as combinators
  			var input = [],
  				results = [],
  				matcher = compile( selector.replace( rtrim, "$1" ) );
  
  			return matcher[ expando ] ?
  				markFunction(function( seed, matches, context, xml ) {
  					var elem,
  						unmatched = matcher( seed, null, xml, [] ),
  						i = seed.length;
  
  					// Match elements unmatched by `matcher`
  					while ( i-- ) {
  						if ( (elem = unmatched[i]) ) {
  							seed[i] = !(matches[i] = elem);
  						}
  					}
  				}) :
  				function( elem, context, xml ) {
  					input[0] = elem;
  					matcher( input, null, xml, results );
  					// Don't keep the element (issue #299)
  					input[0] = null;
  					return !results.pop();
  				};
  		}),
  
  		"has": markFunction(function( selector ) {
  			return function( elem ) {
  				return Sizzle( selector, elem ).length > 0;
  			};
  		}),
  
  		"contains": markFunction(function( text ) {
  			text = text.replace( runescape, funescape );
  			return function( elem ) {
  				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
  			};
  		}),
  
  		// "Whether an element is represented by a :lang() selector
  		// is based solely on the element's language value
  		// being equal to the identifier C,
  		// or beginning with the identifier C immediately followed by "-".
  		// The matching of C against the element's language value is performed case-insensitively.
  		// The identifier C does not have to be a valid language name."
  		// http://www.w3.org/TR/selectors/#lang-pseudo
  		"lang": markFunction( function( lang ) {
  			// lang value must be a valid identifier
  			if ( !ridentifier.test(lang || "") ) {
  				Sizzle.error( "unsupported lang: " + lang );
  			}
  			lang = lang.replace( runescape, funescape ).toLowerCase();
  			return function( elem ) {
  				var elemLang;
  				do {
  					if ( (elemLang = documentIsHTML ?
  						elem.lang :
  						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
  
  						elemLang = elemLang.toLowerCase();
  						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
  					}
  				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
  				return false;
  			};
  		}),
  
  		// Miscellaneous
  		"target": function( elem ) {
  			var hash = window.location && window.location.hash;
  			return hash && hash.slice( 1 ) === elem.id;
  		},
  
  		"root": function( elem ) {
  			return elem === docElem;
  		},
  
  		"focus": function( elem ) {
  			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
  		},
  
  		// Boolean properties
  		"enabled": function( elem ) {
  			return elem.disabled === false;
  		},
  
  		"disabled": function( elem ) {
  			return elem.disabled === true;
  		},
  
  		"checked": function( elem ) {
  			// In CSS3, :checked should return both checked and selected elements
  			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
  			var nodeName = elem.nodeName.toLowerCase();
  			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
  		},
  
  		"selected": function( elem ) {
  			// Accessing this property makes selected-by-default
  			// options in Safari work properly
  			if ( elem.parentNode ) {
  				elem.parentNode.selectedIndex;
  			}
  
  			return elem.selected === true;
  		},
  
  		// Contents
  		"empty": function( elem ) {
  			// http://www.w3.org/TR/selectors/#empty-pseudo
  			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
  			//   but not by others (comment: 8; processing instruction: 7; etc.)
  			// nodeType < 6 works because attributes (2) do not appear as children
  			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
  				if ( elem.nodeType < 6 ) {
  					return false;
  				}
  			}
  			return true;
  		},
  
  		"parent": function( elem ) {
  			return !Expr.pseudos["empty"]( elem );
  		},
  
  		// Element/input types
  		"header": function( elem ) {
  			return rheader.test( elem.nodeName );
  		},
  
  		"input": function( elem ) {
  			return rinputs.test( elem.nodeName );
  		},
  
  		"button": function( elem ) {
  			var name = elem.nodeName.toLowerCase();
  			return name === "input" && elem.type === "button" || name === "button";
  		},
  
  		"text": function( elem ) {
  			var attr;
  			return elem.nodeName.toLowerCase() === "input" &&
  				elem.type === "text" &&
  
  				// Support: IE<8
  				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
  				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
  		},
  
  		// Position-in-collection
  		"first": createPositionalPseudo(function() {
  			return [ 0 ];
  		}),
  
  		"last": createPositionalPseudo(function( matchIndexes, length ) {
  			return [ length - 1 ];
  		}),
  
  		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
  			return [ argument < 0 ? argument + length : argument ];
  		}),
  
  		"even": createPositionalPseudo(function( matchIndexes, length ) {
  			var i = 0;
  			for ( ; i < length; i += 2 ) {
  				matchIndexes.push( i );
  			}
  			return matchIndexes;
  		}),
  
  		"odd": createPositionalPseudo(function( matchIndexes, length ) {
  			var i = 1;
  			for ( ; i < length; i += 2 ) {
  				matchIndexes.push( i );
  			}
  			return matchIndexes;
  		}),
  
  		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
  			var i = argument < 0 ? argument + length : argument;
  			for ( ; --i >= 0; ) {
  				matchIndexes.push( i );
  			}
  			return matchIndexes;
  		}),
  
  		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
  			var i = argument < 0 ? argument + length : argument;
  			for ( ; ++i < length; ) {
  				matchIndexes.push( i );
  			}
  			return matchIndexes;
  		})
  	}
  };
  
  Expr.pseudos["nth"] = Expr.pseudos["eq"];
  
  // Add button/input type pseudos
  for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
  	Expr.pseudos[ i ] = createInputPseudo( i );
  }
  for ( i in { submit: true, reset: true } ) {
  	Expr.pseudos[ i ] = createButtonPseudo( i );
  }
  
  // Easy API for creating new setFilters
  function setFilters() {}
  setFilters.prototype = Expr.filters = Expr.pseudos;
  Expr.setFilters = new setFilters();
  
  tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
  	var matched, match, tokens, type,
  		soFar, groups, preFilters,
  		cached = tokenCache[ selector + " " ];
  
  	if ( cached ) {
  		return parseOnly ? 0 : cached.slice( 0 );
  	}
  
  	soFar = selector;
  	groups = [];
  	preFilters = Expr.preFilter;
  
  	while ( soFar ) {
  
  		// Comma and first run
  		if ( !matched || (match = rcomma.exec( soFar )) ) {
  			if ( match ) {
  				// Don't consume trailing commas as valid
  				soFar = soFar.slice( match[0].length ) || soFar;
  			}
  			groups.push( (tokens = []) );
  		}
  
  		matched = false;
  
  		// Combinators
  		if ( (match = rcombinators.exec( soFar )) ) {
  			matched = match.shift();
  			tokens.push({
  				value: matched,
  				// Cast descendant combinators to space
  				type: match[0].replace( rtrim, " " )
  			});
  			soFar = soFar.slice( matched.length );
  		}
  
  		// Filters
  		for ( type in Expr.filter ) {
  			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
  				(match = preFilters[ type ]( match ))) ) {
  				matched = match.shift();
  				tokens.push({
  					value: matched,
  					type: type,
  					matches: match
  				});
  				soFar = soFar.slice( matched.length );
  			}
  		}
  
  		if ( !matched ) {
  			break;
  		}
  	}
  
  	// Return the length of the invalid excess
  	// if we're just parsing
  	// Otherwise, throw an error or return tokens
  	return parseOnly ?
  		soFar.length :
  		soFar ?
  			Sizzle.error( selector ) :
  			// Cache the tokens
  			tokenCache( selector, groups ).slice( 0 );
  };
  
  function toSelector( tokens ) {
  	var i = 0,
  		len = tokens.length,
  		selector = "";
  	for ( ; i < len; i++ ) {
  		selector += tokens[i].value;
  	}
  	return selector;
  }
  
  function addCombinator( matcher, combinator, base ) {
  	var dir = combinator.dir,
  		checkNonElements = base && dir === "parentNode",
  		doneName = done++;
  
  	return combinator.first ?
  		// Check against closest ancestor/preceding element
  		function( elem, context, xml ) {
  			while ( (elem = elem[ dir ]) ) {
  				if ( elem.nodeType === 1 || checkNonElements ) {
  					return matcher( elem, context, xml );
  				}
  			}
  		} :
  
  		// Check against all ancestor/preceding elements
  		function( elem, context, xml ) {
  			var oldCache, uniqueCache, outerCache,
  				newCache = [ dirruns, doneName ];
  
  			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
  			if ( xml ) {
  				while ( (elem = elem[ dir ]) ) {
  					if ( elem.nodeType === 1 || checkNonElements ) {
  						if ( matcher( elem, context, xml ) ) {
  							return true;
  						}
  					}
  				}
  			} else {
  				while ( (elem = elem[ dir ]) ) {
  					if ( elem.nodeType === 1 || checkNonElements ) {
  						outerCache = elem[ expando ] || (elem[ expando ] = {});
  
  						// Support: IE <9 only
  						// Defend against cloned attroperties (jQuery gh-1709)
  						uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
  
  						if ( (oldCache = uniqueCache[ dir ]) &&
  							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
  
  							// Assign to newCache so results back-propagate to previous elements
  							return (newCache[ 2 ] = oldCache[ 2 ]);
  						} else {
  							// Reuse newcache so results back-propagate to previous elements
  							uniqueCache[ dir ] = newCache;
  
  							// A match means we're done; a fail means we have to keep checking
  							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
  								return true;
  							}
  						}
  					}
  				}
  			}
  		};
  }
  
  function elementMatcher( matchers ) {
  	return matchers.length > 1 ?
  		function( elem, context, xml ) {
  			var i = matchers.length;
  			while ( i-- ) {
  				if ( !matchers[i]( elem, context, xml ) ) {
  					return false;
  				}
  			}
  			return true;
  		} :
  		matchers[0];
  }
  
  function multipleContexts( selector, contexts, results ) {
  	var i = 0,
  		len = contexts.length;
  	for ( ; i < len; i++ ) {
  		Sizzle( selector, contexts[i], results );
  	}
  	return results;
  }
  
  function condense( unmatched, map, filter, context, xml ) {
  	var elem,
  		newUnmatched = [],
  		i = 0,
  		len = unmatched.length,
  		mapped = map != null;
  
  	for ( ; i < len; i++ ) {
  		if ( (elem = unmatched[i]) ) {
  			if ( !filter || filter( elem, context, xml ) ) {
  				newUnmatched.push( elem );
  				if ( mapped ) {
  					map.push( i );
  				}
  			}
  		}
  	}
  
  	return newUnmatched;
  }
  
  function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
  	if ( postFilter && !postFilter[ expando ] ) {
  		postFilter = setMatcher( postFilter );
  	}
  	if ( postFinder && !postFinder[ expando ] ) {
  		postFinder = setMatcher( postFinder, postSelector );
  	}
  	return markFunction(function( seed, results, context, xml ) {
  		var temp, i, elem,
  			preMap = [],
  			postMap = [],
  			preexisting = results.length,
  
  			// Get initial elements from seed or context
  			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
  
  			// Prefilter to get matcher input, preserving a map for seed-results synchronization
  			matcherIn = preFilter && ( seed || !selector ) ?
  				condense( elems, preMap, preFilter, context, xml ) :
  				elems,
  
  			matcherOut = matcher ?
  				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
  				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
  
  					// ...intermediate processing is necessary
  					[] :
  
  					// ...otherwise use results directly
  					results :
  				matcherIn;
  
  		// Find primary matches
  		if ( matcher ) {
  			matcher( matcherIn, matcherOut, context, xml );
  		}
  
  		// Apply postFilter
  		if ( postFilter ) {
  			temp = condense( matcherOut, postMap );
  			postFilter( temp, [], context, xml );
  
  			// Un-match failing elements by moving them back to matcherIn
  			i = temp.length;
  			while ( i-- ) {
  				if ( (elem = temp[i]) ) {
  					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
  				}
  			}
  		}
  
  		if ( seed ) {
  			if ( postFinder || preFilter ) {
  				if ( postFinder ) {
  					// Get the final matcherOut by condensing this intermediate into postFinder contexts
  					temp = [];
  					i = matcherOut.length;
  					while ( i-- ) {
  						if ( (elem = matcherOut[i]) ) {
  							// Restore matcherIn since elem is not yet a final match
  							temp.push( (matcherIn[i] = elem) );
  						}
  					}
  					postFinder( null, (matcherOut = []), temp, xml );
  				}
  
  				// Move matched elements from seed to results to keep them synchronized
  				i = matcherOut.length;
  				while ( i-- ) {
  					if ( (elem = matcherOut[i]) &&
  						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
  
  						seed[temp] = !(results[temp] = elem);
  					}
  				}
  			}
  
  		// Add elements to results, through postFinder if defined
  		} else {
  			matcherOut = condense(
  				matcherOut === results ?
  					matcherOut.splice( preexisting, matcherOut.length ) :
  					matcherOut
  			);
  			if ( postFinder ) {
  				postFinder( null, results, matcherOut, xml );
  			} else {
  				push.apply( results, matcherOut );
  			}
  		}
  	});
  }
  
  function matcherFromTokens( tokens ) {
  	var checkContext, matcher, j,
  		len = tokens.length,
  		leadingRelative = Expr.relative[ tokens[0].type ],
  		implicitRelative = leadingRelative || Expr.relative[" "],
  		i = leadingRelative ? 1 : 0,
  
  		// The foundational matcher ensures that elements are reachable from top-level context(s)
  		matchContext = addCombinator( function( elem ) {
  			return elem === checkContext;
  		}, implicitRelative, true ),
  		matchAnyContext = addCombinator( function( elem ) {
  			return indexOf( checkContext, elem ) > -1;
  		}, implicitRelative, true ),
  		matchers = [ function( elem, context, xml ) {
  			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
  				(checkContext = context).nodeType ?
  					matchContext( elem, context, xml ) :
  					matchAnyContext( elem, context, xml ) );
  			// Avoid hanging onto element (issue #299)
  			checkContext = null;
  			return ret;
  		} ];
  
  	for ( ; i < len; i++ ) {
  		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
  			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
  		} else {
  			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
  
  			// Return special upon seeing a positional matcher
  			if ( matcher[ expando ] ) {
  				// Find the next relative operator (if any) for proper handling
  				j = ++i;
  				for ( ; j < len; j++ ) {
  					if ( Expr.relative[ tokens[j].type ] ) {
  						break;
  					}
  				}
  				return setMatcher(
  					i > 1 && elementMatcher( matchers ),
  					i > 1 && toSelector(
  						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
  						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
  					).replace( rtrim, "$1" ),
  					matcher,
  					i < j && matcherFromTokens( tokens.slice( i, j ) ),
  					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
  					j < len && toSelector( tokens )
  				);
  			}
  			matchers.push( matcher );
  		}
  	}
  
  	return elementMatcher( matchers );
  }
  
  function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  	var bySet = setMatchers.length > 0,
  		byElement = elementMatchers.length > 0,
  		superMatcher = function( seed, context, xml, results, outermost ) {
  			var elem, j, matcher,
  				matchedCount = 0,
  				i = "0",
  				unmatched = seed && [],
  				setMatched = [],
  				contextBackup = outermostContext,
  				// We must always have either seed elements or outermost context
  				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
  				// Use integer dirruns iff this is the outermost matcher
  				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
  				len = elems.length;
  
  			if ( outermost ) {
  				outermostContext = context === document || context || outermost;
  			}
  
  			// Add elements passing elementMatchers directly to results
  			// Support: IE<9, Safari
  			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
  			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
  				if ( byElement && elem ) {
  					j = 0;
  					if ( !context && elem.ownerDocument !== document ) {
  						setDocument( elem );
  						xml = !documentIsHTML;
  					}
  					while ( (matcher = elementMatchers[j++]) ) {
  						if ( matcher( elem, context || document, xml) ) {
  							results.push( elem );
  							break;
  						}
  					}
  					if ( outermost ) {
  						dirruns = dirrunsUnique;
  					}
  				}
  
  				// Track unmatched elements for set filters
  				if ( bySet ) {
  					// They will have gone through all possible matchers
  					if ( (elem = !matcher && elem) ) {
  						matchedCount--;
  					}
  
  					// Lengthen the array for every element, matched or not
  					if ( seed ) {
  						unmatched.push( elem );
  					}
  				}
  			}
  
  			// `i` is now the count of elements visited above, and adding it to `matchedCount`
  			// makes the latter nonnegative.
  			matchedCount += i;
  
  			// Apply set filters to unmatched elements
  			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
  			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
  			// no element matchers and no seed.
  			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
  			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
  			// numerically zero.
  			if ( bySet && i !== matchedCount ) {
  				j = 0;
  				while ( (matcher = setMatchers[j++]) ) {
  					matcher( unmatched, setMatched, context, xml );
  				}
  
  				if ( seed ) {
  					// Reintegrate element matches to eliminate the need for sorting
  					if ( matchedCount > 0 ) {
  						while ( i-- ) {
  							if ( !(unmatched[i] || setMatched[i]) ) {
  								setMatched[i] = pop.call( results );
  							}
  						}
  					}
  
  					// Discard index placeholder values to get only actual matches
  					setMatched = condense( setMatched );
  				}
  
  				// Add matches to results
  				push.apply( results, setMatched );
  
  				// Seedless set matches succeeding multiple successful matchers stipulate sorting
  				if ( outermost && !seed && setMatched.length > 0 &&
  					( matchedCount + setMatchers.length ) > 1 ) {
  
  					Sizzle.uniqueSort( results );
  				}
  			}
  
  			// Override manipulation of globals by nested matchers
  			if ( outermost ) {
  				dirruns = dirrunsUnique;
  				outermostContext = contextBackup;
  			}
  
  			return unmatched;
  		};
  
  	return bySet ?
  		markFunction( superMatcher ) :
  		superMatcher;
  }
  
  compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
  	var i,
  		setMatchers = [],
  		elementMatchers = [],
  		cached = compilerCache[ selector + " " ];
  
  	if ( !cached ) {
  		// Generate a function of recursive functions that can be used to check each element
  		if ( !match ) {
  			match = tokenize( selector );
  		}
  		i = match.length;
  		while ( i-- ) {
  			cached = matcherFromTokens( match[i] );
  			if ( cached[ expando ] ) {
  				setMatchers.push( cached );
  			} else {
  				elementMatchers.push( cached );
  			}
  		}
  
  		// Cache the compiled function
  		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
  
  		// Save selector and tokenization
  		cached.selector = selector;
  	}
  	return cached;
  };
  
  /**
   * A low-level selection function that works with Sizzle's compiled
   *  selector functions
   * @param {String|Function} selector A selector or a pre-compiled
   *  selector function built with Sizzle.compile
   * @param {Element} context
   * @param {Array} [results]
   * @param {Array} [seed] A set of elements to match against
   */
  select = Sizzle.select = function( selector, context, results, seed ) {
  	var i, tokens, token, type, find,
  		compiled = typeof selector === "function" && selector,
  		match = !seed && tokenize( (selector = compiled.selector || selector) );
  
  	results = results || [];
  
  	// Try to minimize operations if there is only one selector in the list and no seed
  	// (the latter of which guarantees us context)
  	if ( match.length === 1 ) {
  
  		// Reduce context if the leading compound selector is an ID
  		tokens = match[0] = match[0].slice( 0 );
  		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
  				support.getById && context.nodeType === 9 && documentIsHTML &&
  				Expr.relative[ tokens[1].type ] ) {
  
  			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
  			if ( !context ) {
  				return results;
  
  			// Precompiled matchers will still verify ancestry, so step up a level
  			} else if ( compiled ) {
  				context = context.parentNode;
  			}
  
  			selector = selector.slice( tokens.shift().value.length );
  		}
  
  		// Fetch a seed set for right-to-left matching
  		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
  		while ( i-- ) {
  			token = tokens[i];
  
  			// Abort if we hit a combinator
  			if ( Expr.relative[ (type = token.type) ] ) {
  				break;
  			}
  			if ( (find = Expr.find[ type ]) ) {
  				// Search, expanding context for leading sibling combinators
  				if ( (seed = find(
  					token.matches[0].replace( runescape, funescape ),
  					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
  				)) ) {
  
  					// If seed is empty or no tokens remain, we can return early
  					tokens.splice( i, 1 );
  					selector = seed.length && toSelector( tokens );
  					if ( !selector ) {
  						push.apply( results, seed );
  						return results;
  					}
  
  					break;
  				}
  			}
  		}
  	}
  
  	// Compile and execute a filtering function if one is not provided
  	// Provide `match` to avoid retokenization if we modified the selector above
  	( compiled || compile( selector, match ) )(
  		seed,
  		context,
  		!documentIsHTML,
  		results,
  		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
  	);
  	return results;
  };
  
  // One-time assignments
  
  // Sort stability
  support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
  
  // Support: Chrome 14-35+
  // Always assume duplicates if they aren't passed to the comparison function
  support.detectDuplicates = !!hasDuplicate;
  
  // Initialize against the default document
  setDocument();
  
  // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
  // Detached nodes confoundingly follow *each other*
  support.sortDetached = assert(function( div1 ) {
  	// Should return 1, but returns 4 (following)
  	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
  });
  
  // Support: IE<8
  // Prevent attribute/property "interpolation"
  // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
  if ( !assert(function( div ) {
  	div.innerHTML = "<a href='#'></a>";
  	return div.firstChild.getAttribute("href") === "#" ;
  }) ) {
  	addHandle( "type|href|height|width", function( elem, name, isXML ) {
  		if ( !isXML ) {
  			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
  		}
  	});
  }
  
  // Support: IE<9
  // Use defaultValue in place of getAttribute("value")
  if ( !support.attributes || !assert(function( div ) {
  	div.innerHTML = "<input/>";
  	div.firstChild.setAttribute( "value", "" );
  	return div.firstChild.getAttribute( "value" ) === "";
  }) ) {
  	addHandle( "value", function( elem, name, isXML ) {
  		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
  			return elem.defaultValue;
  		}
  	});
  }
  
  // Support: IE<9
  // Use getAttributeNode to fetch booleans when getAttribute lies
  if ( !assert(function( div ) {
  	return div.getAttribute("disabled") == null;
  }) ) {
  	addHandle( booleans, function( elem, name, isXML ) {
  		var val;
  		if ( !isXML ) {
  			return elem[ name ] === true ? name.toLowerCase() :
  					(val = elem.getAttributeNode( name )) && val.specified ?
  					val.value :
  				null;
  		}
  	});
  }
  
  return Sizzle;
  
  })( window );
  
  
  
  jQuery.find = Sizzle;
  jQuery.expr = Sizzle.selectors;
  jQuery.expr[ ":" ] = jQuery.expr.pseudos;
  jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
  jQuery.text = Sizzle.getText;
  jQuery.isXMLDoc = Sizzle.isXML;
  jQuery.contains = Sizzle.contains;
  
  
  
  var dir = function( elem, dir, until ) {
  	var matched = [],
  		truncate = until !== undefined;
  
  	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
  		if ( elem.nodeType === 1 ) {
  			if ( truncate && jQuery( elem ).is( until ) ) {
  				break;
  			}
  			matched.push( elem );
  		}
  	}
  	return matched;
  };
  
  
  var siblings = function( n, elem ) {
  	var matched = [];
  
  	for ( ; n; n = n.nextSibling ) {
  		if ( n.nodeType === 1 && n !== elem ) {
  			matched.push( n );
  		}
  	}
  
  	return matched;
  };
  
  
  var rneedsContext = jQuery.expr.match.needsContext;
  
  var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
  
  
  
  var risSimple = /^.[^:#\[\.,]*$/;
  
  // Implement the identical functionality for filter and not
  function winnow( elements, qualifier, not ) {
  	if ( jQuery.isFunction( qualifier ) ) {
  		return jQuery.grep( elements, function( elem, i ) {
  			/* jshint -W018 */
  			return !!qualifier.call( elem, i, elem ) !== not;
  		} );
  
  	}
  
  	if ( qualifier.nodeType ) {
  		return jQuery.grep( elements, function( elem ) {
  			return ( elem === qualifier ) !== not;
  		} );
  
  	}
  
  	if ( typeof qualifier === "string" ) {
  		if ( risSimple.test( qualifier ) ) {
  			return jQuery.filter( qualifier, elements, not );
  		}
  
  		qualifier = jQuery.filter( qualifier, elements );
  	}
  
  	return jQuery.grep( elements, function( elem ) {
  		return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
  	} );
  }
  
  jQuery.filter = function( expr, elems, not ) {
  	var elem = elems[ 0 ];
  
  	if ( not ) {
  		expr = ":not(" + expr + ")";
  	}
  
  	return elems.length === 1 && elem.nodeType === 1 ?
  		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
  		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
  			return elem.nodeType === 1;
  		} ) );
  };
  
  jQuery.fn.extend( {
  	find: function( selector ) {
  		var i,
  			len = this.length,
  			ret = [],
  			self = this;
  
  		if ( typeof selector !== "string" ) {
  			return this.pushStack( jQuery( selector ).filter( function() {
  				for ( i = 0; i < len; i++ ) {
  					if ( jQuery.contains( self[ i ], this ) ) {
  						return true;
  					}
  				}
  			} ) );
  		}
  
  		for ( i = 0; i < len; i++ ) {
  			jQuery.find( selector, self[ i ], ret );
  		}
  
  		// Needed because $( selector, context ) becomes $( context ).find( selector )
  		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
  		ret.selector = this.selector ? this.selector + " " + selector : selector;
  		return ret;
  	},
  	filter: function( selector ) {
  		return this.pushStack( winnow( this, selector || [], false ) );
  	},
  	not: function( selector ) {
  		return this.pushStack( winnow( this, selector || [], true ) );
  	},
  	is: function( selector ) {
  		return !!winnow(
  			this,
  
  			// If this is a positional/relative selector, check membership in the returned set
  			// so $("p:first").is("p:last") won't return true for a doc with two "p".
  			typeof selector === "string" && rneedsContext.test( selector ) ?
  				jQuery( selector ) :
  				selector || [],
  			false
  		).length;
  	}
  } );
  
  
  // Initialize a jQuery object
  
  
  // A central reference to the root jQuery(document)
  var rootjQuery,
  
  	// A simple way to check for HTML strings
  	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
  	// Strict HTML recognition (#11290: must start with <)
  	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
  
  	init = jQuery.fn.init = function( selector, context, root ) {
  		var match, elem;
  
  		// HANDLE: $(""), $(null), $(undefined), $(false)
  		if ( !selector ) {
  			return this;
  		}
  
  		// Method init() accepts an alternate rootjQuery
  		// so migrate can support jQuery.sub (gh-2101)
  		root = root || rootjQuery;
  
  		// Handle HTML strings
  		if ( typeof selector === "string" ) {
  			if ( selector[ 0 ] === "<" &&
  				selector[ selector.length - 1 ] === ">" &&
  				selector.length >= 3 ) {
  
  				// Assume that strings that start and end with <> are HTML and skip the regex check
  				match = [ null, selector, null ];
  
  			} else {
  				match = rquickExpr.exec( selector );
  			}
  
  			// Match html or make sure no context is specified for #id
  			if ( match && ( match[ 1 ] || !context ) ) {
  
  				// HANDLE: $(html) -> $(array)
  				if ( match[ 1 ] ) {
  					context = context instanceof jQuery ? context[ 0 ] : context;
  
  					// Option to run scripts is true for back-compat
  					// Intentionally let the error be thrown if parseHTML is not present
  					jQuery.merge( this, jQuery.parseHTML(
  						match[ 1 ],
  						context && context.nodeType ? context.ownerDocument || context : document,
  						true
  					) );
  
  					// HANDLE: $(html, props)
  					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
  						for ( match in context ) {
  
  							// Properties of context are called as methods if possible
  							if ( jQuery.isFunction( this[ match ] ) ) {
  								this[ match ]( context[ match ] );
  
  							// ...and otherwise set as attributes
  							} else {
  								this.attr( match, context[ match ] );
  							}
  						}
  					}
  
  					return this;
  
  				// HANDLE: $(#id)
  				} else {
  					elem = document.getElementById( match[ 2 ] );
  
  					// Support: Blackberry 4.6
  					// gEBID returns nodes no longer in the document (#6963)
  					if ( elem && elem.parentNode ) {
  
  						// Inject the element directly into the jQuery object
  						this.length = 1;
  						this[ 0 ] = elem;
  					}
  
  					this.context = document;
  					this.selector = selector;
  					return this;
  				}
  
  			// HANDLE: $(expr, $(...))
  			} else if ( !context || context.jquery ) {
  				return ( context || root ).find( selector );
  
  			// HANDLE: $(expr, context)
  			// (which is just equivalent to: $(context).find(expr)
  			} else {
  				return this.constructor( context ).find( selector );
  			}
  
  		// HANDLE: $(DOMElement)
  		} else if ( selector.nodeType ) {
  			this.context = this[ 0 ] = selector;
  			this.length = 1;
  			return this;
  
  		// HANDLE: $(function)
  		// Shortcut for document ready
  		} else if ( jQuery.isFunction( selector ) ) {
  			return root.ready !== undefined ?
  				root.ready( selector ) :
  
  				// Execute immediately if ready is not present
  				selector( jQuery );
  		}
  
  		if ( selector.selector !== undefined ) {
  			this.selector = selector.selector;
  			this.context = selector.context;
  		}
  
  		return jQuery.makeArray( selector, this );
  	};
  
  // Give the init function the jQuery prototype for later instantiation
  init.prototype = jQuery.fn;
  
  // Initialize central reference
  rootjQuery = jQuery( document );
  
  
  var rparentsprev = /^(?:parents|prev(?:Until|All))/,
  
  	// Methods guaranteed to produce a unique set when starting from a unique set
  	guaranteedUnique = {
  		children: true,
  		contents: true,
  		next: true,
  		prev: true
  	};
  
  jQuery.fn.extend( {
  	has: function( target ) {
  		var targets = jQuery( target, this ),
  			l = targets.length;
  
  		return this.filter( function() {
  			var i = 0;
  			for ( ; i < l; i++ ) {
  				if ( jQuery.contains( this, targets[ i ] ) ) {
  					return true;
  				}
  			}
  		} );
  	},
  
  	closest: function( selectors, context ) {
  		var cur,
  			i = 0,
  			l = this.length,
  			matched = [],
  			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
  				jQuery( selectors, context || this.context ) :
  				0;
  
  		for ( ; i < l; i++ ) {
  			for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
  
  				// Always skip document fragments
  				if ( cur.nodeType < 11 && ( pos ?
  					pos.index( cur ) > -1 :
  
  					// Don't pass non-elements to Sizzle
  					cur.nodeType === 1 &&
  						jQuery.find.matchesSelector( cur, selectors ) ) ) {
  
  					matched.push( cur );
  					break;
  				}
  			}
  		}
  
  		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
  	},
  
  	// Determine the position of an element within the set
  	index: function( elem ) {
  
  		// No argument, return index in parent
  		if ( !elem ) {
  			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
  		}
  
  		// Index in selector
  		if ( typeof elem === "string" ) {
  			return indexOf.call( jQuery( elem ), this[ 0 ] );
  		}
  
  		// Locate the position of the desired element
  		return indexOf.call( this,
  
  			// If it receives a jQuery object, the first element is used
  			elem.jquery ? elem[ 0 ] : elem
  		);
  	},
  
  	add: function( selector, context ) {
  		return this.pushStack(
  			jQuery.uniqueSort(
  				jQuery.merge( this.get(), jQuery( selector, context ) )
  			)
  		);
  	},
  
  	addBack: function( selector ) {
  		return this.add( selector == null ?
  			this.prevObject : this.prevObject.filter( selector )
  		);
  	}
  } );
  
  function sibling( cur, dir ) {
  	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
  	return cur;
  }
  
  jQuery.each( {
  	parent: function( elem ) {
  		var parent = elem.parentNode;
  		return parent && parent.nodeType !== 11 ? parent : null;
  	},
  	parents: function( elem ) {
  		return dir( elem, "parentNode" );
  	},
  	parentsUntil: function( elem, i, until ) {
  		return dir( elem, "parentNode", until );
  	},
  	next: function( elem ) {
  		return sibling( elem, "nextSibling" );
  	},
  	prev: function( elem ) {
  		return sibling( elem, "previousSibling" );
  	},
  	nextAll: function( elem ) {
  		return dir( elem, "nextSibling" );
  	},
  	prevAll: function( elem ) {
  		return dir( elem, "previousSibling" );
  	},
  	nextUntil: function( elem, i, until ) {
  		return dir( elem, "nextSibling", until );
  	},
  	prevUntil: function( elem, i, until ) {
  		return dir( elem, "previousSibling", until );
  	},
  	siblings: function( elem ) {
  		return siblings( ( elem.parentNode || {} ).firstChild, elem );
  	},
  	children: function( elem ) {
  		return siblings( elem.firstChild );
  	},
  	contents: function( elem ) {
  		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
  	}
  }, function( name, fn ) {
  	jQuery.fn[ name ] = function( until, selector ) {
  		var matched = jQuery.map( this, fn, until );
  
  		if ( name.slice( -5 ) !== "Until" ) {
  			selector = until;
  		}
  
  		if ( selector && typeof selector === "string" ) {
  			matched = jQuery.filter( selector, matched );
  		}
  
  		if ( this.length > 1 ) {
  
  			// Remove duplicates
  			if ( !guaranteedUnique[ name ] ) {
  				jQuery.uniqueSort( matched );
  			}
  
  			// Reverse order for parents* and prev-derivatives
  			if ( rparentsprev.test( name ) ) {
  				matched.reverse();
  			}
  		}
  
  		return this.pushStack( matched );
  	};
  } );
  var rnotwhite = ( /\S+/g );
  
  
  
  // Convert String-formatted options into Object-formatted ones
  function createOptions( options ) {
  	var object = {};
  	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
  		object[ flag ] = true;
  	} );
  	return object;
  }
  
  /*
   * Create a callback list using the following parameters:
   *
   *	options: an optional list of space-separated options that will change how
   *			the callback list behaves or a more traditional option object
   *
   * By default a callback list will act like an event callback list and can be
   * "fired" multiple times.
   *
   * Possible options:
   *
   *	once:			will ensure the callback list can only be fired once (like a Deferred)
   *
   *	memory:			will keep track of previous values and will call any callback added
   *					after the list has been fired right away with the latest "memorized"
   *					values (like a Deferred)
   *
   *	unique:			will ensure a callback can only be added once (no duplicate in the list)
   *
   *	stopOnFalse:	interrupt callings when a callback returns false
   *
   */
  jQuery.Callbacks = function( options ) {
  
  	// Convert options from String-formatted to Object-formatted if needed
  	// (we check in cache first)
  	options = typeof options === "string" ?
  		createOptions( options ) :
  		jQuery.extend( {}, options );
  
  	var // Flag to know if list is currently firing
  		firing,
  
  		// Last fire value for non-forgettable lists
  		memory,
  
  		// Flag to know if list was already fired
  		fired,
  
  		// Flag to prevent firing
  		locked,
  
  		// Actual callback list
  		list = [],
  
  		// Queue of execution data for repeatable lists
  		queue = [],
  
  		// Index of currently firing callback (modified by add/remove as needed)
  		firingIndex = -1,
  
  		// Fire callbacks
  		fire = function() {
  
  			// Enforce single-firing
  			locked = options.once;
  
  			// Execute callbacks for all pending executions,
  			// respecting firingIndex overrides and runtime changes
  			fired = firing = true;
  			for ( ; queue.length; firingIndex = -1 ) {
  				memory = queue.shift();
  				while ( ++firingIndex < list.length ) {
  
  					// Run callback and check for early termination
  					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
  						options.stopOnFalse ) {
  
  						// Jump to end and forget the data so .add doesn't re-fire
  						firingIndex = list.length;
  						memory = false;
  					}
  				}
  			}
  
  			// Forget the data if we're done with it
  			if ( !options.memory ) {
  				memory = false;
  			}
  
  			firing = false;
  
  			// Clean up if we're done firing for good
  			if ( locked ) {
  
  				// Keep an empty list if we have data for future add calls
  				if ( memory ) {
  					list = [];
  
  				// Otherwise, this object is spent
  				} else {
  					list = "";
  				}
  			}
  		},
  
  		// Actual Callbacks object
  		self = {
  
  			// Add a callback or a collection of callbacks to the list
  			add: function() {
  				if ( list ) {
  
  					// If we have memory from a past run, we should fire after adding
  					if ( memory && !firing ) {
  						firingIndex = list.length - 1;
  						queue.push( memory );
  					}
  
  					( function add( args ) {
  						jQuery.each( args, function( _, arg ) {
  							if ( jQuery.isFunction( arg ) ) {
  								if ( !options.unique || !self.has( arg ) ) {
  									list.push( arg );
  								}
  							} else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
  
  								// Inspect recursively
  								add( arg );
  							}
  						} );
  					} )( arguments );
  
  					if ( memory && !firing ) {
  						fire();
  					}
  				}
  				return this;
  			},
  
  			// Remove a callback from the list
  			remove: function() {
  				jQuery.each( arguments, function( _, arg ) {
  					var index;
  					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
  						list.splice( index, 1 );
  
  						// Handle firing indexes
  						if ( index <= firingIndex ) {
  							firingIndex--;
  						}
  					}
  				} );
  				return this;
  			},
  
  			// Check if a given callback is in the list.
  			// If no argument is given, return whether or not list has callbacks attached.
  			has: function( fn ) {
  				return fn ?
  					jQuery.inArray( fn, list ) > -1 :
  					list.length > 0;
  			},
  
  			// Remove all callbacks from the list
  			empty: function() {
  				if ( list ) {
  					list = [];
  				}
  				return this;
  			},
  
  			// Disable .fire and .add
  			// Abort any current/pending executions
  			// Clear all callbacks and values
  			disable: function() {
  				locked = queue = [];
  				list = memory = "";
  				return this;
  			},
  			disabled: function() {
  				return !list;
  			},
  
  			// Disable .fire
  			// Also disable .add unless we have memory (since it would have no effect)
  			// Abort any pending executions
  			lock: function() {
  				locked = queue = [];
  				if ( !memory ) {
  					list = memory = "";
  				}
  				return this;
  			},
  			locked: function() {
  				return !!locked;
  			},
  
  			// Call all callbacks with the given context and arguments
  			fireWith: function( context, args ) {
  				if ( !locked ) {
  					args = args || [];
  					args = [ context, args.slice ? args.slice() : args ];
  					queue.push( args );
  					if ( !firing ) {
  						fire();
  					}
  				}
  				return this;
  			},
  
  			// Call all the callbacks with the given arguments
  			fire: function() {
  				self.fireWith( this, arguments );
  				return this;
  			},
  
  			// To know if the callbacks have already been called at least once
  			fired: function() {
  				return !!fired;
  			}
  		};
  
  	return self;
  };
  
  
  jQuery.extend( {
  
  	Deferred: function( func ) {
  		var tuples = [
  
  				// action, add listener, listener list, final state
  				[ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ],
  				[ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ],
  				[ "notify", "progress", jQuery.Callbacks( "memory" ) ]
  			],
  			state = "pending",
  			promise = {
  				state: function() {
  					return state;
  				},
  				always: function() {
  					deferred.done( arguments ).fail( arguments );
  					return this;
  				},
  				then: function( /* fnDone, fnFail, fnProgress */ ) {
  					var fns = arguments;
  					return jQuery.Deferred( function( newDefer ) {
  						jQuery.each( tuples, function( i, tuple ) {
  							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
  
  							// deferred[ done | fail | progress ] for forwarding actions to newDefer
  							deferred[ tuple[ 1 ] ]( function() {
  								var returned = fn && fn.apply( this, arguments );
  								if ( returned && jQuery.isFunction( returned.promise ) ) {
  									returned.promise()
  										.progress( newDefer.notify )
  										.done( newDefer.resolve )
  										.fail( newDefer.reject );
  								} else {
  									newDefer[ tuple[ 0 ] + "With" ](
  										this === promise ? newDefer.promise() : this,
  										fn ? [ returned ] : arguments
  									);
  								}
  							} );
  						} );
  						fns = null;
  					} ).promise();
  				},
  
  				// Get a promise for this deferred
  				// If obj is provided, the promise aspect is added to the object
  				promise: function( obj ) {
  					return obj != null ? jQuery.extend( obj, promise ) : promise;
  				}
  			},
  			deferred = {};
  
  		// Keep pipe for back-compat
  		promise.pipe = promise.then;
  
  		// Add list-specific methods
  		jQuery.each( tuples, function( i, tuple ) {
  			var list = tuple[ 2 ],
  				stateString = tuple[ 3 ];
  
  			// promise[ done | fail | progress ] = list.add
  			promise[ tuple[ 1 ] ] = list.add;
  
  			// Handle state
  			if ( stateString ) {
  				list.add( function() {
  
  					// state = [ resolved | rejected ]
  					state = stateString;
  
  				// [ reject_list | resolve_list ].disable; progress_list.lock
  				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
  			}
  
  			// deferred[ resolve | reject | notify ]
  			deferred[ tuple[ 0 ] ] = function() {
  				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
  				return this;
  			};
  			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
  		} );
  
  		// Make the deferred a promise
  		promise.promise( deferred );
  
  		// Call given func if any
  		if ( func ) {
  			func.call( deferred, deferred );
  		}
  
  		// All done!
  		return deferred;
  	},
  
  	// Deferred helper
  	when: function( subordinate /* , ..., subordinateN */ ) {
  		var i = 0,
  			resolveValues = slice.call( arguments ),
  			length = resolveValues.length,
  
  			// the count of uncompleted subordinates
  			remaining = length !== 1 ||
  				( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
  
  			// the master Deferred.
  			// If resolveValues consist of only a single Deferred, just use that.
  			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
  
  			// Update function for both resolve and progress values
  			updateFunc = function( i, contexts, values ) {
  				return function( value ) {
  					contexts[ i ] = this;
  					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
  					if ( values === progressValues ) {
  						deferred.notifyWith( contexts, values );
  					} else if ( !( --remaining ) ) {
  						deferred.resolveWith( contexts, values );
  					}
  				};
  			},
  
  			progressValues, progressContexts, resolveContexts;
  
  		// Add listeners to Deferred subordinates; treat others as resolved
  		if ( length > 1 ) {
  			progressValues = new Array( length );
  			progressContexts = new Array( length );
  			resolveContexts = new Array( length );
  			for ( ; i < length; i++ ) {
  				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
  					resolveValues[ i ].promise()
  						.progress( updateFunc( i, progressContexts, progressValues ) )
  						.done( updateFunc( i, resolveContexts, resolveValues ) )
  						.fail( deferred.reject );
  				} else {
  					--remaining;
  				}
  			}
  		}
  
  		// If we're not waiting on anything, resolve the master
  		if ( !remaining ) {
  			deferred.resolveWith( resolveContexts, resolveValues );
  		}
  
  		return deferred.promise();
  	}
  } );
  
  
  // The deferred used on DOM ready
  var readyList;
  
  jQuery.fn.ready = function( fn ) {
  
  	// Add the callback
  	jQuery.ready.promise().done( fn );
  
  	return this;
  };
  
  jQuery.extend( {
  
  	// Is the DOM ready to be used? Set to true once it occurs.
  	isReady: false,
  
  	// A counter to track how many items to wait for before
  	// the ready event fires. See #6781
  	readyWait: 1,
  
  	// Hold (or release) the ready event
  	holdReady: function( hold ) {
  		if ( hold ) {
  			jQuery.readyWait++;
  		} else {
  			jQuery.ready( true );
  		}
  	},
  
  	// Handle when the DOM is ready
  	ready: function( wait ) {
  
  		// Abort if there are pending holds or we're already ready
  		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
  			return;
  		}
  
  		// Remember that the DOM is ready
  		jQuery.isReady = true;
  
  		// If a normal DOM Ready event fired, decrement, and wait if need be
  		if ( wait !== true && --jQuery.readyWait > 0 ) {
  			return;
  		}
  
  		// If there are functions bound, to execute
  		readyList.resolveWith( document, [ jQuery ] );
  
  		// Trigger any bound ready events
  		if ( jQuery.fn.triggerHandler ) {
  			jQuery( document ).triggerHandler( "ready" );
  			jQuery( document ).off( "ready" );
  		}
  	}
  } );
  
  /**
   * The ready event handler and self cleanup method
   */
  function completed() {
  	document.removeEventListener( "DOMContentLoaded", completed );
  	window.removeEventListener( "load", completed );
  	jQuery.ready();
  }
  
  jQuery.ready.promise = function( obj ) {
  	if ( !readyList ) {
  
  		readyList = jQuery.Deferred();
  
  		// Catch cases where $(document).ready() is called
  		// after the browser event has already occurred.
  		// Support: IE9-10 only
  		// Older IE sometimes signals "interactive" too soon
  		if ( document.readyState === "complete" ||
  			( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
  
  			// Handle it asynchronously to allow scripts the opportunity to delay ready
  			window.setTimeout( jQuery.ready );
  
  		} else {
  
  			// Use the handy event callback
  			document.addEventListener( "DOMContentLoaded", completed );
  
  			// A fallback to window.onload, that will always work
  			window.addEventListener( "load", completed );
  		}
  	}
  	return readyList.promise( obj );
  };
  
  // Kick off the DOM ready check even if the user does not
  jQuery.ready.promise();
  
  
  
  
  // Multifunctional method to get and set values of a collection
  // The value/s can optionally be executed if it's a function
  var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
  	var i = 0,
  		len = elems.length,
  		bulk = key == null;
  
  	// Sets many values
  	if ( jQuery.type( key ) === "object" ) {
  		chainable = true;
  		for ( i in key ) {
  			access( elems, fn, i, key[ i ], true, emptyGet, raw );
  		}
  
  	// Sets one value
  	} else if ( value !== undefined ) {
  		chainable = true;
  
  		if ( !jQuery.isFunction( value ) ) {
  			raw = true;
  		}
  
  		if ( bulk ) {
  
  			// Bulk operations run against the entire set
  			if ( raw ) {
  				fn.call( elems, value );
  				fn = null;
  
  			// ...except when executing function values
  			} else {
  				bulk = fn;
  				fn = function( elem, key, value ) {
  					return bulk.call( jQuery( elem ), value );
  				};
  			}
  		}
  
  		if ( fn ) {
  			for ( ; i < len; i++ ) {
  				fn(
  					elems[ i ], key, raw ?
  					value :
  					value.call( elems[ i ], i, fn( elems[ i ], key ) )
  				);
  			}
  		}
  	}
  
  	return chainable ?
  		elems :
  
  		// Gets
  		bulk ?
  			fn.call( elems ) :
  			len ? fn( elems[ 0 ], key ) : emptyGet;
  };
  var acceptData = function( owner ) {
  
  	// Accepts only:
  	//  - Node
  	//    - Node.ELEMENT_NODE
  	//    - Node.DOCUMENT_NODE
  	//  - Object
  	//    - Any
  	/* jshint -W018 */
  	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
  };
  
  
  
  
  function Data() {
  	this.expando = jQuery.expando + Data.uid++;
  }
  
  Data.uid = 1;
  
  Data.prototype = {
  
  	register: function( owner, initial ) {
  		var value = initial || {};
  
  		// If it is a node unlikely to be stringify-ed or looped over
  		// use plain assignment
  		if ( owner.nodeType ) {
  			owner[ this.expando ] = value;
  
  		// Otherwise secure it in a non-enumerable, non-writable property
  		// configurability must be true to allow the property to be
  		// deleted with the delete operator
  		} else {
  			Object.defineProperty( owner, this.expando, {
  				value: value,
  				writable: true,
  				configurable: true
  			} );
  		}
  		return owner[ this.expando ];
  	},
  	cache: function( owner ) {
  
  		// We can accept data for non-element nodes in modern browsers,
  		// but we should not, see #8335.
  		// Always return an empty object.
  		if ( !acceptData( owner ) ) {
  			return {};
  		}
  
  		// Check if the owner object already has a cache
  		var value = owner[ this.expando ];
  
  		// If not, create one
  		if ( !value ) {
  			value = {};
  
  			// We can accept data for non-element nodes in modern browsers,
  			// but we should not, see #8335.
  			// Always return an empty object.
  			if ( acceptData( owner ) ) {
  
  				// If it is a node unlikely to be stringify-ed or looped over
  				// use plain assignment
  				if ( owner.nodeType ) {
  					owner[ this.expando ] = value;
  
  				// Otherwise secure it in a non-enumerable property
  				// configurable must be true to allow the property to be
  				// deleted when data is removed
  				} else {
  					Object.defineProperty( owner, this.expando, {
  						value: value,
  						configurable: true
  					} );
  				}
  			}
  		}
  
  		return value;
  	},
  	set: function( owner, data, value ) {
  		var prop,
  			cache = this.cache( owner );
  
  		// Handle: [ owner, key, value ] args
  		if ( typeof data === "string" ) {
  			cache[ data ] = value;
  
  		// Handle: [ owner, { properties } ] args
  		} else {
  
  			// Copy the properties one-by-one to the cache object
  			for ( prop in data ) {
  				cache[ prop ] = data[ prop ];
  			}
  		}
  		return cache;
  	},
  	get: function( owner, key ) {
  		return key === undefined ?
  			this.cache( owner ) :
  			owner[ this.expando ] && owner[ this.expando ][ key ];
  	},
  	access: function( owner, key, value ) {
  		var stored;
  
  		// In cases where either:
  		//
  		//   1. No key was specified
  		//   2. A string key was specified, but no value provided
  		//
  		// Take the "read" path and allow the get method to determine
  		// which value to return, respectively either:
  		//
  		//   1. The entire cache object
  		//   2. The data stored at the key
  		//
  		if ( key === undefined ||
  				( ( key && typeof key === "string" ) && value === undefined ) ) {
  
  			stored = this.get( owner, key );
  
  			return stored !== undefined ?
  				stored : this.get( owner, jQuery.camelCase( key ) );
  		}
  
  		// When the key is not a string, or both a key and value
  		// are specified, set or extend (existing objects) with either:
  		//
  		//   1. An object of properties
  		//   2. A key and value
  		//
  		this.set( owner, key, value );
  
  		// Since the "set" path can have two possible entry points
  		// return the expected data based on which path was taken[*]
  		return value !== undefined ? value : key;
  	},
  	remove: function( owner, key ) {
  		var i, name, camel,
  			cache = owner[ this.expando ];
  
  		if ( cache === undefined ) {
  			return;
  		}
  
  		if ( key === undefined ) {
  			this.register( owner );
  
  		} else {
  
  			// Support array or space separated string of keys
  			if ( jQuery.isArray( key ) ) {
  
  				// If "name" is an array of keys...
  				// When data is initially created, via ("key", "val") signature,
  				// keys will be converted to camelCase.
  				// Since there is no way to tell _how_ a key was added, remove
  				// both plain key and camelCase key. #12786
  				// This will only penalize the array argument path.
  				name = key.concat( key.map( jQuery.camelCase ) );
  			} else {
  				camel = jQuery.camelCase( key );
  
  				// Try the string as a key before any manipulation
  				if ( key in cache ) {
  					name = [ key, camel ];
  				} else {
  
  					// If a key with the spaces exists, use it.
  					// Otherwise, create an array by matching non-whitespace
  					name = camel;
  					name = name in cache ?
  						[ name ] : ( name.match( rnotwhite ) || [] );
  				}
  			}
  
  			i = name.length;
  
  			while ( i-- ) {
  				delete cache[ name[ i ] ];
  			}
  		}
  
  		// Remove the expando if there's no more data
  		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
  
  			// Support: Chrome <= 35-45+
  			// Webkit & Blink performance suffers when deleting properties
  			// from DOM nodes, so set to undefined instead
  			// https://code.google.com/p/chromium/issues/detail?id=378607
  			if ( owner.nodeType ) {
  				owner[ this.expando ] = undefined;
  			} else {
  				delete owner[ this.expando ];
  			}
  		}
  	},
  	hasData: function( owner ) {
  		var cache = owner[ this.expando ];
  		return cache !== undefined && !jQuery.isEmptyObject( cache );
  	}
  };
  var dataPriv = new Data();
  
  var dataUser = new Data();
  
  
  
  //	Implementation Summary
  //
  //	1. Enforce API surface and semantic compatibility with 1.9.x branch
  //	2. Improve the module's maintainability by reducing the storage
  //		paths to a single mechanism.
  //	3. Use the same single mechanism to support "private" and "user" data.
  //	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
  //	5. Avoid exposing implementation details on user objects (eg. expando properties)
  //	6. Provide a clear path for implementation upgrade to WeakMap in 2014
  
  var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
  	rmultiDash = /[A-Z]/g;
  
  function dataAttr( elem, key, data ) {
  	var name;
  
  	// If nothing was found internally, try to fetch any
  	// data from the HTML5 data-* attribute
  	if ( data === undefined && elem.nodeType === 1 ) {
  		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
  		data = elem.getAttribute( name );
  
  		if ( typeof data === "string" ) {
  			try {
  				data = data === "true" ? true :
  					data === "false" ? false :
  					data === "null" ? null :
  
  					// Only convert to a number if it doesn't change the string
  					+data + "" === data ? +data :
  					rbrace.test( data ) ? jQuery.parseJSON( data ) :
  					data;
  			} catch ( e ) {}
  
  			// Make sure we set the data so it isn't changed later
  			dataUser.set( elem, key, data );
  		} else {
  			data = undefined;
  		}
  	}
  	return data;
  }
  
  jQuery.extend( {
  	hasData: function( elem ) {
  		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
  	},
  
  	data: function( elem, name, data ) {
  		return dataUser.access( elem, name, data );
  	},
  
  	removeData: function( elem, name ) {
  		dataUser.remove( elem, name );
  	},
  
  	// TODO: Now that all calls to _data and _removeData have been replaced
  	// with direct calls to dataPriv methods, these can be deprecated.
  	_data: function( elem, name, data ) {
  		return dataPriv.access( elem, name, data );
  	},
  
  	_removeData: function( elem, name ) {
  		dataPriv.remove( elem, name );
  	}
  } );
  
  jQuery.fn.extend( {
  	data: function( key, value ) {
  		var i, name, data,
  			elem = this[ 0 ],
  			attrs = elem && elem.attributes;
  
  		// Gets all values
  		if ( key === undefined ) {
  			if ( this.length ) {
  				data = dataUser.get( elem );
  
  				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
  					i = attrs.length;
  					while ( i-- ) {
  
  						// Support: IE11+
  						// The attrs elements can be null (#14894)
  						if ( attrs[ i ] ) {
  							name = attrs[ i ].name;
  							if ( name.indexOf( "data-" ) === 0 ) {
  								name = jQuery.camelCase( name.slice( 5 ) );
  								dataAttr( elem, name, data[ name ] );
  							}
  						}
  					}
  					dataPriv.set( elem, "hasDataAttrs", true );
  				}
  			}
  
  			return data;
  		}
  
  		// Sets multiple values
  		if ( typeof key === "object" ) {
  			return this.each( function() {
  				dataUser.set( this, key );
  			} );
  		}
  
  		return access( this, function( value ) {
  			var data, camelKey;
  
  			// The calling jQuery object (element matches) is not empty
  			// (and therefore has an element appears at this[ 0 ]) and the
  			// `value` parameter was not undefined. An empty jQuery object
  			// will result in `undefined` for elem = this[ 0 ] which will
  			// throw an exception if an attempt to read a data cache is made.
  			if ( elem && value === undefined ) {
  
  				// Attempt to get data from the cache
  				// with the key as-is
  				data = dataUser.get( elem, key ) ||
  
  					// Try to find dashed key if it exists (gh-2779)
  					// This is for 2.2.x only
  					dataUser.get( elem, key.replace( rmultiDash, "-$&" ).toLowerCase() );
  
  				if ( data !== undefined ) {
  					return data;
  				}
  
  				camelKey = jQuery.camelCase( key );
  
  				// Attempt to get data from the cache
  				// with the key camelized
  				data = dataUser.get( elem, camelKey );
  				if ( data !== undefined ) {
  					return data;
  				}
  
  				// Attempt to "discover" the data in
  				// HTML5 custom data-* attrs
  				data = dataAttr( elem, camelKey, undefined );
  				if ( data !== undefined ) {
  					return data;
  				}
  
  				// We tried really hard, but the data doesn't exist.
  				return;
  			}
  
  			// Set the data...
  			camelKey = jQuery.camelCase( key );
  			this.each( function() {
  
  				// First, attempt to store a copy or reference of any
  				// data that might've been store with a camelCased key.
  				var data = dataUser.get( this, camelKey );
  
  				// For HTML5 data-* attribute interop, we have to
  				// store property names with dashes in a camelCase form.
  				// This might not apply to all properties...*
  				dataUser.set( this, camelKey, value );
  
  				// *... In the case of properties that might _actually_
  				// have dashes, we need to also store a copy of that
  				// unchanged property.
  				if ( key.indexOf( "-" ) > -1 && data !== undefined ) {
  					dataUser.set( this, key, value );
  				}
  			} );
  		}, null, value, arguments.length > 1, null, true );
  	},
  
  	removeData: function( key ) {
  		return this.each( function() {
  			dataUser.remove( this, key );
  		} );
  	}
  } );
  
  
  jQuery.extend( {
  	queue: function( elem, type, data ) {
  		var queue;
  
  		if ( elem ) {
  			type = ( type || "fx" ) + "queue";
  			queue = dataPriv.get( elem, type );
  
  			// Speed up dequeue by getting out quickly if this is just a lookup
  			if ( data ) {
  				if ( !queue || jQuery.isArray( data ) ) {
  					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
  				} else {
  					queue.push( data );
  				}
  			}
  			return queue || [];
  		}
  	},
  
  	dequeue: function( elem, type ) {
  		type = type || "fx";
  
  		var queue = jQuery.queue( elem, type ),
  			startLength = queue.length,
  			fn = queue.shift(),
  			hooks = jQuery._queueHooks( elem, type ),
  			next = function() {
  				jQuery.dequeue( elem, type );
  			};
  
  		// If the fx queue is dequeued, always remove the progress sentinel
  		if ( fn === "inprogress" ) {
  			fn = queue.shift();
  			startLength--;
  		}
  
  		if ( fn ) {
  
  			// Add a progress sentinel to prevent the fx queue from being
  			// automatically dequeued
  			if ( type === "fx" ) {
  				queue.unshift( "inprogress" );
  			}
  
  			// Clear up the last queue stop function
  			delete hooks.stop;
  			fn.call( elem, next, hooks );
  		}
  
  		if ( !startLength && hooks ) {
  			hooks.empty.fire();
  		}
  	},
  
  	// Not public - generate a queueHooks object, or return the current one
  	_queueHooks: function( elem, type ) {
  		var key = type + "queueHooks";
  		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
  			empty: jQuery.Callbacks( "once memory" ).add( function() {
  				dataPriv.remove( elem, [ type + "queue", key ] );
  			} )
  		} );
  	}
  } );
  
  jQuery.fn.extend( {
  	queue: function( type, data ) {
  		var setter = 2;
  
  		if ( typeof type !== "string" ) {
  			data = type;
  			type = "fx";
  			setter--;
  		}
  
  		if ( arguments.length < setter ) {
  			return jQuery.queue( this[ 0 ], type );
  		}
  
  		return data === undefined ?
  			this :
  			this.each( function() {
  				var queue = jQuery.queue( this, type, data );
  
  				// Ensure a hooks for this queue
  				jQuery._queueHooks( this, type );
  
  				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
  					jQuery.dequeue( this, type );
  				}
  			} );
  	},
  	dequeue: function( type ) {
  		return this.each( function() {
  			jQuery.dequeue( this, type );
  		} );
  	},
  	clearQueue: function( type ) {
  		return this.queue( type || "fx", [] );
  	},
  
  	// Get a promise resolved when queues of a certain type
  	// are emptied (fx is the type by default)
  	promise: function( type, obj ) {
  		var tmp,
  			count = 1,
  			defer = jQuery.Deferred(),
  			elements = this,
  			i = this.length,
  			resolve = function() {
  				if ( !( --count ) ) {
  					defer.resolveWith( elements, [ elements ] );
  				}
  			};
  
  		if ( typeof type !== "string" ) {
  			obj = type;
  			type = undefined;
  		}
  		type = type || "fx";
  
  		while ( i-- ) {
  			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
  			if ( tmp && tmp.empty ) {
  				count++;
  				tmp.empty.add( resolve );
  			}
  		}
  		resolve();
  		return defer.promise( obj );
  	}
  } );
  var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
  
  var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
  
  
  var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
  
  var isHidden = function( elem, el ) {
  
  		// isHidden might be called from jQuery#filter function;
  		// in that case, element will be second argument
  		elem = el || elem;
  		return jQuery.css( elem, "display" ) === "none" ||
  			!jQuery.contains( elem.ownerDocument, elem );
  	};
  
  
  
  function adjustCSS( elem, prop, valueParts, tween ) {
  	var adjusted,
  		scale = 1,
  		maxIterations = 20,
  		currentValue = tween ?
  			function() { return tween.cur(); } :
  			function() { return jQuery.css( elem, prop, "" ); },
  		initial = currentValue(),
  		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
  
  		// Starting value computation is required for potential unit mismatches
  		initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
  			rcssNum.exec( jQuery.css( elem, prop ) );
  
  	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
  
  		// Trust units reported by jQuery.css
  		unit = unit || initialInUnit[ 3 ];
  
  		// Make sure we update the tween properties later on
  		valueParts = valueParts || [];
  
  		// Iteratively approximate from a nonzero starting point
  		initialInUnit = +initial || 1;
  
  		do {
  
  			// If previous iteration zeroed out, double until we get *something*.
  			// Use string for doubling so we don't accidentally see scale as unchanged below
  			scale = scale || ".5";
  
  			// Adjust and apply
  			initialInUnit = initialInUnit / scale;
  			jQuery.style( elem, prop, initialInUnit + unit );
  
  		// Update scale, tolerating zero or NaN from tween.cur()
  		// Break the loop if scale is unchanged or perfect, or if we've just had enough.
  		} while (
  			scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
  		);
  	}
  
  	if ( valueParts ) {
  		initialInUnit = +initialInUnit || +initial || 0;
  
  		// Apply relative offset (+=/-=) if specified
  		adjusted = valueParts[ 1 ] ?
  			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
  			+valueParts[ 2 ];
  		if ( tween ) {
  			tween.unit = unit;
  			tween.start = initialInUnit;
  			tween.end = adjusted;
  		}
  	}
  	return adjusted;
  }
  var rcheckableType = ( /^(?:checkbox|radio)$/i );
  
  var rtagName = ( /<([\w:-]+)/ );
  
  var rscriptType = ( /^$|\/(?:java|ecma)script/i );
  
  
  
  // We have to close these tags to support XHTML (#13200)
  var wrapMap = {
  
  	// Support: IE9
  	option: [ 1, "<select multiple='multiple'>", "</select>" ],
  
  	// XHTML parsers do not magically insert elements in the
  	// same way that tag soup parsers do. So we cannot shorten
  	// this by omitting <tbody> or other required elements.
  	thead: [ 1, "<table>", "</table>" ],
  	col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
  	tr: [ 2, "<table><tbody>", "</tbody></table>" ],
  	td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
  
  	_default: [ 0, "", "" ]
  };
  
  // Support: IE9
  wrapMap.optgroup = wrapMap.option;
  
  wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
  wrapMap.th = wrapMap.td;
  
  
  function getAll( context, tag ) {
  
  	// Support: IE9-11+
  	// Use typeof to avoid zero-argument method invocation on host objects (#15151)
  	var ret = typeof context.getElementsByTagName !== "undefined" ?
  			context.getElementsByTagName( tag || "*" ) :
  			typeof context.querySelectorAll !== "undefined" ?
  				context.querySelectorAll( tag || "*" ) :
  			[];
  
  	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
  		jQuery.merge( [ context ], ret ) :
  		ret;
  }
  
  
  // Mark scripts as having already been evaluated
  function setGlobalEval( elems, refElements ) {
  	var i = 0,
  		l = elems.length;
  
  	for ( ; i < l; i++ ) {
  		dataPriv.set(
  			elems[ i ],
  			"globalEval",
  			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
  		);
  	}
  }
  
  
  var rhtml = /<|&#?\w+;/;
  
  function buildFragment( elems, context, scripts, selection, ignored ) {
  	var elem, tmp, tag, wrap, contains, j,
  		fragment = context.createDocumentFragment(),
  		nodes = [],
  		i = 0,
  		l = elems.length;
  
  	for ( ; i < l; i++ ) {
  		elem = elems[ i ];
  
  		if ( elem || elem === 0 ) {
  
  			// Add nodes directly
  			if ( jQuery.type( elem ) === "object" ) {
  
  				// Support: Android<4.1, PhantomJS<2
  				// push.apply(_, arraylike) throws on ancient WebKit
  				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
  
  			// Convert non-html into a text node
  			} else if ( !rhtml.test( elem ) ) {
  				nodes.push( context.createTextNode( elem ) );
  
  			// Convert html into DOM nodes
  			} else {
  				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
  
  				// Deserialize a standard representation
  				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
  				wrap = wrapMap[ tag ] || wrapMap._default;
  				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
  
  				// Descend through wrappers to the right content
  				j = wrap[ 0 ];
  				while ( j-- ) {
  					tmp = tmp.lastChild;
  				}
  
  				// Support: Android<4.1, PhantomJS<2
  				// push.apply(_, arraylike) throws on ancient WebKit
  				jQuery.merge( nodes, tmp.childNodes );
  
  				// Remember the top-level container
  				tmp = fragment.firstChild;
  
  				// Ensure the created nodes are orphaned (#12392)
  				tmp.textContent = "";
  			}
  		}
  	}
  
  	// Remove wrapper from fragment
  	fragment.textContent = "";
  
  	i = 0;
  	while ( ( elem = nodes[ i++ ] ) ) {
  
  		// Skip elements already in the context collection (trac-4087)
  		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
  			if ( ignored ) {
  				ignored.push( elem );
  			}
  			continue;
  		}
  
  		contains = jQuery.contains( elem.ownerDocument, elem );
  
  		// Append to fragment
  		tmp = getAll( fragment.appendChild( elem ), "script" );
  
  		// Preserve script evaluation history
  		if ( contains ) {
  			setGlobalEval( tmp );
  		}
  
  		// Capture executables
  		if ( scripts ) {
  			j = 0;
  			while ( ( elem = tmp[ j++ ] ) ) {
  				if ( rscriptType.test( elem.type || "" ) ) {
  					scripts.push( elem );
  				}
  			}
  		}
  	}
  
  	return fragment;
  }
  
  
  ( function() {
  	var fragment = document.createDocumentFragment(),
  		div = fragment.appendChild( document.createElement( "div" ) ),
  		input = document.createElement( "input" );
  
  	// Support: Android 4.0-4.3, Safari<=5.1
  	// Check state lost if the name is set (#11217)
  	// Support: Windows Web Apps (WWA)
  	// `name` and `type` must use .setAttribute for WWA (#14901)
  	input.setAttribute( "type", "radio" );
  	input.setAttribute( "checked", "checked" );
  	input.setAttribute( "name", "t" );
  
  	div.appendChild( input );
  
  	// Support: Safari<=5.1, Android<4.2
  	// Older WebKit doesn't clone checked state correctly in fragments
  	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
  
  	// Support: IE<=11+
  	// Make sure textarea (and checkbox) defaultValue is properly cloned
  	div.innerHTML = "<textarea>x</textarea>";
  	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
  } )();
  
  
  var
  	rkeyEvent = /^key/,
  	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
  	rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
  
  function returnTrue() {
  	return true;
  }
  
  function returnFalse() {
  	return false;
  }
  
  // Support: IE9
  // See #13393 for more info
  function safeActiveElement() {
  	try {
  		return document.activeElement;
  	} catch ( err ) { }
  }
  
  function on( elem, types, selector, data, fn, one ) {
  	var origFn, type;
  
  	// Types can be a map of types/handlers
  	if ( typeof types === "object" ) {
  
  		// ( types-Object, selector, data )
  		if ( typeof selector !== "string" ) {
  
  			// ( types-Object, data )
  			data = data || selector;
  			selector = undefined;
  		}
  		for ( type in types ) {
  			on( elem, type, selector, data, types[ type ], one );
  		}
  		return elem;
  	}
  
  	if ( data == null && fn == null ) {
  
  		// ( types, fn )
  		fn = selector;
  		data = selector = undefined;
  	} else if ( fn == null ) {
  		if ( typeof selector === "string" ) {
  
  			// ( types, selector, fn )
  			fn = data;
  			data = undefined;
  		} else {
  
  			// ( types, data, fn )
  			fn = data;
  			data = selector;
  			selector = undefined;
  		}
  	}
  	if ( fn === false ) {
  		fn = returnFalse;
  	} else if ( !fn ) {
  		return elem;
  	}
  
  	if ( one === 1 ) {
  		origFn = fn;
  		fn = function( event ) {
  
  			// Can use an empty set, since event contains the info
  			jQuery().off( event );
  			return origFn.apply( this, arguments );
  		};
  
  		// Use same guid so caller can remove using origFn
  		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
  	}
  	return elem.each( function() {
  		jQuery.event.add( this, types, fn, data, selector );
  	} );
  }
  
  /*
   * Helper functions for managing events -- not part of the public interface.
   * Props to Dean Edwards' addEvent library for many of the ideas.
   */
  jQuery.event = {
  
  	global: {},
  
  	add: function( elem, types, handler, data, selector ) {
  
  		var handleObjIn, eventHandle, tmp,
  			events, t, handleObj,
  			special, handlers, type, namespaces, origType,
  			elemData = dataPriv.get( elem );
  
  		// Don't attach events to noData or text/comment nodes (but allow plain objects)
  		if ( !elemData ) {
  			return;
  		}
  
  		// Caller can pass in an object of custom data in lieu of the handler
  		if ( handler.handler ) {
  			handleObjIn = handler;
  			handler = handleObjIn.handler;
  			selector = handleObjIn.selector;
  		}
  
  		// Make sure that the handler has a unique ID, used to find/remove it later
  		if ( !handler.guid ) {
  			handler.guid = jQuery.guid++;
  		}
  
  		// Init the element's event structure and main handler, if this is the first
  		if ( !( events = elemData.events ) ) {
  			events = elemData.events = {};
  		}
  		if ( !( eventHandle = elemData.handle ) ) {
  			eventHandle = elemData.handle = function( e ) {
  
  				// Discard the second event of a jQuery.event.trigger() and
  				// when an event is called after a page has unloaded
  				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
  					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
  			};
  		}
  
  		// Handle multiple events separated by a space
  		types = ( types || "" ).match( rnotwhite ) || [ "" ];
  		t = types.length;
  		while ( t-- ) {
  			tmp = rtypenamespace.exec( types[ t ] ) || [];
  			type = origType = tmp[ 1 ];
  			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
  
  			// There *must* be a type, no attaching namespace-only handlers
  			if ( !type ) {
  				continue;
  			}
  
  			// If event changes its type, use the special event handlers for the changed type
  			special = jQuery.event.special[ type ] || {};
  
  			// If selector defined, determine special event api type, otherwise given type
  			type = ( selector ? special.delegateType : special.bindType ) || type;
  
  			// Update special based on newly reset type
  			special = jQuery.event.special[ type ] || {};
  
  			// handleObj is passed to all event handlers
  			handleObj = jQuery.extend( {
  				type: type,
  				origType: origType,
  				data: data,
  				handler: handler,
  				guid: handler.guid,
  				selector: selector,
  				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
  				namespace: namespaces.join( "." )
  			}, handleObjIn );
  
  			// Init the event handler queue if we're the first
  			if ( !( handlers = events[ type ] ) ) {
  				handlers = events[ type ] = [];
  				handlers.delegateCount = 0;
  
  				// Only use addEventListener if the special events handler returns false
  				if ( !special.setup ||
  					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
  
  					if ( elem.addEventListener ) {
  						elem.addEventListener( type, eventHandle );
  					}
  				}
  			}
  
  			if ( special.add ) {
  				special.add.call( elem, handleObj );
  
  				if ( !handleObj.handler.guid ) {
  					handleObj.handler.guid = handler.guid;
  				}
  			}
  
  			// Add to the element's handler list, delegates in front
  			if ( selector ) {
  				handlers.splice( handlers.delegateCount++, 0, handleObj );
  			} else {
  				handlers.push( handleObj );
  			}
  
  			// Keep track of which events have ever been used, for event optimization
  			jQuery.event.global[ type ] = true;
  		}
  
  	},
  
  	// Detach an event or set of events from an element
  	remove: function( elem, types, handler, selector, mappedTypes ) {
  
  		var j, origCount, tmp,
  			events, t, handleObj,
  			special, handlers, type, namespaces, origType,
  			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
  
  		if ( !elemData || !( events = elemData.events ) ) {
  			return;
  		}
  
  		// Once for each type.namespace in types; type may be omitted
  		types = ( types || "" ).match( rnotwhite ) || [ "" ];
  		t = types.length;
  		while ( t-- ) {
  			tmp = rtypenamespace.exec( types[ t ] ) || [];
  			type = origType = tmp[ 1 ];
  			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
  
  			// Unbind all events (on this namespace, if provided) for the element
  			if ( !type ) {
  				for ( type in events ) {
  					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
  				}
  				continue;
  			}
  
  			special = jQuery.event.special[ type ] || {};
  			type = ( selector ? special.delegateType : special.bindType ) || type;
  			handlers = events[ type ] || [];
  			tmp = tmp[ 2 ] &&
  				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
  
  			// Remove matching events
  			origCount = j = handlers.length;
  			while ( j-- ) {
  				handleObj = handlers[ j ];
  
  				if ( ( mappedTypes || origType === handleObj.origType ) &&
  					( !handler || handler.guid === handleObj.guid ) &&
  					( !tmp || tmp.test( handleObj.namespace ) ) &&
  					( !selector || selector === handleObj.selector ||
  						selector === "**" && handleObj.selector ) ) {
  					handlers.splice( j, 1 );
  
  					if ( handleObj.selector ) {
  						handlers.delegateCount--;
  					}
  					if ( special.remove ) {
  						special.remove.call( elem, handleObj );
  					}
  				}
  			}
  
  			// Remove generic event handler if we removed something and no more handlers exist
  			// (avoids potential for endless recursion during removal of special event handlers)
  			if ( origCount && !handlers.length ) {
  				if ( !special.teardown ||
  					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
  
  					jQuery.removeEvent( elem, type, elemData.handle );
  				}
  
  				delete events[ type ];
  			}
  		}
  
  		// Remove data and the expando if it's no longer used
  		if ( jQuery.isEmptyObject( events ) ) {
  			dataPriv.remove( elem, "handle events" );
  		}
  	},
  
  	dispatch: function( event ) {
  
  		// Make a writable jQuery.Event from the native event object
  		event = jQuery.event.fix( event );
  
  		var i, j, ret, matched, handleObj,
  			handlerQueue = [],
  			args = slice.call( arguments ),
  			handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
  			special = jQuery.event.special[ event.type ] || {};
  
  		// Use the fix-ed jQuery.Event rather than the (read-only) native event
  		args[ 0 ] = event;
  		event.delegateTarget = this;
  
  		// Call the preDispatch hook for the mapped type, and let it bail if desired
  		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
  			return;
  		}
  
  		// Determine handlers
  		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
  
  		// Run delegates first; they may want to stop propagation beneath us
  		i = 0;
  		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
  			event.currentTarget = matched.elem;
  
  			j = 0;
  			while ( ( handleObj = matched.handlers[ j++ ] ) &&
  				!event.isImmediatePropagationStopped() ) {
  
  				// Triggered event must either 1) have no namespace, or 2) have namespace(s)
  				// a subset or equal to those in the bound event (both can have no namespace).
  				if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
  
  					event.handleObj = handleObj;
  					event.data = handleObj.data;
  
  					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
  						handleObj.handler ).apply( matched.elem, args );
  
  					if ( ret !== undefined ) {
  						if ( ( event.result = ret ) === false ) {
  							event.preventDefault();
  							event.stopPropagation();
  						}
  					}
  				}
  			}
  		}
  
  		// Call the postDispatch hook for the mapped type
  		if ( special.postDispatch ) {
  			special.postDispatch.call( this, event );
  		}
  
  		return event.result;
  	},
  
  	handlers: function( event, handlers ) {
  		var i, matches, sel, handleObj,
  			handlerQueue = [],
  			delegateCount = handlers.delegateCount,
  			cur = event.target;
  
  		// Support (at least): Chrome, IE9
  		// Find delegate handlers
  		// Black-hole SVG <use> instance trees (#13180)
  		//
  		// Support: Firefox<=42+
  		// Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
  		if ( delegateCount && cur.nodeType &&
  			( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
  
  			for ( ; cur !== this; cur = cur.parentNode || this ) {
  
  				// Don't check non-elements (#13208)
  				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
  				if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) {
  					matches = [];
  					for ( i = 0; i < delegateCount; i++ ) {
  						handleObj = handlers[ i ];
  
  						// Don't conflict with Object.prototype properties (#13203)
  						sel = handleObj.selector + " ";
  
  						if ( matches[ sel ] === undefined ) {
  							matches[ sel ] = handleObj.needsContext ?
  								jQuery( sel, this ).index( cur ) > -1 :
  								jQuery.find( sel, this, null, [ cur ] ).length;
  						}
  						if ( matches[ sel ] ) {
  							matches.push( handleObj );
  						}
  					}
  					if ( matches.length ) {
  						handlerQueue.push( { elem: cur, handlers: matches } );
  					}
  				}
  			}
  		}
  
  		// Add the remaining (directly-bound) handlers
  		if ( delegateCount < handlers.length ) {
  			handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );
  		}
  
  		return handlerQueue;
  	},
  
  	// Includes some event props shared by KeyEvent and MouseEvent
  	props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
  		"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
  
  	fixHooks: {},
  
  	keyHooks: {
  		props: "char charCode key keyCode".split( " " ),
  		filter: function( event, original ) {
  
  			// Add which for key events
  			if ( event.which == null ) {
  				event.which = original.charCode != null ? original.charCode : original.keyCode;
  			}
  
  			return event;
  		}
  	},
  
  	mouseHooks: {
  		props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
  			"screenX screenY toElement" ).split( " " ),
  		filter: function( event, original ) {
  			var eventDoc, doc, body,
  				button = original.button;
  
  			// Calculate pageX/Y if missing and clientX/Y available
  			if ( event.pageX == null && original.clientX != null ) {
  				eventDoc = event.target.ownerDocument || document;
  				doc = eventDoc.documentElement;
  				body = eventDoc.body;
  
  				event.pageX = original.clientX +
  					( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
  					( doc && doc.clientLeft || body && body.clientLeft || 0 );
  				event.pageY = original.clientY +
  					( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
  					( doc && doc.clientTop  || body && body.clientTop  || 0 );
  			}
  
  			// Add which for click: 1 === left; 2 === middle; 3 === right
  			// Note: button is not normalized, so don't use it
  			if ( !event.which && button !== undefined ) {
  				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
  			}
  
  			return event;
  		}
  	},
  
  	fix: function( event ) {
  		if ( event[ jQuery.expando ] ) {
  			return event;
  		}
  
  		// Create a writable copy of the event object and normalize some properties
  		var i, prop, copy,
  			type = event.type,
  			originalEvent = event,
  			fixHook = this.fixHooks[ type ];
  
  		if ( !fixHook ) {
  			this.fixHooks[ type ] = fixHook =
  				rmouseEvent.test( type ) ? this.mouseHooks :
  				rkeyEvent.test( type ) ? this.keyHooks :
  				{};
  		}
  		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
  
  		event = new jQuery.Event( originalEvent );
  
  		i = copy.length;
  		while ( i-- ) {
  			prop = copy[ i ];
  			event[ prop ] = originalEvent[ prop ];
  		}
  
  		// Support: Cordova 2.5 (WebKit) (#13255)
  		// All events should have a target; Cordova deviceready doesn't
  		if ( !event.target ) {
  			event.target = document;
  		}
  
  		// Support: Safari 6.0+, Chrome<28
  		// Target should not be a text node (#504, #13143)
  		if ( event.target.nodeType === 3 ) {
  			event.target = event.target.parentNode;
  		}
  
  		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
  	},
  
  	special: {
  		load: {
  
  			// Prevent triggered image.load events from bubbling to window.load
  			noBubble: true
  		},
  		focus: {
  
  			// Fire native event if possible so blur/focus sequence is correct
  			trigger: function() {
  				if ( this !== safeActiveElement() && this.focus ) {
  					this.focus();
  					return false;
  				}
  			},
  			delegateType: "focusin"
  		},
  		blur: {
  			trigger: function() {
  				if ( this === safeActiveElement() && this.blur ) {
  					this.blur();
  					return false;
  				}
  			},
  			delegateType: "focusout"
  		},
  		click: {
  
  			// For checkbox, fire native event so checked state will be right
  			trigger: function() {
  				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
  					this.click();
  					return false;
  				}
  			},
  
  			// For cross-browser consistency, don't fire native .click() on links
  			_default: function( event ) {
  				return jQuery.nodeName( event.target, "a" );
  			}
  		},
  
  		beforeunload: {
  			postDispatch: function( event ) {
  
  				// Support: Firefox 20+
  				// Firefox doesn't alert if the returnValue field is not set.
  				if ( event.result !== undefined && event.originalEvent ) {
  					event.originalEvent.returnValue = event.result;
  				}
  			}
  		}
  	}
  };
  
  jQuery.removeEvent = function( elem, type, handle ) {
  
  	// This "if" is needed for plain objects
  	if ( elem.removeEventListener ) {
  		elem.removeEventListener( type, handle );
  	}
  };
  
  jQuery.Event = function( src, props ) {
  
  	// Allow instantiation without the 'new' keyword
  	if ( !( this instanceof jQuery.Event ) ) {
  		return new jQuery.Event( src, props );
  	}
  
  	// Event object
  	if ( src && src.type ) {
  		this.originalEvent = src;
  		this.type = src.type;
  
  		// Events bubbling up the document may have been marked as prevented
  		// by a handler lower down the tree; reflect the correct value.
  		this.isDefaultPrevented = src.defaultPrevented ||
  				src.defaultPrevented === undefined &&
  
  				// Support: Android<4.0
  				src.returnValue === false ?
  			returnTrue :
  			returnFalse;
  
  	// Event type
  	} else {
  		this.type = src;
  	}
  
  	// Put explicitly provided properties onto the event object
  	if ( props ) {
  		jQuery.extend( this, props );
  	}
  
  	// Create a timestamp if incoming event doesn't have one
  	this.timeStamp = src && src.timeStamp || jQuery.now();
  
  	// Mark it as fixed
  	this[ jQuery.expando ] = true;
  };
  
  // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
  // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
  jQuery.Event.prototype = {
  	constructor: jQuery.Event,
  	isDefaultPrevented: returnFalse,
  	isPropagationStopped: returnFalse,
  	isImmediatePropagationStopped: returnFalse,
  
  	preventDefault: function() {
  		var e = this.originalEvent;
  
  		this.isDefaultPrevented = returnTrue;
  
  		if ( e ) {
  			e.preventDefault();
  		}
  	},
  	stopPropagation: function() {
  		var e = this.originalEvent;
  
  		this.isPropagationStopped = returnTrue;
  
  		if ( e ) {
  			e.stopPropagation();
  		}
  	},
  	stopImmediatePropagation: function() {
  		var e = this.originalEvent;
  
  		this.isImmediatePropagationStopped = returnTrue;
  
  		if ( e ) {
  			e.stopImmediatePropagation();
  		}
  
  		this.stopPropagation();
  	}
  };
  
  // Create mouseenter/leave events using mouseover/out and event-time checks
  // so that event delegation works in jQuery.
  // Do the same for pointerenter/pointerleave and pointerover/pointerout
  //
  // Support: Safari 7 only
  // Safari sends mouseenter too often; see:
  // https://code.google.com/p/chromium/issues/detail?id=470258
  // for the description of the bug (it existed in older Chrome versions as well).
  jQuery.each( {
  	mouseenter: "mouseover",
  	mouseleave: "mouseout",
  	pointerenter: "pointerover",
  	pointerleave: "pointerout"
  }, function( orig, fix ) {
  	jQuery.event.special[ orig ] = {
  		delegateType: fix,
  		bindType: fix,
  
  		handle: function( event ) {
  			var ret,
  				target = this,
  				related = event.relatedTarget,
  				handleObj = event.handleObj;
  
  			// For mouseenter/leave call the handler if related is outside the target.
  			// NB: No relatedTarget if the mouse left/entered the browser window
  			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
  				event.type = handleObj.origType;
  				ret = handleObj.handler.apply( this, arguments );
  				event.type = fix;
  			}
  			return ret;
  		}
  	};
  } );
  
  jQuery.fn.extend( {
  	on: function( types, selector, data, fn ) {
  		return on( this, types, selector, data, fn );
  	},
  	one: function( types, selector, data, fn ) {
  		return on( this, types, selector, data, fn, 1 );
  	},
  	off: function( types, selector, fn ) {
  		var handleObj, type;
  		if ( types && types.preventDefault && types.handleObj ) {
  
  			// ( event )  dispatched jQuery.Event
  			handleObj = types.handleObj;
  			jQuery( types.delegateTarget ).off(
  				handleObj.namespace ?
  					handleObj.origType + "." + handleObj.namespace :
  					handleObj.origType,
  				handleObj.selector,
  				handleObj.handler
  			);
  			return this;
  		}
  		if ( typeof types === "object" ) {
  
  			// ( types-object [, selector] )
  			for ( type in types ) {
  				this.off( type, selector, types[ type ] );
  			}
  			return this;
  		}
  		if ( selector === false || typeof selector === "function" ) {
  
  			// ( types [, fn] )
  			fn = selector;
  			selector = undefined;
  		}
  		if ( fn === false ) {
  			fn = returnFalse;
  		}
  		return this.each( function() {
  			jQuery.event.remove( this, types, fn, selector );
  		} );
  	}
  } );
  
  
  var
  	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
  
  	// Support: IE 10-11, Edge 10240+
  	// In IE/Edge using regex groups here causes severe slowdowns.
  	// See https://connect.microsoft.com/IE/feedback/details/1736512/
  	rnoInnerhtml = /<script|<style|<link/i,
  
  	// checked="checked" or checked
  	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
  	rscriptTypeMasked = /^true\/(.*)/,
  	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
  
  // Manipulating tables requires a tbody
  function manipulationTarget( elem, content ) {
  	return jQuery.nodeName( elem, "table" ) &&
  		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
  
  		elem.getElementsByTagName( "tbody" )[ 0 ] ||
  			elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
  		elem;
  }
  
  // Replace/restore the type attribute of script elements for safe DOM manipulation
  function disableScript( elem ) {
  	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
  	return elem;
  }
  function restoreScript( elem ) {
  	var match = rscriptTypeMasked.exec( elem.type );
  
  	if ( match ) {
  		elem.type = match[ 1 ];
  	} else {
  		elem.removeAttribute( "type" );
  	}
  
  	return elem;
  }
  
  function cloneCopyEvent( src, dest ) {
  	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
  
  	if ( dest.nodeType !== 1 ) {
  		return;
  	}
  
  	// 1. Copy private data: events, handlers, etc.
  	if ( dataPriv.hasData( src ) ) {
  		pdataOld = dataPriv.access( src );
  		pdataCur = dataPriv.set( dest, pdataOld );
  		events = pdataOld.events;
  
  		if ( events ) {
  			delete pdataCur.handle;
  			pdataCur.events = {};
  
  			for ( type in events ) {
  				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
  					jQuery.event.add( dest, type, events[ type ][ i ] );
  				}
  			}
  		}
  	}
  
  	// 2. Copy user data
  	if ( dataUser.hasData( src ) ) {
  		udataOld = dataUser.access( src );
  		udataCur = jQuery.extend( {}, udataOld );
  
  		dataUser.set( dest, udataCur );
  	}
  }
  
  // Fix IE bugs, see support tests
  function fixInput( src, dest ) {
  	var nodeName = dest.nodeName.toLowerCase();
  
  	// Fails to persist the checked state of a cloned checkbox or radio button.
  	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
  		dest.checked = src.checked;
  
  	// Fails to return the selected option to the default selected state when cloning options
  	} else if ( nodeName === "input" || nodeName === "textarea" ) {
  		dest.defaultValue = src.defaultValue;
  	}
  }
  
  function domManip( collection, args, callback, ignored ) {
  
  	// Flatten any nested arrays
  	args = concat.apply( [], args );
  
  	var fragment, first, scripts, hasScripts, node, doc,
  		i = 0,
  		l = collection.length,
  		iNoClone = l - 1,
  		value = args[ 0 ],
  		isFunction = jQuery.isFunction( value );
  
  	// We can't cloneNode fragments that contain checked, in WebKit
  	if ( isFunction ||
  			( l > 1 && typeof value === "string" &&
  				!support.checkClone && rchecked.test( value ) ) ) {
  		return collection.each( function( index ) {
  			var self = collection.eq( index );
  			if ( isFunction ) {
  				args[ 0 ] = value.call( this, index, self.html() );
  			}
  			domManip( self, args, callback, ignored );
  		} );
  	}
  
  	if ( l ) {
  		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
  		first = fragment.firstChild;
  
  		if ( fragment.childNodes.length === 1 ) {
  			fragment = first;
  		}
  
  		// Require either new content or an interest in ignored elements to invoke the callback
  		if ( first || ignored ) {
  			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
  			hasScripts = scripts.length;
  
  			// Use the original fragment for the last item
  			// instead of the first because it can end up
  			// being emptied incorrectly in certain situations (#8070).
  			for ( ; i < l; i++ ) {
  				node = fragment;
  
  				if ( i !== iNoClone ) {
  					node = jQuery.clone( node, true, true );
  
  					// Keep references to cloned scripts for later restoration
  					if ( hasScripts ) {
  
  						// Support: Android<4.1, PhantomJS<2
  						// push.apply(_, arraylike) throws on ancient WebKit
  						jQuery.merge( scripts, getAll( node, "script" ) );
  					}
  				}
  
  				callback.call( collection[ i ], node, i );
  			}
  
  			if ( hasScripts ) {
  				doc = scripts[ scripts.length - 1 ].ownerDocument;
  
  				// Reenable scripts
  				jQuery.map( scripts, restoreScript );
  
  				// Evaluate executable scripts on first document insertion
  				for ( i = 0; i < hasScripts; i++ ) {
  					node = scripts[ i ];
  					if ( rscriptType.test( node.type || "" ) &&
  						!dataPriv.access( node, "globalEval" ) &&
  						jQuery.contains( doc, node ) ) {
  
  						if ( node.src ) {
  
  							// Optional AJAX dependency, but won't run scripts if not present
  							if ( jQuery._evalUrl ) {
  								jQuery._evalUrl( node.src );
  							}
  						} else {
  							jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
  						}
  					}
  				}
  			}
  		}
  	}
  
  	return collection;
  }
  
  function remove( elem, selector, keepData ) {
  	var node,
  		nodes = selector ? jQuery.filter( selector, elem ) : elem,
  		i = 0;
  
  	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
  		if ( !keepData && node.nodeType === 1 ) {
  			jQuery.cleanData( getAll( node ) );
  		}
  
  		if ( node.parentNode ) {
  			if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
  				setGlobalEval( getAll( node, "script" ) );
  			}
  			node.parentNode.removeChild( node );
  		}
  	}
  
  	return elem;
  }
  
  jQuery.extend( {
  	htmlPrefilter: function( html ) {
  		return html.replace( rxhtmlTag, "<$1></$2>" );
  	},
  
  	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
  		var i, l, srcElements, destElements,
  			clone = elem.cloneNode( true ),
  			inPage = jQuery.contains( elem.ownerDocument, elem );
  
  		// Fix IE cloning issues
  		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
  				!jQuery.isXMLDoc( elem ) ) {
  
  			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
  			destElements = getAll( clone );
  			srcElements = getAll( elem );
  
  			for ( i = 0, l = srcElements.length; i < l; i++ ) {
  				fixInput( srcElements[ i ], destElements[ i ] );
  			}
  		}
  
  		// Copy the events from the original to the clone
  		if ( dataAndEvents ) {
  			if ( deepDataAndEvents ) {
  				srcElements = srcElements || getAll( elem );
  				destElements = destElements || getAll( clone );
  
  				for ( i = 0, l = srcElements.length; i < l; i++ ) {
  					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
  				}
  			} else {
  				cloneCopyEvent( elem, clone );
  			}
  		}
  
  		// Preserve script evaluation history
  		destElements = getAll( clone, "script" );
  		if ( destElements.length > 0 ) {
  			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
  		}
  
  		// Return the cloned set
  		return clone;
  	},
  
  	cleanData: function( elems ) {
  		var data, elem, type,
  			special = jQuery.event.special,
  			i = 0;
  
  		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
  			if ( acceptData( elem ) ) {
  				if ( ( data = elem[ dataPriv.expando ] ) ) {
  					if ( data.events ) {
  						for ( type in data.events ) {
  							if ( special[ type ] ) {
  								jQuery.event.remove( elem, type );
  
  							// This is a shortcut to avoid jQuery.event.remove's overhead
  							} else {
  								jQuery.removeEvent( elem, type, data.handle );
  							}
  						}
  					}
  
  					// Support: Chrome <= 35-45+
  					// Assign undefined instead of using delete, see Data#remove
  					elem[ dataPriv.expando ] = undefined;
  				}
  				if ( elem[ dataUser.expando ] ) {
  
  					// Support: Chrome <= 35-45+
  					// Assign undefined instead of using delete, see Data#remove
  					elem[ dataUser.expando ] = undefined;
  				}
  			}
  		}
  	}
  } );
  
  jQuery.fn.extend( {
  
  	// Keep domManip exposed until 3.0 (gh-2225)
  	domManip: domManip,
  
  	detach: function( selector ) {
  		return remove( this, selector, true );
  	},
  
  	remove: function( selector ) {
  		return remove( this, selector );
  	},
  
  	text: function( value ) {
  		return access( this, function( value ) {
  			return value === undefined ?
  				jQuery.text( this ) :
  				this.empty().each( function() {
  					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
  						this.textContent = value;
  					}
  				} );
  		}, null, value, arguments.length );
  	},
  
  	append: function() {
  		return domManip( this, arguments, function( elem ) {
  			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
  				var target = manipulationTarget( this, elem );
  				target.appendChild( elem );
  			}
  		} );
  	},
  
  	prepend: function() {
  		return domManip( this, arguments, function( elem ) {
  			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
  				var target = manipulationTarget( this, elem );
  				target.insertBefore( elem, target.firstChild );
  			}
  		} );
  	},
  
  	before: function() {
  		return domManip( this, arguments, function( elem ) {
  			if ( this.parentNode ) {
  				this.parentNode.insertBefore( elem, this );
  			}
  		} );
  	},
  
  	after: function() {
  		return domManip( this, arguments, function( elem ) {
  			if ( this.parentNode ) {
  				this.parentNode.insertBefore( elem, this.nextSibling );
  			}
  		} );
  	},
  
  	empty: function() {
  		var elem,
  			i = 0;
  
  		for ( ; ( elem = this[ i ] ) != null; i++ ) {
  			if ( elem.nodeType === 1 ) {
  
  				// Prevent memory leaks
  				jQuery.cleanData( getAll( elem, false ) );
  
  				// Remove any remaining nodes
  				elem.textContent = "";
  			}
  		}
  
  		return this;
  	},
  
  	clone: function( dataAndEvents, deepDataAndEvents ) {
  		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
  		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
  
  		return this.map( function() {
  			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
  		} );
  	},
  
  	html: function( value ) {
  		return access( this, function( value ) {
  			var elem = this[ 0 ] || {},
  				i = 0,
  				l = this.length;
  
  			if ( value === undefined && elem.nodeType === 1 ) {
  				return elem.innerHTML;
  			}
  
  			// See if we can take a shortcut and just use innerHTML
  			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
  				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
  
  				value = jQuery.htmlPrefilter( value );
  
  				try {
  					for ( ; i < l; i++ ) {
  						elem = this[ i ] || {};
  
  						// Remove element nodes and prevent memory leaks
  						if ( elem.nodeType === 1 ) {
  							jQuery.cleanData( getAll( elem, false ) );
  							elem.innerHTML = value;
  						}
  					}
  
  					elem = 0;
  
  				// If using innerHTML throws an exception, use the fallback method
  				} catch ( e ) {}
  			}
  
  			if ( elem ) {
  				this.empty().append( value );
  			}
  		}, null, value, arguments.length );
  	},
  
  	replaceWith: function() {
  		var ignored = [];
  
  		// Make the changes, replacing each non-ignored context element with the new content
  		return domManip( this, arguments, function( elem ) {
  			var parent = this.parentNode;
  
  			if ( jQuery.inArray( this, ignored ) < 0 ) {
  				jQuery.cleanData( getAll( this ) );
  				if ( parent ) {
  					parent.replaceChild( elem, this );
  				}
  			}
  
  		// Force callback invocation
  		}, ignored );
  	}
  } );
  
  jQuery.each( {
  	appendTo: "append",
  	prependTo: "prepend",
  	insertBefore: "before",
  	insertAfter: "after",
  	replaceAll: "replaceWith"
  }, function( name, original ) {
  	jQuery.fn[ name ] = function( selector ) {
  		var elems,
  			ret = [],
  			insert = jQuery( selector ),
  			last = insert.length - 1,
  			i = 0;
  
  		for ( ; i <= last; i++ ) {
  			elems = i === last ? this : this.clone( true );
  			jQuery( insert[ i ] )[ original ]( elems );
  
  			// Support: QtWebKit
  			// .get() because push.apply(_, arraylike) throws
  			push.apply( ret, elems.get() );
  		}
  
  		return this.pushStack( ret );
  	};
  } );
  
  
  var iframe,
  	elemdisplay = {
  
  		// Support: Firefox
  		// We have to pre-define these values for FF (#10227)
  		HTML: "block",
  		BODY: "block"
  	};
  
  /**
   * Retrieve the actual display of a element
   * @param {String} name nodeName of the element
   * @param {Object} doc Document object
   */
  
  // Called only from within defaultDisplay
  function actualDisplay( name, doc ) {
  	var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
  
  		display = jQuery.css( elem[ 0 ], "display" );
  
  	// We don't have any data stored on the element,
  	// so use "detach" method as fast way to get rid of the element
  	elem.detach();
  
  	return display;
  }
  
  /**
   * Try to determine the default display value of an element
   * @param {String} nodeName
   */
  function defaultDisplay( nodeName ) {
  	var doc = document,
  		display = elemdisplay[ nodeName ];
  
  	if ( !display ) {
  		display = actualDisplay( nodeName, doc );
  
  		// If the simple way fails, read from inside an iframe
  		if ( display === "none" || !display ) {
  
  			// Use the already-created iframe if possible
  			iframe = ( iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" ) )
  				.appendTo( doc.documentElement );
  
  			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
  			doc = iframe[ 0 ].contentDocument;
  
  			// Support: IE
  			doc.write();
  			doc.close();
  
  			display = actualDisplay( nodeName, doc );
  			iframe.detach();
  		}
  
  		// Store the correct default display
  		elemdisplay[ nodeName ] = display;
  	}
  
  	return display;
  }
  var rmargin = ( /^margin/ );
  
  var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
  
  var getStyles = function( elem ) {
  
  		// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
  		// IE throws on elements created in popups
  		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
  		var view = elem.ownerDocument.defaultView;
  
  		if ( !view || !view.opener ) {
  			view = window;
  		}
  
  		return view.getComputedStyle( elem );
  	};
  
  var swap = function( elem, options, callback, args ) {
  	var ret, name,
  		old = {};
  
  	// Remember the old values, and insert the new ones
  	for ( name in options ) {
  		old[ name ] = elem.style[ name ];
  		elem.style[ name ] = options[ name ];
  	}
  
  	ret = callback.apply( elem, args || [] );
  
  	// Revert the old values
  	for ( name in options ) {
  		elem.style[ name ] = old[ name ];
  	}
  
  	return ret;
  };
  
  
  var documentElement = document.documentElement;
  
  
  
  ( function() {
  	var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
  		container = document.createElement( "div" ),
  		div = document.createElement( "div" );
  
  	// Finish early in limited (non-browser) environments
  	if ( !div.style ) {
  		return;
  	}
  
  	// Support: IE9-11+
  	// Style of cloned element affects source element cloned (#8908)
  	div.style.backgroundClip = "content-box";
  	div.cloneNode( true ).style.backgroundClip = "";
  	support.clearCloneStyle = div.style.backgroundClip === "content-box";
  
  	container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
  		"padding:0;margin-top:1px;position:absolute";
  	container.appendChild( div );
  
  	// Executing both pixelPosition & boxSizingReliable tests require only one layout
  	// so they're executed at the same time to save the second computation.
  	function computeStyleTests() {
  		div.style.cssText =
  
  			// Support: Firefox<29, Android 2.3
  			// Vendor-prefix box-sizing
  			"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;" +
  			"position:relative;display:block;" +
  			"margin:auto;border:1px;padding:1px;" +
  			"top:1%;width:50%";
  		div.innerHTML = "";
  		documentElement.appendChild( container );
  
  		var divStyle = window.getComputedStyle( div );
  		pixelPositionVal = divStyle.top !== "1%";
  		reliableMarginLeftVal = divStyle.marginLeft === "2px";
  		boxSizingReliableVal = divStyle.width === "4px";
  
  		// Support: Android 4.0 - 4.3 only
  		// Some styles come back with percentage values, even though they shouldn't
  		div.style.marginRight = "50%";
  		pixelMarginRightVal = divStyle.marginRight === "4px";
  
  		documentElement.removeChild( container );
  	}
  
  	jQuery.extend( support, {
  		pixelPosition: function() {
  
  			// This test is executed only once but we still do memoizing
  			// since we can use the boxSizingReliable pre-computing.
  			// No need to check if the test was already performed, though.
  			computeStyleTests();
  			return pixelPositionVal;
  		},
  		boxSizingReliable: function() {
  			if ( boxSizingReliableVal == null ) {
  				computeStyleTests();
  			}
  			return boxSizingReliableVal;
  		},
  		pixelMarginRight: function() {
  
  			// Support: Android 4.0-4.3
  			// We're checking for boxSizingReliableVal here instead of pixelMarginRightVal
  			// since that compresses better and they're computed together anyway.
  			if ( boxSizingReliableVal == null ) {
  				computeStyleTests();
  			}
  			return pixelMarginRightVal;
  		},
  		reliableMarginLeft: function() {
  
  			// Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
  			if ( boxSizingReliableVal == null ) {
  				computeStyleTests();
  			}
  			return reliableMarginLeftVal;
  		},
  		reliableMarginRight: function() {
  
  			// Support: Android 2.3
  			// Check if div with explicit width and no margin-right incorrectly
  			// gets computed margin-right based on width of container. (#3333)
  			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
  			// This support function is only executed once so no memoizing is needed.
  			var ret,
  				marginDiv = div.appendChild( document.createElement( "div" ) );
  
  			// Reset CSS: box-sizing; display; margin; border; padding
  			marginDiv.style.cssText = div.style.cssText =
  
  				// Support: Android 2.3
  				// Vendor-prefix box-sizing
  				"-webkit-box-sizing:content-box;box-sizing:content-box;" +
  				"display:block;margin:0;border:0;padding:0";
  			marginDiv.style.marginRight = marginDiv.style.width = "0";
  			div.style.width = "1px";
  			documentElement.appendChild( container );
  
  			ret = !parseFloat( window.getComputedStyle( marginDiv ).marginRight );
  
  			documentElement.removeChild( container );
  			div.removeChild( marginDiv );
  
  			return ret;
  		}
  	} );
  } )();
  
  
  function curCSS( elem, name, computed ) {
  	var width, minWidth, maxWidth, ret,
  		style = elem.style;
  
  	computed = computed || getStyles( elem );
  	ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
  
  	// Support: Opera 12.1x only
  	// Fall back to style even without computed
  	// computed is undefined for elems on document fragments
  	if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
  		ret = jQuery.style( elem, name );
  	}
  
  	// Support: IE9
  	// getPropertyValue is only needed for .css('filter') (#12537)
  	if ( computed ) {
  
  		// A tribute to the "awesome hack by Dean Edwards"
  		// Android Browser returns percentage for some values,
  		// but width seems to be reliably pixels.
  		// This is against the CSSOM draft spec:
  		// http://dev.w3.org/csswg/cssom/#resolved-values
  		if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
  
  			// Remember the original values
  			width = style.width;
  			minWidth = style.minWidth;
  			maxWidth = style.maxWidth;
  
  			// Put in the new values to get a computed value out
  			style.minWidth = style.maxWidth = style.width = ret;
  			ret = computed.width;
  
  			// Revert the changed values
  			style.width = width;
  			style.minWidth = minWidth;
  			style.maxWidth = maxWidth;
  		}
  	}
  
  	return ret !== undefined ?
  
  		// Support: IE9-11+
  		// IE returns zIndex value as an integer.
  		ret + "" :
  		ret;
  }
  
  
  function addGetHookIf( conditionFn, hookFn ) {
  
  	// Define the hook, we'll check on the first run if it's really needed.
  	return {
  		get: function() {
  			if ( conditionFn() ) {
  
  				// Hook not needed (or it's not possible to use it due
  				// to missing dependency), remove it.
  				delete this.get;
  				return;
  			}
  
  			// Hook needed; redefine it so that the support test is not executed again.
  			return ( this.get = hookFn ).apply( this, arguments );
  		}
  	};
  }
  
  
  var
  
  	// Swappable if display is none or starts with table
  	// except "table", "table-cell", or "table-caption"
  	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
  	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
  
  	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
  	cssNormalTransform = {
  		letterSpacing: "0",
  		fontWeight: "400"
  	},
  
  	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
  	emptyStyle = document.createElement( "div" ).style;
  
  // Return a css property mapped to a potentially vendor prefixed property
  function vendorPropName( name ) {
  
  	// Shortcut for names that are not vendor prefixed
  	if ( name in emptyStyle ) {
  		return name;
  	}
  
  	// Check for vendor prefixed names
  	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
  		i = cssPrefixes.length;
  
  	while ( i-- ) {
  		name = cssPrefixes[ i ] + capName;
  		if ( name in emptyStyle ) {
  			return name;
  		}
  	}
  }
  
  function setPositiveNumber( elem, value, subtract ) {
  
  	// Any relative (+/-) values have already been
  	// normalized at this point
  	var matches = rcssNum.exec( value );
  	return matches ?
  
  		// Guard against undefined "subtract", e.g., when used as in cssHooks
  		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
  		value;
  }
  
  function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  	var i = extra === ( isBorderBox ? "border" : "content" ) ?
  
  		// If we already have the right measurement, avoid augmentation
  		4 :
  
  		// Otherwise initialize for horizontal or vertical properties
  		name === "width" ? 1 : 0,
  
  		val = 0;
  
  	for ( ; i < 4; i += 2 ) {
  
  		// Both box models exclude margin, so add it if we want it
  		if ( extra === "margin" ) {
  			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
  		}
  
  		if ( isBorderBox ) {
  
  			// border-box includes padding, so remove it if we want content
  			if ( extra === "content" ) {
  				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
  			}
  
  			// At this point, extra isn't border nor margin, so remove border
  			if ( extra !== "margin" ) {
  				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
  			}
  		} else {
  
  			// At this point, extra isn't content, so add padding
  			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
  
  			// At this point, extra isn't content nor padding, so add border
  			if ( extra !== "padding" ) {
  				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
  			}
  		}
  	}
  
  	return val;
  }
  
  function getWidthOrHeight( elem, name, extra ) {
  
  	// Start with offset property, which is equivalent to the border-box value
  	var valueIsBorderBox = true,
  		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
  		styles = getStyles( elem ),
  		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
  
  	// Support: IE11 only
  	// In IE 11 fullscreen elements inside of an iframe have
  	// 100x too small dimensions (gh-1764).
  	if ( document.msFullscreenElement && window.top !== window ) {
  
  		// Support: IE11 only
  		// Running getBoundingClientRect on a disconnected node
  		// in IE throws an error.
  		if ( elem.getClientRects().length ) {
  			val = Math.round( elem.getBoundingClientRect()[ name ] * 100 );
  		}
  	}
  
  	// Some non-html elements return undefined for offsetWidth, so check for null/undefined
  	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
  	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
  	if ( val <= 0 || val == null ) {
  
  		// Fall back to computed then uncomputed css if necessary
  		val = curCSS( elem, name, styles );
  		if ( val < 0 || val == null ) {
  			val = elem.style[ name ];
  		}
  
  		// Computed unit is not pixels. Stop here and return.
  		if ( rnumnonpx.test( val ) ) {
  			return val;
  		}
  
  		// Check for style in case a browser which returns unreliable values
  		// for getComputedStyle silently falls back to the reliable elem.style
  		valueIsBorderBox = isBorderBox &&
  			( support.boxSizingReliable() || val === elem.style[ name ] );
  
  		// Normalize "", auto, and prepare for extra
  		val = parseFloat( val ) || 0;
  	}
  
  	// Use the active box-sizing model to add/subtract irrelevant styles
  	return ( val +
  		augmentWidthOrHeight(
  			elem,
  			name,
  			extra || ( isBorderBox ? "border" : "content" ),
  			valueIsBorderBox,
  			styles
  		)
  	) + "px";
  }
  
  function showHide( elements, show ) {
  	var display, elem, hidden,
  		values = [],
  		index = 0,
  		length = elements.length;
  
  	for ( ; index < length; index++ ) {
  		elem = elements[ index ];
  		if ( !elem.style ) {
  			continue;
  		}
  
  		values[ index ] = dataPriv.get( elem, "olddisplay" );
  		display = elem.style.display;
  		if ( show ) {
  
  			// Reset the inline display of this element to learn if it is
  			// being hidden by cascaded rules or not
  			if ( !values[ index ] && display === "none" ) {
  				elem.style.display = "";
  			}
  
  			// Set elements which have been overridden with display: none
  			// in a stylesheet to whatever the default browser style is
  			// for such an element
  			if ( elem.style.display === "" && isHidden( elem ) ) {
  				values[ index ] = dataPriv.access(
  					elem,
  					"olddisplay",
  					defaultDisplay( elem.nodeName )
  				);
  			}
  		} else {
  			hidden = isHidden( elem );
  
  			if ( display !== "none" || !hidden ) {
  				dataPriv.set(
  					elem,
  					"olddisplay",
  					hidden ? display : jQuery.css( elem, "display" )
  				);
  			}
  		}
  	}
  
  	// Set the display of most of the elements in a second loop
  	// to avoid the constant reflow
  	for ( index = 0; index < length; index++ ) {
  		elem = elements[ index ];
  		if ( !elem.style ) {
  			continue;
  		}
  		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
  			elem.style.display = show ? values[ index ] || "" : "none";
  		}
  	}
  
  	return elements;
  }
  
  jQuery.extend( {
  
  	// Add in style property hooks for overriding the default
  	// behavior of getting and setting a style property
  	cssHooks: {
  		opacity: {
  			get: function( elem, computed ) {
  				if ( computed ) {
  
  					// We should always get a number back from opacity
  					var ret = curCSS( elem, "opacity" );
  					return ret === "" ? "1" : ret;
  				}
  			}
  		}
  	},
  
  	// Don't automatically add "px" to these possibly-unitless properties
  	cssNumber: {
  		"animationIterationCount": true,
  		"columnCount": true,
  		"fillOpacity": true,
  		"flexGrow": true,
  		"flexShrink": true,
  		"fontWeight": true,
  		"lineHeight": true,
  		"opacity": true,
  		"order": true,
  		"orphans": true,
  		"widows": true,
  		"zIndex": true,
  		"zoom": true
  	},
  
  	// Add in properties whose names you wish to fix before
  	// setting or getting the value
  	cssProps: {
  		"float": "cssFloat"
  	},
  
  	// Get and set the style property on a DOM Node
  	style: function( elem, name, value, extra ) {
  
  		// Don't set styles on text and comment nodes
  		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
  			return;
  		}
  
  		// Make sure that we're working with the right name
  		var ret, type, hooks,
  			origName = jQuery.camelCase( name ),
  			style = elem.style;
  
  		name = jQuery.cssProps[ origName ] ||
  			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
  
  		// Gets hook for the prefixed version, then unprefixed version
  		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
  
  		// Check if we're setting a value
  		if ( value !== undefined ) {
  			type = typeof value;
  
  			// Convert "+=" or "-=" to relative numbers (#7345)
  			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
  				value = adjustCSS( elem, name, ret );
  
  				// Fixes bug #9237
  				type = "number";
  			}
  
  			// Make sure that null and NaN values aren't set (#7116)
  			if ( value == null || value !== value ) {
  				return;
  			}
  
  			// If a number was passed in, add the unit (except for certain CSS properties)
  			if ( type === "number" ) {
  				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
  			}
  
  			// Support: IE9-11+
  			// background-* props affect original clone's values
  			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
  				style[ name ] = "inherit";
  			}
  
  			// If a hook was provided, use that value, otherwise just set the specified value
  			if ( !hooks || !( "set" in hooks ) ||
  				( value = hooks.set( elem, value, extra ) ) !== undefined ) {
  
  				style[ name ] = value;
  			}
  
  		} else {
  
  			// If a hook was provided get the non-computed value from there
  			if ( hooks && "get" in hooks &&
  				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
  
  				return ret;
  			}
  
  			// Otherwise just get the value from the style object
  			return style[ name ];
  		}
  	},
  
  	css: function( elem, name, extra, styles ) {
  		var val, num, hooks,
  			origName = jQuery.camelCase( name );
  
  		// Make sure that we're working with the right name
  		name = jQuery.cssProps[ origName ] ||
  			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
  
  		// Try prefixed name followed by the unprefixed name
  		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
  
  		// If a hook was provided get the computed value from there
  		if ( hooks && "get" in hooks ) {
  			val = hooks.get( elem, true, extra );
  		}
  
  		// Otherwise, if a way to get the computed value exists, use that
  		if ( val === undefined ) {
  			val = curCSS( elem, name, styles );
  		}
  
  		// Convert "normal" to computed value
  		if ( val === "normal" && name in cssNormalTransform ) {
  			val = cssNormalTransform[ name ];
  		}
  
  		// Make numeric if forced or a qualifier was provided and val looks numeric
  		if ( extra === "" || extra ) {
  			num = parseFloat( val );
  			return extra === true || isFinite( num ) ? num || 0 : val;
  		}
  		return val;
  	}
  } );
  
  jQuery.each( [ "height", "width" ], function( i, name ) {
  	jQuery.cssHooks[ name ] = {
  		get: function( elem, computed, extra ) {
  			if ( computed ) {
  
  				// Certain elements can have dimension info if we invisibly show them
  				// but it must have a current display style that would benefit
  				return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
  					elem.offsetWidth === 0 ?
  						swap( elem, cssShow, function() {
  							return getWidthOrHeight( elem, name, extra );
  						} ) :
  						getWidthOrHeight( elem, name, extra );
  			}
  		},
  
  		set: function( elem, value, extra ) {
  			var matches,
  				styles = extra && getStyles( elem ),
  				subtract = extra && augmentWidthOrHeight(
  					elem,
  					name,
  					extra,
  					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
  					styles
  				);
  
  			// Convert to pixels if value adjustment is needed
  			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
  				( matches[ 3 ] || "px" ) !== "px" ) {
  
  				elem.style[ name ] = value;
  				value = jQuery.css( elem, name );
  			}
  
  			return setPositiveNumber( elem, value, subtract );
  		}
  	};
  } );
  
  jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
  	function( elem, computed ) {
  		if ( computed ) {
  			return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
  				elem.getBoundingClientRect().left -
  					swap( elem, { marginLeft: 0 }, function() {
  						return elem.getBoundingClientRect().left;
  					} )
  				) + "px";
  		}
  	}
  );
  
  // Support: Android 2.3
  jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
  	function( elem, computed ) {
  		if ( computed ) {
  			return swap( elem, { "display": "inline-block" },
  				curCSS, [ elem, "marginRight" ] );
  		}
  	}
  );
  
  // These hooks are used by animate to expand properties
  jQuery.each( {
  	margin: "",
  	padding: "",
  	border: "Width"
  }, function( prefix, suffix ) {
  	jQuery.cssHooks[ prefix + suffix ] = {
  		expand: function( value ) {
  			var i = 0,
  				expanded = {},
  
  				// Assumes a single number if not a string
  				parts = typeof value === "string" ? value.split( " " ) : [ value ];
  
  			for ( ; i < 4; i++ ) {
  				expanded[ prefix + cssExpand[ i ] + suffix ] =
  					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
  			}
  
  			return expanded;
  		}
  	};
  
  	if ( !rmargin.test( prefix ) ) {
  		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
  	}
  } );
  
  jQuery.fn.extend( {
  	css: function( name, value ) {
  		return access( this, function( elem, name, value ) {
  			var styles, len,
  				map = {},
  				i = 0;
  
  			if ( jQuery.isArray( name ) ) {
  				styles = getStyles( elem );
  				len = name.length;
  
  				for ( ; i < len; i++ ) {
  					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
  				}
  
  				return map;
  			}
  
  			return value !== undefined ?
  				jQuery.style( elem, name, value ) :
  				jQuery.css( elem, name );
  		}, name, value, arguments.length > 1 );
  	},
  	show: function() {
  		return showHide( this, true );
  	},
  	hide: function() {
  		return showHide( this );
  	},
  	toggle: function( state ) {
  		if ( typeof state === "boolean" ) {
  			return state ? this.show() : this.hide();
  		}
  
  		return this.each( function() {
  			if ( isHidden( this ) ) {
  				jQuery( this ).show();
  			} else {
  				jQuery( this ).hide();
  			}
  		} );
  	}
  } );
  
  
  function Tween( elem, options, prop, end, easing ) {
  	return new Tween.prototype.init( elem, options, prop, end, easing );
  }
  jQuery.Tween = Tween;
  
  Tween.prototype = {
  	constructor: Tween,
  	init: function( elem, options, prop, end, easing, unit ) {
  		this.elem = elem;
  		this.prop = prop;
  		this.easing = easing || jQuery.easing._default;
  		this.options = options;
  		this.start = this.now = this.cur();
  		this.end = end;
  		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
  	},
  	cur: function() {
  		var hooks = Tween.propHooks[ this.prop ];
  
  		return hooks && hooks.get ?
  			hooks.get( this ) :
  			Tween.propHooks._default.get( this );
  	},
  	run: function( percent ) {
  		var eased,
  			hooks = Tween.propHooks[ this.prop ];
  
  		if ( this.options.duration ) {
  			this.pos = eased = jQuery.easing[ this.easing ](
  				percent, this.options.duration * percent, 0, 1, this.options.duration
  			);
  		} else {
  			this.pos = eased = percent;
  		}
  		this.now = ( this.end - this.start ) * eased + this.start;
  
  		if ( this.options.step ) {
  			this.options.step.call( this.elem, this.now, this );
  		}
  
  		if ( hooks && hooks.set ) {
  			hooks.set( this );
  		} else {
  			Tween.propHooks._default.set( this );
  		}
  		return this;
  	}
  };
  
  Tween.prototype.init.prototype = Tween.prototype;
  
  Tween.propHooks = {
  	_default: {
  		get: function( tween ) {
  			var result;
  
  			// Use a property on the element directly when it is not a DOM element,
  			// or when there is no matching style property that exists.
  			if ( tween.elem.nodeType !== 1 ||
  				tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
  				return tween.elem[ tween.prop ];
  			}
  
  			// Passing an empty string as a 3rd parameter to .css will automatically
  			// attempt a parseFloat and fallback to a string if the parse fails.
  			// Simple values such as "10px" are parsed to Float;
  			// complex values such as "rotate(1rad)" are returned as-is.
  			result = jQuery.css( tween.elem, tween.prop, "" );
  
  			// Empty strings, null, undefined and "auto" are converted to 0.
  			return !result || result === "auto" ? 0 : result;
  		},
  		set: function( tween ) {
  
  			// Use step hook for back compat.
  			// Use cssHook if its there.
  			// Use .style if available and use plain properties where available.
  			if ( jQuery.fx.step[ tween.prop ] ) {
  				jQuery.fx.step[ tween.prop ]( tween );
  			} else if ( tween.elem.nodeType === 1 &&
  				( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
  					jQuery.cssHooks[ tween.prop ] ) ) {
  				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
  			} else {
  				tween.elem[ tween.prop ] = tween.now;
  			}
  		}
  	}
  };
  
  // Support: IE9
  // Panic based approach to setting things on disconnected nodes
  Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
  	set: function( tween ) {
  		if ( tween.elem.nodeType && tween.elem.parentNode ) {
  			tween.elem[ tween.prop ] = tween.now;
  		}
  	}
  };
  
  jQuery.easing = {
  	linear: function( p ) {
  		return p;
  	},
  	swing: function( p ) {
  		return 0.5 - Math.cos( p * Math.PI ) / 2;
  	},
  	_default: "swing"
  };
  
  jQuery.fx = Tween.prototype.init;
  
  // Back Compat <1.8 extension point
  jQuery.fx.step = {};
  
  
  
  
  var
  	fxNow, timerId,
  	rfxtypes = /^(?:toggle|show|hide)$/,
  	rrun = /queueHooks$/;
  
  // Animations created synchronously will run synchronously
  function createFxNow() {
  	window.setTimeout( function() {
  		fxNow = undefined;
  	} );
  	return ( fxNow = jQuery.now() );
  }
  
  // Generate parameters to create a standard animation
  function genFx( type, includeWidth ) {
  	var which,
  		i = 0,
  		attrs = { height: type };
  
  	// If we include width, step value is 1 to do all cssExpand values,
  	// otherwise step value is 2 to skip over Left and Right
  	includeWidth = includeWidth ? 1 : 0;
  	for ( ; i < 4 ; i += 2 - includeWidth ) {
  		which = cssExpand[ i ];
  		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
  	}
  
  	if ( includeWidth ) {
  		attrs.opacity = attrs.width = type;
  	}
  
  	return attrs;
  }
  
  function createTween( value, prop, animation ) {
  	var tween,
  		collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
  		index = 0,
  		length = collection.length;
  	for ( ; index < length; index++ ) {
  		if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
  
  			// We're done with this property
  			return tween;
  		}
  	}
  }
  
  function defaultPrefilter( elem, props, opts ) {
  	/* jshint validthis: true */
  	var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
  		anim = this,
  		orig = {},
  		style = elem.style,
  		hidden = elem.nodeType && isHidden( elem ),
  		dataShow = dataPriv.get( elem, "fxshow" );
  
  	// Handle queue: false promises
  	if ( !opts.queue ) {
  		hooks = jQuery._queueHooks( elem, "fx" );
  		if ( hooks.unqueued == null ) {
  			hooks.unqueued = 0;
  			oldfire = hooks.empty.fire;
  			hooks.empty.fire = function() {
  				if ( !hooks.unqueued ) {
  					oldfire();
  				}
  			};
  		}
  		hooks.unqueued++;
  
  		anim.always( function() {
  
  			// Ensure the complete handler is called before this completes
  			anim.always( function() {
  				hooks.unqueued--;
  				if ( !jQuery.queue( elem, "fx" ).length ) {
  					hooks.empty.fire();
  				}
  			} );
  		} );
  	}
  
  	// Height/width overflow pass
  	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
  
  		// Make sure that nothing sneaks out
  		// Record all 3 overflow attributes because IE9-10 do not
  		// change the overflow attribute when overflowX and
  		// overflowY are set to the same value
  		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
  
  		// Set display property to inline-block for height/width
  		// animations on inline elements that are having width/height animated
  		display = jQuery.css( elem, "display" );
  
  		// Test default display if display is currently "none"
  		checkDisplay = display === "none" ?
  			dataPriv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
  
  		if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
  			style.display = "inline-block";
  		}
  	}
  
  	if ( opts.overflow ) {
  		style.overflow = "hidden";
  		anim.always( function() {
  			style.overflow = opts.overflow[ 0 ];
  			style.overflowX = opts.overflow[ 1 ];
  			style.overflowY = opts.overflow[ 2 ];
  		} );
  	}
  
  	// show/hide pass
  	for ( prop in props ) {
  		value = props[ prop ];
  		if ( rfxtypes.exec( value ) ) {
  			delete props[ prop ];
  			toggle = toggle || value === "toggle";
  			if ( value === ( hidden ? "hide" : "show" ) ) {
  
  				// If there is dataShow left over from a stopped hide or show
  				// and we are going to proceed with show, we should pretend to be hidden
  				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
  					hidden = true;
  				} else {
  					continue;
  				}
  			}
  			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
  
  		// Any non-fx value stops us from restoring the original display value
  		} else {
  			display = undefined;
  		}
  	}
  
  	if ( !jQuery.isEmptyObject( orig ) ) {
  		if ( dataShow ) {
  			if ( "hidden" in dataShow ) {
  				hidden = dataShow.hidden;
  			}
  		} else {
  			dataShow = dataPriv.access( elem, "fxshow", {} );
  		}
  
  		// Store state if its toggle - enables .stop().toggle() to "reverse"
  		if ( toggle ) {
  			dataShow.hidden = !hidden;
  		}
  		if ( hidden ) {
  			jQuery( elem ).show();
  		} else {
  			anim.done( function() {
  				jQuery( elem ).hide();
  			} );
  		}
  		anim.done( function() {
  			var prop;
  
  			dataPriv.remove( elem, "fxshow" );
  			for ( prop in orig ) {
  				jQuery.style( elem, prop, orig[ prop ] );
  			}
  		} );
  		for ( prop in orig ) {
  			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
  
  			if ( !( prop in dataShow ) ) {
  				dataShow[ prop ] = tween.start;
  				if ( hidden ) {
  					tween.end = tween.start;
  					tween.start = prop === "width" || prop === "height" ? 1 : 0;
  				}
  			}
  		}
  
  	// If this is a noop like .hide().hide(), restore an overwritten display value
  	} else if ( ( display === "none" ? defaultDisplay( elem.nodeName ) : display ) === "inline" ) {
  		style.display = display;
  	}
  }
  
  function propFilter( props, specialEasing ) {
  	var index, name, easing, value, hooks;
  
  	// camelCase, specialEasing and expand cssHook pass
  	for ( index in props ) {
  		name = jQuery.camelCase( index );
  		easing = specialEasing[ name ];
  		value = props[ index ];
  		if ( jQuery.isArray( value ) ) {
  			easing = value[ 1 ];
  			value = props[ index ] = value[ 0 ];
  		}
  
  		if ( index !== name ) {
  			props[ name ] = value;
  			delete props[ index ];
  		}
  
  		hooks = jQuery.cssHooks[ name ];
  		if ( hooks && "expand" in hooks ) {
  			value = hooks.expand( value );
  			delete props[ name ];
  
  			// Not quite $.extend, this won't overwrite existing keys.
  			// Reusing 'index' because we have the correct "name"
  			for ( index in value ) {
  				if ( !( index in props ) ) {
  					props[ index ] = value[ index ];
  					specialEasing[ index ] = easing;
  				}
  			}
  		} else {
  			specialEasing[ name ] = easing;
  		}
  	}
  }
  
  function Animation( elem, properties, options ) {
  	var result,
  		stopped,
  		index = 0,
  		length = Animation.prefilters.length,
  		deferred = jQuery.Deferred().always( function() {
  
  			// Don't match elem in the :animated selector
  			delete tick.elem;
  		} ),
  		tick = function() {
  			if ( stopped ) {
  				return false;
  			}
  			var currentTime = fxNow || createFxNow(),
  				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
  
  				// Support: Android 2.3
  				// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
  				temp = remaining / animation.duration || 0,
  				percent = 1 - temp,
  				index = 0,
  				length = animation.tweens.length;
  
  			for ( ; index < length ; index++ ) {
  				animation.tweens[ index ].run( percent );
  			}
  
  			deferred.notifyWith( elem, [ animation, percent, remaining ] );
  
  			if ( percent < 1 && length ) {
  				return remaining;
  			} else {
  				deferred.resolveWith( elem, [ animation ] );
  				return false;
  			}
  		},
  		animation = deferred.promise( {
  			elem: elem,
  			props: jQuery.extend( {}, properties ),
  			opts: jQuery.extend( true, {
  				specialEasing: {},
  				easing: jQuery.easing._default
  			}, options ),
  			originalProperties: properties,
  			originalOptions: options,
  			startTime: fxNow || createFxNow(),
  			duration: options.duration,
  			tweens: [],
  			createTween: function( prop, end ) {
  				var tween = jQuery.Tween( elem, animation.opts, prop, end,
  						animation.opts.specialEasing[ prop ] || animation.opts.easing );
  				animation.tweens.push( tween );
  				return tween;
  			},
  			stop: function( gotoEnd ) {
  				var index = 0,
  
  					// If we are going to the end, we want to run all the tweens
  					// otherwise we skip this part
  					length = gotoEnd ? animation.tweens.length : 0;
  				if ( stopped ) {
  					return this;
  				}
  				stopped = true;
  				for ( ; index < length ; index++ ) {
  					animation.tweens[ index ].run( 1 );
  				}
  
  				// Resolve when we played the last frame; otherwise, reject
  				if ( gotoEnd ) {
  					deferred.notifyWith( elem, [ animation, 1, 0 ] );
  					deferred.resolveWith( elem, [ animation, gotoEnd ] );
  				} else {
  					deferred.rejectWith( elem, [ animation, gotoEnd ] );
  				}
  				return this;
  			}
  		} ),
  		props = animation.props;
  
  	propFilter( props, animation.opts.specialEasing );
  
  	for ( ; index < length ; index++ ) {
  		result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
  		if ( result ) {
  			if ( jQuery.isFunction( result.stop ) ) {
  				jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
  					jQuery.proxy( result.stop, result );
  			}
  			return result;
  		}
  	}
  
  	jQuery.map( props, createTween, animation );
  
  	if ( jQuery.isFunction( animation.opts.start ) ) {
  		animation.opts.start.call( elem, animation );
  	}
  
  	jQuery.fx.timer(
  		jQuery.extend( tick, {
  			elem: elem,
  			anim: animation,
  			queue: animation.opts.queue
  		} )
  	);
  
  	// attach callbacks from options
  	return animation.progress( animation.opts.progress )
  		.done( animation.opts.done, animation.opts.complete )
  		.fail( animation.opts.fail )
  		.always( animation.opts.always );
  }
  
  jQuery.Animation = jQuery.extend( Animation, {
  	tweeners: {
  		"*": [ function( prop, value ) {
  			var tween = this.createTween( prop, value );
  			adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
  			return tween;
  		} ]
  	},
  
  	tweener: function( props, callback ) {
  		if ( jQuery.isFunction( props ) ) {
  			callback = props;
  			props = [ "*" ];
  		} else {
  			props = props.match( rnotwhite );
  		}
  
  		var prop,
  			index = 0,
  			length = props.length;
  
  		for ( ; index < length ; index++ ) {
  			prop = props[ index ];
  			Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
  			Animation.tweeners[ prop ].unshift( callback );
  		}
  	},
  
  	prefilters: [ defaultPrefilter ],
  
  	prefilter: function( callback, prepend ) {
  		if ( prepend ) {
  			Animation.prefilters.unshift( callback );
  		} else {
  			Animation.prefilters.push( callback );
  		}
  	}
  } );
  
  jQuery.speed = function( speed, easing, fn ) {
  	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
  		complete: fn || !fn && easing ||
  			jQuery.isFunction( speed ) && speed,
  		duration: speed,
  		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
  	};
  
  	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ?
  		opt.duration : opt.duration in jQuery.fx.speeds ?
  			jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
  
  	// Normalize opt.queue - true/undefined/null -> "fx"
  	if ( opt.queue == null || opt.queue === true ) {
  		opt.queue = "fx";
  	}
  
  	// Queueing
  	opt.old = opt.complete;
  
  	opt.complete = function() {
  		if ( jQuery.isFunction( opt.old ) ) {
  			opt.old.call( this );
  		}
  
  		if ( opt.queue ) {
  			jQuery.dequeue( this, opt.queue );
  		}
  	};
  
  	return opt;
  };
  
  jQuery.fn.extend( {
  	fadeTo: function( speed, to, easing, callback ) {
  
  		// Show any hidden elements after setting opacity to 0
  		return this.filter( isHidden ).css( "opacity", 0 ).show()
  
  			// Animate to the value specified
  			.end().animate( { opacity: to }, speed, easing, callback );
  	},
  	animate: function( prop, speed, easing, callback ) {
  		var empty = jQuery.isEmptyObject( prop ),
  			optall = jQuery.speed( speed, easing, callback ),
  			doAnimation = function() {
  
  				// Operate on a copy of prop so per-property easing won't be lost
  				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
  
  				// Empty animations, or finishing resolves immediately
  				if ( empty || dataPriv.get( this, "finish" ) ) {
  					anim.stop( true );
  				}
  			};
  			doAnimation.finish = doAnimation;
  
  		return empty || optall.queue === false ?
  			this.each( doAnimation ) :
  			this.queue( optall.queue, doAnimation );
  	},
  	stop: function( type, clearQueue, gotoEnd ) {
  		var stopQueue = function( hooks ) {
  			var stop = hooks.stop;
  			delete hooks.stop;
  			stop( gotoEnd );
  		};
  
  		if ( typeof type !== "string" ) {
  			gotoEnd = clearQueue;
  			clearQueue = type;
  			type = undefined;
  		}
  		if ( clearQueue && type !== false ) {
  			this.queue( type || "fx", [] );
  		}
  
  		return this.each( function() {
  			var dequeue = true,
  				index = type != null && type + "queueHooks",
  				timers = jQuery.timers,
  				data = dataPriv.get( this );
  
  			if ( index ) {
  				if ( data[ index ] && data[ index ].stop ) {
  					stopQueue( data[ index ] );
  				}
  			} else {
  				for ( index in data ) {
  					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
  						stopQueue( data[ index ] );
  					}
  				}
  			}
  
  			for ( index = timers.length; index--; ) {
  				if ( timers[ index ].elem === this &&
  					( type == null || timers[ index ].queue === type ) ) {
  
  					timers[ index ].anim.stop( gotoEnd );
  					dequeue = false;
  					timers.splice( index, 1 );
  				}
  			}
  
  			// Start the next in the queue if the last step wasn't forced.
  			// Timers currently will call their complete callbacks, which
  			// will dequeue but only if they were gotoEnd.
  			if ( dequeue || !gotoEnd ) {
  				jQuery.dequeue( this, type );
  			}
  		} );
  	},
  	finish: function( type ) {
  		if ( type !== false ) {
  			type = type || "fx";
  		}
  		return this.each( function() {
  			var index,
  				data = dataPriv.get( this ),
  				queue = data[ type + "queue" ],
  				hooks = data[ type + "queueHooks" ],
  				timers = jQuery.timers,
  				length = queue ? queue.length : 0;
  
  			// Enable finishing flag on private data
  			data.finish = true;
  
  			// Empty the queue first
  			jQuery.queue( this, type, [] );
  
  			if ( hooks && hooks.stop ) {
  				hooks.stop.call( this, true );
  			}
  
  			// Look for any active animations, and finish them
  			for ( index = timers.length; index--; ) {
  				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
  					timers[ index ].anim.stop( true );
  					timers.splice( index, 1 );
  				}
  			}
  
  			// Look for any animations in the old queue and finish them
  			for ( index = 0; index < length; index++ ) {
  				if ( queue[ index ] && queue[ index ].finish ) {
  					queue[ index ].finish.call( this );
  				}
  			}
  
  			// Turn off finishing flag
  			delete data.finish;
  		} );
  	}
  } );
  
  jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
  	var cssFn = jQuery.fn[ name ];
  	jQuery.fn[ name ] = function( speed, easing, callback ) {
  		return speed == null || typeof speed === "boolean" ?
  			cssFn.apply( this, arguments ) :
  			this.animate( genFx( name, true ), speed, easing, callback );
  	};
  } );
  
  // Generate shortcuts for custom animations
  jQuery.each( {
  	slideDown: genFx( "show" ),
  	slideUp: genFx( "hide" ),
  	slideToggle: genFx( "toggle" ),
  	fadeIn: { opacity: "show" },
  	fadeOut: { opacity: "hide" },
  	fadeToggle: { opacity: "toggle" }
  }, function( name, props ) {
  	jQuery.fn[ name ] = function( speed, easing, callback ) {
  		return this.animate( props, speed, easing, callback );
  	};
  } );
  
  jQuery.timers = [];
  jQuery.fx.tick = function() {
  	var timer,
  		i = 0,
  		timers = jQuery.timers;
  
  	fxNow = jQuery.now();
  
  	for ( ; i < timers.length; i++ ) {
  		timer = timers[ i ];
  
  		// Checks the timer has not already been removed
  		if ( !timer() && timers[ i ] === timer ) {
  			timers.splice( i--, 1 );
  		}
  	}
  
  	if ( !timers.length ) {
  		jQuery.fx.stop();
  	}
  	fxNow = undefined;
  };
  
  jQuery.fx.timer = function( timer ) {
  	jQuery.timers.push( timer );
  	if ( timer() ) {
  		jQuery.fx.start();
  	} else {
  		jQuery.timers.pop();
  	}
  };
  
  jQuery.fx.interval = 13;
  jQuery.fx.start = function() {
  	if ( !timerId ) {
  		timerId = window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
  	}
  };
  
  jQuery.fx.stop = function() {
  	window.clearInterval( timerId );
  
  	timerId = null;
  };
  
  jQuery.fx.speeds = {
  	slow: 600,
  	fast: 200,
  
  	// Default speed
  	_default: 400
  };
  
  
  // Based off of the plugin by Clint Helfers, with permission.
  // http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
  jQuery.fn.delay = function( time, type ) {
  	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
  	type = type || "fx";
  
  	return this.queue( type, function( next, hooks ) {
  		var timeout = window.setTimeout( next, time );
  		hooks.stop = function() {
  			window.clearTimeout( timeout );
  		};
  	} );
  };
  
  
  ( function() {
  	var input = document.createElement( "input" ),
  		select = document.createElement( "select" ),
  		opt = select.appendChild( document.createElement( "option" ) );
  
  	input.type = "checkbox";
  
  	// Support: iOS<=5.1, Android<=4.2+
  	// Default value for a checkbox should be "on"
  	support.checkOn = input.value !== "";
  
  	// Support: IE<=11+
  	// Must access selectedIndex to make default options select
  	support.optSelected = opt.selected;
  
  	// Support: Android<=2.3
  	// Options inside disabled selects are incorrectly marked as disabled
  	select.disabled = true;
  	support.optDisabled = !opt.disabled;
  
  	// Support: IE<=11+
  	// An input loses its value after becoming a radio
  	input = document.createElement( "input" );
  	input.value = "t";
  	input.type = "radio";
  	support.radioValue = input.value === "t";
  } )();
  
  
  var boolHook,
  	attrHandle = jQuery.expr.attrHandle;
  
  jQuery.fn.extend( {
  	attr: function( name, value ) {
  		return access( this, jQuery.attr, name, value, arguments.length > 1 );
  	},
  
  	removeAttr: function( name ) {
  		return this.each( function() {
  			jQuery.removeAttr( this, name );
  		} );
  	}
  } );
  
  jQuery.extend( {
  	attr: function( elem, name, value ) {
  		var ret, hooks,
  			nType = elem.nodeType;
  
  		// Don't get/set attributes on text, comment and attribute nodes
  		if ( nType === 3 || nType === 8 || nType === 2 ) {
  			return;
  		}
  
  		// Fallback to prop when attributes are not supported
  		if ( typeof elem.getAttribute === "undefined" ) {
  			return jQuery.prop( elem, name, value );
  		}
  
  		// All attributes are lowercase
  		// Grab necessary hook if one is defined
  		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
  			name = name.toLowerCase();
  			hooks = jQuery.attrHooks[ name ] ||
  				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
  		}
  
  		if ( value !== undefined ) {
  			if ( value === null ) {
  				jQuery.removeAttr( elem, name );
  				return;
  			}
  
  			if ( hooks && "set" in hooks &&
  				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
  				return ret;
  			}
  
  			elem.setAttribute( name, value + "" );
  			return value;
  		}
  
  		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
  			return ret;
  		}
  
  		ret = jQuery.find.attr( elem, name );
  
  		// Non-existent attributes return null, we normalize to undefined
  		return ret == null ? undefined : ret;
  	},
  
  	attrHooks: {
  		type: {
  			set: function( elem, value ) {
  				if ( !support.radioValue && value === "radio" &&
  					jQuery.nodeName( elem, "input" ) ) {
  					var val = elem.value;
  					elem.setAttribute( "type", value );
  					if ( val ) {
  						elem.value = val;
  					}
  					return value;
  				}
  			}
  		}
  	},
  
  	removeAttr: function( elem, value ) {
  		var name, propName,
  			i = 0,
  			attrNames = value && value.match( rnotwhite );
  
  		if ( attrNames && elem.nodeType === 1 ) {
  			while ( ( name = attrNames[ i++ ] ) ) {
  				propName = jQuery.propFix[ name ] || name;
  
  				// Boolean attributes get special treatment (#10870)
  				if ( jQuery.expr.match.bool.test( name ) ) {
  
  					// Set corresponding property to false
  					elem[ propName ] = false;
  				}
  
  				elem.removeAttribute( name );
  			}
  		}
  	}
  } );
  
  // Hooks for boolean attributes
  boolHook = {
  	set: function( elem, value, name ) {
  		if ( value === false ) {
  
  			// Remove boolean attributes when set to false
  			jQuery.removeAttr( elem, name );
  		} else {
  			elem.setAttribute( name, name );
  		}
  		return name;
  	}
  };
  jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
  	var getter = attrHandle[ name ] || jQuery.find.attr;
  
  	attrHandle[ name ] = function( elem, name, isXML ) {
  		var ret, handle;
  		if ( !isXML ) {
  
  			// Avoid an infinite loop by temporarily removing this function from the getter
  			handle = attrHandle[ name ];
  			attrHandle[ name ] = ret;
  			ret = getter( elem, name, isXML ) != null ?
  				name.toLowerCase() :
  				null;
  			attrHandle[ name ] = handle;
  		}
  		return ret;
  	};
  } );
  
  
  
  
  var rfocusable = /^(?:input|select|textarea|button)$/i,
  	rclickable = /^(?:a|area)$/i;
  
  jQuery.fn.extend( {
  	prop: function( name, value ) {
  		return access( this, jQuery.prop, name, value, arguments.length > 1 );
  	},
  
  	removeProp: function( name ) {
  		return this.each( function() {
  			delete this[ jQuery.propFix[ name ] || name ];
  		} );
  	}
  } );
  
  jQuery.extend( {
  	prop: function( elem, name, value ) {
  		var ret, hooks,
  			nType = elem.nodeType;
  
  		// Don't get/set properties on text, comment and attribute nodes
  		if ( nType === 3 || nType === 8 || nType === 2 ) {
  			return;
  		}
  
  		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
  
  			// Fix name and attach hooks
  			name = jQuery.propFix[ name ] || name;
  			hooks = jQuery.propHooks[ name ];
  		}
  
  		if ( value !== undefined ) {
  			if ( hooks && "set" in hooks &&
  				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
  				return ret;
  			}
  
  			return ( elem[ name ] = value );
  		}
  
  		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
  			return ret;
  		}
  
  		return elem[ name ];
  	},
  
  	propHooks: {
  		tabIndex: {
  			get: function( elem ) {
  
  				// elem.tabIndex doesn't always return the
  				// correct value when it hasn't been explicitly set
  				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
  				// Use proper attribute retrieval(#12072)
  				var tabindex = jQuery.find.attr( elem, "tabindex" );
  
  				return tabindex ?
  					parseInt( tabindex, 10 ) :
  					rfocusable.test( elem.nodeName ) ||
  						rclickable.test( elem.nodeName ) && elem.href ?
  							0 :
  							-1;
  			}
  		}
  	},
  
  	propFix: {
  		"for": "htmlFor",
  		"class": "className"
  	}
  } );
  
  // Support: IE <=11 only
  // Accessing the selectedIndex property
  // forces the browser to respect setting selected
  // on the option
  // The getter ensures a default option is selected
  // when in an optgroup
  if ( !support.optSelected ) {
  	jQuery.propHooks.selected = {
  		get: function( elem ) {
  			var parent = elem.parentNode;
  			if ( parent && parent.parentNode ) {
  				parent.parentNode.selectedIndex;
  			}
  			return null;
  		},
  		set: function( elem ) {
  			var parent = elem.parentNode;
  			if ( parent ) {
  				parent.selectedIndex;
  
  				if ( parent.parentNode ) {
  					parent.parentNode.selectedIndex;
  				}
  			}
  		}
  	};
  }
  
  jQuery.each( [
  	"tabIndex",
  	"readOnly",
  	"maxLength",
  	"cellSpacing",
  	"cellPadding",
  	"rowSpan",
  	"colSpan",
  	"useMap",
  	"frameBorder",
  	"contentEditable"
  ], function() {
  	jQuery.propFix[ this.toLowerCase() ] = this;
  } );
  
  
  
  
  var rclass = /[\t\r\n\f]/g;
  
  function getClass( elem ) {
  	return elem.getAttribute && elem.getAttribute( "class" ) || "";
  }
  
  jQuery.fn.extend( {
  	addClass: function( value ) {
  		var classes, elem, cur, curValue, clazz, j, finalValue,
  			i = 0;
  
  		if ( jQuery.isFunction( value ) ) {
  			return this.each( function( j ) {
  				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
  			} );
  		}
  
  		if ( typeof value === "string" && value ) {
  			classes = value.match( rnotwhite ) || [];
  
  			while ( ( elem = this[ i++ ] ) ) {
  				curValue = getClass( elem );
  				cur = elem.nodeType === 1 &&
  					( " " + curValue + " " ).replace( rclass, " " );
  
  				if ( cur ) {
  					j = 0;
  					while ( ( clazz = classes[ j++ ] ) ) {
  						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
  							cur += clazz + " ";
  						}
  					}
  
  					// Only assign if different to avoid unneeded rendering.
  					finalValue = jQuery.trim( cur );
  					if ( curValue !== finalValue ) {
  						elem.setAttribute( "class", finalValue );
  					}
  				}
  			}
  		}
  
  		return this;
  	},
  
  	removeClass: function( value ) {
  		var classes, elem, cur, curValue, clazz, j, finalValue,
  			i = 0;
  
  		if ( jQuery.isFunction( value ) ) {
  			return this.each( function( j ) {
  				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
  			} );
  		}
  
  		if ( !arguments.length ) {
  			return this.attr( "class", "" );
  		}
  
  		if ( typeof value === "string" && value ) {
  			classes = value.match( rnotwhite ) || [];
  
  			while ( ( elem = this[ i++ ] ) ) {
  				curValue = getClass( elem );
  
  				// This expression is here for better compressibility (see addClass)
  				cur = elem.nodeType === 1 &&
  					( " " + curValue + " " ).replace( rclass, " " );
  
  				if ( cur ) {
  					j = 0;
  					while ( ( clazz = classes[ j++ ] ) ) {
  
  						// Remove *all* instances
  						while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
  							cur = cur.replace( " " + clazz + " ", " " );
  						}
  					}
  
  					// Only assign if different to avoid unneeded rendering.
  					finalValue = jQuery.trim( cur );
  					if ( curValue !== finalValue ) {
  						elem.setAttribute( "class", finalValue );
  					}
  				}
  			}
  		}
  
  		return this;
  	},
  
  	toggleClass: function( value, stateVal ) {
  		var type = typeof value;
  
  		if ( typeof stateVal === "boolean" && type === "string" ) {
  			return stateVal ? this.addClass( value ) : this.removeClass( value );
  		}
  
  		if ( jQuery.isFunction( value ) ) {
  			return this.each( function( i ) {
  				jQuery( this ).toggleClass(
  					value.call( this, i, getClass( this ), stateVal ),
  					stateVal
  				);
  			} );
  		}
  
  		return this.each( function() {
  			var className, i, self, classNames;
  
  			if ( type === "string" ) {
  
  				// Toggle individual class names
  				i = 0;
  				self = jQuery( this );
  				classNames = value.match( rnotwhite ) || [];
  
  				while ( ( className = classNames[ i++ ] ) ) {
  
  					// Check each className given, space separated list
  					if ( self.hasClass( className ) ) {
  						self.removeClass( className );
  					} else {
  						self.addClass( className );
  					}
  				}
  
  			// Toggle whole class name
  			} else if ( value === undefined || type === "boolean" ) {
  				className = getClass( this );
  				if ( className ) {
  
  					// Store className if set
  					dataPriv.set( this, "__className__", className );
  				}
  
  				// If the element has a class name or if we're passed `false`,
  				// then remove the whole classname (if there was one, the above saved it).
  				// Otherwise bring back whatever was previously saved (if anything),
  				// falling back to the empty string if nothing was stored.
  				if ( this.setAttribute ) {
  					this.setAttribute( "class",
  						className || value === false ?
  						"" :
  						dataPriv.get( this, "__className__" ) || ""
  					);
  				}
  			}
  		} );
  	},
  
  	hasClass: function( selector ) {
  		var className, elem,
  			i = 0;
  
  		className = " " + selector + " ";
  		while ( ( elem = this[ i++ ] ) ) {
  			if ( elem.nodeType === 1 &&
  				( " " + getClass( elem ) + " " ).replace( rclass, " " )
  					.indexOf( className ) > -1
  			) {
  				return true;
  			}
  		}
  
  		return false;
  	}
  } );
  
  
  
  
  var rreturn = /\r/g,
  	rspaces = /[\x20\t\r\n\f]+/g;
  
  jQuery.fn.extend( {
  	val: function( value ) {
  		var hooks, ret, isFunction,
  			elem = this[ 0 ];
  
  		if ( !arguments.length ) {
  			if ( elem ) {
  				hooks = jQuery.valHooks[ elem.type ] ||
  					jQuery.valHooks[ elem.nodeName.toLowerCase() ];
  
  				if ( hooks &&
  					"get" in hooks &&
  					( ret = hooks.get( elem, "value" ) ) !== undefined
  				) {
  					return ret;
  				}
  
  				ret = elem.value;
  
  				return typeof ret === "string" ?
  
  					// Handle most common string cases
  					ret.replace( rreturn, "" ) :
  
  					// Handle cases where value is null/undef or number
  					ret == null ? "" : ret;
  			}
  
  			return;
  		}
  
  		isFunction = jQuery.isFunction( value );
  
  		return this.each( function( i ) {
  			var val;
  
  			if ( this.nodeType !== 1 ) {
  				return;
  			}
  
  			if ( isFunction ) {
  				val = value.call( this, i, jQuery( this ).val() );
  			} else {
  				val = value;
  			}
  
  			// Treat null/undefined as ""; convert numbers to string
  			if ( val == null ) {
  				val = "";
  
  			} else if ( typeof val === "number" ) {
  				val += "";
  
  			} else if ( jQuery.isArray( val ) ) {
  				val = jQuery.map( val, function( value ) {
  					return value == null ? "" : value + "";
  				} );
  			}
  
  			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
  
  			// If set returns undefined, fall back to normal setting
  			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
  				this.value = val;
  			}
  		} );
  	}
  } );
  
  jQuery.extend( {
  	valHooks: {
  		option: {
  			get: function( elem ) {
  
  				var val = jQuery.find.attr( elem, "value" );
  				return val != null ?
  					val :
  
  					// Support: IE10-11+
  					// option.text throws exceptions (#14686, #14858)
  					// Strip and collapse whitespace
  					// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
  					jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " );
  			}
  		},
  		select: {
  			get: function( elem ) {
  				var value, option,
  					options = elem.options,
  					index = elem.selectedIndex,
  					one = elem.type === "select-one" || index < 0,
  					values = one ? null : [],
  					max = one ? index + 1 : options.length,
  					i = index < 0 ?
  						max :
  						one ? index : 0;
  
  				// Loop through all the selected options
  				for ( ; i < max; i++ ) {
  					option = options[ i ];
  
  					// IE8-9 doesn't update selected after form reset (#2551)
  					if ( ( option.selected || i === index ) &&
  
  							// Don't return options that are disabled or in a disabled optgroup
  							( support.optDisabled ?
  								!option.disabled : option.getAttribute( "disabled" ) === null ) &&
  							( !option.parentNode.disabled ||
  								!jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
  
  						// Get the specific value for the option
  						value = jQuery( option ).val();
  
  						// We don't need an array for one selects
  						if ( one ) {
  							return value;
  						}
  
  						// Multi-Selects return an array
  						values.push( value );
  					}
  				}
  
  				return values;
  			},
  
  			set: function( elem, value ) {
  				var optionSet, option,
  					options = elem.options,
  					values = jQuery.makeArray( value ),
  					i = options.length;
  
  				while ( i-- ) {
  					option = options[ i ];
  					if ( option.selected =
  						jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
  					) {
  						optionSet = true;
  					}
  				}
  
  				// Force browsers to behave consistently when non-matching value is set
  				if ( !optionSet ) {
  					elem.selectedIndex = -1;
  				}
  				return values;
  			}
  		}
  	}
  } );
  
  // Radios and checkboxes getter/setter
  jQuery.each( [ "radio", "checkbox" ], function() {
  	jQuery.valHooks[ this ] = {
  		set: function( elem, value ) {
  			if ( jQuery.isArray( value ) ) {
  				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
  			}
  		}
  	};
  	if ( !support.checkOn ) {
  		jQuery.valHooks[ this ].get = function( elem ) {
  			return elem.getAttribute( "value" ) === null ? "on" : elem.value;
  		};
  	}
  } );
  
  
  
  
  // Return jQuery for attributes-only inclusion
  
  
  var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
  
  jQuery.extend( jQuery.event, {
  
  	trigger: function( event, data, elem, onlyHandlers ) {
  
  		var i, cur, tmp, bubbleType, ontype, handle, special,
  			eventPath = [ elem || document ],
  			type = hasOwn.call( event, "type" ) ? event.type : event,
  			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
  
  		cur = tmp = elem = elem || document;
  
  		// Don't do events on text and comment nodes
  		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
  			return;
  		}
  
  		// focus/blur morphs to focusin/out; ensure we're not firing them right now
  		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
  			return;
  		}
  
  		if ( type.indexOf( "." ) > -1 ) {
  
  			// Namespaced trigger; create a regexp to match event type in handle()
  			namespaces = type.split( "." );
  			type = namespaces.shift();
  			namespaces.sort();
  		}
  		ontype = type.indexOf( ":" ) < 0 && "on" + type;
  
  		// Caller can pass in a jQuery.Event object, Object, or just an event type string
  		event = event[ jQuery.expando ] ?
  			event :
  			new jQuery.Event( type, typeof event === "object" && event );
  
  		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
  		event.isTrigger = onlyHandlers ? 2 : 3;
  		event.namespace = namespaces.join( "." );
  		event.rnamespace = event.namespace ?
  			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
  			null;
  
  		// Clean up the event in case it is being reused
  		event.result = undefined;
  		if ( !event.target ) {
  			event.target = elem;
  		}
  
  		// Clone any incoming data and prepend the event, creating the handler arg list
  		data = data == null ?
  			[ event ] :
  			jQuery.makeArray( data, [ event ] );
  
  		// Allow special events to draw outside the lines
  		special = jQuery.event.special[ type ] || {};
  		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
  			return;
  		}
  
  		// Determine event propagation path in advance, per W3C events spec (#9951)
  		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
  		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
  
  			bubbleType = special.delegateType || type;
  			if ( !rfocusMorph.test( bubbleType + type ) ) {
  				cur = cur.parentNode;
  			}
  			for ( ; cur; cur = cur.parentNode ) {
  				eventPath.push( cur );
  				tmp = cur;
  			}
  
  			// Only add window if we got to document (e.g., not plain obj or detached DOM)
  			if ( tmp === ( elem.ownerDocument || document ) ) {
  				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
  			}
  		}
  
  		// Fire handlers on the event path
  		i = 0;
  		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
  
  			event.type = i > 1 ?
  				bubbleType :
  				special.bindType || type;
  
  			// jQuery handler
  			handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
  				dataPriv.get( cur, "handle" );
  			if ( handle ) {
  				handle.apply( cur, data );
  			}
  
  			// Native handler
  			handle = ontype && cur[ ontype ];
  			if ( handle && handle.apply && acceptData( cur ) ) {
  				event.result = handle.apply( cur, data );
  				if ( event.result === false ) {
  					event.preventDefault();
  				}
  			}
  		}
  		event.type = type;
  
  		// If nobody prevented the default action, do it now
  		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
  
  			if ( ( !special._default ||
  				special._default.apply( eventPath.pop(), data ) === false ) &&
  				acceptData( elem ) ) {
  
  				// Call a native DOM method on the target with the same name name as the event.
  				// Don't do default actions on window, that's where global variables be (#6170)
  				if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
  
  					// Don't re-trigger an onFOO event when we call its FOO() method
  					tmp = elem[ ontype ];
  
  					if ( tmp ) {
  						elem[ ontype ] = null;
  					}
  
  					// Prevent re-triggering of the same event, since we already bubbled it above
  					jQuery.event.triggered = type;
  					elem[ type ]();
  					jQuery.event.triggered = undefined;
  
  					if ( tmp ) {
  						elem[ ontype ] = tmp;
  					}
  				}
  			}
  		}
  
  		return event.result;
  	},
  
  	// Piggyback on a donor event to simulate a different one
  	simulate: function( type, elem, event ) {
  		var e = jQuery.extend(
  			new jQuery.Event(),
  			event,
  			{
  				type: type,
  				isSimulated: true
  
  				// Previously, `originalEvent: {}` was set here, so stopPropagation call
  				// would not be triggered on donor event, since in our own
  				// jQuery.event.stopPropagation function we had a check for existence of
  				// originalEvent.stopPropagation method, so, consequently it would be a noop.
  				//
  				// But now, this "simulate" function is used only for events
  				// for which stopPropagation() is noop, so there is no need for that anymore.
  				//
  				// For the 1.x branch though, guard for "click" and "submit"
  				// events is still used, but was moved to jQuery.event.stopPropagation function
  				// because `originalEvent` should point to the original event for the constancy
  				// with other events and for more focused logic
  			}
  		);
  
  		jQuery.event.trigger( e, null, elem );
  
  		if ( e.isDefaultPrevented() ) {
  			event.preventDefault();
  		}
  	}
  
  } );
  
  jQuery.fn.extend( {
  
  	trigger: function( type, data ) {
  		return this.each( function() {
  			jQuery.event.trigger( type, data, this );
  		} );
  	},
  	triggerHandler: function( type, data ) {
  		var elem = this[ 0 ];
  		if ( elem ) {
  			return jQuery.event.trigger( type, data, elem, true );
  		}
  	}
  } );
  
  
  jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
  	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
  	"change select submit keydown keypress keyup error contextmenu" ).split( " " ),
  	function( i, name ) {
  
  	// Handle event binding
  	jQuery.fn[ name ] = function( data, fn ) {
  		return arguments.length > 0 ?
  			this.on( name, null, data, fn ) :
  			this.trigger( name );
  	};
  } );
  
  jQuery.fn.extend( {
  	hover: function( fnOver, fnOut ) {
  		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
  	}
  } );
  
  
  
  
  support.focusin = "onfocusin" in window;
  
  
  // Support: Firefox
  // Firefox doesn't have focus(in | out) events
  // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
  //
  // Support: Chrome, Safari
  // focus(in | out) events fire after focus & blur events,
  // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
  // Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
  if ( !support.focusin ) {
  	jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
  
  		// Attach a single capturing handler on the document while someone wants focusin/focusout
  		var handler = function( event ) {
  			jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
  		};
  
  		jQuery.event.special[ fix ] = {
  			setup: function() {
  				var doc = this.ownerDocument || this,
  					attaches = dataPriv.access( doc, fix );
  
  				if ( !attaches ) {
  					doc.addEventListener( orig, handler, true );
  				}
  				dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
  			},
  			teardown: function() {
  				var doc = this.ownerDocument || this,
  					attaches = dataPriv.access( doc, fix ) - 1;
  
  				if ( !attaches ) {
  					doc.removeEventListener( orig, handler, true );
  					dataPriv.remove( doc, fix );
  
  				} else {
  					dataPriv.access( doc, fix, attaches );
  				}
  			}
  		};
  	} );
  }
  var location = window.location;
  
  var nonce = jQuery.now();
  
  var rquery = ( /\?/ );
  
  
  
  // Support: Android 2.3
  // Workaround failure to string-cast null input
  jQuery.parseJSON = function( data ) {
  	return JSON.parse( data + "" );
  };
  
  
  // Cross-browser xml parsing
  jQuery.parseXML = function( data ) {
  	var xml;
  	if ( !data || typeof data !== "string" ) {
  		return null;
  	}
  
  	// Support: IE9
  	try {
  		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
  	} catch ( e ) {
  		xml = undefined;
  	}
  
  	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
  		jQuery.error( "Invalid XML: " + data );
  	}
  	return xml;
  };
  
  
  var
  	rhash = /#.*$/,
  	rts = /([?&])_=[^&]*/,
  	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
  
  	// #7653, #8125, #8152: local protocol detection
  	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
  	rnoContent = /^(?:GET|HEAD)$/,
  	rprotocol = /^\/\//,
  
  	/* Prefilters
  	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
  	 * 2) These are called:
  	 *    - BEFORE asking for a transport
  	 *    - AFTER param serialization (s.data is a string if s.processData is true)
  	 * 3) key is the dataType
  	 * 4) the catchall symbol "*" can be used
  	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
  	 */
  	prefilters = {},
  
  	/* Transports bindings
  	 * 1) key is the dataType
  	 * 2) the catchall symbol "*" can be used
  	 * 3) selection will start with transport dataType and THEN go to "*" if needed
  	 */
  	transports = {},
  
  	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
  	allTypes = "*/".concat( "*" ),
  
  	// Anchor tag for parsing the document origin
  	originAnchor = document.createElement( "a" );
  	originAnchor.href = location.href;
  
  // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
  function addToPrefiltersOrTransports( structure ) {
  
  	// dataTypeExpression is optional and defaults to "*"
  	return function( dataTypeExpression, func ) {
  
  		if ( typeof dataTypeExpression !== "string" ) {
  			func = dataTypeExpression;
  			dataTypeExpression = "*";
  		}
  
  		var dataType,
  			i = 0,
  			dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
  
  		if ( jQuery.isFunction( func ) ) {
  
  			// For each dataType in the dataTypeExpression
  			while ( ( dataType = dataTypes[ i++ ] ) ) {
  
  				// Prepend if requested
  				if ( dataType[ 0 ] === "+" ) {
  					dataType = dataType.slice( 1 ) || "*";
  					( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
  
  				// Otherwise append
  				} else {
  					( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
  				}
  			}
  		}
  	};
  }
  
  // Base inspection function for prefilters and transports
  function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
  
  	var inspected = {},
  		seekingTransport = ( structure === transports );
  
  	function inspect( dataType ) {
  		var selected;
  		inspected[ dataType ] = true;
  		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
  			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
  			if ( typeof dataTypeOrTransport === "string" &&
  				!seekingTransport && !inspected[ dataTypeOrTransport ] ) {
  
  				options.dataTypes.unshift( dataTypeOrTransport );
  				inspect( dataTypeOrTransport );
  				return false;
  			} else if ( seekingTransport ) {
  				return !( selected = dataTypeOrTransport );
  			}
  		} );
  		return selected;
  	}
  
  	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
  }
  
  // A special extend for ajax options
  // that takes "flat" options (not to be deep extended)
  // Fixes #9887
  function ajaxExtend( target, src ) {
  	var key, deep,
  		flatOptions = jQuery.ajaxSettings.flatOptions || {};
  
  	for ( key in src ) {
  		if ( src[ key ] !== undefined ) {
  			( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
  		}
  	}
  	if ( deep ) {
  		jQuery.extend( true, target, deep );
  	}
  
  	return target;
  }
  
  /* Handles responses to an ajax request:
   * - finds the right dataType (mediates between content-type and expected dataType)
   * - returns the corresponding response
   */
  function ajaxHandleResponses( s, jqXHR, responses ) {
  
  	var ct, type, finalDataType, firstDataType,
  		contents = s.contents,
  		dataTypes = s.dataTypes;
  
  	// Remove auto dataType and get content-type in the process
  	while ( dataTypes[ 0 ] === "*" ) {
  		dataTypes.shift();
  		if ( ct === undefined ) {
  			ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
  		}
  	}
  
  	// Check if we're dealing with a known content-type
  	if ( ct ) {
  		for ( type in contents ) {
  			if ( contents[ type ] && contents[ type ].test( ct ) ) {
  				dataTypes.unshift( type );
  				break;
  			}
  		}
  	}
  
  	// Check to see if we have a response for the expected dataType
  	if ( dataTypes[ 0 ] in responses ) {
  		finalDataType = dataTypes[ 0 ];
  	} else {
  
  		// Try convertible dataTypes
  		for ( type in responses ) {
  			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
  				finalDataType = type;
  				break;
  			}
  			if ( !firstDataType ) {
  				firstDataType = type;
  			}
  		}
  
  		// Or just use first one
  		finalDataType = finalDataType || firstDataType;
  	}
  
  	// If we found a dataType
  	// We add the dataType to the list if needed
  	// and return the corresponding response
  	if ( finalDataType ) {
  		if ( finalDataType !== dataTypes[ 0 ] ) {
  			dataTypes.unshift( finalDataType );
  		}
  		return responses[ finalDataType ];
  	}
  }
  
  /* Chain conversions given the request and the original response
   * Also sets the responseXXX fields on the jqXHR instance
   */
  function ajaxConvert( s, response, jqXHR, isSuccess ) {
  	var conv2, current, conv, tmp, prev,
  		converters = {},
  
  		// Work with a copy of dataTypes in case we need to modify it for conversion
  		dataTypes = s.dataTypes.slice();
  
  	// Create converters map with lowercased keys
  	if ( dataTypes[ 1 ] ) {
  		for ( conv in s.converters ) {
  			converters[ conv.toLowerCase() ] = s.converters[ conv ];
  		}
  	}
  
  	current = dataTypes.shift();
  
  	// Convert to each sequential dataType
  	while ( current ) {
  
  		if ( s.responseFields[ current ] ) {
  			jqXHR[ s.responseFields[ current ] ] = response;
  		}
  
  		// Apply the dataFilter if provided
  		if ( !prev && isSuccess && s.dataFilter ) {
  			response = s.dataFilter( response, s.dataType );
  		}
  
  		prev = current;
  		current = dataTypes.shift();
  
  		if ( current ) {
  
  		// There's only work to do if current dataType is non-auto
  			if ( current === "*" ) {
  
  				current = prev;
  
  			// Convert response if prev dataType is non-auto and differs from current
  			} else if ( prev !== "*" && prev !== current ) {
  
  				// Seek a direct converter
  				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
  
  				// If none found, seek a pair
  				if ( !conv ) {
  					for ( conv2 in converters ) {
  
  						// If conv2 outputs current
  						tmp = conv2.split( " " );
  						if ( tmp[ 1 ] === current ) {
  
  							// If prev can be converted to accepted input
  							conv = converters[ prev + " " + tmp[ 0 ] ] ||
  								converters[ "* " + tmp[ 0 ] ];
  							if ( conv ) {
  
  								// Condense equivalence converters
  								if ( conv === true ) {
  									conv = converters[ conv2 ];
  
  								// Otherwise, insert the intermediate dataType
  								} else if ( converters[ conv2 ] !== true ) {
  									current = tmp[ 0 ];
  									dataTypes.unshift( tmp[ 1 ] );
  								}
  								break;
  							}
  						}
  					}
  				}
  
  				// Apply converter (if not an equivalence)
  				if ( conv !== true ) {
  
  					// Unless errors are allowed to bubble, catch and return them
  					if ( conv && s.throws ) {
  						response = conv( response );
  					} else {
  						try {
  							response = conv( response );
  						} catch ( e ) {
  							return {
  								state: "parsererror",
  								error: conv ? e : "No conversion from " + prev + " to " + current
  							};
  						}
  					}
  				}
  			}
  		}
  	}
  
  	return { state: "success", data: response };
  }
  
  jQuery.extend( {
  
  	// Counter for holding the number of active queries
  	active: 0,
  
  	// Last-Modified header cache for next request
  	lastModified: {},
  	etag: {},
  
  	ajaxSettings: {
  		url: location.href,
  		type: "GET",
  		isLocal: rlocalProtocol.test( location.protocol ),
  		global: true,
  		processData: true,
  		async: true,
  		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
  		/*
  		timeout: 0,
  		data: null,
  		dataType: null,
  		username: null,
  		password: null,
  		cache: null,
  		throws: false,
  		traditional: false,
  		headers: {},
  		*/
  
  		accepts: {
  			"*": allTypes,
  			text: "text/plain",
  			html: "text/html",
  			xml: "application/xml, text/xml",
  			json: "application/json, text/javascript"
  		},
  
  		contents: {
  			xml: /\bxml\b/,
  			html: /\bhtml/,
  			json: /\bjson\b/
  		},
  
  		responseFields: {
  			xml: "responseXML",
  			text: "responseText",
  			json: "responseJSON"
  		},
  
  		// Data converters
  		// Keys separate source (or catchall "*") and destination types with a single space
  		converters: {
  
  			// Convert anything to text
  			"* text": String,
  
  			// Text to html (true = no transformation)
  			"text html": true,
  
  			// Evaluate text as a json expression
  			"text json": jQuery.parseJSON,
  
  			// Parse text as xml
  			"text xml": jQuery.parseXML
  		},
  
  		// For options that shouldn't be deep extended:
  		// you can add your own custom options here if
  		// and when you create one that shouldn't be
  		// deep extended (see ajaxExtend)
  		flatOptions: {
  			url: true,
  			context: true
  		}
  	},
  
  	// Creates a full fledged settings object into target
  	// with both ajaxSettings and settings fields.
  	// If target is omitted, writes into ajaxSettings.
  	ajaxSetup: function( target, settings ) {
  		return settings ?
  
  			// Building a settings object
  			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
  
  			// Extending ajaxSettings
  			ajaxExtend( jQuery.ajaxSettings, target );
  	},
  
  	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
  	ajaxTransport: addToPrefiltersOrTransports( transports ),
  
  	// Main method
  	ajax: function( url, options ) {
  
  		// If url is an object, simulate pre-1.5 signature
  		if ( typeof url === "object" ) {
  			options = url;
  			url = undefined;
  		}
  
  		// Force options to be an object
  		options = options || {};
  
  		var transport,
  
  			// URL without anti-cache param
  			cacheURL,
  
  			// Response headers
  			responseHeadersString,
  			responseHeaders,
  
  			// timeout handle
  			timeoutTimer,
  
  			// Url cleanup var
  			urlAnchor,
  
  			// To know if global events are to be dispatched
  			fireGlobals,
  
  			// Loop variable
  			i,
  
  			// Create the final options object
  			s = jQuery.ajaxSetup( {}, options ),
  
  			// Callbacks context
  			callbackContext = s.context || s,
  
  			// Context for global events is callbackContext if it is a DOM node or jQuery collection
  			globalEventContext = s.context &&
  				( callbackContext.nodeType || callbackContext.jquery ) ?
  					jQuery( callbackContext ) :
  					jQuery.event,
  
  			// Deferreds
  			deferred = jQuery.Deferred(),
  			completeDeferred = jQuery.Callbacks( "once memory" ),
  
  			// Status-dependent callbacks
  			statusCode = s.statusCode || {},
  
  			// Headers (they are sent all at once)
  			requestHeaders = {},
  			requestHeadersNames = {},
  
  			// The jqXHR state
  			state = 0,
  
  			// Default abort message
  			strAbort = "canceled",
  
  			// Fake xhr
  			jqXHR = {
  				readyState: 0,
  
  				// Builds headers hashtable if needed
  				getResponseHeader: function( key ) {
  					var match;
  					if ( state === 2 ) {
  						if ( !responseHeaders ) {
  							responseHeaders = {};
  							while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
  								responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
  							}
  						}
  						match = responseHeaders[ key.toLowerCase() ];
  					}
  					return match == null ? null : match;
  				},
  
  				// Raw string
  				getAllResponseHeaders: function() {
  					return state === 2 ? responseHeadersString : null;
  				},
  
  				// Caches the header
  				setRequestHeader: function( name, value ) {
  					var lname = name.toLowerCase();
  					if ( !state ) {
  						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
  						requestHeaders[ name ] = value;
  					}
  					return this;
  				},
  
  				// Overrides response content-type header
  				overrideMimeType: function( type ) {
  					if ( !state ) {
  						s.mimeType = type;
  					}
  					return this;
  				},
  
  				// Status-dependent callbacks
  				statusCode: function( map ) {
  					var code;
  					if ( map ) {
  						if ( state < 2 ) {
  							for ( code in map ) {
  
  								// Lazy-add the new callback in a way that preserves old ones
  								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
  							}
  						} else {
  
  							// Execute the appropriate callbacks
  							jqXHR.always( map[ jqXHR.status ] );
  						}
  					}
  					return this;
  				},
  
  				// Cancel the request
  				abort: function( statusText ) {
  					var finalText = statusText || strAbort;
  					if ( transport ) {
  						transport.abort( finalText );
  					}
  					done( 0, finalText );
  					return this;
  				}
  			};
  
  		// Attach deferreds
  		deferred.promise( jqXHR ).complete = completeDeferred.add;
  		jqXHR.success = jqXHR.done;
  		jqXHR.error = jqXHR.fail;
  
  		// Remove hash character (#7531: and string promotion)
  		// Add protocol if not provided (prefilters might expect it)
  		// Handle falsy url in the settings object (#10093: consistency with old signature)
  		// We also use the url parameter if available
  		s.url = ( ( url || s.url || location.href ) + "" ).replace( rhash, "" )
  			.replace( rprotocol, location.protocol + "//" );
  
  		// Alias method option to type as per ticket #12004
  		s.type = options.method || options.type || s.method || s.type;
  
  		// Extract dataTypes list
  		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
  
  		// A cross-domain request is in order when the origin doesn't match the current origin.
  		if ( s.crossDomain == null ) {
  			urlAnchor = document.createElement( "a" );
  
  			// Support: IE8-11+
  			// IE throws exception if url is malformed, e.g. http://example.com:80x/
  			try {
  				urlAnchor.href = s.url;
  
  				// Support: IE8-11+
  				// Anchor's host property isn't correctly set when s.url is relative
  				urlAnchor.href = urlAnchor.href;
  				s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
  					urlAnchor.protocol + "//" + urlAnchor.host;
  			} catch ( e ) {
  
  				// If there is an error parsing the URL, assume it is crossDomain,
  				// it can be rejected by the transport if it is invalid
  				s.crossDomain = true;
  			}
  		}
  
  		// Convert data if not already a string
  		if ( s.data && s.processData && typeof s.data !== "string" ) {
  			s.data = jQuery.param( s.data, s.traditional );
  		}
  
  		// Apply prefilters
  		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
  
  		// If request was aborted inside a prefilter, stop there
  		if ( state === 2 ) {
  			return jqXHR;
  		}
  
  		// We can fire global events as of now if asked to
  		// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
  		fireGlobals = jQuery.event && s.global;
  
  		// Watch for a new set of requests
  		if ( fireGlobals && jQuery.active++ === 0 ) {
  			jQuery.event.trigger( "ajaxStart" );
  		}
  
  		// Uppercase the type
  		s.type = s.type.toUpperCase();
  
  		// Determine if request has content
  		s.hasContent = !rnoContent.test( s.type );
  
  		// Save the URL in case we're toying with the If-Modified-Since
  		// and/or If-None-Match header later on
  		cacheURL = s.url;
  
  		// More options handling for requests with no content
  		if ( !s.hasContent ) {
  
  			// If data is available, append data to url
  			if ( s.data ) {
  				cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
  
  				// #9682: remove data so that it's not used in an eventual retry
  				delete s.data;
  			}
  
  			// Add anti-cache in url if needed
  			if ( s.cache === false ) {
  				s.url = rts.test( cacheURL ) ?
  
  					// If there is already a '_' parameter, set its value
  					cacheURL.replace( rts, "$1_=" + nonce++ ) :
  
  					// Otherwise add one to the end
  					cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
  			}
  		}
  
  		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  		if ( s.ifModified ) {
  			if ( jQuery.lastModified[ cacheURL ] ) {
  				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
  			}
  			if ( jQuery.etag[ cacheURL ] ) {
  				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
  			}
  		}
  
  		// Set the correct header, if data is being sent
  		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
  			jqXHR.setRequestHeader( "Content-Type", s.contentType );
  		}
  
  		// Set the Accepts header for the server, depending on the dataType
  		jqXHR.setRequestHeader(
  			"Accept",
  			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
  				s.accepts[ s.dataTypes[ 0 ] ] +
  					( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
  				s.accepts[ "*" ]
  		);
  
  		// Check for headers option
  		for ( i in s.headers ) {
  			jqXHR.setRequestHeader( i, s.headers[ i ] );
  		}
  
  		// Allow custom headers/mimetypes and early abort
  		if ( s.beforeSend &&
  			( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
  
  			// Abort if not done already and return
  			return jqXHR.abort();
  		}
  
  		// Aborting is no longer a cancellation
  		strAbort = "abort";
  
  		// Install callbacks on deferreds
  		for ( i in { success: 1, error: 1, complete: 1 } ) {
  			jqXHR[ i ]( s[ i ] );
  		}
  
  		// Get transport
  		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
  
  		// If no transport, we auto-abort
  		if ( !transport ) {
  			done( -1, "No Transport" );
  		} else {
  			jqXHR.readyState = 1;
  
  			// Send global event
  			if ( fireGlobals ) {
  				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
  			}
  
  			// If request was aborted inside ajaxSend, stop there
  			if ( state === 2 ) {
  				return jqXHR;
  			}
  
  			// Timeout
  			if ( s.async && s.timeout > 0 ) {
  				timeoutTimer = window.setTimeout( function() {
  					jqXHR.abort( "timeout" );
  				}, s.timeout );
  			}
  
  			try {
  				state = 1;
  				transport.send( requestHeaders, done );
  			} catch ( e ) {
  
  				// Propagate exception as error if not done
  				if ( state < 2 ) {
  					done( -1, e );
  
  				// Simply rethrow otherwise
  				} else {
  					throw e;
  				}
  			}
  		}
  
  		// Callback for when everything is done
  		function done( status, nativeStatusText, responses, headers ) {
  			var isSuccess, success, error, response, modified,
  				statusText = nativeStatusText;
  
  			// Called once
  			if ( state === 2 ) {
  				return;
  			}
  
  			// State is "done" now
  			state = 2;
  
  			// Clear timeout if it exists
  			if ( timeoutTimer ) {
  				window.clearTimeout( timeoutTimer );
  			}
  
  			// Dereference transport for early garbage collection
  			// (no matter how long the jqXHR object will be used)
  			transport = undefined;
  
  			// Cache response headers
  			responseHeadersString = headers || "";
  
  			// Set readyState
  			jqXHR.readyState = status > 0 ? 4 : 0;
  
  			// Determine if successful
  			isSuccess = status >= 200 && status < 300 || status === 304;
  
  			// Get response data
  			if ( responses ) {
  				response = ajaxHandleResponses( s, jqXHR, responses );
  			}
  
  			// Convert no matter what (that way responseXXX fields are always set)
  			response = ajaxConvert( s, response, jqXHR, isSuccess );
  
  			// If successful, handle type chaining
  			if ( isSuccess ) {
  
  				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
  				if ( s.ifModified ) {
  					modified = jqXHR.getResponseHeader( "Last-Modified" );
  					if ( modified ) {
  						jQuery.lastModified[ cacheURL ] = modified;
  					}
  					modified = jqXHR.getResponseHeader( "etag" );
  					if ( modified ) {
  						jQuery.etag[ cacheURL ] = modified;
  					}
  				}
  
  				// if no content
  				if ( status === 204 || s.type === "HEAD" ) {
  					statusText = "nocontent";
  
  				// if not modified
  				} else if ( status === 304 ) {
  					statusText = "notmodified";
  
  				// If we have data, let's convert it
  				} else {
  					statusText = response.state;
  					success = response.data;
  					error = response.error;
  					isSuccess = !error;
  				}
  			} else {
  
  				// Extract error from statusText and normalize for non-aborts
  				error = statusText;
  				if ( status || !statusText ) {
  					statusText = "error";
  					if ( status < 0 ) {
  						status = 0;
  					}
  				}
  			}
  
  			// Set data for the fake xhr object
  			jqXHR.status = status;
  			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
  
  			// Success/Error
  			if ( isSuccess ) {
  				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
  			} else {
  				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
  			}
  
  			// Status-dependent callbacks
  			jqXHR.statusCode( statusCode );
  			statusCode = undefined;
  
  			if ( fireGlobals ) {
  				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
  					[ jqXHR, s, isSuccess ? success : error ] );
  			}
  
  			// Complete
  			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
  
  			if ( fireGlobals ) {
  				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
  
  				// Handle the global AJAX counter
  				if ( !( --jQuery.active ) ) {
  					jQuery.event.trigger( "ajaxStop" );
  				}
  			}
  		}
  
  		return jqXHR;
  	},
  
  	getJSON: function( url, data, callback ) {
  		return jQuery.get( url, data, callback, "json" );
  	},
  
  	getScript: function( url, callback ) {
  		return jQuery.get( url, undefined, callback, "script" );
  	}
  } );
  
  jQuery.each( [ "get", "post" ], function( i, method ) {
  	jQuery[ method ] = function( url, data, callback, type ) {
  
  		// Shift arguments if data argument was omitted
  		if ( jQuery.isFunction( data ) ) {
  			type = type || callback;
  			callback = data;
  			data = undefined;
  		}
  
  		// The url can be an options object (which then must have .url)
  		return jQuery.ajax( jQuery.extend( {
  			url: url,
  			type: method,
  			dataType: type,
  			data: data,
  			success: callback
  		}, jQuery.isPlainObject( url ) && url ) );
  	};
  } );
  
  
  jQuery._evalUrl = function( url ) {
  	return jQuery.ajax( {
  		url: url,
  
  		// Make this explicit, since user can override this through ajaxSetup (#11264)
  		type: "GET",
  		dataType: "script",
  		async: false,
  		global: false,
  		"throws": true
  	} );
  };
  
  
  jQuery.fn.extend( {
  	wrapAll: function( html ) {
  		var wrap;
  
  		if ( jQuery.isFunction( html ) ) {
  			return this.each( function( i ) {
  				jQuery( this ).wrapAll( html.call( this, i ) );
  			} );
  		}
  
  		if ( this[ 0 ] ) {
  
  			// The elements to wrap the target around
  			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
  
  			if ( this[ 0 ].parentNode ) {
  				wrap.insertBefore( this[ 0 ] );
  			}
  
  			wrap.map( function() {
  				var elem = this;
  
  				while ( elem.firstElementChild ) {
  					elem = elem.firstElementChild;
  				}
  
  				return elem;
  			} ).append( this );
  		}
  
  		return this;
  	},
  
  	wrapInner: function( html ) {
  		if ( jQuery.isFunction( html ) ) {
  			return this.each( function( i ) {
  				jQuery( this ).wrapInner( html.call( this, i ) );
  			} );
  		}
  
  		return this.each( function() {
  			var self = jQuery( this ),
  				contents = self.contents();
  
  			if ( contents.length ) {
  				contents.wrapAll( html );
  
  			} else {
  				self.append( html );
  			}
  		} );
  	},
  
  	wrap: function( html ) {
  		var isFunction = jQuery.isFunction( html );
  
  		return this.each( function( i ) {
  			jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
  		} );
  	},
  
  	unwrap: function() {
  		return this.parent().each( function() {
  			if ( !jQuery.nodeName( this, "body" ) ) {
  				jQuery( this ).replaceWith( this.childNodes );
  			}
  		} ).end();
  	}
  } );
  
  
  jQuery.expr.filters.hidden = function( elem ) {
  	return !jQuery.expr.filters.visible( elem );
  };
  jQuery.expr.filters.visible = function( elem ) {
  
  	// Support: Opera <= 12.12
  	// Opera reports offsetWidths and offsetHeights less than zero on some elements
  	// Use OR instead of AND as the element is not visible if either is true
  	// See tickets #10406 and #13132
  	return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem.getClientRects().length > 0;
  };
  
  
  
  
  var r20 = /%20/g,
  	rbracket = /\[\]$/,
  	rCRLF = /\r?\n/g,
  	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
  	rsubmittable = /^(?:input|select|textarea|keygen)/i;
  
  function buildParams( prefix, obj, traditional, add ) {
  	var name;
  
  	if ( jQuery.isArray( obj ) ) {
  
  		// Serialize array item.
  		jQuery.each( obj, function( i, v ) {
  			if ( traditional || rbracket.test( prefix ) ) {
  
  				// Treat each array item as a scalar.
  				add( prefix, v );
  
  			} else {
  
  				// Item is non-scalar (array or object), encode its numeric index.
  				buildParams(
  					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
  					v,
  					traditional,
  					add
  				);
  			}
  		} );
  
  	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
  
  		// Serialize object item.
  		for ( name in obj ) {
  			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
  		}
  
  	} else {
  
  		// Serialize scalar item.
  		add( prefix, obj );
  	}
  }
  
  // Serialize an array of form elements or a set of
  // key/values into a query string
  jQuery.param = function( a, traditional ) {
  	var prefix,
  		s = [],
  		add = function( key, value ) {
  
  			// If value is a function, invoke it and return its value
  			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
  			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
  		};
  
  	// Set traditional to true for jQuery <= 1.3.2 behavior.
  	if ( traditional === undefined ) {
  		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
  	}
  
  	// If an array was passed in, assume that it is an array of form elements.
  	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
  
  		// Serialize the form elements
  		jQuery.each( a, function() {
  			add( this.name, this.value );
  		} );
  
  	} else {
  
  		// If traditional, encode the "old" way (the way 1.3.2 or older
  		// did it), otherwise encode params recursively.
  		for ( prefix in a ) {
  			buildParams( prefix, a[ prefix ], traditional, add );
  		}
  	}
  
  	// Return the resulting serialization
  	return s.join( "&" ).replace( r20, "+" );
  };
  
  jQuery.fn.extend( {
  	serialize: function() {
  		return jQuery.param( this.serializeArray() );
  	},
  	serializeArray: function() {
  		return this.map( function() {
  
  			// Can add propHook for "elements" to filter or add form elements
  			var elements = jQuery.prop( this, "elements" );
  			return elements ? jQuery.makeArray( elements ) : this;
  		} )
  		.filter( function() {
  			var type = this.type;
  
  			// Use .is( ":disabled" ) so that fieldset[disabled] works
  			return this.name && !jQuery( this ).is( ":disabled" ) &&
  				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
  				( this.checked || !rcheckableType.test( type ) );
  		} )
  		.map( function( i, elem ) {
  			var val = jQuery( this ).val();
  
  			return val == null ?
  				null :
  				jQuery.isArray( val ) ?
  					jQuery.map( val, function( val ) {
  						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
  					} ) :
  					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
  		} ).get();
  	}
  } );
  
  
  jQuery.ajaxSettings.xhr = function() {
  	try {
  		return new window.XMLHttpRequest();
  	} catch ( e ) {}
  };
  
  var xhrSuccessStatus = {
  
  		// File protocol always yields status code 0, assume 200
  		0: 200,
  
  		// Support: IE9
  		// #1450: sometimes IE returns 1223 when it should be 204
  		1223: 204
  	},
  	xhrSupported = jQuery.ajaxSettings.xhr();
  
  support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
  support.ajax = xhrSupported = !!xhrSupported;
  
  jQuery.ajaxTransport( function( options ) {
  	var callback, errorCallback;
  
  	// Cross domain only allowed if supported through XMLHttpRequest
  	if ( support.cors || xhrSupported && !options.crossDomain ) {
  		return {
  			send: function( headers, complete ) {
  				var i,
  					xhr = options.xhr();
  
  				xhr.open(
  					options.type,
  					options.url,
  					options.async,
  					options.username,
  					options.password
  				);
  
  				// Apply custom fields if provided
  				if ( options.xhrFields ) {
  					for ( i in options.xhrFields ) {
  						xhr[ i ] = options.xhrFields[ i ];
  					}
  				}
  
  				// Override mime type if needed
  				if ( options.mimeType && xhr.overrideMimeType ) {
  					xhr.overrideMimeType( options.mimeType );
  				}
  
  				// X-Requested-With header
  				// For cross-domain requests, seeing as conditions for a preflight are
  				// akin to a jigsaw puzzle, we simply never set it to be sure.
  				// (it can always be set on a per-request basis or even using ajaxSetup)
  				// For same-domain requests, won't change header if already provided.
  				if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
  					headers[ "X-Requested-With" ] = "XMLHttpRequest";
  				}
  
  				// Set headers
  				for ( i in headers ) {
  					xhr.setRequestHeader( i, headers[ i ] );
  				}
  
  				// Callback
  				callback = function( type ) {
  					return function() {
  						if ( callback ) {
  							callback = errorCallback = xhr.onload =
  								xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;
  
  							if ( type === "abort" ) {
  								xhr.abort();
  							} else if ( type === "error" ) {
  
  								// Support: IE9
  								// On a manual native abort, IE9 throws
  								// errors on any property access that is not readyState
  								if ( typeof xhr.status !== "number" ) {
  									complete( 0, "error" );
  								} else {
  									complete(
  
  										// File: protocol always yields status 0; see #8605, #14207
  										xhr.status,
  										xhr.statusText
  									);
  								}
  							} else {
  								complete(
  									xhrSuccessStatus[ xhr.status ] || xhr.status,
  									xhr.statusText,
  
  									// Support: IE9 only
  									// IE9 has no XHR2 but throws on binary (trac-11426)
  									// For XHR2 non-text, let the caller handle it (gh-2498)
  									( xhr.responseType || "text" ) !== "text"  ||
  									typeof xhr.responseText !== "string" ?
  										{ binary: xhr.response } :
  										{ text: xhr.responseText },
  									xhr.getAllResponseHeaders()
  								);
  							}
  						}
  					};
  				};
  
  				// Listen to events
  				xhr.onload = callback();
  				errorCallback = xhr.onerror = callback( "error" );
  
  				// Support: IE9
  				// Use onreadystatechange to replace onabort
  				// to handle uncaught aborts
  				if ( xhr.onabort !== undefined ) {
  					xhr.onabort = errorCallback;
  				} else {
  					xhr.onreadystatechange = function() {
  
  						// Check readyState before timeout as it changes
  						if ( xhr.readyState === 4 ) {
  
  							// Allow onerror to be called first,
  							// but that will not handle a native abort
  							// Also, save errorCallback to a variable
  							// as xhr.onerror cannot be accessed
  							window.setTimeout( function() {
  								if ( callback ) {
  									errorCallback();
  								}
  							} );
  						}
  					};
  				}
  
  				// Create the abort callback
  				callback = callback( "abort" );
  
  				try {
  
  					// Do send the request (this may raise an exception)
  					xhr.send( options.hasContent && options.data || null );
  				} catch ( e ) {
  
  					// #14683: Only rethrow if this hasn't been notified as an error yet
  					if ( callback ) {
  						throw e;
  					}
  				}
  			},
  
  			abort: function() {
  				if ( callback ) {
  					callback();
  				}
  			}
  		};
  	}
  } );
  
  
  
  
  // Install script dataType
  jQuery.ajaxSetup( {
  	accepts: {
  		script: "text/javascript, application/javascript, " +
  			"application/ecmascript, application/x-ecmascript"
  	},
  	contents: {
  		script: /\b(?:java|ecma)script\b/
  	},
  	converters: {
  		"text script": function( text ) {
  			jQuery.globalEval( text );
  			return text;
  		}
  	}
  } );
  
  // Handle cache's special case and crossDomain
  jQuery.ajaxPrefilter( "script", function( s ) {
  	if ( s.cache === undefined ) {
  		s.cache = false;
  	}
  	if ( s.crossDomain ) {
  		s.type = "GET";
  	}
  } );
  
  // Bind script tag hack transport
  jQuery.ajaxTransport( "script", function( s ) {
  
  	// This transport only deals with cross domain requests
  	if ( s.crossDomain ) {
  		var script, callback;
  		return {
  			send: function( _, complete ) {
  				script = jQuery( "<script>" ).prop( {
  					charset: s.scriptCharset,
  					src: s.url
  				} ).on(
  					"load error",
  					callback = function( evt ) {
  						script.remove();
  						callback = null;
  						if ( evt ) {
  							complete( evt.type === "error" ? 404 : 200, evt.type );
  						}
  					}
  				);
  
  				// Use native DOM manipulation to avoid our domManip AJAX trickery
  				document.head.appendChild( script[ 0 ] );
  			},
  			abort: function() {
  				if ( callback ) {
  					callback();
  				}
  			}
  		};
  	}
  } );
  
  
  
  
  var oldCallbacks = [],
  	rjsonp = /(=)\?(?=&|$)|\?\?/;
  
  // Default jsonp settings
  jQuery.ajaxSetup( {
  	jsonp: "callback",
  	jsonpCallback: function() {
  		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
  		this[ callback ] = true;
  		return callback;
  	}
  } );
  
  // Detect, normalize options and install callbacks for jsonp requests
  jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
  
  	var callbackName, overwritten, responseContainer,
  		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
  			"url" :
  			typeof s.data === "string" &&
  				( s.contentType || "" )
  					.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
  				rjsonp.test( s.data ) && "data"
  		);
  
  	// Handle iff the expected data type is "jsonp" or we have a parameter to set
  	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
  
  		// Get callback name, remembering preexisting value associated with it
  		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
  			s.jsonpCallback() :
  			s.jsonpCallback;
  
  		// Insert callback into url or form data
  		if ( jsonProp ) {
  			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
  		} else if ( s.jsonp !== false ) {
  			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
  		}
  
  		// Use data converter to retrieve json after script execution
  		s.converters[ "script json" ] = function() {
  			if ( !responseContainer ) {
  				jQuery.error( callbackName + " was not called" );
  			}
  			return responseContainer[ 0 ];
  		};
  
  		// Force json dataType
  		s.dataTypes[ 0 ] = "json";
  
  		// Install callback
  		overwritten = window[ callbackName ];
  		window[ callbackName ] = function() {
  			responseContainer = arguments;
  		};
  
  		// Clean-up function (fires after converters)
  		jqXHR.always( function() {
  
  			// If previous value didn't exist - remove it
  			if ( overwritten === undefined ) {
  				jQuery( window ).removeProp( callbackName );
  
  			// Otherwise restore preexisting value
  			} else {
  				window[ callbackName ] = overwritten;
  			}
  
  			// Save back as free
  			if ( s[ callbackName ] ) {
  
  				// Make sure that re-using the options doesn't screw things around
  				s.jsonpCallback = originalSettings.jsonpCallback;
  
  				// Save the callback name for future use
  				oldCallbacks.push( callbackName );
  			}
  
  			// Call if it was a function and we have a response
  			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
  				overwritten( responseContainer[ 0 ] );
  			}
  
  			responseContainer = overwritten = undefined;
  		} );
  
  		// Delegate to script
  		return "script";
  	}
  } );
  
  
  
  
  // Argument "data" should be string of html
  // context (optional): If specified, the fragment will be created in this context,
  // defaults to document
  // keepScripts (optional): If true, will include scripts passed in the html string
  jQuery.parseHTML = function( data, context, keepScripts ) {
  	if ( !data || typeof data !== "string" ) {
  		return null;
  	}
  	if ( typeof context === "boolean" ) {
  		keepScripts = context;
  		context = false;
  	}
  	context = context || document;
  
  	var parsed = rsingleTag.exec( data ),
  		scripts = !keepScripts && [];
  
  	// Single tag
  	if ( parsed ) {
  		return [ context.createElement( parsed[ 1 ] ) ];
  	}
  
  	parsed = buildFragment( [ data ], context, scripts );
  
  	if ( scripts && scripts.length ) {
  		jQuery( scripts ).remove();
  	}
  
  	return jQuery.merge( [], parsed.childNodes );
  };
  
  
  // Keep a copy of the old load method
  var _load = jQuery.fn.load;
  
  /**
   * Load a url into a page
   */
  jQuery.fn.load = function( url, params, callback ) {
  	if ( typeof url !== "string" && _load ) {
  		return _load.apply( this, arguments );
  	}
  
  	var selector, type, response,
  		self = this,
  		off = url.indexOf( " " );
  
  	if ( off > -1 ) {
  		selector = jQuery.trim( url.slice( off ) );
  		url = url.slice( 0, off );
  	}
  
  	// If it's a function
  	if ( jQuery.isFunction( params ) ) {
  
  		// We assume that it's the callback
  		callback = params;
  		params = undefined;
  
  	// Otherwise, build a param string
  	} else if ( params && typeof params === "object" ) {
  		type = "POST";
  	}
  
  	// If we have elements to modify, make the request
  	if ( self.length > 0 ) {
  		jQuery.ajax( {
  			url: url,
  
  			// If "type" variable is undefined, then "GET" method will be used.
  			// Make value of this field explicit since
  			// user can override it through ajaxSetup method
  			type: type || "GET",
  			dataType: "html",
  			data: params
  		} ).done( function( responseText ) {
  
  			// Save response for use in complete callback
  			response = arguments;
  
  			self.html( selector ?
  
  				// If a selector was specified, locate the right elements in a dummy div
  				// Exclude scripts to avoid IE 'Permission Denied' errors
  				jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
  
  				// Otherwise use the full result
  				responseText );
  
  		// If the request succeeds, this function gets "data", "status", "jqXHR"
  		// but they are ignored because response was set above.
  		// If it fails, this function gets "jqXHR", "status", "error"
  		} ).always( callback && function( jqXHR, status ) {
  			self.each( function() {
  				callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
  			} );
  		} );
  	}
  
  	return this;
  };
  
  
  
  
  // Attach a bunch of functions for handling common AJAX events
  jQuery.each( [
  	"ajaxStart",
  	"ajaxStop",
  	"ajaxComplete",
  	"ajaxError",
  	"ajaxSuccess",
  	"ajaxSend"
  ], function( i, type ) {
  	jQuery.fn[ type ] = function( fn ) {
  		return this.on( type, fn );
  	};
  } );
  
  
  
  
  jQuery.expr.filters.animated = function( elem ) {
  	return jQuery.grep( jQuery.timers, function( fn ) {
  		return elem === fn.elem;
  	} ).length;
  };
  
  
  
  
  /**
   * Gets a window from an element
   */
  function getWindow( elem ) {
  	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
  }
  
  jQuery.offset = {
  	setOffset: function( elem, options, i ) {
  		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
  			position = jQuery.css( elem, "position" ),
  			curElem = jQuery( elem ),
  			props = {};
  
  		// Set position first, in-case top/left are set even on static elem
  		if ( position === "static" ) {
  			elem.style.position = "relative";
  		}
  
  		curOffset = curElem.offset();
  		curCSSTop = jQuery.css( elem, "top" );
  		curCSSLeft = jQuery.css( elem, "left" );
  		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
  			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
  
  		// Need to be able to calculate position if either
  		// top or left is auto and position is either absolute or fixed
  		if ( calculatePosition ) {
  			curPosition = curElem.position();
  			curTop = curPosition.top;
  			curLeft = curPosition.left;
  
  		} else {
  			curTop = parseFloat( curCSSTop ) || 0;
  			curLeft = parseFloat( curCSSLeft ) || 0;
  		}
  
  		if ( jQuery.isFunction( options ) ) {
  
  			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
  			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
  		}
  
  		if ( options.top != null ) {
  			props.top = ( options.top - curOffset.top ) + curTop;
  		}
  		if ( options.left != null ) {
  			props.left = ( options.left - curOffset.left ) + curLeft;
  		}
  
  		if ( "using" in options ) {
  			options.using.call( elem, props );
  
  		} else {
  			curElem.css( props );
  		}
  	}
  };
  
  jQuery.fn.extend( {
  	offset: function( options ) {
  		if ( arguments.length ) {
  			return options === undefined ?
  				this :
  				this.each( function( i ) {
  					jQuery.offset.setOffset( this, options, i );
  				} );
  		}
  
  		var docElem, win,
  			elem = this[ 0 ],
  			box = { top: 0, left: 0 },
  			doc = elem && elem.ownerDocument;
  
  		if ( !doc ) {
  			return;
  		}
  
  		docElem = doc.documentElement;
  
  		// Make sure it's not a disconnected DOM node
  		if ( !jQuery.contains( docElem, elem ) ) {
  			return box;
  		}
  
  		box = elem.getBoundingClientRect();
  		win = getWindow( doc );
  		return {
  			top: box.top + win.pageYOffset - docElem.clientTop,
  			left: box.left + win.pageXOffset - docElem.clientLeft
  		};
  	},
  
  	position: function() {
  		if ( !this[ 0 ] ) {
  			return;
  		}
  
  		var offsetParent, offset,
  			elem = this[ 0 ],
  			parentOffset = { top: 0, left: 0 };
  
  		// Fixed elements are offset from window (parentOffset = {top:0, left: 0},
  		// because it is its only offset parent
  		if ( jQuery.css( elem, "position" ) === "fixed" ) {
  
  			// Assume getBoundingClientRect is there when computed position is fixed
  			offset = elem.getBoundingClientRect();
  
  		} else {
  
  			// Get *real* offsetParent
  			offsetParent = this.offsetParent();
  
  			// Get correct offsets
  			offset = this.offset();
  			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
  				parentOffset = offsetParent.offset();
  			}
  
  			// Add offsetParent borders
  			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
  			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
  		}
  
  		// Subtract parent offsets and element margins
  		return {
  			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
  			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
  		};
  	},
  
  	// This method will return documentElement in the following cases:
  	// 1) For the element inside the iframe without offsetParent, this method will return
  	//    documentElement of the parent window
  	// 2) For the hidden or detached element
  	// 3) For body or html element, i.e. in case of the html node - it will return itself
  	//
  	// but those exceptions were never presented as a real life use-cases
  	// and might be considered as more preferable results.
  	//
  	// This logic, however, is not guaranteed and can change at any point in the future
  	offsetParent: function() {
  		return this.map( function() {
  			var offsetParent = this.offsetParent;
  
  			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
  				offsetParent = offsetParent.offsetParent;
  			}
  
  			return offsetParent || documentElement;
  		} );
  	}
  } );
  
  // Create scrollLeft and scrollTop methods
  jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
  	var top = "pageYOffset" === prop;
  
  	jQuery.fn[ method ] = function( val ) {
  		return access( this, function( elem, method, val ) {
  			var win = getWindow( elem );
  
  			if ( val === undefined ) {
  				return win ? win[ prop ] : elem[ method ];
  			}
  
  			if ( win ) {
  				win.scrollTo(
  					!top ? val : win.pageXOffset,
  					top ? val : win.pageYOffset
  				);
  
  			} else {
  				elem[ method ] = val;
  			}
  		}, method, val, arguments.length );
  	};
  } );
  
  // Support: Safari<7-8+, Chrome<37-44+
  // Add the top/left cssHooks using jQuery.fn.position
  // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
  // Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
  // getComputedStyle returns percent when specified for top/left/bottom/right;
  // rather than make the css module depend on the offset module, just check for it here
  jQuery.each( [ "top", "left" ], function( i, prop ) {
  	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
  		function( elem, computed ) {
  			if ( computed ) {
  				computed = curCSS( elem, prop );
  
  				// If curCSS returns percentage, fallback to offset
  				return rnumnonpx.test( computed ) ?
  					jQuery( elem ).position()[ prop ] + "px" :
  					computed;
  			}
  		}
  	);
  } );
  
  
  // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
  jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
  	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
  		function( defaultExtra, funcName ) {
  
  		// Margin is only for outerHeight, outerWidth
  		jQuery.fn[ funcName ] = function( margin, value ) {
  			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
  				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
  
  			return access( this, function( elem, type, value ) {
  				var doc;
  
  				if ( jQuery.isWindow( elem ) ) {
  
  					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
  					// isn't a whole lot we can do. See pull request at this URL for discussion:
  					// https://github.com/jquery/jquery/pull/764
  					return elem.document.documentElement[ "client" + name ];
  				}
  
  				// Get document width or height
  				if ( elem.nodeType === 9 ) {
  					doc = elem.documentElement;
  
  					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
  					// whichever is greatest
  					return Math.max(
  						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
  						elem.body[ "offset" + name ], doc[ "offset" + name ],
  						doc[ "client" + name ]
  					);
  				}
  
  				return value === undefined ?
  
  					// Get width or height on the element, requesting but not forcing parseFloat
  					jQuery.css( elem, type, extra ) :
  
  					// Set width or height on the element
  					jQuery.style( elem, type, value, extra );
  			}, type, chainable ? margin : undefined, chainable, null );
  		};
  	} );
  } );
  
  
  jQuery.fn.extend( {
  
  	bind: function( types, data, fn ) {
  		return this.on( types, null, data, fn );
  	},
  	unbind: function( types, fn ) {
  		return this.off( types, null, fn );
  	},
  
  	delegate: function( selector, types, data, fn ) {
  		return this.on( types, selector, data, fn );
  	},
  	undelegate: function( selector, types, fn ) {
  
  		// ( namespace ) or ( selector, types [, fn] )
  		return arguments.length === 1 ?
  			this.off( selector, "**" ) :
  			this.off( types, selector || "**", fn );
  	},
  	size: function() {
  		return this.length;
  	}
  } );
  
  jQuery.fn.andSelf = jQuery.fn.addBack;
  
  
  
  
  // Register as a named AMD module, since jQuery can be concatenated with other
  // files that may use define, but not via a proper concatenation script that
  // understands anonymous AMD modules. A named AMD is safest and most robust
  // way to register. Lowercase jquery is used because AMD module names are
  // derived from file names, and jQuery is normally delivered in a lowercase
  // file name. Do this after creating the global so that if an AMD module wants
  // to call noConflict to hide this version of jQuery, it will work.
  
  // Note that for maximum portability, libraries that are not jQuery should
  // declare themselves as anonymous modules, and avoid setting a global if an
  // AMD loader is present. jQuery is a special case. For more information, see
  // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
  
  if ( typeof define === "function" && define.amd ) {
  	define( "jquery", [], function() {
  		return jQuery;
  	} );
  }
  
  
  
  var
  
  	// Map over jQuery in case of overwrite
  	_jQuery = window.jQuery,
  
  	// Map over the $ in case of overwrite
  	_$ = window.$;
  
  jQuery.noConflict = function( deep ) {
  	if ( window.$ === jQuery ) {
  		window.$ = _$;
  	}
  
  	if ( deep && window.jQuery === jQuery ) {
  		window.jQuery = _jQuery;
  	}
  
  	return jQuery;
  };
  
  // Expose jQuery and $ identifiers, even in AMD
  // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
  // and CommonJS for browser emulators (#13566)
  if ( !noGlobal ) {
  	window.jQuery = window.$ = jQuery;
  }
  
  return jQuery;
  }));
  
  /*
   * jQuery Rebox [http://trentrichardson.com/examples/jQuery-Rebox]
   * By: Trent Richardson [http://trentrichardson.com]
   *
   * Copyright 2014 Trent Richardson
   * Dual licensed under the MIT license.
   * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
   */
  (function($){
  	if(!$){
  		return;
  	}
  	$.rebox = function($this, options){
  		this.settings = $.extend(true, {}, $.rebox.defaults, options);
  		this.$el = $this;      // parent container holding items
  		this.$box = null;      // the lightbox modal
  		this.$items = null;    // recomputed each time its opened
  		this.idx = 0;          // of the $items which index are we on
  		this.enable();
  	};
  
  	$.rebox.defaults = {
  		theme: 'rebox',        // class name parent gets (for your css)
  		selector: null,        // the selector to delegate to, should be to the <a> which contains an <img>
  		prev: '&larr;',        // use an image, text, whatever for the previous button
  		next: '&rarr;',        // use an image, text, whatever for the next button
  		loading: '%',          // use an image, text, whatever for the loading notification
  		close: '&times;',      // use an image, text, whatever for the close button
  		speed: 400,            // speed to fade in or out
  		zIndex: 1000,          // zIndex to apply to the outer container
  		cycle: true,           // whether to cycle through galleries or stop at ends
  		captionAttr: 'title',  // name of the attribute to grab the caption from
  		template: 'image',     // the default template to be used (see templates below)
  		templates: {           // define templates to create the elements you need function($item, settings)
  			image: function($item, settings, callback){
  				return $('<img src="'+ $item.attr('href') +'" class="'+ settings.theme +'-content" />').load(callback);
  			}
  		}
  	};
  
  	$.rebox.setDefaults = function(options){
  		$.rebox.defaults = $.extend(true, {}, $.rebox.defaults, options);
  	};
  
  	$.rebox.lookup = { i: 0 };
  
  	$.extend($.rebox.prototype, {
  		enable: function(){
  				var t = this;
  
  				return t.$el.on('click.rebox', t.settings.selector, function(e){
  					e.preventDefault();
  					t.open(this);
  				});
  			},
  		open: function(i){
  				var t = this;
  
  				// figure out where to start
  				t.$items = t.settings.selector === null? t.$el : t.$el.find(t.settings.selector);
  				if(isNaN(i)){
  					i = t.$items.index(i);
  				}
  
  				// build the rebox
  				t.$box = $('<div class="'+ t.settings.theme +'" style="display:none;">'+
  							'<a href="#" class="'+ t.settings.theme +'-close '+ t.settings.theme +'-button">'+ t.settings.close +'</a>' +
  							'<a href="#" class="'+ t.settings.theme +'-prev '+ t.settings.theme +'-button">'+ t.settings.prev +'</a>' +
  							'<a href="#" class="'+ t.settings.theme +'-next '+ t.settings.theme +'-button">'+ t.settings.next +'</a>' +
  							'<div class="'+ t.settings.theme +'-contents"></div>'+
  							'<div class="'+ t.settings.theme +'-caption"><p></p></div>' +
  						'</div>').appendTo('body').css('zIndex',t.settings.zIndex).fadeIn(t.settings.speed)
  						.on('click.rebox','.'+t.settings.theme +'-close', function(e){ e.preventDefault(); t.close(); })
  						.on('click.rebox','.'+t.settings.theme +'-next', function(e){ e.preventDefault(); t.next(); })
  						.on('click.rebox','.'+t.settings.theme +'-prev', function(e){ e.preventDefault(); t.prev(); });
  
  				// add some key hooks
  				$(document).on('swipeLeft.rebox', function(e){ t.next(); })
  					.on('swipeRight.rebox', function(e){ t.prev(); })
  					.on('keydown.rebox', function(e){
  							e.preventDefault();
  							var key = (window.event) ? event.keyCode : e.keyCode;
  							switch(key){
  								case 27: t.close(); break; // escape key closes
  								case 37: t.prev(); break;  // left arrow to prev
  								case 39: t.next(); break;  // right arrow to next
  							}
  						});
  
  				t.$el.trigger('rebox:open',[t]);
  				t.goto(i);
  				return t.$el;
  			},
  		close: function(){
  				var t = this;
  
  				if(t.$box && t.$box.length){
  					t.$box.fadeOut(t.settings.speed, function(e){
  						t.$box.remove();
  						t.$box = null;
  						t.$el.trigger('rebox:close',[t]);
  					});
  				}
  				$(document).off('.rebox');
  
  				return t.$el;
  			},
  		goto: function(i){
  				var t = this,
  					$item = $(t.$items[i]),
  					captionVal = $item.attr(t.settings.captionAttr),
  					$cap = t.$box.children('.'+ t.settings.theme +'-caption')[captionVal?'show':'hide']().children('p').text(captionVal),
  					$bi = t.$box.children('.'+ t.settings.theme +'-contents'),
  					$img = null;
  
  				if($item.length){
  					t.idx = i;
  					$bi.html('<div class="'+ t.settings.theme +'-loading '+ t.settings.theme +'-button">'+ t.settings.loading +'</div>');
  
  					$img = t.settings.templates[$item.data('rebox-template') || t.settings.template]($item, t.settings, function(content){
  						$bi.empty().append($(this));
  					});
  
  					if(t.$items.length == 1 || !t.settings.cycle){
  						t.$box.children('.'+ t.settings.theme +'-prev')[i<=0 ? 'hide' : 'show']();
  						t.$box.children('.'+ t.settings.theme +'-next')[i>=t.$items.length-1 ? 'hide' : 'show']();
  					}
  					t.$el.trigger('rebox:goto',[t, i, $item, $img]);
  				}
  				return t.$el;
  			},
  		prev: function(){
  				var t = this;
  				return t.goto(t.idx===0? t.$items.length-1 : t.idx-1);
  			},
  		next: function(){
  				var t = this;
  				return t.goto(t.idx===t.$items.length-1? 0 : t.idx+1);
  			},
  		disable: function(){
  				var t = this;
  				return t.close().off('.rebox').trigger('rebox:disable',[t]);
  			},
  		destroy: function(){
  				var t = this;
  				return t.disable().removeData('rebox').trigger('rebox:destroy');
  			},
  		option: function(key, val){
  				var t = this;
  				if(val !== undefined){
  					t.settings[key] = val;
  					return t.disable().enable();
  				}
  				return t.settings[key];
  			}
  	});
  
  	$.fn.rebox = function(o) {
  		o = o || {};
  		var tmp_args = Array.prototype.slice.call(arguments);
  
  		if (typeof(o) == 'string'){
  			if(o == 'option' && typeof(tmp_args[1]) == 'string' && tmp_args.length === 2){
  				var inst = $.rebox.lookup[$(this).data('rebox')];
  				return inst[o].apply(inst, tmp_args.slice(1));
  			}
  			else return this.each(function() {
  				var inst = $.rebox.lookup[$(this).data('rebox')];
  				inst[o].apply(inst, tmp_args.slice(1));
  			});
  		} else return this.each(function() {
  				var $t = $(this);
  				$.rebox.lookup[++$.rebox.lookup.i] = new $.rebox($t, o);
  				$t.data('rebox', $.rebox.lookup.i);
  			});
  	};
  
  
  })(window.jQuery || window.Zepto || window.$);
  
  /* jquery.nicescroll 3.6.8 InuYaksa*2015 MIT http://nicescroll.areaaperta.com */(function(f){"function"===typeof define&&define.amd?define(["jquery"],f):"object"===typeof exports?module.exports=f(require("jquery")):f(jQuery)})(function(f){var B=!1,F=!1,O=0,P=2E3,A=0,J=["webkit","ms","moz","o"],v=window.requestAnimationFrame||!1,w=window.cancelAnimationFrame||!1;if(!v)for(var Q in J){var G=J[Q];if(v=window[G+"RequestAnimationFrame"]){w=window[G+"CancelAnimationFrame"]||window[G+"CancelRequestAnimationFrame"];break}}var x=window.MutationObserver||window.WebKitMutationObserver||
  !1,K={zindex:"auto",cursoropacitymin:0,cursoropacitymax:1,cursorcolor:"#424242",cursorwidth:"6px",cursorborder:"1px solid #fff",cursorborderradius:"5px",scrollspeed:60,mousescrollstep:24,touchbehavior:!1,hwacceleration:!0,usetransition:!0,boxzoom:!1,dblclickzoom:!0,gesturezoom:!0,grabcursorenabled:!0,autohidemode:!0,background:"",iframeautoresize:!0,cursorminheight:32,preservenativescrolling:!0,railoffset:!1,railhoffset:!1,bouncescroll:!0,spacebarenabled:!0,railpadding:{top:0,right:0,left:0,bottom:0},
  disableoutline:!0,horizrailenabled:!0,railalign:"right",railvalign:"bottom",enabletranslate3d:!0,enablemousewheel:!0,enablekeyboard:!0,smoothscroll:!0,sensitiverail:!0,enablemouselockapi:!0,cursorfixedheight:!1,directionlockdeadzone:6,hidecursordelay:400,nativeparentscrolling:!0,enablescrollonselection:!0,overflowx:!0,overflowy:!0,cursordragspeed:.3,rtlmode:"auto",cursordragontouch:!1,oneaxismousemode:"auto",scriptpath:function(){var f=document.getElementsByTagName("script"),f=f.length?f[f.length-
  1].src.split("?")[0]:"";return 0<f.split("/").length?f.split("/").slice(0,-1).join("/")+"/":""}(),preventmultitouchscrolling:!0,disablemutationobserver:!1},H=!1,R=function(){if(H)return H;var f=document.createElement("DIV"),c=f.style,k=navigator.userAgent,l=navigator.platform,d={haspointerlock:"pointerLockElement"in document||"webkitPointerLockElement"in document||"mozPointerLockElement"in document};d.isopera="opera"in window;d.isopera12=d.isopera&&"getUserMedia"in navigator;d.isoperamini="[object OperaMini]"===
  Object.prototype.toString.call(window.operamini);d.isie="all"in document&&"attachEvent"in f&&!d.isopera;d.isieold=d.isie&&!("msInterpolationMode"in c);d.isie7=d.isie&&!d.isieold&&(!("documentMode"in document)||7==document.documentMode);d.isie8=d.isie&&"documentMode"in document&&8==document.documentMode;d.isie9=d.isie&&"performance"in window&&9==document.documentMode;d.isie10=d.isie&&"performance"in window&&10==document.documentMode;d.isie11="msRequestFullscreen"in f&&11<=document.documentMode;d.isieedge12=
  navigator.userAgent.match(/Edge\/12\./);d.isieedge="msOverflowStyle"in f;d.ismodernie=d.isie11||d.isieedge;d.isie9mobile=/iemobile.9/i.test(k);d.isie9mobile&&(d.isie9=!1);d.isie7mobile=!d.isie9mobile&&d.isie7&&/iemobile/i.test(k);d.ismozilla="MozAppearance"in c;d.iswebkit="WebkitAppearance"in c;d.ischrome="chrome"in window;d.ischrome38=d.ischrome&&"touchAction"in c;d.ischrome22=!d.ischrome38&&d.ischrome&&d.haspointerlock;d.ischrome26=!d.ischrome38&&d.ischrome&&"transition"in c;d.cantouch="ontouchstart"in
  document.documentElement||"ontouchstart"in window;d.hasw3ctouch=(window.PointerEvent||!1)&&(0<navigator.MaxTouchPoints||0<navigator.msMaxTouchPoints);d.hasmstouch=!d.hasw3ctouch&&(window.MSPointerEvent||!1);d.ismac=/^mac$/i.test(l);d.isios=d.cantouch&&/iphone|ipad|ipod/i.test(l);d.isios4=d.isios&&!("seal"in Object);d.isios7=d.isios&&"webkitHidden"in document;d.isios8=d.isios&&"hidden"in document;d.isandroid=/android/i.test(k);d.haseventlistener="addEventListener"in f;d.trstyle=!1;d.hastransform=!1;
  d.hastranslate3d=!1;d.transitionstyle=!1;d.hastransition=!1;d.transitionend=!1;l=["transform","msTransform","webkitTransform","MozTransform","OTransform"];for(k=0;k<l.length;k++)if(void 0!==c[l[k]]){d.trstyle=l[k];break}d.hastransform=!!d.trstyle;d.hastransform&&(c[d.trstyle]="translate3d(1px,2px,3px)",d.hastranslate3d=/translate3d/.test(c[d.trstyle]));d.transitionstyle=!1;d.prefixstyle="";d.transitionend=!1;for(var l="transition webkitTransition msTransition MozTransition OTransition OTransition KhtmlTransition".split(" "),
  q=" -webkit- -ms- -moz- -o- -o -khtml-".split(" "),t="transitionend webkitTransitionEnd msTransitionEnd transitionend otransitionend oTransitionEnd KhtmlTransitionEnd".split(" "),k=0;k<l.length;k++)if(l[k]in c){d.transitionstyle=l[k];d.prefixstyle=q[k];d.transitionend=t[k];break}d.ischrome26&&(d.prefixstyle=q[1]);d.hastransition=d.transitionstyle;a:{k=["grab","-webkit-grab","-moz-grab"];if(d.ischrome&&!d.ischrome38||d.isie)k=[];for(l=0;l<k.length;l++)if(q=k[l],c.cursor=q,c.cursor==q){c=q;break a}c=
  "default"}d.cursorgrabvalue=c;d.hasmousecapture="setCapture"in f;d.hasMutationObserver=!1!==x;return H=d},S=function(h,c){function k(){var b=a.doc.css(e.trstyle);return b&&"matrix"==b.substr(0,6)?b.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/):!1}function l(){var b=a.win;if("zIndex"in b)return b.zIndex();for(;0<b.length&&9!=b[0].nodeType;){var g=b.css("zIndex");if(!isNaN(g)&&0!=g)return parseInt(g);b=b.parent()}return!1}function d(b,
  g,u){g=b.css(g);b=parseFloat(g);return isNaN(b)?(b=z[g]||0,u=3==b?u?a.win.outerHeight()-a.win.innerHeight():a.win.outerWidth()-a.win.innerWidth():1,a.isie8&&b&&(b+=1),u?b:0):b}function q(b,g,u,c){a._bind(b,g,function(a){a=a?a:window.event;var c={original:a,target:a.target||a.srcElement,type:"wheel",deltaMode:"MozMousePixelScroll"==a.type?0:1,deltaX:0,deltaZ:0,preventDefault:function(){a.preventDefault?a.preventDefault():a.returnValue=!1;return!1},stopImmediatePropagation:function(){a.stopImmediatePropagation?
  a.stopImmediatePropagation():a.cancelBubble=!0}};"mousewheel"==g?(a.wheelDeltaX&&(c.deltaX=-.025*a.wheelDeltaX),a.wheelDeltaY&&(c.deltaY=-.025*a.wheelDeltaY),c.deltaY||c.deltaX||(c.deltaY=-.025*a.wheelDelta)):c.deltaY=a.detail;return u.call(b,c)},c)}function t(b,g,c){var d,e;0==b.deltaMode?(d=-Math.floor(a.opt.mousescrollstep/54*b.deltaX),e=-Math.floor(a.opt.mousescrollstep/54*b.deltaY)):1==b.deltaMode&&(d=-Math.floor(b.deltaX*a.opt.mousescrollstep),e=-Math.floor(b.deltaY*a.opt.mousescrollstep));
  g&&a.opt.oneaxismousemode&&0==d&&e&&(d=e,e=0,c&&(0>d?a.getScrollLeft()>=a.page.maxw:0>=a.getScrollLeft())&&(e=d,d=0));a.isrtlmode&&(d=-d);d&&(a.scrollmom&&a.scrollmom.stop(),a.lastdeltax+=d,a.debounced("mousewheelx",function(){var b=a.lastdeltax;a.lastdeltax=0;a.rail.drag||a.doScrollLeftBy(b)},15));if(e){if(a.opt.nativeparentscrolling&&c&&!a.ispage&&!a.zoomactive)if(0>e){if(a.getScrollTop()>=a.page.maxh)return!0}else if(0>=a.getScrollTop())return!0;a.scrollmom&&a.scrollmom.stop();a.lastdeltay+=e;
  a.synched("mousewheely",function(){var b=a.lastdeltay;a.lastdeltay=0;a.rail.drag||a.doScrollBy(b)},15)}b.stopImmediatePropagation();return b.preventDefault()}var a=this;this.version="3.6.8";this.name="nicescroll";this.me=c;this.opt={doc:f("body"),win:!1};f.extend(this.opt,K);this.opt.snapbackspeed=80;if(h)for(var r in a.opt)void 0!==h[r]&&(a.opt[r]=h[r]);a.opt.disablemutationobserver&&(x=!1);this.iddoc=(this.doc=a.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/^BODY|HTML/.test(a.opt.win?
  a.opt.win[0].nodeName:this.doc[0].nodeName);this.haswrapper=!1!==a.opt.win;this.win=a.opt.win||(this.ispage?f(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?f(window):this.win;this.body=f("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=a.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=
  this.onscrollcancel=this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;if("auto"==this.opt.rtlmode){r=this.win[0]==window?this.body:this.win;var p=r.css("writing-mode")||r.css("-webkit-writing-mode")||r.css("-ms-writing-mode")||r.css("-moz-writing-mode");"horizontal-tb"==p||"lr-tb"==p||""==p?(this.isrtlmode=
  "rtl"==r.css("direction"),this.isvertical=!1):(this.isrtlmode="vertical-rl"==p||"tb"==p||"tb-rl"==p||"rl-tb"==p,this.isvertical="vertical-rl"==p||"tb"==p||"tb-rl"==p)}else this.isrtlmode=!0===this.opt.rtlmode,this.isvertical=!1;this.observerbody=this.observerremover=this.observer=this.scrollmom=this.scrollrunning=!1;do this.id="ascrail"+P++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor=this.rail=
  !1;this.visibility=!0;this.hidden=this.locked=this.railslocked=!1;this.cursoractive=!0;this.wheelprevented=!1;this.overflowx=a.opt.overflowx;this.overflowy=a.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=R();var e=f.extend({},this.detected);this.ishwscroll=(this.canhwscroll=e.hastransform&&a.opt.hwacceleration)&&a.haswrapper;this.hasreversehr=this.isrtlmode?this.isvertical?
  !(e.iswebkit||e.isie||e.isie11):!(e.iswebkit||e.isie&&!e.isie10&&!e.isie11):!1;this.istouchcapable=!1;e.cantouch||!e.hasw3ctouch&&!e.hasmstouch?!e.cantouch||e.isios||e.isandroid||!e.iswebkit&&!e.ismozilla||(this.istouchcapable=!0):this.istouchcapable=!0;a.opt.enablemouselockapi||(e.hasmousecapture=!1,e.haspointerlock=!1);this.debounced=function(b,g,c){a&&(a.delaylist[b]||(g.call(a),a.delaylist[b]={h:v(function(){a.delaylist[b].fn.call(a);a.delaylist[b]=!1},c)}),a.delaylist[b].fn=g)};var I=!1;this.synched=
  function(b,g){a.synclist[b]=g;(function(){I||(v(function(){if(a){I=!1;for(var b in a.synclist){var g=a.synclist[b];g&&g.call(a);a.synclist[b]=!1}}}),I=!0)})();return b};this.unsynched=function(b){a.synclist[b]&&(a.synclist[b]=!1)};this.css=function(b,g){for(var c in g)a.saved.css.push([b,c,b.css(c)]),b.css(c,g[c])};this.scrollTop=function(b){return void 0===b?a.getScrollTop():a.setScrollTop(b)};this.scrollLeft=function(b){return void 0===b?a.getScrollLeft():a.setScrollLeft(b)};var D=function(a,g,
  c,d,e,f,k){this.st=a;this.ed=g;this.spd=c;this.p1=d||0;this.p2=e||1;this.p3=f||0;this.p4=k||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};D.prototype={B2:function(a){return 3*a*a*(1-a)},B3:function(a){return 3*a*(1-a)*(1-a)},B4:function(a){return(1-a)*(1-a)*(1-a)},getNow:function(){var a=1-((new Date).getTime()-this.ts)/this.spd,g=this.B2(a)+this.B3(a)+this.B4(a);return 0>a?this.ed:this.st+Math.round(this.df*g)},update:function(a,g){this.st=this.getNow();this.ed=a;this.spd=g;this.ts=(new Date).getTime();
  this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};e.hastranslate3d&&e.isios&&this.doc.css("-webkit-backface-visibility","hidden");this.getScrollTop=function(b){if(!b){if(b=k())return 16==b.length?-b[13]:-b[5];if(a.timerscroll&&a.timerscroll.bz)return a.timerscroll.bz.getNow()}return a.doc.translate.y};this.getScrollLeft=function(b){if(!b){if(b=k())return 16==b.length?-b[12]:-b[4];if(a.timerscroll&&a.timerscroll.bh)return a.timerscroll.bh.getNow()}return a.doc.translate.x};
  this.notifyScrollEvent=function(a){var g=document.createEvent("UIEvents");g.initUIEvent("scroll",!1,!0,window,1);g.niceevent=!0;a.dispatchEvent(g)};var y=this.isrtlmode?1:-1;e.hastranslate3d&&a.opt.enabletranslate3d?(this.setScrollTop=function(b,g){a.doc.translate.y=b;a.doc.translate.ty=-1*b+"px";a.doc.css(e.trstyle,"translate3d("+a.doc.translate.tx+","+a.doc.translate.ty+",0px)");g||a.notifyScrollEvent(a.win[0])},this.setScrollLeft=function(b,g){a.doc.translate.x=b;a.doc.translate.tx=b*y+"px";a.doc.css(e.trstyle,
  "translate3d("+a.doc.translate.tx+","+a.doc.translate.ty+",0px)");g||a.notifyScrollEvent(a.win[0])}):(this.setScrollTop=function(b,g){a.doc.translate.y=b;a.doc.translate.ty=-1*b+"px";a.doc.css(e.trstyle,"translate("+a.doc.translate.tx+","+a.doc.translate.ty+")");g||a.notifyScrollEvent(a.win[0])},this.setScrollLeft=function(b,g){a.doc.translate.x=b;a.doc.translate.tx=b*y+"px";a.doc.css(e.trstyle,"translate("+a.doc.translate.tx+","+a.doc.translate.ty+")");g||a.notifyScrollEvent(a.win[0])})}else this.getScrollTop=
  function(){return a.docscroll.scrollTop()},this.setScrollTop=function(b){return setTimeout(function(){a&&a.docscroll.scrollTop(b)},1)},this.getScrollLeft=function(){return a.hasreversehr?a.detected.ismozilla?a.page.maxw-Math.abs(a.docscroll.scrollLeft()):a.page.maxw-a.docscroll.scrollLeft():a.docscroll.scrollLeft()},this.setScrollLeft=function(b){return setTimeout(function(){if(a)return a.hasreversehr&&(b=a.detected.ismozilla?-(a.page.maxw-b):a.page.maxw-b),a.docscroll.scrollLeft(b)},1)};this.getTarget=
  function(a){return a?a.target?a.target:a.srcElement?a.srcElement:!1:!1};this.hasParent=function(a,g){if(!a)return!1;for(var c=a.target||a.srcElement||a||!1;c&&c.id!=g;)c=c.parentNode||!1;return!1!==c};var z={thin:1,medium:3,thick:5};this.getDocumentScrollOffset=function(){return{top:window.pageYOffset||document.documentElement.scrollTop,left:window.pageXOffset||document.documentElement.scrollLeft}};this.getOffset=function(){if(a.isfixed){var b=a.win.offset(),g=a.getDocumentScrollOffset();b.top-=g.top;
  b.left-=g.left;return b}b=a.win.offset();if(!a.viewport)return b;g=a.viewport.offset();return{top:b.top-g.top,left:b.left-g.left}};this.updateScrollBar=function(b){var g,c,e;if(a.ishwscroll)a.rail.css({height:a.win.innerHeight()-(a.opt.railpadding.top+a.opt.railpadding.bottom)}),a.railh&&a.railh.css({width:a.win.innerWidth()-(a.opt.railpadding.left+a.opt.railpadding.right)});else{var f=a.getOffset();g=f.top;c=f.left-(a.opt.railpadding.left+a.opt.railpadding.right);g+=d(a.win,"border-top-width",!0);
  c+=a.rail.align?a.win.outerWidth()-d(a.win,"border-right-width")-a.rail.width:d(a.win,"border-left-width");if(e=a.opt.railoffset)e.top&&(g+=e.top),e.left&&(c+=e.left);a.railslocked||a.rail.css({top:g,left:c,height:(b?b.h:a.win.innerHeight())-(a.opt.railpadding.top+a.opt.railpadding.bottom)});a.zoom&&a.zoom.css({top:g+1,left:1==a.rail.align?c-20:c+a.rail.width+4});if(a.railh&&!a.railslocked){g=f.top;c=f.left;if(e=a.opt.railhoffset)e.top&&(g+=e.top),e.left&&(c+=e.left);b=a.railh.align?g+d(a.win,"border-top-width",
  !0)+a.win.innerHeight()-a.railh.height:g+d(a.win,"border-top-width",!0);c+=d(a.win,"border-left-width");a.railh.css({top:b-(a.opt.railpadding.top+a.opt.railpadding.bottom),left:c,width:a.railh.width})}}};this.doRailClick=function(b,g,c){var d;a.railslocked||(a.cancelEvent(b),g?(g=c?a.doScrollLeft:a.doScrollTop,d=c?(b.pageX-a.railh.offset().left-a.cursorwidth/2)*a.scrollratio.x:(b.pageY-a.rail.offset().top-a.cursorheight/2)*a.scrollratio.y,g(d)):(g=c?a.doScrollLeftBy:a.doScrollBy,d=c?a.scroll.x:a.scroll.y,
  b=c?b.pageX-a.railh.offset().left:b.pageY-a.rail.offset().top,c=c?a.view.w:a.view.h,g(d>=b?c:-c)))};a.hasanimationframe=v;a.hascancelanimationframe=w;a.hasanimationframe?a.hascancelanimationframe||(w=function(){a.cancelAnimationFrame=!0}):(v=function(a){return setTimeout(a,15-Math.floor(+new Date/1E3)%16)},w=clearTimeout);this.init=function(){a.saved.css=[];if(e.isie7mobile||e.isoperamini)return!0;e.hasmstouch&&a.css(a.ispage?f("html"):a.win,{_touchaction:"none"});var b=e.ismodernie||e.isie10?{"-ms-overflow-style":"none"}:
  {"overflow-y":"hidden"};a.zindex="auto";a.zindex=a.ispage||"auto"!=a.opt.zindex?a.opt.zindex:l()||"auto";!a.ispage&&"auto"!=a.zindex&&a.zindex>A&&(A=a.zindex);a.isie&&0==a.zindex&&"auto"==a.opt.zindex&&(a.zindex="auto");if(!a.ispage||!e.cantouch&&!e.isieold&&!e.isie9mobile){var c=a.docscroll;a.ispage&&(c=a.haswrapper?a.win:a.doc);e.isie9mobile||a.css(c,b);a.ispage&&e.isie7&&("BODY"==a.doc[0].nodeName?a.css(f("html"),{"overflow-y":"hidden"}):"HTML"==a.doc[0].nodeName&&a.css(f("body"),b));!e.isios||
  a.ispage||a.haswrapper||a.css(f("body"),{"-webkit-overflow-scrolling":"touch"});var d=f(document.createElement("div"));d.css({position:"relative",top:0,"float":"right",width:a.opt.cursorwidth,height:0,"background-color":a.opt.cursorcolor,border:a.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":a.opt.cursorborderradius,"-moz-border-radius":a.opt.cursorborderradius,"border-radius":a.opt.cursorborderradius});d.hborder=parseFloat(d.outerHeight()-d.innerHeight());d.addClass("nicescroll-cursors");
  a.cursor=d;var m=f(document.createElement("div"));m.attr("id",a.id);m.addClass("nicescroll-rails nicescroll-rails-vr");var k,h,p=["left","right","top","bottom"],L;for(L in p)h=p[L],(k=a.opt.railpadding[h])?m.css("padding-"+h,k+"px"):a.opt.railpadding[h]=0;m.append(d);m.width=Math.max(parseFloat(a.opt.cursorwidth),d.outerWidth());m.css({width:m.width+"px",zIndex:a.zindex,background:a.opt.background,cursor:"default"});m.visibility=!0;m.scrollable=!0;m.align="left"==a.opt.railalign?0:1;a.rail=m;d=a.rail.drag=
  !1;!a.opt.boxzoom||a.ispage||e.isieold||(d=document.createElement("div"),a.bind(d,"click",a.doZoom),a.bind(d,"mouseenter",function(){a.zoom.css("opacity",a.opt.cursoropacitymax)}),a.bind(d,"mouseleave",function(){a.zoom.css("opacity",a.opt.cursoropacitymin)}),a.zoom=f(d),a.zoom.css({cursor:"pointer",zIndex:a.zindex,backgroundImage:"url("+a.opt.scriptpath+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),a.opt.dblclickzoom&&a.bind(a.win,"dblclick",a.doZoom),e.cantouch&&a.opt.gesturezoom&&
  (a.ongesturezoom=function(b){1.5<b.scale&&a.doZoomIn(b);.8>b.scale&&a.doZoomOut(b);return a.cancelEvent(b)},a.bind(a.win,"gestureend",a.ongesturezoom)));a.railh=!1;var n;a.opt.horizrailenabled&&(a.css(c,{overflowX:"hidden"}),d=f(document.createElement("div")),d.css({position:"absolute",top:0,height:a.opt.cursorwidth,width:0,backgroundColor:a.opt.cursorcolor,border:a.opt.cursorborder,backgroundClip:"padding-box","-webkit-border-radius":a.opt.cursorborderradius,"-moz-border-radius":a.opt.cursorborderradius,
  "border-radius":a.opt.cursorborderradius}),e.isieold&&d.css("overflow","hidden"),d.wborder=parseFloat(d.outerWidth()-d.innerWidth()),d.addClass("nicescroll-cursors"),a.cursorh=d,n=f(document.createElement("div")),n.attr("id",a.id+"-hr"),n.addClass("nicescroll-rails nicescroll-rails-hr"),n.height=Math.max(parseFloat(a.opt.cursorwidth),d.outerHeight()),n.css({height:n.height+"px",zIndex:a.zindex,background:a.opt.background}),n.append(d),n.visibility=!0,n.scrollable=!0,n.align="top"==a.opt.railvalign?
  0:1,a.railh=n,a.railh.drag=!1);a.ispage?(m.css({position:"fixed",top:0,height:"100%"}),m.align?m.css({right:0}):m.css({left:0}),a.body.append(m),a.railh&&(n.css({position:"fixed",left:0,width:"100%"}),n.align?n.css({bottom:0}):n.css({top:0}),a.body.append(n))):(a.ishwscroll?("static"==a.win.css("position")&&a.css(a.win,{position:"relative"}),c="HTML"==a.win[0].nodeName?a.body:a.win,f(c).scrollTop(0).scrollLeft(0),a.zoom&&(a.zoom.css({position:"absolute",top:1,right:0,"margin-right":m.width+4}),c.append(a.zoom)),
  m.css({position:"absolute",top:0}),m.align?m.css({right:0}):m.css({left:0}),c.append(m),n&&(n.css({position:"absolute",left:0,bottom:0}),n.align?n.css({bottom:0}):n.css({top:0}),c.append(n))):(a.isfixed="fixed"==a.win.css("position"),c=a.isfixed?"fixed":"absolute",a.isfixed||(a.viewport=a.getViewport(a.win[0])),a.viewport&&(a.body=a.viewport,0==/fixed|absolute/.test(a.viewport.css("position"))&&a.css(a.viewport,{position:"relative"})),m.css({position:c}),a.zoom&&a.zoom.css({position:c}),a.updateScrollBar(),
  a.body.append(m),a.zoom&&a.body.append(a.zoom),a.railh&&(n.css({position:c}),a.body.append(n))),e.isios&&a.css(a.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),e.isie&&a.opt.disableoutline&&a.win.attr("hideFocus","true"),e.iswebkit&&a.opt.disableoutline&&a.win.css("outline","none"));!1===a.opt.autohidemode?(a.autohidedom=!1,a.rail.css({opacity:a.opt.cursoropacitymax}),a.railh&&a.railh.css({opacity:a.opt.cursoropacitymax})):!0===a.opt.autohidemode||"leave"===a.opt.autohidemode?
  (a.autohidedom=f().add(a.rail),e.isie8&&(a.autohidedom=a.autohidedom.add(a.cursor)),a.railh&&(a.autohidedom=a.autohidedom.add(a.railh)),a.railh&&e.isie8&&(a.autohidedom=a.autohidedom.add(a.cursorh))):"scroll"==a.opt.autohidemode?(a.autohidedom=f().add(a.rail),a.railh&&(a.autohidedom=a.autohidedom.add(a.railh))):"cursor"==a.opt.autohidemode?(a.autohidedom=f().add(a.cursor),a.railh&&(a.autohidedom=a.autohidedom.add(a.cursorh))):"hidden"==a.opt.autohidemode&&(a.autohidedom=!1,a.hide(),a.railslocked=
  !1);if(e.isie9mobile)a.scrollmom=new M(a),a.onmangotouch=function(){var b=a.getScrollTop(),c=a.getScrollLeft();if(b==a.scrollmom.lastscrolly&&c==a.scrollmom.lastscrollx)return!0;var g=b-a.mangotouch.sy,d=c-a.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(d,2)+Math.pow(g,2)))){var e=0>g?-1:1,f=0>d?-1:1,u=+new Date;a.mangotouch.lazy&&clearTimeout(a.mangotouch.lazy);80<u-a.mangotouch.tm||a.mangotouch.dry!=e||a.mangotouch.drx!=f?(a.scrollmom.stop(),a.scrollmom.reset(c,b),a.mangotouch.sy=b,a.mangotouch.ly=
  b,a.mangotouch.sx=c,a.mangotouch.lx=c,a.mangotouch.dry=e,a.mangotouch.drx=f,a.mangotouch.tm=u):(a.scrollmom.stop(),a.scrollmom.update(a.mangotouch.sx-d,a.mangotouch.sy-g),a.mangotouch.tm=u,g=Math.max(Math.abs(a.mangotouch.ly-b),Math.abs(a.mangotouch.lx-c)),a.mangotouch.ly=b,a.mangotouch.lx=c,2<g&&(a.mangotouch.lazy=setTimeout(function(){a.mangotouch.lazy=!1;a.mangotouch.dry=0;a.mangotouch.drx=0;a.mangotouch.tm=0;a.scrollmom.doMomentum(30)},100)))}},m=a.getScrollTop(),n=a.getScrollLeft(),a.mangotouch=
  {sy:m,ly:m,dry:0,sx:n,lx:n,drx:0,lazy:!1,tm:0},a.bind(a.docscroll,"scroll",a.onmangotouch);else{if(e.cantouch||a.istouchcapable||a.opt.touchbehavior||e.hasmstouch){a.scrollmom=new M(a);a.ontouchstart=function(b){if(b.pointerType&&2!=b.pointerType&&"touch"!=b.pointerType)return!1;a.hasmoving=!1;if(!a.railslocked){var c;if(e.hasmstouch)for(c=b.target?b.target:!1;c;){var g=f(c).getNiceScroll();if(0<g.length&&g[0].me==a.me)break;if(0<g.length)return!1;if("DIV"==c.nodeName&&c.id==a.id)break;c=c.parentNode?
  c.parentNode:!1}a.cancelScroll();if((c=a.getTarget(b))&&/INPUT/i.test(c.nodeName)&&/range/i.test(c.type))return a.stopPropagation(b);!("clientX"in b)&&"changedTouches"in b&&(b.clientX=b.changedTouches[0].clientX,b.clientY=b.changedTouches[0].clientY);a.forcescreen&&(g=b,b={original:b.original?b.original:b},b.clientX=g.screenX,b.clientY=g.screenY);a.rail.drag={x:b.clientX,y:b.clientY,sx:a.scroll.x,sy:a.scroll.y,st:a.getScrollTop(),sl:a.getScrollLeft(),pt:2,dl:!1};if(a.ispage||!a.opt.directionlockdeadzone)a.rail.drag.dl=
  "f";else{var g=f(window).width(),d=f(window).height(),d=Math.max(0,Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)-d),g=Math.max(0,Math.max(document.body.scrollWidth,document.documentElement.scrollWidth)-g);a.rail.drag.ck=!a.rail.scrollable&&a.railh.scrollable?0<d?"v":!1:a.rail.scrollable&&!a.railh.scrollable?0<g?"h":!1:!1;a.rail.drag.ck||(a.rail.drag.dl="f")}a.opt.touchbehavior&&a.isiframe&&e.isie&&(g=a.win.position(),a.rail.drag.x+=g.left,a.rail.drag.y+=g.top);a.hasmoving=
  !1;a.lastmouseup=!1;a.scrollmom.reset(b.clientX,b.clientY);if(!e.cantouch&&!this.istouchcapable&&!b.pointerType){if(!c||!/INPUT|SELECT|TEXTAREA/i.test(c.nodeName))return!a.ispage&&e.hasmousecapture&&c.setCapture(),a.opt.touchbehavior?(c.onclick&&!c._onclick&&(c._onclick=c.onclick,c.onclick=function(b){if(a.hasmoving)return!1;c._onclick.call(this,b)}),a.cancelEvent(b)):a.stopPropagation(b);/SUBMIT|CANCEL|BUTTON/i.test(f(c).attr("type"))&&(pc={tg:c,click:!1},a.preventclick=pc)}}};a.ontouchend=function(b){if(!a.rail.drag)return!0;
  if(2==a.rail.drag.pt){if(b.pointerType&&2!=b.pointerType&&"touch"!=b.pointerType)return!1;a.scrollmom.doMomentum();a.rail.drag=!1;if(a.hasmoving&&(a.lastmouseup=!0,a.hideCursor(),e.hasmousecapture&&document.releaseCapture(),!e.cantouch))return a.cancelEvent(b)}else if(1==a.rail.drag.pt)return a.onmouseup(b)};var q=a.opt.touchbehavior&&a.isiframe&&!e.hasmousecapture;a.ontouchmove=function(b,c){if(!a.rail.drag||b.targetTouches&&a.opt.preventmultitouchscrolling&&1<b.targetTouches.length||b.pointerType&&
  2!=b.pointerType&&"touch"!=b.pointerType)return!1;if(2==a.rail.drag.pt){if(e.cantouch&&e.isios&&void 0===b.original)return!0;a.hasmoving=!0;a.preventclick&&!a.preventclick.click&&(a.preventclick.click=a.preventclick.tg.onclick||!1,a.preventclick.tg.onclick=a.onpreventclick);b=f.extend({original:b},b);"changedTouches"in b&&(b.clientX=b.changedTouches[0].clientX,b.clientY=b.changedTouches[0].clientY);if(a.forcescreen){var g=b;b={original:b.original?b.original:b};b.clientX=g.screenX;b.clientY=g.screenY}var d,
  g=d=0;q&&!c&&(d=a.win.position(),g=-d.left,d=-d.top);var u=b.clientY+d;d=u-a.rail.drag.y;var m=b.clientX+g,k=m-a.rail.drag.x,h=a.rail.drag.st-d;a.ishwscroll&&a.opt.bouncescroll?0>h?h=Math.round(h/2):h>a.page.maxh&&(h=a.page.maxh+Math.round((h-a.page.maxh)/2)):(0>h&&(u=h=0),h>a.page.maxh&&(h=a.page.maxh,u=0));var l;a.railh&&a.railh.scrollable&&(l=a.isrtlmode?k-a.rail.drag.sl:a.rail.drag.sl-k,a.ishwscroll&&a.opt.bouncescroll?0>l?l=Math.round(l/2):l>a.page.maxw&&(l=a.page.maxw+Math.round((l-a.page.maxw)/
  2)):(0>l&&(m=l=0),l>a.page.maxw&&(l=a.page.maxw,m=0)));g=!1;if(a.rail.drag.dl)g=!0,"v"==a.rail.drag.dl?l=a.rail.drag.sl:"h"==a.rail.drag.dl&&(h=a.rail.drag.st);else{d=Math.abs(d);var k=Math.abs(k),C=a.opt.directionlockdeadzone;if("v"==a.rail.drag.ck){if(d>C&&k<=.3*d)return a.rail.drag=!1,!0;k>C&&(a.rail.drag.dl="f",f("body").scrollTop(f("body").scrollTop()))}else if("h"==a.rail.drag.ck){if(k>C&&d<=.3*k)return a.rail.drag=!1,!0;d>C&&(a.rail.drag.dl="f",f("body").scrollLeft(f("body").scrollLeft()))}}a.synched("touchmove",
  function(){a.rail.drag&&2==a.rail.drag.pt&&(a.prepareTransition&&a.prepareTransition(0),a.rail.scrollable&&a.setScrollTop(h),a.scrollmom.update(m,u),a.railh&&a.railh.scrollable?(a.setScrollLeft(l),a.showCursor(h,l)):a.showCursor(h),e.isie10&&document.selection.clear())});e.ischrome&&a.istouchcapable&&(g=!1);if(g)return a.cancelEvent(b)}else if(1==a.rail.drag.pt)return a.onmousemove(b)}}a.onmousedown=function(b,c){if(!a.rail.drag||1==a.rail.drag.pt){if(a.railslocked)return a.cancelEvent(b);a.cancelScroll();
  a.rail.drag={x:b.clientX,y:b.clientY,sx:a.scroll.x,sy:a.scroll.y,pt:1,hr:!!c};var g=a.getTarget(b);!a.ispage&&e.hasmousecapture&&g.setCapture();a.isiframe&&!e.hasmousecapture&&(a.saved.csspointerevents=a.doc.css("pointer-events"),a.css(a.doc,{"pointer-events":"none"}));a.hasmoving=!1;return a.cancelEvent(b)}};a.onmouseup=function(b){if(a.rail.drag){if(1!=a.rail.drag.pt)return!0;e.hasmousecapture&&document.releaseCapture();a.isiframe&&!e.hasmousecapture&&a.doc.css("pointer-events",a.saved.csspointerevents);
  a.rail.drag=!1;a.hasmoving&&a.triggerScrollEnd();return a.cancelEvent(b)}};a.onmousemove=function(b){if(a.rail.drag){if(1==a.rail.drag.pt){if(e.ischrome&&0==b.which)return a.onmouseup(b);a.cursorfreezed=!0;a.hasmoving=!0;if(a.rail.drag.hr){a.scroll.x=a.rail.drag.sx+(b.clientX-a.rail.drag.x);0>a.scroll.x&&(a.scroll.x=0);var c=a.scrollvaluemaxw;a.scroll.x>c&&(a.scroll.x=c)}else a.scroll.y=a.rail.drag.sy+(b.clientY-a.rail.drag.y),0>a.scroll.y&&(a.scroll.y=0),c=a.scrollvaluemax,a.scroll.y>c&&(a.scroll.y=
  c);a.synched("mousemove",function(){a.rail.drag&&1==a.rail.drag.pt&&(a.showCursor(),a.rail.drag.hr?a.hasreversehr?a.doScrollLeft(a.scrollvaluemaxw-Math.round(a.scroll.x*a.scrollratio.x),a.opt.cursordragspeed):a.doScrollLeft(Math.round(a.scroll.x*a.scrollratio.x),a.opt.cursordragspeed):a.doScrollTop(Math.round(a.scroll.y*a.scrollratio.y),a.opt.cursordragspeed))});return a.cancelEvent(b)}}else a.checkarea=0};if(e.cantouch||a.opt.touchbehavior)a.onpreventclick=function(b){if(a.preventclick)return a.preventclick.tg.onclick=
  a.preventclick.click,a.preventclick=!1,a.cancelEvent(b)},a.bind(a.win,"mousedown",a.ontouchstart),a.onclick=e.isios?!1:function(b){return a.lastmouseup?(a.lastmouseup=!1,a.cancelEvent(b)):!0},a.opt.grabcursorenabled&&e.cursorgrabvalue&&(a.css(a.ispage?a.doc:a.win,{cursor:e.cursorgrabvalue}),a.css(a.rail,{cursor:e.cursorgrabvalue}));else{var r=function(b){if(a.selectiondrag){if(b){var c=a.win.outerHeight();b=b.pageY-a.selectiondrag.top;0<b&&b<c&&(b=0);b>=c&&(b-=c);a.selectiondrag.df=b}0!=a.selectiondrag.df&&
  (a.doScrollBy(2*-Math.floor(a.selectiondrag.df/6)),a.debounced("doselectionscroll",function(){r()},50))}};a.hasTextSelected="getSelection"in document?function(){return 0<document.getSelection().rangeCount}:"selection"in document?function(){return"None"!=document.selection.type}:function(){return!1};a.onselectionstart=function(b){a.ispage||(a.selectiondrag=a.win.offset())};a.onselectionend=function(b){a.selectiondrag=!1};a.onselectiondrag=function(b){a.selectiondrag&&a.hasTextSelected()&&a.debounced("selectionscroll",
  function(){r(b)},250)}}e.hasw3ctouch?(a.css(a.rail,{"touch-action":"none"}),a.css(a.cursor,{"touch-action":"none"}),a.bind(a.win,"pointerdown",a.ontouchstart),a.bind(document,"pointerup",a.ontouchend),a.bind(document,"pointermove",a.ontouchmove)):e.hasmstouch?(a.css(a.rail,{"-ms-touch-action":"none"}),a.css(a.cursor,{"-ms-touch-action":"none"}),a.bind(a.win,"MSPointerDown",a.ontouchstart),a.bind(document,"MSPointerUp",a.ontouchend),a.bind(document,"MSPointerMove",a.ontouchmove),a.bind(a.cursor,"MSGestureHold",
  function(a){a.preventDefault()}),a.bind(a.cursor,"contextmenu",function(a){a.preventDefault()})):this.istouchcapable&&(a.bind(a.win,"touchstart",a.ontouchstart),a.bind(document,"touchend",a.ontouchend),a.bind(document,"touchcancel",a.ontouchend),a.bind(document,"touchmove",a.ontouchmove));if(a.opt.cursordragontouch||!e.cantouch&&!a.opt.touchbehavior)a.rail.css({cursor:"default"}),a.railh&&a.railh.css({cursor:"default"}),a.jqbind(a.rail,"mouseenter",function(){if(!a.ispage&&!a.win.is(":visible"))return!1;
  a.canshowonmouseevent&&a.showCursor();a.rail.active=!0}),a.jqbind(a.rail,"mouseleave",function(){a.rail.active=!1;a.rail.drag||a.hideCursor()}),a.opt.sensitiverail&&(a.bind(a.rail,"click",function(b){a.doRailClick(b,!1,!1)}),a.bind(a.rail,"dblclick",function(b){a.doRailClick(b,!0,!1)}),a.bind(a.cursor,"click",function(b){a.cancelEvent(b)}),a.bind(a.cursor,"dblclick",function(b){a.cancelEvent(b)})),a.railh&&(a.jqbind(a.railh,"mouseenter",function(){if(!a.ispage&&!a.win.is(":visible"))return!1;a.canshowonmouseevent&&
  a.showCursor();a.rail.active=!0}),a.jqbind(a.railh,"mouseleave",function(){a.rail.active=!1;a.rail.drag||a.hideCursor()}),a.opt.sensitiverail&&(a.bind(a.railh,"click",function(b){a.doRailClick(b,!1,!0)}),a.bind(a.railh,"dblclick",function(b){a.doRailClick(b,!0,!0)}),a.bind(a.cursorh,"click",function(b){a.cancelEvent(b)}),a.bind(a.cursorh,"dblclick",function(b){a.cancelEvent(b)})));e.cantouch||a.opt.touchbehavior?(a.bind(e.hasmousecapture?a.win:document,"mouseup",a.ontouchend),a.bind(document,"mousemove",
  a.ontouchmove),a.onclick&&a.bind(document,"click",a.onclick),a.opt.cursordragontouch?(a.bind(a.cursor,"mousedown",a.onmousedown),a.bind(a.cursor,"mouseup",a.onmouseup),a.cursorh&&a.bind(a.cursorh,"mousedown",function(b){a.onmousedown(b,!0)}),a.cursorh&&a.bind(a.cursorh,"mouseup",a.onmouseup)):(a.bind(a.rail,"mousedown",function(a){a.preventDefault()}),a.railh&&a.bind(a.railh,"mousedown",function(a){a.preventDefault()}))):(a.bind(e.hasmousecapture?a.win:document,"mouseup",a.onmouseup),a.bind(document,
  "mousemove",a.onmousemove),a.onclick&&a.bind(document,"click",a.onclick),a.bind(a.cursor,"mousedown",a.onmousedown),a.bind(a.cursor,"mouseup",a.onmouseup),a.railh&&(a.bind(a.cursorh,"mousedown",function(b){a.onmousedown(b,!0)}),a.bind(a.cursorh,"mouseup",a.onmouseup)),!a.ispage&&a.opt.enablescrollonselection&&(a.bind(a.win[0],"mousedown",a.onselectionstart),a.bind(document,"mouseup",a.onselectionend),a.bind(a.cursor,"mouseup",a.onselectionend),a.cursorh&&a.bind(a.cursorh,"mouseup",a.onselectionend),
  a.bind(document,"mousemove",a.onselectiondrag)),a.zoom&&(a.jqbind(a.zoom,"mouseenter",function(){a.canshowonmouseevent&&a.showCursor();a.rail.active=!0}),a.jqbind(a.zoom,"mouseleave",function(){a.rail.active=!1;a.rail.drag||a.hideCursor()})));a.opt.enablemousewheel&&(a.isiframe||a.mousewheel(e.isie&&a.ispage?document:a.win,a.onmousewheel),a.mousewheel(a.rail,a.onmousewheel),a.railh&&a.mousewheel(a.railh,a.onmousewheelhr));a.ispage||e.cantouch||/HTML|^BODY/.test(a.win[0].nodeName)||(a.win.attr("tabindex")||
  a.win.attr({tabindex:O++}),a.jqbind(a.win,"focus",function(b){B=a.getTarget(b).id||!0;a.hasfocus=!0;a.canshowonmouseevent&&a.noticeCursor()}),a.jqbind(a.win,"blur",function(b){B=!1;a.hasfocus=!1}),a.jqbind(a.win,"mouseenter",function(b){F=a.getTarget(b).id||!0;a.hasmousefocus=!0;a.canshowonmouseevent&&a.noticeCursor()}),a.jqbind(a.win,"mouseleave",function(){F=!1;a.hasmousefocus=!1;a.rail.drag||a.hideCursor()}))}a.onkeypress=function(b){if(a.railslocked&&0==a.page.maxh)return!0;b=b?b:window.e;var c=
  a.getTarget(b);if(c&&/INPUT|TEXTAREA|SELECT|OPTION/.test(c.nodeName)&&(!c.getAttribute("type")&&!c.type||!/submit|button|cancel/i.tp)||f(c).attr("contenteditable"))return!0;if(a.hasfocus||a.hasmousefocus&&!B||a.ispage&&!B&&!F){c=b.keyCode;if(a.railslocked&&27!=c)return a.cancelEvent(b);var g=b.ctrlKey||!1,d=b.shiftKey||!1,e=!1;switch(c){case 38:case 63233:a.doScrollBy(72);e=!0;break;case 40:case 63235:a.doScrollBy(-72);e=!0;break;case 37:case 63232:a.railh&&(g?a.doScrollLeft(0):a.doScrollLeftBy(72),
  e=!0);break;case 39:case 63234:a.railh&&(g?a.doScrollLeft(a.page.maxw):a.doScrollLeftBy(-72),e=!0);break;case 33:case 63276:a.doScrollBy(a.view.h);e=!0;break;case 34:case 63277:a.doScrollBy(-a.view.h);e=!0;break;case 36:case 63273:a.railh&&g?a.doScrollPos(0,0):a.doScrollTo(0);e=!0;break;case 35:case 63275:a.railh&&g?a.doScrollPos(a.page.maxw,a.page.maxh):a.doScrollTo(a.page.maxh);e=!0;break;case 32:a.opt.spacebarenabled&&(d?a.doScrollBy(a.view.h):a.doScrollBy(-a.view.h),e=!0);break;case 27:a.zoomactive&&
  (a.doZoom(),e=!0)}if(e)return a.cancelEvent(b)}};a.opt.enablekeyboard&&a.bind(document,e.isopera&&!e.isopera12?"keypress":"keydown",a.onkeypress);a.bind(document,"keydown",function(b){b.ctrlKey&&(a.wheelprevented=!0)});a.bind(document,"keyup",function(b){b.ctrlKey||(a.wheelprevented=!1)});a.bind(window,"blur",function(b){a.wheelprevented=!1});a.bind(window,"resize",a.lazyResize);a.bind(window,"orientationchange",a.lazyResize);a.bind(window,"load",a.lazyResize);if(e.ischrome&&!a.ispage&&!a.haswrapper){var t=
  a.win.attr("style"),m=parseFloat(a.win.css("width"))+1;a.win.css("width",m);a.synched("chromefix",function(){a.win.attr("style",t)})}a.onAttributeChange=function(b){a.lazyResize(a.isieold?250:30)};a.isie11||!1===x||(a.observerbody=new x(function(b){b.forEach(function(b){if("attributes"==b.type)return f("body").hasClass("modal-open")&&f("body").hasClass("modal-dialog")&&!f.contains(f(".modal-dialog")[0],a.doc[0])?a.hide():a.show()});if(document.body.scrollHeight!=a.page.maxh)return a.lazyResize(30)}),
  a.observerbody.observe(document.body,{childList:!0,subtree:!0,characterData:!1,attributes:!0,attributeFilter:["class"]}));a.ispage||a.haswrapper||(!1!==x?(a.observer=new x(function(b){b.forEach(a.onAttributeChange)}),a.observer.observe(a.win[0],{childList:!0,characterData:!1,attributes:!0,subtree:!1}),a.observerremover=new x(function(b){b.forEach(function(b){if(0<b.removedNodes.length)for(var c in b.removedNodes)if(a&&b.removedNodes[c]==a.win[0])return a.remove()})}),a.observerremover.observe(a.win[0].parentNode,
  {childList:!0,characterData:!1,attributes:!1,subtree:!1})):(a.bind(a.win,e.isie&&!e.isie9?"propertychange":"DOMAttrModified",a.onAttributeChange),e.isie9&&a.win[0].attachEvent("onpropertychange",a.onAttributeChange),a.bind(a.win,"DOMNodeRemoved",function(b){b.target==a.win[0]&&a.remove()})));!a.ispage&&a.opt.boxzoom&&a.bind(window,"resize",a.resizeZoom);a.istextarea&&(a.bind(a.win,"keydown",a.lazyResize),a.bind(a.win,"mouseup",a.lazyResize));a.lazyResize(30)}if("IFRAME"==this.doc[0].nodeName){var N=
  function(){a.iframexd=!1;var c;try{c="contentDocument"in this?this.contentDocument:this.contentWindow.document}catch(g){a.iframexd=!0,c=!1}if(a.iframexd)return"console"in window&&console.log("NiceScroll error: policy restriced iframe"),!0;a.forcescreen=!0;a.isiframe&&(a.iframe={doc:f(c),html:a.doc.contents().find("html")[0],body:a.doc.contents().find("body")[0]},a.getContentSize=function(){return{w:Math.max(a.iframe.html.scrollWidth,a.iframe.body.scrollWidth),h:Math.max(a.iframe.html.scrollHeight,
  a.iframe.body.scrollHeight)}},a.docscroll=f(a.iframe.body));if(!e.isios&&a.opt.iframeautoresize&&!a.isiframe){a.win.scrollTop(0);a.doc.height("");var d=Math.max(c.getElementsByTagName("html")[0].scrollHeight,c.body.scrollHeight);a.doc.height(d)}a.lazyResize(30);e.isie7&&a.css(f(a.iframe.html),b);a.css(f(a.iframe.body),b);e.isios&&a.haswrapper&&a.css(f(c.body),{"-webkit-transform":"translate3d(0,0,0)"});"contentWindow"in this?a.bind(this.contentWindow,"scroll",a.onscroll):a.bind(c,"scroll",a.onscroll);
  a.opt.enablemousewheel&&a.mousewheel(c,a.onmousewheel);a.opt.enablekeyboard&&a.bind(c,e.isopera?"keypress":"keydown",a.onkeypress);if(e.cantouch||a.opt.touchbehavior)a.bind(c,"mousedown",a.ontouchstart),a.bind(c,"mousemove",function(b){return a.ontouchmove(b,!0)}),a.opt.grabcursorenabled&&e.cursorgrabvalue&&a.css(f(c.body),{cursor:e.cursorgrabvalue});a.bind(c,"mouseup",a.ontouchend);a.zoom&&(a.opt.dblclickzoom&&a.bind(c,"dblclick",a.doZoom),a.ongesturezoom&&a.bind(c,"gestureend",a.ongesturezoom))};
  this.doc[0].readyState&&"complete"==this.doc[0].readyState&&setTimeout(function(){N.call(a.doc[0],!1)},500);a.bind(this.doc,"load",N)}};this.showCursor=function(b,c){a.cursortimeout&&(clearTimeout(a.cursortimeout),a.cursortimeout=0);if(a.rail){a.autohidedom&&(a.autohidedom.stop().css({opacity:a.opt.cursoropacitymax}),a.cursoractive=!0);a.rail.drag&&1==a.rail.drag.pt||(void 0!==b&&!1!==b&&(a.scroll.y=Math.round(1*b/a.scrollratio.y)),void 0!==c&&(a.scroll.x=Math.round(1*c/a.scrollratio.x)));a.cursor.css({height:a.cursorheight,
  top:a.scroll.y});if(a.cursorh){var d=a.hasreversehr?a.scrollvaluemaxw-a.scroll.x:a.scroll.x;!a.rail.align&&a.rail.visibility?a.cursorh.css({width:a.cursorwidth,left:d+a.rail.width}):a.cursorh.css({width:a.cursorwidth,left:d});a.cursoractive=!0}a.zoom&&a.zoom.stop().css({opacity:a.opt.cursoropacitymax})}};this.hideCursor=function(b){a.cursortimeout||!a.rail||!a.autohidedom||a.hasmousefocus&&"leave"==a.opt.autohidemode||(a.cursortimeout=setTimeout(function(){a.rail.active&&a.showonmouseevent||(a.autohidedom.stop().animate({opacity:a.opt.cursoropacitymin}),
  a.zoom&&a.zoom.stop().animate({opacity:a.opt.cursoropacitymin}),a.cursoractive=!1);a.cursortimeout=0},b||a.opt.hidecursordelay))};this.noticeCursor=function(b,c,d){a.showCursor(c,d);a.rail.active||a.hideCursor(b)};this.getContentSize=a.ispage?function(){return{w:Math.max(document.body.scrollWidth,document.documentElement.scrollWidth),h:Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}}:a.haswrapper?function(){return{w:a.doc.outerWidth()+parseInt(a.win.css("paddingLeft"))+
  parseInt(a.win.css("paddingRight")),h:a.doc.outerHeight()+parseInt(a.win.css("paddingTop"))+parseInt(a.win.css("paddingBottom"))}}:function(){return{w:a.docscroll[0].scrollWidth,h:a.docscroll[0].scrollHeight}};this.onResize=function(b,c){if(!a||!a.win)return!1;if(!a.haswrapper&&!a.ispage){if("none"==a.win.css("display"))return a.visibility&&a.hideRail().hideRailHr(),!1;a.hidden||a.visibility||a.showRail().showRailHr()}var d=a.page.maxh,e=a.page.maxw,f=a.view.h,k=a.view.w;a.view={w:a.ispage?a.win.width():
  parseInt(a.win[0].clientWidth),h:a.ispage?a.win.height():parseInt(a.win[0].clientHeight)};a.page=c?c:a.getContentSize();a.page.maxh=Math.max(0,a.page.h-a.view.h);a.page.maxw=Math.max(0,a.page.w-a.view.w);if(a.page.maxh==d&&a.page.maxw==e&&a.view.w==k&&a.view.h==f){if(a.ispage)return a;d=a.win.offset();if(a.lastposition&&(e=a.lastposition,e.top==d.top&&e.left==d.left))return a;a.lastposition=d}0==a.page.maxh?(a.hideRail(),a.scrollvaluemax=0,a.scroll.y=0,a.scrollratio.y=0,a.cursorheight=0,a.setScrollTop(0),
  a.rail&&(a.rail.scrollable=!1)):(a.page.maxh-=a.opt.railpadding.top+a.opt.railpadding.bottom,a.rail.scrollable=!0);0==a.page.maxw?(a.hideRailHr(),a.scrollvaluemaxw=0,a.scroll.x=0,a.scrollratio.x=0,a.cursorwidth=0,a.setScrollLeft(0),a.railh&&(a.railh.scrollable=!1)):(a.page.maxw-=a.opt.railpadding.left+a.opt.railpadding.right,a.railh&&(a.railh.scrollable=a.opt.horizrailenabled));a.railslocked=a.locked||0==a.page.maxh&&0==a.page.maxw;if(a.railslocked)return a.ispage||a.updateScrollBar(a.view),!1;a.hidden||
  a.visibility?!a.railh||a.hidden||a.railh.visibility||a.showRailHr():a.showRail().showRailHr();a.istextarea&&a.win.css("resize")&&"none"!=a.win.css("resize")&&(a.view.h-=20);a.cursorheight=Math.min(a.view.h,Math.round(a.view.h/a.page.h*a.view.h));a.cursorheight=a.opt.cursorfixedheight?a.opt.cursorfixedheight:Math.max(a.opt.cursorminheight,a.cursorheight);a.cursorwidth=Math.min(a.view.w,Math.round(a.view.w/a.page.w*a.view.w));a.cursorwidth=a.opt.cursorfixedheight?a.opt.cursorfixedheight:Math.max(a.opt.cursorminheight,
  a.cursorwidth);a.scrollvaluemax=a.view.h-a.cursorheight-a.cursor.hborder-(a.opt.railpadding.top+a.opt.railpadding.bottom);a.railh&&(a.railh.width=0<a.page.maxh?a.view.w-a.rail.width:a.view.w,a.scrollvaluemaxw=a.railh.width-a.cursorwidth-a.cursorh.wborder-(a.opt.railpadding.left+a.opt.railpadding.right));a.ispage||a.updateScrollBar(a.view);a.scrollratio={x:a.page.maxw/a.scrollvaluemaxw,y:a.page.maxh/a.scrollvaluemax};a.getScrollTop()>a.page.maxh?a.doScrollTop(a.page.maxh):(a.scroll.y=Math.round(a.getScrollTop()*
  (1/a.scrollratio.y)),a.scroll.x=Math.round(a.getScrollLeft()*(1/a.scrollratio.x)),a.cursoractive&&a.noticeCursor());a.scroll.y&&0==a.getScrollTop()&&a.doScrollTo(Math.floor(a.scroll.y*a.scrollratio.y));return a};this.resize=a.onResize;this.hlazyresize=0;this.lazyResize=function(b){a.haswrapper||a.hide();a.hlazyresize&&clearTimeout(a.hlazyresize);a.hlazyresize=setTimeout(function(){a&&a.show().resize()},240);return a};this.jqbind=function(b,c,d){a.events.push({e:b,n:c,f:d,q:!0});f(b).bind(c,d)};this.mousewheel=
  function(b,c,d){b="jquery"in b?b[0]:b;if("onwheel"in document.createElement("div"))a._bind(b,"wheel",c,d||!1);else{var e=void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll";q(b,e,c,d||!1);"DOMMouseScroll"==e&&q(b,"MozMousePixelScroll",c,d||!1)}};e.haseventlistener?(this.bind=function(b,c,d,e){a._bind("jquery"in b?b[0]:b,c,d,e||!1)},this._bind=function(b,c,d,e){a.events.push({e:b,n:c,f:d,b:e,q:!1});b.addEventListener(c,d,e||!1)},this.cancelEvent=function(a){if(!a)return!1;a=a.original?a.original:
  a;a.cancelable&&a.preventDefault();a.stopPropagation();a.preventManipulation&&a.preventManipulation();return!1},this.stopPropagation=function(a){if(!a)return!1;a=a.original?a.original:a;a.stopPropagation();return!1},this._unbind=function(a,c,d,e){a.removeEventListener(c,d,e)}):(this.bind=function(b,c,d,e){var f="jquery"in b?b[0]:b;a._bind(f,c,function(b){(b=b||window.event||!1)&&b.srcElement&&(b.target=b.srcElement);"pageY"in b||(b.pageX=b.clientX+document.documentElement.scrollLeft,b.pageY=b.clientY+
  document.documentElement.scrollTop);return!1===d.call(f,b)||!1===e?a.cancelEvent(b):!0})},this._bind=function(b,c,d,e){a.events.push({e:b,n:c,f:d,b:e,q:!1});b.attachEvent?b.attachEvent("on"+c,d):b["on"+c]=d},this.cancelEvent=function(a){a=window.event||!1;if(!a)return!1;a.cancelBubble=!0;a.cancel=!0;return a.returnValue=!1},this.stopPropagation=function(a){a=window.event||!1;if(!a)return!1;a.cancelBubble=!0;return!1},this._unbind=function(a,c,d,e){a.detachEvent?a.detachEvent("on"+c,d):a["on"+c]=!1});
  this.unbindAll=function(){for(var b=0;b<a.events.length;b++){var c=a.events[b];c.q?c.e.unbind(c.n,c.f):a._unbind(c.e,c.n,c.f,c.b)}};this.showRail=function(){0==a.page.maxh||!a.ispage&&"none"==a.win.css("display")||(a.visibility=!0,a.rail.visibility=!0,a.rail.css("display","block"));return a};this.showRailHr=function(){if(!a.railh)return a;0==a.page.maxw||!a.ispage&&"none"==a.win.css("display")||(a.railh.visibility=!0,a.railh.css("display","block"));return a};this.hideRail=function(){a.visibility=
  !1;a.rail.visibility=!1;a.rail.css("display","none");return a};this.hideRailHr=function(){if(!a.railh)return a;a.railh.visibility=!1;a.railh.css("display","none");return a};this.show=function(){a.hidden=!1;a.railslocked=!1;return a.showRail().showRailHr()};this.hide=function(){a.hidden=!0;a.railslocked=!0;return a.hideRail().hideRailHr()};this.toggle=function(){return a.hidden?a.show():a.hide()};this.remove=function(){a.stop();a.cursortimeout&&clearTimeout(a.cursortimeout);for(var b in a.delaylist)a.delaylist[b]&&
  w(a.delaylist[b].h);a.doZoomOut();a.unbindAll();e.isie9&&a.win[0].detachEvent("onpropertychange",a.onAttributeChange);!1!==a.observer&&a.observer.disconnect();!1!==a.observerremover&&a.observerremover.disconnect();!1!==a.observerbody&&a.observerbody.disconnect();a.events=null;a.cursor&&a.cursor.remove();a.cursorh&&a.cursorh.remove();a.rail&&a.rail.remove();a.railh&&a.railh.remove();a.zoom&&a.zoom.remove();for(b=0;b<a.saved.css.length;b++){var c=a.saved.css[b];c[0].css(c[1],void 0===c[2]?"":c[2])}a.saved=
  !1;a.me.data("__nicescroll","");var d=f.nicescroll;d.each(function(b){if(this&&this.id===a.id){delete d[b];for(var c=++b;c<d.length;c++,b++)d[b]=d[c];d.length--;d.length&&delete d[d.length]}});for(var k in a)a[k]=null,delete a[k];a=null};this.scrollstart=function(b){this.onscrollstart=b;return a};this.scrollend=function(b){this.onscrollend=b;return a};this.scrollcancel=function(b){this.onscrollcancel=b;return a};this.zoomin=function(b){this.onzoomin=b;return a};this.zoomout=function(b){this.onzoomout=
  b;return a};this.isScrollable=function(a){a=a.target?a.target:a;if("OPTION"==a.nodeName)return!0;for(;a&&1==a.nodeType&&!/^BODY|HTML/.test(a.nodeName);){var c=f(a),c=c.css("overflowY")||c.css("overflowX")||c.css("overflow")||"";if(/scroll|auto/.test(c))return a.clientHeight!=a.scrollHeight;a=a.parentNode?a.parentNode:!1}return!1};this.getViewport=function(a){for(a=a&&a.parentNode?a.parentNode:!1;a&&1==a.nodeType&&!/^BODY|HTML/.test(a.nodeName);){var c=f(a);if(/fixed|absolute/.test(c.css("position")))return c;
  var d=c.css("overflowY")||c.css("overflowX")||c.css("overflow")||"";if(/scroll|auto/.test(d)&&a.clientHeight!=a.scrollHeight||0<c.getNiceScroll().length)return c;a=a.parentNode?a.parentNode:!1}return!1};this.triggerScrollEnd=function(){if(a.onscrollend){var b=a.getScrollLeft(),c=a.getScrollTop();a.onscrollend.call(a,{type:"scrollend",current:{x:b,y:c},end:{x:b,y:c}})}};this.onmousewheel=function(b){if(!a.wheelprevented){if(a.railslocked)return a.debounced("checkunlock",a.resize,250),!0;if(a.rail.drag)return a.cancelEvent(b);
  "auto"==a.opt.oneaxismousemode&&0!=b.deltaX&&(a.opt.oneaxismousemode=!1);if(a.opt.oneaxismousemode&&0==b.deltaX&&!a.rail.scrollable)return a.railh&&a.railh.scrollable?a.onmousewheelhr(b):!0;var c=+new Date,d=!1;a.opt.preservenativescrolling&&a.checkarea+600<c&&(a.nativescrollingarea=a.isScrollable(b),d=!0);a.checkarea=c;if(a.nativescrollingarea)return!0;if(b=t(b,!1,d))a.checkarea=0;return b}};this.onmousewheelhr=function(b){if(!a.wheelprevented){if(a.railslocked||!a.railh.scrollable)return!0;if(a.rail.drag)return a.cancelEvent(b);
  var c=+new Date,d=!1;a.opt.preservenativescrolling&&a.checkarea+600<c&&(a.nativescrollingarea=a.isScrollable(b),d=!0);a.checkarea=c;return a.nativescrollingarea?!0:a.railslocked?a.cancelEvent(b):t(b,!0,d)}};this.stop=function(){a.cancelScroll();a.scrollmon&&a.scrollmon.stop();a.cursorfreezed=!1;a.scroll.y=Math.round(a.getScrollTop()*(1/a.scrollratio.y));a.noticeCursor();return a};this.getTransitionSpeed=function(b){b=Math.min(Math.round(10*a.opt.scrollspeed),Math.round(b/20*a.opt.scrollspeed));return 20<
  b?b:0};a.opt.smoothscroll?a.ishwscroll&&e.hastransition&&a.opt.usetransition&&a.opt.smoothscroll?(this.prepareTransition=function(b,c){var d=c?20<b?b:0:a.getTransitionSpeed(b),f=d?e.prefixstyle+"transform "+d+"ms ease-out":"";a.lasttransitionstyle&&a.lasttransitionstyle==f||(a.lasttransitionstyle=f,a.doc.css(e.transitionstyle,f));return d},this.doScrollLeft=function(b,c){var d=a.scrollrunning?a.newscrolly:a.getScrollTop();a.doScrollPos(b,d,c)},this.doScrollTop=function(b,c){var d=a.scrollrunning?
  a.newscrollx:a.getScrollLeft();a.doScrollPos(d,b,c)},this.doScrollPos=function(b,c,d){var f=a.getScrollTop(),k=a.getScrollLeft();(0>(a.newscrolly-f)*(c-f)||0>(a.newscrollx-k)*(b-k))&&a.cancelScroll();0==a.opt.bouncescroll&&(0>c?c=0:c>a.page.maxh&&(c=a.page.maxh),0>b?b=0:b>a.page.maxw&&(b=a.page.maxw));if(a.scrollrunning&&b==a.newscrollx&&c==a.newscrolly)return!1;a.newscrolly=c;a.newscrollx=b;a.newscrollspeed=d||!1;if(a.timer)return!1;a.timer=setTimeout(function(){var d=a.getScrollTop(),f=a.getScrollLeft(),
  k=Math.round(Math.sqrt(Math.pow(b-f,2)+Math.pow(c-d,2))),k=a.newscrollspeed&&1<a.newscrollspeed?a.newscrollspeed:a.getTransitionSpeed(k);a.newscrollspeed&&1>=a.newscrollspeed&&(k*=a.newscrollspeed);a.prepareTransition(k,!0);a.timerscroll&&a.timerscroll.tm&&clearInterval(a.timerscroll.tm);0<k&&(!a.scrollrunning&&a.onscrollstart&&a.onscrollstart.call(a,{type:"scrollstart",current:{x:f,y:d},request:{x:b,y:c},end:{x:a.newscrollx,y:a.newscrolly},speed:k}),e.transitionend?a.scrollendtrapped||(a.scrollendtrapped=
  !0,a.bind(a.doc,e.transitionend,a.onScrollTransitionEnd,!1)):(a.scrollendtrapped&&clearTimeout(a.scrollendtrapped),a.scrollendtrapped=setTimeout(a.onScrollTransitionEnd,k)),a.timerscroll={bz:new D(d,a.newscrolly,k,0,0,.58,1),bh:new D(f,a.newscrollx,k,0,0,.58,1)},a.cursorfreezed||(a.timerscroll.tm=setInterval(function(){a.showCursor(a.getScrollTop(),a.getScrollLeft())},60)));a.synched("doScroll-set",function(){a.timer=0;a.scrollendtrapped&&(a.scrollrunning=!0);a.setScrollTop(a.newscrolly);a.setScrollLeft(a.newscrollx);
  if(!a.scrollendtrapped)a.onScrollTransitionEnd()})},50)},this.cancelScroll=function(){if(!a.scrollendtrapped)return!0;var b=a.getScrollTop(),c=a.getScrollLeft();a.scrollrunning=!1;e.transitionend||clearTimeout(e.transitionend);a.scrollendtrapped=!1;a._unbind(a.doc[0],e.transitionend,a.onScrollTransitionEnd);a.prepareTransition(0);a.setScrollTop(b);a.railh&&a.setScrollLeft(c);a.timerscroll&&a.timerscroll.tm&&clearInterval(a.timerscroll.tm);a.timerscroll=!1;a.cursorfreezed=!1;a.showCursor(b,c);return a},
  this.onScrollTransitionEnd=function(){a.scrollendtrapped&&a._unbind(a.doc[0],e.transitionend,a.onScrollTransitionEnd);a.scrollendtrapped=!1;a.prepareTransition(0);a.timerscroll&&a.timerscroll.tm&&clearInterval(a.timerscroll.tm);a.timerscroll=!1;var b=a.getScrollTop(),c=a.getScrollLeft();a.setScrollTop(b);a.railh&&a.setScrollLeft(c);a.noticeCursor(!1,b,c);a.cursorfreezed=!1;0>b?b=0:b>a.page.maxh&&(b=a.page.maxh);0>c?c=0:c>a.page.maxw&&(c=a.page.maxw);if(b!=a.newscrolly||c!=a.newscrollx)return a.doScrollPos(c,
  b,a.opt.snapbackspeed);a.onscrollend&&a.scrollrunning&&a.triggerScrollEnd();a.scrollrunning=!1}):(this.doScrollLeft=function(b,c){var d=a.scrollrunning?a.newscrolly:a.getScrollTop();a.doScrollPos(b,d,c)},this.doScrollTop=function(b,c){var d=a.scrollrunning?a.newscrollx:a.getScrollLeft();a.doScrollPos(d,b,c)},this.doScrollPos=function(b,c,d){function e(){if(a.cancelAnimationFrame)return!0;a.scrollrunning=!0;if(p=1-p)return a.timer=v(e)||1;var b=0,c,d,f=d=a.getScrollTop();if(a.dst.ay){f=a.bzscroll?
  a.dst.py+a.bzscroll.getNow()*a.dst.ay:a.newscrolly;c=f-d;if(0>c&&f<a.newscrolly||0<c&&f>a.newscrolly)f=a.newscrolly;a.setScrollTop(f);f==a.newscrolly&&(b=1)}else b=1;d=c=a.getScrollLeft();if(a.dst.ax){d=a.bzscroll?a.dst.px+a.bzscroll.getNow()*a.dst.ax:a.newscrollx;c=d-c;if(0>c&&d<a.newscrollx||0<c&&d>a.newscrollx)d=a.newscrollx;a.setScrollLeft(d);d==a.newscrollx&&(b+=1)}else b+=1;2==b?(a.timer=0,a.cursorfreezed=!1,a.bzscroll=!1,a.scrollrunning=!1,0>f?f=0:f>a.page.maxh&&(f=Math.max(0,a.page.maxh)),
  0>d?d=0:d>a.page.maxw&&(d=a.page.maxw),d!=a.newscrollx||f!=a.newscrolly?a.doScrollPos(d,f):a.onscrollend&&a.triggerScrollEnd()):a.timer=v(e)||1}c=void 0===c||!1===c?a.getScrollTop(!0):c;if(a.timer&&a.newscrolly==c&&a.newscrollx==b)return!0;a.timer&&w(a.timer);a.timer=0;var f=a.getScrollTop(),k=a.getScrollLeft();(0>(a.newscrolly-f)*(c-f)||0>(a.newscrollx-k)*(b-k))&&a.cancelScroll();a.newscrolly=c;a.newscrollx=b;a.bouncescroll&&a.rail.visibility||(0>a.newscrolly?a.newscrolly=0:a.newscrolly>a.page.maxh&&
  (a.newscrolly=a.page.maxh));a.bouncescroll&&a.railh.visibility||(0>a.newscrollx?a.newscrollx=0:a.newscrollx>a.page.maxw&&(a.newscrollx=a.page.maxw));a.dst={};a.dst.x=b-k;a.dst.y=c-f;a.dst.px=k;a.dst.py=f;var h=Math.round(Math.sqrt(Math.pow(a.dst.x,2)+Math.pow(a.dst.y,2)));a.dst.ax=a.dst.x/h;a.dst.ay=a.dst.y/h;var l=0,n=h;0==a.dst.x?(l=f,n=c,a.dst.ay=1,a.dst.py=0):0==a.dst.y&&(l=k,n=b,a.dst.ax=1,a.dst.px=0);h=a.getTransitionSpeed(h);d&&1>=d&&(h*=d);a.bzscroll=0<h?a.bzscroll?a.bzscroll.update(n,h):
  new D(l,n,h,0,1,0,1):!1;if(!a.timer){(f==a.page.maxh&&c>=a.page.maxh||k==a.page.maxw&&b>=a.page.maxw)&&a.checkContentSize();var p=1;a.cancelAnimationFrame=!1;a.timer=1;a.onscrollstart&&!a.scrollrunning&&a.onscrollstart.call(a,{type:"scrollstart",current:{x:k,y:f},request:{x:b,y:c},end:{x:a.newscrollx,y:a.newscrolly},speed:h});e();(f==a.page.maxh&&c>=f||k==a.page.maxw&&b>=k)&&a.checkContentSize();a.noticeCursor()}},this.cancelScroll=function(){a.timer&&w(a.timer);a.timer=0;a.bzscroll=!1;a.scrollrunning=
  !1;return a}):(this.doScrollLeft=function(b,c){var d=a.getScrollTop();a.doScrollPos(b,d,c)},this.doScrollTop=function(b,c){var d=a.getScrollLeft();a.doScrollPos(d,b,c)},this.doScrollPos=function(b,c,d){var e=b>a.page.maxw?a.page.maxw:b;0>e&&(e=0);var f=c>a.page.maxh?a.page.maxh:c;0>f&&(f=0);a.synched("scroll",function(){a.setScrollTop(f);a.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(b,c){var d=0,d=c?Math.floor((a.scroll.y-b)*a.scrollratio.y):(a.timer?a.newscrolly:
  a.getScrollTop(!0))-b;if(a.bouncescroll){var e=Math.round(a.view.h/2);d<-e?d=-e:d>a.page.maxh+e&&(d=a.page.maxh+e)}a.cursorfreezed=!1;e=a.getScrollTop(!0);if(0>d&&0>=e)return a.noticeCursor();if(d>a.page.maxh&&e>=a.page.maxh)return a.checkContentSize(),a.noticeCursor();a.doScrollTop(d)};this.doScrollLeftBy=function(b,c){var d=0,d=c?Math.floor((a.scroll.x-b)*a.scrollratio.x):(a.timer?a.newscrollx:a.getScrollLeft(!0))-b;if(a.bouncescroll){var e=Math.round(a.view.w/2);d<-e?d=-e:d>a.page.maxw+e&&(d=a.page.maxw+
  e)}a.cursorfreezed=!1;e=a.getScrollLeft(!0);if(0>d&&0>=e||d>a.page.maxw&&e>=a.page.maxw)return a.noticeCursor();a.doScrollLeft(d)};this.doScrollTo=function(b,c){a.cursorfreezed=!1;a.doScrollTop(b)};this.checkContentSize=function(){var b=a.getContentSize();b.h==a.page.h&&b.w==a.page.w||a.resize(!1,b)};a.onscroll=function(b){a.rail.drag||a.cursorfreezed||a.synched("scroll",function(){a.scroll.y=Math.round(a.getScrollTop()*(1/a.scrollratio.y));a.railh&&(a.scroll.x=Math.round(a.getScrollLeft()*(1/a.scrollratio.x)));
  a.noticeCursor()})};a.bind(a.docscroll,"scroll",a.onscroll);this.doZoomIn=function(b){if(!a.zoomactive){a.zoomactive=!0;a.zoomrestore={style:{}};var c="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),d=a.win[0].style,k;for(k in c){var h=c[k];a.zoomrestore.style[h]=void 0!==d[h]?d[h]:""}a.zoomrestore.style.width=a.win.css("width");a.zoomrestore.style.height=a.win.css("height");a.zoomrestore.padding={w:a.win.outerWidth()-a.win.width(),h:a.win.outerHeight()-
  a.win.height()};e.isios4&&(a.zoomrestore.scrollTop=f(window).scrollTop(),f(window).scrollTop(0));a.win.css({position:e.isios4?"absolute":"fixed",top:0,left:0,zIndex:A+100,margin:0});c=a.win.css("backgroundColor");(""==c||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(c))&&a.win.css("backgroundColor","#fff");a.rail.css({zIndex:A+101});a.zoom.css({zIndex:A+102});a.zoom.css("backgroundPosition","0px -18px");a.resizeZoom();a.onzoomin&&a.onzoomin.call(a);return a.cancelEvent(b)}};this.doZoomOut=
  function(b){if(a.zoomactive)return a.zoomactive=!1,a.win.css("margin",""),a.win.css(a.zoomrestore.style),e.isios4&&f(window).scrollTop(a.zoomrestore.scrollTop),a.rail.css({"z-index":a.zindex}),a.zoom.css({"z-index":a.zindex}),a.zoomrestore=!1,a.zoom.css("backgroundPosition","0px 0px"),a.onResize(),a.onzoomout&&a.onzoomout.call(a),a.cancelEvent(b)};this.doZoom=function(b){return a.zoomactive?a.doZoomOut(b):a.doZoomIn(b)};this.resizeZoom=function(){if(a.zoomactive){var b=a.getScrollTop();a.win.css({width:f(window).width()-
  a.zoomrestore.padding.w+"px",height:f(window).height()-a.zoomrestore.padding.h+"px"});a.onResize();a.setScrollTop(Math.min(a.page.maxh,b))}};this.init();f.nicescroll.push(this)},M=function(f){var c=this;this.nc=f;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(f,h){c.stop();var d=c.time();c.steptime=
  0;c.lasttime=d;c.speedx=0;c.speedy=0;c.lastx=f;c.lasty=h;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(f,h){var d=c.time();c.steptime=d-c.lasttime;c.lasttime=d;var d=h-c.lasty,q=f-c.lastx,t=c.nc.getScrollTop(),a=c.nc.getScrollLeft(),t=t+d,a=a+q;c.snapx=0>a||a>c.nc.page.maxw;c.snapy=0>t||t>c.nc.page.maxh;c.speedx=q;c.speedy=d;c.lastx=f;c.lasty=h};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(f,
  h){var d=!1;0>h?(h=0,d=!0):h>c.nc.page.maxh&&(h=c.nc.page.maxh,d=!0);0>f?(f=0,d=!0):f>c.nc.page.maxw&&(f=c.nc.page.maxw,d=!0);d?c.nc.doScrollPos(f,h,c.nc.opt.snapbackspeed):c.nc.triggerScrollEnd()};this.doMomentum=function(f){var h=c.time(),d=f?h+f:c.lasttime;f=c.nc.getScrollLeft();var q=c.nc.getScrollTop(),t=c.nc.page.maxh,a=c.nc.page.maxw;c.speedx=0<a?Math.min(60,c.speedx):0;c.speedy=0<t?Math.min(60,c.speedy):0;d=d&&60>=h-d;if(0>q||q>t||0>f||f>a)d=!1;f=c.speedx&&d?c.speedx:!1;if(c.speedy&&d&&c.speedy||
  f){var r=Math.max(16,c.steptime);50<r&&(f=r/50,c.speedx*=f,c.speedy*=f,r=50);c.demulxy=0;c.lastscrollx=c.nc.getScrollLeft();c.chkx=c.lastscrollx;c.lastscrolly=c.nc.getScrollTop();c.chky=c.lastscrolly;var p=c.lastscrollx,e=c.lastscrolly,v=function(){var d=600<c.time()-h?.04:.02;c.speedx&&(p=Math.floor(c.lastscrollx-c.speedx*(1-c.demulxy)),c.lastscrollx=p,0>p||p>a)&&(d=.1);c.speedy&&(e=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=e,0>e||e>t)&&(d=.1);c.demulxy=Math.min(1,c.demulxy+
  d);c.nc.synched("domomentum2d",function(){c.speedx&&(c.nc.getScrollLeft(),c.chkx=p,c.nc.setScrollLeft(p));c.speedy&&(c.nc.getScrollTop(),c.chky=e,c.nc.setScrollTop(e));c.timer||(c.nc.hideCursor(),c.doSnapy(p,e))});1>c.demulxy?c.timer=setTimeout(v,r):(c.stop(),c.nc.hideCursor(),c.doSnapy(p,e))};v()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},y=f.fn.scrollTop;f.cssHooks.pageYOffset={get:function(h,c,k){return(c=f.data(h,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():y.call(h)},set:function(h,
  c){var k=f.data(h,"__nicescroll")||!1;k&&k.ishwscroll?k.setScrollTop(parseInt(c)):y.call(h,c);return this}};f.fn.scrollTop=function(h){if(void 0===h){var c=this[0]?f.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():y.call(this)}return this.each(function(){var c=f.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(h)):y.call(f(this),h)})};var z=f.fn.scrollLeft;f.cssHooks.pageXOffset={get:function(h,c,k){return(c=f.data(h,"__nicescroll")||!1)&&c.ishwscroll?
  c.getScrollLeft():z.call(h)},set:function(h,c){var k=f.data(h,"__nicescroll")||!1;k&&k.ishwscroll?k.setScrollLeft(parseInt(c)):z.call(h,c);return this}};f.fn.scrollLeft=function(h){if(void 0===h){var c=this[0]?f.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():z.call(this)}return this.each(function(){var c=f.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(h)):z.call(f(this),h)})};var E=function(h){var c=this;this.length=0;this.name="nicescrollarray";
  this.each=function(d){f.each(c,d);return c};this.push=function(d){c[c.length]=d;c.length++};this.eq=function(d){return c[d]};if(h)for(var k=0;k<h.length;k++){var l=f.data(h[k],"__nicescroll")||!1;l&&(this[this.length]=l,this.length++)}return this};(function(f,c,k){for(var l=0;l<c.length;l++)k(f,c[l])})(E.prototype,"show hide toggle onResize resize remove stop doScrollPos".split(" "),function(f,c){f[c]=function(){var f=arguments;return this.each(function(){this[c].apply(this,f)})}});f.fn.getNiceScroll=
  function(h){return void 0===h?new E(this):this[h]&&f.data(this[h],"__nicescroll")||!1};f.expr[":"].nicescroll=function(h){return void 0!==f.data(h,"__nicescroll")};f.fn.niceScroll=function(h,c){void 0!==c||"object"!=typeof h||"jquery"in h||(c=h,h=!1);c=f.extend({},c);var k=new E;void 0===c&&(c={});h&&(c.doc=f(h),c.win=f(this));var l=!("doc"in c);l||"win"in c||(c.win=f(this));this.each(function(){var d=f(this).data("__nicescroll")||!1;d||(c.doc=l?f(this):c.doc,d=new S(c,f(this)),f(this).data("__nicescroll",
  d));k.push(d)});return 1==k.length?k[0]:k};window.NiceScroll={getjQuery:function(){return f}};f.nicescroll||(f.nicescroll=new E,f.nicescroll.options=K)});
  
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversation;
      (function (conversation) {
          angular.module("RongWebIMWidget.conversation", []);
      })(conversation = RongWebIMWidget.conversation || (RongWebIMWidget.conversation = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversationlist;
      (function (conversationlist) {
          angular.module("RongWebIMWidget.conversationlist", []);
      })(conversationlist = RongWebIMWidget.conversationlist || (RongWebIMWidget.conversationlist = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      angular.module("RongWebIMWidget", [
          "RongWebIMWidget.conversation",
          "RongWebIMWidget.conversationlist",
          "Evaluate"
      ]);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var userAgent = window.navigator.userAgent.toLowerCase();
      var Helper = (function () {
          function Helper() {
          }
          Helper.timeCompare = function (first, second) {
              var pre = first.toString();
              var cur = second.toString();
              return pre.substring(0, pre.lastIndexOf(":")) == cur.substring(0, cur.lastIndexOf(":"));
          };
          Helper.cloneObject = function (obj) {
              if (!obj)
                  return null;
              var ret;
              if (Object.prototype.toString.call(obj) == "[object Array]") {
                  ret = [];
                  var i = obj.length;
                  while (i--) {
                      ret[i] = Helper.cloneObject(obj[i]);
                  }
                  return ret;
              }
              else if (typeof obj === "object") {
                  ret = {};
                  for (var item in obj) {
                      ret[item] = obj[item];
                  }
                  return ret;
              }
              else {
                  return obj;
              }
          };
          Helper.discernUrlEmailInStr = function (str) {
              var html;
              var EMailReg = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/gi;
              var EMailArr = [];
              html = str.replace(EMailReg, function (str) {
                  EMailArr.push(str);
                  return '[email`' + (EMailArr.length - 1) + ']';
              });
              var URLReg = /(((ht|f)tp(s?))\:\/\/)?((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|cn|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk|me|im))(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&amp;%\$#\=~_\-]+))*/gi;
              html = html.replace(URLReg, function (str, $1) {
                  if ($1) {
                      return '<a target="_blank" href="' + str + '">' + str + '</a>';
                  }
                  else {
                      return '<a target="_blank" href="//' + str + '">' + str + '</a>';
                  }
              });
              for (var i = 0, len = EMailArr.length; i < len; i++) {
                  html = html.replace('[email`' + i + ']', '<a href="mailto:' + EMailArr[i] + '">' + EMailArr[i] + '<a>');
              }
              return html;
          };
          Helper.checkType = function (obj) {
              var type = Object.prototype.toString.call(obj);
              return type.substring(8, type.length - 1).toLowerCase();
          };
          Helper.browser = {
              version: (userAgent.match(/.+(?:rv|it|ra|chrome|ie)[\/: ]([\d.]+)/) || [0, '0'])[1],
              safari: /webkit/.test(userAgent),
              opera: /opera|opr/.test(userAgent),
              msie: /msie|trident/.test(userAgent) && !/opera/.test(userAgent),
              chrome: /chrome/.test(userAgent),
              mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit|like gecko)/.test(userAgent)
          };
          Helper.escapeSymbol = {
              encodeHtmlsymbol: function (str) {
                  if (!str)
                      return '';
                  str = str.replace(/&/g, '&amp;');
                  str = str.replace(/</g, '&lt;');
                  str = str.replace(/>/g, '&gt;');
                  str = str.replace(/"/g, '&quot;');
                  str = str.replace(/'/g, '&#039;');
                  return str;
              },
              decodeHtmlsymbol: function (str) {
                  if (!str)
                      return '';
                  str = str.replace(/&amp;/g, '&');
                  str = str.replace(/&lt;/g, '<');
                  str = str.replace(/&gt;/g, '>');
                  str = str.replace(/&quot;/g, '"');
                  str = str.replace(/&#039;/g, '\'');
                  return str;
              }
          };
          Helper.getFocus = function (obj) {
              obj.focus();
              if (obj.createTextRange) {
                  var rtextRange = obj.createTextRange();
                  rtextRange.moveStart('character', obj.value.length);
                  rtextRange.collapse(true);
                  rtextRange.select();
              }
              else if (obj.selectionStart) {
                  obj.selectionStart = obj.value.length;
              }
              else if (window.getSelection && obj.lastChild) {
                  var sel = window.getSelection();
                  var tempRange = document.createRange();
                  if (Helper.browser.msie) {
                      tempRange.setStart(obj.lastChild, obj.lastChild.length);
                  }
                  else {
                      tempRange.setStart(obj.firstChild, obj.firstChild.length);
                  }
                  sel.removeAllRanges();
                  sel.addRange(tempRange);
              }
          };
          Helper.ImageHelper = {
              getThumbnail: function (obj, area, callback) {
                  var canvas = document.createElement("canvas"), context = canvas.getContext('2d');
                  var img = new Image();
                  img.onload = function () {
                      var target_w;
                      var target_h;
                      var imgarea = img.width * img.height;
                      if (imgarea > area) {
                          var scale = Math.sqrt(imgarea / area);
                          scale = Math.ceil(scale * 100) / 100;
                          target_w = img.width / scale;
                          target_h = img.height / scale;
                      }
                      else {
                          target_w = img.width;
                          target_h = img.height;
                      }
                      canvas.width = target_w;
                      canvas.height = target_h;
                      context.drawImage(img, 0, 0, target_w, target_h);
                      try {
                          var _canvas = canvas.toDataURL("image/jpeg", 0.5);
                          _canvas = _canvas.substr(23);
                          callback(obj, _canvas);
                      }
                      catch (e) {
                          callback(obj, null);
                      }
                  };
                  img.src = Helper.ImageHelper.getFullPath(obj);
              },
              getFullPath: function (file) {
                  window.URL = window.URL || window.webkitURL;
                  if (window.URL && window.URL.createObjectURL) {
                      return window.URL.createObjectURL(file);
                  }
                  else {
                      return null;
                  }
              }
          };
          Helper.CookieHelper = {
              setCookie: function (name, value, exires) {
                  if (exires) {
                      var date = new Date();
                      date.setDate(date.getDate() + exires);
                      document.cookie = name + "=" + encodeURI(value) + ";expires=" + date.toUTCString();
                  }
                  else {
                      document.cookie = name + "=" + encodeURI(value) + ";";
                  }
              },
              getCookie: function (name) {
                  var start = document.cookie.indexOf(name + "=");
                  if (start != -1) {
                      var end = document.cookie.indexOf(";", start);
                      if (end == -1) {
                          end = document.cookie.length;
                      }
                      return decodeURI(document.cookie.substring(start + name.length + 1, end));
                  }
                  else {
                      return "";
                  }
              },
              removeCookie: function (name) {
                  var con = this.getCookie(name);
                  if (con) {
                      this.setCookie(name, "con", -1);
                  }
              }
          };
          return Helper;
      })();
      RongWebIMWidget.Helper = Helper;
      var NotificationHelper = (function () {
          function NotificationHelper() {
          }
          NotificationHelper.isNotificationSupported = function () {
              return (typeof Notification === "function" || typeof Notification === "object");
          };
          NotificationHelper.requestPermission = function () {
              if (!NotificationHelper.isNotificationSupported()) {
                  return;
              }
              Notification.requestPermission(function (status) {
              });
          };
          NotificationHelper.onclick = function (n) { };
          NotificationHelper.showNotification = function (config) {
              if (!NotificationHelper.isNotificationSupported()) {
                  console.log('the current browser does not support Notification API');
                  return;
              }
              if (Notification.permission !== "granted") {
                  console.log('the current page has not been granted for notification');
                  return;
              }
              if (!NotificationHelper.desktopNotification) {
                  return;
              }
              var title = config.title;
              delete config.title;
              var n = new Notification(title, config);
              n.onshow = function () {
                  setTimeout(function () {
                      n.close();
                  }, 5000);
              };
              n.onclick = function () {
                  window.focus();
                  NotificationHelper.onclick && NotificationHelper.onclick(n);
                  n.close();
              };
              n.onerror = function () {
              };
              n.onclose = function () {
              };
          };
          NotificationHelper.desktopNotification = true;
          return NotificationHelper;
      })();
      RongWebIMWidget.NotificationHelper = NotificationHelper;
      var DirectiveFactory = (function () {
          function DirectiveFactory() {
          }
          DirectiveFactory.GetFactoryFor = function (classType) {
              var factory = function () {
                  var args = [];
                  for (var _i = 0; _i < arguments.length; _i++) {
                      args[_i - 0] = arguments[_i];
                  }
                  var newInstance = Object.create(classType.prototype);
                  newInstance.constructor.apply(newInstance, args);
                  return newInstance;
              };
              factory.$inject = classType.$inject;
              return factory;
          };
          return DirectiveFactory;
      })();
      RongWebIMWidget.DirectiveFactory = DirectiveFactory;
      var errSrc = (function () {
          function errSrc() {
          }
          errSrc.instance = function () {
              return new errSrc();
          };
          errSrc.prototype.link = function (scope, element, attrs) {
              if (!attrs["ngSrc"]) {
                  attrs.$set('src', attrs["errSrc"]);
              }
              element.bind('error', function () {
                  if (attrs["src"] != attrs["errSrc"]) {
                      attrs.$set('src', attrs["errSrc"]);
                  }
              });
          };
          return errSrc;
      })();
      var contenteditableDire = (function () {
          function contenteditableDire() {
              this.restrict = 'A';
              this.require = '?ngModel';
          }
          contenteditableDire.prototype.link = function (scope, element, attrs, ngModel) {
              function replacemy(e) {
                  return e.replace(new RegExp("<[\\s\\S.]*?>", "ig"), "");
              }
              var domElement = element[0];
              scope.$watch(function () {
                  return ngModel.$modelValue;
              }, function (newVal) {
                  if (document.activeElement === domElement) {
                      return;
                  }
                  if (newVal === '' || newVal === attrs["placeholder"]) {
                      domElement.innerHTML = attrs["placeholder"];
                      domElement.style.color = "#a9a9a9";
                      ngModel.$setViewValue("");
                  }
              });
              element.bind('focus', function () {
                  if (domElement.innerHTML == attrs["placeholder"]) {
                      domElement.innerHTML = '';
                  }
                  domElement.style.color = '';
              });
              element.bind('blur', function () {
                  if (domElement.innerHTML === '') {
                      domElement.innerHTML = attrs["placeholder"];
                      domElement.style.color = "#a9a9a9";
                  }
              });
              if (!ngModel)
                  return;
              element.bind("paste", function (e) {
                  var that = this;
                  var content;
                  e.preventDefault();
                  if (e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData)) {
                      // originalEvent jQuery中的
                      content = (e.originalEvent || e).clipboardData.getData('text/plain');
                      content = replacemy(content || '');
                      content && document.execCommand('insertText', false, content);
                  }
                  else if (window['clipboardData']) {
                      content = window['clipboardData'].getData('Text');
                      content = replacemy(content || '');
                      if (document['selection']) {
                          content && document['selection'].createRange().pasteHTML(content);
                      }
                      else if (document.getSelection) {
                          document.getSelection().getRangeAt(0).insertNode(document.createTextNode(content));
                      }
                  }
                  console.log(that.innerHTML);
                  ngModel.$setViewValue(that.innerHTML);
              });
              ngModel.$render = function () {
                  element.html(ngModel.$viewValue || '');
              };
              element.bind("keyup paste", read);
              element.bind("input", read);
              function read() {
                  var html = element.html();
                  var html = Helper.escapeSymbol.decodeHtmlsymbol(html);
                  html = html.replace(/^<br>$/i, "");
                  html = html.replace(/<br>/gi, "\n");
                  if (attrs["stripBr"] && html == '<br>') {
                      html = '';
                  }
                  ngModel.$setViewValue(html);
              }
          };
          return contenteditableDire;
      })();
      var ctrlEnterKeys = (function () {
          function ctrlEnterKeys() {
              this.restrict = "A";
              this.require = '?ngModel';
              this.scope = {
                  fun: "&",
                  ctrlenter: "=",
                  content: "="
              };
          }
          ctrlEnterKeys.prototype.link = function (scope, element, attrs, ngModel) {
              scope.ctrlenter = scope.ctrlenter || false;
              element.bind("keypress", function (e) {
                  if (scope.ctrlenter) {
                      if (e.ctrlKey === true && e.keyCode === 13 || e.keyCode === 10) {
                          scope.fun();
                          scope.$parent.$apply();
                          e.preventDefault();
                      }
                  }
                  else {
                      if (e.ctrlKey === false && e.shiftKey === false && (e.keyCode === 13 || e.keyCode === 10)) {
                          scope.fun();
                          scope.$parent.$apply();
                          e.preventDefault();
                      }
                      else if (e.ctrlKey === true && e.keyCode === 13 || e.keyCode === 10) {
                      }
                  }
              });
          };
          return ctrlEnterKeys;
      })();
      angular.module("RongWebIMWidget")
          .directive('errSrc', errSrc.instance)
          .directive("contenteditableDire", DirectiveFactory.GetFactoryFor(contenteditableDire))
          .directive("ctrlEnterKeys", DirectiveFactory.GetFactoryFor(ctrlEnterKeys))
          .filter('trustHtml', ["$sce", function ($sce) {
              return function (str) {
                  var trustAsHtml = $sce.trustAsHtml(str);
                  return trustAsHtml;
              };
          }]).filter("historyTime", ["$filter", function ($filter) {
              return function (time) {
                  var today = new Date();
                  if (time.toDateString() === today.toDateString()) {
                      return $filter("date")(time, "HH:mm");
                  }
                  else if (time.toDateString() === new Date(today.setTime(today.getTime() - 1)).toDateString()) {
                      return "昨天" + $filter("date")(time, "HH:mm");
                  }
                  else {
                      return $filter("date")(time, "yyyy-MM-dd HH:mm");
                  }
              };
          }]);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversation;
      (function (conversation) {
          var UploadImageDomain = "http://7xogjk.com1.z0.glb.clouddn.com/";
          var ConversationController = (function () {
              function ConversationController($scope, conversationServer, WebIMWidget, conversationListServer, widgetConfig, providerdata, RongIMSDKServer) {
                  this.$scope = $scope;
                  this.conversationServer = conversationServer;
                  this.WebIMWidget = WebIMWidget;
                  this.conversationListServer = conversationListServer;
                  this.widgetConfig = widgetConfig;
                  this.providerdata = providerdata;
                  this.RongIMSDKServer = RongIMSDKServer;
                  var _this = this;
                  conversationServer.changeConversation = function (obj) {
                      _this.changeConversation(obj);
                  };
                  conversationServer.handleMessage = function (msg) {
                      _this.handleMessage(msg);
                  };
                  conversationServer._handleConnectSuccess = function () {
                      updateUploadToken();
                  };
                  function updateUploadToken() {
                      RongIMSDKServer.getFileToken().then(function (token) {
                          conversationServer._uploadToken = token;
                          uploadFileRefresh();
                      });
                  }
                  $scope.evaluate = {
                      type: 1,
                      showevaluate: false,
                      valid: false,
                      onConfirm: function (data) {
                          //发评价
                          if (data) {
                              if ($scope.evaluate.type == RongWebIMWidget.EnumCustomerStatus.person) {
                                  RongIMSDKServer.evaluateHumanCustomService(conversationServer.current.targetId, data.stars, data.describe).then(function () {
                                  }, function () {
                                  });
                              }
                              else {
                                  RongIMSDKServer.evaluateRebotCustomService(conversationServer.current.targetId, data.value, data.describe).then(function () {
                                  }, function () {
                                  });
                              }
                          }
                          _this.conversationServer._customService.connected = false;
                          RongIMLib.RongIMClient.getInstance().stopCustomeService(conversationServer.current.targetId, {
                              onSuccess: function () {
                              },
                              onError: function () {
                              }
                          });
                          _this.closeState();
                      },
                      onCancle: function () {
                          $scope.evaluate.showSelf = false;
                      }
                  };
                  $scope._inputPanelState = RongWebIMWidget.EnumInputPanelType.person;
                  $scope.$watch("showemoji", function (newVal, oldVal) {
                      if (newVal === oldVal)
                          return;
                      if (!$scope.emojiList || $scope.emojiList.length == 0) {
                          $scope.emojiList = RongIMLib.RongIMEmoji.emojis.slice(0, 70);
                      }
                  });
                  document.addEventListener("click", function (e) {
                      if ($scope.showemoji && e.target.className.indexOf("iconfont-smile") == -1) {
                          $scope.$apply(function () {
                              $scope.showemoji = false;
                          });
                      }
                  });
                  $scope.$watch("showSelf", function (newVal, oldVal) {
                      if (newVal === oldVal)
                          return;
                      if (newVal && conversationServer._uploadToken) {
                          uploadFileRefresh();
                      }
                      else {
                          qiniuuploader && qiniuuploader.destroy();
                      }
                  });
                  $scope.$watch("_inputPanelState", function (newVal, oldVal) {
                      if (newVal === oldVal)
                          return;
                      if (newVal == RongWebIMWidget.EnumInputPanelType.person && conversationServer._uploadToken) {
                          uploadFileRefresh();
                      }
                      else {
                          qiniuuploader && qiniuuploader.destroy();
                      }
                  });
                  $scope.$watch("conversation.messageContent", function (newVal, oldVal) {
                      if (newVal === oldVal)
                          return;
                      if ($scope.conversation) {
                          RongIMLib.RongIMClient.getInstance().saveTextMessageDraft(+$scope.conversation.targetType, $scope.conversation.targetId, newVal);
                      }
                  });
                  $scope.getHistory = function () {
                      var key = $scope.conversation.targetType + "_" + $scope.conversation.targetId;
                      var arr = conversationServer._cacheHistory[key];
                      arr.splice(0, arr.length);
                      conversationServer._getHistoryMessages(+$scope.conversation.targetType, $scope.conversation.targetId, 20).then(function (data) {
                          if (data.has) {
                              conversationServer._cacheHistory[key].unshift(new RongWebIMWidget.GetMoreMessagePanel());
                          }
                      });
                  };
                  $scope.getMoreMessage = function () {
                      var key = $scope.conversation.targetType + "_" + $scope.conversation.targetId;
                      conversationServer._cacheHistory[key].shift();
                      conversationServer._cacheHistory[key].shift();
                      conversationServer._getHistoryMessages(+$scope.conversation.targetType, $scope.conversation.targetId, 20).then(function (data) {
                          if (data.has) {
                              conversationServer._cacheHistory[key].unshift(new RongWebIMWidget.GetMoreMessagePanel());
                          }
                      });
                  };
                  $scope.switchPerson = function () {
                      RongIMLib.RongIMClient.getInstance().switchToHumanMode(conversationServer.current.targetId, {
                          onSuccess: function () {
                          },
                          onError: function () {
                          }
                      });
                  };
                  $scope.send = function () {
                      if (!$scope.conversation.targetId || !$scope.conversation.targetType) {
                          alert("请先选择一个会话目标。");
                          return;
                      }
                      if ($scope.conversation.messageContent == "") {
                          return;
                      }
                      var con = RongIMLib.RongIMEmoji.symbolToEmoji($scope.conversation.messageContent);
                      var msg = RongIMLib.TextMessage.obtain(con);
                      var userinfo = new RongIMLib.UserInfo(providerdata.currentUserInfo.userId, providerdata.currentUserInfo.name, providerdata.currentUserInfo.portraitUri);
                      msg.user = userinfo;
                      try {
                          RongIMLib.RongIMClient.getInstance().sendMessage(+$scope.conversation.targetType, $scope.conversation.targetId, msg, {
                              onSuccess: function (retMessage) {
                                  conversationListServer.updateConversations().then(function () {
                                  });
                              },
                              onError: function (error) {
                              }
                          });
                      }
                      catch (e) {
                      }
                      var content = _this.packDisplaySendMessage(msg, RongWebIMWidget.MessageType.TextMessage);
                      var cmsg = RongWebIMWidget.Message.convert(content);
                      conversationServer._addHistoryMessages(cmsg);
                      $scope.scrollBar();
                      $scope.conversation.messageContent = "";
                      var obj = document.getElementById("inputMsg");
                      RongWebIMWidget.Helper.getFocus(obj);
                  };
                  var qiniuuploader;
                  function uploadFileRefresh() {
                      qiniuuploader && qiniuuploader.destroy();
                      qiniuuploader = Qiniu.uploader({
                          runtimes: 'html5,html4',
                          browse_button: 'upload-file',
                          container: 'funcPanel',
                          drop_element: 'inputMsg',
                          max_file_size: '100mb',
                          dragdrop: true,
                          chunk_size: '4mb',
                          unique_names: true,
                          uptoken: conversationServer._uploadToken,
                          domain: UploadImageDomain,
                          get_new_uptoken: false,
                          filters: {
                              mime_types: [{ title: "Image files", extensions: "jpg,gif,png,jpeg,bmp" }],
                              prevent_duplicates: false
                          },
                          multi_selection: false,
                          auto_start: true,
                          init: {
                              'FilesAdded': function (up, files) {
                              },
                              'BeforeUpload': function (up, file) {
                              },
                              'UploadProgress': function (up, file) {
                              },
                              'UploadComplete': function () {
                              },
                              'FileUploaded': function (up, file, info) {
                                  if (!$scope.conversation.targetId || !$scope.conversation.targetType) {
                                      alert("请先选择一个会话目标。");
                                      return;
                                  }
                                  info = info.replace(/'/g, "\"");
                                  info = JSON.parse(info);
                                  RongIMLib.RongIMClient.getInstance()
                                      .getFileUrl(RongIMLib.FileType.IMAGE, file.target_name, {
                                      onSuccess: function (url) {
                                          RongWebIMWidget.Helper.ImageHelper.getThumbnail(file.getNative(), 60000, function (obj, data) {
                                              var im = RongIMLib.ImageMessage.obtain(data, url.downloadUrl);
                                              var content = _this.packDisplaySendMessage(im, RongWebIMWidget.MessageType.ImageMessage);
                                              RongIMLib.RongIMClient.getInstance()
                                                  .sendMessage($scope.conversation.targetType, $scope.conversation.targetId, im, {
                                                  onSuccess: function () {
                                                      conversationListServer.updateConversations().then(function () {
                                                      });
                                                  },
                                                  onError: function () {
                                                  }
                                              });
                                              conversationServer._addHistoryMessages(RongWebIMWidget.Message.convert(content));
                                              $scope.$apply(function () {
                                                  $scope.scrollBar();
                                              });
                                              updateUploadToken();
                                          });
                                      },
                                      onError: function () {
                                      }
                                  });
                              },
                              'Error': function (up, err, errTip) {
                                  console.log(err);
                                  updateUploadToken();
                              }
                          }
                      });
                  }
                  $scope.close = function () {
                      if (WebIMWidget.onCloseBefore && typeof WebIMWidget.onCloseBefore === "function") {
                          var isClose = WebIMWidget.onCloseBefore({
                              close: function (data) {
                                  if (conversationServer.current.targetType == RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE) {
                                      if ($scope.evaluate.valid) {
                                          $scope.evaluate.showSelf = true;
                                      }
                                      else {
                                          RongIMLib.RongIMClient.getInstance().stopCustomeService(conversationServer.current.targetId, {
                                              onSuccess: function () {
                                              },
                                              onError: function () {
                                              }
                                          });
                                          conversationServer._customService.connected = false;
                                          _this.closeState();
                                      }
                                  }
                                  else {
                                      _this.closeState();
                                  }
                              }
                          });
                      }
                      else {
                          if (conversationServer.current.targetType == RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE) {
                              if ($scope.evaluate.valid) {
                                  $scope.evaluate.showSelf = true;
                              }
                              else {
                                  RongIMLib.RongIMClient.getInstance().stopCustomeService(conversationServer.current.targetId, {
                                      onSuccess: function () {
                                      },
                                      onError: function () {
                                      }
                                  });
                                  conversationServer._customService.connected = false;
                                  _this.closeState();
                              }
                          }
                          else {
                              _this.closeState();
                          }
                      }
                  };
                  $scope.minimize = function () {
                      WebIMWidget.display = false;
                  };
              }
              ConversationController.prototype.closeState = function () {
                  var _this = this;
                  if (this.WebIMWidget.onClose && typeof this.WebIMWidget.onClose === "function") {
                      setTimeout(function () { _this.WebIMWidget.onClose(_this.$scope.conversation); }, 1);
                  }
                  if (this.widgetConfig.displayConversationList) {
                      this.$scope.showSelf = false;
                  }
                  else {
                      this.WebIMWidget.display = false;
                  }
                  this.$scope.messageList = [];
                  this.$scope.conversation = null;
                  this.conversationServer.current = null;
                  _this.$scope.evaluate.showSelf = false;
              };
              ConversationController.prototype.changeConversation = function (obj) {
                  var _this = this;
                  if (_this.widgetConfig.displayConversationList) {
                      _this.$scope.showSelf = true;
                  }
                  else {
                      _this.$scope.showSelf = true;
                      _this.WebIMWidget.display = true;
                  }
                  if (!obj || !obj.targetId) {
                      _this.$scope.conversation = {};
                      _this.$scope.messageList = [];
                      _this.conversationServer.current = null;
                      setTimeout(function () {
                          _this.$scope.$apply();
                      });
                      return;
                  }
                  var key = obj.targetType + "_" + obj.targetId;
                  if (obj.targetType == RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE
                      && (!_this.conversationServer.current || _this.conversationServer.current.targetId != obj.targetId) && !_this.conversationServer._customService.connected) {
                      _this.conversationServer._customService.connected = false;
                      _this.RongIMSDKServer.startCustomService(obj.targetId);
                  }
                  _this.conversationServer.current = obj;
                  _this.$scope.conversation = obj;
                  _this.$scope.conversation.messageContent = RongIMLib.RongIMClient.getInstance().getTextMessageDraft(obj.targetType, obj.targetId) || "";
                  _this.$scope.messageList = _this.conversationServer._cacheHistory[key] = _this.conversationServer._cacheHistory[key] || [];
                  if (_this.$scope.messageList.length == 0 && _this.conversationServer.current.targetType !== RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE) {
                      _this.conversationServer._getHistoryMessages(obj.targetType, obj.targetId, 3)
                          .then(function (data) {
                          if (_this.$scope.messageList.length > 0) {
                              _this.$scope.messageList.unshift(new RongWebIMWidget.TimePanl(_this.$scope.messageList[0].sentTime));
                              if (data.has) {
                                  _this.$scope.messageList.unshift(new RongWebIMWidget.GetMoreMessagePanel());
                              }
                              setTimeout(function () {
                                  _this.$scope.$apply();
                              });
                              _this.$scope.scrollBar();
                          }
                      });
                  }
                  else {
                      setTimeout(function () {
                          _this.$scope.$apply();
                      });
                      _this.$scope.scrollBar();
                  }
              };
              ConversationController.prototype.handleMessage = function (msg) {
                  var _this = this;
                  if (_this.$scope.conversation
                      && msg.targetId == _this.$scope.conversation.targetId
                      && msg.conversationType == _this.$scope.conversation.targetType) {
                      _this.$scope.$apply();
                      var systemMsg = null;
                      switch (msg.messageType) {
                          case RongWebIMWidget.MessageType.HandShakeResponseMessage:
                              _this.conversationServer._customService.type = msg.content.data.serviceType;
                              _this.conversationServer._customService.connected = true;
                              _this.conversationServer._customService.companyName = msg.content.data.companyName;
                              _this.conversationServer._customService.robotName = msg.content.data.robotName;
                              _this.conversationServer._customService.robotIcon = msg.content.data.robotIcon;
                              _this.conversationServer._customService.robotWelcome = msg.content.data.robotWelcome;
                              _this.conversationServer._customService.humanWelcome = msg.content.data.humanWelcome;
                              _this.conversationServer._customService.noOneOnlineTip = msg.content.data.noOneOnlineTip;
                              if (msg.content.data.serviceType == "1") {
                                  _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robot);
                                  msg.content.data.robotWelcome
                                      && (systemMsg = this.packReceiveMessage(RongIMLib.TextMessage.obtain(msg.content.data.robotWelcome), RongWebIMWidget.MessageType.TextMessage));
                              }
                              else if (msg.content.data.serviceType == "3") {
                                  msg.content.data.robotWelcome
                                      && (systemMsg = this.packReceiveMessage(RongIMLib.TextMessage.obtain(msg.content.data.robotWelcome), RongWebIMWidget.MessageType.TextMessage));
                                  _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robotSwitchPerson);
                              }
                              else {
                                  _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.person);
                              }
                              //会话一分钟评价有效，显示评价
                              _this.$scope.evaluate.valid = false;
                              _this.$scope.evaluate.showSelf = false;
                              setTimeout(function () {
                                  _this.$scope.evaluate.valid = true;
                              }, 60 * 1000);
                              _this.providerdata._productInfo && _this.RongIMSDKServer.sendProductInfo(_this.conversationServer.current.targetId, _this.providerdata._productInfo);
                              break;
                          case RongWebIMWidget.MessageType.ChangeModeResponseMessage:
                              switch (msg.content.data.status) {
                                  case 1:
                                      _this.conversationServer._customService.human.name = msg.content.data.name || "客服人员";
                                      _this.conversationServer._customService.human.headimgurl = msg.content.data.headimgurl;
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.person);
                                      break;
                                  case 2:
                                      if (_this.conversationServer._customService.type == "2") {
                                          _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.person);
                                      }
                                      else if (_this.conversationServer._customService.type == "1" || _this.conversationServer._customService.type == "3") {
                                          _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robotSwitchPerson);
                                      }
                                      break;
                                  case 3:
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robot);
                                      systemMsg = this.packReceiveMessage(RongIMLib.InformationNotificationMessage.obtain("你被拉黑了"), RongWebIMWidget.MessageType.InformationNotificationMessage);
                                      break;
                                  case 4:
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.person);
                                      systemMsg = _this.packReceiveMessage(RongIMLib.InformationNotificationMessage.obtain("已经是人工了"), RongWebIMWidget.MessageType.InformationNotificationMessage);
                                      break;
                                  default:
                                      break;
                              }
                              break;
                          case RongWebIMWidget.MessageType.TerminateMessage:
                              //关闭客服
                              _this.conversationServer._customService.connected = false;
                              if (msg.content.code == 0) {
                                  _this.$scope.evaluate.valid = true;
                                  _this.$scope.close();
                              }
                              else {
                                  if (_this.conversationServer._customService.type == "1") {
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robot);
                                  }
                                  else {
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robotSwitchPerson);
                                  }
                              }
                              break;
                          case RongWebIMWidget.MessageType.SuspendMessage:
                              if (msg.messageDirection == RongWebIMWidget.MessageDirection.SEND) {
                                  _this.conversationServer._customService.connected = false;
                                  _this.closeState();
                              }
                              break;
                          case RongWebIMWidget.MessageType.CustomerStatusUpdateMessage:
                              switch (Number(msg.content.serviceStatus)) {
                                  case 1:
                                      if (_this.conversationServer._customService.type == "1") {
                                          _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robot);
                                      }
                                      else {
                                          _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.robotSwitchPerson);
                                      }
                                      break;
                                  case 2:
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.person);
                                      break;
                                  case 3:
                                      _this.changeCustomerState(RongWebIMWidget.EnumInputPanelType.notService);
                                      break;
                                  default:
                                      break;
                              }
                              break;
                          default:
                              break;
                      }
                      if (systemMsg) {
                          var wmsg = RongWebIMWidget.Message.convert(systemMsg);
                          _this.conversationServer.addCustomServiceInfo(wmsg);
                          _this.conversationServer._addHistoryMessages(wmsg);
                      }
                      _this.conversationServer.addCustomServiceInfo(msg);
                      setTimeout(function () {
                          _this.$scope.$apply();
                          _this.$scope.scrollBar();
                      }, 200);
                  }
                  if (msg.messageType === RongWebIMWidget.MessageType.ImageMessage) {
                      setTimeout(function () {
                          _this.$scope.$apply();
                          _this.$scope.scrollBar();
                      }, 800);
                  }
              };
              ConversationController.prototype.changeCustomerState = function (type) {
                  this.$scope._inputPanelState = type;
                  if (type == RongWebIMWidget.EnumInputPanelType.person) {
                      this.$scope.evaluate.type = RongWebIMWidget.EnumCustomerStatus.person;
                      this.conversationServer._customService.currentType = RongWebIMWidget.EnumCustomerStatus.person;
                      this.conversationServer.current.title = this.conversationServer._customService.human.name || "客服人员";
                  }
                  else {
                      this.$scope.evaluate.type = RongWebIMWidget.EnumCustomerStatus.robot;
                      this.conversationServer._customService.currentType = RongWebIMWidget.EnumCustomerStatus.robot;
                      this.conversationServer.current.title = this.conversationServer._customService.robotName;
                  }
              };
              ConversationController.prototype.packDisplaySendMessage = function (msg, messageType) {
                  var ret = new RongIMLib.Message();
                  var userinfo = new RongIMLib.UserInfo(this.providerdata.currentUserInfo.userId, this.providerdata.currentUserInfo.name || "我", this.providerdata.currentUserInfo.portraitUri);
                  msg.user = userinfo;
                  ret.content = msg;
                  ret.conversationType = this.$scope.conversation.targetType;
                  ret.targetId = this.$scope.conversation.targetId;
                  ret.senderUserId = this.providerdata.currentUserInfo.userId;
                  ret.messageDirection = RongIMLib.MessageDirection.SEND;
                  ret.sentTime = (new Date()).getTime() - (RongIMLib.RongIMClient.getInstance().getDeltaTime() || 0);
                  ret.messageType = messageType;
                  return ret;
              };
              ConversationController.prototype.packReceiveMessage = function (msg, messageType) {
                  var ret = new RongIMLib.Message();
                  var userinfo = null;
                  msg.userInfo = userinfo;
                  ret.content = msg;
                  ret.conversationType = this.$scope.conversation.targetType;
                  ret.targetId = this.$scope.conversation.targetId;
                  ret.senderUserId = this.$scope.conversation.targetId;
                  ret.messageDirection = RongIMLib.MessageDirection.RECEIVE;
                  ret.sentTime = (new Date()).getTime() - (RongIMLib.RongIMClient.getInstance().getDeltaTime() || 0);
                  ret.messageType = messageType;
                  return ret;
              };
              ConversationController.$inject = ["$scope",
                  "ConversationServer",
                  "WebIMWidget",
                  "ConversationListServer",
                  "WidgetConfig",
                  "ProviderData",
                  "RongIMSDKServer"];
              return ConversationController;
          })();
          angular.module("RongWebIMWidget.conversation")
              .controller("conversationController", ConversationController);
      })(conversation = RongWebIMWidget.conversation || (RongWebIMWidget.conversation = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversation;
      (function (conversation) {
          var factory = RongWebIMWidget.DirectiveFactory.GetFactoryFor;
          var rongConversation = (function () {
              function rongConversation(conversationServer) {
                  this.conversationServer = conversationServer;
                  this.restrict = "E";
                  this.templateUrl = "./src/ts/conversation/conversation.tpl.html";
                  this.controller = "conversationController";
              }
              rongConversation.prototype.link = function (scope, ele) {
                  if (window["jQuery"] && window["jQuery"].nicescroll) {
                      $("#Messages").niceScroll({
                          'cursorcolor': "#0099ff",
                          'cursoropacitymax': 1,
                          'touchbehavior': true,
                          'cursorwidth': "8px",
                          'cursorborder': "0",
                          'cursorborderradius': "5px"
                      });
                  }
                  scope.scrollBar = function () {
                      setTimeout(function () {
                          var ele = document.getElementById("Messages");
                          if (!ele)
                              return;
                          ele.scrollTop = ele.scrollHeight;
                      }, 200);
                  };
              };
              rongConversation.$inject = ["ConversationServer"];
              return rongConversation;
          })();
          var emoji = (function () {
              function emoji() {
                  this.restrict = "E";
                  this.scope = {
                      item: "=",
                      content: "="
                  };
                  this.template = '<div style="display:inline-block"></div>';
              }
              emoji.prototype.link = function (scope, ele, attr) {
                  ele.find("div").append(scope.item);
                  ele.on("click", function () {
                      scope.content.messageContent = scope.content.messageContent || "";
                      scope.content.messageContent = scope.content.messageContent.replace(/\n$/, "");
                      scope.content.messageContent = scope.content.messageContent + scope.item.children[0].getAttribute("name");
                      scope.$parent.$apply();
                      var obj = document.getElementById("inputMsg");
                      RongWebIMWidget.Helper.getFocus(obj);
                  });
              };
              return emoji;
          })();
          var textmessage = (function () {
              function textmessage() {
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-text"><pre class="rongcloud-Message-entry" ng-bind-html="msg.content|trustHtml"><br></pre></div>' +
                      '</div>';
              }
              textmessage.prototype.link = function (scope, ele, attr) {
              };
              return textmessage;
          })();
          var includinglinkmessage = (function () {
              function includinglinkmessage() {
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-text">' +
                      '<pre class="rongcloud-Message-entry" style="">' +
                      '维护中 由于我们的服务商出现故障，融云官网及相关服务也受到影响，给各位用户带来的不便，还请谅解。  您可以通过 <a href="#">【官方微博】</a>了解</pre>' +
                      '</div>' +
                      '</div>';
              }
              return includinglinkmessage;
          })();
          var imagemessage = (function () {
              function imagemessage() {
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-img">' +
                      '<span id="{{\'rebox_\'+$id}}"  class="rongcloud-Message-entry" style="">' +
                      // '<p>发给您一张示意图</p>' +
                      // '<img ng-src="{{msg.content}}" alt="">' +
                      '<a href="{{msg.imageUri}}" target="_black"><img ng-src="{{msg.content}}"  data-image="{{msg.imageUri}}" alt=""/></a>' +
                      '</span>' +
                      '</div>' +
                      '</div>';
              }
              imagemessage.prototype.link = function (scope, ele, attr) {
                  var img = new Image();
                  img.src = scope.msg.imageUri;
                  setTimeout(function () {
                      if (window["jQuery"] && window["jQuery"].rebox) {
                          $('#rebox_' + scope.$id).rebox({ selector: 'a', zIndex: 999999, theme: "rongcloud-rebox" }).bind("rebox:open", function () {
                              //jQuery rebox 点击空白关闭
                              var rebox = document.getElementsByClassName("rongcloud-rebox")[0];
                              rebox.onclick = function (e) {
                                  if (e.target.tagName.toLowerCase() != "img") {
                                      var rebox_close = document.getElementsByClassName("rongcloud-rebox-close")[0];
                                      rebox_close.click();
                                      rebox = null;
                                      rebox_close = null;
                                  }
                              };
                          });
                      }
                  });
                  img.onload = function () {
                      scope.$apply(function () {
                          scope.msg.content = scope.msg.imageUri;
                      });
                  };
                  scope.showBigImage = function () {
                  };
              };
              return imagemessage;
          })();
          var voicemessage = (function () {
              function voicemessage($timeout) {
                  this.$timeout = $timeout;
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-audio">' +
                      '<span class="rongcloud-Message-entry" style="">' +
                      '<span class="rongcloud-audioBox rongcloud-clearfix " ng-click="play()" ng-class="{\'rongcloud-animate\':isplaying}" ><i></i><i></i><i></i></span>' +
                      '<div style="display: inline-block;" ><span class="rongcloud-audioTimer">{{msg.duration}}”</span><span class="rongcloud-audioState" ng-show="msg.isUnReade"></span></div>' +
                      '</span>' +
                      '</div>' +
                      '</div>';
                  voicemessage.prototype["link"] = function (scope, ele, attr) {
                      scope.msg.duration = parseInt(scope.msg.duration || scope.msg.content.length / 1024);
                      RongIMLib.RongIMVoice.preLoaded(scope.msg.content);
                      scope.play = function () {
                          RongIMLib.RongIMVoice.stop(scope.msg.content);
                          if (!scope.isplaying) {
                              scope.msg.isUnReade = false;
                              RongIMLib.RongIMVoice.play(scope.msg.content, scope.msg.duration);
                              scope.isplaying = true;
                              if (scope.timeoutid) {
                                  $timeout.cancel(scope.timeoutid);
                              }
                              scope.timeoutid = $timeout(function () {
                                  scope.isplaying = false;
                              }, scope.msg.duration * 1000);
                          }
                          else {
                              scope.isplaying = false;
                              $timeout.cancel(scope.timeoutid);
                          }
                      };
                  };
              }
              voicemessage.$inject = ["$timeout"];
              return voicemessage;
          })();
          var locationmessage = (function () {
              function locationmessage() {
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-map">' +
                      '<span class="rongcloud-Message-entry" style="">' +
                      '<div class="rongcloud-mapBox">' +
                      '<img ng-src="{{msg.content}}" alt="">' +
                      '<span>{{msg.poi}}</span>' +
                      '</div>' +
                      '</span>' +
                      '</div>' +
                      '</div>';
              }
              return locationmessage;
          })();
          var richcontentmessage = (function () {
              function richcontentmessage() {
                  this.restrict = "E";
                  this.scope = { msg: "=" };
                  this.template = '<div class="">' +
                      '<div class="rongcloud-Message-image-text">' +
                      '<span class="rongcloud-Message-entry" style="">' +
                      '<div class="rongcloud-image-textBox">' +
                      '<h4>{{msg.title}}</h4>' +
                      '<div class="rongcloud-cont rongcloud-clearfix">' +
                      '<img ng-src="{{msg.imageUri}}" alt="">' +
                      '<div>{{msg.content}}</div>' +
                      '</div>' +
                      '</div>' +
                      '</span>' +
                      '</div>' +
                      '</div>';
              }
              return richcontentmessage;
          })();
          angular.module("RongWebIMWidget.conversation")
              .directive("rongConversation", factory(rongConversation))
              .directive("emoji", factory(emoji))
              .directive("textmessage", factory(textmessage))
              .directive("includinglinkmessage", factory(includinglinkmessage))
              .directive("imagemessage", factory(imagemessage))
              .directive("voicemessage", factory(voicemessage))
              .directive("locationmessage", factory(locationmessage))
              .directive("richcontentmessage", factory(richcontentmessage));
      })(conversation = RongWebIMWidget.conversation || (RongWebIMWidget.conversation = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  /// <reference path="../../../typings/tsd.d.ts"/>
  /// <reference path="../../lib/RongIMLib.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversation;
      (function (conversation_1) {
          var CustomerService = (function () {
              function CustomerService() {
                  this.human = {};
              }
              return CustomerService;
          })();
          var conversationServer = (function () {
              function conversationServer($q, providerdata) {
                  this.$q = $q;
                  this.providerdata = providerdata;
                  this.current = new RongWebIMWidget.Conversation;
                  this._cacheHistory = {};
                  this._customService = new CustomerService();
              }
              conversationServer.prototype.unshiftHistoryMessages = function (id, type, item) {
                  var key = type + "_" + id;
                  var arr = this._cacheHistory[key] = this._cacheHistory[key] || [];
                  if (arr[0] && arr[0].sentTime && arr[0].panelType != RongWebIMWidget.PanelType.Time && item.sentTime) {
                      if (!RongWebIMWidget.Helper.timeCompare(arr[0].sentTime, item.sentTime)) {
                          arr.unshift(new RongWebIMWidget.TimePanl(arr[0].sentTime));
                      }
                  }
                  arr.unshift(item);
              };
              conversationServer.prototype._getHistoryMessages = function (targetType, targetId, number, reset) {
                  var defer = this.$q.defer();
                  var _this = this;
                  RongIMLib.RongIMClient.getInstance().getHistoryMessages(targetType, targetId, reset ? 0 : null, number, {
                      onSuccess: function (data, has) {
                          var msglen = data.length;
                          while (msglen--) {
                              var msg = RongWebIMWidget.Message.convert(data[msglen]);
                              switch (msg.messageType) {
                                  case RongWebIMWidget.MessageType.TextMessage:
                                  case RongWebIMWidget.MessageType.ImageMessage:
                                  case RongWebIMWidget.MessageType.VoiceMessage:
                                  case RongWebIMWidget.MessageType.RichContentMessage:
                                  case RongWebIMWidget.MessageType.LocationMessage:
                                  case RongWebIMWidget.MessageType.InformationNotificationMessage:
                                      _this.unshiftHistoryMessages(targetId, targetType, msg);
                                      _this.addCustomServiceInfo(msg);
                                      if (msg.content && _this.providerdata.getUserInfo) {
                                          (function (msg) {
                                              _this.providerdata.getUserInfo(msg.senderUserId, {
                                                  onSuccess: function (obj) {
                                                      msg.content.userInfo = new RongWebIMWidget.UserInfo(obj.userId, obj.name, obj.portraitUri);
                                                  }
                                              });
                                          })(msg);
                                      }
                                      break;
                                  case RongWebIMWidget.MessageType.UnknownMessage:
                                      break;
                                  default:
                                      break;
                              }
                          }
                          defer.resolve({ data: data, has: has });
                      },
                      onError: function (error) {
                          defer.reject(error);
                      }
                  });
                  return defer.promise;
              };
              conversationServer.prototype._addHistoryMessages = function (item) {
                  var key = item.conversationType + "_" + item.targetId;
                  var arr = this._cacheHistory[key];
                  if (arr.length == 0) {
                      arr.push(new RongWebIMWidget.GetHistoryPanel());
                  }
                  if (arr[arr.length - 1]
                      && arr[arr.length - 1].panelType != RongWebIMWidget.PanelType.Time
                      && arr[arr.length - 1].sentTime
                      && item.sentTime) {
                      if (!RongWebIMWidget.Helper.timeCompare(arr[arr.length - 1].sentTime, item.sentTime)) {
                          arr.push(new RongWebIMWidget.TimePanl(item.sentTime));
                      }
                  }
                  arr.push(item);
              };
              conversationServer.prototype.updateUploadToken = function () {
                  var _this = this;
                  RongIMLib.RongIMClient.getInstance().getFileToken(RongIMLib.FileType.IMAGE, {
                      onSuccess: function (data) {
                          _this._uploadToken = data.token;
                      }, onError: function () {
                      }
                  });
              };
              conversationServer.prototype.addCustomServiceInfo = function (msg) {
                  if (!msg.content || (msg.content.userInfo && msg.content.userInfo.name)) {
                      return;
                  }
                  if (msg.conversationType == RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE && msg.content && msg.messageDirection == RongWebIMWidget.MessageDirection.RECEIVE) {
                      if (this._customService.currentType == 1) {
                          msg.content.userInfo = {
                              name: this._customService.human.name || "客服人员",
                              portraitUri: this._customService.human.headimgurl || this._customService.robotIcon
                          };
                      }
                      else {
                          msg.content.userInfo = {
                              name: this._customService.robotName,
                              portraitUri: this._customService.robotIcon
                          };
                      }
                  }
                  else if (msg.conversationType == RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE && msg.content && msg.messageDirection == RongWebIMWidget.MessageDirection.SEND) {
                      msg.content.userInfo = {
                          name: "我",
                          portraitUri: this.providerdata.currentUserInfo.portraitUri
                      };
                  }
                  return msg;
              };
              conversationServer.$inject = ["$q", "ProviderData"];
              return conversationServer;
          })();
          angular.module("RongWebIMWidget.conversation")
              .service("ConversationServer", conversationServer);
      })(conversation = RongWebIMWidget.conversation || (RongWebIMWidget.conversation = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversationlist;
      (function (conversationlist) {
          var conversationListController = (function () {
              function conversationListController($scope, conversationListServer, WebIMWidget) {
                  this.$scope = $scope;
                  this.conversationListServer = conversationListServer;
                  this.WebIMWidget = WebIMWidget;
                  $scope.minbtn = function () {
                      WebIMWidget.display = false;
                  };
                  $scope.conversationListServer = conversationListServer;
              }
              conversationListController.$inject = [
                  "$scope",
                  "ConversationListServer",
                  "WebIMWidget"
              ];
              return conversationListController;
          })();
          angular.module("RongWebIMWidget.conversationlist")
              .controller("conversationListController", conversationListController);
      })(conversationlist = RongWebIMWidget.conversationlist || (RongWebIMWidget.conversationlist = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  /// <reference path="../../lib/window.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversationlist;
      (function (conversationlist) {
          var factory = RongWebIMWidget.DirectiveFactory.GetFactoryFor;
          var rongConversationList = (function () {
              function rongConversationList() {
                  this.restrict = "E";
                  this.templateUrl = "./src/ts/conversationlist/conversationList.tpl.html";
                  this.controller = "conversationListController";
              }
              rongConversationList.prototype.link = function (scope, ele) {
                  if (window["jQuery"] && window["jQuery"].nicescroll) {
                      $(ele).find(".rongcloud-content").niceScroll({
                          'cursorcolor': "#0099ff",
                          'cursoropacitymax': 1,
                          'touchbehavior': false,
                          'cursorwidth': "8px",
                          'cursorborder': "0",
                          'cursorborderradius': "5px"
                      });
                  }
              };
              return rongConversationList;
          })();
          var conversationItem = (function () {
              function conversationItem(conversationServer, conversationListServer, RongIMSDKServer) {
                  this.conversationServer = conversationServer;
                  this.conversationListServer = conversationListServer;
                  this.RongIMSDKServer = RongIMSDKServer;
                  this.restrict = "E";
                  this.scope = { item: "=" };
                  this.template = '<div class="rongcloud-chatList">' +
                      '<div class="rongcloud-chat_item " ng-class="{\'rongcloud-online\':item.onLine}">' +
                      '<div class="rongcloud-ext">' +
                      '<p class="rongcloud-attr clearfix">' +
                      '<span class="rongcloud-badge" ng-show="item.unreadMessageCount>0">{{item.unreadMessageCount>99?"99+":item.unreadMessageCount}}</span>' +
                      '<i class="rongcloud-sprite rongcloud-no-remind" ng-click="remove($event)"></i>' +
                      '</p>' +
                      '</div>' +
                      '<div class="rongcloud-photo">' +
                      '<img class="rongcloud-img" ng-src="{{item.portraitUri}}" err-src="http://7xo1cb.com1.z0.glb.clouddn.com/rongcloudkefu2.png" alt="">' +
                      '<i ng-show="!!$parent.data.getOnlineStatus&&item.targetType==1" class="rongcloud-Presence rongcloud-Presence--stacked rongcloud-Presence--mainBox"></i>' +
                      '</div>' +
                      '<div class="rongcloud-info">' +
                      '<h3 class="rongcloud-nickname">' +
                      '<span class="rongcloud-nickname_text" title="{{item.title}}">{{item.title}}</span>' +
                      '</h3>' +
                      '</div>' +
                      '</div>' +
                      '</div>';
                  conversationItem.prototype["link"] = function (scope, ele, attr) {
                      ele.on("click", function () {
                          conversationServer
                              .changeConversation(new RongWebIMWidget.Conversation(scope.item.targetType, scope.item.targetId, scope.item.title));
                          if (scope.item.unreadMessageCount > 0) {
                              RongIMSDKServer.clearUnreadCount(scope.item.targetType, scope.item.targetId);
                              RongIMSDKServer.sendReadReceiptMessage(scope.item.targetId, Number(scope.item.targetType));
                              conversationListServer.updateConversations();
                          }
                      });
                      scope.remove = function (e) {
                          e.stopPropagation();
                          RongIMSDKServer.removeConversation(scope.item.targetType, scope.item.targetId).then(function () {
                              if (conversationServer.current.targetType == scope.item.targetType
                                  && conversationServer.current.targetId == scope.item.targetId) {
                              }
                              conversationListServer.updateConversations();
                          }, function (error) {
                          });
                      };
                  };
              }
              conversationItem.$inject = ["ConversationServer",
                  "ConversationListServer",
                  "RongIMSDKServer"];
              return conversationItem;
          })();
          angular.module("RongWebIMWidget.conversationlist")
              .directive("rongConversationList", factory(rongConversationList))
              .directive("conversationItem", factory(conversationItem));
      })(conversationlist = RongWebIMWidget.conversationlist || (RongWebIMWidget.conversationlist = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var conversationlist;
      (function (conversationlist) {
          var ConversationListServer = (function () {
              function ConversationListServer($q, providerdata, widgetConfig, RongIMSDKServer, conversationServer) {
                  this.$q = $q;
                  this.providerdata = providerdata;
                  this.widgetConfig = widgetConfig;
                  this.RongIMSDKServer = RongIMSDKServer;
                  this.conversationServer = conversationServer;
                  this._conversationList = [];
                  this._onlineStatus = [];
                  this.hiddenConversations = [];
                  this._hiddenConversationObject = {};
              }
              ConversationListServer.prototype.setHiddenConversations = function (list) {
                  if (angular.isArray(list)) {
                      for (var i = 0, length = list.length; i < length; i++) {
                          this._hiddenConversationObject[list[i].type + "_" + list[i].id] = true;
                      }
                  }
              };
              ConversationListServer.prototype.updateConversations = function () {
                  var defer = this.$q.defer();
                  var _this = this;
                  RongIMLib.RongIMClient.getInstance().getConversationList({
                      onSuccess: function (data) {
                          var totalUnreadCount = 0;
                          _this._conversationList.splice(0, _this._conversationList.length);
                          for (var i = 0, len = data.length; i < len; i++) {
                              var con = RongWebIMWidget.Conversation.onvert(data[i]);
                              if (_this._hiddenConversationObject[con.targetType + "_" + con.targetId]) {
                                  continue;
                              }
                              switch (con.targetType) {
                                  case RongIMLib.ConversationType.PRIVATE:
                                      if (RongWebIMWidget.Helper.checkType(_this.providerdata.getUserInfo) == "function") {
                                          (function (a, b) {
                                              _this.providerdata.getUserInfo(a.targetId, {
                                                  onSuccess: function (data) {
                                                      a.title = data.name;
                                                      a.portraitUri = data.portraitUri;
                                                      b.conversationTitle = data.name;
                                                      b.portraitUri = data.portraitUri;
                                                  }
                                              });
                                          }(con, data[i]));
                                      }
                                      break;
                                  case RongIMLib.ConversationType.GROUP:
                                      if (RongWebIMWidget.Helper.checkType(_this.providerdata.getGroupInfo) == "function") {
                                          (function (a, b) {
                                              _this.providerdata.getGroupInfo(a.targetId, {
                                                  onSuccess: function (data) {
                                                      a.title = data.name;
                                                      a.portraitUri = data.portraitUri;
                                                      b.conversationTitle = data.name;
                                                      b.portraitUri = data.portraitUri;
                                                  }
                                              });
                                          }(con, data[i]));
                                      }
                                      break;
                                  case RongIMLib.ConversationType.CHATROOM:
                                      con.title = "聊天室：" + con.targetId;
                                      break;
                              }
                              totalUnreadCount += Number(con.unreadMessageCount) || 0;
                              _this._conversationList.push(con);
                          }
                          _this._onlineStatus.forEach(function (item) {
                              var conv = _this._getConversation(RongWebIMWidget.EnumConversationType.PRIVATE, item.id);
                              conv && (conv.onLine = item.status);
                          });
                          if (_this.widgetConfig.displayConversationList) {
                              _this.providerdata.totalUnreadCount = totalUnreadCount;
                              defer.resolve();
                          }
                          else {
                              var cu = _this.conversationServer.current;
                              cu && cu.targetId && _this.RongIMSDKServer.getConversation(cu.targetType, cu.targetId).then(function (conv) {
                                  if (conv && conv.unreadMessageCount) {
                                      _this.providerdata.totalUnreadCount = conv.unreadMessageCount || 0;
                                      defer.resolve();
                                  }
                                  else {
                                      _this.providerdata.totalUnreadCount = 0;
                                      defer.resolve();
                                  }
                              });
                          }
                      },
                      onError: function (error) {
                          defer.reject(error);
                      }
                  }, null);
                  return defer.promise;
              };
              ConversationListServer.prototype._getConversation = function (type, id) {
                  for (var i = 0, len = this._conversationList.length; i < len; i++) {
                      if (this._conversationList[i].targetType == type && this._conversationList[i].targetId == id) {
                          return this._conversationList[i];
                      }
                  }
                  return null;
              };
              ConversationListServer.prototype.startRefreshOnlineStatus = function () {
                  var _this = this;
                  if (_this.widgetConfig.displayConversationList && _this.providerdata.getOnlineStatus) {
                      _this._getOnlineStatus();
                      _this.__intervale && clearInterval(this.__intervale);
                      _this.__intervale = setInterval(function () {
                          _this._getOnlineStatus();
                      }, 30 * 1000);
                  }
              };
              ConversationListServer.prototype._getOnlineStatus = function () {
                  var _this = this;
                  var arr = _this._conversationList.map(function (item) { return item.targetId; });
                  _this.providerdata.getOnlineStatus(arr, {
                      onSuccess: function (data) {
                          _this._onlineStatus = data;
                          _this.updateConversations();
                      }
                  });
              };
              ConversationListServer.prototype.stopRefreshOnlineStatus = function () {
                  clearInterval(this.__intervale);
                  this.__intervale = null;
              };
              ConversationListServer.$inject = ["$q",
                  "ProviderData",
                  "WidgetConfig",
                  "RongIMSDKServer",
                  "ConversationServer"];
              return ConversationListServer;
          })();
          angular.module("RongWebIMWidget.conversationlist")
              .service("ConversationListServer", ConversationListServer);
      })(conversationlist = RongWebIMWidget.conversationlist || (RongWebIMWidget.conversationlist = {}));
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var ProductInfo = (function () {
          function ProductInfo() {
          }
          return ProductInfo;
      })();
      var eleConversationListWidth = 195, eleminbtnHeight = 50, eleminbtnWidth = 195, spacing = 3;
      var WebIMWidget = (function () {
          function WebIMWidget($q, conversationServer, conversationListServer, providerdata, widgetConfig, RongIMSDKServer, $log) {
              this.$q = $q;
              this.conversationServer = conversationServer;
              this.conversationListServer = conversationListServer;
              this.providerdata = providerdata;
              this.widgetConfig = widgetConfig;
              this.RongIMSDKServer = RongIMSDKServer;
              this.$log = $log;
              this.display = false;
              this.connected = false;
              this.EnumConversationType = RongWebIMWidget.EnumConversationType;
              this.EnumConversationListPosition = RongWebIMWidget.EnumConversationListPosition;
          }
          WebIMWidget.prototype.init = function (config) {
              var _this = this;
              config.reminder && (_this.widgetConfig.reminder = config.reminder);
              if (!window.RongIMLib || !window.RongIMLib.RongIMClient) {
                  _this.widgetConfig._config = config;
                  return;
              }
              var defaultStyle = _this.widgetConfig.style;
              angular.extend(_this.widgetConfig, config);
              angular.extend(defaultStyle, config.style);
              if (config.desktopNotification) {
                  RongWebIMWidget.NotificationHelper.requestPermission();
              }
              var eleplaysound = document.getElementById("rongcloud-playsound");
              if (eleplaysound && typeof _this.widgetConfig.voiceUrl === "string") {
                  eleplaysound["src"] = _this.widgetConfig.voiceUrl;
                  _this.widgetConfig.voiceNotification = true;
              }
              else {
                  _this.widgetConfig.voiceNotification = false;
              }
              var eleconversation = document.getElementById("rong-conversation");
              var eleconversationlist = document.getElementById("rong-conversation-list");
              var eleminbtn = document.getElementById("rong-widget-minbtn");
              if (_this.widgetConfig.__isKefu) {
                  eleminbtn = document.getElementById("rong-widget-minbtn-kefu");
              }
              if (defaultStyle) {
                  eleconversation.style["height"] = defaultStyle.height + "px";
                  eleconversation.style["width"] = defaultStyle.width + "px";
                  eleconversationlist.style["height"] = defaultStyle.height + "px";
                  if (defaultStyle.positionFixed) {
                      eleconversationlist.style['position'] = "fixed";
                      eleminbtn.style['position'] = "fixed";
                      eleconversation.style['position'] = "fixed";
                  }
                  else {
                      eleconversationlist.style['position'] = "absolute";
                      eleminbtn.style['position'] = "absolute";
                      eleconversation.style['position'] = "absolute";
                  }
                  if (_this.widgetConfig.displayConversationList) {
                      eleminbtn.style["display"] = "inline-block";
                      eleconversationlist.style["display"] = "inline-block";
                      if (_this.widgetConfig.conversationListPosition == RongWebIMWidget.EnumConversationListPosition.left) {
                          if (!isNaN(defaultStyle.left)) {
                              eleconversationlist.style["left"] = defaultStyle.left + "px";
                              eleminbtn.style["left"] = defaultStyle.left + "px";
                              eleconversation.style["left"] = defaultStyle.left + eleConversationListWidth + spacing + "px";
                          }
                          if (!isNaN(defaultStyle.right)) {
                              eleconversationlist.style["right"] = defaultStyle.right + defaultStyle.width + spacing + "px";
                              eleminbtn.style["right"] = defaultStyle.right + defaultStyle.width + spacing + "px";
                              eleconversation.style["right"] = defaultStyle.right + "px";
                          }
                      }
                      else if (_this.widgetConfig.conversationListPosition == RongWebIMWidget.EnumConversationListPosition.right) {
                          if (!isNaN(defaultStyle.left)) {
                              eleconversationlist.style["left"] = defaultStyle.left + defaultStyle.width + spacing + "px";
                              eleminbtn.style["left"] = defaultStyle.left + defaultStyle.width + spacing + "px";
                              eleconversation.style["left"] = defaultStyle.left + "px";
                          }
                          if (!isNaN(defaultStyle.right)) {
                              eleconversationlist.style["right"] = defaultStyle.right + "px";
                              eleminbtn.style["right"] = defaultStyle.right + "px";
                              eleconversation.style["right"] = defaultStyle.right + eleConversationListWidth + spacing + "px";
                          }
                      }
                      else {
                          throw new Error("config conversationListPosition value is invalid");
                      }
                      if (!isNaN(defaultStyle["top"])) {
                          eleconversationlist.style["top"] = defaultStyle.top + "px";
                          eleminbtn.style["top"] = defaultStyle.top + defaultStyle.height - eleminbtnHeight + "px";
                          eleconversation.style["top"] = defaultStyle.top + "px";
                      }
                      if (!isNaN(defaultStyle["bottom"])) {
                          eleconversationlist.style["bottom"] = defaultStyle.bottom + "px";
                          eleminbtn.style["bottom"] = defaultStyle.bottom + "px";
                          eleconversation.style["bottom"] = defaultStyle.bottom + "px";
                      }
                  }
                  else {
                      eleminbtn.style["display"] = "inline-block";
                      eleconversationlist.style["display"] = "none";
                      eleconversation.style["left"] = defaultStyle["left"] + "px";
                      eleconversation.style["right"] = defaultStyle["right"] + "px";
                      eleconversation.style["top"] = defaultStyle["top"] + "px";
                      eleconversation.style["bottom"] = defaultStyle["bottom"] + "px";
                      eleminbtn.style["top"] = defaultStyle.top + defaultStyle.height - eleminbtnHeight + "px";
                      eleminbtn.style["bottom"] = defaultStyle.bottom + "px";
                      eleminbtn.style["left"] = defaultStyle.left + defaultStyle.width / 2 - eleminbtnWidth / 2 + "px";
                      eleminbtn.style["right"] = defaultStyle.right + defaultStyle.width / 2 - eleminbtnWidth / 2 + "px";
                  }
              }
              if (_this.widgetConfig.displayMinButton == false) {
                  eleminbtn.style["display"] = "none";
              }
              _this.conversationListServer.setHiddenConversations(_this.widgetConfig.hiddenConversations);
              _this.RongIMSDKServer.init(_this.widgetConfig.appkey);
              _this.RongIMSDKServer.registerMessage();
              _this.RongIMSDKServer.connect(_this.widgetConfig.token).then(function (userId) {
                  _this.conversationListServer.updateConversations();
                  _this.conversationListServer.startRefreshOnlineStatus();
                  _this.conversationServer._handleConnectSuccess && _this.conversationServer._handleConnectSuccess();
                  if (RongWebIMWidget.Helper.checkType(_this.widgetConfig.onSuccess) == "function") {
                      _this.widgetConfig.onSuccess(userId);
                  }
                  if (RongWebIMWidget.Helper.checkType(_this.providerdata.getUserInfo) == "function") {
                      _this.providerdata.getUserInfo(userId, {
                          onSuccess: function (data) {
                              _this.providerdata.currentUserInfo =
                                  new RongWebIMWidget.UserInfo(data.userId, data.name, data.portraitUri);
                          }
                      });
                  }
                  //_this.conversationServer._onConnectSuccess();
              }, function (err) {
                  if (err.tokenError) {
                      if (_this.widgetConfig.onError && typeof _this.widgetConfig.onError == "function") {
                          _this.widgetConfig.onError({ code: 0, info: "token 无效" });
                      }
                  }
                  else {
                      if (_this.widgetConfig.onError && typeof _this.widgetConfig.onError == "function") {
                          _this.widgetConfig.onError({ code: err.errorCode });
                      }
                  }
              });
              _this.RongIMSDKServer.setConnectionStatusListener({
                  onChanged: function (status) {
                      _this.providerdata.connectionState = false;
                      switch (status) {
                          //链接成功
                          case RongIMLib.ConnectionStatus.CONNECTED:
                              _this.$log.debug('链接成功');
                              _this.providerdata.connectionState = true;
                              break;
                          //正在链接
                          case RongIMLib.ConnectionStatus.CONNECTING:
                              _this.$log.debug('正在链接');
                              break;
                          //其他设备登陆
                          case RongIMLib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT:
                              _this.$log.debug('其他设备登录');
                              break;
                          case RongIMLib.ConnectionStatus.NETWORK_UNAVAILABLE:
                              _this.$log.debug("网络不可用");
                              break;
                          default:
                              _this.$log.debug(status);
                      }
                  }
              });
              _this.RongIMSDKServer.setOnReceiveMessageListener({
                  onReceived: function (data) {
                      _this.$log.debug(data);
                      var msg = RongWebIMWidget.Message.convert(data);
                      if (RongWebIMWidget.Helper.checkType(_this.providerdata.getUserInfo) == "function" && msg.content) {
                          _this.providerdata.getUserInfo(msg.senderUserId, {
                              onSuccess: function (data) {
                                  if (data) {
                                      msg.content.userInfo = new RongWebIMWidget.UserInfo(data.userId, data.name, data.portraitUri);
                                  }
                              }
                          });
                      }
                      switch (data.messageType) {
                          case RongWebIMWidget.MessageType.VoiceMessage:
                              msg.content.isUnReade = true;
                          case RongWebIMWidget.MessageType.TextMessage:
                          case RongWebIMWidget.MessageType.LocationMessage:
                          case RongWebIMWidget.MessageType.ImageMessage:
                          case RongWebIMWidget.MessageType.RichContentMessage:
                              _this.addMessageAndOperation(msg);
                              var voiceBase = _this.providerdata.voiceSound == true
                                  && eleplaysound
                                  && data.messageDirection == RongWebIMWidget.MessageDirection.RECEIVE
                                  && _this.widgetConfig.voiceNotification;
                              var currentConvversationBase = _this.conversationServer.current
                                  && _this.conversationServer.current.targetType == msg.conversationType
                                  && _this.conversationServer.current.targetId == msg.targetId;
                              var notificationBase = (document.hidden || !_this.display)
                                  && data.messageDirection == RongWebIMWidget.MessageDirection.RECEIVE
                                  && _this.widgetConfig.desktopNotification;
                              if ((_this.widgetConfig.displayConversationList && voiceBase) || (!_this.widgetConfig.displayConversationList && voiceBase && currentConvversationBase)) {
                                  eleplaysound["play"]();
                              }
                              if ((notificationBase && _this.widgetConfig.displayConversationList) || (!_this.widgetConfig.displayConversationList && notificationBase && currentConvversationBase)) {
                                  RongWebIMWidget.NotificationHelper.showNotification({
                                      title: msg.content.userInfo.name,
                                      icon: "",
                                      body: RongWebIMWidget.Message.messageToNotification(data), data: { targetId: msg.targetId, targetType: msg.conversationType }
                                  });
                              }
                              break;
                          case RongWebIMWidget.MessageType.ContactNotificationMessage:
                              //好友通知自行处理
                              break;
                          case RongWebIMWidget.MessageType.InformationNotificationMessage:
                              _this.addMessageAndOperation(msg);
                              break;
                          case RongWebIMWidget.MessageType.UnknownMessage:
                              //未知消息自行处理
                              break;
                          case RongWebIMWidget.MessageType.ReadReceiptMessage:
                              if (data.messageDirection == RongWebIMWidget.MessageDirection.SEND) {
                                  _this.RongIMSDKServer.clearUnreadCount(data.conversationType, data.targetId);
                              }
                              break;
                          default:
                              //未捕获的消息类型
                              break;
                      }
                      if (_this.onReceivedMessage) {
                          _this.onReceivedMessage(data);
                      }
                      _this.conversationServer.handleMessage(msg);
                      if (!document.hidden && _this.display
                          && _this.conversationServer.current
                          && _this.conversationServer.current.targetType == msg.conversationType
                          && _this.conversationServer.current.targetId == msg.targetId
                          && data.messageDirection == RongWebIMWidget.MessageDirection.RECEIVE
                          && data.messageType != RongWebIMWidget.MessageType.ReadReceiptMessage) {
                          _this.RongIMSDKServer.clearUnreadCount(_this.conversationServer.current.targetType, _this.conversationServer.current.targetId);
                          _this.RongIMSDKServer.sendReadReceiptMessage(_this.conversationServer.current.targetId, _this.conversationServer.current.targetType);
                      }
                      _this.conversationListServer.updateConversations().then(function () { });
                  }
              });
              window.onfocus = function () {
                  if (_this.conversationServer.current && _this.conversationServer.current.targetId && _this.display) {
                      _this.RongIMSDKServer.getConversation(_this.conversationServer.current.targetType, _this.conversationServer.current.targetId).then(function (conv) {
                          if (conv && conv.unreadMessageCount > 0) {
                              _this.RongIMSDKServer.clearUnreadCount(_this.conversationServer.current.targetType, _this.conversationServer.current.targetId);
                              _this.RongIMSDKServer.sendReadReceiptMessage(_this.conversationServer.current.targetId, _this.conversationServer.current.targetType);
                              _this.conversationListServer.updateConversations().then(function () { });
                          }
                      });
                  }
              };
          };
          WebIMWidget.prototype.addMessageAndOperation = function (msg) {
              if (msg.conversationType === RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE && !this.conversationServer._customService.connected) {
                  //客服没有连接直接返回不追加显示消息
                  return;
              }
              var key = msg.conversationType + "_" + msg.targetId;
              var hislist = this.conversationServer._cacheHistory[key] = this.conversationServer._cacheHistory[key] || [];
              if (hislist.length == 0) {
                  hislist.push(new RongWebIMWidget.GetHistoryPanel());
                  hislist.push(new RongWebIMWidget.TimePanl(msg.sentTime));
              }
              this.conversationServer._addHistoryMessages(msg);
          };
          WebIMWidget.prototype.setConversation = function (targetType, targetId, title) {
              this.conversationServer.changeConversation(new RongWebIMWidget.Conversation(targetType, targetId, title));
          };
          WebIMWidget.prototype.setUserInfoProvider = function (fun) {
              this.providerdata.getUserInfo = fun;
          };
          WebIMWidget.prototype.setGroupInfoProvider = function (fun) {
              this.providerdata.getGroupInfo = fun;
          };
          WebIMWidget.prototype.setOnlineStatusProvider = function (fun) {
              this.providerdata.getOnlineStatus = fun;
          };
          WebIMWidget.prototype.setProductInfo = function (obj) {
              if (this.conversationServer._customService.connected) {
                  this.RongIMSDKServer.sendProductInfo(this.conversationServer.current.targetId, obj);
              }
              else {
                  this.providerdata._productInfo = obj;
              }
          };
          WebIMWidget.prototype.show = function () {
              this.display = true;
          };
          WebIMWidget.prototype.hidden = function () {
              this.display = false;
          };
          WebIMWidget.prototype.getCurrentConversation = function () {
              return this.conversationServer.current;
          };
          WebIMWidget.$inject = ["$q",
              "ConversationServer",
              "ConversationListServer",
              "ProviderData",
              "WidgetConfig",
              "RongIMSDKServer",
              "$log"];
          return WebIMWidget;
      })();
      RongWebIMWidget.WebIMWidget = WebIMWidget;
      angular.module("RongWebIMWidget")
          .service("WebIMWidget", WebIMWidget);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var Position;
      (function (Position) {
          Position[Position["left"] = 1] = "left";
          Position[Position["right"] = 2] = "right";
      })(Position || (Position = {}));
      var RongCustomerService = (function () {
          function RongCustomerService(WebIMWidget) {
              this.WebIMWidget = WebIMWidget;
              this.defaultconfig = {
                  __isCustomerService: true
              };
              this.Position = Position;
          }
          RongCustomerService.prototype.init = function (config) {
              var _this = this;
              angular.extend(this.defaultconfig, config);
              var style = {
                  right: 20
              };
              if (config.position) {
                  if (config.position == Position.left) {
                      style = {
                          left: 20,
                          bottom: 0,
                          width: 325,
                          positionFixed: true
                      };
                  }
                  else {
                      style = {
                          right: 20,
                          bottom: 0,
                          width: 325,
                          positionFixed: true
                      };
                  }
              }
              if (config.style) {
                  config.style.width && (style.width = config.style.width);
                  config.style.height && (style.height = config.style.height);
              }
              this.defaultconfig.style = style;
              _this.WebIMWidget.init(this.defaultconfig);
              _this.WebIMWidget.onShow = function () {
                  _this.WebIMWidget.setConversation(RongWebIMWidget.EnumConversationType.CUSTOMER_SERVICE, config.customerServiceId, "客服");
              };
          };
          RongCustomerService.prototype.show = function () {
              this.WebIMWidget.show();
          };
          RongCustomerService.prototype.setProductInfo = function (obj) {
              this.WebIMWidget.setProductInfo(obj);
          };
          RongCustomerService.prototype.hidden = function () {
              this.WebIMWidget.hidden();
          };
          RongCustomerService.$inject = ["WebIMWidget"];
          return RongCustomerService;
      })();
      RongWebIMWidget.RongCustomerService = RongCustomerService;
      angular.module("RongWebIMWidget")
          .service("RongCustomerService", RongCustomerService);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var Evaluate;
  (function (Evaluate) {
      var evaluatedir = (function () {
          function evaluatedir($timeout) {
              this.$timeout = $timeout;
              this.restrict = "E";
              this.scope = {
                  type: "=",
                  display: "=",
                  enter: "&confirm",
                  dcancle: "&cancle"
              };
              this.templateUrl = './src/ts/evaluate/evaluate.tpl.html';
              evaluatedir.prototype["link"] = function (scope, ele) {
                  var stars = [false, false, false, false, false];
                  var labels = [{ display: "答非所问" }, { display: "理解能力差" }, { display: "一问三不知" }, { display: "不礼貌" }];
                  var enterStars = false; //鼠标悬浮样式
                  scope.stars = stars;
                  scope.labels = RongWebIMWidget.Helper.cloneObject(labels);
                  scope.end = false;
                  scope.displayDescribe = false;
                  scope.data = {
                      stars: 0,
                      value: 0,
                      describe: "",
                      label: ""
                  };
                  scope.$watch("display", function (newVal, oldVal) {
                      if (newVal === oldVal) {
                          return;
                      }
                      else {
                          enterStars = false;
                          scope.displayDescribe = false;
                          scope.labels = RongWebIMWidget.Helper.cloneObject(labels);
                          scope.data = {
                              stars: 0,
                              value: 0,
                              describe: "",
                              label: ""
                          };
                      }
                  });
                  scope.mousehover = function (data) {
                      !enterStars && (scope.data.stars = data);
                  };
                  scope.confirm = function (data) {
                      if (data != undefined) {
                          enterStars = true;
                          if (scope.type == 1) {
                              scope.data.stars = data;
                              if (scope.data.stars != 5) {
                                  scope.displayDescribe = true;
                              }
                              else {
                                  callbackConfirm(scope.data);
                              }
                          }
                          else {
                              scope.data.value = data;
                              if (scope.data.value === false) {
                                  scope.displayDescribe = true;
                              }
                              else {
                                  callbackConfirm(scope.data);
                              }
                          }
                      }
                      else {
                          callbackConfirm(null);
                      }
                  };
                  scope.commit = function () {
                      var value = [];
                      for (var i = 0, len = scope.labels.length; i < len; i++) {
                          if (scope.labels[i].selected) {
                              value.push(scope.labels[i].display);
                          }
                      }
                      scope.data.label = value;
                      callbackConfirm(scope.data);
                  };
                  scope.cancle = function () {
                      scope.display = false;
                      scope.dcancle();
                  };
                  function callbackConfirm(data) {
                      scope.end = true;
                      if (data) {
                          $timeout(function () {
                              scope.display = false;
                              scope.end = false;
                              scope.enter({ data: data });
                          }, 800);
                      }
                      else {
                          scope.display = false;
                          scope.end = false;
                          scope.enter({ data: data });
                      }
                  }
              };
          }
          evaluatedir.$inject = ["$timeout"];
          return evaluatedir;
      })();
      angular.module("Evaluate", [])
          .directive("evaluatedir", RongWebIMWidget.DirectiveFactory.GetFactoryFor(evaluatedir));
  })(Evaluate || (Evaluate = {}));
  /// <reference path="../../typings/tsd.d.ts"/>
  /// <reference path="../lib/window.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      runApp.$inject = ["$http", "WebIMWidget", "WidgetConfig", "RongCustomerService"];
      function runApp($http, WebIMWidget, WidgetConfig, RongCustomerService) {
          var protocol = location.protocol === "https:" ? "https:" : "http:";
          $script.get(protocol + "//cdn.ronghub.com/RongIMLib-2.2.0.min.js", function () {
              $script.get(protocol + "//cdn.ronghub.com/RongEmoji-2.2.0.min.js", function () {
                  RongIMLib.RongIMEmoji && RongIMLib.RongIMEmoji.init();
              });
              $script.get(protocol + "//cdn.ronghub.com/RongIMVoice-2.2.0.min.js", function () {
                  RongIMLib.RongIMVoice && RongIMLib.RongIMVoice.init();
              });
              if (WidgetConfig._config) {
                  if (WidgetConfig._config.__isKefu) {
                      RongCustomerService.init(WidgetConfig._config);
                  }
                  else {
                      WebIMWidget.init(WidgetConfig._config);
                  }
              }
          });
          $script.get(protocol + "//cdn.bootcss.com/plupload/2.1.8/plupload.full.min.js", function () { });
      }
      var rongWidget = (function () {
          function rongWidget() {
              this.restrict = "E";
              this.templateUrl = "./src/ts/main.tpl.html";
              this.controller = "rongWidgetController";
          }
          return rongWidget;
      })();
      var rongWidgetController = (function () {
          function rongWidgetController($scope, $interval, WebIMWidget, WidgetConfig, providerdata, conversationServer, conversationListServer, RongIMSDKServer) {
              this.$scope = $scope;
              this.$interval = $interval;
              this.WebIMWidget = WebIMWidget;
              this.WidgetConfig = WidgetConfig;
              this.providerdata = providerdata;
              this.conversationServer = conversationServer;
              this.conversationListServer = conversationListServer;
              this.RongIMSDKServer = RongIMSDKServer;
              $scope.main = WebIMWidget;
              $scope.config = WidgetConfig;
              $scope.data = providerdata;
              var voicecookie = RongWebIMWidget.Helper.CookieHelper.getCookie("rongcloud.voiceSound");
              providerdata.voiceSound = voicecookie ? (voicecookie == "true") : true;
              $scope.$watch("data.voiceSound", function (newVal, oldVal) {
                  if (newVal === oldVal)
                      return;
                  RongWebIMWidget.Helper.CookieHelper.setCookie("rongcloud.voiceSound", newVal);
              });
              var interval = null;
              $scope.$watch("data.totalUnreadCount", function (newVal, oldVal) {
                  if (newVal > 0) {
                      interval && $interval.cancel(interval);
                      interval = $interval(function () {
                          $scope.twinkle = !$scope.twinkle;
                      }, 1000);
                  }
                  else {
                      $interval.cancel(interval);
                  }
              });
              $scope.$watch("main.display", function () {
                  if (conversationServer.current && conversationServer.current.targetId && WebIMWidget.display) {
                      RongIMSDKServer.getConversation(conversationServer.current.targetType, conversationServer.current.targetId).then(function (conv) {
                          if (conv && conv.unreadMessageCount > 0) {
                              RongIMSDKServer.clearUnreadCount(conversationServer.current.targetType, conversationServer.current.targetId);
                              RongIMSDKServer.sendReadReceiptMessage(conversationServer.current.targetId, conversationServer.current.targetType);
                              conversationListServer.updateConversations().then(function () { });
                          }
                      });
                  }
              });
              WebIMWidget.show = function () {
                  WebIMWidget.display = true;
                  WebIMWidget.fullScreen = false;
                  WebIMWidget.onShow && WebIMWidget.onShow();
                  setTimeout(function () {
                      $scope.$apply();
                  });
              };
              WebIMWidget.hidden = function () {
                  WebIMWidget.display = false;
                  setTimeout(function () {
                      $scope.$apply();
                  });
              };
              $scope.showbtn = function () {
                  WebIMWidget.display = true;
                  WebIMWidget.onShow && WebIMWidget.onShow();
              };
          }
          rongWidgetController.$inject = ["$scope",
              "$interval",
              "WebIMWidget",
              "WidgetConfig",
              "ProviderData",
              "ConversationServer",
              "ConversationListServer",
              "RongIMSDKServer"
          ];
          return rongWidgetController;
      })();
      angular.module("RongWebIMWidget").run(runApp)
          .directive("rongWidget", RongWebIMWidget.DirectiveFactory.GetFactoryFor(rongWidget))
          .controller("rongWidgetController", rongWidgetController);
      ;
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  var __extends = (this && this.__extends) || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  };
  /// <reference path="../../typings/tsd.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      (function (EnumConversationListPosition) {
          EnumConversationListPosition[EnumConversationListPosition["left"] = 0] = "left";
          EnumConversationListPosition[EnumConversationListPosition["right"] = 1] = "right";
      })(RongWebIMWidget.EnumConversationListPosition || (RongWebIMWidget.EnumConversationListPosition = {}));
      var EnumConversationListPosition = RongWebIMWidget.EnumConversationListPosition;
      (function (EnumConversationType) {
          EnumConversationType[EnumConversationType["PRIVATE"] = 1] = "PRIVATE";
          EnumConversationType[EnumConversationType["DISCUSSION"] = 2] = "DISCUSSION";
          EnumConversationType[EnumConversationType["GROUP"] = 3] = "GROUP";
          EnumConversationType[EnumConversationType["CHATROOM"] = 4] = "CHATROOM";
          EnumConversationType[EnumConversationType["CUSTOMER_SERVICE"] = 5] = "CUSTOMER_SERVICE";
          EnumConversationType[EnumConversationType["SYSTEM"] = 6] = "SYSTEM";
          EnumConversationType[EnumConversationType["APP_PUBLIC_SERVICE"] = 7] = "APP_PUBLIC_SERVICE";
          EnumConversationType[EnumConversationType["PUBLIC_SERVICE"] = 8] = "PUBLIC_SERVICE";
      })(RongWebIMWidget.EnumConversationType || (RongWebIMWidget.EnumConversationType = {}));
      var EnumConversationType = RongWebIMWidget.EnumConversationType;
      (function (MessageDirection) {
          MessageDirection[MessageDirection["SEND"] = 1] = "SEND";
          MessageDirection[MessageDirection["RECEIVE"] = 2] = "RECEIVE";
      })(RongWebIMWidget.MessageDirection || (RongWebIMWidget.MessageDirection = {}));
      var MessageDirection = RongWebIMWidget.MessageDirection;
      (function (ReceivedStatus) {
          ReceivedStatus[ReceivedStatus["READ"] = 1] = "READ";
          ReceivedStatus[ReceivedStatus["LISTENED"] = 2] = "LISTENED";
          ReceivedStatus[ReceivedStatus["DOWNLOADED"] = 4] = "DOWNLOADED";
      })(RongWebIMWidget.ReceivedStatus || (RongWebIMWidget.ReceivedStatus = {}));
      var ReceivedStatus = RongWebIMWidget.ReceivedStatus;
      (function (SentStatus) {
          /**
           * 发送中。
           */
          SentStatus[SentStatus["SENDING"] = 10] = "SENDING";
          /**
           * 发送失败。
           */
          SentStatus[SentStatus["FAILED"] = 20] = "FAILED";
          /**
           * 已发送。
           */
          SentStatus[SentStatus["SENT"] = 30] = "SENT";
          /**
           * 对方已接收。
           */
          SentStatus[SentStatus["RECEIVED"] = 40] = "RECEIVED";
          /**
           * 对方已读。
           */
          SentStatus[SentStatus["READ"] = 50] = "READ";
          /**
           * 对方已销毁。
           */
          SentStatus[SentStatus["DESTROYED"] = 60] = "DESTROYED";
      })(RongWebIMWidget.SentStatus || (RongWebIMWidget.SentStatus = {}));
      var SentStatus = RongWebIMWidget.SentStatus;
      var AnimationType;
      (function (AnimationType) {
          AnimationType[AnimationType["left"] = 1] = "left";
          AnimationType[AnimationType["right"] = 2] = "right";
          AnimationType[AnimationType["top"] = 3] = "top";
          AnimationType[AnimationType["bottom"] = 4] = "bottom";
      })(AnimationType || (AnimationType = {}));
      (function (EnumInputPanelType) {
          EnumInputPanelType[EnumInputPanelType["person"] = 0] = "person";
          EnumInputPanelType[EnumInputPanelType["robot"] = 1] = "robot";
          EnumInputPanelType[EnumInputPanelType["robotSwitchPerson"] = 2] = "robotSwitchPerson";
          EnumInputPanelType[EnumInputPanelType["notService"] = 4] = "notService";
      })(RongWebIMWidget.EnumInputPanelType || (RongWebIMWidget.EnumInputPanelType = {}));
      var EnumInputPanelType = RongWebIMWidget.EnumInputPanelType;
      (function (EnumCustomerStatus) {
          EnumCustomerStatus[EnumCustomerStatus["person"] = 1] = "person";
          EnumCustomerStatus[EnumCustomerStatus["robot"] = 2] = "robot";
      })(RongWebIMWidget.EnumCustomerStatus || (RongWebIMWidget.EnumCustomerStatus = {}));
      var EnumCustomerStatus = RongWebIMWidget.EnumCustomerStatus;
      RongWebIMWidget.MessageType = {
          DiscussionNotificationMessage: "DiscussionNotificationMessage ",
          TextMessage: "TextMessage",
          ImageMessage: "ImageMessage",
          VoiceMessage: "VoiceMessage",
          RichContentMessage: "RichContentMessage",
          HandshakeMessage: "HandshakeMessage",
          UnknownMessage: "UnknownMessage",
          SuspendMessage: "SuspendMessage",
          LocationMessage: "LocationMessage",
          InformationNotificationMessage: "InformationNotificationMessage",
          ContactNotificationMessage: "ContactNotificationMessage",
          ProfileNotificationMessage: "ProfileNotificationMessage",
          CommandNotificationMessage: "CommandNotificationMessage",
          HandShakeResponseMessage: "HandShakeResponseMessage",
          ChangeModeResponseMessage: "ChangeModeResponseMessage",
          TerminateMessage: "TerminateMessage",
          CustomerStatusUpdateMessage: "CustomerStatusUpdateMessage",
          ReadReceiptMessage: "ReadReceiptMessage"
      };
      (function (PanelType) {
          PanelType[PanelType["Message"] = 1] = "Message";
          PanelType[PanelType["InformationNotification"] = 2] = "InformationNotification";
          PanelType[PanelType["System"] = 103] = "System";
          PanelType[PanelType["Time"] = 104] = "Time";
          PanelType[PanelType["getHistory"] = 105] = "getHistory";
          PanelType[PanelType["getMore"] = 106] = "getMore";
          PanelType[PanelType["Other"] = 0] = "Other";
      })(RongWebIMWidget.PanelType || (RongWebIMWidget.PanelType = {}));
      var PanelType = RongWebIMWidget.PanelType;
      var ChatPanel = (function () {
          function ChatPanel(type) {
              this.panelType = type;
          }
          return ChatPanel;
      })();
      RongWebIMWidget.ChatPanel = ChatPanel;
      var TimePanl = (function (_super) {
          __extends(TimePanl, _super);
          function TimePanl(date) {
              _super.call(this, PanelType.Time);
              this.sentTime = date;
          }
          return TimePanl;
      })(ChatPanel);
      RongWebIMWidget.TimePanl = TimePanl;
      var GetHistoryPanel = (function (_super) {
          __extends(GetHistoryPanel, _super);
          function GetHistoryPanel() {
              _super.call(this, PanelType.getHistory);
          }
          return GetHistoryPanel;
      })(ChatPanel);
      RongWebIMWidget.GetHistoryPanel = GetHistoryPanel;
      var GetMoreMessagePanel = (function (_super) {
          __extends(GetMoreMessagePanel, _super);
          function GetMoreMessagePanel() {
              _super.call(this, PanelType.getMore);
          }
          return GetMoreMessagePanel;
      })(ChatPanel);
      RongWebIMWidget.GetMoreMessagePanel = GetMoreMessagePanel;
      var TimePanel = (function (_super) {
          __extends(TimePanel, _super);
          function TimePanel(time) {
              _super.call(this, PanelType.Time);
              this.sentTime = time;
          }
          return TimePanel;
      })(ChatPanel);
      RongWebIMWidget.TimePanel = TimePanel;
      var Message = (function (_super) {
          __extends(Message, _super);
          function Message(content, conversationType, extra, objectName, messageDirection, messageId, receivedStatus, receivedTime, senderUserId, sentStatus, sentTime, targetId, messageType) {
              _super.call(this, PanelType.Message);
          }
          Message.convert = function (SDKmsg) {
              var msg = new Message();
              msg.conversationType = SDKmsg.conversationType;
              msg.extra = SDKmsg.extra;
              msg.objectName = SDKmsg.objectName;
              msg.messageDirection = SDKmsg.messageDirection;
              msg.messageId = SDKmsg.messageId;
              msg.receivedStatus = SDKmsg.receivedStatus;
              msg.receivedTime = new Date(SDKmsg.receivedTime);
              msg.senderUserId = SDKmsg.senderUserId;
              msg.sentStatus = SDKmsg.sendStatusMessage;
              msg.sentTime = new Date(SDKmsg.sentTime);
              msg.targetId = SDKmsg.targetId;
              msg.messageType = SDKmsg.messageType;
              switch (msg.messageType) {
                  case RongWebIMWidget.MessageType.TextMessage:
                      var texmsg = new TextMessage();
                      var content = SDKmsg.content.content;
                      content = RongWebIMWidget.Helper.escapeSymbol.encodeHtmlsymbol(content);
                      content = RongWebIMWidget.Helper.discernUrlEmailInStr(content);
                      if (RongIMLib.RongIMEmoji && RongIMLib.RongIMEmoji.emojiToHTML) {
                          content = RongIMLib.RongIMEmoji.emojiToHTML(content);
                      }
                      texmsg.content = content;
                      texmsg.extra = SDKmsg.content.extra;
                      msg.content = texmsg;
                      break;
                  case RongWebIMWidget.MessageType.ImageMessage:
                      var image = new ImageMessage();
                      var content = SDKmsg.content.content || "";
                      if (content.indexOf("base64,") == -1) {
                          content = "data:image/png;base64," + content;
                      }
                      image.content = content;
                      image.imageUri = SDKmsg.content.imageUri;
                      image.extra = SDKmsg.content.extra;
                      msg.content = image;
                      break;
                  case RongWebIMWidget.MessageType.VoiceMessage:
                      var voice = new VoiceMessage();
                      voice.content = SDKmsg.content.content;
                      voice.duration = SDKmsg.content.duration;
                      voice.extra = SDKmsg.content.extra;
                      msg.content = voice;
                      break;
                  case RongWebIMWidget.MessageType.RichContentMessage:
                      var rich = new RichContentMessage();
                      rich.content = SDKmsg.content.content;
                      rich.title = SDKmsg.content.title;
                      rich.imageUri = SDKmsg.content.imageUri;
                      rich.extra = SDKmsg.content.extra;
                      msg.content = rich;
                      break;
                  case RongWebIMWidget.MessageType.LocationMessage:
                      var location = new LocationMessage();
                      var content = SDKmsg.content.content || "";
                      if (content.indexOf("base64,") == -1) {
                          content = "data:image/png;base64," + content;
                      }
                      location.content = content;
                      location.latiude = SDKmsg.content.latiude;
                      location.longitude = SDKmsg.content.longitude;
                      location.poi = SDKmsg.content.poi;
                      location.extra = SDKmsg.content.extra;
                      msg.content = location;
                      break;
                  case RongWebIMWidget.MessageType.InformationNotificationMessage:
                      var info = new InformationNotificationMessage();
                      msg.panelType = 2; //灰条消息
                      info.content = SDKmsg.content.message;
                      msg.content = info;
                      break;
                  case RongWebIMWidget.MessageType.DiscussionNotificationMessage:
                      var discussion = new DiscussionNotificationMessage();
                      discussion.extension = SDKmsg.content.extension;
                      discussion.operation = SDKmsg.content.operation;
                      discussion.type = SDKmsg.content.type;
                      discussion.isHasReceived = SDKmsg.content.isHasReceived;
                      msg.content = discussion;
                  case RongWebIMWidget.MessageType.HandShakeResponseMessage:
                      var handshak = new HandShakeResponseMessage();
                      handshak.status = SDKmsg.content.status;
                      handshak.msg = SDKmsg.content.msg;
                      handshak.data = SDKmsg.content.data;
                      msg.content = handshak;
                      break;
                  case RongWebIMWidget.MessageType.ChangeModeResponseMessage:
                      var change = new ChangeModeResponseMessage();
                      change.code = SDKmsg.content.code;
                      change.data = SDKmsg.content.data;
                      change.status = SDKmsg.content.status;
                      msg.content = change;
                      break;
                  case RongWebIMWidget.MessageType.CustomerStatusUpdateMessage:
                      var up = new CustomerStatusUpdateMessage();
                      up.serviceStatus = SDKmsg.content.serviceStatus;
                      msg.content = up;
                      break;
                  case RongWebIMWidget.MessageType.TerminateMessage:
                      var ter = new TerminateMessage();
                      ter.code = SDKmsg.content.code;
                      msg.content = ter;
                      break;
                  default:
                      break;
              }
              if (msg.content) {
                  msg.content.userInfo = SDKmsg.content.user;
              }
              return msg;
          };
          Message.messageToNotification = function (msg) {
              if (!msg)
                  return null;
              var msgtype = msg.messageType, msgContent;
              if (msgtype == RongWebIMWidget.MessageType.ImageMessage) {
                  msgContent = "[图片]";
              }
              else if (msgtype == RongWebIMWidget.MessageType.LocationMessage) {
                  msgContent = "[位置]";
              }
              else if (msgtype == RongWebIMWidget.MessageType.VoiceMessage) {
                  msgContent = "[语音]";
              }
              else if (msgtype == RongWebIMWidget.MessageType.ContactNotificationMessage || msgtype == RongWebIMWidget.MessageType.CommandNotificationMessage) {
                  msgContent = "[通知消息]";
              }
              else if (msg.objectName == "RC:GrpNtf") {
                  var data = msg.content.message.content.data.data;
                  switch (msg.content.message.content.operation) {
                      case "Add":
                          msgContent = data.targetUserDisplayNames ? (data.targetUserDisplayNames.join("、") + " 加入了群组") : "加入群组";
                          break;
                      case "Quit":
                          msgContent = data.operatorNickname + " 退出了群组";
                          break;
                      case "Kicked":
                          msgContent = data.targetUserDisplayNames ? (data.targetUserDisplayNames.join("、") + " 被剔出群组") : "移除群组";
                          break;
                      case "Rename":
                          msgContent = data.operatorNickname + " 修改了群名称";
                          break;
                      case "Create":
                          msgContent = data.operatorNickname + " 创建了群组";
                          break;
                      case "Dismiss":
                          msgContent = data.operatorNickname + " 解散了群组 " + data.targetGroupName;
                          break;
                      default:
                          break;
                  }
              }
              else {
                  msgContent = msg.content ? msg.content.content : "";
                  msgContent = RongIMLib.RongIMEmoji.emojiToSymbol(msgContent);
                  msgContent = msgContent.replace(/\n/g, " ");
                  msgContent = msgContent.replace(/([\w]{49,50})/g, "$1 ");
              }
              return msgContent;
          };
          return Message;
      })(ChatPanel);
      RongWebIMWidget.Message = Message;
      var UserInfo = (function () {
          function UserInfo(userId, name, portraitUri) {
              this.userId = userId;
              this.name = name;
              this.portraitUri = portraitUri;
          }
          return UserInfo;
      })();
      RongWebIMWidget.UserInfo = UserInfo;
      var GroupInfo = (function () {
          function GroupInfo(userId, name, portraitUri) {
              this.userId = userId;
              this.name = name;
              this.portraitUri = portraitUri;
          }
          return GroupInfo;
      })();
      RongWebIMWidget.GroupInfo = GroupInfo;
      var TextMessage = (function () {
          function TextMessage(msg) {
              msg = msg || {};
              this.content = msg.content;
              this.userInfo = msg.userInfo;
          }
          return TextMessage;
      })();
      RongWebIMWidget.TextMessage = TextMessage;
      var HandShakeResponseMessage = (function () {
          function HandShakeResponseMessage() {
          }
          return HandShakeResponseMessage;
      })();
      RongWebIMWidget.HandShakeResponseMessage = HandShakeResponseMessage;
      var ChangeModeResponseMessage = (function () {
          function ChangeModeResponseMessage() {
          }
          return ChangeModeResponseMessage;
      })();
      RongWebIMWidget.ChangeModeResponseMessage = ChangeModeResponseMessage;
      var TerminateMessage = (function () {
          function TerminateMessage() {
          }
          return TerminateMessage;
      })();
      RongWebIMWidget.TerminateMessage = TerminateMessage;
      var CustomerStatusUpdateMessage = (function () {
          function CustomerStatusUpdateMessage() {
          }
          return CustomerStatusUpdateMessage;
      })();
      RongWebIMWidget.CustomerStatusUpdateMessage = CustomerStatusUpdateMessage;
      var InformationNotificationMessage = (function () {
          function InformationNotificationMessage() {
          }
          return InformationNotificationMessage;
      })();
      RongWebIMWidget.InformationNotificationMessage = InformationNotificationMessage;
      var ImageMessage = (function () {
          function ImageMessage() {
          }
          return ImageMessage;
      })();
      RongWebIMWidget.ImageMessage = ImageMessage;
      var VoiceMessage = (function () {
          function VoiceMessage() {
          }
          return VoiceMessage;
      })();
      RongWebIMWidget.VoiceMessage = VoiceMessage;
      var LocationMessage = (function () {
          function LocationMessage() {
          }
          return LocationMessage;
      })();
      RongWebIMWidget.LocationMessage = LocationMessage;
      var RichContentMessage = (function () {
          function RichContentMessage() {
          }
          return RichContentMessage;
      })();
      RongWebIMWidget.RichContentMessage = RichContentMessage;
      var DiscussionNotificationMessage = (function () {
          function DiscussionNotificationMessage() {
          }
          return DiscussionNotificationMessage;
      })();
      RongWebIMWidget.DiscussionNotificationMessage = DiscussionNotificationMessage;
      var Conversation = (function () {
          function Conversation(targetType, targetId, title) {
              this.targetType = targetType;
              this.targetId = targetId;
              this.title = title || "";
          }
          Conversation.onvert = function (item) {
              var conver = new Conversation();
              conver.targetId = item.targetId;
              conver.targetType = item.conversationType;
              conver.title = item.conversationTitle;
              conver.portraitUri = item.senderPortraitUri;
              conver.unreadMessageCount = item.unreadMessageCount;
              return conver;
          };
          return Conversation;
      })();
      RongWebIMWidget.Conversation = Conversation;
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  /// <reference path="../../typings/tsd.d.ts"/>
  /// <reference path="../lib/RongIMLib.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var RongIMSDKServer = (function () {
          function RongIMSDKServer($q) {
              var _this = this;
              this.$q = $q;
              this.connect = function (token) {
                  var defer = _this.$q.defer();
                  RongIMLib.RongIMClient.connect(token, {
                      onSuccess: function (data) {
                          defer.resolve(data);
                      },
                      onTokenIncorrect: function () {
                          defer.reject({ tokenError: true });
                      },
                      onError: function (errorCode) {
                          defer.reject({ errorCode: errorCode });
                          var info = '';
                          switch (errorCode) {
                              case RongIMLib.ErrorCode.TIMEOUT:
                                  info = '连接超时';
                                  break;
                              case RongIMLib.ErrorCode.UNKNOWN:
                                  info = '未知错误';
                                  break;
                              case RongIMLib.ConnectionState.UNACCEPTABLE_PROTOCOL_VERSION:
                                  info = '不可接受的协议版本';
                                  break;
                              case RongIMLib.ConnectionState.IDENTIFIER_REJECTED:
                                  info = 'appkey不正确';
                                  break;
                              case RongIMLib.ConnectionState.SERVER_UNAVAILABLE:
                                  info = '服务器不可用';
                                  break;
                              case RongIMLib.ConnectionState.NOT_AUTHORIZED:
                                  info = '未认证';
                                  break;
                              case RongIMLib.ConnectionState.REDIRECT:
                                  info = '重新获取导航';
                                  break;
                              case RongIMLib.ConnectionState.APP_BLOCK_OR_DELETE:
                                  info = '应用已被封禁或已被删除';
                                  break;
                              case RongIMLib.ConnectionState.BLOCK:
                                  info = '用户被封禁';
                                  break;
                          }
                          console.log("失败:" + info + errorCode);
                      }
                  });
                  return defer.promise;
              };
              this.getConversation = function (type, targetId) {
                  var defer = _this.$q.defer();
                  RongIMLib.RongIMClient.getInstance().getConversation(Number(type), targetId, {
                      onSuccess: function (data) {
                          defer.resolve(data);
                      },
                      onError: function () {
                          defer.reject();
                      }
                  });
                  return defer.promise;
              };
              this.getFileToken = function () {
                  var defer = _this.$q.defer();
                  RongIMLib.RongIMClient.getInstance().getFileToken(RongIMLib.FileType.IMAGE, {
                      onSuccess: function (data) {
                          if (data) {
                              defer.resolve(data.token);
                          }
                          else {
                              defer.reject();
                          }
                      }, onError: function () {
                          defer.reject();
                      }
                  });
                  return defer.promise;
              };
          }
          RongIMSDKServer.prototype.init = function (appkey) {
              RongIMLib.RongIMClient.init(appkey);
          };
          RongIMSDKServer.prototype.setOnReceiveMessageListener = function (option) {
              RongIMLib.RongIMClient.setOnReceiveMessageListener(option);
          };
          RongIMSDKServer.prototype.setConnectionStatusListener = function (option) {
              RongIMLib.RongIMClient.setConnectionStatusListener(option);
          };
          RongIMSDKServer.prototype.startCustomService = function (targetId) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance().startCustomService(targetId, {
                  onSuccess: function () {
                      defer.resolve();
                  },
                  onError: function () {
                      defer.reject();
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.sendReadReceiptMessage = function (targetId, type) {
              var that = this;
              RongIMLib.RongIMClient.getInstance()
                  .getConversation(Number(type), targetId, {
                  onSuccess: function (data) {
                      if (data) {
                          var read = RongIMLib.ReadReceiptMessage
                              .obtain(data.latestMessage.messageUId, data.latestMessage.sentTime, "1");
                          that.sendMessage(type, targetId, read);
                      }
                  },
                  onError: function () {
                  }
              });
          };
          RongIMSDKServer.prototype.sendMessage = function (conver, targetId, content) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance().sendMessage(+conver, targetId, content, {
                  onSuccess: function (data) {
                      defer.resolve(data);
                  },
                  onError: function (errorCode, message) {
                      defer.reject({ errorCode: errorCode, message: message });
                      var info = '';
                      switch (errorCode) {
                          case RongIMLib.ErrorCode.TIMEOUT:
                              info = '超时';
                              break;
                          case RongIMLib.ErrorCode.UNKNOWN:
                              info = '未知错误';
                              break;
                          case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
                              info = '在黑名单中，无法向对方发送消息';
                              break;
                          case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
                              info = '不在讨论组中';
                              break;
                          case RongIMLib.ErrorCode.NOT_IN_GROUP:
                              info = '不在群组中';
                              break;
                          case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
                              info = '不在聊天室中';
                              break;
                          default:
                              info = "";
                              break;
                      }
                      console.log('发送失败:' + info);
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.evaluateHumanCustomService = function (targetId, value, describe) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance().evaluateHumanCustomService(targetId, value, describe, {
                  onSuccess: function () {
                      defer.resolve();
                  },
                  onError: function () {
                      defer.reject();
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.evaluateRebotCustomService = function (targetId, value, describe) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance().evaluateRebotCustomService(targetId, value, describe, {
                  onSuccess: function () {
                      defer.resolve();
                  },
                  onError: function () {
                      defer.reject();
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.reconnect = function (callback) {
              RongIMLib.RongIMClient.reconnect(callback);
          };
          RongIMSDKServer.prototype.disconnect = function () {
              RongIMLib.RongIMClient.getInstance().disconnect();
          };
          RongIMSDKServer.prototype.logout = function () {
              if (RongIMLib && RongIMLib.RongIMClient) {
                  RongIMLib.RongIMClient.getInstance().logout();
              }
          };
          RongIMSDKServer.prototype.clearUnreadCount = function (type, targetid) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance()
                  .clearUnreadCount(type, targetid, {
                  onSuccess: function (data) {
                      defer.resolve(data);
                  },
                  onError: function (error) {
                      defer.reject(error);
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.getTotalUnreadCount = function () {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance()
                  .getTotalUnreadCount({
                  onSuccess: function (num) {
                      defer.resolve(num);
                  },
                  onError: function () {
                      defer.reject();
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.getConversationList = function () {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance()
                  .getConversationList({
                  onSuccess: function (data) {
                      defer.resolve(data);
                  },
                  onError: function (error) {
                      defer.reject(error);
                  }
              }, null);
              return defer.promise;
          };
          RongIMSDKServer.prototype.removeConversation = function (type, targetId) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance()
                  .removeConversation(type, targetId, {
                  onSuccess: function (data) {
                      defer.resolve(data);
                  },
                  onError: function (error) {
                      defer.reject(error);
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.getHistoryMessages = function (type, targetId, num) {
              var defer = this.$q.defer();
              RongIMLib.RongIMClient.getInstance()
                  .getHistoryMessages(type, targetId, null, num, {
                  onSuccess: function (data, has) {
                      defer.resolve({
                          data: data,
                          has: has
                      });
                  },
                  onError: function (error) {
                      defer.reject(error);
                  }
              });
              return defer.promise;
          };
          RongIMSDKServer.prototype.getDraft = function (type, targetId) {
              return RongIMLib.RongIMClient.getInstance()
                  .getTextMessageDraft(type, targetId) || "";
          };
          RongIMSDKServer.prototype.setDraft = function (type, targetId, value) {
              return RongIMLib.RongIMClient.getInstance()
                  .saveTextMessageDraft(type, targetId, value);
          };
          RongIMSDKServer.prototype.clearDraft = function (type, targetId) {
              return RongIMLib.RongIMClient.getInstance()
                  .clearTextMessageDraft(type, targetId);
          };
          RongIMSDKServer.prototype.sendProductInfo = function (targetId, msgContent) {
              var msg = new RongIMLib.RongIMClient.RegisterMessage["ProductMessage"](msgContent);
              this.sendMessage(RongIMLib.ConversationType.CUSTOMER_SERVICE, targetId, msg);
          };
          RongIMSDKServer.prototype.registerMessage = function () {
              var messageName = "ProductMessage"; // 消息名称。
              var objectName = "cs:product"; // 消息内置名称，请按照此格式命名。
              var mesasgeTag = new RongIMLib.MessageTag(true, true); // 消息是否保存是否计数，true true 保存且计数，false false 不保存不计数。
              var propertys = ["title", "url", "content", "imageUrl", "extra"]; // 消息类中的属性名。
              RongIMLib.RongIMClient.registerMessageType(messageName, objectName, mesasgeTag, propertys);
          };
          RongIMSDKServer.$inject = ["$q"];
          return RongIMSDKServer;
      })();
      RongWebIMWidget.RongIMSDKServer = RongIMSDKServer;
      angular.module("RongWebIMWidget")
          .service("RongIMSDKServer", RongIMSDKServer);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  /// <reference path="../../typings/tsd.d.ts"/>
  var RongWebIMWidget;
  (function (RongWebIMWidget) {
      var ProviderData = (function () {
          function ProviderData() {
              this._cacheUserInfo = [];
              this._cacheGroupInfo = [];
              this.totalUnreadCount = 0;
              this.connectionState = false;
              this.voiceSound = false;
              this.currentUserInfo = {};
          }
          ProviderData.prototype._getCacheUserInfo = function (id) {
              for (var i = 0, len = this._cacheUserInfo.length; i < len; i++) {
                  if (this._cacheUserInfo[i].userId == id) {
                      return this._cacheUserInfo[i];
                  }
              }
              return null;
          };
          ProviderData.prototype._addUserInfo = function (user) {
              var olduser = this._getCacheUserInfo(user.userId);
              if (olduser) {
                  angular.extend(olduser, user);
              }
              else {
                  this._cacheUserInfo.push(user);
              }
          };
          return ProviderData;
      })();
      RongWebIMWidget.ProviderData = ProviderData;
      var ElementStyle = (function () {
          function ElementStyle() {
          }
          return ElementStyle;
      })();
      var WidgetConfig = (function () {
          function WidgetConfig() {
              this.displayConversationList = false;
              this.conversationListPosition = RongWebIMWidget.EnumConversationListPosition.left;
              this.displayMinButton = true;
              this.desktopNotification = false;
              this.reminder = "";
              this.voiceNotification = false;
              this.style = {
                  positionFixed: false,
                  width: 450,
                  height: 470,
                  bottom: 0,
                  right: 0
              };
              this.refershOnlineStateIntercycle = 1000 * 20;
              this.hiddenConversations = [];
              this.__isKefu = false;
          }
          return WidgetConfig;
      })();
      RongWebIMWidget.WidgetConfig = WidgetConfig;
      angular.module("RongWebIMWidget")
          .service("ProviderData", ProviderData)
          .service("WidgetConfig", WidgetConfig);
  })(RongWebIMWidget || (RongWebIMWidget = {}));
  
  angular.module('RongWebIMWidget').run(['$templateCache', function($templateCache) {
    'use strict';
  
    $templateCache.put('./src/ts/conversation/conversation.tpl.html',
      "<div id=rong-conversation class=\"rongcloud-kefuChatBox rongcloud-both rongcloud-am-fade-and-slide-top\" ng-show=showSelf ng-class=\"{'rongcloud-fullScreen':resoures.fullScreen}\"><evaluatedir type=evaluate.type display=evaluate.showSelf confirm=evaluate.onConfirm(data) cancle=evaluate.onCancle()></evaluatedir><div class=rongcloud-kefuChat><div id=header class=\"rongcloud-rong-header rongcloud-blueBg rongcloud-online\"><div class=\"rongcloud-infoBar rongcloud-pull-left\"><div class=rongcloud-infoBarTit><span class=rongcloud-kefuName ng-bind=conversation.title></span></div></div><div class=\"rongcloud-toolBar rongcloud-headBtn rongcloud-pull-right\"><div ng-show=!config.displayConversationList&&config.voiceNotification class=rongcloud-voice ng-class=\"{'rongcloud-voice-mute':!data.voiceSound,'rongcloud-voice-sound':data.voiceSound}\" ng-click=\"data.voiceSound=!data.voiceSound\"></div><a href=javascript:; class=\"rongcloud-kefuChatBoxHide rongcloud-sprite\" style=margin-right:6px ng-show=!config.displayConversationList ng-click=minimize() title=隐藏></a> <a href=javascript:; class=\"rongcloud-kefuChatBoxClose rongcloud-sprite\" ng-click=close() title=结束对话></a></div></div><div class=rongcloud-outlineBox ng-hide=data.connectionState><div class=rongcloud-sprite></div><span>连接断开,请刷新重连</span></div><div id=Messages><div class=rongcloud-emptyBox>暂时没有新消息</div><div class=rongcloud-MessagesInner><div ng-repeat=\"item in messageList\" ng-switch=item.panelType><div class=rongcloud-Messages-date ng-switch-when=104><b>{{item.sentTime|historyTime}}</b></div><div class=rongcloud-Messages-history ng-switch-when=105><b ng-click=getHistory()>查看历史消息</b></div><div class=rongcloud-Messages-history ng-switch-when=106><b ng-click=getMoreMessage()>获取更多消息</b></div><div class=rongcloud-sys-tips ng-switch-when=2><span ng-bind-html=item.content.content|trustHtml></span></div><div class=rongcloud-Message ng-switch-when=1><div class=rongcloud-Messages-unreadLine></div><div><div class=rongcloud-Message-header><img class=\"rongcloud-img rongcloud-u-isActionable rongcloud-Message-avatar rongcloud-avatar\" ng-src={{item.content.userInfo.portraitUri||item.content.userInfo.icon}} err-src=http://7xo1cb.com1.z0.glb.clouddn.com/rongcloudkefu2.png errsrcserasdfasdfasdfa alt=\"\"><div class=\"rongcloud-Message-author rongcloud-clearfix\"><a class=\"rongcloud-author rongcloud-u-isActionable\">{{item.content.userInfo.name}}</a></div></div></div><div class=rongcloud-Message-body ng-switch=item.messageType><textmessage ng-switch-when=TextMessage msg=item.content></textmessage><imagemessage ng-switch-when=ImageMessage msg=item.content></imagemessage><voicemessage ng-switch-when=VoiceMessage msg=item.content></voicemessage><locationmessage ng-switch-when=LocationMessage msg=item.content></locationmessage><richcontentmessage ng-switch-when=RichContentMessage msg=item.content></richcontentmessage></div></div></div></div></div><div id=footer class=rongcloud-rong-footer style=\"display: block\"><div class=rongcloud-footer-con><div class=rongcloud-text-layout><div id=funcPanel class=\"rongcloud-funcPanel rongcloud-robotMode\"><div class=rongcloud-mode1 ng-show=\"_inputPanelState==0\"><div class=rongcloud-MessageForm-tool id=expressionWrap><i class=\"rongcloud-sprite rongcloud-iconfont-smile\" ng-click=\"showemoji=!showemoji\"></i><div class=rongcloud-expressionWrap ng-show=showemoji><i class=rongcloud-arrow></i><emoji ng-repeat=\"item in emojiList\" item=item content=conversation></emoji></div></div><div class=rongcloud-MessageForm-tool><i class=\"rongcloud-sprite rongcloud-iconfont-upload\" id=upload-file style=\"position: relative; z-index: 1\"></i></div></div><div class=rongcloud-mode2 ng-show=\"_inputPanelState==2\"><a ng-click=switchPerson() id=chatSwitch class=rongcloud-chatSwitch>转人工服务</a></div></div><pre id=inputMsg class=\"rongcloud-text rongcloud-grey\" contenteditable contenteditable-dire ng-focus=\"showemoji=fase\" style=\"background-color: rgba(0,0,0,0);color:black\" ctrl-enter-keys fun=send() ctrlenter=false placeholder=请输入文字... ondrop=\"return false\" ng-model=conversation.messageContent></pre></div><div class=rongcloud-powBox><button type=button style=\"background-color: #0099ff\" class=\"rongcloud-rong-btn rongcloud-rong-send-btn\" id=rong-sendBtn ng-click=send()>发送</button></div></div></div></div></div>"
    );
  
  
    $templateCache.put('./src/ts/conversationlist/conversationList.tpl.html',
      "<div id=rong-conversation-list class=\"rongcloud-kefuListBox rongcloud-both\"><div class=rongcloud-kefuList><div class=\"rongcloud-rong-header rongcloud-blueBg\"><div class=\"rongcloud-toolBar rongcloud-headBtn\"><div ng-show=config.voiceNotification class=rongcloud-voice ng-class=\"{'rongcloud-voice-mute':!data.voiceSound,'rongcloud-voice-sound':data.voiceSound}\" ng-click=\"data.voiceSound=!data.voiceSound\"></div><div class=\"rongcloud-sprite rongcloud-people\"></div><span class=rongcloud-recent>最近联系人</span><div class=\"rongcloud-sprite rongcloud-arrow-down\" style=\"cursor: pointer\" ng-click=minbtn()></div></div></div><div class=rongcloud-content><div class=rongcloud-netStatus ng-hide=data.connectionState><div class=rongcloud-sprite></div><span>连接断开,请刷新重连</span></div><div><conversation-item ng-repeat=\"item in conversationListServer._conversationList\" item=item></conversation-item></div></div></div></div>"
    );
  
  
    $templateCache.put('./src/ts/evaluate/evaluate.tpl.html',
      "<div class=rongcloud-layermbox ng-show=display><div class=rongcloud-laymshade></div><div class=rongcloud-layermmain><div class=rongcloud-section><div class=rongcloud-layermchild ng-show=!end><div class=rongcloud-layermcont><div class=rongcloud-type1 ng-show=\"type==1\"><h4>&nbsp;评价客服</h4><div class=rongcloud-layerPanel1><div class=rongcloud-star><span ng-repeat=\"item in stars track by $index\"><span ng-class=\"{'rongcloud-star-on':$index<data.stars,'rongcloud-star-off':$index>=data.stars}\" ng-click=confirm($index+1) ng-mouseenter=mousehover($index+1) ng-mouseleave=mousehover(0)></span></span></div></div></div><div class=rongcloud-type2 ng-show=\"type==2\"><h4>&nbsp;&nbsp;机器人是否解决了您的问题 ？</h4><div class=rongcloud-layerPanel1><a class=\"rongcloud-rong-btn rongcloud-btnY\" ng-class=\"{'rongcloud-cur':data.value===true}\" href=javascript:void(0); ng-click=confirm(true)>是</a> <a class=\"rongcloud-rong-btn rongcloud-btnN\" ng-class=\"{'rongcloud-cur':data.value===false}\" href=javascript:void(0); ng-click=confirm(false)>否</a></div></div><div class=rongcloud-layerPanel2 ng-show=displayDescribe><p>是否有以下情况 ？</p><div class=rongcloud-labels><span ng-repeat=\"item in labels\"><a class=rongcloud-rong-btn ng-class=\"{'rongcloud-cur':item.selected}\" ng-click=\"item.selected=!item.selected\" href=\"\">{{item.display}}</a></span></div><div class=rongcloud-suggestBox><textarea name=\"\" placeholder=欢迎给我们的服务提建议~ ng-model=data.describe></textarea></div><div class=rongcloud-subBox><a class=rongcloud-rong-btn href=\"\" ng-click=commit()>提交评价</a></div></div></div><div class=rongcloud-layermbtn><span ng-click=confirm()>跳过</span><span ng-click=cancle()>取消</span></div></div><div class=\"rongcloud-layermchild rongcloud-feedback\" ng-show=end><div class=rongcloud-layermcont>感谢您的反馈 ^ - ^ ！</div></div></div></div></div>"
    );
  
  
    $templateCache.put('./src/ts/main.tpl.html',
      "<div id=rong-widget-box class=rongcloud-container><div ng-show=main.display><rong-conversation></rong-conversation><rong-conversation-list></rong-conversation-list></div><div id=rong-widget-minbtn class=\"rongcloud-kefuBtnBox rongcloud-blueBg\" ng-show=!main.display&&config.displayMinButton ng-click=showbtn()><a class=rongcloud-kefuBtn href=\"javascript: void(0);\"><div class=\"rongcloud-sprite rongcloud-people\"></div><span class=rongcloud-recent ng-show=\"!data.totalUnreadCount||data.totalUnreadCount==0\">{{config.reminder||\"最近联系人\"}}</span> <span class=rongcloud-recent ng-show=\"data.totalUnreadCount>0\"><span ng-show=twinkle>(有未读消息)</span></span></a></div><div id=rong-widget-minbtn-kefu class=\"rongcloud-kefuBtnBox rongcloud-blueBg\" ng-show=!main.display&&config.displayMinButton ng-click=showbtn()><a class=rongcloud-kefuBtn href=\"javascript: void(0);\"><div class=\"rongcloud-sprite rongcloud-people rongcloud-sprite-kefu\"></div><span class=rongcloud-recent>{{config.reminder||\"联系客服\"}}</span></a></div><audio id=rongcloud-playsound style=\"width: 0px;height: 0px;display: none\" src=\"\" controls></audio></div>"
    );
  
  }]);
  
  /*!
    * $script.js JS loader & dependency manager
    * https://github.com/ded/script.js
    * (c) Dustin Diaz 2014 | License MIT
    */
  (function(e,t){typeof module!="undefined"&&module.exports?module.exports=t():typeof define=="function"&&define.amd?define(t):this[e]=t()})("$script",function(){function p(e,t){for(var n=0,i=e.length;n<i;++n)if(!t(e[n]))return r;return 1}function d(e,t){p(e,function(e){return!t(e)})}function v(e,t,n){function g(e){return e.call?e():u[e]}function y(){if(!--h){u[o]=1,s&&s();for(var e in f)p(e.split("|"),g)&&!d(f[e],g)&&(f[e]=[])}}e=e[i]?e:[e];var r=t&&t.call,s=r?t:n,o=r?e.join(""):t,h=e.length;return setTimeout(function(){d(e,function t(e,n){if(e===null)return y();!n&&!/^https?:\/\//.test(e)&&c&&(e=e.indexOf(".js")===-1?c+e+".js":c+e);if(l[e])return o&&(a[o]=1),l[e]==2?y():setTimeout(function(){t(e,!0)},0);l[e]=1,o&&(a[o]=1),m(e,y)})},0),v}function m(n,r){var i=e.createElement("script"),u;i.onload=i.onerror=i[o]=function(){if(i[s]&&!/^c|loade/.test(i[s])||u)return;i.onload=i[o]=null,u=1,l[n]=2,r()},i.async=1,i.src=h?n+(n.indexOf("?")===-1?"?":"&")+h:n,t.insertBefore(i,t.lastChild)}var e=document,t=e.getElementsByTagName("head")[0],n="string",r=!1,i="push",s="readyState",o="onreadystatechange",u={},a={},f={},l={},c,h;return v.get=m,v.order=function(e,t,n){(function r(i){i=e.shift(),e.length?v(i,r):v(i,t,n)})()},v.path=function(e){c=e},v.urlArgs=function(e){h=e},v.ready=function(e,t,n){e=e[i]?e:[e];var r=[];return!d(e,function(e){u[e]||r[i](e)})&&p(e,function(e){return u[e]})?t():!function(e){f[e]=f[e]||[],f[e][i](t),n&&n(r)}(e.join("|")),v},v.done=function(e){v([null],e)},v})
  
  /*global plupload ,mOxie*/
  /*global ActiveXObject */
  /*exported Qiniu */
  /*exported QiniuJsSDK */
  
  function QiniuJsSDK() {
    var qiniuUploadUrl;
    if (window.location.protocol === 'https:') {
      qiniuUploadUrl = 'https://up.qbox.me';
    } else {
      qiniuUploadUrl = 'http://upload.qiniu.com';
    }
  
    this.detectIEVersion = function() {
      var v = 4,
        div = document.createElement('div'),
        all = div.getElementsByTagName('i');
      while (
        div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->',
        all[0]
      ) {
        v++;
      }
      return v > 4 ? v : false;
    };
  
    this.isImage = function(url) {
      var res, suffix = "";
      var imageSuffixes = ["png", "jpg", "jpeg", "gif", "bmp"];
      var suffixMatch = /\.([a-zA-Z0-9]+)(\?|\@|$)/;
  
      if (!url || !suffixMatch.test(url)) {
        return false;
      }
      res = suffixMatch.exec(url);
      suffix = res[1].toLowerCase();
      for (var i = 0, l = imageSuffixes.length; i < l; i++) {
        if (suffix === imageSuffixes[i]) {
          return true;
        }
      }
      return false;
    };
  
    this.getFileExtension = function(filename) {
      var tempArr = filename.split(".");
      var ext;
      if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) {
        ext = "";
      } else {
        ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case
      }
      return ext;
    };
  
    this.utf8_encode = function(argString) {
      // http://kevin.vanzonneveld.net
      // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
      // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
      // +   improved by: sowberry
      // +    tweaked by: Jack
      // +   bugfixed by: Onno Marsman
      // +   improved by: Yves Sucaet
      // +   bugfixed by: Onno Marsman
      // +   bugfixed by: Ulrich
      // +   bugfixed by: Rafal Kukawski
      // +   improved by: kirilloid
      // +   bugfixed by: kirilloid
      // *     example 1: this.utf8_encode('Kevin van Zonneveld');
      // *     returns 1: 'Kevin van Zonneveld'
  
      if (argString === null || typeof argString === 'undefined') {
        return '';
      }
  
      var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
      var utftext = '',
        start, end, stringl = 0;
  
      start = end = 0;
      stringl = string.length;
      for (var n = 0; n < stringl; n++) {
        var c1 = string.charCodeAt(n);
        var enc = null;
  
        if (c1 < 128) {
          end++;
        } else if (c1 > 127 && c1 < 2048) {
          enc = String.fromCharCode(
            (c1 >> 6) | 192, (c1 & 63) | 128
          );
        } else if (c1 & 0xF800 ^ 0xD800 > 0) {
          enc = String.fromCharCode(
            (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
          );
        } else { // surrogate pairs
          if (c1 & 0xFC00 ^ 0xD800 > 0) {
            throw new RangeError('Unmatched trail surrogate at ' + n);
          }
          var c2 = string.charCodeAt(++n);
          if (c2 & 0xFC00 ^ 0xDC00 > 0) {
            throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
          }
          c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
          enc = String.fromCharCode(
            (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (
              c1 & 63) | 128
          );
        }
        if (enc !== null) {
          if (end > start) {
            utftext += string.slice(start, end);
          }
          utftext += enc;
          start = end = n + 1;
        }
      }
  
      if (end > start) {
        utftext += string.slice(start, stringl);
      }
  
      return utftext;
    };
  
    this.base64_encode = function(data) {
      // http://kevin.vanzonneveld.net
      // +   original by: Tyler Akins (http://rumkin.com)
      // +   improved by: Bayron Guevara
      // +   improved by: Thunder.m
      // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
      // +   bugfixed by: Pellentesque Malesuada
      // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
      // -    depends on: this.utf8_encode
      // *     example 1: this.base64_encode('Kevin van Zonneveld');
      // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
      // mozilla has this native
      // - but breaks in 2.0.0.12!
      //if (typeof this.window['atob'] == 'function') {
      //    return atob(data);
      //}
      var b64 =
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
      var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
        ac = 0,
        enc = '',
        tmp_arr = [];
  
      if (!data) {
        return data;
      }
  
      data = this.utf8_encode(data + '');
  
      do { // pack three octets into four hexets
        o1 = data.charCodeAt(i++);
        o2 = data.charCodeAt(i++);
        o3 = data.charCodeAt(i++);
  
        bits = o1 << 16 | o2 << 8 | o3;
  
        h1 = bits >> 18 & 0x3f;
        h2 = bits >> 12 & 0x3f;
        h3 = bits >> 6 & 0x3f;
        h4 = bits & 0x3f;
  
        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(
          h4);
      } while (i < data.length);
  
      enc = tmp_arr.join('');
  
      switch (data.length % 3) {
        case 1:
          enc = enc.slice(0, -2) + '==';
          break;
        case 2:
          enc = enc.slice(0, -1) + '=';
          break;
      }
  
      return enc;
    };
  
    this.URLSafeBase64Encode = function(v) {
      v = this.base64_encode(v);
      return v.replace(/\//g, '_').replace(/\+/g, '-');
    };
  
    this.createAjax = function(argument) {
      var xmlhttp = {};
      if (window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest();
      } else {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      return xmlhttp;
    };
  
    this.parseJSON = function(data) {
      // Attempt to parse using the native JSON parser first
      if (window.JSON && window.JSON.parse) {
        return window.JSON.parse(data);
      }
  
      if (data === null) {
        return data;
      }
      if (typeof data === "string") {
  
        // Make sure leading/trailing whitespace is removed (IE can't handle it)
        data = this.trim(data);
  
        if (data) {
          // Make sure the incoming data is actual JSON
          // Logic borrowed from http://json.org/json2.js
          if (/^[\],:{}\s]*$/.test(data.replace(
              /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, "@").replace(
              /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
              "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {
  
            return (function() {
              return data;
            })();
          }
        }
      }
    };
  
    this.trim = function(text) {
      return text === null ? "" : text.replace(/^\s+|\s+$/g, '');
    };
  
    //Todo ie7 handler / this.parseJSON bug;
  
    var that = this;
  
    this.uploader = function(op) {
      if (!op.domain) {
        throw 'uptoken_url or domain is required!';
      }
  
      if (!op.browse_button) {
        throw 'browse_button is required!';
      }
  
      var option = {};
  
      var _Error_Handler = op.init && op.init.Error;
      var _FileUploaded_Handler = op.init && op.init.FileUploaded;
  
      op.init.Error = function() {};
      op.init.FileUploaded = function() {};
  
      that.uptoken_url = op.uptoken_url;
      that.token = '';
      that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : '';
      this.domain = op.domain;
      var ctx = '';
      var speedCalInfo = {
        isResumeUpload: false,
        resumeFilesize: 0,
        startTime: '',
        currentTime: ''
      };
  
      var reset_chunk_size = function() {
        var ie = that.detectIEVersion();
        var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size;
        var isSpecialSafari = (mOxie.Env.browser === "Safari" && mOxie.Env.version <=
            5 && mOxie.Env.os === "Windows" && mOxie.Env.osVersion === "7") ||
          (mOxie.Env.browser === "Safari" && mOxie.Env.os === "iOS" && mOxie.Env
            .osVersion === "7");
        if (ie && ie <= 9 && op.chunk_size && op.runtimes.indexOf('flash') >=
          0) {
          //  link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not
          //  when plupload chunk_size setting is't null ,it cause bug in ie8/9  which runs  flash runtimes (not support html5) .
          op.chunk_size = 0;
  
        } else if (isSpecialSafari) {
          // win7 safari / iOS7 safari have bug when in chunk upload mode
          // reset chunk_size to 0
          // disable chunk in special version safari
          op.chunk_size = 0;
        } else {
          BLOCK_BITS = 20;
          MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M
  
          chunk_size = plupload.parseSize(op.chunk_size);
          if (chunk_size > MAX_CHUNK_SIZE) {
            op.chunk_size = MAX_CHUNK_SIZE;
          }
          // qiniu service  max_chunk_size is 4m
          // reset chunk_size to max_chunk_size(4m) when chunk_size > 4m
        }
      };
      reset_chunk_size();
  
      var getUpToken = function() {
        if (!op.uptoken) {
          var ajax = that.createAjax();
          ajax.open('GET', that.uptoken_url, true);
          // ajax.setRequestHeader("If-Modified-Since", "0");
          ajax.onreadystatechange = function() {
            if (ajax.readyState === 4 && ajax.status === 200) {
              var res = that.parseJSON(ajax.responseText);
              that.token = res.uptoken;
            }
          };
          ajax.send();
        } else {
          that.token = op.uptoken;
        }
      };
  
      var getFileKey = function(up, file, func) {
        var key = '',
          unique_names = false;
        if (!op.save_key) {
          unique_names = up.getOption && up.getOption('unique_names');
          unique_names = unique_names || (up.settings && up.settings.unique_names);
          if (unique_names) {
            var ext = that.getFileExtension(file.name);
            key = ext ? file.id + '.' + ext : file.id;
          } else if (typeof func === 'function') {
            key = func(up, file);
          } else {
            key = file.name;
          }
        }
        return key;
      };
  
      plupload.extend(option, op, {
        url: qiniuUploadUrl,
        multipart_params: {
          token: ''
        }
      });
  
      var uploader = new plupload.Uploader(option);
  
      uploader.bind('Init', function(up, params) {
        getUpToken();
      });
      uploader.init();
  
      uploader.bind('FilesAdded', function(up, files) {
        var auto_start = up.getOption && up.getOption('auto_start');
        auto_start = auto_start || (up.settings && up.settings.auto_start);
        if (auto_start) {
          plupload.each(files, function(i, file) {
            up.start();
          });
        }
        up.refresh(); // Reposition Flash/Silverlight
      });
  
      uploader.bind('BeforeUpload', function(up, file) {
        file.speed = file.speed || 0; // add a key named speed for file obj
        ctx = '';
  
        if (op.get_new_uptoken) {
          getUpToken();
        }
  
        var directUpload = function(up, file, func) {
          speedCalInfo.startTime = new Date().getTime();
          var multipart_params_obj;
          if (op.save_key) {
            multipart_params_obj = {
              'token': that.token
            };
          } else {
            multipart_params_obj = {
              'key': getFileKey(up, file, func),
              'token': that.token
            };
          }
  
          var x_vars = op.x_vars;
          if (x_vars !== undefined && typeof x_vars === 'object') {
            for (var x_key in x_vars) {
              if (x_vars.hasOwnProperty(x_key)) {
                if (typeof x_vars[x_key] === 'function') {
                  multipart_params_obj['x:' + x_key] = x_vars[x_key](up,
                    file);
                } else if (typeof x_vars[x_key] !== 'object') {
                  multipart_params_obj['x:' + x_key] = x_vars[x_key];
                }
              }
            }
          }
  
  
          up.setOption({
            'url': qiniuUploadUrl,
            'multipart': true,
            'chunk_size': undefined,
            'multipart_params': multipart_params_obj
          });
        };
  
  
        var chunk_size = up.getOption && up.getOption('chunk_size');
        chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
        if (uploader.runtime === 'html5' && chunk_size) {
          if (file.size < chunk_size) {
            directUpload(up, file, that.key_handler);
          } else {
            var localFileInfo = localStorage.getItem(file.name);
            var blockSize = chunk_size;
            if (localFileInfo) {
              localFileInfo = JSON.parse(localFileInfo);
              var now = (new Date()).getTime();
              var before = localFileInfo.time || 0;
              var aDay = 24 * 60 * 60 * 1000; //  milliseconds
              if (now - before < aDay) {
                if (localFileInfo.percent !== 100) {
                  if (file.size === localFileInfo.total) {
                    // 通过文件名和文件大小匹配，找到对应的 localstorage 信息，恢复进度
                    file.percent = localFileInfo.percent;
                    file.loaded = localFileInfo.offset;
                    ctx = localFileInfo.ctx;
  
                    //  计算速度时，会用到
                    speedCalInfo.isResumeUpload = true;
                    speedCalInfo.resumeFilesize = localFileInfo.offset;
                    if (localFileInfo.offset + blockSize > file.size) {
                      blockSize = file.size - localFileInfo.offset;
                    }
                  } else {
                    localStorage.removeItem(file.name);
                  }
  
                } else {
                  // 进度100%时，删除对应的localStorage，避免 499 bug
                  localStorage.removeItem(file.name);
                }
              } else {
                localStorage.removeItem(file.name);
              }
            }
            speedCalInfo.startTime = new Date().getTime();
            up.setOption({
              'url': qiniuUploadUrl + '/mkblk/' + blockSize,
              'multipart': false,
              'chunk_size': chunk_size,
              'required_features': "chunks",
              'headers': {
                'Authorization': 'UpToken ' + that.token
              },
              'multipart_params': {}
            });
          }
        } else {
          directUpload(up, file, that.key_handler);
        }
      });
  
      uploader.bind('UploadProgress', function(up, file) {
        // 计算速度
  
        speedCalInfo.currentTime = new Date().getTime();
        var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms
        var fileUploaded = file.loaded || 0;
        if (speedCalInfo.isResumeUpload) {
          fileUploaded = file.loaded - speedCalInfo.resumeFilesize;
        }
        file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s
      });
  
      uploader.bind('ChunkUploaded', function(up, file, info) {
        var res = that.parseJSON(info.response);
  
        ctx = ctx ? ctx + ',' + res.ctx : res.ctx;
        var leftSize = info.total - info.offset;
        var chunk_size = up.getOption && up.getOption('chunk_size');
        chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
        if (leftSize < chunk_size) {
          up.setOption({
            'url': qiniuUploadUrl + '/mkblk/' + leftSize
          });
        }
        localStorage.setItem(file.name, JSON.stringify({
          ctx: ctx,
          percent: file.percent,
          total: info.total,
          offset: info.offset,
          time: (new Date()).getTime()
        }));
      });
  
      uploader.bind('Error', (function(_Error_Handler) {
        return function(up, err) {
          var errTip = '';
          var file = err.file;
          if (file) {
            switch (err.code) {
              case plupload.FAILED:
                errTip = '上传失败。请稍后再试。';
                break;
              case plupload.FILE_SIZE_ERROR:
                var max_file_size = up.getOption && up.getOption(
                  'max_file_size');
                max_file_size = max_file_size || (up.settings && up.settings
                  .max_file_size);
                errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。';
                break;
              case plupload.FILE_EXTENSION_ERROR:
                errTip = '文件验证失败。请稍后重试。';
                break;
              case plupload.HTTP_ERROR:
                if (err.response === '') {
                  // Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE
                  errTip = err.message || '未知网络错误。';
                  break;
                }
                var errorObj = that.parseJSON(err.response);
                var errorText = errorObj.error;
                switch (err.status) {
                  case 400:
                    errTip = "请求报文格式错误。";
                    break;
                  case 401:
                    errTip = "客户端认证授权失败。请重试或提交反馈。";
                    break;
                  case 405:
                    errTip = "客户端请求错误。请重试或提交反馈。";
                    break;
                  case 579:
                    errTip = "资源上传成功，但回调失败。";
                    break;
                  case 599:
                    errTip = "网络连接异常。请重试或提交反馈。";
                    break;
                  case 614:
                    errTip = "文件已存在。";
                    try {
                      errorObj = that.parseJSON(errorObj.error);
                      errorText = errorObj.error || 'file exists';
                    } catch (e) {
                      errorText = errorObj.error || 'file exists';
                    }
                    break;
                  case 631:
                    errTip = "指定空间不存在。";
                    break;
                  case 701:
                    errTip = "上传数据块校验出错。请重试或提交反馈。";
                    break;
                  default:
                    errTip = "未知错误。";
                    break;
                }
                errTip = errTip + '(' + err.status + '：' + errorText +
                  ')';
                break;
              case plupload.SECURITY_ERROR:
                errTip = '安全配置错误。请联系网站管理员。';
                break;
              case plupload.GENERIC_ERROR:
                errTip = '上传失败。请稍后再试。';
                break;
              case plupload.IO_ERROR:
                errTip = '上传失败。请稍后再试。';
                break;
              case plupload.INIT_ERROR:
                errTip = '网站配置错误。请联系网站管理员。';
                uploader.destroy();
                break;
              default:
                errTip = err.message + err.details;
                break;
            }
            if (_Error_Handler) {
              _Error_Handler(up, err, errTip);
            }
          }
          up.refresh(); // Reposition Flash/Silverlight
        };
      })(_Error_Handler));
  
      uploader.bind('FileUploaded', (function(_FileUploaded_Handler) {
        return function(up, file, info) {
  
          var last_step = function(up, file, info) {
            if (op.downtoken_url) {
              var ajax_downtoken = that.createAjax();
              ajax_downtoken.open('POST', op.downtoken_url, true);
              ajax_downtoken.setRequestHeader('Content-type',
                'application/x-www-form-urlencoded');
              ajax_downtoken.onreadystatechange = function() {
                if (ajax_downtoken.readyState === 4) {
                  if (ajax_downtoken.status === 200) {
                    var res_downtoken;
                    try {
                      res_downtoken = that.parseJSON(ajax_downtoken
                        .responseText);
                    } catch (e) {
                      throw ('invalid json format');
                    }
                    var info_extended = {};
                    plupload.extend(info_extended, that.parseJSON(
                      info), res_downtoken);
                    if (_FileUploaded_Handler) {
                      _FileUploaded_Handler(up, file, JSON.stringify(
                        info_extended));
                    }
                  } else {
                    uploader.trigger('Error', {
                      status: ajax_downtoken.status,
                      response: ajax_downtoken.responseText,
                      file: file,
                      code: plupload.HTTP_ERROR
                    });
                  }
                }
              };
              ajax_downtoken.send('key=' + that.parseJSON(info).key +
                '&domain=' + op.domain);
            } else if (_FileUploaded_Handler) {
              _FileUploaded_Handler(up, file, info);
            }
          };
          info.response=info.response.replace(/'/g,"\"");
          var res = that.parseJSON(info.response);
          ctx = ctx ? ctx : res.ctx;
          if (ctx) {
            var key = '';
            if (!op.save_key) {
              key = getFileKey(up, file, that.key_handler);
              key = key ? '/key/' + that.URLSafeBase64Encode(key) : '';
            }
  
            var fname = '/fname/' + that.URLSafeBase64Encode(file.name);
  
            var x_vars = op.x_vars,
              x_val = '',
              x_vars_url = '';
            if (x_vars !== undefined && typeof x_vars === 'object') {
              for (var x_key in x_vars) {
                if (x_vars.hasOwnProperty(x_key)) {
                  if (typeof x_vars[x_key] === 'function') {
                    x_val = that.URLSafeBase64Encode(x_vars[x_key](up,
                      file));
                  } else if (typeof x_vars[x_key] !== 'object') {
                    x_val = that.URLSafeBase64Encode(x_vars[x_key]);
                  }
                  x_vars_url += '/x:' + x_key + '/' + x_val;
                }
              }
            }
  
            var url = qiniuUploadUrl + '/mkfile/' + file.size + key +
              fname + x_vars_url;
            var ajax = that.createAjax();
            ajax.open('POST', url, true);
            ajax.setRequestHeader('Content-Type',
              'text/plain;charset=UTF-8');
            ajax.setRequestHeader('Authorization', 'UpToken ' + that.token);
            ajax.onreadystatechange = function() {
              if (ajax.readyState === 4) {
                localStorage.removeItem(file.name);
                if (ajax.status === 200) {
                  var info = ajax.responseText;
                  last_step(up, file, info);
                } else {
                  uploader.trigger('Error', {
                    status: ajax.status,
                    response: ajax.responseText,
                    file: file,
                    code: -200
                  });
                }
              }
            };
            ajax.send(ctx);
          } else {
            last_step(up, file, info.response);
          }
  
        };
      })(_FileUploaded_Handler));
  
      return uploader;
    };
  
    this.getUrl = function(key) {
      if (!key) {
        return false;
      }
      key = encodeURI(key);
      var domain = this.domain;
      if (domain.slice(domain.length - 1) !== '/') {
        domain = domain + '/';
      }
      return domain + key;
    };
  
    this.imageView2 = function(op, key) {
      var mode = op.mode || '',
        w = op.w || '',
        h = op.h || '',
        q = op.q || '',
        format = op.format || '';
      if (!mode) {
        return false;
      }
      if (!w && !h) {
        return false;
      }
  
      var imageUrl = 'imageView2/' + mode;
      imageUrl += w ? '/w/' + w : '';
      imageUrl += h ? '/h/' + h : '';
      imageUrl += q ? '/q/' + q : '';
      imageUrl += format ? '/format/' + format : '';
      if (key) {
        imageUrl = this.getUrl(key) + '?' + imageUrl;
      }
      return imageUrl;
    };
  
  
    this.imageMogr2 = function(op, key) {
      var auto_orient = op['auto-orient'] || '',
        thumbnail = op.thumbnail || '',
        strip = op.strip || '',
        gravity = op.gravity || '',
        crop = op.crop || '',
        quality = op.quality || '',
        rotate = op.rotate || '',
        format = op.format || '',
        blur = op.blur || '';
      //Todo check option
  
      var imageUrl = 'imageMogr2';
  
      imageUrl += auto_orient ? '/auto-orient' : '';
      imageUrl += thumbnail ? '/thumbnail/' + thumbnail : '';
      imageUrl += strip ? '/strip' : '';
      imageUrl += gravity ? '/gravity/' + gravity : '';
      imageUrl += quality ? '/quality/' + quality : '';
      imageUrl += crop ? '/crop/' + crop : '';
      imageUrl += rotate ? '/rotate/' + rotate : '';
      imageUrl += format ? '/format/' + format : '';
      imageUrl += blur ? '/blur/' + blur : '';
  
      if (key) {
        imageUrl = this.getUrl(key) + '?' + imageUrl;
      }
      return imageUrl;
    };
  
    this.watermark = function(op, key) {
  
      var mode = op.mode;
      if (!mode) {
        return false;
      }
  
      var imageUrl = 'watermark/' + mode;
  
      if (mode === 1) {
        var image = op.image || '';
        if (!image) {
          return false;
        }
        imageUrl += image ? '/image/' + this.URLSafeBase64Encode(image) : '';
      } else if (mode === 2) {
        var text = op.text ? op.text : '',
          font = op.font ? op.font : '',
          fontsize = op.fontsize ? op.fontsize : '',
          fill = op.fill ? op.fill : '';
        if (!text) {
          return false;
        }
        imageUrl += text ? '/text/' + this.URLSafeBase64Encode(text) : '';
        imageUrl += font ? '/font/' + this.URLSafeBase64Encode(font) : '';
        imageUrl += fontsize ? '/fontsize/' + fontsize : '';
        imageUrl += fill ? '/fill/' + this.URLSafeBase64Encode(fill) : '';
      } else {
        // Todo mode3
        return false;
      }
  
      var dissolve = op.dissolve || '',
        gravity = op.gravity || '',
        dx = op.dx || '',
        dy = op.dy || '';
  
      imageUrl += dissolve ? '/dissolve/' + dissolve : '';
      imageUrl += gravity ? '/gravity/' + gravity : '';
      imageUrl += dx ? '/dx/' + dx : '';
      imageUrl += dy ? '/dy/' + dy : '';
  
      if (key) {
        imageUrl = this.getUrl(key) + '?' + imageUrl;
      }
      return imageUrl;
  
    };
  
    this.imageInfo = function(key) {
      if (!key) {
        return false;
      }
      var url = this.getUrl(key) + '?imageInfo';
      var xhr = this.createAjax();
      var info;
      var that = this;
      xhr.open('GET', url, false);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
          info = that.parseJSON(xhr.responseText);
        }
      };
      xhr.send();
      return info;
    };
  
  
    this.exif = function(key) {
      if (!key) {
        return false;
      }
      var url = this.getUrl(key) + '?exif';
      var xhr = this.createAjax();
      var info;
      var that = this;
      xhr.open('GET', url, false);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
          info = that.parseJSON(xhr.responseText);
        }
      };
      xhr.send();
      return info;
    };
  
    this.get = function(type, key) {
      if (!key || !type) {
        return false;
      }
      if (type === 'exif') {
        return this.exif(key);
      } else if (type === 'imageInfo') {
        return this.imageInfo(key);
      }
      return false;
    };
  
  
    this.pipeline = function(arr, key) {
  
      var isArray = Object.prototype.toString.call(arr) === '[object Array]';
      var option, errOp, imageUrl = '';
      if (isArray) {
        for (var i = 0, len = arr.length; i < len; i++) {
          option = arr[i];
          if (!option.fop) {
            return false;
          }
          switch (option.fop) {
            case 'watermark':
              imageUrl += this.watermark(option) + '|';
              break;
            case 'imageView2':
              imageUrl += this.imageView2(option) + '|';
              break;
            case 'imageMogr2':
              imageUrl += this.imageMogr2(option) + '|';
              break;
            default:
              errOp = true;
              break;
          }
          if (errOp) {
            return false;
          }
        }
        if (key) {
          imageUrl = this.getUrl(key) + '?' + imageUrl;
          var length = imageUrl.length;
          if (imageUrl.slice(length - 1) === '|') {
            imageUrl = imageUrl.slice(0, length - 1);
          }
        }
        return imageUrl;
      }
      return false;
    };
  
  }
  
  var Qiniu = new QiniuJsSDK();
  