ucservice 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ucservice.umd.js","sources":["webpack://ucservice/webpack/universalModuleDefinition","webpack://ucservice/webpack/bootstrap","webpack://ucservice/./src/css/scooper.video.css?4798","webpack://ucservice/./node_modules/jquery/dist/jquery.js","webpack://ucservice/./node_modules/sdp/sdp.js","webpack://ucservice/./src/scooper.video.js","webpack://ucservice/./node_modules/@soda/get-current-script/index.js","webpack://ucservice/./src/lib/cometd.js","webpack://ucservice/./src/lib/janus.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/utils.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/safari/safari_shim.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/common_shim.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/adapter_factory.js","webpack://ucservice/./node_modules/webrtc-adapter/src/js/adapter_core.js","webpack://ucservice/./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js","webpack://ucservice/./src/net_url/account_net_url.js","webpack://ucservice/./src/net_url/aduio_net_url.js","webpack://ucservice/./src/net_url/aduio_video_net_url.js","webpack://ucservice/./src/net_url/dept_ry_url.js","webpack://ucservice/./src/net_url/dept_url.js","webpack://ucservice/./src/net_url/location_url.js","webpack://ucservice/./src/net_url/meet_url.js","webpack://ucservice/./src/net_url/message_url.js","webpack://ucservice/./src/net_url/record_url.js","webpack://ucservice/./src/net_url/video_net_url.js","webpack://ucservice/./src/config.js","webpack://ucservice/./src/lib/sha256.js","webpack://ucservice/./src/common-request.js","webpack://ucservice/./index.js","webpack://ucservice/./node_modules/@vue/cli-service/lib/commands/build/entry-lib-no-default.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ucservice\"] = factory();\n\telse\n\t\troot[\"ucservice\"] = factory();\n})((typeof self !== 'undefined' ? self : this), function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"fae3\");\n","// extracted by mini-css-extract-plugin","/*!\n * jQuery JavaScript Library v3.6.0\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * Copyright OpenJS Foundation and other contributors\n * Released under the MIT license\n * https://jquery.org/license\n *\n * Date: 2021-03-02T17:08Z\n */\n( function( global, factory ) {\n\n\t\"use strict\";\n\n\tif ( typeof module === \"object\" && typeof module.exports === \"object\" ) {\n\n\t\t// For CommonJS and CommonJS-like environments where a proper `window`\n\t\t// is present, execute the factory and get jQuery.\n\t\t// For environments that do not have a `window` with a `document`\n\t\t// (such as Node.js), expose a factory as module.exports.\n\t\t// This accentuates the need for the creation of a real `window`.\n\t\t// e.g. var jQuery = require(\"jquery\")(window);\n\t\t// See ticket #14549 for more info.\n\t\tmodule.exports = global.document ?\n\t\t\tfactory( global, true ) :\n\t\t\tfunction( w ) {\n\t\t\t\tif ( !w.document ) {\n\t\t\t\t\tthrow new Error( \"jQuery requires a window with a document\" );\n\t\t\t\t}\n\t\t\t\treturn factory( w );\n\t\t\t};\n\t} else {\n\t\tfactory( global );\n\t}\n\n// Pass this if window is not defined yet\n} )( typeof window !== \"undefined\" ? window : this, function( window, noGlobal ) {\n\n// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1\n// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode\n// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common\n// enough that all such attempts are guarded in a try block.\n\"use strict\";\n\nvar arr = [];\n\nvar getProto = Object.getPrototypeOf;\n\nvar slice = arr.slice;\n\nvar flat = arr.flat ? function( array ) {\n\treturn arr.flat.call( array );\n} : function( array ) {\n\treturn arr.concat.apply( [], array );\n};\n\n\nvar push = arr.push;\n\nvar indexOf = arr.indexOf;\n\nvar class2type = {};\n\nvar toString = class2type.toString;\n\nvar hasOwn = class2type.hasOwnProperty;\n\nvar fnToString = hasOwn.toString;\n\nvar ObjectFunctionString = fnToString.call( Object );\n\nvar support = {};\n\nvar isFunction = function isFunction( obj ) {\n\n\t\t// Support: Chrome <=57, Firefox <=52\n\t\t// In some browsers, typeof returns \"function\" for HTML <object> elements\n\t\t// (i.e., `typeof document.createElement( \"object\" ) === \"function\"`).\n\t\t// We don't want to classify *any* DOM node as a function.\n\t\t// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5\n\t\t// Plus for old WebKit, typeof returns \"function\" for HTML collections\n\t\t// (e.g., `typeof document.getElementsByTagName(\"div\") === \"function\"`). (gh-4756)\n\t\treturn typeof obj === \"function\" && typeof obj.nodeType !== \"number\" &&\n\t\t\ttypeof obj.item !== \"function\";\n\t};\n\n\nvar isWindow = function isWindow( obj ) {\n\t\treturn obj != null && obj === obj.window;\n\t};\n\n\nvar document = window.document;\n\n\n\n\tvar preservedScriptAttributes = {\n\t\ttype: true,\n\t\tsrc: true,\n\t\tnonce: true,\n\t\tnoModule: true\n\t};\n\n\tfunction DOMEval( code, node, doc ) {\n\t\tdoc = doc || document;\n\n\t\tvar i, val,\n\t\t\tscript = doc.createElement( \"script\" );\n\n\t\tscript.text = code;\n\t\tif ( node ) {\n\t\t\tfor ( i in preservedScriptAttributes ) {\n\n\t\t\t\t// Support: Firefox 64+, Edge 18+\n\t\t\t\t// Some browsers don't support the \"nonce\" property on scripts.\n\t\t\t\t// On the other hand, just using `getAttribute` is not enough as\n\t\t\t\t// the `nonce` attribute is reset to an empty string whenever it\n\t\t\t\t// becomes browsing-context connected.\n\t\t\t\t// See https://github.com/whatwg/html/issues/2369\n\t\t\t\t// See https://html.spec.whatwg.org/#nonce-attributes\n\t\t\t\t// The `node.getAttribute` check was added for the sake of\n\t\t\t\t// `jQuery.globalEval` so that it can fake a nonce-containing node\n\t\t\t\t// via an object.\n\t\t\t\tval = node[ i ] || node.getAttribute && node.getAttribute( i );\n\t\t\t\tif ( val ) {\n\t\t\t\t\tscript.setAttribute( i, val );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdoc.head.appendChild( script ).parentNode.removeChild( script );\n\t}\n\n\nfunction toType( obj ) {\n\tif ( obj == null ) {\n\t\treturn obj + \"\";\n\t}\n\n\t// Support: Android <=2.3 only (functionish RegExp)\n\treturn typeof obj === \"object\" || typeof obj === \"function\" ?\n\t\tclass2type[ toString.call( obj ) ] || \"object\" :\n\t\ttypeof obj;\n}\n/* global Symbol */\n// Defining this global in .eslintrc.json would create a danger of using the global\n// unguarded in another place, it seems safer to define global only for this module\n\n\n\nvar\n\tversion = \"3.6.0\",\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\t// Need init if jQuery is called (just allow error to be thrown if not included)\n\t\treturn new jQuery.fn.init( selector, context );\n\t};\n\njQuery.fn = jQuery.prototype = {\n\n\t// The current version of jQuery being used\n\tjquery: version,\n\n\tconstructor: jQuery,\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\ttoArray: function() {\n\t\treturn slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\n\t\t// Return all the elements in a clean array\n\t\tif ( num == null ) {\n\t\t\treturn slice.call( this );\n\t\t}\n\n\t\t// Return just the one element from the set\n\t\treturn num < 0 ? this[ num + this.length ] : this[ num ];\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\teach: function( callback ) {\n\t\treturn jQuery.each( this, callback );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map( this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t} ) );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teven: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn ( i + 1 ) % 2;\n\t\t} ) );\n\t},\n\n\todd: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn i % 2;\n\t\t} ) );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor();\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: arr.sort,\n\tsplice: arr.splice\n};\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[ 0 ] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\n\t\t// Skip the boolean and the target\n\t\ttarget = arguments[ i ] || {};\n\t\ti++;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !isFunction( target ) ) {\n\t\ttarget = {};\n\t}\n\n\t// Extend jQuery itself if only one argument is passed\n\tif ( i === length ) {\n\t\ttarget = this;\n\t\ti--;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\n\t\t// Only deal with non-null/undefined values\n\t\tif ( ( options = arguments[ i ] ) != null ) {\n\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent Object.prototype pollution\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( name === \"__proto__\" || target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject( copy ) ||\n\t\t\t\t\t( copyIsArray = Array.isArray( copy ) ) ) ) {\n\t\t\t\t\tsrc = target[ name ];\n\n\t\t\t\t\t// Ensure proper type for the source value\n\t\t\t\t\tif ( copyIsArray && !Array.isArray( src ) ) {\n\t\t\t\t\t\tclone = [];\n\t\t\t\t\t} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {\n\t\t\t\t\t\tclone = {};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src;\n\t\t\t\t\t}\n\t\t\t\t\tcopyIsArray = false;\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend( {\n\n\t// Unique for each copy of jQuery on the page\n\texpando: \"jQuery\" + ( version + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// Assume jQuery is ready without the ready module\n\tisReady: true,\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tnoop: function() {},\n\n\tisPlainObject: function( obj ) {\n\t\tvar proto, Ctor;\n\n\t\t// Detect obvious negatives\n\t\t// Use toString instead of jQuery.type to catch host objects\n\t\tif ( !obj || toString.call( obj ) !== \"[object Object]\" ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tproto = getProto( obj );\n\n\t\t// Objects with no prototype (e.g., `Object.create( null )`) are plain\n\t\tif ( !proto ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Objects with prototype are plain iff they were constructed by a global Object function\n\t\tCtor = hasOwn.call( proto, \"constructor\" ) && proto.constructor;\n\t\treturn typeof Ctor === \"function\" && fnToString.call( Ctor ) === ObjectFunctionString;\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tvar name;\n\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\t// Evaluates a script in a provided context; falls back to the global one\n\t// if not specified.\n\tglobalEval: function( code, options, doc ) {\n\t\tDOMEval( code, { nonce: options && options.nonce }, doc );\n\t},\n\n\teach: function( obj, callback ) {\n\t\tvar length, i = 0;\n\n\t\tif ( isArrayLike( obj ) ) {\n\t\t\tlength = obj.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( i in obj ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArrayLike( Object( arr ) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tpush.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\treturn arr == null ? -1 : indexOf.call( arr, elem, i );\n\t},\n\n\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t// push.apply(_, arraylike) throws on ancient WebKit\n\tmerge: function( first, second ) {\n\t\tvar len = +second.length,\n\t\t\tj = 0,\n\t\t\ti = first.length;\n\n\t\tfor ( ; j < len; j++ ) {\n\t\t\tfirst[ i++ ] = second[ j ];\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, invert ) {\n\t\tvar callbackInverse,\n\t\t\tmatches = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tcallbackExpect = !invert;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tcallbackInverse = !callback( elems[ i ], i );\n\t\t\tif ( callbackInverse !== callbackExpect ) {\n\t\t\t\tmatches.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn matches;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar length, value,\n\t\t\ti = 0,\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their new values\n\t\tif ( isArrayLike( elems ) ) {\n\t\t\tlength = elems.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn flat( ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// jQuery.support is not used in Core but other projects attach their\n\t// properties to it so it needs to exist.\n\tsupport: support\n} );\n\nif ( typeof Symbol === \"function\" ) {\n\tjQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];\n}\n\n// Populate the class2type map\njQuery.each( \"Boolean Number String Function Array Date RegExp Object Error Symbol\".split( \" \" ),\n\tfunction( _i, name ) {\n\t\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n\t} );\n\nfunction isArrayLike( obj ) {\n\n\t// Support: real iOS 8.2 only (not reproducible in simulator)\n\t// `in` check used to prevent JIT error (gh-2145)\n\t// hasOwn isn't used here due to false negatives\n\t// regarding Nodelist length in IE\n\tvar length = !!obj && \"length\" in obj && obj.length,\n\t\ttype = toType( obj );\n\n\tif ( isFunction( obj ) || isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn type === \"array\" || length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj;\n}\nvar Sizzle =\n/*!\n * Sizzle CSS Selector Engine v2.3.6\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://js.foundation/\n *\n * Date: 2021-02-16\n */\n( function( window ) {\nvar i,\n\tsupport,\n\tExpr,\n\tgetText,\n\tisXML,\n\ttokenize,\n\tcompile,\n\tselect,\n\toutermostContext,\n\tsortInput,\n\thasDuplicate,\n\n\t// Local document vars\n\tsetDocument,\n\tdocument,\n\tdocElem,\n\tdocumentIsHTML,\n\trbuggyQSA,\n\trbuggyMatches,\n\tmatches,\n\tcontains,\n\n\t// Instance-specific data\n\texpando = \"sizzle\" + 1 * new Date(),\n\tpreferredDoc = window.document,\n\tdirruns = 0,\n\tdone = 0,\n\tclassCache = createCache(),\n\ttokenCache = createCache(),\n\tcompilerCache = createCache(),\n\tnonnativeSelectorCache = createCache(),\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// Instance methods\n\thasOwn = ( {} ).hasOwnProperty,\n\tarr = [],\n\tpop = arr.pop,\n\tpushNative = arr.push,\n\tpush = arr.push,\n\tslice = arr.slice,\n\n\t// Use a stripped-down indexOf as it's faster than native\n\t// https://jsperf.com/thor-indexof-vs-for/5\n\tindexOf = function( list, elem ) {\n\t\tvar i = 0,\n\t\t\tlen = list.length;\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tif ( list[ i ] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t},\n\n\tbooleans = \"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|\" +\n\t\t\"ismap|loop|multiple|open|readonly|required|scoped\",\n\n\t// Regular expressions\n\n\t// http://www.w3.org/TR/css3-selectors/#whitespace\n\twhitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n\n\t// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram\n\tidentifier = \"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace +\n\t\t\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",\n\n\t// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors\n\tattributes = \"\\\\[\" + whitespace + \"*(\" + identifier + \")(?:\" + whitespace +\n\n\t\t// Operator (capture 2)\n\t\t\"*([*^$|!~]?=)\" + whitespace +\n\n\t\t// \"Attribute values must be CSS identifiers [capture 5]\n\t\t// or strings [capture 3 or capture 4]\"\n\t\t\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\" + identifier + \"))|)\" +\n\t\twhitespace + \"*\\\\]\",\n\n\tpseudos = \":(\" + identifier + \")(?:\\\\((\" +\n\n\t\t// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:\n\t\t// 1. quoted (capture 3; capture 4 or capture 5)\n\t\t\"('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|\" +\n\n\t\t// 2. simple (capture 6)\n\t\t\"((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\" + attributes + \")*)|\" +\n\n\t\t// 3. anything else (capture 2)\n\t\t\".*\" +\n\t\t\")\\\\)|)\",\n\n\t// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n\trwhitespace = new RegExp( whitespace + \"+\", \"g\" ),\n\trtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" +\n\t\twhitespace + \"+$\", \"g\" ),\n\n\trcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n\trcombinators = new RegExp( \"^\" + whitespace + \"*([>+~]|\" + whitespace + \")\" + whitespace +\n\t\t\"*\" ),\n\trdescend = new RegExp( whitespace + \"|>\" ),\n\n\trpseudo = new RegExp( pseudos ),\n\tridentifier = new RegExp( \"^\" + identifier + \"$\" ),\n\n\tmatchExpr = {\n\t\t\"ID\": new RegExp( \"^#(\" + identifier + \")\" ),\n\t\t\"CLASS\": new RegExp( \"^\\\\.(\" + identifier + \")\" ),\n\t\t\"TAG\": new RegExp( \"^(\" + identifier + \"|[*])\" ),\n\t\t\"ATTR\": new RegExp( \"^\" + attributes ),\n\t\t\"PSEUDO\": new RegExp( \"^\" + pseudos ),\n\t\t\"CHILD\": new RegExp( \"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\" +\n\t\t\twhitespace + \"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" +\n\t\t\twhitespace + \"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n\t\t\"bool\": new RegExp( \"^(?:\" + booleans + \")$\", \"i\" ),\n\n\t\t// For use in libraries implementing .is()\n\t\t// We use this for POS matching in `select`\n\t\t\"needsContext\": new RegExp( \"^\" + whitespace +\n\t\t\t\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" + whitespace +\n\t\t\t\"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\", \"i\" )\n\t},\n\n\trhtml = /HTML$/i,\n\trinputs = /^(?:input|select|textarea|button)$/i,\n\trheader = /^h\\d$/i,\n\n\trnative = /^[^{]+\\{\\s*\\[native \\w/,\n\n\t// Easily-parseable/retrievable ID or TAG or CLASS selectors\n\trquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n\n\trsibling = /[+~]/,\n\n\t// CSS escapes\n\t// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters\n\trunescape = new RegExp( \"\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace + \"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\", \"g\" ),\n\tfunescape = function( escape, nonHex ) {\n\t\tvar high = \"0x\" + escape.slice( 1 ) - 0x10000;\n\n\t\treturn nonHex ?\n\n\t\t\t// Strip the backslash prefix from a non-hex escape sequence\n\t\t\tnonHex :\n\n\t\t\t// Replace a hexadecimal escape sequence with the encoded Unicode code point\n\t\t\t// Support: IE <=11+\n\t\t\t// For values outside the Basic Multilingual Plane (BMP), manually construct a\n\t\t\t// surrogate pair\n\t\t\thigh < 0 ?\n\t\t\t\tString.fromCharCode( high + 0x10000 ) :\n\t\t\t\tString.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );\n\t},\n\n\t// CSS string/identifier serialization\n\t// https://drafts.csswg.org/cssom/#common-serializing-idioms\n\trcssescape = /([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,\n\tfcssescape = function( ch, asCodePoint ) {\n\t\tif ( asCodePoint ) {\n\n\t\t\t// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER\n\t\t\tif ( ch === \"\\0\" ) {\n\t\t\t\treturn \"\\uFFFD\";\n\t\t\t}\n\n\t\t\t// Control characters and (dependent upon position) numbers get escaped as code points\n\t\t\treturn ch.slice( 0, -1 ) + \"\\\\\" +\n\t\t\t\tch.charCodeAt( ch.length - 1 ).toString( 16 ) + \" \";\n\t\t}\n\n\t\t// Other potentially-special ASCII characters get backslash-escaped\n\t\treturn \"\\\\\" + ch;\n\t},\n\n\t// Used for iframes\n\t// See setDocument()\n\t// Removing the function wrapper causes a \"Permission Denied\"\n\t// error in IE\n\tunloadHandler = function() {\n\t\tsetDocument();\n\t},\n\n\tinDisabledFieldset = addCombinator(\n\t\tfunction( elem ) {\n\t\t\treturn elem.disabled === true && elem.nodeName.toLowerCase() === \"fieldset\";\n\t\t},\n\t\t{ dir: \"parentNode\", next: \"legend\" }\n\t);\n\n// Optimize for push.apply( _, NodeList )\ntry {\n\tpush.apply(\n\t\t( arr = slice.call( preferredDoc.childNodes ) ),\n\t\tpreferredDoc.childNodes\n\t);\n\n\t// Support: Android<4.0\n\t// Detect silently failing push.apply\n\t// eslint-disable-next-line no-unused-expressions\n\tarr[ preferredDoc.childNodes.length ].nodeType;\n} catch ( e ) {\n\tpush = { apply: arr.length ?\n\n\t\t// Leverage slice if possible\n\t\tfunction( target, els ) {\n\t\t\tpushNative.apply( target, slice.call( els ) );\n\t\t} :\n\n\t\t// Support: IE<9\n\t\t// Otherwise append directly\n\t\tfunction( target, els ) {\n\t\t\tvar j = target.length,\n\t\t\t\ti = 0;\n\n\t\t\t// Can't trust NodeList.length\n\t\t\twhile ( ( target[ j++ ] = els[ i++ ] ) ) {}\n\t\t\ttarget.length = j - 1;\n\t\t}\n\t};\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n\tvar m, i, elem, nid, match, groups, newSelector,\n\t\tnewContext = context && context.ownerDocument,\n\n\t\t// nodeType defaults to 9, since context defaults to document\n\t\tnodeType = context ? context.nodeType : 9;\n\n\tresults = results || [];\n\n\t// Return early from calls with invalid selector or context\n\tif ( typeof selector !== \"string\" || !selector ||\n\t\tnodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {\n\n\t\treturn results;\n\t}\n\n\t// Try to shortcut find operations (as opposed to filters) in HTML documents\n\tif ( !seed ) {\n\t\tsetDocument( context );\n\t\tcontext = context || document;\n\n\t\tif ( documentIsHTML ) {\n\n\t\t\t// If the selector is sufficiently simple, try using a \"get*By*\" DOM method\n\t\t\t// (excepting DocumentFragment context, where the methods don't exist)\n\t\t\tif ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {\n\n\t\t\t\t// ID selector\n\t\t\t\tif ( ( m = match[ 1 ] ) ) {\n\n\t\t\t\t\t// Document context\n\t\t\t\t\tif ( nodeType === 9 ) {\n\t\t\t\t\t\tif ( ( elem = context.getElementById( m ) ) ) {\n\n\t\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === m ) {\n\t\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// Element context\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\tif ( newContext && ( elem = newContext.getElementById( m ) ) &&\n\t\t\t\t\t\t\tcontains( context, elem ) &&\n\t\t\t\t\t\t\telem.id === m ) {\n\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t// Type selector\n\t\t\t\t} else if ( match[ 2 ] ) {\n\t\t\t\t\tpush.apply( results, context.getElementsByTagName( selector ) );\n\t\t\t\t\treturn results;\n\n\t\t\t\t// Class selector\n\t\t\t\t} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&\n\t\t\t\t\tcontext.getElementsByClassName ) {\n\n\t\t\t\t\tpush.apply( results, context.getElementsByClassName( m ) );\n\t\t\t\t\treturn results;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Take advantage of querySelectorAll\n\t\t\tif ( support.qsa &&\n\t\t\t\t!nonnativeSelectorCache[ selector + \" \" ] &&\n\t\t\t\t( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&\n\n\t\t\t\t// Support: IE 8 only\n\t\t\t\t// Exclude object elements\n\t\t\t\t( nodeType !== 1 || context.nodeName.toLowerCase() !== \"object\" ) ) {\n\n\t\t\t\tnewSelector = selector;\n\t\t\t\tnewContext = context;\n\n\t\t\t\t// qSA considers elements outside a scoping root when evaluating child or\n\t\t\t\t// descendant combinators, which is not what we want.\n\t\t\t\t// In such cases, we work around the behavior by prefixing every selector in the\n\t\t\t\t// list with an ID selector referencing the scope context.\n\t\t\t\t// The technique has to be used as well when a leading combinator is used\n\t\t\t\t// as such selectors are not recognized by querySelectorAll.\n\t\t\t\t// Thanks to Andrew Dupont for this technique.\n\t\t\t\tif ( nodeType === 1 &&\n\t\t\t\t\t( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {\n\n\t\t\t\t\t// Expand context for sibling selectors\n\t\t\t\t\tnewContext = rsibling.test( selector ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext;\n\n\t\t\t\t\t// We can use :scope instead of the ID hack if the browser\n\t\t\t\t\t// supports it & if we're not changing the context.\n\t\t\t\t\tif ( newContext !== context || !support.scope ) {\n\n\t\t\t\t\t\t// Capture the context ID, setting it first if necessary\n\t\t\t\t\t\tif ( ( nid = context.getAttribute( \"id\" ) ) ) {\n\t\t\t\t\t\t\tnid = nid.replace( rcssescape, fcssescape );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontext.setAttribute( \"id\", ( nid = expando ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prefix every selector in the list\n\t\t\t\t\tgroups = tokenize( selector );\n\t\t\t\t\ti = groups.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tgroups[ i ] = ( nid ? \"#\" + nid : \":scope\" ) + \" \" +\n\t\t\t\t\t\t\ttoSelector( groups[ i ] );\n\t\t\t\t\t}\n\t\t\t\t\tnewSelector = groups.join( \",\" );\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tpush.apply( results,\n\t\t\t\t\t\tnewContext.querySelectorAll( newSelector )\n\t\t\t\t\t);\n\t\t\t\t\treturn results;\n\t\t\t\t} catch ( qsaError ) {\n\t\t\t\t\tnonnativeSelectorCache( selector, true );\n\t\t\t\t} finally {\n\t\t\t\t\tif ( nid === expando ) {\n\t\t\t\t\t\tcontext.removeAttribute( \"id\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// All others\n\treturn select( selector.replace( rtrim, \"$1\" ), context, results, seed );\n}\n\n/**\n * Create key-value caches of limited size\n * @returns {function(string, object)} Returns the Object data after storing it on itself with\n *\tproperty name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n *\tdeleting the oldest entry\n */\nfunction createCache() {\n\tvar keys = [];\n\n\tfunction cache( key, value ) {\n\n\t\t// Use (key + \" \") to avoid collision with native prototype properties (see Issue #157)\n\t\tif ( keys.push( key + \" \" ) > Expr.cacheLength ) {\n\n\t\t\t// Only keep the most recent entries\n\t\t\tdelete cache[ keys.shift() ];\n\t\t}\n\t\treturn ( cache[ key + \" \" ] = value );\n\t}\n\treturn cache;\n}\n\n/**\n * Mark a function for special use by Sizzle\n * @param {Function} fn The function to mark\n */\nfunction markFunction( fn ) {\n\tfn[ expando ] = true;\n\treturn fn;\n}\n\n/**\n * Support testing using an element\n * @param {Function} fn Passed the created element and returns a boolean result\n */\nfunction assert( fn ) {\n\tvar el = document.createElement( \"fieldset\" );\n\n\ttry {\n\t\treturn !!fn( el );\n\t} catch ( e ) {\n\t\treturn false;\n\t} finally {\n\n\t\t// Remove from its parent by default\n\t\tif ( el.parentNode ) {\n\t\t\tel.parentNode.removeChild( el );\n\t\t}\n\n\t\t// release memory in IE\n\t\tel = null;\n\t}\n}\n\n/**\n * Adds the same handler for all of the specified attrs\n * @param {String} attrs Pipe-separated list of attributes\n * @param {Function} handler The method that will be applied\n */\nfunction addHandle( attrs, handler ) {\n\tvar arr = attrs.split( \"|\" ),\n\t\ti = arr.length;\n\n\twhile ( i-- ) {\n\t\tExpr.attrHandle[ arr[ i ] ] = handler;\n\t}\n}\n\n/**\n * Checks document order of two siblings\n * @param {Element} a\n * @param {Element} b\n * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n */\nfunction siblingCheck( a, b ) {\n\tvar cur = b && a,\n\t\tdiff = cur && a.nodeType === 1 && b.nodeType === 1 &&\n\t\t\ta.sourceIndex - b.sourceIndex;\n\n\t// Use IE sourceIndex if available on both nodes\n\tif ( diff ) {\n\t\treturn diff;\n\t}\n\n\t// Check if b follows a\n\tif ( cur ) {\n\t\twhile ( ( cur = cur.nextSibling ) ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a ? 1 : -1;\n}\n\n/**\n * Returns a function to use in pseudos for input types\n * @param {String} type\n */\nfunction createInputPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn name === \"input\" && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for buttons\n * @param {String} type\n */\nfunction createButtonPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn ( name === \"input\" || name === \"button\" ) && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for :enabled/:disabled\n * @param {Boolean} disabled true for :disabled; false for :enabled\n */\nfunction createDisabledPseudo( disabled ) {\n\n\t// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable\n\treturn function( elem ) {\n\n\t\t// Only certain elements can match :enabled or :disabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled\n\t\tif ( \"form\" in elem ) {\n\n\t\t\t// Check for inherited disabledness on relevant non-disabled elements:\n\t\t\t// * listed form-associated elements in a disabled fieldset\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#category-listed\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled\n\t\t\t// * option elements in a disabled optgroup\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled\n\t\t\t// All such elements have a \"form\" property.\n\t\t\tif ( elem.parentNode && elem.disabled === false ) {\n\n\t\t\t\t// Option elements defer to a parent optgroup if present\n\t\t\t\tif ( \"label\" in elem ) {\n\t\t\t\t\tif ( \"label\" in elem.parentNode ) {\n\t\t\t\t\t\treturn elem.parentNode.disabled === disabled;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn elem.disabled === disabled;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Support: IE 6 - 11\n\t\t\t\t// Use the isDisabled shortcut property to check for disabled fieldset ancestors\n\t\t\t\treturn elem.isDisabled === disabled ||\n\n\t\t\t\t\t// Where there is no isDisabled, check manually\n\t\t\t\t\t/* jshint -W018 */\n\t\t\t\t\telem.isDisabled !== !disabled &&\n\t\t\t\t\tinDisabledFieldset( elem ) === disabled;\n\t\t\t}\n\n\t\t\treturn elem.disabled === disabled;\n\n\t\t// Try to winnow out elements that can't be disabled before trusting the disabled property.\n\t\t// Some victims get caught in our net (label, legend, menu, track), but it shouldn't\n\t\t// even exist on them, let alone have a boolean value.\n\t\t} else if ( \"label\" in elem ) {\n\t\t\treturn elem.disabled === disabled;\n\t\t}\n\n\t\t// Remaining elements are neither :enabled nor :disabled\n\t\treturn false;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for positionals\n * @param {Function} fn\n */\nfunction createPositionalPseudo( fn ) {\n\treturn markFunction( function( argument ) {\n\t\targument = +argument;\n\t\treturn markFunction( function( seed, matches ) {\n\t\t\tvar j,\n\t\t\t\tmatchIndexes = fn( [], seed.length, argument ),\n\t\t\t\ti = matchIndexes.length;\n\n\t\t\t// Match elements found at the specified indexes\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( seed[ ( j = matchIndexes[ i ] ) ] ) {\n\t\t\t\t\tseed[ j ] = !( matches[ j ] = seed[ j ] );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t} );\n}\n\n/**\n * Checks a node for validity as a Sizzle context\n * @param {Element|Object=} context\n * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n */\nfunction testContext( context ) {\n\treturn context && typeof context.getElementsByTagName !== \"undefined\" && context;\n}\n\n// Expose support vars for convenience\nsupport = Sizzle.support = {};\n\n/**\n * Detects XML nodes\n * @param {Element|Object} elem An element or a document\n * @returns {Boolean} True iff elem is a non-HTML XML node\n */\nisXML = Sizzle.isXML = function( elem ) {\n\tvar namespace = elem && elem.namespaceURI,\n\t\tdocElem = elem && ( elem.ownerDocument || elem ).documentElement;\n\n\t// Support: IE <=8\n\t// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes\n\t// https://bugs.jquery.com/ticket/4833\n\treturn !rhtml.test( namespace || docElem && docElem.nodeName || \"HTML\" );\n};\n\n/**\n * Sets document-related variables once based on the current document\n * @param {Element|Object} [doc] An element or document object to use to set the document\n * @returns {Object} Returns the current document\n */\nsetDocument = Sizzle.setDocument = function( node ) {\n\tvar hasCompare, subWindow,\n\t\tdoc = node ? node.ownerDocument || node : preferredDoc;\n\n\t// Return early if doc is invalid or already selected\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {\n\t\treturn document;\n\t}\n\n\t// Update global variables\n\tdocument = doc;\n\tdocElem = document.documentElement;\n\tdocumentIsHTML = !isXML( document );\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+\n\t// Accessing iframe documents after unload throws \"permission denied\" errors (jQuery #13936)\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( preferredDoc != document &&\n\t\t( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {\n\n\t\t// Support: IE 11, Edge\n\t\tif ( subWindow.addEventListener ) {\n\t\t\tsubWindow.addEventListener( \"unload\", unloadHandler, false );\n\n\t\t// Support: IE 9 - 10 only\n\t\t} else if ( subWindow.attachEvent ) {\n\t\t\tsubWindow.attachEvent( \"onunload\", unloadHandler );\n\t\t}\n\t}\n\n\t// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,\n\t// Safari 4 - 5 only, Opera <=11.6 - 12.x only\n\t// IE/Edge & older browsers don't support the :scope pseudo-class.\n\t// Support: Safari 6.0 only\n\t// Safari 6.0 supports :scope but it's an alias of :root there.\n\tsupport.scope = assert( function( el ) {\n\t\tdocElem.appendChild( el ).appendChild( document.createElement( \"div\" ) );\n\t\treturn typeof el.querySelectorAll !== \"undefined\" &&\n\t\t\t!el.querySelectorAll( \":scope fieldset div\" ).length;\n\t} );\n\n\t/* Attributes\n\t---------------------------------------------------------------------- */\n\n\t// Support: IE<8\n\t// Verify that getAttribute really returns attributes and not properties\n\t// (excepting IE8 booleans)\n\tsupport.attributes = assert( function( el ) {\n\t\tel.className = \"i\";\n\t\treturn !el.getAttribute( \"className\" );\n\t} );\n\n\t/* getElement(s)By*\n\t---------------------------------------------------------------------- */\n\n\t// Check if getElementsByTagName(\"*\") returns only elements\n\tsupport.getElementsByTagName = assert( function( el ) {\n\t\tel.appendChild( document.createComment( \"\" ) );\n\t\treturn !el.getElementsByTagName( \"*\" ).length;\n\t} );\n\n\t// Support: IE<9\n\tsupport.getElementsByClassName = rnative.test( document.getElementsByClassName );\n\n\t// Support: IE<10\n\t// Check if getElementById returns elements by name\n\t// The broken getElementById methods don't pick up programmatically-set names,\n\t// so use a roundabout getElementsByName test\n\tsupport.getById = assert( function( el ) {\n\t\tdocElem.appendChild( el ).id = expando;\n\t\treturn !document.getElementsByName || !document.getElementsByName( expando ).length;\n\t} );\n\n\t// ID filter and find\n\tif ( support.getById ) {\n\t\tExpr.filter[ \"ID\" ] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn elem.getAttribute( \"id\" ) === attrId;\n\t\t\t};\n\t\t};\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar elem = context.getElementById( id );\n\t\t\t\treturn elem ? [ elem ] : [];\n\t\t\t}\n\t\t};\n\t} else {\n\t\tExpr.filter[ \"ID\" ] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" &&\n\t\t\t\t\telem.getAttributeNode( \"id\" );\n\t\t\t\treturn node && node.value === attrId;\n\t\t\t};\n\t\t};\n\n\t\t// Support: IE 6 - 7 only\n\t\t// getElementById is not reliable as a find shortcut\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar node, i, elems,\n\t\t\t\t\telem = context.getElementById( id );\n\n\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t// Verify the id attribute\n\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fall back on getElementsByName\n\t\t\t\t\telems = context.getElementsByName( id );\n\t\t\t\t\ti = 0;\n\t\t\t\t\twhile ( ( elem = elems[ i++ ] ) ) {\n\t\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn [];\n\t\t\t}\n\t\t};\n\t}\n\n\t// Tag\n\tExpr.find[ \"TAG\" ] = support.getElementsByTagName ?\n\t\tfunction( tag, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( tag );\n\n\t\t\t// DocumentFragment nodes don't have gEBTN\n\t\t\t} else if ( support.qsa ) {\n\t\t\t\treturn context.querySelectorAll( tag );\n\t\t\t}\n\t\t} :\n\n\t\tfunction( tag, context ) {\n\t\t\tvar elem,\n\t\t\t\ttmp = [],\n\t\t\t\ti = 0,\n\n\t\t\t\t// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too\n\t\t\t\tresults = context.getElementsByTagName( tag );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( tag === \"*\" ) {\n\t\t\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t\treturn results;\n\t\t};\n\n\t// Class\n\tExpr.find[ \"CLASS\" ] = support.getElementsByClassName && function( className, context ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && documentIsHTML ) {\n\t\t\treturn context.getElementsByClassName( className );\n\t\t}\n\t};\n\n\t/* QSA/matchesSelector\n\t---------------------------------------------------------------------- */\n\n\t// QSA and matchesSelector support\n\n\t// matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n\trbuggyMatches = [];\n\n\t// qSa(:focus) reports false when true (Chrome 21)\n\t// We allow this because of a bug in IE8/9 that throws an error\n\t// whenever `document.activeElement` is accessed on an iframe\n\t// So, we allow :focus to pass through QSA all the time to avoid the IE error\n\t// See https://bugs.jquery.com/ticket/13378\n\trbuggyQSA = [];\n\n\tif ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {\n\n\t\t// Build QSA regex\n\t\t// Regex strategy adopted from Diego Perini\n\t\tassert( function( el ) {\n\n\t\t\tvar input;\n\n\t\t\t// Select is set to empty string on purpose\n\t\t\t// This is to test IE's treatment of not explicitly\n\t\t\t// setting a boolean content attribute,\n\t\t\t// since its presence should be enough\n\t\t\t// https://bugs.jquery.com/ticket/12359\n\t\t\tdocElem.appendChild( el ).innerHTML = \"<a id='\" + expando + \"'></a>\" +\n\t\t\t\t\"<select id='\" + expando + \"-\\r\\\\' msallowcapture=''>\" +\n\t\t\t\t\"<option selected=''></option></select>\";\n\n\t\t\t// Support: IE8, Opera 11-12.16\n\t\t\t// Nothing should be selected when empty strings follow ^= or $= or *=\n\t\t\t// The test attribute must be unknown in Opera but \"safe\" for WinRT\n\t\t\t// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section\n\t\t\tif ( el.querySelectorAll( \"[msallowcapture^='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Support: IE8\n\t\t\t// Boolean attributes and \"value\" are not treated correctly\n\t\t\tif ( !el.querySelectorAll( \"[selected]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:value|\" + booleans + \")\" );\n\t\t\t}\n\n\t\t\t// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+\n\t\t\tif ( !el.querySelectorAll( \"[id~=\" + expando + \"-]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"~=\" );\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 15 - 18+\n\t\t\t// IE 11/Edge don't find elements on a `[name='']` query in some cases.\n\t\t\t// Adding a temporary attribute to the document before the selection works\n\t\t\t// around the issue.\n\t\t\t// Interestingly, IE 10 & older don't seem to have the issue.\n\t\t\tinput = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"name\", \"\" );\n\t\t\tel.appendChild( input );\n\t\t\tif ( !el.querySelectorAll( \"[name='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*name\" + whitespace + \"*=\" +\n\t\t\t\t\twhitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Webkit/Opera - :checked should return selected option elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !el.querySelectorAll( \":checked\" ).length ) {\n\t\t\t\trbuggyQSA.push( \":checked\" );\n\t\t\t}\n\n\t\t\t// Support: Safari 8+, iOS 8+\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=136851\n\t\t\t// In-page `selector#id sibling-combinator selector` fails\n\t\t\tif ( !el.querySelectorAll( \"a#\" + expando + \"+*\" ).length ) {\n\t\t\t\trbuggyQSA.push( \".#.+[+~]\" );\n\t\t\t}\n\n\t\t\t// Support: Firefox <=3.6 - 5 only\n\t\t\t// Old Firefox doesn't throw on a badly-escaped identifier.\n\t\t\tel.querySelectorAll( \"\\\\\\f\" );\n\t\t\trbuggyQSA.push( \"[\\\\r\\\\n\\\\f]\" );\n\t\t} );\n\n\t\tassert( function( el ) {\n\t\t\tel.innerHTML = \"<a href='' disabled='disabled'></a>\" +\n\t\t\t\t\"<select disabled='disabled'><option/></select>\";\n\n\t\t\t// Support: Windows 8 Native Apps\n\t\t\t// The type and name attributes are restricted during .innerHTML assignment\n\t\t\tvar input = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"type\", \"hidden\" );\n\t\t\tel.appendChild( input ).setAttribute( \"name\", \"D\" );\n\n\t\t\t// Support: IE8\n\t\t\t// Enforce case-sensitivity of name attribute\n\t\t\tif ( el.querySelectorAll( \"[name=d]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"name\" + whitespace + \"*[*^$|!~]?=\" );\n\t\t\t}\n\n\t\t\t// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( el.querySelectorAll( \":enabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: IE9-11+\n\t\t\t// IE's :disabled selector does not pick up the children of disabled fieldsets\n\t\t\tdocElem.appendChild( el ).disabled = true;\n\t\t\tif ( el.querySelectorAll( \":disabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: Opera 10 - 11 only\n\t\t\t// Opera 10-11 does not throw on post-comma invalid pseudos\n\t\t\tel.querySelectorAll( \"*,:x\" );\n\t\t\trbuggyQSA.push( \",.*:\" );\n\t\t} );\n\t}\n\n\tif ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||\n\t\tdocElem.webkitMatchesSelector ||\n\t\tdocElem.mozMatchesSelector ||\n\t\tdocElem.oMatchesSelector ||\n\t\tdocElem.msMatchesSelector ) ) ) ) {\n\n\t\tassert( function( el ) {\n\n\t\t\t// Check to see if it's possible to do matchesSelector\n\t\t\t// on a disconnected node (IE 9)\n\t\t\tsupport.disconnectedMatch = matches.call( el, \"*\" );\n\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\tmatches.call( el, \"[s!='']:x\" );\n\t\t\trbuggyMatches.push( \"!=\", pseudos );\n\t\t} );\n\t}\n\n\trbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( \"|\" ) );\n\trbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( \"|\" ) );\n\n\t/* Contains\n\t---------------------------------------------------------------------- */\n\thasCompare = rnative.test( docElem.compareDocumentPosition );\n\n\t// Element contains another\n\t// Purposefully self-exclusive\n\t// As in, an element does not contain itself\n\tcontains = hasCompare || rnative.test( docElem.contains ) ?\n\t\tfunction( a, b ) {\n\t\t\tvar adown = a.nodeType === 9 ? a.documentElement : a,\n\t\t\t\tbup = b && b.parentNode;\n\t\t\treturn a === bup || !!( bup && bup.nodeType === 1 && (\n\t\t\t\tadown.contains ?\n\t\t\t\t\tadown.contains( bup ) :\n\t\t\t\t\ta.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16\n\t\t\t) );\n\t\t} :\n\t\tfunction( a, b ) {\n\t\t\tif ( b ) {\n\t\t\t\twhile ( ( b = b.parentNode ) ) {\n\t\t\t\t\tif ( b === a ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t/* Sorting\n\t---------------------------------------------------------------------- */\n\n\t// Document order sorting\n\tsortOrder = hasCompare ?\n\tfunction( a, b ) {\n\n\t\t// Flag for duplicate removal\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Sort on method existence if only one input has compareDocumentPosition\n\t\tvar compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n\t\tif ( compare ) {\n\t\t\treturn compare;\n\t\t}\n\n\t\t// Calculate position if both inputs belong to the same document\n\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t// two documents; shallow comparisons work.\n\t\t// eslint-disable-next-line eqeqeq\n\t\tcompare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?\n\t\t\ta.compareDocumentPosition( b ) :\n\n\t\t\t// Otherwise we know they are disconnected\n\t\t\t1;\n\n\t\t// Disconnected nodes\n\t\tif ( compare & 1 ||\n\t\t\t( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {\n\n\t\t\t// Choose the first element that is related to our preferred document\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( a == document || a.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, a ) ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( b == document || b.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, b ) ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// Maintain original order\n\t\t\treturn sortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\t\t}\n\n\t\treturn compare & 4 ? -1 : 1;\n\t} :\n\tfunction( a, b ) {\n\n\t\t// Exit early if the nodes are identical\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tap = [ a ],\n\t\t\tbp = [ b ];\n\n\t\t// Parentless nodes are either documents or disconnected\n\t\tif ( !aup || !bup ) {\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\treturn a == document ? -1 :\n\t\t\t\tb == document ? 1 :\n\t\t\t\t/* eslint-enable eqeqeq */\n\t\t\t\taup ? -1 :\n\t\t\t\tbup ? 1 :\n\t\t\t\tsortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\n\t\t// If the nodes are siblings, we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\t\t}\n\n\t\t// Otherwise we need full lists of their ancestors for comparison\n\t\tcur = a;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tap.unshift( cur );\n\t\t}\n\t\tcur = b;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tbp.unshift( cur );\n\t\t}\n\n\t\t// Walk down the tree looking for a discrepancy\n\t\twhile ( ap[ i ] === bp[ i ] ) {\n\t\t\ti++;\n\t\t}\n\n\t\treturn i ?\n\n\t\t\t// Do a sibling check if the nodes have a common ancestor\n\t\t\tsiblingCheck( ap[ i ], bp[ i ] ) :\n\n\t\t\t// Otherwise nodes in our document sort first\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\tap[ i ] == preferredDoc ? -1 :\n\t\t\tbp[ i ] == preferredDoc ? 1 :\n\t\t\t/* eslint-enable eqeqeq */\n\t\t\t0;\n\t};\n\n\treturn document;\n};\n\nSizzle.matches = function( expr, elements ) {\n\treturn Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n\tsetDocument( elem );\n\n\tif ( support.matchesSelector && documentIsHTML &&\n\t\t!nonnativeSelectorCache[ expr + \" \" ] &&\n\t\t( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&\n\t\t( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {\n\n\t\ttry {\n\t\t\tvar ret = matches.call( elem, expr );\n\n\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\tif ( ret || support.disconnectedMatch ||\n\n\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t// fragment in IE 9\n\t\t\t\telem.document && elem.document.nodeType !== 11 ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\tnonnativeSelectorCache( expr, true );\n\t\t}\n\t}\n\n\treturn Sizzle( expr, document, null, [ elem ] ).length > 0;\n};\n\nSizzle.contains = function( context, elem ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( context.ownerDocument || context ) != document ) {\n\t\tsetDocument( context );\n\t}\n\treturn contains( context, elem );\n};\n\nSizzle.attr = function( elem, name ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( elem.ownerDocument || elem ) != document ) {\n\t\tsetDocument( elem );\n\t}\n\n\tvar fn = Expr.attrHandle[ name.toLowerCase() ],\n\n\t\t// Don't get fooled by Object.prototype properties (jQuery #13807)\n\t\tval = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?\n\t\t\tfn( elem, name, !documentIsHTML ) :\n\t\t\tundefined;\n\n\treturn val !== undefined ?\n\t\tval :\n\t\tsupport.attributes || !documentIsHTML ?\n\t\t\telem.getAttribute( name ) :\n\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\tval.value :\n\t\t\t\tnull;\n};\n\nSizzle.escape = function( sel ) {\n\treturn ( sel + \"\" ).replace( rcssescape, fcssescape );\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Document sorting and removing duplicates\n * @param {ArrayLike} results\n */\nSizzle.uniqueSort = function( results ) {\n\tvar elem,\n\t\tduplicates = [],\n\t\tj = 0,\n\t\ti = 0;\n\n\t// Unless we *know* we can detect duplicates, assume their presence\n\thasDuplicate = !support.detectDuplicates;\n\tsortInput = !support.sortStable && results.slice( 0 );\n\tresults.sort( sortOrder );\n\n\tif ( hasDuplicate ) {\n\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\tif ( elem === results[ i ] ) {\n\t\t\t\tj = duplicates.push( i );\n\t\t\t}\n\t\t}\n\t\twhile ( j-- ) {\n\t\t\tresults.splice( duplicates[ j ], 1 );\n\t\t}\n\t}\n\n\t// Clear input after sorting to release objects\n\t// See https://github.com/jquery/sizzle/pull/225\n\tsortInput = null;\n\n\treturn results;\n};\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n\tvar node,\n\t\tret = \"\",\n\t\ti = 0,\n\t\tnodeType = elem.nodeType;\n\n\tif ( !nodeType ) {\n\n\t\t// If no nodeType, this is expected to be an array\n\t\twhile ( ( node = elem[ i++ ] ) ) {\n\n\t\t\t// Do not traverse comment nodes\n\t\t\tret += getText( node );\n\t\t}\n\t} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n\n\t\t// Use textContent for elements\n\t\t// innerText usage removed for consistency of new lines (jQuery #11153)\n\t\tif ( typeof elem.textContent === \"string\" ) {\n\t\t\treturn elem.textContent;\n\t\t} else {\n\n\t\t\t// Traverse its children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tret += getText( elem );\n\t\t\t}\n\t\t}\n\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\treturn elem.nodeValue;\n\t}\n\n\t// Do not include comment or processing instruction nodes\n\n\treturn ret;\n};\n\nExpr = Sizzle.selectors = {\n\n\t// Can be adjusted by the user\n\tcacheLength: 50,\n\n\tcreatePseudo: markFunction,\n\n\tmatch: matchExpr,\n\n\tattrHandle: {},\n\n\tfind: {},\n\n\trelative: {\n\t\t\">\": { dir: \"parentNode\", first: true },\n\t\t\" \": { dir: \"parentNode\" },\n\t\t\"+\": { dir: \"previousSibling\", first: true },\n\t\t\"~\": { dir: \"previousSibling\" }\n\t},\n\n\tpreFilter: {\n\t\t\"ATTR\": function( match ) {\n\t\t\tmatch[ 1 ] = match[ 1 ].replace( runescape, funescape );\n\n\t\t\t// Move the given value to match[3] whether quoted or unquoted\n\t\t\tmatch[ 3 ] = ( match[ 3 ] || match[ 4 ] ||\n\t\t\t\tmatch[ 5 ] || \"\" ).replace( runescape, funescape );\n\n\t\t\tif ( match[ 2 ] === \"~=\" ) {\n\t\t\t\tmatch[ 3 ] = \" \" + match[ 3 ] + \" \";\n\t\t\t}\n\n\t\t\treturn match.slice( 0, 4 );\n\t\t},\n\n\t\t\"CHILD\": function( match ) {\n\n\t\t\t/* matches from matchExpr[\"CHILD\"]\n\t\t\t\t1 type (only|nth|...)\n\t\t\t\t2 what (child|of-type)\n\t\t\t\t3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n\t\t\t\t4 xn-component of xn+y argument ([+-]?\\d*n|)\n\t\t\t\t5 sign of xn-component\n\t\t\t\t6 x of xn-component\n\t\t\t\t7 sign of y-component\n\t\t\t\t8 y of y-component\n\t\t\t*/\n\t\t\tmatch[ 1 ] = match[ 1 ].toLowerCase();\n\n\t\t\tif ( match[ 1 ].slice( 0, 3 ) === \"nth\" ) {\n\n\t\t\t\t// nth-* requires argument\n\t\t\t\tif ( !match[ 3 ] ) {\n\t\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t\t}\n\n\t\t\t\t// numeric x and y parameters for Expr.filter.CHILD\n\t\t\t\t// remember that false/true cast respectively to 0/1\n\t\t\t\tmatch[ 4 ] = +( match[ 4 ] ?\n\t\t\t\t\tmatch[ 5 ] + ( match[ 6 ] || 1 ) :\n\t\t\t\t\t2 * ( match[ 3 ] === \"even\" || match[ 3 ] === \"odd\" ) );\n\t\t\t\tmatch[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === \"odd\" );\n\n\t\t\t\t// other types prohibit arguments\n\t\t\t} else if ( match[ 3 ] ) {\n\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\t\"PSEUDO\": function( match ) {\n\t\t\tvar excess,\n\t\t\t\tunquoted = !match[ 6 ] && match[ 2 ];\n\n\t\t\tif ( matchExpr[ \"CHILD\" ].test( match[ 0 ] ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Accept quoted arguments as-is\n\t\t\tif ( match[ 3 ] ) {\n\t\t\t\tmatch[ 2 ] = match[ 4 ] || match[ 5 ] || \"\";\n\n\t\t\t// Strip excess characters from unquoted arguments\n\t\t\t} else if ( unquoted && rpseudo.test( unquoted ) &&\n\n\t\t\t\t// Get excess from tokenize (recursively)\n\t\t\t\t( excess = tokenize( unquoted, true ) ) &&\n\n\t\t\t\t// advance to the next closing parenthesis\n\t\t\t\t( excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length ) ) {\n\n\t\t\t\t// excess is a negative index\n\t\t\t\tmatch[ 0 ] = match[ 0 ].slice( 0, excess );\n\t\t\t\tmatch[ 2 ] = unquoted.slice( 0, excess );\n\t\t\t}\n\n\t\t\t// Return only captures needed by the pseudo filter method (type and argument)\n\t\t\treturn match.slice( 0, 3 );\n\t\t}\n\t},\n\n\tfilter: {\n\n\t\t\"TAG\": function( nodeNameSelector ) {\n\t\t\tvar nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn nodeNameSelector === \"*\" ?\n\t\t\t\tfunction() {\n\t\t\t\t\treturn true;\n\t\t\t\t} :\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n\t\t\t\t};\n\t\t},\n\n\t\t\"CLASS\": function( className ) {\n\t\t\tvar pattern = classCache[ className + \" \" ];\n\n\t\t\treturn pattern ||\n\t\t\t\t( pattern = new RegExp( \"(^|\" + whitespace +\n\t\t\t\t\t\")\" + className + \"(\" + whitespace + \"|$)\" ) ) && classCache(\n\t\t\t\t\t\tclassName, function( elem ) {\n\t\t\t\t\t\t\treturn pattern.test(\n\t\t\t\t\t\t\t\ttypeof elem.className === \"string\" && elem.className ||\n\t\t\t\t\t\t\t\ttypeof elem.getAttribute !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\telem.getAttribute( \"class\" ) ||\n\t\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t},\n\n\t\t\"ATTR\": function( name, operator, check ) {\n\t\t\treturn function( elem ) {\n\t\t\t\tvar result = Sizzle.attr( elem, name );\n\n\t\t\t\tif ( result == null ) {\n\t\t\t\t\treturn operator === \"!=\";\n\t\t\t\t}\n\t\t\t\tif ( !operator ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tresult += \"\";\n\n\t\t\t\t/* eslint-disable max-len */\n\n\t\t\t\treturn operator === \"=\" ? result === check :\n\t\t\t\t\toperator === \"!=\" ? result !== check :\n\t\t\t\t\toperator === \"^=\" ? check && result.indexOf( check ) === 0 :\n\t\t\t\t\toperator === \"*=\" ? check && result.indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"$=\" ? check && result.slice( -check.length ) === check :\n\t\t\t\t\toperator === \"~=\" ? ( \" \" + result.replace( rwhitespace, \" \" ) + \" \" ).indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"|=\" ? result === check || result.slice( 0, check.length + 1 ) === check + \"-\" :\n\t\t\t\t\tfalse;\n\t\t\t\t/* eslint-enable max-len */\n\n\t\t\t};\n\t\t},\n\n\t\t\"CHILD\": function( type, what, _argument, first, last ) {\n\t\t\tvar simple = type.slice( 0, 3 ) !== \"nth\",\n\t\t\t\tforward = type.slice( -4 ) !== \"last\",\n\t\t\t\tofType = what === \"of-type\";\n\n\t\t\treturn first === 1 && last === 0 ?\n\n\t\t\t\t// Shortcut for :nth-*(n)\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn !!elem.parentNode;\n\t\t\t\t} :\n\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tvar cache, uniqueCache, outerCache, node, nodeIndex, start,\n\t\t\t\t\t\tdir = simple !== forward ? \"nextSibling\" : \"previousSibling\",\n\t\t\t\t\t\tparent = elem.parentNode,\n\t\t\t\t\t\tname = ofType && elem.nodeName.toLowerCase(),\n\t\t\t\t\t\tuseCache = !xml && !ofType,\n\t\t\t\t\t\tdiff = false;\n\n\t\t\t\t\tif ( parent ) {\n\n\t\t\t\t\t\t// :(first|last|only)-(child|of-type)\n\t\t\t\t\t\tif ( simple ) {\n\t\t\t\t\t\t\twhile ( dir ) {\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\twhile ( ( node = node[ dir ] ) ) {\n\t\t\t\t\t\t\t\t\tif ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) {\n\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Reverse direction for :only-* (if we haven't yet done so)\n\t\t\t\t\t\t\t\tstart = dir = type === \"only\" && !start && \"nextSibling\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstart = [ forward ? parent.firstChild : parent.lastChild ];\n\n\t\t\t\t\t\t// non-xml :nth-child(...) stores cache data on `parent`\n\t\t\t\t\t\tif ( forward && useCache ) {\n\n\t\t\t\t\t\t\t// Seek `elem` from a previously-cached index\n\n\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\tnode = parent;\n\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\tdiff = nodeIndex && cache[ 2 ];\n\t\t\t\t\t\t\tnode = nodeIndex && parent.childNodes[ nodeIndex ];\n\n\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\n\t\t\t\t\t\t\t\t// Fallback to seeking `elem` from the start\n\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t// When found, cache indexes on `parent` and break\n\t\t\t\t\t\t\t\tif ( node.nodeType === 1 && ++diff && node === elem ) {\n\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, nodeIndex, diff ];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Use previously-cached element index if available\n\t\t\t\t\t\t\tif ( useCache ) {\n\n\t\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\t\tdiff = nodeIndex;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// xml :nth-child(...)\n\t\t\t\t\t\t\t// or :nth-last-child(...) or :nth(-last)?-of-type(...)\n\t\t\t\t\t\t\tif ( diff === false ) {\n\n\t\t\t\t\t\t\t\t// Use the same loop as above to seek `elem` from the start\n\t\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\t\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t\tif ( ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) &&\n\t\t\t\t\t\t\t\t\t\t++diff ) {\n\n\t\t\t\t\t\t\t\t\t\t// Cache the index of each encountered element\n\t\t\t\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t\t\t\touterCache = node[ expando ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, diff ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif ( node === elem ) {\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Incorporate the offset, then check against cycle size\n\t\t\t\t\t\tdiff -= last;\n\t\t\t\t\t\treturn diff === first || ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t},\n\n\t\t\"PSEUDO\": function( pseudo, argument ) {\n\n\t\t\t// pseudo-class names are case-insensitive\n\t\t\t// http://www.w3.org/TR/selectors/#pseudo-classes\n\t\t\t// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n\t\t\t// Remember that setFilters inherits from pseudos\n\t\t\tvar args,\n\t\t\t\tfn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n\t\t\t\t\tSizzle.error( \"unsupported pseudo: \" + pseudo );\n\n\t\t\t// The user may use createPseudo to indicate that\n\t\t\t// arguments are needed to create the filter function\n\t\t\t// just as Sizzle does\n\t\t\tif ( fn[ expando ] ) {\n\t\t\t\treturn fn( argument );\n\t\t\t}\n\n\t\t\t// But maintain support for old signatures\n\t\t\tif ( fn.length > 1 ) {\n\t\t\t\targs = [ pseudo, pseudo, \"\", argument ];\n\t\t\t\treturn Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n\t\t\t\t\tmarkFunction( function( seed, matches ) {\n\t\t\t\t\t\tvar idx,\n\t\t\t\t\t\t\tmatched = fn( seed, argument ),\n\t\t\t\t\t\t\ti = matched.length;\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tidx = indexOf( seed, matched[ i ] );\n\t\t\t\t\t\t\tseed[ idx ] = !( matches[ idx ] = matched[ i ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} ) :\n\t\t\t\t\tfunction( elem ) {\n\t\t\t\t\t\treturn fn( elem, 0, args );\n\t\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn fn;\n\t\t}\n\t},\n\n\tpseudos: {\n\n\t\t// Potentially complex pseudos\n\t\t\"not\": markFunction( function( selector ) {\n\n\t\t\t// Trim the selector passed to compile\n\t\t\t// to avoid treating leading and trailing\n\t\t\t// spaces as combinators\n\t\t\tvar input = [],\n\t\t\t\tresults = [],\n\t\t\t\tmatcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n\t\t\treturn matcher[ expando ] ?\n\t\t\t\tmarkFunction( function( seed, matches, _context, xml ) {\n\t\t\t\t\tvar elem,\n\t\t\t\t\t\tunmatched = matcher( seed, null, xml, [] ),\n\t\t\t\t\t\ti = seed.length;\n\n\t\t\t\t\t// Match elements unmatched by `matcher`\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\t\t\t\t\tseed[ i ] = !( matches[ i ] = elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} ) :\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tinput[ 0 ] = elem;\n\t\t\t\t\tmatcher( input, null, xml, results );\n\n\t\t\t\t\t// Don't keep the element (issue #299)\n\t\t\t\t\tinput[ 0 ] = null;\n\t\t\t\t\treturn !results.pop();\n\t\t\t\t};\n\t\t} ),\n\n\t\t\"has\": markFunction( function( selector ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn Sizzle( selector, elem ).length > 0;\n\t\t\t};\n\t\t} ),\n\n\t\t\"contains\": markFunction( function( text ) {\n\t\t\ttext = text.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;\n\t\t\t};\n\t\t} ),\n\n\t\t// \"Whether an element is represented by a :lang() selector\n\t\t// is based solely on the element's language value\n\t\t// being equal to the identifier C,\n\t\t// or beginning with the identifier C immediately followed by \"-\".\n\t\t// The matching of C against the element's language value is performed case-insensitively.\n\t\t// The identifier C does not have to be a valid language name.\"\n\t\t// http://www.w3.org/TR/selectors/#lang-pseudo\n\t\t\"lang\": markFunction( function( lang ) {\n\n\t\t\t// lang value must be a valid identifier\n\t\t\tif ( !ridentifier.test( lang || \"\" ) ) {\n\t\t\t\tSizzle.error( \"unsupported lang: \" + lang );\n\t\t\t}\n\t\t\tlang = lang.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn function( elem ) {\n\t\t\t\tvar elemLang;\n\t\t\t\tdo {\n\t\t\t\t\tif ( ( elemLang = documentIsHTML ?\n\t\t\t\t\t\telem.lang :\n\t\t\t\t\t\telem.getAttribute( \"xml:lang\" ) || elem.getAttribute( \"lang\" ) ) ) {\n\n\t\t\t\t\t\telemLang = elemLang.toLowerCase();\n\t\t\t\t\t\treturn elemLang === lang || elemLang.indexOf( lang + \"-\" ) === 0;\n\t\t\t\t\t}\n\t\t\t\t} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );\n\t\t\t\treturn false;\n\t\t\t};\n\t\t} ),\n\n\t\t// Miscellaneous\n\t\t\"target\": function( elem ) {\n\t\t\tvar hash = window.location && window.location.hash;\n\t\t\treturn hash && hash.slice( 1 ) === elem.id;\n\t\t},\n\n\t\t\"root\": function( elem ) {\n\t\t\treturn elem === docElem;\n\t\t},\n\n\t\t\"focus\": function( elem ) {\n\t\t\treturn elem === document.activeElement &&\n\t\t\t\t( !document.hasFocus || document.hasFocus() ) &&\n\t\t\t\t!!( elem.type || elem.href || ~elem.tabIndex );\n\t\t},\n\n\t\t// Boolean properties\n\t\t\"enabled\": createDisabledPseudo( false ),\n\t\t\"disabled\": createDisabledPseudo( true ),\n\n\t\t\"checked\": function( elem ) {\n\n\t\t\t// In CSS3, :checked should return both checked and selected elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\tvar nodeName = elem.nodeName.toLowerCase();\n\t\t\treturn ( nodeName === \"input\" && !!elem.checked ) ||\n\t\t\t\t( nodeName === \"option\" && !!elem.selected );\n\t\t},\n\n\t\t\"selected\": function( elem ) {\n\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\t// eslint-disable-next-line no-unused-expressions\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\t// Contents\n\t\t\"empty\": function( elem ) {\n\n\t\t\t// http://www.w3.org/TR/selectors/#empty-pseudo\n\t\t\t// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),\n\t\t\t// but not by others (comment: 8; processing instruction: 7; etc.)\n\t\t\t// nodeType < 6 works because attributes (2) do not appear as children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tif ( elem.nodeType < 6 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t\"parent\": function( elem ) {\n\t\t\treturn !Expr.pseudos[ \"empty\" ]( elem );\n\t\t},\n\n\t\t// Element/input types\n\t\t\"header\": function( elem ) {\n\t\t\treturn rheader.test( elem.nodeName );\n\t\t},\n\n\t\t\"input\": function( elem ) {\n\t\t\treturn rinputs.test( elem.nodeName );\n\t\t},\n\n\t\t\"button\": function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && elem.type === \"button\" || name === \"button\";\n\t\t},\n\n\t\t\"text\": function( elem ) {\n\t\t\tvar attr;\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" &&\n\t\t\t\telem.type === \"text\" &&\n\n\t\t\t\t// Support: IE<8\n\t\t\t\t// New HTML5 attribute values (e.g., \"search\") appear with elem.type === \"text\"\n\t\t\t\t( ( attr = elem.getAttribute( \"type\" ) ) == null ||\n\t\t\t\t\tattr.toLowerCase() === \"text\" );\n\t\t},\n\n\t\t// Position-in-collection\n\t\t\"first\": createPositionalPseudo( function() {\n\t\t\treturn [ 0 ];\n\t\t} ),\n\n\t\t\"last\": createPositionalPseudo( function( _matchIndexes, length ) {\n\t\t\treturn [ length - 1 ];\n\t\t} ),\n\n\t\t\"eq\": createPositionalPseudo( function( _matchIndexes, length, argument ) {\n\t\t\treturn [ argument < 0 ? argument + length : argument ];\n\t\t} ),\n\n\t\t\"even\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"odd\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 1;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"lt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ?\n\t\t\t\targument + length :\n\t\t\t\targument > length ?\n\t\t\t\t\tlength :\n\t\t\t\t\targument;\n\t\t\tfor ( ; --i >= 0; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"gt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; ++i < length; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} )\n\t}\n};\n\nExpr.pseudos[ \"nth\" ] = Expr.pseudos[ \"eq\" ];\n\n// Add button/input type pseudos\nfor ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {\n\tExpr.pseudos[ i ] = createInputPseudo( i );\n}\nfor ( i in { submit: true, reset: true } ) {\n\tExpr.pseudos[ i ] = createButtonPseudo( i );\n}\n\n// Easy API for creating new setFilters\nfunction setFilters() {}\nsetFilters.prototype = Expr.filters = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\ntokenize = Sizzle.tokenize = function( selector, parseOnly ) {\n\tvar matched, match, tokens, type,\n\t\tsoFar, groups, preFilters,\n\t\tcached = tokenCache[ selector + \" \" ];\n\n\tif ( cached ) {\n\t\treturn parseOnly ? 0 : cached.slice( 0 );\n\t}\n\n\tsoFar = selector;\n\tgroups = [];\n\tpreFilters = Expr.preFilter;\n\n\twhile ( soFar ) {\n\n\t\t// Comma and first run\n\t\tif ( !matched || ( match = rcomma.exec( soFar ) ) ) {\n\t\t\tif ( match ) {\n\n\t\t\t\t// Don't consume trailing commas as valid\n\t\t\t\tsoFar = soFar.slice( match[ 0 ].length ) || soFar;\n\t\t\t}\n\t\t\tgroups.push( ( tokens = [] ) );\n\t\t}\n\n\t\tmatched = false;\n\n\t\t// Combinators\n\t\tif ( ( match = rcombinators.exec( soFar ) ) ) {\n\t\t\tmatched = match.shift();\n\t\t\ttokens.push( {\n\t\t\t\tvalue: matched,\n\n\t\t\t\t// Cast descendant combinators to space\n\t\t\t\ttype: match[ 0 ].replace( rtrim, \" \" )\n\t\t\t} );\n\t\t\tsoFar = soFar.slice( matched.length );\n\t\t}\n\n\t\t// Filters\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||\n\t\t\t\t( match = preFilters[ type ]( match ) ) ) ) {\n\t\t\t\tmatched = match.shift();\n\t\t\t\ttokens.push( {\n\t\t\t\t\tvalue: matched,\n\t\t\t\t\ttype: type,\n\t\t\t\t\tmatches: match\n\t\t\t\t} );\n\t\t\t\tsoFar = soFar.slice( matched.length );\n\t\t\t}\n\t\t}\n\n\t\tif ( !matched ) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Return the length of the invalid excess\n\t// if we're just parsing\n\t// Otherwise, throw an error or return tokens\n\treturn parseOnly ?\n\t\tsoFar.length :\n\t\tsoFar ?\n\t\t\tSizzle.error( selector ) :\n\n\t\t\t// Cache the tokens\n\t\t\ttokenCache( selector, groups ).slice( 0 );\n};\n\nfunction toSelector( tokens ) {\n\tvar i = 0,\n\t\tlen = tokens.length,\n\t\tselector = \"\";\n\tfor ( ; i < len; i++ ) {\n\t\tselector += tokens[ i ].value;\n\t}\n\treturn selector;\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n\tvar dir = combinator.dir,\n\t\tskip = combinator.next,\n\t\tkey = skip || dir,\n\t\tcheckNonElements = base && key === \"parentNode\",\n\t\tdoneName = done++;\n\n\treturn combinator.first ?\n\n\t\t// Check against closest ancestor/preceding element\n\t\tfunction( elem, context, xml ) {\n\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\treturn matcher( elem, context, xml );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} :\n\n\t\t// Check against all ancestor/preceding elements\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar oldCache, uniqueCache, outerCache,\n\t\t\t\tnewCache = [ dirruns, doneName ];\n\n\t\t\t// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching\n\t\t\tif ( xml ) {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\tif ( matcher( elem, context, xml ) ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\touterCache = elem[ expando ] || ( elem[ expando ] = {} );\n\n\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\tuniqueCache = outerCache[ elem.uniqueID ] ||\n\t\t\t\t\t\t\t( outerCache[ elem.uniqueID ] = {} );\n\n\t\t\t\t\t\tif ( skip && skip === elem.nodeName.toLowerCase() ) {\n\t\t\t\t\t\t\telem = elem[ dir ] || elem;\n\t\t\t\t\t\t} else if ( ( oldCache = uniqueCache[ key ] ) &&\n\t\t\t\t\t\t\toldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {\n\n\t\t\t\t\t\t\t// Assign to newCache so results back-propagate to previous elements\n\t\t\t\t\t\t\treturn ( newCache[ 2 ] = oldCache[ 2 ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Reuse newcache so results back-propagate to previous elements\n\t\t\t\t\t\t\tuniqueCache[ key ] = newCache;\n\n\t\t\t\t\t\t\t// A match means we're done; a fail means we have to keep checking\n\t\t\t\t\t\t\tif ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n}\n\nfunction elementMatcher( matchers ) {\n\treturn matchers.length > 1 ?\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar i = matchers.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( !matchers[ i ]( elem, context, xml ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} :\n\t\tmatchers[ 0 ];\n}\n\nfunction multipleContexts( selector, contexts, results ) {\n\tvar i = 0,\n\t\tlen = contexts.length;\n\tfor ( ; i < len; i++ ) {\n\t\tSizzle( selector, contexts[ i ], results );\n\t}\n\treturn results;\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n\tvar elem,\n\t\tnewUnmatched = [],\n\t\ti = 0,\n\t\tlen = unmatched.length,\n\t\tmapped = map != null;\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\tif ( !filter || filter( elem, context, xml ) ) {\n\t\t\t\tnewUnmatched.push( elem );\n\t\t\t\tif ( mapped ) {\n\t\t\t\t\tmap.push( i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n\tif ( postFilter && !postFilter[ expando ] ) {\n\t\tpostFilter = setMatcher( postFilter );\n\t}\n\tif ( postFinder && !postFinder[ expando ] ) {\n\t\tpostFinder = setMatcher( postFinder, postSelector );\n\t}\n\treturn markFunction( function( seed, results, context, xml ) {\n\t\tvar temp, i, elem,\n\t\t\tpreMap = [],\n\t\t\tpostMap = [],\n\t\t\tpreexisting = results.length,\n\n\t\t\t// Get initial elements from seed or context\n\t\t\telems = seed || multipleContexts(\n\t\t\t\tselector || \"*\",\n\t\t\t\tcontext.nodeType ? [ context ] : context,\n\t\t\t\t[]\n\t\t\t),\n\n\t\t\t// Prefilter to get matcher input, preserving a map for seed-results synchronization\n\t\t\tmatcherIn = preFilter && ( seed || !selector ) ?\n\t\t\t\tcondense( elems, preMap, preFilter, context, xml ) :\n\t\t\t\telems,\n\n\t\t\tmatcherOut = matcher ?\n\n\t\t\t\t// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n\t\t\t\tpostFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n\t\t\t\t\t// ...intermediate processing is necessary\n\t\t\t\t\t[] :\n\n\t\t\t\t\t// ...otherwise use results directly\n\t\t\t\t\tresults :\n\t\t\t\tmatcherIn;\n\n\t\t// Find primary matches\n\t\tif ( matcher ) {\n\t\t\tmatcher( matcherIn, matcherOut, context, xml );\n\t\t}\n\n\t\t// Apply postFilter\n\t\tif ( postFilter ) {\n\t\t\ttemp = condense( matcherOut, postMap );\n\t\t\tpostFilter( temp, [], context, xml );\n\n\t\t\t// Un-match failing elements by moving them back to matcherIn\n\t\t\ti = temp.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( ( elem = temp[ i ] ) ) {\n\t\t\t\t\tmatcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( seed ) {\n\t\t\tif ( postFinder || preFilter ) {\n\t\t\t\tif ( postFinder ) {\n\n\t\t\t\t\t// Get the final matcherOut by condensing this intermediate into postFinder contexts\n\t\t\t\t\ttemp = [];\n\t\t\t\t\ti = matcherOut.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) ) {\n\n\t\t\t\t\t\t\t// Restore matcherIn since elem is not yet a final match\n\t\t\t\t\t\t\ttemp.push( ( matcherIn[ i ] = elem ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpostFinder( null, ( matcherOut = [] ), temp, xml );\n\t\t\t\t}\n\n\t\t\t\t// Move matched elements from seed to results to keep them synchronized\n\t\t\t\ti = matcherOut.length;\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) &&\n\t\t\t\t\t\t( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {\n\n\t\t\t\t\t\tseed[ temp ] = !( results[ temp ] = elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Add elements to results, through postFinder if defined\n\t\t} else {\n\t\t\tmatcherOut = condense(\n\t\t\t\tmatcherOut === results ?\n\t\t\t\t\tmatcherOut.splice( preexisting, matcherOut.length ) :\n\t\t\t\t\tmatcherOut\n\t\t\t);\n\t\t\tif ( postFinder ) {\n\t\t\t\tpostFinder( null, results, matcherOut, xml );\n\t\t\t} else {\n\t\t\t\tpush.apply( results, matcherOut );\n\t\t\t}\n\t\t}\n\t} );\n}\n\nfunction matcherFromTokens( tokens ) {\n\tvar checkContext, matcher, j,\n\t\tlen = tokens.length,\n\t\tleadingRelative = Expr.relative[ tokens[ 0 ].type ],\n\t\timplicitRelative = leadingRelative || Expr.relative[ \" \" ],\n\t\ti = leadingRelative ? 1 : 0,\n\n\t\t// The foundational matcher ensures that elements are reachable from top-level context(s)\n\t\tmatchContext = addCombinator( function( elem ) {\n\t\t\treturn elem === checkContext;\n\t\t}, implicitRelative, true ),\n\t\tmatchAnyContext = addCombinator( function( elem ) {\n\t\t\treturn indexOf( checkContext, elem ) > -1;\n\t\t}, implicitRelative, true ),\n\t\tmatchers = [ function( elem, context, xml ) {\n\t\t\tvar ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n\t\t\t\t( checkContext = context ).nodeType ?\n\t\t\t\t\tmatchContext( elem, context, xml ) :\n\t\t\t\t\tmatchAnyContext( elem, context, xml ) );\n\n\t\t\t// Avoid hanging onto element (issue #299)\n\t\t\tcheckContext = null;\n\t\t\treturn ret;\n\t\t} ];\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {\n\t\t\tmatchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];\n\t\t} else {\n\t\t\tmatcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );\n\n\t\t\t// Return special upon seeing a positional matcher\n\t\t\tif ( matcher[ expando ] ) {\n\n\t\t\t\t// Find the next relative operator (if any) for proper handling\n\t\t\t\tj = ++i;\n\t\t\t\tfor ( ; j < len; j++ ) {\n\t\t\t\t\tif ( Expr.relative[ tokens[ j ].type ] ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn setMatcher(\n\t\t\t\t\ti > 1 && elementMatcher( matchers ),\n\t\t\t\t\ti > 1 && toSelector(\n\n\t\t\t\t\t// If the preceding token was a descendant combinator, insert an implicit any-element `*`\n\t\t\t\t\ttokens\n\t\t\t\t\t\t.slice( 0, i - 1 )\n\t\t\t\t\t\t.concat( { value: tokens[ i - 2 ].type === \" \" ? \"*\" : \"\" } )\n\t\t\t\t\t).replace( rtrim, \"$1\" ),\n\t\t\t\t\tmatcher,\n\t\t\t\t\ti < j && matcherFromTokens( tokens.slice( i, j ) ),\n\t\t\t\t\tj < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),\n\t\t\t\t\tj < len && toSelector( tokens )\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchers.push( matcher );\n\t\t}\n\t}\n\n\treturn elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n\tvar bySet = setMatchers.length > 0,\n\t\tbyElement = elementMatchers.length > 0,\n\t\tsuperMatcher = function( seed, context, xml, results, outermost ) {\n\t\t\tvar elem, j, matcher,\n\t\t\t\tmatchedCount = 0,\n\t\t\t\ti = \"0\",\n\t\t\t\tunmatched = seed && [],\n\t\t\t\tsetMatched = [],\n\t\t\t\tcontextBackup = outermostContext,\n\n\t\t\t\t// We must always have either seed elements or outermost context\n\t\t\t\telems = seed || byElement && Expr.find[ \"TAG\" ]( \"*\", outermost ),\n\n\t\t\t\t// Use integer dirruns iff this is the outermost matcher\n\t\t\t\tdirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),\n\t\t\t\tlen = elems.length;\n\n\t\t\tif ( outermost ) {\n\n\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\toutermostContext = context == document || context || outermost;\n\t\t\t}\n\n\t\t\t// Add elements passing elementMatchers directly to results\n\t\t\t// Support: IE<9, Safari\n\t\t\t// Tolerate NodeList properties (IE: \"length\"; Safari: <number>) matching elements by id\n\t\t\tfor ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {\n\t\t\t\tif ( byElement && elem ) {\n\t\t\t\t\tj = 0;\n\n\t\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\t\tif ( !context && elem.ownerDocument != document ) {\n\t\t\t\t\t\tsetDocument( elem );\n\t\t\t\t\t\txml = !documentIsHTML;\n\t\t\t\t\t}\n\t\t\t\t\twhile ( ( matcher = elementMatchers[ j++ ] ) ) {\n\t\t\t\t\t\tif ( matcher( elem, context || document, xml ) ) {\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( outermost ) {\n\t\t\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track unmatched elements for set filters\n\t\t\t\tif ( bySet ) {\n\n\t\t\t\t\t// They will have gone through all possible matchers\n\t\t\t\t\tif ( ( elem = !matcher && elem ) ) {\n\t\t\t\t\t\tmatchedCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Lengthen the array for every element, matched or not\n\t\t\t\t\tif ( seed ) {\n\t\t\t\t\t\tunmatched.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `i` is now the count of elements visited above, and adding it to `matchedCount`\n\t\t\t// makes the latter nonnegative.\n\t\t\tmatchedCount += i;\n\n\t\t\t// Apply set filters to unmatched elements\n\t\t\t// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`\n\t\t\t// equals `i`), unless we didn't visit _any_ elements in the above loop because we have\n\t\t\t// no element matchers and no seed.\n\t\t\t// Incrementing an initially-string \"0\" `i` allows `i` to remain a string only in that\n\t\t\t// case, which will result in a \"00\" `matchedCount` that differs from `i` but is also\n\t\t\t// numerically zero.\n\t\t\tif ( bySet && i !== matchedCount ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( ( matcher = setMatchers[ j++ ] ) ) {\n\t\t\t\t\tmatcher( unmatched, setMatched, context, xml );\n\t\t\t\t}\n\n\t\t\t\tif ( seed ) {\n\n\t\t\t\t\t// Reintegrate element matches to eliminate the need for sorting\n\t\t\t\t\tif ( matchedCount > 0 ) {\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tif ( !( unmatched[ i ] || setMatched[ i ] ) ) {\n\t\t\t\t\t\t\t\tsetMatched[ i ] = pop.call( results );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Discard index placeholder values to get only actual matches\n\t\t\t\t\tsetMatched = condense( setMatched );\n\t\t\t\t}\n\n\t\t\t\t// Add matches to results\n\t\t\t\tpush.apply( results, setMatched );\n\n\t\t\t\t// Seedless set matches succeeding multiple successful matchers stipulate sorting\n\t\t\t\tif ( outermost && !seed && setMatched.length > 0 &&\n\t\t\t\t\t( matchedCount + setMatchers.length ) > 1 ) {\n\n\t\t\t\t\tSizzle.uniqueSort( results );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Override manipulation of globals by nested matchers\n\t\t\tif ( outermost ) {\n\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\toutermostContext = contextBackup;\n\t\t\t}\n\n\t\t\treturn unmatched;\n\t\t};\n\n\treturn bySet ?\n\t\tmarkFunction( superMatcher ) :\n\t\tsuperMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {\n\tvar i,\n\t\tsetMatchers = [],\n\t\telementMatchers = [],\n\t\tcached = compilerCache[ selector + \" \" ];\n\n\tif ( !cached ) {\n\n\t\t// Generate a function of recursive functions that can be used to check each element\n\t\tif ( !match ) {\n\t\t\tmatch = tokenize( selector );\n\t\t}\n\t\ti = match.length;\n\t\twhile ( i-- ) {\n\t\t\tcached = matcherFromTokens( match[ i ] );\n\t\t\tif ( cached[ expando ] ) {\n\t\t\t\tsetMatchers.push( cached );\n\t\t\t} else {\n\t\t\t\telementMatchers.push( cached );\n\t\t\t}\n\t\t}\n\n\t\t// Cache the compiled function\n\t\tcached = compilerCache(\n\t\t\tselector,\n\t\t\tmatcherFromGroupMatchers( elementMatchers, setMatchers )\n\t\t);\n\n\t\t// Save selector and tokenization\n\t\tcached.selector = selector;\n\t}\n\treturn cached;\n};\n\n/**\n * A low-level selection function that works with Sizzle's compiled\n * selector functions\n * @param {String|Function} selector A selector or a pre-compiled\n * selector function built with Sizzle.compile\n * @param {Element} context\n * @param {Array} [results]\n * @param {Array} [seed] A set of elements to match against\n */\nselect = Sizzle.select = function( selector, context, results, seed ) {\n\tvar i, tokens, token, type, find,\n\t\tcompiled = typeof selector === \"function\" && selector,\n\t\tmatch = !seed && tokenize( ( selector = compiled.selector || selector ) );\n\n\tresults = results || [];\n\n\t// Try to minimize operations if there is only one selector in the list and no seed\n\t// (the latter of which guarantees us context)\n\tif ( match.length === 1 ) {\n\n\t\t// Reduce context if the leading compound selector is an ID\n\t\ttokens = match[ 0 ] = match[ 0 ].slice( 0 );\n\t\tif ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === \"ID\" &&\n\t\t\tcontext.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {\n\n\t\t\tcontext = ( Expr.find[ \"ID\" ]( token.matches[ 0 ]\n\t\t\t\t.replace( runescape, funescape ), context ) || [] )[ 0 ];\n\t\t\tif ( !context ) {\n\t\t\t\treturn results;\n\n\t\t\t// Precompiled matchers will still verify ancestry, so step up a level\n\t\t\t} else if ( compiled ) {\n\t\t\t\tcontext = context.parentNode;\n\t\t\t}\n\n\t\t\tselector = selector.slice( tokens.shift().value.length );\n\t\t}\n\n\t\t// Fetch a seed set for right-to-left matching\n\t\ti = matchExpr[ \"needsContext\" ].test( selector ) ? 0 : tokens.length;\n\t\twhile ( i-- ) {\n\t\t\ttoken = tokens[ i ];\n\n\t\t\t// Abort if we hit a combinator\n\t\t\tif ( Expr.relative[ ( type = token.type ) ] ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( ( find = Expr.find[ type ] ) ) {\n\n\t\t\t\t// Search, expanding context for leading sibling combinators\n\t\t\t\tif ( ( seed = find(\n\t\t\t\t\ttoken.matches[ 0 ].replace( runescape, funescape ),\n\t\t\t\t\trsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext\n\t\t\t\t) ) ) {\n\n\t\t\t\t\t// If seed is empty or no tokens remain, we can return early\n\t\t\t\t\ttokens.splice( i, 1 );\n\t\t\t\t\tselector = seed.length && toSelector( tokens );\n\t\t\t\t\tif ( !selector ) {\n\t\t\t\t\t\tpush.apply( results, seed );\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compile and execute a filtering function if one is not provided\n\t// Provide `match` to avoid retokenization if we modified the selector above\n\t( compiled || compile( selector, match ) )(\n\t\tseed,\n\t\tcontext,\n\t\t!documentIsHTML,\n\t\tresults,\n\t\t!context || rsibling.test( selector ) && testContext( context.parentNode ) || context\n\t);\n\treturn results;\n};\n\n// One-time assignments\n\n// Sort stability\nsupport.sortStable = expando.split( \"\" ).sort( sortOrder ).join( \"\" ) === expando;\n\n// Support: Chrome 14-35+\n// Always assume duplicates if they aren't passed to the comparison function\nsupport.detectDuplicates = !!hasDuplicate;\n\n// Initialize against the default document\nsetDocument();\n\n// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)\n// Detached nodes confoundingly follow *each other*\nsupport.sortDetached = assert( function( el ) {\n\n\t// Should return 1, but returns 4 (following)\n\treturn el.compareDocumentPosition( document.createElement( \"fieldset\" ) ) & 1;\n} );\n\n// Support: IE<8\n// Prevent attribute/property \"interpolation\"\n// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx\nif ( !assert( function( el ) {\n\tel.innerHTML = \"<a href='#'></a>\";\n\treturn el.firstChild.getAttribute( \"href\" ) === \"#\";\n} ) ) {\n\taddHandle( \"type|href|height|width\", function( elem, name, isXML ) {\n\t\tif ( !isXML ) {\n\t\t\treturn elem.getAttribute( name, name.toLowerCase() === \"type\" ? 1 : 2 );\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use defaultValue in place of getAttribute(\"value\")\nif ( !support.attributes || !assert( function( el ) {\n\tel.innerHTML = \"<input/>\";\n\tel.firstChild.setAttribute( \"value\", \"\" );\n\treturn el.firstChild.getAttribute( \"value\" ) === \"\";\n} ) ) {\n\taddHandle( \"value\", function( elem, _name, isXML ) {\n\t\tif ( !isXML && elem.nodeName.toLowerCase() === \"input\" ) {\n\t\t\treturn elem.defaultValue;\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use getAttributeNode to fetch booleans when getAttribute lies\nif ( !assert( function( el ) {\n\treturn el.getAttribute( \"disabled\" ) == null;\n} ) ) {\n\taddHandle( booleans, function( elem, name, isXML ) {\n\t\tvar val;\n\t\tif ( !isXML ) {\n\t\t\treturn elem[ name ] === true ? name.toLowerCase() :\n\t\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\t\tval.value :\n\t\t\t\t\tnull;\n\t\t}\n\t} );\n}\n\nreturn Sizzle;\n\n} )( window );\n\n\n\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\n\n// Deprecated\njQuery.expr[ \":\" ] = jQuery.expr.pseudos;\njQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\njQuery.escapeSelector = Sizzle.escape;\n\n\n\n\nvar dir = function( elem, dir, until ) {\n\tvar matched = [],\n\t\ttruncate = until !== undefined;\n\n\twhile ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\tif ( truncate && jQuery( elem ).is( until ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatched.push( elem );\n\t\t}\n\t}\n\treturn matched;\n};\n\n\nvar siblings = function( n, elem ) {\n\tvar matched = [];\n\n\tfor ( ; n; n = n.nextSibling ) {\n\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\tmatched.push( n );\n\t\t}\n\t}\n\n\treturn matched;\n};\n\n\nvar rneedsContext = jQuery.expr.match.needsContext;\n\n\n\nfunction nodeName( elem, name ) {\n\n\treturn elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\n}\nvar rsingleTag = ( /^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i );\n\n\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, not ) {\n\tif ( isFunction( qualifier ) ) {\n\t\treturn jQuery.grep( elements, function( elem, i ) {\n\t\t\treturn !!qualifier.call( elem, i, elem ) !== not;\n\t\t} );\n\t}\n\n\t// Single element\n\tif ( qualifier.nodeType ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( elem === qualifier ) !== not;\n\t\t} );\n\t}\n\n\t// Arraylike of elements (jQuery, arguments, Array)\n\tif ( typeof qualifier !== \"string\" ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( indexOf.call( qualifier, elem ) > -1 ) !== not;\n\t\t} );\n\t}\n\n\t// Filtered directly for both simple and complex selectors\n\treturn jQuery.filter( qualifier, elements, not );\n}\n\njQuery.filter = function( expr, elems, not ) {\n\tvar elem = elems[ 0 ];\n\n\tif ( not ) {\n\t\texpr = \":not(\" + expr + \")\";\n\t}\n\n\tif ( elems.length === 1 && elem.nodeType === 1 ) {\n\t\treturn jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];\n\t}\n\n\treturn jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {\n\t\treturn elem.nodeType === 1;\n\t} ) );\n};\n\njQuery.fn.extend( {\n\tfind: function( selector ) {\n\t\tvar i, ret,\n\t\t\tlen = this.length,\n\t\t\tself = this;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn this.pushStack( jQuery( selector ).filter( function() {\n\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tret = this.pushStack( [] );\n\n\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\tjQuery.find( selector, self[ i ], ret );\n\t\t}\n\n\t\treturn len > 1 ? jQuery.uniqueSort( ret ) : ret;\n\t},\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], false ) );\n\t},\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], true ) );\n\t},\n\tis: function( selector ) {\n\t\treturn !!winnow(\n\t\t\tthis,\n\n\t\t\t// If this is a positional/relative selector, check membership in the returned set\n\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\ttypeof selector === \"string\" && rneedsContext.test( selector ) ?\n\t\t\t\tjQuery( selector ) :\n\t\t\t\tselector || [],\n\t\t\tfalse\n\t\t).length;\n\t}\n} );\n\n\n// Initialize a jQuery object\n\n\n// A central reference to the root jQuery(document)\nvar rootjQuery,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)\n\t// Strict HTML recognition (#11290: must start with <)\n\t// Shortcut simple #id case for speed\n\trquickExpr = /^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/,\n\n\tinit = jQuery.fn.init = function( selector, context, root ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Method init() accepts an alternate rootjQuery\n\t\t// so migrate can support jQuery.sub (gh-2101)\n\t\troot = root || rootjQuery;\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector[ 0 ] === \"<\" &&\n\t\t\t\tselector[ selector.length - 1 ] === \">\" &&\n\t\t\t\tselector.length >= 3 ) {\n\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && ( match[ 1 ] || !context ) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[ 1 ] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[ 0 ] : context;\n\n\t\t\t\t\t// Option to run scripts is true for back-compat\n\t\t\t\t\t// Intentionally let the error be thrown if parseHTML is not present\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[ 1 ],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[ 2 ] );\n\n\t\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t\t// Inject the element directly into the jQuery object\n\t\t\t\t\t\tthis[ 0 ] = elem;\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || root ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\t} else if ( selector.nodeType ) {\n\t\t\tthis[ 0 ] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( isFunction( selector ) ) {\n\t\t\treturn root.ready !== undefined ?\n\t\t\t\troot.ready( selector ) :\n\n\t\t\t\t// Execute immediately if ready is not present\n\t\t\t\tselector( jQuery );\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t};\n\n// Give the init function the jQuery prototype for later instantiation\ninit.prototype = jQuery.fn;\n\n// Initialize central reference\nrootjQuery = jQuery( document );\n\n\nvar rparentsprev = /^(?:parents|prev(?:Until|All))/,\n\n\t// Methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend( {\n\thas: function( target ) {\n\t\tvar targets = jQuery( target, this ),\n\t\t\tl = targets.length;\n\n\t\treturn this.filter( function() {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[ i ] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tmatched = [],\n\t\t\ttargets = typeof selectors !== \"string\" && jQuery( selectors );\n\n\t\t// Positional selectors never match, since there's no _selection_ context\n\t\tif ( !rneedsContext.test( selectors ) ) {\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tfor ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {\n\n\t\t\t\t\t// Always skip document fragments\n\t\t\t\t\tif ( cur.nodeType < 11 && ( targets ?\n\t\t\t\t\t\ttargets.index( cur ) > -1 :\n\n\t\t\t\t\t\t// Don't pass non-elements to Sizzle\n\t\t\t\t\t\tcur.nodeType === 1 &&\n\t\t\t\t\t\t\tjQuery.find.matchesSelector( cur, selectors ) ) ) {\n\n\t\t\t\t\t\tmatched.push( cur );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );\n\t},\n\n\t// Determine the position of an element within the set\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;\n\t\t}\n\n\t\t// Index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn indexOf.call( jQuery( elem ), this[ 0 ] );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn indexOf.call( this,\n\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[ 0 ] : elem\n\t\t);\n\t},\n\n\tadd: function( selector, context ) {\n\t\treturn this.pushStack(\n\t\t\tjQuery.uniqueSort(\n\t\t\t\tjQuery.merge( this.get(), jQuery( selector, context ) )\n\t\t\t)\n\t\t);\n\t},\n\n\taddBack: function( selector ) {\n\t\treturn this.add( selector == null ?\n\t\t\tthis.prevObject : this.prevObject.filter( selector )\n\t\t);\n\t}\n} );\n\nfunction sibling( cur, dir ) {\n\twhile ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}\n\treturn cur;\n}\n\njQuery.each( {\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn sibling( elem, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn sibling( elem, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn siblings( ( elem.parentNode || {} ).firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn siblings( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\tif ( elem.contentDocument != null &&\n\n\t\t\t// Support: IE 11+\n\t\t\t// <object> elements with no `data` attribute has an object\n\t\t\t// `contentDocument` with a `null` prototype.\n\t\t\tgetProto( elem.contentDocument ) ) {\n\n\t\t\treturn elem.contentDocument;\n\t\t}\n\n\t\t// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only\n\t\t// Treat the template element as a regular one in browsers that\n\t\t// don't support it.\n\t\tif ( nodeName( elem, \"template\" ) ) {\n\t\t\telem = elem.content || elem;\n\t\t}\n\n\t\treturn jQuery.merge( [], elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar matched = jQuery.map( this, fn, until );\n\n\t\tif ( name.slice( -5 ) !== \"Until\" ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tmatched = jQuery.filter( selector, matched );\n\t\t}\n\n\t\tif ( this.length > 1 ) {\n\n\t\t\t// Remove duplicates\n\t\t\tif ( !guaranteedUnique[ name ] ) {\n\t\t\t\tjQuery.uniqueSort( matched );\n\t\t\t}\n\n\t\t\t// Reverse order for parents* and prev-derivatives\n\t\t\tif ( rparentsprev.test( name ) ) {\n\t\t\t\tmatched.reverse();\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched );\n\t};\n} );\nvar rnothtmlwhite = ( /[^\\x20\\t\\r\\n\\f]+/g );\n\n\n\n// Convert String-formatted options into Object-formatted ones\nfunction createOptions( options ) {\n\tvar object = {};\n\tjQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {\n\t\tobject[ flag ] = true;\n\t} );\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\toptions: an optional list of space-separated options that will change how\n *\t\t\tthe callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n\t// Convert options from String-formatted to Object-formatted if needed\n\t// (we check in cache first)\n\toptions = typeof options === \"string\" ?\n\t\tcreateOptions( options ) :\n\t\tjQuery.extend( {}, options );\n\n\tvar // Flag to know if list is currently firing\n\t\tfiring,\n\n\t\t// Last fire value for non-forgettable lists\n\t\tmemory,\n\n\t\t// Flag to know if list was already fired\n\t\tfired,\n\n\t\t// Flag to prevent firing\n\t\tlocked,\n\n\t\t// Actual callback list\n\t\tlist = [],\n\n\t\t// Queue of execution data for repeatable lists\n\t\tqueue = [],\n\n\t\t// Index of currently firing callback (modified by add/remove as needed)\n\t\tfiringIndex = -1,\n\n\t\t// Fire callbacks\n\t\tfire = function() {\n\n\t\t\t// Enforce single-firing\n\t\t\tlocked = locked || options.once;\n\n\t\t\t// Execute callbacks for all pending executions,\n\t\t\t// respecting firingIndex overrides and runtime changes\n\t\t\tfired = firing = true;\n\t\t\tfor ( ; queue.length; firingIndex = -1 ) {\n\t\t\t\tmemory = queue.shift();\n\t\t\t\twhile ( ++firingIndex < list.length ) {\n\n\t\t\t\t\t// Run callback and check for early termination\n\t\t\t\t\tif ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&\n\t\t\t\t\t\toptions.stopOnFalse ) {\n\n\t\t\t\t\t\t// Jump to end and forget the data so .add doesn't re-fire\n\t\t\t\t\t\tfiringIndex = list.length;\n\t\t\t\t\t\tmemory = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Forget the data if we're done with it\n\t\t\tif ( !options.memory ) {\n\t\t\t\tmemory = false;\n\t\t\t}\n\n\t\t\tfiring = false;\n\n\t\t\t// Clean up if we're done firing for good\n\t\t\tif ( locked ) {\n\n\t\t\t\t// Keep an empty list if we have data for future add calls\n\t\t\t\tif ( memory ) {\n\t\t\t\t\tlist = [];\n\n\t\t\t\t// Otherwise, this object is spent\n\t\t\t\t} else {\n\t\t\t\t\tlist = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Actual Callbacks object\n\t\tself = {\n\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\n\t\t\t\t\t// If we have memory from a past run, we should fire after adding\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfiringIndex = list.length - 1;\n\t\t\t\t\t\tqueue.push( memory );\n\t\t\t\t\t}\n\n\t\t\t\t\t( function add( args ) {\n\t\t\t\t\t\tjQuery.each( args, function( _, arg ) {\n\t\t\t\t\t\t\tif ( isFunction( arg ) ) {\n\t\t\t\t\t\t\t\tif ( !options.unique || !self.has( arg ) ) {\n\t\t\t\t\t\t\t\t\tlist.push( arg );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( arg && arg.length && toType( arg ) !== \"string\" ) {\n\n\t\t\t\t\t\t\t\t// Inspect recursively\n\t\t\t\t\t\t\t\tadd( arg );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t} )( arguments );\n\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tjQuery.each( arguments, function( _, arg ) {\n\t\t\t\t\tvar index;\n\t\t\t\t\twhile ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n\t\t\t\t\t\tlist.splice( index, 1 );\n\n\t\t\t\t\t\t// Handle firing indexes\n\t\t\t\t\t\tif ( index <= firingIndex ) {\n\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Check if a given callback is in the list.\n\t\t\t// If no argument is given, return whether or not list has callbacks attached.\n\t\t\thas: function( fn ) {\n\t\t\t\treturn fn ?\n\t\t\t\t\tjQuery.inArray( fn, list ) > -1 :\n\t\t\t\t\tlist.length > 0;\n\t\t\t},\n\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Disable .fire and .add\n\t\t\t// Abort any current/pending executions\n\t\t\t// Clear all callbacks and values\n\t\t\tdisable: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tlist = memory = \"\";\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\n\t\t\t// Disable .fire\n\t\t\t// Also disable .add unless we have memory (since it would have no effect)\n\t\t\t// Abort any pending executions\n\t\t\tlock: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tif ( !memory && !firing ) {\n\t\t\t\t\tlist = memory = \"\";\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tlocked: function() {\n\t\t\t\treturn !!locked;\n\t\t\t},\n\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( !locked ) {\n\t\t\t\t\targs = args || [];\n\t\t\t\t\targs = [ context, args.slice ? args.slice() : args ];\n\t\t\t\t\tqueue.push( args );\n\t\t\t\t\tif ( !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!fired;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\nfunction Identity( v ) {\n\treturn v;\n}\nfunction Thrower( ex ) {\n\tthrow ex;\n}\n\nfunction adoptValue( value, resolve, reject, noValue ) {\n\tvar method;\n\n\ttry {\n\n\t\t// Check for promise aspect first to privilege synchronous behavior\n\t\tif ( value && isFunction( ( method = value.promise ) ) ) {\n\t\t\tmethod.call( value ).done( resolve ).fail( reject );\n\n\t\t// Other thenables\n\t\t} else if ( value && isFunction( ( method = value.then ) ) ) {\n\t\t\tmethod.call( value, resolve, reject );\n\n\t\t// Other non-thenables\n\t\t} else {\n\n\t\t\t// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:\n\t\t\t// * false: [ value ].slice( 0 ) => resolve( value )\n\t\t\t// * true: [ value ].slice( 1 ) => resolve()\n\t\t\tresolve.apply( undefined, [ value ].slice( noValue ) );\n\t\t}\n\n\t// For Promises/A+, convert exceptions into rejections\n\t// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in\n\t// Deferred#then to conditionally suppress rejection.\n\t} catch ( value ) {\n\n\t\t// Support: Android 4.0 only\n\t\t// Strict mode functions invoked without .call/.apply get global-object context\n\t\treject.apply( undefined, [ value ] );\n\t}\n}\n\njQuery.extend( {\n\n\tDeferred: function( func ) {\n\t\tvar tuples = [\n\n\t\t\t\t// action, add listener, callbacks,\n\t\t\t\t// ... .then handlers, argument index, [final state]\n\t\t\t\t[ \"notify\", \"progress\", jQuery.Callbacks( \"memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"memory\" ), 2 ],\n\t\t\t\t[ \"resolve\", \"done\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 0, \"resolved\" ],\n\t\t\t\t[ \"reject\", \"fail\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 1, \"rejected\" ]\n\t\t\t],\n\t\t\tstate = \"pending\",\n\t\t\tpromise = {\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done( arguments ).fail( arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\t\"catch\": function( fn ) {\n\t\t\t\t\treturn promise.then( null, fn );\n\t\t\t\t},\n\n\t\t\t\t// Keep pipe for back-compat\n\t\t\t\tpipe: function( /* fnDone, fnFail, fnProgress */ ) {\n\t\t\t\t\tvar fns = arguments;\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( tuples, function( _i, tuple ) {\n\n\t\t\t\t\t\t\t// Map tuples (progress, done, fail) to arguments (done, fail, progress)\n\t\t\t\t\t\t\tvar fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];\n\n\t\t\t\t\t\t\t// deferred.progress(function() { bind to newDefer or newDefer.notify })\n\t\t\t\t\t\t\t// deferred.done(function() { bind to newDefer or newDefer.resolve })\n\t\t\t\t\t\t\t// deferred.fail(function() { bind to newDefer or newDefer.reject })\n\t\t\t\t\t\t\tdeferred[ tuple[ 1 ] ]( function() {\n\t\t\t\t\t\t\t\tvar returned = fn && fn.apply( this, arguments );\n\t\t\t\t\t\t\t\tif ( returned && isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\treturned.promise()\n\t\t\t\t\t\t\t\t\t\t.progress( newDefer.notify )\n\t\t\t\t\t\t\t\t\t\t.done( newDefer.resolve )\n\t\t\t\t\t\t\t\t\t\t.fail( newDefer.reject );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnewDefer[ tuple[ 0 ] + \"With\" ](\n\t\t\t\t\t\t\t\t\t\tthis,\n\t\t\t\t\t\t\t\t\t\tfn ? [ returned ] : arguments\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} );\n\t\t\t\t\t\tfns = null;\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\t\t\t\tthen: function( onFulfilled, onRejected, onProgress ) {\n\t\t\t\t\tvar maxDepth = 0;\n\t\t\t\t\tfunction resolve( depth, deferred, handler, special ) {\n\t\t\t\t\t\treturn function() {\n\t\t\t\t\t\t\tvar that = this,\n\t\t\t\t\t\t\t\targs = arguments,\n\t\t\t\t\t\t\t\tmightThrow = function() {\n\t\t\t\t\t\t\t\t\tvar returned, then;\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.3\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-59\n\t\t\t\t\t\t\t\t\t// Ignore double-resolution attempts\n\t\t\t\t\t\t\t\t\tif ( depth < maxDepth ) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturned = handler.apply( that, args );\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.1\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-48\n\t\t\t\t\t\t\t\t\tif ( returned === deferred.promise() ) {\n\t\t\t\t\t\t\t\t\t\tthrow new TypeError( \"Thenable self-resolution\" );\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ sections 2.3.3.1, 3.5\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-54\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-75\n\t\t\t\t\t\t\t\t\t// Retrieve `then` only once\n\t\t\t\t\t\t\t\t\tthen = returned &&\n\n\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.4\n\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-64\n\t\t\t\t\t\t\t\t\t\t// Only check objects and functions for thenability\n\t\t\t\t\t\t\t\t\t\t( typeof returned === \"object\" ||\n\t\t\t\t\t\t\t\t\t\t\ttypeof returned === \"function\" ) &&\n\t\t\t\t\t\t\t\t\t\treturned.then;\n\n\t\t\t\t\t\t\t\t\t// Handle a returned thenable\n\t\t\t\t\t\t\t\t\tif ( isFunction( then ) ) {\n\n\t\t\t\t\t\t\t\t\t\t// Special processors (notify) just wait for resolution\n\t\t\t\t\t\t\t\t\t\tif ( special ) {\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special )\n\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Normal processors (resolve) also hook into progress\n\t\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t\t// ...and disregard older resolution values\n\t\t\t\t\t\t\t\t\t\t\tmaxDepth++;\n\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdeferred.notifyWith )\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle all other returned values\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\tif ( handler !== Identity ) {\n\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\targs = [ returned ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Process the value(s)\n\t\t\t\t\t\t\t\t\t\t// Default process is resolve\n\t\t\t\t\t\t\t\t\t\t( special || deferred.resolveWith )( that, args );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\n\t\t\t\t\t\t\t\t// Only normal processors (resolve) catch and reject exceptions\n\t\t\t\t\t\t\t\tprocess = special ?\n\t\t\t\t\t\t\t\t\tmightThrow :\n\t\t\t\t\t\t\t\t\tfunction() {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tmightThrow();\n\t\t\t\t\t\t\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t\t\t\t\t\t\tif ( jQuery.Deferred.exceptionHook ) {\n\t\t\t\t\t\t\t\t\t\t\t\tjQuery.Deferred.exceptionHook( e,\n\t\t\t\t\t\t\t\t\t\t\t\t\tprocess.stackTrace );\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.4.1\n\t\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-61\n\t\t\t\t\t\t\t\t\t\t\t// Ignore post-resolution exceptions\n\t\t\t\t\t\t\t\t\t\t\tif ( depth + 1 >= maxDepth ) {\n\n\t\t\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\t\t\tif ( handler !== Thrower ) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\t\t\targs = [ e ];\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\tdeferred.rejectWith( that, args );\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.1\n\t\t\t\t\t\t\t// https://promisesaplus.com/#point-57\n\t\t\t\t\t\t\t// Re-resolve promises immediately to dodge false rejection from\n\t\t\t\t\t\t\t// subsequent errors\n\t\t\t\t\t\t\tif ( depth ) {\n\t\t\t\t\t\t\t\tprocess();\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// Call an optional hook to record the stack, in case of exception\n\t\t\t\t\t\t\t\t// since it's otherwise lost when execution goes async\n\t\t\t\t\t\t\t\tif ( jQuery.Deferred.getStackHook ) {\n\t\t\t\t\t\t\t\t\tprocess.stackTrace = jQuery.Deferred.getStackHook();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\twindow.setTimeout( process );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\n\t\t\t\t\t\t// progress_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 0 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onProgress ) ?\n\t\t\t\t\t\t\t\t\tonProgress :\n\t\t\t\t\t\t\t\t\tIdentity,\n\t\t\t\t\t\t\t\tnewDefer.notifyWith\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// fulfilled_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 1 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onFulfilled ) ?\n\t\t\t\t\t\t\t\t\tonFulfilled :\n\t\t\t\t\t\t\t\t\tIdentity\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// rejected_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 2 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onRejected ) ?\n\t\t\t\t\t\t\t\t\tonRejected :\n\t\t\t\t\t\t\t\t\tThrower\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\treturn obj != null ? jQuery.extend( obj, promise ) : promise;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = {};\n\n\t\t// Add list-specific methods\n\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\tvar list = tuple[ 2 ],\n\t\t\t\tstateString = tuple[ 5 ];\n\n\t\t\t// promise.progress = list.add\n\t\t\t// promise.done = list.add\n\t\t\t// promise.fail = list.add\n\t\t\tpromise[ tuple[ 1 ] ] = list.add;\n\n\t\t\t// Handle state\n\t\t\tif ( stateString ) {\n\t\t\t\tlist.add(\n\t\t\t\t\tfunction() {\n\n\t\t\t\t\t\t// state = \"resolved\" (i.e., fulfilled)\n\t\t\t\t\t\t// state = \"rejected\"\n\t\t\t\t\t\tstate = stateString;\n\t\t\t\t\t},\n\n\t\t\t\t\t// rejected_callbacks.disable\n\t\t\t\t\t// fulfilled_callbacks.disable\n\t\t\t\t\ttuples[ 3 - i ][ 2 ].disable,\n\n\t\t\t\t\t// rejected_handlers.disable\n\t\t\t\t\t// fulfilled_handlers.disable\n\t\t\t\t\ttuples[ 3 - i ][ 3 ].disable,\n\n\t\t\t\t\t// progress_callbacks.lock\n\t\t\t\t\ttuples[ 0 ][ 2 ].lock,\n\n\t\t\t\t\t// progress_handlers.lock\n\t\t\t\t\ttuples[ 0 ][ 3 ].lock\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// progress_handlers.fire\n\t\t\t// fulfilled_handlers.fire\n\t\t\t// rejected_handlers.fire\n\t\t\tlist.add( tuple[ 3 ].fire );\n\n\t\t\t// deferred.notify = function() { deferred.notifyWith(...) }\n\t\t\t// deferred.resolve = function() { deferred.resolveWith(...) }\n\t\t\t// deferred.reject = function() { deferred.rejectWith(...) }\n\t\t\tdeferred[ tuple[ 0 ] ] = function() {\n\t\t\t\tdeferred[ tuple[ 0 ] + \"With\" ]( this === deferred ? undefined : this, arguments );\n\t\t\t\treturn this;\n\t\t\t};\n\n\t\t\t// deferred.notifyWith = list.fireWith\n\t\t\t// deferred.resolveWith = list.fireWith\n\t\t\t// deferred.rejectWith = list.fireWith\n\t\t\tdeferred[ tuple[ 0 ] + \"With\" ] = list.fireWith;\n\t\t} );\n\n\t\t// Make the deferred a promise\n\t\tpromise.promise( deferred );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( singleValue ) {\n\t\tvar\n\n\t\t\t// count of uncompleted subordinates\n\t\t\tremaining = arguments.length,\n\n\t\t\t// count of unprocessed arguments\n\t\t\ti = remaining,\n\n\t\t\t// subordinate fulfillment data\n\t\t\tresolveContexts = Array( i ),\n\t\t\tresolveValues = slice.call( arguments ),\n\n\t\t\t// the primary Deferred\n\t\t\tprimary = jQuery.Deferred(),\n\n\t\t\t// subordinate callback factory\n\t\t\tupdateFunc = function( i ) {\n\t\t\t\treturn function( value ) {\n\t\t\t\t\tresolveContexts[ i ] = this;\n\t\t\t\t\tresolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;\n\t\t\t\t\tif ( !( --remaining ) ) {\n\t\t\t\t\t\tprimary.resolveWith( resolveContexts, resolveValues );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t};\n\n\t\t// Single- and empty arguments are adopted like Promise.resolve\n\t\tif ( remaining <= 1 ) {\n\t\t\tadoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,\n\t\t\t\t!remaining );\n\n\t\t\t// Use .then() to unwrap secondary thenables (cf. gh-3000)\n\t\t\tif ( primary.state() === \"pending\" ||\n\t\t\t\tisFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {\n\n\t\t\t\treturn primary.then();\n\t\t\t}\n\t\t}\n\n\t\t// Multiple arguments are aggregated like Promise.all array elements\n\t\twhile ( i-- ) {\n\t\t\tadoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );\n\t\t}\n\n\t\treturn primary.promise();\n\t}\n} );\n\n\n// These usually indicate a programmer mistake during development,\n// warn about them ASAP rather than swallowing them by default.\nvar rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;\n\njQuery.Deferred.exceptionHook = function( error, stack ) {\n\n\t// Support: IE 8 - 9 only\n\t// Console exists when dev tools are open, which can happen at any time\n\tif ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {\n\t\twindow.console.warn( \"jQuery.Deferred exception: \" + error.message, error.stack, stack );\n\t}\n};\n\n\n\n\njQuery.readyException = function( error ) {\n\twindow.setTimeout( function() {\n\t\tthrow error;\n\t} );\n};\n\n\n\n\n// The deferred used on DOM ready\nvar readyList = jQuery.Deferred();\n\njQuery.fn.ready = function( fn ) {\n\n\treadyList\n\t\t.then( fn )\n\n\t\t// Wrap jQuery.readyException in a function so that the lookup\n\t\t// happens at the time of error handling instead of callback\n\t\t// registration.\n\t\t.catch( function( error ) {\n\t\t\tjQuery.readyException( error );\n\t\t} );\n\n\treturn this;\n};\n\njQuery.extend( {\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\t}\n} );\n\njQuery.ready.then = readyList.then;\n\n// The ready event handler and self cleanup method\nfunction completed() {\n\tdocument.removeEventListener( \"DOMContentLoaded\", completed );\n\twindow.removeEventListener( \"load\", completed );\n\tjQuery.ready();\n}\n\n// Catch cases where $(document).ready() is called\n// after the browser event has already occurred.\n// Support: IE <=9 - 10 only\n// Older IE sometimes signals \"interactive\" too soon\nif ( document.readyState === \"complete\" ||\n\t( document.readyState !== \"loading\" && !document.documentElement.doScroll ) ) {\n\n\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\twindow.setTimeout( jQuery.ready );\n\n} else {\n\n\t// Use the handy event callback\n\tdocument.addEventListener( \"DOMContentLoaded\", completed );\n\n\t// A fallback to window.onload, that will always work\n\twindow.addEventListener( \"load\", completed );\n}\n\n\n\n\n// Multifunctional method to get and set values of a collection\n// The value/s can optionally be executed if it's a function\nvar access = function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\tvar i = 0,\n\t\tlen = elems.length,\n\t\tbulk = key == null;\n\n\t// Sets many values\n\tif ( toType( key ) === \"object\" ) {\n\t\tchainable = true;\n\t\tfor ( i in key ) {\n\t\t\taccess( elems, fn, i, key[ i ], true, emptyGet, raw );\n\t\t}\n\n\t// Sets one value\n\t} else if ( value !== undefined ) {\n\t\tchainable = true;\n\n\t\tif ( !isFunction( value ) ) {\n\t\t\traw = true;\n\t\t}\n\n\t\tif ( bulk ) {\n\n\t\t\t// Bulk operations run against the entire set\n\t\t\tif ( raw ) {\n\t\t\t\tfn.call( elems, value );\n\t\t\t\tfn = null;\n\n\t\t\t// ...except when executing function values\n\t\t\t} else {\n\t\t\t\tbulk = fn;\n\t\t\t\tfn = function( elem, _key, value ) {\n\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tfn(\n\t\t\t\t\telems[ i ], key, raw ?\n\t\t\t\t\t\tvalue :\n\t\t\t\t\t\tvalue.call( elems[ i ], i, fn( elems[ i ], key ) )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( chainable ) {\n\t\treturn elems;\n\t}\n\n\t// Gets\n\tif ( bulk ) {\n\t\treturn fn.call( elems );\n\t}\n\n\treturn len ? fn( elems[ 0 ], key ) : emptyGet;\n};\n\n\n// Matches dashed string for camelizing\nvar rmsPrefix = /^-ms-/,\n\trdashAlpha = /-([a-z])/g;\n\n// Used by camelCase as callback to replace()\nfunction fcamelCase( _all, letter ) {\n\treturn letter.toUpperCase();\n}\n\n// Convert dashed to camelCase; used by the css and data modules\n// Support: IE <=9 - 11, Edge 12 - 15\n// Microsoft forgot to hump their vendor prefix (#9572)\nfunction camelCase( string ) {\n\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n}\nvar acceptData = function( owner ) {\n\n\t// Accepts only:\n\t// - Node\n\t// - Node.ELEMENT_NODE\n\t// - Node.DOCUMENT_NODE\n\t// - Object\n\t// - Any\n\treturn owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );\n};\n\n\n\n\nfunction Data() {\n\tthis.expando = jQuery.expando + Data.uid++;\n}\n\nData.uid = 1;\n\nData.prototype = {\n\n\tcache: function( owner ) {\n\n\t\t// Check if the owner object already has a cache\n\t\tvar value = owner[ this.expando ];\n\n\t\t// If not, create one\n\t\tif ( !value ) {\n\t\t\tvalue = {};\n\n\t\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t\t// but we should not, see #8335.\n\t\t\t// Always return an empty object.\n\t\t\tif ( acceptData( owner ) ) {\n\n\t\t\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t\t\t// use plain assignment\n\t\t\t\tif ( owner.nodeType ) {\n\t\t\t\t\towner[ this.expando ] = value;\n\n\t\t\t\t// Otherwise secure it in a non-enumerable property\n\t\t\t\t// configurable must be true to allow the property to be\n\t\t\t\t// deleted when data is removed\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tconfigurable: true\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn value;\n\t},\n\tset: function( owner, data, value ) {\n\t\tvar prop,\n\t\t\tcache = this.cache( owner );\n\n\t\t// Handle: [ owner, key, value ] args\n\t\t// Always use camelCase key (gh-2257)\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tcache[ camelCase( data ) ] = value;\n\n\t\t// Handle: [ owner, { properties } ] args\n\t\t} else {\n\n\t\t\t// Copy the properties one-by-one to the cache object\n\t\t\tfor ( prop in data ) {\n\t\t\t\tcache[ camelCase( prop ) ] = data[ prop ];\n\t\t\t}\n\t\t}\n\t\treturn cache;\n\t},\n\tget: function( owner, key ) {\n\t\treturn key === undefined ?\n\t\t\tthis.cache( owner ) :\n\n\t\t\t// Always use camelCase key (gh-2257)\n\t\t\towner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];\n\t},\n\taccess: function( owner, key, value ) {\n\n\t\t// In cases where either:\n\t\t//\n\t\t// 1. No key was specified\n\t\t// 2. A string key was specified, but no value provided\n\t\t//\n\t\t// Take the \"read\" path and allow the get method to determine\n\t\t// which value to return, respectively either:\n\t\t//\n\t\t// 1. The entire cache object\n\t\t// 2. The data stored at the key\n\t\t//\n\t\tif ( key === undefined ||\n\t\t\t\t( ( key && typeof key === \"string\" ) && value === undefined ) ) {\n\n\t\t\treturn this.get( owner, key );\n\t\t}\n\n\t\t// When the key is not a string, or both a key and value\n\t\t// are specified, set or extend (existing objects) with either:\n\t\t//\n\t\t// 1. An object of properties\n\t\t// 2. A key and value\n\t\t//\n\t\tthis.set( owner, key, value );\n\n\t\t// Since the \"set\" path can have two possible entry points\n\t\t// return the expected data based on which path was taken[*]\n\t\treturn value !== undefined ? value : key;\n\t},\n\tremove: function( owner, key ) {\n\t\tvar i,\n\t\t\tcache = owner[ this.expando ];\n\n\t\tif ( cache === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( key !== undefined ) {\n\n\t\t\t// Support array or space separated string of keys\n\t\t\tif ( Array.isArray( key ) ) {\n\n\t\t\t\t// If key is an array of keys...\n\t\t\t\t// We always set camelCase keys, so remove that.\n\t\t\t\tkey = key.map( camelCase );\n\t\t\t} else {\n\t\t\t\tkey = camelCase( key );\n\n\t\t\t\t// If a key with the spaces exists, use it.\n\t\t\t\t// Otherwise, create an array by matching non-whitespace\n\t\t\t\tkey = key in cache ?\n\t\t\t\t\t[ key ] :\n\t\t\t\t\t( key.match( rnothtmlwhite ) || [] );\n\t\t\t}\n\n\t\t\ti = key.length;\n\n\t\t\twhile ( i-- ) {\n\t\t\t\tdelete cache[ key[ i ] ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if there's no more data\n\t\tif ( key === undefined || jQuery.isEmptyObject( cache ) ) {\n\n\t\t\t// Support: Chrome <=35 - 45\n\t\t\t// Webkit & Blink performance suffers when deleting properties\n\t\t\t// from DOM nodes, so set to undefined instead\n\t\t\t// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)\n\t\t\tif ( owner.nodeType ) {\n\t\t\t\towner[ this.expando ] = undefined;\n\t\t\t} else {\n\t\t\t\tdelete owner[ this.expando ];\n\t\t\t}\n\t\t}\n\t},\n\thasData: function( owner ) {\n\t\tvar cache = owner[ this.expando ];\n\t\treturn cache !== undefined && !jQuery.isEmptyObject( cache );\n\t}\n};\nvar dataPriv = new Data();\n\nvar dataUser = new Data();\n\n\n\n//\tImplementation Summary\n//\n//\t1. Enforce API surface and semantic compatibility with 1.9.x branch\n//\t2. Improve the module's maintainability by reducing the storage\n//\t\tpaths to a single mechanism.\n//\t3. Use the same single mechanism to support \"private\" and \"user\" data.\n//\t4. _Never_ expose \"private\" data to user code (TODO: Drop _data, _removeData)\n//\t5. Avoid exposing implementation details on user objects (eg. expando properties)\n//\t6. Provide a clear path for implementation upgrade to WeakMap in 2014\n\nvar rbrace = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,\n\trmultiDash = /[A-Z]/g;\n\nfunction getData( data ) {\n\tif ( data === \"true\" ) {\n\t\treturn true;\n\t}\n\n\tif ( data === \"false\" ) {\n\t\treturn false;\n\t}\n\n\tif ( data === \"null\" ) {\n\t\treturn null;\n\t}\n\n\t// Only convert to a number if it doesn't change the string\n\tif ( data === +data + \"\" ) {\n\t\treturn +data;\n\t}\n\n\tif ( rbrace.test( data ) ) {\n\t\treturn JSON.parse( data );\n\t}\n\n\treturn data;\n}\n\nfunction dataAttr( elem, key, data ) {\n\tvar name;\n\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tname = \"data-\" + key.replace( rmultiDash, \"-$&\" ).toLowerCase();\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = getData( data );\n\t\t\t} catch ( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tdataUser.set( elem, key, data );\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\treturn data;\n}\n\njQuery.extend( {\n\thasData: function( elem ) {\n\t\treturn dataUser.hasData( elem ) || dataPriv.hasData( elem );\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\treturn dataUser.access( elem, name, data );\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tdataUser.remove( elem, name );\n\t},\n\n\t// TODO: Now that all calls to _data and _removeData have been replaced\n\t// with direct calls to dataPriv methods, these can be deprecated.\n\t_data: function( elem, name, data ) {\n\t\treturn dataPriv.access( elem, name, data );\n\t},\n\n\t_removeData: function( elem, name ) {\n\t\tdataPriv.remove( elem, name );\n\t}\n} );\n\njQuery.fn.extend( {\n\tdata: function( key, value ) {\n\t\tvar i, name, data,\n\t\t\telem = this[ 0 ],\n\t\t\tattrs = elem && elem.attributes;\n\n\t\t// Gets all values\n\t\tif ( key === undefined ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = dataUser.get( elem );\n\n\t\t\t\tif ( elem.nodeType === 1 && !dataPriv.get( elem, \"hasDataAttrs\" ) ) {\n\t\t\t\t\ti = attrs.length;\n\t\t\t\t\twhile ( i-- ) {\n\n\t\t\t\t\t\t// Support: IE 11 only\n\t\t\t\t\t\t// The attrs elements can be null (#14894)\n\t\t\t\t\t\tif ( attrs[ i ] ) {\n\t\t\t\t\t\t\tname = attrs[ i ].name;\n\t\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\t\tname = camelCase( name.slice( 5 ) );\n\t\t\t\t\t\t\t\tdataAttr( elem, name, data[ name ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdataPriv.set( elem, \"hasDataAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// Sets multiple values\n\t\tif ( typeof key === \"object\" ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tdataUser.set( this, key );\n\t\t\t} );\n\t\t}\n\n\t\treturn access( this, function( value ) {\n\t\t\tvar data;\n\n\t\t\t// The calling jQuery object (element matches) is not empty\n\t\t\t// (and therefore has an element appears at this[ 0 ]) and the\n\t\t\t// `value` parameter was not undefined. An empty jQuery object\n\t\t\t// will result in `undefined` for elem = this[ 0 ] which will\n\t\t\t// throw an exception if an attempt to read a data cache is made.\n\t\t\tif ( elem && value === undefined ) {\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// The key will always be camelCased in Data\n\t\t\t\tdata = dataUser.get( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Attempt to \"discover\" the data in\n\t\t\t\t// HTML5 custom data-* attrs\n\t\t\t\tdata = dataAttr( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// We tried really hard, but the data doesn't exist.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the data...\n\t\t\tthis.each( function() {\n\n\t\t\t\t// We always store the camelCased key\n\t\t\t\tdataUser.set( this, key, value );\n\t\t\t} );\n\t\t}, null, value, arguments.length > 1, null, true );\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each( function() {\n\t\t\tdataUser.remove( this, key );\n\t\t} );\n\t}\n} );\n\n\njQuery.extend( {\n\tqueue: function( elem, type, data ) {\n\t\tvar queue;\n\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tqueue = dataPriv.get( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !queue || Array.isArray( data ) ) {\n\t\t\t\t\tqueue = dataPriv.access( elem, type, jQuery.makeArray( data ) );\n\t\t\t\t} else {\n\t\t\t\t\tqueue.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queue || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tstartLength = queue.length,\n\t\t\tfn = queue.shift(),\n\t\t\thooks = jQuery._queueHooks( elem, type ),\n\t\t\tnext = function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t\tstartLength--;\n\t\t}\n\n\t\tif ( fn ) {\n\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\t// Clear up the last queue stop function\n\t\t\tdelete hooks.stop;\n\t\t\tfn.call( elem, next, hooks );\n\t\t}\n\n\t\tif ( !startLength && hooks ) {\n\t\t\thooks.empty.fire();\n\t\t}\n\t},\n\n\t// Not public - generate a queueHooks object, or return the current one\n\t_queueHooks: function( elem, type ) {\n\t\tvar key = type + \"queueHooks\";\n\t\treturn dataPriv.get( elem, key ) || dataPriv.access( elem, key, {\n\t\t\tempty: jQuery.Callbacks( \"once memory\" ).add( function() {\n\t\t\t\tdataPriv.remove( elem, [ type + \"queue\", key ] );\n\t\t\t} )\n\t\t} );\n\t}\n} );\n\njQuery.fn.extend( {\n\tqueue: function( type, data ) {\n\t\tvar setter = 2;\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t\tsetter--;\n\t\t}\n\n\t\tif ( arguments.length < setter ) {\n\t\t\treturn jQuery.queue( this[ 0 ], type );\n\t\t}\n\n\t\treturn data === undefined ?\n\t\t\tthis :\n\t\t\tthis.each( function() {\n\t\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\t\t// Ensure a hooks for this queue\n\t\t\t\tjQuery._queueHooks( this, type );\n\n\t\t\t\tif ( type === \"fx\" && queue[ 0 ] !== \"inprogress\" ) {\n\t\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t\t}\n\t\t\t} );\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t} );\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, obj ) {\n\t\tvar tmp,\n\t\t\tcount = 1,\n\t\t\tdefer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = this.length,\n\t\t\tresolve = function() {\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobj = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\n\t\twhile ( i-- ) {\n\t\t\ttmp = dataPriv.get( elements[ i ], type + \"queueHooks\" );\n\t\t\tif ( tmp && tmp.empty ) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.empty.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise( obj );\n\t}\n} );\nvar pnum = ( /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/ ).source;\n\nvar rcssNum = new RegExp( \"^(?:([+-])=|)(\" + pnum + \")([a-z%]*)$\", \"i\" );\n\n\nvar cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ];\n\nvar documentElement = document.documentElement;\n\n\n\n\tvar isAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem );\n\t\t},\n\t\tcomposed = { composed: true };\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only\n\t// Check attachment across shadow DOM boundaries when possible (gh-3504)\n\t// Support: iOS 10.0-10.2 only\n\t// Early iOS 10 versions support `attachShadow` but not `getRootNode`,\n\t// leading to errors. We need to check for `getRootNode`.\n\tif ( documentElement.getRootNode ) {\n\t\tisAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem ) ||\n\t\t\t\telem.getRootNode( composed ) === elem.ownerDocument;\n\t\t};\n\t}\nvar isHiddenWithinTree = function( elem, el ) {\n\n\t\t// isHiddenWithinTree might be called from jQuery#filter function;\n\t\t// in that case, element will be second argument\n\t\telem = el || elem;\n\n\t\t// Inline style trumps all\n\t\treturn elem.style.display === \"none\" ||\n\t\t\telem.style.display === \"\" &&\n\n\t\t\t// Otherwise, check computed style\n\t\t\t// Support: Firefox <=43 - 45\n\t\t\t// Disconnected elements can have computed display: none, so first confirm that elem is\n\t\t\t// in the document.\n\t\t\tisAttached( elem ) &&\n\n\t\t\tjQuery.css( elem, \"display\" ) === \"none\";\n\t};\n\n\n\nfunction adjustCSS( elem, prop, valueParts, tween ) {\n\tvar adjusted, scale,\n\t\tmaxIterations = 20,\n\t\tcurrentValue = tween ?\n\t\t\tfunction() {\n\t\t\t\treturn tween.cur();\n\t\t\t} :\n\t\t\tfunction() {\n\t\t\t\treturn jQuery.css( elem, prop, \"\" );\n\t\t\t},\n\t\tinitial = currentValue(),\n\t\tunit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" ),\n\n\t\t// Starting value computation is required for potential unit mismatches\n\t\tinitialInUnit = elem.nodeType &&\n\t\t\t( jQuery.cssNumber[ prop ] || unit !== \"px\" && +initial ) &&\n\t\t\trcssNum.exec( jQuery.css( elem, prop ) );\n\n\tif ( initialInUnit && initialInUnit[ 3 ] !== unit ) {\n\n\t\t// Support: Firefox <=54\n\t\t// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)\n\t\tinitial = initial / 2;\n\n\t\t// Trust units reported by jQuery.css\n\t\tunit = unit || initialInUnit[ 3 ];\n\n\t\t// Iteratively approximate from a nonzero starting point\n\t\tinitialInUnit = +initial || 1;\n\n\t\twhile ( maxIterations-- ) {\n\n\t\t\t// Evaluate and update our best guess (doubling guesses that zero out).\n\t\t\t// Finish if the scale equals or crosses 1 (making the old*new product non-positive).\n\t\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\t\t\tif ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {\n\t\t\t\tmaxIterations = 0;\n\t\t\t}\n\t\t\tinitialInUnit = initialInUnit / scale;\n\n\t\t}\n\n\t\tinitialInUnit = initialInUnit * 2;\n\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\n\t\t// Make sure we update the tween properties later on\n\t\tvalueParts = valueParts || [];\n\t}\n\n\tif ( valueParts ) {\n\t\tinitialInUnit = +initialInUnit || +initial || 0;\n\n\t\t// Apply relative offset (+=/-=) if specified\n\t\tadjusted = valueParts[ 1 ] ?\n\t\t\tinitialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :\n\t\t\t+valueParts[ 2 ];\n\t\tif ( tween ) {\n\t\t\ttween.unit = unit;\n\t\t\ttween.start = initialInUnit;\n\t\t\ttween.end = adjusted;\n\t\t}\n\t}\n\treturn adjusted;\n}\n\n\nvar defaultDisplayMap = {};\n\nfunction getDefaultDisplay( elem ) {\n\tvar temp,\n\t\tdoc = elem.ownerDocument,\n\t\tnodeName = elem.nodeName,\n\t\tdisplay = defaultDisplayMap[ nodeName ];\n\n\tif ( display ) {\n\t\treturn display;\n\t}\n\n\ttemp = doc.body.appendChild( doc.createElement( nodeName ) );\n\tdisplay = jQuery.css( temp, \"display\" );\n\n\ttemp.parentNode.removeChild( temp );\n\n\tif ( display === \"none\" ) {\n\t\tdisplay = \"block\";\n\t}\n\tdefaultDisplayMap[ nodeName ] = display;\n\n\treturn display;\n}\n\nfunction showHide( elements, show ) {\n\tvar display, elem,\n\t\tvalues = [],\n\t\tindex = 0,\n\t\tlength = elements.length;\n\n\t// Determine new display value for elements that need to change\n\tfor ( ; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tdisplay = elem.style.display;\n\t\tif ( show ) {\n\n\t\t\t// Since we force visibility upon cascade-hidden elements, an immediate (and slow)\n\t\t\t// check is required in this first loop unless we have a nonempty display value (either\n\t\t\t// inline or about-to-be-restored)\n\t\t\tif ( display === \"none\" ) {\n\t\t\t\tvalues[ index ] = dataPriv.get( elem, \"display\" ) || null;\n\t\t\t\tif ( !values[ index ] ) {\n\t\t\t\t\telem.style.display = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( elem.style.display === \"\" && isHiddenWithinTree( elem ) ) {\n\t\t\t\tvalues[ index ] = getDefaultDisplay( elem );\n\t\t\t}\n\t\t} else {\n\t\t\tif ( display !== \"none\" ) {\n\t\t\t\tvalues[ index ] = \"none\";\n\n\t\t\t\t// Remember what we're overwriting\n\t\t\t\tdataPriv.set( elem, \"display\", display );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set the display of the elements in a second loop to avoid constant reflow\n\tfor ( index = 0; index < length; index++ ) {\n\t\tif ( values[ index ] != null ) {\n\t\t\telements[ index ].style.display = values[ index ];\n\t\t}\n\t}\n\n\treturn elements;\n}\n\njQuery.fn.extend( {\n\tshow: function() {\n\t\treturn showHide( this, true );\n\t},\n\thide: function() {\n\t\treturn showHide( this );\n\t},\n\ttoggle: function( state ) {\n\t\tif ( typeof state === \"boolean\" ) {\n\t\t\treturn state ? this.show() : this.hide();\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tif ( isHiddenWithinTree( this ) ) {\n\t\t\t\tjQuery( this ).show();\n\t\t\t} else {\n\t\t\t\tjQuery( this ).hide();\n\t\t\t}\n\t\t} );\n\t}\n} );\nvar rcheckableType = ( /^(?:checkbox|radio)$/i );\n\nvar rtagName = ( /<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i );\n\nvar rscriptType = ( /^$|^module$|\\/(?:java|ecma)script/i );\n\n\n\n( function() {\n\tvar fragment = document.createDocumentFragment(),\n\t\tdiv = fragment.appendChild( document.createElement( \"div\" ) ),\n\t\tinput = document.createElement( \"input\" );\n\n\t// Support: Android 4.0 - 4.3 only\n\t// Check state lost if the name is set (#11217)\n\t// Support: Windows Web Apps (WWA)\n\t// `name` and `type` must use .setAttribute for WWA (#14901)\n\tinput.setAttribute( \"type\", \"radio\" );\n\tinput.setAttribute( \"checked\", \"checked\" );\n\tinput.setAttribute( \"name\", \"t\" );\n\n\tdiv.appendChild( input );\n\n\t// Support: Android <=4.1 only\n\t// Older WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Support: IE <=11 only\n\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\tdiv.innerHTML = \"<textarea>x</textarea>\";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n\n\t// Support: IE <=9 only\n\t// IE <=9 replaces <option> tags with their contents when inserted outside of\n\t// the select element.\n\tdiv.innerHTML = \"<option></option>\";\n\tsupport.option = !!div.lastChild;\n} )();\n\n\n// We have to close these tags to support XHTML (#13200)\nvar wrapMap = {\n\n\t// XHTML parsers do not magically insert elements in the\n\t// same way that tag soup parsers do. So we cannot shorten\n\t// this by omitting <tbody> or other required elements.\n\tthead: [ 1, \"<table>\", \"</table>\" ],\n\tcol: [ 2, \"<table><colgroup>\", \"</colgroup></table>\" ],\n\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\n\t_default: [ 0, \"\", \"\" ]\n};\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// Support: IE <=9 only\nif ( !support.option ) {\n\twrapMap.optgroup = wrapMap.option = [ 1, \"<select multiple='multiple'>\", \"</select>\" ];\n}\n\n\nfunction getAll( context, tag ) {\n\n\t// Support: IE <=9 - 11 only\n\t// Use typeof to avoid zero-argument method invocation on host objects (#15151)\n\tvar ret;\n\n\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\tret = context.getElementsByTagName( tag || \"*\" );\n\n\t} else if ( typeof context.querySelectorAll !== \"undefined\" ) {\n\t\tret = context.querySelectorAll( tag || \"*\" );\n\n\t} else {\n\t\tret = [];\n\t}\n\n\tif ( tag === undefined || tag && nodeName( context, tag ) ) {\n\t\treturn jQuery.merge( [ context ], ret );\n\t}\n\n\treturn ret;\n}\n\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdataPriv.set(\n\t\t\telems[ i ],\n\t\t\t\"globalEval\",\n\t\t\t!refElements || dataPriv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\n\nvar rhtml = /<|&#?\\w+;/;\n\nfunction buildFragment( elems, context, scripts, selection, ignored ) {\n\tvar elem, tmp, tag, wrap, attached, j,\n\t\tfragment = context.createDocumentFragment(),\n\t\tnodes = [],\n\t\ti = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\telem = elems[ i ];\n\n\t\tif ( elem || elem === 0 ) {\n\n\t\t\t// Add nodes directly\n\t\t\tif ( toType( elem ) === \"object\" ) {\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t// Convert non-html into a text node\n\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t// Convert html into DOM nodes\n\t\t\t} else {\n\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement( \"div\" ) );\n\n\t\t\t\t// Deserialize a standard representation\n\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\ttmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];\n\n\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\tj = wrap[ 0 ];\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t// Remember the top-level container\n\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t// Ensure the created nodes are orphaned (#12392)\n\t\t\t\ttmp.textContent = \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove wrapper from fragment\n\tfragment.textContent = \"\";\n\n\ti = 0;\n\twhile ( ( elem = nodes[ i++ ] ) ) {\n\n\t\t// Skip elements already in the context collection (trac-4087)\n\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\tif ( ignored ) {\n\t\t\t\tignored.push( elem );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tattached = isAttached( elem );\n\n\t\t// Append to fragment\n\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t// Preserve script evaluation history\n\t\tif ( attached ) {\n\t\t\tsetGlobalEval( tmp );\n\t\t}\n\n\t\t// Capture executables\n\t\tif ( scripts ) {\n\t\t\tj = 0;\n\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\tscripts.push( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragment;\n}\n\n\nvar rtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\n// Support: IE <=9 - 11+\n// focus() and blur() are asynchronous, except when they are no-op.\n// So expect focus to be synchronous when the element is already active,\n// and blur to be synchronous when the element is not already active.\n// (focus and blur are always synchronous in other supported browsers,\n// this just defines when we can count on it).\nfunction expectSync( elem, type ) {\n\treturn ( elem === safeActiveElement() ) === ( type === \"focus\" );\n}\n\n// Support: IE <=9 only\n// Accessing document.activeElement can throw unexpectedly\n// https://bugs.jquery.com/ticket/13393\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\nfunction on( elem, types, selector, data, fn, one ) {\n\tvar origFn, type;\n\n\t// Types can be a map of types/handlers\n\tif ( typeof types === \"object\" ) {\n\n\t\t// ( types-Object, selector, data )\n\t\tif ( typeof selector !== \"string\" ) {\n\n\t\t\t// ( types-Object, data )\n\t\t\tdata = data || selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tfor ( type in types ) {\n\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t}\n\t\treturn elem;\n\t}\n\n\tif ( data == null && fn == null ) {\n\n\t\t// ( types, fn )\n\t\tfn = selector;\n\t\tdata = selector = undefined;\n\t} else if ( fn == null ) {\n\t\tif ( typeof selector === \"string\" ) {\n\n\t\t\t// ( types, selector, fn )\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t} else {\n\n\t\t\t// ( types, data, fn )\n\t\t\tfn = data;\n\t\t\tdata = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t}\n\tif ( fn === false ) {\n\t\tfn = returnFalse;\n\t} else if ( !fn ) {\n\t\treturn elem;\n\t}\n\n\tif ( one === 1 ) {\n\t\torigFn = fn;\n\t\tfn = function( event ) {\n\n\t\t\t// Can use an empty set, since event contains the info\n\t\t\tjQuery().off( event );\n\t\t\treturn origFn.apply( this, arguments );\n\t\t};\n\n\t\t// Use same guid so caller can remove using origFn\n\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t}\n\treturn elem.each( function() {\n\t\tjQuery.event.add( this, types, fn, data, selector );\n\t} );\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.get( elem );\n\n\t\t// Only attach events to objects that accept data\n\t\tif ( !acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Ensure that invalid selectors throw exceptions at attach time\n\t\t// Evaluate against documentElement in case elem is a non-element node (e.g., document)\n\t\tif ( selector ) {\n\t\t\tjQuery.find.matchesSelector( documentElement, selector );\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !( events = elemData.events ) ) {\n\t\t\tevents = elemData.events = Object.create( null );\n\t\t}\n\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend( {\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup ||\n\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.hasData( elem ) && dataPriv.get( elem );\n\n\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove data and the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdataPriv.remove( elem, \"handle events\" );\n\t\t}\n\t},\n\n\tdispatch: function( nativeEvent ) {\n\n\t\tvar i, j, ret, matched, handleObj, handlerQueue,\n\t\t\targs = new Array( arguments.length ),\n\n\t\t\t// Make a writable jQuery.Event from the native event object\n\t\t\tevent = jQuery.event.fix( nativeEvent ),\n\n\t\t\thandlers = (\n\t\t\t\tdataPriv.get( this, \"events\" ) || Object.create( null )\n\t\t\t)[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[ 0 ] = event;\n\n\t\tfor ( i = 1; i < arguments.length; i++ ) {\n\t\t\targs[ i ] = arguments[ i ];\n\t\t}\n\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// If the event is namespaced, then each handler is only invoked if it is\n\t\t\t\t// specially universal or its namespaces are a superset of the event's.\n\t\t\t\tif ( !event.rnamespace || handleObj.namespace === false ||\n\t\t\t\t\tevent.rnamespace.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, handleObj, sel, matchedHandlers, matchedSelectors,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Find delegate handlers\n\t\tif ( delegateCount &&\n\n\t\t\t// Support: IE <=9\n\t\t\t// Black-hole SVG <use> instance trees (trac-13180)\n\t\t\tcur.nodeType &&\n\n\t\t\t// Support: Firefox <=42\n\t\t\t// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)\n\t\t\t// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click\n\t\t\t// Support: IE 11 only\n\t\t\t// ...but not arrow key \"clicks\" of radio inputs, which can have `button` -1 (gh-2343)\n\t\t\t!( event.type === \"click\" && event.button >= 1 ) ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't check non-elements (#13208)\n\t\t\t\t// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)\n\t\t\t\tif ( cur.nodeType === 1 && !( event.type === \"click\" && cur.disabled === true ) ) {\n\t\t\t\t\tmatchedHandlers = [];\n\t\t\t\t\tmatchedSelectors = {};\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (#13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatchedSelectors[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) > -1 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] ) {\n\t\t\t\t\t\t\tmatchedHandlers.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matchedHandlers.length ) {\n\t\t\t\t\t\thandlerQueue.push( { elem: cur, handlers: matchedHandlers } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tcur = this;\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\taddProp: function( name, hook ) {\n\t\tObject.defineProperty( jQuery.Event.prototype, name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\n\t\t\tget: isFunction( hook ) ?\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\treturn hook( this.originalEvent );\n\t\t\t\t\t}\n\t\t\t\t} :\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\treturn this.originalEvent[ name ];\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\tset: function( value ) {\n\t\t\t\tObject.defineProperty( this, name, {\n\t\t\t\t\tenumerable: true,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t\tvalue: value\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\tfix: function( originalEvent ) {\n\t\treturn originalEvent[ jQuery.expando ] ?\n\t\t\toriginalEvent :\n\t\t\tnew jQuery.Event( originalEvent );\n\t},\n\n\tspecial: {\n\t\tload: {\n\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tclick: {\n\n\t\t\t// Utilize native event to ensure correct state for checkable inputs\n\t\t\tsetup: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Claim the first handler\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\t// dataPriv.set( el, \"click\", ... )\n\t\t\t\t\tleverageNative( el, \"click\", returnTrue );\n\t\t\t\t}\n\n\t\t\t\t// Return false to allow normal processing in the caller\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\ttrigger: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Force setup before triggering a click\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\tleverageNative( el, \"click\" );\n\t\t\t\t}\n\n\t\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, suppress native .click() on links\n\t\t\t// Also prevent it if we're currently inside a leveraged native-event stack\n\t\t\t_default: function( event ) {\n\t\t\t\tvar target = event.target;\n\t\t\t\treturn rcheckableType.test( target.type ) &&\n\t\t\t\t\ttarget.click && nodeName( target, \"input\" ) &&\n\t\t\t\t\tdataPriv.get( target, \"click\" ) ||\n\t\t\t\t\tnodeName( target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Ensure the presence of an event listener that handles manually-triggered\n// synthetic events by interrupting progress until reinvoked in response to\n// *native* events that it fires directly, ensuring that state changes have\n// already occurred before other listeners are invoked.\nfunction leverageNative( el, type, expectSync ) {\n\n\t// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add\n\tif ( !expectSync ) {\n\t\tif ( dataPriv.get( el, type ) === undefined ) {\n\t\t\tjQuery.event.add( el, type, returnTrue );\n\t\t}\n\t\treturn;\n\t}\n\n\t// Register the controller as a special universal handler for all event namespaces\n\tdataPriv.set( el, type, false );\n\tjQuery.event.add( el, type, {\n\t\tnamespace: false,\n\t\thandler: function( event ) {\n\t\t\tvar notAsync, result,\n\t\t\t\tsaved = dataPriv.get( this, type );\n\n\t\t\tif ( ( event.isTrigger & 1 ) && this[ type ] ) {\n\n\t\t\t\t// Interrupt processing of the outer synthetic .trigger()ed event\n\t\t\t\t// Saved data should be false in such cases, but might be a leftover capture object\n\t\t\t\t// from an async native handler (gh-4350)\n\t\t\t\tif ( !saved.length ) {\n\n\t\t\t\t\t// Store arguments for use when handling the inner native event\n\t\t\t\t\t// There will always be at least one argument (an event object), so this array\n\t\t\t\t\t// will not be confused with a leftover capture object.\n\t\t\t\t\tsaved = slice.call( arguments );\n\t\t\t\t\tdataPriv.set( this, type, saved );\n\n\t\t\t\t\t// Trigger the native event and capture its result\n\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t// focus() and blur() are asynchronous\n\t\t\t\t\tnotAsync = expectSync( this, type );\n\t\t\t\t\tthis[ type ]();\n\t\t\t\t\tresult = dataPriv.get( this, type );\n\t\t\t\t\tif ( saved !== result || notAsync ) {\n\t\t\t\t\t\tdataPriv.set( this, type, false );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = {};\n\t\t\t\t\t}\n\t\t\t\t\tif ( saved !== result ) {\n\n\t\t\t\t\t\t// Cancel the outer synthetic event\n\t\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\t\tevent.preventDefault();\n\n\t\t\t\t\t\t// Support: Chrome 86+\n\t\t\t\t\t\t// In Chrome, if an element having a focusout handler is blurred by\n\t\t\t\t\t\t// clicking outside of it, it invokes the handler synchronously. If\n\t\t\t\t\t\t// that handler calls `.remove()` on the element, the data is cleared,\n\t\t\t\t\t\t// leaving `result` undefined. We need to guard against this.\n\t\t\t\t\t\treturn result && result.value;\n\t\t\t\t\t}\n\n\t\t\t\t// If this is an inner synthetic event for an event with a bubbling surrogate\n\t\t\t\t// (focus or blur), assume that the surrogate already propagated from triggering the\n\t\t\t\t// native event and prevent that from happening again here.\n\t\t\t\t// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the\n\t\t\t\t// bubbling surrogate propagates *after* the non-bubbling base), but that seems\n\t\t\t\t// less bad than duplication.\n\t\t\t\t} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t}\n\n\t\t\t// If this is a native event triggered above, everything is now in order\n\t\t\t// Fire an inner synthetic event with the original arguments\n\t\t\t} else if ( saved.length ) {\n\n\t\t\t\t// ...and capture the result\n\t\t\t\tdataPriv.set( this, type, {\n\t\t\t\t\tvalue: jQuery.event.trigger(\n\n\t\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t\t// Extend with the prototype to reset the above stopImmediatePropagation()\n\t\t\t\t\t\tjQuery.extend( saved[ 0 ], jQuery.Event.prototype ),\n\t\t\t\t\t\tsaved.slice( 1 ),\n\t\t\t\t\t\tthis\n\t\t\t\t\t)\n\t\t\t\t} );\n\n\t\t\t\t// Abort handling of the native event\n\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t}\n\t\t}\n\t} );\n}\n\njQuery.removeEvent = function( elem, type, handle ) {\n\n\t// This \"if\" is needed for plain objects\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\n\t// Allow instantiation without the 'new' keyword\n\tif ( !( this instanceof jQuery.Event ) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\n\t\t\t\t// Support: Android <=2.3 only\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t\t// Create target properties\n\t\t// Support: Safari <=6 - 7 only\n\t\t// Target should not be a text node (#504, #13143)\n\t\tthis.target = ( src.target && src.target.nodeType === 3 ) ?\n\t\t\tsrc.target.parentNode :\n\t\t\tsrc.target;\n\n\t\tthis.currentTarget = src.currentTarget;\n\t\tthis.relatedTarget = src.relatedTarget;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || Date.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tconstructor: jQuery.Event,\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\tisSimulated: false,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Includes all common event props including KeyEvent and MouseEvent specific props\njQuery.each( {\n\taltKey: true,\n\tbubbles: true,\n\tcancelable: true,\n\tchangedTouches: true,\n\tctrlKey: true,\n\tdetail: true,\n\teventPhase: true,\n\tmetaKey: true,\n\tpageX: true,\n\tpageY: true,\n\tshiftKey: true,\n\tview: true,\n\t\"char\": true,\n\tcode: true,\n\tcharCode: true,\n\tkey: true,\n\tkeyCode: true,\n\tbutton: true,\n\tbuttons: true,\n\tclientX: true,\n\tclientY: true,\n\toffsetX: true,\n\toffsetY: true,\n\tpointerId: true,\n\tpointerType: true,\n\tscreenX: true,\n\tscreenY: true,\n\ttargetTouches: true,\n\ttoElement: true,\n\ttouches: true,\n\twhich: true\n}, jQuery.event.addProp );\n\njQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( type, delegateType ) {\n\tjQuery.event.special[ type ] = {\n\n\t\t// Utilize native event if possible so blur/focus sequence is correct\n\t\tsetup: function() {\n\n\t\t\t// Claim the first handler\n\t\t\t// dataPriv.set( this, \"focus\", ... )\n\t\t\t// dataPriv.set( this, \"blur\", ... )\n\t\t\tleverageNative( this, type, expectSync );\n\n\t\t\t// Return false to allow normal processing in the caller\n\t\t\treturn false;\n\t\t},\n\t\ttrigger: function() {\n\n\t\t\t// Force setup before trigger\n\t\t\tleverageNative( this, type );\n\n\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\treturn true;\n\t\t},\n\n\t\t// Suppress native focus or blur as it's already being fired\n\t\t// in leverageNative.\n\t\t_default: function() {\n\t\t\treturn true;\n\t\t},\n\n\t\tdelegateType: delegateType\n\t};\n} );\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// so that event delegation works in jQuery.\n// Do the same for pointerenter/pointerleave and pointerover/pointerout\n//\n// Support: Safari 7 only\n// Safari sends mouseenter too often; see:\n// https://bugs.chromium.org/p/chromium/issues/detail?id=470258\n// for the description of the bug (it existed in older Chrome versions as well).\njQuery.each( {\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mouseenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n} );\n\njQuery.fn.extend( {\n\n\ton: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn );\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\n\t\t\t// ( event ) dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ?\n\t\t\t\t\thandleObj.origType + \".\" + handleObj.namespace :\n\t\t\t\t\thandleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t} );\n\t}\n} );\n\n\nvar\n\n\t// Support: IE <=10 - 11, Edge 12 - 13 only\n\t// In IE/Edge using regex groups here causes severe slowdowns.\n\t// See https://connect.microsoft.com/IE/feedback/details/1736512/\n\trnoInnerhtml = /<script|<style|<link/i,\n\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\trcleanScript = /^\\s*<!(?:\\[CDATA\\[|--)|(?:\\]\\]|--)>\\s*$/g;\n\n// Prefer a tbody over its parent table for containing new rows\nfunction manipulationTarget( elem, content ) {\n\tif ( nodeName( elem, \"table\" ) &&\n\t\tnodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ) {\n\n\t\treturn jQuery( elem ).children( \"tbody\" )[ 0 ] || elem;\n\t}\n\n\treturn elem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = ( elem.getAttribute( \"type\" ) !== null ) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tif ( ( elem.type || \"\" ).slice( 0, 5 ) === \"true/\" ) {\n\t\telem.type = elem.type.slice( 5 );\n\t} else {\n\t\telem.removeAttribute( \"type\" );\n\t}\n\n\treturn elem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( dataPriv.hasData( src ) ) {\n\t\tpdataOld = dataPriv.get( src );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdataPriv.remove( dest, \"handle events\" );\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( dataUser.hasData( src ) ) {\n\t\tudataOld = dataUser.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdataUser.set( dest, udataCur );\n\t}\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\nfunction domManip( collection, args, callback, ignored ) {\n\n\t// Flatten any nested arrays\n\targs = flat( args );\n\n\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\ti = 0,\n\t\tl = collection.length,\n\t\tiNoClone = l - 1,\n\t\tvalue = args[ 0 ],\n\t\tvalueIsFunction = isFunction( value );\n\n\t// We can't cloneNode fragments that contain checked, in WebKit\n\tif ( valueIsFunction ||\n\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\treturn collection.each( function( index ) {\n\t\t\tvar self = collection.eq( index );\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t}\n\t\t\tdomManip( self, args, callback, ignored );\n\t\t} );\n\t}\n\n\tif ( l ) {\n\t\tfragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );\n\t\tfirst = fragment.firstChild;\n\n\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\tfragment = first;\n\t\t}\n\n\t\t// Require either new content or an interest in ignored elements to invoke the callback\n\t\tif ( first || ignored ) {\n\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\thasScripts = scripts.length;\n\n\t\t\t// Use the original fragment for the last item\n\t\t\t// instead of the first because it can end up\n\t\t\t// being emptied incorrectly in certain situations (#8070).\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tnode = fragment;\n\n\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\tif ( hasScripts ) {\n\n\t\t\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallback.call( collection[ i ], node, i );\n\t\t\t}\n\n\t\t\tif ( hasScripts ) {\n\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t// Reenable scripts\n\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t!dataPriv.access( node, \"globalEval\" ) &&\n\t\t\t\t\t\tjQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\tif ( node.src && ( node.type || \"\" ).toLowerCase() !== \"module\" ) {\n\n\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\tif ( jQuery._evalUrl && !node.noModule ) {\n\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src, {\n\t\t\t\t\t\t\t\t\tnonce: node.nonce || node.getAttribute( \"nonce\" )\n\t\t\t\t\t\t\t\t}, doc );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tDOMEval( node.textContent.replace( rcleanScript, \"\" ), node, doc );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn collection;\n}\n\nfunction remove( elem, selector, keepData ) {\n\tvar node,\n\t\tnodes = selector ? jQuery.filter( selector, elem ) : elem,\n\t\ti = 0;\n\n\tfor ( ; ( node = nodes[ i ] ) != null; i++ ) {\n\t\tif ( !keepData && node.nodeType === 1 ) {\n\t\t\tjQuery.cleanData( getAll( node ) );\n\t\t}\n\n\t\tif ( node.parentNode ) {\n\t\t\tif ( keepData && isAttached( node ) ) {\n\t\t\t\tsetGlobalEval( getAll( node, \"script\" ) );\n\t\t\t}\n\t\t\tnode.parentNode.removeChild( node );\n\t\t}\n\t}\n\n\treturn elem;\n}\n\njQuery.extend( {\n\thtmlPrefilter: function( html ) {\n\t\treturn html;\n\t},\n\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = isAttached( elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {\n\t\t\tif ( acceptData( elem ) ) {\n\t\t\t\tif ( ( data = elem[ dataPriv.expando ] ) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataPriv.expando ] = undefined;\n\t\t\t\t}\n\t\t\t\tif ( elem[ dataUser.expando ] ) {\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataUser.expando ] = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} );\n\njQuery.fn.extend( {\n\tdetach: function( selector ) {\n\t\treturn remove( this, selector, true );\n\t},\n\n\tremove: function( selector ) {\n\t\treturn remove( this, selector );\n\t},\n\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each( function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t} );\n\t},\n\n\tprepend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t} );\n\t},\n\n\tbefore: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t} );\n\t},\n\n\tafter: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t} );\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = this[ i ] ) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t} );\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = jQuery.htmlPrefilter( value );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch ( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar ignored = [];\n\n\t\t// Make the changes, replacing each non-ignored context element with the new content\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tvar parent = this.parentNode;\n\n\t\t\tif ( jQuery.inArray( this, ignored ) < 0 ) {\n\t\t\t\tjQuery.cleanData( getAll( this ) );\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.replaceChild( elem, this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Force callback invocation\n\t\t}, ignored );\n\t}\n} );\n\njQuery.each( {\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t// .get() because push.apply(_, arraylike) throws on ancient WebKit\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n} );\nvar rnumnonpx = new RegExp( \"^(\" + pnum + \")(?!px)[a-z%]+$\", \"i\" );\n\nvar getStyles = function( elem ) {\n\n\t\t// Support: IE <=11 only, Firefox <=30 (#15098, #14150)\n\t\t// IE throws on elements created in popups\n\t\t// FF meanwhile throws on frame elements through \"defaultView.getComputedStyle\"\n\t\tvar view = elem.ownerDocument.defaultView;\n\n\t\tif ( !view || !view.opener ) {\n\t\t\tview = window;\n\t\t}\n\n\t\treturn view.getComputedStyle( elem );\n\t};\n\nvar swap = function( elem, options, callback ) {\n\tvar ret, name,\n\t\told = {};\n\n\t// Remember the old values, and insert the new ones\n\tfor ( name in options ) {\n\t\told[ name ] = elem.style[ name ];\n\t\telem.style[ name ] = options[ name ];\n\t}\n\n\tret = callback.call( elem );\n\n\t// Revert the old values\n\tfor ( name in options ) {\n\t\telem.style[ name ] = old[ name ];\n\t}\n\n\treturn ret;\n};\n\n\nvar rboxStyle = new RegExp( cssExpand.join( \"|\" ), \"i\" );\n\n\n\n( function() {\n\n\t// Executing both pixelPosition & boxSizingReliable tests require only one layout\n\t// so they're executed at the same time to save the second computation.\n\tfunction computeStyleTests() {\n\n\t\t// This is a singleton, we need to execute it only once\n\t\tif ( !div ) {\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.style.cssText = \"position:absolute;left:-11111px;width:60px;\" +\n\t\t\t\"margin-top:1px;padding:0;border:0\";\n\t\tdiv.style.cssText =\n\t\t\t\"position:relative;display:block;box-sizing:border-box;overflow:scroll;\" +\n\t\t\t\"margin:auto;border:1px;padding:1px;\" +\n\t\t\t\"width:60%;top:1%\";\n\t\tdocumentElement.appendChild( container ).appendChild( div );\n\n\t\tvar divStyle = window.getComputedStyle( div );\n\t\tpixelPositionVal = divStyle.top !== \"1%\";\n\n\t\t// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44\n\t\treliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;\n\n\t\t// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3\n\t\t// Some styles come back with percentage values, even though they shouldn't\n\t\tdiv.style.right = \"60%\";\n\t\tpixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;\n\n\t\t// Support: IE 9 - 11 only\n\t\t// Detect misreporting of content dimensions for box-sizing:border-box elements\n\t\tboxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;\n\n\t\t// Support: IE 9 only\n\t\t// Detect overflow:scroll screwiness (gh-3699)\n\t\t// Support: Chrome <=64\n\t\t// Don't get tricked when zoom affects offsetWidth (gh-4029)\n\t\tdiv.style.position = \"absolute\";\n\t\tscrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;\n\n\t\tdocumentElement.removeChild( container );\n\n\t\t// Nullify the div so it wouldn't be stored in the memory and\n\t\t// it will also be a sign that checks already performed\n\t\tdiv = null;\n\t}\n\n\tfunction roundPixelMeasures( measure ) {\n\t\treturn Math.round( parseFloat( measure ) );\n\t}\n\n\tvar pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,\n\t\treliableTrDimensionsVal, reliableMarginLeftVal,\n\t\tcontainer = document.createElement( \"div\" ),\n\t\tdiv = document.createElement( \"div\" );\n\n\t// Finish early in limited (non-browser) environments\n\tif ( !div.style ) {\n\t\treturn;\n\t}\n\n\t// Support: IE <=9 - 11 only\n\t// Style of cloned element affects source element cloned (#8908)\n\tdiv.style.backgroundClip = \"content-box\";\n\tdiv.cloneNode( true ).style.backgroundClip = \"\";\n\tsupport.clearCloneStyle = div.style.backgroundClip === \"content-box\";\n\n\tjQuery.extend( support, {\n\t\tboxSizingReliable: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn boxSizingReliableVal;\n\t\t},\n\t\tpixelBoxStyles: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelBoxStylesVal;\n\t\t},\n\t\tpixelPosition: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelPositionVal;\n\t\t},\n\t\treliableMarginLeft: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn reliableMarginLeftVal;\n\t\t},\n\t\tscrollboxSize: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn scrollboxSizeVal;\n\t\t},\n\n\t\t// Support: IE 9 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Behavior in IE 9 is more subtle than in newer versions & it passes\n\t\t// some versions of this test; make sure not to make it pass there!\n\t\t//\n\t\t// Support: Firefox 70+\n\t\t// Only Firefox includes border widths\n\t\t// in computed dimensions. (gh-4529)\n\t\treliableTrDimensions: function() {\n\t\t\tvar table, tr, trChild, trStyle;\n\t\t\tif ( reliableTrDimensionsVal == null ) {\n\t\t\t\ttable = document.createElement( \"table\" );\n\t\t\t\ttr = document.createElement( \"tr\" );\n\t\t\t\ttrChild = document.createElement( \"div\" );\n\n\t\t\t\ttable.style.cssText = \"position:absolute;left:-11111px;border-collapse:separate\";\n\t\t\t\ttr.style.cssText = \"border:1px solid\";\n\n\t\t\t\t// Support: Chrome 86+\n\t\t\t\t// Height set through cssText does not get applied.\n\t\t\t\t// Computed height then comes back as 0.\n\t\t\t\ttr.style.height = \"1px\";\n\t\t\t\ttrChild.style.height = \"9px\";\n\n\t\t\t\t// Support: Android 8 Chrome 86+\n\t\t\t\t// In our bodyBackground.html iframe,\n\t\t\t\t// display for all div elements is set to \"inline\",\n\t\t\t\t// which causes a problem only in Android 8 Chrome 86.\n\t\t\t\t// Ensuring the div is display: block\n\t\t\t\t// gets around this issue.\n\t\t\t\ttrChild.style.display = \"block\";\n\n\t\t\t\tdocumentElement\n\t\t\t\t\t.appendChild( table )\n\t\t\t\t\t.appendChild( tr )\n\t\t\t\t\t.appendChild( trChild );\n\n\t\t\t\ttrStyle = window.getComputedStyle( tr );\n\t\t\t\treliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +\n\t\t\t\t\tparseInt( trStyle.borderTopWidth, 10 ) +\n\t\t\t\t\tparseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;\n\n\t\t\t\tdocumentElement.removeChild( table );\n\t\t\t}\n\t\t\treturn reliableTrDimensionsVal;\n\t\t}\n\t} );\n} )();\n\n\nfunction curCSS( elem, name, computed ) {\n\tvar width, minWidth, maxWidth, ret,\n\n\t\t// Support: Firefox 51+\n\t\t// Retrieving style before computed somehow\n\t\t// fixes an issue with getting wrong values\n\t\t// on detached elements\n\t\tstyle = elem.style;\n\n\tcomputed = computed || getStyles( elem );\n\n\t// getPropertyValue is needed for:\n\t// .css('filter') (IE 9 only, #12537)\n\t// .css('--customProperty) (#3144)\n\tif ( computed ) {\n\t\tret = computed.getPropertyValue( name ) || computed[ name ];\n\n\t\tif ( ret === \"\" && !isAttached( elem ) ) {\n\t\t\tret = jQuery.style( elem, name );\n\t\t}\n\n\t\t// A tribute to the \"awesome hack by Dean Edwards\"\n\t\t// Android Browser returns percentage for some values,\n\t\t// but width seems to be reliably pixels.\n\t\t// This is against the CSSOM draft spec:\n\t\t// https://drafts.csswg.org/cssom/#resolved-values\n\t\tif ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {\n\n\t\t\t// Remember the original values\n\t\t\twidth = style.width;\n\t\t\tminWidth = style.minWidth;\n\t\t\tmaxWidth = style.maxWidth;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tstyle.minWidth = style.maxWidth = style.width = ret;\n\t\t\tret = computed.width;\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.width = width;\n\t\t\tstyle.minWidth = minWidth;\n\t\t\tstyle.maxWidth = maxWidth;\n\t\t}\n\t}\n\n\treturn ret !== undefined ?\n\n\t\t// Support: IE <=9 - 11 only\n\t\t// IE returns zIndex value as an integer.\n\t\tret + \"\" :\n\t\tret;\n}\n\n\nfunction addGetHookIf( conditionFn, hookFn ) {\n\n\t// Define the hook, we'll check on the first run if it's really needed.\n\treturn {\n\t\tget: function() {\n\t\t\tif ( conditionFn() ) {\n\n\t\t\t\t// Hook not needed (or it's not possible to use it due\n\t\t\t\t// to missing dependency), remove it.\n\t\t\t\tdelete this.get;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Hook needed; redefine it so that the support test is not executed again.\n\t\t\treturn ( this.get = hookFn ).apply( this, arguments );\n\t\t}\n\t};\n}\n\n\nvar cssPrefixes = [ \"Webkit\", \"Moz\", \"ms\" ],\n\temptyStyle = document.createElement( \"div\" ).style,\n\tvendorProps = {};\n\n// Return a vendor-prefixed property or undefined\nfunction vendorPropName( name ) {\n\n\t// Check for vendor prefixed names\n\tvar capName = name[ 0 ].toUpperCase() + name.slice( 1 ),\n\t\ti = cssPrefixes.length;\n\n\twhile ( i-- ) {\n\t\tname = cssPrefixes[ i ] + capName;\n\t\tif ( name in emptyStyle ) {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n\n// Return a potentially-mapped jQuery.cssProps or vendor prefixed property\nfunction finalPropName( name ) {\n\tvar final = jQuery.cssProps[ name ] || vendorProps[ name ];\n\n\tif ( final ) {\n\t\treturn final;\n\t}\n\tif ( name in emptyStyle ) {\n\t\treturn name;\n\t}\n\treturn vendorProps[ name ] = vendorPropName( name ) || name;\n}\n\n\nvar\n\n\t// Swappable if display is none or starts with table\n\t// except \"table\", \"table-cell\", or \"table-caption\"\n\t// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n\trdisplayswap = /^(none|table(?!-c[ea]).+)/,\n\trcustomProp = /^--/,\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssNormalTransform = {\n\t\tletterSpacing: \"0\",\n\t\tfontWeight: \"400\"\n\t};\n\nfunction setPositiveNumber( _elem, value, subtract ) {\n\n\t// Any relative (+/-) values have already been\n\t// normalized at this point\n\tvar matches = rcssNum.exec( value );\n\treturn matches ?\n\n\t\t// Guard against undefined \"subtract\", e.g., when used as in cssHooks\n\t\tMath.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || \"px\" ) :\n\t\tvalue;\n}\n\nfunction boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {\n\tvar i = dimension === \"width\" ? 1 : 0,\n\t\textra = 0,\n\t\tdelta = 0;\n\n\t// Adjustment may not be necessary\n\tif ( box === ( isBorderBox ? \"border\" : \"content\" ) ) {\n\t\treturn 0;\n\t}\n\n\tfor ( ; i < 4; i += 2 ) {\n\n\t\t// Both box models exclude margin\n\t\tif ( box === \"margin\" ) {\n\t\t\tdelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );\n\t\t}\n\n\t\t// If we get here with a content-box, we're seeking \"padding\" or \"border\" or \"margin\"\n\t\tif ( !isBorderBox ) {\n\n\t\t\t// Add padding\n\t\t\tdelta += jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\n\t\t\t// For \"border\" or \"margin\", add border\n\t\t\tif ( box !== \"padding\" ) {\n\t\t\t\tdelta += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\n\t\t\t// But still keep track of it otherwise\n\t\t\t} else {\n\t\t\t\textra += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\n\t\t// If we get here with a border-box (content + padding + border), we're seeking \"content\" or\n\t\t// \"padding\" or \"margin\"\n\t\t} else {\n\n\t\t\t// For \"content\", subtract padding\n\t\t\tif ( box === \"content\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\t\t\t}\n\n\t\t\t// For \"content\" or \"padding\", subtract border\n\t\t\tif ( box !== \"margin\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Account for positive content-box scroll gutter when requested by providing computedVal\n\tif ( !isBorderBox && computedVal >= 0 ) {\n\n\t\t// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border\n\t\t// Assuming integer scroll gutter, subtract the rest and round down\n\t\tdelta += Math.max( 0, Math.ceil(\n\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\tcomputedVal -\n\t\t\tdelta -\n\t\t\textra -\n\t\t\t0.5\n\n\t\t// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter\n\t\t// Use an explicit zero to avoid NaN (gh-3964)\n\t\t) ) || 0;\n\t}\n\n\treturn delta;\n}\n\nfunction getWidthOrHeight( elem, dimension, extra ) {\n\n\t// Start with computed style\n\tvar styles = getStyles( elem ),\n\n\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).\n\t\t// Fake content-box until we know it's needed to know the true value.\n\t\tboxSizingNeeded = !support.boxSizingReliable() || extra,\n\t\tisBorderBox = boxSizingNeeded &&\n\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\tvalueIsBorderBox = isBorderBox,\n\n\t\tval = curCSS( elem, dimension, styles ),\n\t\toffsetProp = \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );\n\n\t// Support: Firefox <=54\n\t// Return a confounding non-pixel value or feign ignorance, as appropriate.\n\tif ( rnumnonpx.test( val ) ) {\n\t\tif ( !extra ) {\n\t\t\treturn val;\n\t\t}\n\t\tval = \"auto\";\n\t}\n\n\n\t// Support: IE 9 - 11 only\n\t// Use offsetWidth/offsetHeight for when box sizing is unreliable.\n\t// In those cases, the computed value can be trusted to be border-box.\n\tif ( ( !support.boxSizingReliable() && isBorderBox ||\n\n\t\t// Support: IE 10 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Interestingly, in some cases IE 9 doesn't suffer from this issue.\n\t\t!support.reliableTrDimensions() && nodeName( elem, \"tr\" ) ||\n\n\t\t// Fall back to offsetWidth/offsetHeight when value is \"auto\"\n\t\t// This happens for inline elements with no explicit setting (gh-3571)\n\t\tval === \"auto\" ||\n\n\t\t// Support: Android <=4.1 - 4.3 only\n\t\t// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)\n\t\t!parseFloat( val ) && jQuery.css( elem, \"display\", false, styles ) === \"inline\" ) &&\n\n\t\t// Make sure the element is visible & connected\n\t\telem.getClientRects().length ) {\n\n\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\";\n\n\t\t// Where available, offsetWidth/offsetHeight approximate border box dimensions.\n\t\t// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the\n\t\t// retrieved value as a content box dimension.\n\t\tvalueIsBorderBox = offsetProp in elem;\n\t\tif ( valueIsBorderBox ) {\n\t\t\tval = elem[ offsetProp ];\n\t\t}\n\t}\n\n\t// Normalize \"\" and auto\n\tval = parseFloat( val ) || 0;\n\n\t// Adjust for the element's box model\n\treturn ( val +\n\t\tboxModelAdjustment(\n\t\t\telem,\n\t\t\tdimension,\n\t\t\textra || ( isBorderBox ? \"border\" : \"content\" ),\n\t\t\tvalueIsBorderBox,\n\t\t\tstyles,\n\n\t\t\t// Provide the current computed size to request scroll gutter calculation (gh-3589)\n\t\t\tval\n\t\t)\n\t) + \"px\";\n}\n\njQuery.extend( {\n\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Don't automatically add \"px\" to these possibly-unitless properties\n\tcssNumber: {\n\t\t\"animationIterationCount\": true,\n\t\t\"columnCount\": true,\n\t\t\"fillOpacity\": true,\n\t\t\"flexGrow\": true,\n\t\t\"flexShrink\": true,\n\t\t\"fontWeight\": true,\n\t\t\"gridArea\": true,\n\t\t\"gridColumn\": true,\n\t\t\"gridColumnEnd\": true,\n\t\t\"gridColumnStart\": true,\n\t\t\"gridRow\": true,\n\t\t\"gridRowEnd\": true,\n\t\t\"gridRowStart\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"order\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name ),\n\t\t\tstyle = elem.style;\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to query the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Gets hook for the prefixed version, then unprefixed version\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// Convert \"+=\" or \"-=\" to relative numbers (#7345)\n\t\t\tif ( type === \"string\" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {\n\t\t\t\tvalue = adjustCSS( elem, name, ret );\n\n\t\t\t\t// Fixes bug #9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that null and NaN values aren't set (#7116)\n\t\t\tif ( value == null || value !== value ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add the unit (except for certain CSS properties)\n\t\t\t// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append\n\t\t\t// \"px\" to a few hardcoded values.\n\t\t\tif ( type === \"number\" && !isCustomProp ) {\n\t\t\t\tvalue += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? \"\" : \"px\" );\n\t\t\t}\n\n\t\t\t// background-* props affect original clone's values\n\t\t\tif ( !support.clearCloneStyle && value === \"\" && name.indexOf( \"background\" ) === 0 ) {\n\t\t\t\tstyle[ name ] = \"inherit\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !( \"set\" in hooks ) ||\n\t\t\t\t( value = hooks.set( elem, value, extra ) ) !== undefined ) {\n\n\t\t\t\tif ( isCustomProp ) {\n\t\t\t\t\tstyle.setProperty( name, value );\n\t\t\t\t} else {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks &&\n\t\t\t\t( ret = hooks.get( elem, false, extra ) ) !== undefined ) {\n\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra, styles ) {\n\t\tvar val, num, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name );\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to modify the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Try prefixed name followed by the unprefixed name\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks ) {\n\t\t\tval = hooks.get( elem, true, extra );\n\t\t}\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\tif ( val === undefined ) {\n\t\t\tval = curCSS( elem, name, styles );\n\t\t}\n\n\t\t// Convert \"normal\" to computed value\n\t\tif ( val === \"normal\" && name in cssNormalTransform ) {\n\t\t\tval = cssNormalTransform[ name ];\n\t\t}\n\n\t\t// Make numeric if forced or a qualifier was provided and val looks numeric\n\t\tif ( extra === \"\" || extra ) {\n\t\t\tnum = parseFloat( val );\n\t\t\treturn extra === true || isFinite( num ) ? num || 0 : val;\n\t\t}\n\n\t\treturn val;\n\t}\n} );\n\njQuery.each( [ \"height\", \"width\" ], function( _i, dimension ) {\n\tjQuery.cssHooks[ dimension ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tif ( computed ) {\n\n\t\t\t\t// Certain elements can have dimension info if we invisibly show them\n\t\t\t\t// but it must have a current display style that would benefit\n\t\t\t\treturn rdisplayswap.test( jQuery.css( elem, \"display\" ) ) &&\n\n\t\t\t\t\t// Support: Safari 8+\n\t\t\t\t\t// Table columns in Safari have non-zero offsetWidth & zero\n\t\t\t\t\t// getBoundingClientRect().width unless display is changed.\n\t\t\t\t\t// Support: IE <=11 only\n\t\t\t\t\t// Running getBoundingClientRect on a disconnected node\n\t\t\t\t\t// in IE throws an error.\n\t\t\t\t\t( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?\n\t\t\t\t\tswap( elem, cssShow, function() {\n\t\t\t\t\t\treturn getWidthOrHeight( elem, dimension, extra );\n\t\t\t\t\t} ) :\n\t\t\t\t\tgetWidthOrHeight( elem, dimension, extra );\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value, extra ) {\n\t\t\tvar matches,\n\t\t\t\tstyles = getStyles( elem ),\n\n\t\t\t\t// Only read styles.position if the test has a chance to fail\n\t\t\t\t// to avoid forcing a reflow.\n\t\t\t\tscrollboxSizeBuggy = !support.scrollboxSize() &&\n\t\t\t\t\tstyles.position === \"absolute\",\n\n\t\t\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)\n\t\t\t\tboxSizingNeeded = scrollboxSizeBuggy || extra,\n\t\t\t\tisBorderBox = boxSizingNeeded &&\n\t\t\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\t\t\tsubtract = extra ?\n\t\t\t\t\tboxModelAdjustment(\n\t\t\t\t\t\telem,\n\t\t\t\t\t\tdimension,\n\t\t\t\t\t\textra,\n\t\t\t\t\t\tisBorderBox,\n\t\t\t\t\t\tstyles\n\t\t\t\t\t) :\n\t\t\t\t\t0;\n\n\t\t\t// Account for unreliable border-box dimensions by comparing offset* to computed and\n\t\t\t// faking a content-box to get border and padding (gh-3699)\n\t\t\tif ( isBorderBox && scrollboxSizeBuggy ) {\n\t\t\t\tsubtract -= Math.ceil(\n\t\t\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\t\t\tparseFloat( styles[ dimension ] ) -\n\t\t\t\t\tboxModelAdjustment( elem, dimension, \"border\", false, styles ) -\n\t\t\t\t\t0.5\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert to pixels if value adjustment is needed\n\t\t\tif ( subtract && ( matches = rcssNum.exec( value ) ) &&\n\t\t\t\t( matches[ 3 ] || \"px\" ) !== \"px\" ) {\n\n\t\t\t\telem.style[ dimension ] = value;\n\t\t\t\tvalue = jQuery.css( elem, dimension );\n\t\t\t}\n\n\t\t\treturn setPositiveNumber( elem, value, subtract );\n\t\t}\n\t};\n} );\n\njQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn ( parseFloat( curCSS( elem, \"marginLeft\" ) ) ||\n\t\t\t\telem.getBoundingClientRect().left -\n\t\t\t\t\tswap( elem, { marginLeft: 0 }, function() {\n\t\t\t\t\t\treturn elem.getBoundingClientRect().left;\n\t\t\t\t\t} )\n\t\t\t) + \"px\";\n\t\t}\n\t}\n);\n\n// These hooks are used by animate to expand properties\njQuery.each( {\n\tmargin: \"\",\n\tpadding: \"\",\n\tborder: \"Width\"\n}, function( prefix, suffix ) {\n\tjQuery.cssHooks[ prefix + suffix ] = {\n\t\texpand: function( value ) {\n\t\t\tvar i = 0,\n\t\t\t\texpanded = {},\n\n\t\t\t\t// Assumes a single number if not a string\n\t\t\t\tparts = typeof value === \"string\" ? value.split( \" \" ) : [ value ];\n\n\t\t\tfor ( ; i < 4; i++ ) {\n\t\t\t\texpanded[ prefix + cssExpand[ i ] + suffix ] =\n\t\t\t\t\tparts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n\t\t\t}\n\n\t\t\treturn expanded;\n\t\t}\n\t};\n\n\tif ( prefix !== \"margin\" ) {\n\t\tjQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n\t}\n} );\n\njQuery.fn.extend( {\n\tcss: function( name, value ) {\n\t\treturn access( this, function( elem, name, value ) {\n\t\t\tvar styles, len,\n\t\t\t\tmap = {},\n\t\t\t\ti = 0;\n\n\t\t\tif ( Array.isArray( name ) ) {\n\t\t\t\tstyles = getStyles( elem );\n\t\t\t\tlen = name.length;\n\n\t\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t\tmap[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\t\t\t}\n\n\t\t\treturn value !== undefined ?\n\t\t\t\tjQuery.style( elem, name, value ) :\n\t\t\t\tjQuery.css( elem, name );\n\t\t}, name, value, arguments.length > 1 );\n\t}\n} );\n\n\nfunction Tween( elem, options, prop, end, easing ) {\n\treturn new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n\tconstructor: Tween,\n\tinit: function( elem, options, prop, end, easing, unit ) {\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\t\tthis.easing = easing || jQuery.easing._default;\n\t\tthis.options = options;\n\t\tthis.start = this.now = this.cur();\n\t\tthis.end = end;\n\t\tthis.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\t},\n\tcur: function() {\n\t\tvar hooks = Tween.propHooks[ this.prop ];\n\n\t\treturn hooks && hooks.get ?\n\t\t\thooks.get( this ) :\n\t\t\tTween.propHooks._default.get( this );\n\t},\n\trun: function( percent ) {\n\t\tvar eased,\n\t\t\thooks = Tween.propHooks[ this.prop ];\n\n\t\tif ( this.options.duration ) {\n\t\t\tthis.pos = eased = jQuery.easing[ this.easing ](\n\t\t\t\tpercent, this.options.duration * percent, 0, 1, this.options.duration\n\t\t\t);\n\t\t} else {\n\t\t\tthis.pos = eased = percent;\n\t\t}\n\t\tthis.now = ( this.end - this.start ) * eased + this.start;\n\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\tif ( hooks && hooks.set ) {\n\t\t\thooks.set( this );\n\t\t} else {\n\t\t\tTween.propHooks._default.set( this );\n\t\t}\n\t\treturn this;\n\t}\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n\t_default: {\n\t\tget: function( tween ) {\n\t\t\tvar result;\n\n\t\t\t// Use a property on the element directly when it is not a DOM element,\n\t\t\t// or when there is no matching style property that exists.\n\t\t\tif ( tween.elem.nodeType !== 1 ||\n\t\t\t\ttween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {\n\t\t\t\treturn tween.elem[ tween.prop ];\n\t\t\t}\n\n\t\t\t// Passing an empty string as a 3rd parameter to .css will automatically\n\t\t\t// attempt a parseFloat and fallback to a string if the parse fails.\n\t\t\t// Simple values such as \"10px\" are parsed to Float;\n\t\t\t// complex values such as \"rotate(1rad)\" are returned as-is.\n\t\t\tresult = jQuery.css( tween.elem, tween.prop, \"\" );\n\n\t\t\t// Empty strings, null, undefined and \"auto\" are converted to 0.\n\t\t\treturn !result || result === \"auto\" ? 0 : result;\n\t\t},\n\t\tset: function( tween ) {\n\n\t\t\t// Use step hook for back compat.\n\t\t\t// Use cssHook if its there.\n\t\t\t// Use .style if available and use plain properties where available.\n\t\t\tif ( jQuery.fx.step[ tween.prop ] ) {\n\t\t\t\tjQuery.fx.step[ tween.prop ]( tween );\n\t\t\t} else if ( tween.elem.nodeType === 1 && (\n\t\t\t\tjQuery.cssHooks[ tween.prop ] ||\n\t\t\t\t\ttween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {\n\t\t\t\tjQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n\t\t\t} else {\n\t\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Support: IE <=9 only\n// Panic based approach to setting things on disconnected nodes\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n\tset: function( tween ) {\n\t\tif ( tween.elem.nodeType && tween.elem.parentNode ) {\n\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t}\n\t}\n};\n\njQuery.easing = {\n\tlinear: function( p ) {\n\t\treturn p;\n\t},\n\tswing: function( p ) {\n\t\treturn 0.5 - Math.cos( p * Math.PI ) / 2;\n\t},\n\t_default: \"swing\"\n};\n\njQuery.fx = Tween.prototype.init;\n\n// Back compat <1.8 extension point\njQuery.fx.step = {};\n\n\n\n\nvar\n\tfxNow, inProgress,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trrun = /queueHooks$/;\n\nfunction schedule() {\n\tif ( inProgress ) {\n\t\tif ( document.hidden === false && window.requestAnimationFrame ) {\n\t\t\twindow.requestAnimationFrame( schedule );\n\t\t} else {\n\t\t\twindow.setTimeout( schedule, jQuery.fx.interval );\n\t\t}\n\n\t\tjQuery.fx.tick();\n\t}\n}\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\twindow.setTimeout( function() {\n\t\tfxNow = undefined;\n\t} );\n\treturn ( fxNow = Date.now() );\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n\tvar which,\n\t\ti = 0,\n\t\tattrs = { height: type };\n\n\t// If we include width, step value is 1 to do all cssExpand values,\n\t// otherwise step value is 2 to skip over Left and Right\n\tincludeWidth = includeWidth ? 1 : 0;\n\tfor ( ; i < 4; i += 2 - includeWidth ) {\n\t\twhich = cssExpand[ i ];\n\t\tattrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n\t}\n\n\tif ( includeWidth ) {\n\t\tattrs.opacity = attrs.width = type;\n\t}\n\n\treturn attrs;\n}\n\nfunction createTween( value, prop, animation ) {\n\tvar tween,\n\t\tcollection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ \"*\" ] ),\n\t\tindex = 0,\n\t\tlength = collection.length;\n\tfor ( ; index < length; index++ ) {\n\t\tif ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {\n\n\t\t\t// We're done with this property\n\t\t\treturn tween;\n\t\t}\n\t}\n}\n\nfunction defaultPrefilter( elem, props, opts ) {\n\tvar prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,\n\t\tisBox = \"width\" in props || \"height\" in props,\n\t\tanim = this,\n\t\torig = {},\n\t\tstyle = elem.style,\n\t\thidden = elem.nodeType && isHiddenWithinTree( elem ),\n\t\tdataShow = dataPriv.get( elem, \"fxshow\" );\n\n\t// Queue-skipping animations hijack the fx hooks\n\tif ( !opts.queue ) {\n\t\thooks = jQuery._queueHooks( elem, \"fx\" );\n\t\tif ( hooks.unqueued == null ) {\n\t\t\thooks.unqueued = 0;\n\t\t\toldfire = hooks.empty.fire;\n\t\t\thooks.empty.fire = function() {\n\t\t\t\tif ( !hooks.unqueued ) {\n\t\t\t\t\toldfire();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\thooks.unqueued++;\n\n\t\tanim.always( function() {\n\n\t\t\t// Ensure the complete handler is called before this completes\n\t\t\tanim.always( function() {\n\t\t\t\thooks.unqueued--;\n\t\t\t\tif ( !jQuery.queue( elem, \"fx\" ).length ) {\n\t\t\t\t\thooks.empty.fire();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t// Detect show/hide animations\n\tfor ( prop in props ) {\n\t\tvalue = props[ prop ];\n\t\tif ( rfxtypes.test( value ) ) {\n\t\t\tdelete props[ prop ];\n\t\t\ttoggle = toggle || value === \"toggle\";\n\t\t\tif ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n\n\t\t\t\t// Pretend to be hidden if this is a \"show\" and\n\t\t\t\t// there is still data from a stopped show/hide\n\t\t\t\tif ( value === \"show\" && dataShow && dataShow[ prop ] !== undefined ) {\n\t\t\t\t\thidden = true;\n\n\t\t\t\t// Ignore all other no-op show/hide data\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\torig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );\n\t\t}\n\t}\n\n\t// Bail out if this is a no-op like .hide().hide()\n\tpropTween = !jQuery.isEmptyObject( props );\n\tif ( !propTween && jQuery.isEmptyObject( orig ) ) {\n\t\treturn;\n\t}\n\n\t// Restrict \"overflow\" and \"display\" styles during box animations\n\tif ( isBox && elem.nodeType === 1 ) {\n\n\t\t// Support: IE <=9 - 11, Edge 12 - 15\n\t\t// Record all 3 overflow attributes because IE does not infer the shorthand\n\t\t// from identically-valued overflowX and overflowY and Edge just mirrors\n\t\t// the overflowX value there.\n\t\topts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n\t\t// Identify a display type, preferring old show/hide data over the CSS cascade\n\t\trestoreDisplay = dataShow && dataShow.display;\n\t\tif ( restoreDisplay == null ) {\n\t\t\trestoreDisplay = dataPriv.get( elem, \"display\" );\n\t\t}\n\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\tif ( display === \"none\" ) {\n\t\t\tif ( restoreDisplay ) {\n\t\t\t\tdisplay = restoreDisplay;\n\t\t\t} else {\n\n\t\t\t\t// Get nonempty value(s) by temporarily forcing visibility\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t\trestoreDisplay = elem.style.display || restoreDisplay;\n\t\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\t\t\tshowHide( [ elem ] );\n\t\t\t}\n\t\t}\n\n\t\t// Animate inline elements as inline-block\n\t\tif ( display === \"inline\" || display === \"inline-block\" && restoreDisplay != null ) {\n\t\t\tif ( jQuery.css( elem, \"float\" ) === \"none\" ) {\n\n\t\t\t\t// Restore the original display value at the end of pure show/hide animations\n\t\t\t\tif ( !propTween ) {\n\t\t\t\t\tanim.done( function() {\n\t\t\t\t\t\tstyle.display = restoreDisplay;\n\t\t\t\t\t} );\n\t\t\t\t\tif ( restoreDisplay == null ) {\n\t\t\t\t\t\tdisplay = style.display;\n\t\t\t\t\t\trestoreDisplay = display === \"none\" ? \"\" : display;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstyle.display = \"inline-block\";\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( opts.overflow ) {\n\t\tstyle.overflow = \"hidden\";\n\t\tanim.always( function() {\n\t\t\tstyle.overflow = opts.overflow[ 0 ];\n\t\t\tstyle.overflowX = opts.overflow[ 1 ];\n\t\t\tstyle.overflowY = opts.overflow[ 2 ];\n\t\t} );\n\t}\n\n\t// Implement show/hide animations\n\tpropTween = false;\n\tfor ( prop in orig ) {\n\n\t\t// General show/hide setup for this element animation\n\t\tif ( !propTween ) {\n\t\t\tif ( dataShow ) {\n\t\t\t\tif ( \"hidden\" in dataShow ) {\n\t\t\t\t\thidden = dataShow.hidden;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdataShow = dataPriv.access( elem, \"fxshow\", { display: restoreDisplay } );\n\t\t\t}\n\n\t\t\t// Store hidden/visible for toggle so `.stop().toggle()` \"reverses\"\n\t\t\tif ( toggle ) {\n\t\t\t\tdataShow.hidden = !hidden;\n\t\t\t}\n\n\t\t\t// Show elements before animating them\n\t\t\tif ( hidden ) {\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t}\n\n\t\t\t/* eslint-disable no-loop-func */\n\n\t\t\tanim.done( function() {\n\n\t\t\t\t/* eslint-enable no-loop-func */\n\n\t\t\t\t// The final step of a \"hide\" animation is actually hiding the element\n\t\t\t\tif ( !hidden ) {\n\t\t\t\t\tshowHide( [ elem ] );\n\t\t\t\t}\n\t\t\t\tdataPriv.remove( elem, \"fxshow\" );\n\t\t\t\tfor ( prop in orig ) {\n\t\t\t\t\tjQuery.style( elem, prop, orig[ prop ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t// Per-property setup\n\t\tpropTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );\n\t\tif ( !( prop in dataShow ) ) {\n\t\t\tdataShow[ prop ] = propTween.start;\n\t\t\tif ( hidden ) {\n\t\t\t\tpropTween.end = propTween.start;\n\t\t\t\tpropTween.start = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction propFilter( props, specialEasing ) {\n\tvar index, name, easing, value, hooks;\n\n\t// camelCase, specialEasing and expand cssHook pass\n\tfor ( index in props ) {\n\t\tname = camelCase( index );\n\t\teasing = specialEasing[ name ];\n\t\tvalue = props[ index ];\n\t\tif ( Array.isArray( value ) ) {\n\t\t\teasing = value[ 1 ];\n\t\t\tvalue = props[ index ] = value[ 0 ];\n\t\t}\n\n\t\tif ( index !== name ) {\n\t\t\tprops[ name ] = value;\n\t\t\tdelete props[ index ];\n\t\t}\n\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tif ( hooks && \"expand\" in hooks ) {\n\t\t\tvalue = hooks.expand( value );\n\t\t\tdelete props[ name ];\n\n\t\t\t// Not quite $.extend, this won't overwrite existing keys.\n\t\t\t// Reusing 'index' because we have the correct \"name\"\n\t\t\tfor ( index in value ) {\n\t\t\t\tif ( !( index in props ) ) {\n\t\t\t\t\tprops[ index ] = value[ index ];\n\t\t\t\t\tspecialEasing[ index ] = easing;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tspecialEasing[ name ] = easing;\n\t\t}\n\t}\n}\n\nfunction Animation( elem, properties, options ) {\n\tvar result,\n\t\tstopped,\n\t\tindex = 0,\n\t\tlength = Animation.prefilters.length,\n\t\tdeferred = jQuery.Deferred().always( function() {\n\n\t\t\t// Don't match elem in the :animated selector\n\t\t\tdelete tick.elem;\n\t\t} ),\n\t\ttick = function() {\n\t\t\tif ( stopped ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar currentTime = fxNow || createFxNow(),\n\t\t\t\tremaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n\n\t\t\t\t// Support: Android 2.3 only\n\t\t\t\t// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)\n\t\t\t\ttemp = remaining / animation.duration || 0,\n\t\t\t\tpercent = 1 - temp,\n\t\t\t\tindex = 0,\n\t\t\t\tlength = animation.tweens.length;\n\n\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\tanimation.tweens[ index ].run( percent );\n\t\t\t}\n\n\t\t\tdeferred.notifyWith( elem, [ animation, percent, remaining ] );\n\n\t\t\t// If there's more to do, yield\n\t\t\tif ( percent < 1 && length ) {\n\t\t\t\treturn remaining;\n\t\t\t}\n\n\t\t\t// If this was an empty animation, synthesize a final progress notification\n\t\t\tif ( !length ) {\n\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t}\n\n\t\t\t// Resolve the animation and report its conclusion\n\t\t\tdeferred.resolveWith( elem, [ animation ] );\n\t\t\treturn false;\n\t\t},\n\t\tanimation = deferred.promise( {\n\t\t\telem: elem,\n\t\t\tprops: jQuery.extend( {}, properties ),\n\t\t\topts: jQuery.extend( true, {\n\t\t\t\tspecialEasing: {},\n\t\t\t\teasing: jQuery.easing._default\n\t\t\t}, options ),\n\t\t\toriginalProperties: properties,\n\t\t\toriginalOptions: options,\n\t\t\tstartTime: fxNow || createFxNow(),\n\t\t\tduration: options.duration,\n\t\t\ttweens: [],\n\t\t\tcreateTween: function( prop, end ) {\n\t\t\t\tvar tween = jQuery.Tween( elem, animation.opts, prop, end,\n\t\t\t\t\tanimation.opts.specialEasing[ prop ] || animation.opts.easing );\n\t\t\t\tanimation.tweens.push( tween );\n\t\t\t\treturn tween;\n\t\t\t},\n\t\t\tstop: function( gotoEnd ) {\n\t\t\t\tvar index = 0,\n\n\t\t\t\t\t// If we are going to the end, we want to run all the tweens\n\t\t\t\t\t// otherwise we skip this part\n\t\t\t\t\tlength = gotoEnd ? animation.tweens.length : 0;\n\t\t\t\tif ( stopped ) {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tstopped = true;\n\t\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\t\tanimation.tweens[ index ].run( 1 );\n\t\t\t\t}\n\n\t\t\t\t// Resolve when we played the last frame; otherwise, reject\n\t\t\t\tif ( gotoEnd ) {\n\t\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t\t\tdeferred.resolveWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t} else {\n\t\t\t\t\tdeferred.rejectWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\t\t} ),\n\t\tprops = animation.props;\n\n\tpropFilter( props, animation.opts.specialEasing );\n\n\tfor ( ; index < length; index++ ) {\n\t\tresult = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );\n\t\tif ( result ) {\n\t\t\tif ( isFunction( result.stop ) ) {\n\t\t\t\tjQuery._queueHooks( animation.elem, animation.opts.queue ).stop =\n\t\t\t\t\tresult.stop.bind( result );\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tjQuery.map( props, createTween, animation );\n\n\tif ( isFunction( animation.opts.start ) ) {\n\t\tanimation.opts.start.call( elem, animation );\n\t}\n\n\t// Attach callbacks from options\n\tanimation\n\t\t.progress( animation.opts.progress )\n\t\t.done( animation.opts.done, animation.opts.complete )\n\t\t.fail( animation.opts.fail )\n\t\t.always( animation.opts.always );\n\n\tjQuery.fx.timer(\n\t\tjQuery.extend( tick, {\n\t\t\telem: elem,\n\t\t\tanim: animation,\n\t\t\tqueue: animation.opts.queue\n\t\t} )\n\t);\n\n\treturn animation;\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\n\ttweeners: {\n\t\t\"*\": [ function( prop, value ) {\n\t\t\tvar tween = this.createTween( prop, value );\n\t\t\tadjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );\n\t\t\treturn tween;\n\t\t} ]\n\t},\n\n\ttweener: function( props, callback ) {\n\t\tif ( isFunction( props ) ) {\n\t\t\tcallback = props;\n\t\t\tprops = [ \"*\" ];\n\t\t} else {\n\t\t\tprops = props.match( rnothtmlwhite );\n\t\t}\n\n\t\tvar prop,\n\t\t\tindex = 0,\n\t\t\tlength = props.length;\n\n\t\tfor ( ; index < length; index++ ) {\n\t\t\tprop = props[ index ];\n\t\t\tAnimation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];\n\t\t\tAnimation.tweeners[ prop ].unshift( callback );\n\t\t}\n\t},\n\n\tprefilters: [ defaultPrefilter ],\n\n\tprefilter: function( callback, prepend ) {\n\t\tif ( prepend ) {\n\t\t\tAnimation.prefilters.unshift( callback );\n\t\t} else {\n\t\t\tAnimation.prefilters.push( callback );\n\t\t}\n\t}\n} );\n\njQuery.speed = function( speed, easing, fn ) {\n\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\tcomplete: fn || !fn && easing ||\n\t\t\tisFunction( speed ) && speed,\n\t\tduration: speed,\n\t\teasing: fn && easing || easing && !isFunction( easing ) && easing\n\t};\n\n\t// Go to the end state if fx are off\n\tif ( jQuery.fx.off ) {\n\t\topt.duration = 0;\n\n\t} else {\n\t\tif ( typeof opt.duration !== \"number\" ) {\n\t\t\tif ( opt.duration in jQuery.fx.speeds ) {\n\t\t\t\topt.duration = jQuery.fx.speeds[ opt.duration ];\n\n\t\t\t} else {\n\t\t\t\topt.duration = jQuery.fx.speeds._default;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Normalize opt.queue - true/undefined/null -> \"fx\"\n\tif ( opt.queue == null || opt.queue === true ) {\n\t\topt.queue = \"fx\";\n\t}\n\n\t// Queueing\n\topt.old = opt.complete;\n\n\topt.complete = function() {\n\t\tif ( isFunction( opt.old ) ) {\n\t\t\topt.old.call( this );\n\t\t}\n\n\t\tif ( opt.queue ) {\n\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t}\n\t};\n\n\treturn opt;\n};\n\njQuery.fn.extend( {\n\tfadeTo: function( speed, to, easing, callback ) {\n\n\t\t// Show any hidden elements after setting opacity to 0\n\t\treturn this.filter( isHiddenWithinTree ).css( \"opacity\", 0 ).show()\n\n\t\t\t// Animate to the value specified\n\t\t\t.end().animate( { opacity: to }, speed, easing, callback );\n\t},\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar empty = jQuery.isEmptyObject( prop ),\n\t\t\toptall = jQuery.speed( speed, easing, callback ),\n\t\t\tdoAnimation = function() {\n\n\t\t\t\t// Operate on a copy of prop so per-property easing won't be lost\n\t\t\t\tvar anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n\t\t\t\t// Empty animations, or finishing resolves immediately\n\t\t\t\tif ( empty || dataPriv.get( this, \"finish\" ) ) {\n\t\t\t\t\tanim.stop( true );\n\t\t\t\t}\n\t\t\t};\n\n\t\tdoAnimation.finish = doAnimation;\n\n\t\treturn empty || optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tvar stopQueue = function( hooks ) {\n\t\t\tvar stop = hooks.stop;\n\t\t\tdelete hooks.stop;\n\t\t\tstop( gotoEnd );\n\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar dequeue = true,\n\t\t\t\tindex = type != null && type + \"queueHooks\",\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = dataPriv.get( this );\n\n\t\t\tif ( index ) {\n\t\t\t\tif ( data[ index ] && data[ index ].stop ) {\n\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n\t\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this &&\n\t\t\t\t\t( type == null || timers[ index ].queue === type ) ) {\n\n\t\t\t\t\ttimers[ index ].anim.stop( gotoEnd );\n\t\t\t\t\tdequeue = false;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start the next in the queue if the last step wasn't forced.\n\t\t\t// Timers currently will call their complete callbacks, which\n\t\t\t// will dequeue but only if they were gotoEnd.\n\t\t\tif ( dequeue || !gotoEnd ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t} );\n\t},\n\tfinish: function( type ) {\n\t\tif ( type !== false ) {\n\t\t\ttype = type || \"fx\";\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tvar index,\n\t\t\t\tdata = dataPriv.get( this ),\n\t\t\t\tqueue = data[ type + \"queue\" ],\n\t\t\t\thooks = data[ type + \"queueHooks\" ],\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tlength = queue ? queue.length : 0;\n\n\t\t\t// Enable finishing flag on private data\n\t\t\tdata.finish = true;\n\n\t\t\t// Empty the queue first\n\t\t\tjQuery.queue( this, type, [] );\n\n\t\t\tif ( hooks && hooks.stop ) {\n\t\t\t\thooks.stop.call( this, true );\n\t\t\t}\n\n\t\t\t// Look for any active animations, and finish them\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && timers[ index ].queue === type ) {\n\t\t\t\t\ttimers[ index ].anim.stop( true );\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for any animations in the old queue and finish them\n\t\t\tfor ( index = 0; index < length; index++ ) {\n\t\t\t\tif ( queue[ index ] && queue[ index ].finish ) {\n\t\t\t\t\tqueue[ index ].finish.call( this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Turn off finishing flag\n\t\t\tdelete data.finish;\n\t\t} );\n\t}\n} );\n\njQuery.each( [ \"toggle\", \"show\", \"hide\" ], function( _i, name ) {\n\tvar cssFn = jQuery.fn[ name ];\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn speed == null || typeof speed === \"boolean\" ?\n\t\t\tcssFn.apply( this, arguments ) :\n\t\t\tthis.animate( genFx( name, true ), speed, easing, callback );\n\t};\n} );\n\n// Generate shortcuts for custom animations\njQuery.each( {\n\tslideDown: genFx( \"show\" ),\n\tslideUp: genFx( \"hide\" ),\n\tslideToggle: genFx( \"toggle\" ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n} );\n\njQuery.timers = [];\njQuery.fx.tick = function() {\n\tvar timer,\n\t\ti = 0,\n\t\ttimers = jQuery.timers;\n\n\tfxNow = Date.now();\n\n\tfor ( ; i < timers.length; i++ ) {\n\t\ttimer = timers[ i ];\n\n\t\t// Run the timer and safely remove it when done (allowing for external removal)\n\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\ttimers.splice( i--, 1 );\n\t\t}\n\t}\n\n\tif ( !timers.length ) {\n\t\tjQuery.fx.stop();\n\t}\n\tfxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n\tjQuery.timers.push( timer );\n\tjQuery.fx.start();\n};\n\njQuery.fx.interval = 13;\njQuery.fx.start = function() {\n\tif ( inProgress ) {\n\t\treturn;\n\t}\n\n\tinProgress = true;\n\tschedule();\n};\n\njQuery.fx.stop = function() {\n\tinProgress = null;\n};\n\njQuery.fx.speeds = {\n\tslow: 600,\n\tfast: 200,\n\n\t// Default speed\n\t_default: 400\n};\n\n\n// Based off of the plugin by Clint Helfers, with permission.\n// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/\njQuery.fn.delay = function( time, type ) {\n\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\ttype = type || \"fx\";\n\n\treturn this.queue( type, function( next, hooks ) {\n\t\tvar timeout = window.setTimeout( next, time );\n\t\thooks.stop = function() {\n\t\t\twindow.clearTimeout( timeout );\n\t\t};\n\t} );\n};\n\n\n( function() {\n\tvar input = document.createElement( \"input\" ),\n\t\tselect = document.createElement( \"select\" ),\n\t\topt = select.appendChild( document.createElement( \"option\" ) );\n\n\tinput.type = \"checkbox\";\n\n\t// Support: Android <=4.3 only\n\t// Default value for a checkbox should be \"on\"\n\tsupport.checkOn = input.value !== \"\";\n\n\t// Support: IE <=11 only\n\t// Must access selectedIndex to make default options select\n\tsupport.optSelected = opt.selected;\n\n\t// Support: IE <=11 only\n\t// An input loses its value after becoming a radio\n\tinput = document.createElement( \"input\" );\n\tinput.value = \"t\";\n\tinput.type = \"radio\";\n\tsupport.radioValue = input.value === \"t\";\n} )();\n\n\nvar boolHook,\n\tattrHandle = jQuery.expr.attrHandle;\n\njQuery.fn.extend( {\n\tattr: function( name, value ) {\n\t\treturn access( this, jQuery.attr, name, value, arguments.length > 1 );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tattr: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set attributes on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\t// Attribute hooks are determined by the lowercase version\n\t\t// Grab necessary hook if one is defined\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\t\t\thooks = jQuery.attrHooks[ name.toLowerCase() ] ||\n\t\t\t\t( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, value + \"\" );\n\t\t\treturn value;\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tret = jQuery.find.attr( elem, name );\n\n\t\t// Non-existent attributes return null, we normalize to undefined\n\t\treturn ret == null ? undefined : ret;\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( !support.radioValue && value === \"radio\" &&\n\t\t\t\t\tnodeName( elem, \"input\" ) ) {\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar name,\n\t\t\ti = 0,\n\n\t\t\t// Attribute names can contain non-HTML whitespace characters\n\t\t\t// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n\t\t\tattrNames = value && value.match( rnothtmlwhite );\n\n\t\tif ( attrNames && elem.nodeType === 1 ) {\n\t\t\twhile ( ( name = attrNames[ i++ ] ) ) {\n\t\t\t\telem.removeAttribute( name );\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Hooks for boolean attributes\nboolHook = {\n\tset: function( elem, value, name ) {\n\t\tif ( value === false ) {\n\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\telem.setAttribute( name, name );\n\t\t}\n\t\treturn name;\n\t}\n};\n\njQuery.each( jQuery.expr.match.bool.source.match( /\\w+/g ), function( _i, name ) {\n\tvar getter = attrHandle[ name ] || jQuery.find.attr;\n\n\tattrHandle[ name ] = function( elem, name, isXML ) {\n\t\tvar ret, handle,\n\t\t\tlowercaseName = name.toLowerCase();\n\n\t\tif ( !isXML ) {\n\n\t\t\t// Avoid an infinite loop by temporarily removing this function from the getter\n\t\t\thandle = attrHandle[ lowercaseName ];\n\t\t\tattrHandle[ lowercaseName ] = ret;\n\t\t\tret = getter( elem, name, isXML ) != null ?\n\t\t\t\tlowercaseName :\n\t\t\t\tnull;\n\t\t\tattrHandle[ lowercaseName ] = handle;\n\t\t}\n\t\treturn ret;\n\t};\n} );\n\n\n\n\nvar rfocusable = /^(?:input|select|textarea|button)$/i,\n\trclickable = /^(?:a|area)$/i;\n\njQuery.fn.extend( {\n\tprop: function( name, value ) {\n\t\treturn access( this, jQuery.prop, name, value, arguments.length > 1 );\n\t},\n\n\tremoveProp: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tdelete this[ jQuery.propFix[ name ] || name ];\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set properties on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\treturn ( elem[ name ] = value );\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\treturn elem[ name ];\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// Support: IE <=9 - 11 only\n\t\t\t\t// elem.tabIndex doesn't always return the\n\t\t\t\t// correct value when it hasn't been explicitly set\n\t\t\t\t// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\t// Use proper attribute retrieval(#12072)\n\t\t\t\tvar tabindex = jQuery.find.attr( elem, \"tabindex\" );\n\n\t\t\t\tif ( tabindex ) {\n\t\t\t\t\treturn parseInt( tabindex, 10 );\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\trfocusable.test( elem.nodeName ) ||\n\t\t\t\t\trclickable.test( elem.nodeName ) &&\n\t\t\t\t\telem.href\n\t\t\t\t) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\"\n\t}\n} );\n\n// Support: IE <=11 only\n// Accessing the selectedIndex property\n// forces the browser to respect setting selected\n// on the option\n// The getter ensures a default option is selected\n// when in an optgroup\n// eslint rule \"no-unused-expressions\" is disabled for this code\n// since it considers such accessions noop\nif ( !support.optSelected ) {\n\tjQuery.propHooks.selected = {\n\t\tget: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent && parent.parentNode ) {\n\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tset: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent ) {\n\t\t\t\tparent.selectedIndex;\n\n\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\njQuery.each( [\n\t\"tabIndex\",\n\t\"readOnly\",\n\t\"maxLength\",\n\t\"cellSpacing\",\n\t\"cellPadding\",\n\t\"rowSpan\",\n\t\"colSpan\",\n\t\"useMap\",\n\t\"frameBorder\",\n\t\"contentEditable\"\n], function() {\n\tjQuery.propFix[ this.toLowerCase() ] = this;\n} );\n\n\n\n\n\t// Strip and collapse whitespace according to HTML spec\n\t// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace\n\tfunction stripAndCollapse( value ) {\n\t\tvar tokens = value.match( rnothtmlwhite ) || [];\n\t\treturn tokens.join( \" \" );\n\t}\n\n\nfunction getClass( elem ) {\n\treturn elem.getAttribute && elem.getAttribute( \"class\" ) || \"\";\n}\n\nfunction classesToArray( value ) {\n\tif ( Array.isArray( value ) ) {\n\t\treturn value;\n\t}\n\tif ( typeof value === \"string\" ) {\n\t\treturn value.match( rnothtmlwhite ) || [];\n\t}\n\treturn [];\n}\n\njQuery.fn.extend( {\n\taddClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\t\t\t\t\t\tif ( cur.indexOf( \" \" + clazz + \" \" ) < 0 ) {\n\t\t\t\t\t\t\tcur += clazz + \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( !arguments.length ) {\n\t\t\treturn this.attr( \"class\", \"\" );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\n\t\t\t\t// This expression is here for better compressibility (see addClass)\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\n\t\t\t\t\t\t// Remove *all* instances\n\t\t\t\t\t\twhile ( cur.indexOf( \" \" + clazz + \" \" ) > -1 ) {\n\t\t\t\t\t\t\tcur = cur.replace( \" \" + clazz + \" \", \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value,\n\t\t\tisValidValue = type === \"string\" || Array.isArray( value );\n\n\t\tif ( typeof stateVal === \"boolean\" && isValidValue ) {\n\t\t\treturn stateVal ? this.addClass( value ) : this.removeClass( value );\n\t\t}\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).toggleClass(\n\t\t\t\t\tvalue.call( this, i, getClass( this ), stateVal ),\n\t\t\t\t\tstateVal\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar className, i, self, classNames;\n\n\t\t\tif ( isValidValue ) {\n\n\t\t\t\t// Toggle individual class names\n\t\t\t\ti = 0;\n\t\t\t\tself = jQuery( this );\n\t\t\t\tclassNames = classesToArray( value );\n\n\t\t\t\twhile ( ( className = classNames[ i++ ] ) ) {\n\n\t\t\t\t\t// Check each className given, space separated list\n\t\t\t\t\tif ( self.hasClass( className ) ) {\n\t\t\t\t\t\tself.removeClass( className );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.addClass( className );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Toggle whole class name\n\t\t\t} else if ( value === undefined || type === \"boolean\" ) {\n\t\t\t\tclassName = getClass( this );\n\t\t\t\tif ( className ) {\n\n\t\t\t\t\t// Store className if set\n\t\t\t\t\tdataPriv.set( this, \"__className__\", className );\n\t\t\t\t}\n\n\t\t\t\t// If the element has a class name or if we're passed `false`,\n\t\t\t\t// then remove the whole classname (if there was one, the above saved it).\n\t\t\t\t// Otherwise bring back whatever was previously saved (if anything),\n\t\t\t\t// falling back to the empty string if nothing was stored.\n\t\t\t\tif ( this.setAttribute ) {\n\t\t\t\t\tthis.setAttribute( \"class\",\n\t\t\t\t\t\tclassName || value === false ?\n\t\t\t\t\t\t\t\"\" :\n\t\t\t\t\t\t\tdataPriv.get( this, \"__className__\" ) || \"\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className, elem,\n\t\t\ti = 0;\n\n\t\tclassName = \" \" + selector + \" \";\n\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\tif ( elem.nodeType === 1 &&\n\t\t\t\t( \" \" + stripAndCollapse( getClass( elem ) ) + \" \" ).indexOf( className ) > -1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n} );\n\n\n\n\nvar rreturn = /\\r/g;\n\njQuery.fn.extend( {\n\tval: function( value ) {\n\t\tvar hooks, ret, valueIsFunction,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.type ] ||\n\t\t\t\t\tjQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n\t\t\t\tif ( hooks &&\n\t\t\t\t\t\"get\" in hooks &&\n\t\t\t\t\t( ret = hooks.get( elem, \"value\" ) ) !== undefined\n\t\t\t\t) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\t// Handle most common string cases\n\t\t\t\tif ( typeof ret === \"string\" ) {\n\t\t\t\t\treturn ret.replace( rreturn, \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Handle cases where value is null/undef or number\n\t\t\t\treturn ret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tvalueIsFunction = isFunction( value );\n\n\t\treturn this.each( function( i ) {\n\t\t\tvar val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\tval = value.call( this, i, jQuery( this ).val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\n\t\t\t} else if ( Array.isArray( val ) ) {\n\t\t\t\tval = jQuery.map( val, function( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !( \"set\" in hooks ) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\tvar val = jQuery.find.attr( elem, \"value\" );\n\t\t\t\treturn val != null ?\n\t\t\t\t\tval :\n\n\t\t\t\t\t// Support: IE <=10 - 11 only\n\t\t\t\t\t// option.text throws exceptions (#14686, #14858)\n\t\t\t\t\t// Strip and collapse whitespace\n\t\t\t\t\t// https://html.spec.whatwg.org/#strip-and-collapse-whitespace\n\t\t\t\t\tstripAndCollapse( jQuery.text( elem ) );\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, option, i,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tone = elem.type === \"select-one\",\n\t\t\t\t\tvalues = one ? null : [],\n\t\t\t\t\tmax = one ? index + 1 : options.length;\n\n\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\ti = max;\n\n\t\t\t\t} else {\n\t\t\t\t\ti = one ? index : 0;\n\t\t\t\t}\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t// IE8-9 doesn't update selected after form reset (#2551)\n\t\t\t\t\tif ( ( option.selected || i === index ) &&\n\n\t\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\t\t!option.disabled &&\n\t\t\t\t\t\t\t( !option.parentNode.disabled ||\n\t\t\t\t\t\t\t\t!nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar optionSet, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tvalues = jQuery.makeArray( value ),\n\t\t\t\t\ti = options.length;\n\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t/* eslint-disable no-cond-assign */\n\n\t\t\t\t\tif ( option.selected =\n\t\t\t\t\t\tjQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1\n\t\t\t\t\t) {\n\t\t\t\t\t\toptionSet = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* eslint-enable no-cond-assign */\n\t\t\t\t}\n\n\t\t\t\t// Force browsers to behave consistently when non-matching value is set\n\t\t\t\tif ( !optionSet ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Radios and checkboxes getter/setter\njQuery.each( [ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = {\n\t\tset: function( elem, value ) {\n\t\t\tif ( Array.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );\n\t\t\t}\n\t\t}\n\t};\n\tif ( !support.checkOn ) {\n\t\tjQuery.valHooks[ this ].get = function( elem ) {\n\t\t\treturn elem.getAttribute( \"value\" ) === null ? \"on\" : elem.value;\n\t\t};\n\t}\n} );\n\n\n\n\n// Return jQuery for attributes-only inclusion\n\n\nsupport.focusin = \"onfocusin\" in window;\n\n\nvar rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\tstopPropagationCallback = function( e ) {\n\t\te.stopPropagation();\n\t};\n\njQuery.extend( jQuery.event, {\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special, lastElement,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\n\t\tcur = lastElement = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split( \".\" );\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.rnamespace = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tlastElement = cur;\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = ( dataPriv.get( cur, \"events\" ) || Object.create( null ) )[ event.type ] &&\n\t\t\t\tdataPriv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( ( !special._default ||\n\t\t\t\tspecial._default.apply( eventPath.pop(), data ) === false ) &&\n\t\t\t\tacceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\tif ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.addEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\telem[ type ]();\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.removeEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Piggyback on a donor event to simulate a different one\n\t// Used only for `focus(in | out)` events\n\tsimulate: function( type, elem, event ) {\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true\n\t\t\t}\n\t\t);\n\n\t\tjQuery.event.trigger( e, null, elem );\n\t}\n\n} );\n\njQuery.fn.extend( {\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t} );\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[ 0 ];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n} );\n\n\n// Support: Firefox <=44\n// Firefox doesn't have focus(in | out) events\n// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787\n//\n// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1\n// focus(in | out) events fire after focus & blur events,\n// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order\n// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857\nif ( !support.focusin ) {\n\tjQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );\n\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\n\t\t\t\t// Handle: regular nodes (via `this.ownerDocument`), window\n\t\t\t\t// (via `this.document`) & document (via `this`).\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdataPriv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdataPriv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdataPriv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t} );\n}\nvar location = window.location;\n\nvar nonce = { guid: Date.now() };\n\nvar rquery = ( /\\?/ );\n\n\n\n// Cross-browser xml parsing\njQuery.parseXML = function( data ) {\n\tvar xml, parserErrorElem;\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\n\t// Support: IE 9 - 11 only\n\t// IE throws on parseFromString with invalid input.\n\ttry {\n\t\txml = ( new window.DOMParser() ).parseFromString( data, \"text/xml\" );\n\t} catch ( e ) {}\n\n\tparserErrorElem = xml && xml.getElementsByTagName( \"parsererror\" )[ 0 ];\n\tif ( !xml || parserErrorElem ) {\n\t\tjQuery.error( \"Invalid XML: \" + (\n\t\t\tparserErrorElem ?\n\t\t\t\tjQuery.map( parserErrorElem.childNodes, function( el ) {\n\t\t\t\t\treturn el.textContent;\n\t\t\t\t} ).join( \"\\n\" ) :\n\t\t\t\tdata\n\t\t) );\n\t}\n\treturn xml;\n};\n\n\nvar\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,\n\trsubmittable = /^(?:input|select|textarea|keygen)/i;\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tvar name;\n\n\tif ( Array.isArray( obj ) ) {\n\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\n\t\t\t\t// Item is non-scalar (array or object), encode its numeric index.\n\t\t\t\tbuildParams(\n\t\t\t\t\tprefix + \"[\" + ( typeof v === \"object\" && v != null ? i : \"\" ) + \"]\",\n\t\t\t\t\tv,\n\t\t\t\t\ttraditional,\n\t\t\t\t\tadd\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t} else if ( !traditional && toType( obj ) === \"object\" ) {\n\n\t\t// Serialize object item.\n\t\tfor ( name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// Serialize an array of form elements or a set of\n// key/values into a query string\njQuery.param = function( a, traditional ) {\n\tvar prefix,\n\t\ts = [],\n\t\tadd = function( key, valueOrFunction ) {\n\n\t\t\t// If value is a function, invoke it and use its return value\n\t\t\tvar value = isFunction( valueOrFunction ) ?\n\t\t\t\tvalueOrFunction() :\n\t\t\t\tvalueOrFunction;\n\n\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" +\n\t\t\t\tencodeURIComponent( value == null ? \"\" : value );\n\t\t};\n\n\tif ( a == null ) {\n\t\treturn \"\";\n\t}\n\n\t// If an array was passed in, assume that it is an array of form elements.\n\tif ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\n\t\t// Serialize the form elements\n\t\tjQuery.each( a, function() {\n\t\t\tadd( this.name, this.value );\n\t\t} );\n\n\t} else {\n\n\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t// did it), otherwise encode params recursively.\n\t\tfor ( prefix in a ) {\n\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t}\n\t}\n\n\t// Return the resulting serialization\n\treturn s.join( \"&\" );\n};\n\njQuery.fn.extend( {\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\tserializeArray: function() {\n\t\treturn this.map( function() {\n\n\t\t\t// Can add propHook for \"elements\" to filter or add form elements\n\t\t\tvar elements = jQuery.prop( this, \"elements\" );\n\t\t\treturn elements ? jQuery.makeArray( elements ) : this;\n\t\t} ).filter( function() {\n\t\t\tvar type = this.type;\n\n\t\t\t// Use .is( \":disabled\" ) so that fieldset[disabled] works\n\t\t\treturn this.name && !jQuery( this ).is( \":disabled\" ) &&\n\t\t\t\trsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&\n\t\t\t\t( this.checked || !rcheckableType.test( type ) );\n\t\t} ).map( function( _i, elem ) {\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\tif ( val == null ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\treturn jQuery.map( val, function( val ) {\n\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t} ).get();\n\t}\n} );\n\n\nvar\n\tr20 = /%20/g,\n\trhash = /#.*$/,\n\trantiCache = /([?&])_=[^&]*/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)$/mg,\n\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t * - BEFORE asking for a transport\n\t * - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n\tallTypes = \"*/\".concat( \"*\" ),\n\n\t// Anchor tag for parsing the document origin\n\toriginAnchor = document.createElement( \"a\" );\n\noriginAnchor.href = location.href;\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tvar dataType,\n\t\t\ti = 0,\n\t\t\tdataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];\n\n\t\tif ( isFunction( func ) ) {\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\twhile ( ( dataType = dataTypes[ i++ ] ) ) {\n\n\t\t\t\t// Prepend if requested\n\t\t\t\tif ( dataType[ 0 ] === \"+\" ) {\n\t\t\t\t\tdataType = dataType.slice( 1 ) || \"*\";\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );\n\n\t\t\t\t// Otherwise append\n\t\t\t\t} else {\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).push( func );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {\n\n\tvar inspected = {},\n\t\tseekingTransport = ( structure === transports );\n\n\tfunction inspect( dataType ) {\n\t\tvar selected;\n\t\tinspected[ dataType ] = true;\n\t\tjQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {\n\t\t\tvar dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );\n\t\t\tif ( typeof dataTypeOrTransport === \"string\" &&\n\t\t\t\t!seekingTransport && !inspected[ dataTypeOrTransport ] ) {\n\n\t\t\t\toptions.dataTypes.unshift( dataTypeOrTransport );\n\t\t\t\tinspect( dataTypeOrTransport );\n\t\t\t\treturn false;\n\t\t\t} else if ( seekingTransport ) {\n\t\t\t\treturn !( selected = dataTypeOrTransport );\n\t\t\t}\n\t\t} );\n\t\treturn selected;\n\t}\n\n\treturn inspect( options.dataTypes[ 0 ] ) || !inspected[ \"*\" ] && inspect( \"*\" );\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n\n\treturn target;\n}\n\n/* Handles responses to an ajax request:\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar ct, type, finalDataType, firstDataType,\n\t\tcontents = s.contents,\n\t\tdataTypes = s.dataTypes;\n\n\t// Remove auto dataType and get content-type in the process\n\twhile ( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"Content-Type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[ 0 ] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n/* Chain conversions given the request and the original response\n * Also sets the responseXXX fields on the jqXHR instance\n */\nfunction ajaxConvert( s, response, jqXHR, isSuccess ) {\n\tvar conv2, current, conv, tmp, prev,\n\t\tconverters = {},\n\n\t\t// Work with a copy of dataTypes in case we need to modify it for conversion\n\t\tdataTypes = s.dataTypes.slice();\n\n\t// Create converters map with lowercased keys\n\tif ( dataTypes[ 1 ] ) {\n\t\tfor ( conv in s.converters ) {\n\t\t\tconverters[ conv.toLowerCase() ] = s.converters[ conv ];\n\t\t}\n\t}\n\n\tcurrent = dataTypes.shift();\n\n\t// Convert to each sequential dataType\n\twhile ( current ) {\n\n\t\tif ( s.responseFields[ current ] ) {\n\t\t\tjqXHR[ s.responseFields[ current ] ] = response;\n\t\t}\n\n\t\t// Apply the dataFilter if provided\n\t\tif ( !prev && isSuccess && s.dataFilter ) {\n\t\t\tresponse = s.dataFilter( response, s.dataType );\n\t\t}\n\n\t\tprev = current;\n\t\tcurrent = dataTypes.shift();\n\n\t\tif ( current ) {\n\n\t\t\t// There's only work to do if current dataType is non-auto\n\t\t\tif ( current === \"*\" ) {\n\n\t\t\t\tcurrent = prev;\n\n\t\t\t// Convert response if prev dataType is non-auto and differs from current\n\t\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t\t// Seek a direct converter\n\t\t\t\tconv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n\t\t\t\t// If none found, seek a pair\n\t\t\t\tif ( !conv ) {\n\t\t\t\t\tfor ( conv2 in converters ) {\n\n\t\t\t\t\t\t// If conv2 outputs current\n\t\t\t\t\t\ttmp = conv2.split( \" \" );\n\t\t\t\t\t\tif ( tmp[ 1 ] === current ) {\n\n\t\t\t\t\t\t\t// If prev can be converted to accepted input\n\t\t\t\t\t\t\tconv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n\t\t\t\t\t\t\t\tconverters[ \"* \" + tmp[ 0 ] ];\n\t\t\t\t\t\t\tif ( conv ) {\n\n\t\t\t\t\t\t\t\t// Condense equivalence converters\n\t\t\t\t\t\t\t\tif ( conv === true ) {\n\t\t\t\t\t\t\t\t\tconv = converters[ conv2 ];\n\n\t\t\t\t\t\t\t\t// Otherwise, insert the intermediate dataType\n\t\t\t\t\t\t\t\t} else if ( converters[ conv2 ] !== true ) {\n\t\t\t\t\t\t\t\t\tcurrent = tmp[ 0 ];\n\t\t\t\t\t\t\t\t\tdataTypes.unshift( tmp[ 1 ] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply converter (if not an equivalence)\n\t\t\t\tif ( conv !== true ) {\n\n\t\t\t\t\t// Unless errors are allowed to bubble, catch and return them\n\t\t\t\t\tif ( conv && s.throws ) {\n\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tstate: \"parsererror\",\n\t\t\t\t\t\t\t\terror: conv ? e : \"No conversion from \" + prev + \" to \" + current\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { state: \"success\", data: response };\n}\n\njQuery.extend( {\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\ttype: \"GET\",\n\t\tisLocal: rlocalProtocol.test( location.protocol ),\n\t\tglobal: true,\n\t\tprocessData: true,\n\t\tasync: true,\n\t\tcontentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\tthrows: false,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\t\"*\": allTypes,\n\t\t\ttext: \"text/plain\",\n\t\t\thtml: \"text/html\",\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\tjson: \"application/json, text/javascript\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /\\bxml\\b/,\n\t\t\thtml: /\\bhtml/,\n\t\t\tjson: /\\bjson\\b/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\",\n\t\t\tjson: \"responseJSON\"\n\t\t},\n\n\t\t// Data converters\n\t\t// Keys separate source (or catchall \"*\") and destination types with a single space\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": JSON.parse,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\turl: true,\n\t\t\tcontext: true\n\t\t}\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\treturn settings ?\n\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :\n\n\t\t\t// Extending ajaxSettings\n\t\t\tajaxExtend( jQuery.ajaxSettings, target );\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar transport,\n\n\t\t\t// URL without anti-cache param\n\t\t\tcacheURL,\n\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\n\t\t\t// Url cleanup var\n\t\t\turlAnchor,\n\n\t\t\t// Request state (becomes false upon send and true upon completion)\n\t\t\tcompleted,\n\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\n\t\t\t// Loop variable\n\t\t\ti,\n\n\t\t\t// uncached part of the url\n\t\t\tuncached,\n\n\t\t\t// Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\n\t\t\t// Context for global events is callbackContext if it is a DOM node or jQuery collection\n\t\t\tglobalEventContext = s.context &&\n\t\t\t\t( callbackContext.nodeType || callbackContext.jquery ) ?\n\t\t\t\tjQuery( callbackContext ) :\n\t\t\t\tjQuery.event,\n\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\n\t\t\t// Default abort message\n\t\t\tstrAbort = \"canceled\",\n\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( completed ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile ( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[ 1 ].toLowerCase() + \" \" ] =\n\t\t\t\t\t\t\t\t\t( responseHeaders[ match[ 1 ].toLowerCase() + \" \" ] || [] )\n\t\t\t\t\t\t\t\t\t\t.concat( match[ 2 ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() + \" \" ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match == null ? null : match.join( \", \" );\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn completed ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\tname = requestHeadersNames[ name.toLowerCase() ] =\n\t\t\t\t\t\t\trequestHeadersNames[ name.toLowerCase() ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Status-dependent callbacks\n\t\t\t\tstatusCode: function( map ) {\n\t\t\t\t\tvar code;\n\t\t\t\t\tif ( map ) {\n\t\t\t\t\t\tif ( completed ) {\n\n\t\t\t\t\t\t\t// Execute the appropriate callbacks\n\t\t\t\t\t\t\tjqXHR.always( map[ jqXHR.status ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Lazy-add the new callbacks in a way that preserves old ones\n\t\t\t\t\t\t\tfor ( code in map ) {\n\t\t\t\t\t\t\t\tstatusCode[ code ] = [ statusCode[ code ], map[ code ] ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tvar finalText = statusText || strAbort;\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( finalText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, finalText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\n\t\t// Add protocol if not provided (prefilters might expect it)\n\t\t// Handle falsy url in the settings object (#10093: consistency with old signature)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url || location.href ) + \"\" )\n\t\t\t.replace( rprotocol, location.protocol + \"//\" );\n\n\t\t// Alias method option to type as per ticket #12004\n\t\ts.type = options.method || options.type || s.method || s.type;\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = ( s.dataType || \"*\" ).toLowerCase().match( rnothtmlwhite ) || [ \"\" ];\n\n\t\t// A cross-domain request is in order when the origin doesn't match the current origin.\n\t\tif ( s.crossDomain == null ) {\n\t\t\turlAnchor = document.createElement( \"a\" );\n\n\t\t\t// Support: IE <=8 - 11, Edge 12 - 15\n\t\t\t// IE throws exception on accessing the href property if url is malformed,\n\t\t\t// e.g. http://example.com:80x/\n\t\t\ttry {\n\t\t\t\turlAnchor.href = s.url;\n\n\t\t\t\t// Support: IE <=8 - 11 only\n\t\t\t\t// Anchor's host property isn't correctly set when s.url is relative\n\t\t\t\turlAnchor.href = urlAnchor.href;\n\t\t\t\ts.crossDomain = originAnchor.protocol + \"//\" + originAnchor.host !==\n\t\t\t\t\turlAnchor.protocol + \"//\" + urlAnchor.host;\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// If there is an error parsing the URL, assume it is crossDomain,\n\t\t\t\t// it can be rejected by the transport if it is invalid\n\t\t\t\ts.crossDomain = true;\n\t\t\t}\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefilter, stop there\n\t\tif ( completed ) {\n\t\t\treturn jqXHR;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\t// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)\n\t\tfireGlobals = jQuery.event && s.global;\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Save the URL in case we're toying with the If-Modified-Since\n\t\t// and/or If-None-Match header later on\n\t\t// Remove hash to simplify url manipulation\n\t\tcacheURL = s.url.replace( rhash, \"\" );\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// Remember the hash so we can put it back\n\t\t\tuncached = s.url.slice( cacheURL.length );\n\n\t\t\t// If data is available and should be processed, append data to url\n\t\t\tif ( s.data && ( s.processData || typeof s.data === \"string\" ) ) {\n\t\t\t\tcacheURL += ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + s.data;\n\n\t\t\t\t// #9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Add or update anti-cache param if needed\n\t\t\tif ( s.cache === false ) {\n\t\t\t\tcacheURL = cacheURL.replace( rantiCache, \"$1\" );\n\t\t\t\tuncached = ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + \"_=\" + ( nonce.guid++ ) +\n\t\t\t\t\tuncached;\n\t\t\t}\n\n\t\t\t// Put hash and anti-cache on the URL that will be requested (gh-1732)\n\t\t\ts.url = cacheURL + uncached;\n\n\t\t// Change '%20' to '+' if this is encoded form body content (gh-2658)\n\t\t} else if ( s.data && s.processData &&\n\t\t\t( s.contentType || \"\" ).indexOf( \"application/x-www-form-urlencoded\" ) === 0 ) {\n\t\t\ts.data = s.data.replace( r20, \"+\" );\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tif ( jQuery.lastModified[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ cacheURL ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ cacheURL ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[ 0 ] ] +\n\t\t\t\t\t( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend &&\n\t\t\t( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {\n\n\t\t\t// Abort if not done already and return\n\t\t\treturn jqXHR.abort();\n\t\t}\n\n\t\t// Aborting is no longer a cancellation\n\t\tstrAbort = \"abort\";\n\n\t\t// Install callbacks on deferreds\n\t\tcompleteDeferred.add( s.complete );\n\t\tjqXHR.done( s.success );\n\t\tjqXHR.fail( s.error );\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\n\t\t\t// If request was aborted inside ajaxSend, stop there\n\t\t\tif ( completed ) {\n\t\t\t\treturn jqXHR;\n\t\t\t}\n\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = window.setTimeout( function() {\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcompleted = false;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// Rethrow post-completion exceptions\n\t\t\t\tif ( completed ) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\n\t\t\t\t// Propagate others as results\n\t\t\t\tdone( -1, e );\n\t\t\t}\n\t\t}\n\n\t\t// Callback for when everything is done\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\t\t\tvar isSuccess, success, error, response, modified,\n\t\t\t\tstatusText = nativeStatusText;\n\n\t\t\t// Ignore repeat invocations\n\t\t\tif ( completed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcompleted = true;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\twindow.clearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\t// Determine if successful\n\t\t\tisSuccess = status >= 200 && status < 300 || status === 304;\n\n\t\t\t// Get response data\n\t\t\tif ( responses ) {\n\t\t\t\tresponse = ajaxHandleResponses( s, jqXHR, responses );\n\t\t\t}\n\n\t\t\t// Use a noop converter for missing script but not if jsonp\n\t\t\tif ( !isSuccess &&\n\t\t\t\tjQuery.inArray( \"script\", s.dataTypes ) > -1 &&\n\t\t\t\tjQuery.inArray( \"json\", s.dataTypes ) < 0 ) {\n\t\t\t\ts.converters[ \"text script\" ] = function() {};\n\t\t\t}\n\n\t\t\t// Convert no matter what (that way responseXXX fields are always set)\n\t\t\tresponse = ajaxConvert( s, response, jqXHR, isSuccess );\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( isSuccess ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"Last-Modified\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.lastModified[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"etag\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.etag[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if no content\n\t\t\t\tif ( status === 204 || s.type === \"HEAD\" ) {\n\t\t\t\t\tstatusText = \"nocontent\";\n\n\t\t\t\t// if not modified\n\t\t\t\t} else if ( status === 304 ) {\n\t\t\t\t\tstatusText = \"notmodified\";\n\n\t\t\t\t// If we have data, let's convert it\n\t\t\t\t} else {\n\t\t\t\t\tstatusText = response.state;\n\t\t\t\t\tsuccess = response.data;\n\t\t\t\t\terror = response.error;\n\t\t\t\t\tisSuccess = !error;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Extract error from statusText and normalize for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( status || !statusText ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( isSuccess ? \"ajaxSuccess\" : \"ajaxError\",\n\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t}\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( _i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\n\t\t// Shift arguments if data argument was omitted\n\t\tif ( isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\t// The url can be an options object (which then must have .url)\n\t\treturn jQuery.ajax( jQuery.extend( {\n\t\t\turl: url,\n\t\t\ttype: method,\n\t\t\tdataType: type,\n\t\t\tdata: data,\n\t\t\tsuccess: callback\n\t\t}, jQuery.isPlainObject( url ) && url ) );\n\t};\n} );\n\njQuery.ajaxPrefilter( function( s ) {\n\tvar i;\n\tfor ( i in s.headers ) {\n\t\tif ( i.toLowerCase() === \"content-type\" ) {\n\t\t\ts.contentType = s.headers[ i ] || \"\";\n\t\t}\n\t}\n} );\n\n\njQuery._evalUrl = function( url, options, doc ) {\n\treturn jQuery.ajax( {\n\t\turl: url,\n\n\t\t// Make this explicit, since user can override this through ajaxSetup (#11264)\n\t\ttype: \"GET\",\n\t\tdataType: \"script\",\n\t\tcache: true,\n\t\tasync: false,\n\t\tglobal: false,\n\n\t\t// Only evaluate the response if it is successful (gh-4126)\n\t\t// dataFilter is not invoked for failure responses, so using it instead\n\t\t// of the default converter is kludgy but it works.\n\t\tconverters: {\n\t\t\t\"text script\": function() {}\n\t\t},\n\t\tdataFilter: function( response ) {\n\t\t\tjQuery.globalEval( response, options, doc );\n\t\t}\n\t} );\n};\n\n\njQuery.fn.extend( {\n\twrapAll: function( html ) {\n\t\tvar wrap;\n\n\t\tif ( this[ 0 ] ) {\n\t\t\tif ( isFunction( html ) ) {\n\t\t\t\thtml = html.call( this[ 0 ] );\n\t\t\t}\n\n\t\t\t// The elements to wrap the target around\n\t\t\twrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );\n\n\t\t\tif ( this[ 0 ].parentNode ) {\n\t\t\t\twrap.insertBefore( this[ 0 ] );\n\t\t\t}\n\n\t\t\twrap.map( function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstElementChild ) {\n\t\t\t\t\telem = elem.firstElementChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t} ).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapInner( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t} );\n\t},\n\n\twrap: function( html ) {\n\t\tvar htmlIsFunction = isFunction( html );\n\n\t\treturn this.each( function( i ) {\n\t\t\tjQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );\n\t\t} );\n\t},\n\n\tunwrap: function( selector ) {\n\t\tthis.parent( selector ).not( \"body\" ).each( function() {\n\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t} );\n\t\treturn this;\n\t}\n} );\n\n\njQuery.expr.pseudos.hidden = function( elem ) {\n\treturn !jQuery.expr.pseudos.visible( elem );\n};\njQuery.expr.pseudos.visible = function( elem ) {\n\treturn !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n\n\n\n\njQuery.ajaxSettings.xhr = function() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch ( e ) {}\n};\n\nvar xhrSuccessStatus = {\n\n\t\t// File protocol always yields status code 0, assume 200\n\t\t0: 200,\n\n\t\t// Support: IE <=9 only\n\t\t// #1450: sometimes IE returns 1223 when it should be 204\n\t\t1223: 204\n\t},\n\txhrSupported = jQuery.ajaxSettings.xhr();\n\nsupport.cors = !!xhrSupported && ( \"withCredentials\" in xhrSupported );\nsupport.ajax = xhrSupported = !!xhrSupported;\n\njQuery.ajaxTransport( function( options ) {\n\tvar callback, errorCallback;\n\n\t// Cross domain only allowed if supported through XMLHttpRequest\n\tif ( support.cors || xhrSupported && !options.crossDomain ) {\n\t\treturn {\n\t\t\tsend: function( headers, complete ) {\n\t\t\t\tvar i,\n\t\t\t\t\txhr = options.xhr();\n\n\t\t\t\txhr.open(\n\t\t\t\t\toptions.type,\n\t\t\t\t\toptions.url,\n\t\t\t\t\toptions.async,\n\t\t\t\t\toptions.username,\n\t\t\t\t\toptions.password\n\t\t\t\t);\n\n\t\t\t\t// Apply custom fields if provided\n\t\t\t\tif ( options.xhrFields ) {\n\t\t\t\t\tfor ( i in options.xhrFields ) {\n\t\t\t\t\t\txhr[ i ] = options.xhrFields[ i ];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Override mime type if needed\n\t\t\t\tif ( options.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\txhr.overrideMimeType( options.mimeType );\n\t\t\t\t}\n\n\t\t\t\t// X-Requested-With header\n\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\tif ( !options.crossDomain && !headers[ \"X-Requested-With\" ] ) {\n\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t}\n\n\t\t\t\t// Set headers\n\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t}\n\n\t\t\t\t// Callback\n\t\t\t\tcallback = function( type ) {\n\t\t\t\t\treturn function() {\n\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\tcallback = errorCallback = xhr.onload =\n\t\t\t\t\t\t\t\txhr.onerror = xhr.onabort = xhr.ontimeout =\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = null;\n\n\t\t\t\t\t\t\tif ( type === \"abort\" ) {\n\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t} else if ( type === \"error\" ) {\n\n\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t// On a manual native abort, IE9 throws\n\t\t\t\t\t\t\t\t// errors on any property access that is not readyState\n\t\t\t\t\t\t\t\tif ( typeof xhr.status !== \"number\" ) {\n\t\t\t\t\t\t\t\t\tcomplete( 0, \"error\" );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcomplete(\n\n\t\t\t\t\t\t\t\t\t\t// File: protocol always yields status 0; see #8605, #14207\n\t\t\t\t\t\t\t\t\t\txhr.status,\n\t\t\t\t\t\t\t\t\t\txhr.statusText\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcomplete(\n\t\t\t\t\t\t\t\t\txhrSuccessStatus[ xhr.status ] || xhr.status,\n\t\t\t\t\t\t\t\t\txhr.statusText,\n\n\t\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t\t// IE9 has no XHR2 but throws on binary (trac-11426)\n\t\t\t\t\t\t\t\t\t// For XHR2 non-text, let the caller handle it (gh-2498)\n\t\t\t\t\t\t\t\t\t( xhr.responseType || \"text\" ) !== \"text\" ||\n\t\t\t\t\t\t\t\t\ttypeof xhr.responseText !== \"string\" ?\n\t\t\t\t\t\t\t\t\t\t{ binary: xhr.response } :\n\t\t\t\t\t\t\t\t\t\t{ text: xhr.responseText },\n\t\t\t\t\t\t\t\t\txhr.getAllResponseHeaders()\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\t// Listen to events\n\t\t\t\txhr.onload = callback();\n\t\t\t\terrorCallback = xhr.onerror = xhr.ontimeout = callback( \"error\" );\n\n\t\t\t\t// Support: IE 9 only\n\t\t\t\t// Use onreadystatechange to replace onabort\n\t\t\t\t// to handle uncaught aborts\n\t\t\t\tif ( xhr.onabort !== undefined ) {\n\t\t\t\t\txhr.onabort = errorCallback;\n\t\t\t\t} else {\n\t\t\t\t\txhr.onreadystatechange = function() {\n\n\t\t\t\t\t\t// Check readyState before timeout as it changes\n\t\t\t\t\t\tif ( xhr.readyState === 4 ) {\n\n\t\t\t\t\t\t\t// Allow onerror to be called first,\n\t\t\t\t\t\t\t// but that will not handle a native abort\n\t\t\t\t\t\t\t// Also, save errorCallback to a variable\n\t\t\t\t\t\t\t// as xhr.onerror cannot be accessed\n\t\t\t\t\t\t\twindow.setTimeout( function() {\n\t\t\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\t\t\terrorCallback();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Create the abort callback\n\t\t\t\tcallback = callback( \"abort\" );\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do send the request (this may raise an exception)\n\t\t\t\t\txhr.send( options.hasContent && options.data || null );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// #14683: Only rethrow if this hasn't been notified as an error yet\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\n// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)\njQuery.ajaxPrefilter( function( s ) {\n\tif ( s.crossDomain ) {\n\t\ts.contents.script = false;\n\t}\n} );\n\n// Install script dataType\njQuery.ajaxSetup( {\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, \" +\n\t\t\t\"application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /\\b(?:java|ecma)script\\b/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n} );\n\n// Handle cache's special case and crossDomain\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function( s ) {\n\n\t// This transport only deals with cross domain or forced-by-attrs requests\n\tif ( s.crossDomain || s.scriptAttrs ) {\n\t\tvar script, callback;\n\t\treturn {\n\t\t\tsend: function( _, complete ) {\n\t\t\t\tscript = jQuery( \"<script>\" )\n\t\t\t\t\t.attr( s.scriptAttrs || {} )\n\t\t\t\t\t.prop( { charset: s.scriptCharset, src: s.url } )\n\t\t\t\t\t.on( \"load error\", callback = function( evt ) {\n\t\t\t\t\t\tscript.remove();\n\t\t\t\t\t\tcallback = null;\n\t\t\t\t\t\tif ( evt ) {\n\t\t\t\t\t\t\tcomplete( evt.type === \"error\" ? 404 : 200, evt.type );\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\n\t\t\t\t// Use native DOM manipulation to avoid our domManip AJAX trickery\n\t\t\t\tdocument.head.appendChild( script[ 0 ] );\n\t\t\t},\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\nvar oldCallbacks = [],\n\trjsonp = /(=)\\?(?=&|$)|\\?\\?/;\n\n// Default jsonp settings\njQuery.ajaxSetup( {\n\tjsonp: \"callback\",\n\tjsonpCallback: function() {\n\t\tvar callback = oldCallbacks.pop() || ( jQuery.expando + \"_\" + ( nonce.guid++ ) );\n\t\tthis[ callback ] = true;\n\t\treturn callback;\n\t}\n} );\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n\tvar callbackName, overwritten, responseContainer,\n\t\tjsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?\n\t\t\t\"url\" :\n\t\t\ttypeof s.data === \"string\" &&\n\t\t\t\t( s.contentType || \"\" )\n\t\t\t\t\t.indexOf( \"application/x-www-form-urlencoded\" ) === 0 &&\n\t\t\t\trjsonp.test( s.data ) && \"data\"\n\t\t);\n\n\t// Handle iff the expected data type is \"jsonp\" or we have a parameter to set\n\tif ( jsonProp || s.dataTypes[ 0 ] === \"jsonp\" ) {\n\n\t\t// Get callback name, remembering preexisting value associated with it\n\t\tcallbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?\n\t\t\ts.jsonpCallback() :\n\t\t\ts.jsonpCallback;\n\n\t\t// Insert callback into url or form data\n\t\tif ( jsonProp ) {\n\t\t\ts[ jsonProp ] = s[ jsonProp ].replace( rjsonp, \"$1\" + callbackName );\n\t\t} else if ( s.jsonp !== false ) {\n\t\t\ts.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.jsonp + \"=\" + callbackName;\n\t\t}\n\n\t\t// Use data converter to retrieve json after script execution\n\t\ts.converters[ \"script json\" ] = function() {\n\t\t\tif ( !responseContainer ) {\n\t\t\t\tjQuery.error( callbackName + \" was not called\" );\n\t\t\t}\n\t\t\treturn responseContainer[ 0 ];\n\t\t};\n\n\t\t// Force json dataType\n\t\ts.dataTypes[ 0 ] = \"json\";\n\n\t\t// Install callback\n\t\toverwritten = window[ callbackName ];\n\t\twindow[ callbackName ] = function() {\n\t\t\tresponseContainer = arguments;\n\t\t};\n\n\t\t// Clean-up function (fires after converters)\n\t\tjqXHR.always( function() {\n\n\t\t\t// If previous value didn't exist - remove it\n\t\t\tif ( overwritten === undefined ) {\n\t\t\t\tjQuery( window ).removeProp( callbackName );\n\n\t\t\t// Otherwise restore preexisting value\n\t\t\t} else {\n\t\t\t\twindow[ callbackName ] = overwritten;\n\t\t\t}\n\n\t\t\t// Save back as free\n\t\t\tif ( s[ callbackName ] ) {\n\n\t\t\t\t// Make sure that re-using the options doesn't screw things around\n\t\t\t\ts.jsonpCallback = originalSettings.jsonpCallback;\n\n\t\t\t\t// Save the callback name for future use\n\t\t\t\toldCallbacks.push( callbackName );\n\t\t\t}\n\n\t\t\t// Call if it was a function and we have a response\n\t\t\tif ( responseContainer && isFunction( overwritten ) ) {\n\t\t\t\toverwritten( responseContainer[ 0 ] );\n\t\t\t}\n\n\t\t\tresponseContainer = overwritten = undefined;\n\t\t} );\n\n\t\t// Delegate to script\n\t\treturn \"script\";\n\t}\n} );\n\n\n\n\n// Support: Safari 8 only\n// In Safari 8 documents created via document.implementation.createHTMLDocument\n// collapse sibling forms: the second one becomes a child of the first one.\n// Because of that, this security measure has to be disabled in Safari 8.\n// https://bugs.webkit.org/show_bug.cgi?id=137337\nsupport.createHTMLDocument = ( function() {\n\tvar body = document.implementation.createHTMLDocument( \"\" ).body;\n\tbody.innerHTML = \"<form></form><form></form>\";\n\treturn body.childNodes.length === 2;\n} )();\n\n\n// Argument \"data\" should be string of html\n// context (optional): If specified, the fragment will be created in this context,\n// defaults to document\n// keepScripts (optional): If true, will include scripts passed in the html string\njQuery.parseHTML = function( data, context, keepScripts ) {\n\tif ( typeof data !== \"string\" ) {\n\t\treturn [];\n\t}\n\tif ( typeof context === \"boolean\" ) {\n\t\tkeepScripts = context;\n\t\tcontext = false;\n\t}\n\n\tvar base, parsed, scripts;\n\n\tif ( !context ) {\n\n\t\t// Stop scripts or inline event handlers from being executed immediately\n\t\t// by using document.implementation\n\t\tif ( support.createHTMLDocument ) {\n\t\t\tcontext = document.implementation.createHTMLDocument( \"\" );\n\n\t\t\t// Set the base href for the created document\n\t\t\t// so any parsed elements with URLs\n\t\t\t// are based on the document's URL (gh-2965)\n\t\t\tbase = context.createElement( \"base\" );\n\t\t\tbase.href = document.location.href;\n\t\t\tcontext.head.appendChild( base );\n\t\t} else {\n\t\t\tcontext = document;\n\t\t}\n\t}\n\n\tparsed = rsingleTag.exec( data );\n\tscripts = !keepScripts && [];\n\n\t// Single tag\n\tif ( parsed ) {\n\t\treturn [ context.createElement( parsed[ 1 ] ) ];\n\t}\n\n\tparsed = buildFragment( [ data ], context, scripts );\n\n\tif ( scripts && scripts.length ) {\n\t\tjQuery( scripts ).remove();\n\t}\n\n\treturn jQuery.merge( [], parsed.childNodes );\n};\n\n\n/**\n * Load a url into a page\n */\njQuery.fn.load = function( url, params, callback ) {\n\tvar selector, type, response,\n\t\tself = this,\n\t\toff = url.indexOf( \" \" );\n\n\tif ( off > -1 ) {\n\t\tselector = stripAndCollapse( url.slice( off ) );\n\t\turl = url.slice( 0, off );\n\t}\n\n\t// If it's a function\n\tif ( isFunction( params ) ) {\n\n\t\t// We assume that it's the callback\n\t\tcallback = params;\n\t\tparams = undefined;\n\n\t// Otherwise, build a param string\n\t} else if ( params && typeof params === \"object\" ) {\n\t\ttype = \"POST\";\n\t}\n\n\t// If we have elements to modify, make the request\n\tif ( self.length > 0 ) {\n\t\tjQuery.ajax( {\n\t\t\turl: url,\n\n\t\t\t// If \"type\" variable is undefined, then \"GET\" method will be used.\n\t\t\t// Make value of this field explicit since\n\t\t\t// user can override it through ajaxSetup method\n\t\t\ttype: type || \"GET\",\n\t\t\tdataType: \"html\",\n\t\t\tdata: params\n\t\t} ).done( function( responseText ) {\n\n\t\t\t// Save response for use in complete callback\n\t\t\tresponse = arguments;\n\n\t\t\tself.html( selector ?\n\n\t\t\t\t// If a selector was specified, locate the right elements in a dummy div\n\t\t\t\t// Exclude scripts to avoid IE 'Permission Denied' errors\n\t\t\t\tjQuery( \"<div>\" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :\n\n\t\t\t\t// Otherwise use the full result\n\t\t\t\tresponseText );\n\n\t\t// If the request succeeds, this function gets \"data\", \"status\", \"jqXHR\"\n\t\t// but they are ignored because response was set above.\n\t\t// If it fails, this function gets \"jqXHR\", \"status\", \"error\"\n\t\t} ).always( callback && function( jqXHR, status ) {\n\t\t\tself.each( function() {\n\t\t\t\tcallback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );\n\t\t\t} );\n\t\t} );\n\t}\n\n\treturn this;\n};\n\n\n\n\njQuery.expr.pseudos.animated = function( elem ) {\n\treturn jQuery.grep( jQuery.timers, function( fn ) {\n\t\treturn elem === fn.elem;\n\t} ).length;\n};\n\n\n\n\njQuery.offset = {\n\tsetOffset: function( elem, options, i ) {\n\t\tvar curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,\n\t\t\tposition = jQuery.css( elem, \"position\" ),\n\t\t\tcurElem = jQuery( elem ),\n\t\t\tprops = {};\n\n\t\t// Set position first, in-case top/left are set even on static elem\n\t\tif ( position === \"static\" ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\n\t\tcurOffset = curElem.offset();\n\t\tcurCSSTop = jQuery.css( elem, \"top\" );\n\t\tcurCSSLeft = jQuery.css( elem, \"left\" );\n\t\tcalculatePosition = ( position === \"absolute\" || position === \"fixed\" ) &&\n\t\t\t( curCSSTop + curCSSLeft ).indexOf( \"auto\" ) > -1;\n\n\t\t// Need to be able to calculate position if either\n\t\t// top or left is auto and position is either absolute or fixed\n\t\tif ( calculatePosition ) {\n\t\t\tcurPosition = curElem.position();\n\t\t\tcurTop = curPosition.top;\n\t\t\tcurLeft = curPosition.left;\n\n\t\t} else {\n\t\t\tcurTop = parseFloat( curCSSTop ) || 0;\n\t\t\tcurLeft = parseFloat( curCSSLeft ) || 0;\n\t\t}\n\n\t\tif ( isFunction( options ) ) {\n\n\t\t\t// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)\n\t\t\toptions = options.call( elem, i, jQuery.extend( {}, curOffset ) );\n\t\t}\n\n\t\tif ( options.top != null ) {\n\t\t\tprops.top = ( options.top - curOffset.top ) + curTop;\n\t\t}\n\t\tif ( options.left != null ) {\n\t\t\tprops.left = ( options.left - curOffset.left ) + curLeft;\n\t\t}\n\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\njQuery.fn.extend( {\n\n\t// offset() relates an element's border box to the document origin\n\toffset: function( options ) {\n\n\t\t// Preserve chaining for setter\n\t\tif ( arguments.length ) {\n\t\t\treturn options === undefined ?\n\t\t\t\tthis :\n\t\t\t\tthis.each( function( i ) {\n\t\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t\t} );\n\t\t}\n\n\t\tvar rect, win,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !elem ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Return zeros for disconnected and hidden (display: none) elements (gh-2310)\n\t\t// Support: IE <=11 only\n\t\t// Running getBoundingClientRect on a\n\t\t// disconnected node in IE throws an error\n\t\tif ( !elem.getClientRects().length ) {\n\t\t\treturn { top: 0, left: 0 };\n\t\t}\n\n\t\t// Get document-relative position by adding viewport scroll to viewport-relative gBCR\n\t\trect = elem.getBoundingClientRect();\n\t\twin = elem.ownerDocument.defaultView;\n\t\treturn {\n\t\t\ttop: rect.top + win.pageYOffset,\n\t\t\tleft: rect.left + win.pageXOffset\n\t\t};\n\t},\n\n\t// position() relates an element's margin box to its offset parent's padding box\n\t// This corresponds to the behavior of CSS absolute positioning\n\tposition: function() {\n\t\tif ( !this[ 0 ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar offsetParent, offset, doc,\n\t\t\telem = this[ 0 ],\n\t\t\tparentOffset = { top: 0, left: 0 };\n\n\t\t// position:fixed elements are offset from the viewport, which itself always has zero offset\n\t\tif ( jQuery.css( elem, \"position\" ) === \"fixed\" ) {\n\n\t\t\t// Assume position:fixed implies availability of getBoundingClientRect\n\t\t\toffset = elem.getBoundingClientRect();\n\n\t\t} else {\n\t\t\toffset = this.offset();\n\n\t\t\t// Account for the *real* offset parent, which can be the document or its root element\n\t\t\t// when a statically positioned element is identified\n\t\t\tdoc = elem.ownerDocument;\n\t\t\toffsetParent = elem.offsetParent || doc.documentElement;\n\t\t\twhile ( offsetParent &&\n\t\t\t\t( offsetParent === doc.body || offsetParent === doc.documentElement ) &&\n\t\t\t\tjQuery.css( offsetParent, \"position\" ) === \"static\" ) {\n\n\t\t\t\toffsetParent = offsetParent.parentNode;\n\t\t\t}\n\t\t\tif ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {\n\n\t\t\t\t// Incorporate borders into its offset, since they are outside its content origin\n\t\t\t\tparentOffset = jQuery( offsetParent ).offset();\n\t\t\t\tparentOffset.top += jQuery.css( offsetParent, \"borderTopWidth\", true );\n\t\t\t\tparentOffset.left += jQuery.css( offsetParent, \"borderLeftWidth\", true );\n\t\t\t}\n\t\t}\n\n\t\t// Subtract parent offsets and element margins\n\t\treturn {\n\t\t\ttop: offset.top - parentOffset.top - jQuery.css( elem, \"marginTop\", true ),\n\t\t\tleft: offset.left - parentOffset.left - jQuery.css( elem, \"marginLeft\", true )\n\t\t};\n\t},\n\n\t// This method will return documentElement in the following cases:\n\t// 1) For the element inside the iframe without offsetParent, this method will return\n\t// documentElement of the parent window\n\t// 2) For the hidden or detached element\n\t// 3) For body or html element, i.e. in case of the html node - it will return itself\n\t//\n\t// but those exceptions were never presented as a real life use-cases\n\t// and might be considered as more preferable results.\n\t//\n\t// This logic, however, is not guaranteed and can change at any point in the future\n\toffsetParent: function() {\n\t\treturn this.map( function() {\n\t\t\tvar offsetParent = this.offsetParent;\n\n\t\t\twhile ( offsetParent && jQuery.css( offsetParent, \"position\" ) === \"static\" ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\n\t\t\treturn offsetParent || documentElement;\n\t\t} );\n\t}\n} );\n\n// Create scrollLeft and scrollTop methods\njQuery.each( { scrollLeft: \"pageXOffset\", scrollTop: \"pageYOffset\" }, function( method, prop ) {\n\tvar top = \"pageYOffset\" === prop;\n\n\tjQuery.fn[ method ] = function( val ) {\n\t\treturn access( this, function( elem, method, val ) {\n\n\t\t\t// Coalesce documents and windows\n\t\t\tvar win;\n\t\t\tif ( isWindow( elem ) ) {\n\t\t\t\twin = elem;\n\t\t\t} else if ( elem.nodeType === 9 ) {\n\t\t\t\twin = elem.defaultView;\n\t\t\t}\n\n\t\t\tif ( val === undefined ) {\n\t\t\t\treturn win ? win[ prop ] : elem[ method ];\n\t\t\t}\n\n\t\t\tif ( win ) {\n\t\t\t\twin.scrollTo(\n\t\t\t\t\t!top ? val : win.pageXOffset,\n\t\t\t\t\ttop ? val : win.pageYOffset\n\t\t\t\t);\n\n\t\t\t} else {\n\t\t\t\telem[ method ] = val;\n\t\t\t}\n\t\t}, method, val, arguments.length );\n\t};\n} );\n\n// Support: Safari <=7 - 9.1, Chrome <=37 - 49\n// Add the top/left cssHooks using jQuery.fn.position\n// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084\n// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347\n// getComputedStyle returns percent when specified for top/left/bottom/right;\n// rather than make the css module depend on the offset module, just check for it here\njQuery.each( [ \"top\", \"left\" ], function( _i, prop ) {\n\tjQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,\n\t\tfunction( elem, computed ) {\n\t\t\tif ( computed ) {\n\t\t\t\tcomputed = curCSS( elem, prop );\n\n\t\t\t\t// If curCSS returns percentage, fallback to offset\n\t\t\t\treturn rnumnonpx.test( computed ) ?\n\t\t\t\t\tjQuery( elem ).position()[ prop ] + \"px\" :\n\t\t\t\t\tcomputed;\n\t\t\t}\n\t\t}\n\t);\n} );\n\n\n// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods\njQuery.each( { Height: \"height\", Width: \"width\" }, function( name, type ) {\n\tjQuery.each( {\n\t\tpadding: \"inner\" + name,\n\t\tcontent: type,\n\t\t\"\": \"outer\" + name\n\t}, function( defaultExtra, funcName ) {\n\n\t\t// Margin is only for outerHeight, outerWidth\n\t\tjQuery.fn[ funcName ] = function( margin, value ) {\n\t\t\tvar chainable = arguments.length && ( defaultExtra || typeof margin !== \"boolean\" ),\n\t\t\t\textra = defaultExtra || ( margin === true || value === true ? \"margin\" : \"border\" );\n\n\t\t\treturn access( this, function( elem, type, value ) {\n\t\t\t\tvar doc;\n\n\t\t\t\tif ( isWindow( elem ) ) {\n\n\t\t\t\t\t// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)\n\t\t\t\t\treturn funcName.indexOf( \"outer\" ) === 0 ?\n\t\t\t\t\t\telem[ \"inner\" + name ] :\n\t\t\t\t\t\telem.document.documentElement[ \"client\" + name ];\n\t\t\t\t}\n\n\t\t\t\t// Get document width or height\n\t\t\t\tif ( elem.nodeType === 9 ) {\n\t\t\t\t\tdoc = elem.documentElement;\n\n\t\t\t\t\t// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],\n\t\t\t\t\t// whichever is greatest\n\t\t\t\t\treturn Math.max(\n\t\t\t\t\t\telem.body[ \"scroll\" + name ], doc[ \"scroll\" + name ],\n\t\t\t\t\t\telem.body[ \"offset\" + name ], doc[ \"offset\" + name ],\n\t\t\t\t\t\tdoc[ \"client\" + name ]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn value === undefined ?\n\n\t\t\t\t\t// Get width or height on the element, requesting but not forcing parseFloat\n\t\t\t\t\tjQuery.css( elem, type, extra ) :\n\n\t\t\t\t\t// Set width or height on the element\n\t\t\t\t\tjQuery.style( elem, type, value, extra );\n\t\t\t}, type, chainable ? margin : undefined, chainable );\n\t\t};\n\t} );\n} );\n\n\njQuery.each( [\n\t\"ajaxStart\",\n\t\"ajaxStop\",\n\t\"ajaxComplete\",\n\t\"ajaxError\",\n\t\"ajaxSuccess\",\n\t\"ajaxSend\"\n], function( _i, type ) {\n\tjQuery.fn[ type ] = function( fn ) {\n\t\treturn this.on( type, fn );\n\t};\n} );\n\n\n\n\njQuery.fn.extend( {\n\n\tbind: function( types, data, fn ) {\n\t\treturn this.on( types, null, data, fn );\n\t},\n\tunbind: function( types, fn ) {\n\t\treturn this.off( types, null, fn );\n\t},\n\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.on( types, selector, data, fn );\n\t},\n\tundelegate: function( selector, types, fn ) {\n\n\t\t// ( namespace ) or ( selector, types [, fn] )\n\t\treturn arguments.length === 1 ?\n\t\t\tthis.off( selector, \"**\" ) :\n\t\t\tthis.off( types, selector || \"**\", fn );\n\t},\n\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n} );\n\njQuery.each(\n\t( \"blur focus focusin focusout resize scroll click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup contextmenu\" ).split( \" \" ),\n\tfunction( _i, name ) {\n\n\t\t// Handle event binding\n\t\tjQuery.fn[ name ] = function( data, fn ) {\n\t\t\treturn arguments.length > 0 ?\n\t\t\t\tthis.on( name, null, data, fn ) :\n\t\t\t\tthis.trigger( name );\n\t\t};\n\t}\n);\n\n\n\n\n// Support: Android <=4.0 only\n// Make sure we trim BOM and NBSP\nvar rtrim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n\n// Bind a function to a context, optionally partially applying any\n// arguments.\n// jQuery.proxy is deprecated to promote standards (specifically Function#bind)\n// However, it is not slated for removal any time soon\njQuery.proxy = function( fn, context ) {\n\tvar tmp, args, proxy;\n\n\tif ( typeof context === \"string\" ) {\n\t\ttmp = fn[ context ];\n\t\tcontext = fn;\n\t\tfn = tmp;\n\t}\n\n\t// Quick check to determine if target is callable, in the spec\n\t// this throws a TypeError, but we will just return undefined.\n\tif ( !isFunction( fn ) ) {\n\t\treturn undefined;\n\t}\n\n\t// Simulated bind\n\targs = slice.call( arguments, 2 );\n\tproxy = function() {\n\t\treturn fn.apply( context || this, args.concat( slice.call( arguments ) ) );\n\t};\n\n\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\tproxy.guid = fn.guid = fn.guid || jQuery.guid++;\n\n\treturn proxy;\n};\n\njQuery.holdReady = function( hold ) {\n\tif ( hold ) {\n\t\tjQuery.readyWait++;\n\t} else {\n\t\tjQuery.ready( true );\n\t}\n};\njQuery.isArray = Array.isArray;\njQuery.parseJSON = JSON.parse;\njQuery.nodeName = nodeName;\njQuery.isFunction = isFunction;\njQuery.isWindow = isWindow;\njQuery.camelCase = camelCase;\njQuery.type = toType;\n\njQuery.now = Date.now;\n\njQuery.isNumeric = function( obj ) {\n\n\t// As of jQuery 3.0, isNumeric is limited to\n\t// strings and numbers (primitives or objects)\n\t// that can be coerced to finite numbers (gh-2662)\n\tvar type = jQuery.type( obj );\n\treturn ( type === \"number\" || type === \"string\" ) &&\n\n\t\t// parseFloat NaNs numeric-cast false positives (\"\")\n\t\t// ...but misinterprets leading-number strings, particularly hex literals (\"0x...\")\n\t\t// subtraction forces infinities to NaN\n\t\t!isNaN( obj - parseFloat( obj ) );\n};\n\njQuery.trim = function( text ) {\n\treturn text == null ?\n\t\t\"\" :\n\t\t( text + \"\" ).replace( rtrim, \"\" );\n};\n\n\n\n// Register as a named AMD module, since jQuery can be concatenated with other\n// files that may use define, but not via a proper concatenation script that\n// understands anonymous AMD modules. A named AMD is safest and most robust\n// way to register. Lowercase jquery is used because AMD module names are\n// derived from file names, and jQuery is normally delivered in a lowercase\n// file name. Do this after creating the global so that if an AMD module wants\n// to call noConflict to hide this version of jQuery, it will work.\n\n// Note that for maximum portability, libraries that are not jQuery should\n// declare themselves as anonymous modules, and avoid setting a global if an\n// AMD loader is present. jQuery is a special case. For more information, see\n// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon\n\nif ( typeof define === \"function\" && define.amd ) {\n\tdefine( \"jquery\", [], function() {\n\t\treturn jQuery;\n\t} );\n}\n\n\n\n\nvar\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$;\n\njQuery.noConflict = function( deep ) {\n\tif ( window.$ === jQuery ) {\n\t\twindow.$ = _$;\n\t}\n\n\tif ( deep && window.jQuery === jQuery ) {\n\t\twindow.jQuery = _jQuery;\n\t}\n\n\treturn jQuery;\n};\n\n// Expose jQuery and $ identifiers, even in AMD\n// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)\n// and CommonJS for browser emulators (#13566)\nif ( typeof noGlobal === \"undefined\" ) {\n\twindow.jQuery = window.$ = jQuery;\n}\n\n\n\n\nreturn jQuery;\n} );\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substr(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp:<pt> is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substr(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","/**\r\n * video工具类\r\n * @author xiyongqiang\r\n * @version 1.4.3\r\n * @update 2019-12-13\r\n */\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD\n define(['jquery', './lib/janus', 'webrtc-adapter'], factory);\n } else if (typeof exports === 'object') {\n // Node, CommonJS之类的\n module.exports = factory(require('jquery'), require('./lib/janus'), require('webrtc-adapter'));\n } else {\n // 浏览器全局变量(root 即 window)\n root.scooper = root.scooper || {};\n root.scooper.video = factory(root.jQuery, root.Janus, root.adapter);\n }\n})(this, function ($, Janus, adapter) {\n \"use strict\";\n\n window.console = window.console || function () {\n var c = {};\n\n c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile = c.clear = c.exception = c.trace = c.assert = function () {};\n\n return c;\n }();\n /**\r\n * 立即执行函数形式\r\n * 兼容IE8 indexOf\r\n */\n\n\n (function () {\n if (Array.prototype.indexOf) {\n return;\n }\n\n Array.prototype.indexOf = function (item) {\n var len = this.length >>> 0; //保证结构为非负数\n\n for (var i = 0; i < len; i++) {\n if (i in this && this[i] === item) {\n return i;\n }\n }\n\n return -1;\n };\n })();\n /**\r\n * 闭包\r\n * 为事件添加唯一标识字符串\r\n */\n\n\n var guid = function () {\n var id = 1;\n return function () {\n return 'SCVIDEO_ID_' + (++id).toString();\n };\n }();\n /**\r\n * 公共方法\r\n * 判断是否为IE\r\n */\n\n\n function isIE() {\n return !!window.ActiveXObject || \"ActiveXObject\" in window;\n }\n /**\r\n \t * 公共方法\r\n * 克隆对象\r\n * 让新出来的对象是独立的\r\n */\n\n\n function cloneObj(obj) {\n var str,\n newObj = $.isArray(obj) ? [] : {};\n\n if (typeof obj != 'object') {\n return;\n }\n\n if (window.JSON) {\n str = JSON.stringify(obj);\n newObj = JSON.parse(str);\n } else {\n $.each(obj, function (key, val) {\n newObj[key] = typeof val == 'object' ? cloneObj(val) : val;\n });\n }\n\n return newObj;\n }\n /**\r\n * 公共方法\r\n * 提示\r\n */\n\n\n var showPrompt = true;\n\n function promptSuccess(msg, type) {\n console.log(msg); //panduanpromptView弹层对象是否引入\n\n 'undefined' != typeof promptView && showPrompt && promptView.showSuccess(msg);\n }\n\n function promptFailed(msg, type) {\n console.error(msg);\n 'undefined' != typeof promptView && showPrompt && promptView.showFailed(msg);\n }\n\n function promptAlarm(msg, type) {\n console.info(msg);\n 'undefined' != typeof promptView && showPrompt && promptView.showAlarm(msg);\n }\n /**\r\n * 公共方法\r\n * 判断能否获取本地声卡设备\r\n */\n\n\n function checkUserMediaAvailable() {\n return Janus.isGetUserMediaAvailable() && !checkIsHttp() && window.hasAudioInputDevices;\n }\n /**\r\n * 公共方法\r\n * 是否有声卡设备\r\n * 异步方法 结果保存在window.hasAudioInputDevices中 true or false\r\n */\n\n\n function hasMediaDevices() {\n //参数初始化,防止为空报错\n window.checkAudioDevicesStatus = false;\n window.hasAudioInputDevices = false;\n\n if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {\n console.log(\"Not support enumerateDevices() .\");\n return;\n }\n\n var startTime = new Date().getTime(); // 列出相机和麦克风.\n\n navigator.mediaDevices.enumerateDevices().then(function (devices) {\n devices.forEach(function (device) {\n if (device.kind == \"audioinput\") {\n //判断到1个就返回true\n window.hasAudioInputDevices = true;\n }\n });\n window.checkAudioDevicesStatus = true;\n console.log(\"检查音频输入设备总计耗时: \" + (new Date().getTime() - startTime) + \"ms\");\n }).catch(function (err) {\n console.log(err.name + \": \" + err.message);\n });\n }\n /**\r\n * 公共方法\r\n * 判断是否是http\r\n */\n\n\n function checkIsHttp() {\n var protocolStr = document.location.protocol;\n if (protocolStr == \"http:\" && window.location.hostname != \"localhost\") return true;\n return false;\n }\n /**\r\n * 公共方法\r\n * 判断是否是https 和检测http分开的目的是还有本地打开的情况(前缀为file:)\r\n * webrtc规定打开摄像头、麦克风需要加密传输 所以需要再https环境下, 本地打开不需要加密 可以正常获取\r\n * 这也是分开的另一个目的\r\n */\n\n\n function checkIsHttps() {\n var protocolStr = document.location.protocol;\n if (protocolStr == \"https:\") return true;\n return false;\n }\n /**\r\n * 事件对象包装类,通过事件type,加载必要的属性\r\n * @class\r\n */\n\n\n function VideoEvent(type, target) {\n this.type = type;\n this.returnValue = true;\n this.target = target || null;\n this.currentTarget = null;\n }\n /**\r\n * 轮巡工具类\r\n */\n\n\n var PollTimer =\n /** @class */\n function () {\n function PollTimer(cb, time) {\n cb(true); // 首次执行\n\n this.timerId = setInterval(cb, time * 1000);\n }\n\n PollTimer.prototype.stop = function () {\n if (!this.timerId) return;\n clearInterval(this.timerId);\n this.timerId = 0;\n\n if (waitTimerId) {\n clearTimeout(waitTimerId);\n waitTimerId = null;\n }\n };\n\n return PollTimer;\n }();\n\n var waitTimerId = 0;\n\n function wait(time) {\n return new Promise(function (resolve, reject) {\n if (waitTimerId) {\n clearTimeout(waitTimerId);\n waitTimerId = null;\n }\n\n waitTimerId = setTimeout(resolve, time);\n });\n }\n /**\r\n \t * 事件\r\n \t */\n\n\n function Listener() {\n // 在构造函数中进行初始化\n this._listeners = {};\n }\n\n Listener.prototype = {\n constructor: Listener,\n\n /**\r\n * 添加事件\r\n */\n addListener: function (type, handler, key) {\n if (typeof handler != 'function') {\n return;\n }\n\n var t = this._listeners,\n id;\n\n if (typeof key == \"string\" && key) {\n if (/[^\\w\\-]/.test(key)) {\n throw \"nonstandard key:\" + key;\n } else {\n handler.hashCode = key;\n id = key;\n }\n }\n\n type.indexOf(\"on\") != 0 && (type = \"on\" + type);\n typeof t[type] != \"object\" && (t[type] = {});\n id = id || guid();\n handler.hashCode = id;\n t[type][id] = handler;\n },\n\n /**\r\n * 删除事件\r\n */\n removeListener: function (type, handler) {\n type.indexOf(\"on\") != 0 && (type = \"on\" + type);\n var t = this._listeners;\n\n if (!t[type]) {\n return;\n }\n\n if (handler) {\n if (typeof handler == 'function') {\n handler = handler.hashCode;\n } else if (typeof key != \"string\") {\n return;\n }\n\n t[type][handler] && delete t[type][handler];\n } else {\n t[type] = {};\n }\n },\n\n /**\r\n * 派发事件\r\n */\n dispatch: function (e, options) {\n if (typeof e == 'string') {\n e = new VideoEvent(e);\n }\n\n options = options || {};\n\n for (var i in options) {\n e[i] = options[i];\n }\n\n var i,\n t = this._listeners,\n p = e.type;\n e.target || (e.target = this);\n e.currentTarget = this;\n p.indexOf(\"on\") != 0 && (p = \"on\" + p);\n typeof this[p] == 'function' && this[p].apply(this, arguments);\n\n if (typeof t[p] == \"object\") {\n for (i in t[p]) {\n t[p][i].call(this, e);\n }\n }\n\n return e.returnValue;\n }\n };\n var videoListener = new Listener();\n /**\r\n * 使用ocx方式的video工具类\r\n * @author savage\r\n * @version 1.0.0\r\n */\n\n function VideoOcx($dom, opts) {\n if (!isIE()) {\n console.log('视频控件不支持非IE浏览器');\n return this._getTipObj();\n }\n\n if (!opts) {\n console.error('VideoOcx的参数opts不能为空');\n return;\n }\n\n this._init($dom, opts);\n }\n /**\r\n * 使用ocx方式的视频播放的方法\r\n */\n\n\n VideoOcx.prototype = {\n constructor: VideoOcx,\n\n /**\r\n * 播放视频\r\n * 参数:index(视频窗口编号,从0开始) video(视频设备id,即devId) id(标识,空的话使用设备id) opts(其他参数)\r\n * 返回true/false\r\n */\n play: function (index, video, id, opts) {\n /*if (!$.isNumeric(video*1)) {\r\n \t\tthrow new Error('video is not number,id = '+id);\r\n }*/\n console.log(\"play >>> \" + \"video=\" + video + \"; id=\" + id + \"; index=\" + index);\n this.dispatch('beforeplay', {\n index: index,\n video: video,\n id: id\n });\n\n this._obj.SCWebSetCurrentWindow(index);\n\n var playId = this._obj.SCWebRealPlay(video, this._opts.streamType);\n\n console.log(\"play = \" + playId);\n opts = opts || {};\n playId > 0 && (this.videoStatus[index] = {\n video: video,\n id: id,\n playId: playId,\n opts: opts\n }, this.dispatch('playsuccess', this.videoStatus[index]));\n\n if (playId < 0) {\n var name = opts.name || '视频';\n promptFailed(name + '播放失败!');\n }\n\n return playId > 0;\n },\n\n /**\r\n * 在序号最小的空闲窗口播放视频\r\n * 参数:index(视频窗口编号,从0开始) video(视频设备id,即devId) opts(其他参数)\r\n * 返回true/false\r\n */\n playByOrder: function (video, id, opts) {\n var freeWindow = this._getMinIndexFreeWindow(0);\n\n if (freeWindow == -1 || this._getChoiceWindow() != -1 && !this.getInChoiceVideo()) {\n return this.playInChoice(video, id, opts);\n }\n\n return this.play(freeWindow, video, id, opts);\n },\n\n /**\r\n * 扩展视频窗口数的播放方式\r\n */\n playByOrderExpandWindow: function (video, id, opts) {\n var freeWindow = this._getMinIndexFreeWindow(0);\n\n var maxWindows = this._opts.windowsNum || 16;\n\n while (this._opts.windows < maxWindows && freeWindow == -1) {\n var zoom = Math.sqrt(this._opts.windows * 1) + 1;\n\n if (this._opts.windows == 6) {\n this.setWindowsNum(9);\n } else {\n this.setWindowsNum(Math.pow(zoom, 2));\n }\n\n freeWindow = this._getMinIndexFreeWindow(zoom - 1);\n }\n\n if (freeWindow == -1 || this._getChoiceWindow() != -1 && !this.getInChoiceVideo()) {\n return this.playInChoice(video, id, opts);\n }\n\n return this.play(freeWindow, video, id, opts);\n },\n\n /**\r\n * 播放所有视频\r\n */\n playAll: function (array) {\n if (array.length > this._opts.windows) {\n throw new Error('windows is not enough');\n }\n\n var me = this;\n me.closeAll();\n $.each(array, function (i, obj) {\n me.play(i, obj.video, obj.id, obj.opts);\n });\n },\n\n /**\r\n * 在鼠标选中的窗口中播放视频\r\n */\n playInChoice: function (video, id, opts) {\n var index = this._obj.SCWebGetCurrentWindowIndex();\n\n this.videoStatus[index] && this.close(index);\n return this.play(index, video, id, opts);\n },\n\n /**\r\n * 如果被指定的窗口有视频id,则播放\r\n */\n playMulWindows: function (indexs) {\n var me = this;\n $.each(indexs, function (i, index) {\n me.videoStatus[index] && me.play(index, me.videoStatus[index].video, me.videoStatus[index].id);\n });\n },\n\n /** \r\n * 检索当前点的视频是否被播放,被播放则返回播放的视频窗口\r\n */\n isPlaying: function (id) {\n var index = -1;\n $.each(this.videoStatus, function (key, val) {\n if (id == val.id) {\n index = key;\n return false;\n }\n });\n return index;\n },\n\n /** \r\n * 判断指定窗口是否正在播放视频\r\n */\n isPlayingByIndex: function (index) {\n return this.videoStatus[index] && true;\n },\n\n /** \r\n * 获取视频数据\r\n */\n getVideoData: function () {\n return this.videoStatus;\n },\n\n /**\r\n * 关闭语音\r\n * @param index\r\n */\n closeVoice: function (index) {\n this._obj.SCWebVoiceComControl(this.videoStatus[index].video, this.videoStatus[index].playId, 0);\n },\n\n /**\r\n * 根据playId获取窗口\r\n * @param playId\r\n * IE下才有该方法\r\n */\n getWindowByPlayId: function (playId) {\n var index = -1;\n $.each(this.videoStatus, function (key, val) {\n if (playId == val.playId) {\n index = key;\n return false;\n }\n });\n return index;\n },\n\n /**\r\n * 根据窗口获取playId\r\n * @param index\r\n * IE下才有该方法\r\n */\n getPlayIdBywindow: function (index) {\n return this.videoStatus[index].playId;\n },\n\n /**\r\n * 关闭指定窗口的视频\r\n */\n close: function (index, isSave) {\n if (!this.videoStatus[index] || index >= this._opts.windows || this._disableClosed.indexOf(index * 1) > -1) {\n return;\n }\n\n this._obj.SCWebSetCurrentWindow(index);\n\n var close = this._obj.SCWebStopRealPlay(this.videoStatus[index].playId);\n\n console.log(\"close = \" + close);\n\n if (!isSave) {\n this.dispatch('afterclose', this.videoStatus[index]);\n delete this.videoStatus[index];\n }\n },\n\n /**\r\n * 关闭指定视频编号正在播放的视频\r\n */\n closeByVideo: function (video, isSave) {\n var me = this;\n\n if (video == undefined) {\n console.info('video参数错误');\n return;\n }\n\n var videoData = me.videoStatus;\n\n if (!videoData || videoData.length == 0) {\n return;\n }\n\n for (var i in videoData) {\n if (video == videoData[i].video) {\n me.close(videoData[i].index);\n }\n }\n },\n\n /**\r\n * \r\n */\n closeHistory: function (playId) {\n var close = this._obj.SCWebStopRealPlay(playId);\n\n console.log(\"close = \" + close);\n\n var index = this._getChoiceWindow();\n\n delete this.videoStatus[index];\n },\n\n /**\r\n * 关闭所有视频\r\n * TODO 不可关闭\r\n */\n closeAll: function (isSave) {\n var me = this;\n isSave || (this._disableClosed = []);\n\n var windows = me._obj.SCWebGetRealPlayWindows();\n\n console.log('要关闭的视频窗:' + windows);\n\n if (windows) {\n $.each(windows.split(','), function (i, index) {\n me.close(index, isSave);\n console.log('窗口:' + i);\n });\n }\n },\n\n /**\r\n * 获取视频窗口\r\n * @returns\r\n */\n getWindowsPlay: function () {\n var me = this;\n\n var windows = me._obj.SCWebGetRealPlayWindows();\n\n return windows;\n },\n\n /**\r\n * 设置鼠标选中窗口\r\n */\n setChoiceWindow: function (index) {\n this._obj.SCWebSetCurrentWindow(index);\n },\n\n /**\r\n * 获取视频窗口数\r\n */\n getWindowsNum: function () {\n return this._opts.windows;\n },\n\n /**\r\n * 是否还有空闲窗口\r\n */\n hasFreeWindow: function () {\n return this._obj.SCWebGetFreeWindows() != '';\n },\n\n /**\r\n * 设置视频窗口数\r\n */\n setWindowsNum: function (num, extra) {\n var me = this;\n num = num * 1;\n var result;\n\n if (extra) {\n result = me._obj.SCWebDecoderSetExtraScreenMode(num);\n } else {\n result = me._obj.SCWebDecoderSetScreenMode(num);\n }\n\n console.log(\"setWindowsNum >>> \" + \"result=\" + result);\n /*extra\r\n ? me._obj.SCWebDecoderSetExtraScreenMode(num)\r\n : me._obj.SCWebDecoderSetScreenMode(num);*/\n\n num = num == 66 ? 6 : num;\n\n me._changeWindowsAction(num);\n\n me.dispatch('screenchange', {\n windowNums: num\n });\n me._opts.windows = num;\n me._opts.extra = extra;\n },\n\n /**\r\n * 询问是否无可用窗口\r\n */\n isUnavailable: function () {\n return this._opts.windows == 16 && !this.hasFreeWindow();\n },\n disableClosed: function (num) {\n $.isNumeric(num) ? this._disableClosed.push(num) : $.isArray(num) && (this._disableClosed = this._disableClosed.concat(num));\n },\n enableClosed: function () {\n this._disableClosed = [];\n },\n //轮巡就是建立一个轮巡数组,然后根据最大屏幕数去遍历数组,记录当前轮巡下标,并标记该视频已被打开,如果视频关闭后 将视频标记为未关闭,建立一个定时器反复去执行这些动作。\n //如果要执行之前视频的记录保存,那么关闭轮巡的关闭方法就需要重新写,或者关闭的时候加一个参数去判断。\n //云台就是调用 dataHolder方法\n\n /**\r\n * 执行轮巡\r\n */\n startPoll: function (array, time) {\n if (!array || array.length == 0) {\n promptAlarm('请选择视频设备!');\n return false;\n }\n\n time = $.isNumeric(time) ? time : this._opts.pollInterval;\n this._pollArray = array;\n var me = this,\n windowNum = me._opts.windows,\n maxNum = array.length,\n nowNum = 0;\n me.saveList(); //延迟\n\n me.delayTimeId = 0;\n me._polltimer = new PollTimer(function (firstRun) {\n me.closeAll();\n\n if (me.delayTimeId) {\n clearTimeout(me.delayTimeId);\n }\n\n me.delayTimeId = setTimeout(function () {\n for (var i = 0; i < windowNum; i++) {\n if (nowNum == maxNum) {\n nowNum = 0;\n\n if (maxNum < windowNum) {\n break;\n }\n }\n\n me.playByOrder(array[nowNum].video, array[nowNum].id, array[nowNum].opts);\n nowNum++;\n }\n\n if (firstRun) {\n me.dispatch('startpoll', array);\n }\n }, 1000);\n }, time);\n },\n\n /**\r\n * 结束轮巡\r\n */\n stopPoll: function () {\n if (!this._polltimer) {\n return;\n }\n\n this._polltimer.stop();\n\n this._polltimer = undefined;\n console.log('------> 轮巡结束 <-------');\n this.dispatch('stoppoll', {}); //恢复之前打开的视频\n\n this.playSaveList();\n },\n\n /**\r\n * 获取是否在轮巡\r\n */\n isPolling: function () {\n return !!this._polltimer;\n },\n\n /**\r\n * 云台控制\r\n * up \t\t\t\t上\r\n * down\t\t\t\t下\r\n * left\t\t\t\t左\r\n * right\t\t\t右\r\n * upleft\t\t\t上左\r\n * upright\t\t\t上右\r\n * downleft\t\t\t下左\r\n * downright\t\t下右\r\n * zoomin\t\t\t倍率变大\r\n * zoomout\t\t\t倍率变下\r\n * focusnear\t\t焦点+\r\n * focusfar\t\t\t焦点-\r\n * irisopen\t\t\t光圈+\r\n * irisclose\t\t光圈-\r\n * pointset\t\t\t设置预置点\r\n * pointdel\t\t\t删除预置点\r\n * pointgoto\t\t到预置点\r\n * scansetleft\t\t自动扫描左边界\r\n * scansetright\t\t自动扫描又边界\r\n * scansetspeed\t\t设置扫描速度\r\n * scanrun\t\t\t自动扫描运行\r\n * cruiseadd\t\t添加巡航点\r\n * cruisedel\t\t删除巡航点\r\n * cruisespeed\t\t设置巡航点速度\r\n * cruisepausetime\t设置巡航滞留时间\r\n * cruiserun\t\t启动巡航\r\n */\n holder: function (type, opts, isStop) {\n var index = this._getChoiceWindow();\n\n if (!this.videoStatus[index]) {\n promptAlarm('未选中播放的视频!');\n return;\n }\n\n var holdType = 'up down left right upleft upright downleft downright zoomin zoomout focusnear focusfar irisopen irisclose pointset pointdel pointgoto ' + 'scansetleft scansetright scansetspeed scanrun cruiseadd cruisedel cruisespeed cruisepausetime cruiserun';\n var typeVal = Number(holdType.split(' ').indexOf(type)) + Number(1);\n console.log(\"holdType:\" + typeVal + \" type:\" + type + (isStop ? ' 停止' : ''));\n\n if (!typeVal || typeVal <= 0) {\n console.error('异常PTZ指令:' + typeVal);\n }\n\n var video = this.videoStatus[index].video;\n\n if (isStop) {\n //var playerId = this._obj.SCWebGetCurrentPlayID();\n var result = this._obj.SCWebPTZControl(video, 1, typeVal, 0, 0, 0, 0); //var result = this._obj.SCWebPTZControlEx(playerId, 4, 1, 1);\n\n\n console.log(\">>>>yuntai control,stop, video=\" + video + \", result=\" + result);\n return;\n }\n\n var _opts = {\n speed: 200,\n group: 0,\n present: 0,\n time: 0\n };\n $.extend(_opts, opts);\n\n this._obj.SCWebPTZControl(video, 0, typeVal, _opts.speed, _opts.group, 1, _opts.time);\n\n console.log('设备号:' + video + ', 云台移动-->' + type);\n },\n\n /**\r\n * 视频回放\r\n * @param startTime [YYYY-MM-DDTHH:mm:ss]\r\n * @param endTime\r\n * @return playId\r\n */\n playHistory: function (video, startTime, endTime, opts) {\n var me = this;\n\n var playId = me._obj.SCWebHistoryPlay(video, startTime, endTime);\n\n console.log(\"play = \" + playId);\n\n if (playId < 0) {\n var name = opts.name || '视频';\n promptFailed(name + '播放失败!');\n return playId;\n }\n\n var index = me._getChoiceWindow();\n\n opts = opts || {};\n playId > 0 && (me.videoStatus[index] = {\n video: video,\n id: video,\n playId: playId,\n opts: opts,\n isPlayBack: true\n }, me.dispatch('playsuccess', me.videoStatus[index]));\n return playId;\n },\n\n /**\r\n * 历史流控制\r\n * @param {number} playId \r\n * @param {string} playType 播放类型(play && pause)\r\n * @param {numner} playSpeed 播放速率\r\n * @param {string} startTime 回放时间(暂时无用)\r\n */\n historyControl: function (playId, playType, playSpeed, startTime) {\n var contorlType = {\n play: 1,\n pause: 2\n };\n var playCommand = contorlType[playType];\n\n if (!playCommand) {\n console.error('unsupported type: ', playType);\n return;\n }\n\n this._obj.SCWebHistoryControl(playId, playCommand, playSpeed, startTime);\n },\n\n /**\r\n * 历史流控制(原)\r\n */\n _historyControl: function (playId, type, speed, startTime) {\n var contorlType = {\n normal: 1,\n pause: 2,\n 'continue': 3\n };\n var commad = 1;\n type == 'speed' ? speed && speed < 1 ? (commad = 7, speed = 1 / speed) : commad = 6 : commad = contorlType[type];\n\n this._obj.SCWebHistoryControl(playId, commad, speed);\n },\n\n /**\r\n * 跳转历史流\r\n */\n skipHistory: function (playId, time) {\n this._obj.SCWebHistorySeekPlay(playId, time);\n },\n\n /**\r\n * 抓图\r\n * @return 0:失败|1:成功\r\n */\n capturePicture: function (playId) {\n var videoCapImagePath = this._opts.videoCapImagePath ? this._opts.videoCapImagePath : '';\n return this._obj.SCWebCapturePicture(playId, videoCapImagePath, '');\n },\n saveList: function () {\n this._saveList = cloneObj(this.videoStatus);\n },\n playSaveList: function () {\n if (!this._saveList) {\n return;\n }\n\n this.closeAll();\n var me = this;\n $.each(this._saveList, function (index, obj) {\n me.play(index, obj.video, obj.id, obj.opts);\n });\n },\n\n /**\r\n * 获取当前选择窗口的视频video\r\n */\n getInChoiceVideo: function () {\n return this.videoStatus[this._obj.SCWebGetCurrentWindowIndex()];\n },\n\n /**\r\n * 获取当前选择的窗口的playId\r\n */\n getInChoicePlayId: function () {\n return this._obj.SCWebGetCurrentPlayID();\n },\n\n /**\r\n * 设置全屏\r\n */\n setFullScreen: function () {\n this._obj.ScWebFullScreen(1);\n },\n\n /**\r\n * 获取当前视频控件的配置项\r\n */\n getVideoControllerOpts: function () {\n return this._opts;\n },\n\n /**\r\n * 获取截屏数据\r\n */\n getScreenCaptureData: function (index, callback) {\n var msg = 'IE下暂不支持获取截屏数据!';\n console.log(msg);\n throw Error(msg);\n },\n\n /**\r\n * 添加事件\r\n */\n addListener: function (type, handler, key) {\n videoListener.addListener(type, handler, key);\n },\n\n /**\r\n * 删除事件\r\n */\n removeListener: function (type, handler) {\n videoListener.removeListener(type, handler);\n },\n\n /**\r\n * 派发事件\r\n */\n dispatch: function (e, options) {\n videoListener.dispatch(e, options);\n },\n\n /**\r\n * 获取ocx的版本信息\r\n */\n getOcxVersion: function () {\n return this._obj.SCWebDecoderGetVersion();\n },\n\n /*\r\n * 总的初始化\r\n * 参数示例:\t\t\t\r\n * var opts = {\r\n \t\t\t\t\twindows: 4, //初始化的窗口数\r\n \t\t\t\t\tconf: { //配置参数\r\n \t\t\t\t\t\tuser: conf['video.username'], //用户名\r\n \t\t\t\t\t\tpasswd: conf['video.password'], //密码\r\n \t\t\t\t\t\tip: conf['video.ip'], //videoServer ip\r\n \t\t\t\t\t\tport: conf['video.port'], //videoServer port\r\n \t\t\t\t\t\ttoken : token //通讯录令牌,若token非空,则用户名和密码可不传\r\n \t\t\t\t\t},\r\n \t\t\t\t\textra: false,\r\n \t\t\t\t\tstreamType: 0, //码流类型 默认0\r\n \t\t\t\t\topenChangeWindowStrategy: true, //打开策略,切屏:属性为true,则当从大分屏切换到小分屏的时候会保留被关闭的视频,如果未设置 则不保留\r\n \t\t\t\t\tcapImage: true, //是否截屏:属性为true的时候,视频上会浮现控制条,进行截图、云台控制操作\r\n \t\t\t\t\tvideoCapImagePath: conf['video.cap.image.path'] //截屏默认保存的地址\r\n \t\t\t\t\tpollInterval: 30 //轮巡时间,单位秒\r\n };\r\n */\n _init: function ($dom, opts) {\n var me = this; //配置项\n\n me._opts = {\n windows: 4,\n conf: {},\n extra: false,\n ocxName: '__videoDiv',\n streamType: 0,\n openChangeWindowStrategy: false,\n capImage: false,\n videoCapImagePath: '',\n pollInterval: 10\n };\n $.extend(me._opts, opts);\n var windowsArr = me._opts.windowsArr;\n\n if (!windowsArr || windowsArr.length == 0) {\n me._windowList = {\n 1: {},\n 4: {},\n 6: {},\n 9: {},\n 16: {}\n };\n me._windowsArr = [1, 4, 6, 9, 16];\n } else {\n me._windowList = {};\n\n for (var index in windowsArr) {\n var item = windowsArr[index];\n me._windowList[item] = {};\n }\n\n me._windowsArr = windowsArr;\n } //me._windowList = {1:{},2:{},3:{},4:{}}\n\n\n if (!$.isNumeric(me._opts.pollInterval)) {\n console.error('pollInterval should be number');\n return;\n } //是否提示\n\n\n me._opts.showPrompt === false && (showPrompt = false); //事件注册区域\n\n me._addStopHandler();\n\n me._addClickHandler();\n\n me._addHisHandler();\n\n me._addCapPicHandler();\n\n me._addHistoryPlayHandler(); //浏览器刷新/关闭前执行\n\n\n me._beforeUnload(); //当前视频窗口的值\n\n\n me.videoStatus = {}; //不可关闭的窗口\n\n me._disableClosed = new Array(); //动态生成monitor对象\n\n me._obj = $('<OBJECT>').attr({\n id: me._opts.ocxName,\n name: me._opts.ocxName,\n classid: 'clsid:5D62BFBF-7B2D-4E9E-A359-5228400C77BC',\n width: '100%',\n height: '100%'\n });\n $dom.append(me._obj);\n me._obj = me._obj[0];\n var conf = me._opts.conf; //初始化和登录\n\n var init = me._obj.ScWebDecoderInit(0);\n\n console.log('init:' + init); //是否开启抓图\n\n conf.ip == '127.0.0.1' && (conf.ip = location.hostname);\n var login;\n\n if (conf.token && conf.token.length > 0 && me._obj.SCWebDecoderLoginInVS_Token) {\n login = me._obj.SCWebDecoderLoginInVS_Token(conf.token, conf.ip, conf.port);\n } else {\n login = me._obj.SCWebDecoderLoginInVs(conf.user, conf.passwd, conf.ip, conf.port);\n }\n\n console.log('login:' + login); //调整分屏模式\n\n setTimeout(function () {\n //设置云台抓图控制按钮\n me._opts.capImage && me._obj.SCWebSetPtzButtonLayout('');\n me._opts.extra ? me._obj.SCWebDecoderSetExtraScreenMode(me._opts.windows) : me._obj.SCWebDecoderSetScreenMode(me._opts.windows);\n }, 100);\n },\n\n /**\r\n * TODO 添加注释\r\n */\n _getTipObj: function () {\n var obj = {};\n var fncName = 'play playByOrder playAll playInChoice playMulWindows click getWindowsNum setWindowsNum',\n fncName2 = 'isPlaying setChoiceWindow close closeAll enableClosede disableClosed addListener removeListener dispatch';\n $.each(fncName.split(' '), function (i, name) {\n obj[name] = function () {\n alert('不支持非IE环境');\n };\n });\n $.each(fncName2.split(' '), function (i, name) {\n obj[name] = function () {\n console.log('不支持非IE环境');\n };\n });\n obj.type = 'disable';\n return obj;\n },\n\n /**\r\n * 获取编号最小的空闲窗口的编号\r\n */\n _getMinIndexFreeWindow: function (searchedZoom) {\n searchedZoom = searchedZoom || 0;\n var index = Math.ceil(Math.pow(searchedZoom, 2));\n\n for (; index < this._opts.windows; index++) {\n if (!this.videoStatus[index]) {\n return index;\n }\n }\n\n return -1;\n },\n\n /**\r\n * 获取选择的窗口的编号\r\n */\n _getChoiceWindow: function () {\n // \tvar playId = this._obj.SCWebGetCurrentPlayID();\n // \tvar index;\n // \tif (playId > 0) {\n // \t\tindex = this._obj.SCWebGetRealPlayWindows().split(',')[0];\n // \t\tthis.setChoiceWindow(index ? index : 0);\n // \t}\n // \treturn index;\n return this._obj.SCWebGetCurrentWindowIndex();\n },\n\n /**\r\n * 切换分屏\r\n */\n\n /* _changeWindowsAction: function(num) {\r\n \t\t\t\t\tvar me = this,\r\n \t\t\t\t\t\told = me._opts.windows,\r\n \t\t\t\t\t\tnow = num,\r\n \t\t\t\t\t\toldZoom = Math.sqrt(old),\r\n \t\t\t\t\t\tnowZoom = Math.sqrt(now);\r\n \t\t\t\t\tif (old == now) {\r\n \t\t\t\t\t\treturn;\r\n \t\t\t\t\t}\r\n \t\t\t\t\tif (me._opts.openChangeWindowStrategy) {\r\n \t\t\t\t\t\tif (old > now) {\r\n \t\t\t\t\t\t\t\tfor (; now < old ; now++) {\r\n \t\t\t\t\t\t\t\t\tvar tempZoom = Math.ceil(Math.sqrt(now+1));\r\n \r\n \t\t\t\t\t\t\t\t\tme.videoStatus[now] && (me._windowList[tempZoom][now] = {\r\n \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvideo: me.videoStatus[now].video,\r\n \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tid: me.videoStatus[now].id,\r\n \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topts: me.videoStatus[now].opts,\r\n \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\r\n \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tme.close(now));\r\n \t\t\t\t\t\t\t\t}\r\n \t\t\t\t\t\t\t\t\t\r\n \t\t\t\t\t\t\t} else {\r\n \t\t\t\t\t\t\t\twhile (oldZoom < nowZoom) {\r\n \t\t\t\t\t\t\t\t\toldZoom = oldZoom+1;\r\n \t\t\t\t\t\t\t\t\tme._windowList[oldZoom] && $.each(me._windowList[oldZoom],function(index,obj) {\r\n \t\t\t\t\t\t\t\t\t\tme.play(index, obj.video, obj.id, obj.opts);\r\n \t\t\t\t\t\t\t\t\t});\r\n \t\t\t\t\t\t\t\t\tme._windowList[oldZoom] = {};\r\n \t\t\t\t\t\t\t\t}\r\n \t\t\t\t\t\t\t}\r\n \t\t\t\t\t} else {\r\n \t\t\t\t\t\tif (old > now) {\r\n \t\t\t\t\t\t\t\tfor (; now < old ; now++) {\r\n \t\t\t\t\t\t\t\t\tme.videoStatus[now] && me.close(now);\r\n \t\t\t\t\t\t\t\t}\r\n \t\t\t\t\t\t}\r\n \t\t\t\t\t}\r\n \t\t\t\t},*/\n\n /**\r\n * 切换分屏\r\n */\n _changeWindowsAction: function (num) {\n var me = this,\n old = me._opts.windows,\n now = num; // \t\toldZoom = Math.sqrt(old),\n // \t\tnowZoom = Math.sqrt(now);\n\n if (old == now) {\n return;\n }\n\n if (me._opts.openChangeWindowStrategy) {\n if (old > now) {\n for (; now < old; now++) {\n //var tempZoom = Math.ceil(Math.sqrt(now+1));\n var tempZoom = me._getWindowsNum(now + 1);\n\n me.videoStatus[now] && (me._windowList[tempZoom][now] = {\n video: me.videoStatus[now].video,\n id: me.videoStatus[now].id,\n opts: me.videoStatus[now].opts\n }, me.close(now));\n }\n } else {\n while (old < now) {\n old = me._getNextWindowsNum(old);\n me._windowList[old] && $.each(me._windowList[old], function (index, obj) {\n me.play(index, obj.video, obj.id, obj.opts);\n });\n me._windowList[old] = {};\n }\n }\n } else {\n if (old > now) {\n for (; now < old; now++) {\n me.videoStatus[now] && me.close(now);\n }\n }\n }\n },\n\n /**\r\n * 获取当前的分屏数\r\n * now为当前窗口的编号\r\n */\n _getWindowsNum: function (now) {\n var me = this;\n var windowsArr = me._windowsArr;\n\n for (var index in windowsArr) {\n if (now <= windowsArr[index]) {\n return windowsArr[index];\n }\n }\n },\n\n /**\r\n * 获取下一窗口\r\n */\n _getNextWindowsNum: function (old) {\n var me = this;\n var windowsArr = me._windowsArr;\n\n for (var index in windowsArr) {\n if (index == windowsArr.length - 1) {\n return windowsArr[index];\n }\n\n if (old == windowsArr[index]) {\n return windowsArr[Number(index) + Number(1)];\n }\n }\n },\n _addStopHandler: function () {\n var me = this;\n\n window.stopVideo = function (playId, videoId) {\n if (me.getWindowByPlayId(playId) != -1 && me.videoStatus[me.getWindowByPlayId(playId)].isPlayBack) {\n me.closeHistory(playId);\n } else {\n me.close(me.getWindowByPlayId(playId));\n }\n\n me.dispatch('streamoff', {\n playId: playId,\n videoId: videoId\n });\n };\n\n me._regEvent('SCWebStopVideoEvent(playId,videoId)', 'stopVideo(playId,videoId);');\n },\n _addClickHandler: function () {\n var me = this;\n\n window.clickVideo = function (playId) {\n me.dispatch('click', me.videoStatus[me.getWindowByPlayId(playId)]);\n };\n\n me._regEvent('SCWebDecoderClick(playId,type)', 'clickVideo(playId);');\n },\n _addHisHandler: function () {\n var me = this;\n\n window.playbackVideo = function (playId, videoId, historytime) {\n me.dispatch('playback', {\n playId: playId,\n videoId: videoId,\n historyTime: historytime\n });\n };\n\n me._regEvent('SCWebHistoryPlayTime(playId,videoId,historytime)', 'playbackVideo(playId,videoId,historytime);');\n },\n //监听截图事件\n _addCapPicHandler: function () {\n var me = this;\n\n window.getCapPicPath = function (playId, capPicPath, capPicName) {\n me.dispatch('getCapPicPath', {\n playId: playId,\n capPicPath: capPicPath,\n capPicName: capPicName\n });\n };\n\n me._regEvent('SCWebGetCapPicPathName(playId, capPicPath, capPicName)', 'getCapPicPath(playId, capPicPath, capPicName);');\n },\n //监听点击视频框的历史回放\n _addHistoryPlayHandler: function () {\n var me = this;\n\n window.historyPlay = function (playId, videoId) {\n me.dispatch('historyPlay', {\n playId: playId,\n videoId: videoId\n });\n };\n\n me._regEvent('SCWebHistoryPlayEvent(playId, videoId)', 'historyPlay(playId, videoId);');\n },\n _regEvent: function (eventName, handlerName) {\n window._addScriptFlag = true;\n var script;\n\n try {\n script = $('<script>').attr({\n 'for': this._opts.ocxName,\n 'event': eventName\n }).text('if (!_addScriptFlag) {' + handlerName + '}' + '_addScriptFlag = false;');\n } catch (e) {\n script = '<script for=\"' + this._opts.ocxName + '\" event = \"' + eventName + '\" >' + 'if (!_addScriptFlag) {' + handlerName + '}' + '_addScriptFlag = false;' + '</script>';\n }\n\n $('body').append(script);\n },\n _beforeUnload: function () {\n var me = this;\n\n window.onbeforeunload = function (e) {\n me.closeAll();\n\n me._obj.SCWebDecoderDestroy();\n };\n }\n };\n /**\r\n * 使用webRtc方式的video工具类\r\n */\n\n function VideoWebRtc($dom, opts) {\n var _self = this;\n\n if (!opts) {\n console.error('VideoWebRtc的参数opts不能为空');\n return;\n }\n\n hasMediaDevices(); //检测声卡设备为异步方法,耗时在几十毫秒左右,因此需要做延迟\n //默认检查次数为40 间隔为20ms\n\n var count = 0;\n var index = setInterval(function () {\n if (count == 40) {\n clearInterval(index);\n\n _self._init($dom, opts);\n } else {\n if (window.checkAudioDevicesStatus) {\n clearInterval(index);\n\n _self._init($dom, opts);\n }\n }\n\n count++;\n }, 20);\n }\n /**\r\n * 使用WEB-RTC方式的视频播放的方法\r\n */\n\n\n VideoWebRtc.prototype = {\n constructor: VideoWebRtc,\n VIDEO_DATA: [],\n //记录视频各分屏的状态\n\n /**\r\n * 播放视频\r\n * 参数:index(视频窗口编号,从0开始) video(视频设备id,即devId) id(标识,空的话使用设备id) opts(其他参数)\r\n * 返回true/false\r\n */\n play: function (index, video, id, opts) {\n var me = this; // \tif (me.isPlaying(id) != -1) {\n // \t\tconsole.info('该视频正在播放');\n // \t\treturn false;\n // \t}\n\n console.log(\"play >>> \" + \"video=\" + video + \"; id=\" + id + \"; index=\" + index);\n me.dispatch('beforeplay', {\n index: index,\n video: video,\n id: id\n });\n\n if (index < 0) {\n console.error('index参数错误');\n return false;\n }\n\n me.VIDEO_DATA[index].play(video);\n opts = opts || {};\n\n if (me.VIDEO_DATA[index].playing) {\n me.VIDEO_DATA[index].opts = opts;\n me.VIDEO_DATA[index].id = id;\n me.dispatch('playsuccess', me.VIDEO_DATA[index]);\n return true;\n }\n\n return false;\n },\n\n /**\r\n * 在序号最小的空闲窗口播放视频\r\n * 参数:index(视频窗口编号,从0开始) video(视频设备id,即devId) opts(其他参数)\r\n * 返回true/false\r\n */\n playByOrder: function (video, id, opts) {\n var freeWindow = this._getMinIndexFreeWindow();\n\n if (freeWindow == -1 || this._getChoiceWindow() != -1 && !this.getInChoiceVideo() && this._opts.windowsNum > 1) {\n return this.playInChoice(video, id, opts);\n }\n\n return this.play(freeWindow, video, id, opts);\n },\n\n /**\r\n * 扩展视频窗口数的播放方式\r\n */\n playByOrderExpandWindow: function (video, id, opts) {\n var me = this;\n\n var freeWindow = this._getMinIndexFreeWindow(); // \twhile (this._opts.windows < 16 && freeWindow == -1) {\n // \t\tme.setWindowsNum(me._getNextWindowsNum(parseInt(me._opts.windows)));\n // \t\tfreeWindow = this._getMinIndexFreeWindow(0);\n // \t}\n\n\n var maxWindows = this._opts.windowsNum || 16;\n\n while (me._opts.windows < maxWindows && freeWindow == -1) {\n var zoom = Math.sqrt(this._opts.windows * 1) + 1;\n\n if (this._opts.windows == 6) {\n this.setWindowsNum(9);\n } else {\n this.setWindowsNum(Math.pow(zoom, 2));\n }\n\n freeWindow = this._getMinIndexFreeWindow(zoom - 1);\n }\n\n if (this._getChoiceWindow() == -1 && this._getMinIndexFreeWindow() != -1) {\n this.setChoiceWindow(this._getMinIndexFreeWindow());\n }\n\n if (freeWindow == -1 || this._getChoiceWindow() != -1 && !this.getInChoiceVideo() && this._opts.windowsNum > 1) {\n return this.playInChoice(video, id, opts);\n }\n\n return this.play(freeWindow, video, id, opts);\n },\n\n /**\r\n * 播放所有视频\r\n */\n playAll: function (array) {\n if (array.length > this._opts.windows) {\n throw new Error('windows is not enough');\n }\n\n var me = this;\n me.closeAll();\n $.each(array, function (i, obj) {\n me.play(i, obj.video, obj.id, obj.opts);\n });\n },\n\n /**\r\n * 在鼠标选中的窗口中播放视频\r\n */\n playInChoice: function (video, id, opts) {\n var me = this;\n var selView = $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li.sel');\n\n if (selView.length) {\n if (me.VIDEO_DATA[selView.index()].playing) {\n me.close(selView.index()); //延时打开 禁止同时一关一开\n\n setTimeout(function () {\n me.play(selView.index(), video, id, opts);\n }, 1000);\n } else {\n me.play(selView.index(), video, id, opts);\n }\n }\n },\n\n /** \r\n * 检索当前点的视频是否被播放,被播放则返回播放的视频窗口\r\n */\n isPlaying: function (id) {\n var me = this;\n var index = -1;\n $.each(me.VIDEO_DATA, function (key, val) {\n if (id == val.id && me.VIDEO_DATA[key].playing) {\n index = key;\n }\n });\n return index;\n },\n\n /** \r\n * 判断指定窗口是否正在播放视频\r\n */\n isPlayingByIndex: function (index) {\n var me = this;\n if (index > me.VIDEO_DATA.length - 1 || index < 0) return false;\n return me.VIDEO_DATA[index].playing;\n },\n\n /** \r\n * 获取视频数据\r\n */\n getVideoData: function () {\n return this.VIDEO_DATA;\n },\n\n /**\r\n * 关闭指定窗口的视频\r\n */\n close: function (index, isSave) {\n if (index < 0) {\n console.info('index参数错误');\n return;\n }\n\n this.VIDEO_DATA[index].close();\n\n if (!isSave) {\n this.dispatch('afterclose', this.VIDEO_DATA[index]);\n }\n },\n\n /**\r\n * 关闭指定视频编号正在播放的视频\r\n */\n closeByVideo: function (video, isSave) {\n var me = this;\n\n if (video == undefined) {\n console.info('video参数错误');\n return;\n }\n\n var videoData = me.VIDEO_DATA;\n\n if (!videoData || videoData.length == 0) {\n return;\n }\n\n for (var i in videoData) {\n if (video == videoData[i].video) {\n me.close(videoData[i].index);\n\n if (!isSave) {\n me.dispatch('afterclose', videoData[i]);\n }\n }\n }\n },\n\n /**\r\n * 关闭所有视频\r\n */\n closeAll: function (isSave) {\n var me = this;\n var openVideos = [];\n\n for (var i = me._opts.windowsBeginIndex; i < me._opts.windowsBeginIndex + me._opts.windowsNum; i++) {\n if (me.VIDEO_DATA[i].playing) {\n me.close(i, isSave);\n }\n } //\t\t\tif(!openVideos.length) return;\n //\t\t\t\n //\t\t\t//适当延时进行关闭\n //\t\t\tvar count = 0;\t\t\t\n //\t\t\tvar interval = setInterval(function(){\n //\t\t\t\tif(count == openVideos.length){\n //\t\t\t\t\tclearInterval(interval);\n //\t\t\t\t}else{\n //\t\t\t\t\tme.close(openVideos[count], isSave)\n //\t\t\t\t\tcount++;\n //\t\t\t\t}\n //\t\t\t}, 50);\n\n },\n\n /**\r\n * 设置鼠标选中窗口\r\n */\n setChoiceWindow: function (index) {\n $('.screen-' + (index + 1)).click();\n },\n\n /**\r\n * 获取视频窗口数\r\n */\n getWindowsNum: function () {\n return this._opts.windows;\n },\n\n /**\r\n * 分屏切换,传入的参数为新的屏数\r\n */\n setWindowsNum: function (num) {\n var me = this; //切换屏幕后 选中空闲最小的窗口\n\n if (me._opts.windows != num && me._getMinIndexFreeWindow() != -1) {\n me.setChoiceWindow(me._getMinIndexFreeWindow());\n }\n\n $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]').removeClass(\"mode-\" + me._opts.windows).addClass(\"mode-\" + num);\n\n var _li = $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li').hide();\n\n me._opts.windows = num;\n\n for (var i = 0; i < num; i++) {\n _li.eq(i).show();\n } //分屏切换事件\n\n\n this.dispatch('screenchange', {\n windowNums: num,\n flag: me._opts.flag\n });\n },\n\n /**\r\n * 询问是否无可用窗口\r\n */\n isUnavailable: function () {\n return this._opts.windows == 16 && this._getMinIndexFreeWindow() == -1;\n },\n\n /**\r\n * 执行轮巡\r\n */\n startPoll: function (array, time) {\n if (!array || array.length == 0) {\n promptAlarm('请选择视频设备!');\n return false;\n }\n\n time = $.isNumeric(time) ? time : this._opts.pollInterval;\n this._pollArray = array;\n var me = this,\n windowNum = me._opts.windows,\n maxNum = array.length,\n nowNum = 0;\n me.saveList();\n this._polltimer = new PollTimer(function (firstRun) {\n me.closeAll();\n wait(500).then(function () {\n for (var i = 0; i < windowNum; i++) {\n if (nowNum == maxNum) {\n nowNum = 0;\n\n if (maxNum < windowNum) {\n break;\n }\n }\n\n me.playByOrder(array[nowNum].video, array[nowNum].id, array[nowNum].opts);\n nowNum++;\n }\n\n if (firstRun) {\n me.dispatch('startpoll', array);\n }\n });\n }, time);\n },\n\n /**\r\n * 结束轮巡\r\n */\n stopPoll: function () {\n if (!this._polltimer) {\n return;\n }\n\n this._polltimer.stop();\n\n this._polltimer = undefined;\n console.log('------> 轮巡结束 <-------');\n this.dispatch('stoppoll', {});\n this._pollArray = null;\n this.closeAll(); //恢复之前打开的视频\n //this.playSaveList();\n },\n\n /**\r\n * 获取是否在轮巡\r\n */\n isPolling: function () {\n return !!this._polltimer;\n },\n holder: function (type, opts, isStop) {\n var index = this._getChoiceWindow();\n\n if (index < 0) {\n promptFailed('请先选中视频');\n return false;\n }\n\n return this.VIDEO_DATA[index].holder(type, opts, isStop);\n },\n\n /**\r\n * 视频回放\r\n * @param startTime [YYYY-MM-DDTHH:mm:ss]\r\n * @param endTime\r\n * @return playId\r\n */\n playHistory: function (video, startTime, endTime, opts) {\n var me = this;\n var index; //回放是一分屏的默认使用一个窗口\n\n if (me._opts.windowsNum == 1) {\n index = me._opts.windowsBeginIndex;\n } else {\n index = me._getMinIndexFreeWindow();\n }\n\n if (index < 0) {\n console.error('窗口编号获取失败!');\n }\n\n me.setChoiceWindow(index);\n me.VIDEO_DATA[index].opts = opts;\n return me.VIDEO_DATA[index].playback(video, startTime, endTime);\n },\n\n /**\r\n * 关闭历史流\r\n */\n closeHistory: function (video, index) {\n if (!video) return;\n var me = this;\n\n if (!index) {\n var index;\n\n if (me._opts.windowsNum == 1) {\n index = me._opts.windowsBeginIndex;\n }\n }\n\n return me.VIDEO_DATA[index].playbackControl(video, \"stop\", 0);\n },\n\n /**\r\n * 历史流控制\r\n * @param {number} video \r\n * @param {string} playType 播放类型(play && pause & stop)\r\n * @param {numner} playSpeed 播放速率\r\n * @param {string} startTime 回放时间(暂时无用)\r\n */\n historyControl: function (video, playType, playSpeed, startTime) {\n var me = this;\n var index;\n\n if (me._opts.windowsNum == 1) {\n index = me._opts.windowsBeginIndex;\n } else {\n index = me._getChoiceWindow();\n }\n\n return me.VIDEO_DATA[index].playbackControl(video, playType, playSpeed, startTime);\n },\n saveList: function () {\n this._saveList = cloneObj(this.VIDEO_DATA);\n },\n playSaveList: function () {\n if (!this._saveList) {\n return;\n }\n\n this.closeAll();\n var me = this;\n $.each(this._saveList, function (index, obj) {\n if (obj.video) {\n me.play(obj.index, obj.video, obj.id, obj.opts);\n }\n });\n },\n\n /**\r\n * 获取当前选择窗口的视频video\r\n */\n getInChoiceVideo: function () {\n var me = this;\n var selView = $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li.sel');\n\n if (selView.length && me.VIDEO_DATA[selView.index()].video) {\n return me.VIDEO_DATA[selView.index()];\n }\n\n return undefined;\n },\n\n /**\r\n * 设置全屏\r\n */\n setFullScreen: function () {\n this._fullScreenEvent(document.getElementById(\"video-main-web-rtc\"));\n },\n\n /**\r\n * 获取当前视频控件的配置项\r\n */\n getVideoControllerOpts: function () {\n return this._opts;\n },\n\n /**\r\n * 获取截屏数据\r\n * @param callback 回调函数\r\n */\n getScreenCaptureData: function (index, callback) {\n var me = this; //如果index为空则使用选中窗口的index\n\n var dataObj = me.VIDEO_DATA[index != undefined ? index : me._getChoiceWindow()];\n\n if (!dataObj.playing) {\n promptAlarm('请选择播放的视频源!');\n return;\n }\n\n var canvasObj = document.getElementById('myCanvas');\n var videoObj = dataObj.tagBox.get(0);\n canvasObj.width = dataObj.tagBox.parent().width();\n canvasObj.height = dataObj.tagBox.parent().height();\n var ctx = canvasObj.getContext(\"2d\");\n ctx.drawImage(videoObj, 0, 0, dataObj.tagBox.parent().width(), dataObj.tagBox.parent().height());\n var dataUrl = canvasObj.toDataURL('image/jpeg');\n callback ? callback(dataUrl) : null;\n },\n\n /**\r\n * 开始录像\r\n */\n startAvRecord: function (index) {\n var me = this;\n return me.VIDEO_DATA[index].recordAv(\"start_av_record\", recordAvBusinessId);\n },\n\n /**\r\n * 结束录像\r\n */\n stopAvRecord: function (index) {\n var me = this;\n return me.VIDEO_DATA[index].recordAv(\"stop_av_record\", recordAvBusinessId);\n },\n\n /**\r\n * 改变分辨率\r\n * @param resolution 新分辨率\r\n */\n changeResolution: function (index, resolution) {\n var me = this;\n return me.VIDEO_DATA[index].changeResolution(\"change_resolution\", resolution);\n },\n\n /**\r\n * 开启对讲预呼叫\r\n * @param pocNo 对讲号码\r\n * @param centerTel 调度中心号\r\n */\n openPocCall: function (pocNo, centerTel) {\n var me = this;\n var index; //对讲预呼叫默认使用一个窗口\n\n if (me._opts.windowsNum == 1) {\n index = me._opts.windowsBeginIndex;\n } else {\n index = me._getMinIndexFreeWindow();\n }\n\n if (index < 0) {\n console.error('窗口编号获取失败!');\n }\n\n return me.VIDEO_DATA[index].pocCall(\"open_poccall\", pocNo, centerTel);\n },\n\n /**\r\n * 关闭对讲预呼叫\r\n * @param pocNo 对讲号码\r\n * @param centerTel 调度中心号\r\n */\n closePocCall: function (pocNo, centerTel) {\n var me = this;\n var index; //对讲预呼叫默认使用一个窗口\n\n if (me._opts.windowsNum == 1) {\n index = me._opts.windowsBeginIndex;\n } else {\n index = me._getMinIndexFreeWindow();\n }\n\n if (index < 0) {\n console.error('窗口编号获取失败!');\n }\n\n me.VIDEO_DATA[index].stream = null;\n return me.VIDEO_DATA[index].pocCall(\"close_poccall\", pocNo, centerTel);\n },\n\n /**\r\n * 设置录像的businessId\r\n * @param businessId 业务ID\r\n */\n setRecordAvBusinessId: function (businessId) {\n //var me = this;\n recordAvBusinessId = businessId;\n },\n\n /**\r\n * 开启音频\r\n */\n sendAudio: function (index) {\n var me = this;\n var index = index != undefined ? index : me._opts.windowsBeginIndex;\n me.VIDEO_DATA[index].stream.getAudioTracks()[0].enabled = true;\n },\n\n /**\r\n * 关闭音频\r\n */\n unSendAudio: function (index) {\n var me = this;\n var index = index != undefined ? index : me._opts.windowsBeginIndex;\n me.VIDEO_DATA[index].stream.getAudioTracks()[0].enabled = false;\n },\n\n /**\r\n * 添加事件\r\n */\n addListener: function (type, handler, key) {\n videoListener.addListener(type, handler, key);\n },\n\n /**\r\n * 删除事件\r\n */\n removeListener: function (type, handler) {\n videoListener.removeListener(type, handler);\n },\n\n /**\r\n * 派发事件\r\n */\n dispatch: function (e, options) {\n videoListener.dispatch(e, options);\n },\n\n /**\r\n * 获取编号最小的空闲窗口的编号\r\n */\n _getMinIndexFreeWindow: function (index) {\n var me = this;\n index = index || me._opts.windowsBeginIndex;\n\n for (; index < me._opts.windowsBeginIndex + me._opts.windows; index++) {\n if (!me.VIDEO_DATA[index].playing) {\n return index;\n }\n }\n\n return -1;\n },\n\n /**\r\n * 获取选中的窗口的index\r\n */\n _getChoiceWindow: function () {\n var me = this;\n var selView = $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li.sel');\n\n if (selView.length) {\n return selView.index();\n }\n\n return -1;\n },\n\n /**\r\n * 获取下一窗口\r\n */\n _getNextWindowsNum: function (old) {\n var me = this;\n var windowsArr = me._windowsArr;\n\n for (var index in windowsArr) {\n if (index == windowsArr.length - 1) {\n return windowsArr[index];\n }\n\n if (old == windowsArr[index]) {\n return windowsArr[Number(index) + Number(1)];\n }\n }\n },\n\n /**\r\n * 设置鼠标选中窗口\r\n */\n _clickWindow: function () {\n var me = this; //视频窗口选中\n\n $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li').click(function () {\n $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]>li').removeClass(\"sel\");\n $(this).addClass(\"sel\"); //如果视频正在播放则分发点击消息\n\n if (me.getInChoiceVideo()) {\n me.dispatch('click', me.getInChoiceVideo());\n }\n });\n },\n\n /*\r\n * 总的初始化\r\n * 参数示例:\t\t\t\r\n * var opts = {\r\n \t\t\t\t\twindows: 4, //初始化的窗口数\r\n \t\t\t\t\tconf: { //配置参数\r\n \t\t\t\t\t\tuser: 'admin', //用户名\r\n \t\t\t\t\t\tpasswd: '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b', //密码\r\n \t\t\t\t\t\tjanusUrl: 'wss: //192.168.103.226:8989', //janusServer服务地址\r\n \t\t\t\t\t},\r\n \t\t\t\t\tpollInterval: 30 //轮巡时间,单位秒\r\n };\r\n */\n _init: function ($dom, opts) {\n var me = this; //配置项\n\n me._opts = {\n windows: 4,\n conf: {},\n pollInterval: 10,\n windowsNum: 16,\n isVideoTag: true //true生成video标签 false生产audio标签 默认true\n\n };\n $.extend(me._opts, opts);\n var windowsArr = me._opts.windowsArr;\n\n if (!windowsArr || windowsArr.length == 0) {\n me._windowList = {\n 1: {},\n 4: {},\n 6: {},\n 9: {},\n 16: {}\n };\n me._windowsArr = [1, 4, 6, 9, 16];\n } else {\n me._windowList = {};\n\n for (var index in windowsArr) {\n var item = windowsArr[index];\n me._windowList[item] = {};\n }\n\n me._windowsArr = windowsArr;\n } //获取起始的窗口位置\n\n\n me._opts.windowsBeginIndex = windowsSum; //窗口总数\n\n windowsSum += me._opts.windowsNum; //是否提示\n\n me._opts.showPrompt === false && (showPrompt = false); //初始化界面\n\n me._opts.isVideoTag ? me._initVideoView($dom) : me._initAudioView($dom); //初始化Janus\n\n me._initJanus(); //初始化按钮点击事件\n\n\n me._initBtnEvent(); //设置鼠标点击选中窗口\n\n\n me._clickWindow(); //显示视频信息:名称、分辨率、码率、丢包率\n\n\n me._showVideoInfo(); //双击某个视频全屏\n\n\n me._dbClickFullScreen();\n },\n\n /**\r\n * 初始化按钮点击事件\r\n */\n _initBtnEvent: function () {\n var me = this;\n var videoFlagObj = $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]'); //锁定视频事件\n\n videoFlagObj.find(\"button[name='lockVideo']\").click(function () {\n var index = Number($(this).parent().parent().attr(\"index\"));\n\n if (!me.VIDEO_DATA[index].playing) {\n promptAlarm('请选择播放的视频源!');\n return;\n }\n\n if ($(this).attr('class') == 'lock-video-btn') {\n me.VIDEO_DATA[index].isLockVideo = false;\n $(this).attr('class', 'unlock-video-btn');\n } else {\n me.VIDEO_DATA[index].isLockVideo = true;\n $(this).attr('class', 'lock-video-btn');\n }\n }); //接收音频事件\n\n videoFlagObj.find(\"button[name='recvAudio']\").click(function () {\n var _self = this;\n\n var index = Number($(this).parent().parent().attr(\"index\"));\n\n if (!me.VIDEO_DATA[index].playing) {\n promptAlarm('请选择播放的视频源!');\n return;\n }\n\n if ($(this).attr('class') == 'recv-audio-btn') {\n //发送停止播放音频的请求\n me.VIDEO_DATA[index].operateAudio(\"stop_audio\");\n $(this).attr('class', 'unrecv-audio-btn');\n me.VIDEO_DATA[index].tagBox.muted = false;\n } else {\n //发送前先关闭其它音频通道(包括接收和发送音频)\n var lis = $($(_self).parent().parent().parent()).children();\n\n for (var i = 0; i < lis.length; i++) {\n var btnIndex = Number($(lis[i]).attr(\"index\"));\n\n if (index == btnIndex) {\n continue;\n }\n\n var recvBtn = $($(lis[i]).find(\"button[name='recvAudio']\")[0]);\n var sendBtn = $($(lis[i]).find(\"button[name='sendAudio']\")[0]);\n\n if (recvBtn.attr(\"class\") == 'recv-audio-btn') {\n if (me.VIDEO_DATA[btnIndex].playing) {\n recvBtn.click();\n }\n }\n\n if (sendBtn.attr(\"class\") == 'send-audio-btn') {\n if (me.VIDEO_DATA[btnIndex].playing) {\n sendBtn.click();\n }\n }\n } //发送播放音频的请求\n\n\n $(_self).attr('class', 'recv-audio-btn');\n setTimeout(function () {\n me.VIDEO_DATA[index].operateAudio(\"recv_audio\");\n me.VIDEO_DATA[index].tagBox.muted = true;\n }, 1000);\n }\n }); //发送音频事件\n\n videoFlagObj.find(\"button[name='sendAudio']\").click(function () {\n var index = Number($(this).parent().parent().attr(\"index\"));\n\n if (!me.VIDEO_DATA[index].playing) {\n promptAlarm('请选择播放的视频源!');\n return;\n }\n\n if ($(this).attr('class') == 'send-audio-btn') {\n me.VIDEO_DATA[index].operateAudio(\"unsend_audio\");\n $(this).attr('class', 'unsend-audio-btn');\n me.VIDEO_DATA[index].stream.getAudioTracks()[0].enabled = false;\n } else {\n me.VIDEO_DATA[index].operateAudio(\"send_audio\");\n $(this).attr('class', 'send-audio-btn');\n me.VIDEO_DATA[index].stream.getAudioTracks()[0].enabled = true;\n }\n }); //关闭视频事件\n\n videoFlagObj.find(\"button[name='closeVideo']\").click(function () {\n var index = Number($(this).parent().parent().attr(\"index\"));\n me.close(index);\n });\n },\n\n /**\r\n * 双击某个视频全屏\r\n */\n _dbClickFullScreen: function () {\n var me = this;\n $('[video-flag=\"' + me._opts.windowsBeginIndex + '\"]').find('.video-box').on(\"dblclick\", function () {\n me._fullScreenEvent(this);\n });\n },\n\n /**\r\n * 全屏事件\r\n */\n _fullScreenEvent: function (el) {\n var isFullscreen = el.fullScreen || el.mozFullScreen || el.webkitIsFullScreen;\n\n if (!isFullscreen) {\n //进入全屏,多重短路表达式\n el.requestFullscreen && el.requestFullscreen() || el.mozRequestFullScreen && el.mozRequestFullScreen() || el.webkitRequestFullscreen && el.webkitRequestFullscreen() || el.msRequestFullscreen && el.msRequestFullscreen();\n } //关闭全屏\n // \tcolseFullscreen(); \t\n // \tfunction colseFullscreen() {\n //\t \tel.onmouseup = function(e){ //在body里点击触发事件\n //\t if(e.button === 0){ \n //\t \tdocument.exitFullscreen?document.exitFullscreen():\n //\t \tdocument.mozCancelFullScreen?document.mozCancelFullScreen():\n //\t \tdocument.webkitExitFullscreen?document.webkitExitFullscreen():'';\t \t\n //\t }\n //\t }\n // \t}\n\n },\n\n /**\r\n * 在视频界面上显示视频的信息 码率、分辨率、丢包率\r\n */\n _showVideoInfo: function () {\n var me = this;\n\n function clock() {\n for (var i = 0; i < me.VIDEO_DATA.length; i++) {\n if (me.VIDEO_DATA[i].playing) {\n var number = Number(i) + Number(1);\n var videoObj = document.getElementById('video-' + number);\n if (!videoObj) continue;\n var width = videoObj.videoWidth;\n var height = videoObj.videoHeight; //码率 \n\n var bitrate = me.VIDEO_DATA[i].sipcall.getBitrate();\n\n if (!width || !height || !bitrate || bitrate.indexOf(\"kbits\") == -1 || bitrate.indexOf(\"NaN\") != -1) {\n continue;\n }\n\n var config = me.VIDEO_DATA[i].sipcall.webrtcStuff;\n\n me._setPacketsLostRate(i, config, me);\n\n var resolution = width + \"×\" + height;\n\n switch (height + '') {\n case '1080':\n resolution = \"1080P\";\n break;\n\n case '720':\n resolution = \"720P\";\n break;\n\n case '480':\n resolution = \"480P\";\n break;\n\n case '288':\n resolution = \"CIF\";\n break;\n\n default:\n break;\n }\n\n var videoInfoObj = document.getElementById('info-' + number);\n var videoName = me.VIDEO_DATA[i].opts && me.VIDEO_DATA[i].opts.name ? me.VIDEO_DATA[i].opts.name : me.VIDEO_DATA[i].video;\n videoInfoObj.innerHTML = videoName + '&nbsp;&nbsp;' + resolution + '<br>丢包率:' + me.VIDEO_DATA[i].packetsLostRate + '&nbsp;&nbsp;' + bitrate.replace(\"kbits/sec\", \"kbps\");\n }\n }\n } //已存在则清除\n\n\n if (me.videoInfoTimer) {\n clearInterval(me.videoInfoTimer);\n me.videoInfoTimer = null;\n }\n\n me.videoInfoTimer = setInterval(clock, 1000);\n },\n\n /**\r\n * 获取设置丢包率\r\n */\n _setPacketsLostRate: function (index, config, me) {\n if (config.pc != null && config.pc != undefined && config.pc.getStats) {\n config.pc.getStats().then(function (stats) {\n stats.forEach(function (res) {\n if (!res) {\n return;\n }\n\n var inStats = false; // Check if these are statistics on incoming media\n\n if ((res.mediaType === \"video\" || res.id.toLowerCase().indexOf(\"video\") > -1) && res.type === \"inbound-rtp\" && res.id.indexOf(\"rtcp\") < 0) {\n // New stats\n inStats = true;\n } else if (res.type == 'ssrc' && res.bytesReceived && (res.googCodecName === \"VP8\" || res.googCodecName === \"\")) {\n // Older Chromer versions\n inStats = true;\n } // Parse stats now\n\n\n if (inStats) {\n if (res.hasOwnProperty(\"packetsLost\")) {\n //console.info('丢包率:' + res.packetsLost);\n if (!res.packetsLost || isNaN(res.packetsLost) || Number(res.packetsLost) == '0' || res.packetsLost == 'NaN') {\n me.VIDEO_DATA[index].packetsLostRate = '0.00%';\n } else {\n var packetsLost = Number(res.packetsLost) - Number(me.VIDEO_DATA[index].packetsLostSum);\n var packetsReceived = Number(res.packetsReceived) - Number(me.VIDEO_DATA[index].packetsReceivedSum);\n var packetsLostRate = 0; //防止分母是0\n\n if (packetsLost > 0) {\n var packetsLostRate = Number(packetsLost) / (Number(packetsLost) + Number(packetsReceived)) * 100;\n }\n\n me.VIDEO_DATA[index].packetsLostRate = packetsLostRate.toFixed(2) + '%';\n } //window.VIDEO_DATA[index].packetsLostRate = (Number(res.packetsLost) / (Number(res.packetsLost) + Number(res.packetsReceived))) * 100;\n\n /* \t\t\t\t\t \t\tvideoInfoObj.innerHTML = videoName + '&nbsp;&nbsp;' + width + \"×\" + height +\r\n \t\t\t\t\t\t\t\t\t\t\t\t'<br>' + bitrate + '&nbsp;&nbsp;Lost:' + res.packetsLost;*/\n\n }\n }\n });\n });\n }\n },\n\n /**\r\n * 视频界面的初始化,在传过来的dom中创建界面\r\n */\n _initVideoView: function ($dom) {\n var me = this;\n var objClass = (me._opts.windowsNum == 1 ? 'video-main-full mode-' : 'video-main mode-') + me._opts.windows;\n var videoHtml = '<ul class=\"' + objClass + '\" id=\"video-main-web-rtc\" video-flag=\"' + me._opts.windowsBeginIndex + '\">';\n var index = me._opts.windowsBeginIndex + 1;\n\n for (var i = index; i < index + me._opts.windowsNum; i++) {\n videoHtml += '<li class=\"screen-' + i + '\" index=\"' + (i - 1) + '\"><video class=\"video-box\" id=\"video-' + i + '\" autoplay ></video>' + '<div class=\"info hide\" id=\"info-' + i + '\"></div>' + '<div class=\"operate-btn\">'; //videoHtml +='<button type=\"button\" class=\"unlock-video-btn hide\" name=\"lockVideo\"></button>';\n\n checkUserMediaAvailable() && (videoHtml += '<button type=\"button\" class=\"unsend-audio-btn hide\" name=\"sendAudio\"></button>');\n videoHtml += '<button type=\"button\" class=\"unrecv-audio-btn hide\" name=\"recvAudio\"></button>' + '<button type=\"button\" class=\"close-btn hide\" name=\"closeVideo\"></button>' + '</div>' + '</li>';\n }\n\n videoHtml += '</ul> <div class=\"canvasBox hide\"><canvas id=\"myCanvas\" ></canvas></div>';\n $dom.append(videoHtml);\n me.setWindowsNum(me._opts.windows);\n },\n\n /**\r\n * 音频界面的初始化,在传过来的dom中创建界面\r\n */\n _initAudioView: function ($dom) {\n var me = this;\n var objClass = (me._opts.windowsNum == 1 ? 'video-main-full mode-' : 'video-main mode-') + me._opts.windows;\n var videoHtml = '<ul class=\"' + objClass + '\" id=\"video-main-web-rtc\" video-flag=\"' + me._opts.windowsBeginIndex + '\">';\n var index = me._opts.windowsBeginIndex + 1;\n\n for (var i = index; i < index + me._opts.windowsNum; i++) {\n videoHtml += '<audio id=\"audio-' + i + '\" autoplay ></video>';\n }\n\n $dom.append(videoHtml);\n },\n\n /**\r\n * Janus的初始化\r\n */\n _initJanus: function () {\n var me = this; // \tif (window.parent && window.parent.janus) {\n // \t\tme._initSVideo(window.parent.janus);\n // \t\treturn;\n // \t}\n\n Janus.init({\n debug: \"error\",\n dependencies: Janus.useDefaultDependencies({\n adapter: adapter\n }),\n callback: function () {\n if (!Janus.isWebrtcSupported()) {\n alert(\"不支持WEB-RTC\");\n return;\n }\n\n var janusUrl = me._opts.conf.janusUrl;\n checkIsHttps() && (janusUrl = janusUrl.replace('ws:', 'wss:'));\n me._opts.janus = new Janus({\n server: janusUrl.indexOf(\"127.0.0.1\") == -1 ? janusUrl : janusUrl.split(\"//\")[0] + \"//\" + location.hostname + \":\" + janusUrl.split(\":\")[2],\n success: function () {\n console.log(\"Janus服务连接成功!\");\n\n me._initSVideo(me._opts.janus);\n },\n error: function (error) {\n //只提示一次,防止Janus挂了一直重连\n !me._opts.isNotFistTip && promptFailed('Janus服务连接失败!');\n me._opts.isNotFistTip = true;\n console.error('Janus服务连接失败:' + error);\n me.closeAll();\n\n me._initJanus();\n },\n destroyed: function () {\n me._opts.isNotFistTip = true;\n }\n });\n }\n });\n },\n\n /**\r\n * 销毁:Janus销毁、资源释放\r\n */\n destoryJanus: function () {\n var me = this;\n\n me._opts.janus.destroy({\n success: function () {\n window.windowsSum = 0; //userToken = null;\n\n registered = false;\n },\n asyncRequest: true,\n notifyDestroyed: true\n });\n },\n\n /**\r\n * 初始化插件、登陆,初始化窗口\r\n */\n _initSVideo: function (janus) {\n var me = this; //已经初始化过,则直接拿来用\n // \t\tif (janus && (registered || (window.parent && window.parent.registered))) {\n //\t\t\t\t//生成主界面\n //\t\t\t\tfor(var i = me._opts.windowsBeginIndex; i < me._opts.windowsBeginIndex + me._opts.windowsNum; i++){\n //\t\t\t\t\tme.VIDEO_DATA[i] = new SVideo({\n //\t\t\t\t\t\tindex:i,\n //\t\t\t\t\t\tjanus:janus\n //\t\t\t\t\t\t\n //\t\t\t\t\t});\n //\t\t\t\t}\t\t\t\n //\t\t\t\treturn;\n // \t\t}\n //初始化一个插件,用来登陆,不能用此插件播放视频。\n\n var _loginSipCall = null;\n janus.attach({\n plugin: me._opts.conf.janusPlugin ? me._opts.conf.janusPlugin : \"janus.plugin.videoserver\",\n opaqueId: \"siptest-\" + Janus.randomString(12),\n success: function (pluginHandle) {\n console.log(\"Janus登陆插件初始化成功!\");\n _loginSipCall = pluginHandle;\n var register = {\n \"request\": \"register\",\n \"username\": me._opts.conf.user,\n \"secret\": me._opts.conf.passwd\n };\n\n _loginSipCall.send({\n \"message\": register\n });\n },\n error: function (error) {\n promptFailed('Janus登陆插件初始化失败!');\n console.error(\"Janus登陆插件初始化失败:\" + error);\n },\n onmessage: function (msg, jsep) {\n if (msg.error_code) {\n console.error(msg.error);\n promptFailed(FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code);\n return false;\n }\n\n var result = msg[\"result\"];\n var event = result[\"event\"];\n\n if (result.error_code && result.error_code != 0) {\n promptFailed(FAILED_CODE[result.error_code] || '错误码:' + result.error_code);\n }\n\n if (result !== null && result !== undefined && result[\"event\"] !== undefined && result[\"event\"] !== null) {\n console.log(\"result event:\" + event);\n\n if (event === 'registration_failed') {\n promptFailed(\"janus登陆失败: \" + result[\"code\"] + \" \" + result[\"reason\"]);\n return;\n }\n\n if (event === 'registered') {\n var userToken = result[\"userToken\"];\n console.log('janus登陆成功');\n registered = true; //生成主界面\n\n for (var i = me._opts.windowsBeginIndex; i < me._opts.windowsBeginIndex + me._opts.windowsNum; i++) {\n me.VIDEO_DATA[i] = new SVideo({\n index: i,\n janus: janus,\n userToken: userToken,\n windowsNum: me._opts.windowsNum,\n flag: me._opts.flag,\n isVideoTag: me._opts.isVideoTag,\n janusPlugin: me._opts.conf.janusPlugin\n });\n }\n }\n }\n }\n });\n }\n };\n var FAILED_CODE = {\n 1001: \"账号或密码错误\",\n 1002: \"该帐号已连接\",\n 1004: \"没有鉴权\",\n 2001: \"未携带token或token错误\",\n 2002: \"视频源id不存在\",\n 2003: \"操作指令非法(该账号不允许执行这个指令,或者不支持该指令)\",\n 3001: \"nat通道未建立\",\n 3002: \"该视频流已在传输\",\n 3003: \"无法获取远端视频流\",\n 4001: \"视频流断开连接\",\n 4002: \"获取视频失败\",\n 5000: \"内部错误\",\n 8001: \"设备不支持该指令\",\n 453: \"videoServer连接失败\",\n 454: \"录像回放窗口已占用,请先停止播放\",\n 455: \"无视频录像,操作失败\",\n 5001: \"呼叫异常断开\",\n 5002: \"禁止修改分辨率\",\n 4003: \"音频被占用\",\n 4004: \"音频被占用\",\n 4005: \"客户端已打开对讲\",\n 4006: \"终端离开云眼音频\"\n };\n var STATUS_CODE_ARR = ['5002', '4003', '4004', '4005', '4006']; //window.VIDEO_DATA = [];//记录视频各分屏的状态\n\n window.windowsSum = 0; //当前页面的视频总数,可能有多个VideoWebRtc对象\n //var userToken = null;\n\n var registered = false;\n var recordAvBusinessId = null;\n\n function SVideo(opts) {\n this.janus = opts.janus;\n this.userToken = opts.userToken;\n this.sipcall = null;\n this.index = opts.index;\n this.tagBox = opts.isVideoTag ? $('#video-' + (this.index + 1)) : $('#audio-' + (this.index + 1));\n this.windowsNum = opts.windowsNum;\n this.flag = opts.flag;\n this.isVideoTag = opts.isVideoTag;\n this.janusPlugin = opts.janusPlugin;\n this.init();\n }\n /**\r\n * 使用web-rtc方式的视频播放\r\n */\n\n\n SVideo.prototype = {\n /**\r\n * 初始化\r\n */\n init: function () {\n var self = this;\n self.janus.attach({\n plugin: self.janusPlugin ? self.janusPlugin : \"janus.plugin.videoserver\",\n opaqueId: \"siptest-\" + Janus.randomString(12),\n success: function (pluginHandle) {\n console.log(\"视频插件初始化成功:\" + self.index);\n self.sipcall = pluginHandle;\n\n if (self.index + 1 >= self.windowsNum) {\n videoListener.dispatch('initsucc', self);\n }\n },\n error: function (error) {\n console.error(\"插件初始化失败\");\n },\n consentDialog: function (on) {\n console.log(\"Consent dialog should be \" + (on ? \"on\" : \"off\") + \" now\");\n },\n onmessage: function (msg, jsep) {\n if (!registered) {\n promptFailed(\"请先登陆\");\n }\n\n if (msg.error_code) {\n console.error(msg.error_code + ' ' + msg.error);\n promptFailed(FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code);\n videoListener.dispatch('msginfo', {\n 'msg': FAILED_CODE[msg.error_code] || '错误码:' + msg.error_code\n });\n return false;\n }\n\n var result = msg[\"result\"];\n if (!result) return;\n\n if (result.error_code && result.error_code != 0) {\n //videoListener.dispatch('afterclose', self); \n if (!STATUS_CODE_ARR.includes(result.error_code + '')) {\n //videoListener.dispatch('afterclose', self); \n //播放失败 关闭视频\n var video = self.video;\n self.close('err');\n\n if (self.isLockVideo) {\n self.play(video);\n } else {\n videoListener.dispatch('afterclose', self);\n }\n }\n\n videoListener.dispatch('msginfo', {\n 'msg': FAILED_CODE[result.error_code] || '错误码:' + result.error_code\n });\n promptFailed(FAILED_CODE[result.error_code] || '错误码:' + result.error_code);\n self.tagBox.parent().find('.recv-audio-btn').attr(\"class\", \"unrecv-audio-btn\");\n self.tagBox.parent().find('.send-audio-btn').attr(\"class\", \"unsend-audio-btn\");\n } //事件响应\n\n\n var event = result[\"event\"];\n if (!event) return;\n\n switch (event) {\n //即将播放视频 \n case 'incomingcall':\n self.sipcall.createAnswer({\n jsep: jsep,\n media: {\n audioSend: checkUserMediaAvailable(),\n videoSend: false,\n audioRecv: true,\n videoRecv: true\n },\n success: function (jsep) {\n var body = {\n request: \"accept\",\n videoidentify: result[\"videoidentify\"],\n playseq: parseInt(result[\"playseq\"]),\n userToken: self.userToken\n };\n self.sipcall.send({\n \"message\": body,\n \"jsep\": jsep\n });\n },\n error: function (error) {\n console.error(error);\n var body = {\n \"request\": \"decline\",\n \"code\": 480\n };\n self.sipcall.send({\n \"message\": body\n });\n }\n });\n break;\n //呼叫失败 \n\n case 'callfaild':\n //promptFailed(FAILED_CODE[result.error_code] || '错误码:' + result.error_code);\n break;\n //云台 \n\n case 'ptz':\n break;\n //历史流控制\t\n\n case 'av_control':\n break;\n //录像通知\n\n case 'start_av_record':\n if (result && result.filename) {\n self.videoFileName = result.filename;\n videoListener.dispatch('recordvideo', self);\n console.log('录像地址:' + result.filename);\n }\n\n break;\n //修改分辨率\n\n case 'ser_notify_resolution':\n if (result && result.resolution) {\n self.resolution = result.resolution;\n videoListener.dispatch('notifyresolution', self);\n console.log('接收notifyresolution:' + result.resolution); //promptAlarm('当前分辨率:' + result.resolution);\n }\n\n break;\n //建立预呼叫响应\n\n case 'open_poccall':\n console.log('建立预呼叫');\n self.sipcall.createAnswer({\n jsep: jsep,\n media: {\n audioSend: checkUserMediaAvailable(),\n videoSend: false,\n audioRecv: true,\n videoRecv: false\n },\n success: function (jsep) {\n var body = {\n request: \"accept\",\n userToken: self.userToken\n };\n self.sipcall.send({\n \"message\": body,\n \"jsep\": jsep\n });\n },\n error: function (error) {\n console.error(error); //\t \t\t\t\t\t\t\t\tvar body = { \"request\": \"decline\", \"code\": 480 };\n //\t \t\t\t\t\t\t\t\tself.sipcall.send({\"message\": body});\n }\n });\n break;\n //预呼叫异常断开\n\n case 'ser_close_poccall':\n console.log('预呼叫异常断开');\n break;\n\n case 'ser_stop_audio':\n console.log('音频异常关闭!');\n break;\n\n default:\n console.log('event:' + event);\n break;\n }\n },\n //处理远程流\n onremotestream: function (stream) {\n console.log('onremotestream:分屏=' + (self.index + 1) + ', video=' + self.video);\n Janus.attachMediaStream(self.tagBox.get(0), stream);\n },\n //处理本地流\n onlocalstream: function (stream) {\n stream.getAudioTracks()[0].enabled = false;\n self.stream = stream; // \t\t\t\t\tJanus.attachMediaStream(self.tagBox.get(0), stream); \t\t\t\t\t\n // \t\t\t\t\tvar s = stream.getAudioTracks()[0];\n // \t\t\t\t\tstream.removeTrack(s); \t\t\t\t\t\n // \t\t\t\t\tself.sipcall.webrtcStuff.myStream.addTrack(s); \t\t\t\t\t\n // \t\t\t\t\tself.sipcall.webrtcStuff.myStream.removeTrack(s);\n // \t\t\t\t\tstream.getTracks().forEach(track => track.stop());\n // \t\t\t\t\tif (self.isVideoTag) {\n //\t \t\t\t\t\tvar btnObj = self.tagBox.parent().find(\"button[name='sendAudio']\");\n //\t \t\t\t\t\tbtnObj.unbind('click'); \t\t\t\t\t\n //\t \t\t\t\t\t//stream.getAudioTracks()[0].enabled = false;\n //\t \t\t\t\t\t\n //\t \t\t\t\t\tbtnObj.click(function() {\n //\t \t\t\t\t\t\tif (btnObj.attr('class') == 'send-audio-btn') {\n //\t \t\t\t\t\t\t\tstream.getAudioTracks()[0].enabled = false;\n //\t \t\t\t\t\t\t\tself.operateAudio(\"unsend_audio\");\n //\t \t\t\t\t\t\t\tbtnObj.attr('class', 'unsend-audio-btn'); \t\t\t\t\t\t\t\n //\t \t\t\t\t\t\t} else {\n //\t \t\t\t\t\t\t\tstream.getAudioTracks()[0].enabled = true;\n //\t \t\t\t\t\t\t\tself.operateAudio(\"send_audio\");\n //\t \t\t\t\t\t\t\tbtnObj.attr('class', 'send-audio-btn');\n //\t \t\t\t\t\t\t} \t\t\t\t\t\t\n //\t\t\t\t\t\t\t}); \t\t\t\t\t\n //\t \t\t\t\t\t\n //\t \t\t\t\t\tconsole.log('onrelocalstream:分屏=' + (self.index + 1) + ', video=' + self.video);\n // \t\t\t\t\t}\n },\n //清理\n oncleanup: function () {\n console.log('oncleanup:分屏=' + (self.index + 1));\n }\n }); //<video>注册playing/canplay事件,用于判断视频播放成功\n\n self.tagBox.bind(\"canplay play playing\", function () {\n if (self.video) {\n if (self.tagBox.is(':visible')) return;\n console.log('分屏=' + (self.index + 1) + ', ' + self.video + ' 播放成功');\n promptSuccess('分屏' + (self.index + 1) + ' 播放成功');\n self.tagBox.show(); //self.tagBox.parent().removeClass(\"loading\").find(\".stream-loading\").remove();\n\n self.tagBox.parent().find(\".stream-loading\").remove(); //显示视频信息\n\n self.tagBox.parent().find('.info').show(); //显示音频按钮\n\n self.tagBox.parent().find('.operate-btn button').show();\n\n if (self.isPlayBack) {\n self.tagBox.parent().find('.unrecv-audio-btn').hide();\n }\n\n self.tagBox.parent().find('.recv-audio-btn').attr(\"class\", \"unrecv-audio-btn\");\n self.tagBox.parent().find('.send-audio-btn').attr(\"class\", \"unsend-audio-btn\");\n var width = this.videoWidth;\n var height = this.videoHeight; //保持长宽比例填充video视频框\n\n if (width <= height && width != '0') {\n self.tagBox.css('object-fit', 'contain');\n }\n }\n });\n },\n\n /**\r\n * 播放视频\r\n * video:视频编号(ID)\r\n */\n play: function (video) {\n if (!video) {\n //alert(\"请输入视频ID\");\n promptAlarm('视频ID不能为空');\n return;\n }\n\n var body = {\n request: \"call\",\n videoidentify: video,\n playseq: this.index,\n userToken: this.userToken,\n autoack: true\n };\n this.sipcall.send({\n \"message\": body\n });\n this.video = video;\n this.playing = true;\n this.packetsLostRate = '0.00%';\n this.packetsLostSum = 0;\n this.packetsReceivedSum = 0; //loading\n\n var _li = this.tagBox.parent();\n\n if (!_li.hasClass(\"loading\")) {\n _li.addClass(\"loading\").append('<div class=\"stream-loading\">等待数据流传送...</div>');\n }\n\n _li.find('.operate-btn .close-btn').show();\n\n console.log(\"视频播放:分屏=\" + (this.index + 1) + \",设备ID=\" + video);\n },\n close: function (type) {\n if (!type) {\n this.isLockVideo = false;\n this.tagBox.parent().find('.lock-video-btn').attr(\"class\", \"unlock-video-btn\");\n } //var reqType = type ? type : \"hangup\";\n\n\n if (!this.playing) return;\n var _hangup = {\n request: \"hangup\",\n videoidentify: this.video,\n playseq: this.index,\n userToken: this.userToken\n };\n this.sipcall.send({\n \"message\": _hangup\n });\n this.sipcall.hangup();\n this.tagBox.hide();\n this.playing = false;\n this.video = null;\n this.packetsLostRate = '0.00%';\n this.packetsLostSum = 0;\n this.packetsReceivedSum = 0; //清除显示的视频信息\n\n document.getElementById('info-' + (Number(this.index) + Number(1))).innerHTML = ''; //隐藏视频信息\n\n this.tagBox.parent().find('.info').hide(); //隐藏音频喇叭\n\n this.tagBox.parent().find('.operate-btn button').hide(); //避免没收到成功时就点击关闭,清除loading框\n\n this.tagBox.parent().removeClass(\"loading\").find(\".stream-loading\").remove(); //清除object-fit\n\n this.tagBox.css('object-fit', '');\n },\n\n /**\r\n * 云台控制\r\n * up \t\t\t\t上\r\n * down\t\t\t\t下\r\n * left\t\t\t\t左\r\n * right\t\t\t右\r\n * upleft\t\t\t上左\r\n * upright\t\t\t上右\r\n * downleft\t\t\t下左\r\n * downright\t\t下右\r\n * zoomin\t\t\t倍率变大\r\n * zoomout\t\t\t倍率变下\r\n * focusnear\t\t焦点+\r\n * focusfar\t\t\t焦点-\r\n * irisopen\t\t\t光圈+\r\n * irisclose\t\t光圈-\r\n * pointset\t\t\t设置预置点\r\n * pointdel\t\t\t删除预置点\r\n * pointgoto\t\t到预置点\r\n * scansetleft\t\t自动扫描左边界\r\n * scansetright\t\t自动扫描又边界\r\n * scansetspeed\t\t设置扫描速度\r\n * scanrun\t\t\t自动扫描运行\r\n * cruiseadd\t\t添加巡航点\r\n * cruisedel\t\t删除巡航点\r\n * cruisespeed\t\t设置巡航点速度\r\n * cruisepausetime\t设置巡航滞留时间\r\n * cruiserun\t\t启动巡航\r\n */\n holder: function (type, opts, isStop) {\n if (!this.video || !this.playing) {\n promptAlarm('未选中播放的视频!');\n return;\n }\n\n var holdType = 'up down left right upleft upright downleft downright zoomin zoomout focusnear focusfar irisopen irisclose pointset pointdel pointgoto ' + 'scansetleft scansetright scansetspeed scanrun cruiseadd cruisedel cruisespeed cruisepausetime cruiserun';\n var typeVal = Number(holdType.split(' ').indexOf(type)) + Number(1);\n console.log(\"holdType:\" + typeVal + \" type:\" + type + (isStop ? ' 停止' : ''));\n\n if (!typeVal || typeVal <= 0) {\n console.error('异常PTZ指令:' + typeVal);\n return false;\n }\n\n var body = {\n request: \"ptz\",\n videoidentify: this.video,\n playseq: this.index,\n userToken: this.userToken\n };\n body.cmd = {\n speed: opts.speed ? opts.speed : 200,\n group: 0,\n present: opts.present ? opts.present : 0,\n time: 0,\n type: typeVal\n };\n\n if (isStop) {\n console.log(\">>>>yuntai control,stop, video=\" + this.video);\n body.cmd.action = 1;\n } else {\n body.cmd.action = 0;\n console.log('设备号:' + this.video + ', 云台移动-->' + type);\n }\n\n this.sipcall.send({\n \"message\": body\n });\n },\n\n /**\r\n * 历史回放\r\n * video设备ID startTime录像开始 stopTime录像结束 例:2015-07-22T12:00:00,注意T隔开\r\n */\n playback: function (video, startTime, stopTime) {\n if (!video) {\n promptAlarm('视频ID不能为空');\n return;\n }\n\n var body = {\n request: \"av_playback\",\n videoidentify: video,\n playseq: this.index,\n userToken: this.userToken,\n start_time: startTime,\n stop_time: stopTime\n };\n console.log(\">>>>playback video=\" + video + \" startTime=\" + startTime + \" stopTime=\" + stopTime);\n this.sipcall.send({\n \"message\": body\n });\n this.video = video;\n this.playing = true;\n this.packetsLostRate = '0.00%';\n this.packetsLostSum = 0;\n this.packetsReceivedSum = 0;\n this.isPlayBack = true; //loading\n\n var _li = this.tagBox.parent();\n\n if (!_li.hasClass(\"loading\")) {\n _li.addClass(\"loading\").append('<div class=\"stream-loading\">等待数据流传送...</div>');\n }\n\n _li.find('.operate-btn .close-btn').show();\n\n console.log(\"历史回放:分屏=\" + (this.index + 1) + \",视频ID=\" + video);\n },\n\n /**\r\n * 历史回放控制\r\n * video设备ID playType类型 playSpeed速率 startTime开始时间\r\n */\n playbackControl: function (video, playType, playSpeed, startTime) {\n var self = this;\n var body = {\n request: \"av_control\",\n videoidentify: video,\n playseq: self.index,\n userToken: self.userToken,\n start_time: startTime,\n rate: String(playSpeed),\n cmd: playType\n };\n console.log(\">>>>playbackControl video=\" + video + \" playType=\" + playType + \" playSpeed=\" + playSpeed); //setTimeout(function (){\n\n self.sipcall.send({\n \"message\": body\n }); //}, 1000);\n\n if (playType == 'stop') {\n self.close();\n self.tagBox.hide();\n self.playing = false;\n self.video = null;\n self.packetsLostRate = '0.00%';\n self.packetsLostSum = 0;\n self.packetsReceivedSum = 0;\n self.isPlayBack = false; //清除显示的视频信息\n\n document.getElementById('info-' + (Number(self.index) + Number(1))).innerHTML = ''; //隐藏音频喇叭\n\n this.tagBox.parent().find('.operate-btn button').hide(); //避免没收到成功时就点击关闭,清除loading框\n\n self.tagBox.parent().removeClass(\"loading\").find(\".stream-loading\").remove(); //清除object-fit\n\n self.tagBox.css('object-fit', '');\n }\n },\n\n /**\r\n * 音频流操作\r\n * requstType: recv_audio、stop_audio、send_audio、unsend_audio\r\n */\n operateAudio: function (requestType) {\n var self = this;\n\n if (!self.video) {\n promptAlarm('视频ID不能为空');\n return;\n }\n\n var body = {\n request: requestType,\n videoidentify: self.video,\n playseq: self.index,\n userToken: self.userToken\n };\n self.sipcall.send({\n \"message\": body\n });\n console.log(\"音频操作=\" + requestType + \", 分屏=\" + (self.index + 1) + \",设备ID=\" + self.video);\n },\n\n /**\r\n * 录像操作\r\n * requstType: start_av_record(开启录像)、stop_av_record(停止录像)\r\n */\n recordAv: function (requestType, businessId) {\n var self = this;\n\n if (!self.video) {\n promptAlarm('视频ID不能为空');\n return;\n }\n\n var body = {\n request: requestType,\n videoidentify: self.video,\n playseq: self.index,\n userToken: self.userToken,\n businessid: businessId || null\n };\n self.sipcall.send({\n \"message\": body\n });\n console.log(\"录像操作=\" + requestType + \", 分屏=\" + (self.index + 1) + \",设备ID=\" + self.video + \",业务ID=\" + (businessId || null));\n },\n\n /**\r\n * 修改分辨率\r\n * requstType: change_resolution\r\n * CIF 480P 720P 1080P\r\n */\n changeResolution: function (requestType, resolution) {\n var self = this;\n\n if (!self.video) {\n promptAlarm('视频ID不能为空');\n return;\n }\n\n var body = {\n request: requestType,\n videoidentify: self.video,\n playseq: self.index,\n resolution: resolution,\n userToken: self.userToken\n };\n self.sipcall.send({\n \"message\": body\n });\n console.log(\"修改分辨率操作=\" + resolution + \", 分屏=\" + (self.index + 1) + \",设备ID=\" + self.video);\n },\n\n /**\r\n * 对象预呼叫\r\n * requstType: open_poccall(开启) close_poccall(关闭)\r\n * pocno\r\n * centerTel\r\n */\n pocCall: function (requestType, pocNo, centerTel) {\n var self = this;\n var body = {\n request: requestType,\n pocno: pocNo,\n playseq: self.index,\n center_tel: centerTel,\n userToken: self.userToken\n };\n self.sipcall.send({\n \"message\": body\n });\n console.log(\"预呼叫操作=\" + requestType + \", 对讲号码=\" + pocNo + \",调度中心号码=\" + centerTel);\n }\n };\n return isIE() ? VideoOcx : VideoWebRtc;\n});","// addapted from the document.currentScript polyfill by Adam Miller\n// MIT license\n// source: https://github.com/amiller-gh/currentScript-polyfill\n\n// added support for Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1620505\n\n(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define([], factory);\n } else if (typeof module === 'object' && module.exports) {\n module.exports = factory();\n } else {\n root.getCurrentScript = factory();\n }\n}(typeof self !== 'undefined' ? self : this, function () {\n function getCurrentScript () {\n var descriptor = Object.getOwnPropertyDescriptor(document, 'currentScript')\n // for chrome\n if (!descriptor && 'currentScript' in document && document.currentScript) {\n return document.currentScript\n }\n\n // for other browsers with native support for currentScript\n if (descriptor && descriptor.get !== getCurrentScript && document.currentScript) {\n return document.currentScript\n }\n \n // IE 8-10 support script readyState\n // IE 11+ & Firefox support stack trace\n try {\n throw new Error();\n }\n catch (err) {\n // Find the second match for the \"at\" string to get file src url from stack.\n var ieStackRegExp = /.*at [^(]*\\((.*):(.+):(.+)\\)$/ig,\n ffStackRegExp = /@([^@]*):(\\d+):(\\d+)\\s*$/ig,\n stackDetails = ieStackRegExp.exec(err.stack) || ffStackRegExp.exec(err.stack),\n scriptLocation = (stackDetails && stackDetails[1]) || false,\n line = (stackDetails && stackDetails[2]) || false,\n currentLocation = document.location.href.replace(document.location.hash, ''),\n pageSource,\n inlineScriptSourceRegExp,\n inlineScriptSource,\n scripts = document.getElementsByTagName('script'); // Live NodeList collection\n \n if (scriptLocation === currentLocation) {\n pageSource = document.documentElement.outerHTML;\n inlineScriptSourceRegExp = new RegExp('(?:[^\\\\n]+?\\\\n){0,' + (line - 2) + '}[^<]*<script>([\\\\d\\\\D]*?)<\\\\/script>[\\\\d\\\\D]*', 'i');\n inlineScriptSource = pageSource.replace(inlineScriptSourceRegExp, '$1').trim();\n }\n \n for (var i = 0; i < scripts.length; i++) {\n // If ready state is interactive, return the script tag\n if (scripts[i].readyState === 'interactive') {\n return scripts[i];\n }\n \n // If src matches, return the script tag\n if (scripts[i].src === scriptLocation) {\n return scripts[i];\n }\n \n // If inline source matches, return the script tag\n if (\n scriptLocation === currentLocation &&\n scripts[i].innerHTML &&\n scripts[i].innerHTML.trim() === inlineScriptSource\n ) {\n return scripts[i];\n }\n }\n \n // If no match, return null\n return null;\n }\n };\n\n return getCurrentScript\n}));\n","/*\n * Copyright (c) 2008-2016 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* CometD Version 3.1.0 */\n(function (root, factory) {\n if (typeof exports === 'object') {\n // CommonJS.\n module.exports = factory();\n } else if (typeof define === 'function' && define.amd) {\n // AMD.\n define([], factory);\n } else {\n // Globals.\n root.org = root.org || {};\n root.org.cometd = factory();\n }\n})(this, function () {\n /**\n * Utility functions.\n */\n var Utils = {\n isString: function (value) {\n if (value === undefined || value === null) {\n return false;\n }\n\n return typeof value === 'string' || value instanceof String;\n },\n isArray: function (value) {\n if (value === undefined || value === null) {\n return false;\n }\n\n return value instanceof Array;\n },\n\n /**\n * Returns whether the given element is contained into the given array.\n * @param element the element to check presence for\n * @param array the array to check for the element presence\n * @return the index of the element, if present, or a negative index if the element is not present\n */\n inArray: function (element, array) {\n for (var i = 0; i < array.length; ++i) {\n if (element === array[i]) {\n return i;\n }\n }\n\n return -1;\n },\n setTimeout: function (cometd, funktion, delay) {\n return window.setTimeout(function () {\n try {\n cometd._debug('Invoking timed function', funktion);\n\n funktion();\n } catch (x) {\n cometd._debug('Exception invoking timed function', funktion, x);\n }\n }, delay);\n },\n clearTimeout: function (timeoutHandle) {\n window.clearTimeout(timeoutHandle);\n }\n };\n /**\n * A registry for transports used by the CometD object.\n */\n\n var TransportRegistry = function () {\n var _types = [];\n var _transports = {};\n\n this.getTransportTypes = function () {\n return _types.slice(0);\n };\n\n this.findTransportTypes = function (version, crossDomain, url) {\n var result = [];\n\n for (var i = 0; i < _types.length; ++i) {\n var type = _types[i];\n\n if (_transports[type].accept(version, crossDomain, url) === true) {\n result.push(type);\n }\n }\n\n return result;\n };\n\n this.negotiateTransport = function (types, version, crossDomain, url) {\n for (var i = 0; i < _types.length; ++i) {\n var type = _types[i];\n\n for (var j = 0; j < types.length; ++j) {\n if (type === types[j]) {\n var transport = _transports[type];\n\n if (transport.accept(version, crossDomain, url) === true) {\n return transport;\n }\n }\n }\n }\n\n return null;\n };\n\n this.add = function (type, transport, index) {\n var existing = false;\n\n for (var i = 0; i < _types.length; ++i) {\n if (_types[i] === type) {\n existing = true;\n break;\n }\n }\n\n if (!existing) {\n if (typeof index !== 'number') {\n _types.push(type);\n } else {\n _types.splice(index, 0, type);\n }\n\n _transports[type] = transport;\n }\n\n return !existing;\n };\n\n this.find = function (type) {\n for (var i = 0; i < _types.length; ++i) {\n if (_types[i] === type) {\n return _transports[type];\n }\n }\n\n return null;\n };\n\n this.remove = function (type) {\n for (var i = 0; i < _types.length; ++i) {\n if (_types[i] === type) {\n _types.splice(i, 1);\n\n var transport = _transports[type];\n delete _transports[type];\n return transport;\n }\n }\n\n return null;\n };\n\n this.clear = function () {\n _types = [];\n _transports = {};\n };\n\n this.reset = function (init) {\n for (var i = 0; i < _types.length; ++i) {\n _transports[_types[i]].reset(init);\n }\n };\n };\n /**\n * Base object with the common functionality for transports.\n */\n\n\n var Transport = function () {\n var _type;\n\n var _cometd;\n\n var _url;\n /**\n * Function invoked just after a transport has been successfully registered.\n * @param type the type of transport (for example 'long-polling')\n * @param cometd the cometd object this transport has been registered to\n * @see #unregistered()\n */\n\n\n this.registered = function (type, cometd) {\n _type = type;\n _cometd = cometd;\n };\n /**\n * Function invoked just after a transport has been successfully unregistered.\n * @see #registered(type, cometd)\n */\n\n\n this.unregistered = function () {\n _type = null;\n _cometd = null;\n };\n\n this._debug = function () {\n _cometd._debug.apply(_cometd, arguments);\n };\n\n this._mixin = function () {\n return _cometd._mixin.apply(_cometd, arguments);\n };\n\n this.getConfiguration = function () {\n return _cometd.getConfiguration();\n };\n\n this.getAdvice = function () {\n return _cometd.getAdvice();\n };\n\n this.setTimeout = function (funktion, delay) {\n return Utils.setTimeout(_cometd, funktion, delay);\n };\n\n this.clearTimeout = function (handle) {\n Utils.clearTimeout(handle);\n };\n /**\n * Converts the given response into an array of bayeux messages\n * @param response the response to convert\n * @return an array of bayeux messages obtained by converting the response\n */\n\n\n this.convertToMessages = function (response) {\n if (Utils.isString(response)) {\n try {\n return JSON.parse(response);\n } catch (x) {\n this._debug('Could not convert to JSON the following string', '\"' + response + '\"');\n\n throw x;\n }\n }\n\n if (Utils.isArray(response)) {\n return response;\n }\n\n if (response === undefined || response === null) {\n return [];\n }\n\n if (response instanceof Object) {\n return [response];\n }\n\n throw 'Conversion Error ' + response + ', typeof ' + typeof response;\n };\n /**\n * Returns whether this transport can work for the given version and cross domain communication case.\n * @param version a string indicating the transport version\n * @param crossDomain a boolean indicating whether the communication is cross domain\n * @param url the URL to connect to\n * @return true if this transport can work for the given version and cross domain communication case,\n * false otherwise\n */\n\n\n this.accept = function (version, crossDomain, url) {\n throw 'Abstract';\n };\n /**\n * Returns the type of this transport.\n * @see #registered(type, cometd)\n */\n\n\n this.getType = function () {\n return _type;\n };\n\n this.getURL = function () {\n return _url;\n };\n\n this.setURL = function (url) {\n _url = url;\n };\n\n this.send = function (envelope, metaConnect) {\n throw 'Abstract';\n };\n\n this.reset = function (init) {\n this._debug('Transport', _type, 'reset', init ? 'initial' : 'retry');\n };\n\n this.abort = function () {\n this._debug('Transport', _type, 'aborted');\n };\n\n this.toString = function () {\n return this.getType();\n };\n };\n\n Transport.derive = function (baseObject) {\n function F() {}\n\n F.prototype = baseObject;\n return new F();\n };\n /**\n * Base object with the common functionality for transports based on requests.\n * The key responsibility is to allow at most 2 outstanding requests to the server,\n * to avoid that requests are sent behind a long poll.\n * To achieve this, we have one reserved request for the long poll, and all other\n * requests are serialized one after the other.\n */\n\n\n var RequestTransport = function () {\n var _super = new Transport();\n\n var _self = Transport.derive(_super);\n\n var _requestIds = 0;\n var _metaConnectRequest = null;\n var _requests = [];\n var _envelopes = [];\n\n function _coalesceEnvelopes(envelope) {\n while (_envelopes.length > 0) {\n var envelopeAndRequest = _envelopes[0];\n var newEnvelope = envelopeAndRequest[0];\n var newRequest = envelopeAndRequest[1];\n\n if (newEnvelope.url === envelope.url && newEnvelope.sync === envelope.sync) {\n _envelopes.shift();\n\n envelope.messages = envelope.messages.concat(newEnvelope.messages);\n\n this._debug('Coalesced', newEnvelope.messages.length, 'messages from request', newRequest.id);\n\n continue;\n }\n\n break;\n }\n }\n\n function _transportSend(envelope, request) {\n this.transportSend(envelope, request);\n request.expired = false;\n\n if (!envelope.sync) {\n var maxDelay = this.getConfiguration().maxNetworkDelay;\n var delay = maxDelay;\n\n if (request.metaConnect === true) {\n delay += this.getAdvice().timeout;\n }\n\n this._debug('Transport', this.getType(), 'waiting at most', delay, 'ms for the response, maxNetworkDelay', maxDelay);\n\n var self = this;\n request.timeout = this.setTimeout(function () {\n request.expired = true;\n var errorMessage = 'Request ' + request.id + ' of transport ' + self.getType() + ' exceeded ' + delay + ' ms max network delay';\n var failure = {\n reason: errorMessage\n };\n var xhr = request.xhr;\n failure.httpCode = self.xhrStatus(xhr);\n self.abortXHR(xhr);\n\n self._debug(errorMessage);\n\n self.complete(request, false, request.metaConnect);\n envelope.onFailure(xhr, envelope.messages, failure);\n }, delay);\n }\n }\n\n function _queueSend(envelope) {\n var requestId = ++_requestIds;\n var request = {\n id: requestId,\n metaConnect: false,\n envelope: envelope\n }; // Consider the metaConnect requests which should always be present\n\n if (_requests.length < this.getConfiguration().maxConnections - 1) {\n _requests.push(request);\n\n _transportSend.call(this, envelope, request);\n } else {\n this._debug('Transport', this.getType(), 'queueing request', requestId, 'envelope', envelope);\n\n _envelopes.push([envelope, request]);\n }\n }\n\n function _metaConnectComplete(request) {\n var requestId = request.id;\n\n this._debug('Transport', this.getType(), 'metaConnect complete, request', requestId);\n\n if (_metaConnectRequest !== null && _metaConnectRequest.id !== requestId) {\n throw 'Longpoll request mismatch, completing request ' + requestId;\n } // Reset metaConnect request\n\n\n _metaConnectRequest = null;\n }\n\n function _complete(request, success) {\n var index = Utils.inArray(request, _requests); // The index can be negative if the request has been aborted\n\n if (index >= 0) {\n _requests.splice(index, 1);\n }\n\n if (_envelopes.length > 0) {\n var envelopeAndRequest = _envelopes.shift();\n\n var nextEnvelope = envelopeAndRequest[0];\n var nextRequest = envelopeAndRequest[1];\n\n this._debug('Transport dequeued request', nextRequest.id);\n\n if (success) {\n if (this.getConfiguration().autoBatch) {\n _coalesceEnvelopes.call(this, nextEnvelope);\n }\n\n _queueSend.call(this, nextEnvelope);\n\n this._debug('Transport completed request', request.id, nextEnvelope);\n } else {\n // Keep the semantic of calling response callbacks asynchronously after the request\n var self = this;\n this.setTimeout(function () {\n self.complete(nextRequest, false, nextRequest.metaConnect);\n var failure = {\n reason: 'Previous request failed'\n };\n var xhr = nextRequest.xhr;\n failure.httpCode = self.xhrStatus(xhr);\n nextEnvelope.onFailure(xhr, nextEnvelope.messages, failure);\n }, 0);\n }\n }\n }\n\n _self.complete = function (request, success, metaConnect) {\n if (metaConnect) {\n _metaConnectComplete.call(this, request);\n } else {\n _complete.call(this, request, success);\n }\n };\n /**\n * Performs the actual send depending on the transport type details.\n * @param envelope the envelope to send\n * @param request the request information\n */\n\n\n _self.transportSend = function (envelope, request) {\n throw 'Abstract';\n };\n\n _self.transportSuccess = function (envelope, request, responses) {\n if (!request.expired) {\n this.clearTimeout(request.timeout);\n this.complete(request, true, request.metaConnect);\n\n if (responses && responses.length > 0) {\n envelope.onSuccess(responses);\n } else {\n envelope.onFailure(request.xhr, envelope.messages, {\n httpCode: 204\n });\n }\n }\n };\n\n _self.transportFailure = function (envelope, request, failure) {\n if (!request.expired) {\n this.clearTimeout(request.timeout);\n this.complete(request, false, request.metaConnect);\n envelope.onFailure(request.xhr, envelope.messages, failure);\n }\n };\n\n function _metaConnectSend(envelope) {\n if (_metaConnectRequest !== null) {\n throw 'Concurrent metaConnect requests not allowed, request id=' + _metaConnectRequest.id + ' not yet completed';\n }\n\n var requestId = ++_requestIds;\n\n this._debug('Transport', this.getType(), 'metaConnect send, request', requestId, 'envelope', envelope);\n\n var request = {\n id: requestId,\n metaConnect: true,\n envelope: envelope\n };\n\n _transportSend.call(this, envelope, request);\n\n _metaConnectRequest = request;\n }\n\n _self.send = function (envelope, metaConnect) {\n if (metaConnect) {\n _metaConnectSend.call(this, envelope);\n } else {\n _queueSend.call(this, envelope);\n }\n };\n\n _self.abort = function () {\n _super.abort();\n\n for (var i = 0; i < _requests.length; ++i) {\n var request = _requests[i];\n\n if (request) {\n this._debug('Aborting request', request);\n\n if (!this.abortXHR(request.xhr)) {\n this.transportFailure(request.envelope, request, {\n reason: 'abort'\n });\n }\n }\n }\n\n var metaConnectRequest = _metaConnectRequest;\n\n if (metaConnectRequest) {\n this._debug('Aborting metaConnect request', metaConnectRequest);\n\n if (!this.abortXHR(metaConnectRequest.xhr)) {\n this.transportFailure(metaConnectRequest.envelope, metaConnectRequest, {\n reason: 'abort'\n });\n }\n }\n\n this.reset(true);\n };\n\n _self.reset = function (init) {\n _super.reset(init);\n\n _metaConnectRequest = null;\n _requests = [];\n _envelopes = [];\n };\n\n _self.abortXHR = function (xhr) {\n if (xhr) {\n try {\n var state = xhr.readyState;\n xhr.abort();\n return state !== XMLHttpRequest.UNSENT;\n } catch (x) {\n this._debug(x);\n }\n }\n\n return false;\n };\n\n _self.xhrStatus = function (xhr) {\n if (xhr) {\n try {\n return xhr.status;\n } catch (x) {\n this._debug(x);\n }\n }\n\n return -1;\n };\n\n return _self;\n };\n\n var LongPollingTransport = function () {\n var _super = new RequestTransport();\n\n var _self = Transport.derive(_super); // By default, support cross domain\n\n\n var _supportsCrossDomain = true;\n\n _self.accept = function (version, crossDomain, url) {\n return _supportsCrossDomain || !crossDomain;\n };\n\n _self.xhrSend = function (packet) {\n var xhr = new XMLHttpRequest();\n xhr.withCredentials = true;\n xhr.open('POST', packet.url, packet.sync !== true);\n var headers = packet.headers;\n\n if (headers) {\n for (var headerName in headers) {\n if (headers.hasOwnProperty(headerName)) {\n xhr.setRequestHeader(headerName, headers[headerName]);\n }\n }\n }\n\n xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');\n\n xhr.onload = function () {\n if (xhr.status === 200) {\n packet.onSuccess(xhr.responseText);\n } else {\n packet.onError(xhr.statusText);\n }\n };\n\n xhr.onerror = function () {\n packet.onError(xhr.statusText);\n };\n\n xhr.send(packet.body);\n return xhr;\n };\n\n _self.transportSend = function (envelope, request) {\n this._debug('Transport', this.getType(), 'sending request', request.id, 'envelope', envelope);\n\n var self = this;\n\n try {\n var sameStack = true;\n request.xhr = this.xhrSend({\n transport: this,\n url: envelope.url,\n sync: envelope.sync,\n headers: this.getConfiguration().requestHeaders,\n body: JSON.stringify(envelope.messages),\n onSuccess: function (response) {\n self._debug('Transport', self.getType(), 'received response', response);\n\n var success = false;\n\n try {\n var received = self.convertToMessages(response);\n\n if (received.length === 0) {\n _supportsCrossDomain = false;\n self.transportFailure(envelope, request, {\n httpCode: 204\n });\n } else {\n success = true;\n self.transportSuccess(envelope, request, received);\n }\n } catch (x) {\n self._debug(x);\n\n if (!success) {\n _supportsCrossDomain = false;\n var failure = {\n exception: x\n };\n failure.httpCode = self.xhrStatus(request.xhr);\n self.transportFailure(envelope, request, failure);\n }\n }\n },\n onError: function (reason, exception) {\n self._debug('Transport', self.getType(), 'received error', reason, exception);\n\n _supportsCrossDomain = false;\n var failure = {\n reason: reason,\n exception: exception\n };\n failure.httpCode = self.xhrStatus(request.xhr);\n\n if (sameStack) {\n // Keep the semantic of calling response callbacks asynchronously after the request\n self.setTimeout(function () {\n self.transportFailure(envelope, request, failure);\n }, 0);\n } else {\n self.transportFailure(envelope, request, failure);\n }\n }\n });\n sameStack = false;\n } catch (x) {\n _supportsCrossDomain = false; // Keep the semantic of calling response callbacks asynchronously after the request\n\n this.setTimeout(function () {\n self.transportFailure(envelope, request, {\n exception: x\n });\n }, 0);\n }\n };\n\n _self.reset = function (init) {\n _super.reset(init);\n\n _supportsCrossDomain = true;\n };\n\n return _self;\n };\n\n var CallbackPollingTransport = function () {\n var _super = new RequestTransport();\n\n var _self = Transport.derive(_super);\n\n var jsonp = 0;\n\n _self.accept = function (version, crossDomain, url) {\n return true;\n };\n\n _self.jsonpSend = function (packet) {\n var head = document.getElementsByTagName('head')[0];\n var script = document.createElement('script');\n var callbackName = '_cometd_jsonp_' + jsonp++;\n\n window[callbackName] = function (responseText) {\n head.removeChild(script);\n delete window[callbackName];\n packet.onSuccess(responseText);\n };\n\n var url = packet.url;\n url += url.indexOf('?') < 0 ? '?' : '&';\n url += 'jsonp=' + callbackName;\n url += '&message=' + packet.body;\n script.src = url;\n script.async = packet.sync !== true;\n script.type = 'application/javascript';\n\n script.onerror = function (e) {\n packet.onError('jsonp ' + e.type);\n };\n\n head.appendChild(script);\n };\n\n function _failTransportFn(envelope, request, x) {\n var self = this;\n return function () {\n self.transportFailure(envelope, request, 'error', x);\n };\n }\n\n _self.transportSend = function (envelope, request) {\n var self = this; // Microsoft Internet Explorer has a 2083 URL max length\n // We must ensure that we stay within that length\n\n var start = 0;\n var length = envelope.messages.length;\n var lengths = [];\n\n while (length > 0) {\n // Encode the messages because all brackets, quotes, commas, colons, etc\n // present in the JSON will be URL encoded, taking many more characters\n var json = JSON.stringify(envelope.messages.slice(start, start + length));\n var urlLength = envelope.url.length + encodeURI(json).length;\n var maxLength = this.getConfiguration().maxURILength;\n\n if (urlLength > maxLength) {\n if (length === 1) {\n var x = 'Bayeux message too big (' + urlLength + ' bytes, max is ' + maxLength + ') ' + 'for transport ' + this.getType(); // Keep the semantic of calling response callbacks asynchronously after the request\n\n this.setTimeout(_failTransportFn.call(this, envelope, request, x), 0);\n return;\n }\n\n --length;\n continue;\n }\n\n lengths.push(length);\n start += length;\n length = envelope.messages.length - start;\n } // Here we are sure that the messages can be sent within the URL limit\n\n\n var envelopeToSend = envelope;\n\n if (lengths.length > 1) {\n var begin = 0;\n var end = lengths[0];\n\n this._debug('Transport', this.getType(), 'split', envelope.messages.length, 'messages into', lengths.join(' + '));\n\n envelopeToSend = this._mixin(false, {}, envelope);\n envelopeToSend.messages = envelope.messages.slice(begin, end);\n envelopeToSend.onSuccess = envelope.onSuccess;\n envelopeToSend.onFailure = envelope.onFailure;\n\n for (var i = 1; i < lengths.length; ++i) {\n var nextEnvelope = this._mixin(false, {}, envelope);\n\n begin = end;\n end += lengths[i];\n nextEnvelope.messages = envelope.messages.slice(begin, end);\n nextEnvelope.onSuccess = envelope.onSuccess;\n nextEnvelope.onFailure = envelope.onFailure;\n this.send(nextEnvelope, request.metaConnect);\n }\n }\n\n this._debug('Transport', this.getType(), 'sending request', request.id, 'envelope', envelopeToSend);\n\n try {\n var sameStack = true;\n this.jsonpSend({\n transport: this,\n url: envelopeToSend.url,\n sync: envelopeToSend.sync,\n headers: this.getConfiguration().requestHeaders,\n body: JSON.stringify(envelopeToSend.messages),\n onSuccess: function (responses) {\n var success = false;\n\n try {\n var received = self.convertToMessages(responses);\n\n if (received.length === 0) {\n self.transportFailure(envelopeToSend, request, {\n httpCode: 204\n });\n } else {\n success = true;\n self.transportSuccess(envelopeToSend, request, received);\n }\n } catch (x) {\n self._debug(x);\n\n if (!success) {\n self.transportFailure(envelopeToSend, request, {\n exception: x\n });\n }\n }\n },\n onError: function (reason, exception) {\n var failure = {\n reason: reason,\n exception: exception\n };\n\n if (sameStack) {\n // Keep the semantic of calling response callbacks asynchronously after the request\n self.setTimeout(function () {\n self.transportFailure(envelopeToSend, request, failure);\n }, 0);\n } else {\n self.transportFailure(envelopeToSend, request, failure);\n }\n }\n });\n sameStack = false;\n } catch (xx) {\n // Keep the semantic of calling response callbacks asynchronously after the request\n this.setTimeout(function () {\n self.transportFailure(envelopeToSend, request, {\n exception: xx\n });\n }, 0);\n }\n };\n\n return _self;\n };\n\n var WebSocketTransport = function () {\n var _super = new Transport();\n\n var _self = Transport.derive(_super);\n\n var _cometd; // By default WebSocket is supported\n\n\n var _webSocketSupported = true; // Whether we were able to establish a WebSocket connection\n\n var _webSocketConnected = false;\n var _stickyReconnect = true; // The context contains the envelopes that have been sent\n // and the timeouts for the messages that have been sent.\n\n var _context = null;\n var _connecting = null;\n var _connected = false;\n var _successCallback = null;\n\n _self.reset = function (init) {\n _super.reset(init);\n\n _webSocketSupported = true;\n\n if (init) {\n _webSocketConnected = false;\n }\n\n _stickyReconnect = true;\n _context = null;\n _connecting = null;\n _connected = false;\n };\n\n function _forceClose(context, event) {\n if (context) {\n this.webSocketClose(context, event.code, event.reason); // Force immediate failure of pending messages to trigger reconnect.\n // This is needed because the server may not reply to our close()\n // and therefore the onclose function is never called.\n\n this.onClose(context, event);\n }\n }\n\n function _sameContext(context) {\n return context === _connecting || context === _context;\n }\n\n function _storeEnvelope(context, envelope, metaConnect) {\n var messageIds = [];\n\n for (var i = 0; i < envelope.messages.length; ++i) {\n var message = envelope.messages[i];\n\n if (message.id) {\n messageIds.push(message.id);\n }\n }\n\n context.envelopes[messageIds.join(',')] = [envelope, metaConnect];\n\n this._debug('Transport', this.getType(), 'stored envelope, envelopes', context.envelopes);\n }\n\n function _websocketConnect(context) {\n // We may have multiple attempts to open a WebSocket\n // connection, for example a /meta/connect request that\n // may take time, along with a user-triggered publish.\n // Early return if we are already connecting.\n if (_connecting) {\n return;\n } // Mangle the URL, changing the scheme from 'http' to 'ws'.\n\n\n var url = _cometd.getURL().replace(/^http/, 'ws');\n\n this._debug('Transport', this.getType(), 'connecting to URL', url);\n\n try {\n var protocol = _cometd.getConfiguration().protocol;\n\n context.webSocket = protocol ? new window.WebSocket(url, protocol) : new window.WebSocket(url);\n _connecting = context;\n } catch (x) {\n _webSocketSupported = false;\n\n this._debug('Exception while creating WebSocket object', x);\n\n throw x;\n } // By default use sticky reconnects.\n\n\n _stickyReconnect = _cometd.getConfiguration().stickyReconnect !== false;\n var self = this;\n\n var connectTimeout = _cometd.getConfiguration().connectTimeout;\n\n if (connectTimeout > 0) {\n context.connectTimer = this.setTimeout(function () {\n _cometd._debug('Transport', self.getType(), 'timed out while connecting to URL', url, ':', connectTimeout, 'ms'); // The connection was not opened, close anyway.\n\n\n _forceClose.call(self, context, {\n code: 1000,\n reason: 'Connect Timeout'\n });\n }, connectTimeout);\n }\n\n var onopen = function () {\n _cometd._debug('WebSocket onopen', context);\n\n if (context.connectTimer) {\n self.clearTimeout(context.connectTimer);\n }\n\n if (_sameContext(context)) {\n _connecting = null;\n _context = context;\n _webSocketConnected = true;\n self.onOpen(context);\n } else {\n // We have a valid connection already, close this one.\n _cometd._warn('Closing extra WebSocket connection', this, 'active connection', _context);\n\n _forceClose.call(self, context, {\n code: 1000,\n reason: 'Extra Connection'\n });\n }\n }; // This callback is invoked when the server sends the close frame.\n // The close frame for a connection may arrive *after* another\n // connection has been opened, so we must make sure that actions\n // are performed only if it's the same connection.\n\n\n var onclose = function (event) {\n event = event || {\n code: 1000\n };\n\n _cometd._debug('WebSocket onclose', context, event, 'connecting', _connecting, 'current', _context);\n\n if (context.connectTimer) {\n self.clearTimeout(context.connectTimer);\n }\n\n self.onClose(context, event);\n };\n\n var onmessage = function (wsMessage) {\n _cometd._debug('WebSocket onmessage', wsMessage, context);\n\n self.onMessage(context, wsMessage);\n };\n\n context.webSocket.onopen = onopen;\n context.webSocket.onclose = onclose;\n\n context.webSocket.onerror = function () {\n // Clients should call onclose(), but if they do not we do it here for safety.\n onclose({\n code: 1000,\n reason: 'Error'\n });\n };\n\n context.webSocket.onmessage = onmessage;\n\n this._debug('Transport', this.getType(), 'configured callbacks on', context);\n }\n\n function _webSocketSend(context, envelope, metaConnect) {\n var json = JSON.stringify(envelope.messages);\n context.webSocket.send(json);\n\n this._debug('Transport', this.getType(), 'sent', envelope, 'metaConnect =', metaConnect); // Manage the timeout waiting for the response.\n\n\n var maxDelay = this.getConfiguration().maxNetworkDelay;\n var delay = maxDelay;\n\n if (metaConnect) {\n delay += this.getAdvice().timeout;\n _connected = true;\n }\n\n var self = this;\n var messageIds = [];\n\n for (var i = 0; i < envelope.messages.length; ++i) {\n (function () {\n var message = envelope.messages[i];\n\n if (message.id) {\n messageIds.push(message.id);\n context.timeouts[message.id] = self.setTimeout(function () {\n _cometd._debug('Transport', self.getType(), 'timing out message', message.id, 'after', delay, 'on', context);\n\n _forceClose.call(self, context, {\n code: 1000,\n reason: 'Message Timeout'\n });\n }, delay);\n }\n })();\n }\n\n this._debug('Transport', this.getType(), 'waiting at most', delay, 'ms for messages', messageIds, 'maxNetworkDelay', maxDelay, ', timeouts:', context.timeouts);\n }\n\n _self._notifySuccess = function (fn, messages) {\n fn.call(this, messages);\n };\n\n _self._notifyFailure = function (fn, context, messages, failure) {\n fn.call(this, context, messages, failure);\n };\n\n function _send(context, envelope, metaConnect) {\n try {\n if (context === null) {\n context = _connecting || {\n envelopes: {},\n timeouts: {}\n };\n\n _storeEnvelope.call(this, context, envelope, metaConnect);\n\n _websocketConnect.call(this, context);\n } else {\n _storeEnvelope.call(this, context, envelope, metaConnect);\n\n _webSocketSend.call(this, context, envelope, metaConnect);\n }\n } catch (x) {\n // Keep the semantic of calling response callbacks asynchronously after the request.\n var self = this;\n this.setTimeout(function () {\n _forceClose.call(self, context, {\n code: 1000,\n reason: 'Exception',\n exception: x\n });\n }, 0);\n }\n }\n\n _self.onOpen = function (context) {\n var envelopes = context.envelopes;\n\n this._debug('Transport', this.getType(), 'opened', context, 'pending messages', envelopes);\n\n for (var key in envelopes) {\n if (envelopes.hasOwnProperty(key)) {\n var element = envelopes[key];\n var envelope = element[0];\n var metaConnect = element[1]; // Store the success callback, which is independent from the envelope,\n // so that it can be used to notify arrival of messages.\n\n _successCallback = envelope.onSuccess;\n\n _webSocketSend.call(this, context, envelope, metaConnect);\n }\n }\n };\n\n _self.onMessage = function (context, wsMessage) {\n this._debug('Transport', this.getType(), 'received websocket message', wsMessage, context);\n\n var close = false;\n var messages = this.convertToMessages(wsMessage.data);\n var messageIds = [];\n\n for (var i = 0; i < messages.length; ++i) {\n var message = messages[i]; // Detect if the message is a response to a request we made.\n // If it's a meta message, for sure it's a response; otherwise it's\n // a publish message and publish responses don't have the data field.\n\n if (/^\\/meta\\//.test(message.channel) || message.data === undefined) {\n if (message.id) {\n messageIds.push(message.id);\n var timeout = context.timeouts[message.id];\n\n if (timeout) {\n this.clearTimeout(timeout);\n delete context.timeouts[message.id];\n\n this._debug('Transport', this.getType(), 'removed timeout for message', message.id, ', timeouts', context.timeouts);\n }\n }\n }\n\n if ('/meta/connect' === message.channel) {\n _connected = false;\n }\n\n if ('/meta/disconnect' === message.channel && !_connected) {\n close = true;\n }\n } // Remove the envelope corresponding to the messages.\n\n\n var removed = false;\n var envelopes = context.envelopes;\n\n for (var j = 0; j < messageIds.length; ++j) {\n var id = messageIds[j];\n\n for (var key in envelopes) {\n if (envelopes.hasOwnProperty(key)) {\n var ids = key.split(',');\n var index = Utils.inArray(id, ids);\n\n if (index >= 0) {\n removed = true;\n ids.splice(index, 1);\n var envelope = envelopes[key][0];\n var metaConnect = envelopes[key][1];\n delete envelopes[key];\n\n if (ids.length > 0) {\n envelopes[ids.join(',')] = [envelope, metaConnect];\n }\n\n break;\n }\n }\n }\n }\n\n if (removed) {\n this._debug('Transport', this.getType(), 'removed envelope, envelopes', envelopes);\n }\n\n this._notifySuccess(_successCallback, messages);\n\n if (close) {\n this.webSocketClose(context, 1000, 'Disconnect');\n }\n };\n\n _self.onClose = function (context, event) {\n this._debug('Transport', this.getType(), 'closed', context, event);\n\n if (_sameContext(context)) {\n // Remember if we were able to connect.\n // This close event could be due to server shutdown,\n // and if it restarts we want to try websocket again.\n _webSocketSupported = _stickyReconnect && _webSocketConnected;\n _connecting = null;\n _context = null;\n }\n\n var timeouts = context.timeouts;\n context.timeouts = {};\n\n for (var id in timeouts) {\n if (timeouts.hasOwnProperty(id)) {\n this.clearTimeout(timeouts[id]);\n }\n }\n\n var envelopes = context.envelopes;\n context.envelopes = {};\n\n for (var key in envelopes) {\n if (envelopes.hasOwnProperty(key)) {\n var envelope = envelopes[key][0];\n var metaConnect = envelopes[key][1];\n\n if (metaConnect) {\n _connected = false;\n }\n\n var failure = {\n websocketCode: event.code,\n reason: event.reason\n };\n\n if (event.exception) {\n failure.exception = event.exception;\n }\n\n this._notifyFailure(envelope.onFailure, context, envelope.messages, failure);\n }\n }\n };\n\n _self.registered = function (type, cometd) {\n _super.registered(type, cometd);\n\n _cometd = cometd;\n };\n\n _self.accept = function (version, crossDomain, url) {\n this._debug('Transport', this.getType(), 'accept, supported:', _webSocketSupported); // Using !! to return a boolean (and not the WebSocket object).\n\n\n return _webSocketSupported && !!window.WebSocket && _cometd.websocketEnabled !== false;\n };\n\n _self.send = function (envelope, metaConnect) {\n this._debug('Transport', this.getType(), 'sending', envelope, 'metaConnect =', metaConnect);\n\n _send.call(this, _context, envelope, metaConnect);\n };\n\n _self.webSocketClose = function (context, code, reason) {\n try {\n if (context.webSocket) {\n context.webSocket.close(code, reason);\n }\n } catch (x) {\n this._debug(x);\n }\n };\n\n _self.abort = function () {\n _super.abort();\n\n _forceClose.call(this, _context, {\n code: 1000,\n reason: 'Abort'\n });\n\n this.reset(true);\n };\n\n return _self;\n };\n /**\n * The constructor for a CometD object, identified by an optional name.\n * The default name is the string 'default'.\n * @param name the optional name of this cometd object\n */\n\n\n var CometD = function (name) {\n var _cometd = this;\n\n var _name = name || 'default';\n\n var _crossDomain = false;\n\n var _transports = new TransportRegistry();\n\n var _transport;\n\n var _status = 'disconnected';\n var _messageId = 0;\n var _clientId = null;\n var _batch = 0;\n var _messageQueue = [];\n var _internalBatch = false;\n var _listenerId = 0;\n var _listeners = {};\n var _backoff = 0;\n var _scheduledSend = null;\n var _extensions = [];\n var _advice = {};\n\n var _handshakeProps;\n\n var _handshakeCallback;\n\n var _callbacks = {};\n var _remoteCalls = {};\n var _reestablish = false;\n var _connected = false;\n var _unconnectTime = 0;\n var _handshakeMessages = 0;\n var _config = {\n protocol: null,\n stickyReconnect: true,\n connectTimeout: 0,\n maxConnections: 2,\n backoffIncrement: 1000,\n maxBackoff: 60000,\n logLevel: 'info',\n reverseIncomingExtensions: true,\n maxNetworkDelay: 10000,\n requestHeaders: {},\n appendMessageTypeToURL: true,\n autoBatch: false,\n urls: {},\n maxURILength: 2000,\n advice: {\n timeout: 60000,\n interval: 0,\n reconnect: undefined,\n maxInterval: 0\n }\n };\n\n function _fieldValue(object, name) {\n try {\n return object[name];\n } catch (x) {\n return undefined;\n }\n }\n /**\n * Mixes in the given objects into the target object by copying the properties.\n * @param deep if the copy must be deep\n * @param target the target object\n * @param objects the objects whose properties are copied into the target\n */\n\n\n this._mixin = function (deep, target, objects) {\n var result = target || {}; // Skip first 2 parameters (deep and target), and loop over the others\n\n for (var i = 2; i < arguments.length; ++i) {\n var object = arguments[i];\n\n if (object === undefined || object === null) {\n continue;\n }\n\n for (var propName in object) {\n if (object.hasOwnProperty(propName)) {\n var prop = _fieldValue(object, propName);\n\n var targ = _fieldValue(result, propName); // Avoid infinite loops\n\n\n if (prop === target) {\n continue;\n } // Do not mixin undefined values\n\n\n if (prop === undefined) {\n continue;\n }\n\n if (deep && typeof prop === 'object' && prop !== null) {\n if (prop instanceof Array) {\n result[propName] = this._mixin(deep, targ instanceof Array ? targ : [], prop);\n } else {\n var source = typeof targ === 'object' && !(targ instanceof Array) ? targ : {};\n result[propName] = this._mixin(deep, source, prop);\n }\n } else {\n result[propName] = prop;\n }\n }\n }\n }\n\n return result;\n };\n\n function _isString(value) {\n return Utils.isString(value);\n }\n\n function _isFunction(value) {\n if (value === undefined || value === null) {\n return false;\n }\n\n return typeof value === 'function';\n }\n\n function _zeroPad(value, length) {\n var result = '';\n\n while (--length > 0) {\n if (value >= Math.pow(10, length)) {\n break;\n }\n\n result += '0';\n }\n\n result += value;\n return result;\n }\n\n function _log(level, args) {\n if (window.console) {\n var logger = window.console[level];\n\n if (_isFunction(logger)) {\n var now = new Date();\n [].splice.call(args, 0, 0, _zeroPad(now.getHours(), 2) + ':' + _zeroPad(now.getMinutes(), 2) + ':' + _zeroPad(now.getSeconds(), 2) + '.' + _zeroPad(now.getMilliseconds(), 3));\n logger.apply(window.console, args);\n }\n }\n }\n\n this._warn = function () {\n _log('warn', arguments);\n };\n\n this._info = function () {\n if (_config.logLevel !== 'warn') {\n _log('info', arguments);\n }\n };\n\n this._debug = function () {\n if (_config.logLevel === 'debug') {\n _log('debug', arguments);\n }\n };\n\n function _splitURL(url) {\n // [1] = protocol://,\n // [2] = host:port,\n // [3] = host,\n // [4] = IPv6_host,\n // [5] = IPv4_host,\n // [6] = :port,\n // [7] = port,\n // [8] = uri,\n // [9] = rest (query / fragment)\n return /(^https?:\\/\\/)?(((\\[[^\\]]+\\])|([^:\\/\\?#]+))(:(\\d+))?)?([^\\?#]*)(.*)?/.exec(url);\n }\n /**\n * Returns whether the given hostAndPort is cross domain.\n * The default implementation checks against window.location.host\n * but this function can be overridden to make it work in non-browser\n * environments.\n *\n * @param hostAndPort the host and port in format host:port\n * @return whether the given hostAndPort is cross domain\n */\n\n\n this._isCrossDomain = function (hostAndPort) {\n return hostAndPort && hostAndPort !== window.location.host;\n };\n\n function _configure(configuration) {\n _cometd._debug('Configuring cometd object with', configuration); // Support old style param, where only the Bayeux server URL was passed\n\n\n if (_isString(configuration)) {\n configuration = {\n url: configuration\n };\n }\n\n if (!configuration) {\n configuration = {};\n }\n\n _config = _cometd._mixin(false, _config, configuration);\n\n var url = _cometd.getURL();\n\n if (!url) {\n throw 'Missing required configuration parameter \\'url\\' specifying the Bayeux server URL';\n } // Check if we're cross domain.\n\n\n var urlParts = _splitURL(url);\n\n var hostAndPort = urlParts[2];\n var uri = urlParts[8];\n var afterURI = urlParts[9];\n _crossDomain = _cometd._isCrossDomain(hostAndPort); // Check if appending extra path is supported\n\n if (_config.appendMessageTypeToURL) {\n if (afterURI !== undefined && afterURI.length > 0) {\n _cometd._info('Appending message type to URI ' + uri + afterURI + ' is not supported, disabling \\'appendMessageTypeToURL\\' configuration');\n\n _config.appendMessageTypeToURL = false;\n } else {\n var uriSegments = uri.split('/');\n var lastSegmentIndex = uriSegments.length - 1;\n\n if (uri.match(/\\/$/)) {\n lastSegmentIndex -= 1;\n }\n\n if (uriSegments[lastSegmentIndex].indexOf('.') >= 0) {\n // Very likely the CometD servlet's URL pattern is mapped to an extension, such as *.cometd\n // It will be difficult to add the extra path in this case\n _cometd._info('Appending message type to URI ' + uri + ' is not supported, disabling \\'appendMessageTypeToURL\\' configuration');\n\n _config.appendMessageTypeToURL = false;\n }\n }\n }\n }\n\n function _removeListener(subscription) {\n if (subscription) {\n var subscriptions = _listeners[subscription.channel];\n\n if (subscriptions && subscriptions[subscription.id]) {\n delete subscriptions[subscription.id];\n\n _cometd._debug('Removed', subscription.listener ? 'listener' : 'subscription', subscription);\n }\n }\n }\n\n function _removeSubscription(subscription) {\n if (subscription && !subscription.listener) {\n _removeListener(subscription);\n }\n }\n\n function _clearSubscriptions() {\n for (var channel in _listeners) {\n if (_listeners.hasOwnProperty(channel)) {\n var subscriptions = _listeners[channel];\n\n if (subscriptions) {\n for (var id in subscriptions) {\n if (subscriptions.hasOwnProperty(id)) {\n _removeSubscription(subscriptions[id]);\n }\n }\n }\n }\n }\n }\n\n function _setStatus(newStatus) {\n if (_status !== newStatus) {\n _cometd._debug('Status', _status, '->', newStatus);\n\n _status = newStatus;\n }\n }\n\n function _isDisconnected() {\n return _status === 'disconnecting' || _status === 'disconnected';\n }\n\n function _nextMessageId() {\n var result = ++_messageId;\n return '' + result;\n }\n\n function _applyExtension(scope, callback, name, message, outgoing) {\n try {\n return callback.call(scope, message);\n } catch (x) {\n var handler = _cometd.onExtensionException;\n\n if (_isFunction(handler)) {\n _cometd._debug('Invoking extension exception handler', name, x);\n\n try {\n handler.call(_cometd, x, name, outgoing, message);\n } catch (xx) {\n _cometd._info('Exception during execution of extension exception handler', name, xx);\n }\n } else {\n _cometd._info('Exception during execution of extension', name, x);\n }\n\n return message;\n }\n }\n\n function _applyIncomingExtensions(message) {\n for (var i = 0; i < _extensions.length; ++i) {\n if (message === undefined || message === null) {\n break;\n }\n\n var index = _config.reverseIncomingExtensions ? _extensions.length - 1 - i : i;\n var extension = _extensions[index];\n var callback = extension.extension.incoming;\n\n if (_isFunction(callback)) {\n var result = _applyExtension(extension.extension, callback, extension.name, message, false);\n\n message = result === undefined ? message : result;\n }\n }\n\n return message;\n }\n\n function _applyOutgoingExtensions(message) {\n for (var i = 0; i < _extensions.length; ++i) {\n if (message === undefined || message === null) {\n break;\n }\n\n var extension = _extensions[i];\n var callback = extension.extension.outgoing;\n\n if (_isFunction(callback)) {\n var result = _applyExtension(extension.extension, callback, extension.name, message, true);\n\n message = result === undefined ? message : result;\n }\n }\n\n return message;\n }\n\n function _notify(channel, message) {\n var subscriptions = _listeners[channel];\n\n if (subscriptions) {\n for (var id in subscriptions) {\n if (subscriptions.hasOwnProperty(id)) {\n var subscription = subscriptions[id]; // Subscriptions may come and go, so the array may have 'holes'\n\n if (subscription) {\n try {\n subscription.callback.call(subscription.scope, message);\n } catch (x) {\n var handler = _cometd.onListenerException;\n\n if (_isFunction(handler)) {\n _cometd._debug('Invoking listener exception handler', subscription, x);\n\n try {\n handler.call(_cometd, x, subscription, subscription.listener, message);\n } catch (xx) {\n _cometd._info('Exception during execution of listener exception handler', subscription, xx);\n }\n } else {\n _cometd._info('Exception during execution of listener', subscription, message, x);\n }\n }\n }\n }\n }\n }\n }\n\n function _notifyListeners(channel, message) {\n // Notify direct listeners\n _notify(channel, message); // Notify the globbing listeners\n\n\n var channelParts = channel.split('/');\n var last = channelParts.length - 1;\n\n for (var i = last; i > 0; --i) {\n var channelPart = channelParts.slice(0, i).join('/') + '/*'; // We don't want to notify /foo/* if the channel is /foo/bar/baz,\n // so we stop at the first non recursive globbing\n\n if (i === last) {\n _notify(channelPart, message);\n } // Add the recursive globber and notify\n\n\n channelPart += '*';\n\n _notify(channelPart, message);\n }\n }\n\n function _cancelDelayedSend() {\n if (_scheduledSend !== null) {\n Utils.clearTimeout(_scheduledSend);\n }\n\n _scheduledSend = null;\n }\n\n function _delayedSend(operation, delay) {\n _cancelDelayedSend();\n\n var time = _advice.interval + delay;\n\n _cometd._debug('Function scheduled in', time, 'ms, interval =', _advice.interval, 'backoff =', _backoff, operation);\n\n _scheduledSend = Utils.setTimeout(_cometd, operation, time);\n } // Needed to break cyclic dependencies between function definitions\n\n\n var _handleMessages;\n\n var _handleFailure;\n /**\n * Delivers the messages to the CometD server\n * @param sync whether the send is synchronous\n * @param messages the array of messages to send\n * @param metaConnect true if this send is on /meta/connect\n * @param extraPath an extra path to append to the Bayeux server URL\n */\n\n\n function _send(sync, messages, metaConnect, extraPath) {\n // We must be sure that the messages have a clientId.\n // This is not guaranteed since the handshake may take time to return\n // (and hence the clientId is not known yet) and the application\n // may create other messages.\n for (var i = 0; i < messages.length; ++i) {\n var message = messages[i];\n var messageId = message.id;\n\n if (_clientId) {\n message.clientId = _clientId;\n }\n\n message = _applyOutgoingExtensions(message);\n\n if (message !== undefined && message !== null) {\n // Extensions may have modified the message id, but we need to own it.\n message.id = messageId;\n messages[i] = message;\n } else {\n delete _callbacks[messageId];\n messages.splice(i--, 1);\n }\n }\n\n if (messages.length === 0) {\n return;\n }\n\n var url = _cometd.getURL();\n\n if (_config.appendMessageTypeToURL) {\n // If url does not end with '/', then append it\n if (!url.match(/\\/$/)) {\n url = url + '/';\n }\n\n if (extraPath) {\n url = url + extraPath;\n }\n }\n\n var envelope = {\n url: url,\n sync: sync,\n messages: messages,\n onSuccess: function (rcvdMessages) {\n try {\n _handleMessages.call(_cometd, rcvdMessages);\n } catch (x) {\n _cometd._info('Exception during handling of messages', x);\n }\n },\n onFailure: function (conduit, messages, failure) {\n try {\n var transport = _cometd.getTransport();\n\n failure.connectionType = transport ? transport.getType() : \"unknown\";\n\n _handleFailure.call(_cometd, conduit, messages, failure);\n } catch (x) {\n _cometd._info('Exception during handling of failure', x);\n }\n }\n };\n\n _cometd._debug('Send', envelope);\n\n _transport.send(envelope, metaConnect);\n }\n\n function _queueSend(message) {\n if (_batch > 0 || _internalBatch === true) {\n _messageQueue.push(message);\n } else {\n _send(false, [message], false);\n }\n }\n /**\n * Sends a complete bayeux message.\n * This method is exposed as a public so that extensions may use it\n * to send bayeux message directly, for example in case of re-sending\n * messages that have already been sent but that for some reason must\n * be resent.\n */\n\n\n this.send = _queueSend;\n\n function _resetBackoff() {\n _backoff = 0;\n }\n\n function _increaseBackoff() {\n if (_backoff < _config.maxBackoff) {\n _backoff += _config.backoffIncrement;\n }\n\n return _backoff;\n }\n /**\n * Starts a the batch of messages to be sent in a single request.\n * @see #_endBatch(sendMessages)\n */\n\n\n function _startBatch() {\n ++_batch;\n\n _cometd._debug('Starting batch, depth', _batch);\n }\n\n function _flushBatch() {\n var messages = _messageQueue;\n _messageQueue = [];\n\n if (messages.length > 0) {\n _send(false, messages, false);\n }\n }\n /**\n * Ends the batch of messages to be sent in a single request,\n * optionally sending messages present in the message queue depending\n * on the given argument.\n * @see #_startBatch()\n */\n\n\n function _endBatch() {\n --_batch;\n\n _cometd._debug('Ending batch, depth', _batch);\n\n if (_batch < 0) {\n throw 'Calls to startBatch() and endBatch() are not paired';\n }\n\n if (_batch === 0 && !_isDisconnected() && !_internalBatch) {\n _flushBatch();\n }\n }\n /**\n * Sends the connect message\n */\n\n\n function _connect() {\n if (!_isDisconnected()) {\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: '/meta/connect',\n connectionType: _transport.getType()\n }; // In case of reload or temporary loss of connection\n // we want the next successful connect to return immediately\n // instead of being held by the server, so that connect listeners\n // can be notified that the connection has been re-established\n\n if (!_connected) {\n bayeuxMessage.advice = {\n timeout: 0\n };\n }\n\n _setStatus('connecting');\n\n _cometd._debug('Connect sent', bayeuxMessage);\n\n _send(false, [bayeuxMessage], true, 'connect');\n\n _setStatus('connected');\n }\n }\n\n function _delayedConnect(delay) {\n _setStatus('connecting');\n\n _delayedSend(function () {\n _connect();\n }, delay);\n }\n\n function _updateAdvice(newAdvice) {\n if (newAdvice) {\n _advice = _cometd._mixin(false, {}, _config.advice, newAdvice);\n\n _cometd._debug('New advice', _advice);\n }\n }\n\n function _disconnect(abort) {\n _cancelDelayedSend();\n\n if (abort && _transport) {\n _transport.abort();\n }\n\n _clientId = null;\n\n _setStatus('disconnected');\n\n _batch = 0;\n\n _resetBackoff();\n\n _transport = null;\n _reestablish = false;\n _connected = false; // Fail any existing queued message\n\n if (_messageQueue.length > 0) {\n var messages = _messageQueue;\n _messageQueue = [];\n\n _handleFailure.call(_cometd, undefined, messages, {\n reason: 'Disconnected'\n });\n }\n }\n\n function _notifyTransportException(oldTransport, newTransport, failure) {\n var handler = _cometd.onTransportException;\n\n if (_isFunction(handler)) {\n _cometd._debug('Invoking transport exception handler', oldTransport, newTransport, failure);\n\n try {\n handler.call(_cometd, failure, oldTransport, newTransport);\n } catch (x) {\n _cometd._info('Exception during execution of transport exception handler', x);\n }\n }\n }\n /**\n * Sends the initial handshake message\n */\n\n\n function _handshake(handshakeProps, handshakeCallback) {\n if (_isFunction(handshakeProps)) {\n handshakeCallback = handshakeProps;\n handshakeProps = undefined;\n }\n\n _clientId = null;\n\n _clearSubscriptions(); // Reset the transports if we're not retrying the handshake\n\n\n if (_isDisconnected()) {\n _transports.reset(true);\n\n _updateAdvice(_config.advice);\n }\n\n _batch = 0; // Mark the start of an internal batch.\n // This is needed because handshake and connect are async.\n // It may happen that the application calls init() then subscribe()\n // and the subscribe message is sent before the connect message, if\n // the subscribe message is not held until the connect message is sent.\n // So here we start a batch to hold temporarily any message until\n // the connection is fully established.\n\n _internalBatch = true; // Save the properties provided by the user, so that\n // we can reuse them during automatic re-handshake\n\n _handshakeProps = handshakeProps;\n _handshakeCallback = handshakeCallback;\n var version = '1.0'; // Figure out the transports to send to the server\n\n var url = _cometd.getURL();\n\n var transportTypes = _transports.findTransportTypes(version, _crossDomain, url);\n\n var bayeuxMessage = {\n id: _nextMessageId(),\n version: version,\n minimumVersion: version,\n channel: '/meta/handshake',\n supportedConnectionTypes: transportTypes,\n advice: {\n timeout: _advice.timeout,\n interval: _advice.interval\n }\n }; // Do not allow the user to override important fields.\n\n var message = _cometd._mixin(false, {}, _handshakeProps, bayeuxMessage); // Save the callback.\n\n\n _cometd._putCallback(message.id, handshakeCallback); // Pick up the first available transport as initial transport\n // since we don't know if the server supports it\n\n\n if (!_transport) {\n _transport = _transports.negotiateTransport(transportTypes, version, _crossDomain, url);\n\n if (!_transport) {\n var failure = 'Could not find initial transport among: ' + _transports.getTransportTypes();\n\n _cometd._warn(failure);\n\n throw failure;\n }\n }\n\n _cometd._debug('Initial transport is', _transport.getType()); // We started a batch to hold the application messages,\n // so here we must bypass it and send immediately.\n\n\n _setStatus('handshaking');\n\n _cometd._debug('Handshake sent', message);\n\n _send(false, [message], false, 'handshake');\n }\n\n function _delayedHandshake(delay) {\n _setStatus('handshaking'); // We will call _handshake() which will reset _clientId, but we want to avoid\n // that between the end of this method and the call to _handshake() someone may\n // call publish() (or other methods that call _queueSend()).\n\n\n _internalBatch = true;\n\n _delayedSend(function () {\n _handshake(_handshakeProps, _handshakeCallback);\n }, delay);\n }\n\n function _notifyCallback(callback, message) {\n try {\n callback.call(_cometd, message);\n } catch (x) {\n var handler = _cometd.onCallbackException;\n\n if (_isFunction(handler)) {\n _cometd._debug('Invoking callback exception handler', x);\n\n try {\n handler.call(_cometd, x, message);\n } catch (xx) {\n _cometd._info('Exception during execution of callback exception handler', xx);\n }\n } else {\n _cometd._info('Exception during execution of message callback', x);\n }\n }\n }\n\n this._getCallback = function (messageId) {\n return _callbacks[messageId];\n };\n\n this._putCallback = function (messageId, callback) {\n var result = this._getCallback(messageId);\n\n if (_isFunction(callback)) {\n _callbacks[messageId] = callback;\n }\n\n return result;\n };\n\n function _handleCallback(message) {\n var callback = _cometd._getCallback([message.id]);\n\n if (_isFunction(callback)) {\n delete _callbacks[message.id];\n\n _notifyCallback(callback, message);\n }\n }\n\n function _handleRemoteCall(message) {\n var context = _remoteCalls[message.id];\n delete _remoteCalls[message.id];\n\n if (context) {\n _cometd._debug('Handling remote call response for', message, 'with context', context); // Clear the timeout, if present.\n\n\n var timeout = context.timeout;\n\n if (timeout) {\n Utils.clearTimeout(timeout);\n }\n\n var callback = context.callback;\n\n if (_isFunction(callback)) {\n _notifyCallback(callback, message);\n\n return true;\n }\n }\n\n return false;\n }\n\n this.onTransportFailure = function (message, failureInfo, failureHandler) {\n this._debug('Transport failure', failureInfo, 'for', message);\n\n var transports = this.getTransportRegistry();\n var url = this.getURL();\n\n var crossDomain = this._isCrossDomain(_splitURL(url)[2]);\n\n var version = '1.0';\n var transportTypes = transports.findTransportTypes(version, crossDomain, url);\n\n if (failureInfo.action === 'none') {\n if (message.channel === '/meta/handshake') {\n if (!failureInfo.transport) {\n var failure = 'Could not negotiate transport, client=[' + transportTypes + '], server=[' + message.supportedConnectionTypes + ']';\n\n this._warn(failure);\n\n _notifyTransportException(_transport.getType(), null, {\n reason: failure,\n connectionType: _transport.getType(),\n transport: _transport\n });\n }\n }\n } else {\n failureInfo.delay = this.getBackoffPeriod(); // Different logic depending on whether we are handshaking or connecting.\n\n if (message.channel === '/meta/handshake') {\n if (!failureInfo.transport) {\n // The transport is invalid, try to negotiate again.\n var newTransport = transports.negotiateTransport(transportTypes, version, crossDomain, url);\n\n if (!newTransport) {\n this._warn('Could not negotiate transport, client=[' + transportTypes + ']');\n\n _notifyTransportException(_transport.getType(), null, message.failure);\n\n failureInfo.action = 'none';\n } else {\n this._debug('Transport', _transport.getType(), '->', newTransport.getType());\n\n _notifyTransportException(_transport.getType(), newTransport.getType(), message.failure);\n\n failureInfo.action = 'handshake';\n failureInfo.transport = newTransport;\n }\n }\n\n if (failureInfo.action !== 'none') {\n this.increaseBackoffPeriod();\n }\n } else {\n var now = new Date().getTime();\n\n if (_unconnectTime === 0) {\n _unconnectTime = now;\n }\n\n if (failureInfo.action === 'retry') {\n failureInfo.delay = this.increaseBackoffPeriod(); // Check whether we may switch to handshaking.\n\n var maxInterval = _advice.maxInterval;\n\n if (maxInterval > 0) {\n var expiration = _advice.timeout + _advice.interval + maxInterval;\n var unconnected = now - _unconnectTime;\n\n if (unconnected + _backoff > expiration) {\n failureInfo.action = 'handshake';\n }\n }\n }\n\n if (failureInfo.action === 'handshake') {\n failureInfo.delay = 0;\n transports.reset(false);\n this.resetBackoffPeriod();\n }\n }\n }\n\n failureHandler.call(_cometd, failureInfo);\n };\n\n function _handleTransportFailure(failureInfo) {\n _cometd._debug('Transport failure handling', failureInfo);\n\n if (failureInfo.transport) {\n _transport = failureInfo.transport;\n }\n\n if (failureInfo.url) {\n _transport.setURL(failureInfo.url);\n }\n\n var action = failureInfo.action;\n var delay = failureInfo.delay || 0;\n\n switch (action) {\n case 'handshake':\n _delayedHandshake(delay);\n\n break;\n\n case 'retry':\n _delayedConnect(delay);\n\n break;\n\n case 'none':\n _disconnect(true);\n\n break;\n\n default:\n throw 'Unknown action ' + action;\n }\n }\n\n function _failHandshake(message, failureInfo) {\n _handleCallback(message);\n\n _notifyListeners('/meta/handshake', message);\n\n _notifyListeners('/meta/unsuccessful', message); // The listeners may have disconnected.\n\n\n if (_isDisconnected()) {\n failureInfo.action = 'none';\n }\n\n _cometd.onTransportFailure.call(_cometd, message, failureInfo, _handleTransportFailure);\n }\n\n function _handshakeResponse(message) {\n var url = _cometd.getURL();\n\n if (message.successful) {\n var crossDomain = _cometd._isCrossDomain(_splitURL(url)[2]);\n\n var newTransport = _transports.negotiateTransport(message.supportedConnectionTypes, message.version, crossDomain, url);\n\n if (newTransport === null) {\n message.successful = false;\n\n _failHandshake(message, {\n cause: 'negotiation',\n action: 'none',\n transport: null\n });\n\n return;\n } else if (_transport !== newTransport) {\n _cometd._debug('Transport', _transport.getType(), '->', newTransport.getType());\n\n _transport = newTransport;\n }\n\n _clientId = message.clientId; // End the internal batch and allow held messages from the application\n // to go to the server (see _handshake() where we start the internal batch).\n\n _internalBatch = false;\n\n _flushBatch(); // Here the new transport is in place, as well as the clientId, so\n // the listeners can perform a publish() if they want.\n // Notify the listeners before the connect below.\n\n\n message.reestablish = _reestablish;\n _reestablish = true;\n\n _handleCallback(message);\n\n _notifyListeners('/meta/handshake', message);\n\n _handshakeMessages = message['x-messages'] || 0;\n var action = _isDisconnected() ? 'none' : _advice.reconnect || 'retry';\n\n switch (action) {\n case 'retry':\n _resetBackoff();\n\n if (_handshakeMessages === 0) {\n _delayedConnect(0);\n } else {\n _cometd._debug('Processing', _handshakeMessages, 'handshake-delivered messages');\n }\n\n break;\n\n case 'none':\n _disconnect(true);\n\n break;\n\n default:\n throw 'Unrecognized advice action ' + action;\n }\n } else {\n _failHandshake(message, {\n cause: 'unsuccessful',\n action: _advice.reconnect || 'handshake',\n transport: _transport\n });\n }\n }\n\n function _handshakeFailure(message) {\n _failHandshake(message, {\n cause: 'failure',\n action: 'handshake',\n transport: null\n });\n }\n\n function _failConnect(message, failureInfo) {\n // Notify the listeners after the status change but before the next action.\n _notifyListeners('/meta/connect', message);\n\n _notifyListeners('/meta/unsuccessful', message); // The listeners may have disconnected.\n\n\n if (_isDisconnected()) {\n failureInfo.action = 'none';\n }\n\n _cometd.onTransportFailure.call(_cometd, message, failureInfo, _handleTransportFailure);\n }\n\n function _connectResponse(message) {\n _connected = message.successful;\n\n if (_connected) {\n _notifyListeners('/meta/connect', message); // Normally, the advice will say \"reconnect: 'retry', interval: 0\"\n // and the server will hold the request, so when a response returns\n // we immediately call the server again (long polling).\n // Listeners can call disconnect(), so check the state after they run.\n\n\n var action = _isDisconnected() ? 'none' : _advice.reconnect || 'retry';\n\n switch (action) {\n case 'retry':\n _resetBackoff();\n\n _delayedConnect(_backoff);\n\n break;\n\n case 'none':\n _disconnect(false);\n\n break;\n\n default:\n throw 'Unrecognized advice action ' + action;\n }\n } else {\n _failConnect(message, {\n cause: 'unsuccessful',\n action: _advice.reconnect || 'retry',\n transport: _transport\n });\n }\n }\n\n function _connectFailure(message) {\n _connected = false;\n\n _failConnect(message, {\n cause: 'failure',\n action: 'retry',\n transport: null\n });\n }\n\n function _failDisconnect(message) {\n _disconnect(true);\n\n _handleCallback(message);\n\n _notifyListeners('/meta/disconnect', message);\n\n _notifyListeners('/meta/unsuccessful', message);\n }\n\n function _disconnectResponse(message) {\n if (message.successful) {\n // Wait for the /meta/connect to arrive.\n _disconnect(false);\n\n _handleCallback(message);\n\n _notifyListeners('/meta/disconnect', message);\n } else {\n _failDisconnect(message);\n }\n }\n\n function _disconnectFailure(message) {\n _failDisconnect(message);\n }\n\n function _failSubscribe(message) {\n var subscriptions = _listeners[message.subscription];\n\n if (subscriptions) {\n for (var id in subscriptions) {\n if (subscriptions.hasOwnProperty(id)) {\n var subscription = subscriptions[id];\n\n if (subscription && !subscription.listener) {\n delete subscriptions[id];\n\n _cometd._debug('Removed failed subscription', subscription);\n }\n }\n }\n }\n\n _handleCallback(message);\n\n _notifyListeners('/meta/subscribe', message);\n\n _notifyListeners('/meta/unsuccessful', message);\n }\n\n function _subscribeResponse(message) {\n if (message.successful) {\n _handleCallback(message);\n\n _notifyListeners('/meta/subscribe', message);\n } else {\n _failSubscribe(message);\n }\n }\n\n function _subscribeFailure(message) {\n _failSubscribe(message);\n }\n\n function _failUnsubscribe(message) {\n _handleCallback(message);\n\n _notifyListeners('/meta/unsubscribe', message);\n\n _notifyListeners('/meta/unsuccessful', message);\n }\n\n function _unsubscribeResponse(message) {\n if (message.successful) {\n _handleCallback(message);\n\n _notifyListeners('/meta/unsubscribe', message);\n } else {\n _failUnsubscribe(message);\n }\n }\n\n function _unsubscribeFailure(message) {\n _failUnsubscribe(message);\n }\n\n function _failMessage(message) {\n if (!_handleRemoteCall(message)) {\n _handleCallback(message);\n\n _notifyListeners('/meta/publish', message);\n\n _notifyListeners('/meta/unsuccessful', message);\n }\n }\n\n function _messageResponse(message) {\n if (message.data !== undefined) {\n if (!_handleRemoteCall(message)) {\n _notifyListeners(message.channel, message);\n\n if (_handshakeMessages > 0) {\n --_handshakeMessages;\n\n if (_handshakeMessages === 0) {\n _cometd._debug('Processed last handshake-delivered message');\n\n _delayedConnect(0);\n }\n }\n }\n } else {\n if (message.successful === undefined) {\n _cometd._warn('Unknown Bayeux Message', message);\n } else {\n if (message.successful) {\n _handleCallback(message);\n\n _notifyListeners('/meta/publish', message);\n } else {\n _failMessage(message);\n }\n }\n }\n }\n\n function _messageFailure(failure) {\n _failMessage(failure);\n }\n\n function _receive(message) {\n _unconnectTime = 0;\n message = _applyIncomingExtensions(message);\n\n if (message === undefined || message === null) {\n return;\n }\n\n _updateAdvice(message.advice);\n\n var channel = message.channel;\n\n switch (channel) {\n case '/meta/handshake':\n _handshakeResponse(message);\n\n break;\n\n case '/meta/connect':\n _connectResponse(message);\n\n break;\n\n case '/meta/disconnect':\n _disconnectResponse(message);\n\n break;\n\n case '/meta/subscribe':\n _subscribeResponse(message);\n\n break;\n\n case '/meta/unsubscribe':\n _unsubscribeResponse(message);\n\n break;\n\n default:\n _messageResponse(message);\n\n break;\n }\n }\n /**\n * Receives a message.\n * This method is exposed as a public so that extensions may inject\n * messages simulating that they had been received.\n */\n\n\n this.receive = _receive;\n\n _handleMessages = function (rcvdMessages) {\n _cometd._debug('Received', rcvdMessages);\n\n for (var i = 0; i < rcvdMessages.length; ++i) {\n var message = rcvdMessages[i];\n\n _receive(message);\n }\n };\n\n _handleFailure = function (conduit, messages, failure) {\n _cometd._debug('handleFailure', conduit, messages, failure);\n\n failure.transport = conduit;\n\n for (var i = 0; i < messages.length; ++i) {\n var message = messages[i];\n var failureMessage = {\n id: message.id,\n successful: false,\n channel: message.channel,\n failure: failure\n };\n failure.message = message;\n\n switch (message.channel) {\n case '/meta/handshake':\n _handshakeFailure(failureMessage);\n\n break;\n\n case '/meta/connect':\n _connectFailure(failureMessage);\n\n break;\n\n case '/meta/disconnect':\n _disconnectFailure(failureMessage);\n\n break;\n\n case '/meta/subscribe':\n failureMessage.subscription = message.subscription;\n\n _subscribeFailure(failureMessage);\n\n break;\n\n case '/meta/unsubscribe':\n failureMessage.subscription = message.subscription;\n\n _unsubscribeFailure(failureMessage);\n\n break;\n\n default:\n _messageFailure(failureMessage);\n\n break;\n }\n }\n };\n\n function _hasSubscriptions(channel) {\n var subscriptions = _listeners[channel];\n\n if (subscriptions) {\n for (var id in subscriptions) {\n if (subscriptions.hasOwnProperty(id)) {\n if (subscriptions[id]) {\n return true;\n }\n }\n }\n }\n\n return false;\n }\n\n function _resolveScopedCallback(scope, callback) {\n var delegate = {\n scope: scope,\n method: callback\n };\n\n if (_isFunction(scope)) {\n delegate.scope = undefined;\n delegate.method = scope;\n } else {\n if (_isString(callback)) {\n if (!scope) {\n throw 'Invalid scope ' + scope;\n }\n\n delegate.method = scope[callback];\n\n if (!_isFunction(delegate.method)) {\n throw 'Invalid callback ' + callback + ' for scope ' + scope;\n }\n } else if (!_isFunction(callback)) {\n throw 'Invalid callback ' + callback;\n }\n }\n\n return delegate;\n }\n\n function _addListener(channel, scope, callback, isListener) {\n // The data structure is a map<channel, subscription[]>, where each subscription\n // holds the callback to be called and its scope.\n var delegate = _resolveScopedCallback(scope, callback);\n\n _cometd._debug('Adding', isListener ? 'listener' : 'subscription', 'on', channel, 'with scope', delegate.scope, 'and callback', delegate.method);\n\n var id = ++_listenerId;\n var subscription = {\n id: id,\n channel: channel,\n scope: delegate.scope,\n callback: delegate.method,\n listener: isListener\n };\n var subscriptions = _listeners[channel];\n\n if (!subscriptions) {\n subscriptions = {};\n _listeners[channel] = subscriptions;\n }\n\n subscriptions[id] = subscription;\n\n _cometd._debug('Added', isListener ? 'listener' : 'subscription', subscription);\n\n return subscription;\n } //\n // PUBLIC API\n //\n\n /**\n * Registers the given transport under the given transport type.\n * The optional index parameter specifies the \"priority\" at which the\n * transport is registered (where 0 is the max priority).\n * If a transport with the same type is already registered, this function\n * does nothing and returns false.\n * @param type the transport type\n * @param transport the transport object\n * @param index the index at which this transport is to be registered\n * @return true if the transport has been registered, false otherwise\n * @see #unregisterTransport(type)\n */\n\n\n this.registerTransport = function (type, transport, index) {\n var result = _transports.add(type, transport, index);\n\n if (result) {\n this._debug('Registered transport', type);\n\n if (_isFunction(transport.registered)) {\n transport.registered(type, this);\n }\n }\n\n return result;\n };\n /**\n * Unregisters the transport with the given transport type.\n * @param type the transport type to unregister\n * @return the transport that has been unregistered,\n * or null if no transport was previously registered under the given transport type\n */\n\n\n this.unregisterTransport = function (type) {\n var transport = _transports.remove(type);\n\n if (transport !== null) {\n this._debug('Unregistered transport', type);\n\n if (_isFunction(transport.unregistered)) {\n transport.unregistered();\n }\n }\n\n return transport;\n };\n\n this.unregisterTransports = function () {\n _transports.clear();\n };\n /**\n * @return an array of all registered transport types\n */\n\n\n this.getTransportTypes = function () {\n return _transports.getTransportTypes();\n };\n\n this.findTransport = function (name) {\n return _transports.find(name);\n };\n /**\n * @returns the TransportRegistry object\n */\n\n\n this.getTransportRegistry = function () {\n return _transports;\n };\n /**\n * Configures the initial Bayeux communication with the Bayeux server.\n * Configuration is passed via an object that must contain a mandatory field <code>url</code>\n * of type string containing the URL of the Bayeux server.\n * @param configuration the configuration object\n */\n\n\n this.configure = function (configuration) {\n _configure.call(this, configuration);\n };\n /**\n * Configures and establishes the Bayeux communication with the Bayeux server\n * via a handshake and a subsequent connect.\n * @param configuration the configuration object\n * @param handshakeProps an object to be merged with the handshake message\n * @see #configure(configuration)\n * @see #handshake(handshakeProps)\n */\n\n\n this.init = function (configuration, handshakeProps) {\n this.configure(configuration);\n this.handshake(handshakeProps);\n };\n /**\n * Establishes the Bayeux communication with the Bayeux server\n * via a handshake and a subsequent connect.\n * @param handshakeProps an object to be merged with the handshake message\n * @param handshakeCallback a function to be invoked when the handshake is acknowledged\n */\n\n\n this.handshake = function (handshakeProps, handshakeCallback) {\n if (_status !== 'disconnected') {\n throw 'Illegal state: handshaken';\n }\n\n _handshake(handshakeProps, handshakeCallback);\n };\n /**\n * Disconnects from the Bayeux server.\n * It is possible to suggest to attempt a synchronous disconnect, but this feature\n * may only be available in certain transports (for example, long-polling may support\n * it, callback-polling certainly does not).\n * @param sync whether attempt to perform a synchronous disconnect\n * @param disconnectProps an object to be merged with the disconnect message\n * @param disconnectCallback a function to be invoked when the disconnect is acknowledged\n */\n\n\n this.disconnect = function (sync, disconnectProps, disconnectCallback) {\n if (_isDisconnected()) {\n return;\n }\n\n if (typeof sync !== 'boolean') {\n disconnectCallback = disconnectProps;\n disconnectProps = sync;\n sync = false;\n }\n\n if (_isFunction(disconnectProps)) {\n disconnectCallback = disconnectProps;\n disconnectProps = undefined;\n }\n\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: '/meta/disconnect'\n }; // Do not allow the user to override important fields.\n\n var message = this._mixin(false, {}, disconnectProps, bayeuxMessage); // Save the callback.\n\n\n _cometd._putCallback(message.id, disconnectCallback);\n\n _setStatus('disconnecting');\n\n _send(sync === true, [message], false, 'disconnect');\n };\n /**\n * Marks the start of a batch of application messages to be sent to the server\n * in a single request, obtaining a single response containing (possibly) many\n * application reply messages.\n * Messages are held in a queue and not sent until {@link #endBatch()} is called.\n * If startBatch() is called multiple times, then an equal number of endBatch()\n * calls must be made to close and send the batch of messages.\n * @see #endBatch()\n */\n\n\n this.startBatch = function () {\n _startBatch();\n };\n /**\n * Marks the end of a batch of application messages to be sent to the server\n * in a single request.\n * @see #startBatch()\n */\n\n\n this.endBatch = function () {\n _endBatch();\n };\n /**\n * Executes the given callback in the given scope, surrounded by a {@link #startBatch()}\n * and {@link #endBatch()} calls.\n * @param scope the scope of the callback, may be omitted\n * @param callback the callback to be executed within {@link #startBatch()} and {@link #endBatch()} calls\n */\n\n\n this.batch = function (scope, callback) {\n var delegate = _resolveScopedCallback(scope, callback);\n\n this.startBatch();\n\n try {\n delegate.method.call(delegate.scope);\n this.endBatch();\n } catch (x) {\n this._info('Exception during execution of batch', x);\n\n this.endBatch();\n throw x;\n }\n };\n /**\n * Adds a listener for bayeux messages, performing the given callback in the given scope\n * when a message for the given channel arrives.\n * @param channel the channel the listener is interested to\n * @param scope the scope of the callback, may be omitted\n * @param callback the callback to call when a message is sent to the channel\n * @returns the subscription handle to be passed to {@link #removeListener(object)}\n * @see #removeListener(subscription)\n */\n\n\n this.addListener = function (channel, scope, callback) {\n if (arguments.length < 2) {\n throw 'Illegal arguments number: required 2, got ' + arguments.length;\n }\n\n if (!_isString(channel)) {\n throw 'Illegal argument type: channel must be a string';\n }\n\n return _addListener(channel, scope, callback, true);\n };\n /**\n * Removes the subscription obtained with a call to {@link #addListener(string, object, function)}.\n * @param subscription the subscription to unsubscribe.\n * @see #addListener(channel, scope, callback)\n */\n\n\n this.removeListener = function (subscription) {\n // Beware of subscription.id == 0, which is falsy => cannot use !subscription.id\n if (!subscription || !subscription.channel || !(\"id\" in subscription)) {\n throw 'Invalid argument: expected subscription, not ' + subscription;\n }\n\n _removeListener(subscription);\n };\n /**\n * Removes all listeners registered with {@link #addListener(channel, scope, callback)} or\n * {@link #subscribe(channel, scope, callback)}.\n */\n\n\n this.clearListeners = function () {\n _listeners = {};\n };\n /**\n * Subscribes to the given channel, performing the given callback in the given scope\n * when a message for the channel arrives.\n * @param channel the channel to subscribe to\n * @param scope the scope of the callback, may be omitted\n * @param callback the callback to call when a message is sent to the channel\n * @param subscribeProps an object to be merged with the subscribe message\n * @param subscribeCallback a function to be invoked when the subscription is acknowledged\n * @return the subscription handle to be passed to {@link #unsubscribe(object)}\n */\n\n\n this.subscribe = function (channel, scope, callback, subscribeProps, subscribeCallback) {\n if (arguments.length < 2) {\n throw 'Illegal arguments number: required 2, got ' + arguments.length;\n }\n\n if (!_isString(channel)) {\n throw 'Illegal argument type: channel must be a string';\n }\n\n if (_isDisconnected()) {\n throw 'Illegal state: disconnected';\n } // Normalize arguments\n\n\n if (_isFunction(scope)) {\n subscribeCallback = subscribeProps;\n subscribeProps = callback;\n callback = scope;\n scope = undefined;\n }\n\n if (_isFunction(subscribeProps)) {\n subscribeCallback = subscribeProps;\n subscribeProps = undefined;\n } // Only send the message to the server if this client has not yet subscribed to the channel\n\n\n var send = !_hasSubscriptions(channel);\n\n var subscription = _addListener(channel, scope, callback, false);\n\n if (send) {\n // Send the subscription message after the subscription registration to avoid\n // races where the server would send a message to the subscribers, but here\n // on the client the subscription has not been added yet to the data structures\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: '/meta/subscribe',\n subscription: channel\n }; // Do not allow the user to override important fields.\n\n var message = this._mixin(false, {}, subscribeProps, bayeuxMessage); // Save the callback.\n\n\n _cometd._putCallback(message.id, subscribeCallback);\n\n _queueSend(message);\n }\n\n return subscription;\n };\n /**\n * Unsubscribes the subscription obtained with a call to {@link #subscribe(string, object, function)}.\n * @param subscription the subscription to unsubscribe.\n * @param unsubscribeProps an object to be merged with the unsubscribe message\n * @param unsubscribeCallback a function to be invoked when the unsubscription is acknowledged\n */\n\n\n this.unsubscribe = function (subscription, unsubscribeProps, unsubscribeCallback) {\n if (arguments.length < 1) {\n throw 'Illegal arguments number: required 1, got ' + arguments.length;\n }\n\n if (_isDisconnected()) {\n throw 'Illegal state: disconnected';\n }\n\n if (_isFunction(unsubscribeProps)) {\n unsubscribeCallback = unsubscribeProps;\n unsubscribeProps = undefined;\n } // Remove the local listener before sending the message\n // This ensures that if the server fails, this client does not get notifications\n\n\n this.removeListener(subscription);\n var channel = subscription.channel; // Only send the message to the server if this client unsubscribes the last subscription\n\n if (!_hasSubscriptions(channel)) {\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: '/meta/unsubscribe',\n subscription: channel\n }; // Do not allow the user to override important fields.\n\n var message = this._mixin(false, {}, unsubscribeProps, bayeuxMessage); // Save the callback.\n\n\n _cometd._putCallback(message.id, unsubscribeCallback);\n\n _queueSend(message);\n }\n };\n\n this.resubscribe = function (subscription, subscribeProps) {\n _removeSubscription(subscription);\n\n if (subscription) {\n return this.subscribe(subscription.channel, subscription.scope, subscription.callback, subscribeProps);\n }\n\n return undefined;\n };\n /**\n * Removes all subscriptions added via {@link #subscribe(channel, scope, callback, subscribeProps)},\n * but does not remove the listeners added via {@link addListener(channel, scope, callback)}.\n */\n\n\n this.clearSubscriptions = function () {\n _clearSubscriptions();\n };\n /**\n * Publishes a message on the given channel, containing the given content.\n * @param channel the channel to publish the message to\n * @param content the content of the message\n * @param publishProps an object to be merged with the publish message\n * @param publishCallback a function to be invoked when the publish is acknowledged by the server\n */\n\n\n this.publish = function (channel, content, publishProps, publishCallback) {\n if (arguments.length < 1) {\n throw 'Illegal arguments number: required 1, got ' + arguments.length;\n }\n\n if (!_isString(channel)) {\n throw 'Illegal argument type: channel must be a string';\n }\n\n if (/^\\/meta\\//.test(channel)) {\n throw 'Illegal argument: cannot publish to meta channels';\n }\n\n if (_isDisconnected()) {\n throw 'Illegal state: disconnected';\n }\n\n if (_isFunction(content)) {\n publishCallback = content;\n content = {};\n publishProps = undefined;\n } else if (_isFunction(publishProps)) {\n publishCallback = publishProps;\n publishProps = undefined;\n }\n\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: channel,\n data: content\n }; // Do not allow the user to override important fields.\n\n var message = this._mixin(false, {}, publishProps, bayeuxMessage); // Save the callback.\n\n\n _cometd._putCallback(message.id, publishCallback);\n\n _queueSend(message);\n };\n /**\n * Publishes a message with binary data on the given channel.\n * The binary data chunk may be an ArrayBuffer, a DataView, a TypedArray\n * (such as Uint8Array) or a plain integer array.\n * The meta data object may contain additional application data such as\n * a file name, a mime type, etc.\n * @param channel the channel to publish the message to\n * @param data the binary data to publish\n * @param last whether the binary data chunk is the last\n * @param meta an object containing meta data associated to the binary chunk\n * @param callback a function to be invoked when the publish is acknowledged by the server\n */\n\n\n this.publishBinary = function (channel, data, last, meta, callback) {\n if (_isFunction(data)) {\n callback = data;\n data = new ArrayBuffer(0);\n last = true;\n meta = undefined;\n } else if (_isFunction(last)) {\n callback = last;\n last = true;\n meta = undefined;\n } else if (_isFunction(meta)) {\n callback = meta;\n meta = undefined;\n }\n\n var content = {\n meta: meta,\n data: data,\n last: last\n };\n var ext = {\n ext: {\n binary: {}\n }\n };\n this.publish(channel, content, ext, callback);\n };\n\n this.remoteCall = function (target, content, timeout, callProps, callback) {\n if (arguments.length < 1) {\n throw 'Illegal arguments number: required 1, got ' + arguments.length;\n }\n\n if (!_isString(target)) {\n throw 'Illegal argument type: target must be a string';\n }\n\n if (_isDisconnected()) {\n throw 'Illegal state: disconnected';\n }\n\n if (_isFunction(content)) {\n callback = content;\n content = {};\n timeout = _config.maxNetworkDelay;\n callProps = undefined;\n } else if (_isFunction(timeout)) {\n callback = timeout;\n timeout = _config.maxNetworkDelay;\n callProps = undefined;\n } else if (_isFunction(callProps)) {\n callback = callProps;\n callProps = undefined;\n }\n\n if (typeof timeout !== 'number') {\n throw 'Illegal argument type: timeout must be a number';\n }\n\n if (!target.match(/^\\//)) {\n target = '/' + target;\n }\n\n var channel = '/service' + target;\n var bayeuxMessage = {\n id: _nextMessageId(),\n channel: channel,\n data: content\n };\n\n var message = this._mixin(false, {}, callProps, bayeuxMessage);\n\n var context = {\n callback: callback\n };\n\n if (timeout > 0) {\n context.timeout = Utils.setTimeout(_cometd, function () {\n _cometd._debug('Timing out remote call', message, 'after', timeout, 'ms');\n\n _failMessage({\n id: message.id,\n error: '406::timeout',\n successful: false,\n failure: {\n message: message,\n reason: 'Remote Call Timeout'\n }\n });\n }, timeout);\n\n _cometd._debug('Scheduled remote call timeout', message, 'in', timeout, 'ms');\n }\n\n _remoteCalls[message.id] = context;\n\n _queueSend(message);\n };\n\n this.remoteCallBinary = function (target, data, last, meta, timeout, callback) {\n if (_isFunction(data)) {\n callback = data;\n data = new ArrayBuffer(0);\n last = true;\n meta = undefined;\n timeout = _config.maxNetworkDelay;\n } else if (_isFunction(last)) {\n callback = last;\n last = true;\n meta = undefined;\n timeout = _config.maxNetworkDelay;\n } else if (_isFunction(meta)) {\n callback = meta;\n meta = undefined;\n timeout = _config.maxNetworkDelay;\n } else if (_isFunction(timeout)) {\n callback = timeout;\n timeout = _config.maxNetworkDelay;\n }\n\n var content = {\n meta: meta,\n data: data,\n last: last\n };\n var ext = {\n ext: {\n binary: {}\n }\n };\n this.remoteCall(target, content, timeout, ext, callback);\n };\n /**\n * Returns a string representing the status of the bayeux communication with the Bayeux server.\n */\n\n\n this.getStatus = function () {\n return _status;\n };\n /**\n * Returns whether this instance has been disconnected.\n */\n\n\n this.isDisconnected = _isDisconnected;\n /**\n * Sets the backoff period used to increase the backoff time when retrying an unsuccessful or failed message.\n * Default value is 1 second, which means if there is a persistent failure the retries will happen\n * after 1 second, then after 2 seconds, then after 3 seconds, etc. So for example with 15 seconds of\n * elapsed time, there will be 5 retries (at 1, 3, 6, 10 and 15 seconds elapsed).\n * @param period the backoff period to set\n * @see #getBackoffIncrement()\n */\n\n this.setBackoffIncrement = function (period) {\n _config.backoffIncrement = period;\n };\n /**\n * Returns the backoff period used to increase the backoff time when retrying an unsuccessful or failed message.\n * @see #setBackoffIncrement(period)\n */\n\n\n this.getBackoffIncrement = function () {\n return _config.backoffIncrement;\n };\n /**\n * Returns the backoff period to wait before retrying an unsuccessful or failed message.\n */\n\n\n this.getBackoffPeriod = function () {\n return _backoff;\n };\n /**\n * Increases the backoff period up to the maximum value configured.\n * @returns the backoff period after increment\n * @see getBackoffIncrement\n */\n\n\n this.increaseBackoffPeriod = function () {\n return _increaseBackoff();\n };\n /**\n * Resets the backoff period to zero.\n */\n\n\n this.resetBackoffPeriod = function () {\n _resetBackoff();\n };\n /**\n * Sets the log level for console logging.\n * Valid values are the strings 'error', 'warn', 'info' and 'debug', from\n * less verbose to more verbose.\n * @param level the log level string\n */\n\n\n this.setLogLevel = function (level) {\n _config.logLevel = level;\n };\n /**\n * Registers an extension whose callbacks are called for every incoming message\n * (that comes from the server to this client implementation) and for every\n * outgoing message (that originates from this client implementation for the\n * server).\n * The format of the extension object is the following:\n * <pre>\n * {\n * incoming: function(message) { ... },\n * outgoing: function(message) { ... }\n * }\n * </pre>\n * Both properties are optional, but if they are present they will be called\n * respectively for each incoming message and for each outgoing message.\n * @param name the name of the extension\n * @param extension the extension to register\n * @return true if the extension was registered, false otherwise\n * @see #unregisterExtension(name)\n */\n\n\n this.registerExtension = function (name, extension) {\n if (arguments.length < 2) {\n throw 'Illegal arguments number: required 2, got ' + arguments.length;\n }\n\n if (!_isString(name)) {\n throw 'Illegal argument type: extension name must be a string';\n }\n\n var existing = false;\n\n for (var i = 0; i < _extensions.length; ++i) {\n var existingExtension = _extensions[i];\n\n if (existingExtension.name === name) {\n existing = true;\n break;\n }\n }\n\n if (!existing) {\n _extensions.push({\n name: name,\n extension: extension\n });\n\n this._debug('Registered extension', name); // Callback for extensions\n\n\n if (_isFunction(extension.registered)) {\n extension.registered(name, this);\n }\n\n return true;\n } else {\n this._info('Could not register extension with name', name, 'since another extension with the same name already exists');\n\n return false;\n }\n };\n /**\n * Unregister an extension previously registered with\n * {@link #registerExtension(name, extension)}.\n * @param name the name of the extension to unregister.\n * @return true if the extension was unregistered, false otherwise\n */\n\n\n this.unregisterExtension = function (name) {\n if (!_isString(name)) {\n throw 'Illegal argument type: extension name must be a string';\n }\n\n var unregistered = false;\n\n for (var i = 0; i < _extensions.length; ++i) {\n var extension = _extensions[i];\n\n if (extension.name === name) {\n _extensions.splice(i, 1);\n\n unregistered = true;\n\n this._debug('Unregistered extension', name); // Callback for extensions\n\n\n var ext = extension.extension;\n\n if (_isFunction(ext.unregistered)) {\n ext.unregistered();\n }\n\n break;\n }\n }\n\n return unregistered;\n };\n /**\n * Find the extension registered with the given name.\n * @param name the name of the extension to find\n * @return the extension found or null if no extension with the given name has been registered\n */\n\n\n this.getExtension = function (name) {\n for (var i = 0; i < _extensions.length; ++i) {\n var extension = _extensions[i];\n\n if (extension.name === name) {\n return extension.extension;\n }\n }\n\n return null;\n };\n /**\n * Returns the name assigned to this CometD object, or the string 'default'\n * if no name has been explicitly passed as parameter to the constructor.\n */\n\n\n this.getName = function () {\n return _name;\n };\n /**\n * Returns the clientId assigned by the Bayeux server during handshake.\n */\n\n\n this.getClientId = function () {\n return _clientId;\n };\n /**\n * Returns the URL of the Bayeux server.\n */\n\n\n this.getURL = function () {\n if (_transport) {\n var url = _transport.getURL();\n\n if (url) {\n return url;\n }\n\n url = _config.urls[_transport.getType()];\n\n if (url) {\n return url;\n }\n }\n\n return _config.url;\n };\n\n this.getTransport = function () {\n return _transport;\n };\n\n this.getConfiguration = function () {\n return this._mixin(true, {}, _config);\n };\n\n this.getAdvice = function () {\n return this._mixin(true, {}, _advice);\n }; // Initialize transports.\n\n\n if (window.WebSocket) {\n this.registerTransport('websocket', new WebSocketTransport());\n }\n\n this.registerTransport('long-polling', new LongPollingTransport());\n this.registerTransport('callback-polling', new CallbackPollingTransport());\n };\n\n var _z85EncodeTable = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '.', '-', ':', '+', '=', '^', '!', '/', '*', '?', '&', '<', '>', '(', ')', '[', ']', '{', '}', '@', '%', '$', '#'];\n var _z85DecodeTable = [0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00, 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47, 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00];\n var Z85 = {\n encode: function (bytes) {\n var buffer = null;\n\n if (bytes instanceof ArrayBuffer) {\n buffer = bytes;\n } else if (bytes.buffer instanceof ArrayBuffer) {\n buffer = bytes.buffer;\n } else if (Array.isArray(bytes)) {\n buffer = new Uint8Array(bytes).buffer;\n }\n\n if (buffer == null) {\n throw 'Cannot Z85 encode ' + bytes;\n }\n\n var length = buffer.byteLength;\n var remainder = length % 4;\n var padding = 4 - (remainder === 0 ? 4 : remainder);\n var view = new DataView(buffer);\n var result = '';\n var value = 0;\n\n for (var i = 0; i < length + padding; ++i) {\n var isPadding = i >= length;\n value = value * 256 + (isPadding ? 0 : view.getUint8(i));\n\n if ((i + 1) % 4 === 0) {\n var divisor = 85 * 85 * 85 * 85;\n\n for (var j = 5; j > 0; --j) {\n if (!isPadding || j > padding) {\n var code = Math.floor(value / divisor) % 85;\n result += _z85EncodeTable[code];\n }\n\n divisor /= 85;\n }\n\n value = 0;\n }\n }\n\n return result;\n },\n decode: function (string) {\n var remainder = string.length % 5;\n var padding = 5 - (remainder === 0 ? 5 : remainder);\n\n for (var p = 0; p < padding; ++p) {\n string += _z85EncodeTable[_z85EncodeTable.length - 1];\n }\n\n var length = string.length;\n var buffer = new ArrayBuffer(length * 4 / 5 - padding);\n var view = new DataView(buffer);\n var value = 0;\n var charIdx = 0;\n var byteIdx = 0;\n\n for (var i = 0; i < length; ++i) {\n var code = string.charCodeAt(charIdx++) - 32;\n value = value * 85 + _z85DecodeTable[code];\n\n if (charIdx % 5 === 0) {\n var divisor = 256 * 256 * 256;\n\n while (divisor >= 1) {\n if (byteIdx < view.byteLength) {\n view.setUint8(byteIdx++, Math.floor(value / divisor) % 256);\n }\n\n divisor /= 256;\n }\n\n value = 0;\n }\n }\n\n return buffer;\n }\n };\n return {\n CometD: CometD,\n Transport: Transport,\n RequestTransport: RequestTransport,\n LongPollingTransport: LongPollingTransport,\n CallbackPollingTransport: CallbackPollingTransport,\n WebSocketTransport: WebSocketTransport,\n Utils: Utils,\n Z85: Z85\n };\n});","/*\r\n\tThe MIT License (MIT)\r\n\r\n\tCopyright (c) 2016 Meetecho\r\n\r\n\tPermission is hereby granted, free of charge, to any person obtaining\r\n\ta copy of this software and associated documentation files (the \"Software\"),\r\n\tto deal in the Software without restriction, including without limitation\r\n\tthe rights to use, copy, modify, merge, publish, distribute, sublicense,\r\n\tand/or sell copies of the Software, and to permit persons to whom the\r\n\tSoftware is furnished to do so, subject to the following conditions:\r\n\r\n\tThe above copyright notice and this permission notice shall be included\r\n\tin all copies or substantial portions of the Software.\r\n\r\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r\n\tOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r\n\tTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\r\n\tOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\r\n\tARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\n\tOTHER DEALINGS IN THE SOFTWARE.\r\n */\n// List of sessions\nJanus.sessions = {};\n\nJanus.isExtensionEnabled = function () {\n if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {\n // No need for the extension, getDisplayMedia is supported\n return true;\n }\n\n if (window.navigator.userAgent.match('Chrome')) {\n var chromever = parseInt(window.navigator.userAgent.match(/Chrome\\/(.*) /)[1], 10);\n var maxver = 33;\n if (window.navigator.userAgent.match('Linux')) maxver = 35; // \"known\" crash in chrome 34 and 35 on linux\n\n if (chromever >= 26 && chromever <= maxver) {\n // Older versions of Chrome don't support this extension-based approach, so lie\n return true;\n }\n\n return Janus.extension.isInstalled();\n } else {\n // Firefox of others, no need for the extension (but this doesn't mean it will work)\n return true;\n }\n};\n\nvar defaultExtension = {\n // Screensharing Chrome Extension ID\n extensionId: 'hapfgfdkleiggjjpfpenajgdnfckjpaj',\n isInstalled: function () {\n return document.querySelector('#janus-extension-installed') !== null;\n },\n getScreen: function (callback) {\n var pending = window.setTimeout(function () {\n error = new Error('NavigatorUserMediaError');\n error.name = 'The required Chrome extension is not installed: click <a href=\"#\">here</a> to install it. (NOTE: this will need you to refresh the page)';\n return callback(error);\n }, 1000);\n this.cache[pending] = callback;\n window.postMessage({\n type: 'janusGetScreen',\n id: pending\n }, '*');\n },\n init: function () {\n var cache = {};\n this.cache = cache; // Wait for events from the Chrome Extension\n\n window.addEventListener('message', function (event) {\n if (event.origin != window.location.origin) return;\n\n if (event.data.type == 'janusGotScreen' && cache[event.data.id]) {\n var callback = cache[event.data.id];\n delete cache[event.data.id];\n\n if (event.data.sourceId === '') {\n // user canceled\n var error = new Error('NavigatorUserMediaError');\n error.name = 'You cancelled the request for permission, giving up...';\n callback(error);\n } else {\n callback(null, event.data.sourceId);\n }\n } else if (event.data.type == 'janusGetScreenPending') {\n console.log('clearing ', event.data.id);\n window.clearTimeout(event.data.id);\n }\n });\n }\n};\n\nJanus.useDefaultDependencies = function (deps) {\n var f = deps && deps.fetch || fetch;\n var p = deps && deps.Promise || Promise;\n var socketCls = deps && deps.WebSocket || WebSocket;\n return {\n newWebSocket: function (server, proto) {\n return new socketCls(server, proto);\n },\n extension: deps && deps.extension || defaultExtension,\n isArray: function (arr) {\n return Array.isArray(arr);\n },\n webRTCAdapter: deps && deps.adapter || adapter,\n httpAPICall: function (url, options) {\n var fetchOptions = {\n method: options.verb,\n headers: {\n 'Accept': 'application/json, text/plain, */*'\n },\n cache: 'no-cache'\n };\n\n if (options.verb === \"POST\") {\n fetchOptions.headers['Content-Type'] = 'application/json';\n }\n\n if (options.withCredentials !== undefined) {\n fetchOptions.credentials = options.withCredentials === true ? 'include' : options.withCredentials ? options.withCredentials : 'omit';\n }\n\n if (options.body !== undefined) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n var fetching = f(url, fetchOptions).catch(function (error) {\n return p.reject({\n message: 'Probably a network error, is the server down?',\n error: error\n });\n });\n /*\r\n * fetch() does not natively support timeouts.\r\n * Work around this by starting a timeout manually, and racing it agains the fetch() to see which thing resolves first.\r\n */\n\n if (options.timeout !== undefined) {\n var timeout = new p(function (resolve, reject) {\n var timerId = setTimeout(function () {\n clearTimeout(timerId);\n return reject({\n message: 'Request timed out',\n timeout: options.timeout\n });\n }, options.timeout);\n });\n fetching = p.race([fetching, timeout]);\n }\n\n fetching.then(function (response) {\n if (response.ok) {\n if (typeof options.success === typeof Janus.noop) {\n return response.json().then(function (parsed) {\n options.success(parsed);\n }).catch(function (error) {\n return p.reject({\n message: 'Failed to parse response body',\n error: error,\n response: response\n });\n });\n }\n } else {\n return p.reject({\n message: 'API call failed',\n response: response\n });\n }\n }).catch(function (error) {\n if (typeof options.error === typeof Janus.noop) {\n options.error(error.message || '<< internal error >>', error);\n }\n });\n return fetching;\n }\n };\n};\n\nJanus.useOldDependencies = function (deps) {\n var jq = deps && deps.jQuery || jQuery;\n var socketCls = deps && deps.WebSocket || WebSocket;\n return {\n newWebSocket: function (server, proto) {\n return new socketCls(server, proto);\n },\n isArray: function (arr) {\n return jq.isArray(arr);\n },\n extension: deps && deps.extension || defaultExtension,\n webRTCAdapter: deps && deps.adapter || adapter,\n httpAPICall: function (url, options) {\n var payload = options.body !== undefined ? {\n contentType: 'application/json',\n data: JSON.stringify(options.body)\n } : {};\n var credentials = options.withCredentials !== undefined ? {\n xhrFields: {\n withCredentials: options.withCredentials\n }\n } : {};\n return jq.ajax(jq.extend(payload, credentials, {\n url: url,\n type: options.verb,\n cache: false,\n dataType: 'json',\n async: options.async,\n timeout: options.timeout,\n success: function (result) {\n if (typeof options.success === typeof Janus.noop) {\n options.success(result);\n }\n },\n error: function (xhr, status, err) {\n if (typeof options.error === typeof Janus.noop) {\n options.error(status, err);\n }\n }\n }));\n }\n };\n};\n\nJanus.noop = function () {};\n\nJanus.dataChanDefaultLabel = \"JanusDataChannel\"; // Initialization\n\nJanus.init = function (options) {\n options = options || {};\n options.callback = typeof options.callback == \"function\" ? options.callback : Janus.noop;\n\n if (Janus.initDone === true) {\n // Already initialized\n options.callback();\n } else {\n if (typeof console == \"undefined\" || typeof console.log == \"undefined\") console = {\n log: function () {}\n }; // Console logging (all debugging disabled by default)\n\n Janus.trace = Janus.noop;\n Janus.debug = Janus.noop;\n Janus.vdebug = Janus.noop;\n Janus.log = Janus.noop;\n Janus.warn = Janus.noop;\n Janus.error = Janus.noop;\n\n if (options.debug === true || options.debug === \"all\") {\n // Enable all debugging levels\n Janus.trace = console.trace.bind(console);\n Janus.debug = console.debug.bind(console);\n Janus.vdebug = console.debug.bind(console);\n Janus.log = console.log.bind(console);\n Janus.warn = console.warn.bind(console);\n Janus.error = console.error.bind(console);\n } else if (Array.isArray(options.debug)) {\n for (var i in options.debug) {\n var d = options.debug[i];\n\n switch (d) {\n case \"trace\":\n Janus.trace = console.trace.bind(console);\n break;\n\n case \"debug\":\n Janus.debug = console.debug.bind(console);\n break;\n\n case \"vdebug\":\n Janus.vdebug = console.debug.bind(console);\n break;\n\n case \"log\":\n Janus.log = console.log.bind(console);\n break;\n\n case \"warn\":\n Janus.warn = console.warn.bind(console);\n break;\n\n case \"error\":\n Janus.error = console.error.bind(console);\n break;\n\n default:\n console.error(\"Unknown debugging option '\" + d + \"' (supported: 'trace', 'debug', 'vdebug', 'log', warn', 'error')\");\n break;\n }\n }\n }\n\n Janus.log(\"Initializing library\");\n var usedDependencies = options.dependencies || Janus.useDefaultDependencies();\n Janus.isArray = usedDependencies.isArray;\n Janus.webRTCAdapter = usedDependencies.webRTCAdapter;\n Janus.httpAPICall = usedDependencies.httpAPICall;\n Janus.newWebSocket = usedDependencies.newWebSocket;\n Janus.extension = usedDependencies.extension;\n Janus.extension.init(); // Helper method to enumerate devices\n\n Janus.listDevices = function (callback, config) {\n callback = typeof callback == \"function\" ? callback : Janus.noop;\n if (config == null) config = {\n audio: true,\n video: true\n };\n\n if (Janus.isGetUserMediaAvailable()) {\n navigator.mediaDevices.getUserMedia(config).then(function (stream) {\n navigator.mediaDevices.enumerateDevices().then(function (devices) {\n Janus.debug(devices);\n callback(devices); // Get rid of the now useless stream\n\n try {\n var tracks = stream.getTracks();\n\n for (var i in tracks) {\n var mst = tracks[i];\n if (mst !== null && mst !== undefined) mst.stop();\n }\n } catch (e) {}\n });\n }).catch(function (err) {\n Janus.error(err);\n callback([]);\n });\n } else {\n Janus.warn(\"navigator.mediaDevices unavailable\");\n callback([]);\n }\n }; // Helper methods to attach/reattach a stream to a video element (previously part of adapter.js)\n\n\n Janus.attachMediaStream = function (element, stream) {\n if (Janus.webRTCAdapter.browserDetails.browser === 'chrome') {\n var chromever = Janus.webRTCAdapter.browserDetails.version;\n\n if (chromever >= 52) {\n element.srcObject = stream;\n } else if (typeof element.src !== 'undefined') {\n element.src = URL.createObjectURL(stream);\n } else {\n Janus.error(\"Error attaching stream to element\");\n }\n } else {\n element.srcObject = stream;\n }\n };\n\n Janus.reattachMediaStream = function (to, from) {\n if (Janus.webRTCAdapter.browserDetails.browser === 'chrome') {\n var chromever = Janus.webRTCAdapter.browserDetails.version;\n\n if (chromever >= 52) {\n to.srcObject = from.srcObject;\n } else if (typeof to.src !== 'undefined') {\n to.src = from.src;\n } else {\n Janus.error(\"Error reattaching stream to element\");\n }\n } else {\n to.srcObject = from.srcObject;\n }\n }; // Detect tab close: make sure we don't loose existing onbeforeunload handlers\n // (note: for iOS we need to subscribe to a different event, 'pagehide', see\n // https://gist.github.com/thehunmonkgroup/6bee8941a49b86be31a787fe8f4b8cfe)\n\n\n var iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0;\n var eventName = iOS ? 'pagehide' : 'beforeunload';\n var oldOBF = window[\"on\" + eventName];\n window.addEventListener(eventName, function (event) {\n Janus.log(\"Closing window\");\n\n for (var s in Janus.sessions) {\n if (Janus.sessions[s] !== null && Janus.sessions[s] !== undefined && Janus.sessions[s].destroyOnUnload) {\n Janus.log(\"Destroying session \" + s);\n Janus.sessions[s].destroy({\n asyncRequest: false,\n notifyDestroyed: false\n });\n }\n }\n\n if (oldOBF && typeof oldOBF == \"function\") oldOBF();\n }); // If this is a Safari Technology Preview, check if VP8 is supported\n\n Janus.safariVp8 = false;\n\n if (Janus.webRTCAdapter.browserDetails.browser === 'safari' && Janus.webRTCAdapter.browserDetails.version >= 605) {\n // Let's see if RTCRtpSender.getCapabilities() is there\n if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities(\"video\") && RTCRtpSender.getCapabilities(\"video\").codecs && RTCRtpSender.getCapabilities(\"video\").codecs.length) {\n for (var i in RTCRtpSender.getCapabilities(\"video\").codecs) {\n var codec = RTCRtpSender.getCapabilities(\"video\").codecs[i];\n\n if (codec && codec.mimeType && codec.mimeType.toLowerCase() === \"video/vp8\") {\n Janus.safariVp8 = true;\n break;\n }\n }\n\n if (Janus.safariVp8) {\n Janus.log(\"This version of Safari supports VP8\");\n } else {\n Janus.warn(\"This version of Safari does NOT support VP8: if you're using a Technology Preview, \" + \"try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu\");\n }\n } else {\n // We do it in a very ugly way, as there's no alternative...\n // We create a PeerConnection to see if VP8 is in an offer\n var testpc = new RTCPeerConnection({}, {});\n testpc.createOffer({\n offerToReceiveVideo: true\n }).then(function (offer) {\n Janus.safariVp8 = offer.sdp.indexOf(\"VP8\") !== -1;\n\n if (Janus.safariVp8) {\n Janus.log(\"This version of Safari supports VP8\");\n } else {\n Janus.warn(\"This version of Safari does NOT support VP8: if you're using a Technology Preview, \" + \"try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu\");\n }\n\n testpc.close();\n testpc = null;\n });\n }\n }\n\n Janus.initDone = true;\n options.callback();\n }\n}; // Helper method to check whether WebRTC is supported by this browser\n\n\nJanus.isWebrtcSupported = function () {\n return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null;\n}; // Helper method to check whether devices can be accessed by this browser (e.g., not possible via plain HTTP)\n\n\nJanus.isGetUserMediaAvailable = function () {\n return navigator.mediaDevices !== undefined && navigator.mediaDevices !== null && navigator.mediaDevices.getUserMedia !== undefined && navigator.mediaDevices.getUserMedia !== null;\n}; // Helper method to create random identifiers (e.g., transaction)\n\n\nJanus.randomString = function (len) {\n var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n var randomString = '';\n\n for (var i = 0; i < len; i++) {\n var randomPoz = Math.floor(Math.random() * charSet.length);\n randomString += charSet.substring(randomPoz, randomPoz + 1);\n }\n\n return randomString;\n};\n\nfunction Janus(gatewayCallbacks) {\n if (Janus.initDone === undefined) {\n gatewayCallbacks.error(\"Library not initialized\");\n return {};\n }\n\n if (!Janus.isWebrtcSupported()) {\n gatewayCallbacks.error(\"WebRTC not supported by this browser\");\n return {};\n }\n\n Janus.log(\"Library initialized: \" + Janus.initDone);\n gatewayCallbacks = gatewayCallbacks || {};\n gatewayCallbacks.success = typeof gatewayCallbacks.success == \"function\" ? gatewayCallbacks.success : Janus.noop;\n gatewayCallbacks.error = typeof gatewayCallbacks.error == \"function\" ? gatewayCallbacks.error : Janus.noop;\n gatewayCallbacks.destroyed = typeof gatewayCallbacks.destroyed == \"function\" ? gatewayCallbacks.destroyed : Janus.noop;\n\n if (gatewayCallbacks.server === null || gatewayCallbacks.server === undefined) {\n gatewayCallbacks.error(\"Invalid server url\");\n return {};\n }\n\n var websockets = false;\n var ws = null;\n var wsHandlers = {};\n var wsKeepaliveTimeoutId = null;\n var servers = null,\n serversIndex = 0;\n var server = gatewayCallbacks.server;\n\n if (Janus.isArray(server)) {\n Janus.log(\"Multiple servers provided (\" + server.length + \"), will use the first that works\");\n server = null;\n servers = gatewayCallbacks.server;\n Janus.debug(servers);\n } else {\n if (server.indexOf(\"ws\") === 0) {\n websockets = true;\n Janus.log(\"Using WebSockets to contact Janus: \" + server);\n } else {\n websockets = false;\n Janus.log(\"Using REST API to contact Janus: \" + server);\n }\n }\n\n var iceServers = gatewayCallbacks.iceServers;\n if (iceServers === undefined || iceServers === null) iceServers = [{\n urls: \"stun:stun.l.google.com:19302\"\n }];\n var iceTransportPolicy = gatewayCallbacks.iceTransportPolicy;\n var bundlePolicy = gatewayCallbacks.bundlePolicy; // Whether IPv6 candidates should be gathered\n\n var ipv6Support = gatewayCallbacks.ipv6;\n if (ipv6Support === undefined || ipv6Support === null) ipv6Support = false; // Whether we should enable the withCredentials flag for XHR requests\n\n var withCredentials = false;\n if (gatewayCallbacks.withCredentials !== undefined && gatewayCallbacks.withCredentials !== null) withCredentials = gatewayCallbacks.withCredentials === true; // Optional max events\n\n var maxev = 10;\n if (gatewayCallbacks.max_poll_events !== undefined && gatewayCallbacks.max_poll_events !== null) maxev = gatewayCallbacks.max_poll_events;\n if (maxev < 1) maxev = 1; // Token to use (only if the token based authentication mechanism is enabled)\n\n var token = null;\n if (gatewayCallbacks.token !== undefined && gatewayCallbacks.token !== null) token = gatewayCallbacks.token; // API secret to use (only if the shared API secret is enabled)\n\n var apisecret = null;\n if (gatewayCallbacks.apisecret !== undefined && gatewayCallbacks.apisecret !== null) apisecret = gatewayCallbacks.apisecret; // Whether we should destroy this session when onbeforeunload is called\n\n this.destroyOnUnload = true;\n if (gatewayCallbacks.destroyOnUnload !== undefined && gatewayCallbacks.destroyOnUnload !== null) this.destroyOnUnload = gatewayCallbacks.destroyOnUnload === true; // Some timeout-related values\n\n var keepAlivePeriod = 25000;\n if (gatewayCallbacks.keepAlivePeriod !== undefined && gatewayCallbacks.keepAlivePeriod !== null) keepAlivePeriod = gatewayCallbacks.keepAlivePeriod;\n if (isNaN(keepAlivePeriod)) keepAlivePeriod = 25000;\n var longPollTimeout = 60000;\n if (gatewayCallbacks.longPollTimeout !== undefined && gatewayCallbacks.longPollTimeout !== null) longPollTimeout = gatewayCallbacks.longPollTimeout;\n if (isNaN(longPollTimeout)) longPollTimeout = 60000;\n var connected = false;\n var sessionId = null;\n var pluginHandles = {};\n var that = this;\n var retries = 0;\n var transactions = {};\n createSession(gatewayCallbacks); // Public methods\n\n this.getServer = function () {\n return server;\n };\n\n this.isConnected = function () {\n return connected;\n };\n\n this.reconnect = function (callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n callbacks[\"reconnect\"] = true;\n createSession(callbacks);\n };\n\n this.getSessionId = function () {\n return sessionId;\n };\n\n this.destroy = function (callbacks) {\n destroySession(callbacks);\n };\n\n this.attach = function (callbacks) {\n createHandle(callbacks);\n };\n\n function eventHandler() {\n if (sessionId == null) return;\n Janus.debug('Long poll...');\n\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n return;\n }\n\n var longpoll = server + \"/\" + sessionId + \"?rid=\" + new Date().getTime();\n if (maxev !== undefined && maxev !== null) longpoll = longpoll + \"&maxev=\" + maxev;\n if (token !== null && token !== undefined) longpoll = longpoll + \"&token=\" + encodeURIComponent(token);\n if (apisecret !== null && apisecret !== undefined) longpoll = longpoll + \"&apisecret=\" + encodeURIComponent(apisecret);\n Janus.httpAPICall(longpoll, {\n verb: 'GET',\n withCredentials: withCredentials,\n success: handleEvent,\n timeout: longPollTimeout,\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown);\n retries++;\n\n if (retries > 3) {\n // Did we just lose the server? :-(\n connected = false;\n gatewayCallbacks.error(\"Lost connection to the server (is it down?)\");\n return;\n }\n\n eventHandler();\n }\n });\n } // Private event handler: this will trigger plugin callbacks, if set\n\n\n function handleEvent(json, skipTimeout) {\n retries = 0;\n if (!websockets && sessionId !== undefined && sessionId !== null && skipTimeout !== true) eventHandler();\n\n if (!websockets && Janus.isArray(json)) {\n // We got an array: it means we passed a maxev > 1, iterate on all objects\n for (var i = 0; i < json.length; i++) {\n handleEvent(json[i], true);\n }\n\n return;\n }\n\n if (json[\"janus\"] === \"keepalive\") {\n // Nothing happened\n Janus.vdebug(\"Got a keepalive on session \" + sessionId);\n return;\n } else if (json[\"janus\"] === \"ack\") {\n // Just an ack, we can probably ignore\n Janus.debug(\"Got an ack on session \" + sessionId);\n Janus.debug(json);\n var transaction = json[\"transaction\"];\n\n if (transaction !== null && transaction !== undefined) {\n var reportSuccess = transactions[transaction];\n\n if (reportSuccess !== null && reportSuccess !== undefined) {\n reportSuccess(json);\n }\n\n delete transactions[transaction];\n }\n\n return;\n } else if (json[\"janus\"] === \"success\") {\n // Success!\n Janus.debug(\"Got a success on session \" + sessionId);\n Janus.debug(json);\n var transaction = json[\"transaction\"];\n\n if (transaction !== null && transaction !== undefined) {\n var reportSuccess = transactions[transaction];\n\n if (reportSuccess !== null && reportSuccess !== undefined) {\n reportSuccess(json);\n }\n\n delete transactions[transaction];\n }\n\n return;\n } else if (json[\"janus\"] === \"trickle\") {\n // We got a trickle candidate from Janus\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.debug(\"This handle is not attached to this session\");\n return;\n }\n\n var candidate = json[\"candidate\"];\n Janus.debug(\"Got a trickled candidate on session \" + sessionId);\n Janus.debug(candidate);\n var config = pluginHandle.webrtcStuff;\n\n if (config.pc && config.remoteSdp) {\n // Add candidate right now\n Janus.debug(\"Adding remote candidate:\", candidate);\n\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate();\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n } else {\n // We didn't do setRemoteDescription (trickle got here before the offer?)\n Janus.debug(\"We didn't do setRemoteDescription (trickle got here before the offer?), caching candidate\");\n if (!config.candidates) config.candidates = [];\n config.candidates.push(candidate);\n Janus.debug(config.candidates);\n }\n } else if (json[\"janus\"] === \"webrtcup\") {\n // The PeerConnection with the server is up! Notify this\n Janus.debug(\"Got a webrtcup event on session \" + sessionId);\n Janus.debug(json);\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.debug(\"This handle is not attached to this session\");\n return;\n }\n\n pluginHandle.webrtcState(true);\n return;\n } else if (json[\"janus\"] === \"hangup\") {\n // A plugin asked the core to hangup a PeerConnection on one of our handles\n Janus.debug(\"Got a hangup event on session \" + sessionId);\n Janus.debug(json);\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.debug(\"This handle is not attached to this session\");\n return;\n }\n\n pluginHandle.webrtcState(false, json[\"reason\"]);\n pluginHandle.hangup();\n } else if (json[\"janus\"] === \"detached\") {\n // A plugin asked the core to detach one of our handles\n Janus.debug(\"Got a detached event on session \" + sessionId);\n Janus.debug(json);\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n // Don't warn here because destroyHandle causes this situation.\n return;\n }\n\n pluginHandle.detached = true;\n pluginHandle.ondetached();\n pluginHandle.detach();\n } else if (json[\"janus\"] === \"media\") {\n // Media started/stopped flowing\n Janus.debug(\"Got a media event on session \" + sessionId);\n Janus.debug(json);\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.debug(\"This handle is not attached to this session\");\n return;\n }\n\n pluginHandle.mediaState(json[\"type\"], json[\"receiving\"]);\n } else if (json[\"janus\"] === \"slowlink\") {\n Janus.debug(\"Got a slowlink event on session \" + sessionId);\n Janus.debug(json); // Trouble uplink or downlink\n\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.debug(\"This handle is not attached to this session\");\n return;\n }\n\n pluginHandle.slowLink(json[\"uplink\"], json[\"nacks\"]);\n } else if (json[\"janus\"] === \"error\") {\n // Oops, something wrong happened\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n Janus.debug(json);\n var transaction = json[\"transaction\"];\n\n if (transaction !== null && transaction !== undefined) {\n var reportSuccess = transactions[transaction];\n\n if (reportSuccess !== null && reportSuccess !== undefined) {\n reportSuccess(json);\n }\n\n delete transactions[transaction];\n }\n\n return;\n } else if (json[\"janus\"] === \"event\") {\n Janus.debug(\"Got a plugin event on session \" + sessionId);\n Janus.debug(json);\n var sender = json[\"sender\"];\n\n if (sender === undefined || sender === null) {\n Janus.warn(\"Missing sender...\");\n return;\n }\n\n var plugindata = json[\"plugindata\"];\n\n if (plugindata === undefined || plugindata === null) {\n Janus.warn(\"Missing plugindata...\");\n return;\n }\n\n Janus.debug(\" -- Event is coming from \" + sender + \" (\" + plugindata[\"plugin\"] + \")\");\n var data = plugindata[\"data\"];\n Janus.debug(data);\n var pluginHandle = pluginHandles[sender];\n\n if (pluginHandle === undefined || pluginHandle === null) {\n Janus.warn(\"This handle is not attached to this session\");\n return;\n }\n\n var jsep = json[\"jsep\"];\n\n if (jsep !== undefined && jsep !== null) {\n Janus.debug(\"Handling SDP as well...\");\n Janus.debug(jsep);\n }\n\n var callback = pluginHandle.onmessage;\n\n if (callback !== null && callback !== undefined) {\n Janus.debug(\"Notifying application...\"); // Send to callback specified when attaching plugin handle\n\n callback(data, jsep);\n } else {\n // Send to generic callback (?)\n Janus.debug(\"No provided notification callback\");\n }\n } else if (json[\"janus\"] === \"timeout\") {\n Janus.error(\"Timeout on session \" + sessionId);\n Janus.debug(json);\n\n if (websockets) {\n ws.close(3504, \"Gateway timeout\");\n }\n\n return;\n } else {\n Janus.warn(\"Unknown message/event '\" + json[\"janus\"] + \"' on session \" + sessionId);\n Janus.debug(json);\n }\n } // Private helper to send keep-alive messages on WebSockets\n\n\n function keepAlive() {\n if (server === null || !websockets || !connected) return;\n wsKeepaliveTimeoutId = setTimeout(keepAlive, keepAlivePeriod);\n var request = {\n \"janus\": \"keepalive\",\n \"session_id\": sessionId,\n \"transaction\": Janus.randomString(12)\n };\n if (token !== null && token !== undefined) request[\"token\"] = token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n ws.send(JSON.stringify(request));\n } // Private method to create a session\n\n\n function createSession(callbacks) {\n var transaction = Janus.randomString(12);\n var request = {\n \"janus\": \"create\",\n \"transaction\": transaction\n };\n\n if (callbacks[\"reconnect\"]) {\n // We're reconnecting, claim the session\n connected = false;\n request[\"janus\"] = \"claim\";\n request[\"session_id\"] = sessionId; // If we were using websockets, ignore the old connection\n\n if (ws) {\n ws.onopen = null;\n ws.onerror = null;\n ws.onclose = null;\n\n if (wsKeepaliveTimeoutId) {\n clearTimeout(wsKeepaliveTimeoutId);\n wsKeepaliveTimeoutId = null;\n }\n }\n }\n\n if (token !== null && token !== undefined) request[\"token\"] = token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n\n if (server === null && Janus.isArray(servers)) {\n // We still need to find a working server from the list we were given\n server = servers[serversIndex];\n\n if (server.indexOf(\"ws\") === 0) {\n websockets = true;\n Janus.log(\"Server #\" + (serversIndex + 1) + \": trying WebSockets to contact Janus (\" + server + \")\");\n } else {\n websockets = false;\n Janus.log(\"Server #\" + (serversIndex + 1) + \": trying REST API to contact Janus (\" + server + \")\");\n }\n }\n\n if (websockets) {\n ws = Janus.newWebSocket(server, 'janus-protocol');\n wsHandlers = {\n 'error': function () {\n Janus.error(\"Error connecting to the Janus WebSockets server... \" + server);\n\n if (Janus.isArray(servers) && !callbacks[\"reconnect\"]) {\n serversIndex++;\n\n if (serversIndex == servers.length) {\n // We tried all the servers the user gave us and they all failed\n callbacks.error(\"Error connecting to any of the provided Janus servers: Is the server down?\");\n return;\n } // Let's try the next server\n\n\n server = null;\n setTimeout(function () {\n createSession(callbacks);\n }, 200);\n return;\n }\n\n callbacks.error(\"Error connecting to the Janus WebSockets server: Is the server down?\");\n },\n 'open': function () {\n // We need to be notified about the success\n transactions[transaction] = function (json) {\n Janus.debug(json);\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(json[\"error\"].reason);\n return;\n }\n\n wsKeepaliveTimeoutId = setTimeout(keepAlive, keepAlivePeriod);\n connected = true;\n sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n if (callbacks[\"reconnect\"]) {\n Janus.log(\"Claimed session: \" + sessionId);\n } else {\n Janus.log(\"Created session: \" + sessionId);\n }\n\n Janus.sessions[sessionId] = that;\n callbacks.success();\n };\n\n ws.send(JSON.stringify(request));\n },\n 'message': function (event) {\n handleEvent(JSON.parse(event.data));\n },\n 'close': function () {\n if (server === null || !connected) {\n return;\n }\n\n connected = false; // FIXME What if this is called when the page is closed?\n\n gatewayCallbacks.error(\"Lost connection to the server (is it down?)\");\n }\n };\n\n for (var eventName in wsHandlers) {\n ws.addEventListener(eventName, wsHandlers[eventName]);\n }\n\n return;\n }\n\n Janus.httpAPICall(server, {\n verb: 'POST',\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.debug(json);\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(json[\"error\"].reason);\n return;\n }\n\n connected = true;\n sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n\n if (callbacks[\"reconnect\"]) {\n Janus.log(\"Claimed session: \" + sessionId);\n } else {\n Janus.log(\"Created session: \" + sessionId);\n }\n\n Janus.sessions[sessionId] = that;\n eventHandler();\n callbacks.success();\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n\n if (Janus.isArray(servers) && !callbacks[\"reconnect\"]) {\n serversIndex++;\n\n if (serversIndex == servers.length) {\n // We tried all the servers the user gave us and they all failed\n callbacks.error(\"Error connecting to any of the provided Janus servers: Is the server down?\");\n return;\n } // Let's try the next server\n\n\n server = null;\n setTimeout(function () {\n createSession(callbacks);\n }, 200);\n return;\n }\n\n if (errorThrown === \"\") callbacks.error(textStatus + \": Is the server down?\");else callbacks.error(textStatus + \": \" + errorThrown);\n }\n });\n } // Private method to destroy a session\n\n\n function destroySession(callbacks) {\n callbacks = callbacks || {}; // FIXME This method triggers a success even when we fail\n\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n var asyncRequest = true;\n if (callbacks.asyncRequest !== undefined && callbacks.asyncRequest !== null) asyncRequest = callbacks.asyncRequest === true;\n var notifyDestroyed = true;\n if (callbacks.notifyDestroyed !== undefined && callbacks.notifyDestroyed !== null) notifyDestroyed = callbacks.notifyDestroyed === true;\n Janus.log(\"Destroying session \" + sessionId + \" (async=\" + asyncRequest + \")\");\n\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n callbacks.success();\n return;\n }\n\n if (sessionId === undefined || sessionId === null) {\n Janus.warn(\"No session to destroy\");\n callbacks.success();\n if (notifyDestroyed) gatewayCallbacks.destroyed();\n return;\n }\n\n delete Janus.sessions[sessionId]; // No need to destroy all handles first, Janus will do that itself\n\n var request = {\n \"janus\": \"destroy\",\n \"transaction\": Janus.randomString(12)\n };\n if (token !== null && token !== undefined) request[\"token\"] = token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n\n if (websockets) {\n request[\"session_id\"] = sessionId;\n\n var unbindWebSocket = function () {\n for (var eventName in wsHandlers) {\n ws.removeEventListener(eventName, wsHandlers[eventName]);\n }\n\n ws.removeEventListener('message', onUnbindMessage);\n ws.removeEventListener('error', onUnbindError);\n\n if (wsKeepaliveTimeoutId) {\n clearTimeout(wsKeepaliveTimeoutId);\n }\n\n ws.close();\n };\n\n var onUnbindMessage = function (event) {\n var data = JSON.parse(event.data);\n\n if (data.session_id == request.session_id && data.transaction == request.transaction) {\n unbindWebSocket();\n callbacks.success();\n if (notifyDestroyed) gatewayCallbacks.destroyed();\n }\n };\n\n var onUnbindError = function (event) {\n unbindWebSocket();\n callbacks.error(\"Failed to destroy the server: Is the server down?\");\n if (notifyDestroyed) gatewayCallbacks.destroyed();\n };\n\n ws.addEventListener('message', onUnbindMessage);\n ws.addEventListener('error', onUnbindError);\n ws.send(JSON.stringify(request));\n return;\n }\n\n Janus.httpAPICall(server + \"/\" + sessionId, {\n verb: 'POST',\n async: asyncRequest,\n // Sometimes we need false here, or destroying in onbeforeunload won't work\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.log(\"Destroyed session:\");\n Janus.debug(json);\n sessionId = null;\n connected = false;\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n }\n\n callbacks.success();\n if (notifyDestroyed) gatewayCallbacks.destroyed();\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n // Reset everything anyway\n\n sessionId = null;\n connected = false;\n callbacks.success();\n if (notifyDestroyed) gatewayCallbacks.destroyed();\n }\n });\n } // Private method to create a plugin handle\n\n\n function createHandle(callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n callbacks.consentDialog = typeof callbacks.consentDialog == \"function\" ? callbacks.consentDialog : Janus.noop;\n callbacks.iceState = typeof callbacks.iceState == \"function\" ? callbacks.iceState : Janus.noop;\n callbacks.mediaState = typeof callbacks.mediaState == \"function\" ? callbacks.mediaState : Janus.noop;\n callbacks.webrtcState = typeof callbacks.webrtcState == \"function\" ? callbacks.webrtcState : Janus.noop;\n callbacks.slowLink = typeof callbacks.slowLink == \"function\" ? callbacks.slowLink : Janus.noop;\n callbacks.onmessage = typeof callbacks.onmessage == \"function\" ? callbacks.onmessage : Janus.noop;\n callbacks.onlocalstream = typeof callbacks.onlocalstream == \"function\" ? callbacks.onlocalstream : Janus.noop;\n callbacks.onremotestream = typeof callbacks.onremotestream == \"function\" ? callbacks.onremotestream : Janus.noop;\n callbacks.ondata = typeof callbacks.ondata == \"function\" ? callbacks.ondata : Janus.noop;\n callbacks.ondataopen = typeof callbacks.ondataopen == \"function\" ? callbacks.ondataopen : Janus.noop;\n callbacks.oncleanup = typeof callbacks.oncleanup == \"function\" ? callbacks.oncleanup : Janus.noop;\n callbacks.ondetached = typeof callbacks.ondetached == \"function\" ? callbacks.ondetached : Janus.noop;\n\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n callbacks.error(\"Is the server down? (connected=false)\");\n return;\n }\n\n var plugin = callbacks.plugin;\n\n if (plugin === undefined || plugin === null) {\n Janus.error(\"Invalid plugin\");\n callbacks.error(\"Invalid plugin\");\n return;\n }\n\n var opaqueId = callbacks.opaqueId;\n var handleToken = callbacks.token ? callbacks.token : token;\n var transaction = Janus.randomString(12);\n var request = {\n \"janus\": \"attach\",\n \"plugin\": plugin,\n \"opaque_id\": opaqueId,\n \"transaction\": transaction\n };\n if (handleToken !== null && handleToken !== undefined) request[\"token\"] = handleToken;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n\n if (websockets) {\n transactions[transaction] = function (json) {\n Janus.debug(json);\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason);\n return;\n }\n\n var handleId = json.data[\"id\"];\n Janus.log(\"Created handle: \" + handleId);\n var pluginHandle = {\n session: that,\n plugin: plugin,\n id: handleId,\n token: handleToken,\n detached: false,\n webrtcStuff: {\n started: false,\n myStream: null,\n streamExternal: false,\n remoteStream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannel: {},\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n volume: {\n value: null,\n timer: null\n },\n bitrate: {\n value: null,\n bsnow: null,\n bsbefore: null,\n tsnow: null,\n tsbefore: null,\n timer: null\n }\n },\n getId: function () {\n return handleId;\n },\n getPlugin: function () {\n return plugin;\n },\n getVolume: function () {\n return getVolume(handleId, true);\n },\n getRemoteVolume: function () {\n return getVolume(handleId, true);\n },\n getLocalVolume: function () {\n return getVolume(handleId, false);\n },\n isAudioMuted: function () {\n return isMuted(handleId, false);\n },\n muteAudio: function () {\n return mute(handleId, false, true);\n },\n unmuteAudio: function () {\n return mute(handleId, false, false);\n },\n isVideoMuted: function () {\n return isMuted(handleId, true);\n },\n muteVideo: function () {\n return mute(handleId, true, true);\n },\n unmuteVideo: function () {\n return mute(handleId, true, false);\n },\n getBitrate: function () {\n return getBitrate(handleId);\n },\n send: function (callbacks) {\n sendMessage(handleId, callbacks);\n },\n data: function (callbacks) {\n sendData(handleId, callbacks);\n },\n dtmf: function (callbacks) {\n sendDtmf(handleId, callbacks);\n },\n consentDialog: callbacks.consentDialog,\n iceState: callbacks.iceState,\n mediaState: callbacks.mediaState,\n webrtcState: callbacks.webrtcState,\n slowLink: callbacks.slowLink,\n onmessage: callbacks.onmessage,\n createOffer: function (callbacks) {\n prepareWebrtc(handleId, true, callbacks);\n },\n createAnswer: function (callbacks) {\n prepareWebrtc(handleId, false, callbacks);\n },\n handleRemoteJsep: function (callbacks) {\n prepareWebrtcPeer(handleId, callbacks);\n },\n onlocalstream: callbacks.onlocalstream,\n onremotestream: callbacks.onremotestream,\n ondata: callbacks.ondata,\n ondataopen: callbacks.ondataopen,\n oncleanup: callbacks.oncleanup,\n ondetached: callbacks.ondetached,\n hangup: function (sendRequest) {\n cleanupWebrtc(handleId, sendRequest === true);\n },\n detach: function (callbacks) {\n destroyHandle(handleId, callbacks);\n }\n };\n pluginHandles[handleId] = pluginHandle;\n callbacks.success(pluginHandle);\n };\n\n request[\"session_id\"] = sessionId;\n ws.send(JSON.stringify(request));\n return;\n }\n\n Janus.httpAPICall(server + \"/\" + sessionId, {\n verb: 'POST',\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.debug(json);\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason);\n return;\n }\n\n var handleId = json.data[\"id\"];\n Janus.log(\"Created handle: \" + handleId);\n var pluginHandle = {\n session: that,\n plugin: plugin,\n id: handleId,\n token: handleToken,\n detached: false,\n webrtcStuff: {\n started: false,\n myStream: null,\n streamExternal: false,\n remoteStream: null,\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannel: {},\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n volume: {\n value: null,\n timer: null\n },\n bitrate: {\n value: null,\n bsnow: null,\n bsbefore: null,\n tsnow: null,\n tsbefore: null,\n timer: null\n }\n },\n getId: function () {\n return handleId;\n },\n getPlugin: function () {\n return plugin;\n },\n getVolume: function () {\n return getVolume(handleId, true);\n },\n getRemoteVolume: function () {\n return getVolume(handleId, true);\n },\n getLocalVolume: function () {\n return getVolume(handleId, false);\n },\n isAudioMuted: function () {\n return isMuted(handleId, false);\n },\n muteAudio: function () {\n return mute(handleId, false, true);\n },\n unmuteAudio: function () {\n return mute(handleId, false, false);\n },\n isVideoMuted: function () {\n return isMuted(handleId, true);\n },\n muteVideo: function () {\n return mute(handleId, true, true);\n },\n unmuteVideo: function () {\n return mute(handleId, true, false);\n },\n getBitrate: function () {\n return getBitrate(handleId);\n },\n send: function (callbacks) {\n sendMessage(handleId, callbacks);\n },\n data: function (callbacks) {\n sendData(handleId, callbacks);\n },\n dtmf: function (callbacks) {\n sendDtmf(handleId, callbacks);\n },\n consentDialog: callbacks.consentDialog,\n iceState: callbacks.iceState,\n mediaState: callbacks.mediaState,\n webrtcState: callbacks.webrtcState,\n slowLink: callbacks.slowLink,\n onmessage: callbacks.onmessage,\n createOffer: function (callbacks) {\n prepareWebrtc(handleId, true, callbacks);\n },\n createAnswer: function (callbacks) {\n prepareWebrtc(handleId, false, callbacks);\n },\n handleRemoteJsep: function (callbacks) {\n prepareWebrtcPeer(handleId, callbacks);\n },\n onlocalstream: callbacks.onlocalstream,\n onremotestream: callbacks.onremotestream,\n ondata: callbacks.ondata,\n ondataopen: callbacks.ondataopen,\n oncleanup: callbacks.oncleanup,\n ondetached: callbacks.ondetached,\n hangup: function (sendRequest) {\n cleanupWebrtc(handleId, sendRequest === true);\n },\n detach: function (callbacks) {\n destroyHandle(handleId, callbacks);\n }\n };\n pluginHandles[handleId] = pluginHandle;\n callbacks.success(pluginHandle);\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n }\n });\n } // Private method to send a message\n\n\n function sendMessage(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n callbacks.error(\"Is the server down? (connected=false)\");\n return;\n }\n\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var message = callbacks.message;\n var jsep = callbacks.jsep;\n var transaction = Janus.randomString(12);\n var request = {\n \"janus\": \"message\",\n \"body\": message,\n \"transaction\": transaction\n };\n if (pluginHandle.token !== null && pluginHandle.token !== undefined) request[\"token\"] = pluginHandle.token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n if (jsep !== null && jsep !== undefined) request.jsep = jsep;\n Janus.debug(\"Sending message to plugin (handle=\" + handleId + \"):\");\n Janus.debug(request);\n\n if (websockets) {\n request[\"session_id\"] = sessionId;\n request[\"handle_id\"] = handleId;\n\n transactions[transaction] = function (json) {\n Janus.debug(\"Message sent!\");\n Janus.debug(json);\n\n if (json[\"janus\"] === \"success\") {\n // We got a success, must have been a synchronous transaction\n var plugindata = json[\"plugindata\"];\n\n if (plugindata === undefined || plugindata === null) {\n Janus.warn(\"Request succeeded, but missing plugindata...\");\n callbacks.success();\n return;\n }\n\n Janus.log(\"Synchronous transaction successful (\" + plugindata[\"plugin\"] + \")\");\n var data = plugindata[\"data\"];\n Janus.debug(data);\n callbacks.success(data);\n return;\n } else if (json[\"janus\"] !== \"ack\") {\n // Not a success and not an ack, must be an error\n if (json[\"error\"] !== undefined && json[\"error\"] !== null) {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(json[\"error\"].code + \" \" + json[\"error\"].reason);\n } else {\n Janus.error(\"Unknown error\"); // FIXME\n\n callbacks.error(\"Unknown error\");\n }\n\n return;\n } // If we got here, the plugin decided to handle the request asynchronously\n\n\n callbacks.success();\n };\n\n ws.send(JSON.stringify(request));\n return;\n }\n\n Janus.httpAPICall(server + \"/\" + sessionId + \"/\" + handleId, {\n verb: 'POST',\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.debug(\"Message sent!\");\n Janus.debug(json);\n\n if (json[\"janus\"] === \"success\") {\n // We got a success, must have been a synchronous transaction\n var plugindata = json[\"plugindata\"];\n\n if (plugindata === undefined || plugindata === null) {\n Janus.warn(\"Request succeeded, but missing plugindata...\");\n callbacks.success();\n return;\n }\n\n Janus.log(\"Synchronous transaction successful (\" + plugindata[\"plugin\"] + \")\");\n var data = plugindata[\"data\"];\n Janus.debug(data);\n callbacks.success(data);\n return;\n } else if (json[\"janus\"] !== \"ack\") {\n // Not a success and not an ack, must be an error\n if (json[\"error\"] !== undefined && json[\"error\"] !== null) {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n callbacks.error(json[\"error\"].code + \" \" + json[\"error\"].reason);\n } else {\n Janus.error(\"Unknown error\"); // FIXME\n\n callbacks.error(\"Unknown error\");\n }\n\n return;\n } // If we got here, the plugin decided to handle the request asynchronously\n\n\n callbacks.success();\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n\n callbacks.error(textStatus + \": \" + errorThrown);\n }\n });\n } // Private method to send a trickle candidate\n\n\n function sendTrickleCandidate(handleId, candidate) {\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n return;\n }\n\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return;\n }\n\n var request = {\n \"janus\": \"trickle\",\n \"candidate\": candidate,\n \"transaction\": Janus.randomString(12)\n };\n if (pluginHandle.token !== null && pluginHandle.token !== undefined) request[\"token\"] = pluginHandle.token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n Janus.vdebug(\"Sending trickle candidate (handle=\" + handleId + \"):\");\n Janus.vdebug(request);\n\n if (websockets) {\n request[\"session_id\"] = sessionId;\n request[\"handle_id\"] = handleId;\n ws.send(JSON.stringify(request));\n return;\n }\n\n Janus.httpAPICall(server + \"/\" + sessionId + \"/\" + handleId, {\n verb: 'POST',\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.vdebug(\"Candidate sent!\");\n Janus.vdebug(json);\n\n if (json[\"janus\"] !== \"ack\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n\n return;\n }\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n }\n });\n } // Private method to create a data channel\n\n\n function createDataChannel(handleId, dclabel, incoming, pendingText) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n var onDataChannelMessage = function (event) {\n Janus.log('Received message on data channel:', event);\n var label = event.target.label;\n pluginHandle.ondata(event.data, label);\n };\n\n var onDataChannelStateChange = function (event) {\n Janus.log('Received state change on data channel:', event);\n var label = event.target.label;\n var dcState = config.dataChannel[label] !== null ? config.dataChannel[label].readyState : \"null\";\n Janus.log('State change on <' + label + '> data channel: ' + dcState);\n\n if (dcState === 'open') {\n // Any pending messages to send?\n if (config.dataChannel[label].pending && config.dataChannel[label].pending.length > 0) {\n Janus.log(\"Sending pending messages on <' + label + '>:\", config.dataChannel[label].pending.length);\n\n for (var i in config.dataChannel[label].pending) {\n var text = config.dataChannel[label].pending[i];\n Janus.log(\"Sending string on data channel <\" + label + \">: \" + text);\n config.dataChannel[label].send(text);\n }\n\n config.dataChannel[label].pending = [];\n } // Notify the open data channel\n\n\n pluginHandle.ondataopen(label);\n }\n };\n\n var onDataChannelError = function (error) {\n Janus.error('Got error on data channel:', error); // TODO\n };\n\n if (!incoming) {\n // FIXME Add options (ordered, maxRetransmits, etc.)\n config.dataChannel[dclabel] = config.pc.createDataChannel(dclabel, {\n ordered: false\n });\n } else {\n // The channel was created by Janus\n config.dataChannel[dclabel] = incoming;\n }\n\n config.dataChannel[dclabel].onmessage = onDataChannelMessage;\n config.dataChannel[dclabel].onopen = onDataChannelStateChange;\n config.dataChannel[dclabel].onclose = onDataChannelStateChange;\n config.dataChannel[dclabel].onerror = onDataChannelError;\n config.dataChannel[dclabel].pending = [];\n if (pendingText) config.dataChannel[dclabel].pending.push(pendingText);\n } // Private method to send a data channel message\n\n\n function sendData(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n var text = callbacks.text;\n\n if (text === null || text === undefined) {\n Janus.warn(\"Invalid text\");\n callbacks.error(\"Invalid text\");\n return;\n }\n\n var label = callbacks.label ? callbacks.label : Janus.dataChanDefaultLabel;\n\n if (!config.dataChannel[label]) {\n // Create new data channel and wait for it to open\n createDataChannel(handleId, label, false, text);\n callbacks.success();\n return;\n }\n\n if (config.dataChannel[label].readyState !== \"open\") {\n config.dataChannel[label].pending.push(text);\n callbacks.success();\n return;\n }\n\n Janus.log(\"Sending string on data channel <\" + label + \">: \" + text);\n config.dataChannel[label].send(text);\n callbacks.success();\n } // Private method to send a DTMF tone\n\n\n function sendDtmf(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n if (config.dtmfSender === null || config.dtmfSender === undefined) {\n // Create the DTMF sender the proper way, if possible\n if (config.pc !== undefined && config.pc !== null) {\n var senders = config.pc.getSenders();\n var audioSender = senders.find(function (sender) {\n return sender.track && sender.track.kind === 'audio';\n });\n\n if (!audioSender) {\n Janus.warn(\"Invalid DTMF configuration (no audio track)\");\n callbacks.error(\"Invalid DTMF configuration (no audio track)\");\n return;\n }\n\n config.dtmfSender = audioSender.dtmf;\n\n if (config.dtmfSender) {\n Janus.log(\"Created DTMF Sender\");\n\n config.dtmfSender.ontonechange = function (tone) {\n Janus.debug(\"Sent DTMF tone: \" + tone.tone);\n };\n }\n }\n\n if (config.dtmfSender === null || config.dtmfSender === undefined) {\n Janus.warn(\"Invalid DTMF configuration\");\n callbacks.error(\"Invalid DTMF configuration\");\n return;\n }\n }\n\n var dtmf = callbacks.dtmf;\n\n if (dtmf === null || dtmf === undefined) {\n Janus.warn(\"Invalid DTMF parameters\");\n callbacks.error(\"Invalid DTMF parameters\");\n return;\n }\n\n var tones = dtmf.tones;\n\n if (tones === null || tones === undefined) {\n Janus.warn(\"Invalid DTMF string\");\n callbacks.error(\"Invalid DTMF string\");\n return;\n }\n\n var duration = dtmf.duration;\n if (duration === null || duration === undefined) duration = 500; // We choose 500ms as the default duration for a tone\n\n var gap = dtmf.gap;\n if (gap === null || gap === undefined) gap = 50; // We choose 50ms as the default gap between tones\n\n Janus.debug(\"Sending DTMF string \" + tones + \" (duration \" + duration + \"ms, gap \" + gap + \"ms)\");\n config.dtmfSender.insertDTMF(tones, duration, gap);\n callbacks.success();\n } // Private method to destroy a plugin handle\n\n\n function destroyHandle(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n var asyncRequest = true;\n if (callbacks.asyncRequest !== undefined && callbacks.asyncRequest !== null) asyncRequest = callbacks.asyncRequest === true;\n Janus.log(\"Destroying handle \" + handleId + \" (async=\" + asyncRequest + \")\");\n cleanupWebrtc(handleId);\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.detached) {\n // Plugin was already detached by Janus, calling detach again will return a handle not found error, so just exit here\n delete pluginHandles[handleId];\n callbacks.success();\n return;\n }\n\n if (!connected) {\n Janus.warn(\"Is the server down? (connected=false)\");\n callbacks.error(\"Is the server down? (connected=false)\");\n return;\n }\n\n var request = {\n \"janus\": \"detach\",\n \"transaction\": Janus.randomString(12)\n };\n if (pluginHandle.token !== null && pluginHandle.token !== undefined) request[\"token\"] = pluginHandle.token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n\n if (websockets) {\n request[\"session_id\"] = sessionId;\n request[\"handle_id\"] = handleId;\n ws.send(JSON.stringify(request));\n delete pluginHandles[handleId];\n callbacks.success();\n return;\n }\n\n Janus.httpAPICall(server + \"/\" + sessionId + \"/\" + handleId, {\n verb: 'POST',\n async: asyncRequest,\n // Sometimes we need false here, or destroying in onbeforeunload won't work\n withCredentials: withCredentials,\n body: request,\n success: function (json) {\n Janus.log(\"Destroyed handle:\");\n Janus.debug(json);\n\n if (json[\"janus\"] !== \"success\") {\n Janus.error(\"Ooops: \" + json[\"error\"].code + \" \" + json[\"error\"].reason); // FIXME\n }\n\n delete pluginHandles[handleId];\n callbacks.success();\n },\n error: function (textStatus, errorThrown) {\n Janus.error(textStatus + \":\", errorThrown); // FIXME\n // We cleanup anyway\n\n delete pluginHandles[handleId];\n callbacks.success();\n }\n });\n } // WebRTC stuff\n\n\n function streamsDone(handleId, jsep, media, callbacks, stream) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n Janus.debug(\"streamsDone:\", stream);\n\n if (stream) {\n Janus.debug(\" -- Audio tracks:\", stream.getAudioTracks());\n Janus.debug(\" -- Video tracks:\", stream.getVideoTracks());\n } // We're now capturing the new stream: check if we're updating or if it's a new thing\n\n\n var addTracks = false;\n\n if (!config.myStream || !media.update || config.streamExternal) {\n config.myStream = stream;\n addTracks = true;\n } else {\n // We only need to update the existing stream\n if ((!media.update && isAudioSendEnabled(media) || media.update && (media.addAudio || media.replaceAudio)) && stream.getAudioTracks() && stream.getAudioTracks().length) {\n config.myStream.addTrack(stream.getAudioTracks()[0]);\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" && Janus.webRTCAdapter.browserDetails.version >= 59 || Janus.webRTCAdapter.browserDetails.browser === \"safari\" && window.RTCRtpSender.prototype.replaceTrack || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72) {\n // Use Transceivers\n Janus.log((media.replaceAudio ? \"Replacing\" : \"Adding\") + \" audio track:\", stream.getAudioTracks()[0]);\n var audioTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\") {\n audioTransceiver = t;\n break;\n }\n }\n }\n\n if (audioTransceiver && audioTransceiver.sender) {\n audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);\n } else {\n config.pc.addTrack(stream.getAudioTracks()[0], stream);\n }\n } else {\n Janus.log((media.replaceAudio ? \"Replacing\" : \"Adding\") + \" audio track:\", stream.getAudioTracks()[0]);\n config.pc.addTrack(stream.getAudioTracks()[0], stream);\n }\n }\n\n if ((!media.update && isVideoSendEnabled(media) || media.update && (media.addVideo || media.replaceVideo)) && stream.getVideoTracks() && stream.getVideoTracks().length) {\n config.myStream.addTrack(stream.getVideoTracks()[0]);\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" && Janus.webRTCAdapter.browserDetails.version >= 59 || Janus.webRTCAdapter.browserDetails.browser === \"safari\" && window.RTCRtpSender.prototype.replaceTrack || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72) {\n // Use Transceivers\n Janus.log((media.replaceVideo ? \"Replacing\" : \"Adding\") + \" video track:\", stream.getVideoTracks()[0]);\n var videoTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\") {\n videoTransceiver = t;\n break;\n }\n }\n }\n\n if (videoTransceiver && videoTransceiver.sender) {\n videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);\n } else {\n config.pc.addTrack(stream.getVideoTracks()[0], stream);\n }\n } else {\n Janus.log((media.replaceVideo ? \"Replacing\" : \"Adding\") + \" video track:\", stream.getVideoTracks()[0]);\n config.pc.addTrack(stream.getVideoTracks()[0], stream);\n }\n }\n } // If we still need to create a PeerConnection, let's do that\n\n\n if (!config.pc) {\n var pc_config = {\n \"iceServers\": iceServers,\n \"iceTransportPolicy\": iceTransportPolicy,\n \"bundlePolicy\": bundlePolicy\n };\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"chrome\") {\n // For Chrome versions before 72, we force a plan-b semantic, and unified-plan otherwise\n pc_config[\"sdpSemantics\"] = Janus.webRTCAdapter.browserDetails.version < 72 ? \"plan-b\" : \"unified-plan\";\n }\n\n var pc_constraints = {\n \"optional\": [{\n \"DtlsSrtpKeyAgreement\": true\n }]\n };\n\n if (ipv6Support === true) {\n pc_constraints.optional.push({\n \"googIPv6\": true\n });\n } // Any custom constraint to add?\n\n\n if (callbacks.rtcConstraints && typeof callbacks.rtcConstraints === 'object') {\n Janus.debug(\"Adding custom PeerConnection constraints:\", callbacks.rtcConstraints);\n\n for (var i in callbacks.rtcConstraints) {\n pc_constraints.optional.push(callbacks.rtcConstraints[i]);\n }\n }\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"edge\") {\n // This is Edge, enable BUNDLE explicitly\n pc_config.bundlePolicy = \"max-bundle\";\n }\n\n Janus.log(\"Creating PeerConnection\");\n Janus.debug(pc_constraints);\n config.pc = new RTCPeerConnection(pc_config, pc_constraints);\n Janus.debug(config.pc);\n\n if (config.pc.getStats) {\n // FIXME\n config.volume = {};\n config.bitrate.value = \"0 kbits/sec\";\n }\n\n Janus.log(\"Preparing local SDP and gathering candidates (trickle=\" + config.trickle + \")\");\n\n config.pc.oniceconnectionstatechange = function (e) {\n if (config.pc) pluginHandle.iceState(config.pc.iceConnectionState);\n };\n\n config.pc.onicecandidate = function (event) {\n if (event.candidate == null || Janus.webRTCAdapter.browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0) {\n Janus.log(\"End of candidates.\");\n config.iceDone = true;\n\n if (config.trickle === true) {\n // Notify end of candidates\n sendTrickleCandidate(handleId, {\n \"completed\": true\n });\n } else {\n // No trickle, time to send the complete SDP (including all candidates)\n sendSDP(handleId, callbacks);\n }\n } else {\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var myStr = event.candidate.candidate;\n var arr1 = myStr.split(\" \"); //判断是否是IPV4\n\n var exp = /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;\n var flag = arr1[4].match(exp);\n\n if (!flag || flag == \"\") {\n arr1[4] = '127.0.0.1';\n }\n\n var String = arr1.join(' ');\n var candidate = {\n \"candidate\": String,\n \"sdpMid\": event.candidate.sdpMid,\n \"sdpMLineIndex\": event.candidate.sdpMLineIndex\n };\n\n if (config.trickle === true) {\n // Send candidate\n sendTrickleCandidate(handleId, candidate);\n }\n }\n };\n\n config.pc.ontrack = function (event) {\n Janus.log(\"Handling Remote Track\");\n Janus.debug(event);\n if (!event.streams) return;\n config.remoteStream = event.streams[0];\n pluginHandle.onremotestream(config.remoteStream);\n\n if (event.track && !event.track.onended) {\n Janus.log(\"Adding onended callback to track:\", event.track);\n\n event.track.onended = function (ev) {\n Janus.log(\"Remote track removed:\", ev);\n\n if (config.remoteStream) {\n config.remoteStream.removeTrack(ev.target);\n pluginHandle.onremotestream(config.remoteStream);\n }\n };\n }\n };\n }\n\n if (addTracks && stream !== null && stream !== undefined) {\n Janus.log('Adding local stream');\n var simulcast2 = callbacks.simulcast2 === true ? true : false;\n stream.getTracks().forEach(function (track) {\n Janus.log('Adding local track:', track);\n\n if (!simulcast2) {\n config.pc.addTrack(track, stream);\n } else {\n if (track.kind === \"audio\") {\n config.pc.addTrack(track, stream);\n } else {\n Janus.log('Enabling rid-based simulcasting:', track);\n config.pc.addTransceiver(track, {\n direction: \"sendrecv\",\n streams: [stream],\n sendEncodings: [{\n rid: \"h\",\n active: true,\n maxBitrate: 900000\n }, {\n rid: \"m\",\n active: true,\n maxBitrate: 300000,\n scaleResolutionDownBy: 2\n }, {\n rid: \"l\",\n active: true,\n maxBitrate: 100000,\n scaleResolutionDownBy: 4\n }]\n });\n }\n }\n });\n } // Any data channel to create?\n\n\n if (isDataEnabled(media) && !config.dataChannel[Janus.dataChanDefaultLabel]) {\n Janus.log(\"Creating data channel\");\n createDataChannel(handleId, Janus.dataChanDefaultLabel, false);\n\n config.pc.ondatachannel = function (event) {\n Janus.log(\"Data channel created by Janus:\", event);\n createDataChannel(handleId, event.channel.label, event.channel);\n };\n } // If there's a new local stream, let's notify the application\n\n\n if (config.myStream) pluginHandle.onlocalstream(config.myStream); // Create offer/answer now\n\n if (jsep === null || jsep === undefined) {\n createOffer(handleId, media, callbacks);\n } else {\n config.pc.setRemoteDescription(jsep).then(function () {\n Janus.log(\"Remote description accepted!\");\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i in config.candidates) {\n var candidate = config.candidates[i];\n Janus.debug(\"Adding remote candidate:\", candidate);\n\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate();\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Create the answer now\n\n\n createAnswer(handleId, media, callbacks);\n }, callbacks.error);\n }\n }\n\n function prepareWebrtc(handleId, offer, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : webrtcError;\n var jsep = callbacks.jsep;\n\n if (offer && jsep) {\n Janus.error(\"Provided a JSEP to a createOffer\");\n callbacks.error(\"Provided a JSEP to a createOffer\");\n return;\n } else if (!offer && (!jsep || !jsep.type || !jsep.sdp)) {\n Janus.error(\"A valid JSEP is required for createAnswer\");\n callbacks.error(\"A valid JSEP is required for createAnswer\");\n return;\n }\n\n callbacks.media = callbacks.media || {\n audio: true,\n video: true\n };\n var media = callbacks.media;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n config.trickle = isTrickleEnabled(callbacks.trickle); // Are we updating a session?\n\n if (config.pc === undefined || config.pc === null) {\n // Nope, new PeerConnection\n media.update = false;\n media.keepAudio = false;\n media.keepVideo = false;\n } else if (config.pc !== undefined && config.pc !== null) {\n Janus.log(\"Updating existing media session\");\n media.update = true; // Check if there's anything to add/remove/replace, or if we\n // can go directly to preparing the new SDP offer or answer\n\n if (callbacks.stream !== null && callbacks.stream !== undefined) {\n // External stream: is this the same as the one we were using before?\n if (callbacks.stream !== config.myStream) {\n Janus.log(\"Renegotiation involves a new external stream\");\n }\n } else {\n // Check if there are changes on audio\n if (media.addAudio) {\n media.keepAudio = false;\n media.replaceAudio = false;\n media.removeAudio = false;\n media.audioSend = true;\n\n if (config.myStream && config.myStream.getAudioTracks() && config.myStream.getAudioTracks().length) {\n Janus.error(\"Can't add audio stream, there already is one\");\n callbacks.error(\"Can't add audio stream, there already is one\");\n return;\n }\n } else if (media.removeAudio) {\n media.keepAudio = false;\n media.replaceAudio = false;\n media.addAudio = false;\n media.audioSend = false;\n } else if (media.replaceAudio) {\n media.keepAudio = false;\n media.addAudio = false;\n media.removeAudio = false;\n media.audioSend = true;\n }\n\n if (config.myStream === null || config.myStream === undefined) {\n // No media stream: if we were asked to replace, it's actually an \"add\"\n if (media.replaceAudio) {\n media.keepAudio = false;\n media.replaceAudio = false;\n media.addAudio = true;\n media.audioSend = true;\n }\n\n if (isAudioSendEnabled(media)) {\n media.keepAudio = false;\n media.addAudio = true;\n }\n } else {\n if (config.myStream.getAudioTracks() === null || config.myStream.getAudioTracks() === undefined || config.myStream.getAudioTracks().length === 0) {\n // No audio track: if we were asked to replace, it's actually an \"add\"\n if (media.replaceAudio) {\n media.keepAudio = false;\n media.replaceAudio = false;\n media.addAudio = true;\n media.audioSend = true;\n }\n\n if (isAudioSendEnabled(media)) {\n media.keepVideo = false;\n media.addAudio = true;\n }\n } else {\n // We have an audio track: should we keep it as it is?\n if (isAudioSendEnabled(media) && !media.removeAudio && !media.replaceAudio) {\n media.keepAudio = true;\n }\n }\n } // Check if there are changes on video\n\n\n if (media.addVideo) {\n media.keepVideo = false;\n media.replaceVideo = false;\n media.removeVideo = false;\n media.videoSend = true;\n\n if (config.myStream && config.myStream.getVideoTracks() && config.myStream.getVideoTracks().length) {\n Janus.error(\"Can't add video stream, there already is one\");\n callbacks.error(\"Can't add video stream, there already is one\");\n return;\n }\n } else if (media.removeVideo) {\n media.keepVideo = false;\n media.replaceVideo = false;\n media.addVideo = false;\n media.videoSend = false;\n } else if (media.replaceVideo) {\n media.keepVideo = false;\n media.addVideo = false;\n media.removeVideo = false;\n media.videoSend = true;\n }\n\n if (config.myStream === null || config.myStream === undefined) {\n // No media stream: if we were asked to replace, it's actually an \"add\"\n if (media.replaceVideo) {\n media.keepVideo = false;\n media.replaceVideo = false;\n media.addVideo = true;\n media.videoSend = true;\n }\n\n if (isVideoSendEnabled(media)) {\n media.keepVideo = false;\n media.addVideo = true;\n }\n } else {\n if (config.myStream.getVideoTracks() === null || config.myStream.getVideoTracks() === undefined || config.myStream.getVideoTracks().length === 0) {\n // No video track: if we were asked to replace, it's actually an \"add\"\n if (media.replaceVideo) {\n media.keepVideo = false;\n media.replaceVideo = false;\n media.addVideo = true;\n media.videoSend = true;\n }\n\n if (isVideoSendEnabled(media)) {\n media.keepVideo = false;\n media.addVideo = true;\n }\n } else {\n // We have a video track: should we keep it as it is?\n if (isVideoSendEnabled(media) && !media.removeVideo && !media.replaceVideo) {\n media.keepVideo = true;\n }\n }\n } // Data channels can only be added\n\n\n if (media.addData) media.data = true;\n } // If we're updating and keeping all tracks, let's skip the getUserMedia part\n\n\n if (isAudioSendEnabled(media) && media.keepAudio && isVideoSendEnabled(media) && media.keepVideo) {\n pluginHandle.consentDialog(false);\n streamsDone(handleId, jsep, media, callbacks, config.myStream);\n return;\n }\n } // If we're updating, check if we need to remove/replace one of the tracks\n\n\n if (media.update && !config.streamExternal) {\n if (media.removeAudio || media.replaceAudio) {\n if (config.myStream && config.myStream.getAudioTracks() && config.myStream.getAudioTracks().length) {\n var s = config.myStream.getAudioTracks()[0];\n Janus.log(\"Removing audio track:\", s);\n config.myStream.removeTrack(s);\n\n try {\n s.stop();\n } catch (e) {}\n\n ;\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n var ra = true;\n\n if (media.replaceAudio && (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72)) {\n // On Firefox we can use replaceTrack\n ra = false;\n }\n\n if (ra) {\n for (var index in config.pc.getSenders()) {\n var s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"audio\") {\n Janus.log(\"Removing audio sender:\", s);\n config.pc.removeTrack(s);\n }\n }\n }\n }\n }\n\n if (media.removeVideo || media.replaceVideo) {\n if (config.myStream && config.myStream.getVideoTracks() && config.myStream.getVideoTracks().length) {\n var s = config.myStream.getVideoTracks()[0];\n Janus.log(\"Removing video track:\", s);\n config.myStream.removeTrack(s);\n\n try {\n s.stop();\n } catch (e) {}\n\n ;\n }\n\n if (config.pc.getSenders() && config.pc.getSenders().length) {\n var rv = true;\n\n if (media.replaceVideo && (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72)) {\n // On Firefox we can use replaceTrack\n rv = false;\n }\n\n if (rv) {\n for (var index in config.pc.getSenders()) {\n var s = config.pc.getSenders()[index];\n\n if (s && s.track && s.track.kind === \"video\") {\n Janus.log(\"Removing video sender:\", s);\n config.pc.removeTrack(s);\n }\n }\n }\n }\n }\n } // Was a MediaStream object passed, or do we need to take care of that?\n\n\n if (callbacks.stream !== null && callbacks.stream !== undefined) {\n var stream = callbacks.stream;\n Janus.log(\"MediaStream provided by the application\");\n Janus.debug(stream); // If this is an update, let's check if we need to release the previous stream\n\n if (media.update) {\n if (config.myStream && config.myStream !== callbacks.stream && !config.streamExternal) {\n // We're replacing a stream we captured ourselves with an external one\n try {\n // Try a MediaStreamTrack.stop() for each track\n var tracks = config.myStream.getTracks();\n\n for (var i in tracks) {\n var mst = tracks[i];\n Janus.log(mst);\n if (mst !== null && mst !== undefined) mst.stop();\n }\n } catch (e) {// Do nothing if this fails\n }\n\n config.myStream = null;\n }\n } // Skip the getUserMedia part\n\n\n config.streamExternal = true;\n pluginHandle.consentDialog(false);\n streamsDone(handleId, jsep, media, callbacks, stream);\n return;\n }\n\n if (isAudioSendEnabled(media) || isVideoSendEnabled(media)) {\n if (!Janus.isGetUserMediaAvailable()) {\n callbacks.error(\"getUserMedia not available\");\n return;\n }\n\n var constraints = {\n mandatory: {},\n optional: []\n };\n pluginHandle.consentDialog(true);\n var audioSupport = isAudioSendEnabled(media);\n\n if (audioSupport === true && media != undefined && media != null) {\n if (typeof media.audio === 'object') {\n audioSupport = media.audio;\n }\n }\n\n var videoSupport = isVideoSendEnabled(media);\n\n if (videoSupport === true && media != undefined && media != null) {\n var simulcast = callbacks.simulcast === true ? true : false;\n var simulcast2 = callbacks.simulcast2 === true ? true : false;\n if ((simulcast || simulcast2) && !jsep && (media.video === undefined || media.video === false)) media.video = \"hires\";\n\n if (media.video && media.video != 'screen' && media.video != 'window') {\n if (typeof media.video === 'object') {\n videoSupport = media.video;\n } else {\n var width = 0;\n var height = 0,\n maxHeight = 0;\n\n if (media.video === 'lowres') {\n // Small resolution, 4:3\n height = 240;\n maxHeight = 240;\n width = 320;\n } else if (media.video === 'lowres-16:9') {\n // Small resolution, 16:9\n height = 180;\n maxHeight = 180;\n width = 320;\n } else if (media.video === 'hires' || media.video === 'hires-16:9' || media.video === 'hdres') {\n // High(HD) resolution is only 16:9\n height = 720;\n maxHeight = 720;\n width = 1280;\n } else if (media.video === 'fhdres') {\n // Full HD resolution is only 16:9\n height = 1080;\n maxHeight = 1080;\n width = 1920;\n } else if (media.video === '4kres') {\n // 4K resolution is only 16:9\n height = 2160;\n maxHeight = 2160;\n width = 3840;\n } else if (media.video === 'stdres') {\n // Normal resolution, 4:3\n height = 480;\n maxHeight = 480;\n width = 640;\n } else if (media.video === 'stdres-16:9') {\n // Normal resolution, 16:9\n height = 360;\n maxHeight = 360;\n width = 640;\n } else {\n Janus.log(\"Default video setting is stdres 4:3\");\n height = 480;\n maxHeight = 480;\n width = 640;\n }\n\n Janus.log(\"Adding media constraint:\", media.video);\n videoSupport = {\n 'height': {\n 'ideal': height\n },\n 'width': {\n 'ideal': width\n }\n };\n Janus.log(\"Adding video constraint:\", videoSupport);\n }\n } else if (media.video === 'screen' || media.video === 'window') {\n if (!media.screenshareFrameRate) {\n media.screenshareFrameRate = 3;\n }\n\n if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {\n // The new experimental getDisplayMedia API is available, let's use that\n // https://groups.google.com/forum/#!topic/discuss-webrtc/Uf0SrR4uxzk\n // https://webrtchacks.com/chrome-screensharing-getdisplaymedia/\n navigator.mediaDevices.getDisplayMedia({\n video: true\n }).then(function (stream) {\n pluginHandle.consentDialog(false);\n\n if (isAudioSendEnabled(media) && !media.keepAudio) {\n navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n }).then(function (audioStream) {\n stream.addTrack(audioStream.getAudioTracks()[0]);\n streamsDone(handleId, jsep, media, callbacks, stream);\n });\n } else {\n streamsDone(handleId, jsep, media, callbacks, stream);\n }\n }, function (error) {\n pluginHandle.consentDialog(false);\n callbacks.error(error);\n });\n return;\n } // We're going to try and use the extension for Chrome 34+, the old approach\n // for older versions of Chrome, or the experimental support in Firefox 33+\n\n\n function callbackUserMedia(error, stream) {\n pluginHandle.consentDialog(false);\n\n if (error) {\n callbacks.error(error);\n } else {\n streamsDone(handleId, jsep, media, callbacks, stream);\n }\n }\n\n ;\n\n function getScreenMedia(constraints, gsmCallback, useAudio) {\n Janus.log(\"Adding media constraint (screen capture)\");\n Janus.debug(constraints);\n navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {\n if (useAudio) {\n navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n }).then(function (audioStream) {\n stream.addTrack(audioStream.getAudioTracks()[0]);\n gsmCallback(null, stream);\n });\n } else {\n gsmCallback(null, stream);\n }\n }).catch(function (error) {\n pluginHandle.consentDialog(false);\n gsmCallback(error);\n });\n }\n\n ;\n\n if (Janus.webRTCAdapter.browserDetails.browser === 'chrome') {\n var chromever = Janus.webRTCAdapter.browserDetails.version;\n var maxver = 33;\n if (window.navigator.userAgent.match('Linux')) maxver = 35; // \"known\" crash in chrome 34 and 35 on linux\n\n if (chromever >= 26 && chromever <= maxver) {\n // Chrome 26->33 requires some awkward chrome://flags manipulation\n constraints = {\n video: {\n mandatory: {\n googLeakyBucket: true,\n maxWidth: window.screen.width,\n maxHeight: window.screen.height,\n minFrameRate: media.screenshareFrameRate,\n maxFrameRate: media.screenshareFrameRate,\n chromeMediaSource: 'screen'\n }\n },\n audio: isAudioSendEnabled(media) && !media.keepAudio\n };\n getScreenMedia(constraints, callbackUserMedia);\n } else {\n // Chrome 34+ requires an extension\n Janus.extension.getScreen(function (error, sourceId) {\n if (error) {\n pluginHandle.consentDialog(false);\n return callbacks.error(error);\n }\n\n constraints = {\n audio: false,\n video: {\n mandatory: {\n chromeMediaSource: 'desktop',\n maxWidth: window.screen.width,\n maxHeight: window.screen.height,\n minFrameRate: media.screenshareFrameRate,\n maxFrameRate: media.screenshareFrameRate\n },\n optional: [{\n googLeakyBucket: true\n }, {\n googTemporalLayeredScreencast: true\n }]\n }\n };\n constraints.video.mandatory.chromeMediaSourceId = sourceId;\n getScreenMedia(constraints, callbackUserMedia, isAudioSendEnabled(media) && !media.keepAudio);\n });\n }\n } else if (Janus.webRTCAdapter.browserDetails.browser === 'firefox') {\n if (Janus.webRTCAdapter.browserDetails.version >= 33) {\n // Firefox 33+ has experimental support for screen sharing\n constraints = {\n video: {\n mozMediaSource: media.video,\n mediaSource: media.video\n },\n audio: isAudioSendEnabled(media) && !media.keepAudio\n };\n getScreenMedia(constraints, function (err, stream) {\n callbackUserMedia(err, stream); // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1045810\n\n if (!err) {\n var lastTime = stream.currentTime;\n var polly = window.setInterval(function () {\n if (!stream) window.clearInterval(polly);\n\n if (stream.currentTime == lastTime) {\n window.clearInterval(polly);\n\n if (stream.onended) {\n stream.onended();\n }\n }\n\n lastTime = stream.currentTime;\n }, 500);\n }\n });\n } else {\n var error = new Error('NavigatorUserMediaError');\n error.name = 'Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)';\n pluginHandle.consentDialog(false);\n callbacks.error(error);\n return;\n }\n }\n\n return;\n }\n } // If we got here, we're not screensharing\n\n\n if (media === null || media === undefined || media.video !== 'screen') {\n // Check whether all media sources are actually available or not\n navigator.mediaDevices.enumerateDevices().then(function (devices) {\n var audioExist = devices.some(function (device) {\n return device.kind === 'audioinput';\n }),\n videoExist = isScreenSendEnabled(media) || devices.some(function (device) {\n return device.kind === 'videoinput';\n }); // Check whether a missing device is really a problem\n\n var audioSend = isAudioSendEnabled(media);\n var videoSend = isVideoSendEnabled(media);\n var needAudioDevice = isAudioSendRequired(media);\n var needVideoDevice = isVideoSendRequired(media);\n\n if (audioSend || videoSend || needAudioDevice || needVideoDevice) {\n // We need to send either audio or video\n var haveAudioDevice = audioSend ? audioExist : false;\n var haveVideoDevice = videoSend ? videoExist : false;\n\n if (!haveAudioDevice && !haveVideoDevice) {\n // FIXME Should we really give up, or just assume recvonly for both?\n pluginHandle.consentDialog(false);\n callbacks.error('No capture device found');\n return false;\n } else if (!haveAudioDevice && needAudioDevice) {\n pluginHandle.consentDialog(false);\n callbacks.error('Audio capture is required, but no capture device found');\n return false;\n } else if (!haveVideoDevice && needVideoDevice) {\n pluginHandle.consentDialog(false);\n callbacks.error('Video capture is required, but no capture device found');\n return false;\n }\n }\n\n var gumConstraints = {\n audio: audioExist && !media.keepAudio ? audioSupport : false,\n video: videoExist && !media.keepVideo ? videoSupport : false\n };\n Janus.debug(\"getUserMedia constraints\", gumConstraints);\n\n if (!gumConstraints.audio && !gumConstraints.video) {\n pluginHandle.consentDialog(false);\n streamsDone(handleId, jsep, media, callbacks, stream);\n } else {\n navigator.mediaDevices.getUserMedia(gumConstraints).then(function (stream) {\n pluginHandle.consentDialog(false);\n streamsDone(handleId, jsep, media, callbacks, stream);\n }).catch(function (error) {\n pluginHandle.consentDialog(false);\n callbacks.error({\n code: error.code,\n name: error.name,\n message: error.message\n });\n });\n }\n }).catch(function (error) {\n pluginHandle.consentDialog(false);\n callbacks.error('enumerateDevices error', error);\n });\n }\n } else {\n // No need to do a getUserMedia, create offer/answer right away\n streamsDone(handleId, jsep, media, callbacks);\n }\n }\n\n function prepareWebrtcPeer(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : webrtcError;\n var jsep = callbacks.jsep;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n if (jsep !== undefined && jsep !== null) {\n if (config.pc === null) {\n Janus.warn(\"Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep\");\n callbacks.error(\"No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep\");\n return;\n }\n\n config.pc.setRemoteDescription(jsep).then(function () {\n Janus.log(\"Remote description accepted!\");\n config.remoteSdp = jsep.sdp; // Any trickle candidate we cached?\n\n if (config.candidates && config.candidates.length > 0) {\n for (var i in config.candidates) {\n var candidate = config.candidates[i];\n Janus.debug(\"Adding remote candidate:\", candidate);\n\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate();\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n\n config.candidates = [];\n } // Done\n\n\n callbacks.success();\n }, callbacks.error);\n } else {\n callbacks.error(\"Invalid JSEP\");\n }\n }\n\n function createOffer(handleId, media, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n callbacks.customizeSdp = typeof callbacks.customizeSdp == \"function\" ? callbacks.customizeSdp : Janus.noop;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n var simulcast = callbacks.simulcast === true ? true : false;\n\n if (!simulcast) {\n Janus.log(\"Creating offer (iceDone=\" + config.iceDone + \")\");\n } else {\n Janus.log(\"Creating offer (iceDone=\" + config.iceDone + \", simulcast=\" + simulcast + \")\");\n } // https://code.google.com/p/webrtc/issues/detail?id=3508\n\n\n var mediaConstraints = {};\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" && Janus.webRTCAdapter.browserDetails.version >= 59 || Janus.webRTCAdapter.browserDetails.browser === \"safari\" && window.RTCRtpSender.prototype.replaceTrack || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72) {\n // Firefox >= 59, Safari and Chrome >= 72 use Transceivers\n var audioTransceiver = null,\n videoTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\") {\n if (!audioTransceiver) audioTransceiver = t;\n continue;\n }\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\") {\n if (!videoTransceiver) videoTransceiver = t;\n continue;\n }\n }\n } // Handle audio (and related changes, if any)\n\n\n var audioSend = isAudioSendEnabled(media);\n var audioRecv = isAudioRecvEnabled(media);\n\n if (!audioSend && !audioRecv) {\n // Audio disabled: have we removed it?\n if (media.removeAudio && audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"inactive\");\n } else {\n audioTransceiver.direction = \"inactive\";\n }\n\n Janus.log(\"Setting audio transceiver to inactive:\", audioTransceiver);\n }\n } else {\n // Take care of audio m-line\n if (audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendrecv\");\n } else {\n audioTransceiver.direction = \"sendrecv\";\n }\n\n Janus.log(\"Setting audio transceiver to sendrecv:\", audioTransceiver);\n }\n } else if (audioSend && !audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendonly\");\n } else {\n audioTransceiver.direction = \"sendonly\";\n }\n\n Janus.log(\"Setting audio transceiver to sendonly:\", audioTransceiver);\n }\n } else if (!audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"recvonly\");\n } else {\n audioTransceiver.direction = \"recvonly\";\n }\n\n Janus.log(\"Setting audio transceiver to recvonly:\", audioTransceiver);\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n audioTransceiver = config.pc.addTransceiver(\"audio\", {\n direction: \"recvonly\"\n });\n Janus.log(\"Adding recvonly audio transceiver:\", audioTransceiver);\n }\n }\n } // Handle video (and related changes, if any)\n\n\n var videoSend = isVideoSendEnabled(media);\n var videoRecv = isVideoRecvEnabled(media);\n\n if (!videoSend && !videoRecv) {\n // Video disabled: have we removed it?\n if (media.removeVideo && videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"inactive\");\n } else {\n videoTransceiver.direction = \"inactive\";\n }\n\n Janus.log(\"Setting video transceiver to inactive:\", videoTransceiver);\n }\n } else {\n // Take care of video m-line\n if (videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendrecv\");\n } else {\n videoTransceiver.direction = \"sendrecv\";\n }\n\n Janus.log(\"Setting video transceiver to sendrecv:\", videoTransceiver);\n }\n } else if (videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendonly\");\n } else {\n videoTransceiver.direction = \"sendonly\";\n }\n\n Janus.log(\"Setting video transceiver to sendonly:\", videoTransceiver);\n }\n } else if (!videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"recvonly\");\n } else {\n videoTransceiver.direction = \"recvonly\";\n }\n\n Janus.log(\"Setting video transceiver to recvonly:\", videoTransceiver);\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n videoTransceiver = config.pc.addTransceiver(\"video\", {\n direction: \"recvonly\"\n });\n Janus.log(\"Adding recvonly video transceiver:\", videoTransceiver);\n }\n }\n }\n } else {\n mediaConstraints[\"offerToReceiveAudio\"] = isAudioRecvEnabled(media);\n mediaConstraints[\"offerToReceiveVideo\"] = isVideoRecvEnabled(media);\n }\n\n var iceRestart = callbacks.iceRestart === true ? true : false;\n\n if (iceRestart) {\n mediaConstraints[\"iceRestart\"] = true;\n }\n\n Janus.debug(mediaConstraints); // Check if this is Firefox and we've been asked to do simulcasting\n\n var sendVideo = isVideoSendEnabled(media);\n\n if (sendVideo && simulcast && Janus.webRTCAdapter.browserDetails.browser === \"firefox\") {\n // FIXME Based on https://gist.github.com/voluntas/088bc3cc62094730647b\n Janus.log(\"Enabling Simulcasting for Firefox (RID)\");\n var sender = config.pc.getSenders().find(function (s) {\n return s.track.kind == \"video\";\n });\n\n if (sender) {\n var parameters = sender.getParameters();\n if (!parameters) parameters = {};\n parameters.encodings = [{\n rid: \"h\",\n active: true,\n maxBitrate: 900000\n }, {\n rid: \"m\",\n active: true,\n maxBitrate: 300000,\n scaleResolutionDownBy: 2\n }, {\n rid: \"l\",\n active: true,\n maxBitrate: 100000,\n scaleResolutionDownBy: 4\n }];\n sender.setParameters(parameters);\n }\n }\n\n config.pc.createOffer(mediaConstraints).then(function (offer) {\n Janus.debug(offer); // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n\n var jsep = {\n \"type\": offer.type,\n \"sdp\": offer.sdp\n };\n callbacks.customizeSdp(jsep);\n offer.sdp = jsep.sdp;\n Janus.log(\"Setting local description\");\n\n if (sendVideo && simulcast) {\n // This SDP munging only works with Chrome (Safari STP may support it too)\n if (Janus.webRTCAdapter.browserDetails.browser === \"chrome\" || Janus.webRTCAdapter.browserDetails.browser === \"safari\") {\n Janus.log(\"Enabling Simulcasting for Chrome (SDP munging)\");\n offer.sdp = mungeSdpForSimulcasting(offer.sdp);\n } else if (Janus.webRTCAdapter.browserDetails.browser !== \"firefox\") {\n Janus.warn(\"simulcast=true, but this is not Chrome nor Firefox, ignoring\");\n }\n }\n\n config.mySdp = offer.sdp;\n config.pc.setLocalDescription(offer).catch(callbacks.error);\n config.mediaConstraints = mediaConstraints;\n\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n Janus.log(\"Waiting for all candidates...\");\n return;\n }\n\n Janus.log(\"Offer ready\");\n Janus.debug(callbacks);\n callbacks.success(offer);\n }, callbacks.error);\n }\n\n function createAnswer(handleId, media, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n callbacks.customizeSdp = typeof callbacks.customizeSdp == \"function\" ? callbacks.customizeSdp : Janus.noop;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n callbacks.error(\"Invalid handle\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n var simulcast = callbacks.simulcast === true ? true : false;\n\n if (!simulcast) {\n Janus.log(\"Creating answer (iceDone=\" + config.iceDone + \")\");\n } else {\n Janus.log(\"Creating answer (iceDone=\" + config.iceDone + \", simulcast=\" + simulcast + \")\");\n }\n\n var mediaConstraints = null;\n\n if (Janus.webRTCAdapter.browserDetails.browser === \"firefox\" && Janus.webRTCAdapter.browserDetails.version >= 59 || Janus.webRTCAdapter.browserDetails.browser === \"safari\" && window.RTCRtpSender.prototype.replaceTrack || Janus.webRTCAdapter.browserDetails.browser === \"chrome\" && Janus.webRTCAdapter.browserDetails.version >= 72) {\n // Firefox >= 59, Safari and Chrome >= 72 use Transceivers\n mediaConstraints = {};\n var audioTransceiver = null,\n videoTransceiver = null;\n var transceivers = config.pc.getTransceivers();\n\n if (transceivers && transceivers.length > 0) {\n for (var i in transceivers) {\n var t = transceivers[i];\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\") {\n if (!audioTransceiver) audioTransceiver = t;\n continue;\n }\n\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\") {\n if (!videoTransceiver) videoTransceiver = t;\n continue;\n }\n }\n } // Handle audio (and related changes, if any)\n\n\n var audioSend = isAudioSendEnabled(media);\n var audioRecv = isAudioRecvEnabled(media);\n\n if (!audioSend && !audioRecv) {\n // Audio disabled: have we removed it?\n if (media.removeAudio && audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"inactive\");\n } else {\n audioTransceiver.direction = \"inactive\";\n }\n\n Janus.log(\"Setting audio transceiver to inactive:\", audioTransceiver);\n }\n } else {\n // Take care of audio m-line\n if (audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendrecv\");\n } else {\n audioTransceiver.direction = \"sendrecv\";\n }\n\n Janus.log(\"Setting audio transceiver to sendrecv:\", audioTransceiver);\n }\n } else if (audioSend && !audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"sendonly\");\n } else {\n audioTransceiver.direction = \"sendonly\";\n }\n\n Janus.log(\"Setting audio transceiver to sendonly:\", audioTransceiver);\n }\n } else if (!audioSend && audioRecv) {\n if (audioTransceiver) {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection(\"recvonly\");\n } else {\n audioTransceiver.direction = \"recvonly\";\n }\n\n Janus.log(\"Setting audio transceiver to recvonly:\", audioTransceiver);\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n audioTransceiver = config.pc.addTransceiver(\"audio\", {\n direction: \"recvonly\"\n });\n Janus.log(\"Adding recvonly audio transceiver:\", audioTransceiver);\n }\n }\n } // Handle video (and related changes, if any)\n\n\n var videoSend = isVideoSendEnabled(media);\n var videoRecv = isVideoRecvEnabled(media);\n\n if (!videoSend && !videoRecv) {\n // Video disabled: have we removed it?\n if (media.removeVideo && videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"inactive\");\n } else {\n videoTransceiver.direction = \"inactive\";\n }\n\n Janus.log(\"Setting video transceiver to inactive:\", videoTransceiver);\n }\n } else {\n // Take care of video m-line\n if (videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendrecv\");\n } else {\n videoTransceiver.direction = \"sendrecv\";\n }\n\n Janus.log(\"Setting video transceiver to sendrecv:\", videoTransceiver);\n }\n } else if (videoSend && !videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"sendonly\");\n } else {\n videoTransceiver.direction = \"sendonly\";\n }\n\n Janus.log(\"Setting video transceiver to sendonly:\", videoTransceiver);\n }\n } else if (!videoSend && videoRecv) {\n if (videoTransceiver) {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection(\"recvonly\");\n } else {\n videoTransceiver.direction = \"recvonly\";\n }\n\n Janus.log(\"Setting video transceiver to recvonly:\", videoTransceiver);\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n videoTransceiver = config.pc.addTransceiver(\"video\", {\n direction: \"recvonly\"\n });\n Janus.log(\"Adding recvonly video transceiver:\", videoTransceiver);\n }\n }\n }\n } else {\n if (Janus.webRTCAdapter.browserDetails.browser == \"firefox\" || Janus.webRTCAdapter.browserDetails.browser == \"edge\") {\n mediaConstraints = {\n offerToReceiveAudio: isAudioRecvEnabled(media),\n offerToReceiveVideo: isVideoRecvEnabled(media)\n };\n } else {\n mediaConstraints = {\n mandatory: {\n OfferToReceiveAudio: isAudioRecvEnabled(media),\n OfferToReceiveVideo: isVideoRecvEnabled(media)\n }\n };\n }\n }\n\n Janus.debug(mediaConstraints); // Check if this is Firefox and we've been asked to do simulcasting\n\n var sendVideo = isVideoSendEnabled(media);\n\n if (sendVideo && simulcast && Janus.webRTCAdapter.browserDetails.browser === \"firefox\") {\n // FIXME Based on https://gist.github.com/voluntas/088bc3cc62094730647b\n Janus.log(\"Enabling Simulcasting for Firefox (RID)\");\n var sender = config.pc.getSenders()[1];\n Janus.log(sender);\n var parameters = sender.getParameters();\n Janus.log(parameters);\n sender.setParameters({\n encodings: [{\n rid: \"high\",\n active: true,\n priority: \"high\",\n maxBitrate: 1000000\n }, {\n rid: \"medium\",\n active: true,\n priority: \"medium\",\n maxBitrate: 300000\n }, {\n rid: \"low\",\n active: true,\n priority: \"low\",\n maxBitrate: 100000\n }]\n });\n }\n\n config.pc.createAnswer(mediaConstraints).then(function (answer) {\n Janus.debug(answer); // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n\n var jsep = {\n \"type\": answer.type,\n \"sdp\": answer.sdp\n };\n callbacks.customizeSdp(jsep);\n answer.sdp = jsep.sdp;\n Janus.log(\"Setting local description\");\n\n if (sendVideo && simulcast) {\n // This SDP munging only works with Chrome\n if (Janus.webRTCAdapter.browserDetails.browser === \"chrome\") {\n // FIXME Apparently trying to simulcast when answering breaks video in Chrome...\n //~ Janus.log(\"Enabling Simulcasting for Chrome (SDP munging)\");\n //~ answer.sdp = mungeSdpForSimulcasting(answer.sdp);\n Janus.warn(\"simulcast=true, but this is an answer, and video breaks in Chrome if we enable it\");\n } else if (Janus.webRTCAdapter.browserDetails.browser !== \"firefox\") {\n Janus.warn(\"simulcast=true, but this is not Chrome nor Firefox, ignoring\");\n }\n }\n\n config.mySdp = answer.sdp;\n config.pc.setLocalDescription(answer).catch(callbacks.error);\n config.mediaConstraints = mediaConstraints;\n\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n Janus.log(\"Waiting for all candidates...\");\n return;\n }\n\n callbacks.success(answer);\n }, callbacks.error);\n }\n\n function sendSDP(handleId, callbacks) {\n callbacks = callbacks || {};\n callbacks.success = typeof callbacks.success == \"function\" ? callbacks.success : Janus.noop;\n callbacks.error = typeof callbacks.error == \"function\" ? callbacks.error : Janus.noop;\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle, not sending anything\");\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n Janus.log(\"Sending offer/answer SDP...\");\n\n if (config.mySdp === null || config.mySdp === undefined) {\n Janus.warn(\"Local SDP instance is invalid, not sending anything...\");\n return;\n }\n\n config.mySdp = {\n \"type\": config.pc.localDescription.type,\n \"sdp\": config.pc.localDescription.sdp\n };\n if (config.trickle === false) config.mySdp[\"trickle\"] = false;\n Janus.debug(callbacks);\n config.sdpSent = true;\n callbacks.success(config.mySdp);\n }\n\n function getVolume(handleId, remote) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return 0;\n }\n\n var stream = remote ? \"remote\" : \"local\";\n var config = pluginHandle.webrtcStuff;\n if (!config.volume[stream]) config.volume[stream] = {\n value: 0\n }; // Start getting the volume, if getStats is supported\n\n if (config.pc.getStats && Janus.webRTCAdapter.browserDetails.browser === \"chrome\") {\n if (remote && (config.remoteStream === null || config.remoteStream === undefined)) {\n Janus.warn(\"Remote stream unavailable\");\n return 0;\n } else if (!remote && (config.myStream === null || config.myStream === undefined)) {\n Janus.warn(\"Local stream unavailable\");\n return 0;\n }\n\n if (config.volume[stream].timer === null || config.volume[stream].timer === undefined) {\n Janus.log(\"Starting \" + stream + \" volume monitor\");\n config.volume[stream].timer = setInterval(function () {\n config.pc.getStats(function (stats) {\n var results = stats.result();\n\n for (var i = 0; i < results.length; i++) {\n var res = results[i];\n\n if (res.type == 'ssrc') {\n if (remote && res.stat('audioOutputLevel')) config.volume[stream].value = parseInt(res.stat('audioOutputLevel'));else if (!remote && res.stat('audioInputLevel')) config.volume[stream].value = parseInt(res.stat('audioInputLevel'));\n }\n }\n });\n }, 200);\n return 0; // We don't have a volume to return yet\n }\n\n return config.volume[stream].value;\n } else {\n // audioInputLevel and audioOutputLevel seem only available in Chrome? audioLevel\n // seems to be available on Chrome and Firefox, but they don't seem to work\n Janus.warn(\"Getting the \" + stream + \" volume unsupported by browser\");\n return 0;\n }\n }\n\n function isMuted(handleId, video) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return true;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n if (config.pc === null || config.pc === undefined) {\n Janus.warn(\"Invalid PeerConnection\");\n return true;\n }\n\n if (config.myStream === undefined || config.myStream === null) {\n Janus.warn(\"Invalid local MediaStream\");\n return true;\n }\n\n if (video) {\n // Check video track\n if (config.myStream.getVideoTracks() === null || config.myStream.getVideoTracks() === undefined || config.myStream.getVideoTracks().length === 0) {\n Janus.warn(\"No video track\");\n return true;\n }\n\n return !config.myStream.getVideoTracks()[0].enabled;\n } else {\n // Check audio track\n if (config.myStream.getAudioTracks() === null || config.myStream.getAudioTracks() === undefined || config.myStream.getAudioTracks().length === 0) {\n Janus.warn(\"No audio track\");\n return true;\n }\n\n return !config.myStream.getAudioTracks()[0].enabled;\n }\n }\n\n function mute(handleId, video, mute) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return false;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n if (config.pc === null || config.pc === undefined) {\n Janus.warn(\"Invalid PeerConnection\");\n return false;\n }\n\n if (config.myStream === undefined || config.myStream === null) {\n Janus.warn(\"Invalid local MediaStream\");\n return false;\n }\n\n if (video) {\n // Mute/unmute video track\n if (config.myStream.getVideoTracks() === null || config.myStream.getVideoTracks() === undefined || config.myStream.getVideoTracks().length === 0) {\n Janus.warn(\"No video track\");\n return false;\n }\n\n config.myStream.getVideoTracks()[0].enabled = mute ? false : true;\n return true;\n } else {\n // Mute/unmute audio track\n if (config.myStream.getAudioTracks() === null || config.myStream.getAudioTracks() === undefined || config.myStream.getAudioTracks().length === 0) {\n Janus.warn(\"No audio track\");\n return false;\n }\n\n config.myStream.getAudioTracks()[0].enabled = mute ? false : true;\n return true;\n }\n }\n\n function getBitrate(handleId) {\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined || pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {\n Janus.warn(\"Invalid handle\");\n return \"Invalid handle\";\n }\n\n var config = pluginHandle.webrtcStuff;\n if (config.pc === null || config.pc === undefined) return \"Invalid PeerConnection\"; // Start getting the bitrate, if getStats is supported\n\n if (config.pc.getStats) {\n if (config.bitrate.timer === null || config.bitrate.timer === undefined) {\n Janus.log(\"Starting bitrate timer (via getStats)\");\n config.bitrate.timer = setInterval(function () {\n config.pc.getStats().then(function (stats) {\n stats.forEach(function (res) {\n if (!res) return;\n var inStats = false; // Check if these are statistics on incoming media\n\n if ((res.mediaType === \"video\" || res.id.toLowerCase().indexOf(\"video\") > -1) && res.type === \"inbound-rtp\" && res.id.indexOf(\"rtcp\") < 0) {\n // New stats\n inStats = true;\n } else if (res.type == 'ssrc' && res.bytesReceived && (res.googCodecName === \"VP8\" || res.googCodecName === \"\")) {\n // Older Chromer versions\n inStats = true;\n } // Parse stats now\n\n\n if (inStats) {\n config.bitrate.bsnow = res.bytesReceived;\n config.bitrate.tsnow = res.timestamp;\n\n if (config.bitrate.bsbefore === null || config.bitrate.tsbefore === null) {\n // Skip this round\n config.bitrate.bsbefore = config.bitrate.bsnow;\n config.bitrate.tsbefore = config.bitrate.tsnow;\n } else {\n // Calculate bitrate\n var timePassed = config.bitrate.tsnow - config.bitrate.tsbefore;\n if (Janus.webRTCAdapter.browserDetails.browser == \"safari\") timePassed = timePassed / 1000; // Apparently the timestamp is in microseconds, in Safari\n\n var bitRate = Math.round((config.bitrate.bsnow - config.bitrate.bsbefore) * 8 / timePassed);\n if (Janus.webRTCAdapter.browserDetails.browser === 'safari') bitRate = parseInt(bitRate / 1000);\n config.bitrate.value = bitRate + ' kbits/sec'; //~ Janus.log(\"Estimated bitrate is \" + config.bitrate.value);\n\n config.bitrate.bsbefore = config.bitrate.bsnow;\n config.bitrate.tsbefore = config.bitrate.tsnow;\n }\n }\n });\n });\n }, 1000);\n return \"0 kbits/sec\"; // We don't have a bitrate value yet\n }\n\n return config.bitrate.value;\n } else {\n Janus.warn(\"Getting the video bitrate unsupported by browser\");\n return \"Feature unsupported by browser\";\n }\n }\n\n function webrtcError(error) {\n Janus.error(\"WebRTC error:\", error);\n }\n\n function cleanupWebrtc(handleId, hangupRequest) {\n Janus.log(\"Cleaning WebRTC stuff\");\n var pluginHandle = pluginHandles[handleId];\n\n if (pluginHandle === null || pluginHandle === undefined) {\n // Nothing to clean\n return;\n }\n\n var config = pluginHandle.webrtcStuff;\n\n if (config !== null && config !== undefined) {\n if (hangupRequest === true) {\n // Send a hangup request (we don't really care about the response)\n var request = {\n \"janus\": \"hangup\",\n \"transaction\": Janus.randomString(12)\n };\n if (pluginHandle.token !== null && pluginHandle.token !== undefined) request[\"token\"] = pluginHandle.token;\n if (apisecret !== null && apisecret !== undefined) request[\"apisecret\"] = apisecret;\n Janus.debug(\"Sending hangup request (handle=\" + handleId + \"):\");\n Janus.debug(request);\n\n if (websockets) {\n request[\"session_id\"] = sessionId;\n request[\"handle_id\"] = handleId;\n ws.send(JSON.stringify(request));\n } else {\n Janus.httpAPICall(server + \"/\" + sessionId + \"/\" + handleId, {\n verb: 'POST',\n withCredentials: withCredentials,\n body: request\n });\n }\n } // Cleanup stack\n\n\n config.remoteStream = null;\n\n if (config.volume) {\n if (config.volume[\"local\"] && config.volume[\"local\"].timer) clearInterval(config.volume[\"local\"].timer);\n if (config.volume[\"remote\"] && config.volume[\"remote\"].timer) clearInterval(config.volume[\"remote\"].timer);\n }\n\n config.volume = {};\n if (config.bitrate.timer) clearInterval(config.bitrate.timer);\n config.bitrate.timer = null;\n config.bitrate.bsnow = null;\n config.bitrate.bsbefore = null;\n config.bitrate.tsnow = null;\n config.bitrate.tsbefore = null;\n config.bitrate.value = null;\n\n try {\n // Try a MediaStreamTrack.stop() for each track\n if (!config.streamExternal && config.myStream !== null && config.myStream !== undefined) {\n Janus.log(\"Stopping local stream tracks\");\n var tracks = config.myStream.getTracks();\n\n for (var i in tracks) {\n var mst = tracks[i];\n Janus.log(mst);\n if (mst !== null && mst !== undefined) mst.stop();\n }\n }\n } catch (e) {// Do nothing if this fails\n }\n\n config.streamExternal = false;\n config.myStream = null; // Close PeerConnection\n\n try {\n config.pc.close();\n } catch (e) {// Do nothing\n }\n\n config.pc = null;\n config.candidates = null;\n config.mySdp = null;\n config.remoteSdp = null;\n config.iceDone = false;\n config.dataChannel = {};\n config.dtmfSender = null;\n }\n\n pluginHandle.oncleanup();\n } // Helper method to munge an SDP to enable simulcasting (Chrome only)\n\n\n function mungeSdpForSimulcasting(sdp) {\n // Let's munge the SDP to add the attributes for enabling simulcasting\n // (based on https://gist.github.com/ggarber/a19b4c33510028b9c657)\n var lines = sdp.split(\"\\r\\n\");\n var video = false;\n var ssrc = [-1],\n ssrc_fid = [-1];\n var cname = null,\n msid = null,\n mslabel = null,\n label = null;\n var insertAt = -1;\n\n for (var i = 0; i < lines.length; i++) {\n var mline = lines[i].match(/m=(\\w+) */);\n\n if (mline) {\n var medium = mline[1];\n\n if (medium === \"video\") {\n // New video m-line: make sure it's the first one\n if (ssrc[0] < 0) {\n video = true;\n } else {\n // We're done, let's add the new attributes here\n insertAt = i;\n break;\n }\n } else {\n // New non-video m-line: do we have what we were looking for?\n if (ssrc[0] > -1) {\n // We're done, let's add the new attributes here\n insertAt = i;\n break;\n }\n }\n\n continue;\n }\n\n if (!video) continue;\n var fid = lines[i].match(/a=ssrc-group:FID (\\d+) (\\d+)/);\n\n if (fid) {\n ssrc[0] = fid[1];\n ssrc_fid[0] = fid[2];\n lines.splice(i, 1);\n i--;\n continue;\n }\n\n if (ssrc[0]) {\n var match = lines[i].match('a=ssrc:' + ssrc[0] + ' cname:(.+)');\n\n if (match) {\n cname = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' msid:(.+)');\n\n if (match) {\n msid = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' mslabel:(.+)');\n\n if (match) {\n mslabel = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' label:(.+)');\n\n if (match) {\n label = match[1];\n }\n\n if (lines[i].indexOf('a=ssrc:' + ssrc_fid[0]) === 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n\n if (lines[i].indexOf('a=ssrc:' + ssrc[0]) === 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n }\n\n if (lines[i].length == 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n }\n\n if (ssrc[0] < 0) {\n // Couldn't find a FID attribute, let's just take the first video SSRC we find\n insertAt = -1;\n video = false;\n\n for (var i = 0; i < lines.length; i++) {\n var mline = lines[i].match(/m=(\\w+) */);\n\n if (mline) {\n var medium = mline[1];\n\n if (medium === \"video\") {\n // New video m-line: make sure it's the first one\n if (ssrc[0] < 0) {\n video = true;\n } else {\n // We're done, let's add the new attributes here\n insertAt = i;\n break;\n }\n } else {\n // New non-video m-line: do we have what we were looking for?\n if (ssrc[0] > -1) {\n // We're done, let's add the new attributes here\n insertAt = i;\n break;\n }\n }\n\n continue;\n }\n\n if (!video) continue;\n\n if (ssrc[0] < 0) {\n var value = lines[i].match(/a=ssrc:(\\d+)/);\n\n if (value) {\n ssrc[0] = value[1];\n lines.splice(i, 1);\n i--;\n continue;\n }\n } else {\n var match = lines[i].match('a=ssrc:' + ssrc[0] + ' cname:(.+)');\n\n if (match) {\n cname = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' msid:(.+)');\n\n if (match) {\n msid = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' mslabel:(.+)');\n\n if (match) {\n mslabel = match[1];\n }\n\n match = lines[i].match('a=ssrc:' + ssrc[0] + ' label:(.+)');\n\n if (match) {\n label = match[1];\n }\n\n if (lines[i].indexOf('a=ssrc:' + ssrc_fid[0]) === 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n\n if (lines[i].indexOf('a=ssrc:' + ssrc[0]) === 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n }\n\n if (lines[i].length == 0) {\n lines.splice(i, 1);\n i--;\n continue;\n }\n }\n }\n\n if (ssrc[0] < 0) {\n // Still nothing, let's just return the SDP we were asked to munge\n Janus.warn(\"Couldn't find the video SSRC, simulcasting NOT enabled\");\n return sdp;\n }\n\n if (insertAt < 0) {\n // Append at the end\n insertAt = lines.length;\n } // Generate a couple of SSRCs (for retransmissions too)\n // Note: should we check if there are conflicts, here?\n\n\n ssrc[1] = Math.floor(Math.random() * 0xFFFFFFFF);\n ssrc[2] = Math.floor(Math.random() * 0xFFFFFFFF);\n ssrc_fid[1] = Math.floor(Math.random() * 0xFFFFFFFF);\n ssrc_fid[2] = Math.floor(Math.random() * 0xFFFFFFFF); // Add attributes to the SDP\n\n for (var i = 0; i < ssrc.length; i++) {\n if (cname) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' cname:' + cname);\n insertAt++;\n }\n\n if (msid) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' msid:' + msid);\n insertAt++;\n }\n\n if (mslabel) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' mslabel:' + mslabel);\n insertAt++;\n }\n\n if (label) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc[i] + ' label:' + label);\n insertAt++;\n } // Add the same info for the retransmission SSRC\n\n\n if (cname) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc_fid[i] + ' cname:' + cname);\n insertAt++;\n }\n\n if (msid) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc_fid[i] + ' msid:' + msid);\n insertAt++;\n }\n\n if (mslabel) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc_fid[i] + ' mslabel:' + mslabel);\n insertAt++;\n }\n\n if (label) {\n lines.splice(insertAt, 0, 'a=ssrc:' + ssrc_fid[i] + ' label:' + label);\n insertAt++;\n }\n }\n\n lines.splice(insertAt, 0, 'a=ssrc-group:FID ' + ssrc[2] + ' ' + ssrc_fid[2]);\n lines.splice(insertAt, 0, 'a=ssrc-group:FID ' + ssrc[1] + ' ' + ssrc_fid[1]);\n lines.splice(insertAt, 0, 'a=ssrc-group:FID ' + ssrc[0] + ' ' + ssrc_fid[0]);\n lines.splice(insertAt, 0, 'a=ssrc-group:SIM ' + ssrc[0] + ' ' + ssrc[1] + ' ' + ssrc[2]);\n sdp = lines.join(\"\\r\\n\");\n if (!sdp.endsWith(\"\\r\\n\")) sdp += \"\\r\\n\";\n return sdp;\n } // Helper methods to parse a media object\n\n\n function isAudioSendEnabled(media) {\n Janus.debug(\"isAudioSendEnabled:\", media);\n if (media === undefined || media === null) return true; // Default\n\n if (media.audio === false) return false; // Generic audio has precedence\n\n if (media.audioSend === undefined || media.audioSend === null) return true; // Default\n\n return media.audioSend === true;\n }\n\n function isAudioSendRequired(media) {\n Janus.debug(\"isAudioSendRequired:\", media);\n if (media === undefined || media === null) return false; // Default\n\n if (media.audio === false || media.audioSend === false) return false; // If we're not asking to capture audio, it's not required\n\n if (media.failIfNoAudio === undefined || media.failIfNoAudio === null) return false; // Default\n\n return media.failIfNoAudio === true;\n }\n\n function isAudioRecvEnabled(media) {\n Janus.debug(\"isAudioRecvEnabled:\", media);\n if (media === undefined || media === null) return true; // Default\n\n if (media.audio === false) return false; // Generic audio has precedence\n\n if (media.audioRecv === undefined || media.audioRecv === null) return true; // Default\n\n return media.audioRecv === true;\n }\n\n function isVideoSendEnabled(media) {\n Janus.debug(\"isVideoSendEnabled:\", media);\n if (media === undefined || media === null) return true; // Default\n\n if (media.video === false) return false; // Generic video has precedence\n\n if (media.videoSend === undefined || media.videoSend === null) return true; // Default\n\n return media.videoSend === true;\n }\n\n function isVideoSendRequired(media) {\n Janus.debug(\"isVideoSendRequired:\", media);\n if (media === undefined || media === null) return false; // Default\n\n if (media.video === false || media.videoSend === false) return false; // If we're not asking to capture video, it's not required\n\n if (media.failIfNoVideo === undefined || media.failIfNoVideo === null) return false; // Default\n\n return media.failIfNoVideo === true;\n }\n\n function isVideoRecvEnabled(media) {\n Janus.debug(\"isVideoRecvEnabled:\", media);\n if (media === undefined || media === null) return true; // Default\n\n if (media.video === false) return false; // Generic video has precedence\n\n if (media.videoRecv === undefined || media.videoRecv === null) return true; // Default\n\n return media.videoRecv === true;\n }\n\n function isScreenSendEnabled(media) {\n Janus.debug(\"isScreenSendEnabled:\", media);\n if (media === undefined || media === null) return false;\n if (typeof media.video !== 'object' || typeof media.video.mandatory !== 'object') return false;\n var constraints = media.video.mandatory;\n if (constraints.chromeMediaSource) return constraints.chromeMediaSource === 'desktop' || constraints.chromeMediaSource === 'screen';else if (constraints.mozMediaSource) return constraints.mozMediaSource === 'window' || constraints.mozMediaSource === 'screen';else if (constraints.mediaSource) return constraints.mediaSource === 'window' || constraints.mediaSource === 'screen';\n return false;\n }\n\n function isDataEnabled(media) {\n Janus.debug(\"isDataEnabled:\", media);\n\n if (Janus.webRTCAdapter.browserDetails.browser == \"edge\") {\n Janus.warn(\"Edge doesn't support data channels yet\");\n return false;\n }\n\n if (media === undefined || media === null) return false; // Default\n\n return media.data === true;\n }\n\n function isTrickleEnabled(trickle) {\n Janus.debug(\"isTrickleEnabled:\", trickle);\n if (trickle === undefined || trickle === null) return true; // Default is true\n\n return trickle === true;\n }\n}\n\n;\nexport default Janus;","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n const match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n const nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n const wrappedCallback = (e) => {\n const modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n if (cb.handleEvent) {\n cb.handleEvent(modifiedEvent);\n } else {\n cb(modifiedEvent);\n }\n }\n };\n this._eventMap = this._eventMap || {};\n if (!this._eventMap[eventNameToWrap]) {\n this._eventMap[eventNameToWrap] = new Map();\n }\n this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n const nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[eventNameToWrap]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n if (!this._eventMap[eventNameToWrap].has(cb)) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n this._eventMap[eventNameToWrap].delete(cb);\n if (this._eventMap[eventNameToWrap].size === 0) {\n delete this._eventMap[eventNameToWrap];\n }\n if (Object.keys(this._eventMap).length === 0) {\n delete this._eventMap;\n }\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get() {\n return this['_on' + eventNameToWrap];\n },\n set(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\nexport function disableLog(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\nexport function detectBrowser(window) {\n // Returned result object.\n const result = {browser: null, version: null};\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n const {navigator} = window;\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia ||\n (window.isSecureContext === false && window.webkitRTCPeerConnection &&\n !window.RTCIceGatherer)) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n // more complicated fallback to webkitRTCPeerConnection.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n 'currentDirection' in window.RTCRtpTransceiver.prototype;\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n if (!isObject(data)) {\n return data;\n }\n\n return Object.keys(data).reduce(function(accumulator, key) {\n const isObj = isObject(data[key]);\n const value = isObj ? compactObject(data[key]) : data[key];\n const isEmptyObject = isObj && !Object.keys(value).length;\n if (value === undefined || isEmptyObject) {\n return accumulator;\n }\n return Object.assign(accumulator, {[key]: value});\n }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n if (!base || resultSet.has(base.id)) {\n return;\n }\n resultSet.set(base.id, base);\n Object.keys(base).forEach(name => {\n if (name.endsWith('Id')) {\n walkStats(stats, stats.get(base[name]), resultSet);\n } else if (name.endsWith('Ids')) {\n base[name].forEach(id => {\n walkStats(stats, stats.get(id), resultSet);\n });\n }\n });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n const filteredResult = new Map();\n if (track === null) {\n return filteredResult;\n }\n const trackStats = [];\n result.forEach(value => {\n if (value.type === 'track' &&\n value.trackIdentifier === track.id) {\n trackStats.push(value);\n }\n });\n trackStats.forEach(trackStat => {\n result.forEach(stats => {\n if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n walkStats(result, stats, filteredResult);\n }\n });\n });\n return filteredResult;\n}\n\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n\n if (!navigator.mediaDevices) {\n return;\n }\n\n const constraintsToChrome_ = function(c) {\n if (typeof c !== 'object' || c.mandatory || c.optional) {\n return c;\n }\n const cc = {};\n Object.keys(c).forEach(key => {\n if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n return;\n }\n const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n if (r.exact !== undefined && typeof r.exact === 'number') {\n r.min = r.max = r.exact;\n }\n const oldname_ = function(prefix, name) {\n if (prefix) {\n return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n }\n return (name === 'deviceId') ? 'sourceId' : name;\n };\n if (r.ideal !== undefined) {\n cc.optional = cc.optional || [];\n let oc = {};\n if (typeof r.ideal === 'number') {\n oc[oldname_('min', key)] = r.ideal;\n cc.optional.push(oc);\n oc = {};\n oc[oldname_('max', key)] = r.ideal;\n cc.optional.push(oc);\n } else {\n oc[oldname_('', key)] = r.ideal;\n cc.optional.push(oc);\n }\n }\n if (r.exact !== undefined && typeof r.exact !== 'number') {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_('', key)] = r.exact;\n } else {\n ['min', 'max'].forEach(mix => {\n if (r[mix] !== undefined) {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_(mix, key)] = r[mix];\n }\n });\n }\n });\n if (c.advanced) {\n cc.optional = (cc.optional || []).concat(c.advanced);\n }\n return cc;\n };\n\n const shimConstraints_ = function(constraints, func) {\n if (browserDetails.version >= 61) {\n return func(constraints);\n }\n constraints = JSON.parse(JSON.stringify(constraints));\n if (constraints && typeof constraints.audio === 'object') {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n constraints = JSON.parse(JSON.stringify(constraints));\n remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n constraints.audio = constraintsToChrome_(constraints.audio);\n }\n if (constraints && typeof constraints.video === 'object') {\n // Shim facingMode for mobile & surface pro.\n let face = constraints.video.facingMode;\n face = face && ((typeof face === 'object') ? face : {ideal: face});\n const getSupportedFacingModeLies = browserDetails.version < 66;\n\n if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n face.ideal === 'user' || face.ideal === 'environment')) &&\n !(navigator.mediaDevices.getSupportedConstraints &&\n navigator.mediaDevices.getSupportedConstraints().facingMode &&\n !getSupportedFacingModeLies)) {\n delete constraints.video.facingMode;\n let matches;\n if (face.exact === 'environment' || face.ideal === 'environment') {\n matches = ['back', 'rear'];\n } else if (face.exact === 'user' || face.ideal === 'user') {\n matches = ['front'];\n }\n if (matches) {\n // Look for matches in label, or use last cam for back (typical).\n return navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n devices = devices.filter(d => d.kind === 'videoinput');\n let dev = devices.find(d => matches.some(match =>\n d.label.toLowerCase().includes(match)));\n if (!dev && devices.length && matches.includes('back')) {\n dev = devices[devices.length - 1]; // more likely the back cam\n }\n if (dev) {\n constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :\n {ideal: dev.deviceId};\n }\n constraints.video = constraintsToChrome_(constraints.video);\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n });\n }\n }\n constraints.video = constraintsToChrome_(constraints.video);\n }\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n };\n\n const shimError_ = function(e) {\n if (browserDetails.version >= 64) {\n return e;\n }\n return {\n name: {\n PermissionDeniedError: 'NotAllowedError',\n PermissionDismissedError: 'NotAllowedError',\n InvalidStateError: 'NotAllowedError',\n DevicesNotFoundError: 'NotFoundError',\n ConstraintNotSatisfiedError: 'OverconstrainedError',\n TrackStartError: 'NotReadableError',\n MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n MediaDeviceKillSwitchOn: 'NotAllowedError',\n TabCaptureError: 'AbortError',\n ScreenCaptureError: 'AbortError',\n DeviceCaptureError: 'AbortError'\n }[e.name] || e.name,\n message: e.message,\n constraint: e.constraint || e.constraintName,\n toString() {\n return this.name + (this.message && ': ') + this.message;\n }\n };\n };\n\n const getUserMedia_ = function(constraints, onSuccess, onError) {\n shimConstraints_(constraints, c => {\n navigator.webkitGetUserMedia(c, onSuccess, e => {\n if (onError) {\n onError(shimError_(e));\n }\n });\n });\n };\n navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n // function which returns a Promise, it does not accept spec-style\n // constraints.\n if (navigator.mediaDevices.getUserMedia) {\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(cs) {\n return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n if (c.audio && !stream.getAudioTracks().length ||\n c.video && !stream.getVideoTracks().length) {\n stream.getTracks().forEach(track => {\n track.stop();\n });\n throw new DOMException('', 'NotFoundError');\n }\n return stream;\n }, e => Promise.reject(shimError_(e))));\n };\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n // getSourceId is a function that returns a promise resolving with\n // the sourceId of the screen/window/tab to be shared.\n if (typeof getSourceId !== 'function') {\n console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n 'a function');\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n return getSourceId(constraints)\n .then(sourceId => {\n const widthSpecified = constraints.video && constraints.video.width;\n const heightSpecified = constraints.video &&\n constraints.video.height;\n const frameRateSpecified = constraints.video &&\n constraints.video.frameRate;\n constraints.video = {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: sourceId,\n maxFrameRate: frameRateSpecified || 3\n }\n };\n if (widthSpecified) {\n constraints.video.mandatory.maxWidth = widthSpecified;\n }\n if (heightSpecified) {\n constraints.video.mandatory.maxHeight = heightSpecified;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n });\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n get() {\n return this._ontrack;\n },\n set(f) {\n if (this._ontrack) {\n this.removeEventListener('track', this._ontrack);\n }\n this.addEventListener('track', this._ontrack = f);\n },\n enumerable: true,\n configurable: true\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n if (!this._ontrackpoly) {\n this._ontrackpoly = (e) => {\n // onaddstream does not fire when a track is added to an existing\n // stream. But stream.onaddtrack is implemented so we use that.\n e.stream.addEventListener('addtrack', te => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === te.track.id);\n } else {\n receiver = {track: te.track};\n }\n\n const event = new Event('track');\n event.track = te.track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n e.stream.getTracks().forEach(track => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === track.id);\n } else {\n receiver = {track};\n }\n const event = new Event('track');\n event.track = track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n };\n this.addEventListener('addstream', this._ontrackpoly);\n }\n return origSetRemoteDescription.apply(this, arguments);\n };\n } else {\n // even if RTCRtpTransceiver is in window, it is only used and\n // emitted in unified-plan. Unfortunately this means we need\n // to unconditionally wrap the event.\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n if (!e.transceiver) {\n Object.defineProperty(e, 'transceiver',\n {value: {receiver: e.receiver}});\n }\n return e;\n });\n }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n if (typeof window === 'object' && window.RTCPeerConnection &&\n !('getSenders' in window.RTCPeerConnection.prototype) &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype) {\n const shimSenderWithDtmf = function(pc, track) {\n return {\n track,\n get dtmf() {\n if (this._dtmf === undefined) {\n if (track.kind === 'audio') {\n this._dtmf = pc.createDTMFSender(track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n },\n _pc: pc\n };\n };\n\n // augment addTrack when getSenders is not available.\n if (!window.RTCPeerConnection.prototype.getSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n this._senders = this._senders || [];\n return this._senders.slice(); // return a copy of the internal state.\n };\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n let sender = origAddTrack.apply(this, arguments);\n if (!sender) {\n sender = shimSenderWithDtmf(this, track);\n this._senders.push(sender);\n }\n return sender;\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n origRemoveTrack.apply(this, arguments);\n const idx = this._senders.indexOf(sender);\n if (idx !== -1) {\n this._senders.splice(idx, 1);\n }\n };\n }\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._senders = this._senders || [];\n origAddStream.apply(this, [stream]);\n stream.getTracks().forEach(track => {\n this._senders.push(shimSenderWithDtmf(this, track));\n });\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._senders = this._senders || [];\n origRemoveStream.apply(this, [stream]);\n\n stream.getTracks().forEach(track => {\n const sender = this._senders.find(s => s.track === track);\n if (sender) { // remove sender\n this._senders.splice(this._senders.indexOf(sender), 1);\n }\n });\n };\n } else if (typeof window === 'object' && window.RTCPeerConnection &&\n 'getSenders' in window.RTCPeerConnection.prototype &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype &&\n window.RTCRtpSender &&\n !('dtmf' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = this._pc.createDTMFSender(this.track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n}\n\nexport function shimGetStats(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n\n // If selector is a function then we are in the old style stats so just\n // pass back the original getStats format to avoid breaking old users.\n if (arguments.length > 0 && typeof selector === 'function') {\n return origGetStats.apply(this, arguments);\n }\n\n // When spec-style getStats is supported, return those when called with\n // either no arguments or the selector argument is null.\n if (origGetStats.length === 0 && (arguments.length === 0 ||\n typeof selector !== 'function')) {\n return origGetStats.apply(this, []);\n }\n\n const fixChromeStats_ = function(response) {\n const standardReport = {};\n const reports = response.result();\n reports.forEach(report => {\n const standardStats = {\n id: report.id,\n timestamp: report.timestamp,\n type: {\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[report.type] || report.type\n };\n report.names().forEach(name => {\n standardStats[name] = report.stat(name);\n });\n standardReport[standardStats.id] = standardStats;\n });\n\n return standardReport;\n };\n\n // shim getStats with maplike support\n const makeMapStats = function(stats) {\n return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n };\n\n if (arguments.length >= 2) {\n const successCallbackWrapper_ = function(response) {\n onSucc(makeMapStats(fixChromeStats_(response)));\n };\n\n return origGetStats.apply(this, [successCallbackWrapper_,\n selector]);\n }\n\n // promise-support\n return new Promise((resolve, reject) => {\n origGetStats.apply(this, [\n function(response) {\n resolve(makeMapStats(fixChromeStats_(response)));\n }, reject]);\n }).then(onSucc, onErr);\n };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender && window.RTCRtpReceiver)) {\n return;\n }\n\n // shim sender stats.\n if (!('getStats' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n const sender = this;\n return this._pc.getStats().then(result =>\n /* Note: this will include stats of all senders that\n * send a track with the same id as sender.track as\n * it is not possible to identify the RTCRtpSender.\n */\n utils.filterStats(result, sender.track, true));\n };\n }\n\n // shim receiver stats.\n if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers =\n function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n const receiver = this;\n return this._pc.getStats().then(result =>\n utils.filterStats(result, receiver.track, false));\n };\n }\n\n if (!('getStats' in window.RTCRtpSender.prototype &&\n 'getStats' in window.RTCRtpReceiver.prototype)) {\n return;\n }\n\n // shim RTCPeerConnection.getStats(track).\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n if (arguments.length > 0 &&\n arguments[0] instanceof window.MediaStreamTrack) {\n const track = arguments[0];\n let sender;\n let receiver;\n let err;\n this.getSenders().forEach(s => {\n if (s.track === track) {\n if (sender) {\n err = true;\n } else {\n sender = s;\n }\n }\n });\n this.getReceivers().forEach(r => {\n if (r.track === track) {\n if (receiver) {\n err = true;\n } else {\n receiver = r;\n }\n }\n return r.track === track;\n });\n if (err || (sender && receiver)) {\n return Promise.reject(new DOMException(\n 'There are more than one sender or receiver for the track.',\n 'InvalidAccessError'));\n } else if (sender) {\n return sender.getStats();\n } else if (receiver) {\n return receiver.getStats();\n }\n return Promise.reject(new DOMException(\n 'There is no sender or receiver for the track.',\n 'InvalidAccessError'));\n }\n return origGetStats.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n // shim addTrack/removeTrack with native variants in order to make\n // the interactions with legacy getLocalStreams behave as in other browsers.\n // Keeps a mapping stream.id => [stream, rtpsenders...]\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n return Object.keys(this._shimmedLocalStreams)\n .map(streamId => this._shimmedLocalStreams[streamId][0]);\n };\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (!stream) {\n return origAddTrack.apply(this, arguments);\n }\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n const sender = origAddTrack.apply(this, arguments);\n if (!this._shimmedLocalStreams[stream.id]) {\n this._shimmedLocalStreams[stream.id] = [stream, sender];\n } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n this._shimmedLocalStreams[stream.id].push(sender);\n }\n return sender;\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n const existingSenders = this.getSenders();\n origAddStream.apply(this, arguments);\n const newSenders = this.getSenders()\n .filter(newSender => existingSenders.indexOf(newSender) === -1);\n this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n delete this._shimmedLocalStreams[stream.id];\n return origRemoveStream.apply(this, arguments);\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n if (sender) {\n Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n if (idx !== -1) {\n this._shimmedLocalStreams[streamId].splice(idx, 1);\n }\n if (this._shimmedLocalStreams[streamId].length === 1) {\n delete this._shimmedLocalStreams[streamId];\n }\n });\n }\n return origRemoveTrack.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // shim addTrack and removeTrack.\n if (window.RTCPeerConnection.prototype.addTrack &&\n browserDetails.version >= 65) {\n return shimAddTrackRemoveTrackWithNative(window);\n }\n\n // also shim pc.getLocalStreams when addTrack is shimmed\n // to return the original streams.\n const origGetLocalStreams = window.RTCPeerConnection.prototype\n .getLocalStreams;\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n const nativeStreams = origGetLocalStreams.apply(this);\n this._reverseStreams = this._reverseStreams || {};\n return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n // Add identity mapping for consistency with addTrack.\n // Unless this is being used with a stream from addTrack.\n if (!this._reverseStreams[stream.id]) {\n const newStream = new window.MediaStream(stream.getTracks());\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n stream = newStream;\n }\n origAddStream.apply(this, [stream]);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n delete this._reverseStreams[(this._streams[stream.id] ?\n this._streams[stream.id].id : stream.id)];\n delete this._streams[stream.id];\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n const streams = [].slice.call(arguments, 1);\n if (streams.length !== 1 ||\n !streams[0].getTracks().find(t => t === track)) {\n // this is not fully correct but all we can manage without\n // [[associated MediaStreams]] internal slot.\n throw new DOMException(\n 'The adapter.js addTrack polyfill only supports a single ' +\n ' stream which is associated with the specified track.',\n 'NotSupportedError');\n }\n\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n const oldStream = this._streams[stream.id];\n if (oldStream) {\n // this is using odd Chrome behaviour, use with caution:\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n // Note: we rely on the high-level addTrack/dtmf shim to\n // create the sender with a dtmf sender.\n oldStream.addTrack(track);\n\n // Trigger ONN async.\n Promise.resolve().then(() => {\n this.dispatchEvent(new Event('negotiationneeded'));\n });\n } else {\n const newStream = new window.MediaStream([track]);\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n this.addStream(newStream);\n }\n return this.getSenders().find(s => s.track === track);\n };\n\n // replace the internal stream id with the external one and\n // vice versa.\n function replaceInternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n externalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n function replaceExternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n internalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n ['createOffer', 'createAnswer'].forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n const args = arguments;\n const isLegacyCall = arguments.length &&\n typeof arguments[0] === 'function';\n if (isLegacyCall) {\n return nativeMethod.apply(this, [\n (description) => {\n const desc = replaceInternalStreamId(this, description);\n args[0].apply(null, [desc]);\n },\n (err) => {\n if (args[1]) {\n args[1].apply(null, err);\n }\n }, arguments[2]\n ]);\n }\n return nativeMethod.apply(this, arguments)\n .then(description => replaceInternalStreamId(this, description));\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n\n const origSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n if (!arguments.length || !arguments[0].type) {\n return origSetLocalDescription.apply(this, arguments);\n }\n arguments[0] = replaceExternalStreamId(this, arguments[0]);\n return origSetLocalDescription.apply(this, arguments);\n };\n\n // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n const origLocalDescription = Object.getOwnPropertyDescriptor(\n window.RTCPeerConnection.prototype, 'localDescription');\n Object.defineProperty(window.RTCPeerConnection.prototype,\n 'localDescription', {\n get() {\n const description = origLocalDescription.get.apply(this);\n if (description.type === '') {\n return description;\n }\n return replaceInternalStreamId(this, description);\n }\n });\n\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n // We can not yet check for sender instanceof RTCRtpSender\n // since we shim RTPSender. So we check if sender._pc is set.\n if (!sender._pc) {\n throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.', 'TypeError');\n }\n const isLocal = sender._pc === this;\n if (!isLocal) {\n throw new DOMException('Sender was not created by this connection.',\n 'InvalidAccessError');\n }\n\n // Search for the native stream the senders track belongs to.\n this._streams = this._streams || {};\n let stream;\n Object.keys(this._streams).forEach(streamid => {\n const hasTrack = this._streams[streamid].getTracks()\n .find(track => sender.track === track);\n if (hasTrack) {\n stream = this._streams[streamid];\n }\n });\n\n if (stream) {\n if (stream.getTracks().length === 1) {\n // if this is the last track of the stream, remove the stream. This\n // takes care of any shimmed _senders.\n this.removeStream(this._reverseStreams[stream.id]);\n } else {\n // relying on the same odd chrome behaviour as above.\n stream.removeTrack(sender.track);\n }\n this.dispatchEvent(new Event('negotiationneeded'));\n }\n };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.webkitRTCPeerConnection;\n }\n if (!window.RTCPeerConnection) {\n return;\n }\n\n // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n if (browserDetails.version < 53) {\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n const pc = e.target;\n if (browserDetails.version < 72 || (pc.getConfiguration &&\n pc.getConfiguration().sdpSemantics === 'plan-b')) {\n if (pc.signalingState !== 'stable') {\n return;\n }\n }\n return e;\n });\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n const MediaStreamTrack = window && window.MediaStreamTrack;\n\n navigator.getUserMedia = function(constraints, onSuccess, onError) {\n // Replace Firefox 44+'s deprecation warning with unprefixed version.\n utils.deprecated('navigator.getUserMedia',\n 'navigator.mediaDevices.getUserMedia');\n navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n };\n\n if (!(browserDetails.version > 55 &&\n 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n\n const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n if (typeof c === 'object' && typeof c.audio === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeGetUserMedia(c);\n };\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n MediaStreamTrack.prototype.getSettings = function() {\n const obj = nativeGetSettings.apply(this, arguments);\n remap(obj, 'mozAutoGainControl', 'autoGainControl');\n remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n return obj;\n };\n }\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n const nativeApplyConstraints =\n MediaStreamTrack.prototype.applyConstraints;\n MediaStreamTrack.prototype.applyConstraints = function(c) {\n if (this.kind === 'audio' && typeof c === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c, 'autoGainControl', 'mozAutoGainControl');\n remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeApplyConstraints.apply(this, [c]);\n };\n }\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n if (!(constraints && constraints.video)) {\n const err = new DOMException('getDisplayMedia without video ' +\n 'constraints is undefined');\n err.name = 'NotFoundError';\n // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n err.code = 8;\n return Promise.reject(err);\n }\n if (constraints.video === true) {\n constraints.video = {mediaSource: preferredMediaSource};\n } else {\n constraints.video.mediaSource = preferredMediaSource;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCTrackEvent &&\n ('receiver' in window.RTCTrackEvent.prototype) &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (typeof window !== 'object' ||\n !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n return; // probably media.peerconnection.enabled=false in about:config\n }\n if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.mozRTCPeerConnection;\n }\n\n if (browserDetails.version < 53) {\n // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n\n const modernStatsTypes = {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n };\n\n const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n return nativeGetStats.apply(this, [selector || null])\n .then(stats => {\n if (browserDetails.version < 53 && !onSucc) {\n // Shim only promise getStats with spec-hyphens in type names\n // Leave callback version alone; misc old uses of forEach before Map\n try {\n stats.forEach(stat => {\n stat.type = modernStatsTypes[stat.type] || stat.type;\n });\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n stats.forEach((stat, i) => {\n stats.set(i, Object.assign({}, stat, {\n type: modernStatsTypes[stat.type] || stat.type\n }));\n });\n }\n }\n return stats;\n })\n .then(onSucc, onErr);\n };\n}\n\nexport function shimSenderGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n return;\n }\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n return this.track ? this._pc.getStats(this.track) :\n Promise.resolve(new Map());\n };\n}\n\nexport function shimReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n return;\n }\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n return this._pc.getStats(this.track);\n };\n}\n\nexport function shimRemoveStream(window) {\n if (!window.RTCPeerConnection ||\n 'removeStream' in window.RTCPeerConnection.prototype) {\n return;\n }\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n utils.deprecated('removeStream', 'removeTrack');\n this.getSenders().forEach(sender => {\n if (sender.track && stream.getTracks().includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n}\n\nexport function shimRTCDataChannel(window) {\n // rename DataChannel to RTCDataChannel (native fix in FF60):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n if (window.DataChannel && !window.RTCDataChannel) {\n window.RTCDataChannel = window.DataChannel;\n }\n}\n\nexport function shimAddTransceiver(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n if (origAddTransceiver) {\n window.RTCPeerConnection.prototype.addTransceiver =\n function addTransceiver() {\n this.setParametersPromises = [];\n const initParameters = arguments[1];\n const shouldPerformCheck = initParameters &&\n 'sendEncodings' in initParameters;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n initParameters.sendEncodings.forEach((encodingParam) => {\n if ('rid' in encodingParam) {\n const ridRegex = /^[a-z0-9]{0,16}$/i;\n if (!ridRegex.test(encodingParam.rid)) {\n throw new TypeError('Invalid RID value provided.');\n }\n }\n if ('scaleResolutionDownBy' in encodingParam) {\n if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n throw new RangeError('scale_resolution_down_by must be >= 1.0');\n }\n }\n if ('maxFramerate' in encodingParam) {\n if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n throw new RangeError('max_framerate must be >= 0.0');\n }\n }\n });\n }\n const transceiver = origAddTransceiver.apply(this, arguments);\n if (shouldPerformCheck) {\n // Check if the init options were applied. If not we do this in an\n // asynchronous way and save the promise reference in a global object.\n // This is an ugly hack, but at the same time is way more robust than\n // checking the sender parameters before and after the createOffer\n // Also note that after the createoffer we are not 100% sure that\n // the params were asynchronously applied so we might miss the\n // opportunity to recreate offer.\n const {sender} = transceiver;\n const params = sender.getParameters();\n if (!('encodings' in params) ||\n // Avoid being fooled by patched getParameters() below.\n (params.encodings.length === 1 &&\n Object.keys(params.encodings[0]).length === 0)) {\n params.encodings = initParameters.sendEncodings;\n sender.sendEncodings = initParameters.sendEncodings;\n this.setParametersPromises.push(sender.setParameters(params)\n .then(() => {\n delete sender.sendEncodings;\n }).catch(() => {\n delete sender.sendEncodings;\n })\n );\n }\n }\n return transceiver;\n };\n }\n}\n\nexport function shimGetParameters(window) {\n if (!(typeof window === 'object' && window.RTCRtpSender)) {\n return;\n }\n const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n if (origGetParameters) {\n window.RTCRtpSender.prototype.getParameters =\n function getParameters() {\n const params = origGetParameters.apply(this, arguments);\n if (!('encodings' in params)) {\n params.encodings = [].concat(this.sendEncodings || [{}]);\n }\n return params;\n };\n }\n}\n\nexport function shimCreateOffer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateOffer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimCreateAnswer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateAnswer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateAnswer.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n return this._localStreams;\n };\n }\n if (!('addStream' in window.RTCPeerConnection.prototype)) {\n const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n // Try to emulate Chrome's behaviour of adding in audio-video order.\n // Safari orders by track id.\n stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n stream));\n stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n stream));\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, ...streams) {\n if (streams) {\n streams.forEach((stream) => {\n if (!this._localStreams) {\n this._localStreams = [stream];\n } else if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n });\n }\n return _addTrack.apply(this, arguments);\n };\n }\n if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n const index = this._localStreams.indexOf(stream);\n if (index === -1) {\n return;\n }\n this._localStreams.splice(index, 1);\n const tracks = stream.getTracks();\n this.getSenders().forEach(sender => {\n if (tracks.includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getRemoteStreams =\n function getRemoteStreams() {\n return this._remoteStreams ? this._remoteStreams : [];\n };\n }\n if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n get() {\n return this._onaddstream;\n },\n set(f) {\n if (this._onaddstream) {\n this.removeEventListener('addstream', this._onaddstream);\n this.removeEventListener('track', this._onaddstreampoly);\n }\n this.addEventListener('addstream', this._onaddstream = f);\n this.addEventListener('track', this._onaddstreampoly = (e) => {\n e.streams.forEach(stream => {\n if (!this._remoteStreams) {\n this._remoteStreams = [];\n }\n if (this._remoteStreams.includes(stream)) {\n return;\n }\n this._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n this.dispatchEvent(event);\n });\n });\n }\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n const pc = this;\n if (!this._onaddstreampoly) {\n this.addEventListener('track', this._onaddstreampoly = function(e) {\n e.streams.forEach(stream => {\n if (!pc._remoteStreams) {\n pc._remoteStreams = [];\n }\n if (pc._remoteStreams.indexOf(stream) >= 0) {\n return;\n }\n pc._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n pc.dispatchEvent(event);\n });\n });\n }\n return origSetRemoteDescription.apply(pc, arguments);\n };\n }\n}\n\nexport function shimCallbacksAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n const prototype = window.RTCPeerConnection.prototype;\n const origCreateOffer = prototype.createOffer;\n const origCreateAnswer = prototype.createAnswer;\n const setLocalDescription = prototype.setLocalDescription;\n const setRemoteDescription = prototype.setRemoteDescription;\n const addIceCandidate = prototype.addIceCandidate;\n\n prototype.createOffer =\n function createOffer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateOffer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n prototype.createAnswer =\n function createAnswer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateAnswer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n let withCallback = function(description, successCallback, failureCallback) {\n const promise = setLocalDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setLocalDescription = withCallback;\n\n withCallback = function(description, successCallback, failureCallback) {\n const promise = setRemoteDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setRemoteDescription = withCallback;\n\n withCallback = function(candidate, successCallback, failureCallback) {\n const promise = addIceCandidate.apply(this, [candidate]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n // shim not needed in Safari 12.1\n const mediaDevices = navigator.mediaDevices;\n const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n navigator.mediaDevices.getUserMedia = (constraints) => {\n return _getUserMedia(shimConstraints(constraints));\n };\n }\n\n if (!navigator.getUserMedia && navigator.mediaDevices &&\n navigator.mediaDevices.getUserMedia) {\n navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(cb, errcb);\n }.bind(navigator);\n }\n}\n\nexport function shimConstraints(constraints) {\n if (constraints && constraints.video !== undefined) {\n return Object.assign({},\n constraints,\n {video: utils.compactObject(constraints.video)}\n );\n }\n\n return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n const OrigPeerConnection = window.RTCPeerConnection;\n window.RTCPeerConnection =\n function RTCPeerConnection(pcConfig, pcConstraints) {\n if (pcConfig && pcConfig.iceServers) {\n const newIceServers = [];\n for (let i = 0; i < pcConfig.iceServers.length; i++) {\n let server = pcConfig.iceServers[i];\n if (!server.hasOwnProperty('urls') &&\n server.hasOwnProperty('url')) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n server = JSON.parse(JSON.stringify(server));\n server.urls = server.url;\n delete server.url;\n newIceServers.push(server);\n } else {\n newIceServers.push(pcConfig.iceServers[i]);\n }\n }\n pcConfig.iceServers = newIceServers;\n }\n return new OrigPeerConnection(pcConfig, pcConstraints);\n };\n window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n // wrap static methods. Currently just generateCertificate.\n if ('generateCertificate' in OrigPeerConnection) {\n Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n get() {\n return OrigPeerConnection.generateCertificate;\n }\n });\n }\n}\n\nexport function shimTrackEventTransceiver(window) {\n // Add event.transceiver member over deprecated event.receiver\n if (typeof window === 'object' && window.RTCTrackEvent &&\n 'receiver' in window.RTCTrackEvent.prototype &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimCreateOfferLegacy(window) {\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer =\n function createOffer(offerOptions) {\n if (offerOptions) {\n if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveAudio =\n !!offerOptions.offerToReceiveAudio;\n }\n const audioTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'audio');\n if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n if (audioTransceiver.direction === 'sendrecv') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('sendonly');\n } else {\n audioTransceiver.direction = 'sendonly';\n }\n } else if (audioTransceiver.direction === 'recvonly') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('inactive');\n } else {\n audioTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveAudio === true &&\n !audioTransceiver) {\n this.addTransceiver('audio', {direction: 'recvonly'});\n }\n\n if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveVideo =\n !!offerOptions.offerToReceiveVideo;\n }\n const videoTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'video');\n if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n if (videoTransceiver.direction === 'sendrecv') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('sendonly');\n } else {\n videoTransceiver.direction = 'sendonly';\n }\n } else if (videoTransceiver.direction === 'recvonly') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('inactive');\n } else {\n videoTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveVideo === true &&\n !videoTransceiver) {\n this.addTransceiver('video', {direction: 'recvonly'});\n }\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimAudioContext(window) {\n if (typeof window !== 'object' || window.AudioContext) {\n return;\n }\n window.AudioContext = window.webkitAudioContext;\n}\n\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n // foundation is arbitrarily chosen as an indicator for full support for\n // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n const NativeRTCIceCandidate = window.RTCIceCandidate;\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n // Remove the a= which shouldn't be part of the candidate string.\n if (typeof args === 'object' && args.candidate &&\n args.candidate.indexOf('a=') === 0) {\n args = JSON.parse(JSON.stringify(args));\n args.candidate = args.candidate.substr(2);\n }\n\n if (args.candidate && args.candidate.length) {\n // Augment the native candidate with the parsed fields.\n const nativeCandidate = new NativeRTCIceCandidate(args);\n const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n const augmentedCandidate = Object.assign(nativeCandidate,\n parsedCandidate);\n\n // Add a serializer that does not serialize the extra attributes.\n augmentedCandidate.toJSON = function toJSON() {\n return {\n candidate: augmentedCandidate.candidate,\n sdpMid: augmentedCandidate.sdpMid,\n sdpMLineIndex: augmentedCandidate.sdpMLineIndex,\n usernameFragment: augmentedCandidate.usernameFragment,\n };\n };\n return augmentedCandidate;\n }\n return new NativeRTCIceCandidate(args);\n };\n window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n Object.defineProperty(e, 'candidate', {\n value: new window.RTCIceCandidate(e.candidate),\n writable: 'false'\n });\n }\n return e;\n });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n if (!('sctp' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n }\n });\n }\n\n const sctpInDescription = function(description) {\n if (!description || !description.sdp) {\n return false;\n }\n const sections = SDPUtils.splitSections(description.sdp);\n sections.shift();\n return sections.some(mediaSection => {\n const mLine = SDPUtils.parseMLine(mediaSection);\n return mLine && mLine.kind === 'application'\n && mLine.protocol.indexOf('SCTP') !== -1;\n });\n };\n\n const getRemoteFirefoxVersion = function(description) {\n // TODO: Is there a better solution for detecting Firefox?\n const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n if (match === null || match.length < 2) {\n return -1;\n }\n const version = parseInt(match[1], 10);\n // Test for NaN (yes, this is ugly)\n return version !== version ? -1 : version;\n };\n\n const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n // Every implementation we know can send at least 64 KiB.\n // Note: Although Chrome is technically able to send up to 256 KiB, the\n // data does not reach the other peer reliably.\n // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n let canSendMaxMessageSize = 65536;\n if (browserDetails.browser === 'firefox') {\n if (browserDetails.version < 57) {\n if (remoteIsFirefox === -1) {\n // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n // fragmentation.\n canSendMaxMessageSize = 16384;\n } else {\n // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n // messages. Thus, supporting ~2 GiB when sending.\n canSendMaxMessageSize = 2147483637;\n }\n } else if (browserDetails.version < 60) {\n // Currently, all FF >= 57 will reset the remote maximum message size\n // to the default value when a data channel is created at a later\n // stage. :(\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n canSendMaxMessageSize =\n browserDetails.version === 57 ? 65535 : 65536;\n } else {\n // FF >= 60 supports sending ~2 GiB\n canSendMaxMessageSize = 2147483637;\n }\n }\n return canSendMaxMessageSize;\n };\n\n const getMaxMessageSize = function(description, remoteIsFirefox) {\n // Note: 65536 bytes is the default value from the SDP spec. Also,\n // every implementation we know supports receiving 65536 bytes.\n let maxMessageSize = 65536;\n\n // FF 57 has a slightly incorrect default remote max message size, so\n // we need to adjust it here to avoid a failure when sending.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n if (browserDetails.browser === 'firefox'\n && browserDetails.version === 57) {\n maxMessageSize = 65535;\n }\n\n const match = SDPUtils.matchPrefix(description.sdp,\n 'a=max-message-size:');\n if (match.length > 0) {\n maxMessageSize = parseInt(match[0].substr(19), 10);\n } else if (browserDetails.browser === 'firefox' &&\n remoteIsFirefox !== -1) {\n // If the maximum message size is not present in the remote SDP and\n // both local and remote are Firefox, the remote peer can receive\n // ~2 GiB.\n maxMessageSize = 2147483637;\n }\n return maxMessageSize;\n };\n\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n this._sctp = null;\n // Chrome decided to not expose .sctp in plan-b mode.\n // As usual, adapter.js has to do an 'ugly worakaround'\n // to cover up the mess.\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n const {sdpSemantics} = this.getConfiguration();\n if (sdpSemantics === 'plan-b') {\n Object.defineProperty(this, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n if (sctpInDescription(arguments[0])) {\n // Check if the remote is FF.\n const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n // Get the maximum message size the local peer is capable of sending\n const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n // Get the maximum message size of the remote peer.\n const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n // Determine final maximum message size\n let maxMessageSize;\n if (canSendMMS === 0 && remoteMMS === 0) {\n maxMessageSize = Number.POSITIVE_INFINITY;\n } else if (canSendMMS === 0 || remoteMMS === 0) {\n maxMessageSize = Math.max(canSendMMS, remoteMMS);\n } else {\n maxMessageSize = Math.min(canSendMMS, remoteMMS);\n }\n\n // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n // attribute.\n const sctp = {};\n Object.defineProperty(sctp, 'maxMessageSize', {\n get() {\n return maxMessageSize;\n }\n });\n this._sctp = sctp;\n }\n\n return origSetRemoteDescription.apply(this, arguments);\n };\n}\n\nexport function shimSendThrowTypeError(window) {\n if (!(window.RTCPeerConnection &&\n 'createDataChannel' in window.RTCPeerConnection.prototype)) {\n return;\n }\n\n // Note: Although Firefox >= 57 has a native implementation, the maximum\n // message size can be reset for all data channels at a later stage.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n function wrapDcSend(dc, pc) {\n const origDataChannelSend = dc.send;\n dc.send = function send() {\n const data = arguments[0];\n const length = data.length || data.size || data.byteLength;\n if (dc.readyState === 'open' &&\n pc.sctp && length > pc.sctp.maxMessageSize) {\n throw new TypeError('Message too large (can send a maximum of ' +\n pc.sctp.maxMessageSize + ' bytes)');\n }\n return origDataChannelSend.apply(dc, arguments);\n };\n }\n const origCreateDataChannel =\n window.RTCPeerConnection.prototype.createDataChannel;\n window.RTCPeerConnection.prototype.createDataChannel =\n function createDataChannel() {\n const dataChannel = origCreateDataChannel.apply(this, arguments);\n wrapDcSend(dataChannel, this);\n return dataChannel;\n };\n utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n wrapDcSend(e.channel, e.target);\n return e;\n });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n if (!window.RTCPeerConnection ||\n 'connectionState' in window.RTCPeerConnection.prototype) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n Object.defineProperty(proto, 'connectionState', {\n get() {\n return {\n completed: 'connected',\n checking: 'connecting'\n }[this.iceConnectionState] || this.iceConnectionState;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(proto, 'onconnectionstatechange', {\n get() {\n return this._onconnectionstatechange || null;\n },\n set(cb) {\n if (this._onconnectionstatechange) {\n this.removeEventListener('connectionstatechange',\n this._onconnectionstatechange);\n delete this._onconnectionstatechange;\n }\n if (cb) {\n this.addEventListener('connectionstatechange',\n this._onconnectionstatechange = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n\n ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n const origMethod = proto[method];\n proto[method] = function() {\n if (!this._connectionstatechangepoly) {\n this._connectionstatechangepoly = e => {\n const pc = e.target;\n if (pc._lastConnectionState !== pc.connectionState) {\n pc._lastConnectionState = pc.connectionState;\n const newEvent = new Event('connectionstatechange', e);\n pc.dispatchEvent(newEvent);\n }\n return e;\n };\n this.addEventListener('iceconnectionstatechange',\n this._connectionstatechangepoly);\n }\n return origMethod.apply(this, arguments);\n };\n });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n if (!window.RTCPeerConnection) {\n return;\n }\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n return;\n }\n if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n return;\n }\n const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription(desc) {\n if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n const sdp = desc.sdp.split('\\n').filter((line) => {\n return line.trim() !== 'a=extmap-allow-mixed';\n }).join('\\n');\n // Safari enforces read-only-ness of RTCSessionDescription fields.\n if (window.RTCSessionDescription &&\n desc instanceof window.RTCSessionDescription) {\n arguments[0] = new window.RTCSessionDescription({\n type: desc.type,\n sdp,\n });\n } else {\n desc.sdp = sdp;\n }\n }\n return nativeSRD.apply(this, arguments);\n };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n // Support for addIceCandidate(null or undefined)\n // as well as addIceCandidate({candidate: \"\", ...})\n // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n // Note: must be called before other polyfills which change the signature.\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeAddIceCandidate =\n window.RTCPeerConnection.prototype.addIceCandidate;\n if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.addIceCandidate =\n function addIceCandidate() {\n if (!arguments[0]) {\n if (arguments[1]) {\n arguments[1].apply(null);\n }\n return Promise.resolve();\n }\n // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n // in older versions.\n // Native support for ignoring exists for Chrome M77+.\n // Safari ignores as well, exact version unknown but works in the same\n // version that also ignores addIceCandidate(null).\n if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n || (browserDetails.browser === 'firefox'\n && browserDetails.version < 68)\n || (browserDetails.browser === 'safari'))\n && arguments[0] && arguments[0].candidate === '') {\n return Promise.resolve();\n }\n return nativeAddIceCandidate.apply(this, arguments);\n };\n}\n\n// Note: Make sure to call this ahead of APIs that modify\n// setLocalDescription.length\nexport function shimParameterlessSetLocalDescription(window, browserDetails) {\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n let desc = arguments[0] || {};\n if (typeof desc !== 'object' || (desc.type && desc.sdp)) {\n return nativeSetLocalDescription.apply(this, arguments);\n }\n // The remaining steps should technically happen when SLD comes off the\n // RTCPeerConnection's operations chain (not ahead of going on it), but\n // this is too difficult to shim. Instead, this shim only covers the\n // common case where the operations chain is empty. This is imperfect, but\n // should cover many cases. Rationale: Even if we can't reduce the glare\n // window to zero on imperfect implementations, there's value in tapping\n // into the perfect negotiation pattern that several browsers support.\n desc = {type: desc.type, sdp: desc.sdp};\n if (!desc.type) {\n switch (this.signalingState) {\n case 'stable':\n case 'have-local-offer':\n case 'have-remote-pranswer':\n desc.type = 'offer';\n break;\n default:\n desc.type = 'answer';\n break;\n }\n }\n if (desc.sdp || (desc.type !== 'offer' && desc.type !== 'answer')) {\n return nativeSetLocalDescription.apply(this, [desc]);\n }\n const func = desc.type === 'offer' ? this.createOffer : this.createAnswer;\n return func.apply(this)\n .then(d => nativeSetLocalDescription.apply(this, [d]));\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\nimport * as utils from './utils';\n\n // Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\nimport * as sdp from 'sdp';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n shimChrome: true,\n shimFirefox: true,\n shimSafari: true,\n}) {\n // Utils.\n const logging = utils.log;\n const browserDetails = utils.detectBrowser(window);\n\n const adapter = {\n browserDetails,\n commonShim,\n extractVersion: utils.extractVersion,\n disableLog: utils.disableLog,\n disableWarnings: utils.disableWarnings,\n // Expose sdp as a convenience. For production apps include directly.\n sdp,\n };\n\n // Shim browser if found.\n switch (browserDetails.browser) {\n case 'chrome':\n if (!chromeShim || !chromeShim.shimPeerConnection ||\n !options.shimChrome) {\n logging('Chrome shim is not included in this adapter release.');\n return adapter;\n }\n if (browserDetails.version === null) {\n logging('Chrome shim can not determine version, not shimming.');\n return adapter;\n }\n logging('adapter.js shimming chrome.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = chromeShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n chromeShim.shimGetUserMedia(window, browserDetails);\n chromeShim.shimMediaStream(window, browserDetails);\n chromeShim.shimPeerConnection(window, browserDetails);\n chromeShim.shimOnTrack(window, browserDetails);\n chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n chromeShim.shimGetStats(window, browserDetails);\n chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n case 'firefox':\n if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n !options.shimFirefox) {\n logging('Firefox shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming firefox.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = firefoxShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n firefoxShim.shimGetUserMedia(window, browserDetails);\n firefoxShim.shimPeerConnection(window, browserDetails);\n firefoxShim.shimOnTrack(window, browserDetails);\n firefoxShim.shimRemoveStream(window, browserDetails);\n firefoxShim.shimSenderGetStats(window, browserDetails);\n firefoxShim.shimReceiverGetStats(window, browserDetails);\n firefoxShim.shimRTCDataChannel(window, browserDetails);\n firefoxShim.shimAddTransceiver(window, browserDetails);\n firefoxShim.shimGetParameters(window, browserDetails);\n firefoxShim.shimCreateOffer(window, browserDetails);\n firefoxShim.shimCreateAnswer(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'safari':\n if (!safariShim || !options.shimSafari) {\n logging('Safari shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming safari.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = safariShim;\n\n // Must be called before shimCallbackAPI.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n safariShim.shimRTCIceServerUrls(window, browserDetails);\n safariShim.shimCreateOfferLegacy(window, browserDetails);\n safariShim.shimCallbacksAPI(window, browserDetails);\n safariShim.shimLocalStreamsAPI(window, browserDetails);\n safariShim.shimRemoteStreamsAPI(window, browserDetails);\n safariShim.shimTrackEventTransceiver(window, browserDetails);\n safariShim.shimGetUserMedia(window, browserDetails);\n safariShim.shimAudioContext(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n default:\n logging('Unsupported browser!');\n break;\n }\n\n return adapter;\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","// This file is imported into lib/wc client bundles.\n\nif (typeof window !== 'undefined') {\n var currentScript = window.document.currentScript\n if (process.env.NEED_CURRENTSCRIPT_POLYFILL) {\n var getCurrentScript = require('@soda/get-current-script')\n currentScript = getCurrentScript()\n\n // for backward compatibility, because previously we directly included the polyfill\n if (!('currentScript' in document)) {\n Object.defineProperty(document, 'currentScript', { get: getCurrentScript })\n }\n }\n\n var src = currentScript && currentScript.src.match(/(.+\\/)[^/]+\\.js(\\?.*)?$/)\n if (src) {\n __webpack_public_path__ = src[1] // eslint-disable-line\n }\n}\n\n// Indicate to webpack that this file can be concatenated\nexport default null\n","let account = {\n loginNet: {\n //登录接口\n url: '/scooper-core-rest/data/system/authManage/loginTo',\n requestParam: {\n accUsername: '',\n accPassword: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //token\n\n }\n },\n heartBeatNet: {\n //账号心跳保持接口, 长轮询调用该接口保持与服务端的连接,每15s发送一次该消息,如果60s内没有收到该消息,服务端主动注销该用户\n url: '/scooper-core-rest/data/system/authManage/heartbeat',\n requestParam: {\n token: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n logoutNet: {\n //登出接口\n url: '/scooper-core-rest/data/system/authManage/logout',\n requestParam: {\n token: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n accountMqttNet: {\n //MQTT订阅方式获取账号连接状态改变通知 cometd\n topic: '/notify/auth/connect',\n responseParam: {\n id: '',\n //0 数据id\n type: '',\n //connectStatus \n source: '',\n //scooper-core\n data: {\n user: '',\n token: '',\n status: '' //0:退出登录 1:登录成功\n\n } //通知消息体\n\n }\n },\n accountHttpNet: {\n //账号连接状态改变HTTP推送通知\n url: '/notify/auth/connect',\n method: 'POST',\n requestParam: {\n user: '',\n type: 'connectStatus',\n status: '' //0: 退出登录 1:登录成功\n\n },\n responseParam: {\n code: ''\n }\n },\n saveAccountNet: {\n //新增帐号\n\n /**\r\n * 新增账号,该接口只支持简单的账号信息填写和关联一个部门成员,如有其它业务需要,可以通过我司的通讯录配置网页进行编辑添加\r\n */\n url: '/scooper-core-rest/data/system/accountManage/saveAccount',\n method: 'POST',\n requestParam: {\n token: '',\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型,该字段已弃用 0-管理员; 1-一般用户; 2-调度操作员\n memId: '' //通讯录成员ID (帐号关联成员)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //返回插入成功的ID\n\n }\n },\n updateAccountNet: {\n //更新帐号\n\n /**\r\n * 根据token和id更新帐号信息\r\n */\n url: '/scooper-core-rest/data/system/accountManage/updateAccount',\n method: 'POST',\n requestParam: {\n id: '',\n token: '',\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型,该字段已弃用 0-管理员; 1-一般用户; 2-调度操作员\n memId: '' //通讯录成员ID (帐号关联成员)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //返回更新成功的ID\n\n }\n },\n deleteAccountNet: {\n //删除帐号\n url: '/scooper-core-rest/data/system/accountManage/deleteAccount',\n method: 'POST',\n requestParam: {\n ids: '',\n //编号 多个以\",\"分割\n token: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n resetPwdNet: {\n //密码重置\n url: '/scooper-core-rest/data/system/accountManage/resetPwd',\n method: 'POST',\n requestParam: {\n id: '',\n //帐号标识 id / accUserName 必须选择一个参数\n token: '',\n accUsername: '',\n //用户名\n origPwd: '',\n //原密码,传递了原密码会对原密码校验,不通过则无法重置密码\n newPwd: '' //新密码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n resetSelfPwdNet: {\n //账号自身密码重置\n\n /**\r\n * 根据账号token,更改自身密码\r\n */\n url: '/data/system/accountManage/resetSelfPwd',\n method: 'POST',\n requestParam: {\n token: '',\n origPwd: '',\n //原密码,传递了原密码会对原密码校验,不通过则无法重置密码\n newPwd: '' //新密码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n listAccountNet: {\n //查询帐号列表\n url: '/scooper-core-rest/data/system/accountManage/listAccount',\n requestParam: {\n id: '',\n token: '',\n accUsername: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型,该字段已弃用 0-管理员; 1-一般用户; 2-调度操作员\n validToken: '',\n //有效token\n invalidTimeStart: '' //登录失效开始日期\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //帐号标识\n groupId: '',\n //账号组\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型 0-管理员;1-一般用户;2-调度操作员\n scopeDept: '',\n //管辖的部门,保留字段\n scopeVideo: '',\n //管辖的视频,保留字段\n validToken: '',\n //有效token\n isActive: '',\n //是否激活\n modifyTime: '',\n createTime: '',\n useSbc: '',\n //是否启用边界网关0-否|1-是\n single: '',\n //更新时是否只更新账号信息\n jkSbc: '' //捷控关联的帐号(sip帐号)\n\n }]\n }\n },\n queryAccountNet: {\n //分页查询帐号列表\n url: '/scooper-core-rest/data/system/accountManage/queryAccount',\n requestParam: {\n currentPage: '',\n token: '',\n pageSize: '',\n id: '',\n accUsername: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型,该字段已弃用 0-管理员; 1-一般用户; 2-调度操作员\n validToken: '',\n //有效token\n invalidTimeStart: '' //登录失效开始日期\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n pageNum: '',\n pageSize: '',\n //每页的数量\n size: '',\n //当前页的数量\n startRow: '',\n //当前页面第一个元素在数据库中的行号\n endRow: '',\n //当前页面最后一个元素在数据库中的行号\n total: '',\n pages: '',\n modifyPwTime: '',\n firstPage: '',\n perPage: '',\n nextPage: '',\n lastPage: '',\n isFirstPage: '',\n isLastPage: '',\n hasPreviousPage: '',\n hasNextPage: '',\n navigatePage: '',\n //导航页码数\n navigatePageNums: '',\n //所有导航页号\n list: [{\n id: '',\n //帐号标识\n groupId: '',\n //账号组\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型 0-管理员;1-一般用户;2-调度操作员\n scopeDept: '',\n //管辖的部门,保留字段\n scopeVideo: '',\n //管辖的视频,保留字段\n validToken: '',\n //有效token\n isActive: '',\n //是否激活\n modifyTime: '',\n createTime: '',\n useSbc: '',\n //是否启用边界网关0-否|1-是\n single: '',\n //更新时是否只更新账号信息\n jkSbc: '' //捷控关联的帐号(sip帐号)\n\n }]\n }\n }\n },\n getAccountLinkMemberNet: {\n //查询帐号关联部门成员信息\n\n /**\r\n * 根据id,validToken,accUsername获取账号关联部门成员信息\r\n */\n url: '/scooper-core-rest/data/system/accountManage/getAccountLinkMember',\n requestParam: {\n token: '',\n accUsername: '',\n id: '',\n validToken: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n //帐号标识\n groupId: '',\n //账号组\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型 0-管理员;1-一般用户;2-调度操作员\n scopeDept: '',\n //管辖的部门,保留字段\n scopeVideo: '',\n //管辖的视频,保留字段\n validToken: '',\n //有效token\n isActive: '',\n //是否激活\n modifyTime: '',\n createTime: '',\n useSbc: '',\n //是否启用边界网关0-否|1-是\n single: '',\n //更新时是否只更新账号信息\n jkSbc: '',\n //捷控关联的帐号(sip帐号)\n memId: '',\n //\t成员ID\n memName: '',\n //\t成员姓名\n deptId: '',\n //\t部门编号\n deptName: '',\n //\t部门名称\n orgCode: '',\n //\t组织编码\n sex: '',\n //\t性别\n memTel: '',\n //\t成员号码\n memMobile: '' //\t成员手机号\n\n }\n }\n },\n getAccountDetailNet: {\n //查询帐号详情\n\n /**\r\n * 根据id,validToken,accUsername获取账号信息\r\n */\n url: '/scooper-core-rest/data/system/accountManage/getAccountDetail',\n requestParam: {\n token: '',\n accUsername: '',\n id: '',\n validToken: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n //帐号标识\n groupId: '',\n //账号组\n accUsername: '',\n accPassword: '',\n accShowname: '',\n //帐号显示名称\n accType: '',\n //帐号类型 0-管理员;1-一般用户;2-调度操作员\n scopeDept: '',\n //管辖的部门,保留字段\n scopeVideo: '',\n //管辖的视频,保留字段\n validToken: '',\n //有效token\n isActive: '',\n //是否激活\n modifyTime: '',\n createTime: '',\n invalidTime: '',\n //失效时间\n accessTime: '',\n //登录系统时间\n useSbc: '',\n //是否启用边界网关0-否|1-是\n modifyPwdTime: '',\n //\n realDispUsername: '',\n //真实的调度帐号名\n businessConfigList: {\n //业务配置信息\n id: '',\n //业务配置信息标识\n accId: '',\n //\t帐号标识\n type: '',\n //\t帐号类型\n actived: '',\n //\t是否启用\n lastOnline: '',\n //最后在线时间(应用按固定频率更新该值)\n modifyTime: '',\n //\t最后修改时间\n ext: '' //应用附加数据(保留)\n\n },\n single: '',\n //更新时是否只更新账号信息\n jkSbc: '',\n //捷控关联帐号的sbc是否使用边界网关 0-否; 1-是\n PmDataList: {\n //数据权限列表\n accId: '',\n //\t帐号id\n deptOrgCode: '',\n //\t部门的orgCode\n isActive: '',\n //\t是否激活\n deptName: '' //\t部门名称\n\n }\n }\n }\n },\n\n /**\r\n * MQTT订阅\r\n */\n accountAddMQNet: function () {\n //新增帐号通知\n return {\n topic: '/scooper_core/dataChange/account',\n responseParam: {\n id: '',\n type: 'add',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n accountUpdateMQNet: function () {\n //更新帐号通知\n return {\n topic: '/scooper_core/dataChange/account',\n responseParam: {\n id: '',\n type: 'update',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n accountDeleteMQNet: function () {\n //删除帐号通知\n return {\n topic: '/scooper_core/dataChange/account',\n responseParam: {\n id: '',\n type: 'delete',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n\n /**\r\n * HTTP推送\r\n */\n accountAddHttpNet: function () {\n return {\n //新增帐号通知HTTP推送通知\n url: '/data/dataChange/account/add',\n method: 'POST',\n requestParam: {\n id: '' //新增账号ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n accountUpdateHttpNet: function () {\n /**\r\n * 根据token和id更新帐号信息\r\n */\n return {\n //更新帐号通知HTTP推送通知\n url: '/data/dataChange/account/update',\n method: 'POST',\n requestParam: {\n id: '' //更新账号ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n accountDeleteHttpNet: function () {\n return {\n //删除帐号通知HTTP推送通知\n url: '/data/dataChange/account/delete',\n method: 'POST',\n requestParam: {\n ids: '' //编号 多个以\",\"分割\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n }\n};\nexport default account;","let aduio = {\n callNet: {\n //语音拨话\n\n /***基本语音呼叫,呼叫单个用户。具体呼叫结果(是否摘机应答)不在调用响应结果中体现,通过推送号码状态通知判断呼叫结果\r\n (其他的语音服务接口都类似,直接的响应结果并不包含实际的操作结果,通过通知的方式反馈具体状态)**/\n url: '/dispatch-web/api/call/makeCall',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码(一般使用坐席号码)\n callee: '',\n //被叫号码\n businessId: '',\n //业务Id,用于业务操作与相关功能的关联\n isRecord: '' //是否录音 预留字段\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n answerNet: {\n //对拨入的号码进行选择性的应答\n\n /***对呼入列表中的某一号码做应答优先设置,坐席设备摘机后会自动与选择的号码建立通话**/\n url: '/dispatch-web/api/call/answer',\n requestParam: {\n token: '',\n tel: '',\n //呼入队列中的某一号码\n businessId: '' //业务Id,用于业务操作与相关功能的关联\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n holdNet: {\n //保持通话\n\n /***保持通话,对与坐席号码进行通话的号码进行保持,保持后如果通话有录音则录音结束**/\n url: '/dispatch-web/api/call/hold',\n requestParam: {\n token: '',\n tel: '' //呼入队列中的某一号码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n unholdNet: {\n //取消保持通话恢复通话\n\n /***恢复通话,可以使被保持号码通话\r\n * 主叫号码可以指定任意其他号码,非必须是上次通话的号码\r\n * 如果通话有录音,新产生录音记录\r\n **/\n url: '/dispatch-web/api/call/unhold',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码(一般使用坐席号码)\n callee: '' //被保持号码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n transferNet: {\n //通话转接,由操作员操作转接\n\n /***将正在与操作员通话的号码转接到其他号码,实现两个号码的通话连接。\r\n * 转接成功后操作员号码话机可挂机\r\n **/\n url: '/dispatch-web/api/call/transfer',\n requestParam: {\n token: '',\n from: '',\n //被转接号码(与操作员通话的号码)\n to: '',\n //转接到号码\n businessId: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n retrieveNet: {\n //取消转接\n\n /***取消到某个号码的转接动作,\r\n * 该接口只支持在转接过程中的操作,转接成功后无法取消转接\r\n **/\n url: '/dispatch-web/api/call/retrieve',\n requestParam: {\n token: '',\n tel: '' //转接到号码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n tripleBreakinNet: {\n //强插\n\n /***针对状态监测的用户(未经过调度的双方通话),可以执行\r\n 强插等操作。\r\n 如操作员A,用户B和C正在通话。\r\n A【强插】B或C,ABC进行三方通话\r\n **/\n url: '/dispatch-web/api/call/tripleBreakin',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码 (坐席号码)\n callee: '' //被叫号码,被操作对象\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n tripleHungupNet: {\n //强拆\n\n /***针对状态监测的用户(未经过调度的双方通话),可以执行强拆操作。\r\n 如操作员A,用户B和C正在通话。\r\n A【强拆】B或C,BC都被拆线\r\n **/\n url: '/dispatch-web/api/call/tripleHungup',\n requestParam: {\n token: '',\n tel: '' //被拆线号码,被操作对象\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n tripleMonitorNet: {\n //监听\n\n /***针对状态监测的用户(未经过调度的双方通话),可以执行监听操作。\r\n 如操作员A,用户B和C正在通话。\r\n A【监听】B或C,BC正常通话,A可以听到BC的语音\r\n **/\n url: '/dispatch-web/api/call/tripleHungup',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码 (坐席号码)\n callee: '' //被叫号码,被操作对象\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n groupNotifyNet: {\n //组呼通知\n\n /***1、将一组通知音播放给多个号码,通知音必须先录制。\r\n 通知音查询 请参照 查询通知音 接口。\r\n 通知音上传 请参照 通知音上传 接口,\r\n 2、或者将一段文字,转成语音播放给多个号码。\r\n 3、组呼通知结果以通知的形式告知\r\n **/\n url: '/dispatch-web/api/call/groupNotify',\n requestParam: {\n token: '',\n\n /**\r\n * 1、type != text,则通知语音文件,\r\n 如果多个文件以“,”隔开,最多支持4个录音文件。\r\n 如:5006_12059301042.au,5006_3866025493.au\r\n 2、type = text,\r\n 该字段长度不超过64个中文汉字,128字节。必须为中文。 \r\n */\n files: '',\n //\n\n /***\r\n * 多个成员号码。格式为8000,8001,8002,8003,8004。\r\n 号码数目最多16个。\r\n */\n tels: '',\n //\n\n /***\r\n * notify : 通知音录音。\r\n call_record : 通话录音。\r\n text: 文字通知(TTS 文字转语音)\r\n 默认为notify。\r\n */\n type: '',\n\n /***\r\n * 组呼通知记录ID,由客户端(第三方程序)构造,\r\n * 用于查询组呼通知结果(如不设置将无法对应查找结果)\r\n * 要求:长度不超过32位,请确保唯一。\r\n 建议用户名+时间戳或随机数。如:scooper_1522381878\r\n */\n notifyId: '',\n\n /**\r\n * 循环播放次数,默认为1次。\r\n 当为-1时,循环播放直到调【结束组呼通知】接口结束\r\n */\n times: '',\n\n /**\r\n * 通知确认音\r\n 格式:{数字}={数字代表含义};{数字}={数字代表含义},中间用英文分号分隔 \r\n 如:1=确认;2=忽视;3=拒绝\r\n 后台自动将确认文字转换成确认音,并统计回复结果,在组呼通知结果中体现\r\n */\n confirm: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n queryGroupNotifyNet: {\n //分页查询组呼通知结果\n url: '/dispatch-web/api/call/queryGroupNotifyResult',\n requestParam: {\n token: '',\n notifyId: '',\n //通知ID\n caller: '',\n //主叫,发起者用户名\n timeMin: '',\n //开始时间范围最小值(针对通知发起时间tmNotify)\n timeMax: '',\n //开始时间范围最大值(针对通知发起时间tmNotify)\n pageIndex: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n //NotifyRecord\n id: '',\n //组呼通知结果记录ID\n notifyId: '',\n //通知ID\n caller: '',\n //主叫\n callee: '',\n //被通知号码\n tmNotify: '',\n //通知时间\n tmAnswer: '',\n //通知号码应答时间\n tmHangup: '',\n //通知结束时间\n tmLen: '',\n //通知时长,单位“秒”\n notifyType: '',\n //组呼通知类型 0:通知音 2:tts\n files: '',\n //通知内容 notifyType=0 通知音文件名 notifyType=2 tts文本内容\n\n /**\r\n * 通知结果。\r\n 200:通知正常;\r\n 489:调度员手动停止;\r\n 487:被叫未听完主动挂机;\r\n 486:遇忙;\r\n 480:暂时无法接通;\r\n 408:无应答;\r\n 404:空号;\r\n 500:未知\r\n */\n result: '',\n\n /***\r\n * 组呼通知反馈结果,根据组呼通知时自定义确认音\r\n accept: 确认\r\n refuse:拒绝\r\n ignore:忽略\r\n */\n confirm: ''\n }] //记录列表,[0, n]个NotifyRecord\n\n }\n }\n },\n queryCallNet: {\n //分页查询呼叫记录\n url: '/dispatch-web/api/call/queryCallRecord',\n requestParam: {\n token: '',\n caller: '',\n //主叫\n timeMin: '',\n //开始时间最小值\n timeMax: '',\n //开始时间最大值\n pageIndex: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n //记录列表,[0, n]个CallRecord\n id: '',\n //呼入未接记录ID\n businessId: '',\n //呼叫记录业务id,可用于关联查询通话录音文件\n caller: '',\n //主叫\n callee: '',\n //被叫\n tmCall: '',\n //呼叫发起时间\n tmAnswer: '',\n //应答时间\n tmHangup: '',\n //挂机时间\n tmLen: '',\n //通话时长(单位:秒)\n\n /**\r\n * 呼叫类型\r\n meet: 会议呼叫\r\n session: 语音呼叫\r\n callout: 平台呼出 \r\n callin: 平台呼入 \r\n incallout: 调度呼出 \r\n incallin: 调度呼入\r\n */\n callType: '',\n\n /**\r\n * 呼叫结果\r\n 200:呼叫正常;\r\n 486:遇忙;\r\n 408:无应答;\r\n 404:空号;\r\n 500:未知\r\n */\n result: ''\n }]\n }\n }\n }\n};\nexport default aduio;","let aduio_video = {\n callHungupNet: {\n //拆线/挂断\n\n /***对某个通话用户拆线/挂断,使其停止通话,适用于语音和视频通话 或者 将号码踢出会场**/\n url: '/dispatch-web/api/call/hungup',\n requestParam: {\n token: '',\n tel: '' //通话号码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n listStatusNet: {\n //获取当前所有号码状态\n url: '/dispatch-web/api/call/listStatus',\n requestParam: {\n token: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n total: '',\n list: [{\n //记录列表,[0, n]个TelStatus。\n tel: '',\n //号码\n businessId: '',\n //业务id\n status: '',\n //号码状态\n recording: '',\n //是否在录音中\n handleNumber: '',\n //主叫号码\n meetId: '',\n //会议号,如在会议中该值有内容\n conNumber: '',\n //与tel通话的号码\n videoInfo: '' //通话类型 audio:语音通话, video:视频通话\n\n }]\n }\n },\n telsStatusNet: {\n //获取号码状态\n url: '/dispatch-web/api/call/telsStatus',\n requestParam: {\n token: '',\n tels: '' //查询状态的号码,用’,’隔开\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n total: '',\n list: [{\n //记录列表,[0, n]个TelStatus。\n tel: '',\n //号码\n businessId: '',\n //业务id\n status: '',\n //号码状态\n recording: '',\n //是否在录音中\n handleNumber: '',\n //主叫号码\n meetId: '',\n //会议号,如在会议中该值有内容\n conNumber: '',\n //与tel通话的号码\n videoInfo: '' //通话类型 audio:语音通话, video:视频通话\n\n }]\n }\n },\n telsStatusMQNet: function (accId) {\n return {\n //号码状态改变通知\n topic: `/${accId}/dispatch-web/notify/call/telStatus`,\n responseParam: {\n id: 0,\n //数据id\n type: 'telStatus',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n tel: '',\n //被通知状态的号码\n status: '',\n //号码状态\n handleNumber: '',\n //主叫号码 F\n conNumber: '',\n //通话号码 F\n meetId: '',\n //会议ID F\n videoInfo: '',\n //呼叫类型 audio: 语音; video:视频\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n callInMQNet: function (accId) {\n return {\n //呼入队列改变通知\n topic: `/${accId}/dispatch-web/notify/call/callIn`,\n responseParam: {\n id: 0,\n //数据id\n type: 'callIn',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n tel: '',\n //被通知状态的号码\n type: '',\n //队列改变类型 add: 新增呼入 del: 去除呼入\n time: '',\n //yyyy-MM-dd HH:mm:ss\n level: '',\n //号码等级 -1:默认 2:普通紧急电话 3:其他成员电话\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n groupNotifyMQNet: function (accId) {\n return {\n //组呼通知结果通知\n topic: `/${accId}/dispatch-web/notify/call/groupNotify`,\n responseParam: {\n id: 0,\n //数据id\n type: 'groupNotify',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n result: '',\n //操作结果\n desc: '',\n //组呼通知结果文字描述\n descJson: '',\n //组呼通知结果json格式 totalCount:组呼总数 sucCount:成功总数 sucTels:成功号码,|分隔 failTels:失败号码,|分隔 dtmf:回复统计\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n telsStatusHttpNet: {\n //号码状态改变通知Http推送方式\n url: '/notify/call/telStatus',\n method: 'POST',\n //推送当前状态改变的号码及改变后的状态信息\n requestParam: {\n tel: '',\n //号码\n status: '',\n //状态\n handleNumber: '',\n // 主叫号码\n conNumber: '',\n // 通话号码\n meetId: '',\n // 会议id\n videoInfo: '',\n // 呼叫类型 audio: 语音; video:视频\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n },\n callInHttpNet: {\n //呼入队列改变通知Http推送方式\n url: '/notify/call/callIn',\n method: 'POST',\n //推送当前呼入队列的成员改变通知\n requestParam: {\n type: '',\n //队列改变类型 add: 新增呼入 del: 去除呼入\n tel: '',\n // 被通知状态的号码\n time: '',\n //yyyy-MM-dd HH:mm:ss\n level: '',\n //等级 -1:默认 2:普通紧急电话 3:其他成员电话\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n },\n groupNotifyHttpNet: {\n //组呼通知结果通知Http推送方式\n url: '/notify/call/groupNotify',\n method: 'POST',\n //推送组呼通知结果\n requestParam: {\n result: '',\n //操作结果\n desc: '',\n //组呼通知结果文字描述\n descJson: '',\n // 组呼通知结果json格式 totalCount:组呼总数 sucCount:成功总数 sucTels:成功号码,|分隔 failTels:失败号码,|分隔 dtmf:回复统计\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n },\n videoStatusHttpNet: {\n //视频通话状态通知Http推送方式\n url: '/notify/call/video',\n method: 'POST',\n //推送视频通话状态通知\n requestParam: {\n cfgType: '',\n //视频通话状态 decoder_info:视频通话建立 stop_decoder:视频通话结束\n tel: '',\n //通知号码\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n }\n};\nexport default aduio_video;","let dept_ry = {\n saveOrgMemberNet: {\n //新增部门成员信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/saveOrgMember',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tF\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型: 0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码(memMobile和memTel2不能都为空)\n memTel2: '',\n //\tT\t号码二(memMobile和memTel2不能都为空)\n memFax: '',\n //\tF\t传真号码\n memEmail: '',\n //\tF\t电子邮箱\n dutyId: '' //\tF\t职务id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //保存成功的id\n\n }\n },\n updateOrgMemberNet: {\n //更新部门成员信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/updateOrgMember',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tF\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型: 0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码(memMobile和memTel2不能都为空)\n memTel2: '',\n //\tT\t号码二(memMobile和memTel2不能都为空)\n memFax: '',\n //\tF\t传真号码\n memEmail: '',\n //\tF\t电子邮箱\n dutyId: '',\n //\tF\t职务id\n id: '' //成员id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {} //结果内容\n\n }\n },\n deleteOrgMemberNet: {\n //删除部门成员信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/deleteOrgMember',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n ids: '' //\tT\tid 多个用“,”分隔\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {} //结果内容\n\n }\n },\n getOrgMemberDetailNet: {\n //查询部门成员详情信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/getOrgMemberDetail',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '' //\tT\t成员编号\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //T\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n } //结果内容\n\n }\n },\n queryOrgMemberNet: {\n //分页查询成员列表信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/queryOrgMember',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n currentPage: '',\n //\tF\t当前页码(默认1)\n pageSize: '',\n //\tF\t每页条数(默认30)\n deptId: '',\n //\tF\t所属部门ID\n modifyTimeStart: '',\n //\tF\t记录修改开始时间\n modifyTimeEnd: '',\n //\tF\t记录修改结束时间\n\n /**\r\n * 可以模糊匹配:\r\n 1.成员名字 2.成员号码 3.号码二~五\r\n 4.手机号码 5.传真号码\r\n */\n keyword: '',\n //\tF\t关键字\n ids: '' //\tF\t通过ids串查询列表(','分割)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n pageNum: '',\n //\tT\t当前页\n pageSize: '',\n //\tT\t每页的数量\n size: '',\n //\tT\t当前页的数量\n startRow: '',\n //\tT\t当前页面第一个元素在数据库中的行号\n endRow: '',\n //\tT\t当前页面最后一个元素在数据库中的行号\n total: '',\n //\tT\t总记录数\n pages: '',\n //\tT\t总页数\n list: [{\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //T\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n }],\n //部门成员列表, //\tT\t结果集\n modifyPwTime: '',\n //\tT\t密码修改时间\n firstPage: '',\n //\tT\t第一页\n perPage: '',\n //\tT\t前一页\n nextPage: '',\n //\tT\t下一页\n lastPage: '',\n //\tT\t最后一页\n isFirstPage: '',\n //\tT\t是否为第一页\n isLastPage: '',\n //\tT\t是否为最后一页\n hasPreviousPage: '',\n //\tT\t是否有前一页\n hasNextPage: '',\n //\tT\t是否有下一页\n navigatePage: '',\n //\tT\t导航页码数\n navigatePageNums: '' //\tT\t所有导航页号\n\n }\n }\n },\n listOrgMemberNet: {\n //查询部门成员列表信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/listOrgMember',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n deptId: '',\n //\tF\t所属部门ID\n\n /**\r\n * 可以模糊匹配:\r\n 1.成员名字 2.成员号码 3.号码二~五\r\n 4.手机号码 5.传真号码\r\n */\n keyword: '',\n //\tF\t关键字\n ids: '' //\tF\t通过ids串查询列表(','分割)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //T\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n }] //部门成员列表, //\tT\t结果集\n\n }\n },\n findOrgMemberByTelNet: {\n //按号码搜索成员\n url: '/scooper-core-rest/data/contacts/orgMemberManage/findOrgMemberByTel',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n tel: '',\n //\tT\t号码\n all: '',\n //\tF\ttrue:查找所有可能的号码 fasle-主号码\n type: '' //\tF\t如果all = false, 则必填type参数 0: 号码二(默认) 1: 手机号码 2: 传真号码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //T\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n }] //部门成员列表, //\tT\t结果集\n\n }\n },\n findOrgMemberPathNet: {\n //查询某部门成员在树形结构中的路径\n\n /**\r\n * 给定ID获取从公司到该部门成员经过的所有单位/部门/成员的id\r\n */\n url: '/scooper-core-rest/data/contacts/orgMemberManage/findOrgMemberPath',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '' //\tT\t部门成员ID\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //ids串(\",\"分隔)\n\n }\n },\n findAccountByOrgMemberIdNet: {\n //根据部门成员ID获取关联账号\n\n /**\r\n * 账号生成时可以关联一个部门成员,该接口提供部门成员反向获取账号信息\r\n */\n url: '/scooper-core-rest/data/contacts/orgMemberManage/findAccountByOrgMemberId',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n orgMemberId: '' //\tT\t部门成员ID\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n accId: '',\n //\tT\t帐号ID\n memId: '',\n //\tT\t部门成员ID\n memName: '' //\tT\t部门成员名\n\n }\n }\n },\n changeOrgMemberSortIndexNet: {\n //修改部门成员排序\n url: '/scooper-core-rest/data/contacts/orgMemberManage/changeOrgMemberSortIndex',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '',\n //\tint\tT\tid\n sortIndex: '',\n //\tint\tT\t新排序号\n oldSortIndex: '' //\tint\tF\t旧排序号\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n listOrgMemberByIdsNet: {\n //查询成员ids的成员列表信息\n url: '/scooper-core-rest/data/contacts/orgMemberManage/listOrgMemberByIds',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n ids: '' //\tT\t通过ids串查询列表(','分割)(最多100个id)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号, 供第三方关联使用\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别: 1-男; 2-女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //T\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n }] //部门成员列表, //\tT\t结果集\n\n }\n },\n\n /**\r\n * MQTT订阅\r\n */\n orgMemberAddMQNet: function () {\n //新增部门成员信息通知\n return {\n topic: '/scooper_core/dataChange/orgMember',\n responseParam: {\n id: '',\n type: 'add',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n orgMemberUpdateMQNet: function () {\n //更新部门成员信息通知\n return {\n topic: '/scooper_core/dataChange/orgMember',\n responseParam: {\n id: '',\n type: 'update',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n orgMemberDeleteMQNet: function () {\n //删除部门成员信息通知\n return {\n topic: '/scooper_core/dataChange/orgMember',\n responseParam: {\n id: '',\n type: 'delete',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: '' //删除的id, 多个以“,”分隔\n\n }\n };\n },\n\n /**\r\n * HTTP推送\r\n */\n orgMemberAddHttpNet: function () {\n return {\n //新增部门成员信息通知HTTP推送通知\n url: '/data/dataChange/orgMember/add',\n method: 'POST',\n requestParam: {\n id: '' //新增成员ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n orgMemberUpdateHttpNet: function () {\n return {\n //更新部门成员信息通知HTTP推送通知\n url: '/data/dataChange/orgMember/update',\n method: 'POST',\n requestParam: {\n id: '' //更新部门成员ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n orgMemberDeleteHttpNet: function () {\n return {\n //删除部门成员信息通知HTTP推送通知\n url: '/data/dataChange/orgMember/delete',\n method: 'POST',\n requestParam: {\n id: '' //id 多个以“,”分隔\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n }\n /*** end */\n\n};\nexport default dept_ry;","let dept = {\n saveOrgDeptNet: {\n //新增部门信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/saveOrgDept',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n deptName: '',\n //\tT\t部门名称\n parentId: '',\n //\tT\t上级部门ID,一级部门(单位) = 0\n deptType: '',\n //\tT\t部门类别:1:单位(支持多级单位); 2:部门\n areaCode: '',\n //\tF\t行政区域码,获取通过查询行政区域列表接口 如果deptType=1,则必填\n shortName: '',\n //\tF\t部门简称\n corpTel: '',\n //\tF\t部门\n corpAddr: '',\n //\tF\t部门地址\n sortIndex: '' //\tF\t部门排序, 默认0\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //新增部门id\n\n }\n },\n updateOrgDeptNet: {\n //更新部门信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/updateOrgDept',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '',\n //部门id\n deptName: '',\n //\tT\t部门名称\n parentId: '',\n //\tT\t上级部门ID,一级部门(单位) = 0\n deptType: '',\n //\tT\t部门类别:1:单位(支持多级单位); 2:部门\n areaCode: '',\n //\tF\t行政区域码,获取通过查询行政区域列表接口 如果deptType=1,则必填\n shortName: '',\n //\tF\t部门简称\n corpTel: '',\n //\tF\t部门\n corpAddr: '',\n //\tF\t部门地址\n sortIndex: '' //\tF\t部门排序, 默认0\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //结果内容\n\n }\n },\n deleteOrgDeptNet: {\n //删除部门信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/deleteOrgDept',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n ids: '' //id 多个以“,”分隔\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //结果内容\n\n }\n },\n getOrgDeptDetailNet: {\n //查询部门详情信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/getOrgDeptDetail',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '' //部门编号\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n //\tT\t\n parentId: '',\n //\tT\t上级部门ID\n deptName: '',\n //\tT\t部门名称\n firstLetter: '',\n //\tT\t部门名称拼音首字母\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //\tT\t排序\n pathName: '',\n //\tT\t部门全称\n syncKey: '',\n //\tT\t同步KEY,该字段在和其他系统对接时使用\n deptDesc: '',\n //\tT\t描述\n deptExt: '',\n //\tT\t扩展字段, 建议使用json格式\n isActive: '',\n //\tT\t是否激活: 0-否; 1-是\n modifyTime: '',\n //\tT\t记录修改时间\n createTime: '',\n //\tT\t记录创建时间\n parentDeptName: '',\n //\tT\t父部门名称\n deptType: '' //\tT\t部门类型: 1-公司; 2-部门\n\n }\n }\n },\n queryOrgDeptNet: {\n //分页查询部门列表信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/queryOrgDept',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n parentId: '',\n //\tF\t上级部门ID\n deptName: '',\n //\tF\t部门名称\n firstLetter: '',\n //\tF\t首字母\n orgCode: '',\n //\tF\t组织编码\n currentPage: '',\n //\tF\t当前页码\n pageSize: '',\n //\tF\t每页条数\n modifyTimeStart: '',\n //\tF\t记录修改开始时间\n modifyTimeEnd: '' //\tF\t记录修改结束时间\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n pageNum: '',\n //\t当前页\n pageSize: '',\n //\t每页的数量\n size: '',\n //\t当前页的数量\n startRow: '',\n //\t当前页面第一个元素在数据库中的行号\n endRow: '',\n //\t当前页面最后一个元素在数据库中的行号\n total: '',\n //总记录数\n pages: '',\n //总页数\n list: [{\n id: '',\n //\tT\t\n parentId: '',\n //\tT\t上级部门ID\n deptName: '',\n //\tT\t部门名称\n firstLetter: '',\n //\tT\t部门名称拼音首字母\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //\tT\t排序\n pathName: '',\n //\tT\t部门全称\n syncKey: '',\n //\tT\t同步KEY,该字段在和其他系统对接时使用\n deptDesc: '',\n //\tT\t描述\n deptExt: '',\n //\tT\t扩展字段, 建议使用json格式\n isActive: '',\n //\tT\t是否激活: 0-否; 1-是\n modifyTime: '',\n //\tT\t记录修改时间\n createTime: '',\n //\tT\t记录创建时间\n parentDeptName: '',\n //\tT\t父部门名称\n corpTel: '',\n //\t公司联系方式\n corpAddr: '',\n //\t公司地址\n shortName: '',\n //\t公司层的部门简称\n deptType: '' //\tT\t部门类型: 1-公司; 2-部门\n\n }],\n //\t结果集\n modifyPwTime: '',\n //密码修改时间\n firstPage: '',\n //\t第一页\n perPage: '',\n //\t前一页\n nextPage: '',\n //\t下一页\n lastPage: '',\n //\t最后一页\n isFirstPage: '',\n //\t是否为第一页\n isLastPage: '',\n //\t是否为最后一页\n hasPreviousPage: '',\n //\t是否有前一页\n hasNextPage: '',\n //\t是否有下一页\n navigatePage: '',\n //\t导航页码数\n navigatePageNums: '' //\t所有导航页号\n\n }\n }\n },\n listOrgDeptNet: {\n //查询部门列表信息\n url: '/scooper-core-rest/data/contacts/orgDeptManage/listOrgDept',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n parentId: '',\n //\tF\t上级部门ID\n orgCode: '',\n //\tF\t组织编码\n deptType: '',\n //\tF\t部门类型,默认为2 1、一级部门(单位) 2、二级及以下部门 3、所有部门全查\n modifyTimeStart: '',\n //\tF\t记录修改开始时间\n modifyTimeEnd: '' //\tF\t记录修改结束时间\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t\n parentId: '',\n //\tT\t上级部门ID\n deptName: '',\n //\tT\t部门名称\n firstLetter: '',\n //\tT\t部门名称拼音首字母\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //\tT\t排序\n pathName: '',\n //\tT\t部门全称\n syncKey: '',\n //\tT\t同步KEY,该字段在和其他系统对接时使用\n deptDesc: '',\n //\tT\t描述\n deptExt: '',\n //\tT\t扩展字段, 建议使用json格式\n isActive: '',\n //\tT\t是否激活: 0-否; 1-是\n modifyTime: '',\n //\tT\t记录修改时间\n createTime: '',\n //\tT\t记录创建时间\n parentDeptName: '',\n //\tT\t父部门名称\n corpId: '',\n //\tT\t公司联系方式\n deptType: '' //\tT\t部门类型: 1-公司; 2-部门\n\n }]\n }\n },\n listDeptByParentNet: {\n //按层级加载部门(树型结构)\n url: '/scooper-core-rest/data/contacts/orgDeptManage/listDeptByParent',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n orgCorpId: '',\n //\tF\t公司编号\n id: '',\n //\tF\t上级部门编号ID (默认为0)\n firstExpand: '',\n //\tF\t是否为第一次加载 true/fasle\n expandMember: '' //\tF\t是否加载其下部门成员信息 true/fasle\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t节点ID\n name: '',\n //\tT\t节点名称\n childNum: '',\n //\tT\t子节点数量\n dataType: '',\n //\tT\t数据类型\n data: '',\n //\t自定义数据\n isParent: '',\n //\t是否为父节点\n pid: '' //\tT\t父节点ID\n\n }]\n }\n },\n findDepartmentPathNet: {\n //查询某部门在树形结构中的路径\n url: '/scooper-core-rest/data/contacts/orgDeptManage/findDepartmentPath',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '' //\tT\t部门编号ID \n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t\n parentId: '',\n //\tT\t上级部门ID\n deptName: '',\n //\tT\t部门名称\n firstLetter: '',\n //\tT\t部门名称拼音首字母\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //\tT\t排序\n pathName: '',\n //\tT\t部门全称\n syncKey: '',\n //\tT\t同步KEY,该字段在和其他系统对接时使用\n deptDesc: '',\n //\tT\t描述\n deptExt: '',\n //\tT\t扩展字段, 建议使用json格式\n isActive: '',\n //\tT\t是否激活: 0-否; 1-是\n modifyTime: '',\n //\tT\t记录修改时间\n createTime: '',\n //\tT\t记录创建时间\n parentDeptName: '',\n //\tT\t父部门名称\n corpId: '',\n //\tT\t公司联系方式\n deptType: '' //\tT\t部门类型: 1-公司; 2-部门\n\n }]\n }\n },\n listTreeDeptByParentNet: {\n //层级加载部门树结构\n\n /**\r\n * 从一部部门(单位)开始加载,即id=0开始加载可加载完整的通讯录数据(部门&成员)\r\n */\n url: '/scooper-core-rest/data/contacts/orgDeptManage/listTreeDeptByParent',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '',\n //\tT\t部门编号ID \n orgCorpId: '',\n //\tF\t单位id\n firstExpand: '',\n //\tF\t是否为第一次加载 true/fasle\n expandMember: '' //\tF\t是否加载其下部门成员信息 true/fasle\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tT\t节点ID\n name: '',\n //\tT\t节点名称\n childNum: '',\n //\tT\t子节点数量\n dataType: '',\n //\tT\t数据类型 orgMember: 部门成员 orgDept: 部门\n data: '',\n //\tT\t自定义数据\n isParent: '',\n //\tT\t是否为父节点\n pid: '' //\tT\t父节点ID\n\n }]\n }\n },\n findDeptMemberCountNet: {\n //查询某部门下成员数量\n url: '/scooper-core-rest/data/contacts/orgDeptManage/findDeptMemberCount',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '',\n //\tT\t部门编号ID \n type: '' //两种模式:0:本层下所有成员数 (默认) 1:包含下级部门的所有成员数\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: '' //数量\n\n }\n },\n changeDeptSortIndexNet: {\n //修改部门排序\n url: '/scooper-core-rest/data/contacts/orgDeptManage/changeDeptSortIndex',\n method: 'POST',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '',\n //\tT\t部门编号ID \n sortIndex: '',\n //新排序号\n oldSortIndex: '' //旧排序号\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {} //结果内容\n\n }\n },\n listDeptMemberByDeotIdNet: {\n //查询某部门下成员列表\n url: '/scooper-core-rest/data/contacts/orgDeptManage/listDeptMemberByDeotId',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n id: '' //\tT\t部门编号ID \n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n //部门成员数组\n id: '',\n //\tT\t\n deptId: '',\n //\tT\t部门ID\n memCode: '',\n //\tT\t成员编号\n memName: '',\n //\tT\t成员姓名\n sex: '',\n //\tT\t性别,1:男;2:女\n firstLetter: '',\n //\tT\t姓名拼音首字母\n memTel: '',\n //\tT\t成员号码\n memType: '',\n //\tT\t成员类型:0-一般用户;1-视频话机用户;2-移动终端用户;\n memMobile: '',\n //\tT\t手机号码\n memTel2: '',\n //\tT\t号码二\n memTel3: '',\n //\tT\t号码三\n memTel4: '',\n //\tT\t号码四\n memTel5: '',\n //\tT\t号码五\n memFax: '',\n //\tT\t传真号码\n memEmail: '',\n //\tT\t电子邮箱\n memPicture: '',\n //\tT\t图片\n memVideo: '',\n //\tT\t视频\n memGis: '',\n //\tT\tGI坐标\n orgCode: '',\n //\tT\t组织编码\n sortIndex: '',\n //\tT\t排序号\n updateTime: '',\n //\tT\t更新时间\n deptExt: '',\n //\tT\t扩展字段\n isActive: '',\n //\tT\t是否激活\n modifyTime: '',\n //\tT\t记录修改时间\n memLevel: '',\n //\tT\t成员等级\n memQq: '',\n //\tT\tQQ号码\n deptName: '' //\tT\t部门名称\n\n }]\n }\n },\n listAdCodeNet: {\n //查询行政区域列表信息\n url: '/scooper-core-rest/data/contacts/orgCorpManage/listAdCode',\n requestParam: {\n token: '',\n //\tT\t身份令牌\n idareaCodeParent: '' //\tF\t上级行政区划代码\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n //行政区域列表\n id: '',\n //\tT\t\n areaCode: '',\n //\tT\t行政区域代码\n areaName: '',\n //\tT\t行政区域名称\n areaCodeParent: '',\n //\tT\t上级行政区划代码\n areaLevel: '' //\tT\t地区级别:1-省;2-市;3-区县;4-镇\n\n }]\n }\n },\n\n /**\r\n * MQTT订阅\r\n */\n orgDeptAddMQNet: function () {\n //新增部门信息通知\n return {\n topic: '/scooper_core/dataChange/orgDept',\n responseParam: {\n id: '',\n type: 'add',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n orgDeptUpdateMQNet: function () {\n //更新部门信息通知\n return {\n topic: '/scooper_core/dataChange/orgDept',\n responseParam: {\n id: '',\n type: 'update',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: {//通知的具体消息\n }\n }\n };\n },\n orgDeptDeleteMQNet: function () {\n //删除部门信息通知\n return {\n topic: '/scooper_core/dataChange/orgDept',\n responseParam: {\n id: '',\n type: 'delete',\n //消息类别\n source: 'scooper-core-ds',\n //消息来源\n data: '' //删除的id, 多个以“,”分隔\n\n }\n };\n },\n\n /**\r\n * HTTP推送\r\n */\n orgDeptAddHttpNet: function () {\n return {\n //新增部门信息通知HTTP推送通知\n url: '/data/dataChange/orgDept/add',\n method: 'POST',\n requestParam: {\n id: '' //新增部门ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n orgDeptUpdateHttpNet: function () {\n return {\n //更新部门信息通知HTTP推送通知\n url: '/dataChange/orgDept/update',\n method: 'POST',\n requestParam: {\n id: '' //更新部门ID\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n },\n orgDeptDeleteHttpNet: function () {\n return {\n //删除部门信息通知HTTP推送通知\n url: '/dataChange/orgDept/delete',\n method: 'POST',\n requestParam: {\n id: '' //id 多个以“,”分隔\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n };\n }\n /*** end */\n\n};\nexport default dept;","let location = {\n reLocationNet: {\n //上报位置坐标\n\n /***上报人员、摄像头、定位设备的位置数据\r\n **/\n url: '/scooper-trace/data/trace/reLocation',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n requestParam: {\n token: '',\n propType: '',\n //坐标位置所属类型 0-人员,1-摄像头,2-定位设备\n propKey: '',\n //属性关联如:人员ID、摄像头ID、定位设备ID\n posLocation: '',\n //gis点位坐标对象属性(longitude,latitude)\n recordTime: '',\n //定位时间,不传默认上报时间为定位时间\n posChange: '',\n //坐标位置改变标识0-未改变,1-改变\n posAltitude: '',\n //位置海拔高度(米)\n posSpeed: '',\n //移动速度(km/h)\n posDirectionHorizontal: '',\n //方位角(水平)\n posDirectionVertical: '',\n //仰角(垂直)\n posAttr: '',\n //坐标位置属性1:室内,0:室外\n propKeyIndoor: '',\n //室内关联属性,标识在哪个室内\n coordinateType: '',\n //使用的坐标系类型(谷歌坐标系gcj02、百度坐标系bd09、wgs84)\n locType: '',\n //定位方式161 室内,61室外\n radius: '' //定位精度\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n propType: '',\n propKey: \"\",\n posLocation: {\n longitude: '',\n latitude: ''\n }\n }\n }\n },\n listTraceByArgsNet: {\n //列表查询人员轨迹\n\n /***按时间查询轨迹列表\r\n **/\n url: '/scooper-trace/data/trace/listTraceByArgs',\n method: 'GET',\n requestParam: {\n token: '',\n propType: '',\n //坐标位置所属类型 0-人员,1-摄像头,2-定位设备\n propKey: '',\n //属性关联如:人员ID、摄像头ID、定位设备ID\n startTime: '',\n endTime: '',\n useExternal: '',\n //使用第三方纠偏数据\n decrease: '',\n //轨迹数据是否抽稀\n clusterDistance: '' //抽稀距离,单位米(m)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n propType: '',\n propKey: '',\n posLocation: {\n longitude: '',\n latitude: ''\n },\n recordTime: '',\n //记录时间\n breakpoint: '',\n //是否为断点:0-否;1-是\n posAltitude: '',\n //位置海拔高度(米)\n posSpeed: '',\n //移动速度(km/h)\n posDirectionHorizontal: '',\n //方位角(水平)\n posDirectionVertical: '',\n //仰角(垂直)\n posAttr: '',\n //坐标位置属性:1:室内,0:室外\n propKeyIndoor: '',\n //室内关联属性,标识在哪个室内\n posChange: '',\n //坐标位置改变标识,0-未改变,1-改变\n updateTime: '',\n //本条记录最后更新时间\n radius: '' //定位精度\n\n }]\n }\n },\n listTraceByArgsPageNet: {\n //分页查询人员轨迹\n\n /***按时间分页查询轨迹\r\n **/\n url: '/scooper-trace/data/trace/listTraceByArgsPage',\n method: 'GET',\n requestParam: {\n token: '',\n propType: '',\n //坐标位置所属类型 0-人员,1-摄像头,2-定位设备\n propKey: '',\n //属性关联如:人员ID、摄像头ID、定位设备ID\n startTime: '',\n endTime: '',\n useExternal: '',\n //使用第三方纠偏数据\n decrease: '',\n //轨迹数据是否抽稀\n timespan: '',\n //指定跳过时间间隔,单位秒\n pageNumber: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n id: '',\n propType: '',\n propKey: '',\n posLocation: {\n longitude: '',\n latitude: ''\n },\n recordTime: '',\n //记录时间\n breakpoint: '',\n //是否为断点:0-否;1-是\n posAltitude: '',\n //位置海拔高度(米)\n posSpeed: '',\n //移动速度(km/h)\n posDirectionHorizontal: '',\n //方位角(水平)\n posDirectionVertical: '',\n //仰角(垂直)\n posAttr: '',\n //坐标位置属性:1:室内,0:室外\n propKeyIndoor: '',\n //室内关联属性,标识在哪个室内\n posChange: '',\n //坐标位置改变标识,0-未改变,1-改变\n updateTime: '',\n //本条记录最后更新时间\n radius: '' //定位精度\n\n }]\n }\n }\n },\n\n /**\r\n * MQTT订阅\r\n */\n moveMQNet: function (propType, propKey) {\n /**\r\n * {propType}为类型、{propKey}为坐标关联属性,如人员id\r\n */\n return {\n //坐标位置变动通知\n topic: `/scooper-trace/notify/move/${propType}/${propKey}`,\n responseParam: {\n id: 0,\n //轨迹服务推送队列id\n type: 'notify',\n //消息类别\n source: 'scooper-trace',\n //消息来源\n data: {\n //通知的具体消息\n id: '',\n propType: '',\n propKey: '',\n posLocation: {\n longitude: '',\n latitude: ''\n },\n posAltitude: '',\n //位置海拔高度(米)\n posSpeed: '',\n //移动速度(km/h)\n posDirectionHorizontal: '',\n //方位角(水平)\n posDirectionVertical: '',\n //仰角(垂直)\n posAttr: '',\n //坐标位置属性:1:室内,0:室外\n propKeyIndoor: '',\n //室内关联属性,标识在哪个室内\n posChange: '',\n //坐标位置改变标识,0-未改变,1-改变\n updateTime: '',\n //本条记录最后更新时间\n radius: '' //定位精度\n\n }\n }\n };\n },\n\n /**\r\n * HTTP推送\r\n */\n gisHttpNet: function () {\n return {\n //坐标位置变动通知HTTP推送通知\n url: '/notify/orgmember/gis',\n method: 'POST',\n requestParam: {\n id: '',\n //属性关联id, 如成员id等对比上报位置接口的propKey属性\n gis: '' //人员gis坐标,经纬度之间用\",\"隔开(经度放前面)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n };\n }\n};\nexport default location;","let meet = {\n selectCallNet: {\n //发起会议\n\n /***将多个号码连接一起进行多方通话(语音通话),主叫号码也会进入会议。\r\n 发起会议的结果以 通知 的方式告知\r\n **/\n url: '/dispatch-web/api/call/selectCall',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码 (一般使用坐席号码)\n callees: '',\n //多个成员号码\n type: '',\n //类型:默认为0. 【0】加入默认会场 【1】新创建会场,并将成员加入会场中。\n meetId: '' //type=1的情况下需要自定义会场id,且保证不重复。建议使用操作员名称+时间戳(如不设置,则由平台自动生成)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n endMeetNet: {\n //结束会议\n\n /***结束会议,踢出所有的与会人员\r\n **/\n url: '/dispatch-web/api/meet/endMeet',\n method: 'POST',\n requestParam: {\n token: '',\n meetId: '' //会场id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n joinVideoMemberNet: {\n //成员加入会场(视频)\n\n /***成员加入会场,具体以语音或视频的哪种方式发起呼叫由号码配置类型决定\r\n 如果坐席号码加入会场,且会场中没有主持人,则坐席号码自动升级为主持人角色。\r\n 如果要将会场成员踢出,调用拆线接口即可\r\n **/\n url: '/dispatch-web/api/meet/joinVideoMember',\n method: 'POST',\n requestParam: {\n token: '',\n meetId: '',\n businessId: '',\n //业务Id,用于业务操作与相关功能的关联。\n tel: '',\n level: '' //成员会场等级类型选择:状态:speak(可发言,默认);audience(禁言\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n joinMemberNet: {\n //成员加入会场(语音)\n\n /***成员加入会场,具体以语音或视频的哪种方式发起呼叫由号码配置类型决定\r\n 如果坐席号码加入会场,且会场中没有主持人,则坐席号码自动升级为主持人角色。\r\n 如果要将会场成员踢出,调用拆线接口即可\r\n **/\n url: '/dispatch-web/api/meet/joinMember',\n method: 'POST',\n requestParam: {\n token: '',\n meetId: '',\n businessId: '',\n //业务Id,用于业务操作与相关功能的关联。\n tel: '',\n level: '' //成员会场等级类型选择:状态:speak(可发言,默认);audience(禁言\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n listMeetsNet: {\n //查询会场列表\n\n /***查询当前所有会场信息\r\n **/\n url: '/dispatch-web/api/meet/joinVideoMember',\n requestParam: {\n token: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n meetId: '',\n meetName: '',\n chairman: '',\n //主持人号码\n recording: '',\n //是否在录音\n playvoice: '',\n //是否在放音\n locked: '',\n //是否锁定\n list: [{\n meetId: '',\n tel: '',\n level: '' //成员会场 角色/状态 ,类型如下:角色:chairman(主持人) 状态:speak(可发言);audience(禁言)\n\n }]\n }]\n }\n }\n },\n getMeetNet: {\n //查询单个会场信息\n\n /***查询当前所有会场信息\r\n **/\n url: '/dispatch-web/api/meet/getMeet',\n requestParam: {\n token: '',\n meetId: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n meetId: '',\n meetName: '',\n chairman: '',\n //主持人号码\n recording: '',\n //是否在录音\n playvoice: '',\n //是否在放音\n locked: '',\n //是否锁定\n members: [{\n meetId: '',\n tel: '',\n level: '' //成员会场 角色/状态 ,类型如下:角色:chairman(主持人) 状态:speak(可发言);audience(禁言)\n\n }]\n }\n }\n },\n listMeetMembersNet: {\n //查询与会成员列表\n\n /***查询当前会场所有成员信息。\r\n **/\n url: '/dispatch-web/api/meet/listMeetMembers',\n requestParam: {\n token: '',\n meetId: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n meetId: '',\n tel: '',\n level: '' //成员会场 角色/状态 ,类型如下:角色:chairman(主持人) 状态:speak(可发言);audience(禁言)\n\n }]\n }\n }\n },\n meetStatusMQNet: function (accId) {\n return {\n //会场状态改变通知\n topic: `/${accId}/dispatch-web/notify/meet/meetStatus`,\n responseParam: {\n id: 0,\n //数据id\n type: 'meetStatus',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n meetId: '',\n meetName: '',\n destory: '',\n //是否删除 true: 会场被销毁 false: 会场新创 or 已存在会场状态改变\n chairman: '',\n //主持人号码 F\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n memberMQNet: function (accId) {\n return {\n //会场成员列表改变通知\n topic: `/${accId}/dispatch-web/notify/meet/member`,\n responseParam: {\n id: 0,\n //数据id\n type: 'member',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n meetId: '',\n tel: '',\n type: '',\n // 成员列表改变类型 add: 加入会场 del:退出会场\n level: '',\n //会场成员状态\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n selectCallMQNet: function (accId) {\n return {\n //发起会议结果通知\n topic: `/${accId}/dispatch-web/notify/call/selectCall`,\n responseParam: {\n id: 0,\n //数据id\n type: 'selectCall',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n result: '',\n //操作结果\n desc: '',\n //选呼结文字描述\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n meetStatusHttpNet: {\n //会场状态改变通知Http推送方式\n url: '/notify/meet/meetStatus',\n method: 'POST',\n //推送当前会场状态改变通知\n requestParam: {\n meetId: '',\n meetName: '',\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n },\n selectCallHttpNet: {\n //发起会议结果通知Http推送方式\n url: '/notify/call/selectCall',\n method: 'POST',\n //推送选呼结果\n requestParam: {\n result: '',\n desc: '',\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n },\n memberHttpNet: {\n //会场成员列表改变通知Http推送方式\n url: '/notify/meet/member',\n method: 'POST',\n //推送当前会场成员列表改变通知,即单个会场成员的增加或删除通知消息\n requestParam: {\n meetId: '',\n tel: '',\n type: '',\n //列表更新状态 add: 新增会场 del:销毁会场\n level: '',\n // 成员会场 角色/状态 角色:chairman(主持人)状态:speak(可发言);audience(禁言) F\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n },\n responseParam: {\n code: '' //返回结果状态码\n\n }\n }\n};\nexport default meet;","let message = {\n sendMsgNet: {\n //发送短信\n\n /***发送结果不会立即返回,第三方可根据发送短信结果通知 获取\r\n **/\n url: '/scooper-sms/data/message/sendMsg',\n requestParam: {\n token: '',\n caller: '',\n //发送人号码 \n callees: '',\n //收件人号码 多个号码用英文分号“;”分割\n content: '' //短信内容,最长512个字节(汉字算两个字节)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n msgId: '',\n //短信记录id 短信超过50条会分批次发送,返回结果有多个msgId,中间用“;”分割\n flagBatch: '' //0 - 非群发 1 - 群发\n\n }\n }\n },\n queryMsgSendNet: {\n //分页查询发送短信记录列表\n\n /***短信发送记录 分页查询接口,支持条件查询\r\n * 普通成员只能查询自己发送短信记录,如需查询所有发送短信记录,需在对应账号上开通该权限\r\n **/\n url: '/scooper-sms/data/message/queryMsgSend',\n requestParam: {\n token: '',\n caller: '',\n //发送人号码 \n callee: '',\n //收件人号码 多个号码用英文分号“;”分割\n content: '',\n //短信内容,最长512个字节(汉字算两个字节)\n flagBatch: '',\n //是否群发 0:非群发 1、群发 2、所有(默认)\n startTime: '',\n endTime: '',\n msgStatus: '',\n //发送状态【-1:全部;0:等待发送;1:发送中;2:发送成功;3:发送失败;4:发送超时】\n pageNumber: '',\n //查询页码\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n totalRow: '',\n list: [{\n //记录列表, [0, n]个sendMsg。\n id: '',\n caller: '',\n callee: '',\n flag_batch: '',\n msg_status: '',\n //短信发送状态 0:等待发送,1:发送中,2:发送成功,3:发送失败,4:发送超时\n time_send: '',\n sms_content: '',\n //短信发送内容\n succ_callee: '',\n //发送成功的收件人数\n total_callee: '',\n //总共的收件人数\n time_schedule: '',\n //定时发送的时间\n reply_read: '' //回复是否已读(0:未读 1:已读)\n\n }],\n firstPage: '',\n //是否首页\n lastPage: '',\n //是否尾页\n pageNumber: '',\n //当前页\n pageSize: '',\n totalPage: ''\n }\n }\n },\n findMsgSendByIdNet: {\n //查询(单条)发送短信记录\n\n /***根据发送短信记录id查询发送短信消息记录\r\n **/\n url: '/scooper-sms/data/message/queryMsgSend',\n requestParam: {\n token: '',\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n caller: '',\n callee: '',\n formatedCallee: '',\n //收件人姓名(号码)查找不到姓名时,该字段与callee内容相同\n flag_batch: '',\n msg_status: '',\n // 短信发送状态 0:等待发送, 1:发送中, 2:发送成功,3:发送失败, 4:发送超时\n time_send: '',\n //短信发送时间\n sms_content: '' //短信发送内容\n\n }\n }\n },\n findBatchResultNet: {\n //查询群发短信(单个)发送状态\n\n /***查询群发短信结果,获取单条群发短信每个接收者的接收状态\r\n **/\n url: '/scooper-sms/data/message/findBatchResult',\n requestParam: {\n token: '',\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n callee: '',\n //接收号码\n memName: '',\n //接收人员姓名\n reason: '',\n //发送状态,0:等待发送,1:发送中,2:发送成功,3:发送失败,4:发送超时\n id: '',\n // 群发id\n tx_id: '' //发送的短信id\n\n }]\n }\n }\n },\n deleteMsgSendNet: {\n //删除发送短信记录\n\n /***不能删除发送短信状态为【0:等待发送,1:发送中的短信记录】。\r\n **/\n url: '/scooper-sms/data/message/deleteMsgSend',\n requestParam: {\n token: '',\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n },\n\n /***\r\n * 接收短信\r\n */\n queryMsgRecvNet: {\n //分页查询接收短信记录列表\n\n /***短信接收记录分页查询接口,支持条件查询。\r\n **/\n url: '/scooper-sms/data/message/queryMsgRecv',\n requestParam: {\n token: '',\n caller: '',\n //短信记录id\n callee: '',\n content: '',\n startTime: '',\n endTime: '',\n msgStatus: '',\n //接收短信状态 0:未读 1:已获取, 2:已读\n pageNumber: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n totalRow: '',\n pageNumber: '',\n list: [{\n id: '',\n //短信id\n caller: '',\n callee: '',\n msg_status: '',\n //短信接收状态0:未读 1:已获取 2:已读\n time_recv: '',\n //短信接收时间\n sms_content: '',\n //短信接收内容\n succ_callee: '',\n //发送成功的收件人数量\n total_callee: '' //总共的收件人数量\n\n }],\n lastPage: '',\n firstPage: '',\n pageSize: '',\n totalPage: ''\n }\n }\n },\n findMsgRecvByIdNet: {\n //查询单条接收短信记录\n\n /***根据接收短信记录id查询接收短信消息记录\r\n **/\n url: '/scooper-sms/data/message/findMsgRecvById',\n requestParam: {\n token: '',\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '',\n //短信id\n caller: '',\n callee: '',\n msg_status: '',\n //短信接收状态0:未读 1:已获取 2:已读\n time_recv: '',\n //短信接收时间\n sms_content: '',\n //短信接收内容\n msg_type: '' //消息类型0:sms,1:mms\n\n }\n }\n },\n updateRecvStatusNet: {\n //阅读接收短信记录\n\n /***阅读接收短信记录。将接收到的短信记录标记为已读状态\r\n **/\n url: '/scooper-sms/data/message/updateRecvStatus',\n method: 'POST',\n requestParam: {\n token: '',\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n unReadNum: 1 //未读接收短信数量\n\n }\n }\n },\n getUnReadRecvNumNet: {\n //查询未读接收短信记录数量\n\n /***阅读接收短信。将接收到的短信标记为已读状态\r\n **/\n url: '/scooper-sms/data/message/getUnReadRecvNum',\n requestParam: {\n token: '',\n //短信记录id\n msgId: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: 1 //未读接收短信剩余总数\n\n }\n }\n },\n\n /**\r\n * MQTT订阅\r\n * @param {*} accId \r\n * @returns \r\n */\n sendMsgMQNet: function () {\n return {\n //发送短信通知\n topic: '/scooper-sms/notify/msg/sendMsg',\n responseParam: {\n id: 0,\n //短信id\n type: 'sendMsg',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {\n msgId: '' //短信ID\n\n }\n }\n };\n },\n sendMsgResultMQNet: function () {\n return {\n //发送短信结果通知\n topic: '/scooper-sms/notify/msg/sendMsgResult',\n responseParam: {\n id: 0,\n //短信id\n type: 'sendMsgResult',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {\n msgStatus: '' //短信发送状态 2:发送成功,3:发送失败,4:发送超时\n\n }\n }\n };\n },\n deleteMsgSendMQNet: function () {\n return {\n //删除发送短信记录通知\n topic: '/scooper-sms/notify/msg/deleteMsgSend',\n responseParam: {\n id: 0,\n //短信id\n type: 'deleteMsgSend',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {}\n }\n };\n },\n recvMsgMQNet: function () {\n return {\n //接收到短信通知\n topic: '/scooper-sms/notify/msg/recvMsg',\n responseParam: {\n id: 0,\n //短信id\n type: 'recvMsg',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {\n total: '' //未读接收短信剩余总数\n\n }\n }\n };\n },\n deleteMsgRecvMQNet: function () {\n return {\n //删除接收短信记录通知\n topic: '/scooper-sms/notify/msg/deleteMsgRecv',\n responseParam: {\n id: 0,\n //短信id\n type: 'deleteMsgRecv',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {\n total: '' //未读接收短信剩余总数\n\n }\n }\n };\n },\n readMsgRecvMQNet: function () {\n return {\n //阅读接收短信记录通知\n topic: '/scooper-sms/notify/msg/readMsgRecv',\n responseParam: {\n id: 0,\n //短信id\n type: 'readMsgRecv',\n //消息类别\n source: 'scooper-sms',\n //消息来源\n data: {\n unReadNum: '' //未读接收短信剩余总数\n\n }\n }\n };\n },\n\n /**\r\n * \r\n * @returns HTTP推送\r\n */\n sendMsgHttpNet: function () {\n /**\r\n * 推送发送短信通知,用于短信发送列表数据刷新显示\r\n */\n return {\n //发送短信通知\n url: '/notify/msg/sendMsg',\n method: 'POST',\n requestParam: {\n msgId: '' //短信记录id\n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n },\n sendMsgResultHttpNet: function () {\n /**\r\n * 推送发送短信结果通知,用于短信发送状态刷新显示\r\n */\n return {\n //发送短信结果通知\n url: '/notify/msg/sendMsgResult',\n method: 'POST',\n requestParam: {\n msgId: '',\n //短信记录id \n msgStatus: '' //短信发送状态 2:发送成功,3:发送失败,4:发送超时\n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n },\n deleteMsgSendHttpNet: function () {\n /**\r\n * 推送删除发送短信结果通知,用于短信列表数据刷新显示\r\n */\n return {\n //删除发送短信记录通知\n url: '/notify/msg/deleteMsgSend',\n method: 'POST',\n requestParam: {\n msgId: '' //短信记录id \n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n },\n recvMsgHttpNet: function () {\n /**\r\n * 接收到新短信通知,用于短信接收列表刷新和未读数量刷新显示\r\n */\n return {\n //接收到短信通知\n url: '/notify/msg/recvMsg',\n method: 'POST',\n requestParam: {\n msgId: '',\n //接收短信id\n total: '' //未读接收短信剩余总数\n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n },\n deleteMsgRecvHttpNet: function () {\n /**\r\n * 接收到新短信通知,用于短信接收列表刷新和未读数量刷新显示\r\n */\n return {\n //删除接收短信记录通知\n url: '/notify/msg/deleteMsgRecv',\n method: 'POST',\n requestParam: {\n msgId: '',\n //接收短信id\n total: '' //未读接收短信剩余总数\n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n },\n readMsgRecvHttpNet: function () {\n /**\r\n * 阅读接收短信结果通知,用于接收短信阅读状态刷新显示和未读数量刷新显示\r\n */\n return {\n //阅读接收短信记录通知\n url: '/notify/msg/readMsgRecv',\n method: 'POST',\n requestParam: {\n msgId: '',\n //接收短信id\n unReadNum: '' //未读接收短信剩余总数\n\n },\n responseParam: {\n code: '' //操作结果代码\n\n }\n };\n }\n};\nexport default message;","let record = {\n queryRecordNet: {\n //分页查询录音(通话录音/会议录音)\n\n /***录音查询接口\r\n **/\n url: '/scooper-record/data/record/queryRecord',\n requestParam: {\n token: '',\n recType: '',\n //录音类型 1:通话录音(调度&平台)2:会议录音\n id: '',\n //录音id\n businessId: '',\n //业务id\n meetId: '',\n //\n caller: '',\n callee: '',\n timeMin: '',\n //录音开始时间查询起始值\n timeMax: '',\n pageIndex: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n id: '',\n businessId: '',\n meetId: '',\n caller: '',\n callee: '',\n tmBegin: '',\n //开始时间\n tmEnd: '',\n //结束时间\n callLength: '',\n //录音长度\n recFile: '',\n //录音文件名\n recType: '',\n //录音类型(字段无效)\n recExt: '' //备注信息\n\n }]\n }\n }\n },\n queryVideoNet: {\n //分页查询录像 (通话录像/会议录像)\n\n /***录像查询接口\r\n **/\n url: '/scooper-record/data/record/queryRecord',\n requestParam: {\n token: '',\n recType: '',\n //录像类型 1:通话录像(调度&平台)2:会议录像\n id: '',\n //录像id\n businessId: '',\n //业务id\n meetId: '',\n //\n tel: '',\n timeMin: '',\n //录像开始时间查询起始值\n timeMax: '',\n pageIndex: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n id: '',\n businessId: '',\n meetId: '',\n tel: '',\n tmBegin: '',\n //开始时间\n tmEnd: '',\n //结束时间\n tmLength: '',\n //录像长度\n recFile: '',\n //录像文件名\n recType: '' //录像类型(字段无效)\n\n }]\n }\n }\n },\n queryRecordNotifyNet: {\n //分页查询通知音记录\n\n /**通知音查询接口,通知音可用于 组呼通知 操作\r\n **/\n url: '/scooper-record/data/notify/queryRecordNotify',\n requestParam: {\n token: '',\n title: '',\n //标题\n id: '',\n //通知音记录id\n recTel: '',\n //录制通知音号码\n timeMin: '',\n //开始时间查询起始值\n timeMax: '',\n pageIndex: '',\n pageSize: ''\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n total: '',\n list: [{\n id: 1,\n recTel: '',\n title: '',\n recTm: '',\n //通知音开始录制时间\n callLength: '',\n //通知音录音时长,单位“秒”\n recFile: '' //通知音录音文件名\n\n }]\n }\n }\n },\n uploadNet: {\n //上传通知音\n\n /**上传通知音接口,通知音目前只支持格式为wav的语音文件\r\n 通知音可用于 组呼通知 操作\r\n **/\n url: '/scooper-record/data/notify/upload',\n requestParam: {\n token: '',\n recTel: '',\n //接收号码\n title: '',\n //通知音标题\n recTm: '',\n //通知音录制开始时间 yyyy-MM-dd HH:mm:ss\n callLength: '',\n //通知音录音时长,单位“秒”\n file: '' //通知音文件流\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {\n id: '' //通知音记录id\n\n }\n }\n },\n doDownloadNet: {\n //录音录像下载\n\n /**下载相应类别的录音、录像文件\r\n **/\n url: '/scooper-record/data/file/doDownload',\n requestParam: {\n token: '',\n id: '',\n //录音/录像id\n\n /**\r\n * 0: 通话录音\r\n 1: 通话录像\r\n 10: 会议录音\r\n 11: 会议录像\r\n 20: 通知音\r\n 30:对讲录音\r\n */\n fileType: '',\n //录音类型\n recFile: '' //录音/录像文件名\n\n },\n responseParam: {\n file: '' //文件流\n\n }\n },\n deleteFileNet: {\n //录音录像删除\n\n /**删除文件记录,同时删除录音或录像文件。\r\n **/\n url: '/scooper-record/data/file/deleteFile',\n requestParam: {\n token: '',\n id: '',\n //录音/录像id\n\n /**\r\n * 0: 通话录音\r\n 1: 通话录像\r\n 10: 会议录音\r\n 11: 会议录像\r\n 20: 通知音\r\n 30:对讲录音\r\n */\n fileType: '',\n //录音类型\n recFile: '' //录音/录像文件名\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: {}\n }\n }\n};\nexport default record;","let video = {\n callVideoNet: {\n //视频拨话\n\n /***发起视频呼叫,呼叫单个用户,被呼叫用户必须是可视设备,且配置为视频用户。\r\n 发起视频呼叫后,可使用视频组件(OCX控件)在网页(IE11)上使用号码callee作为视频id打开视频。需要注意的是网页上关闭视频不会影响视频通话**/\n url: '/dispatch-web/api/call/makeVideoCall',\n requestParam: {\n token: '',\n caller: '',\n //主叫号码(一般使用坐席号码)\n callee: '',\n //被叫号码\n businessId: '',\n //业务Id,用于业务操作与相关功能的关联\n isRecord: '' //是否录音 预留字段\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: ''\n }\n },\n videoStatusMQNet: function (accId) {\n //视频通话状态通知\n return {\n /**\r\n * 用于视频通话时判断是否视频通话建立or结束的通知,如视频呼叫某号码,该号码摘机建立视频通话后操作员收到cfgType=decoder_info通知,则第三方在收到通知时根据实际需求判断是否需要打开通话视频。\r\n (同理,视频加入会议,视频呼入调度与坐席通话也根据该通知判断视频打开/结束)\r\n */\n topic: `/${accId}/dispatch-web/notify/call/video`,\n responseParam: {\n id: 0,\n //数据id\n type: 'video',\n //消息类别\n source: 'dispatch-web',\n //消息来源\n data: {\n //通知的具体消息\n cfgType: '',\n //视频通话状态 decoder_info:视频通话建立 stop_decoder:视频通话结束\n tel: '',\n //通知号码\n accId: '',\n //消息接收的账号id\n account: '' //消息接收的账号名\n\n }\n }\n };\n },\n getNodesVideoNet: {\n //加载视频树节点数据(树型结构)\n\n /***\r\n * 树图层级加载树节点数据接口,查询返回的设备数据根据权限配置的数据节点过滤后获得\r\n * *\r\n * */\n url: '/scooper-video/data/video/load/getNodes',\n requestParam: {\n token: '',\n parentId: '' //父节点节点id,关联父节点的nodeId 默认0,加载一级节点(控制中心)\n\n },\n responseParam: {\n code: '',\n //返回结果状态码\n message: '',\n systemTime: '',\n //yyyy-MM-dd HH:mm:ss\n data: [{\n id: '',\n //\tInt\tT\t数据序号,没有节点意义\n nodeId: '',\n //\tInt\tT\t节点Id,用于上下级查询\n devId: '',\n //\tString\tT\t视频设备编号,可用于视频打开等操作\n dev_short_num: '',\n //\tString\tF\t视频设备短号,可用于视频打开等操作\n name: '',\n //\tString\tT\t设备名称\n parental: '',\n //\tInt\tT\t是否有子节点 0-没有(设备),1-有(目录节点)\n parentid: '',\n //\tInt\tT\t父节点id,关联父节点的nodeId\n port: '',\n //\tString\tF\t端口\n sort: '',\n //\tInt\tF\t排序号(暂时不支持)\n status: '',\n //\tString\tF\t在/离线状态parental=0(设备)这个属性有值,标识设备的在离线\n province: '',\n //\tString\tF\t省\n city: '',\n //\tString\tF\t市\n district: '',\n //\tString\tF\t区县\n ipaddress: '',\n //\tString\tF\tIP地址\n latitude: '',\n //\tString\tF\t纬度\n longitude: '' //\tString\tF\t经度\n\n }]\n }\n }\n};\nexport default video;","import account from '@/net_url/account_net_url'; //账号\n\nimport aduio from '@/net_url/aduio_net_url'; //音频\n\nimport aduio_video from '@/net_url/aduio_video_net_url'; //音频、视频公共接口\n\nimport dept_ry from '@/net_url/dept_ry_url'; //部门成员\n\nimport dept from '@/net_url/dept_url'; //部门\n\nimport location from '@/net_url/location_url'; //定位\n\nimport meet from '@/net_url/meet_url'; //会议\n\nimport message from '@/net_url/message_url'; //短信\n\nimport record from '@/net_url/record_url'; //录音录像\n\nimport video from '@/net_url/video_net_url'; //视频\n\nexport const server = 'http://10.249.4.152:8080'; //服务地址\n\nexport const cometd_server = '/scooper-msg-queue/cometd'; //\n\nexport { account, aduio, aduio_video, dept_ry, dept, location, meet, message, record, video };","/*\r\n* A JavaScript implementation of the SHA256 hash function.\r\n*\r\n* FILE:\tsha256.js\r\n* VERSION:\t0.8\r\n* AUTHOR:\tChristoph Bichlmeier <informatik@zombiearena.de>\r\n*\r\n* NOTE: This version is not tested thoroughly!\r\n*\r\n* Copyright (c) 2003, Christoph Bichlmeier\r\n* All rights reserved.\r\n*\r\n* Redistribution and use in source and binary forms, with or without\r\n* modification, are permitted provided that the following conditions\r\n* are met:\r\n* 1. Redistributions of source code must retain the above copyright\r\n* notice, this list of conditions and the following disclaimer.\r\n* 2. Redistributions in binary form must reproduce the above copyright\r\n* notice, this list of conditions and the following disclaimer in the\r\n* documentation and/or other materials provided with the distribution.\r\n* 3. Neither the name of the copyright holder nor the names of contributors\r\n* may be used to endorse or promote products derived from this software\r\n* without specific prior written permission.\r\n*\r\n* ======================================================================\r\n*\r\n* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS\r\n* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r\n* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE\r\n* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r\n* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r\n* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\r\n* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r\n* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\r\n* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r\n* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n*/\n\n/* SHA256 logical functions */\nfunction rotateRight(n, x) {\n return x >>> n | x << 32 - n;\n}\n\nfunction choice(x, y, z) {\n return x & y ^ ~x & z;\n}\n\nfunction majority(x, y, z) {\n return x & y ^ x & z ^ y & z;\n}\n\nfunction sha256_Sigma0(x) {\n return rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x);\n}\n\nfunction sha256_Sigma1(x) {\n return rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x);\n}\n\nfunction sha256_sigma0(x) {\n return rotateRight(7, x) ^ rotateRight(18, x) ^ x >>> 3;\n}\n\nfunction sha256_sigma1(x) {\n return rotateRight(17, x) ^ rotateRight(19, x) ^ x >>> 10;\n}\n\nfunction sha256_expand(W, j) {\n return W[j & 0x0f] += sha256_sigma1(W[j + 14 & 0x0f]) + W[j + 9 & 0x0f] + sha256_sigma0(W[j + 1 & 0x0f]);\n}\n/* Hash constant words K: */\n\n\nvar K256 = new Array(0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2);\n/* global arrays */\n\nvar ihash, count, buffer;\nvar sha256_hex_digits = \"0123456789abcdef\";\n/* Add 32-bit integers with 16-bit operations (bug in some JS-interpreters: \r\noverflow) */\n\nfunction safe_add(x, y) {\n var lsw = (x & 0xffff) + (y & 0xffff);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return msw << 16 | lsw & 0xffff;\n}\n/* Initialise the SHA256 computation */\n\n\nfunction sha256_init() {\n ihash = new Array(8);\n count = new Array(2);\n buffer = new Array(64);\n count[0] = count[1] = 0;\n ihash[0] = 0x6a09e667;\n ihash[1] = 0xbb67ae85;\n ihash[2] = 0x3c6ef372;\n ihash[3] = 0xa54ff53a;\n ihash[4] = 0x510e527f;\n ihash[5] = 0x9b05688c;\n ihash[6] = 0x1f83d9ab;\n ihash[7] = 0x5be0cd19;\n}\n/* Transform a 512-bit message block */\n\n\nfunction sha256_transform() {\n var a, b, c, d, e, f, g, h, T1, T2;\n var W = new Array(16);\n /* Initialize registers with the previous intermediate value */\n\n a = ihash[0];\n b = ihash[1];\n c = ihash[2];\n d = ihash[3];\n e = ihash[4];\n f = ihash[5];\n g = ihash[6];\n h = ihash[7];\n /* make 32-bit words */\n\n for (var i = 0; i < 16; i++) W[i] = buffer[(i << 2) + 3] | buffer[(i << 2) + 2] << 8 | buffer[(i << 2) + 1] << 16 | buffer[i << 2] << 24;\n\n for (var j = 0; j < 64; j++) {\n T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j];\n if (j < 16) T1 += W[j];else T1 += sha256_expand(W, j);\n T2 = sha256_Sigma0(a) + majority(a, b, c);\n h = g;\n g = f;\n f = e;\n e = safe_add(d, T1);\n d = c;\n c = b;\n b = a;\n a = safe_add(T1, T2);\n }\n /* Compute the current intermediate hash value */\n\n\n ihash[0] += a;\n ihash[1] += b;\n ihash[2] += c;\n ihash[3] += d;\n ihash[4] += e;\n ihash[5] += f;\n ihash[6] += g;\n ihash[7] += h;\n}\n/* Read the next chunk of data and update the SHA256 computation */\n\n\nfunction sha256_update(data, inputLen) {\n var i,\n index,\n curpos = 0;\n /* Compute number of bytes mod 64 */\n\n index = count[0] >> 3 & 0x3f;\n var remainder = inputLen & 0x3f;\n /* Update number of bits */\n\n if ((count[0] += inputLen << 3) < inputLen << 3) count[1]++;\n count[1] += inputLen >> 29;\n /* Transform as many times as possible */\n\n for (i = 0; i + 63 < inputLen; i += 64) {\n for (var j = index; j < 64; j++) buffer[j] = data.charCodeAt(curpos++);\n\n sha256_transform();\n index = 0;\n }\n /* Buffer remaining input */\n\n\n for (var j = 0; j < remainder; j++) buffer[j] = data.charCodeAt(curpos++);\n}\n/* Finish the computation by operations such as padding */\n\n\nfunction sha256_final() {\n var index = count[0] >> 3 & 0x3f;\n buffer[index++] = 0x80;\n\n if (index <= 56) {\n for (var i = index; i < 56; i++) buffer[i] = 0;\n } else {\n for (var i = index; i < 64; i++) buffer[i] = 0;\n\n sha256_transform();\n\n for (var i = 0; i < 56; i++) buffer[i] = 0;\n }\n\n buffer[56] = count[1] >>> 24 & 0xff;\n buffer[57] = count[1] >>> 16 & 0xff;\n buffer[58] = count[1] >>> 8 & 0xff;\n buffer[59] = count[1] & 0xff;\n buffer[60] = count[0] >>> 24 & 0xff;\n buffer[61] = count[0] >>> 16 & 0xff;\n buffer[62] = count[0] >>> 8 & 0xff;\n buffer[63] = count[0] & 0xff;\n sha256_transform();\n}\n/* Split the internal hash values into an array of bytes */\n\n\nfunction sha256_encode_bytes() {\n var j = 0;\n var output = new Array(32);\n\n for (var i = 0; i < 8; i++) {\n output[j++] = ihash[i] >>> 24 & 0xff;\n output[j++] = ihash[i] >>> 16 & 0xff;\n output[j++] = ihash[i] >>> 8 & 0xff;\n output[j++] = ihash[i] & 0xff;\n }\n\n return output;\n}\n/* Get the internal hash as a hex string */\n\n\nfunction sha256_encode_hex() {\n var output = new String();\n\n for (var i = 0; i < 8; i++) {\n for (var j = 28; j >= 0; j -= 4) output += sha256_hex_digits.charAt(ihash[i] >>> j & 0x0f);\n }\n\n return output;\n}\n/* Main function: returns a hex string representing the SHA256 value of the \r\ngiven data */\n\n\nexport default function sha256_digest(data) {\n sha256_init();\n sha256_update(data, data.length);\n sha256_final();\n return sha256_encode_hex();\n}\n/* test if the JS-interpreter is working properly */\n\nfunction sha256_self_test() {\n return sha256_digest(\"message digest\") == \"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650\";\n}","let cometd = require('./lib/cometd.js');\n\nimport { server, cometd_server } from '@/config';\n\nlet Video = require('./scooper.video');\n\nlet $ = require('jquery');\n/**\r\n * 接口请求通用方法\r\n * @param {*} url \r\n * @param {*} param \r\n * @param {*} backSuccfn \r\n * @param {*} async \r\n * @param {*} method \r\n */\n\n\nexport const loadJson = function (url, param, backSuccfn, async, method, headers) {\n method = method ? method : 'POST';\n async = async == undefined ? true : async;\n let ajaxContent = {\n 'method': method,\n 'url': server + url,\n 'data': param,\n 'async': async\n };\n if (headers) Object.assign(ajaxContent, headers);\n $.ajax(ajaxContent).fail(function (jqXHR, sts) {\n console.error('加载数据失败:' + sts + \", \" + url);\n }).done(function (ret) {\n if (!ret || ret.code != 0) {\n console.error('加载数据失败:' + JSON.stringify(ret) + \", \" + url);\n }\n\n if (backSuccfn) backSuccfn(ret);\n });\n};\n/**\r\n * \r\n * @param {*} meetcallfunc 会议通知处理\r\n * @param {*} callfunc 呼叫通知处理\r\n * @param {*} videocallfunc 视频通知处理\r\n * @param {*} connectcallfunc 通讯录通知处理\r\n */\n\nexport const initCometd = function (meetcallfunc, callfunc, videocallfunc, connectcallfunc) {\n //初始\n let _cometd = new cometd.CometD(\"xjInfoReport\");\n\n _cometd.websocketEnabled = true;\n let cometdUrl = server + cometd_server;\n\n _cometd.init({\n url: cometdUrl,\n logLevel: \"info\"\n });\n\n _cometd.addListener(\"/meta/handshake\", function (message) {\n if (message.successful) {\n console.log(\"handshake success.\");\n }\n });\n\n let _connected = false;\n\n _cometd.addListener('/meta/connect', function (message) {\n if (message.successful) {\n if (!_connected) {\n doSubscribe(_cometd, {\n meetcallfunc,\n callfunc,\n videocallfunc,\n connectcallfunc\n });\n }\n\n _connected = true;\n } else {\n _connected = false;\n console.log(\"连接cometd后台失败\");\n }\n });\n};\n\nfunction doSubscribe(_cometd, callback) {\n /**\r\n * 会场模块消息通知\r\n */\n if (callback.meetcallfunc) {\n _cometd.subscribe(\"/dispatch-web/notify/meet/#\", null, function (message) {\n callback.meetcallfunc(message);\n });\n }\n /**\r\n * 呼叫模块消息通知\r\n */\n\n\n if (callback.callfunc) {\n _cometd.subscribe(\"/dispatch-web/notify/call/#\", null, function (message) {\n callback.callfunc(message);\n });\n }\n /**\r\n * 视频通话状态消息通知(视频通话建立 和 中断)\r\n */\n\n\n if (callback.videocallfunc) {\n _cometd.subscribe(\"/server/#\", null, function (message) {\n callback.videocallfunc(message);\n });\n } //'#':多级通配,订阅所有的主题\n\n\n if (callback.connectcallfunc) {\n _cometd.subscribe(\"/scooper_core/#\", null, function (message) {\n callback.connectcallfunc(message);\n });\n }\n}\n/**\r\n * 初始化视频获取视频对象\r\n * @param {*} token \r\n * @param {*} videoArea //展示视频的容器模块\r\n * 使用该方法前提是videoArea等相关dom节点必须已加载完毕\r\n */\n\n\nexport const initVideo = function (token, videoArea) {\n //读取视频服务配置初始化视频\n return new Promise((resolve, reject) => {\n let url = server + '/scooper-video/conf/data';\n var param = {};\n $.getJSON(url, param, function (conf) {\n // 初始化视频播放方式\n // initplaVideoType();\n let janusUrl = conf['video.janus.url'];\n var videoOpts = {\n //初始化时的界面显示的分屏树\n windows: 4,\n //共有哪几种分屏\n windowsArr: [1, 4, 9, 16],\n //总的窗口数\n windowsNum: 16,\n conf: {\n user: conf['video.username'],\n passwd: conf['video.password'],\n ip: conf['video.ip'],\n port: conf['video.port'],\n janusUrl: conf['video.janus.url'],\n token: token\n },\n extra: false,\n streamType: conf['video.stream'],\n openChangeWindowStrategy: conf['video.openChangeWindowStrategy'] === 'true',\n capImage: conf['video.cap.image'] === 'true',\n videoCapImagePath: conf['video.cap.image.path'],\n videoInfoInBottom: false\n };\n conf['video.poll.time'] && (videoOpts.pollInterval = conf['video.poll.time']);\n let videoController = new Video($(videoArea), videoOpts);\n resolve(videoController);\n });\n });\n};","import { account, aduio, aduio_video, dept_ry, dept, location, meet, message, record, video } from '@/config';\nimport '@/css/scooper.video.css';\nimport sha256_digest from '@/lib/sha256';\nimport { loadJson, initCometd, initVideo } from '@/common-request';\nexport const dologin = function (username, password) {\n let net = account.loginNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, {\n accUsername: username,\n accPassword: sha256_digest(password)\n }, function (ret) {\n if (ret.code == 0) {\n let token = ret.data; //启动心跳保持\n // if(heartTime) {\n // clearInterval(heartTime);\n // }\n // setInterval(keepHeart, 15000);\n\n resolve(token);\n /**\r\n * 初始化操作:\r\n * 1.启动心跳保持\r\n * 2.加载账号信息\r\n * 3.初始化推送cometd\r\n */\n } else {\n reject(\"登录失败\", ret.code);\n }\n }, '', net.method);\n });\n};\nexport const doLogout = function (token) {\n let net = account.logoutNet;\n return new Promise((resolve, reject) => {\n if (token) {\n loadJson(net.url, {\n token\n }, function (ret) {\n if (ret.code == 0) {\n let token = ret.data;\n resolve();\n /**\r\n * 清除心跳保持(全局定时器)\r\n */\n } else {\n reject(\"登出失败\", ret.code);\n }\n }, '', net.method);\n } else {\n reject(\"token值无效\", -1);\n }\n });\n};\nexport const keepHeart = function (token) {\n let net = account.heartBeatNet;\n loadJson(net.url, {\n token\n }, function (ret) {\n if (ret.code == 0) {\n console(\"心跳设置完成\", ret.code);\n } else {\n console(\"心跳设置失败\", ret.code);\n }\n }, '', net.method);\n};\nexport { initCometd, initVideo };\nexport const accountOp = {\n add: function (param) {\n let net = account.saveAccountNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n console(\"账号新增完成\", ret.code);\n resolve(ret);\n } else {\n console(\"账号新增失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n update: function (param) {\n let net = account.updateAccountNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n console(\"账号更新完成\", ret.code);\n resolve(ret);\n } else {\n console(\"账号更新失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n delete: function (param) {\n let net = account.deleteAccountNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n console(\"账号删除完成\", ret.code);\n resolve(ret);\n } else {\n console(\"账号删除失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n resetPwd: function (param) {\n let net = account.resetPwdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n console(\"账号密码重置完成\", ret.code);\n resolve(ret);\n } else {\n console(\"账号密码重置失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n resetSelfPwd: function (param) {\n let net = account.resetSelfPwdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n console(\"当前账号密码重置完成\", ret.code);\n resolve(ret);\n } else {\n console(\"当前账号密码重置失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n list: function (param) {\n let net = account.listAccountNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"账号列表查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listPage: function (param) {\n let net = account.queryAccountNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"账号列表分页查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n linkMember: function (param) {\n let net = account.getAccountLinkMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"账号关联部门成员查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n info: function (param) {\n let net = account.getAccountDetailNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"账号详情查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const aduioOp = {\n call: function (param) {\n let net = aduio.callNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"语音呼叫失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n answer: function (param) {\n let net = aduio.answerNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"语音呼叫接听失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n hold: function (param) {\n let net = aduio.holdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"保持通话失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n unhold: function (param) {\n let net = aduio.unholdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"取消保持通话失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n transfer: function (param) {\n let net = aduio.transferNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"通话转接失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n retrieve: function (param) {\n let net = aduio.retrieveNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"取消通话转接失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n tripleBreakin: function (param) {\n let net = aduio.tripleBreakinNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"强插失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n tripleHungup: function (param) {\n let net = aduio.tripleHungupNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"强拆失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n tripleMonitor: function (param) {\n let net = aduio.tripleMonitorNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"监听失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n groupNotify: function (param) {\n let net = aduio.groupNotifyNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"组呼通知失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n groupNotifyListPage: function (param) {\n let net = aduio.queryGroupNotifyNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"组呼通知分页查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n callListPage: function (param) {\n let net = aduio.queryCallNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"呼叫记录分页查询失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const aduioVideoOp = {\n callHungup: function (param) {\n let net = aduio_video.callHungupNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"语音或视频拆线/挂断失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listStatus: function (param) {\n let net = aduio_video.listStatusNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"获取所有号码状态失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n telsStatus: function (param) {\n let net = aduio_video.telsStatusNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"获取指定号码状态失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const deptOp = {\n add: function (param) {\n let net = dept.saveOrgDeptNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"新增部门失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n update: function (param) {\n let net = dept.updateOrgDeptNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"更新部门失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n delete: function (param) {\n let net = dept.deleteOrgDeptNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"删除部门失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n info: function (param) {\n let net = dept.getOrgDeptDetailNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listPage: function (param) {\n let net = dept.queryOrgDeptNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询部门列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n list: function (param) {\n let net = dept.listOrgDeptNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listFrom: function (param) {\n let net = dept.listDeptByParentNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门下属层级关系失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n path: function (param) {\n let net = dept.findDepartmentPathNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门所在层级的关系路径失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n treeFrom: function (param) {\n let net = dept.listTreeDeptByParentNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门所在树形的关系失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n memberCount: function (param) {\n let net = dept.findDeptMemberCount;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门成员数量\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n changeSort: function (param) {\n let net = dept.changeDeptSortIndexNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"修改部门排序失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n memberList: function (param) {\n let net = dept.listDeptMemberByDeotIdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门成员列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listAreaCode: function (param) {\n let net = dept.listDeptMemberByDeotIdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询行政区划列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const deptRyOp = {\n add: function (param) {\n let net = dept_ry.saveOrgMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"新增部门成员失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n update: function (param) {\n let net = dept_ry.updateOrgMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"更新部门成员失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n delete: function (param) {\n let net = dept_ry.deleteOrgMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"删除部门成员失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n info: function (param) {\n let net = dept_ry.getOrgMemberDetailNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门成员信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listPage: function (param) {\n let net = dept_ry.queryOrgMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询部门成员失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n list: function (param) {\n let net = dept_ry.listOrgMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门成员列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n findByTel: function (param) {\n let net = dept_ry.findOrgMemberByTelNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"通过关联号码查询部门成员信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n path: function (param) {\n let net = dept_ry.findOrgMemberPathNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询部门成员所在层级路径失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n linkAccount: function (param) {\n let net = dept_ry.findAccountByOrgMemberIdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询成员关联账号失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n changeSort: function (param) {\n let net = dept_ry.changeOrgMemberSortIndexNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"修改成员排序失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listIds: function (param) {\n let net = dept_ry.listOrgMemberByIdsNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"多id查询成员列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const locationOp = {\n locate: function (param) {\n let net = location.reLocationNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"上报位置信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method, {\n headers: net.headers\n });\n });\n },\n trace: function (param) {\n let net = location.listTraceByArgsNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询人员轨迹失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n tracePage: function (param) {\n let net = location.listTraceByArgsPageNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询人员轨迹失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const meetOp = {\n create: function (param) {\n let net = meet.selectCallNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"发起会议失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n end: function (param) {\n let net = meet.endMeetNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"结束会议失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n joinVideo: function (param) {\n let net = meet.joinVideoMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"加入视频会议失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n join: function (param) {\n let net = meet.joinMemberNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"加入语音会议失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n list: function (param) {\n let net = meet.listMeetsNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询会议列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n info: function (param) {\n let net = meet.getMeetNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询会议信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n members: function (param) {\n let net = meet.listMeetMembersNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询与会成员列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const messageOp = {\n send: function (param) {\n let net = message.sendMsgNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"发送短信失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n listPage: function (param) {\n let net = message.queryMsgSendNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询短信记录失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n info: function (param) {\n let net = message.findMsgSendByIdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询短信信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n findBatchResult: function (param) {\n let net = message.findBatchResultNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询某条群发短信的发送状态失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n delete: function (param) {\n let net = message.deleteMsgSendNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"删除短信失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n recvListPage: function (param) {\n let net = message.queryMsgRecvNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"删除短信失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n recvInfo: function (param) {\n let net = message.findMsgRecvByIdNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"查询接收短信信息失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n read: function (param) {\n let net = message.updateRecvStatusNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"阅读接收短信失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n unreadNum: function (param) {\n let net = message.getUnReadRecvNumNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"获取未读短信数量失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const recordOp = {\n aduioListPage: function (param) {\n let net = record.queryRecordNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询录音记录列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n videoListPage: function (param) {\n let net = record.queryVideoNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询录像记录列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n notifyPageList: function (param) {\n let net = record.queryRecordNotifyNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询通知音记录列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n uploadNotify: function (param) {\n let net = record.uploadNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"分页查询通知音记录列表失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n download: function (param) {\n let net = record.doDownloadNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"录音录像下载失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n delete: function (param) {\n let net = record.deleteFileNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"录音录像删除失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport const videoOp = {\n call: function (param) {\n let net = video.callVideoNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"拨打视频失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n },\n tree: function (param) {\n let net = video.getNodesVideoNet;\n return new Promise((resolve, reject) => {\n loadJson(net.url, { ...param\n }, function (ret) {\n if (ret.code == 0) {\n resolve(ret);\n } else {\n console(\"加载视频树节点数据失败\", ret.code);\n reject(ret.code);\n }\n }, '', net.method);\n });\n }\n};\nexport { account, aduio, aduio_video, dept_ry, dept, location, meet, message, record, video }; //接口相关参数以及返回值,暴露出去可以供开发者查看","import './setPublicPath'\nexport * from '~entry'\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;AClFA;;;;;;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;AChoVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;AChxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA,WAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACxnGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA,WAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;AC9EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;AC1mHA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACr4HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACtQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC3LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACjDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AC7rBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AClEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACvSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AChWA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AChbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACxIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9kBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9ZA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/sBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClrBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACriBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;ACvBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvnCA;AACA;;;;;;A","sourceRoot":""}