1 line
2.0 MiB
1 line
2.0 MiB
{"version":3,"file":"firebase-firestore.memory.js","sources":["../../node_modules/tslib/tslib.es6.js","../logger/src/logger.ts","../util/src/environment.ts","../util/src/errors.ts","../component/src/component.ts","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/base.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/disposable/disposable.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/array/array.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/string/internal.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/useragent/util.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/object/object.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/reflect/reflect.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/useragent/useragent.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/useragent/browser.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/useragent/engine.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/browserfeature.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/event.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/browserevent.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/eventtype.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/listenable.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/listener.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/listenermap.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/events.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/eventtarget.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/json/json.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/async/workqueue.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/async/run.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/async/freelist.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/async/nexttick.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/timer/timer.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/async/throttle.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/events/eventhandler.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/webchanneldebug.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/requeststats.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/errorcode.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/eventtype.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/xmlhttpfactory.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/xmlhttp.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/channelrequest.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/environment.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/webchannelbase.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/uri/uri.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/string/string.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/xhrio.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/structs/structs.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/structs/map.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/uri/utils.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/wire.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/json/nativejsonprocessor.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/wirev8.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/netutils.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/json/hybrid.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/httpstatus.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/net/rpc/httpcors.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchanneltransport.js","../webchannel-wrapper/dist/temp/src/index.js","../webchannel-wrapper/dist/temp/node_modules/google-closure-library/closure/goog/labs/net/webchanneltransportfactory.js","../firestore/src/core/version.ts","../firestore/src/auth/user.ts","../firestore/src/api/credentials.ts","../firestore/src/util/error.ts","../firestore/src/api/timestamp.ts","../firestore/src/core/snapshot_version.ts","../firestore/src/model/path.ts","../firestore/src/model/document_key.ts","../firestore/src/util/obj.ts","../firestore/src/util/byte_string.ts","../firestore/src/util/types.ts","../firestore/src/model/server_timestamps.ts","../firestore/src/model/values.ts","../firestore/src/model/transform_operation.ts","../firestore/src/model/mutation.ts","../firestore/src/model/object_value.ts","../firestore/src/model/document.ts","../firestore/src/remote/existence_filter.ts","../firestore/src/remote/rpc_error.ts","../firestore/src/core/target.ts","../firestore/src/core/query.ts","../firestore/src/local/target_data.ts","../firestore/src/util/sorted_map.ts","../firestore/src/util/sorted_set.ts","../firestore/src/model/collections.ts","../firestore/src/remote/watch_change.ts","../firestore/src/model/document_set.ts","../firestore/src/core/view_snapshot.ts","../firestore/src/remote/remote_event.ts","../firestore/src/remote/serializer.ts","../firestore/src/platform/platform.ts","../firestore/src/util/log.ts","../firestore/src/util/assert.ts","../firestore/src/util/misc.ts","../firestore/src/core/database_info.ts","../firestore/src/util/obj_map.ts","../firestore/src/model/mutation_batch.ts","../firestore/src/local/persistence_promise.ts","../firestore/src/local/local_documents_view.ts","../firestore/src/local/local_view_changes.ts","../firestore/src/core/listen_sequence.ts","../firestore/src/util/promise.ts","../firestore/src/remote/backoff.ts","../firestore/src/local/memory_index_manager.ts","../firestore/src/core/target_id_generator.ts","../firestore/src/local/simple_db.ts","../firestore/src/util/async_queue.ts","../firestore/src/local/lru_garbage_collector.ts","../firestore/src/local/local_store.ts","../firestore/src/local/persistence.ts","../firestore/src/local/reference_set.ts","../firestore/src/util/input_validation.ts","../firestore/src/api/blob.ts","../firestore/src/api/field_path.ts","../firestore/src/api/field_value.ts","../firestore/src/api/geo_point.ts","../firestore/src/api/user_data_reader.ts","../firestore/src/core/view.ts","../firestore/src/core/sync_engine.ts","../firestore/src/core/event_manager.ts","../firestore/src/remote/persistent_stream.ts","../firestore/src/remote/datastore.ts","../firestore/src/core/transaction.ts","../firestore/src/remote/online_state_tracker.ts","../firestore/src/remote/remote_store.ts","../firestore/src/local/shared_client_state.ts","../firestore/src/core/transaction_runner.ts","../firestore/src/local/index_free_query_engine.ts","../firestore/src/local/memory_mutation_queue.ts","../firestore/src/local/memory_remote_document_cache.ts","../firestore/src/local/remote_document_change_buffer.ts","../firestore/src/local/memory_target_cache.ts","../firestore/src/local/memory_persistence.ts","../firestore/src/core/component_provider.ts","../firestore/src/core/firestore_client.ts","../firestore/src/util/async_observer.ts","../firestore/src/api/observer.ts","../firestore/src/api/user_data_writer.ts","../firestore/src/api/database.ts","../firestore/src/util/api.ts","../firestore/index.memory.ts","../firestore/src/platform/config.ts","../firestore/src/remote/connectivity_monitor_noop.ts","../firestore/src/platform_browser/browser_connectivity_monitor.ts","../firestore/src/remote/stream_bridge.ts","../firestore/src/platform_browser/webchannel_connection.ts","../firestore/src/platform_browser/browser_platform.ts","../firestore/src/platform_browser/browser_init.ts"],"sourcesContent":["/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation. All rights reserved.\r\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use\r\nthis file except in compliance with the License. You may obtain a copy of the\r\nLicense at http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nTHIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\nKIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED\r\nWARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r\nMERCHANTABLITY OR NON-INFRINGEMENT.\r\n\r\nSee the Apache Version 2.0 License for specific language governing permissions\r\nand limitations under the License.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport function __exportStar(m, exports) {\r\n for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n};\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\r\n result.default = mod;\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n}\r\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nexport type LogLevelString =\n | 'debug'\n | 'verbose'\n | 'info'\n | 'warn'\n | 'error'\n | 'silent';\n\nexport interface LogOptions {\n level: LogLevelString;\n}\n\nexport type LogCallback = (callbackParams: LogCallbackParams) => void;\n\nexport interface LogCallbackParams {\n level: LogLevelString;\n message: string;\n args: unknown[];\n type: string;\n}\n\n/**\n * A container for all of the Logger instances\n */\nexport const instances: Logger[] = [];\n\n/**\n * The JS SDK supports 5 log levels and also allows a user the ability to\n * silence the logs altogether.\n *\n * The order is a follows:\n * DEBUG < VERBOSE < INFO < WARN < ERROR\n *\n * All of the log types above the current log level will be captured (i.e. if\n * you set the log level to `INFO`, errors will still be logged, but `DEBUG` and\n * `VERBOSE` logs will not)\n */\nexport enum LogLevel {\n DEBUG,\n VERBOSE,\n INFO,\n WARN,\n ERROR,\n SILENT\n}\n\nconst levelStringToEnum: { [key in LogLevelString]: LogLevel } = {\n 'debug': LogLevel.DEBUG,\n 'verbose': LogLevel.VERBOSE,\n 'info': LogLevel.INFO,\n 'warn': LogLevel.WARN,\n 'error': LogLevel.ERROR,\n 'silent': LogLevel.SILENT\n};\n\n/**\n * The default log level\n */\nconst defaultLogLevel: LogLevel = LogLevel.INFO;\n\n/**\n * We allow users the ability to pass their own log handler. We will pass the\n * type of log, the current log level, and any other arguments passed (i.e. the\n * messages that the user wants to log) to this function.\n */\nexport type LogHandler = (\n loggerInstance: Logger,\n logType: LogLevel,\n ...args: unknown[]\n) => void;\n\n/**\n * By default, `console.debug` is not displayed in the developer console (in\n * chrome). To avoid forcing users to have to opt-in to these logs twice\n * (i.e. once for firebase, and once in the console), we are sending `DEBUG`\n * logs to the `console.log` function.\n */\nconst ConsoleMethod = {\n [LogLevel.DEBUG]: 'log',\n [LogLevel.VERBOSE]: 'log',\n [LogLevel.INFO]: 'info',\n [LogLevel.WARN]: 'warn',\n [LogLevel.ERROR]: 'error'\n};\n\n/**\n * The default log handler will forward DEBUG, VERBOSE, INFO, WARN, and ERROR\n * messages on to their corresponding console counterparts (if the log method\n * is supported by the current log level)\n */\nconst defaultLogHandler: LogHandler = (instance, logType, ...args): void => {\n if (logType < instance.logLevel) {\n return;\n }\n const now = new Date().toISOString();\n const method = ConsoleMethod[logType as keyof typeof ConsoleMethod];\n if (method) {\n console[method as 'log' | 'info' | 'warn' | 'error'](\n `[${now}] ${instance.name}:`,\n ...args\n );\n } else {\n throw new Error(\n `Attempted to log a message with an invalid logType (value: ${logType})`\n );\n }\n};\n\nexport class Logger {\n /**\n * Gives you an instance of a Logger to capture messages according to\n * Firebase's logging scheme.\n *\n * @param name The name that the logs will be associated with\n */\n constructor(public name: string) {\n /**\n * Capture the current instance for later use\n */\n instances.push(this);\n }\n\n /**\n * The log level of the given Logger instance.\n */\n private _logLevel = defaultLogLevel;\n get logLevel(): LogLevel {\n return this._logLevel;\n }\n set logLevel(val: LogLevel) {\n if (!(val in LogLevel)) {\n throw new TypeError('Invalid value assigned to `logLevel`');\n }\n this._logLevel = val;\n }\n\n /**\n * The main (internal) log handler for the Logger instance.\n * Can be set to a new function in internal package code but not by user.\n */\n private _logHandler: LogHandler = defaultLogHandler;\n get logHandler(): LogHandler {\n return this._logHandler;\n }\n set logHandler(val: LogHandler) {\n if (typeof val !== 'function') {\n throw new TypeError('Value assigned to `logHandler` must be a function');\n }\n this._logHandler = val;\n }\n\n /**\n * The optional, additional, user-defined log handler for the Logger instance.\n */\n private _userLogHandler: LogHandler | null = null;\n get userLogHandler(): LogHandler | null {\n return this._userLogHandler;\n }\n set userLogHandler(val: LogHandler | null) {\n this._userLogHandler = val;\n }\n\n /**\n * The functions below are all based on the `console` interface\n */\n\n debug(...args: unknown[]): void {\n this._userLogHandler && this._userLogHandler(this, LogLevel.DEBUG, ...args);\n this._logHandler(this, LogLevel.DEBUG, ...args);\n }\n log(...args: unknown[]): void {\n this._userLogHandler &&\n this._userLogHandler(this, LogLevel.VERBOSE, ...args);\n this._logHandler(this, LogLevel.VERBOSE, ...args);\n }\n info(...args: unknown[]): void {\n this._userLogHandler && this._userLogHandler(this, LogLevel.INFO, ...args);\n this._logHandler(this, LogLevel.INFO, ...args);\n }\n warn(...args: unknown[]): void {\n this._userLogHandler && this._userLogHandler(this, LogLevel.WARN, ...args);\n this._logHandler(this, LogLevel.WARN, ...args);\n }\n error(...args: unknown[]): void {\n this._userLogHandler && this._userLogHandler(this, LogLevel.ERROR, ...args);\n this._logHandler(this, LogLevel.ERROR, ...args);\n }\n}\n\nexport function setLogLevel(level: LogLevelString | LogLevel): void {\n const newLevel = typeof level === 'string' ? levelStringToEnum[level] : level;\n instances.forEach(inst => {\n inst.logLevel = newLevel;\n });\n}\n\nexport function setUserLogHandler(\n logCallback: LogCallback | null,\n options?: LogOptions\n): void {\n for (const instance of instances) {\n let customLogLevel: LogLevel | null = null;\n if (options && options.level) {\n customLogLevel = levelStringToEnum[options.level];\n }\n if (logCallback === null) {\n instance.userLogHandler = null;\n } else {\n instance.userLogHandler = (\n instance: Logger,\n level: LogLevel,\n ...args: unknown[]\n ) => {\n const message = args\n .map(arg => {\n if (arg == null) {\n return null;\n } else if (typeof arg === 'string') {\n return arg;\n } else if (typeof arg === 'number' || typeof arg === 'boolean') {\n return arg.toString();\n } else if (arg instanceof Error) {\n return arg.message;\n } else {\n try {\n return JSON.stringify(arg);\n } catch (ignored) {\n return null;\n }\n }\n })\n .filter(arg => arg)\n .join(' ');\n if (level >= (customLogLevel ?? instance.logLevel)) {\n logCallback({\n level: LogLevel[level].toLowerCase() as LogLevelString,\n message,\n args,\n type: instance.name\n });\n }\n };\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { CONSTANTS } from './constants';\n\n/**\n * Returns navigator.userAgent string or '' if it's not defined.\n * @return user agent string\n */\nexport function getUA(): string {\n if (\n typeof navigator !== 'undefined' &&\n typeof navigator['userAgent'] === 'string'\n ) {\n return navigator['userAgent'];\n } else {\n return '';\n }\n}\n\n/**\n * Detect Cordova / PhoneGap / Ionic frameworks on a mobile device.\n *\n * Deliberately does not rely on checking `file://` URLs (as this fails PhoneGap\n * in the Ripple emulator) nor Cordova `onDeviceReady`, which would normally\n * wait for a callback.\n */\nexport function isMobileCordova(): boolean {\n return (\n typeof window !== 'undefined' &&\n // @ts-ignore Setting up an broadly applicable index signature for Window\n // just to deal with this case would probably be a bad idea.\n !!(window['cordova'] || window['phonegap'] || window['PhoneGap']) &&\n /ios|iphone|ipod|ipad|android|blackberry|iemobile/i.test(getUA())\n );\n}\n\n/**\n * Detect Node.js.\n *\n * @return true if Node.js environment is detected.\n */\n// Node detection logic from: https://github.com/iliakan/detect-node/\nexport function isNode(): boolean {\n try {\n return (\n Object.prototype.toString.call(global.process) === '[object process]'\n );\n } catch (e) {\n return false;\n }\n}\n\n/**\n * Detect Browser Environment\n */\nexport function isBrowser(): boolean {\n return typeof self === 'object' && self.self === self;\n}\n\n/**\n * Detect browser extensions (Chrome and Firefox at least).\n */\ninterface BrowserRuntime {\n id?: unknown;\n}\ndeclare const chrome: { runtime?: BrowserRuntime };\ndeclare const browser: { runtime?: BrowserRuntime };\nexport function isBrowserExtension(): boolean {\n const runtime =\n typeof chrome === 'object'\n ? chrome.runtime\n : typeof browser === 'object'\n ? browser.runtime\n : undefined;\n return typeof runtime === 'object' && runtime.id !== undefined;\n}\n\n/**\n * Detect React Native.\n *\n * @return true if ReactNative environment is detected.\n */\nexport function isReactNative(): boolean {\n return (\n typeof navigator === 'object' && navigator['product'] === 'ReactNative'\n );\n}\n\n/** Detects Electron apps. */\nexport function isElectron(): boolean {\n return getUA().indexOf('Electron/') >= 0;\n}\n\n/** Detects Internet Explorer. */\nexport function isIE(): boolean {\n const ua = getUA();\n return ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0;\n}\n\n/** Detects Universal Windows Platform apps. */\nexport function isUWP(): boolean {\n return getUA().indexOf('MSAppHost/') >= 0;\n}\n\n/**\n * Detect whether the current SDK build is the Node version.\n *\n * @return true if it's the Node SDK build.\n */\nexport function isNodeSdk(): boolean {\n return CONSTANTS.NODE_CLIENT === true || CONSTANTS.NODE_ADMIN === true;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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 * @fileoverview Standardized Firebase Error.\n *\n * Usage:\n *\n * // Typescript string literals for type-safe codes\n * type Err =\n * 'unknown' |\n * 'object-not-found'\n * ;\n *\n * // Closure enum for type-safe error codes\n * // at-enum {string}\n * var Err = {\n * UNKNOWN: 'unknown',\n * OBJECT_NOT_FOUND: 'object-not-found',\n * }\n *\n * let errors: Map<Err, string> = {\n * 'generic-error': \"Unknown error\",\n * 'file-not-found': \"Could not find file: {$file}\",\n * };\n *\n * // Type-safe function - must pass a valid error code as param.\n * let error = new ErrorFactory<Err>('service', 'Service', errors);\n *\n * ...\n * throw error.create(Err.GENERIC);\n * ...\n * throw error.create(Err.FILE_NOT_FOUND, {'file': fileName});\n * ...\n * // Service: Could not file file: foo.txt (service/file-not-found).\n *\n * catch (e) {\n * assert(e.message === \"Could not find file: foo.txt.\");\n * if (e.code === 'service/file-not-found') {\n * console.log(\"Could not read file: \" + e['file']);\n * }\n * }\n */\n\nexport type ErrorMap<ErrorCode extends string> = {\n readonly [K in ErrorCode]: string;\n};\n\nconst ERROR_NAME = 'FirebaseError';\n\nexport interface StringLike {\n toString(): string;\n}\n\nexport interface ErrorData {\n [key: string]: StringLike | undefined;\n}\n\nexport interface FirebaseError extends Error, ErrorData {\n // Unique code for error - format is service/error-code-string.\n readonly code: string;\n\n // Developer-friendly error message.\n readonly message: string;\n\n // Always 'FirebaseError'.\n readonly name: typeof ERROR_NAME;\n\n // Where available - stack backtrace in a string.\n readonly stack?: string;\n}\n\n// Based on code from:\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types\nexport class FirebaseError extends Error {\n readonly name = ERROR_NAME;\n\n constructor(readonly code: string, message: string) {\n super(message);\n\n // Fix For ES5\n // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work\n Object.setPrototypeOf(this, FirebaseError.prototype);\n\n // Maintains proper stack trace for where our error was thrown.\n // Only available on V8.\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ErrorFactory.prototype.create);\n }\n }\n}\n\nexport class ErrorFactory<\n ErrorCode extends string,\n ErrorParams extends { readonly [K in ErrorCode]?: ErrorData } = {}\n> {\n constructor(\n private readonly service: string,\n private readonly serviceName: string,\n private readonly errors: ErrorMap<ErrorCode>\n ) {}\n\n create<K extends ErrorCode>(\n code: K,\n ...data: K extends keyof ErrorParams ? [ErrorParams[K]] : []\n ): FirebaseError {\n const customData = (data[0] as ErrorData) || {};\n const fullCode = `${this.service}/${code}`;\n const template = this.errors[code];\n\n const message = template ? replaceTemplate(template, customData) : 'Error';\n // Service Name: Error message (service/code).\n const fullMessage = `${this.serviceName}: ${message} (${fullCode}).`;\n\n const error = new FirebaseError(fullCode, fullMessage);\n\n // Keys with an underscore at the end of their name are not included in\n // error.data for some reason.\n // TODO: Replace with Object.entries when lib is updated to es2017.\n for (const key of Object.keys(customData)) {\n if (key.slice(-1) !== '_') {\n if (key in error) {\n console.warn(\n `Overwriting FirebaseError base field \"${key}\" can cause unexpected behavior.`\n );\n }\n error[key] = customData[key];\n }\n }\n\n return error;\n }\n}\n\nfunction replaceTemplate(template: string, data: ErrorData): string {\n return template.replace(PATTERN, (_, key) => {\n const value = data[key];\n return value != null ? value.toString() : `<${key}?>`;\n });\n}\n\nconst PATTERN = /\\{\\$([^}]+)}/g;\n","/**\n * @license\n * Copyright 2019 Google LLC\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 */\nimport {\n InstantiationMode,\n InstanceFactory,\n ComponentType,\n Dictionary,\n Name\n} from './types';\n\n/**\n * Component for service name T, e.g. `auth`, `auth-internal`\n */\nexport class Component<T extends Name = Name> {\n multipleInstances = false;\n /**\n * Properties to be added to the service namespace\n */\n serviceProps: Dictionary = {};\n\n instantiationMode = InstantiationMode.LAZY;\n\n /**\n *\n * @param name The public service name, e.g. app, auth, firestore, database\n * @param instanceFactory Service factory responsible for creating the public interface\n * @param type whether the service provided by the component is public or private\n */\n constructor(\n readonly name: T,\n readonly instanceFactory: InstanceFactory<T>,\n readonly type: ComponentType\n ) {}\n\n setInstantiationMode(mode: InstantiationMode): this {\n this.instantiationMode = mode;\n return this;\n }\n\n setMultipleInstances(multipleInstances: boolean): this {\n this.multipleInstances = multipleInstances;\n return this;\n }\n\n setServiceProps(props: Dictionary): this {\n this.serviceProps = props;\n return this;\n }\n}\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Bootstrap for the Google JS Library (Closure).\n *\n * In uncompiled mode base.js will attempt to load Closure's deps file, unless\n * the global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects\n * to include their own deps file(s) from different locations.\n *\n * Avoid including base.js more than once. This is strictly discouraged and not\n * supported. goog.require(...) won't work properly in that case.\n *\n * @provideGoog\n */\n\n\n/**\n * @define {boolean} Overridden to true by the compiler.\n */\nvar COMPILED = false;\n\n\n/**\n * Base namespace for the Closure library. Checks to see goog is already\n * defined in the current scope before assigning to prevent clobbering if\n * base.js is loaded more than once.\n *\n * @const\n */\nvar goog = goog || {};\n\n/**\n * Reference to the global object.\n * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-global-object\n *\n * More info on this implementation here:\n * https://docs.google.com/document/d/1NAeW4Wk7I7FV0Y2tcUFvQdGMc89k2vdgSXInw8_nvCI/edit\n *\n * @const\n * @suppress {undefinedVars} self won't be referenced unless `this` is falsy.\n * @type {!Global}\n */\ngoog.global =\n // Check `this` first for backwards compatibility.\n // Valid unless running as an ES module or in a function wrapper called\n // without setting `this` properly.\n // Note that base.js can't usefully be imported as an ES module, but it may\n // be compiled into bundles that are loadable as ES modules.\n this ||\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/self\n // For in-page browser environments and workers.\n self;\n\n\n/**\n * A hook for overriding the define values in uncompiled mode.\n *\n * In uncompiled mode, `CLOSURE_UNCOMPILED_DEFINES` may be defined before\n * loading base.js. If a key is defined in `CLOSURE_UNCOMPILED_DEFINES`,\n * `goog.define` will use the value instead of the default value. This\n * allows flags to be overwritten without compilation (this is normally\n * accomplished with the compiler's \"define\" flag).\n *\n * Example:\n * <pre>\n * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_UNCOMPILED_DEFINES;\n\n\n/**\n * A hook for overriding the define values in uncompiled or compiled mode,\n * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In\n * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.\n *\n * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or\n * string literals or the compiler will emit an error.\n *\n * While any @define value may be set, only those set with goog.define will be\n * effective for uncompiled code.\n *\n * Example:\n * <pre>\n * var CLOSURE_DEFINES = {'goog.DEBUG': false} ;\n * </pre>\n *\n * @type {Object<string, (string|number|boolean)>|undefined}\n */\ngoog.global.CLOSURE_DEFINES;\n\n\n/**\n * Builds an object structure for the provided namespace path, ensuring that\n * names that already exist are not overwritten. For example:\n * \"a.b.c\" -> a = {};a.b={};a.b.c={};\n * Used by goog.provide and goog.exportSymbol.\n * @param {string} name name of the object that this file defines.\n * @param {*=} opt_object the object to expose at the end of the path.\n * @param {Object=} opt_objectToExportTo The object to add the path to; default\n * is `goog.global`.\n * @private\n */\ngoog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {\n var parts = name.split('.');\n var cur = opt_objectToExportTo || goog.global;\n\n // Internet Explorer exhibits strange behavior when throwing errors from\n // methods externed in this manner. See the testExportSymbolExceptions in\n // base_test.html for an example.\n if (!(parts[0] in cur) && typeof cur.execScript != 'undefined') {\n cur.execScript('var ' + parts[0]);\n }\n\n for (var part; parts.length && (part = parts.shift());) {\n if (!parts.length && opt_object !== undefined) {\n // last part and we have an object; use it\n cur[part] = opt_object;\n } else if (cur[part] && cur[part] !== Object.prototype[part]) {\n cur = cur[part];\n } else {\n cur = cur[part] = {};\n }\n }\n};\n\n\n/**\n * Defines a named value. In uncompiled mode, the value is retrieved from\n * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and\n * has the property specified, and otherwise used the defined defaultValue.\n * When compiled the default can be overridden using the compiler options or the\n * value set in the CLOSURE_DEFINES object. Returns the defined value so that it\n * can be used safely in modules. Note that the value type MUST be either\n * boolean, number, or string.\n *\n * @param {string} name The distinguished name to provide.\n * @param {T} defaultValue\n * @return {T} The defined value.\n * @template T\n */\ngoog.define = function(name, defaultValue) {\n var value = defaultValue;\n if (!COMPILED) {\n var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES;\n var defines = goog.global.CLOSURE_DEFINES;\n if (uncompiledDefines &&\n // Anti DOM-clobbering runtime check (b/37736576).\n /** @type {?} */ (uncompiledDefines).nodeType === undefined &&\n Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) {\n value = uncompiledDefines[name];\n } else if (\n defines &&\n // Anti DOM-clobbering runtime check (b/37736576).\n /** @type {?} */ (defines).nodeType === undefined &&\n Object.prototype.hasOwnProperty.call(defines, name)) {\n value = defines[name];\n }\n }\n return value;\n};\n\n\n/**\n * @define {number} Integer year indicating the set of browser features that are\n * guaranteed to be present. This is defined to include exactly features that\n * work correctly on all \"modern\" browsers that are stable on January 1 of the\n * specified year. For example,\n * ```js\n * if (goog.FEATURESET_YEAR >= 2019) {\n * // use APIs known to be available on all major stable browsers Jan 1, 2019\n * } else {\n * // polyfill for older browsers\n * }\n * ```\n * This is intended to be the primary define for removing\n * unnecessary browser compatibility code (such as ponyfills and workarounds),\n * and should inform the default value for most other defines:\n * ```js\n * const ASSUME_NATIVE_PROMISE =\n * goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016);\n * ```\n *\n * The default assumption is that IE9 is the lowest supported browser, which was\n * first available Jan 1, 2012.\n *\n * TODO(user): Reference more thorough documentation when it's available.\n */\ngoog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012);\n\n\n/**\n * @define {boolean} DEBUG is provided as a convenience so that debugging code\n * that should not be included in a production. It can be easily stripped\n * by specifying --define goog.DEBUG=false to the Closure Compiler aka\n * JSCompiler. For example, most toString() methods should be declared inside an\n * \"if (goog.DEBUG)\" conditional because they are generally used for debugging\n * purposes and it is difficult for the JSCompiler to statically determine\n * whether they are used.\n */\ngoog.DEBUG = goog.define('goog.DEBUG', true);\n\n\n/**\n * @define {string} LOCALE defines the locale being used for compilation. It is\n * used to select locale specific data to be compiled in js binary. BUILD rule\n * can specify this value by \"--define goog.LOCALE=<locale_name>\" as a compiler\n * option.\n *\n * Take into account that the locale code format is important. You should use\n * the canonical Unicode format with hyphen as a delimiter. Language must be\n * lowercase, Language Script - Capitalized, Region - UPPERCASE.\n * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.\n *\n * See more info about locale codes here:\n * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers\n *\n * For language codes you should use values defined by ISO 693-1. See it here\n * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from\n * this rule: the Hebrew language. For legacy reasons the old code (iw) should\n * be used instead of the new code (he).\n *\n */\ngoog.LOCALE = goog.define('goog.LOCALE', 'en'); // default to en\n\n\n/**\n * @define {boolean} Whether this code is running on trusted sites.\n *\n * On untrusted sites, several native functions can be defined or overridden by\n * external libraries like Prototype, Datejs, and JQuery and setting this flag\n * to false forces closure to use its own implementations when possible.\n *\n * If your JavaScript can be loaded by a third party site and you are wary about\n * relying on non-standard implementations, specify\n * \"--define goog.TRUSTED_SITE=false\" to the compiler.\n */\ngoog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true);\n\n\n/**\n * @define {boolean} Whether a project is expected to be running in strict mode.\n *\n * This define can be used to trigger alternate implementations compatible with\n * running in EcmaScript Strict mode or warn about unavailable functionality.\n * @see https://goo.gl/PudQ4y\n *\n */\ngoog.STRICT_MODE_COMPATIBLE = goog.define('goog.STRICT_MODE_COMPATIBLE', false);\n\n\n/**\n * @define {boolean} Whether code that calls {@link goog.setTestOnly} should\n * be disallowed in the compilation unit.\n */\ngoog.DISALLOW_TEST_ONLY_CODE =\n goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);\n\n\n/**\n * @define {boolean} Whether to use a Chrome app CSP-compliant method for\n * loading scripts via goog.require. @see appendScriptSrcNode_.\n */\ngoog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING =\n goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);\n\n\n/**\n * Defines a namespace in Closure.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * The presence of one or more goog.provide() calls in a file indicates\n * that the file defines the given objects/namespaces.\n * Provided symbols must not be null or undefined.\n *\n * In addition, goog.provide() creates the object stubs for a namespace\n * (for example, goog.provide(\"goog.foo.bar\") will create the object\n * goog.foo.bar if it does not already exist).\n *\n * Build tools also scan for provide/require/module statements\n * to discern dependencies, build dependency files (see deps.js), etc.\n *\n * @see goog.require\n * @see goog.module\n * @param {string} name Namespace provided by this file in the form\n * \"goog.package.part\".\n */\ngoog.provide = function(name) {\n if (goog.isInModuleLoader_()) {\n throw new Error('goog.provide cannot be used within a module.');\n }\n if (!COMPILED) {\n // Ensure that the same namespace isn't provided twice.\n // A goog.module/goog.provide maps a goog.require to a specific file\n if (goog.isProvided_(name)) {\n throw new Error('Namespace \"' + name + '\" already declared.');\n }\n }\n\n goog.constructNamespace_(name);\n};\n\n\n/**\n * @param {string} name Namespace provided by this file in the form\n * \"goog.package.part\".\n * @param {Object=} opt_obj The object to embed in the namespace.\n * @private\n */\ngoog.constructNamespace_ = function(name, opt_obj) {\n if (!COMPILED) {\n delete goog.implicitNamespaces_[name];\n\n var namespace = name;\n while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {\n if (goog.getObjectByName(namespace)) {\n break;\n }\n goog.implicitNamespaces_[namespace] = true;\n }\n }\n\n goog.exportPath_(name, opt_obj);\n};\n\n\n/**\n * Returns CSP nonce, if set for any script tag.\n * @param {?Window=} opt_window The window context used to retrieve the nonce.\n * Defaults to global context.\n * @return {string} CSP nonce or empty string if no nonce is present.\n */\ngoog.getScriptNonce = function(opt_window) {\n if (opt_window && opt_window != goog.global) {\n return goog.getScriptNonce_(opt_window.document);\n }\n if (goog.cspNonce_ === null) {\n goog.cspNonce_ = goog.getScriptNonce_(goog.global.document);\n }\n return goog.cspNonce_;\n};\n\n\n/**\n * According to the CSP3 spec a nonce must be a valid base64 string.\n * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value\n * @private @const\n */\ngoog.NONCE_PATTERN_ = /^[\\w+/_-]+[=]{0,2}$/;\n\n\n/**\n * @private {?string}\n */\ngoog.cspNonce_ = null;\n\n\n/**\n * Returns CSP nonce, if set for any script tag.\n * @param {!Document} doc\n * @return {string} CSP nonce or empty string if no nonce is present.\n * @private\n */\ngoog.getScriptNonce_ = function(doc) {\n var script = doc.querySelector && doc.querySelector('script[nonce]');\n if (script) {\n // Try to get the nonce from the IDL property first, because browsers that\n // implement additional nonce protection features (currently only Chrome) to\n // prevent nonce stealing via CSS do not expose the nonce via attributes.\n // See https://github.com/whatwg/html/issues/2369\n var nonce = script['nonce'] || script.getAttribute('nonce');\n if (nonce && goog.NONCE_PATTERN_.test(nonce)) {\n return nonce;\n }\n }\n return '';\n};\n\n\n/**\n * Module identifier validation regexp.\n * Note: This is a conservative check, it is very possible to be more lenient,\n * the primary exclusion here is \"/\" and \"\\\" and a leading \".\", these\n * restrictions are intended to leave the door open for using goog.require\n * with relative file paths rather than module identifiers.\n * @private\n */\ngoog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;\n\n\n/**\n * Defines a module in Closure.\n *\n * Marks that this file must be loaded as a module and claims the namespace.\n *\n * A namespace may only be defined once in a codebase. It may be defined using\n * goog.provide() or goog.module().\n *\n * goog.module() has three requirements:\n * - goog.module may not be used in the same file as goog.provide.\n * - goog.module must be the first statement in the file.\n * - only one goog.module is allowed per file.\n *\n * When a goog.module annotated file is loaded, it is enclosed in\n * a strict function closure. This means that:\n * - any variables declared in a goog.module file are private to the file\n * (not global), though the compiler is expected to inline the module.\n * - The code must obey all the rules of \"strict\" JavaScript.\n * - the file will be marked as \"use strict\"\n *\n * NOTE: unlike goog.provide, goog.module does not declare any symbols by\n * itself. If declared symbols are desired, use\n * goog.module.declareLegacyNamespace().\n *\n *\n * See the public goog.module proposal: http://goo.gl/Va1hin\n *\n * @param {string} name Namespace provided by this file in the form\n * \"goog.package.part\", is expected but not required.\n * @return {void}\n */\ngoog.module = function(name) {\n if (typeof name !== 'string' || !name ||\n name.search(goog.VALID_MODULE_RE_) == -1) {\n throw new Error('Invalid module identifier');\n }\n if (!goog.isInGoogModuleLoader_()) {\n throw new Error(\n 'Module ' + name + ' has been loaded incorrectly. Note, ' +\n 'modules cannot be loaded as normal scripts. They require some kind of ' +\n 'pre-processing step. You\\'re likely trying to load a module via a ' +\n 'script tag or as a part of a concatenated bundle without rewriting the ' +\n 'module. For more info see: ' +\n 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.');\n }\n if (goog.moduleLoaderState_.moduleName) {\n throw new Error('goog.module may only be called once per module.');\n }\n\n // Store the module name for the loader.\n goog.moduleLoaderState_.moduleName = name;\n if (!COMPILED) {\n // Ensure that the same namespace isn't provided twice.\n // A goog.module/goog.provide maps a goog.require to a specific file\n if (goog.isProvided_(name)) {\n throw new Error('Namespace \"' + name + '\" already declared.');\n }\n delete goog.implicitNamespaces_[name];\n }\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n *\n * Note: This is not an alternative to goog.require, it does not\n * indicate a hard dependency, instead it is used to indicate\n * an optional dependency or to access the exports of a module\n * that has already been loaded.\n * @suppress {missingProvide}\n */\ngoog.module.get = function(name) {\n return goog.module.getInternal_(name);\n};\n\n\n/**\n * @param {string} name The module identifier.\n * @return {?} The module exports for an already loaded module or null.\n * @private\n */\ngoog.module.getInternal_ = function(name) {\n if (!COMPILED) {\n if (name in goog.loadedModules_) {\n return goog.loadedModules_[name].exports;\n } else if (!goog.implicitNamespaces_[name]) {\n var ns = goog.getObjectByName(name);\n return ns != null ? ns : null;\n }\n }\n return null;\n};\n\n\n/**\n * Types of modules the debug loader can load.\n * @enum {string}\n */\ngoog.ModuleType = {\n ES6: 'es6',\n GOOG: 'goog'\n};\n\n\n/**\n * @private {?{\n * moduleName: (string|undefined),\n * declareLegacyNamespace:boolean,\n * type: ?goog.ModuleType\n * }}\n */\ngoog.moduleLoaderState_ = null;\n\n\n/**\n * @private\n * @return {boolean} Whether a goog.module or an es6 module is currently being\n * initialized.\n */\ngoog.isInModuleLoader_ = function() {\n return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_();\n};\n\n\n/**\n * @private\n * @return {boolean} Whether a goog.module is currently being initialized.\n */\ngoog.isInGoogModuleLoader_ = function() {\n return !!goog.moduleLoaderState_ &&\n goog.moduleLoaderState_.type == goog.ModuleType.GOOG;\n};\n\n\n/**\n * @private\n * @return {boolean} Whether an es6 module is currently being initialized.\n */\ngoog.isInEs6ModuleLoader_ = function() {\n var inLoader = !!goog.moduleLoaderState_ &&\n goog.moduleLoaderState_.type == goog.ModuleType.ES6;\n\n if (inLoader) {\n return true;\n }\n\n var jscomp = goog.global['$jscomp'];\n\n if (jscomp) {\n // jscomp may not have getCurrentModulePath if this is a compiled bundle\n // that has some of the runtime, but not all of it. This can happen if\n // optimizations are turned on so the unused runtime is removed but renaming\n // and Closure pass are off (so $jscomp is still named $jscomp and the\n // goog.provide/require calls still exist).\n if (typeof jscomp.getCurrentModulePath != 'function') {\n return false;\n }\n\n // Bundled ES6 module.\n return !!jscomp.getCurrentModulePath();\n }\n\n return false;\n};\n\n\n/**\n * Provide the module's exports as a globally accessible object under the\n * module's declared name. This is intended to ease migration to goog.module\n * for files that have existing usages.\n * @suppress {missingProvide}\n */\ngoog.module.declareLegacyNamespace = function() {\n if (!COMPILED && !goog.isInGoogModuleLoader_()) {\n throw new Error(\n 'goog.module.declareLegacyNamespace must be called from ' +\n 'within a goog.module');\n }\n if (!COMPILED && !goog.moduleLoaderState_.moduleName) {\n throw new Error(\n 'goog.module must be called prior to ' +\n 'goog.module.declareLegacyNamespace.');\n }\n goog.moduleLoaderState_.declareLegacyNamespace = true;\n};\n\n\n/**\n * Associates an ES6 module with a Closure module ID so that is available via\n * goog.require. The associated ID acts like a goog.module ID - it does not\n * create any global names, it is merely available via goog.require /\n * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and\n * goog.module.get will return the entire module as if it was import *'d. This\n * allows Closure files to reference ES6 modules for the sake of migration.\n *\n * @param {string} namespace\n * @suppress {missingProvide}\n */\ngoog.declareModuleId = function(namespace) {\n if (!COMPILED) {\n if (!goog.isInEs6ModuleLoader_()) {\n throw new Error(\n 'goog.declareModuleId may only be called from ' +\n 'within an ES6 module');\n }\n if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) {\n throw new Error(\n 'goog.declareModuleId may only be called once per module.');\n }\n if (namespace in goog.loadedModules_) {\n throw new Error(\n 'Module with namespace \"' + namespace + '\" already exists.');\n }\n }\n if (goog.moduleLoaderState_) {\n // Not bundled - debug loading.\n goog.moduleLoaderState_.moduleName = namespace;\n } else {\n // Bundled - not debug loading, no module loader state.\n var jscomp = goog.global['$jscomp'];\n if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') {\n throw new Error(\n 'Module with namespace \"' + namespace +\n '\" has been loaded incorrectly.');\n }\n var exports = jscomp.require(jscomp.getCurrentModulePath());\n goog.loadedModules_[namespace] = {\n exports: exports,\n type: goog.ModuleType.ES6,\n moduleId: namespace\n };\n }\n};\n\n\n/**\n * Marks that the current file should only be used for testing, and never for\n * live code in production.\n *\n * In the case of unit tests, the message may optionally be an exact namespace\n * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra\n * provide (if not explicitly defined in the code).\n *\n * @param {string=} opt_message Optional message to add to the error that's\n * raised when used in production code.\n */\ngoog.setTestOnly = function(opt_message) {\n if (goog.DISALLOW_TEST_ONLY_CODE) {\n opt_message = opt_message || '';\n throw new Error(\n 'Importing test-only code into non-debug environment' +\n (opt_message ? ': ' + opt_message : '.'));\n }\n};\n\n\n/**\n * Forward declares a symbol. This is an indication to the compiler that the\n * symbol may be used in the source yet is not required and may not be provided\n * in compilation.\n *\n * The most common usage of forward declaration is code that takes a type as a\n * function parameter but does not need to require it. By forward declaring\n * instead of requiring, no hard dependency is made, and (if not required\n * elsewhere) the namespace may never be required and thus, not be pulled\n * into the JavaScript binary. If it is required elsewhere, it will be type\n * checked as normal.\n *\n * Before using goog.forwardDeclare, please read the documentation at\n * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to\n * understand the options and tradeoffs when working with forward declarations.\n *\n * @param {string} name The namespace to forward declare in the form of\n * \"goog.package.part\".\n * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead.\n */\ngoog.forwardDeclare = function(name) {};\n\n\n/**\n * Forward declare type information. Used to assign types to goog.global\n * referenced object that would otherwise result in unknown type references\n * and thus block property disambiguation.\n */\ngoog.forwardDeclare('Document');\ngoog.forwardDeclare('HTMLScriptElement');\ngoog.forwardDeclare('XMLHttpRequest');\n\n\nif (!COMPILED) {\n /**\n * Check if the given name has been goog.provided. This will return false for\n * names that are available only as implicit namespaces.\n * @param {string} name name of the object to look for.\n * @return {boolean} Whether the name has been provided.\n * @private\n */\n goog.isProvided_ = function(name) {\n return (name in goog.loadedModules_) ||\n (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null);\n };\n\n /**\n * Namespaces implicitly defined by goog.provide. For example,\n * goog.provide('goog.events.Event') implicitly declares that 'goog' and\n * 'goog.events' must be namespaces.\n *\n * @type {!Object<string, (boolean|undefined)>}\n * @private\n */\n goog.implicitNamespaces_ = {'goog.module': true};\n\n // NOTE: We add goog.module as an implicit namespace as goog.module is defined\n // here and because the existing module package has not been moved yet out of\n // the goog.module namespace. This satisifies both the debug loader and\n // ahead-of-time dependency management.\n}\n\n\n/**\n * Returns an object based on its fully qualified external name. The object\n * is not found if null or undefined. If you are using a compilation pass that\n * renames property names beware that using this function will not find renamed\n * properties.\n *\n * @param {string} name The fully qualified name.\n * @param {Object=} opt_obj The object within which to look; default is\n * |goog.global|.\n * @return {?} The value (object or primitive) or, if not found, null.\n */\ngoog.getObjectByName = function(name, opt_obj) {\n var parts = name.split('.');\n var cur = opt_obj || goog.global;\n for (var i = 0; i < parts.length; i++) {\n cur = cur[parts[i]];\n if (cur == null) {\n return null;\n }\n }\n return cur;\n};\n\n\n/**\n * Globalizes a whole namespace, such as goog or goog.lang.\n *\n * @param {!Object} obj The namespace to globalize.\n * @param {Object=} opt_global The object to add the properties to.\n * @deprecated Properties may be explicitly exported to the global scope, but\n * this should no longer be done in bulk.\n */\ngoog.globalize = function(obj, opt_global) {\n var global = opt_global || goog.global;\n for (var x in obj) {\n global[x] = obj[x];\n }\n};\n\n\n/**\n * Adds a dependency from a file to the files it requires.\n * @param {string} relPath The path to the js file.\n * @param {!Array<string>} provides An array of strings with\n * the names of the objects this file provides.\n * @param {!Array<string>} requires An array of strings with\n * the names of the objects this file requires.\n * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating\n * how the file must be loaded. The boolean 'true' is equivalent\n * to {'module': 'goog'} for backwards-compatibility. Valid properties\n * and values include {'module': 'goog'} and {'lang': 'es6'}.\n */\ngoog.addDependency = function(relPath, provides, requires, opt_loadFlags) {\n if (!COMPILED && goog.DEPENDENCIES_ENABLED) {\n goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags);\n }\n};\n\n\n// NOTE(nnaze): The debug DOM loader was included in base.js as an original way\n// to do \"debug-mode\" development. The dependency system can sometimes be\n// confusing, as can the debug DOM loader's asynchronous nature.\n//\n// With the DOM loader, a call to goog.require() is not blocking -- the script\n// will not load until some point after the current script. If a namespace is\n// needed at runtime, it needs to be defined in a previous script, or loaded via\n// require() with its registered dependencies.\n//\n// User-defined namespaces may need their own deps file. For a reference on\n// creating a deps file, see:\n// Externally: https://developers.google.com/closure/library/docs/depswriter\n//\n// Because of legacy clients, the DOM loader can't be easily removed from\n// base.js. Work was done to make it disableable or replaceable for\n// different environments (DOM-less JavaScript interpreters like Rhino or V8,\n// for example). See bootstrap/ for more information.\n\n\n/**\n * @define {boolean} Whether to enable the debug loader.\n *\n * If enabled, a call to goog.require() will attempt to load the namespace by\n * appending a script tag to the DOM (if the namespace has been registered).\n *\n * If disabled, goog.require() will simply assert that the namespace has been\n * provided (and depend on the fact that some outside tool correctly ordered\n * the script).\n */\ngoog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true);\n\n\n/**\n * @param {string} msg\n * @private\n */\ngoog.logToConsole_ = function(msg) {\n if (goog.global.console) {\n goog.global.console['error'](msg);\n }\n};\n\n\n/**\n * Implements a system for the dynamic resolution of dependencies that works in\n * parallel with the BUILD system.\n *\n * Note that all calls to goog.require will be stripped by the compiler.\n *\n * @see goog.provide\n * @param {string} namespace Namespace (as was given in goog.provide,\n * goog.module, or goog.declareModuleId) in the form\n * \"goog.package.part\".\n * @return {?} If called within a goog.module or ES6 module file, the associated\n * namespace or module otherwise null.\n */\ngoog.require = function(namespace) {\n if (!COMPILED) {\n // Might need to lazy load on old IE.\n if (goog.ENABLE_DEBUG_LOADER) {\n goog.debugLoader_.requested(namespace);\n }\n\n // If the object already exists we do not need to do anything.\n if (goog.isProvided_(namespace)) {\n if (goog.isInModuleLoader_()) {\n return goog.module.getInternal_(namespace);\n }\n } else if (goog.ENABLE_DEBUG_LOADER) {\n var moduleLoaderState = goog.moduleLoaderState_;\n goog.moduleLoaderState_ = null;\n try {\n goog.debugLoader_.load_(namespace);\n } finally {\n goog.moduleLoaderState_ = moduleLoaderState;\n }\n }\n\n return null;\n }\n};\n\n\n/**\n * Requires a symbol for its type information. This is an indication to the\n * compiler that the symbol may appear in type annotations, yet it is not\n * referenced at runtime.\n *\n * When called within a goog.module or ES6 module file, the return value may be\n * assigned to or destructured into a variable, but it may not be otherwise used\n * in code outside of a type annotation.\n *\n * Note that all calls to goog.requireType will be stripped by the compiler.\n *\n * @param {string} namespace Namespace (as was given in goog.provide,\n * goog.module, or goog.declareModuleId) in the form\n * \"goog.package.part\".\n * @return {?}\n */\ngoog.requireType = function(namespace) {\n // Return an empty object so that single-level destructuring of the return\n // value doesn't crash at runtime when using the debug loader. Multi-level\n // destructuring isn't supported.\n return {};\n};\n\n\n/**\n * Path for included scripts.\n * @type {string}\n */\ngoog.basePath = '';\n\n\n/**\n * A hook for overriding the base path.\n * @type {string|undefined}\n */\ngoog.global.CLOSURE_BASE_PATH;\n\n\n/**\n * Whether to attempt to load Closure's deps file. By default, when uncompiled,\n * deps files will attempt to be loaded.\n * @type {boolean|undefined}\n */\ngoog.global.CLOSURE_NO_DEPS;\n\n\n/**\n * A function to import a single script. This is meant to be overridden when\n * Closure is being run in non-HTML contexts, such as web workers. It's defined\n * in the global scope so that it can be set before base.js is loaded, which\n * allows deps.js to be imported properly.\n *\n * The first parameter the script source, which is a relative URI. The second,\n * optional parameter is the script contents, in the event the script needed\n * transformation. It should return true if the script was imported, false\n * otherwise.\n * @type {(function(string, string=): boolean)|undefined}\n */\ngoog.global.CLOSURE_IMPORT_SCRIPT;\n\n\n/**\n * Null function used for default values of callbacks, etc.\n * @return {void} Nothing.\n */\ngoog.nullFunction = function() {};\n\n\n/**\n * When defining a class Foo with an abstract method bar(), you can do:\n * Foo.prototype.bar = goog.abstractMethod\n *\n * Now if a subclass of Foo fails to override bar(), an error will be thrown\n * when bar() is invoked.\n *\n * @type {!Function}\n * @throws {Error} when invoked to indicate the method should be overridden.\n * @deprecated Use \"@abstract\" annotation instead of goog.abstractMethod in new\n * code. See\n * https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods\n */\ngoog.abstractMethod = function() {\n throw new Error('unimplemented abstract method');\n};\n\n\n/**\n * Adds a `getInstance` static method that always returns the same\n * instance object.\n * @param {!Function} ctor The constructor for the class to add the static\n * method to.\n * @suppress {missingProperties} 'instance_' isn't a property on 'Function'\n * but we don't have a better type to use here.\n */\ngoog.addSingletonGetter = function(ctor) {\n // instance_ is immediately set to prevent issues with sealed constructors\n // such as are encountered when a constructor is returned as the export object\n // of a goog.module in unoptimized code.\n // Delcare type to avoid conformance violations that ctor.instance_ is unknown\n /** @type {undefined|!Object} @suppress {underscore} */\n ctor.instance_ = undefined;\n ctor.getInstance = function() {\n if (ctor.instance_) {\n return ctor.instance_;\n }\n if (goog.DEBUG) {\n // NOTE: JSCompiler can't optimize away Array#push.\n goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;\n }\n // Cast to avoid conformance violations that ctor.instance_ is unknown\n return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor;\n };\n};\n\n\n/**\n * All singleton classes that have been instantiated, for testing. Don't read\n * it directly, use the `goog.testing.singleton` module. The compiler\n * removes this variable if unused.\n * @type {!Array<!Function>}\n * @private\n */\ngoog.instantiatedSingletons_ = [];\n\n\n/**\n * @define {boolean} Whether to load goog.modules using `eval` when using\n * the debug loader. This provides a better debugging experience as the\n * source is unmodified and can be edited using Chrome Workspaces or similar.\n * However in some environments the use of `eval` is banned\n * so we provide an alternative.\n */\ngoog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true);\n\n\n/**\n * @define {boolean} Whether the exports of goog.modules should be sealed when\n * possible.\n */\ngoog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);\n\n\n/**\n * The registry of initialized modules:\n * The module identifier or path to module exports map.\n * @private @const {!Object<string, {exports:?,type:string,moduleId:string}>}\n */\ngoog.loadedModules_ = {};\n\n\n/**\n * True if the debug loader enabled and used.\n * @const {boolean}\n */\ngoog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;\n\n\n/**\n * @define {string} How to decide whether to transpile. Valid values\n * are 'always', 'never', and 'detect'. The default ('detect') is to\n * use feature detection to determine which language levels need\n * transpilation.\n */\n// NOTE(sdh): we could expand this to accept a language level to bypass\n// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but\n// would leave ES3 and ES5 files alone.\ngoog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect');\n\n/**\n * @define {boolean} If true assume that ES modules have already been\n * transpiled by the jscompiler (in the same way that transpile.js would\n * transpile them - to jscomp modules). Useful only for servers that wish to use\n * the debug loader and transpile server side. Thus this is only respected if\n * goog.TRANSPILE is \"never\".\n */\ngoog.ASSUME_ES_MODULES_TRANSPILED =\n goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false);\n\n\n/**\n * @define {string} If a file needs to be transpiled what the output language\n * should be. By default this is the highest language level this file detects\n * the current environment supports. Generally this flag should not be set, but\n * it could be useful to override. Example: If the current environment supports\n * ES6 then by default ES7+ files will be transpiled to ES6, unless this is\n * overridden.\n *\n * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized\n * is treated as es3.\n *\n * Note that setting this value does not force transpilation. Just if\n * transpilation occurs this will be the output. So this is most useful when\n * goog.TRANSPILE is set to 'always' and then forcing the language level to be\n * something lower than what the environment detects.\n */\ngoog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', '');\n\n\n/**\n * @define {string} Path to the transpiler. Executing the script at this\n * path (relative to base.js) should define a function $jscomp.transpile.\n */\ngoog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js');\n\n\n/**\n * @package {?boolean}\n * Visible for testing.\n */\ngoog.hasBadLetScoping = null;\n\n\n/**\n * @return {boolean}\n * @package Visible for testing.\n */\ngoog.useSafari10Workaround = function() {\n if (goog.hasBadLetScoping == null) {\n var hasBadLetScoping;\n try {\n hasBadLetScoping = !eval(\n '\"use strict\";' +\n 'let x = 1; function f() { return typeof x; };' +\n 'f() == \"number\";');\n } catch (e) {\n // Assume that ES6 syntax isn't supported.\n hasBadLetScoping = false;\n }\n goog.hasBadLetScoping = hasBadLetScoping;\n }\n return goog.hasBadLetScoping;\n};\n\n\n/**\n * @param {string} moduleDef\n * @return {string}\n * @package Visible for testing.\n */\ngoog.workaroundSafari10EvalBug = function(moduleDef) {\n return '(function(){' + moduleDef +\n '\\n' + // Terminate any trailing single line comment.\n ';' + // Terminate any trailing expression.\n '})();\\n';\n};\n\n\n/**\n * @param {function(?):?|string} moduleDef The module definition.\n */\ngoog.loadModule = function(moduleDef) {\n // NOTE: we allow function definitions to be either in the from\n // of a string to eval (which keeps the original source intact) or\n // in a eval forbidden environment (CSP) we allow a function definition\n // which in its body must call `goog.module`, and return the exports\n // of the module.\n var previousState = goog.moduleLoaderState_;\n try {\n goog.moduleLoaderState_ = {\n moduleName: '',\n declareLegacyNamespace: false,\n type: goog.ModuleType.GOOG\n };\n var exports;\n if (goog.isFunction(moduleDef)) {\n exports = moduleDef.call(undefined, {});\n } else if (typeof moduleDef === 'string') {\n if (goog.useSafari10Workaround()) {\n moduleDef = goog.workaroundSafari10EvalBug(moduleDef);\n }\n\n exports = goog.loadModuleFromSource_.call(undefined, moduleDef);\n } else {\n throw new Error('Invalid module definition');\n }\n\n var moduleName = goog.moduleLoaderState_.moduleName;\n if (typeof moduleName === 'string' && moduleName) {\n // Don't seal legacy namespaces as they may be used as a parent of\n // another namespace\n if (goog.moduleLoaderState_.declareLegacyNamespace) {\n goog.constructNamespace_(moduleName, exports);\n } else if (\n goog.SEAL_MODULE_EXPORTS && Object.seal &&\n typeof exports == 'object' && exports != null) {\n Object.seal(exports);\n }\n\n var data = {\n exports: exports,\n type: goog.ModuleType.GOOG,\n moduleId: goog.moduleLoaderState_.moduleName\n };\n goog.loadedModules_[moduleName] = data;\n } else {\n throw new Error('Invalid module name \\\"' + moduleName + '\\\"');\n }\n } finally {\n goog.moduleLoaderState_ = previousState;\n }\n};\n\n\n/**\n * @private @const\n */\ngoog.loadModuleFromSource_ = /** @type {function(string):?} */ (function() {\n // NOTE: we avoid declaring parameters or local variables here to avoid\n // masking globals or leaking values into the module definition.\n 'use strict';\n var exports = {};\n eval(arguments[0]);\n return exports;\n});\n\n\n/**\n * Normalize a file path by removing redundant \"..\" and extraneous \".\" file\n * path components.\n * @param {string} path\n * @return {string}\n * @private\n */\ngoog.normalizePath_ = function(path) {\n var components = path.split('/');\n var i = 0;\n while (i < components.length) {\n if (components[i] == '.') {\n components.splice(i, 1);\n } else if (\n i && components[i] == '..' && components[i - 1] &&\n components[i - 1] != '..') {\n components.splice(--i, 2);\n } else {\n i++;\n }\n }\n return components.join('/');\n};\n\n\n/**\n * Provides a hook for loading a file when using Closure's goog.require() API\n * with goog.modules. In particular this hook is provided to support Node.js.\n *\n * @type {(function(string):string)|undefined}\n */\ngoog.global.CLOSURE_LOAD_FILE_SYNC;\n\n\n/**\n * Loads file by synchronous XHR. Should not be used in production environments.\n * @param {string} src Source URL.\n * @return {?string} File contents, or null if load failed.\n * @private\n */\ngoog.loadFileSync_ = function(src) {\n if (goog.global.CLOSURE_LOAD_FILE_SYNC) {\n return goog.global.CLOSURE_LOAD_FILE_SYNC(src);\n } else {\n try {\n /** @type {XMLHttpRequest} */\n var xhr = new goog.global['XMLHttpRequest']();\n xhr.open('get', src, false);\n xhr.send();\n // NOTE: Successful http: requests have a status of 200, but successful\n // file: requests may have a status of zero. Any other status, or a\n // thrown exception (particularly in case of file: requests) indicates\n // some sort of error, which we treat as a missing or unavailable file.\n return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null;\n } catch (err) {\n // No need to rethrow or log, since errors should show up on their own.\n return null;\n }\n }\n};\n\n\n/**\n * Lazily retrieves the transpiler and applies it to the source.\n * @param {string} code JS code.\n * @param {string} path Path to the code.\n * @param {string} target Language level output.\n * @return {string} The transpiled code.\n * @private\n */\ngoog.transpile_ = function(code, path, target) {\n var jscomp = goog.global['$jscomp'];\n if (!jscomp) {\n goog.global['$jscomp'] = jscomp = {};\n }\n var transpile = jscomp.transpile;\n if (!transpile) {\n var transpilerPath = goog.basePath + goog.TRANSPILER;\n var transpilerCode = goog.loadFileSync_(transpilerPath);\n if (transpilerCode) {\n // This must be executed synchronously, since by the time we know we\n // need it, we're about to load and write the ES6 code synchronously,\n // so a normal script-tag load will be too slow. Wrapped in a function\n // so that code is eval'd in the global scope.\n (function() {\n (0, eval)(transpilerCode + '\\n//# sourceURL=' + transpilerPath);\n }).call(goog.global);\n // Even though the transpiler is optional, if $gwtExport is found, it's\n // a sign the transpiler was loaded and the $jscomp.transpile *should*\n // be there.\n if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] &&\n !goog.global['$gwtExport']['$jscomp']['transpile']) {\n throw new Error(\n 'The transpiler did not properly export the \"transpile\" ' +\n 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport']));\n }\n // transpile.js only exports a single $jscomp function, transpile. We\n // grab just that and add it to the existing definition of $jscomp which\n // contains the polyfills.\n goog.global['$jscomp'].transpile =\n goog.global['$gwtExport']['$jscomp']['transpile'];\n jscomp = goog.global['$jscomp'];\n transpile = jscomp.transpile;\n }\n }\n if (!transpile) {\n // The transpiler is an optional component. If it's not available then\n // replace it with a pass-through function that simply logs.\n var suffix = ' requires transpilation but no transpiler was found.';\n transpile = jscomp.transpile = function(code, path) {\n // TODO(sdh): figure out some way to get this error to show up\n // in test results, noting that the failure may occur in many\n // different ways, including in loadModule() before the test\n // runner even comes up.\n goog.logToConsole_(path + suffix);\n return code;\n };\n }\n // Note: any transpilation errors/warnings will be logged to the console.\n return transpile(code, path, target);\n};\n\n//==============================================================================\n// Language Enhancements\n//==============================================================================\n\n\n/**\n * This is a \"fixed\" version of the typeof operator. It differs from the typeof\n * operator in such a way that null returns 'null' and arrays return 'array'.\n * @param {?} value The value to get the type of.\n * @return {string} The name of the type.\n */\ngoog.typeOf = function(value) {\n var s = typeof value;\n if (s == 'object') {\n if (value) {\n // Check these first, so we can avoid calling Object.prototype.toString if\n // possible.\n //\n // IE9 and below improperly marshals typeof across execution contexts, but\n // a cross-context object will still return false for \"instanceof Object\".\n if (value instanceof Array) {\n return 'array';\n } else if (value instanceof Object) {\n return s;\n }\n\n // HACK: In order to use an Object prototype method on the arbitrary\n // value, the compiler requires the value be cast to type Object,\n // even though the ECMA spec explicitly allows it.\n var className = Object.prototype.toString.call(\n /** @type {!Object} */ (value));\n // In Firefox 3.6, attempting to access iframe window objects' length\n // property throws an NS_ERROR_FAILURE, so we need to special-case it\n // here.\n if (className == '[object Window]') {\n return 'object';\n }\n\n // We cannot always use constructor == Array or instanceof Array because\n // different frames have different Array objects. In IE6, if the iframe\n // where the array was created is destroyed, the array loses its\n // prototype. Then dereferencing val.splice here throws an exception, so\n // we can't use goog.isFunction. Calling typeof directly returns 'unknown'\n // so that will work. In this case, this function will return false and\n // most array functions will still work because the array is still\n // array-like (supports length and []) even though it has lost its\n // prototype.\n // Mark Miller noticed that Object.prototype.toString\n // allows access to the unforgeable [[Class]] property.\n // 15.2.4.2 Object.prototype.toString ( )\n // When the toString method is called, the following steps are taken:\n // 1. Get the [[Class]] property of this object.\n // 2. Compute a string value by concatenating the three strings\n // \"[object \", Result(1), and \"]\".\n // 3. Return Result(2).\n // and this behavior survives the destruction of the execution context.\n if ((className == '[object Array]' ||\n // In IE all non value types are wrapped as objects across window\n // boundaries (not iframe though) so we have to do object detection\n // for this edge case.\n typeof value.length == 'number' &&\n typeof value.splice != 'undefined' &&\n typeof value.propertyIsEnumerable != 'undefined' &&\n !value.propertyIsEnumerable('splice')\n\n )) {\n return 'array';\n }\n // HACK: There is still an array case that fails.\n // function ArrayImpostor() {}\n // ArrayImpostor.prototype = [];\n // var impostor = new ArrayImpostor;\n // this can be fixed by getting rid of the fast path\n // (value instanceof Array) and solely relying on\n // (value && Object.prototype.toString.vall(value) === '[object Array]')\n // but that would require many more function calls and is not warranted\n // unless closure code is receiving objects from untrusted sources.\n\n // IE in cross-window calls does not correctly marshal the function type\n // (it appears just as an object) so we cannot use just typeof val ==\n // 'function'. However, if the object has a call property, it is a\n // function.\n if ((className == '[object Function]' ||\n typeof value.call != 'undefined' &&\n typeof value.propertyIsEnumerable != 'undefined' &&\n !value.propertyIsEnumerable('call'))) {\n return 'function';\n }\n\n } else {\n return 'null';\n }\n\n } else if (s == 'function' && typeof value.call == 'undefined') {\n // In Safari typeof nodeList returns 'function', and on Firefox typeof\n // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We\n // would like to return object for those and we can detect an invalid\n // function by making sure that the function object has a call method.\n return 'object';\n }\n return s;\n};\n\n\n/**\n * Returns true if the specified value is an array.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an array.\n * @deprecated Use Array.isArray instead.\n */\ngoog.isArray = function(val) {\n return goog.typeOf(val) == 'array';\n};\n\n\n/**\n * Returns true if the object looks like an array. To qualify as array like\n * the value needs to be either a NodeList or an object with a Number length\n * property. Note that for this function neither strings nor functions are\n * considered \"array-like\".\n *\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an array.\n */\ngoog.isArrayLike = function(val) {\n var type = goog.typeOf(val);\n // We do not use goog.isObject here in order to exclude function values.\n return type == 'array' || type == 'object' && typeof val.length == 'number';\n};\n\n\n/**\n * Returns true if the object looks like a Date. To qualify as Date-like the\n * value needs to be an object and have a getFullYear() function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a like a Date.\n */\ngoog.isDateLike = function(val) {\n return goog.isObject(val) && typeof val.getFullYear == 'function';\n};\n\n\n/**\n * Returns true if the specified value is a function.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is a function.\n */\ngoog.isFunction = function(val) {\n return goog.typeOf(val) == 'function';\n};\n\n\n/**\n * Returns true if the specified value is an object. This includes arrays and\n * functions.\n * @param {?} val Variable to test.\n * @return {boolean} Whether variable is an object.\n */\ngoog.isObject = function(val) {\n var type = typeof val;\n return type == 'object' && val != null || type == 'function';\n // return Object(val) === val also works, but is slower, especially if val is\n // not an object.\n};\n\n\n/**\n * Gets a unique ID for an object. This mutates the object so that further calls\n * with the same object as a parameter returns the same value. The unique ID is\n * guaranteed to be unique across the current session amongst objects that are\n * passed into `getUid`. There is no guarantee that the ID is unique or\n * consistent across sessions. It is unsafe to generate unique ID for function\n * prototypes.\n *\n * @param {Object} obj The object to get the unique ID for.\n * @return {number} The unique ID for the object.\n */\ngoog.getUid = function(obj) {\n // TODO(arv): Make the type stricter, do not accept null.\n return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) &&\n obj[goog.UID_PROPERTY_] ||\n (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);\n};\n\n\n/**\n * Whether the given object is already assigned a unique ID.\n *\n * This does not modify the object.\n *\n * @param {!Object} obj The object to check.\n * @return {boolean} Whether there is an assigned unique id for the object.\n */\ngoog.hasUid = function(obj) {\n return !!obj[goog.UID_PROPERTY_];\n};\n\n\n/**\n * Removes the unique ID from an object. This is useful if the object was\n * previously mutated using `goog.getUid` in which case the mutation is\n * undone.\n * @param {Object} obj The object to remove the unique ID field from.\n */\ngoog.removeUid = function(obj) {\n // TODO(arv): Make the type stricter, do not accept null.\n\n // In IE, DOM nodes are not instances of Object and throw an exception if we\n // try to delete. Instead we try to use removeAttribute.\n if (obj !== null && 'removeAttribute' in obj) {\n obj.removeAttribute(goog.UID_PROPERTY_);\n }\n\n try {\n delete obj[goog.UID_PROPERTY_];\n } catch (ex) {\n }\n};\n\n\n/**\n * Name for unique ID property. Initialized in a way to help avoid collisions\n * with other closure JavaScript on the same page.\n * @type {string}\n * @private\n */\ngoog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);\n\n\n/**\n * Counter for UID.\n * @type {number}\n * @private\n */\ngoog.uidCounter_ = 0;\n\n\n/**\n * Adds a hash code field to an object. The hash code is unique for the\n * given object.\n * @param {Object} obj The object to get the hash code for.\n * @return {number} The hash code for the object.\n * @deprecated Use goog.getUid instead.\n */\ngoog.getHashCode = goog.getUid;\n\n\n/**\n * Removes the hash code field from an object.\n * @param {Object} obj The object to remove the field from.\n * @deprecated Use goog.removeUid instead.\n */\ngoog.removeHashCode = goog.removeUid;\n\n\n/**\n * Clones a value. The input may be an Object, Array, or basic type. Objects and\n * arrays will be cloned recursively.\n *\n * WARNINGS:\n * <code>goog.cloneObject</code> does not detect reference loops. Objects that\n * refer to themselves will cause infinite recursion.\n *\n * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies\n * UIDs created by <code>getUid</code> into cloned results.\n *\n * @param {*} obj The value to clone.\n * @return {*} A clone of the input value.\n * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.\n */\ngoog.cloneObject = function(obj) {\n var type = goog.typeOf(obj);\n if (type == 'object' || type == 'array') {\n if (typeof obj.clone === 'function') {\n return obj.clone();\n }\n var clone = type == 'array' ? [] : {};\n for (var key in obj) {\n clone[key] = goog.cloneObject(obj[key]);\n }\n return clone;\n }\n\n return obj;\n};\n\n\n/**\n * A native implementation of goog.bind.\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n * function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n * function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n * invoked as a method of.\n * @template T\n * @private\n */\ngoog.bindNative_ = function(fn, selfObj, var_args) {\n return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));\n};\n\n\n/**\n * A pure-JS implementation of goog.bind.\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n * function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n * function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n * invoked as a method of.\n * @template T\n * @private\n */\ngoog.bindJs_ = function(fn, selfObj, var_args) {\n if (!fn) {\n throw new Error();\n }\n\n if (arguments.length > 2) {\n var boundArgs = Array.prototype.slice.call(arguments, 2);\n return function() {\n // Prepend the bound arguments to the current arguments.\n var newArgs = Array.prototype.slice.call(arguments);\n Array.prototype.unshift.apply(newArgs, boundArgs);\n return fn.apply(selfObj, newArgs);\n };\n\n } else {\n return function() {\n return fn.apply(selfObj, arguments);\n };\n }\n};\n\n\n/**\n * Partially applies this function to a particular 'this object' and zero or\n * more arguments. The result is a new function with some arguments of the first\n * function pre-filled and the value of this 'pre-specified'.\n *\n * Remaining arguments specified at call-time are appended to the pre-specified\n * ones.\n *\n * Also see: {@link #partial}.\n *\n * Usage:\n * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');\n * barMethBound('arg3', 'arg4');</pre>\n *\n * @param {?function(this:T, ...)} fn A function to partially apply.\n * @param {T} selfObj Specifies the object which this should point to when the\n * function is run.\n * @param {...*} var_args Additional arguments that are partially applied to the\n * function.\n * @return {!Function} A partially-applied form of the function goog.bind() was\n * invoked as a method of.\n * @template T\n * @suppress {deprecated} See above.\n */\ngoog.bind = function(fn, selfObj, var_args) {\n // TODO(nicksantos): narrow the type signature.\n if (Function.prototype.bind &&\n // NOTE(nicksantos): Somebody pulled base.js into the default Chrome\n // extension environment. This means that for Chrome extensions, they get\n // the implementation of Function.prototype.bind that calls goog.bind\n // instead of the native one. Even worse, we don't want to introduce a\n // circular dependency between goog.bind and Function.prototype.bind, so\n // we have to hack this to make sure it works correctly.\n Function.prototype.bind.toString().indexOf('native code') != -1) {\n goog.bind = goog.bindNative_;\n } else {\n goog.bind = goog.bindJs_;\n }\n return goog.bind.apply(null, arguments);\n};\n\n\n/**\n * Like goog.bind(), except that a 'this object' is not required. Useful when\n * the target function is already bound.\n *\n * Usage:\n * var g = goog.partial(f, arg1, arg2);\n * g(arg3, arg4);\n *\n * @param {Function} fn A function to partially apply.\n * @param {...*} var_args Additional arguments that are partially applied to fn.\n * @return {!Function} A partially-applied form of the function goog.partial()\n * was invoked as a method of.\n */\ngoog.partial = function(fn, var_args) {\n var args = Array.prototype.slice.call(arguments, 1);\n return function() {\n // Clone the array (with slice()) and append additional arguments\n // to the existing arguments.\n var newArgs = args.slice();\n newArgs.push.apply(newArgs, arguments);\n return fn.apply(/** @type {?} */ (this), newArgs);\n };\n};\n\n\n/**\n * Copies all the members of a source object to a target object. This method\n * does not work on all browsers for all objects that contain keys such as\n * toString or hasOwnProperty. Use goog.object.extend for this purpose.\n *\n * NOTE: Some have advocated for the use of goog.mixin to setup classes\n * with multiple inheritence (traits, mixins, etc). However, as it simply\n * uses \"for in\", this is not compatible with ES6 classes whose methods are\n * non-enumerable. Changing this, would break cases where non-enumerable\n * properties are not expected.\n *\n * @param {Object} target Target.\n * @param {Object} source Source.\n * @deprecated Prefer Object.assign\n */\ngoog.mixin = function(target, source) {\n for (var x in source) {\n target[x] = source[x];\n }\n\n // For IE7 or lower, the for-in-loop does not contain any properties that are\n // not enumerable on the prototype object (for example, isPrototypeOf from\n // Object.prototype) but also it will not include 'replace' on objects that\n // extend String and change 'replace' (not that it is common for anyone to\n // extend anything except Object).\n};\n\n\n/**\n * @return {number} An integer value representing the number of milliseconds\n * between midnight, January 1, 1970 and the current time.\n * @deprecated Use Date.now\n */\ngoog.now = (goog.TRUSTED_SITE && Date.now) || (function() {\n // Unary plus operator converts its operand to a number which in\n // the case of\n // a date is done by calling getTime().\n return +new Date();\n });\n\n\n/**\n * Evals JavaScript in the global scope. In IE this uses execScript, other\n * browsers use goog.global.eval. If goog.global.eval does not evaluate in the\n * global scope (for example, in Safari), appends a script tag instead.\n * Throws an exception if neither execScript or eval is defined.\n * @param {string} script JavaScript string.\n */\ngoog.globalEval = function(script) {\n if (goog.global.execScript) {\n goog.global.execScript(script, 'JavaScript');\n } else if (goog.global.eval) {\n // Test to see if eval works\n if (goog.evalWorksForGlobals_ == null) {\n try {\n goog.global.eval('var _evalTest_ = 1;');\n } catch (ignore) {\n }\n if (typeof goog.global['_evalTest_'] != 'undefined') {\n try {\n delete goog.global['_evalTest_'];\n } catch (ignore) {\n // Microsoft edge fails the deletion above in strict mode.\n }\n goog.evalWorksForGlobals_ = true;\n } else {\n goog.evalWorksForGlobals_ = false;\n }\n }\n\n if (goog.evalWorksForGlobals_) {\n goog.global.eval(script);\n } else {\n /** @type {!Document} */\n var doc = goog.global.document;\n var scriptElt =\n /** @type {!HTMLScriptElement} */ (doc.createElement('script'));\n scriptElt.type = 'text/javascript';\n scriptElt.defer = false;\n // Note(user): can't use .innerHTML since \"t('<test>')\" will fail and\n // .text doesn't work in Safari 2. Therefore we append a text node.\n scriptElt.appendChild(doc.createTextNode(script));\n doc.head.appendChild(scriptElt);\n doc.head.removeChild(scriptElt);\n }\n } else {\n throw new Error('goog.globalEval not available');\n }\n};\n\n\n/**\n * Indicates whether or not we can call 'eval' directly to eval code in the\n * global scope. Set to a Boolean by the first call to goog.globalEval (which\n * empirically tests whether eval works for globals). @see goog.globalEval\n * @type {?boolean}\n * @private\n */\ngoog.evalWorksForGlobals_ = null;\n\n\n/**\n * Optional map of CSS class names to obfuscated names used with\n * goog.getCssName().\n * @private {!Object<string, string>|undefined}\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMapping_;\n\n\n/**\n * Optional obfuscation style for CSS class names. Should be set to either\n * 'BY_WHOLE' or 'BY_PART' if defined.\n * @type {string|undefined}\n * @private\n * @see goog.setCssNameMapping\n */\ngoog.cssNameMappingStyle_;\n\n\n\n/**\n * A hook for modifying the default behavior goog.getCssName. The function\n * if present, will receive the standard output of the goog.getCssName as\n * its input.\n *\n * @type {(function(string):string)|undefined}\n */\ngoog.global.CLOSURE_CSS_NAME_MAP_FN;\n\n\n/**\n * Handles strings that are intended to be used as CSS class names.\n *\n * This function works in tandem with @see goog.setCssNameMapping.\n *\n * Without any mapping set, the arguments are simple joined with a hyphen and\n * passed through unaltered.\n *\n * When there is a mapping, there are two possible styles in which these\n * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)\n * of the passed in css name is rewritten according to the map. In the BY_WHOLE\n * style, the full css name is looked up in the map directly. If a rewrite is\n * not specified by the map, the compiler will output a warning.\n *\n * When the mapping is passed to the compiler, it will replace calls to\n * goog.getCssName with the strings from the mapping, e.g.\n * var x = goog.getCssName('foo');\n * var y = goog.getCssName(this.baseClass, 'active');\n * becomes:\n * var x = 'foo';\n * var y = this.baseClass + '-active';\n *\n * If one argument is passed it will be processed, if two are passed only the\n * modifier will be processed, as it is assumed the first argument was generated\n * as a result of calling goog.getCssName.\n *\n * @param {string} className The class name.\n * @param {string=} opt_modifier A modifier to be appended to the class name.\n * @return {string} The class name or the concatenation of the class name and\n * the modifier.\n */\ngoog.getCssName = function(className, opt_modifier) {\n // String() is used for compatibility with compiled soy where the passed\n // className can be non-string objects.\n if (String(className).charAt(0) == '.') {\n throw new Error(\n 'className passed in goog.getCssName must not start with \".\".' +\n ' You passed: ' + className);\n }\n\n var getMapping = function(cssName) {\n return goog.cssNameMapping_[cssName] || cssName;\n };\n\n var renameByParts = function(cssName) {\n // Remap all the parts individually.\n var parts = cssName.split('-');\n var mapped = [];\n for (var i = 0; i < parts.length; i++) {\n mapped.push(getMapping(parts[i]));\n }\n return mapped.join('-');\n };\n\n var rename;\n if (goog.cssNameMapping_) {\n rename =\n goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts;\n } else {\n rename = function(a) {\n return a;\n };\n }\n\n var result =\n opt_modifier ? className + '-' + rename(opt_modifier) : rename(className);\n\n // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further\n // processing of the class name.\n if (goog.global.CLOSURE_CSS_NAME_MAP_FN) {\n return goog.global.CLOSURE_CSS_NAME_MAP_FN(result);\n }\n\n return result;\n};\n\n\n/**\n * Sets the map to check when returning a value from goog.getCssName(). Example:\n * <pre>\n * goog.setCssNameMapping({\n * \"goog\": \"a\",\n * \"disabled\": \"b\",\n * });\n *\n * var x = goog.getCssName('goog');\n * // The following evaluates to: \"a a-b\".\n * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')\n * </pre>\n * When declared as a map of string literals to string literals, the JSCompiler\n * will replace all calls to goog.getCssName() using the supplied map if the\n * --process_closure_primitives flag is set.\n *\n * @param {!Object} mapping A map of strings to strings where keys are possible\n * arguments to goog.getCssName() and values are the corresponding values\n * that should be returned.\n * @param {string=} opt_style The style of css name mapping. There are two valid\n * options: 'BY_PART', and 'BY_WHOLE'.\n * @see goog.getCssName for a description.\n */\ngoog.setCssNameMapping = function(mapping, opt_style) {\n goog.cssNameMapping_ = mapping;\n goog.cssNameMappingStyle_ = opt_style;\n};\n\n\n/**\n * To use CSS renaming in compiled mode, one of the input files should have a\n * call to goog.setCssNameMapping() with an object literal that the JSCompiler\n * can extract and use to replace all calls to goog.getCssName(). In uncompiled\n * mode, JavaScript code should be loaded before this base.js file that declares\n * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is\n * to ensure that the mapping is loaded before any calls to goog.getCssName()\n * are made in uncompiled mode.\n *\n * A hook for overriding the CSS name mapping.\n * @type {!Object<string, string>|undefined}\n */\ngoog.global.CLOSURE_CSS_NAME_MAPPING;\n\n\nif (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {\n // This does not call goog.setCssNameMapping() because the JSCompiler\n // requires that goog.setCssNameMapping() be called with an object literal.\n goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;\n}\n\n\n/**\n * Gets a localized message.\n *\n * This function is a compiler primitive. If you give the compiler a localized\n * message bundle, it will replace the string at compile-time with a localized\n * version, and expand goog.getMsg call to a concatenated string.\n *\n * Messages must be initialized in the form:\n * <code>\n * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});\n * </code>\n *\n * This function produces a string which should be treated as plain text. Use\n * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to\n * produce SafeHtml.\n *\n * @param {string} str Translatable string, places holders in the form {$foo}.\n * @param {Object<string, string>=} opt_values Maps place holder name to value.\n * @param {{html: boolean}=} opt_options Options:\n * html: Escape '<' in str to '<'. Used by Closure Templates where the\n * generated code size and performance is critical which is why {@link\n * goog.html.SafeHtmlFormatter} is not used. The value must be literal true\n * or false.\n * @return {string} message with placeholders filled.\n */\ngoog.getMsg = function(str, opt_values, opt_options) {\n if (opt_options && opt_options.html) {\n // Note that '&' is not replaced because the translation can contain HTML\n // entities.\n str = str.replace(/</g, '<');\n }\n if (opt_values) {\n str = str.replace(/\\{\\$([^}]+)}/g, function(match, key) {\n return (opt_values != null && key in opt_values) ? opt_values[key] :\n match;\n });\n }\n return str;\n};\n\n\n/**\n * Gets a localized message. If the message does not have a translation, gives a\n * fallback message.\n *\n * This is useful when introducing a new message that has not yet been\n * translated into all languages.\n *\n * This function is a compiler primitive. Must be used in the form:\n * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>\n * where MSG_A and MSG_B were initialized with goog.getMsg.\n *\n * @param {string} a The preferred message.\n * @param {string} b The fallback message.\n * @return {string} The best translated message.\n */\ngoog.getMsgWithFallback = function(a, b) {\n return a;\n};\n\n\n/**\n * Exposes an unobfuscated global namespace path for the given object.\n * Note that fields of the exported object *will* be obfuscated, unless they are\n * exported in turn via this function or goog.exportProperty.\n *\n * Also handy for making public items that are defined in anonymous closures.\n *\n * ex. goog.exportSymbol('public.path.Foo', Foo);\n *\n * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);\n * public.path.Foo.staticFunction();\n *\n * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',\n * Foo.prototype.myMethod);\n * new public.path.Foo().myMethod();\n *\n * @param {string} publicPath Unobfuscated name to export.\n * @param {*} object Object the name should point to.\n * @param {Object=} opt_objectToExportTo The object to add the path to; default\n * is goog.global.\n */\ngoog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {\n goog.exportPath_(publicPath, object, opt_objectToExportTo);\n};\n\n\n/**\n * Exports a property unobfuscated into the object's namespace.\n * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);\n * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);\n * @param {Object} object Object whose static property is being exported.\n * @param {string} publicName Unobfuscated name to export.\n * @param {*} symbol Object the name should point to.\n */\ngoog.exportProperty = function(object, publicName, symbol) {\n object[publicName] = symbol;\n};\n\n\n/**\n * Inherit the prototype methods from one constructor into another.\n *\n * Usage:\n * <pre>\n * function ParentClass(a, b) { }\n * ParentClass.prototype.foo = function(a) { };\n *\n * function ChildClass(a, b, c) {\n * ChildClass.base(this, 'constructor', a, b);\n * }\n * goog.inherits(ChildClass, ParentClass);\n *\n * var child = new ChildClass('a', 'b', 'see');\n * child.foo(); // This works.\n * </pre>\n *\n * @param {!Function} childCtor Child class.\n * @param {!Function} parentCtor Parent class.\n * @suppress {strictMissingProperties} superClass_ and base is not defined on\n * Function.\n */\ngoog.inherits = function(childCtor, parentCtor) {\n /** @constructor */\n function tempCtor() {}\n tempCtor.prototype = parentCtor.prototype;\n childCtor.superClass_ = parentCtor.prototype;\n childCtor.prototype = new tempCtor();\n /** @override */\n childCtor.prototype.constructor = childCtor;\n\n /**\n * Calls superclass constructor/method.\n *\n * This function is only available if you use goog.inherits to\n * express inheritance relationships between classes.\n *\n * NOTE: This is a replacement for goog.base and for superClass_\n * property defined in childCtor.\n *\n * @param {!Object} me Should always be \"this\".\n * @param {string} methodName The method name to call. Calling\n * superclass constructor can be done with the special string\n * 'constructor'.\n * @param {...*} var_args The arguments to pass to superclass\n * method/constructor.\n * @return {*} The return value of the superclass method/constructor.\n */\n childCtor.base = function(me, methodName, var_args) {\n // Copying using loop to avoid deop due to passing arguments object to\n // function. This is faster in many JS engines as of late 2014.\n var args = new Array(arguments.length - 2);\n for (var i = 2; i < arguments.length; i++) {\n args[i - 2] = arguments[i];\n }\n return parentCtor.prototype[methodName].apply(me, args);\n };\n};\n\n\n/**\n * Allow for aliasing within scope functions. This function exists for\n * uncompiled code - in compiled code the calls will be inlined and the aliases\n * applied. In uncompiled code the function is simply run since the aliases as\n * written are valid JavaScript.\n *\n *\n * @param {function()} fn Function to call. This function can contain aliases\n * to namespaces (e.g. \"var dom = goog.dom\") or classes\n * (e.g. \"var Timer = goog.Timer\").\n * @deprecated Use goog.module instead.\n */\ngoog.scope = function(fn) {\n if (goog.isInModuleLoader_()) {\n throw new Error('goog.scope is not supported within a module.');\n }\n fn.call(goog.global);\n};\n\n\n/*\n * To support uncompiled, strict mode bundles that use eval to divide source\n * like so:\n * eval('someSource;//# sourceUrl sourcefile.js');\n * We need to export the globally defined symbols \"goog\" and \"COMPILED\".\n * Exporting \"goog\" breaks the compiler optimizations, so we required that\n * be defined externally.\n * NOTE: We don't use goog.exportSymbol here because we don't want to trigger\n * extern generation when that compiler option is enabled.\n */\nif (!COMPILED) {\n goog.global['COMPILED'] = COMPILED;\n}\n\n\n//==============================================================================\n// goog.defineClass implementation\n//==============================================================================\n\n\n/**\n * Creates a restricted form of a Closure \"class\":\n * - from the compiler's perspective, the instance returned from the\n * constructor is sealed (no new properties may be added). This enables\n * better checks.\n * - the compiler will rewrite this definition to a form that is optimal\n * for type checking and optimization (initially this will be a more\n * traditional form).\n *\n * @param {Function} superClass The superclass, Object or null.\n * @param {goog.defineClass.ClassDescriptor} def\n * An object literal describing\n * the class. It may have the following properties:\n * \"constructor\": the constructor function\n * \"statics\": an object literal containing methods to add to the constructor\n * as \"static\" methods or a function that will receive the constructor\n * function as its only parameter to which static properties can\n * be added.\n * all other properties are added to the prototype.\n * @return {!Function} The class constructor.\n * @deprecated Use ES6 class syntax instead.\n */\ngoog.defineClass = function(superClass, def) {\n // TODO(johnlenz): consider making the superClass an optional parameter.\n var constructor = def.constructor;\n var statics = def.statics;\n // Wrap the constructor prior to setting up the prototype and static methods.\n if (!constructor || constructor == Object.prototype.constructor) {\n constructor = function() {\n throw new Error(\n 'cannot instantiate an interface (no constructor defined).');\n };\n }\n\n var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);\n if (superClass) {\n goog.inherits(cls, superClass);\n }\n\n // Remove all the properties that should not be copied to the prototype.\n delete def.constructor;\n delete def.statics;\n\n goog.defineClass.applyProperties_(cls.prototype, def);\n if (statics != null) {\n if (statics instanceof Function) {\n statics(cls);\n } else {\n goog.defineClass.applyProperties_(cls, statics);\n }\n }\n\n return cls;\n};\n\n\n/**\n * @typedef {{\n * constructor: (!Function|undefined),\n * statics: (Object|undefined|function(Function):void)\n * }}\n */\ngoog.defineClass.ClassDescriptor;\n\n\n/**\n * @define {boolean} Whether the instances returned by goog.defineClass should\n * be sealed when possible.\n *\n * When sealing is disabled the constructor function will not be wrapped by\n * goog.defineClass, making it incompatible with ES6 class methods.\n */\ngoog.defineClass.SEAL_CLASS_INSTANCES =\n goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);\n\n\n/**\n * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is\n * defined, this function will wrap the constructor in a function that seals the\n * results of the provided constructor function.\n *\n * @param {!Function} ctr The constructor whose results maybe be sealed.\n * @param {Function} superClass The superclass constructor.\n * @return {!Function} The replacement constructor.\n * @private\n */\ngoog.defineClass.createSealingConstructor_ = function(ctr, superClass) {\n if (!goog.defineClass.SEAL_CLASS_INSTANCES) {\n // Do now wrap the constructor when sealing is disabled. Angular code\n // depends on this for injection to work properly.\n return ctr;\n }\n\n // Compute whether the constructor is sealable at definition time, rather\n // than when the instance is being constructed.\n var superclassSealable = !goog.defineClass.isUnsealable_(superClass);\n\n /**\n * @this {Object}\n * @return {?}\n */\n var wrappedCtr = function() {\n // Don't seal an instance of a subclass when it calls the constructor of\n // its super class as there is most likely still setup to do.\n var instance = ctr.apply(this, arguments) || this;\n instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];\n\n if (this.constructor === wrappedCtr && superclassSealable &&\n Object.seal instanceof Function) {\n Object.seal(instance);\n }\n return instance;\n };\n\n return wrappedCtr;\n};\n\n\n/**\n * @param {Function} ctr The constructor to test.\n * @return {boolean} Whether the constructor has been tagged as unsealable\n * using goog.tagUnsealableClass.\n * @private\n */\ngoog.defineClass.isUnsealable_ = function(ctr) {\n return ctr && ctr.prototype &&\n ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_];\n};\n\n\n// TODO(johnlenz): share these values with the goog.object\n/**\n * The names of the fields that are defined on Object.prototype.\n * @type {!Array<string>}\n * @private\n * @const\n */\ngoog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [\n 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',\n 'toLocaleString', 'toString', 'valueOf'\n];\n\n\n// TODO(johnlenz): share this function with the goog.object\n/**\n * @param {!Object} target The object to add properties to.\n * @param {!Object} source The object to copy properties from.\n * @private\n */\ngoog.defineClass.applyProperties_ = function(target, source) {\n // TODO(johnlenz): update this to support ES5 getters/setters\n\n var key;\n for (key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n\n // For IE the for-in-loop does not contain any properties that are not\n // enumerable on the prototype object (for example isPrototypeOf from\n // Object.prototype) and it will also not include 'replace' on objects that\n // extend String and change 'replace' (not that it is common for anyone to\n // extend anything except Object).\n for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {\n key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n};\n\n\n/**\n * Sealing classes breaks the older idiom of assigning properties on the\n * prototype rather than in the constructor. As such, goog.defineClass\n * must not seal subclasses of these old-style classes until they are fixed.\n * Until then, this marks a class as \"broken\", instructing defineClass\n * not to seal subclasses.\n * @param {!Function} ctr The legacy constructor to tag as unsealable.\n */\ngoog.tagUnsealableClass = function(ctr) {\n if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {\n ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;\n }\n};\n\n\n/**\n * Name for unsealable tag property.\n * @const @private {string}\n */\ngoog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';\n\n\n// There's a bug in the compiler where without collapse properties the\n// Closure namespace defines do not guard code correctly. To help reduce code\n// size also check for !COMPILED even though it redundant until this is fixed.\nif (!COMPILED && goog.DEPENDENCIES_ENABLED) {\n\n /**\n * Tries to detect whether is in the context of an HTML document.\n * @return {boolean} True if it looks like HTML document.\n * @private\n */\n goog.inHtmlDocument_ = function() {\n /** @type {!Document} */\n var doc = goog.global.document;\n return doc != null && 'write' in doc; // XULDocument misses write.\n };\n\n\n /**\n * We'd like to check for if the document readyState is 'loading'; however\n * there are bugs on IE 10 and below where the readyState being anything other\n * than 'complete' is not reliable.\n * @return {boolean}\n * @private\n */\n goog.isDocumentLoading_ = function() {\n // attachEvent is available on IE 6 thru 10 only, and thus can be used to\n // detect those browsers.\n /** @type {!HTMLDocument} */\n var doc = goog.global.document;\n return doc.attachEvent ? doc.readyState != 'complete' :\n doc.readyState == 'loading';\n };\n\n\n /**\n * Tries to detect the base path of base.js script that bootstraps Closure.\n * @private\n */\n goog.findBasePath_ = function() {\n if (goog.global.CLOSURE_BASE_PATH != undefined &&\n // Anti DOM-clobbering runtime check (b/37736576).\n typeof goog.global.CLOSURE_BASE_PATH === 'string') {\n goog.basePath = goog.global.CLOSURE_BASE_PATH;\n return;\n } else if (!goog.inHtmlDocument_()) {\n return;\n }\n /** @type {!Document} */\n var doc = goog.global.document;\n // If we have a currentScript available, use it exclusively.\n var currentScript = doc.currentScript;\n if (currentScript) {\n var scripts = [currentScript];\n } else {\n var scripts = doc.getElementsByTagName('SCRIPT');\n }\n // Search backwards since the current script is in almost all cases the one\n // that has base.js.\n for (var i = scripts.length - 1; i >= 0; --i) {\n var script = /** @type {!HTMLScriptElement} */ (scripts[i]);\n var src = script.src;\n var qmark = src.lastIndexOf('?');\n var l = qmark == -1 ? src.length : qmark;\n if (src.substr(l - 7, 7) == 'base.js') {\n goog.basePath = src.substr(0, l - 7);\n return;\n }\n }\n };\n\n goog.findBasePath_();\n\n /** @struct @constructor @final */\n goog.Transpiler = function() {\n /** @private {?Object<string, boolean>} */\n this.requiresTranspilation_ = null;\n /** @private {string} */\n this.transpilationTarget_ = goog.TRANSPILE_TO_LANGUAGE;\n };\n /**\n * Returns a newly created map from language mode string to a boolean\n * indicating whether transpilation should be done for that mode as well as\n * the highest level language that this environment supports.\n *\n * Guaranteed invariant:\n * For any two modes, l1 and l2 where l2 is a newer mode than l1,\n * `map[l1] == true` implies that `map[l2] == true`.\n *\n * Note this method is extracted and used elsewhere, so it cannot rely on\n * anything external (it should easily be able to be transformed into a\n * standalone, top level function).\n *\n * @private\n * @return {{\n * target: string,\n * map: !Object<string, boolean>\n * }}\n */\n goog.Transpiler.prototype.createRequiresTranspilation_ = function() {\n var transpilationTarget = 'es3';\n var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false};\n var transpilationRequiredForAllLaterModes = false;\n\n /**\n * Adds an entry to requiresTranspliation for the given language mode.\n *\n * IMPORTANT: Calls must be made in order from oldest to newest language\n * mode.\n * @param {string} modeName\n * @param {function(): boolean} isSupported Returns true if the JS engine\n * supports the given mode.\n */\n function addNewerLanguageTranspilationCheck(modeName, isSupported) {\n if (transpilationRequiredForAllLaterModes) {\n requiresTranspilation[modeName] = true;\n } else if (isSupported()) {\n transpilationTarget = modeName;\n requiresTranspilation[modeName] = false;\n } else {\n requiresTranspilation[modeName] = true;\n transpilationRequiredForAllLaterModes = true;\n }\n }\n\n /**\n * Does the given code evaluate without syntax errors and return a truthy\n * result?\n */\n function /** boolean */ evalCheck(/** string */ code) {\n try {\n return !!eval(code);\n } catch (ignored) {\n return false;\n }\n }\n\n var userAgent = goog.global.navigator && goog.global.navigator.userAgent ?\n goog.global.navigator.userAgent :\n '';\n\n // Identify ES3-only browsers by their incorrect treatment of commas.\n addNewerLanguageTranspilationCheck('es5', function() {\n return evalCheck('[1,].length==1');\n });\n addNewerLanguageTranspilationCheck('es6', function() {\n // Edge has a non-deterministic (i.e., not reproducible) bug with ES6:\n // https://github.com/Microsoft/ChakraCore/issues/1496.\n var re = /Edge\\/(\\d+)(\\.\\d)*/i;\n var edgeUserAgent = userAgent.match(re);\n if (edgeUserAgent) {\n // The Reflect.construct test below is flaky on Edge. It can sometimes\n // pass or fail on 40 15.15063, so just exit early for Edge and treat\n // it as ES5. Until we're on a more up to date version just always use\n // ES5. See https://github.com/Microsoft/ChakraCore/issues/3217.\n return false;\n }\n // Test es6: [FF50 (?), Edge 14 (?), Chrome 50]\n // (a) default params (specifically shadowing locals),\n // (b) destructuring, (c) block-scoped functions,\n // (d) for-of (const), (e) new.target/Reflect.construct\n var es6fullTest =\n 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' +\n 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' +\n 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' +\n 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' +\n '==3}';\n\n return evalCheck('(()=>{\"use strict\";' + es6fullTest + '})()');\n });\n // ** and **= are the only new features in 'es7'\n addNewerLanguageTranspilationCheck('es7', function() {\n return evalCheck('2 ** 2 == 4');\n });\n // async functions are the only new features in 'es8'\n addNewerLanguageTranspilationCheck('es8', function() {\n return evalCheck('async () => 1, true');\n });\n addNewerLanguageTranspilationCheck('es9', function() {\n return evalCheck('({...rest} = {}), true');\n });\n addNewerLanguageTranspilationCheck('es_next', function() {\n return false; // assume it always need to transpile\n });\n return {target: transpilationTarget, map: requiresTranspilation};\n };\n\n\n /**\n * Determines whether the given language needs to be transpiled.\n * @param {string} lang\n * @param {string|undefined} module\n * @return {boolean}\n */\n goog.Transpiler.prototype.needsTranspile = function(lang, module) {\n if (goog.TRANSPILE == 'always') {\n return true;\n } else if (goog.TRANSPILE == 'never') {\n return false;\n } else if (!this.requiresTranspilation_) {\n var obj = this.createRequiresTranspilation_();\n this.requiresTranspilation_ = obj.map;\n this.transpilationTarget_ = this.transpilationTarget_ || obj.target;\n }\n if (lang in this.requiresTranspilation_) {\n if (this.requiresTranspilation_[lang]) {\n return true;\n } else if (\n goog.inHtmlDocument_() && module == 'es6' &&\n !('noModule' in goog.global.document.createElement('script'))) {\n return true;\n } else {\n return false;\n }\n } else {\n throw new Error('Unknown language mode: ' + lang);\n }\n };\n\n\n /**\n * Lazily retrieves the transpiler and applies it to the source.\n * @param {string} code JS code.\n * @param {string} path Path to the code.\n * @return {string} The transpiled code.\n */\n goog.Transpiler.prototype.transpile = function(code, path) {\n // TODO(johnplaisted): We should delete goog.transpile_ and just have this\n // function. But there's some compile error atm where goog.global is being\n // stripped incorrectly without this.\n return goog.transpile_(code, path, this.transpilationTarget_);\n };\n\n\n /** @private @final {!goog.Transpiler} */\n goog.transpiler_ = new goog.Transpiler();\n\n /**\n * Rewrites closing script tags in input to avoid ending an enclosing script\n * tag.\n *\n * @param {string} str\n * @return {string}\n * @private\n */\n goog.protectScriptTag_ = function(str) {\n return str.replace(/<\\/(SCRIPT)/ig, '\\\\x3c/$1');\n };\n\n\n /**\n * A debug loader is responsible for downloading and executing javascript\n * files in an unbundled, uncompiled environment.\n *\n * This can be custimized via the setDependencyFactory method, or by\n * CLOSURE_IMPORT_SCRIPT/CLOSURE_LOAD_FILE_SYNC.\n *\n * @struct @constructor @final @private\n */\n goog.DebugLoader_ = function() {\n /** @private @const {!Object<string, !goog.Dependency>} */\n this.dependencies_ = {};\n /** @private @const {!Object<string, string>} */\n this.idToPath_ = {};\n /** @private @const {!Object<string, boolean>} */\n this.written_ = {};\n /** @private @const {!Array<!goog.Dependency>} */\n this.loadingDeps_ = [];\n /** @private {!Array<!goog.Dependency>} */\n this.depsToLoad_ = [];\n /** @private {boolean} */\n this.paused_ = false;\n /** @private {!goog.DependencyFactory} */\n this.factory_ = new goog.DependencyFactory(goog.transpiler_);\n /** @private @const {!Object<string, !Function>} */\n this.deferredCallbacks_ = {};\n /** @private @const {!Array<string>} */\n this.deferredQueue_ = [];\n };\n\n /**\n * @param {!Array<string>} namespaces\n * @param {function(): undefined} callback Function to call once all the\n * namespaces have loaded.\n */\n goog.DebugLoader_.prototype.bootstrap = function(namespaces, callback) {\n var cb = callback;\n function resolve() {\n if (cb) {\n goog.global.setTimeout(cb, 0);\n cb = null;\n }\n }\n\n if (!namespaces.length) {\n resolve();\n return;\n }\n\n var deps = [];\n for (var i = 0; i < namespaces.length; i++) {\n var path = this.getPathFromDeps_(namespaces[i]);\n if (!path) {\n throw new Error('Unregonized namespace: ' + namespaces[i]);\n }\n deps.push(this.dependencies_[path]);\n }\n\n var require = goog.require;\n var loaded = 0;\n for (var i = 0; i < namespaces.length; i++) {\n require(namespaces[i]);\n deps[i].onLoad(function() {\n if (++loaded == namespaces.length) {\n resolve();\n }\n });\n }\n };\n\n\n /**\n * Loads the Closure Dependency file.\n *\n * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base\n * loaded, setDependencyFactory called, and then this called. i.e. allows\n * custom loading of the deps file.\n */\n goog.DebugLoader_.prototype.loadClosureDeps = function() {\n // Circumvent addDependency, which would try to transpile deps.js if\n // transpile is set to always.\n var relPath = 'deps.js';\n this.depsToLoad_.push(this.factory_.createDependency(\n goog.normalizePath_(goog.basePath + relPath), relPath, [], [], {},\n false));\n this.loadDeps_();\n };\n\n\n /**\n * Notifies the debug loader when a dependency has been requested.\n *\n * @param {string} absPathOrId Path of the dependency or goog id.\n * @param {boolean=} opt_force\n */\n goog.DebugLoader_.prototype.requested = function(absPathOrId, opt_force) {\n var path = this.getPathFromDeps_(absPathOrId);\n if (path &&\n (opt_force || this.areDepsLoaded_(this.dependencies_[path].requires))) {\n var callback = this.deferredCallbacks_[path];\n if (callback) {\n delete this.deferredCallbacks_[path];\n callback();\n }\n }\n };\n\n\n /**\n * Sets the dependency factory, which can be used to create custom\n * goog.Dependency implementations to control how dependencies are loaded.\n *\n * @param {!goog.DependencyFactory} factory\n */\n goog.DebugLoader_.prototype.setDependencyFactory = function(factory) {\n this.factory_ = factory;\n };\n\n\n /**\n * Travserses the dependency graph and queues the given dependency, and all of\n * its transitive dependencies, for loading and then starts loading if not\n * paused.\n *\n * @param {string} namespace\n * @private\n */\n goog.DebugLoader_.prototype.load_ = function(namespace) {\n if (!this.getPathFromDeps_(namespace)) {\n var errorMessage = 'goog.require could not find: ' + namespace;\n\n goog.logToConsole_(errorMessage);\n throw Error(errorMessage);\n } else {\n var loader = this;\n\n var deps = [];\n\n /** @param {string} namespace */\n var visit = function(namespace) {\n var path = loader.getPathFromDeps_(namespace);\n\n if (!path) {\n throw new Error('Bad dependency path or symbol: ' + namespace);\n }\n\n if (loader.written_[path]) {\n return;\n }\n\n loader.written_[path] = true;\n\n var dep = loader.dependencies_[path];\n for (var i = 0; i < dep.requires.length; i++) {\n if (!goog.isProvided_(dep.requires[i])) {\n visit(dep.requires[i]);\n }\n }\n\n deps.push(dep);\n };\n\n visit(namespace);\n\n var wasLoading = !!this.depsToLoad_.length;\n this.depsToLoad_ = this.depsToLoad_.concat(deps);\n\n if (!this.paused_ && !wasLoading) {\n this.loadDeps_();\n }\n }\n };\n\n\n /**\n * Loads any queued dependencies until they are all loaded or paused.\n *\n * @private\n */\n goog.DebugLoader_.prototype.loadDeps_ = function() {\n var loader = this;\n var paused = this.paused_;\n\n while (this.depsToLoad_.length && !paused) {\n (function() {\n var loadCallDone = false;\n var dep = loader.depsToLoad_.shift();\n\n var loaded = false;\n loader.loading_(dep);\n\n var controller = {\n pause: function() {\n if (loadCallDone) {\n throw new Error('Cannot call pause after the call to load.');\n } else {\n paused = true;\n }\n },\n resume: function() {\n if (loadCallDone) {\n loader.resume_();\n } else {\n // Some dep called pause and then resume in the same load call.\n // Just keep running this same loop.\n paused = false;\n }\n },\n loaded: function() {\n if (loaded) {\n throw new Error('Double call to loaded.');\n }\n\n loaded = true;\n loader.loaded_(dep);\n },\n pending: function() {\n // Defensive copy.\n var pending = [];\n for (var i = 0; i < loader.loadingDeps_.length; i++) {\n pending.push(loader.loadingDeps_[i]);\n }\n return pending;\n },\n /**\n * @param {goog.ModuleType} type\n */\n setModuleState: function(type) {\n goog.moduleLoaderState_ = {\n type: type,\n moduleName: '',\n declareLegacyNamespace: false\n };\n },\n /** @type {function(string, string, string=)} */\n registerEs6ModuleExports: function(\n path, exports, opt_closureNamespace) {\n if (opt_closureNamespace) {\n goog.loadedModules_[opt_closureNamespace] = {\n exports: exports,\n type: goog.ModuleType.ES6,\n moduleId: opt_closureNamespace || ''\n };\n }\n },\n /** @type {function(string, ?)} */\n registerGoogModuleExports: function(moduleId, exports) {\n goog.loadedModules_[moduleId] = {\n exports: exports,\n type: goog.ModuleType.GOOG,\n moduleId: moduleId\n };\n },\n clearModuleState: function() {\n goog.moduleLoaderState_ = null;\n },\n defer: function(callback) {\n if (loadCallDone) {\n throw new Error(\n 'Cannot register with defer after the call to load.');\n }\n loader.defer_(dep, callback);\n },\n areDepsLoaded: function() {\n return loader.areDepsLoaded_(dep.requires);\n }\n };\n\n try {\n dep.load(controller);\n } finally {\n loadCallDone = true;\n }\n })();\n }\n\n if (paused) {\n this.pause_();\n }\n };\n\n\n /** @private */\n goog.DebugLoader_.prototype.pause_ = function() {\n this.paused_ = true;\n };\n\n\n /** @private */\n goog.DebugLoader_.prototype.resume_ = function() {\n if (this.paused_) {\n this.paused_ = false;\n this.loadDeps_();\n }\n };\n\n\n /**\n * Marks the given dependency as loading (load has been called but it has not\n * yet marked itself as finished). Useful for dependencies that want to know\n * what else is loading. Example: goog.modules cannot eval if there are\n * loading dependencies.\n *\n * @param {!goog.Dependency} dep\n * @private\n */\n goog.DebugLoader_.prototype.loading_ = function(dep) {\n this.loadingDeps_.push(dep);\n };\n\n\n /**\n * Marks the given dependency as having finished loading and being available\n * for require.\n *\n * @param {!goog.Dependency} dep\n * @private\n */\n goog.DebugLoader_.prototype.loaded_ = function(dep) {\n for (var i = 0; i < this.loadingDeps_.length; i++) {\n if (this.loadingDeps_[i] == dep) {\n this.loadingDeps_.splice(i, 1);\n break;\n }\n }\n\n for (var i = 0; i < this.deferredQueue_.length; i++) {\n if (this.deferredQueue_[i] == dep.path) {\n this.deferredQueue_.splice(i, 1);\n break;\n }\n }\n\n if (this.loadingDeps_.length == this.deferredQueue_.length &&\n !this.depsToLoad_.length) {\n // Something has asked to load these, but they may not be directly\n // required again later, so load them now that we know we're done loading\n // everything else. e.g. a goog module entry point.\n while (this.deferredQueue_.length) {\n this.requested(this.deferredQueue_.shift(), true);\n }\n }\n\n dep.loaded();\n };\n\n\n /**\n * @param {!Array<string>} pathsOrIds\n * @return {boolean}\n * @private\n */\n goog.DebugLoader_.prototype.areDepsLoaded_ = function(pathsOrIds) {\n for (var i = 0; i < pathsOrIds.length; i++) {\n var path = this.getPathFromDeps_(pathsOrIds[i]);\n if (!path ||\n (!(path in this.deferredCallbacks_) &&\n !goog.isProvided_(pathsOrIds[i]))) {\n return false;\n }\n }\n\n return true;\n };\n\n\n /**\n * @param {string} absPathOrId\n * @return {?string}\n * @private\n */\n goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) {\n if (absPathOrId in this.idToPath_) {\n return this.idToPath_[absPathOrId];\n } else if (absPathOrId in this.dependencies_) {\n return absPathOrId;\n } else {\n return null;\n }\n };\n\n\n /**\n * @param {!goog.Dependency} dependency\n * @param {!Function} callback\n * @private\n */\n goog.DebugLoader_.prototype.defer_ = function(dependency, callback) {\n this.deferredCallbacks_[dependency.path] = callback;\n this.deferredQueue_.push(dependency.path);\n };\n\n\n /**\n * Interface for goog.Dependency implementations to have some control over\n * loading of dependencies.\n *\n * @record\n */\n goog.LoadController = function() {};\n\n\n /**\n * Tells the controller to halt loading of more dependencies.\n */\n goog.LoadController.prototype.pause = function() {};\n\n\n /**\n * Tells the controller to resume loading of more dependencies if paused.\n */\n goog.LoadController.prototype.resume = function() {};\n\n\n /**\n * Tells the controller that this dependency has finished loading.\n *\n * This causes this to be removed from pending() and any load callbacks to\n * fire.\n */\n goog.LoadController.prototype.loaded = function() {};\n\n\n /**\n * List of dependencies on which load has been called but which have not\n * called loaded on their controller. This includes the current dependency.\n *\n * @return {!Array<!goog.Dependency>}\n */\n goog.LoadController.prototype.pending = function() {};\n\n\n /**\n * Registers an object as an ES6 module's exports so that goog.modules may\n * require it by path.\n *\n * @param {string} path Full path of the module.\n * @param {?} exports\n * @param {string=} opt_closureNamespace Closure namespace to associate with\n * this module.\n */\n goog.LoadController.prototype.registerEs6ModuleExports = function(\n path, exports, opt_closureNamespace) {};\n\n\n /**\n * Sets the current module state.\n *\n * @param {goog.ModuleType} type Type of module.\n */\n goog.LoadController.prototype.setModuleState = function(type) {};\n\n\n /**\n * Clears the current module state.\n */\n goog.LoadController.prototype.clearModuleState = function() {};\n\n\n /**\n * Registers a callback to call once the dependency is actually requested\n * via goog.require + all of the immediate dependencies have been loaded or\n * all other files have been loaded. Allows for lazy loading until\n * require'd without pausing dependency loading, which is needed on old IE.\n *\n * @param {!Function} callback\n */\n goog.LoadController.prototype.defer = function(callback) {};\n\n\n /**\n * @return {boolean}\n */\n goog.LoadController.prototype.areDepsLoaded = function() {};\n\n\n /**\n * Basic super class for all dependencies Closure Library can load.\n *\n * This default implementation is designed to load untranspiled, non-module\n * scripts in a web broswer.\n *\n * For transpiled non-goog.module files {@see goog.TranspiledDependency}.\n * For goog.modules see {@see goog.GoogModuleDependency}.\n * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}.\n *\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides goog.provided or goog.module symbols\n * in this file.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @struct @constructor\n */\n goog.Dependency = function(\n path, relativePath, provides, requires, loadFlags) {\n /** @const */\n this.path = path;\n /** @const */\n this.relativePath = relativePath;\n /** @const */\n this.provides = provides;\n /** @const */\n this.requires = requires;\n /** @const */\n this.loadFlags = loadFlags;\n /** @private {boolean} */\n this.loaded_ = false;\n /** @private {!Array<function()>} */\n this.loadCallbacks_ = [];\n };\n\n\n /**\n * @return {string} The pathname part of this dependency's path if it is a\n * URI.\n */\n goog.Dependency.prototype.getPathName = function() {\n var pathName = this.path;\n var protocolIndex = pathName.indexOf('://');\n if (protocolIndex >= 0) {\n pathName = pathName.substring(protocolIndex + 3);\n var slashIndex = pathName.indexOf('/');\n if (slashIndex >= 0) {\n pathName = pathName.substring(slashIndex + 1);\n }\n }\n return pathName;\n };\n\n\n /**\n * @param {function()} callback Callback to fire as soon as this has loaded.\n * @final\n */\n goog.Dependency.prototype.onLoad = function(callback) {\n if (this.loaded_) {\n callback();\n } else {\n this.loadCallbacks_.push(callback);\n }\n };\n\n\n /**\n * Marks this dependency as loaded and fires any callbacks registered with\n * onLoad.\n * @final\n */\n goog.Dependency.prototype.loaded = function() {\n this.loaded_ = true;\n var callbacks = this.loadCallbacks_;\n this.loadCallbacks_ = [];\n for (var i = 0; i < callbacks.length; i++) {\n callbacks[i]();\n }\n };\n\n\n /**\n * Whether or not document.written / appended script tags should be deferred.\n *\n * @private {boolean}\n */\n goog.Dependency.defer_ = false;\n\n\n /**\n * Map of script ready / state change callbacks. Old IE cannot handle putting\n * these properties on goog.global.\n *\n * @private @const {!Object<string, function(?):undefined>}\n */\n goog.Dependency.callbackMap_ = {};\n\n\n /**\n * @param {function(...?):?} callback\n * @return {string}\n * @private\n */\n goog.Dependency.registerCallback_ = function(callback) {\n var key = Math.random().toString(32);\n goog.Dependency.callbackMap_[key] = callback;\n return key;\n };\n\n\n /**\n * @param {string} key\n * @private\n */\n goog.Dependency.unregisterCallback_ = function(key) {\n delete goog.Dependency.callbackMap_[key];\n };\n\n\n /**\n * @param {string} key\n * @param {...?} var_args\n * @private\n * @suppress {unusedPrivateMembers}\n */\n goog.Dependency.callback_ = function(key, var_args) {\n if (key in goog.Dependency.callbackMap_) {\n var callback = goog.Dependency.callbackMap_[key];\n var args = [];\n for (var i = 1; i < arguments.length; i++) {\n args.push(arguments[i]);\n }\n callback.apply(undefined, args);\n } else {\n var errorMessage = 'Callback key ' + key +\n ' does not exist (was base.js loaded more than once?).';\n throw Error(errorMessage);\n }\n };\n\n\n /**\n * Starts loading this dependency. This dependency can pause loading if it\n * needs to and resume it later via the controller interface.\n *\n * When this is loaded it should call controller.loaded(). Note that this will\n * end up calling the loaded method of this dependency; there is no need to\n * call it explicitly.\n *\n * @param {!goog.LoadController} controller\n */\n goog.Dependency.prototype.load = function(controller) {\n if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {\n controller.loaded();\n } else {\n controller.pause();\n }\n return;\n }\n\n if (!goog.inHtmlDocument_()) {\n goog.logToConsole_(\n 'Cannot use default debug loader outside of HTML documents.');\n if (this.relativePath == 'deps.js') {\n // Some old code is relying on base.js auto loading deps.js failing with\n // no error before later setting CLOSURE_IMPORT_SCRIPT.\n // CLOSURE_IMPORT_SCRIPT should be set *before* base.js is loaded, or\n // CLOSURE_NO_DEPS set to true.\n goog.logToConsole_(\n 'Consider setting CLOSURE_IMPORT_SCRIPT before loading base.js, ' +\n 'or setting CLOSURE_NO_DEPS to true.');\n controller.loaded();\n } else {\n controller.pause();\n }\n return;\n }\n\n /** @type {!HTMLDocument} */\n var doc = goog.global.document;\n\n // If the user tries to require a new symbol after document load,\n // something has gone terribly wrong. Doing a document.write would\n // wipe out the page. This does not apply to the CSP-compliant method\n // of writing script tags.\n if (doc.readyState == 'complete' &&\n !goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {\n // Certain test frameworks load base.js multiple times, which tries\n // to write deps.js each time. If that happens, just fail silently.\n // These frameworks wipe the page between each load of base.js, so this\n // is OK.\n var isDeps = /\\bdeps.js$/.test(this.path);\n if (isDeps) {\n controller.loaded();\n return;\n } else {\n throw Error('Cannot write \"' + this.path + '\" after document load');\n }\n }\n\n if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&\n goog.isDocumentLoading_()) {\n var key = goog.Dependency.registerCallback_(function(script) {\n if (!goog.DebugLoader_.IS_OLD_IE_ || script.readyState == 'complete') {\n goog.Dependency.unregisterCallback_(key);\n controller.loaded();\n }\n });\n var nonceAttr = !goog.DebugLoader_.IS_OLD_IE_ && goog.getScriptNonce() ?\n ' nonce=\"' + goog.getScriptNonce() + '\"' :\n '';\n var event =\n goog.DebugLoader_.IS_OLD_IE_ ? 'onreadystatechange' : 'onload';\n var defer = goog.Dependency.defer_ ? 'defer' : '';\n var script = '<script src=\"' + this.path + '\" ' + event +\n '=\"goog.Dependency.callback_(\\'' + key +\n '\\', this)\" type=\"text/javascript\" ' + defer + nonceAttr + '><' +\n '/script>';\n doc.write(\n goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n script);\n } else {\n var scriptEl =\n /** @type {!HTMLScriptElement} */ (doc.createElement('script'));\n scriptEl.defer = goog.Dependency.defer_;\n scriptEl.async = false;\n scriptEl.type = 'text/javascript';\n\n // If CSP nonces are used, propagate them to dynamically created scripts.\n // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.\n var nonce = goog.getScriptNonce();\n if (nonce) {\n scriptEl.setAttribute('nonce', nonce);\n }\n\n if (goog.DebugLoader_.IS_OLD_IE_) {\n // Execution order is not guaranteed on old IE, halt loading and write\n // these scripts one at a time, after each loads.\n controller.pause();\n scriptEl.onreadystatechange = function() {\n if (scriptEl.readyState == 'loaded' ||\n scriptEl.readyState == 'complete') {\n controller.loaded();\n controller.resume();\n }\n };\n } else {\n scriptEl.onload = function() {\n scriptEl.onload = null;\n controller.loaded();\n };\n }\n\n scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createScriptURL(this.path) :\n this.path;\n doc.head.appendChild(scriptEl);\n }\n };\n\n\n /**\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides Should be an empty array.\n * TODO(johnplaisted) add support for adding closure namespaces to ES6\n * modules for interop purposes.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @struct @constructor\n * @extends {goog.Dependency}\n */\n goog.Es6ModuleDependency = function(\n path, relativePath, provides, requires, loadFlags) {\n goog.Es6ModuleDependency.base(\n this, 'constructor', path, relativePath, provides, requires, loadFlags);\n };\n goog.inherits(goog.Es6ModuleDependency, goog.Dependency);\n\n\n /** @override */\n goog.Es6ModuleDependency.prototype.load = function(controller) {\n if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n if (goog.global.CLOSURE_IMPORT_SCRIPT(this.path)) {\n controller.loaded();\n } else {\n controller.pause();\n }\n return;\n }\n\n if (!goog.inHtmlDocument_()) {\n goog.logToConsole_(\n 'Cannot use default debug loader outside of HTML documents.');\n controller.pause();\n return;\n }\n\n /** @type {!HTMLDocument} */\n var doc = goog.global.document;\n\n var dep = this;\n\n // TODO(johnplaisted): Does document.writing really speed up anything? Any\n // difference between this and just waiting for interactive mode and then\n // appending?\n function write(src, contents) {\n if (contents) {\n var script = '<script type=\"module\" crossorigin>' + contents + '</' +\n 'script>';\n doc.write(\n goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n script);\n } else {\n var script = '<script type=\"module\" crossorigin src=\"' + src + '\"></' +\n 'script>';\n doc.write(\n goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n script);\n }\n }\n\n function append(src, contents) {\n var scriptEl =\n /** @type {!HTMLScriptElement} */ (doc.createElement('script'));\n scriptEl.defer = true;\n scriptEl.async = false;\n scriptEl.type = 'module';\n scriptEl.setAttribute('crossorigin', true);\n\n // If CSP nonces are used, propagate them to dynamically created scripts.\n // This is necessary to allow nonce-based CSPs without 'strict-dynamic'.\n var nonce = goog.getScriptNonce();\n if (nonce) {\n scriptEl.setAttribute('nonce', nonce);\n }\n\n if (contents) {\n scriptEl.textContent = goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createScript(contents) :\n contents;\n } else {\n scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createScriptURL(src) :\n src;\n }\n\n doc.head.appendChild(scriptEl);\n }\n\n var create;\n\n if (goog.isDocumentLoading_()) {\n create = write;\n // We can ONLY call document.write if we are guaranteed that any\n // non-module script tags document.written after this are deferred.\n // Small optimization, in theory document.writing is faster.\n goog.Dependency.defer_ = true;\n } else {\n create = append;\n }\n\n // Write 4 separate tags here:\n // 1) Sets the module state at the correct time (just before execution).\n // 2) A src node for this, which just hopefully lets the browser load it a\n // little early (no need to parse #3).\n // 3) Import the module and register it.\n // 4) Clear the module state at the correct time. Guaranteed to run even\n // if there is an error in the module (#3 will not run if there is an\n // error in the module).\n var beforeKey = goog.Dependency.registerCallback_(function() {\n goog.Dependency.unregisterCallback_(beforeKey);\n controller.setModuleState(goog.ModuleType.ES6);\n });\n create(undefined, 'goog.Dependency.callback_(\"' + beforeKey + '\")');\n\n // TODO(johnplaisted): Does this really speed up anything?\n create(this.path, undefined);\n\n var registerKey = goog.Dependency.registerCallback_(function(exports) {\n goog.Dependency.unregisterCallback_(registerKey);\n controller.registerEs6ModuleExports(\n dep.path, exports, goog.moduleLoaderState_.moduleName);\n });\n create(\n undefined,\n 'import * as m from \"' + this.path + '\"; goog.Dependency.callback_(\"' +\n registerKey + '\", m)');\n\n var afterKey = goog.Dependency.registerCallback_(function() {\n goog.Dependency.unregisterCallback_(afterKey);\n controller.clearModuleState();\n controller.loaded();\n });\n create(undefined, 'goog.Dependency.callback_(\"' + afterKey + '\")');\n };\n\n\n /**\n * Superclass of any dependency that needs to be loaded into memory,\n * transformed, and then eval'd (goog.modules and transpiled files).\n *\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides goog.provided or goog.module symbols\n * in this file.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @struct @constructor @abstract\n * @extends {goog.Dependency}\n */\n goog.TransformedDependency = function(\n path, relativePath, provides, requires, loadFlags) {\n goog.TransformedDependency.base(\n this, 'constructor', path, relativePath, provides, requires, loadFlags);\n /** @private {?string} */\n this.contents_ = null;\n\n /**\n * Whether to lazily make the synchronous XHR (when goog.require'd) or make\n * the synchronous XHR when initially loading. On FireFox 61 there is a bug\n * where an ES6 module cannot make a synchronous XHR (rather, it can, but if\n * it does then no other ES6 modules will load after).\n *\n * tl;dr we lazy load due to bugs on older browsers and eager load due to\n * bugs on newer ones.\n *\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1477090\n *\n * @private @const {boolean}\n */\n this.lazyFetch_ = !goog.inHtmlDocument_() ||\n !('noModule' in goog.global.document.createElement('script'));\n };\n goog.inherits(goog.TransformedDependency, goog.Dependency);\n\n\n /** @override */\n goog.TransformedDependency.prototype.load = function(controller) {\n var dep = this;\n\n function fetch() {\n dep.contents_ = goog.loadFileSync_(dep.path);\n\n if (dep.contents_) {\n dep.contents_ = dep.transform(dep.contents_);\n if (dep.contents_) {\n dep.contents_ += '\\n//# sourceURL=' + dep.path;\n }\n }\n }\n\n if (goog.global.CLOSURE_IMPORT_SCRIPT) {\n fetch();\n if (this.contents_ &&\n goog.global.CLOSURE_IMPORT_SCRIPT('', this.contents_)) {\n this.contents_ = null;\n controller.loaded();\n } else {\n controller.pause();\n }\n return;\n }\n\n\n var isEs6 = this.loadFlags['module'] == goog.ModuleType.ES6;\n\n if (!this.lazyFetch_) {\n fetch();\n }\n\n function load() {\n if (dep.lazyFetch_) {\n fetch();\n }\n\n if (!dep.contents_) {\n // loadFileSync_ or transform are responsible. Assume they logged an\n // error.\n return;\n }\n\n if (isEs6) {\n controller.setModuleState(goog.ModuleType.ES6);\n }\n\n var namespace;\n\n try {\n var contents = dep.contents_;\n dep.contents_ = null;\n goog.globalEval(contents);\n if (isEs6) {\n namespace = goog.moduleLoaderState_.moduleName;\n }\n } finally {\n if (isEs6) {\n controller.clearModuleState();\n }\n }\n\n if (isEs6) {\n // Due to circular dependencies this may not be available for require\n // right now.\n goog.global['$jscomp']['require']['ensure'](\n [dep.getPathName()], function() {\n controller.registerEs6ModuleExports(\n dep.path,\n goog.global['$jscomp']['require'](dep.getPathName()),\n namespace);\n });\n }\n\n controller.loaded();\n }\n\n // Do not fetch now; in FireFox 47 the synchronous XHR doesn't block all\n // events. If we fetched now and then document.write'd the contents the\n // document.write would be an eval and would execute too soon! Instead write\n // a script tag to fetch and eval synchronously at the correct time.\n function fetchInOwnScriptThenLoad() {\n /** @type {!HTMLDocument} */\n var doc = goog.global.document;\n\n var key = goog.Dependency.registerCallback_(function() {\n goog.Dependency.unregisterCallback_(key);\n load();\n });\n\n var script = '<script type=\"text/javascript\">' +\n goog.protectScriptTag_('goog.Dependency.callback_(\"' + key + '\");') +\n '</' +\n 'script>';\n doc.write(\n goog.TRUSTED_TYPES_POLICY_ ?\n goog.TRUSTED_TYPES_POLICY_.createHTML(script) :\n script);\n }\n\n // If one thing is pending it is this.\n var anythingElsePending = controller.pending().length > 1;\n\n // If anything else is loading we need to lazy load due to bugs in old IE.\n // Specifically script tags with src and script tags with contents could\n // execute out of order if document.write is used, so we cannot use\n // document.write. Do not pause here; it breaks old IE as well.\n var useOldIeWorkAround =\n anythingElsePending && goog.DebugLoader_.IS_OLD_IE_;\n\n // Additionally if we are meant to defer scripts but the page is still\n // loading (e.g. an ES6 module is loading) then also defer. Or if we are\n // meant to defer and anything else is pending then defer (those may be\n // scripts that did not need transformation and are just script tags with\n // defer set to true, and we need to evaluate after that deferred script).\n var needsAsyncLoading = goog.Dependency.defer_ &&\n (anythingElsePending || goog.isDocumentLoading_());\n\n if (useOldIeWorkAround || needsAsyncLoading) {\n // Note that we only defer when we have to rather than 100% of the time.\n // Always defering would work, but then in theory the order of\n // goog.require calls would then matter. We want to enforce that most of\n // the time the order of the require calls does not matter.\n controller.defer(function() {\n load();\n });\n return;\n }\n // TODO(johnplaisted): Externs are missing onreadystatechange for\n // HTMLDocument.\n /** @type {?} */\n var doc = goog.global.document;\n\n var isInternetExplorer =\n goog.inHtmlDocument_() && 'ActiveXObject' in goog.global;\n\n // Don't delay in any version of IE. There's bug around this that will\n // cause out of order script execution. This means that on older IE ES6\n // modules will load too early (while the document is still loading + the\n // dom is not available). The other option is to load too late (when the\n // document is complete and the onload even will never fire). This seems\n // to be the lesser of two evils as scripts already act like the former.\n if (isEs6 && goog.inHtmlDocument_() && goog.isDocumentLoading_() &&\n !isInternetExplorer) {\n goog.Dependency.defer_ = true;\n // Transpiled ES6 modules still need to load like regular ES6 modules,\n // aka only after the document is interactive.\n controller.pause();\n var oldCallback = doc.onreadystatechange;\n doc.onreadystatechange = function() {\n if (doc.readyState == 'interactive') {\n doc.onreadystatechange = oldCallback;\n load();\n controller.resume();\n }\n if (goog.isFunction(oldCallback)) {\n oldCallback.apply(undefined, arguments);\n }\n };\n } else {\n // Always eval on old IE.\n if (goog.DebugLoader_.IS_OLD_IE_ || !goog.inHtmlDocument_() ||\n !goog.isDocumentLoading_()) {\n load();\n } else {\n fetchInOwnScriptThenLoad();\n }\n }\n };\n\n\n /**\n * @param {string} contents\n * @return {string}\n * @abstract\n */\n goog.TransformedDependency.prototype.transform = function(contents) {};\n\n\n /**\n * Any non-goog.module dependency which needs to be transpiled before eval.\n *\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides goog.provided or goog.module symbols\n * in this file.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @param {!goog.Transpiler} transpiler\n * @struct @constructor\n * @extends {goog.TransformedDependency}\n */\n goog.TranspiledDependency = function(\n path, relativePath, provides, requires, loadFlags, transpiler) {\n goog.TranspiledDependency.base(\n this, 'constructor', path, relativePath, provides, requires, loadFlags);\n /** @protected @const*/\n this.transpiler = transpiler;\n };\n goog.inherits(goog.TranspiledDependency, goog.TransformedDependency);\n\n\n /** @override */\n goog.TranspiledDependency.prototype.transform = function(contents) {\n // Transpile with the pathname so that ES6 modules are domain agnostic.\n return this.transpiler.transpile(contents, this.getPathName());\n };\n\n\n /**\n * An ES6 module dependency that was transpiled to a jscomp module outside\n * of the debug loader, e.g. server side.\n *\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides goog.provided or goog.module symbols\n * in this file.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @struct @constructor\n * @extends {goog.TransformedDependency}\n */\n goog.PreTranspiledEs6ModuleDependency = function(\n path, relativePath, provides, requires, loadFlags) {\n goog.PreTranspiledEs6ModuleDependency.base(\n this, 'constructor', path, relativePath, provides, requires, loadFlags);\n };\n goog.inherits(\n goog.PreTranspiledEs6ModuleDependency, goog.TransformedDependency);\n\n\n /** @override */\n goog.PreTranspiledEs6ModuleDependency.prototype.transform = function(\n contents) {\n return contents;\n };\n\n\n /**\n * A goog.module, transpiled or not. Will always perform some minimal\n * transformation even when not transpiled to wrap in a goog.loadModule\n * statement.\n *\n * @param {string} path Absolute path of this script.\n * @param {string} relativePath Path of this script relative to goog.basePath.\n * @param {!Array<string>} provides goog.provided or goog.module symbols\n * in this file.\n * @param {!Array<string>} requires goog symbols or relative paths to Closure\n * this depends on.\n * @param {!Object<string, string>} loadFlags\n * @param {boolean} needsTranspile\n * @param {!goog.Transpiler} transpiler\n * @struct @constructor\n * @extends {goog.TransformedDependency}\n */\n goog.GoogModuleDependency = function(\n path, relativePath, provides, requires, loadFlags, needsTranspile,\n transpiler) {\n goog.GoogModuleDependency.base(\n this, 'constructor', path, relativePath, provides, requires, loadFlags);\n /** @private @const */\n this.needsTranspile_ = needsTranspile;\n /** @private @const */\n this.transpiler_ = transpiler;\n };\n goog.inherits(goog.GoogModuleDependency, goog.TransformedDependency);\n\n\n /** @override */\n goog.GoogModuleDependency.prototype.transform = function(contents) {\n if (this.needsTranspile_) {\n contents = this.transpiler_.transpile(contents, this.getPathName());\n }\n\n if (!goog.LOAD_MODULE_USING_EVAL || goog.global.JSON === undefined) {\n return '' +\n 'goog.loadModule(function(exports) {' +\n '\"use strict\";' + contents +\n '\\n' + // terminate any trailing single line comment.\n ';return exports' +\n '});' +\n '\\n//# sourceURL=' + this.path + '\\n';\n } else {\n return '' +\n 'goog.loadModule(' +\n goog.global.JSON.stringify(\n contents + '\\n//# sourceURL=' + this.path + '\\n') +\n ');';\n }\n };\n\n\n /**\n * Whether the browser is IE9 or earlier, which needs special handling\n * for deferred modules.\n * @const @private {boolean}\n */\n goog.DebugLoader_.IS_OLD_IE_ = !!(\n !goog.global.atob && goog.global.document && goog.global.document['all']);\n\n\n /**\n * @param {string} relPath\n * @param {!Array<string>|undefined} provides\n * @param {!Array<string>} requires\n * @param {boolean|!Object<string>=} opt_loadFlags\n * @see goog.addDependency\n */\n goog.DebugLoader_.prototype.addDependency = function(\n relPath, provides, requires, opt_loadFlags) {\n provides = provides || [];\n relPath = relPath.replace(/\\\\/g, '/');\n var path = goog.normalizePath_(goog.basePath + relPath);\n if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') {\n opt_loadFlags = opt_loadFlags ? {'module': goog.ModuleType.GOOG} : {};\n }\n var dep = this.factory_.createDependency(\n path, relPath, provides, requires, opt_loadFlags,\n goog.transpiler_.needsTranspile(\n opt_loadFlags['lang'] || 'es3', opt_loadFlags['module']));\n this.dependencies_[path] = dep;\n for (var i = 0; i < provides.length; i++) {\n this.idToPath_[provides[i]] = path;\n }\n this.idToPath_[relPath] = path;\n };\n\n\n /**\n * Creates goog.Dependency instances for the debug loader to load.\n *\n * Should be overridden to have the debug loader use custom subclasses of\n * goog.Dependency.\n *\n * @param {!goog.Transpiler} transpiler\n * @struct @constructor\n */\n goog.DependencyFactory = function(transpiler) {\n /** @protected @const */\n this.transpiler = transpiler;\n };\n\n\n /**\n * @param {string} path Absolute path of the file.\n * @param {string} relativePath Path relative to closure’s base.js.\n * @param {!Array<string>} provides Array of provided goog.provide/module ids.\n * @param {!Array<string>} requires Array of required goog.provide/module /\n * relative ES6 module paths.\n * @param {!Object<string, string>} loadFlags\n * @param {boolean} needsTranspile True if the file needs to be transpiled\n * per the goog.Transpiler.\n * @return {!goog.Dependency}\n */\n goog.DependencyFactory.prototype.createDependency = function(\n path, relativePath, provides, requires, loadFlags, needsTranspile) {\n\n if (loadFlags['module'] == goog.ModuleType.GOOG) {\n return new goog.GoogModuleDependency(\n path, relativePath, provides, requires, loadFlags, needsTranspile,\n this.transpiler);\n } else if (needsTranspile) {\n return new goog.TranspiledDependency(\n path, relativePath, provides, requires, loadFlags, this.transpiler);\n } else {\n if (loadFlags['module'] == goog.ModuleType.ES6) {\n if (goog.TRANSPILE == 'never' && goog.ASSUME_ES_MODULES_TRANSPILED) {\n return new goog.PreTranspiledEs6ModuleDependency(\n path, relativePath, provides, requires, loadFlags);\n } else {\n return new goog.Es6ModuleDependency(\n path, relativePath, provides, requires, loadFlags);\n }\n } else {\n return new goog.Dependency(\n path, relativePath, provides, requires, loadFlags);\n }\n }\n };\n\n\n /** @private @const */\n goog.debugLoader_ = new goog.DebugLoader_();\n\n\n /**\n * Loads the Closure Dependency file.\n *\n * Exposed a public function so CLOSURE_NO_DEPS can be set to false, base\n * loaded, setDependencyFactory called, and then this called. i.e. allows\n * custom loading of the deps file.\n */\n goog.loadClosureDeps = function() {\n goog.debugLoader_.loadClosureDeps();\n };\n\n\n /**\n * Sets the dependency factory, which can be used to create custom\n * goog.Dependency implementations to control how dependencies are loaded.\n *\n * Note: if you wish to call this function and provide your own implemnetation\n * it is a wise idea to set CLOSURE_NO_DEPS to true, otherwise the dependency\n * file and all of its goog.addDependency calls will use the default factory.\n * You can call goog.loadClosureDeps to load the Closure dependency file\n * later, after your factory is injected.\n *\n * @param {!goog.DependencyFactory} factory\n */\n goog.setDependencyFactory = function(factory) {\n goog.debugLoader_.setDependencyFactory(factory);\n };\n\n\n if (!goog.global.CLOSURE_NO_DEPS) {\n goog.debugLoader_.loadClosureDeps();\n }\n\n\n /**\n * Bootstraps the given namespaces and calls the callback once they are\n * available either via goog.require. This is a replacement for using\n * `goog.require` to bootstrap Closure JavaScript. Previously a `goog.require`\n * in an HTML file would guarantee that the require'd namespace was available\n * in the next immediate script tag. With ES6 modules this no longer a\n * guarantee.\n *\n * @param {!Array<string>} namespaces\n * @param {function(): ?} callback Function to call once all the namespaces\n * have loaded. Always called asynchronously.\n */\n goog.bootstrap = function(namespaces, callback) {\n goog.debugLoader_.bootstrap(namespaces, callback);\n };\n}\n\n\n/**\n * @define {string} Trusted Types policy name. If non-empty then Closure will\n * use Trusted Types.\n */\ngoog.TRUSTED_TYPES_POLICY_NAME =\n goog.define('goog.TRUSTED_TYPES_POLICY_NAME', '');\n\n\n/**\n * Returns the parameter.\n * @param {string} s\n * @return {string}\n * @private\n */\ngoog.identity_ = function(s) {\n return s;\n};\n\n\n/**\n * Creates Trusted Types policy if Trusted Types are supported by the browser.\n * The policy just blesses any string as a Trusted Type. It is not visibility\n * restricted because anyone can also call TrustedTypes.createPolicy directly.\n * However, the allowed names should be restricted by a HTTP header and the\n * reference to the created policy should be visibility restricted.\n * @param {string} name\n * @return {?TrustedTypePolicy}\n */\ngoog.createTrustedTypesPolicy = function(name) {\n var policy = null;\n // TODO(koto): Remove window.TrustedTypes variant when the newer API ships.\n var policyFactory = goog.global.trustedTypes || goog.global.TrustedTypes;\n if (!policyFactory || !policyFactory.createPolicy) {\n return policy;\n }\n // TrustedTypes.createPolicy throws if called with a name that is already\n // registered, even in report-only mode. Until the API changes, catch the\n // error not to break the applications functionally. In such case, the code\n // will fall back to using regular Safe Types.\n // TODO(koto): Remove catching once createPolicy API stops throwing.\n try {\n policy = policyFactory.createPolicy(name, {\n createHTML: goog.identity_,\n createScript: goog.identity_,\n createScriptURL: goog.identity_,\n createURL: goog.identity_\n });\n } catch (e) {\n goog.logToConsole_(e.message);\n }\n return policy;\n};\n\n\n/** @private @const {?TrustedTypePolicy} */\ngoog.TRUSTED_TYPES_POLICY_ = goog.TRUSTED_TYPES_POLICY_NAME ?\n goog.createTrustedTypesPolicy(goog.TRUSTED_TYPES_POLICY_NAME + '#base') :\n null;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Implements the disposable interface. The dispose method is used\n * to clean up references and resources.\n */\n\n\ngoog.provide('goog.Disposable');\ngoog.provide('goog.dispose');\ngoog.provide('goog.disposeAll');\n\ngoog.require('goog.disposable.IDisposable');\n\n\n\n/**\n * Class that provides the basic implementation for disposable objects. If your\n * class holds references or resources that can't be collected by standard GC,\n * it should extend this class or implement the disposable interface (defined\n * in goog.disposable.IDisposable). See description of\n * goog.disposable.IDisposable for examples of cleanup.\n * @constructor\n * @implements {goog.disposable.IDisposable}\n */\ngoog.Disposable = function() {\n /**\n * If monitoring the goog.Disposable instances is enabled, stores the creation\n * stack trace of the Disposable instance.\n * @type {string|undefined}\n */\n this.creationStack;\n\n if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {\n if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {\n this.creationStack = new Error().stack;\n }\n goog.Disposable.instances_[goog.getUid(this)] = this;\n }\n // Support sealing\n this.disposed_ = this.disposed_;\n this.onDisposeCallbacks_ = this.onDisposeCallbacks_;\n};\n\n\n/**\n * @enum {number} Different monitoring modes for Disposable.\n */\ngoog.Disposable.MonitoringMode = {\n /**\n * No monitoring.\n */\n OFF: 0,\n /**\n * Creating and disposing the goog.Disposable instances is monitored. All\n * disposable objects need to call the `goog.Disposable` base\n * constructor. The PERMANENT mode must be switched on before creating any\n * goog.Disposable instances.\n */\n PERMANENT: 1,\n /**\n * INTERACTIVE mode can be switched on and off on the fly without producing\n * errors. It also doesn't warn if the disposable objects don't call the\n * `goog.Disposable` base constructor.\n */\n INTERACTIVE: 2\n};\n\n\n/**\n * @define {number} The monitoring mode of the goog.Disposable\n * instances. Default is OFF. Switching on the monitoring is only\n * recommended for debugging because it has a significant impact on\n * performance and memory usage. If switched off, the monitoring code\n * compiles down to 0 bytes.\n */\ngoog.Disposable.MONITORING_MODE =\n goog.define('goog.Disposable.MONITORING_MODE', 0);\n\n\n/**\n * @define {boolean} Whether to attach creation stack to each created disposable\n * instance; This is only relevant for when MonitoringMode != OFF.\n */\ngoog.Disposable.INCLUDE_STACK_ON_CREATION =\n goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);\n\n\n/**\n * Maps the unique ID of every undisposed `goog.Disposable` object to\n * the object itself.\n * @type {!Object<number, !goog.Disposable>}\n * @private\n */\ngoog.Disposable.instances_ = {};\n\n\n/**\n * @return {!Array<!goog.Disposable>} All `goog.Disposable` objects that\n * haven't been disposed of.\n */\ngoog.Disposable.getUndisposedObjects = function() {\n var ret = [];\n for (var id in goog.Disposable.instances_) {\n if (goog.Disposable.instances_.hasOwnProperty(id)) {\n ret.push(goog.Disposable.instances_[Number(id)]);\n }\n }\n return ret;\n};\n\n\n/**\n * Clears the registry of undisposed objects but doesn't dispose of them.\n */\ngoog.Disposable.clearUndisposedObjects = function() {\n goog.Disposable.instances_ = {};\n};\n\n\n/**\n * Whether the object has been disposed of.\n * @type {boolean}\n * @private\n */\ngoog.Disposable.prototype.disposed_ = false;\n\n\n/**\n * Callbacks to invoke when this object is disposed.\n * @type {Array<!Function>}\n * @private\n */\ngoog.Disposable.prototype.onDisposeCallbacks_;\n\n\n/**\n * @return {boolean} Whether the object has been disposed of.\n * @override\n */\ngoog.Disposable.prototype.isDisposed = function() {\n return this.disposed_;\n};\n\n\n/**\n * @return {boolean} Whether the object has been disposed of.\n * @deprecated Use {@link #isDisposed} instead.\n */\ngoog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;\n\n\n/**\n * Disposes of the object. If the object hasn't already been disposed of, calls\n * {@link #disposeInternal}. Classes that extend `goog.Disposable` should\n * override {@link #disposeInternal} in order to cleanup references, resources\n * and other disposable objects. Reentrant.\n *\n * @return {void} Nothing.\n * @override\n */\ngoog.Disposable.prototype.dispose = function() {\n if (!this.disposed_) {\n // Set disposed_ to true first, in case during the chain of disposal this\n // gets disposed recursively.\n this.disposed_ = true;\n this.disposeInternal();\n if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {\n var uid = goog.getUid(this);\n if (goog.Disposable.MONITORING_MODE ==\n goog.Disposable.MonitoringMode.PERMANENT &&\n !goog.Disposable.instances_.hasOwnProperty(uid)) {\n throw new Error(\n this + ' did not call the goog.Disposable base ' +\n 'constructor or was disposed of after a clearUndisposedObjects ' +\n 'call');\n }\n if (goog.Disposable.MONITORING_MODE !=\n goog.Disposable.MonitoringMode.OFF &&\n this.onDisposeCallbacks_ && this.onDisposeCallbacks_.length > 0) {\n throw new Error(\n this + ' did not empty its onDisposeCallbacks queue. This ' +\n 'probably means it overrode dispose() or disposeInternal() ' +\n 'without calling the superclass\\' method.');\n }\n delete goog.Disposable.instances_[uid];\n }\n }\n};\n\n\n/**\n * Associates a disposable object with this object so that they will be disposed\n * together.\n * @param {goog.disposable.IDisposable} disposable that will be disposed when\n * this object is disposed.\n */\ngoog.Disposable.prototype.registerDisposable = function(disposable) {\n this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));\n};\n\n\n/**\n * Invokes a callback function when this object is disposed. Callbacks are\n * invoked in the order in which they were added. If a callback is added to\n * an already disposed Disposable, it will be called immediately.\n * @param {function(this:T):?} callback The callback function.\n * @param {T=} opt_scope An optional scope to call the callback in.\n * @template T\n */\ngoog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {\n if (this.disposed_) {\n opt_scope !== undefined ? callback.call(opt_scope) : callback();\n return;\n }\n if (!this.onDisposeCallbacks_) {\n this.onDisposeCallbacks_ = [];\n }\n\n this.onDisposeCallbacks_.push(\n opt_scope !== undefined ? goog.bind(callback, opt_scope) : callback);\n};\n\n\n/**\n * Performs appropriate cleanup. See description of goog.disposable.IDisposable\n * for examples. Classes that extend `goog.Disposable` should override this\n * method. Not reentrant. To avoid calling it twice, it must only be called from\n * the subclass' `disposeInternal` method. Everywhere else the public `dispose`\n * method must be used. For example:\n *\n * <pre>\n * mypackage.MyClass = function() {\n * mypackage.MyClass.base(this, 'constructor');\n * // Constructor logic specific to MyClass.\n * ...\n * };\n * goog.inherits(mypackage.MyClass, goog.Disposable);\n *\n * mypackage.MyClass.prototype.disposeInternal = function() {\n * // Dispose logic specific to MyClass.\n * ...\n * // Call superclass's disposeInternal at the end of the subclass's, like\n * // in C++, to avoid hard-to-catch issues.\n * mypackage.MyClass.base(this, 'disposeInternal');\n * };\n * </pre>\n *\n * @protected\n */\ngoog.Disposable.prototype.disposeInternal = function() {\n if (this.onDisposeCallbacks_) {\n while (this.onDisposeCallbacks_.length) {\n this.onDisposeCallbacks_.shift()();\n }\n }\n};\n\n\n/**\n * Returns True if we can verify the object is disposed.\n * Calls `isDisposed` on the argument if it supports it. If obj\n * is not an object with an isDisposed() method, return false.\n * @param {*} obj The object to investigate.\n * @return {boolean} True if we can verify the object is disposed.\n */\ngoog.Disposable.isDisposed = function(obj) {\n if (obj && typeof obj.isDisposed == 'function') {\n return obj.isDisposed();\n }\n return false;\n};\n\n\n/**\n * Calls `dispose` on the argument if it supports it. If obj is not an\n * object with a dispose() method, this is a no-op.\n * @param {*} obj The object to dispose of.\n */\ngoog.dispose = function(obj) {\n if (obj && typeof obj.dispose == 'function') {\n obj.dispose();\n }\n};\n\n\n/**\n * Calls `dispose` on each member of the list that supports it. (If the\n * member is an ArrayLike, then `goog.disposeAll()` will be called\n * recursively on each of its members.) If the member is not an object with a\n * `dispose()` method, then it is ignored.\n * @param {...*} var_args The list.\n */\ngoog.disposeAll = function(var_args) {\n for (var i = 0, len = arguments.length; i < len; ++i) {\n var disposable = arguments[i];\n if (goog.isArrayLike(disposable)) {\n goog.disposeAll.apply(null, disposable);\n } else {\n goog.dispose(disposable);\n }\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for manipulating arrays.\n */\n\n\ngoog.provide('goog.array');\n\ngoog.require('goog.asserts');\n\n\n/**\n * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should\n * rely on Array.prototype functions, if available.\n *\n * The Array.prototype functions can be defined by external libraries like\n * Prototype and setting this flag to false forces closure to use its own\n * goog.array implementation.\n *\n * If your javascript can be loaded by a third party site and you are wary about\n * relying on the prototype functions, specify\n * \"--define goog.NATIVE_ARRAY_PROTOTYPES=false\" to the JSCompiler.\n *\n * Setting goog.TRUSTED_SITE to false will automatically set\n * NATIVE_ARRAY_PROTOTYPES to false.\n */\ngoog.NATIVE_ARRAY_PROTOTYPES =\n goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);\n\n\n/**\n * @define {boolean} If true, JSCompiler will use the native implementation of\n * array functions where appropriate (e.g., `Array#filter`) and remove the\n * unused pure JS implementation.\n */\ngoog.array.ASSUME_NATIVE_FUNCTIONS = goog.define(\n 'goog.array.ASSUME_NATIVE_FUNCTIONS', goog.FEATURESET_YEAR > 2012);\n\n\n/**\n * Returns the last element in an array without removing it.\n * Same as goog.array.last.\n * @param {IArrayLike<T>|string} array The array.\n * @return {T} Last item in array.\n * @template T\n */\ngoog.array.peek = function(array) {\n return array[array.length - 1];\n};\n\n\n/**\n * Returns the last element in an array without removing it.\n * Same as goog.array.peek.\n * @param {IArrayLike<T>|string} array The array.\n * @return {T} Last item in array.\n * @template T\n */\ngoog.array.last = goog.array.peek;\n\n// NOTE(arv): Since most of the array functions are generic it allows you to\n// pass an array-like object. Strings have a length and are considered array-\n// like. However, the 'in' operator does not work on strings so we cannot just\n// use the array path even if the browser supports indexing into strings. We\n// therefore end up splitting the string.\n\n\n/**\n * Returns the index of the first element of an array with a specified value, or\n * -1 if the element is not present in the array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}\n *\n * @param {IArrayLike<T>|string} arr The array to be searched.\n * @param {T} obj The object for which we are searching.\n * @param {number=} opt_fromIndex The index at which to start the search. If\n * omitted the search starts at index 0.\n * @return {number} The index of the first matching array element.\n * @template T\n */\ngoog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ?\n function(arr, obj, opt_fromIndex) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.indexOf.call(arr, obj, opt_fromIndex);\n } :\n function(arr, obj, opt_fromIndex) {\n var fromIndex = opt_fromIndex == null ?\n 0 :\n (opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) :\n opt_fromIndex);\n\n if (typeof arr === 'string') {\n // Array.prototype.indexOf uses === so only strings should be found.\n if (typeof obj !== 'string' || obj.length != 1) {\n return -1;\n }\n return arr.indexOf(obj, fromIndex);\n }\n\n for (var i = fromIndex; i < arr.length; i++) {\n if (i in arr && arr[i] === obj) return i;\n }\n return -1;\n };\n\n\n/**\n * Returns the index of the last element of an array with a specified value, or\n * -1 if the element is not present in the array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}\n *\n * @param {!IArrayLike<T>|string} arr The array to be searched.\n * @param {T} obj The object for which we are searching.\n * @param {?number=} opt_fromIndex The index at which to start the search. If\n * omitted the search starts at the end of the array.\n * @return {number} The index of the last matching array element.\n * @template T\n */\ngoog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ?\n function(arr, obj, opt_fromIndex) {\n goog.asserts.assert(arr.length != null);\n\n // Firefox treats undefined and null as 0 in the fromIndex argument which\n // leads it to always return -1\n var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;\n return Array.prototype.lastIndexOf.call(arr, obj, fromIndex);\n } :\n function(arr, obj, opt_fromIndex) {\n var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;\n\n if (fromIndex < 0) {\n fromIndex = Math.max(0, arr.length + fromIndex);\n }\n\n if (typeof arr === 'string') {\n // Array.prototype.lastIndexOf uses === so only strings should be found.\n if (typeof obj !== 'string' || obj.length != 1) {\n return -1;\n }\n return arr.lastIndexOf(obj, fromIndex);\n }\n\n for (var i = fromIndex; i >= 0; i--) {\n if (i in arr && arr[i] === obj) return i;\n }\n return -1;\n };\n\n\n/**\n * Calls a function for each element in an array. Skips holes in the array.\n * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}\n *\n * @param {IArrayLike<T>|string} arr Array or array like object over\n * which to iterate.\n * @param {?function(this: S, T, number, ?): ?} f The function to call for every\n * element. This function takes 3 arguments (the element, the index and the\n * array). The return value is ignored.\n * @param {S=} opt_obj The object to be used as the value of 'this' within f.\n * @template T,S\n */\ngoog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ?\n function(arr, f, opt_obj) {\n goog.asserts.assert(arr.length != null);\n\n Array.prototype.forEach.call(arr, f, opt_obj);\n } :\n function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2) {\n f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n }\n }\n };\n\n\n/**\n * Calls a function for each element in an array, starting from the last\n * element rather than the first.\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this: S, T, number, ?): ?} f The function to call for every\n * element. This function\n * takes 3 arguments (the element, the index and the array). The return\n * value is ignored.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @template T,S\n */\ngoog.array.forEachRight = function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = l - 1; i >= 0; --i) {\n if (i in arr2) {\n f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n }\n }\n};\n\n\n/**\n * Calls a function for each element in an array, and if the function returns\n * true adds the element to a new array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?):boolean} f The function to call for\n * every element. This function\n * takes 3 arguments (the element, the index and the array) and must\n * return a Boolean. If the return value is true the element is added to the\n * result array. If it is false the element is not included.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @return {!Array<T>} a new array in which only elements that passed the test\n * are present.\n * @template T,S\n */\ngoog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ?\n function(arr, f, opt_obj) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.filter.call(arr, f, opt_obj);\n } :\n function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var res = [];\n var resLength = 0;\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2) {\n var val = arr2[i]; // in case f mutates arr2\n if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) {\n res[resLength++] = val;\n }\n }\n }\n return res;\n };\n\n\n/**\n * Calls a function for each element in an array and inserts the result into a\n * new array.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-map}\n *\n * @param {IArrayLike<VALUE>|string} arr Array or array like object\n * over which to iterate.\n * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call\n * for every element. This function takes 3 arguments (the element,\n * the index and the array) and should return something. The result will be\n * inserted into a new array.\n * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.\n * @return {!Array<RESULT>} a new array with the results from f.\n * @template THIS, VALUE, RESULT\n */\ngoog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ?\n function(arr, f, opt_obj) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.map.call(arr, f, opt_obj);\n } :\n function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var res = new Array(l);\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2) {\n res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);\n }\n }\n return res;\n };\n\n\n/**\n * Passes every element of an array into a function and accumulates the result.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}\n *\n * For example:\n * var a = [1, 2, 3, 4];\n * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);\n * returns 10\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {function(this:S, R, T, number, ?) : R} f The function to call for\n * every element. This function\n * takes 4 arguments (the function's previous result or the initial value,\n * the value of the current array element, the current array index, and the\n * array itself)\n * function(previousValue, currentValue, index, array).\n * @param {?} val The initial value to pass into the function on the first call.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @return {R} Result of evaluating f repeatedly across the values of the array.\n * @template T,S,R\n */\ngoog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ?\n function(arr, f, val, opt_obj) {\n goog.asserts.assert(arr.length != null);\n if (opt_obj) {\n f = goog.bind(f, opt_obj);\n }\n return Array.prototype.reduce.call(arr, f, val);\n } :\n function(arr, f, val, opt_obj) {\n var rval = val;\n goog.array.forEach(arr, function(val, index) {\n rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);\n });\n return rval;\n };\n\n\n/**\n * Passes every element of an array into a function and accumulates the result,\n * starting from the last element and working towards the first.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}\n *\n * For example:\n * var a = ['a', 'b', 'c'];\n * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');\n * returns 'cba'\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, R, T, number, ?) : R} f The function to call for\n * every element. This function\n * takes 4 arguments (the function's previous result or the initial value,\n * the value of the current array element, the current array index, and the\n * array itself)\n * function(previousValue, currentValue, index, array).\n * @param {?} val The initial value to pass into the function on the first call.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @return {R} Object returned as a result of evaluating f repeatedly across the\n * values of the array.\n * @template T,S,R\n */\ngoog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ?\n function(arr, f, val, opt_obj) {\n goog.asserts.assert(arr.length != null);\n goog.asserts.assert(f != null);\n if (opt_obj) {\n f = goog.bind(f, opt_obj);\n }\n return Array.prototype.reduceRight.call(arr, f, val);\n } :\n function(arr, f, val, opt_obj) {\n var rval = val;\n goog.array.forEachRight(arr, function(val, index) {\n rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);\n });\n return rval;\n };\n\n\n/**\n * Calls f for each element of an array. If any call returns true, some()\n * returns true (without checking the remaining elements). If all calls\n * return false, some() returns false.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-some}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n * for every element. This function takes 3 arguments (the element, the\n * index and the array) and should return a boolean.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @return {boolean} true if any element passes the test.\n * @template T,S\n */\ngoog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ?\n function(arr, f, opt_obj) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.some.call(arr, f, opt_obj);\n } :\n function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n return true;\n }\n }\n return false;\n };\n\n\n/**\n * Call f for each element of an array. If all calls return true, every()\n * returns true. If any call returns false, every() returns false and\n * does not continue to check the remaining elements.\n *\n * See {@link http://tinyurl.com/developer-mozilla-org-array-every}\n *\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n * for every element. This function takes 3 arguments (the element, the\n * index and the array) and should return a boolean.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within f.\n * @return {boolean} false if any element fails the test.\n * @template T,S\n */\ngoog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&\n (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ?\n function(arr, f, opt_obj) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.every.call(arr, f, opt_obj);\n } :\n function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n return false;\n }\n }\n return true;\n };\n\n\n/**\n * Counts the array elements that fulfill the predicate, i.e. for which the\n * callback function returns true. Skips holes in the array.\n *\n * @param {!IArrayLike<T>|string} arr Array or array like object\n * over which to iterate.\n * @param {function(this: S, T, number, ?): boolean} f The function to call for\n * every element. Takes 3 arguments (the element, the index and the array).\n * @param {S=} opt_obj The object to be used as the value of 'this' within f.\n * @return {number} The number of the matching elements.\n * @template T,S\n */\ngoog.array.count = function(arr, f, opt_obj) {\n var count = 0;\n goog.array.forEach(arr, function(element, index, arr) {\n if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) {\n ++count;\n }\n }, opt_obj);\n return count;\n};\n\n\n/**\n * Search an array for the first element that satisfies a given condition and\n * return that element.\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n * for every element. This function takes 3 arguments (the element, the\n * index and the array) and should return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {T|null} The first array element that passes the test, or null if no\n * element is found.\n * @template T,S\n */\ngoog.array.find = function(arr, f, opt_obj) {\n var i = goog.array.findIndex(arr, f, opt_obj);\n return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];\n};\n\n\n/**\n * Search an array for the first element that satisfies a given condition and\n * return its index.\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call for\n * every element. This function\n * takes 3 arguments (the element, the index and the array) and should\n * return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The index of the first array element that passes the test,\n * or -1 if no element is found.\n * @template T,S\n */\ngoog.array.findIndex = function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = 0; i < l; i++) {\n if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n return i;\n }\n }\n return -1;\n};\n\n\n/**\n * Search an array (in reverse order) for the last element that satisfies a\n * given condition and return that element.\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n * for every element. This function\n * takes 3 arguments (the element, the index and the array) and should\n * return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {T|null} The last array element that passes the test, or null if no\n * element is found.\n * @template T,S\n */\ngoog.array.findRight = function(arr, f, opt_obj) {\n var i = goog.array.findIndexRight(arr, f, opt_obj);\n return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];\n};\n\n\n/**\n * Search an array (in reverse order) for the last element that satisfies a\n * given condition and return its index.\n * @param {IArrayLike<T>|string} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n * for every element. This function\n * takes 3 arguments (the element, the index and the array) and should\n * return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The index of the last array element that passes the test,\n * or -1 if no element is found.\n * @template T,S\n */\ngoog.array.findIndexRight = function(arr, f, opt_obj) {\n var l = arr.length; // must be fixed during loop... see docs\n var arr2 = (typeof arr === 'string') ? arr.split('') : arr;\n for (var i = l - 1; i >= 0; i--) {\n if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {\n return i;\n }\n }\n return -1;\n};\n\n\n/**\n * Whether the array contains the given object.\n * @param {IArrayLike<?>|string} arr The array to test for the presence of the\n * element.\n * @param {*} obj The object for which to test.\n * @return {boolean} true if obj is present.\n */\ngoog.array.contains = function(arr, obj) {\n return goog.array.indexOf(arr, obj) >= 0;\n};\n\n\n/**\n * Whether the array is empty.\n * @param {IArrayLike<?>|string} arr The array to test.\n * @return {boolean} true if empty.\n */\ngoog.array.isEmpty = function(arr) {\n return arr.length == 0;\n};\n\n\n/**\n * Clears the array.\n * @param {IArrayLike<?>} arr Array or array like object to clear.\n */\ngoog.array.clear = function(arr) {\n // For non real arrays we don't have the magic length so we delete the\n // indices.\n if (!Array.isArray(arr)) {\n for (var i = arr.length - 1; i >= 0; i--) {\n delete arr[i];\n }\n }\n arr.length = 0;\n};\n\n\n/**\n * Pushes an item into an array, if it's not already in the array.\n * @param {Array<T>} arr Array into which to insert the item.\n * @param {T} obj Value to add.\n * @template T\n */\ngoog.array.insert = function(arr, obj) {\n if (!goog.array.contains(arr, obj)) {\n arr.push(obj);\n }\n};\n\n\n/**\n * Inserts an object at the given index of the array.\n * @param {IArrayLike<?>} arr The array to modify.\n * @param {*} obj The object to insert.\n * @param {number=} opt_i The index at which to insert the object. If omitted,\n * treated as 0. A negative index is counted from the end of the array.\n */\ngoog.array.insertAt = function(arr, obj, opt_i) {\n goog.array.splice(arr, opt_i, 0, obj);\n};\n\n\n/**\n * Inserts at the given index of the array, all elements of another array.\n * @param {IArrayLike<?>} arr The array to modify.\n * @param {IArrayLike<?>} elementsToAdd The array of elements to add.\n * @param {number=} opt_i The index at which to insert the object. If omitted,\n * treated as 0. A negative index is counted from the end of the array.\n */\ngoog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {\n goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);\n};\n\n\n/**\n * Inserts an object into an array before a specified object.\n * @param {Array<T>} arr The array to modify.\n * @param {T} obj The object to insert.\n * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2\n * is omitted or not found, obj is inserted at the end of the array.\n * @template T\n */\ngoog.array.insertBefore = function(arr, obj, opt_obj2) {\n var i;\n if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {\n arr.push(obj);\n } else {\n goog.array.insertAt(arr, obj, i);\n }\n};\n\n\n/**\n * Removes the first occurrence of a particular value from an array.\n * @param {IArrayLike<T>} arr Array from which to remove\n * value.\n * @param {T} obj Object to remove.\n * @return {boolean} True if an element was removed.\n * @template T\n */\ngoog.array.remove = function(arr, obj) {\n var i = goog.array.indexOf(arr, obj);\n var rv;\n if ((rv = i >= 0)) {\n goog.array.removeAt(arr, i);\n }\n return rv;\n};\n\n\n/**\n * Removes the last occurrence of a particular value from an array.\n * @param {!IArrayLike<T>} arr Array from which to remove value.\n * @param {T} obj Object to remove.\n * @return {boolean} True if an element was removed.\n * @template T\n */\ngoog.array.removeLast = function(arr, obj) {\n var i = goog.array.lastIndexOf(arr, obj);\n if (i >= 0) {\n goog.array.removeAt(arr, i);\n return true;\n }\n return false;\n};\n\n\n/**\n * Removes from an array the element at index i\n * @param {IArrayLike<?>} arr Array or array like object from which to\n * remove value.\n * @param {number} i The index to remove.\n * @return {boolean} True if an element was removed.\n */\ngoog.array.removeAt = function(arr, i) {\n goog.asserts.assert(arr.length != null);\n\n // use generic form of splice\n // splice returns the removed items and if successful the length of that\n // will be 1\n return Array.prototype.splice.call(arr, i, 1).length == 1;\n};\n\n\n/**\n * Removes the first value that satisfies the given condition.\n * @param {IArrayLike<T>} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n * for every element. This function\n * takes 3 arguments (the element, the index and the array) and should\n * return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {boolean} True if an element was removed.\n * @template T,S\n */\ngoog.array.removeIf = function(arr, f, opt_obj) {\n var i = goog.array.findIndex(arr, f, opt_obj);\n if (i >= 0) {\n goog.array.removeAt(arr, i);\n return true;\n }\n return false;\n};\n\n\n/**\n * Removes all values that satisfy the given condition.\n * @param {IArrayLike<T>} arr Array or array\n * like object over which to iterate.\n * @param {?function(this:S, T, number, ?) : boolean} f The function to call\n * for every element. This function\n * takes 3 arguments (the element, the index and the array) and should\n * return a boolean.\n * @param {S=} opt_obj An optional \"this\" context for the function.\n * @return {number} The number of items removed\n * @template T,S\n */\ngoog.array.removeAllIf = function(arr, f, opt_obj) {\n var removedCount = 0;\n goog.array.forEachRight(arr, function(val, index) {\n if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) {\n if (goog.array.removeAt(arr, index)) {\n removedCount++;\n }\n }\n });\n return removedCount;\n};\n\n\n/**\n * Returns a new array that is the result of joining the arguments. If arrays\n * are passed then their items are added, however, if non-arrays are passed they\n * will be added to the return array as is.\n *\n * Note that ArrayLike objects will be added as is, rather than having their\n * items added.\n *\n * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]\n * goog.array.concat(0, [1, 2]) -> [0, 1, 2]\n * goog.array.concat([1, 2], null) -> [1, 2, null]\n *\n * There is bug in all current versions of IE (6, 7 and 8) where arrays created\n * in an iframe become corrupted soon (not immediately) after the iframe is\n * destroyed. This is common if loading data via goog.net.IframeIo, for example.\n * This corruption only affects the concat method which will start throwing\n * Catastrophic Errors (#-2147418113).\n *\n * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.\n *\n * Internally goog.array should use this, so that all methods will continue to\n * work on these broken array objects.\n *\n * @param {...*} var_args Items to concatenate. Arrays will have each item\n * added, while primitives and objects will be added as is.\n * @return {!Array<?>} The new resultant array.\n */\ngoog.array.concat = function(var_args) {\n return Array.prototype.concat.apply([], arguments);\n};\n\n\n/**\n * Returns a new array that contains the contents of all the arrays passed.\n * @param {...!Array<T>} var_args\n * @return {!Array<T>}\n * @template T\n */\ngoog.array.join = function(var_args) {\n return Array.prototype.concat.apply([], arguments);\n};\n\n\n/**\n * Converts an object to an array.\n * @param {IArrayLike<T>|string} object The object to convert to an\n * array.\n * @return {!Array<T>} The object converted into an array. If object has a\n * length property, every property indexed with a non-negative number\n * less than length will be included in the result. If object does not\n * have a length property, an empty array will be returned.\n * @template T\n */\ngoog.array.toArray = function(object) {\n var length = object.length;\n\n // If length is not a number the following is false. This case is kept for\n // backwards compatibility since there are callers that pass objects that are\n // not array like.\n if (length > 0) {\n var rv = new Array(length);\n for (var i = 0; i < length; i++) {\n rv[i] = object[i];\n }\n return rv;\n }\n return [];\n};\n\n\n/**\n * Does a shallow copy of an array.\n * @param {IArrayLike<T>|string} arr Array or array-like object to\n * clone.\n * @return {!Array<T>} Clone of the input array.\n * @template T\n */\ngoog.array.clone = goog.array.toArray;\n\n\n/**\n * Extends an array with another array, element, or \"array like\" object.\n * This function operates 'in-place', it does not create a new Array.\n *\n * Example:\n * var a = [];\n * goog.array.extend(a, [0, 1]);\n * a; // [0, 1]\n * goog.array.extend(a, 2);\n * a; // [0, 1, 2]\n *\n * @param {Array<VALUE>} arr1 The array to modify.\n * @param {...(IArrayLike<VALUE>|VALUE)} var_args The elements or arrays of\n * elements to add to arr1.\n * @template VALUE\n */\ngoog.array.extend = function(arr1, var_args) {\n for (var i = 1; i < arguments.length; i++) {\n var arr2 = arguments[i];\n if (goog.isArrayLike(arr2)) {\n var len1 = arr1.length || 0;\n var len2 = arr2.length || 0;\n arr1.length = len1 + len2;\n for (var j = 0; j < len2; j++) {\n arr1[len1 + j] = arr2[j];\n }\n } else {\n arr1.push(arr2);\n }\n }\n};\n\n\n/**\n * Adds or removes elements from an array. This is a generic version of Array\n * splice. This means that it might work on other objects similar to arrays,\n * such as the arguments object.\n *\n * @param {IArrayLike<T>} arr The array to modify.\n * @param {number|undefined} index The index at which to start changing the\n * array. If not defined, treated as 0.\n * @param {number} howMany How many elements to remove (0 means no removal. A\n * value below 0 is treated as zero and so is any other non number. Numbers\n * are floored).\n * @param {...T} var_args Optional, additional elements to insert into the\n * array.\n * @return {!Array<T>} the removed elements.\n * @template T\n */\ngoog.array.splice = function(arr, index, howMany, var_args) {\n goog.asserts.assert(arr.length != null);\n\n return Array.prototype.splice.apply(arr, goog.array.slice(arguments, 1));\n};\n\n\n/**\n * Returns a new array from a segment of an array. This is a generic version of\n * Array slice. This means that it might work on other objects similar to\n * arrays, such as the arguments object.\n *\n * @param {IArrayLike<T>|string} arr The array from\n * which to copy a segment.\n * @param {number} start The index of the first element to copy.\n * @param {number=} opt_end The index after the last element to copy.\n * @return {!Array<T>} A new array containing the specified segment of the\n * original array.\n * @template T\n */\ngoog.array.slice = function(arr, start, opt_end) {\n goog.asserts.assert(arr.length != null);\n\n // passing 1 arg to slice is not the same as passing 2 where the second is\n // null or undefined (in that case the second argument is treated as 0).\n // we could use slice on the arguments object and then use apply instead of\n // testing the length\n if (arguments.length <= 2) {\n return Array.prototype.slice.call(arr, start);\n } else {\n return Array.prototype.slice.call(arr, start, opt_end);\n }\n};\n\n\n/**\n * Removes all duplicates from an array (retaining only the first\n * occurrence of each array element). This function modifies the\n * array in place and doesn't change the order of the non-duplicate items.\n *\n * For objects, duplicates are identified as having the same unique ID as\n * defined by {@link goog.getUid}.\n *\n * Alternatively you can specify a custom hash function that returns a unique\n * value for each item in the array it should consider unique.\n *\n * Runtime: N,\n * Worstcase space: 2N (no dupes)\n *\n * @param {IArrayLike<T>} arr The array from which to remove\n * duplicates.\n * @param {Array=} opt_rv An optional array in which to return the results,\n * instead of performing the removal inplace. If specified, the original\n * array will remain unchanged.\n * @param {function(T):string=} opt_hashFn An optional function to use to\n * apply to every item in the array. This function should return a unique\n * value for each item in the array it should consider unique.\n * @template T\n */\ngoog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {\n var returnArray = opt_rv || arr;\n var defaultHashFn = function(item) {\n // Prefix each type with a single character representing the type to\n // prevent conflicting keys (e.g. true and 'true').\n return goog.isObject(item) ? 'o' + goog.getUid(item) :\n (typeof item).charAt(0) + item;\n };\n var hashFn = opt_hashFn || defaultHashFn;\n\n var seen = {}, cursorInsert = 0, cursorRead = 0;\n while (cursorRead < arr.length) {\n var current = arr[cursorRead++];\n var key = hashFn(current);\n if (!Object.prototype.hasOwnProperty.call(seen, key)) {\n seen[key] = true;\n returnArray[cursorInsert++] = current;\n }\n }\n returnArray.length = cursorInsert;\n};\n\n\n/**\n * Searches the specified array for the specified target using the binary\n * search algorithm. If no opt_compareFn is specified, elements are compared\n * using <code>goog.array.defaultCompare</code>, which compares the elements\n * using the built in < and > operators. This will produce the expected\n * behavior for homogeneous arrays of String(s) and Number(s). The array\n * specified <b>must</b> be sorted in ascending order (as defined by the\n * comparison function). If the array is not sorted, results are undefined.\n * If the array contains multiple instances of the specified target value, the\n * left-most instance will be found.\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<VALUE>} arr The array to be searched.\n * @param {TARGET} target The sought value.\n * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison\n * function by which the array is ordered. Should take 2 arguments to\n * compare, the target value and an element from your array, and return a\n * negative number, zero, or a positive number depending on whether the\n * first argument is less than, equal to, or greater than the second.\n * @return {number} Lowest index of the target value if found, otherwise\n * (-(insertion point) - 1). The insertion point is where the value should\n * be inserted into arr to preserve the sorted property. Return value >= 0\n * iff target is found.\n * @template TARGET, VALUE\n */\ngoog.array.binarySearch = function(arr, target, opt_compareFn) {\n return goog.array.binarySearch_(\n arr, opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,\n target);\n};\n\n\n/**\n * Selects an index in the specified array using the binary search algorithm.\n * The evaluator receives an element and determines whether the desired index\n * is before, at, or after it. The evaluator must be consistent (formally,\n * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)\n * must be monotonically non-increasing).\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<VALUE>} arr The array to be searched.\n * @param {function(this:THIS, VALUE, number, ?): number} evaluator\n * Evaluator function that receives 3 arguments (the element, the index and\n * the array). Should return a negative number, zero, or a positive number\n * depending on whether the desired index is before, at, or after the\n * element passed to it.\n * @param {THIS=} opt_obj The object to be used as the value of 'this'\n * within evaluator.\n * @return {number} Index of the leftmost element matched by the evaluator, if\n * such exists; otherwise (-(insertion point) - 1). The insertion point is\n * the index of the first element for which the evaluator returns negative,\n * or arr.length if no such element exists. The return value is non-negative\n * iff a match is found.\n * @template THIS, VALUE\n */\ngoog.array.binarySelect = function(arr, evaluator, opt_obj) {\n return goog.array.binarySearch_(\n arr, evaluator, true /* isEvaluator */, undefined /* opt_target */,\n opt_obj);\n};\n\n\n/**\n * Implementation of a binary search algorithm which knows how to use both\n * comparison functions and evaluators. If an evaluator is provided, will call\n * the evaluator with the given optional data object, conforming to the\n * interface defined in binarySelect. Otherwise, if a comparison function is\n * provided, will call the comparison function against the given data object.\n *\n * This implementation purposefully does not use goog.bind or goog.partial for\n * performance reasons.\n *\n * Runtime: O(log n)\n *\n * @param {IArrayLike<?>} arr The array to be searched.\n * @param {function(?, ?, ?): number | function(?, ?): number} compareFn\n * Either an evaluator or a comparison function, as defined by binarySearch\n * and binarySelect above.\n * @param {boolean} isEvaluator Whether the function is an evaluator or a\n * comparison function.\n * @param {?=} opt_target If the function is a comparison function, then\n * this is the target to binary search for.\n * @param {Object=} opt_selfObj If the function is an evaluator, this is an\n * optional this object for the evaluator.\n * @return {number} Lowest index of the target value if found, otherwise\n * (-(insertion point) - 1). The insertion point is where the value should\n * be inserted into arr to preserve the sorted property. Return value >= 0\n * iff target is found.\n * @private\n */\ngoog.array.binarySearch_ = function(\n arr, compareFn, isEvaluator, opt_target, opt_selfObj) {\n var left = 0; // inclusive\n var right = arr.length; // exclusive\n var found;\n while (left < right) {\n var middle = left + ((right - left) >>> 1);\n var compareResult;\n if (isEvaluator) {\n compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);\n } else {\n // NOTE(dimvar): To avoid this cast, we'd have to use function overloading\n // for the type of binarySearch_, which the type system can't express yet.\n compareResult = /** @type {function(?, ?): number} */ (compareFn)(\n opt_target, arr[middle]);\n }\n if (compareResult > 0) {\n left = middle + 1;\n } else {\n right = middle;\n // We are looking for the lowest index so we can't return immediately.\n found = !compareResult;\n }\n }\n // left is the index if found, or the insertion point otherwise.\n // Avoiding bitwise not operator, as that causes a loss in precision for array\n // indexes outside the bounds of a 32-bit signed integer. Array indexes have\n // a maximum value of 2^32-2 https://tc39.es/ecma262/#array-index\n return found ? left : -left - 1;\n};\n\n\n/**\n * Sorts the specified array into ascending order. If no opt_compareFn is\n * specified, elements are compared using\n * <code>goog.array.defaultCompare</code>, which compares the elements using\n * the built in < and > operators. This will produce the expected behavior\n * for homogeneous arrays of String(s) and Number(s), unlike the native sort,\n * but will give unpredictable results for heterogeneous lists of strings and\n * numbers with different numbers of digits.\n *\n * This sort is not guaranteed to be stable.\n *\n * Runtime: Same as <code>Array.prototype.sort</code>\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {?function(T,T):number=} opt_compareFn Optional comparison\n * function by which the\n * array is to be ordered. Should take 2 arguments to compare, and return a\n * negative number, zero, or a positive number depending on whether the\n * first argument is less than, equal to, or greater than the second.\n * @template T\n */\ngoog.array.sort = function(arr, opt_compareFn) {\n // TODO(arv): Update type annotation since null is not accepted.\n arr.sort(opt_compareFn || goog.array.defaultCompare);\n};\n\n\n/**\n * Sorts the specified array into ascending order in a stable way. If no\n * opt_compareFn is specified, elements are compared using\n * <code>goog.array.defaultCompare</code>, which compares the elements using\n * the built in < and > operators. This will produce the expected behavior\n * for homogeneous arrays of String(s) and Number(s).\n *\n * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional\n * O(n) overhead of copying the array twice.\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {?function(T, T): number=} opt_compareFn Optional comparison function\n * by which the array is to be ordered. Should take 2 arguments to compare,\n * and return a negative number, zero, or a positive number depending on\n * whether the first argument is less than, equal to, or greater than the\n * second.\n * @template T\n */\ngoog.array.stableSort = function(arr, opt_compareFn) {\n var compArr = new Array(arr.length);\n for (var i = 0; i < arr.length; i++) {\n compArr[i] = {index: i, value: arr[i]};\n }\n var valueCompareFn = opt_compareFn || goog.array.defaultCompare;\n function stableCompareFn(obj1, obj2) {\n return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;\n }\n goog.array.sort(compArr, stableCompareFn);\n for (var i = 0; i < arr.length; i++) {\n arr[i] = compArr[i].value;\n }\n};\n\n\n/**\n * Sort the specified array into ascending order based on item keys\n * returned by the specified key function.\n * If no opt_compareFn is specified, the keys are compared in ascending order\n * using <code>goog.array.defaultCompare</code>.\n *\n * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code>\n * and f(n) is runtime of the key function.\n *\n * @param {Array<T>} arr The array to be sorted.\n * @param {function(T): K} keyFn Function taking array element and returning\n * a key used for sorting this element.\n * @param {?function(K, K): number=} opt_compareFn Optional comparison function\n * by which the keys are to be ordered. Should take 2 arguments to compare,\n * and return a negative number, zero, or a positive number depending on\n * whether the first argument is less than, equal to, or greater than the\n * second.\n * @template T,K\n */\ngoog.array.sortByKey = function(arr, keyFn, opt_compareFn) {\n var keyCompareFn = opt_compareFn || goog.array.defaultCompare;\n goog.array.sort(\n arr, function(a, b) { return keyCompareFn(keyFn(a), keyFn(b)); });\n};\n\n\n/**\n * Sorts an array of objects by the specified object key and compare\n * function. If no compare function is provided, the key values are\n * compared in ascending order using <code>goog.array.defaultCompare</code>.\n * This won't work for keys that get renamed by the compiler. So use\n * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.\n * @param {Array<Object>} arr An array of objects to sort.\n * @param {string} key The object key to sort by.\n * @param {Function=} opt_compareFn The function to use to compare key\n * values.\n */\ngoog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {\n goog.array.sortByKey(arr, function(obj) { return obj[key]; }, opt_compareFn);\n};\n\n\n/**\n * Tells if the array is sorted.\n * @param {!IArrayLike<T>} arr The array.\n * @param {?function(T,T):number=} opt_compareFn Function to compare the\n * array elements.\n * Should take 2 arguments to compare, and return a negative number, zero,\n * or a positive number depending on whether the first argument is less\n * than, equal to, or greater than the second.\n * @param {boolean=} opt_strict If true no equal elements are allowed.\n * @return {boolean} Whether the array is sorted.\n * @template T\n */\ngoog.array.isSorted = function(arr, opt_compareFn, opt_strict) {\n var compare = opt_compareFn || goog.array.defaultCompare;\n for (var i = 1; i < arr.length; i++) {\n var compareResult = compare(arr[i - 1], arr[i]);\n if (compareResult > 0 || compareResult == 0 && opt_strict) {\n return false;\n }\n }\n return true;\n};\n\n\n/**\n * Compares two arrays for equality. Two arrays are considered equal if they\n * have the same length and their corresponding elements are equal according to\n * the comparison function.\n *\n * @param {IArrayLike<?>} arr1 The first array to compare.\n * @param {IArrayLike<?>} arr2 The second array to compare.\n * @param {Function=} opt_equalsFn Optional comparison function.\n * Should take 2 arguments to compare, and return true if the arguments\n * are equal. Defaults to {@link goog.array.defaultCompareEquality} which\n * compares the elements using the built-in '===' operator.\n * @return {boolean} Whether the two arrays are equal.\n */\ngoog.array.equals = function(arr1, arr2, opt_equalsFn) {\n if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||\n arr1.length != arr2.length) {\n return false;\n }\n var l = arr1.length;\n var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;\n for (var i = 0; i < l; i++) {\n if (!equalsFn(arr1[i], arr2[i])) {\n return false;\n }\n }\n return true;\n};\n\n\n/**\n * 3-way array compare function.\n * @param {!IArrayLike<VALUE>} arr1 The first array to\n * compare.\n * @param {!IArrayLike<VALUE>} arr2 The second array to\n * compare.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n * function by which the array is to be ordered. Should take 2 arguments to\n * compare, and return a negative number, zero, or a positive number\n * depending on whether the first argument is less than, equal to, or\n * greater than the second.\n * @return {number} Negative number, zero, or a positive number depending on\n * whether the first argument is less than, equal to, or greater than the\n * second.\n * @template VALUE\n */\ngoog.array.compare3 = function(arr1, arr2, opt_compareFn) {\n var compare = opt_compareFn || goog.array.defaultCompare;\n var l = Math.min(arr1.length, arr2.length);\n for (var i = 0; i < l; i++) {\n var result = compare(arr1[i], arr2[i]);\n if (result != 0) {\n return result;\n }\n }\n return goog.array.defaultCompare(arr1.length, arr2.length);\n};\n\n\n/**\n * Compares its two arguments for order, using the built in < and >\n * operators.\n * @param {VALUE} a The first object to be compared.\n * @param {VALUE} b The second object to be compared.\n * @return {number} A negative number, zero, or a positive number as the first\n * argument is less than, equal to, or greater than the second,\n * respectively.\n * @template VALUE\n */\ngoog.array.defaultCompare = function(a, b) {\n return a > b ? 1 : a < b ? -1 : 0;\n};\n\n\n/**\n * Compares its two arguments for inverse order, using the built in < and >\n * operators.\n * @param {VALUE} a The first object to be compared.\n * @param {VALUE} b The second object to be compared.\n * @return {number} A negative number, zero, or a positive number as the first\n * argument is greater than, equal to, or less than the second,\n * respectively.\n * @template VALUE\n */\ngoog.array.inverseDefaultCompare = function(a, b) {\n return -goog.array.defaultCompare(a, b);\n};\n\n\n/**\n * Compares its two arguments for equality, using the built in === operator.\n * @param {*} a The first object to compare.\n * @param {*} b The second object to compare.\n * @return {boolean} True if the two arguments are equal, false otherwise.\n */\ngoog.array.defaultCompareEquality = function(a, b) {\n return a === b;\n};\n\n\n/**\n * Inserts a value into a sorted array. The array is not modified if the\n * value is already present.\n * @param {IArrayLike<VALUE>} array The array to modify.\n * @param {VALUE} value The object to insert.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n * function by which the array is ordered. Should take 2 arguments to\n * compare, and return a negative number, zero, or a positive number\n * depending on whether the first argument is less than, equal to, or\n * greater than the second.\n * @return {boolean} True if an element was inserted.\n * @template VALUE\n */\ngoog.array.binaryInsert = function(array, value, opt_compareFn) {\n var index = goog.array.binarySearch(array, value, opt_compareFn);\n if (index < 0) {\n goog.array.insertAt(array, value, -(index + 1));\n return true;\n }\n return false;\n};\n\n\n/**\n * Removes a value from a sorted array.\n * @param {!IArrayLike<VALUE>} array The array to modify.\n * @param {VALUE} value The object to remove.\n * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison\n * function by which the array is ordered. Should take 2 arguments to\n * compare, and return a negative number, zero, or a positive number\n * depending on whether the first argument is less than, equal to, or\n * greater than the second.\n * @return {boolean} True if an element was removed.\n * @template VALUE\n */\ngoog.array.binaryRemove = function(array, value, opt_compareFn) {\n var index = goog.array.binarySearch(array, value, opt_compareFn);\n return (index >= 0) ? goog.array.removeAt(array, index) : false;\n};\n\n\n/**\n * Splits an array into disjoint buckets according to a splitting function.\n * @param {IArrayLike<T>} array The array.\n * @param {function(this:S, T, number, !IArrayLike<T>):?} sorter Function to\n * call for every element. This takes 3 arguments (the element, the index\n * and the array) and must return a valid object key (a string, number,\n * etc), or undefined, if that object should not be placed in a bucket.\n * @param {S=} opt_obj The object to be used as the value of 'this' within\n * sorter.\n * @return {!Object<!Array<T>>} An object, with keys being all of the unique\n * return values of sorter, and values being arrays containing the items for\n * which the splitter returned that key.\n * @template T,S\n */\ngoog.array.bucket = function(array, sorter, opt_obj) {\n var buckets = {};\n\n for (var i = 0; i < array.length; i++) {\n var value = array[i];\n var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);\n if (key !== undefined) {\n // Push the value to the right bucket, creating it if necessary.\n var bucket = buckets[key] || (buckets[key] = []);\n bucket.push(value);\n }\n }\n\n return buckets;\n};\n\n\n/**\n * Creates a new object built from the provided array and the key-generation\n * function.\n * @param {IArrayLike<T>} arr Array or array like object over\n * which to iterate whose elements will be the values in the new object.\n * @param {?function(this:S, T, number, ?) : string} keyFunc The function to\n * call for every element. This function takes 3 arguments (the element, the\n * index and the array) and should return a string that will be used as the\n * key for the element in the new object. If the function returns the same\n * key for more than one element, the value for that key is\n * implementation-defined.\n * @param {S=} opt_obj The object to be used as the value of 'this'\n * within keyFunc.\n * @return {!Object<T>} The new object.\n * @template T,S\n */\ngoog.array.toObject = function(arr, keyFunc, opt_obj) {\n var ret = {};\n goog.array.forEach(arr, function(element, index) {\n ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] =\n element;\n });\n return ret;\n};\n\n\n/**\n * Creates a range of numbers in an arithmetic progression.\n *\n * Range takes 1, 2, or 3 arguments:\n * <pre>\n * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]\n * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]\n * range(-2, -5, -1) produces [-2, -3, -4]\n * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.\n * </pre>\n *\n * @param {number} startOrEnd The starting value of the range if an end argument\n * is provided. Otherwise, the start value is 0, and this is the end value.\n * @param {number=} opt_end The optional end value of the range.\n * @param {number=} opt_step The step size between range values. Defaults to 1\n * if opt_step is undefined or 0.\n * @return {!Array<number>} An array of numbers for the requested range. May be\n * an empty array if adding the step would not converge toward the end\n * value.\n */\ngoog.array.range = function(startOrEnd, opt_end, opt_step) {\n var array = [];\n var start = 0;\n var end = startOrEnd;\n var step = opt_step || 1;\n if (opt_end !== undefined) {\n start = startOrEnd;\n end = opt_end;\n }\n\n if (step * (end - start) < 0) {\n // Sign mismatch: start + step will never reach the end value.\n return [];\n }\n\n if (step > 0) {\n for (var i = start; i < end; i += step) {\n array.push(i);\n }\n } else {\n for (var i = start; i > end; i += step) {\n array.push(i);\n }\n }\n return array;\n};\n\n\n/**\n * Returns an array consisting of the given value repeated N times.\n *\n * @param {VALUE} value The value to repeat.\n * @param {number} n The repeat count.\n * @return {!Array<VALUE>} An array with the repeated value.\n * @template VALUE\n */\ngoog.array.repeat = function(value, n) {\n var array = [];\n for (var i = 0; i < n; i++) {\n array[i] = value;\n }\n return array;\n};\n\n\n/**\n * Returns an array consisting of every argument with all arrays\n * expanded in-place recursively.\n *\n * @param {...*} var_args The values to flatten.\n * @return {!Array<?>} An array containing the flattened values.\n */\ngoog.array.flatten = function(var_args) {\n var CHUNK_SIZE = 8192;\n\n var result = [];\n for (var i = 0; i < arguments.length; i++) {\n var element = arguments[i];\n if (Array.isArray(element)) {\n for (var c = 0; c < element.length; c += CHUNK_SIZE) {\n var chunk = goog.array.slice(element, c, c + CHUNK_SIZE);\n var recurseResult = goog.array.flatten.apply(null, chunk);\n for (var r = 0; r < recurseResult.length; r++) {\n result.push(recurseResult[r]);\n }\n }\n } else {\n result.push(element);\n }\n }\n return result;\n};\n\n\n/**\n * Rotates an array in-place. After calling this method, the element at\n * index i will be the element previously at index (i - n) %\n * array.length, for all values of i between 0 and array.length - 1,\n * inclusive.\n *\n * For example, suppose list comprises [t, a, n, k, s]. After invoking\n * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].\n *\n * @param {!Array<T>} array The array to rotate.\n * @param {number} n The amount to rotate.\n * @return {!Array<T>} The array.\n * @template T\n */\ngoog.array.rotate = function(array, n) {\n goog.asserts.assert(array.length != null);\n\n if (array.length) {\n n %= array.length;\n if (n > 0) {\n Array.prototype.unshift.apply(array, array.splice(-n, n));\n } else if (n < 0) {\n Array.prototype.push.apply(array, array.splice(0, -n));\n }\n }\n return array;\n};\n\n\n/**\n * Moves one item of an array to a new position keeping the order of the rest\n * of the items. Example use case: keeping a list of JavaScript objects\n * synchronized with the corresponding list of DOM elements after one of the\n * elements has been dragged to a new position.\n * @param {!IArrayLike<?>} arr The array to modify.\n * @param {number} fromIndex Index of the item to move between 0 and\n * {@code arr.length - 1}.\n * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.\n */\ngoog.array.moveItem = function(arr, fromIndex, toIndex) {\n goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);\n goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);\n // Remove 1 item at fromIndex.\n var removedItems = Array.prototype.splice.call(arr, fromIndex, 1);\n // Insert the removed item at toIndex.\n Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]);\n // We don't use goog.array.insertAt and goog.array.removeAt, because they're\n // significantly slower than splice.\n};\n\n\n/**\n * Creates a new array for which the element at position i is an array of the\n * ith element of the provided arrays. The returned array will only be as long\n * as the shortest array provided; additional values are ignored. For example,\n * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].\n *\n * This is similar to the zip() function in Python. See {@link\n * http://docs.python.org/library/functions.html#zip}\n *\n * @param {...!IArrayLike<?>} var_args Arrays to be combined.\n * @return {!Array<!Array<?>>} A new array of arrays created from\n * provided arrays.\n */\ngoog.array.zip = function(var_args) {\n if (!arguments.length) {\n return [];\n }\n var result = [];\n var minLen = arguments[0].length;\n for (var i = 1; i < arguments.length; i++) {\n if (arguments[i].length < minLen) {\n minLen = arguments[i].length;\n }\n }\n for (var i = 0; i < minLen; i++) {\n var value = [];\n for (var j = 0; j < arguments.length; j++) {\n value.push(arguments[j][i]);\n }\n result.push(value);\n }\n return result;\n};\n\n\n/**\n * Shuffles the values in the specified array using the Fisher-Yates in-place\n * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()\n * and so resets the state of that random number generator. Similarly, may reset\n * the state of any other specified random number generator.\n *\n * Runtime: O(n)\n *\n * @param {!Array<?>} arr The array to be shuffled.\n * @param {function():number=} opt_randFn Optional random function to use for\n * shuffling.\n * Takes no arguments, and returns a random number on the interval [0, 1).\n * Defaults to Math.random() using JavaScript's built-in Math library.\n */\ngoog.array.shuffle = function(arr, opt_randFn) {\n var randFn = opt_randFn || Math.random;\n\n for (var i = arr.length - 1; i > 0; i--) {\n // Choose a random array index in [0, i] (inclusive with i).\n var j = Math.floor(randFn() * (i + 1));\n\n var tmp = arr[i];\n arr[i] = arr[j];\n arr[j] = tmp;\n }\n};\n\n\n/**\n * Returns a new array of elements from arr, based on the indexes of elements\n * provided by index_arr. For example, the result of index copying\n * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].\n *\n * @param {!IArrayLike<T>} arr The array to get a indexed copy from.\n * @param {!IArrayLike<number>} index_arr An array of indexes to get from arr.\n * @return {!Array<T>} A new array of elements from arr in index_arr order.\n * @template T\n */\ngoog.array.copyByIndex = function(arr, index_arr) {\n var result = [];\n goog.array.forEach(index_arr, function(index) { result.push(arr[index]); });\n return result;\n};\n\n\n/**\n * Maps each element of the input array into zero or more elements of the output\n * array.\n *\n * @param {!IArrayLike<VALUE>|string} arr Array or array like object\n * over which to iterate.\n * @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function\n * to call for every element. This function takes 3 arguments (the element,\n * the index and the array) and should return an array. The result will be\n * used to extend a new array.\n * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.\n * @return {!Array<RESULT>} a new array with the concatenation of all arrays\n * returned from f.\n * @template THIS, VALUE, RESULT\n */\ngoog.array.concatMap = function(arr, f, opt_obj) {\n return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj));\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview String functions called from Closure packages that couldn't\n * depend on each other. Outside Closure, use goog.string function which\n * delegate to these.\n */\n\n\ngoog.provide('goog.string.internal');\n\n\n/**\n * Fast prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the start of `str`.\n * @return {boolean} True if `str` begins with `prefix`.\n * @see goog.string.startsWith\n */\ngoog.string.internal.startsWith = function(str, prefix) {\n return str.lastIndexOf(prefix, 0) == 0;\n};\n\n\n/**\n * Fast suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix`.\n * @see goog.string.endsWith\n */\ngoog.string.internal.endsWith = function(str, suffix) {\n const l = str.length - suffix.length;\n return l >= 0 && str.indexOf(suffix, l) == l;\n};\n\n\n/**\n * Case-insensitive prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the end of `str`.\n * @return {boolean} True if `str` begins with `prefix` (ignoring\n * case).\n * @see goog.string.caseInsensitiveStartsWith\n */\ngoog.string.internal.caseInsensitiveStartsWith = function(str, prefix) {\n return goog.string.internal.caseInsensitiveCompare(\n prefix, str.substr(0, prefix.length)) == 0;\n};\n\n\n/**\n * Case-insensitive suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix` (ignoring\n * case).\n * @see goog.string.caseInsensitiveEndsWith\n */\ngoog.string.internal.caseInsensitiveEndsWith = function(str, suffix) {\n return (\n goog.string.internal.caseInsensitiveCompare(\n suffix, str.substr(str.length - suffix.length, suffix.length)) == 0);\n};\n\n\n/**\n * Case-insensitive equality checker.\n * @param {string} str1 First string to check.\n * @param {string} str2 Second string to check.\n * @return {boolean} True if `str1` and `str2` are the same string,\n * ignoring case.\n * @see goog.string.caseInsensitiveEquals\n */\ngoog.string.internal.caseInsensitiveEquals = function(str1, str2) {\n return str1.toLowerCase() == str2.toLowerCase();\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n * @see goog.string.isEmptyOrWhitespace\n */\ngoog.string.internal.isEmptyOrWhitespace = function(str) {\n // testing length == 0 first is actually slower in all browsers (about the\n // same in Opera).\n // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n // include it in the regexp to enforce consistent cross-browser behavior.\n return /^[\\s\\xa0]*$/.test(str);\n};\n\n\n/**\n * Trims white spaces to the left and right of a string.\n * @param {string} str The string to trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.internal.trim =\n (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) {\n return str.trim();\n } : function(str) {\n // Since IE doesn't include non-breaking-space (0xa0) in their \\s\n // character class (as required by section 7.2 of the ECMAScript spec),\n // we explicitly include it in the regexp to enforce consistent\n // cross-browser behavior.\n // NOTE: We don't use String#replace because it might have side effects\n // causing this function to not compile to 0 bytes.\n return /^[\\s\\xa0]*([\\s\\S]*?)[\\s\\xa0]*$/.exec(str)[1];\n };\n\n\n/**\n * A string comparator that ignores case.\n * -1 = str1 less than str2\n * 0 = str1 equals str2\n * 1 = str1 greater than str2\n *\n * @param {string} str1 The string to compare.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} The comparator result, as described above.\n * @see goog.string.caseInsensitiveCompare\n */\ngoog.string.internal.caseInsensitiveCompare = function(str1, str2) {\n const test1 = String(str1).toLowerCase();\n const test2 = String(str2).toLowerCase();\n\n if (test1 < test2) {\n return -1;\n } else if (test1 == test2) {\n return 0;\n } else {\n return 1;\n }\n};\n\n\n/**\n * Converts \\n to <br>s or <br />s.\n * @param {string} str The string in which to convert newlines.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} A copy of `str` with converted newlines.\n * @see goog.string.newLineToBr\n */\ngoog.string.internal.newLineToBr = function(str, opt_xml) {\n return str.replace(/(\\r\\n|\\r|\\n)/g, opt_xml ? '<br />' : '<br>');\n};\n\n\n/**\n * Escapes double quote '\"' and single quote '\\'' characters in addition to\n * '&', '<', and '>' so that a string can be included in an HTML tag attribute\n * value within double or single quotes.\n * @param {string} str string to be escaped.\n * @param {boolean=} opt_isLikelyToContainHtmlChars\n * @return {string} An escaped copy of `str`.\n * @see goog.string.htmlEscape\n */\ngoog.string.internal.htmlEscape = function(\n str, opt_isLikelyToContainHtmlChars) {\n if (opt_isLikelyToContainHtmlChars) {\n str = str.replace(goog.string.internal.AMP_RE_, '&')\n .replace(goog.string.internal.LT_RE_, '<')\n .replace(goog.string.internal.GT_RE_, '>')\n .replace(goog.string.internal.QUOT_RE_, '"')\n .replace(goog.string.internal.SINGLE_QUOTE_RE_, ''')\n .replace(goog.string.internal.NULL_RE_, '�');\n return str;\n\n } else {\n // quick test helps in the case when there are no chars to replace, in\n // worst case this makes barely a difference to the time taken\n if (!goog.string.internal.ALL_RE_.test(str)) return str;\n\n // str.indexOf is faster than regex.test in this case\n if (str.indexOf('&') != -1) {\n str = str.replace(goog.string.internal.AMP_RE_, '&');\n }\n if (str.indexOf('<') != -1) {\n str = str.replace(goog.string.internal.LT_RE_, '<');\n }\n if (str.indexOf('>') != -1) {\n str = str.replace(goog.string.internal.GT_RE_, '>');\n }\n if (str.indexOf('\"') != -1) {\n str = str.replace(goog.string.internal.QUOT_RE_, '"');\n }\n if (str.indexOf('\\'') != -1) {\n str = str.replace(goog.string.internal.SINGLE_QUOTE_RE_, ''');\n }\n if (str.indexOf('\\x00') != -1) {\n str = str.replace(goog.string.internal.NULL_RE_, '�');\n }\n return str;\n }\n};\n\n\n/**\n * Regular expression that matches an ampersand, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.AMP_RE_ = /&/g;\n\n\n/**\n * Regular expression that matches a less than sign, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.LT_RE_ = /</g;\n\n\n/**\n * Regular expression that matches a greater than sign, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.GT_RE_ = />/g;\n\n\n/**\n * Regular expression that matches a double quote, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.QUOT_RE_ = /\"/g;\n\n\n/**\n * Regular expression that matches a single quote, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.SINGLE_QUOTE_RE_ = /'/g;\n\n\n/**\n * Regular expression that matches null character, for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.NULL_RE_ = /\\x00/g;\n\n\n/**\n * Regular expression that matches any character that needs to be escaped.\n * @const {!RegExp}\n * @private\n */\ngoog.string.internal.ALL_RE_ = /[\\x00&<>\"']/;\n\n\n/**\n * Do escaping of whitespace to preserve spatial formatting. We use character\n * entity #160 to make it safer for xml.\n * @param {string} str The string in which to escape whitespace.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} An escaped copy of `str`.\n * @see goog.string.whitespaceEscape\n */\ngoog.string.internal.whitespaceEscape = function(str, opt_xml) {\n // This doesn't use goog.string.preserveSpaces for backwards compatibility.\n return goog.string.internal.newLineToBr(\n str.replace(/ /g, '  '), opt_xml);\n};\n\n\n/**\n * Determines whether a string contains a substring.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n * @see goog.string.contains\n */\ngoog.string.internal.contains = function(str, subString) {\n return str.indexOf(subString) != -1;\n};\n\n\n/**\n * Determines whether a string contains a substring, ignoring case.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n * @see goog.string.caseInsensitiveContains\n */\ngoog.string.internal.caseInsensitiveContains = function(str, subString) {\n return goog.string.internal.contains(\n str.toLowerCase(), subString.toLowerCase());\n};\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string|number} version1 Version of first item.\n * @param {string|number} version2 Version of second item.\n *\n * @return {number} 1 if `version1` is higher.\n * 0 if arguments are equal.\n * -1 if `version2` is higher.\n * @see goog.string.compareVersions\n */\ngoog.string.internal.compareVersions = function(version1, version2) {\n let order = 0;\n // Trim leading and trailing whitespace and split the versions into\n // subversions.\n const v1Subs = goog.string.internal.trim(String(version1)).split('.');\n const v2Subs = goog.string.internal.trim(String(version2)).split('.');\n const subCount = Math.max(v1Subs.length, v2Subs.length);\n\n // Iterate over the subversions, as long as they appear to be equivalent.\n for (let subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {\n let v1Sub = v1Subs[subIdx] || '';\n let v2Sub = v2Subs[subIdx] || '';\n\n do {\n // Split the subversions into pairs of numbers and qualifiers (like 'b').\n // Two different RegExp objects are use to make it clear the code\n // is side-effect free\n const v1Comp = /(\\d*)(\\D*)(.*)/.exec(v1Sub) || ['', '', '', ''];\n const v2Comp = /(\\d*)(\\D*)(.*)/.exec(v2Sub) || ['', '', '', ''];\n // Break if there are no more matches.\n if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {\n break;\n }\n\n // Parse the numeric part of the subversion. A missing number is\n // equivalent to 0.\n const v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);\n const v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);\n\n // Compare the subversion components. The number has the highest\n // precedence. Next, if the numbers are equal, a subversion without any\n // qualifier is always higher than a subversion with any qualifier. Next,\n // the qualifiers are compared as strings.\n order = goog.string.internal.compareElements_(v1CompNum, v2CompNum) ||\n goog.string.internal.compareElements_(\n v1Comp[2].length == 0, v2Comp[2].length == 0) ||\n goog.string.internal.compareElements_(v1Comp[2], v2Comp[2]);\n // Stop as soon as an inequality is discovered.\n\n v1Sub = v1Comp[3];\n v2Sub = v2Comp[3];\n } while (order == 0);\n }\n\n return order;\n};\n\n\n/**\n * Compares elements of a version number.\n *\n * @param {string|number|boolean} left An element from a version number.\n * @param {string|number|boolean} right An element from a version number.\n *\n * @return {number} 1 if `left` is higher.\n * 0 if arguments are equal.\n * -1 if `right` is higher.\n * @private\n */\ngoog.string.internal.compareElements_ = function(left, right) {\n if (left < right) {\n return -1;\n } else if (left > right) {\n return 1;\n }\n return 0;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities used by goog.labs.userAgent tools. These functions\n * should not be used outside of goog.labs.userAgent.*.\n *\n */\n\ngoog.provide('goog.labs.userAgent.util');\n\ngoog.require('goog.string.internal');\n\n\n/**\n * Gets the native userAgent string from navigator if it exists.\n * If navigator or navigator.userAgent string is missing, returns an empty\n * string.\n * @return {string}\n * @private\n */\ngoog.labs.userAgent.util.getNativeUserAgentString_ = function() {\n var navigator = goog.labs.userAgent.util.getNavigator_();\n if (navigator) {\n var userAgent = navigator.userAgent;\n if (userAgent) {\n return userAgent;\n }\n }\n return '';\n};\n\n\n/**\n * Getter for the native navigator.\n * This is a separate function so it can be stubbed out in testing.\n * @return {!Navigator}\n * @private\n */\ngoog.labs.userAgent.util.getNavigator_ = function() {\n return goog.global.navigator;\n};\n\n\n/**\n * A possible override for applications which wish to not check\n * navigator.userAgent but use a specified value for detection instead.\n * @private {string}\n */\ngoog.labs.userAgent.util.userAgent_ =\n goog.labs.userAgent.util.getNativeUserAgentString_();\n\n\n/**\n * Applications may override browser detection on the built in\n * navigator.userAgent object by setting this string. Set to null to use the\n * browser object instead.\n * @param {?string=} opt_userAgent The User-Agent override.\n */\ngoog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {\n goog.labs.userAgent.util.userAgent_ =\n opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_();\n};\n\n\n/**\n * @return {string} The user agent string.\n */\ngoog.labs.userAgent.util.getUserAgent = function() {\n return goog.labs.userAgent.util.userAgent_;\n};\n\n\n/**\n * @param {string} str\n * @return {boolean} Whether the user agent contains the given string.\n */\ngoog.labs.userAgent.util.matchUserAgent = function(str) {\n var userAgent = goog.labs.userAgent.util.getUserAgent();\n return goog.string.internal.contains(userAgent, str);\n};\n\n\n/**\n * @param {string} str\n * @return {boolean} Whether the user agent contains the given string, ignoring\n * case.\n */\ngoog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {\n var userAgent = goog.labs.userAgent.util.getUserAgent();\n return goog.string.internal.caseInsensitiveContains(userAgent, str);\n};\n\n\n/**\n * Parses the user agent into tuples for each section.\n * @param {string} userAgent\n * @return {!Array<!Array<string>>} Tuples of key, version, and the contents\n * of the parenthetical.\n */\ngoog.labs.userAgent.util.extractVersionTuples = function(userAgent) {\n // Matches each section of a user agent string.\n // Example UA:\n // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)\n // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405\n // This has three version tuples: Mozilla, AppleWebKit, and Mobile.\n\n var versionRegExp = new RegExp(\n // Key. Note that a key may have a space.\n // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')\n '(\\\\w[\\\\w ]+)' +\n\n '/' + // slash\n '([^\\\\s]+)' + // version (i.e. '5.0b')\n '\\\\s*' + // whitespace\n '(?:\\\\((.*?)\\\\))?', // parenthetical info. parentheses not matched.\n 'g');\n\n var data = [];\n var match;\n\n // Iterate and collect the version tuples. Each iteration will be the\n // next regex match.\n while (match = versionRegExp.exec(userAgent)) {\n data.push([\n match[1], // key\n match[2], // value\n // || undefined as this is not undefined in IE7 and IE8\n match[3] || undefined // info\n ]);\n }\n\n return data;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for manipulating objects/maps/hashes.\n */\n\ngoog.provide('goog.object');\n\n\n/**\n * Whether two values are not observably distinguishable. This\n * correctly detects that 0 is not the same as -0 and two NaNs are\n * practically equivalent.\n *\n * The implementation is as suggested by harmony:egal proposal.\n *\n * @param {*} v The first value to compare.\n * @param {*} v2 The second value to compare.\n * @return {boolean} Whether two values are not observably distinguishable.\n * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal\n */\ngoog.object.is = function(v, v2) {\n if (v === v2) {\n // 0 === -0, but they are not identical.\n // We need the cast because the compiler requires that v2 is a\n // number (although 1/v2 works with non-number). We cast to ? to\n // stop the compiler from type-checking this statement.\n return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);\n }\n\n // NaN is non-reflexive: NaN !== NaN, although they are identical.\n return v !== v && v2 !== v2;\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):?} f The function to call\n * for every element. This function takes 3 arguments (the value, the\n * key and the object) and the return value is ignored.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @template T,K,V\n */\ngoog.object.forEach = function(obj, f, opt_obj) {\n for (const key in obj) {\n f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);\n }\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If that call returns\n * true, adds the element to a new object.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call\n * for every element. This\n * function takes 3 arguments (the value, the key and the object)\n * and should return a boolean. If the return value is true the\n * element is added to the result object. If it is false the\n * element is not included.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {!Object<K,V>} a new object in which only elements that passed the\n * test are present.\n * @template T,K,V\n */\ngoog.object.filter = function(obj, f, opt_obj) {\n const res = {};\n for (const key in obj) {\n if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n res[key] = obj[key];\n }\n }\n return res;\n};\n\n\n/**\n * For every element in an object/map/hash calls a function and inserts the\n * result into a new object.\n *\n * @param {Object<K,V>} obj The object over which to iterate.\n * @param {function(this:T,V,?,Object<K,V>):R} f The function to call\n * for every element. This function\n * takes 3 arguments (the value, the key and the object)\n * and should return something. The result will be inserted\n * into a new object.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {!Object<K,R>} a new object with the results from f.\n * @template T,K,V,R\n */\ngoog.object.map = function(obj, f, opt_obj) {\n const res = {};\n for (const key in obj) {\n res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);\n }\n return res;\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If any\n * call returns true, returns true (without checking the rest). If\n * all calls return false, returns false.\n *\n * @param {Object<K,V>} obj The object to check.\n * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to\n * call for every element. This function\n * takes 3 arguments (the value, the key and the object) and should\n * return a boolean.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {boolean} true if any element passes the test.\n * @template T,K,V\n */\ngoog.object.some = function(obj, f, opt_obj) {\n for (const key in obj) {\n if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n return true;\n }\n }\n return false;\n};\n\n\n/**\n * Calls a function for each element in an object/map/hash. If\n * all calls return true, returns true. If any call returns false, returns\n * false at this point and does not continue to check the remaining elements.\n *\n * @param {Object<K,V>} obj The object to check.\n * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to\n * call for every element. This function\n * takes 3 arguments (the value, the key and the object) and should\n * return a boolean.\n * @param {T=} opt_obj This is used as the 'this' object within f.\n * @return {boolean} false if any element fails the test.\n * @template T,K,V\n */\ngoog.object.every = function(obj, f, opt_obj) {\n for (const key in obj) {\n if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {\n return false;\n }\n }\n return true;\n};\n\n\n/**\n * Returns the number of key-value pairs in the object map.\n *\n * @param {Object} obj The object for which to get the number of key-value\n * pairs.\n * @return {number} The number of key-value pairs in the object map.\n */\ngoog.object.getCount = function(obj) {\n let rv = 0;\n for (const key in obj) {\n rv++;\n }\n return rv;\n};\n\n\n/**\n * Returns one key from the object map, if any exists.\n * For map literals the returned key will be the first one in most of the\n * browsers (a know exception is Konqueror).\n *\n * @param {Object} obj The object to pick a key from.\n * @return {string|undefined} The key or undefined if the object is empty.\n */\ngoog.object.getAnyKey = function(obj) {\n for (const key in obj) {\n return key;\n }\n};\n\n\n/**\n * Returns one value from the object map, if any exists.\n * For map literals the returned value will be the first one in most of the\n * browsers (a know exception is Konqueror).\n *\n * @param {Object<K,V>} obj The object to pick a value from.\n * @return {V|undefined} The value or undefined if the object is empty.\n * @template K,V\n */\ngoog.object.getAnyValue = function(obj) {\n for (const key in obj) {\n return obj[key];\n }\n};\n\n\n/**\n * Whether the object/hash/map contains the given object as a value.\n * An alias for goog.object.containsValue(obj, val).\n *\n * @param {Object<K,V>} obj The object in which to look for val.\n * @param {V} val The object for which to check.\n * @return {boolean} true if val is present.\n * @template K,V\n */\ngoog.object.contains = function(obj, val) {\n return goog.object.containsValue(obj, val);\n};\n\n\n/**\n * Returns the values of the object/map/hash.\n *\n * @param {Object<K,V>} obj The object from which to get the values.\n * @return {!Array<V>} The values in the object/map/hash.\n * @template K,V\n */\ngoog.object.getValues = function(obj) {\n const res = [];\n let i = 0;\n for (const key in obj) {\n res[i++] = obj[key];\n }\n return res;\n};\n\n\n/**\n * Returns the keys of the object/map/hash.\n *\n * @param {Object} obj The object from which to get the keys.\n * @return {!Array<string>} Array of property keys.\n */\ngoog.object.getKeys = function(obj) {\n const res = [];\n let i = 0;\n for (const key in obj) {\n res[i++] = key;\n }\n return res;\n};\n\n\n/**\n * Get a value from an object multiple levels deep. This is useful for\n * pulling values from deeply nested objects, such as JSON responses.\n * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)\n *\n * @param {!Object} obj An object to get the value from. Can be array-like.\n * @param {...(string|number|!IArrayLike<number|string>)}\n * var_args A number of keys\n * (as strings, or numbers, for array-like objects). Can also be\n * specified as a single array of keys.\n * @return {*} The resulting value. If, at any point, the value for a key\n * in the current object is null or undefined, returns undefined.\n */\ngoog.object.getValueByKeys = function(obj, var_args) {\n const isArrayLike = goog.isArrayLike(var_args);\n const keys = isArrayLike ?\n /** @type {!IArrayLike<number|string>} */ (var_args) :\n arguments;\n\n // Start with the 2nd parameter for the variable parameters syntax.\n for (let i = isArrayLike ? 0 : 1; i < keys.length; i++) {\n if (obj == null) return undefined;\n obj = obj[keys[i]];\n }\n\n return obj;\n};\n\n\n/**\n * Whether the object/map/hash contains the given key.\n *\n * @param {Object} obj The object in which to look for key.\n * @param {?} key The key for which to check.\n * @return {boolean} true If the map contains the key.\n */\ngoog.object.containsKey = function(obj, key) {\n return obj !== null && key in obj;\n};\n\n\n/**\n * Whether the object/map/hash contains the given value. This is O(n).\n *\n * @param {Object<K,V>} obj The object in which to look for val.\n * @param {V} val The value for which to check.\n * @return {boolean} true If the map contains the value.\n * @template K,V\n */\ngoog.object.containsValue = function(obj, val) {\n for (const key in obj) {\n if (obj[key] == val) {\n return true;\n }\n }\n return false;\n};\n\n\n/**\n * Searches an object for an element that satisfies the given condition and\n * returns its key.\n * @param {Object<K,V>} obj The object to search in.\n * @param {function(this:T,V,string,Object<K,V>):boolean} f The\n * function to call for every element. Takes 3 arguments (the value,\n * the key and the object) and should return a boolean.\n * @param {T=} opt_this An optional \"this\" context for the function.\n * @return {string|undefined} The key of an element for which the function\n * returns true or undefined if no such element is found.\n * @template T,K,V\n */\ngoog.object.findKey = function(obj, f, opt_this) {\n for (const key in obj) {\n if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) {\n return key;\n }\n }\n return undefined;\n};\n\n\n/**\n * Searches an object for an element that satisfies the given condition and\n * returns its value.\n * @param {Object<K,V>} obj The object to search in.\n * @param {function(this:T,V,string,Object<K,V>):boolean} f The function\n * to call for every element. Takes 3 arguments (the value, the key\n * and the object) and should return a boolean.\n * @param {T=} opt_this An optional \"this\" context for the function.\n * @return {V} The value of an element for which the function returns true or\n * undefined if no such element is found.\n * @template T,K,V\n */\ngoog.object.findValue = function(obj, f, opt_this) {\n const key = goog.object.findKey(obj, f, opt_this);\n return key && obj[key];\n};\n\n\n/**\n * Whether the object/map/hash is empty.\n *\n * @param {Object} obj The object to test.\n * @return {boolean} true if obj is empty.\n */\ngoog.object.isEmpty = function(obj) {\n for (const key in obj) {\n return false;\n }\n return true;\n};\n\n\n/**\n * Removes all key value pairs from the object/map/hash.\n *\n * @param {Object} obj The object to clear.\n */\ngoog.object.clear = function(obj) {\n for (const i in obj) {\n delete obj[i];\n }\n};\n\n\n/**\n * Removes a key-value pair based on the key.\n *\n * @param {Object} obj The object from which to remove the key.\n * @param {?} key The key to remove.\n * @return {boolean} Whether an element was removed.\n */\ngoog.object.remove = function(obj, key) {\n let rv;\n if (rv = key in /** @type {!Object} */ (obj)) {\n delete obj[key];\n }\n return rv;\n};\n\n\n/**\n * Adds a key-value pair to the object. Throws an exception if the key is\n * already in use. Use set if you want to change an existing pair.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} val The value to add.\n * @template K,V\n */\ngoog.object.add = function(obj, key, val) {\n if (obj !== null && key in obj) {\n throw new Error('The object already contains the key \"' + key + '\"');\n }\n goog.object.set(obj, key, val);\n};\n\n\n/**\n * Returns the value for the given key.\n *\n * @param {Object<K,V>} obj The object from which to get the value.\n * @param {string} key The key for which to get the value.\n * @param {R=} opt_val The value to return if no item is found for the given\n * key (default is undefined).\n * @return {V|R|undefined} The value for the given key.\n * @template K,V,R\n */\ngoog.object.get = function(obj, key, opt_val) {\n if (obj !== null && key in obj) {\n return obj[key];\n }\n return opt_val;\n};\n\n\n/**\n * Adds a key-value pair to the object/map/hash.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} value The value to add.\n * @template K,V\n */\ngoog.object.set = function(obj, key, value) {\n obj[key] = value;\n};\n\n\n/**\n * Adds a key-value pair to the object/map/hash if it doesn't exist yet.\n *\n * @param {Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {V} value The value to add if the key wasn't present.\n * @return {V} The value of the entry at the end of the function.\n * @template K,V\n */\ngoog.object.setIfUndefined = function(obj, key, value) {\n return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);\n};\n\n\n/**\n * Sets a key and value to an object if the key is not set. The value will be\n * the return value of the given function. If the key already exists, the\n * object will not be changed and the function will not be called (the function\n * will be lazily evaluated -- only called if necessary).\n *\n * This function is particularly useful when used with an `Object` which is\n * acting as a cache.\n *\n * @param {!Object<K,V>} obj The object to which to add the key-value pair.\n * @param {string} key The key to add.\n * @param {function():V} f The value to add if the key wasn't present.\n * @return {V} The value of the entry at the end of the function.\n * @template K,V\n */\ngoog.object.setWithReturnValueIfNotSet = function(obj, key, f) {\n if (key in obj) {\n return obj[key];\n }\n\n const val = f();\n obj[key] = val;\n return val;\n};\n\n\n/**\n * Compares two objects for equality using === on the values.\n *\n * @param {!Object<K,V>} a\n * @param {!Object<K,V>} b\n * @return {boolean}\n * @template K,V\n */\ngoog.object.equals = function(a, b) {\n for (const k in a) {\n if (!(k in b) || a[k] !== b[k]) {\n return false;\n }\n }\n for (const k in b) {\n if (!(k in a)) {\n return false;\n }\n }\n return true;\n};\n\n\n/**\n * Returns a shallow clone of the object.\n *\n * @param {Object<K,V>} obj Object to clone.\n * @return {!Object<K,V>} Clone of the input object.\n * @template K,V\n */\ngoog.object.clone = function(obj) {\n // We cannot use the prototype trick because a lot of methods depend on where\n // the actual key is set.\n\n const res = {};\n for (const key in obj) {\n res[key] = obj[key];\n }\n return res;\n // We could also use goog.mixin but I wanted this to be independent from that.\n};\n\n\n/**\n * Clones a value. The input may be an Object, Array, or basic type. Objects and\n * arrays will be cloned recursively.\n *\n * WARNINGS:\n * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects\n * that refer to themselves will cause infinite recursion.\n *\n * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and\n * copies UIDs created by <code>getUid</code> into cloned results.\n *\n * @param {T} obj The value to clone.\n * @return {T} A clone of the input value.\n * @template T\n */\ngoog.object.unsafeClone = function(obj) {\n const type = goog.typeOf(obj);\n if (type == 'object' || type == 'array') {\n if (goog.isFunction(obj.clone)) {\n return obj.clone();\n }\n const clone = type == 'array' ? [] : {};\n for (const key in obj) {\n clone[key] = goog.object.unsafeClone(obj[key]);\n }\n return clone;\n }\n\n return obj;\n};\n\n\n/**\n * Returns a new object in which all the keys and values are interchanged\n * (keys become values and values become keys). If multiple keys map to the\n * same value, the chosen transposed value is implementation-dependent.\n *\n * @param {Object} obj The object to transpose.\n * @return {!Object} The transposed object.\n */\ngoog.object.transpose = function(obj) {\n const transposed = {};\n for (const key in obj) {\n transposed[obj[key]] = key;\n }\n return transposed;\n};\n\n\n/**\n * The names of the fields that are defined on Object.prototype.\n * @type {Array<string>}\n * @private\n */\ngoog.object.PROTOTYPE_FIELDS_ = [\n 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',\n 'toLocaleString', 'toString', 'valueOf'\n];\n\n\n/**\n * Extends an object with another object.\n * This operates 'in-place'; it does not create a new Object.\n *\n * Example:\n * var o = {};\n * goog.object.extend(o, {a: 0, b: 1});\n * o; // {a: 0, b: 1}\n * goog.object.extend(o, {b: 2, c: 3});\n * o; // {a: 0, b: 2, c: 3}\n *\n * @param {Object} target The object to modify. Existing properties will be\n * overwritten if they are also present in one of the objects in\n * `var_args`.\n * @param {...(Object|null|undefined)} var_args The objects from which values\n * will be copied.\n * @deprecated Prefer Object.assign\n */\ngoog.object.extend = function(target, var_args) {\n let key;\n let source;\n for (let i = 1; i < arguments.length; i++) {\n source = arguments[i];\n for (key in source) {\n target[key] = source[key];\n }\n\n // For IE the for-in-loop does not contain any properties that are not\n // enumerable on the prototype object (for example isPrototypeOf from\n // Object.prototype) and it will also not include 'replace' on objects that\n // extend String and change 'replace' (not that it is common for anyone to\n // extend anything except Object).\n\n for (let j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {\n key = goog.object.PROTOTYPE_FIELDS_[j];\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n};\n\n\n/**\n * Creates a new object built from the key-value pairs provided as arguments.\n * @param {...*} var_args If only one argument is provided and it is an array\n * then this is used as the arguments, otherwise even arguments are used as\n * the property names and odd arguments are used as the property values.\n * @return {!Object} The new object.\n * @throws {Error} If there are uneven number of arguments or there is only one\n * non array argument.\n */\ngoog.object.create = function(var_args) {\n const argLength = arguments.length;\n if (argLength == 1 && Array.isArray(arguments[0])) {\n return goog.object.create.apply(null, arguments[0]);\n }\n\n if (argLength % 2) {\n throw new Error('Uneven number of arguments');\n }\n\n const rv = {};\n for (let i = 0; i < argLength; i += 2) {\n rv[arguments[i]] = arguments[i + 1];\n }\n return rv;\n};\n\n\n/**\n * Creates a new object where the property names come from the arguments but\n * the value is always set to true\n * @param {...*} var_args If only one argument is provided and it is an array\n * then this is used as the arguments, otherwise the arguments are used\n * as the property names.\n * @return {!Object} The new object.\n */\ngoog.object.createSet = function(var_args) {\n const argLength = arguments.length;\n if (argLength == 1 && Array.isArray(arguments[0])) {\n return goog.object.createSet.apply(null, arguments[0]);\n }\n\n const rv = {};\n for (let i = 0; i < argLength; i++) {\n rv[arguments[i]] = true;\n }\n return rv;\n};\n\n\n/**\n * Creates an immutable view of the underlying object, if the browser\n * supports immutable objects.\n *\n * In default mode, writes to this view will fail silently. In strict mode,\n * they will throw an error.\n *\n * @param {!Object<K,V>} obj An object.\n * @return {!Object<K,V>} An immutable view of that object, or the\n * original object if this browser does not support immutables.\n * @template K,V\n */\ngoog.object.createImmutableView = function(obj) {\n let result = obj;\n if (Object.isFrozen && !Object.isFrozen(obj)) {\n result = Object.create(obj);\n Object.freeze(result);\n }\n return result;\n};\n\n\n/**\n * @param {!Object} obj An object.\n * @return {boolean} Whether this is an immutable view of the object.\n */\ngoog.object.isImmutableView = function(obj) {\n return !!Object.isFrozen && Object.isFrozen(obj);\n};\n\n\n/**\n * Get all properties names on a given Object regardless of enumerability.\n *\n * <p> If the browser does not support `Object.getOwnPropertyNames` nor\n * `Object.getPrototypeOf` then this is equivalent to using\n * `goog.object.getKeys`\n *\n * @param {?Object} obj The object to get the properties of.\n * @param {boolean=} opt_includeObjectPrototype Whether properties defined on\n * `Object.prototype` should be included in the result.\n * @param {boolean=} opt_includeFunctionPrototype Whether properties defined on\n * `Function.prototype` should be included in the result.\n * @return {!Array<string>}\n * @public\n */\ngoog.object.getAllPropertyNames = function(\n obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {\n if (!obj) {\n return [];\n }\n\n // Naively use a for..in loop to get the property names if the browser doesn't\n // support any other APIs for getting it.\n if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {\n return goog.object.getKeys(obj);\n }\n\n const visitedSet = {};\n\n // Traverse the prototype chain and add all properties to the visited set.\n let proto = obj;\n while (proto &&\n (proto !== Object.prototype || !!opt_includeObjectPrototype) &&\n (proto !== Function.prototype || !!opt_includeFunctionPrototype)) {\n const names = Object.getOwnPropertyNames(proto);\n for (let i = 0; i < names.length; i++) {\n visitedSet[names[i]] = true;\n }\n proto = Object.getPrototypeOf(proto);\n }\n\n return goog.object.getKeys(visitedSet);\n};\n\n\n/**\n * Given a ES5 or ES6 class reference, return its super class / super\n * constructor.\n *\n * This should be used in rare cases where you need to walk up the inheritance\n * tree (this is generally a bad idea). But this work with ES5 and ES6 classes,\n * unlike relying on the superClass_ property.\n *\n * Note: To start walking up the hierarchy from an instance call this with its\n * `constructor` property; e.g. `getSuperClass(instance.constructor)`.\n *\n * @param {function(new: ?)} constructor\n * @return {?Object}\n */\ngoog.object.getSuperClass = function(constructor) {\n var proto = Object.getPrototypeOf(constructor.prototype);\n return proto && proto.constructor;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Useful compiler idioms.\n */\n\ngoog.provide('goog.reflect');\n\n\n/**\n * Syntax for object literal casts.\n * @see http://go/jscompiler-renaming\n * @see https://goo.gl/CRs09P\n *\n * Use this if you have an object literal whose keys need to have the same names\n * as the properties of some class even after they are renamed by the compiler.\n *\n * @param {!Function} type Type to cast to.\n * @param {Object} object Object literal to cast.\n * @return {Object} The object literal.\n */\ngoog.reflect.object = function(type, object) {\n return object;\n};\n\n/**\n * Syntax for renaming property strings.\n * @see http://go/jscompiler-renaming\n * @see https://goo.gl/CRs09P\n *\n * Use this if you have an need to access a property as a string, but want\n * to also have the property renamed by the compiler. In contrast to\n * goog.reflect.object, this method takes an instance of an object.\n *\n * Properties must be simple names (not qualified names).\n *\n * @param {string} prop Name of the property\n * @param {!Object} object Instance of the object whose type will be used\n * for renaming\n * @return {string} The renamed property.\n */\ngoog.reflect.objectProperty = function(prop, object) {\n return prop;\n};\n\n/**\n * To assert to the compiler that an operation is needed when it would\n * otherwise be stripped. For example:\n * <code>\n * // Force a layout\n * goog.reflect.sinkValue(dialog.offsetHeight);\n * </code>\n * @param {T} x\n * @return {T}\n * @template T\n */\ngoog.reflect.sinkValue = function(x) {\n goog.reflect.sinkValue[' '](x);\n return x;\n};\n\n\n/**\n * The compiler should optimize this function away iff no one ever uses\n * goog.reflect.sinkValue.\n */\ngoog.reflect.sinkValue[' '] = goog.nullFunction;\n\n\n/**\n * Check if a property can be accessed without throwing an exception.\n * @param {Object} obj The owner of the property.\n * @param {string} prop The property name.\n * @return {boolean} Whether the property is accessible. Will also return true\n * if obj is null.\n */\ngoog.reflect.canAccessProperty = function(obj, prop) {\n\n try {\n goog.reflect.sinkValue(obj[prop]);\n return true;\n } catch (e) {\n }\n return false;\n};\n\n\n/**\n * Retrieves a value from a cache given a key. The compiler provides special\n * consideration for this call such that it is generally considered side-effect\n * free. However, if the `opt_keyFn` or `valueFn` have side-effects\n * then the entire call is considered to have side-effects.\n *\n * Conventionally storing the value on the cache would be considered a\n * side-effect and preclude unused calls from being pruned, ie. even if\n * the value was never used, it would still always be stored in the cache.\n *\n * Providing a side-effect free `valueFn` and `opt_keyFn`\n * allows unused calls to `goog.reflect.cache` to be pruned.\n *\n * @param {!Object<K, V>} cacheObj The object that contains the cached values.\n * @param {?} key The key to lookup in the cache. If it is not string or number\n * then a `opt_keyFn` should be provided. The key is also used as the\n * parameter to the `valueFn`.\n * @param {function(?):V} valueFn The value provider to use to calculate the\n * value to store in the cache. This function should be side-effect free\n * to take advantage of the optimization.\n * @param {function(?):K=} opt_keyFn The key provider to determine the cache\n * map key. This should be used if the given key is not a string or number.\n * If not provided then the given key is used. This function should be\n * side-effect free to take advantage of the optimization.\n * @return {V} The cached or calculated value.\n * @template K\n * @template V\n */\ngoog.reflect.cache = function(cacheObj, key, valueFn, opt_keyFn) {\n const storedKey = opt_keyFn ? opt_keyFn(key) : key;\n\n if (Object.prototype.hasOwnProperty.call(cacheObj, storedKey)) {\n return cacheObj[storedKey];\n }\n\n return (cacheObj[storedKey] = valueFn(key));\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Rendering engine detection.\n * @see <a href=\"http://www.useragentstring.com/\">User agent strings</a>\n * For information on the browser brand (such as Safari versus Chrome), see\n * goog.userAgent.product.\n * @see ../demos/useragent.html\n */\n\ngoog.provide('goog.userAgent');\n\ngoog.require('goog.labs.userAgent.browser');\ngoog.require('goog.labs.userAgent.engine');\ngoog.require('goog.labs.userAgent.platform');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.reflect');\ngoog.require('goog.string');\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is IE.\n */\ngoog.userAgent.ASSUME_IE = goog.define('goog.userAgent.ASSUME_IE', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is EDGE,\n * referring to EdgeHTML based Edge.\n */\ngoog.userAgent.ASSUME_EDGE = goog.define('goog.userAgent.ASSUME_EDGE', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is GECKO.\n */\ngoog.userAgent.ASSUME_GECKO = goog.define('goog.userAgent.ASSUME_GECKO', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.\n */\ngoog.userAgent.ASSUME_WEBKIT =\n goog.define('goog.userAgent.ASSUME_WEBKIT', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is a\n * mobile device running WebKit e.g. iPhone or Android.\n */\ngoog.userAgent.ASSUME_MOBILE_WEBKIT =\n goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);\n\n\n/**\n * @define {boolean} Whether we know at compile-time that the browser is OPERA,\n * referring to Presto-based Opera.\n */\ngoog.userAgent.ASSUME_OPERA = goog.define('goog.userAgent.ASSUME_OPERA', false);\n\n\n/**\n * @define {boolean} Whether the\n * `goog.userAgent.isVersionOrHigher`\n * function will return true for any version.\n */\ngoog.userAgent.ASSUME_ANY_VERSION =\n goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);\n\n\n/**\n * Whether we know the browser engine at compile-time.\n * @type {boolean}\n * @private\n */\ngoog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||\n goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||\n goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||\n goog.userAgent.ASSUME_OPERA;\n\n\n/**\n * Returns the userAgent string for the current browser.\n *\n * @return {string} The userAgent string.\n */\ngoog.userAgent.getUserAgentString = function() {\n return goog.labs.userAgent.util.getUserAgent();\n};\n\n\n/**\n * @return {?Navigator} The native navigator object.\n */\ngoog.userAgent.getNavigatorTyped = function() {\n // Need a local navigator reference instead of using the global one,\n // to avoid the rare case where they reference different objects.\n // (in a WorkerPool, for example).\n return goog.global['navigator'] || null;\n};\n\n\n/**\n * TODO(nnaze): Change type to \"Navigator\" and update compilation targets.\n * @return {?Object} The native navigator object.\n */\ngoog.userAgent.getNavigator = function() {\n return goog.userAgent.getNavigatorTyped();\n};\n\n\n/**\n * Whether the user agent is Presto-based Opera.\n * @type {boolean}\n */\ngoog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?\n goog.userAgent.ASSUME_OPERA :\n goog.labs.userAgent.browser.isOpera();\n\n\n/**\n * Whether the user agent is Internet Explorer.\n * @type {boolean}\n */\ngoog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?\n goog.userAgent.ASSUME_IE :\n goog.labs.userAgent.browser.isIE();\n\n\n/**\n * Whether the user agent is Microsoft Edge (EdgeHTML based).\n * @type {boolean}\n */\ngoog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?\n goog.userAgent.ASSUME_EDGE :\n goog.labs.userAgent.engine.isEdge();\n\n\n/**\n * Whether the user agent is MS Internet Explorer or MS Edge (EdgeHTML based).\n * @type {boolean}\n */\ngoog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;\n\n\n/**\n * Whether the user agent is Gecko. Gecko is the rendering engine used by\n * Mozilla, Firefox, and others.\n * @type {boolean}\n */\ngoog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?\n goog.userAgent.ASSUME_GECKO :\n goog.labs.userAgent.engine.isGecko();\n\n\n/**\n * Whether the user agent is WebKit. WebKit is the rendering engine that\n * Safari, Edge Chromium, Opera Chromium, Android and others use.\n * @type {boolean}\n */\ngoog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?\n goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :\n goog.labs.userAgent.engine.isWebKit();\n\n\n/**\n * Whether the user agent is running on a mobile device.\n *\n * This is a separate function so that the logic can be tested.\n *\n * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().\n *\n * @return {boolean} Whether the user agent is running on a mobile device.\n * @private\n */\ngoog.userAgent.isMobile_ = function() {\n return goog.userAgent.WEBKIT &&\n goog.labs.userAgent.util.matchUserAgent('Mobile');\n};\n\n\n/**\n * Whether the user agent is running on a mobile device.\n *\n * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent\n * is promoted as the gecko/webkit logic is likely inaccurate.\n *\n * @type {boolean}\n */\ngoog.userAgent.MOBILE =\n goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();\n\n\n/**\n * Used while transitioning code to use WEBKIT instead.\n * @type {boolean}\n * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.\n * TODO(nicksantos): Delete this from goog.userAgent.\n */\ngoog.userAgent.SAFARI = goog.userAgent.WEBKIT;\n\n\n/**\n * @return {string} the platform (operating system) the user agent is running\n * on. Default to empty string because navigator.platform may not be defined\n * (on Rhino, for example).\n * @private\n */\ngoog.userAgent.determinePlatform_ = function() {\n var navigator = goog.userAgent.getNavigatorTyped();\n return navigator && navigator.platform || '';\n};\n\n\n/**\n * The platform (operating system) the user agent is running on. Default to\n * empty string because navigator.platform may not be defined (on Rhino, for\n * example).\n * @type {string}\n */\ngoog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Macintosh operating\n * system.\n */\ngoog.userAgent.ASSUME_MAC = goog.define('goog.userAgent.ASSUME_MAC', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Windows operating\n * system.\n */\ngoog.userAgent.ASSUME_WINDOWS =\n goog.define('goog.userAgent.ASSUME_WINDOWS', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a Linux operating\n * system.\n */\ngoog.userAgent.ASSUME_LINUX = goog.define('goog.userAgent.ASSUME_LINUX', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on a X11 windowing\n * system.\n */\ngoog.userAgent.ASSUME_X11 = goog.define('goog.userAgent.ASSUME_X11', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on Android.\n */\ngoog.userAgent.ASSUME_ANDROID =\n goog.define('goog.userAgent.ASSUME_ANDROID', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPhone.\n */\ngoog.userAgent.ASSUME_IPHONE =\n goog.define('goog.userAgent.ASSUME_IPHONE', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPad.\n */\ngoog.userAgent.ASSUME_IPAD = goog.define('goog.userAgent.ASSUME_IPAD', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on an iPod.\n */\ngoog.userAgent.ASSUME_IPOD = goog.define('goog.userAgent.ASSUME_IPOD', false);\n\n\n/**\n * @define {boolean} Whether the user agent is running on KaiOS.\n */\ngoog.userAgent.ASSUME_KAIOS = goog.define('goog.userAgent.ASSUME_KAIOS', false);\n\n\n/**\n * @type {boolean}\n * @private\n */\ngoog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||\n goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||\n goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||\n goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||\n goog.userAgent.ASSUME_IPOD;\n\n\n/**\n * Whether the user agent is running on a Macintosh operating system.\n * @type {boolean}\n */\ngoog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_MAC :\n goog.labs.userAgent.platform.isMacintosh();\n\n\n/**\n * Whether the user agent is running on a Windows operating system.\n * @type {boolean}\n */\ngoog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_WINDOWS :\n goog.labs.userAgent.platform.isWindows();\n\n\n/**\n * Whether the user agent is Linux per the legacy behavior of\n * goog.userAgent.LINUX, which considered ChromeOS to also be\n * Linux.\n * @return {boolean}\n * @private\n */\ngoog.userAgent.isLegacyLinux_ = function() {\n return goog.labs.userAgent.platform.isLinux() ||\n goog.labs.userAgent.platform.isChromeOS();\n};\n\n\n/**\n * Whether the user agent is running on a Linux operating system.\n *\n * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,\n * while goog.labs.userAgent.platform considers ChromeOS and\n * Linux to be different OSes.\n *\n * @type {boolean}\n */\ngoog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_LINUX :\n goog.userAgent.isLegacyLinux_();\n\n\n/**\n * @return {boolean} Whether the user agent is an X11 windowing system.\n * @private\n */\ngoog.userAgent.isX11_ = function() {\n var navigator = goog.userAgent.getNavigatorTyped();\n return !!navigator &&\n goog.string.contains(navigator['appVersion'] || '', 'X11');\n};\n\n\n/**\n * Whether the user agent is running on a X11 windowing system.\n * @type {boolean}\n */\ngoog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_X11 :\n goog.userAgent.isX11_();\n\n\n/**\n * Whether the user agent is running on Android.\n * @type {boolean}\n */\ngoog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_ANDROID :\n goog.labs.userAgent.platform.isAndroid();\n\n\n/**\n * Whether the user agent is running on an iPhone.\n * @type {boolean}\n */\ngoog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_IPHONE :\n goog.labs.userAgent.platform.isIphone();\n\n\n/**\n * Whether the user agent is running on an iPad.\n * @type {boolean}\n */\ngoog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_IPAD :\n goog.labs.userAgent.platform.isIpad();\n\n\n/**\n * Whether the user agent is running on an iPod.\n * @type {boolean}\n */\ngoog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_IPOD :\n goog.labs.userAgent.platform.isIpod();\n\n\n/**\n * Whether the user agent is running on iOS.\n * @type {boolean}\n */\ngoog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?\n (goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||\n goog.userAgent.ASSUME_IPOD) :\n goog.labs.userAgent.platform.isIos();\n\n/**\n * Whether the user agent is running on KaiOS.\n * @type {boolean}\n */\ngoog.userAgent.KAIOS = goog.userAgent.PLATFORM_KNOWN_ ?\n goog.userAgent.ASSUME_KAIOS :\n goog.labs.userAgent.platform.isKaiOS();\n\n\n/**\n * @return {string} The string that describes the version number of the user\n * agent.\n * @private\n */\ngoog.userAgent.determineVersion_ = function() {\n // All browsers have different ways to detect the version and they all have\n // different naming schemes.\n // version is a string rather than a number because it may contain 'b', 'a',\n // and so on.\n var version = '';\n var arr = goog.userAgent.getVersionRegexResult_();\n if (arr) {\n version = arr ? arr[1] : '';\n }\n\n if (goog.userAgent.IE) {\n // IE9 can be in document mode 9 but be reporting an inconsistent user agent\n // version. If it is identifying as a version lower than 9 we take the\n // documentMode as the version instead. IE8 has similar behavior.\n // It is recommended to set the X-UA-Compatible header to ensure that IE9\n // uses documentMode 9.\n var docMode = goog.userAgent.getDocumentMode_();\n if (docMode != null && docMode > parseFloat(version)) {\n return String(docMode);\n }\n }\n\n return version;\n};\n\n\n/**\n * @return {?IArrayLike<string>|undefined} The version regex matches from\n * parsing the user\n * agent string. These regex statements must be executed inline so they can\n * be compiled out by the closure compiler with the rest of the useragent\n * detection logic when ASSUME_* is specified.\n * @private\n */\ngoog.userAgent.getVersionRegexResult_ = function() {\n var userAgent = goog.userAgent.getUserAgentString();\n if (goog.userAgent.GECKO) {\n return /rv\\:([^\\);]+)(\\)|;)/.exec(userAgent);\n }\n if (goog.userAgent.EDGE) {\n return /Edge\\/([\\d\\.]+)/.exec(userAgent);\n }\n if (goog.userAgent.IE) {\n return /\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(userAgent);\n }\n if (goog.userAgent.WEBKIT) {\n // WebKit/125.4\n return /WebKit\\/(\\S+)/.exec(userAgent);\n }\n if (goog.userAgent.OPERA) {\n // If none of the above browsers were detected but the browser is Opera, the\n // only string that is of interest is 'Version/<number>'.\n return /(?:Version)[ \\/]?(\\S+)/.exec(userAgent);\n }\n return undefined;\n};\n\n\n/**\n * @return {number|undefined} Returns the document mode (for testing).\n * @private\n */\ngoog.userAgent.getDocumentMode_ = function() {\n // NOTE(user): goog.userAgent may be used in context where there is no DOM.\n var doc = goog.global['document'];\n return doc ? doc['documentMode'] : undefined;\n};\n\n\n/**\n * The version of the user agent. This is a string because it might contain\n * 'b' (as in beta) as well as multiple dots.\n * @type {string}\n */\ngoog.userAgent.VERSION = goog.userAgent.determineVersion_();\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string} v1 Version of first item.\n * @param {string} v2 Version of second item.\n *\n * @return {number} 1 if first argument is higher\n * 0 if arguments are equal\n * -1 if second argument is higher.\n * @deprecated Use goog.string.compareVersions.\n */\ngoog.userAgent.compare = function(v1, v2) {\n return goog.string.compareVersions(v1, v2);\n};\n\n\n/**\n * Cache for {@link goog.userAgent.isVersionOrHigher}.\n * Calls to compareVersions are surprisingly expensive and, as a browser's\n * version number is unlikely to change during a session, we cache the results.\n * @const\n * @private\n */\ngoog.userAgent.isVersionOrHigherCache_ = {};\n\n\n/**\n * Whether the user agent version is higher or the same as the given version.\n * NOTE: When checking the version numbers for Firefox and Safari, be sure to\n * use the engine's version, not the browser's version number. For example,\n * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.\n * Opera and Internet Explorer versions match the product release number.<br>\n * @see <a href=\"http://en.wikipedia.org/wiki/Safari_version_history\">\n * Webkit</a>\n * @see <a href=\"http://en.wikipedia.org/wiki/Gecko_engine\">Gecko</a>\n *\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the user agent version is higher or the same as\n * the given version.\n */\ngoog.userAgent.isVersionOrHigher = function(version) {\n return goog.userAgent.ASSUME_ANY_VERSION ||\n goog.reflect.cache(\n goog.userAgent.isVersionOrHigherCache_, version, function() {\n return goog.string.compareVersions(\n goog.userAgent.VERSION, version) >= 0;\n });\n};\n\n\n/**\n * Deprecated alias to `goog.userAgent.isVersionOrHigher`.\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the user agent version is higher or the same as\n * the given version.\n * @deprecated Use goog.userAgent.isVersionOrHigher().\n */\ngoog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;\n\n\n/**\n * Whether the IE effective document mode is higher or the same as the given\n * document mode version.\n * NOTE: Only for IE, return false for another browser.\n *\n * @param {number} documentMode The document mode version to check.\n * @return {boolean} Whether the IE effective document mode is higher or the\n * same as the given version.\n */\ngoog.userAgent.isDocumentModeOrHigher = function(documentMode) {\n return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;\n};\n\n\n/**\n * Deprecated alias to `goog.userAgent.isDocumentModeOrHigher`.\n * @param {number} version The version to check.\n * @return {boolean} Whether the IE effective document mode is higher or the\n * same as the given version.\n * @deprecated Use goog.userAgent.isDocumentModeOrHigher().\n */\ngoog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;\n\n\n/**\n * For IE version < 7, documentMode is undefined, so attempt to use the\n * CSS1Compat property to see if we are in standards mode. If we are in\n * standards mode, treat the browser version as the document mode. Otherwise,\n * IE is emulating version 5.\n *\n * NOTE(user): Support for IE < 7 is long gone, so this is now simplified.\n * It returns document.documentMode for IE and undefined for everything else.\n *\n * @type {number|undefined}\n * @const\n */\ngoog.userAgent.DOCUMENT_MODE = (function() {\n var doc = goog.global['document'];\n if (!doc || !goog.userAgent.IE) return undefined;\n // This must be an IE user agent.\n var documentMode = goog.userAgent.getDocumentMode_();\n if (documentMode) return documentMode;\n // The user agent version string begins with the major version.\n // Parse the major version and truncate anything following.\n var ieVersion = parseInt(goog.userAgent.VERSION, 10);\n return ieVersion || undefined;\n})();\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Closure user agent detection (Browser).\n * @see <a href=\"http://www.useragentstring.com/\">User agent strings</a>\n * For more information on rendering engine, platform, or device see the other\n * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,\n * goog.labs.userAgent.device respectively.)\n */\n\ngoog.provide('goog.labs.userAgent.browser');\n\ngoog.require('goog.array');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.object');\ngoog.require('goog.string.internal');\n\n\n// TODO(nnaze): Refactor to remove excessive exclusion logic in matching\n// functions.\n\n\n/**\n * @return {boolean} Whether the user's browser is Opera. Note: Chromium\n * based Opera (Opera 15+) is detected as Chrome to avoid unnecessary\n * special casing.\n * @private\n */\ngoog.labs.userAgent.browser.matchOpera_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Opera');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is IE.\n * @private\n */\ngoog.labs.userAgent.browser.matchIE_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Trident') ||\n goog.labs.userAgent.util.matchUserAgent('MSIE');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Edge. This refers to EdgeHTML\n * based Edge.\n * @private\n */\ngoog.labs.userAgent.browser.matchEdgeHtml_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Edge');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Edge.\n * @private\n */\ngoog.labs.userAgent.browser.matchEdgeChromium_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Edg/');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Opera.\n * @private\n */\ngoog.labs.userAgent.browser.matchOperaChromium_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('OPR');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Firefox.\n * @private\n */\ngoog.labs.userAgent.browser.matchFirefox_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Firefox') ||\n goog.labs.userAgent.util.matchUserAgent('FxiOS');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Safari.\n * @private\n */\ngoog.labs.userAgent.browser.matchSafari_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Safari') &&\n !(goog.labs.userAgent.browser.matchChrome_() ||\n goog.labs.userAgent.browser.matchCoast_() ||\n goog.labs.userAgent.browser.matchOpera_() ||\n goog.labs.userAgent.browser.matchEdgeHtml_() ||\n goog.labs.userAgent.browser.matchEdgeChromium_() ||\n goog.labs.userAgent.browser.matchOperaChromium_() ||\n goog.labs.userAgent.browser.matchFirefox_() ||\n goog.labs.userAgent.browser.isSilk() ||\n goog.labs.userAgent.util.matchUserAgent('Android'));\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based\n * iOS browser).\n * @private\n */\ngoog.labs.userAgent.browser.matchCoast_ = function() {\n return goog.labs.userAgent.util.matchUserAgent('Coast');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is iOS Webview.\n * @private\n */\ngoog.labs.userAgent.browser.matchIosWebview_ = function() {\n // iOS Webview does not show up as Chrome or Safari. Also check for Opera's\n // WebKit-based iOS browser, Coast.\n return (goog.labs.userAgent.util.matchUserAgent('iPad') ||\n goog.labs.userAgent.util.matchUserAgent('iPhone')) &&\n !goog.labs.userAgent.browser.matchSafari_() &&\n !goog.labs.userAgent.browser.matchChrome_() &&\n !goog.labs.userAgent.browser.matchCoast_() &&\n !goog.labs.userAgent.browser.matchFirefox_() &&\n goog.labs.userAgent.util.matchUserAgent('AppleWebKit');\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is any Chromium browser. This\n * returns true for Chrome, Opera 15+, and Edge Chromium.\n * @private\n */\ngoog.labs.userAgent.browser.matchChrome_ = function() {\n return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||\n goog.labs.userAgent.util.matchUserAgent('CriOS')) &&\n !goog.labs.userAgent.browser.matchEdgeHtml_();\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is the Android browser.\n * @private\n */\ngoog.labs.userAgent.browser.matchAndroidBrowser_ = function() {\n // Android can appear in the user agent string for Chrome on Android.\n // This is not the Android standalone browser if it does.\n return goog.labs.userAgent.util.matchUserAgent('Android') &&\n !(goog.labs.userAgent.browser.isChrome() ||\n goog.labs.userAgent.browser.isFirefox() ||\n goog.labs.userAgent.browser.isOpera() ||\n goog.labs.userAgent.browser.isSilk());\n};\n\n\n/**\n * @return {boolean} Whether the user's browser is Opera.\n */\ngoog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;\n\n\n/**\n * @return {boolean} Whether the user's browser is IE.\n */\ngoog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;\n\n\n/**\n * @return {boolean} Whether the user's browser is EdgeHTML based Edge.\n */\ngoog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdgeHtml_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Edge.\n */\ngoog.labs.userAgent.browser.isEdgeChromium =\n goog.labs.userAgent.browser.matchEdgeChromium_;\n\n/**\n * @return {boolean} Whether the user's browser is Chromium based Opera.\n */\ngoog.labs.userAgent.browser.isOperaChromium =\n goog.labs.userAgent.browser.matchOperaChromium_;\n\n/**\n * @return {boolean} Whether the user's browser is Firefox.\n */\ngoog.labs.userAgent.browser.isFirefox =\n goog.labs.userAgent.browser.matchFirefox_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Safari.\n */\ngoog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;\n\n\n/**\n * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based\n * iOS browser).\n */\ngoog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;\n\n\n/**\n * @return {boolean} Whether the user's browser is iOS Webview.\n */\ngoog.labs.userAgent.browser.isIosWebview =\n goog.labs.userAgent.browser.matchIosWebview_;\n\n\n/**\n * @return {boolean} Whether the user's browser is any Chromium based browser (\n * Chrome, Blink-based Opera (15+) and Edge Chromium).\n */\ngoog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;\n\n\n/**\n * @return {boolean} Whether the user's browser is the Android browser.\n */\ngoog.labs.userAgent.browser.isAndroidBrowser =\n goog.labs.userAgent.browser.matchAndroidBrowser_;\n\n\n/**\n * For more information, see:\n * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html\n * @return {boolean} Whether the user's browser is Silk.\n */\ngoog.labs.userAgent.browser.isSilk = function() {\n return goog.labs.userAgent.util.matchUserAgent('Silk');\n};\n\n\n/**\n * @return {string} The browser version or empty string if version cannot be\n * determined. Note that for Internet Explorer, this returns the version of\n * the browser, not the version of the rendering engine. (IE 8 in\n * compatibility mode will return 8.0 rather than 7.0. To determine the\n * rendering engine version, look at document.documentMode instead. See\n * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more\n * details.)\n */\ngoog.labs.userAgent.browser.getVersion = function() {\n var userAgentString = goog.labs.userAgent.util.getUserAgent();\n // Special case IE since IE's version is inside the parenthesis and\n // without the '/'.\n if (goog.labs.userAgent.browser.isIE()) {\n return goog.labs.userAgent.browser.getIEVersion_(userAgentString);\n }\n\n var versionTuples =\n goog.labs.userAgent.util.extractVersionTuples(userAgentString);\n\n // Construct a map for easy lookup.\n var versionMap = {};\n goog.array.forEach(versionTuples, function(tuple) {\n // Note that the tuple is of length three, but we only care about the\n // first two.\n var key = tuple[0];\n var value = tuple[1];\n versionMap[key] = value;\n });\n\n var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);\n\n // Gives the value with the first key it finds, otherwise empty string.\n function lookUpValueWithKeys(keys) {\n var key = goog.array.find(keys, versionMapHasKey);\n return versionMap[key] || '';\n }\n\n // Check Opera before Chrome since Opera 15+ has \"Chrome\" in the string.\n // See\n // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond\n if (goog.labs.userAgent.browser.isOpera()) {\n // Opera 10 has Version/10.0 but Opera/9.8, so look for \"Version\" first.\n // Opera uses 'OPR' for more recent UAs.\n return lookUpValueWithKeys(['Version', 'Opera']);\n }\n\n // Check Edge before Chrome since it has Chrome in the string.\n if (goog.labs.userAgent.browser.isEdge()) {\n return lookUpValueWithKeys(['Edge']);\n }\n\n // Check Chromium Edge before Chrome since it has Chrome in the string.\n if (goog.labs.userAgent.browser.isEdgeChromium()) {\n return lookUpValueWithKeys(['Edg']);\n }\n\n if (goog.labs.userAgent.browser.isChrome()) {\n return lookUpValueWithKeys(['Chrome', 'CriOS', 'HeadlessChrome']);\n }\n\n // Usually products browser versions are in the third tuple after \"Mozilla\"\n // and the engine.\n var tuple = versionTuples[2];\n return tuple && tuple[1] || '';\n};\n\n\n/**\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the browser version is higher or the same as the\n * given version.\n */\ngoog.labs.userAgent.browser.isVersionOrHigher = function(version) {\n return goog.string.internal.compareVersions(\n goog.labs.userAgent.browser.getVersion(), version) >= 0;\n};\n\n\n/**\n * Determines IE version. More information:\n * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString\n * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx\n * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx\n * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx\n *\n * @param {string} userAgent the User-Agent.\n * @return {string}\n * @private\n */\ngoog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {\n // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade\n // bug. Example UA:\n // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)\n // like Gecko.\n // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.\n var rv = /rv: *([\\d\\.]*)/.exec(userAgent);\n if (rv && rv[1]) {\n return rv[1];\n }\n\n var version = '';\n var msie = /MSIE +([\\d\\.]+)/.exec(userAgent);\n if (msie && msie[1]) {\n // IE in compatibility mode usually identifies itself as MSIE 7.0; in this\n // case, use the Trident version to determine the version of IE. For more\n // details, see the links above.\n var tridentVersion = /Trident\\/(\\d.\\d)/.exec(userAgent);\n if (msie[1] == '7.0') {\n if (tridentVersion && tridentVersion[1]) {\n switch (tridentVersion[1]) {\n case '4.0':\n version = '8.0';\n break;\n case '5.0':\n version = '9.0';\n break;\n case '6.0':\n version = '10.0';\n break;\n case '7.0':\n version = '11.0';\n break;\n }\n } else {\n version = '7.0';\n }\n } else {\n version = msie[1];\n }\n }\n return version;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Closure user agent detection.\n * @see http://en.wikipedia.org/wiki/User_agent\n * For more information on browser brand, platform, or device see the other\n * sub-namespaces in goog.labs.userAgent (browser, platform, and device).\n */\n\ngoog.provide('goog.labs.userAgent.engine');\n\ngoog.require('goog.array');\ngoog.require('goog.labs.userAgent.util');\ngoog.require('goog.string');\n\n\n/**\n * @return {boolean} Whether the rendering engine is Presto.\n */\ngoog.labs.userAgent.engine.isPresto = function() {\n return goog.labs.userAgent.util.matchUserAgent('Presto');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is Trident.\n */\ngoog.labs.userAgent.engine.isTrident = function() {\n // IE only started including the Trident token in IE8.\n return goog.labs.userAgent.util.matchUserAgent('Trident') ||\n goog.labs.userAgent.util.matchUserAgent('MSIE');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is EdgeHTML.\n */\ngoog.labs.userAgent.engine.isEdge = function() {\n return goog.labs.userAgent.util.matchUserAgent('Edge');\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is WebKit. This will return\n * true for Chrome, Blink-based Opera (15+), Edge Chromium and Safari.\n */\ngoog.labs.userAgent.engine.isWebKit = function() {\n return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') &&\n !goog.labs.userAgent.engine.isEdge();\n};\n\n\n/**\n * @return {boolean} Whether the rendering engine is Gecko.\n */\ngoog.labs.userAgent.engine.isGecko = function() {\n return goog.labs.userAgent.util.matchUserAgent('Gecko') &&\n !goog.labs.userAgent.engine.isWebKit() &&\n !goog.labs.userAgent.engine.isTrident() &&\n !goog.labs.userAgent.engine.isEdge();\n};\n\n\n/**\n * @return {string} The rendering engine's version or empty string if version\n * can't be determined.\n */\ngoog.labs.userAgent.engine.getVersion = function() {\n var userAgentString = goog.labs.userAgent.util.getUserAgent();\n if (userAgentString) {\n var tuples = goog.labs.userAgent.util.extractVersionTuples(userAgentString);\n\n var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples);\n if (engineTuple) {\n // In Gecko, the version string is either in the browser info or the\n // Firefox version. See Gecko user agent string reference:\n // http://goo.gl/mULqa\n if (engineTuple[0] == 'Gecko') {\n return goog.labs.userAgent.engine.getVersionForKey_(tuples, 'Firefox');\n }\n\n return engineTuple[1];\n }\n\n // MSIE has only one version identifier, and the Trident version is\n // specified in the parenthetical. IE Edge is covered in the engine tuple\n // detection.\n var browserTuple = tuples[0];\n var info;\n if (browserTuple && (info = browserTuple[2])) {\n var match = /Trident\\/([^\\s;]+)/.exec(info);\n if (match) {\n return match[1];\n }\n }\n }\n return '';\n};\n\n\n/**\n * @param {!Array<!Array<string>>} tuples Extracted version tuples.\n * @return {!Array<string>|undefined} The engine tuple or undefined if not\n * found.\n * @private\n */\ngoog.labs.userAgent.engine.getEngineTuple_ = function(tuples) {\n if (!goog.labs.userAgent.engine.isEdge()) {\n return tuples[1];\n }\n for (var i = 0; i < tuples.length; i++) {\n var tuple = tuples[i];\n if (tuple[0] == 'Edge') {\n return tuple;\n }\n }\n};\n\n\n/**\n * @param {string|number} version The version to check.\n * @return {boolean} Whether the rendering engine version is higher or the same\n * as the given version.\n */\ngoog.labs.userAgent.engine.isVersionOrHigher = function(version) {\n return goog.string.compareVersions(\n goog.labs.userAgent.engine.getVersion(), version) >= 0;\n};\n\n\n/**\n * @param {!Array<!Array<string>>} tuples Version tuples.\n * @param {string} key The key to look for.\n * @return {string} The version string of the given key, if present.\n * Otherwise, the empty string.\n * @private\n */\ngoog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {\n // TODO(nnaze): Move to util if useful elsewhere.\n\n var pair = goog.array.find(tuples, function(pair) { return key == pair[0]; });\n\n return pair && pair[1] || '';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Browser capability checks for the events package.\n */\n\n\ngoog.provide('goog.events.BrowserFeature');\n\ngoog.require('goog.userAgent');\ngoog.scope(function() {\n\n\n\n/**\n * Enum of browser capabilities.\n * @enum {boolean}\n */\ngoog.events.BrowserFeature = {\n /**\n * Whether the button attribute of the event is W3C compliant. False in\n * Internet Explorer prior to version 9; document-version dependent.\n */\n HAS_W3C_BUTTON:\n !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),\n\n /**\n * Whether the browser supports full W3C event model.\n */\n HAS_W3C_EVENT_SUPPORT:\n !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),\n\n /**\n * To prevent default in IE7-8 for certain keydown events we need set the\n * keyCode to -1.\n */\n SET_KEY_CODE_TO_PREVENT_DEFAULT:\n goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),\n\n /**\n * Whether the `navigator.onLine` property is supported.\n */\n HAS_NAVIGATOR_ONLINE_PROPERTY:\n !goog.userAgent.WEBKIT || goog.userAgent.isVersionOrHigher('528'),\n\n /**\n * Whether HTML5 network online/offline events are supported.\n */\n HAS_HTML5_NETWORK_EVENT_SUPPORT:\n goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||\n goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||\n goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||\n goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),\n\n /**\n * Whether HTML5 network events fire on document.body, or otherwise the\n * window.\n */\n HTML5_NETWORK_EVENTS_FIRE_ON_BODY:\n goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||\n goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),\n\n /**\n * Whether touch is enabled in the browser.\n */\n TOUCH_ENABLED:\n ('ontouchstart' in goog.global ||\n !!(goog.global['document'] && document.documentElement &&\n 'ontouchstart' in document.documentElement) ||\n // IE10 uses non-standard touch events, so it has a different check.\n !!(goog.global['navigator'] &&\n (goog.global['navigator']['maxTouchPoints'] ||\n goog.global['navigator']['msMaxTouchPoints']))),\n\n /**\n * Whether addEventListener supports W3C standard pointer events.\n * http://www.w3.org/TR/pointerevents/\n */\n POINTER_EVENTS: ('PointerEvent' in goog.global),\n\n /**\n * Whether addEventListener supports MSPointer events (only used in IE10).\n * http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx\n * http://msdn.microsoft.com/library/hh673557(v=vs.85).aspx\n */\n MSPOINTER_EVENTS:\n ('MSPointerEvent' in goog.global &&\n !!(goog.global['navigator'] &&\n goog.global['navigator']['msPointerEnabled'])),\n\n /**\n * Whether addEventListener supports {passive: true}.\n * https://developers.google.com/web/updates/2016/06/passive-event-listeners\n */\n PASSIVE_EVENTS: purify(function() {\n // If we're in a web worker or other custom environment, we can't tell.\n if (!goog.global.addEventListener || !Object.defineProperty) { // IE 8\n return false;\n }\n\n var passive = false;\n var options = Object.defineProperty({}, 'passive', {\n get: function() {\n passive = true;\n }\n });\n try {\n goog.global.addEventListener('test', goog.nullFunction, options);\n goog.global.removeEventListener('test', goog.nullFunction, options);\n } catch (e) {\n }\n\n return passive;\n })\n};\n\n\n/**\n * Tricks Closure Compiler into believing that a function is pure. The compiler\n * assumes that any `valueOf` function is pure, without analyzing its contents.\n *\n * @param {function(): T} fn\n * @return {T}\n * @template T\n */\nfunction purify(fn) {\n return ({valueOf: fn}).valueOf();\n}\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A base class for event objects.\n */\n\n\ngoog.provide('goog.events.Event');\ngoog.provide('goog.events.EventLike');\n\n/**\n * goog.events.Event no longer depends on goog.Disposable. Keep requiring\n * goog.Disposable here to not break projects which assume this dependency.\n * @suppress {extraRequire}\n */\ngoog.require('goog.Disposable');\ngoog.require('goog.events.EventId');\n\n\n/**\n * A typedef for event like objects that are dispatchable via the\n * goog.events.dispatchEvent function. strings are treated as the type for a\n * goog.events.Event. Objects are treated as an extension of a new\n * goog.events.Event with the type property of the object being used as the type\n * of the Event.\n * @typedef {string|Object|goog.events.Event|goog.events.EventId}\n */\ngoog.events.EventLike;\n\n\n\n/**\n * A base class for event objects, so that they can support preventDefault and\n * stopPropagation.\n *\n * @param {string|!goog.events.EventId} type Event Type.\n * @param {Object=} opt_target Reference to the object that is the target of\n * this event. It has to implement the `EventTarget` interface\n * declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.\n * @constructor\n */\ngoog.events.Event = function(type, opt_target) {\n /**\n * Event type.\n * @type {string}\n */\n this.type = type instanceof goog.events.EventId ? String(type) : type;\n\n /**\n * TODO(tbreisacher): The type should probably be\n * EventTarget|goog.events.EventTarget.\n *\n * Target of the event.\n * @type {Object|undefined}\n */\n this.target = opt_target;\n\n /**\n * Object that had the listener attached.\n * @type {Object|undefined}\n */\n this.currentTarget = this.target;\n\n /**\n * Whether to cancel the event in internal capture/bubble processing for IE.\n * @type {boolean}\n * @private\n */\n this.propagationStopped_ = false;\n\n /**\n * Whether the default action has been prevented.\n * This is a property to match the W3C specification at\n * {@link http://www.w3.org/TR/DOM-Level-3-Events/\n * #events-event-type-defaultPrevented}.\n * Must be treated as read-only outside the class.\n * @type {boolean}\n */\n this.defaultPrevented = false;\n};\n\n/**\n * @return {boolean} true iff internal propagation has been stopped.\n */\ngoog.events.Event.prototype.hasPropagationStopped = function() {\n return this.propagationStopped_;\n};\n\n/**\n * Stops event propagation.\n */\ngoog.events.Event.prototype.stopPropagation = function() {\n this.propagationStopped_ = true;\n};\n\n\n/**\n * Prevents the default action, for example a link redirecting to a url.\n */\ngoog.events.Event.prototype.preventDefault = function() {\n this.defaultPrevented = true;\n};\n\n\n/**\n * Stops the propagation of the event. It is equivalent to\n * `e.stopPropagation()`, but can be used as the callback argument of\n * {@link goog.events.listen} without declaring another function.\n * @param {!goog.events.Event} e An event.\n */\ngoog.events.Event.stopPropagation = function(e) {\n e.stopPropagation();\n};\n\n\n/**\n * Prevents the default action. It is equivalent to\n * `e.preventDefault()`, but can be used as the callback argument of\n * {@link goog.events.listen} without declaring another function.\n * @param {!goog.events.Event} e An event.\n */\ngoog.events.Event.preventDefault = function(e) {\n e.preventDefault();\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A patched, standardized event object for browser events.\n *\n * <pre>\n * The patched event object contains the following members:\n * - type {string} Event type, e.g. 'click'\n * - target {Object} The element that actually triggered the event\n * - currentTarget {Object} The element the listener is attached to\n * - relatedTarget {Object} For mouseover and mouseout, the previous object\n * - offsetX {number} X-coordinate relative to target\n * - offsetY {number} Y-coordinate relative to target\n * - clientX {number} X-coordinate relative to viewport\n * - clientY {number} Y-coordinate relative to viewport\n * - screenX {number} X-coordinate relative to the edge of the screen\n * - screenY {number} Y-coordinate relative to the edge of the screen\n * - button {number} Mouse button. Use isButton() to test.\n * - keyCode {number} Key-code\n * - ctrlKey {boolean} Was ctrl key depressed\n * - altKey {boolean} Was alt key depressed\n * - shiftKey {boolean} Was shift key depressed\n * - metaKey {boolean} Was meta key depressed\n * - pointerId {number} Pointer ID\n * - pointerType {string} Pointer type, e.g. 'mouse', 'pen', or 'touch'\n * - defaultPrevented {boolean} Whether the default action has been prevented\n * - state {Object} History state object\n *\n * NOTE: The keyCode member contains the raw browser keyCode. For normalized\n * key and character code use {@link goog.events.KeyHandler}.\n * </pre>\n */\n\ngoog.provide('goog.events.BrowserEvent');\ngoog.provide('goog.events.BrowserEvent.MouseButton');\ngoog.provide('goog.events.BrowserEvent.PointerType');\n\ngoog.require('goog.debug');\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.EventType');\ngoog.require('goog.reflect');\ngoog.require('goog.userAgent');\n\n/**\n * @define {boolean} If true, use the layerX and layerY properties of a native\n * browser event over the offsetX and offsetY properties, which cause expensive\n * reflow. If layerX or layerY is not defined, offsetX and offsetY will be used\n * as usual.\n */\ngoog.events.USE_LAYER_XY_AS_OFFSET_XY =\n goog.define('goog.events.USE_LAYER_XY_AS_OFFSET_XY', false);\n\n/**\n * Accepts a browser event object and creates a patched, cross browser event\n * object.\n * The content of this object will not be initialized if no event object is\n * provided. If this is the case, init() needs to be invoked separately.\n * @param {Event=} opt_e Browser event object.\n * @param {EventTarget=} opt_currentTarget Current target for event.\n * @constructor\n * @extends {goog.events.Event}\n */\ngoog.events.BrowserEvent = function(opt_e, opt_currentTarget) {\n goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');\n\n /**\n * Target that fired the event.\n * @override\n * @type {?Node}\n */\n this.target = null;\n\n /**\n * Node that had the listener attached.\n * @override\n * @type {?Node|undefined}\n */\n this.currentTarget = null;\n\n /**\n * For mouseover and mouseout events, the related object for the event.\n * @type {?Node}\n */\n this.relatedTarget = null;\n\n /**\n * X-coordinate relative to target.\n * @type {number}\n */\n this.offsetX = 0;\n\n /**\n * Y-coordinate relative to target.\n * @type {number}\n */\n this.offsetY = 0;\n\n /**\n * X-coordinate relative to the window.\n * @type {number}\n */\n this.clientX = 0;\n\n /**\n * Y-coordinate relative to the window.\n * @type {number}\n */\n this.clientY = 0;\n\n /**\n * X-coordinate relative to the monitor.\n * @type {number}\n */\n this.screenX = 0;\n\n /**\n * Y-coordinate relative to the monitor.\n * @type {number}\n */\n this.screenY = 0;\n\n /**\n * Which mouse button was pressed.\n * @type {number}\n */\n this.button = 0;\n\n /**\n * Key of key press.\n * @type {string}\n */\n this.key = '';\n\n /**\n * Keycode of key press.\n * @type {number}\n */\n this.keyCode = 0;\n\n /**\n * Keycode of key press.\n * @type {number}\n */\n this.charCode = 0;\n\n /**\n * Whether control was pressed at time of event.\n * @type {boolean}\n */\n this.ctrlKey = false;\n\n /**\n * Whether alt was pressed at time of event.\n * @type {boolean}\n */\n this.altKey = false;\n\n /**\n * Whether shift was pressed at time of event.\n * @type {boolean}\n */\n this.shiftKey = false;\n\n /**\n * Whether the meta key was pressed at time of event.\n * @type {boolean}\n */\n this.metaKey = false;\n\n /**\n * History state object, only set for PopState events where it's a copy of the\n * state object provided to pushState or replaceState.\n * @type {?Object}\n */\n this.state = null;\n\n /**\n * Whether the default platform modifier key was pressed at time of event.\n * (This is control for all platforms except Mac, where it's Meta.)\n * @type {boolean}\n */\n this.platformModifierKey = false;\n\n /**\n * @type {number}\n */\n this.pointerId = 0;\n\n /**\n * @type {string}\n */\n this.pointerType = '';\n\n /**\n * The browser event object.\n * @private {?Event}\n */\n this.event_ = null;\n\n if (opt_e) {\n this.init(opt_e, opt_currentTarget);\n }\n};\ngoog.inherits(goog.events.BrowserEvent, goog.events.Event);\n\n\n/**\n * Normalized button constants for the mouse.\n * @enum {number}\n */\ngoog.events.BrowserEvent.MouseButton = {\n LEFT: 0,\n MIDDLE: 1,\n RIGHT: 2\n};\n\n\n/**\n * Normalized pointer type constants for pointer events.\n * @enum {string}\n */\ngoog.events.BrowserEvent.PointerType = {\n MOUSE: 'mouse',\n PEN: 'pen',\n TOUCH: 'touch'\n};\n\n\n/**\n * Static data for mapping mouse buttons.\n * @type {!Array<number>}\n * @deprecated Use `goog.events.BrowserEvent.IE_BUTTON_MAP` instead.\n */\ngoog.events.BrowserEvent.IEButtonMap = goog.debug.freeze([\n 1, // LEFT\n 4, // MIDDLE\n 2 // RIGHT\n]);\n\n\n/**\n * Static data for mapping mouse buttons.\n * @const {!Array<number>}\n */\ngoog.events.BrowserEvent.IE_BUTTON_MAP = goog.events.BrowserEvent.IEButtonMap;\n\n\n/**\n * Static data for mapping MSPointerEvent types to PointerEvent types.\n * @const {!Object<number, goog.events.BrowserEvent.PointerType>}\n */\ngoog.events.BrowserEvent.IE_POINTER_TYPE_MAP = goog.debug.freeze({\n 2: goog.events.BrowserEvent.PointerType.TOUCH,\n 3: goog.events.BrowserEvent.PointerType.PEN,\n 4: goog.events.BrowserEvent.PointerType.MOUSE\n});\n\n\n/**\n * Accepts a browser event object and creates a patched, cross browser event\n * object.\n * @param {Event} e Browser event object.\n * @param {EventTarget=} opt_currentTarget Current target for event.\n */\ngoog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {\n var type = this.type = e.type;\n\n /**\n * On touch devices use the first \"changed touch\" as the relevant touch.\n * @type {?Touch}\n */\n var relevantTouch =\n e.changedTouches && e.changedTouches.length ? e.changedTouches[0] : null;\n\n // TODO(nicksantos): Change this.target to type EventTarget.\n this.target = /** @type {Node} */ (e.target) || e.srcElement;\n\n // TODO(nicksantos): Change this.currentTarget to type EventTarget.\n this.currentTarget = /** @type {Node} */ (opt_currentTarget);\n\n var relatedTarget = /** @type {Node} */ (e.relatedTarget);\n if (relatedTarget) {\n // There's a bug in FireFox where sometimes, relatedTarget will be a\n // chrome element, and accessing any property of it will get a permission\n // denied exception. See:\n // https://bugzilla.mozilla.org/show_bug.cgi?id=497780\n if (goog.userAgent.GECKO) {\n if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {\n relatedTarget = null;\n }\n }\n } else if (type == goog.events.EventType.MOUSEOVER) {\n relatedTarget = e.fromElement;\n } else if (type == goog.events.EventType.MOUSEOUT) {\n relatedTarget = e.toElement;\n }\n\n this.relatedTarget = relatedTarget;\n\n if (relevantTouch) {\n this.clientX = relevantTouch.clientX !== undefined ? relevantTouch.clientX :\n relevantTouch.pageX;\n this.clientY = relevantTouch.clientY !== undefined ? relevantTouch.clientY :\n relevantTouch.pageY;\n this.screenX = relevantTouch.screenX || 0;\n this.screenY = relevantTouch.screenY || 0;\n } else {\n if (goog.events.USE_LAYER_XY_AS_OFFSET_XY) {\n this.offsetX = (e.layerX !== undefined) ? e.layerX : e.offsetX;\n this.offsetY = (e.layerY !== undefined) ? e.layerY : e.offsetY;\n } else {\n // Webkit emits a lame warning whenever layerX/layerY is accessed.\n // http://code.google.com/p/chromium/issues/detail?id=101733\n this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?\n e.offsetX :\n e.layerX;\n this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?\n e.offsetY :\n e.layerY;\n }\n this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;\n this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;\n this.screenX = e.screenX || 0;\n this.screenY = e.screenY || 0;\n }\n\n this.button = e.button;\n\n this.keyCode = e.keyCode || 0;\n this.key = e.key || '';\n this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);\n this.ctrlKey = e.ctrlKey;\n this.altKey = e.altKey;\n this.shiftKey = e.shiftKey;\n this.metaKey = e.metaKey;\n this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;\n this.pointerId = e.pointerId || 0;\n this.pointerType = goog.events.BrowserEvent.getPointerType_(e);\n this.state = e.state;\n this.event_ = e;\n if (e.defaultPrevented) {\n this.preventDefault();\n }\n};\n\n\n/**\n * Tests to see which button was pressed during the event. This is really only\n * useful in IE and Gecko browsers. And in IE, it's only useful for\n * mousedown/mouseup events, because click only fires for the left mouse button.\n *\n * Safari 2 only reports the left button being clicked, and uses the value '1'\n * instead of 0. Opera only reports a mousedown event for the middle button, and\n * no mouse events for the right button. Opera has default behavior for left and\n * middle click that can only be overridden via a configuration setting.\n *\n * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.\n *\n * @param {goog.events.BrowserEvent.MouseButton} button The button\n * to test for.\n * @return {boolean} True if button was pressed.\n */\ngoog.events.BrowserEvent.prototype.isButton = function(button) {\n if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {\n if (this.type == 'click') {\n return button == goog.events.BrowserEvent.MouseButton.LEFT;\n } else {\n return !!(\n this.event_.button & goog.events.BrowserEvent.IE_BUTTON_MAP[button]);\n }\n } else {\n return this.event_.button == button;\n }\n};\n\n\n/**\n * Whether this has an \"action\"-producing mouse button.\n *\n * By definition, this includes left-click on windows/linux, and left-click\n * without the ctrl key on Macs.\n *\n * @return {boolean} The result.\n */\ngoog.events.BrowserEvent.prototype.isMouseActionButton = function() {\n // Webkit does not ctrl+click to be a right-click, so we\n // normalize it to behave like Gecko and Opera.\n return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&\n !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);\n};\n\n\n/**\n * @override\n */\ngoog.events.BrowserEvent.prototype.stopPropagation = function() {\n goog.events.BrowserEvent.superClass_.stopPropagation.call(this);\n if (this.event_.stopPropagation) {\n this.event_.stopPropagation();\n } else {\n this.event_.cancelBubble = true;\n }\n};\n\n\n/**\n * @override\n */\ngoog.events.BrowserEvent.prototype.preventDefault = function() {\n goog.events.BrowserEvent.superClass_.preventDefault.call(this);\n var be = this.event_;\n if (!be.preventDefault) {\n be.returnValue = false;\n if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {\n\n try {\n // Most keys can be prevented using returnValue. Some special keys\n // require setting the keyCode to -1 as well:\n //\n // In IE7:\n // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)\n //\n // In IE8:\n // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)\n //\n // We therefore do this for all function keys as well as when Ctrl key\n // is pressed.\n var VK_F1 = 112;\n var VK_F12 = 123;\n if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {\n be.keyCode = -1;\n }\n } catch (ex) {\n // IE throws an 'access denied' exception when trying to change\n // keyCode in some situations (e.g. srcElement is input[type=file],\n // or srcElement is an anchor tag rewritten by parent's innerHTML).\n // Do nothing in this case.\n }\n }\n } else {\n be.preventDefault();\n }\n};\n\n\n/**\n * @return {Event} The underlying browser event object.\n */\ngoog.events.BrowserEvent.prototype.getBrowserEvent = function() {\n return this.event_;\n};\n\n\n/**\n * Extracts the pointer type from the given event.\n * @param {!Event} e\n * @return {string} The pointer type, e.g. 'mouse', 'pen', or 'touch'.\n * @private\n */\ngoog.events.BrowserEvent.getPointerType_ = function(e) {\n if (typeof (e.pointerType) === 'string') {\n return e.pointerType;\n }\n // IE10 uses integer codes for pointer type.\n // https://msdn.microsoft.com/en-us/library/hh772359(v=vs.85).aspx\n return goog.events.BrowserEvent.IE_POINTER_TYPE_MAP[e.pointerType] || '';\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Event Types.\n */\n\n\ngoog.provide('goog.events.EventType');\ngoog.provide('goog.events.MouseAsMouseEventType');\ngoog.provide('goog.events.MouseEvents');\ngoog.provide('goog.events.PointerAsMouseEventType');\ngoog.provide('goog.events.PointerAsTouchEventType');\ngoog.provide('goog.events.PointerFallbackEventType');\ngoog.provide('goog.events.PointerTouchFallbackEventType');\n\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.userAgent');\n\n\n/**\n * Returns a prefixed event name for the current browser.\n * @param {string} eventName The name of the event.\n * @return {string} The prefixed event name.\n * @suppress {missingRequire|missingProvide}\n * @private\n */\ngoog.events.getVendorPrefixedName_ = function(eventName) {\n return goog.userAgent.WEBKIT ?\n 'webkit' + eventName :\n (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :\n eventName.toLowerCase());\n};\n\n\n/**\n * Constants for event names.\n * @enum {string}\n */\ngoog.events.EventType = {\n // Mouse events\n CLICK: 'click',\n RIGHTCLICK: 'rightclick',\n DBLCLICK: 'dblclick',\n AUXCLICK: 'auxclick',\n MOUSEDOWN: 'mousedown',\n MOUSEUP: 'mouseup',\n MOUSEOVER: 'mouseover',\n MOUSEOUT: 'mouseout',\n MOUSEMOVE: 'mousemove',\n MOUSEENTER: 'mouseenter',\n MOUSELEAVE: 'mouseleave',\n\n // Non-existent event; will never fire. This exists as a mouse counterpart to\n // POINTERCANCEL.\n MOUSECANCEL: 'mousecancel',\n\n // Selection events.\n // https://www.w3.org/TR/selection-api/\n SELECTIONCHANGE: 'selectionchange',\n SELECTSTART: 'selectstart', // IE, Safari, Chrome\n\n // Wheel events\n // http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents\n WHEEL: 'wheel',\n\n // Key events\n KEYPRESS: 'keypress',\n KEYDOWN: 'keydown',\n KEYUP: 'keyup',\n\n // Focus\n BLUR: 'blur',\n FOCUS: 'focus',\n DEACTIVATE: 'deactivate', // IE only\n FOCUSIN: 'focusin',\n FOCUSOUT: 'focusout',\n\n // Forms\n CHANGE: 'change',\n RESET: 'reset',\n SELECT: 'select',\n SUBMIT: 'submit',\n INPUT: 'input',\n PROPERTYCHANGE: 'propertychange', // IE only\n\n // Drag and drop\n DRAGSTART: 'dragstart',\n DRAG: 'drag',\n DRAGENTER: 'dragenter',\n DRAGOVER: 'dragover',\n DRAGLEAVE: 'dragleave',\n DROP: 'drop',\n DRAGEND: 'dragend',\n\n // Touch events\n // Note that other touch events exist, but we should follow the W3C list here.\n // http://www.w3.org/TR/touch-events/#list-of-touchevent-types\n TOUCHSTART: 'touchstart',\n TOUCHMOVE: 'touchmove',\n TOUCHEND: 'touchend',\n TOUCHCANCEL: 'touchcancel',\n\n // Misc\n BEFOREUNLOAD: 'beforeunload',\n CONSOLEMESSAGE: 'consolemessage',\n CONTEXTMENU: 'contextmenu',\n DEVICECHANGE: 'devicechange',\n DEVICEMOTION: 'devicemotion',\n DEVICEORIENTATION: 'deviceorientation',\n DOMCONTENTLOADED: 'DOMContentLoaded',\n ERROR: 'error',\n HELP: 'help',\n LOAD: 'load',\n LOSECAPTURE: 'losecapture',\n ORIENTATIONCHANGE: 'orientationchange',\n READYSTATECHANGE: 'readystatechange',\n RESIZE: 'resize',\n SCROLL: 'scroll',\n UNLOAD: 'unload',\n\n // Media events\n CANPLAY: 'canplay',\n CANPLAYTHROUGH: 'canplaythrough',\n DURATIONCHANGE: 'durationchange',\n EMPTIED: 'emptied',\n ENDED: 'ended',\n LOADEDDATA: 'loadeddata',\n LOADEDMETADATA: 'loadedmetadata',\n PAUSE: 'pause',\n PLAY: 'play',\n PLAYING: 'playing',\n PROGRESS: 'progress',\n RATECHANGE: 'ratechange',\n SEEKED: 'seeked',\n SEEKING: 'seeking',\n STALLED: 'stalled',\n SUSPEND: 'suspend',\n TIMEUPDATE: 'timeupdate',\n VOLUMECHANGE: 'volumechange',\n WAITING: 'waiting',\n\n // Media Source Extensions events\n // https://www.w3.org/TR/media-source/#mediasource-events\n SOURCEOPEN: 'sourceopen',\n SOURCEENDED: 'sourceended',\n SOURCECLOSED: 'sourceclosed',\n // https://www.w3.org/TR/media-source/#sourcebuffer-events\n ABORT: 'abort',\n UPDATE: 'update',\n UPDATESTART: 'updatestart',\n UPDATEEND: 'updateend',\n\n // HTML 5 History events\n // See http://www.w3.org/TR/html5/browsers.html#event-definitions-0\n HASHCHANGE: 'hashchange',\n PAGEHIDE: 'pagehide',\n PAGESHOW: 'pageshow',\n POPSTATE: 'popstate',\n\n // Copy and Paste\n // Support is limited. Make sure it works on your favorite browser\n // before using.\n // http://www.quirksmode.org/dom/events/cutcopypaste.html\n COPY: 'copy',\n PASTE: 'paste',\n CUT: 'cut',\n BEFORECOPY: 'beforecopy',\n BEFORECUT: 'beforecut',\n BEFOREPASTE: 'beforepaste',\n\n // HTML5 online/offline events.\n // http://www.w3.org/TR/offline-webapps/#related\n ONLINE: 'online',\n OFFLINE: 'offline',\n\n // HTML 5 worker events\n MESSAGE: 'message',\n CONNECT: 'connect',\n\n // Service Worker Events - ServiceWorkerGlobalScope context\n // See https://w3c.github.io/ServiceWorker/#execution-context-events\n // Note: message event defined in worker events section\n INSTALL: 'install',\n ACTIVATE: 'activate',\n FETCH: 'fetch',\n FOREIGNFETCH: 'foreignfetch',\n MESSAGEERROR: 'messageerror',\n\n // Service Worker Events - Document context\n // See https://w3c.github.io/ServiceWorker/#document-context-events\n STATECHANGE: 'statechange',\n UPDATEFOUND: 'updatefound',\n CONTROLLERCHANGE: 'controllerchange',\n\n // CSS animation events.\n /** @suppress {missingRequire} */\n ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),\n /** @suppress {missingRequire} */\n ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),\n /** @suppress {missingRequire} */\n ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),\n\n // CSS transition events. Based on the browser support described at:\n // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility\n /** @suppress {missingRequire} */\n TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),\n\n // W3C Pointer Events\n // http://www.w3.org/TR/pointerevents/\n POINTERDOWN: 'pointerdown',\n POINTERUP: 'pointerup',\n POINTERCANCEL: 'pointercancel',\n POINTERMOVE: 'pointermove',\n POINTEROVER: 'pointerover',\n POINTEROUT: 'pointerout',\n POINTERENTER: 'pointerenter',\n POINTERLEAVE: 'pointerleave',\n GOTPOINTERCAPTURE: 'gotpointercapture',\n LOSTPOINTERCAPTURE: 'lostpointercapture',\n\n // IE specific events.\n // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx\n // Note: these events will be supplanted in IE11.\n MSGESTURECHANGE: 'MSGestureChange',\n MSGESTUREEND: 'MSGestureEnd',\n MSGESTUREHOLD: 'MSGestureHold',\n MSGESTURESTART: 'MSGestureStart',\n MSGESTURETAP: 'MSGestureTap',\n MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',\n MSINERTIASTART: 'MSInertiaStart',\n MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',\n MSPOINTERCANCEL: 'MSPointerCancel',\n MSPOINTERDOWN: 'MSPointerDown',\n MSPOINTERENTER: 'MSPointerEnter',\n MSPOINTERHOVER: 'MSPointerHover',\n MSPOINTERLEAVE: 'MSPointerLeave',\n MSPOINTERMOVE: 'MSPointerMove',\n MSPOINTEROUT: 'MSPointerOut',\n MSPOINTEROVER: 'MSPointerOver',\n MSPOINTERUP: 'MSPointerUp',\n\n // Native IMEs/input tools events.\n TEXT: 'text',\n // The textInput event is supported in IE9+, but only in lower case. All other\n // browsers use the camel-case event name.\n TEXTINPUT: goog.userAgent.IE ? 'textinput' : 'textInput',\n COMPOSITIONSTART: 'compositionstart',\n COMPOSITIONUPDATE: 'compositionupdate',\n COMPOSITIONEND: 'compositionend',\n\n // The beforeinput event is initially only supported in Safari. See\n // https://bugs.chromium.org/p/chromium/issues/detail?id=342670 for Chrome\n // implementation tracking.\n BEFOREINPUT: 'beforeinput',\n\n // Webview tag events\n // See https://developer.chrome.com/apps/tags/webview\n EXIT: 'exit',\n LOADABORT: 'loadabort',\n LOADCOMMIT: 'loadcommit',\n LOADREDIRECT: 'loadredirect',\n LOADSTART: 'loadstart',\n LOADSTOP: 'loadstop',\n RESPONSIVE: 'responsive',\n SIZECHANGED: 'sizechanged',\n UNRESPONSIVE: 'unresponsive',\n\n // HTML5 Page Visibility API. See details at\n // `goog.labs.dom.PageVisibilityMonitor`.\n VISIBILITYCHANGE: 'visibilitychange',\n\n // LocalStorage event.\n STORAGE: 'storage',\n\n // DOM Level 2 mutation events (deprecated).\n DOMSUBTREEMODIFIED: 'DOMSubtreeModified',\n DOMNODEINSERTED: 'DOMNodeInserted',\n DOMNODEREMOVED: 'DOMNodeRemoved',\n DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',\n DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',\n DOMATTRMODIFIED: 'DOMAttrModified',\n DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified',\n\n // Print events.\n BEFOREPRINT: 'beforeprint',\n AFTERPRINT: 'afterprint',\n\n // Web app manifest events.\n BEFOREINSTALLPROMPT: 'beforeinstallprompt',\n APPINSTALLED: 'appinstalled'\n};\n\n\n/**\n * Returns one of the given pointer fallback event names in order of preference:\n * 1. pointerEventName\n * 2. msPointerEventName\n * 3. fallbackEventName\n * @param {string} pointerEventName\n * @param {string} msPointerEventName\n * @param {string} fallbackEventName\n * @return {string} The supported pointer or fallback (mouse or touch) event\n * name.\n * @private\n */\ngoog.events.getPointerFallbackEventName_ = function(\n pointerEventName, msPointerEventName, fallbackEventName) {\n if (goog.events.BrowserFeature.POINTER_EVENTS) {\n return pointerEventName;\n }\n if (goog.events.BrowserFeature.MSPOINTER_EVENTS) {\n return msPointerEventName;\n }\n return fallbackEventName;\n};\n\n\n/**\n * Constants for pointer event names that fall back to corresponding mouse event\n * names on unsupported platforms. These are intended to be drop-in replacements\n * for corresponding values in `goog.events.EventType`.\n * @enum {string}\n */\ngoog.events.PointerFallbackEventType = {\n POINTERDOWN: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERDOWN, goog.events.EventType.MSPOINTERDOWN,\n goog.events.EventType.MOUSEDOWN),\n POINTERUP: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERUP, goog.events.EventType.MSPOINTERUP,\n goog.events.EventType.MOUSEUP),\n POINTERCANCEL: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERCANCEL,\n goog.events.EventType.MSPOINTERCANCEL,\n // When falling back to mouse events, there is no MOUSECANCEL equivalent\n // of POINTERCANCEL. In this case POINTERUP already falls back to MOUSEUP\n // which represents both UP and CANCEL. POINTERCANCEL does not fall back\n // to MOUSEUP to prevent listening twice on the same event.\n goog.events.EventType.MOUSECANCEL),\n POINTERMOVE: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERMOVE, goog.events.EventType.MSPOINTERMOVE,\n goog.events.EventType.MOUSEMOVE),\n POINTEROVER: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTEROVER, goog.events.EventType.MSPOINTEROVER,\n goog.events.EventType.MOUSEOVER),\n POINTEROUT: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTEROUT, goog.events.EventType.MSPOINTEROUT,\n goog.events.EventType.MOUSEOUT),\n POINTERENTER: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERENTER, goog.events.EventType.MSPOINTERENTER,\n goog.events.EventType.MOUSEENTER),\n POINTERLEAVE: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERLEAVE, goog.events.EventType.MSPOINTERLEAVE,\n goog.events.EventType.MOUSELEAVE)\n};\n\n\n/**\n * Constants for pointer event names that fall back to corresponding touch event\n * names on unsupported platforms. These are intended to be drop-in replacements\n * for corresponding values in `goog.events.EventType`.\n * @enum {string}\n */\ngoog.events.PointerTouchFallbackEventType = {\n POINTERDOWN: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERDOWN, goog.events.EventType.MSPOINTERDOWN,\n goog.events.EventType.TOUCHSTART),\n POINTERUP: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERUP, goog.events.EventType.MSPOINTERUP,\n goog.events.EventType.TOUCHEND),\n POINTERCANCEL: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERCANCEL,\n goog.events.EventType.MSPOINTERCANCEL, goog.events.EventType.TOUCHCANCEL),\n POINTERMOVE: goog.events.getPointerFallbackEventName_(\n goog.events.EventType.POINTERMOVE, goog.events.EventType.MSPOINTERMOVE,\n goog.events.EventType.TOUCHMOVE)\n};\n\n\n/**\n * Mapping of mouse event names to underlying browser event names.\n * @typedef {{\n * MOUSEDOWN: string,\n * MOUSEUP: string,\n * MOUSECANCEL:string,\n * MOUSEMOVE:string,\n * MOUSEOVER:string,\n * MOUSEOUT:string,\n * MOUSEENTER:string,\n * MOUSELEAVE: string,\n * }}\n */\ngoog.events.MouseEvents;\n\n\n/**\n * An alias for `goog.events.EventType.MOUSE*` event types that is overridden by\n * corresponding `POINTER*` event types.\n * @const {!goog.events.MouseEvents}\n */\ngoog.events.PointerAsMouseEventType = {\n MOUSEDOWN: goog.events.PointerFallbackEventType.POINTERDOWN,\n MOUSEUP: goog.events.PointerFallbackEventType.POINTERUP,\n MOUSECANCEL: goog.events.PointerFallbackEventType.POINTERCANCEL,\n MOUSEMOVE: goog.events.PointerFallbackEventType.POINTERMOVE,\n MOUSEOVER: goog.events.PointerFallbackEventType.POINTEROVER,\n MOUSEOUT: goog.events.PointerFallbackEventType.POINTEROUT,\n MOUSEENTER: goog.events.PointerFallbackEventType.POINTERENTER,\n MOUSELEAVE: goog.events.PointerFallbackEventType.POINTERLEAVE\n};\n\n\n/**\n * An alias for `goog.events.EventType.MOUSE*` event types that continue to use\n * mouse events.\n * @const {!goog.events.MouseEvents}\n */\ngoog.events.MouseAsMouseEventType = {\n MOUSEDOWN: goog.events.EventType.MOUSEDOWN,\n MOUSEUP: goog.events.EventType.MOUSEUP,\n MOUSECANCEL: goog.events.EventType.MOUSECANCEL,\n MOUSEMOVE: goog.events.EventType.MOUSEMOVE,\n MOUSEOVER: goog.events.EventType.MOUSEOVER,\n MOUSEOUT: goog.events.EventType.MOUSEOUT,\n MOUSEENTER: goog.events.EventType.MOUSEENTER,\n MOUSELEAVE: goog.events.EventType.MOUSELEAVE\n};\n\n\n/**\n * An alias for `goog.events.EventType.TOUCH*` event types that is overridden by\n * corresponding `POINTER*` event types.\n * @enum {string}\n */\ngoog.events.PointerAsTouchEventType = {\n TOUCHCANCEL: goog.events.PointerTouchFallbackEventType.POINTERCANCEL,\n TOUCHEND: goog.events.PointerTouchFallbackEventType.POINTERUP,\n TOUCHMOVE: goog.events.PointerTouchFallbackEventType.POINTERMOVE,\n TOUCHSTART: goog.events.PointerTouchFallbackEventType.POINTERDOWN\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview An interface for a listenable JavaScript object.\n */\n\ngoog.provide('goog.events.Listenable');\ngoog.provide('goog.events.ListenableKey');\n/** @suppress {extraRequire} */\ngoog.require('goog.events.EventId');\n\ngoog.requireType('goog.events.EventLike');\n\n\n/**\n * A listenable interface. A listenable is an object with the ability\n * to dispatch/broadcast events to \"event listeners\" registered via\n * listen/listenOnce.\n *\n * The interface allows for an event propagation mechanism similar\n * to one offered by native browser event targets, such as\n * capture/bubble mechanism, stopping propagation, and preventing\n * default actions. Capture/bubble mechanism depends on the ancestor\n * tree constructed via `#getParentEventTarget`; this tree\n * must be directed acyclic graph. The meaning of default action(s)\n * in preventDefault is specific to a particular use case.\n *\n * Implementations that do not support capture/bubble or can not have\n * a parent listenable can simply not implement any ability to set the\n * parent listenable (and have `#getParentEventTarget` return\n * null).\n *\n * Implementation of this class can be used with or independently from\n * goog.events.\n *\n * Implementation must call `#addImplementation(implClass)`.\n *\n * @interface\n * @see goog.events\n * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html\n */\ngoog.events.Listenable = function() {};\n\n\n/**\n * An expando property to indicate that an object implements\n * goog.events.Listenable.\n *\n * See addImplementation/isImplementedBy.\n *\n * @type {string}\n * @const\n */\ngoog.events.Listenable.IMPLEMENTED_BY_PROP =\n 'closure_listenable_' + ((Math.random() * 1e6) | 0);\n\n\n/**\n * Marks a given class (constructor) as an implementation of\n * Listenable, so that we can query that fact at runtime. The class\n * must have already implemented the interface.\n * @param {function(new:goog.events.Listenable,...)} cls The class constructor.\n * The corresponding class must have already implemented the interface.\n */\ngoog.events.Listenable.addImplementation = function(cls) {\n cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;\n};\n\n\n/**\n * @param {Object} obj The object to check.\n * @return {boolean} Whether a given instance implements Listenable. The\n * class/superclass of the instance must call addImplementation.\n */\ngoog.events.Listenable.isImplementedBy = function(obj) {\n return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);\n};\n\n\n/**\n * Adds an event listener. A listener can only be added once to an\n * object and if it is added again the key for the listener is\n * returned. Note that if the existing listener is a one-off listener\n * (registered via listenOnce), it will no longer be a one-off\n * listener after a call to listen().\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n * method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n * (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.listen;\n\n\n/**\n * Adds an event listener that is removed automatically after the\n * listener fired once.\n *\n * If an existing listener already exists, listenOnce will do\n * nothing. In particular, if the listener was previously registered\n * via listen(), listenOnce() will not turn the listener into a\n * one-off listener. Similarly, if there is already an existing\n * one-off listener, listenOnce does not modify the listeners (it is\n * still a once listener).\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n * method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n * (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.listenOnce;\n\n\n/**\n * Removes an event listener which was added with listen() or listenOnce().\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback\n * method.\n * @param {boolean=} opt_useCapture Whether to fire in capture phase\n * (defaults to false).\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call\n * the listener.\n * @return {boolean} Whether any listener was removed.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.unlisten;\n\n\n/**\n * Removes an event listener which was added with listen() by the key\n * returned by listen().\n *\n * @param {!goog.events.ListenableKey} key The key returned by\n * listen() or listenOnce().\n * @return {boolean} Whether any listener was removed.\n */\ngoog.events.Listenable.prototype.unlistenByKey;\n\n\n/**\n * Dispatches an event (or event like object) and calls all listeners\n * listening for events of this type. The type of the event is decided by the\n * type property on the event object.\n *\n * If any of the listeners returns false OR calls preventDefault then this\n * function will return false. If one of the capture listeners calls\n * stopPropagation, then the bubble listeners won't fire.\n *\n * @param {goog.events.EventLike} e Event object.\n * @return {boolean} If anyone called preventDefault on the event object (or\n * if any of the listeners returns false) this will also return false.\n */\ngoog.events.Listenable.prototype.dispatchEvent;\n\n\n/**\n * Removes all listeners from this listenable. If type is specified,\n * it will only remove listeners of the particular type. otherwise all\n * registered listeners will be removed.\n *\n * @param {string=} opt_type Type of event to remove, default is to\n * remove all types.\n * @return {number} Number of listeners removed.\n */\ngoog.events.Listenable.prototype.removeAllListeners;\n\n\n/**\n * Returns the parent of this event target to use for capture/bubble\n * mechanism.\n *\n * NOTE(chrishenry): The name reflects the original implementation of\n * custom event target (`goog.events.EventTarget`). We decided\n * that changing the name is not worth it.\n *\n * @return {goog.events.Listenable} The parent EventTarget or null if\n * there is no parent.\n */\ngoog.events.Listenable.prototype.getParentEventTarget;\n\n\n/**\n * Fires all registered listeners in this listenable for the given\n * type and capture mode, passing them the given eventObject. This\n * does not perform actual capture/bubble. Only implementors of the\n * interface should be using this.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the\n * listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @param {EVENTOBJ} eventObject The event object to fire.\n * @return {boolean} Whether all listeners succeeded without\n * attempting to prevent default behavior. If any listener returns\n * false or called goog.events.Event#preventDefault, this returns\n * false.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.fireListeners;\n\n\n/**\n * Gets all listeners in this listenable for the given type and\n * capture mode.\n *\n * @param {string|!goog.events.EventId} type The type of the listeners to fire.\n * @param {boolean} capture The capture mode of the listeners to fire.\n * @return {!Array<!goog.events.ListenableKey>} An array of registered\n * listeners.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.getListeners;\n\n\n/**\n * Gets the goog.events.ListenableKey for the event or null if no such\n * listener is in use.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event\n * without the 'on' prefix.\n * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The\n * listener function to get.\n * @param {boolean} capture Whether the listener is a capturing listener.\n * @param {SCOPE=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n * @template SCOPE,EVENTOBJ\n */\ngoog.events.Listenable.prototype.getListener;\n\n\n/**\n * Whether there is any active listeners matching the specified\n * signature. If either the type or capture parameters are\n * unspecified, the function will match on the remaining criteria.\n *\n * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.\n * @param {boolean=} opt_capture Whether to check for capture or bubble\n * listeners.\n * @return {boolean} Whether there is any active listeners matching\n * the requested type and/or capture phase.\n * @template EVENTOBJ\n */\ngoog.events.Listenable.prototype.hasListener;\n\n\n\n/**\n * An interface that describes a single registered listener.\n * @interface\n */\ngoog.events.ListenableKey = function() {};\n\n\n/**\n * Counter used to create a unique key\n * @type {number}\n * @private\n */\ngoog.events.ListenableKey.counter_ = 0;\n\n\n/**\n * Reserves a key to be used for ListenableKey#key field.\n * @return {number} A number to be used to fill ListenableKey#key\n * field.\n */\ngoog.events.ListenableKey.reserveKey = function() {\n return ++goog.events.ListenableKey.counter_;\n};\n\n\n/**\n * The source event target.\n * @type {?Object|?goog.events.Listenable}\n */\ngoog.events.ListenableKey.prototype.src;\n\n\n/**\n * The event type the listener is listening to.\n * @type {string}\n */\ngoog.events.ListenableKey.prototype.type;\n\n\n/**\n * The listener function.\n * @type {function(?):?|{handleEvent:function(?):?}|null}\n */\ngoog.events.ListenableKey.prototype.listener;\n\n\n/**\n * Whether the listener works on capture phase.\n * @type {boolean}\n */\ngoog.events.ListenableKey.prototype.capture;\n\n\n/**\n * The 'this' object for the listener function's scope.\n * @type {Object|undefined}\n */\ngoog.events.ListenableKey.prototype.handler;\n\n\n/**\n * A globally unique number to identify the key.\n * @type {number}\n */\ngoog.events.ListenableKey.prototype.key;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Listener object.\n * @see ../demos/events.html\n */\n\ngoog.provide('goog.events.Listener');\n\ngoog.require('goog.events.ListenableKey');\n\n\n\n/**\n * Simple class that stores information about a listener\n * @param {function(?):?} listener Callback function.\n * @param {Function} proxy Wrapper for the listener that patches the event.\n * @param {EventTarget|goog.events.Listenable} src Source object for\n * the event.\n * @param {string} type Event type.\n * @param {boolean} capture Whether in capture or bubble phase.\n * @param {Object=} opt_handler Object in whose context to execute the callback.\n * @implements {goog.events.ListenableKey}\n * @constructor\n */\ngoog.events.Listener = function(\n listener, proxy, src, type, capture, opt_handler) {\n if (goog.events.Listener.ENABLE_MONITORING) {\n this.creationStack = new Error().stack;\n }\n\n /** @override */\n this.listener = listener;\n\n /**\n * A wrapper over the original listener. This is used solely to\n * handle native browser events (it is used to simulate the capture\n * phase and to patch the event object).\n * @type {Function}\n */\n this.proxy = proxy;\n\n /**\n * Object or node that callback is listening to\n * @type {EventTarget|goog.events.Listenable}\n */\n this.src = src;\n\n /**\n * The event type.\n * @const {string}\n */\n this.type = type;\n\n /**\n * Whether the listener is being called in the capture or bubble phase\n * @const {boolean}\n */\n this.capture = !!capture;\n\n /**\n * Optional object whose context to execute the listener in\n * @type {Object|undefined}\n */\n this.handler = opt_handler;\n\n /**\n * The key of the listener.\n * @const {number}\n * @override\n */\n this.key = goog.events.ListenableKey.reserveKey();\n\n /**\n * Whether to remove the listener after it has been called.\n * @type {boolean}\n */\n this.callOnce = false;\n\n /**\n * Whether the listener has been removed.\n * @type {boolean}\n */\n this.removed = false;\n};\n\n\n/**\n * @define {boolean} Whether to enable the monitoring of the\n * goog.events.Listener instances. Switching on the monitoring is only\n * recommended for debugging because it has a significant impact on\n * performance and memory usage. If switched off, the monitoring code\n * compiles down to 0 bytes.\n */\ngoog.events.Listener.ENABLE_MONITORING =\n goog.define('goog.events.Listener.ENABLE_MONITORING', false);\n\n\n/**\n * If monitoring the goog.events.Listener instances is enabled, stores the\n * creation stack trace of the Disposable instance.\n * @type {string}\n */\ngoog.events.Listener.prototype.creationStack;\n\n\n/**\n * Marks this listener as removed. This also remove references held by\n * this listener object (such as listener and event source).\n */\ngoog.events.Listener.prototype.markAsRemoved = function() {\n this.removed = true;\n this.listener = null;\n this.proxy = null;\n this.src = null;\n this.handler = null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A map of listeners that provides utility functions to\n * deal with listeners on an event target. Used by\n * `goog.events.EventTarget`.\n *\n * WARNING: Do not use this class from outside goog.events package.\n *\n */\n\ngoog.provide('goog.events.ListenerMap');\n\ngoog.require('goog.array');\ngoog.require('goog.events.Listener');\ngoog.require('goog.object');\n\n\n\n/**\n * Creates a new listener map.\n * @param {EventTarget|goog.events.Listenable} src The src object.\n * @constructor\n * @final\n */\ngoog.events.ListenerMap = function(src) {\n /** @type {EventTarget|goog.events.Listenable} */\n this.src = src;\n\n /**\n * Maps of event type to an array of listeners.\n * @type {!Object<string, !Array<!goog.events.Listener>>}\n */\n this.listeners = {};\n\n /**\n * The count of types in this map that have registered listeners.\n * @private {number}\n */\n this.typeCount_ = 0;\n};\n\n\n/**\n * @return {number} The count of event types in this map that actually\n * have registered listeners.\n */\ngoog.events.ListenerMap.prototype.getTypeCount = function() {\n return this.typeCount_;\n};\n\n\n/**\n * @return {number} Total number of registered listeners.\n */\ngoog.events.ListenerMap.prototype.getListenerCount = function() {\n var count = 0;\n for (var type in this.listeners) {\n count += this.listeners[type].length;\n }\n return count;\n};\n\n\n/**\n * Adds an event listener. A listener can only be added once to an\n * object and if it is added again the key for the listener is\n * returned.\n *\n * Note that a one-off listener will not change an existing listener,\n * if any. On the other hand a normal listener will change existing\n * one-off listener to become a normal listener.\n *\n * @param {string|!goog.events.EventId} type The listener event type.\n * @param {!Function} listener This listener callback method.\n * @param {boolean} callOnce Whether the listener is a one-off\n * listener.\n * @param {boolean=} opt_useCapture The capture mode of the listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {!goog.events.ListenableKey} Unique key for the listener.\n */\ngoog.events.ListenerMap.prototype.add = function(\n type, listener, callOnce, opt_useCapture, opt_listenerScope) {\n var typeStr = type.toString();\n var listenerArray = this.listeners[typeStr];\n if (!listenerArray) {\n listenerArray = this.listeners[typeStr] = [];\n this.typeCount_++;\n }\n\n var listenerObj;\n var index = goog.events.ListenerMap.findListenerIndex_(\n listenerArray, listener, opt_useCapture, opt_listenerScope);\n if (index > -1) {\n listenerObj = listenerArray[index];\n if (!callOnce) {\n // Ensure that, if there is an existing callOnce listener, it is no\n // longer a callOnce listener.\n listenerObj.callOnce = false;\n }\n } else {\n listenerObj = new goog.events.Listener(\n listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);\n listenerObj.callOnce = callOnce;\n listenerArray.push(listenerObj);\n }\n return listenerObj;\n};\n\n\n/**\n * Removes a matching listener.\n * @param {string|!goog.events.EventId} type The listener event type.\n * @param {!Function} listener This listener callback method.\n * @param {boolean=} opt_useCapture The capture mode of the listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {boolean} Whether any listener was removed.\n */\ngoog.events.ListenerMap.prototype.remove = function(\n type, listener, opt_useCapture, opt_listenerScope) {\n var typeStr = type.toString();\n if (!(typeStr in this.listeners)) {\n return false;\n }\n\n var listenerArray = this.listeners[typeStr];\n var index = goog.events.ListenerMap.findListenerIndex_(\n listenerArray, listener, opt_useCapture, opt_listenerScope);\n if (index > -1) {\n var listenerObj = listenerArray[index];\n listenerObj.markAsRemoved();\n goog.array.removeAt(listenerArray, index);\n if (listenerArray.length == 0) {\n delete this.listeners[typeStr];\n this.typeCount_--;\n }\n return true;\n }\n return false;\n};\n\n\n/**\n * Removes the given listener object.\n * @param {!goog.events.ListenableKey} listener The listener to remove.\n * @return {boolean} Whether the listener is removed.\n */\ngoog.events.ListenerMap.prototype.removeByKey = function(listener) {\n var type = listener.type;\n if (!(type in this.listeners)) {\n return false;\n }\n\n var removed = goog.array.remove(this.listeners[type], listener);\n if (removed) {\n /** @type {!goog.events.Listener} */ (listener).markAsRemoved();\n if (this.listeners[type].length == 0) {\n delete this.listeners[type];\n this.typeCount_--;\n }\n }\n return removed;\n};\n\n\n/**\n * Removes all listeners from this map. If opt_type is provided, only\n * listeners that match the given type are removed.\n * @param {string|!goog.events.EventId=} opt_type Type of event to remove.\n * @return {number} Number of listeners removed.\n */\ngoog.events.ListenerMap.prototype.removeAll = function(opt_type) {\n var typeStr = opt_type && opt_type.toString();\n var count = 0;\n for (var type in this.listeners) {\n if (!typeStr || type == typeStr) {\n var listenerArray = this.listeners[type];\n for (var i = 0; i < listenerArray.length; i++) {\n ++count;\n listenerArray[i].markAsRemoved();\n }\n delete this.listeners[type];\n this.typeCount_--;\n }\n }\n return count;\n};\n\n\n/**\n * Gets all listeners that match the given type and capture mode. The\n * returned array is a copy (but the listener objects are not).\n * @param {string|!goog.events.EventId} type The type of the listeners\n * to retrieve.\n * @param {boolean} capture The capture mode of the listeners to retrieve.\n * @return {!Array<!goog.events.ListenableKey>} An array of matching\n * listeners.\n */\ngoog.events.ListenerMap.prototype.getListeners = function(type, capture) {\n var listenerArray = this.listeners[type.toString()];\n var rv = [];\n if (listenerArray) {\n for (var i = 0; i < listenerArray.length; ++i) {\n var listenerObj = listenerArray[i];\n if (listenerObj.capture == capture) {\n rv.push(listenerObj);\n }\n }\n }\n return rv;\n};\n\n\n/**\n * Gets the goog.events.ListenableKey for the event or null if no such\n * listener is in use.\n *\n * @param {string|!goog.events.EventId} type The type of the listener\n * to retrieve.\n * @param {!Function} listener The listener function to get.\n * @param {boolean} capture Whether the listener is a capturing listener.\n * @param {Object=} opt_listenerScope Object in whose scope to call the\n * listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n */\ngoog.events.ListenerMap.prototype.getListener = function(\n type, listener, capture, opt_listenerScope) {\n var listenerArray = this.listeners[type.toString()];\n var i = -1;\n if (listenerArray) {\n i = goog.events.ListenerMap.findListenerIndex_(\n listenerArray, listener, capture, opt_listenerScope);\n }\n return i > -1 ? listenerArray[i] : null;\n};\n\n\n/**\n * Whether there is a matching listener. If either the type or capture\n * parameters are unspecified, the function will match on the\n * remaining criteria.\n *\n * @param {string|!goog.events.EventId=} opt_type The type of the listener.\n * @param {boolean=} opt_capture The capture mode of the listener.\n * @return {boolean} Whether there is an active listener matching\n * the requested type and/or capture phase.\n */\ngoog.events.ListenerMap.prototype.hasListener = function(\n opt_type, opt_capture) {\n var hasType = (opt_type !== undefined);\n var typeStr = hasType ? opt_type.toString() : '';\n var hasCapture = (opt_capture !== undefined);\n\n return goog.object.some(this.listeners, function(listenerArray, type) {\n for (var i = 0; i < listenerArray.length; ++i) {\n if ((!hasType || listenerArray[i].type == typeStr) &&\n (!hasCapture || listenerArray[i].capture == opt_capture)) {\n return true;\n }\n }\n\n return false;\n });\n};\n\n\n/**\n * Finds the index of a matching goog.events.Listener in the given\n * listenerArray.\n * @param {!Array<!goog.events.Listener>} listenerArray Array of listener.\n * @param {!Function} listener The listener function.\n * @param {boolean=} opt_useCapture The capture flag for the listener.\n * @param {Object=} opt_listenerScope The listener scope.\n * @return {number} The index of the matching listener within the\n * listenerArray.\n * @private\n */\ngoog.events.ListenerMap.findListenerIndex_ = function(\n listenerArray, listener, opt_useCapture, opt_listenerScope) {\n for (var i = 0; i < listenerArray.length; ++i) {\n var listenerObj = listenerArray[i];\n if (!listenerObj.removed && listenerObj.listener == listener &&\n listenerObj.capture == !!opt_useCapture &&\n listenerObj.handler == opt_listenerScope) {\n return i;\n }\n }\n return -1;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview An event manager for both native browser event\n * targets and custom JavaScript event targets\n * (`goog.events.Listenable`). This provides an abstraction\n * over browsers' event systems.\n *\n * It also provides a simulation of W3C event model's capture phase in\n * Internet Explorer (IE 8 and below). Caveat: the simulation does not\n * interact well with listeners registered directly on the elements\n * (bypassing goog.events) or even with listeners registered via\n * goog.events in a separate JS binary. In these cases, we provide\n * no ordering guarantees.\n *\n * The listeners will receive a \"patched\" event object. Such event object\n * contains normalized values for certain event properties that differs in\n * different browsers.\n *\n * Example usage:\n * <pre>\n * goog.events.listen(myNode, 'click', function(e) { alert('woo') });\n * goog.events.listen(myNode, 'mouseover', mouseHandler, true);\n * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);\n * goog.events.removeAll(myNode);\n * </pre>\n *\n * in IE and event object patching]\n *\n * @see ../demos/events.html\n * @see ../demos/event-propagation.html\n * @see ../demos/stopevent.html\n */\n\n// IMPLEMENTATION NOTES:\n// goog.events stores an auxiliary data structure on each EventTarget\n// source being listened on. This allows us to take advantage of GC,\n// having the data structure GC'd when the EventTarget is GC'd. This\n// GC behavior is equivalent to using W3C DOM Events directly.\n\ngoog.provide('goog.events');\ngoog.provide('goog.events.CaptureSimulationMode');\ngoog.provide('goog.events.Key');\ngoog.provide('goog.events.ListenableType');\n\ngoog.forwardDeclare('goog.debug.ErrorHandler');\ngoog.forwardDeclare('goog.events.EventWrapper');\ngoog.require('goog.asserts');\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.events.BrowserEvent');\ngoog.require('goog.events.BrowserFeature');\ngoog.require('goog.events.Listenable');\ngoog.require('goog.events.ListenerMap');\n\n\n/**\n * @typedef {number|goog.events.ListenableKey}\n */\ngoog.events.Key;\n\n\n/**\n * @typedef {EventTarget|goog.events.Listenable}\n */\ngoog.events.ListenableType;\n\n\n/**\n * Property name on a native event target for the listener map\n * associated with the event target.\n * @private @const {string}\n */\ngoog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);\n\n\n/**\n * String used to prepend to IE event types.\n * @const\n * @private\n */\ngoog.events.onString_ = 'on';\n\n\n/**\n * Map of computed \"on<eventname>\" strings for IE event types. Caching\n * this removes an extra object allocation in goog.events.listen which\n * improves IE6 performance.\n * @const\n * @dict\n * @private\n */\ngoog.events.onStringMap_ = {};\n\n\n/**\n * @enum {number} Different capture simulation mode for IE8-.\n */\ngoog.events.CaptureSimulationMode = {\n /**\n * Does not perform capture simulation. Will asserts in IE8- when you\n * add capture listeners.\n */\n OFF_AND_FAIL: 0,\n\n /**\n * Does not perform capture simulation, silently ignore capture\n * listeners.\n */\n OFF_AND_SILENT: 1,\n\n /**\n * Performs capture simulation.\n */\n ON: 2\n};\n\n\n/**\n * @define {number} The capture simulation mode for IE8-. By default,\n * this is ON.\n */\ngoog.events.CAPTURE_SIMULATION_MODE =\n goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);\n\n\n/**\n * Estimated count of total native listeners.\n * @private {number}\n */\ngoog.events.listenerCountEstimate_ = 0;\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target (such as a DOM element) or an object that has implemented\n * {@link goog.events.Listenable}. A listener can only be added once\n * to an object and if it is added again the key for the listener is\n * returned. Note that if the existing listener is a one-off listener\n * (registered via listenOnce), it will no longer be a one-off\n * listener after a call to listen().\n *\n * @param {EventTarget|goog.events.Listenable} src The node to listen\n * to events on.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}\n * listener Callback method, or an object with a handleEvent function.\n * WARNING: passing an Object is now softly deprecated.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.Key} Unique key for the listener.\n * @template T,EVENTOBJ\n */\ngoog.events.listen = function(src, type, listener, opt_options, opt_handler) {\n if (opt_options && opt_options.once) {\n return goog.events.listenOnce(\n src, type, listener, opt_options, opt_handler);\n }\n if (Array.isArray(type)) {\n for (var i = 0; i < type.length; i++) {\n goog.events.listen(src, type[i], listener, opt_options, opt_handler);\n }\n return null;\n }\n\n listener = goog.events.wrapListener(listener);\n if (goog.events.Listenable.isImplementedBy(src)) {\n var capture =\n goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n return src.listen(\n /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n opt_handler);\n } else {\n return goog.events.listen_(\n /** @type {!EventTarget} */ (src), type, listener,\n /* callOnce */ false, opt_options, opt_handler);\n }\n};\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target. A listener can only be added once to an object and if it\n * is added again the key for the listener is returned.\n *\n * Note that a one-off listener will not change an existing listener,\n * if any. On the other hand a normal listener will change existing\n * one-off listener to become a normal listener.\n *\n * @param {EventTarget} src The node to listen to events on.\n * @param {string|?goog.events.EventId<EVENTOBJ>} type Event type.\n * @param {!Function} listener Callback function.\n * @param {boolean} callOnce Whether the listener is a one-off\n * listener or otherwise.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.ListenableKey} Unique key for the listener.\n * @template EVENTOBJ\n * @private\n */\ngoog.events.listen_ = function(\n src, type, listener, callOnce, opt_options, opt_handler) {\n if (!type) {\n throw new Error('Invalid event type');\n }\n\n var capture =\n goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {\n if (goog.events.CAPTURE_SIMULATION_MODE ==\n goog.events.CaptureSimulationMode.OFF_AND_FAIL) {\n goog.asserts.fail('Can not register capture listener in IE8-.');\n return null;\n } else if (\n goog.events.CAPTURE_SIMULATION_MODE ==\n goog.events.CaptureSimulationMode.OFF_AND_SILENT) {\n return null;\n }\n }\n\n var listenerMap = goog.events.getListenerMap_(src);\n if (!listenerMap) {\n src[goog.events.LISTENER_MAP_PROP_] = listenerMap =\n new goog.events.ListenerMap(src);\n }\n\n var listenerObj = /** @type {goog.events.Listener} */ (\n listenerMap.add(type, listener, callOnce, capture, opt_handler));\n\n // If the listenerObj already has a proxy, it has been set up\n // previously. We simply return.\n if (listenerObj.proxy) {\n return listenerObj;\n }\n\n var proxy = goog.events.getProxy();\n listenerObj.proxy = proxy;\n\n proxy.src = src;\n proxy.listener = listenerObj;\n\n // Attach the proxy through the browser's API\n if (src.addEventListener) {\n // Don't pass an object as `capture` if the browser doesn't support that.\n if (!goog.events.BrowserFeature.PASSIVE_EVENTS) {\n opt_options = capture;\n }\n // Don't break tests that expect a boolean.\n if (opt_options === undefined) opt_options = false;\n src.addEventListener(type.toString(), proxy, opt_options);\n } else if (src.attachEvent) {\n // The else if above used to be an unconditional else. It would call\n // attachEvent come gws or high water. This would sometimes throw an\n // exception on IE11, spoiling the day of some callers. The previous\n // incarnation of this code, from 2007, indicates that it replaced an\n // earlier still version that caused excess allocations on IE6.\n src.attachEvent(goog.events.getOnString_(type.toString()), proxy);\n } else if (src.addListener && src.removeListener) {\n // In IE, MediaQueryList uses addListener() insteadd of addEventListener. In\n // Safari, there is no global for the MediaQueryList constructor, so we just\n // check whether the object \"looks like\" MediaQueryList.\n goog.asserts.assert(\n type === 'change', 'MediaQueryList only has a change event');\n src.addListener(proxy);\n } else {\n throw new Error('addEventListener and attachEvent are unavailable.');\n }\n\n goog.events.listenerCountEstimate_++;\n return listenerObj;\n};\n\n\n/**\n * Helper function for returning a proxy function.\n * @return {!Function} A new or reused function object.\n */\ngoog.events.getProxy = function() {\n var proxyCallbackFunction = goog.events.handleBrowserEvent_;\n // Use a local var f to prevent one allocation.\n var f =\n goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ? function(eventObject) {\n return proxyCallbackFunction.call(f.src, f.listener, eventObject);\n } : function(eventObject) {\n var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);\n // NOTE(chrishenry): In IE, we hack in a capture phase. However, if\n // there is inline event handler which tries to prevent default (for\n // example <a href=\"...\" onclick=\"return false\">...</a>) in a\n // descendant element, the prevent default will be overridden\n // by this listener if this listener were to return true. Hence, we\n // return undefined.\n if (!v) return v;\n };\n return f;\n};\n\n\n/**\n * Adds an event listener for a specific event on a native event\n * target (such as a DOM element) or an object that has implemented\n * {@link goog.events.Listenable}. After the event has fired the event\n * listener is removed from the target.\n *\n * If an existing listener already exists, listenOnce will do\n * nothing. In particular, if the listener was previously registered\n * via listen(), listenOnce() will not turn the listener into a\n * one-off listener. Similarly, if there is already an existing\n * one-off listener, listenOnce does not modify the listeners (it is\n * still a once listener).\n *\n * @param {EventTarget|goog.events.Listenable} src The node to listen\n * to events on.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}\n * listener Callback method.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.Key} Unique key for the listener.\n * @template T,EVENTOBJ\n */\ngoog.events.listenOnce = function(\n src, type, listener, opt_options, opt_handler) {\n if (Array.isArray(type)) {\n for (var i = 0; i < type.length; i++) {\n goog.events.listenOnce(src, type[i], listener, opt_options, opt_handler);\n }\n return null;\n }\n\n listener = goog.events.wrapListener(listener);\n if (goog.events.Listenable.isImplementedBy(src)) {\n var capture =\n goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n return src.listenOnce(\n /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n opt_handler);\n } else {\n return goog.events.listen_(\n /** @type {!EventTarget} */ (src), type, listener,\n /* callOnce */ true, opt_options, opt_handler);\n }\n};\n\n\n/**\n * Adds an event listener with a specific event wrapper on a DOM Node or an\n * object that has implemented {@link goog.events.Listenable}. A listener can\n * only be added once to an object.\n *\n * @param {EventTarget|goog.events.Listenable} src The target to\n * listen to events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener\n * Callback method, or an object with a handleEvent function.\n * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to\n * false).\n * @param {T=} opt_handler Element in whose scope to call the listener.\n * @template T\n */\ngoog.events.listenWithWrapper = function(\n src, wrapper, listener, opt_capt, opt_handler) {\n wrapper.listen(src, listener, opt_capt, opt_handler);\n};\n\n\n/**\n * Removes an event listener which was added with listen().\n *\n * @param {EventTarget|goog.events.Listenable} src The target to stop\n * listening to events on.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type or array of event types to unlisten to.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener The\n * listener function to remove.\n * @param {(boolean|!EventListenerOptions)=} opt_options\n * whether the listener is fired during the capture or bubble phase of the\n * event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {?boolean} indicating whether the listener was there to remove.\n * @template EVENTOBJ\n */\ngoog.events.unlisten = function(src, type, listener, opt_options, opt_handler) {\n if (Array.isArray(type)) {\n for (var i = 0; i < type.length; i++) {\n goog.events.unlisten(src, type[i], listener, opt_options, opt_handler);\n }\n return null;\n }\n var capture =\n goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n\n listener = goog.events.wrapListener(listener);\n if (goog.events.Listenable.isImplementedBy(src)) {\n return src.unlisten(\n /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n opt_handler);\n }\n\n if (!src) {\n // TODO(chrishenry): We should tighten the API to only accept\n // non-null objects, or add an assertion here.\n return false;\n }\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (src));\n if (listenerMap) {\n var listenerObj = listenerMap.getListener(\n /** @type {string|!goog.events.EventId} */ (type), listener, capture,\n opt_handler);\n if (listenerObj) {\n return goog.events.unlistenByKey(listenerObj);\n }\n }\n\n return false;\n};\n\n\n/**\n * Removes an event listener which was added with listen() by the key\n * returned by listen().\n *\n * @param {goog.events.Key} key The key returned by listen() for this\n * event listener.\n * @return {boolean} indicating whether the listener was there to remove.\n */\ngoog.events.unlistenByKey = function(key) {\n // TODO(chrishenry): Remove this check when tests that rely on this\n // are fixed.\n if (typeof key === 'number') {\n return false;\n }\n\n var listener = key;\n if (!listener || listener.removed) {\n return false;\n }\n\n var src = listener.src;\n if (goog.events.Listenable.isImplementedBy(src)) {\n return /** @type {!goog.events.Listenable} */ (src).unlistenByKey(listener);\n }\n\n var type = listener.type;\n var proxy = listener.proxy;\n if (src.removeEventListener) {\n src.removeEventListener(type, proxy, listener.capture);\n } else if (src.detachEvent) {\n src.detachEvent(goog.events.getOnString_(type), proxy);\n } else if (src.addListener && src.removeListener) {\n src.removeListener(proxy);\n }\n goog.events.listenerCountEstimate_--;\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (src));\n // TODO(chrishenry): Try to remove this conditional and execute the\n // first branch always. This should be safe.\n if (listenerMap) {\n listenerMap.removeByKey(listener);\n if (listenerMap.getTypeCount() == 0) {\n // Null the src, just because this is simple to do (and useful\n // for IE <= 7).\n listenerMap.src = null;\n // We don't use delete here because IE does not allow delete\n // on a window object.\n src[goog.events.LISTENER_MAP_PROP_] = null;\n }\n } else {\n /** @type {!goog.events.Listener} */ (listener).markAsRemoved();\n }\n\n return true;\n};\n\n\n/**\n * Removes an event listener which was added with listenWithWrapper().\n *\n * @param {EventTarget|goog.events.Listenable} src The target to stop\n * listening to events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener The\n * listener function to remove.\n * @param {boolean=} opt_capt In DOM-compliant browsers, this determines\n * whether the listener is fired during the capture or bubble phase of the\n * event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n */\ngoog.events.unlistenWithWrapper = function(\n src, wrapper, listener, opt_capt, opt_handler) {\n wrapper.unlisten(src, listener, opt_capt, opt_handler);\n};\n\n\n/**\n * Removes all listeners from an object. You can also optionally\n * remove listeners of a particular type.\n *\n * @param {Object|undefined} obj Object to remove listeners from. Must be an\n * EventTarget or a goog.events.Listenable.\n * @param {string|!goog.events.EventId=} opt_type Type of event to remove.\n * Default is all types.\n * @return {number} Number of listeners removed.\n */\ngoog.events.removeAll = function(obj, opt_type) {\n // TODO(chrishenry): Change the type of obj to\n // (!EventTarget|!goog.events.Listenable).\n\n if (!obj) {\n return 0;\n }\n\n if (goog.events.Listenable.isImplementedBy(obj)) {\n return /** @type {?} */ (obj).removeAllListeners(opt_type);\n }\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (obj));\n if (!listenerMap) {\n return 0;\n }\n\n var count = 0;\n var typeStr = opt_type && opt_type.toString();\n for (var type in listenerMap.listeners) {\n if (!typeStr || type == typeStr) {\n // Clone so that we don't need to worry about unlistenByKey\n // changing the content of the ListenerMap.\n var listeners = listenerMap.listeners[type].concat();\n for (var i = 0; i < listeners.length; ++i) {\n if (goog.events.unlistenByKey(listeners[i])) {\n ++count;\n }\n }\n }\n }\n return count;\n};\n\n\n/**\n * Gets the listeners for a given object, type and capture phase.\n *\n * @param {Object} obj Object to get listeners for.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Capture phase?.\n * @return {!Array<!goog.events.Listener>} Array of listener objects.\n */\ngoog.events.getListeners = function(obj, type, capture) {\n if (goog.events.Listenable.isImplementedBy(obj)) {\n return /** @type {!goog.events.Listenable} */ (obj).getListeners(\n type, capture);\n } else {\n if (!obj) {\n // TODO(chrishenry): We should tighten the API to accept\n // !EventTarget|goog.events.Listenable, and add an assertion here.\n return [];\n }\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (obj));\n return listenerMap ? listenerMap.getListeners(type, capture) : [];\n }\n};\n\n\n/**\n * Gets the goog.events.Listener for the event or null if no such listener is\n * in use.\n *\n * @param {EventTarget|goog.events.Listenable} src The target from\n * which to get listeners.\n * @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.\n * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The\n * listener function to get.\n * @param {boolean=} opt_capt In DOM-compliant browsers, this determines\n * whether the listener is fired during the\n * capture or bubble phase of the event.\n * @param {Object=} opt_handler Element in whose scope to call the listener.\n * @return {goog.events.ListenableKey} the found listener or null if not found.\n * @template EVENTOBJ\n */\ngoog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {\n // TODO(chrishenry): Change type from ?string to string, or add assertion.\n type = /** @type {string} */ (type);\n listener = goog.events.wrapListener(listener);\n var capture = !!opt_capt;\n if (goog.events.Listenable.isImplementedBy(src)) {\n return src.getListener(type, listener, capture, opt_handler);\n }\n\n if (!src) {\n // TODO(chrishenry): We should tighten the API to only accept\n // non-null objects, or add an assertion here.\n return null;\n }\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (src));\n if (listenerMap) {\n return listenerMap.getListener(type, listener, capture, opt_handler);\n }\n return null;\n};\n\n\n/**\n * Returns whether an event target has any active listeners matching the\n * specified signature. If either the type or capture parameters are\n * unspecified, the function will match on the remaining criteria.\n *\n * @param {EventTarget|goog.events.Listenable} obj Target to get\n * listeners for.\n * @param {string|!goog.events.EventId=} opt_type Event type.\n * @param {boolean=} opt_capture Whether to check for capture or bubble-phase\n * listeners.\n * @return {boolean} Whether an event target has one or more listeners matching\n * the requested type and/or capture phase.\n */\ngoog.events.hasListener = function(obj, opt_type, opt_capture) {\n if (goog.events.Listenable.isImplementedBy(obj)) {\n return obj.hasListener(opt_type, opt_capture);\n }\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {!EventTarget} */ (obj));\n return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);\n};\n\n\n/**\n * Provides a nice string showing the normalized event objects public members\n * @param {Object} e Event Object.\n * @return {string} String of the public members of the normalized event object.\n */\ngoog.events.expose = function(e) {\n var str = [];\n for (var key in e) {\n if (e[key] && e[key].id) {\n str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');\n } else {\n str.push(key + ' = ' + e[key]);\n }\n }\n return str.join('\\n');\n};\n\n\n/**\n * Returns a string with on prepended to the specified type. This is used for IE\n * which expects \"on\" to be prepended. This function caches the string in order\n * to avoid extra allocations in steady state.\n * @param {string} type Event type.\n * @return {string} The type string with 'on' prepended.\n * @private\n */\ngoog.events.getOnString_ = function(type) {\n if (type in goog.events.onStringMap_) {\n return goog.events.onStringMap_[type];\n }\n return goog.events.onStringMap_[type] = goog.events.onString_ + type;\n};\n\n\n/**\n * Fires an object's listeners of a particular type and phase\n *\n * @param {Object} obj Object whose listeners to call.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Which event phase.\n * @param {Object} eventObject Event object to be passed to listener.\n * @return {boolean} True if all listeners returned true else false.\n */\ngoog.events.fireListeners = function(obj, type, capture, eventObject) {\n if (goog.events.Listenable.isImplementedBy(obj)) {\n return /** @type {!goog.events.Listenable} */ (obj).fireListeners(\n type, capture, eventObject);\n }\n\n return goog.events.fireListeners_(obj, type, capture, eventObject);\n};\n\n\n/**\n * Fires an object's listeners of a particular type and phase.\n * @param {Object} obj Object whose listeners to call.\n * @param {string|!goog.events.EventId} type Event type.\n * @param {boolean} capture Which event phase.\n * @param {Object} eventObject Event object to be passed to listener.\n * @return {boolean} True if all listeners returned true else false.\n * @private\n */\ngoog.events.fireListeners_ = function(obj, type, capture, eventObject) {\n /** @type {boolean} */\n var retval = true;\n\n var listenerMap = goog.events.getListenerMap_(\n /** @type {EventTarget} */ (obj));\n if (listenerMap) {\n // TODO(chrishenry): Original code avoids array creation when there\n // is no listener, so we do the same. If this optimization turns\n // out to be not required, we can replace this with\n // listenerMap.getListeners(type, capture) instead, which is simpler.\n var listenerArray = listenerMap.listeners[type.toString()];\n if (listenerArray) {\n listenerArray = listenerArray.concat();\n for (var i = 0; i < listenerArray.length; i++) {\n var listener = listenerArray[i];\n // We might not have a listener if the listener was removed.\n if (listener && listener.capture == capture && !listener.removed) {\n var result = goog.events.fireListener(listener, eventObject);\n retval = retval && (result !== false);\n }\n }\n }\n }\n return retval;\n};\n\n\n/**\n * Fires a listener with a set of arguments\n *\n * @param {goog.events.Listener} listener The listener object to call.\n * @param {Object} eventObject The event object to pass to the listener.\n * @return {*} Result of listener.\n */\ngoog.events.fireListener = function(listener, eventObject) {\n var listenerFn = listener.listener;\n var listenerHandler = listener.handler || listener.src;\n\n if (listener.callOnce) {\n goog.events.unlistenByKey(listener);\n }\n return listenerFn.call(listenerHandler, eventObject);\n};\n\n\n/**\n * Gets the total number of listeners currently in the system.\n * @return {number} Number of listeners.\n * @deprecated This returns estimated count, now that Closure no longer\n * stores a central listener registry. We still return an estimation\n * to keep existing listener-related tests passing. In the near future,\n * this function will be removed.\n */\ngoog.events.getTotalListenerCount = function() {\n return goog.events.listenerCountEstimate_;\n};\n\n\n/**\n * Dispatches an event (or event like object) and calls all listeners\n * listening for events of this type. The type of the event is decided by the\n * type property on the event object.\n *\n * If any of the listeners returns false OR calls preventDefault then this\n * function will return false. If one of the capture listeners calls\n * stopPropagation, then the bubble listeners won't fire.\n *\n * @param {goog.events.Listenable} src The event target.\n * @param {goog.events.EventLike} e Event object.\n * @return {boolean} If anyone called preventDefault on the event object (or\n * if any of the handlers returns false) this will also return false.\n * If there are no handlers, or if all handlers return true, this returns\n * true.\n */\ngoog.events.dispatchEvent = function(src, e) {\n goog.asserts.assert(\n goog.events.Listenable.isImplementedBy(src),\n 'Can not use goog.events.dispatchEvent with ' +\n 'non-goog.events.Listenable instance.');\n return src.dispatchEvent(e);\n};\n\n\n/**\n * Installs exception protection for the browser event entry point using the\n * given error handler.\n *\n * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to\n * protect the entry point.\n */\ngoog.events.protectBrowserEventEntryPoint = function(errorHandler) {\n goog.events.handleBrowserEvent_ =\n errorHandler.protectEntryPoint(goog.events.handleBrowserEvent_);\n};\n\n\n/**\n * Handles an event and dispatches it to the correct listeners. This\n * function is a proxy for the real listener the user specified.\n *\n * @param {goog.events.Listener} listener The listener object.\n * @param {Event=} opt_evt Optional event object that gets passed in via the\n * native event handlers.\n * @return {*} Result of the event handler.\n * @this {EventTarget} The object or Element that fired the event.\n * @private\n */\ngoog.events.handleBrowserEvent_ = function(listener, opt_evt) {\n if (listener.removed) {\n return true;\n }\n\n // Synthesize event propagation if the browser does not support W3C\n // event model.\n if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {\n var ieEvent = opt_evt ||\n /** @type {Event} */ (goog.getObjectByName('window.event'));\n var evt = new goog.events.BrowserEvent(ieEvent, this);\n /** @type {*} */\n var retval = true;\n\n if (goog.events.CAPTURE_SIMULATION_MODE ==\n goog.events.CaptureSimulationMode.ON) {\n // If we have not marked this event yet, we should perform capture\n // simulation.\n if (!goog.events.isMarkedIeEvent_(ieEvent)) {\n goog.events.markIeEvent_(ieEvent);\n\n var ancestors = [];\n for (var parent = evt.currentTarget; parent;\n parent = parent.parentNode) {\n ancestors.push(parent);\n }\n\n // Fire capture listeners.\n var type = listener.type;\n for (var i = ancestors.length - 1;\n !evt.hasPropagationStopped() && i >= 0; i--) {\n evt.currentTarget = ancestors[i];\n var result =\n goog.events.fireListeners_(ancestors[i], type, true, evt);\n retval = retval && result;\n }\n\n // Fire bubble listeners.\n //\n // We can technically rely on IE to perform bubble event\n // propagation. However, it turns out that IE fires events in\n // opposite order of attachEvent registration, which broke\n // some code and tests that rely on the order. (While W3C DOM\n // Level 2 Events TR leaves the event ordering unspecified,\n // modern browsers and W3C DOM Level 3 Events Working Draft\n // actually specify the order as the registration order.)\n for (var i = 0; !evt.hasPropagationStopped() && i < ancestors.length;\n i++) {\n evt.currentTarget = ancestors[i];\n var result =\n goog.events.fireListeners_(ancestors[i], type, false, evt);\n retval = retval && result;\n }\n }\n } else {\n retval = goog.events.fireListener(listener, evt);\n }\n return retval;\n }\n\n // Otherwise, simply fire the listener.\n return goog.events.fireListener(\n listener, new goog.events.BrowserEvent(opt_evt, this));\n};\n\n\n/**\n * This is used to mark the IE event object so we do not do the Closure pass\n * twice for a bubbling event.\n * @param {Event} e The IE browser event.\n * @private\n */\ngoog.events.markIeEvent_ = function(e) {\n // Only the keyCode and the returnValue can be changed. We use keyCode for\n // non keyboard events.\n // event.returnValue is a bit more tricky. It is undefined by default. A\n // boolean false prevents the default action. In a window.onbeforeunload and\n // the returnValue is non undefined it will be alerted. However, we will only\n // modify the returnValue for keyboard events. We can get a problem if non\n // closure events sets the keyCode or the returnValue\n\n var useReturnValue = false;\n\n if (e.keyCode == 0) {\n // We cannot change the keyCode in case that srcElement is input[type=file].\n // We could test that that is the case but that would allocate 3 objects.\n // If we use try/catch we will only allocate extra objects in the case of a\n // failure.\n\n try {\n e.keyCode = -1;\n return;\n } catch (ex) {\n useReturnValue = true;\n }\n }\n\n if (useReturnValue ||\n /** @type {boolean|undefined} */ (e.returnValue) == undefined) {\n e.returnValue = true;\n }\n};\n\n\n/**\n * This is used to check if an IE event has already been handled by the Closure\n * system so we do not do the Closure pass twice for a bubbling event.\n * @param {Event} e The IE browser event.\n * @return {boolean} True if the event object has been marked.\n * @private\n */\ngoog.events.isMarkedIeEvent_ = function(e) {\n return e.keyCode < 0 || e.returnValue != undefined;\n};\n\n\n/**\n * Counter to create unique event ids.\n * @private {number}\n */\ngoog.events.uniqueIdCounter_ = 0;\n\n\n/**\n * Creates a unique event id.\n *\n * @param {string} identifier The identifier.\n * @return {string} A unique identifier.\n * @idGenerator {unique}\n */\ngoog.events.getUniqueId = function(identifier) {\n return identifier + '_' + goog.events.uniqueIdCounter_++;\n};\n\n\n/**\n * @param {EventTarget} src The source object.\n * @return {goog.events.ListenerMap} A listener map for the given\n * source object, or null if none exists.\n * @private\n */\ngoog.events.getListenerMap_ = function(src) {\n var listenerMap = src[goog.events.LISTENER_MAP_PROP_];\n // IE serializes the property as well (e.g. when serializing outer\n // HTML). So we must check that the value is of the correct type.\n return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;\n};\n\n\n/**\n * Expando property for listener function wrapper for Object with\n * handleEvent.\n * @private @const {string}\n */\ngoog.events.LISTENER_WRAPPER_PROP_ =\n '__closure_events_fn_' + ((Math.random() * 1e9) >>> 0);\n\n\n/**\n * @param {Object|Function} listener The listener function or an\n * object that contains handleEvent method.\n * @return {!Function} Either the original function or a function that\n * calls obj.handleEvent. If the same listener is passed to this\n * function more than once, the same function is guaranteed to be\n * returned.\n */\ngoog.events.wrapListener = function(listener) {\n goog.asserts.assert(listener, 'Listener can not be null.');\n\n if (goog.isFunction(listener)) {\n return listener;\n }\n\n goog.asserts.assert(\n listener.handleEvent, 'An object listener must have handleEvent method.');\n if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {\n listener[goog.events.LISTENER_WRAPPER_PROP_] = function(e) {\n return /** @type {?} */ (listener).handleEvent(e);\n };\n }\n return listener[goog.events.LISTENER_WRAPPER_PROP_];\n};\n\n\n// Register the browser event handler as an entry point, so that\n// it can be monitored for exception handling, etc.\ngoog.debug.entryPointRegistry.register(\n /**\n * @param {function(!Function): !Function} transformer The transforming\n * function.\n */\n function(transformer) {\n goog.events.handleBrowserEvent_ =\n transformer(goog.events.handleBrowserEvent_);\n });\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A disposable implementation of a custom\n * listenable/event target. See also: documentation for\n * `goog.events.Listenable`.\n *\n * @see ../demos/eventtarget.html\n * @see goog.events.Listenable\n */\n\ngoog.provide('goog.events.EventTarget');\n\ngoog.require('goog.Disposable');\ngoog.require('goog.asserts');\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.Listenable');\ngoog.require('goog.events.ListenerMap');\ngoog.require('goog.object');\n\n\n\n/**\n * An implementation of `goog.events.Listenable` with full W3C\n * EventTarget-like support (capture/bubble mechanism, stopping event\n * propagation, preventing default actions).\n *\n * You may subclass this class to turn your class into a Listenable.\n *\n * Unless propagation is stopped, an event dispatched by an\n * EventTarget will bubble to the parent returned by\n * `getParentEventTarget`. To set the parent, call\n * `setParentEventTarget`. Subclasses that don't support\n * changing the parent can override the setter to throw an error.\n *\n * Example usage:\n * <pre>\n * var source = new goog.events.EventTarget();\n * function handleEvent(e) {\n * alert('Type: ' + e.type + '; Target: ' + e.target);\n * }\n * source.listen('foo', handleEvent);\n * // Or: goog.events.listen(source, 'foo', handleEvent);\n * ...\n * source.dispatchEvent('foo'); // will call handleEvent\n * ...\n * source.unlisten('foo', handleEvent);\n * // Or: goog.events.unlisten(source, 'foo', handleEvent);\n * </pre>\n *\n * @constructor\n * @extends {goog.Disposable}\n * @implements {goog.events.Listenable}\n */\ngoog.events.EventTarget = function() {\n goog.Disposable.call(this);\n\n /**\n * Maps of event type to an array of listeners.\n * @private {!goog.events.ListenerMap}\n */\n this.eventTargetListeners_ = new goog.events.ListenerMap(this);\n\n /**\n * The object to use for event.target. Useful when mixing in an\n * EventTarget to another object.\n * @private {!Object}\n */\n this.actualEventTarget_ = this;\n\n /**\n * Parent event target, used during event bubbling.\n *\n * TODO(chrishenry): Change this to goog.events.Listenable. This\n * currently breaks people who expect getParentEventTarget to return\n * goog.events.EventTarget.\n *\n * @private {?goog.events.EventTarget}\n */\n this.parentEventTarget_ = null;\n};\ngoog.inherits(goog.events.EventTarget, goog.Disposable);\ngoog.events.Listenable.addImplementation(goog.events.EventTarget);\n\n\n/**\n * An artificial cap on the number of ancestors you can have. This is mainly\n * for loop detection.\n * @const {number}\n * @private\n */\ngoog.events.EventTarget.MAX_ANCESTORS_ = 1000;\n\n\n/**\n * Returns the parent of this event target to use for bubbling.\n *\n * @return {goog.events.EventTarget} The parent EventTarget or null if\n * there is no parent.\n * @override\n */\ngoog.events.EventTarget.prototype.getParentEventTarget = function() {\n return this.parentEventTarget_;\n};\n\n\n/**\n * Sets the parent of this event target to use for capture/bubble\n * mechanism.\n * @param {goog.events.EventTarget} parent Parent listenable (null if none).\n */\ngoog.events.EventTarget.prototype.setParentEventTarget = function(parent) {\n this.parentEventTarget_ = parent;\n};\n\n\n/**\n * Adds an event listener to the event target. The same handler can only be\n * added once per the type. Even if you add the same handler multiple times\n * using the same type then it will only be called once when the event is\n * dispatched.\n *\n * @param {string|!goog.events.EventId} type The type of the event to listen for\n * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function\n * to handle the event. The handler can also be an object that implements\n * the handleEvent method which takes the event object as argument.\n * @param {boolean=} opt_capture In DOM-compliant browsers, this determines\n * whether the listener is fired during the capture or bubble phase\n * of the event.\n * @param {Object=} opt_handlerScope Object in whose scope to call\n * the listener.\n * @deprecated Use `#listen` instead, when possible. Otherwise, use\n * `goog.events.listen` if you are passing Object\n * (instead of Function) as handler.\n */\ngoog.events.EventTarget.prototype.addEventListener = function(\n type, handler, opt_capture, opt_handlerScope) {\n goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);\n};\n\n\n/**\n * Removes an event listener from the event target. The handler must be the\n * same object as the one added. If the handler has not been added then\n * nothing is done.\n *\n * @param {string} type The type of the event to listen for.\n * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function\n * to handle the event. The handler can also be an object that implements\n * the handleEvent method which takes the event object as argument.\n * @param {boolean=} opt_capture In DOM-compliant browsers, this determines\n * whether the listener is fired during the capture or bubble phase\n * of the event.\n * @param {Object=} opt_handlerScope Object in whose scope to call\n * the listener.\n * @deprecated Use `#unlisten` instead, when possible. Otherwise, use\n * `goog.events.unlisten` if you are passing Object\n * (instead of Function) as handler.\n */\ngoog.events.EventTarget.prototype.removeEventListener = function(\n type, handler, opt_capture, opt_handlerScope) {\n goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.dispatchEvent = function(e) {\n this.assertInitialized_();\n\n var ancestorsTree, ancestor = this.getParentEventTarget();\n if (ancestor) {\n ancestorsTree = [];\n var ancestorCount = 1;\n for (; ancestor; ancestor = ancestor.getParentEventTarget()) {\n ancestorsTree.push(ancestor);\n goog.asserts.assert(\n (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),\n 'infinite loop');\n }\n }\n\n return goog.events.EventTarget.dispatchEventInternal_(\n this.actualEventTarget_, e, ancestorsTree);\n};\n\n\n/**\n * Removes listeners from this object. Classes that extend EventTarget may\n * need to override this method in order to remove references to DOM Elements\n * and additional listeners.\n * @override\n * @protected\n */\ngoog.events.EventTarget.prototype.disposeInternal = function() {\n goog.events.EventTarget.superClass_.disposeInternal.call(this);\n\n this.removeAllListeners();\n this.parentEventTarget_ = null;\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.listen = function(\n type, listener, opt_useCapture, opt_listenerScope) {\n this.assertInitialized_();\n return this.eventTargetListeners_.add(\n String(type), listener, false /* callOnce */, opt_useCapture,\n opt_listenerScope);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.listenOnce = function(\n type, listener, opt_useCapture, opt_listenerScope) {\n return this.eventTargetListeners_.add(\n String(type), listener, true /* callOnce */, opt_useCapture,\n opt_listenerScope);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.unlisten = function(\n type, listener, opt_useCapture, opt_listenerScope) {\n return this.eventTargetListeners_.remove(\n String(type), listener, opt_useCapture, opt_listenerScope);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.unlistenByKey = function(key) {\n return this.eventTargetListeners_.removeByKey(key);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {\n // TODO(chrishenry): Previously, removeAllListeners can be called on\n // uninitialized EventTarget, so we preserve that behavior. We\n // should remove this when usages that rely on that fact are purged.\n if (!this.eventTargetListeners_) {\n return 0;\n }\n return this.eventTargetListeners_.removeAll(opt_type);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.fireListeners = function(\n type, capture, eventObject) {\n // TODO(chrishenry): Original code avoids array creation when there\n // is no listener, so we do the same. If this optimization turns\n // out to be not required, we can replace this with\n // getListeners(type, capture) instead, which is simpler.\n var listenerArray = this.eventTargetListeners_.listeners[String(type)];\n if (!listenerArray) {\n return true;\n }\n listenerArray = listenerArray.concat();\n\n var rv = true;\n for (var i = 0; i < listenerArray.length; ++i) {\n var listener = listenerArray[i];\n // We might not have a listener if the listener was removed.\n if (listener && !listener.removed && listener.capture == capture) {\n var listenerFn = listener.listener;\n var listenerHandler = listener.handler || listener.src;\n\n if (listener.callOnce) {\n this.unlistenByKey(listener);\n }\n rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;\n }\n }\n\n return rv && !eventObject.defaultPrevented;\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.getListeners = function(type, capture) {\n return this.eventTargetListeners_.getListeners(String(type), capture);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.getListener = function(\n type, listener, capture, opt_listenerScope) {\n return this.eventTargetListeners_.getListener(\n String(type), listener, capture, opt_listenerScope);\n};\n\n\n/** @override */\ngoog.events.EventTarget.prototype.hasListener = function(\n opt_type, opt_capture) {\n var id = (opt_type !== undefined) ? String(opt_type) : undefined;\n return this.eventTargetListeners_.hasListener(id, opt_capture);\n};\n\n\n/**\n * Sets the target to be used for `event.target` when firing\n * event. Mainly used for testing. For example, see\n * `goog.testing.events.mixinListenable`.\n * @param {!Object} target The target.\n */\ngoog.events.EventTarget.prototype.setTargetForTesting = function(target) {\n this.actualEventTarget_ = target;\n};\n\n\n/**\n * Asserts that the event target instance is initialized properly.\n * @private\n */\ngoog.events.EventTarget.prototype.assertInitialized_ = function() {\n goog.asserts.assert(\n this.eventTargetListeners_,\n 'Event target is not initialized. Did you call the superclass ' +\n '(goog.events.EventTarget) constructor?');\n};\n\n\n/**\n * Dispatches the given event on the ancestorsTree.\n *\n * @param {!Object} target The target to dispatch on.\n * @param {goog.events.Event|Object|string} e The event object.\n * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors\n * tree of the target, in reverse order from the closest ancestor\n * to the root event target. May be null if the target has no ancestor.\n * @return {boolean} If anyone called preventDefault on the event object (or\n * if any of the listeners returns false) this will also return false.\n * @private\n */\ngoog.events.EventTarget.dispatchEventInternal_ = function(\n target, e, opt_ancestorsTree) {\n /** @suppress {missingProperties} */\n var type = e.type || /** @type {string} */ (e);\n\n // If accepting a string or object, create a custom event object so that\n // preventDefault and stopPropagation work with the event.\n if (typeof e === 'string') {\n e = new goog.events.Event(e, target);\n } else if (!(e instanceof goog.events.Event)) {\n var oldEvent = e;\n e = new goog.events.Event(type, target);\n goog.object.extend(e, oldEvent);\n } else {\n e.target = e.target || target;\n }\n\n var rv = true, currentTarget;\n\n // Executes all capture listeners on the ancestors, if any.\n if (opt_ancestorsTree) {\n for (var i = opt_ancestorsTree.length - 1;\n !e.hasPropagationStopped() && i >= 0; i--) {\n currentTarget = e.currentTarget = opt_ancestorsTree[i];\n rv = currentTarget.fireListeners(type, true, e) && rv;\n }\n }\n\n // Executes capture and bubble listeners on the target.\n if (!e.hasPropagationStopped()) {\n currentTarget = /** @type {?} */ (e.currentTarget = target);\n rv = currentTarget.fireListeners(type, true, e) && rv;\n if (!e.hasPropagationStopped()) {\n rv = currentTarget.fireListeners(type, false, e) && rv;\n }\n }\n\n // Executes all bubble listeners on the ancestors, if any.\n if (opt_ancestorsTree) {\n for (i = 0; !e.hasPropagationStopped() && i < opt_ancestorsTree.length;\n i++) {\n currentTarget = e.currentTarget = opt_ancestorsTree[i];\n rv = currentTarget.fireListeners(type, false, e) && rv;\n }\n }\n\n return rv;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview JSON utility functions.\n */\n\n\ngoog.provide('goog.json');\ngoog.provide('goog.json.Replacer');\ngoog.provide('goog.json.Reviver');\ngoog.provide('goog.json.Serializer');\n\n\n/**\n * @define {boolean} If true, use the native JSON parsing API.\n * NOTE: The default `goog.json.parse` implementation is able to handle\n * invalid JSON. JSPB used to produce invalid JSON which is not the case\n * anymore so this is safe to enable for parsing JSPB. Using native JSON is\n * faster and safer than the default implementation using `eval`.\n */\ngoog.json.USE_NATIVE_JSON = goog.define('goog.json.USE_NATIVE_JSON', false);\n\n/**\n * @define {boolean} If true, try the native JSON parsing API first. If it\n * fails, log an error and use `eval` instead. This is useful when\n * transitioning to `goog.json.USE_NATIVE_JSON`. The error logger needs to\n * be set by `goog.json.setErrorLogger`. If it is not set then the error\n * is ignored.\n */\ngoog.json.TRY_NATIVE_JSON = goog.define('goog.json.TRY_NATIVE_JSON', false);\n\n\n/**\n * Tests if a string is an invalid JSON string. This only ensures that we are\n * not using any invalid characters\n * @param {string} s The string to test.\n * @return {boolean} True if the input is a valid JSON string.\n */\ngoog.json.isValid = function(s) {\n // All empty whitespace is not valid.\n if (/^\\s*$/.test(s)) {\n return false;\n }\n\n // This is taken from http://www.json.org/json2.js which is released to the\n // public domain.\n // Changes: We dissallow \\u2028 Line separator and \\u2029 Paragraph separator\n // inside strings. We also treat \\u2028 and \\u2029 as whitespace which they\n // are in the RFC but IE and Safari does not match \\s to these so we need to\n // include them in the reg exps in all places where whitespace is allowed.\n // We allowed \\x7f inside strings because some tools don't escape it,\n // e.g. http://www.json.org/java/org/json/JSONObject.java\n\n // Parsing happens in three stages. In the first stage, we run the text\n // against regular expressions that look for non-JSON patterns. We are\n // especially concerned with '()' and 'new' because they can cause invocation,\n // and '=' because it can cause mutation. But just to be safe, we want to\n // reject all unexpected forms.\n\n // We split the first stage into 4 regexp operations in order to work around\n // crippling inefficiencies in IE's and Safari's regexp engines. First we\n // replace all backslash pairs with '@' (a non-JSON character). Second, we\n // replace all simple value tokens with ']' characters, but only when followed\n // by a colon, comma, closing bracket or end of string. Third, we delete all\n // open brackets that follow a colon or comma or that begin the text. Finally,\n // we look to see that the remaining characters are only whitespace or ']' or\n // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.\n\n // Don't make these static since they have the global flag.\n const backslashesRe = /\\\\[\"\\\\\\/bfnrtu]/g;\n const simpleValuesRe =\n /(?:\"[^\"\\\\\\n\\r\\u2028\\u2029\\x00-\\x08\\x0a-\\x1f]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)[\\s\\u2028\\u2029]*(?=:|,|]|}|$)/g;\n const openBracketsRe = /(?:^|:|,)(?:[\\s\\u2028\\u2029]*\\[)+/g;\n const remainderRe = /^[\\],:{}\\s\\u2028\\u2029]*$/;\n\n return remainderRe.test(\n s.replace(backslashesRe, '@')\n .replace(simpleValuesRe, ']')\n .replace(openBracketsRe, ''));\n};\n\n/**\n * Logs a parsing error in `JSON.parse` solvable by using `eval`\n * if `goog.json.TRY_NATIVE_JSON` is enabled.\n * @private {function(string, !Error)} The first parameter is the error message,\n * the second is the exception thrown by `JSON.parse`.\n */\ngoog.json.errorLogger_ = goog.nullFunction;\n\n\n/**\n * Sets an error logger to use if there's a recoverable parsing error and\n * `goog.json.TRY_NATIVE_JSON` is enabled.\n * @param {function(string, !Error)} errorLogger The first parameter is the\n * error message, the second is the exception thrown by `JSON.parse`.\n */\ngoog.json.setErrorLogger = function(errorLogger) {\n goog.json.errorLogger_ = errorLogger;\n};\n\n\n/**\n * Parses a JSON string and returns the result. This throws an exception if\n * the string is an invalid JSON string.\n *\n * Note that this is very slow on large strings. Use JSON.parse if possible.\n *\n * @param {*} s The JSON string to parse.\n * @throws Error if s is invalid JSON.\n * @return {Object} The object generated from the JSON string, or null.\n * @deprecated Use JSON.parse.\n */\ngoog.json.parse = goog.json.USE_NATIVE_JSON ?\n /** @type {function(*):Object} */ (goog.global['JSON']['parse']) :\n function(s) {\n let error;\n if (goog.json.TRY_NATIVE_JSON) {\n try {\n return goog.global['JSON']['parse'](s);\n } catch (ex) {\n error = ex;\n }\n }\n const o = String(s);\n if (goog.json.isValid(o)) {\n\n try {\n const result = /** @type {?Object} */ (eval('(' + o + ')'));\n if (error) {\n goog.json.errorLogger_('Invalid JSON: ' + o, error);\n }\n return result;\n } catch (ex) {\n }\n }\n throw new Error('Invalid JSON string: ' + o);\n };\n\n\n/**\n * JSON replacer, as defined in Section 15.12.3 of the ES5 spec.\n * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3\n *\n * TODO(nicksantos): Array should also be a valid replacer.\n *\n * @typedef {function(this:Object, string, *): *}\n */\ngoog.json.Replacer;\n\n\n/**\n * JSON reviver, as defined in Section 15.12.2 of the ES5 spec.\n * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3\n *\n * @typedef {function(this:Object, string, *): *}\n */\ngoog.json.Reviver;\n\n\n/**\n * Serializes an object or a value to a JSON string.\n *\n * @param {*} object The object to serialize.\n * @param {?goog.json.Replacer=} opt_replacer A replacer function\n * called for each (key, value) pair that determines how the value\n * should be serialized. By defult, this just returns the value\n * and allows default serialization to kick in.\n * @throws Error if there are loops in the object graph.\n * @return {string} A JSON string representation of the input.\n */\ngoog.json.serialize = goog.json.USE_NATIVE_JSON ?\n /** @type {function(*, ?goog.json.Replacer=):string} */\n (goog.global['JSON']['stringify']) :\n function(object, opt_replacer) {\n // NOTE(nicksantos): Currently, we never use JSON.stringify.\n //\n // The last time I evaluated this, JSON.stringify had subtle bugs and\n // behavior differences on all browsers, and the performance win was not\n // large enough to justify all the issues. This may change in the future\n // as browser implementations get better.\n //\n // assertSerialize in json_test contains if branches for the cases\n // that fail.\n return new goog.json.Serializer(opt_replacer).serialize(object);\n };\n\n\n\n/**\n * Class that is used to serialize JSON objects to a string.\n * @param {?goog.json.Replacer=} opt_replacer Replacer.\n * @constructor\n */\ngoog.json.Serializer = function(opt_replacer) {\n /**\n * @type {goog.json.Replacer|null|undefined}\n * @private\n */\n this.replacer_ = opt_replacer;\n};\n\n\n/**\n * Serializes an object or a value to a JSON string.\n *\n * @param {*} object The object to serialize.\n * @throws Error if there are loops in the object graph.\n * @return {string} A JSON string representation of the input.\n */\ngoog.json.Serializer.prototype.serialize = function(object) {\n const sb = [];\n this.serializeInternal(object, sb);\n return sb.join('');\n};\n\n\n/**\n * Serializes a generic value to a JSON string\n * @protected\n * @param {*} object The object to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n * @throws Error if there are loops in the object graph.\n */\ngoog.json.Serializer.prototype.serializeInternal = function(object, sb) {\n if (object == null) {\n // undefined == null so this branch covers undefined as well as null\n sb.push('null');\n return;\n }\n\n if (typeof object == 'object') {\n if (Array.isArray(object)) {\n this.serializeArray(object, sb);\n return;\n } else if (\n object instanceof String || object instanceof Number ||\n object instanceof Boolean) {\n object = object.valueOf();\n // Fall through to switch below.\n } else {\n this.serializeObject_(/** @type {!Object} */ (object), sb);\n return;\n }\n }\n\n switch (typeof object) {\n case 'string':\n this.serializeString_(object, sb);\n break;\n case 'number':\n this.serializeNumber_(object, sb);\n break;\n case 'boolean':\n sb.push(String(object));\n break;\n case 'function':\n sb.push('null');\n break;\n default:\n throw new Error('Unknown type: ' + typeof object);\n }\n};\n\n\n/**\n * Character mappings used internally for goog.string.quote\n * @private\n * @type {!Object}\n */\ngoog.json.Serializer.charToJsonCharCache_ = {\n '\\\"': '\\\\\"',\n '\\\\': '\\\\\\\\',\n '/': '\\\\/',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n\n '\\x0B': '\\\\u000b' // '\\v' is not supported in JScript\n};\n\n\n/**\n * Regular expression used to match characters that need to be replaced.\n * The S60 browser has a bug where unicode characters are not matched by\n * regular expressions. The condition below detects such behaviour and\n * adjusts the regular expression accordingly.\n * @private\n * @type {!RegExp}\n */\ngoog.json.Serializer.charsToReplace_ = /\\uffff/.test('\\uffff') ?\n /[\\\\\\\"\\x00-\\x1f\\x7f-\\uffff]/g :\n /[\\\\\\\"\\x00-\\x1f\\x7f-\\xff]/g;\n\n\n/**\n * Serializes a string to a JSON string\n * @private\n * @param {string} s The string to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeString_ = function(s, sb) {\n // The official JSON implementation does not work with international\n // characters.\n sb.push('\"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {\n // caching the result improves performance by a factor 2-3\n let rv = goog.json.Serializer.charToJsonCharCache_[c];\n if (!rv) {\n rv = '\\\\u' + (c.charCodeAt(0) | 0x10000).toString(16).substr(1);\n goog.json.Serializer.charToJsonCharCache_[c] = rv;\n }\n return rv;\n }), '\"');\n};\n\n\n/**\n * Serializes a number to a JSON string\n * @private\n * @param {number} n The number to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {\n sb.push(isFinite(n) && !isNaN(n) ? String(n) : 'null');\n};\n\n\n/**\n * Serializes an array to a JSON string\n * @param {Array<string>} arr The array to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n * @protected\n */\ngoog.json.Serializer.prototype.serializeArray = function(arr, sb) {\n const l = arr.length;\n sb.push('[');\n let sep = '';\n for (let i = 0; i < l; i++) {\n sb.push(sep);\n\n const value = arr[i];\n this.serializeInternal(\n this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,\n sb);\n\n sep = ',';\n }\n sb.push(']');\n};\n\n\n/**\n * Serializes an object to a JSON string\n * @private\n * @param {!Object} obj The object to serialize.\n * @param {Array<string>} sb Array used as a string builder.\n */\ngoog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {\n sb.push('{');\n let sep = '';\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = obj[key];\n // Skip functions.\n if (typeof value != 'function') {\n sb.push(sep);\n this.serializeString_(key, sb);\n sb.push(':');\n\n this.serializeInternal(\n this.replacer_ ? this.replacer_.call(obj, key, value) : value, sb);\n\n sep = ',';\n }\n }\n }\n sb.push('}');\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.async.WorkItem');\ngoog.provide('goog.async.WorkQueue');\n\ngoog.require('goog.asserts');\ngoog.require('goog.async.FreeList');\n\n\n// TODO(johnlenz): generalize the WorkQueue if this is used by more\n// than goog.async.run.\n\n\n\n/**\n * A low GC workqueue. The key elements of this design:\n * - avoids the need for goog.bind or equivalent by carrying scope\n * - avoids the need for array reallocation by using a linked list\n * - minimizes work entry objects allocation by recycling objects\n * @constructor\n * @final\n * @struct\n */\ngoog.async.WorkQueue = function() {\n this.workHead_ = null;\n this.workTail_ = null;\n};\n\n\n/** @define {number} The maximum number of entries to keep for recycling. */\ngoog.async.WorkQueue.DEFAULT_MAX_UNUSED =\n goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100);\n\n\n/** @const @private {goog.async.FreeList<goog.async.WorkItem>} */\ngoog.async.WorkQueue.freelist_ = new goog.async.FreeList(\n function() { return new goog.async.WorkItem(); },\n function(item) { item.reset(); }, goog.async.WorkQueue.DEFAULT_MAX_UNUSED);\n\n\n/**\n * @param {function()} fn\n * @param {Object|null|undefined} scope\n */\ngoog.async.WorkQueue.prototype.add = function(fn, scope) {\n var item = this.getUnusedItem_();\n item.set(fn, scope);\n\n if (this.workTail_) {\n this.workTail_.next = item;\n this.workTail_ = item;\n } else {\n goog.asserts.assert(!this.workHead_);\n this.workHead_ = item;\n this.workTail_ = item;\n }\n};\n\n\n/**\n * @return {goog.async.WorkItem}\n */\ngoog.async.WorkQueue.prototype.remove = function() {\n var item = null;\n\n if (this.workHead_) {\n item = this.workHead_;\n this.workHead_ = this.workHead_.next;\n if (!this.workHead_) {\n this.workTail_ = null;\n }\n item.next = null;\n }\n return item;\n};\n\n\n/**\n * @param {goog.async.WorkItem} item\n */\ngoog.async.WorkQueue.prototype.returnUnused = function(item) {\n goog.async.WorkQueue.freelist_.put(item);\n};\n\n\n/**\n * @return {goog.async.WorkItem}\n * @private\n */\ngoog.async.WorkQueue.prototype.getUnusedItem_ = function() {\n return goog.async.WorkQueue.freelist_.get();\n};\n\n\n\n/**\n * @constructor\n * @final\n * @struct\n */\ngoog.async.WorkItem = function() {\n /** @type {?function()} */\n this.fn = null;\n /** @type {?Object|null|undefined} */\n this.scope = null;\n /** @type {?goog.async.WorkItem} */\n this.next = null;\n};\n\n\n/**\n * @param {function()} fn\n * @param {Object|null|undefined} scope\n */\ngoog.async.WorkItem.prototype.set = function(fn, scope) {\n this.fn = fn;\n this.scope = scope;\n this.next = null;\n};\n\n\n/** Reset the work item so they don't prevent GC before reuse */\ngoog.async.WorkItem.prototype.reset = function() {\n this.fn = null;\n this.scope = null;\n this.next = null;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\ngoog.provide('goog.async.run');\n\ngoog.require('goog.async.WorkQueue');\ngoog.require('goog.async.nextTick');\ngoog.require('goog.async.throwException');\n\n/**\n * @define {boolean} If true, use the global Promise to implement goog.async.run\n * assuming either the native, or polyfill version will be used. Does still\n * permit tests to use forceNextTick.\n */\ngoog.ASSUME_NATIVE_PROMISE = goog.define('goog.ASSUME_NATIVE_PROMISE', false);\n\n/**\n * Fires the provided callback just before the current callstack unwinds, or as\n * soon as possible after the current JS execution context.\n * @param {function(this:THIS)} callback\n * @param {THIS=} opt_context Object to use as the \"this value\" when calling\n * the provided function.\n * @template THIS\n */\ngoog.async.run = function(callback, opt_context) {\n if (!goog.async.run.schedule_) {\n goog.async.run.initializeRunner_();\n }\n if (!goog.async.run.workQueueScheduled_) {\n // Nothing is currently scheduled, schedule it now.\n goog.async.run.schedule_();\n goog.async.run.workQueueScheduled_ = true;\n }\n\n goog.async.run.workQueue_.add(callback, opt_context);\n};\n\n\n/**\n * Initializes the function to use to process the work queue.\n * @private\n */\ngoog.async.run.initializeRunner_ = function() {\n if (goog.ASSUME_NATIVE_PROMISE ||\n (goog.global.Promise && goog.global.Promise.resolve)) {\n // Use goog.global.Promise instead of just Promise because the relevant\n // externs may be missing, and don't alias it because this could confuse the\n // compiler into thinking the polyfill is required when it should be treated\n // as optional.\n var promise = goog.global.Promise.resolve(undefined);\n goog.async.run.schedule_ = function() {\n promise.then(goog.async.run.processWorkQueue);\n };\n } else {\n goog.async.run.schedule_ = function() {\n goog.async.nextTick(goog.async.run.processWorkQueue);\n };\n }\n};\n\n\n/**\n * Forces goog.async.run to use nextTick instead of Promise.\n *\n * This should only be done in unit tests. It's useful because MockClock\n * replaces nextTick, but not the browser Promise implementation, so it allows\n * Promise-based code to be tested with MockClock.\n *\n * However, we also want to run promises if the MockClock is no longer in\n * control so we schedule a backup \"setTimeout\" to the unmocked timeout if\n * provided.\n *\n * @param {function(function())=} opt_realSetTimeout\n */\ngoog.async.run.forceNextTick = function(opt_realSetTimeout) {\n goog.async.run.schedule_ = function() {\n goog.async.nextTick(goog.async.run.processWorkQueue);\n if (opt_realSetTimeout) {\n opt_realSetTimeout(goog.async.run.processWorkQueue);\n }\n };\n};\n\n\n/**\n * The function used to schedule work asynchronousely.\n * @private {function()}\n */\ngoog.async.run.schedule_;\n\n\n/** @private {boolean} */\ngoog.async.run.workQueueScheduled_ = false;\n\n\n/** @private {!goog.async.WorkQueue} */\ngoog.async.run.workQueue_ = new goog.async.WorkQueue();\n\n\nif (goog.DEBUG) {\n /**\n * Reset the work queue. Only available for tests in debug mode.\n */\n goog.async.run.resetQueue = function() {\n goog.async.run.workQueueScheduled_ = false;\n goog.async.run.workQueue_ = new goog.async.WorkQueue();\n };\n}\n\n\n/**\n * Run any pending goog.async.run work items. This function is not intended\n * for general use, but for use by entry point handlers to run items ahead of\n * goog.async.nextTick.\n */\ngoog.async.run.processWorkQueue = function() {\n // NOTE: additional work queue items may be added while processing.\n var item = null;\n while (item = goog.async.run.workQueue_.remove()) {\n try {\n item.fn.call(item.scope);\n } catch (e) {\n goog.async.throwException(e);\n }\n goog.async.run.workQueue_.returnUnused(item);\n }\n\n // There are no more work items, allow processing to be scheduled again.\n goog.async.run.workQueueScheduled_ = false;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Simple freelist.\n *\n * An anterative to goog.structs.SimplePool, it imposes the requirement that the\n * objects in the list contain a \"next\" property that can be used to maintain\n * the pool.\n */\n\ngoog.provide('goog.async.FreeList');\n\n\n/**\n * @template ITEM\n */\ngoog.async.FreeList = class {\n /**\n * @param {function():ITEM} create\n * @param {function(ITEM):void} reset\n * @param {number} limit\n */\n constructor(create, reset, limit) {\n /** @private @const {number} */\n this.limit_ = limit;\n /** @private @const {function()} */\n this.create_ = create;\n /** @private @const {function(ITEM):void} */\n this.reset_ = reset;\n\n /** @private {number} */\n this.occupants_ = 0;\n /** @private {ITEM} */\n this.head_ = null;\n }\n\n /**\n * @return {ITEM}\n */\n get() {\n let item;\n if (this.occupants_ > 0) {\n this.occupants_--;\n item = this.head_;\n this.head_ = item.next;\n item.next = null;\n } else {\n item = this.create_();\n }\n return item;\n }\n\n /**\n * @param {ITEM} item An item available for possible future reuse.\n */\n put(item) {\n this.reset_(item);\n if (this.occupants_ < this.limit_) {\n this.occupants_++;\n item.next = this.head_;\n this.head_ = item;\n }\n }\n\n /**\n * Visible for testing.\n * @package\n * @return {number}\n */\n occupants() {\n return this.occupants_;\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Provides a function to schedule running a function as soon\n * as possible after the current JS execution stops and yields to the event\n * loop.\n */\n\ngoog.provide('goog.async.nextTick');\ngoog.provide('goog.async.throwException');\n\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.dom');\ngoog.require('goog.dom.TagName');\ngoog.require('goog.dom.safe');\ngoog.require('goog.functions');\ngoog.require('goog.html.SafeHtml');\ngoog.require('goog.html.TrustedResourceUrl');\ngoog.require('goog.labs.userAgent.browser');\ngoog.require('goog.labs.userAgent.engine');\ngoog.require('goog.string.Const');\n\n\n/**\n * Throw an item without interrupting the current execution context. For\n * example, if processing a group of items in a loop, sometimes it is useful\n * to report an error while still allowing the rest of the batch to be\n * processed.\n * @param {*} exception\n */\ngoog.async.throwException = function(exception) {\n // Each throw needs to be in its own context.\n goog.global.setTimeout(function() { throw exception; }, 0);\n};\n\n\n/**\n * Fires the provided callbacks as soon as possible after the current JS\n * execution context. setTimeout(…, 0) takes at least 4ms when called from\n * within another setTimeout(…, 0) for legacy reasons.\n *\n * This will not schedule the callback as a microtask (i.e. a task that can\n * preempt user input or networking callbacks). It is meant to emulate what\n * setTimeout(_, 0) would do if it were not throttled. If you desire microtask\n * behavior, use {@see goog.Promise} instead.\n *\n * @param {function(this:SCOPE)} callback Callback function to fire as soon as\n * possible.\n * @param {SCOPE=} opt_context Object in whose scope to call the listener.\n * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that\n * ensures correctness at the cost of speed. See comments for details.\n * @template SCOPE\n */\ngoog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) {\n var cb = callback;\n if (opt_context) {\n cb = goog.bind(callback, opt_context);\n }\n cb = goog.async.nextTick.wrapCallback_(cb);\n // Note we do allow callers to also request setImmediate if they are willing\n // to accept the possible tradeoffs of incorrectness in exchange for speed.\n // The IE fallback of readystate change is much slower. See useSetImmediate_\n // for details.\n if (goog.isFunction(goog.global.setImmediate) &&\n (opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) {\n goog.global.setImmediate(cb);\n return;\n }\n\n // Look for and cache the custom fallback version of setImmediate.\n if (!goog.async.nextTick.setImmediate_) {\n goog.async.nextTick.setImmediate_ =\n goog.async.nextTick.getSetImmediateEmulator_();\n }\n goog.async.nextTick.setImmediate_(cb);\n};\n\n\n/**\n * Returns whether should use setImmediate implementation currently on window.\n *\n * window.setImmediate was introduced and currently only supported by IE10+,\n * but due to a bug in the implementation it is not guaranteed that\n * setImmediate is faster than setTimeout nor that setImmediate N is before\n * setImmediate N+1. That is why we do not use the native version if\n * available. We do, however, call setImmediate if it is a non-native function\n * because that indicates that it has been replaced by goog.testing.MockClock\n * which we do want to support.\n * See\n * http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10\n *\n * @return {boolean} Whether to use the implementation of setImmediate defined\n * on Window.\n * @private\n * @suppress {missingProperties} For \"Window.prototype.setImmediate\"\n */\ngoog.async.nextTick.useSetImmediate_ = function() {\n // Not a browser environment.\n if (!goog.global.Window || !goog.global.Window.prototype) {\n return true;\n }\n\n // MS Edge has window.setImmediate natively, but it's not on Window.prototype.\n // Also, there's no clean way to detect if the goog.global.setImmediate has\n // been replaced by mockClock as its replacement also shows up as \"[native\n // code]\" when using toString. Therefore, just always use\n // goog.global.setImmediate for Edge. It's unclear if it suffers the same\n // issues as IE10/11, but based on\n // https://dev.modern.ie/testdrive/demos/setimmediatesorting/\n // it seems they've been working to ensure it's WAI.\n if (goog.labs.userAgent.browser.isEdge() ||\n goog.global.Window.prototype.setImmediate != goog.global.setImmediate) {\n // Something redefined setImmediate in which case we decide to use it (This\n // is so that we use the mockClock setImmediate).\n return true;\n }\n\n return false;\n};\n\n\n/**\n * Cache for the setImmediate implementation.\n * @type {function(function())}\n * @private\n */\ngoog.async.nextTick.setImmediate_;\n\n\n/**\n * Determines the best possible implementation to run a function as soon as\n * the JS event loop is idle.\n * @return {function(function())} The \"setImmediate\" implementation.\n * @private\n */\ngoog.async.nextTick.getSetImmediateEmulator_ = function() {\n // Create a private message channel and use it to postMessage empty messages\n // to ourselves.\n /** @type {!Function|undefined} */\n var Channel = goog.global['MessageChannel'];\n // If MessageChannel is not available and we are in a browser, implement\n // an iframe based polyfill in browsers that have postMessage and\n // document.addEventListener. The latter excludes IE8 because it has a\n // synchronous postMessage implementation.\n if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&\n window.postMessage && window.addEventListener &&\n // Presto (The old pre-blink Opera engine) has problems with iframes\n // and contentWindow.\n !goog.labs.userAgent.engine.isPresto()) {\n /** @constructor */\n Channel = function() {\n // Make an empty, invisible iframe.\n var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);\n iframe.style.display = 'none';\n goog.dom.safe.setIframeSrc(\n iframe,\n goog.html.TrustedResourceUrl.fromConstant(goog.string.Const.EMPTY));\n document.documentElement.appendChild(iframe);\n var win = iframe.contentWindow;\n var doc = win.document;\n doc.open();\n goog.dom.safe.documentWrite(doc, goog.html.SafeHtml.EMPTY);\n doc.close();\n // Do not post anything sensitive over this channel, as the workaround for\n // pages with file: origin could allow that information to be modified or\n // intercepted.\n var message = 'callImmediate' + Math.random();\n // The same origin policy rejects attempts to postMessage from file: urls\n // unless the origin is '*'.\n var origin = win.location.protocol == 'file:' ?\n '*' :\n win.location.protocol + '//' + win.location.host;\n var onmessage = goog.bind(function(e) {\n // Validate origin and message to make sure that this message was\n // intended for us. If the origin is set to '*' (see above) only the\n // message needs to match since, for example, '*' != 'file://'. Allowing\n // the wildcard is ok, as we are not concerned with security here.\n if ((origin != '*' && e.origin != origin) || e.data != message) {\n return;\n }\n this['port1'].onmessage();\n }, this);\n win.addEventListener('message', onmessage, false);\n this['port1'] = {};\n this['port2'] = {\n postMessage: function() { win.postMessage(message, origin); }\n };\n };\n }\n if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) {\n // Exclude all of IE due to\n // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/\n // which allows starving postMessage with a busy setTimeout loop.\n // This currently affects IE10 and IE11 which would otherwise be able\n // to use the postMessage based fallbacks.\n var channel = new Channel();\n // Use a fifo linked list to call callbacks in the right order.\n var head = {};\n var tail = head;\n channel['port1'].onmessage = function() {\n if (head.next !== undefined) {\n head = head.next;\n var cb = head.cb;\n head.cb = null;\n cb();\n }\n };\n return function(cb) {\n tail.next = {cb: cb};\n tail = tail.next;\n channel['port2'].postMessage(0);\n };\n }\n // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms\n // or more.\n // NOTE(user): This fallback is used for IE.\n return function(cb) {\n goog.global.setTimeout(/** @type {function()} */ (cb), 0);\n };\n};\n\n\n/**\n * Helper function that is overrided to protect callbacks with entry point\n * monitor if the application monitors entry points.\n * @param {function()} callback Callback function to fire as soon as possible.\n * @return {function()} The wrapped callback.\n * @private\n */\ngoog.async.nextTick.wrapCallback_ = goog.functions.identity;\n\n\n// Register the callback function as an entry point, so that it can be\n// monitored for exception handling, etc. This has to be done in this file\n// since it requires special code to handle all browsers.\ngoog.debug.entryPointRegistry.register(\n /**\n * @param {function(!Function): !Function} transformer The transforming\n * function.\n */\n function(transformer) { goog.async.nextTick.wrapCallback_ = transformer; });\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A timer class to which other classes and objects can listen on.\n * This is only an abstraction above `setInterval`.\n *\n * @see ../demos/timers.html\n */\n\ngoog.provide('goog.Timer');\n\ngoog.require('goog.Promise');\ngoog.require('goog.events.EventTarget');\n\n\n\n/**\n * Class for handling timing events.\n *\n * @param {number=} opt_interval Number of ms between ticks (default: 1ms).\n * @param {Object=} opt_timerObject An object that has `setTimeout`,\n * `setInterval`, `clearTimeout` and `clearInterval`\n * (e.g., `window`).\n * @constructor\n * @extends {goog.events.EventTarget}\n */\ngoog.Timer = function(opt_interval, opt_timerObject) {\n goog.events.EventTarget.call(this);\n\n /**\n * Number of ms between ticks\n * @private {number}\n */\n this.interval_ = opt_interval || 1;\n\n /**\n * An object that implements `setTimeout`, `setInterval`,\n * `clearTimeout` and `clearInterval`. We default to the window\n * object. Changing this on {@link goog.Timer.prototype} changes the object\n * for all timer instances which can be useful if your environment has some\n * other implementation of timers than the `window` object.\n * @private {{setTimeout:!Function, clearTimeout:!Function}}\n */\n this.timerObject_ = /** @type {{setTimeout, clearTimeout}} */ (\n opt_timerObject || goog.Timer.defaultTimerObject);\n\n /**\n * Cached `tick_` bound to the object for later use in the timer.\n * @private {Function}\n * @const\n */\n this.boundTick_ = goog.bind(this.tick_, this);\n\n /**\n * Firefox browser often fires the timer event sooner (sometimes MUCH sooner)\n * than the requested timeout. So we compare the time to when the event was\n * last fired, and reschedule if appropriate. See also\n * {@link goog.Timer.intervalScale}.\n * @private {number}\n */\n this.last_ = goog.now();\n};\ngoog.inherits(goog.Timer, goog.events.EventTarget);\n\n\n/**\n * Maximum timeout value.\n *\n * Timeout values too big to fit into a signed 32-bit integer may cause overflow\n * in FF, Safari, and Chrome, resulting in the timeout being scheduled\n * immediately. It makes more sense simply not to schedule these timeouts, since\n * 24.8 days is beyond a reasonable expectation for the browser to stay open.\n *\n * @private {number}\n * @const\n */\ngoog.Timer.MAX_TIMEOUT_ = 2147483647;\n\n\n/**\n * A timer ID that cannot be returned by any known implementation of\n * `window.setTimeout`. Passing this value to `window.clearTimeout`\n * should therefore be a no-op.\n *\n * @private {number}\n * @const\n */\ngoog.Timer.INVALID_TIMEOUT_ID_ = -1;\n\n\n/**\n * Whether this timer is enabled\n * @type {boolean}\n */\ngoog.Timer.prototype.enabled = false;\n\n\n/**\n * An object that implements `setTimeout`, `setInterval`,\n * `clearTimeout` and `clearInterval`. We default to the global\n * object. Changing `goog.Timer.defaultTimerObject` changes the object for\n * all timer instances which can be useful if your environment has some other\n * implementation of timers you'd like to use.\n * @type {{setTimeout, clearTimeout}}\n */\ngoog.Timer.defaultTimerObject = goog.global;\n\n\n/**\n * Variable that controls the timer error correction. If the timer is called\n * before the requested interval times `intervalScale`, which often\n * happens on Mozilla, the timer is rescheduled.\n * @see {@link #last_}\n * @type {number}\n */\ngoog.Timer.intervalScale = 0.8;\n\n\n/**\n * Variable for storing the result of `setInterval`.\n * @private {?number}\n */\ngoog.Timer.prototype.timer_ = null;\n\n\n/**\n * Gets the interval of the timer.\n * @return {number} interval Number of ms between ticks.\n */\ngoog.Timer.prototype.getInterval = function() {\n return this.interval_;\n};\n\n\n/**\n * Sets the interval of the timer.\n * @param {number} interval Number of ms between ticks.\n */\ngoog.Timer.prototype.setInterval = function(interval) {\n this.interval_ = interval;\n if (this.timer_ && this.enabled) {\n // Stop and then start the timer to reset the interval.\n this.stop();\n this.start();\n } else if (this.timer_) {\n this.stop();\n }\n};\n\n\n/**\n * Callback for the `setTimeout` used by the timer.\n * @private\n */\ngoog.Timer.prototype.tick_ = function() {\n if (this.enabled) {\n var elapsed = goog.now() - this.last_;\n if (elapsed > 0 && elapsed < this.interval_ * goog.Timer.intervalScale) {\n this.timer_ = this.timerObject_.setTimeout(\n this.boundTick_, this.interval_ - elapsed);\n return;\n }\n\n // Prevents setInterval from registering a duplicate timeout when called\n // in the timer event handler.\n if (this.timer_) {\n this.timerObject_.clearTimeout(this.timer_);\n this.timer_ = null;\n }\n\n this.dispatchTick();\n // The timer could be stopped in the timer event handler.\n if (this.enabled) {\n // Stop and start to ensure there is always only one timeout even if\n // start is called in the timer event handler.\n this.stop();\n this.start();\n }\n }\n};\n\n\n/**\n * Dispatches the TICK event. This is its own method so subclasses can override.\n */\ngoog.Timer.prototype.dispatchTick = function() {\n this.dispatchEvent(goog.Timer.TICK);\n};\n\n\n/**\n * Starts the timer.\n */\ngoog.Timer.prototype.start = function() {\n this.enabled = true;\n\n // If there is no interval already registered, start it now\n if (!this.timer_) {\n // IMPORTANT!\n // window.setInterval in FireFox has a bug - it fires based on\n // absolute time, rather than on relative time. What this means\n // is that if a computer is sleeping/hibernating for 24 hours\n // and the timer interval was configured to fire every 1000ms,\n // then after the PC wakes up the timer will fire, in rapid\n // succession, 3600*24 times.\n // This bug is described here and is already fixed, but it will\n // take time to propagate, so for now I am switching this over\n // to setTimeout logic.\n // https://bugzilla.mozilla.org/show_bug.cgi?id=376643\n //\n this.timer_ = this.timerObject_.setTimeout(this.boundTick_, this.interval_);\n this.last_ = goog.now();\n }\n};\n\n\n/**\n * Stops the timer.\n */\ngoog.Timer.prototype.stop = function() {\n this.enabled = false;\n if (this.timer_) {\n this.timerObject_.clearTimeout(this.timer_);\n this.timer_ = null;\n }\n};\n\n\n/** @override */\ngoog.Timer.prototype.disposeInternal = function() {\n goog.Timer.superClass_.disposeInternal.call(this);\n this.stop();\n delete this.timerObject_;\n};\n\n\n/**\n * Constant for the timer's event type.\n * @const\n */\ngoog.Timer.TICK = 'tick';\n\n\n/**\n * Calls the given function once, after the optional pause.\n * <p>\n * The function is always called asynchronously, even if the delay is 0. This\n * is a common trick to schedule a function to run after a batch of browser\n * event processing.\n *\n * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function\n * or object that has a handleEvent method.\n * @param {number=} opt_delay Milliseconds to wait; default is 0.\n * @param {SCOPE=} opt_handler Object in whose scope to call the listener.\n * @return {number} A handle to the timer ID.\n * @template SCOPE\n */\ngoog.Timer.callOnce = function(listener, opt_delay, opt_handler) {\n if (goog.isFunction(listener)) {\n if (opt_handler) {\n listener = goog.bind(listener, opt_handler);\n }\n } else if (listener && typeof listener.handleEvent == 'function') {\n // using typeof to prevent strict js warning\n listener = goog.bind(listener.handleEvent, listener);\n } else {\n throw new Error('Invalid listener argument');\n }\n\n if (Number(opt_delay) > goog.Timer.MAX_TIMEOUT_) {\n // Timeouts greater than MAX_INT return immediately due to integer\n // overflow in many browsers. Since MAX_INT is 24.8 days, just don't\n // schedule anything at all.\n return goog.Timer.INVALID_TIMEOUT_ID_;\n } else {\n return goog.Timer.defaultTimerObject.setTimeout(listener, opt_delay || 0);\n }\n};\n\n\n/**\n * Clears a timeout initiated by {@link #callOnce}.\n * @param {?number} timerId A timer ID.\n */\ngoog.Timer.clear = function(timerId) {\n goog.Timer.defaultTimerObject.clearTimeout(timerId);\n};\n\n\n/**\n * @param {number} delay Milliseconds to wait.\n * @param {(RESULT|goog.Thenable<RESULT>|Thenable)=} opt_result The value\n * with which the promise will be resolved.\n * @return {!goog.Promise<RESULT>} A promise that will be resolved after\n * the specified delay, unless it is canceled first.\n * @template RESULT\n */\ngoog.Timer.promise = function(delay, opt_result) {\n var timerKey = null;\n return new goog\n .Promise(function(resolve, reject) {\n timerKey =\n goog.Timer.callOnce(function() { resolve(opt_result); }, delay);\n if (timerKey == goog.Timer.INVALID_TIMEOUT_ID_) {\n reject(new Error('Failed to schedule timer.'));\n }\n })\n .thenCatch(function(error) {\n // Clear the timer. The most likely reason is \"cancel\" signal.\n goog.Timer.clear(timerKey);\n throw error;\n });\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Definition of the goog.async.Throttle class.\n *\n * @see ../demos/timers.html\n */\n\ngoog.provide('goog.Throttle');\ngoog.provide('goog.async.Throttle');\n\ngoog.require('goog.Disposable');\ngoog.require('goog.Timer');\n\n\n\n/**\n * Throttle will perform an action that is passed in no more than once\n * per interval (specified in milliseconds). If it gets multiple signals\n * to perform the action while it is waiting, it will only perform the action\n * once at the end of the interval.\n * @param {function(this: T, ...?)} listener Function to callback when the\n * action is triggered.\n * @param {number} interval Interval over which to throttle. The listener can\n * only be called once per interval.\n * @param {T=} opt_handler Object in whose scope to call the listener.\n * @constructor\n * @struct\n * @extends {goog.Disposable}\n * @final\n * @template T\n */\ngoog.async.Throttle = function(listener, interval, opt_handler) {\n goog.async.Throttle.base(this, 'constructor');\n\n /**\n * Function to callback\n * @type {function(this: T, ...?)}\n * @private\n */\n this.listener_ =\n opt_handler != null ? goog.bind(listener, opt_handler) : listener;\n\n /**\n * Interval for the throttle time\n * @type {number}\n * @private\n */\n this.interval_ = interval;\n\n /**\n * Cached callback function invoked after the throttle timeout completes\n * @type {Function}\n * @private\n */\n this.callback_ = goog.bind(this.onTimer_, this);\n\n /**\n * The last arguments passed into `fire`.\n * @private {!IArrayLike}\n */\n this.args_ = [];\n};\ngoog.inherits(goog.async.Throttle, goog.Disposable);\n\n\n\n/**\n * A deprecated alias.\n * @deprecated Use goog.async.Throttle instead.\n * @constructor\n * @final\n */\ngoog.Throttle = goog.async.Throttle;\n\n\n/**\n * Indicates that the action is pending and needs to be fired.\n * @type {boolean}\n * @private\n */\ngoog.async.Throttle.prototype.shouldFire_ = false;\n\n\n/**\n * Indicates the count of nested pauses currently in effect on the throttle.\n * When this count is not zero, fired actions will be postponed until the\n * throttle is resumed enough times to drop the pause count to zero.\n * @type {number}\n * @private\n */\ngoog.async.Throttle.prototype.pauseCount_ = 0;\n\n\n/**\n * Timer for scheduling the next callback\n * @type {?number}\n * @private\n */\ngoog.async.Throttle.prototype.timer_ = null;\n\n\n/**\n * Notifies the throttle that the action has happened. It will throttle the call\n * so that the callback is not called too often according to the interval\n * parameter passed to the constructor, passing the arguments from the last call\n * of this function into the throttled function.\n * @param {...?} var_args Arguments to pass on to the throttled function.\n */\ngoog.async.Throttle.prototype.fire = function(var_args) {\n this.args_ = arguments;\n if (!this.timer_ && !this.pauseCount_) {\n this.doAction_();\n } else {\n this.shouldFire_ = true;\n }\n};\n\n\n/**\n * Cancels any pending action callback. The throttle can be restarted by\n * calling {@link #fire}.\n */\ngoog.async.Throttle.prototype.stop = function() {\n if (this.timer_) {\n goog.Timer.clear(this.timer_);\n this.timer_ = null;\n this.shouldFire_ = false;\n this.args_ = [];\n }\n};\n\n\n/**\n * Pauses the throttle. All pending and future action callbacks will be\n * delayed until the throttle is resumed. Pauses can be nested.\n */\ngoog.async.Throttle.prototype.pause = function() {\n this.pauseCount_++;\n};\n\n\n/**\n * Resumes the throttle. If doing so drops the pausing count to zero, pending\n * action callbacks will be executed as soon as possible, but still no sooner\n * than an interval's delay after the previous call. Future action callbacks\n * will be executed as normal.\n */\ngoog.async.Throttle.prototype.resume = function() {\n this.pauseCount_--;\n if (!this.pauseCount_ && this.shouldFire_ && !this.timer_) {\n this.shouldFire_ = false;\n this.doAction_();\n }\n};\n\n\n/** @override */\ngoog.async.Throttle.prototype.disposeInternal = function() {\n goog.async.Throttle.base(this, 'disposeInternal');\n this.stop();\n};\n\n\n/**\n * Handler for the timer to fire the throttle\n * @private\n */\ngoog.async.Throttle.prototype.onTimer_ = function() {\n this.timer_ = null;\n\n if (this.shouldFire_ && !this.pauseCount_) {\n this.shouldFire_ = false;\n this.doAction_();\n }\n};\n\n\n/**\n * Calls the callback\n * @private\n */\ngoog.async.Throttle.prototype.doAction_ = function() {\n this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_);\n this.listener_.apply(null, this.args_);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Class to create objects which want to handle multiple events\n * and have their listeners easily cleaned up via a dispose method.\n *\n * Example:\n * <pre>\n * function Something() {\n * Something.base(this);\n *\n * ... set up object ...\n *\n * // Add event listeners\n * this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);\n * this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);\n * this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);\n * this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);\n * this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);\n * }\n * goog.inherits(Something, goog.events.EventHandler);\n *\n * Something.prototype.disposeInternal = function() {\n * Something.base(this, 'disposeInternal');\n * goog.dom.removeNode(this.container);\n * };\n *\n *\n * // Then elsewhere:\n *\n * var activeSomething = null;\n * function openSomething() {\n * activeSomething = new Something();\n * }\n *\n * function closeSomething() {\n * if (activeSomething) {\n * activeSomething.dispose(); // Remove event listeners\n * activeSomething = null;\n * }\n * }\n * </pre>\n */\n\ngoog.provide('goog.events.EventHandler');\n\ngoog.forwardDeclare('goog.events.EventWrapper');\ngoog.require('goog.Disposable');\ngoog.require('goog.events');\ngoog.require('goog.object');\ngoog.requireType('goog.events.EventTarget');\n\n\n\n/**\n * Super class for objects that want to easily manage a number of event\n * listeners. It allows a short cut to listen and also provides a quick way\n * to remove all events listeners belonging to this object.\n * @param {SCOPE=} opt_scope Object in whose scope to call the listeners.\n * @constructor\n * @extends {goog.Disposable}\n * @template SCOPE\n */\ngoog.events.EventHandler = function(opt_scope) {\n goog.Disposable.call(this);\n // TODO(mknichel): Rename this to this.scope_ and fix the classes in google3\n // that access this private variable. :(\n this.handler_ = opt_scope;\n\n /**\n * Keys for events that are being listened to.\n * @type {!Object<!goog.events.Key>}\n * @private\n */\n this.keys_ = {};\n};\ngoog.inherits(goog.events.EventHandler, goog.Disposable);\n\n\n/**\n * Utility array used to unify the cases of listening for an array of types\n * and listening for a single event, without using recursion or allocating\n * an array each time.\n * @type {!Array<string>}\n * @const\n * @private\n */\ngoog.events.EventHandler.typeArray_ = [];\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted then the\n * EventHandler's handleEvent method will be used.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}\n * opt_fn Optional callback function to be used as the listener or an object\n * with handleEvent function.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template EVENTOBJ, THIS\n */\ngoog.events.EventHandler.prototype.listen = function(\n src, type, opt_fn, opt_options) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n return self.listen_(src, type, opt_fn, opt_options);\n};\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted then the\n * EventHandler's handleEvent method will be used.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|\n * null|undefined} fn Optional callback function to be used as the\n * listener or an object with handleEvent function.\n * @param {boolean|!AddEventListenerOptions|undefined} options\n * @param {T} scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template T, EVENTOBJ, THIS\n */\ngoog.events.EventHandler.prototype.listenWithScope = function(\n src, type, fn, options, scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n // TODO(mknichel): Deprecate this function.\n return self.listen_(src, type, fn, options, scope);\n};\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted then the\n * EventHandler's handleEvent method will be used.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn\n * Optional callback function to be used as the listener or an object with\n * handleEvent function.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {Object=} opt_scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template EVENTOBJ, THIS\n * @private\n */\ngoog.events.EventHandler.prototype.listen_ = function(\n src, type, opt_fn, opt_options, opt_scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n if (!Array.isArray(type)) {\n if (type) {\n goog.events.EventHandler.typeArray_[0] = type.toString();\n }\n type = goog.events.EventHandler.typeArray_;\n }\n for (var i = 0; i < type.length; i++) {\n var listenerObj = goog.events.listen(\n src, type[i], opt_fn || self.handleEvent, opt_options || false,\n opt_scope || self.handler_ || self);\n\n if (!listenerObj) {\n // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT\n // (goog.events.CaptureSimulationMode) in IE8-, it will return null\n // value.\n return self;\n }\n\n var key = listenerObj.key;\n self.keys_[key] = listenerObj;\n }\n\n return self;\n};\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted, then the\n * EventHandler's handleEvent method will be used. After the event has fired the\n * event listener is removed from the target. If an array of event types is\n * provided, each event type will be listened to once.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}\n * opt_fn\n * Optional callback function to be used as the listener or an object with\n * handleEvent function.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template EVENTOBJ, THIS\n */\ngoog.events.EventHandler.prototype.listenOnce = function(\n src, type, opt_fn, opt_options) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n return self.listenOnce_(src, type, opt_fn, opt_options);\n};\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted, then the\n * EventHandler's handleEvent method will be used. After the event has fired the\n * event listener is removed from the target. If an array of event types is\n * provided, each event type will be listened to once.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|\n * null|undefined} fn Optional callback function to be used as the\n * listener or an object with handleEvent function.\n * @param {boolean|undefined} capture Optional whether to use capture phase.\n * @param {T} scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template T, EVENTOBJ, THIS\n */\ngoog.events.EventHandler.prototype.listenOnceWithScope = function(\n src, type, fn, capture, scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n // TODO(mknichel): Deprecate this function.\n return self.listenOnce_(src, type, fn, capture, scope);\n};\n\n\n/**\n * Listen to an event on a Listenable. If the function is omitted, then the\n * EventHandler's handleEvent method will be used. After the event has fired\n * the event listener is removed from the target. If an array of event types is\n * provided, each event type will be listened to once.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type to listen for or array of event types.\n * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn\n * Optional callback function to be used as the listener or an object with\n * handleEvent function.\n * @param {(boolean|!AddEventListenerOptions)=} opt_options\n * @param {Object=} opt_scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template EVENTOBJ, THIS\n * @private\n */\ngoog.events.EventHandler.prototype.listenOnce_ = function(\n src, type, opt_fn, opt_options, opt_scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n if (Array.isArray(type)) {\n for (var i = 0; i < type.length; i++) {\n self.listenOnce_(src, type[i], opt_fn, opt_options, opt_scope);\n }\n } else {\n var listenerObj = goog.events.listenOnce(\n src, type, opt_fn || self.handleEvent, opt_options,\n opt_scope || self.handler_ || self);\n if (!listenerObj) {\n // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT\n // (goog.events.CaptureSimulationMode) in IE8-, it will return null\n // value.\n return self;\n }\n\n var key = listenerObj.key;\n self.keys_[key] = listenerObj;\n }\n\n return self;\n};\n\n\n/**\n * Adds an event listener with a specific event wrapper on a DOM Node or an\n * object that has implemented {@link goog.events.EventTarget}. A listener can\n * only be added once to an object.\n *\n * @param {EventTarget|goog.events.EventTarget} src The node to listen to\n * events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(this:SCOPE, ?):?|{handleEvent:function(?):?}|null} listener\n * Callback method, or an object with a handleEvent function.\n * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to\n * false).\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template THIS\n */\ngoog.events.EventHandler.prototype.listenWithWrapper = function(\n src, wrapper, listener, opt_capt) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n // TODO(mknichel): Remove the opt_scope from this function and then\n // templatize it.\n return self.listenWithWrapper_(src, wrapper, listener, opt_capt);\n};\n\n\n/**\n * Adds an event listener with a specific event wrapper on a DOM Node or an\n * object that has implemented {@link goog.events.EventTarget}. A listener can\n * only be added once to an object.\n *\n * @param {EventTarget|goog.events.EventTarget} src The node to listen to\n * events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(this:T, ?):?|{handleEvent:function(this:T, ?):?}|null}\n * listener Optional callback function to be used as the\n * listener or an object with handleEvent function.\n * @param {boolean|undefined} capture Optional whether to use capture phase.\n * @param {T} scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template T, THIS\n */\ngoog.events.EventHandler.prototype.listenWithWrapperAndScope = function(\n src, wrapper, listener, capture, scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n // TODO(mknichel): Deprecate this function.\n return self.listenWithWrapper_(src, wrapper, listener, capture, scope);\n};\n\n\n/**\n * Adds an event listener with a specific event wrapper on a DOM Node or an\n * object that has implemented {@link goog.events.EventTarget}. A listener can\n * only be added once to an object.\n *\n * @param {EventTarget|goog.events.EventTarget} src The node to listen to\n * events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback\n * method, or an object with a handleEvent function.\n * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to\n * false).\n * @param {Object=} opt_scope Element in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template THIS\n * @private\n */\ngoog.events.EventHandler.prototype.listenWithWrapper_ = function(\n src, wrapper, listener, opt_capt, opt_scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n wrapper.listen(\n src, listener, opt_capt, opt_scope || self.handler_ || self, self);\n return self;\n};\n\n\n/**\n * @return {number} Number of listeners registered by this handler.\n */\ngoog.events.EventHandler.prototype.getListenerCount = function() {\n var count = 0;\n for (var key in this.keys_) {\n if (Object.prototype.hasOwnProperty.call(this.keys_, key)) {\n count++;\n }\n }\n return count;\n};\n\n\n/**\n * Unlistens on an event.\n * @param {goog.events.ListenableType} src Event source.\n * @param {string|Array<string>|\n * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}\n * type Event type or array of event types to unlisten to.\n * @param {function(this:?, EVENTOBJ):?|{handleEvent:function(?):?}|null=}\n * opt_fn Optional callback function to be used as the listener or an object\n * with handleEvent function.\n * @param {(boolean|!EventListenerOptions)=} opt_options\n * @param {Object=} opt_scope Object in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template EVENTOBJ, THIS\n */\ngoog.events.EventHandler.prototype.unlisten = function(\n src, type, opt_fn, opt_options, opt_scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n if (Array.isArray(type)) {\n for (var i = 0; i < type.length; i++) {\n self.unlisten(src, type[i], opt_fn, opt_options, opt_scope);\n }\n } else {\n var capture =\n goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;\n var listener = goog.events.getListener(\n src, type, opt_fn || self.handleEvent, capture,\n opt_scope || self.handler_ || self);\n\n if (listener) {\n goog.events.unlistenByKey(listener);\n delete self.keys_[listener.key];\n }\n }\n\n return self;\n};\n\n\n/**\n * Removes an event listener which was added with listenWithWrapper().\n *\n * @param {EventTarget|goog.events.EventTarget} src The target to stop\n * listening to events on.\n * @param {goog.events.EventWrapper} wrapper Event wrapper to use.\n * @param {function(?):?|{handleEvent:function(?):?}|null} listener The\n * listener function to remove.\n * @param {boolean=} opt_capt In DOM-compliant browsers, this determines\n * whether the listener is fired during the capture or bubble phase of the\n * event.\n * @param {Object=} opt_scope Element in whose scope to call the listener.\n * @return {THIS} This object, allowing for chaining of calls.\n * @this {THIS}\n * @template THIS\n */\ngoog.events.EventHandler.prototype.unlistenWithWrapper = function(\n src, wrapper, listener, opt_capt, opt_scope) {\n var self = /** @type {!goog.events.EventHandler} */ (this);\n wrapper.unlisten(\n src, listener, opt_capt, opt_scope || self.handler_ || self, self);\n return self;\n};\n\n\n/**\n * Unlistens to all events.\n */\ngoog.events.EventHandler.prototype.removeAll = function() {\n goog.object.forEach(this.keys_, function(listenerObj, key) {\n if (this.keys_.hasOwnProperty(key)) {\n goog.events.unlistenByKey(listenerObj);\n }\n }, this);\n\n this.keys_ = {};\n};\n\n\n/**\n * Disposes of this EventHandler and removes all listeners that it registered.\n * @override\n * @protected\n */\ngoog.events.EventHandler.prototype.disposeInternal = function() {\n goog.events.EventHandler.superClass_.disposeInternal.call(this);\n this.removeAll();\n};\n\n\n/**\n * Default event handler\n * @param {goog.events.Event} e Event object.\n */\ngoog.events.EventHandler.prototype.handleEvent = function(e) {\n throw new Error('EventHandler.handleEvent not implemented');\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Provides a utility for tracing and debugging WebChannel\n * requests.\n *\n */\n\n\ngoog.provide('goog.labs.net.webChannel.WebChannelDebug');\n\ngoog.require('goog.json');\ngoog.require('goog.log');\ngoog.requireType('goog.Uri');\ngoog.requireType('goog.net.XmlHttp.ReadyState');\n\n\n\n/**\n * Logs and keeps a buffer of debugging info for the Channel.\n *\n * @constructor\n * @struct\n * @final\n */\ngoog.labs.net.webChannel.WebChannelDebug = function() {\n /**\n * The logger instance.\n * @const\n * @private {?goog.log.Logger}\n */\n this.logger_ = goog.log.getLogger('goog.labs.net.webChannel.WebChannelDebug');\n\n /**\n * Whether to enable redact. Defaults to true.\n * @private {boolean}\n */\n this.redactEnabled_ = true;\n};\n\n\ngoog.scope(function() {\nvar WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;\n\n\n/**\n * Turns off redact.\n */\nWebChannelDebug.prototype.disableRedact = function() {\n this.redactEnabled_ = false;\n};\n\n\n/**\n * Logs that the browser went offline during the lifetime of a request.\n * @param {goog.Uri} url The URL being requested.\n */\nWebChannelDebug.prototype.browserOfflineResponse = function(url) {\n this.info(function() {\n return 'BROWSER_OFFLINE: ' + url;\n });\n};\n\n\n/**\n * Logs an XmlHttp request..\n * @param {string} verb The request type (GET/POST).\n * @param {goog.Uri} uri The request destination.\n * @param {string|number|undefined} id The request id.\n * @param {number} attempt Which attempt # the request was.\n * @param {?string} postData The data posted in the request.\n */\nWebChannelDebug.prototype.xmlHttpChannelRequest = function(\n verb, uri, id, attempt, postData) {\n var self = this;\n this.info(function() {\n return 'XMLHTTP REQ (' + id + ') [attempt ' + attempt + ']: ' + verb +\n '\\n' + uri + '\\n' + self.maybeRedactPostData_(postData);\n });\n};\n\n\n/**\n * Logs the meta data received from an XmlHttp request.\n * @param {string} verb The request type (GET/POST).\n * @param {goog.Uri} uri The request destination.\n * @param {string|number|undefined} id The request id.\n * @param {number} attempt Which attempt # the request was.\n * @param {goog.net.XmlHttp.ReadyState} readyState The ready state.\n * @param {number} statusCode The HTTP status code.\n */\nWebChannelDebug.prototype.xmlHttpChannelResponseMetaData = function(\n verb, uri, id, attempt, readyState, statusCode) {\n this.info(function() {\n return 'XMLHTTP RESP (' + id + ') [ attempt ' + attempt + ']: ' + verb +\n '\\n' + uri + '\\n' + readyState + ' ' + statusCode;\n });\n};\n\n\n/**\n * Logs the response data received from an XmlHttp request.\n * @param {string|number|undefined} id The request id.\n * @param {?string} responseText The response text.\n * @param {?string=} opt_desc Optional request description.\n */\nWebChannelDebug.prototype.xmlHttpChannelResponseText = function(\n id, responseText, opt_desc) {\n var self = this;\n this.info(function() {\n return 'XMLHTTP TEXT (' + id + '): ' + self.redactResponse_(responseText) +\n (opt_desc ? ' ' + opt_desc : '');\n });\n};\n\n\n/**\n * Logs a request timeout.\n * @param {goog.Uri} uri The uri that timed out.\n */\nWebChannelDebug.prototype.timeoutResponse = function(uri) {\n this.info(function() {\n return 'TIMEOUT: ' + uri;\n });\n};\n\n\n/**\n * Logs a debug message.\n * @param {!goog.debug.Loggable} text The message.\n */\nWebChannelDebug.prototype.debug = function(text) {\n goog.log.fine(this.logger_, text);\n};\n\n\n/**\n * Logs an exception\n * @param {Error} e The error or error event.\n * @param {goog.debug.Loggable=} opt_msg The optional message,\n * defaults to 'Exception'.\n */\nWebChannelDebug.prototype.dumpException = function(e, opt_msg) {\n goog.log.error(this.logger_, opt_msg || 'Exception', e);\n};\n\n\n/**\n * Logs an info message.\n * @param {!goog.debug.Loggable} text The message.\n */\nWebChannelDebug.prototype.info = function(text) {\n goog.log.info(this.logger_, text);\n};\n\n\n/**\n * Logs a warning message.\n * @param {!goog.debug.Loggable} text The message.\n */\nWebChannelDebug.prototype.warning = function(text) {\n goog.log.warning(this.logger_, text);\n};\n\n\n/**\n * Logs a severe message.\n * @param {!goog.debug.Loggable} text The message.\n */\nWebChannelDebug.prototype.severe = function(text) {\n goog.log.error(this.logger_, text);\n};\n\n\n/**\n * Removes potentially private data from a response so that we don't\n * accidentally save private and personal data to the server logs.\n * @param {?string} responseText A JSON response to clean.\n * @return {?string} The cleaned response.\n * @private\n */\nWebChannelDebug.prototype.redactResponse_ = function(responseText) {\n if (!this.redactEnabled_) {\n return responseText;\n }\n\n if (!responseText) {\n return null;\n }\n\n try {\n var responseArray = JSON.parse(responseText);\n if (responseArray) {\n for (var i = 0; i < responseArray.length; i++) {\n if (Array.isArray(responseArray[i])) {\n this.maybeRedactArray_(responseArray[i]);\n }\n }\n }\n\n return goog.json.serialize(responseArray);\n } catch (e) {\n this.debug('Exception parsing expected JS array - probably was not JS');\n return responseText;\n }\n};\n\n\n/**\n * Removes data from a response array that may be sensitive.\n * @param {!Array<?>} array The array to clean.\n * @private\n */\nWebChannelDebug.prototype.maybeRedactArray_ = function(array) {\n if (array.length < 2) {\n return;\n }\n var dataPart = array[1];\n if (!Array.isArray(dataPart)) {\n return;\n }\n if (dataPart.length < 1) {\n return;\n }\n\n var type = dataPart[0];\n if (type != 'noop' && type != 'stop' && type != 'close') {\n // redact all fields in the array\n for (var i = 1; i < dataPart.length; i++) {\n dataPart[i] = '';\n }\n }\n};\n\n\n/**\n * Removes potentially private data from a request POST body so that we don't\n * accidentally save private and personal data to the server logs.\n * @param {?string} data The data string to clean.\n * @return {?string} The data string with sensitive data replaced by 'redacted'.\n * @private\n */\nWebChannelDebug.prototype.maybeRedactPostData_ = function(data) {\n if (!this.redactEnabled_) {\n return data;\n }\n\n if (!data) {\n return null;\n }\n var out = '';\n var params = data.split('&');\n for (var i = 0; i < params.length; i++) {\n var param = params[i];\n var keyValue = param.split('=');\n if (keyValue.length > 1) {\n var key = keyValue[0];\n var value = keyValue[1];\n\n var keyParts = key.split('_');\n if (keyParts.length >= 2 && keyParts[1] == 'type') {\n out += key + '=' + value + '&';\n } else {\n out += key + '=' +\n 'redacted' +\n '&';\n }\n }\n }\n return out;\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Static utilities for collecting stats associated with\n * ChannelRequest.\n *\n */\n\n\ngoog.provide('goog.labs.net.webChannel.requestStats');\ngoog.provide('goog.labs.net.webChannel.requestStats.Event');\ngoog.provide('goog.labs.net.webChannel.requestStats.ServerReachability');\ngoog.provide('goog.labs.net.webChannel.requestStats.ServerReachabilityEvent');\ngoog.provide('goog.labs.net.webChannel.requestStats.Stat');\ngoog.provide('goog.labs.net.webChannel.requestStats.StatEvent');\ngoog.provide('goog.labs.net.webChannel.requestStats.TimingEvent');\n\ngoog.require('goog.events.Event');\ngoog.require('goog.events.EventTarget');\n\n\ngoog.scope(function() {\nvar requestStats = goog.labs.net.webChannel.requestStats;\n\n\n/**\n * Events fired.\n * @const\n */\nrequestStats.Event = {};\n\n\n/**\n * Singleton event target for firing stat events\n * @type {?goog.events.EventTarget}\n * @private\n */\nrequestStats.eventTarget_ = null;\n\n/**\n * Singleton event target for firing stat events\n * @return {!goog.events.EventTarget}\n * @private\n */\nrequestStats.getStatEventTarget_ = function() {\n requestStats.eventTarget_ =\n requestStats.eventTarget_ || new goog.events.EventTarget();\n return requestStats.eventTarget_;\n};\n\n/**\n * The type of event that occurs every time some information about how reachable\n * the server is is discovered.\n */\nrequestStats.Event.SERVER_REACHABILITY_EVENT = 'serverreachability';\n\n\n/**\n * Types of events which reveal information about the reachability of the\n * server.\n * @enum {number}\n */\nrequestStats.ServerReachability = {\n REQUEST_MADE: 1,\n REQUEST_SUCCEEDED: 2,\n REQUEST_FAILED: 3,\n BACK_CHANNEL_ACTIVITY: 4 // any response data received\n};\n\n\n\n/**\n * Event class for SERVER_REACHABILITY_EVENT.\n *\n * @param {goog.events.EventTarget} target The stat event target for\n the channel.\n * @param {requestStats.ServerReachability} reachabilityType\n * The reachability event type.\n * @constructor\n * @extends {goog.events.Event}\n */\nrequestStats.ServerReachabilityEvent = function(target, reachabilityType) {\n goog.events.Event.call(\n this, requestStats.Event.SERVER_REACHABILITY_EVENT, target);\n\n /**\n * @type {requestStats.ServerReachability}\n */\n this.reachabilityType = reachabilityType;\n};\ngoog.inherits(requestStats.ServerReachabilityEvent, goog.events.Event);\n\n\n/**\n * Notify the channel that a particular fine grained network event has occurred.\n * Should be considered package-private.\n * @param {requestStats.ServerReachability} reachabilityType\n * The reachability event type.\n */\nrequestStats.notifyServerReachabilityEvent = function(reachabilityType) {\n var target = requestStats.getStatEventTarget_();\n target.dispatchEvent(\n new requestStats.ServerReachabilityEvent(target, reachabilityType));\n};\n\n\n/**\n * Stat Event that fires when things of interest happen that may be useful for\n * applications to know about for stats or debugging purposes.\n */\nrequestStats.Event.STAT_EVENT = 'statevent';\n\n\n/**\n * Enum that identifies events for statistics that are interesting to track.\n * @enum {number}\n */\nrequestStats.Stat = {\n /** Event indicating a new connection attempt. */\n CONNECT_ATTEMPT: 0,\n\n /** Event indicating a connection error due to a general network problem. */\n ERROR_NETWORK: 1,\n\n /**\n * Event indicating a connection error that isn't due to a general network\n * problem.\n */\n ERROR_OTHER: 2,\n\n /** Event indicating the start of test stage one. */\n TEST_STAGE_ONE_START: 3,\n\n /** Event indicating the start of test stage two. */\n TEST_STAGE_TWO_START: 4,\n\n /** Event indicating the first piece of test data was received. */\n TEST_STAGE_TWO_DATA_ONE: 5,\n\n /**\n * Event indicating that the second piece of test data was received and it was\n * received separately from the first.\n */\n TEST_STAGE_TWO_DATA_TWO: 6,\n\n /** Event indicating both pieces of test data were received simultaneously. */\n TEST_STAGE_TWO_DATA_BOTH: 7,\n\n /** Event indicating stage one of the test request failed. */\n TEST_STAGE_ONE_FAILED: 8,\n\n /** Event indicating stage two of the test request failed. */\n TEST_STAGE_TWO_FAILED: 9,\n\n /**\n * Event indicating that a buffering proxy is likely between the client and\n * the server.\n */\n PROXY: 10,\n\n /**\n * Event indicating that no buffering proxy is likely between the client and\n * the server.\n */\n NOPROXY: 11,\n\n /** Event indicating an unknown SID error. */\n REQUEST_UNKNOWN_SESSION_ID: 12,\n\n /** Event indicating a bad status code was received. */\n REQUEST_BAD_STATUS: 13,\n\n /** Event indicating incomplete data was received */\n REQUEST_INCOMPLETE_DATA: 14,\n\n /** Event indicating bad data was received */\n REQUEST_BAD_DATA: 15,\n\n /** Event indicating no data was received when data was expected. */\n REQUEST_NO_DATA: 16,\n\n /** Event indicating a request timeout. */\n REQUEST_TIMEOUT: 17,\n\n /**\n * Event indicating that the server never received our hanging GET and so it\n * is being retried.\n */\n BACKCHANNEL_MISSING: 18,\n\n /**\n * Event indicating that we have determined that our hanging GET is not\n * receiving data when it should be. Thus it is dead dead and will be retried.\n */\n BACKCHANNEL_DEAD: 19,\n\n /**\n * The browser declared itself offline during the lifetime of a request, or\n * was offline when a request was initially made.\n */\n BROWSER_OFFLINE: 20\n};\n\n\n\n/**\n * Event class for STAT_EVENT.\n *\n * @param {goog.events.EventTarget} eventTarget The stat event target for\n the channel.\n * @param {requestStats.Stat} stat The stat.\n * @constructor\n * @extends {goog.events.Event}\n */\nrequestStats.StatEvent = function(eventTarget, stat) {\n goog.events.Event.call(this, requestStats.Event.STAT_EVENT, eventTarget);\n\n /**\n * The stat\n * @type {requestStats.Stat}\n */\n this.stat = stat;\n\n};\ngoog.inherits(requestStats.StatEvent, goog.events.Event);\n\n\n/**\n * Returns the singleton event target for stat events.\n * @return {!goog.events.EventTarget} The event target for stat events.\n */\nrequestStats.getStatEventTarget = function() {\n return requestStats.getStatEventTarget_();\n};\n\n\n/**\n * Helper function to call the stat event callback.\n * @param {requestStats.Stat} stat The stat.\n */\nrequestStats.notifyStatEvent = function(stat) {\n var target = requestStats.getStatEventTarget_();\n target.dispatchEvent(new requestStats.StatEvent(target, stat));\n};\n\n\n/**\n * An event that fires when POST requests complete successfully, indicating\n * the size of the POST and the round trip time.\n */\nrequestStats.Event.TIMING_EVENT = 'timingevent';\n\n\n\n/**\n * Event class for requestStats.Event.TIMING_EVENT\n *\n * @param {goog.events.EventTarget} target The stat event target for\n the channel.\n * @param {number} size The number of characters in the POST data.\n * @param {number} rtt The total round trip time from POST to response in MS.\n * @param {number} retries The number of times the POST had to be retried.\n * @constructor\n * @extends {goog.events.Event}\n */\nrequestStats.TimingEvent = function(target, size, rtt, retries) {\n goog.events.Event.call(this, requestStats.Event.TIMING_EVENT, target);\n\n /**\n * @type {number}\n */\n this.size = size;\n\n /**\n * @type {number}\n */\n this.rtt = rtt;\n\n /**\n * @type {number}\n */\n this.retries = retries;\n\n};\ngoog.inherits(requestStats.TimingEvent, goog.events.Event);\n\n\n/**\n * Helper function to notify listeners about POST request performance.\n *\n * @param {number} size Number of characters in the POST data.\n * @param {number} rtt The amount of time from POST start to response.\n * @param {number} retries The number of times the POST had to be retried.\n */\nrequestStats.notifyTimingEvent = function(size, rtt, retries) {\n var target = requestStats.getStatEventTarget_();\n target.dispatchEvent(\n new requestStats.TimingEvent(target, size, rtt, retries));\n};\n\n\n/**\n * Allows the application to set an execution hooks for when a channel\n * starts processing requests. This is useful to track timing or logging\n * special information. The function takes no parameters and return void.\n * @param {Function} startHook The function for the start hook.\n */\nrequestStats.setStartThreadExecutionHook = function(startHook) {\n requestStats.startExecutionHook_ = startHook;\n};\n\n\n/**\n * Allows the application to set an execution hooks for when a channel\n * stops processing requests. This is useful to track timing or logging\n * special information. The function takes no parameters and return void.\n * @param {Function} endHook The function for the end hook.\n */\nrequestStats.setEndThreadExecutionHook = function(endHook) {\n requestStats.endExecutionHook_ = endHook;\n};\n\n\n/**\n * Application provided execution hook for the start hook.\n *\n * @type {Function}\n * @private\n */\nrequestStats.startExecutionHook_ = function() {};\n\n\n/**\n * Application provided execution hook for the end hook.\n *\n * @type {Function}\n * @private\n */\nrequestStats.endExecutionHook_ = function() {};\n\n\n/**\n * Helper function to call the start hook\n */\nrequestStats.onStartExecution = function() {\n requestStats.startExecutionHook_();\n};\n\n\n/**\n * Helper function to call the end hook\n */\nrequestStats.onEndExecution = function() {\n requestStats.endExecutionHook_();\n};\n\n\n/**\n * Wrapper around SafeTimeout which calls the start and end execution hooks\n * with a try...finally block.\n * @param {Function} fn The callback function.\n * @param {number} ms The time in MS for the timer.\n * @return {number} The ID of the timer.\n */\nrequestStats.setTimeout = function(fn, ms) {\n if (!goog.isFunction(fn)) {\n throw new Error('Fn must not be null and must be a function');\n }\n return goog.global.setTimeout(function() {\n requestStats.onStartExecution();\n try {\n fn();\n } finally {\n requestStats.onEndExecution();\n }\n }, ms);\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Error codes shared between goog.net.IframeIo and\n * goog.net.XhrIo.\n */\n\ngoog.provide('goog.net.ErrorCode');\n\n\n/**\n * Error codes\n * @enum {number}\n */\ngoog.net.ErrorCode = {\n\n /**\n * There is no error condition.\n */\n NO_ERROR: 0,\n\n /**\n * The most common error from iframeio, unfortunately, is that the browser\n * responded with an error page that is classed as a different domain. The\n * situations, are when a browser error page is shown -- 404, access denied,\n * DNS failure, connection reset etc.)\n *\n */\n ACCESS_DENIED: 1,\n\n /**\n * Currently the only case where file not found will be caused is when the\n * code is running on the local file system and a non-IE browser makes a\n * request to a file that doesn't exist.\n */\n FILE_NOT_FOUND: 2,\n\n /**\n * If Firefox shows a browser error page, such as a connection reset by\n * server or access denied, then it will fail silently without the error or\n * load handlers firing.\n */\n FF_SILENT_ERROR: 3,\n\n /**\n * Custom error provided by the client through the error check hook.\n */\n CUSTOM_ERROR: 4,\n\n /**\n * Exception was thrown while processing the request.\n */\n EXCEPTION: 5,\n\n /**\n * The Http response returned a non-successful http status code.\n */\n HTTP_ERROR: 6,\n\n /**\n * The request was aborted.\n */\n ABORT: 7,\n\n /**\n * The request timed out.\n */\n TIMEOUT: 8,\n\n /**\n * The resource is not available offline.\n */\n OFFLINE: 9,\n};\n\n\n/**\n * Returns a friendly error message for an error code. These messages are for\n * debugging and are not localized.\n * @param {goog.net.ErrorCode} errorCode An error code.\n * @return {string} A message for debugging.\n */\ngoog.net.ErrorCode.getDebugMessage = function(errorCode) {\n switch (errorCode) {\n case goog.net.ErrorCode.NO_ERROR:\n return 'No Error';\n\n case goog.net.ErrorCode.ACCESS_DENIED:\n return 'Access denied to content document';\n\n case goog.net.ErrorCode.FILE_NOT_FOUND:\n return 'File not found';\n\n case goog.net.ErrorCode.FF_SILENT_ERROR:\n return 'Firefox silently errored';\n\n case goog.net.ErrorCode.CUSTOM_ERROR:\n return 'Application custom error';\n\n case goog.net.ErrorCode.EXCEPTION:\n return 'An exception occurred';\n\n case goog.net.ErrorCode.HTTP_ERROR:\n return 'Http response at 400 or 500 level';\n\n case goog.net.ErrorCode.ABORT:\n return 'Request was aborted';\n\n case goog.net.ErrorCode.TIMEOUT:\n return 'Request timed out';\n\n case goog.net.ErrorCode.OFFLINE:\n return 'The resource is not available offline';\n\n default:\n return 'Unrecognized error code';\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Common events for the network classes.\n */\n\n\ngoog.provide('goog.net.EventType');\n\n\n/**\n * Event names for network events\n * @enum {string}\n */\ngoog.net.EventType = {\n COMPLETE: 'complete',\n SUCCESS: 'success',\n ERROR: 'error',\n ABORT: 'abort',\n READY: 'ready',\n READY_STATE_CHANGE: 'readystatechange',\n TIMEOUT: 'timeout',\n INCREMENTAL_DATA: 'incrementaldata',\n PROGRESS: 'progress',\n // DOWNLOAD_PROGRESS and UPLOAD_PROGRESS are special events dispatched by\n // goog.net.XhrIo to allow binding listeners specific to each type of\n // progress.\n DOWNLOAD_PROGRESS: 'downloadprogress',\n UPLOAD_PROGRESS: 'uploadprogress',\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Interface for a factory for creating XMLHttpRequest objects\n * and metadata about them.\n */\n\ngoog.provide('goog.net.XmlHttpFactory');\n\n/** @suppress {extraRequire} Typedef. */\ngoog.require('goog.net.XhrLike');\n\n\n\n/**\n * Abstract base class for an XmlHttpRequest factory.\n * @constructor\n */\ngoog.net.XmlHttpFactory = function() {};\n\n\n/**\n * Cache of options - we only actually call internalGetOptions once.\n * @type {?Object}\n * @private\n */\ngoog.net.XmlHttpFactory.prototype.cachedOptions_ = null;\n\n\n/**\n * @return {!goog.net.XhrLike.OrNative} A new XhrLike instance.\n */\ngoog.net.XmlHttpFactory.prototype.createInstance = goog.abstractMethod;\n\n\n/**\n * @return {Object} Options describing how xhr objects obtained from this\n * factory should be used.\n */\ngoog.net.XmlHttpFactory.prototype.getOptions = function() {\n return this.cachedOptions_ ||\n (this.cachedOptions_ = this.internalGetOptions());\n};\n\n\n/**\n * Override this method in subclasses to preserve the caching offered by\n * getOptions().\n * @return {Object} Options describing how xhr objects obtained from this\n * factory should be used.\n * @protected\n */\ngoog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Low level handling of XMLHttpRequest.\n */\n\ngoog.provide('goog.net.DefaultXmlHttpFactory');\ngoog.provide('goog.net.XmlHttp');\ngoog.provide('goog.net.XmlHttp.OptionType');\ngoog.provide('goog.net.XmlHttp.ReadyState');\ngoog.provide('goog.net.XmlHttpDefines');\n\ngoog.require('goog.asserts');\ngoog.require('goog.net.WrapperXmlHttpFactory');\ngoog.require('goog.net.XmlHttpFactory');\n\n\n/**\n * Static class for creating XMLHttpRequest objects.\n * @return {!goog.net.XhrLike.OrNative} A new XMLHttpRequest object.\n */\ngoog.net.XmlHttp = function() {\n return goog.net.XmlHttp.factory_.createInstance();\n};\n\n\n/**\n * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to\n * true bypasses the ActiveX probing code.\n * NOTE(ruilopes): Due to the way JSCompiler works, this define *will not* strip\n * out the ActiveX probing code from binaries. To achieve this, use\n * `goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR` instead.\n * TODO(ruilopes): Collapse both defines.\n */\ngoog.net.XmlHttp.ASSUME_NATIVE_XHR =\n goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false);\n\n\n/** @const */\ngoog.net.XmlHttpDefines = {};\n\n\n/**\n * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to\n * true eliminates the ActiveX probing code.\n */\ngoog.net.XmlHttpDefines.ASSUME_NATIVE_XHR =\n goog.define('goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR', false);\n\n\n/**\n * Gets the options to use with the XMLHttpRequest objects obtained using\n * the static methods.\n * @return {Object} The options.\n */\ngoog.net.XmlHttp.getOptions = function() {\n return goog.net.XmlHttp.factory_.getOptions();\n};\n\n\n/**\n * Type of options that an XmlHttp object can have.\n * @enum {number}\n */\ngoog.net.XmlHttp.OptionType = {\n /**\n * Whether a goog.nullFunction should be used to clear the onreadystatechange\n * handler instead of null.\n */\n USE_NULL_FUNCTION: 0,\n\n /**\n * NOTE(user): In IE if send() errors on a *local* request the readystate\n * is still changed to COMPLETE. We need to ignore it and allow the\n * try/catch around send() to pick up the error.\n */\n LOCAL_REQUEST_ERROR: 1,\n};\n\n\n/**\n * Status constants for XMLHTTP, matches:\n * https://msdn.microsoft.com/en-us/library/ms534361(v=vs.85).aspx\n * @enum {number}\n */\ngoog.net.XmlHttp.ReadyState = {\n /**\n * Constant for when xmlhttprequest.readyState is uninitialized\n */\n UNINITIALIZED: 0,\n\n /**\n * Constant for when xmlhttprequest.readyState is loading.\n */\n LOADING: 1,\n\n /**\n * Constant for when xmlhttprequest.readyState is loaded.\n */\n LOADED: 2,\n\n /**\n * Constant for when xmlhttprequest.readyState is in an interactive state.\n */\n INTERACTIVE: 3,\n\n /**\n * Constant for when xmlhttprequest.readyState is completed\n */\n COMPLETE: 4,\n};\n\n\n/**\n * The global factory instance for creating XMLHttpRequest objects.\n * @type {goog.net.XmlHttpFactory}\n * @private\n */\ngoog.net.XmlHttp.factory_;\n\n\n/**\n * Sets the factories for creating XMLHttpRequest objects and their options.\n * @param {Function} factory The factory for XMLHttpRequest objects.\n * @param {Function} optionsFactory The factory for options.\n * @deprecated Use setGlobalFactory instead.\n */\ngoog.net.XmlHttp.setFactory = function(factory, optionsFactory) {\n goog.net.XmlHttp.setGlobalFactory(\n new goog.net.WrapperXmlHttpFactory(\n goog.asserts.assert(factory), goog.asserts.assert(optionsFactory)));\n};\n\n\n/**\n * Sets the global factory object.\n * @param {!goog.net.XmlHttpFactory} factory New global factory object.\n */\ngoog.net.XmlHttp.setGlobalFactory = function(factory) {\n goog.net.XmlHttp.factory_ = factory;\n};\n\n\n\n/**\n * Default factory to use when creating xhr objects. You probably shouldn't be\n * instantiating this directly, but rather using it via goog.net.XmlHttp.\n * @extends {goog.net.XmlHttpFactory}\n * @constructor\n */\ngoog.net.DefaultXmlHttpFactory = function() {\n goog.net.XmlHttpFactory.call(this);\n};\ngoog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory);\n\n\n/** @override */\ngoog.net.DefaultXmlHttpFactory.prototype.createInstance = function() {\n var progId = this.getProgId_();\n if (progId) {\n return new ActiveXObject(progId);\n } else {\n return new XMLHttpRequest();\n }\n};\n\n\n/** @override */\ngoog.net.DefaultXmlHttpFactory.prototype.internalGetOptions = function() {\n var progId = this.getProgId_();\n var options = {};\n if (progId) {\n options[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] = true;\n options[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] = true;\n }\n return options;\n};\n\n\n/**\n * The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.\n * @type {string|undefined}\n * @private\n */\ngoog.net.DefaultXmlHttpFactory.prototype.ieProgId_;\n\n\n/**\n * Initialize the private state used by other functions.\n * @return {string} The ActiveX PROG ID string to use to create xhr's in IE.\n * @private\n */\ngoog.net.DefaultXmlHttpFactory.prototype.getProgId_ = function() {\n if (goog.net.XmlHttp.ASSUME_NATIVE_XHR ||\n goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR) {\n return '';\n }\n\n // The following blog post describes what PROG IDs to use to create the\n // XMLHTTP object in Internet Explorer:\n // http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx\n // However we do not (yet) fully trust that this will be OK for old versions\n // of IE on Win9x so we therefore keep the last 2.\n if (!this.ieProgId_ && typeof XMLHttpRequest == 'undefined' &&\n typeof ActiveXObject != 'undefined') {\n // Candidate Active X types.\n var ACTIVE_X_IDENTS = [\n 'MSXML2.XMLHTTP.6.0',\n 'MSXML2.XMLHTTP.3.0',\n 'MSXML2.XMLHTTP',\n 'Microsoft.XMLHTTP',\n ];\n for (var i = 0; i < ACTIVE_X_IDENTS.length; i++) {\n var candidate = ACTIVE_X_IDENTS[i];\n\n try {\n new ActiveXObject(candidate);\n // NOTE(user): cannot assign progid and return candidate in one line\n // because JSCompiler complaings: BUG 658126\n this.ieProgId_ = candidate;\n return candidate;\n } catch (e) {\n // do nothing; try next choice\n }\n }\n\n // couldn't find any matches\n throw new Error(\n 'Could not create ActiveXObject. ActiveX might be disabled,' +\n ' or MSXML might not be installed');\n }\n\n return /** @type {string} */ (this.ieProgId_);\n};\n\n\n// Set the global factory to an instance of the default factory.\ngoog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory());\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview The API spec for the WebChannel messaging library.\n *\n * Similar to HTML5 WebSocket and Closure BrowserChannel, WebChannel\n * offers an abstraction for point-to-point socket-like communication between\n * a browser client and a remote origin.\n *\n * WebChannels are created via <code>WebChannel</code>. Multiple WebChannels\n * may be multiplexed over the same WebChannelTransport, which represents\n * the underlying physical connectivity over standard wire protocols\n * such as HTTP and SPDY.\n *\n * A WebChannels in turn represents a logical communication channel between\n * the client and server end point. A WebChannel remains open for\n * as long as the client or server end-point allows.\n *\n * Messages may be delivered in-order or out-of-order, reliably or unreliably\n * over the same WebChannel. Message delivery guarantees of a WebChannel is\n * to be specified by the application code; and the choice of the\n * underlying wire protocols is completely transparent to the API users.\n *\n * Client-to-client messaging via WebRTC based transport may also be support\n * via the same WebChannel API in future.\n *\n * Note that we have no immediate plan to move this API out of labs. While\n * the implementation is production ready, the API is subject to change\n * (addition only):\n * 1. Adopt new Web APIs (mainly whatwg streams) and goog.net.streams.\n * 2. New programming models for cloud (on the server-side) may require\n * new APIs to be defined.\n * 3. WebRTC DataChannel alignment\n */\n\ngoog.provide('goog.net.WebChannel');\n\ngoog.require('goog.events');\ngoog.require('goog.events.Event');\ngoog.require('goog.events.Listenable');\ngoog.require('goog.net.XmlHttpFactory');\n\n\n\n/**\n * A WebChannel represents a logical bi-directional channel over which the\n * client communicates with a remote server that holds the other endpoint\n * of the channel. A WebChannel is always created in the context of a shared\n * {@link WebChannelTransport} instance. It is up to the underlying client-side\n * and server-side implementations to decide how or when multiplexing is\n * to be enabled.\n *\n * @interface\n * @extends {EventTarget}\n * @extends {goog.events.Listenable}\n */\ngoog.net.WebChannel = function() {};\n\n\n\n/**\n * This interface defines a pluggable API to allow WebChannel runtime to support\n * customized algorithms in order to recover from transient failures such as\n * those failures caused by network or proxies (intermediaries).\n *\n * The algorithm may also choose to fail-fast, e.g. switch the client to some\n * offline mode.\n *\n * Extra measurements and logging could also be implemented in the custom\n * module, which has the full knowledge of all the state transitions\n * (due to failures).\n *\n * A default algorithm will be provided by the webchannel library itself. Custom\n * algorithms are expected to be tailored to specific client platforms or\n * networking environments, e.g. mobile, cellular network.\n *\n * @interface\n */\ngoog.net.WebChannel.FailureRecovery = function() {};\n\n\n/**\n * Configuration spec for newly created WebChannel instances.\n *\n * WebChannels are configured in the context of the containing\n * {@link WebChannelTransport}. The configuration parameters are specified\n * when a new instance of WebChannel is created via {@link WebChannelTransport}.\n *\n * messageHeaders: custom headers to be added to every message sent to the\n * server. This object is mutable, and custom headers may be changed, removed,\n * or added during the runtime after a channel has been opened.\n *\n * initMessageHeaders: similar to messageHeaders, but any custom headers will\n * be sent only once when the channel is opened. Typical usage is to send\n * an auth header to the server, which only checks the auth header at the time\n * when the channel is opened.\n *\n * messageContentType: sent as initMessageHeaders via X-WebChannel-Content-Type,\n * to inform the server the MIME type of WebChannel messages.\n *\n * messageUrlParams: custom url query parameters to be added to every message\n * sent to the server. This object is mutable, and custom parameters may be\n * changed, removed or added during the runtime after a channel has been opened.\n *\n * clientProtocolHeaderRequired: whether a special header should be added to\n * each message so that the server can dispatch webchannel messages without\n * knowing the URL path prefix. Defaults to false.\n *\n * concurrentRequestLimit: the maximum number of in-flight HTTP requests allowed\n * when SPDY is enabled. Currently we only detect SPDY in Chrome.\n * This parameter defaults to 10. When SPDY is not enabled, this parameter\n * will have no effect.\n *\n * supportsCrossDomainXhr: setting this to true to allow the use of sub-domains\n * (as configured by the server) to send XHRs with the CORS withCredentials\n * bit set to true.\n *\n * sendRawJson: whether to bypass v8 encoding of client-sent messages.\n * This defaults to false now due to legacy servers. New applications should\n * always configure this option to true.\n *\n * httpSessionIdParam: the URL parameter name that contains the session id (\n * for sticky routing of HTTP requests). When this param is specified, a server\n * that supports this option will respond with an opaque session id as part of\n * the initial handshake (via the X-HTTP-Session-Id header); and all the\n * subsequent requests will contain the httpSessionIdParam. This option will\n * take precedence over any duplicated parameter specified with\n * messageUrlParams, whose value will be ignored.\n *\n * httpHeadersOverwriteParam: the URL parameter name to allow custom HTTP\n * headers to be overwritten as a URL param to bypass CORS preflight.\n * goog.net.rpc.HttpCors is used to encode the HTTP headers.\n *\n * forceLongPolling: whether to force long-polling from client to server.\n * This defaults to false. Long-polling may be necessary when a (MITM) proxy\n * is buffering data sent by the server.\n *\n * fastHandshake: enable true 0-RTT message delivery, including\n * leveraging QUIC 0-RTT (which requires GET to be used). This option\n * defaults to false. Note it is allowed to send messages before Open event is\n * received, after a channel has been opened. In order to enable 0-RTT,\n * messages will be encoded as part of URL and therefore there needs be a size\n * limit for those initial messages that are sent immediately as part of the\n * GET handshake request. With sendRawJson=true, this limit is currently set\n * to 4K chars and data beyond this limit will be buffered till the handshake\n * (1-RTT) finishes. With sendRawJson=false, it's up to the application\n * to limit the amount of data that is sent as part of the handshake.\n *\n * disableRedact: whether to disable logging redact. By default, redact is\n * enabled to remove any message payload or user-provided info\n * from closure logs.\n *\n * clientProfile: inform the server about the client profile to enable\n * customized configs that are optimized for certain clients or environments.\n * Currently this information is sent via X-WebChannel-Client-Profile header.\n *\n * internalChannelParams: the internal channel parameter name to allow\n * experimental channel configurations. Supported options include fastfail,\n * baseRetryDelayMs, retryDelaySeedMs, forwardChannelMaxRetries and\n * forwardChannelRequestTimeoutMs. Note that these options are subject to\n * change.\n *\n * xmlHttpFactory: allows the caller to override the factory used to create\n * XMLHttpRequest objects. This is introduced to disable CORS on firefox OS.\n *\n * requestRefreshThresholds: client-side thresholds that decide when to refresh\n * an underlying HTTP request, to limit memory consumption due to XHR buffering\n * or compression context. The client-side thresholds should be signficantly\n * smaller than the server-side thresholds. This allows the client to eliminate\n * any latency introduced by request refreshing, i.e. an RTT window during which\n * messages may be buffered on the server-side. Supported params include\n * totalBytesReceived, totalDurationMs.\n *\n * @typedef {{\n * messageHeaders: (!Object<string, string>|undefined),\n * initMessageHeaders: (!Object<string, string>|undefined),\n * messageContentType: (string|undefined),\n * messageUrlParams: (!Object<string, string>|undefined),\n * clientProtocolHeaderRequired: (boolean|undefined),\n * concurrentRequestLimit: (number|undefined),\n * supportsCrossDomainXhr: (boolean|undefined),\n * testUrl: (string|undefined),\n * sendRawJson: (boolean|undefined),\n * httpSessionIdParam: (string|undefined),\n * httpHeadersOverwriteParam: (string|undefined),\n * backgroundChannelTest: (boolean|undefined),\n * forceLongPolling: (boolean|undefined),\n * fastHandshake: (boolean|undefined),\n * disableRedact: (boolean|undefined),\n * clientProfile: (string|undefined),\n * internalChannelParams: (!Object<string, boolean|number>|undefined),\n * xmlHttpFactory: (!goog.net.XmlHttpFactory|undefined),\n * requestRefreshThresholds: (!Object<string, number>|undefined),\n * }}\n */\ngoog.net.WebChannel.Options;\n\n\n/**\n * Types that are allowed as message data.\n *\n * Note that JS objects (sent by the client) can only have string encoded\n * values due to the limitation of the current wire protocol.\n *\n * Unicode strings (sent by the server) may or may not need be escaped, as\n * decided by the server.\n *\n * @typedef {(!ArrayBuffer|!Blob|!Object<string, !Object|string>|!Array|string)}\n */\ngoog.net.WebChannel.MessageData;\n\n\n/**\n * Open the WebChannel against the URI specified in the constructor.\n */\ngoog.net.WebChannel.prototype.open = goog.abstractMethod;\n\n\n/**\n * Close the WebChannel.\n *\n * This is a full close (shutdown) with no guarantee of FIFO delivery in respect\n * to any in-flight messages sent to the server.\n *\n * If you need such a guarantee, see the Half the halfClose() method.\n */\ngoog.net.WebChannel.prototype.close = goog.abstractMethod;\n\n\n/**\n * Half-close the WebChannel.\n *\n * Half-close semantics:\n * 1. delivered as a regular message in FIFO programming order\n * 2. the server is expected to return a half-close too (with or without\n * application involved), which will trigger a full close (shutdown)\n * on the client side\n * 3. for now, the half-close event defined for server-initiated\n * half-close is not exposed to the client application\n * 4. a client-side half-close may be triggered internally when the client\n * receives a half-close from the server; and the client is expected to\n * do a full close after the half-close is acked and delivered\n * on the server-side.\n * 5. Full close is always a forced one. See the close() method.\n *\n * New messages sent after halfClose() will be dropped.\n *\n * NOTE: This is not yet implemented, and will throw an exception if called.\n */\ngoog.net.WebChannel.prototype.halfClose = goog.abstractMethod;\n\n\n/**\n * Sends a message to the server that maintains the other end point of\n * the WebChannel.\n *\n * O-RTT behavior:\n * 1. messages sent before open() is called will always be delivered as\n * part of the handshake, i.e. with 0-RTT\n * 2. messages sent after open() is called but before the OPEN event\n * is received will be delivered as part of the handshake if\n * send() is called from the same execution context as open().\n * 3. otherwise, those messages will be buffered till the handshake\n * is completed (which will fire the OPEN event).\n *\n * @param {!goog.net.WebChannel.MessageData} message The message to send.\n */\ngoog.net.WebChannel.prototype.send = goog.abstractMethod;\n\n\n/**\n * Common events fired by WebChannels.\n * @enum {string}\n */\ngoog.net.WebChannel.EventType = {\n /** Dispatched when the channel is opened. */\n OPEN: goog.events.getUniqueId('open'),\n\n /** Dispatched when the channel is closed. */\n CLOSE: goog.events.getUniqueId('close'),\n\n /**\n * Dispatched when the channel is aborted due to errors.\n *\n * For backward compatibility reasons, a CLOSE event will also be\n * dispatched, following the ERROR event, which indicates that the channel\n * has been completely shutdown .\n */\n ERROR: goog.events.getUniqueId('error'),\n\n /** Dispatched when the channel has received a new message. */\n MESSAGE: goog.events.getUniqueId('message')\n};\n\n\n\n/**\n * The event interface for the MESSAGE event.\n *\n * @constructor\n * @extends {goog.events.Event}\n */\ngoog.net.WebChannel.MessageEvent = function() {\n goog.net.WebChannel.MessageEvent.base(\n this, 'constructor', goog.net.WebChannel.EventType.MESSAGE);\n};\ngoog.inherits(goog.net.WebChannel.MessageEvent, goog.events.Event);\n\n\n/**\n * The content of the message received from the server.\n *\n * @type {!goog.net.WebChannel.MessageData}\n */\ngoog.net.WebChannel.MessageEvent.prototype.data;\n\n\n/**\n * The metadata key when the MESSAGE event represents a metadata message.\n *\n * @type {string|undefined}\n */\ngoog.net.WebChannel.MessageEvent.prototype.metadataKey;\n\n\n/**\n * WebChannel level error conditions.\n *\n * Summary of error debugging and reporting in WebChannel:\n *\n * Network Error\n * 1. By default the webchannel library will set the error status to\n * NETWORK_ERROR when a channel has to be aborted or closed. NETWORK_ERROR\n * may be recovered by the application by retrying and opening a new channel.\n * 2. There may be lost messages (not acked by the server) when a channel is\n * aborted. Currently we don't have a public API to retrieve messages that\n * are waiting to be acked on the client side. File a bug if you think it\n * is useful to expose such an API.\n * 3. Details of why a channel fails are available via closure debug logs,\n * and stats events (see webchannel/requeststats.js). Those are internal\n * stats and are subject to change. File a bug if you think it's useful to\n * version and expose such stats as part of the WebChannel API.\n *\n * Server Error\n * 1. SERVER_ERROR is intended to indicate a non-recoverable condition, e.g.\n * when auth fails.\n * 2. We don't currently generate any such errors, because most of the time\n * it's the responsibility of upper-layer frameworks or the application\n * itself to indicate to the client why a webchannel has been failed\n * by the server.\n * 3. When a channel is failed by the server explicitly, we still signal\n * NETWORK_ERROR to the client. Explicit server failure may happen when the\n * server does a fail-over, or becomes overloaded, or conducts a forced\n * shutdown etc.\n * 4. We use some heuristic to decide if the network (aka cloud) is down\n * v.s. the actual server is down.\n *\n * RuntimeProperties.getLastStatusCode is a useful state that we expose to\n * the client to indicate the HTTP response status code of the last HTTP\n * request initiated by the WebChannel client library, for debugging\n * purposes only.\n *\n * See WebChannel.Options.backChannelFailureRecovery and\n * WebChannel.FailureRecovery to install a custom failure-recovery algorithm.\n *\n * @enum {number}\n */\ngoog.net.WebChannel.ErrorStatus = {\n /** No error has occurred. */\n OK: 0,\n\n /** Communication to the server has failed. */\n NETWORK_ERROR: 1,\n\n /** The server fails to accept or process the WebChannel. */\n SERVER_ERROR: 2\n};\n\n\n\n/**\n * The event interface for the ERROR event.\n *\n * @constructor\n * @extends {goog.events.Event}\n */\ngoog.net.WebChannel.ErrorEvent = function() {\n goog.net.WebChannel.ErrorEvent.base(\n this, 'constructor', goog.net.WebChannel.EventType.ERROR);\n};\ngoog.inherits(goog.net.WebChannel.ErrorEvent, goog.events.Event);\n\n\n/**\n * The error status.\n *\n * @type {!goog.net.WebChannel.ErrorStatus}\n */\ngoog.net.WebChannel.ErrorEvent.prototype.status;\n\n\n/**\n * @return {!goog.net.WebChannel.RuntimeProperties} The runtime properties\n * of the WebChannel instance.\n */\ngoog.net.WebChannel.prototype.getRuntimeProperties = goog.abstractMethod;\n\n\n\n/**\n * The runtime properties of the WebChannel instance.\n *\n * This class is defined for debugging and monitoring purposes, as well as for\n * runtime functions that the application may choose to manage by itself.\n *\n * @interface\n */\ngoog.net.WebChannel.RuntimeProperties = function() {};\n\n\n/**\n * @return {number} The effective limit for the number of concurrent HTTP\n * requests that are allowed to be made for sending messages from the client\n * to the server. When SPDY is not enabled, this limit will be one.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.getConcurrentRequestLimit =\n goog.abstractMethod;\n\n\n/**\n * For applications that need support multiple channels (e.g. from\n * different tabs) to the same origin, use this method to decide if SPDY is\n * enabled and therefore it is safe to open multiple channels.\n *\n * If SPDY is disabled, the application may choose to limit the number of active\n * channels to one or use other means such as sub-domains to work around\n * the browser connection limit.\n *\n * @return {boolean} Whether SPDY is enabled for the origin against which\n * the channel is created.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.isSpdyEnabled =\n goog.abstractMethod;\n\n\n/**\n * @return {number} The number of requests (for sending messages to the server)\n * that are pending. If this number is approaching the value of\n * getConcurrentRequestLimit(), client-to-server message delivery may experience\n * a higher latency.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.getPendingRequestCount =\n goog.abstractMethod;\n\n\n/**\n * For applications to query the current HTTP session id, sent by the server\n * during the initial handshake.\n *\n * @return {?string} the HTTP session id or null if no HTTP session is in use.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.getHttpSessionId =\n goog.abstractMethod;\n\n\n/**\n * Experimental API.\n *\n * This method generates an in-band commit request to the server, which will\n * ack the commit request as soon as all messages sent prior to this commit\n * request have been committed by the application.\n *\n * Committing a message has a stronger semantics than delivering a message\n * to the application. Detail spec:\n * https://github.com/bidiweb/webchannel/blob/master/commit.md\n *\n * Timeout or cancellation is not supported and the application is expected to\n * abort the channel if the commit-ack fails to arrive in time.\n *\n * ===\n *\n * This is currently implemented only in the client layer and the commit\n * callback will be invoked after all the pending client-sent messages have been\n * delivered by the server-side webchannel end-point. This semantics is\n * different and weaker than what's required for end-to-end ack which requires\n * the server application to ack the in-order delivery of messages that are sent\n * before the commit request is issued.\n *\n * Commit should only be called after the channel open event is received.\n * Duplicated commits are allowed and only the last callback is guaranteed.\n * Commit called after the channel has been closed will be ignored.\n *\n * @param {function()} callback The callback will be invoked once an\n * ack has been received for the current commit or any newly issued commit.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.commit = goog.abstractMethod;\n\n\n/**\n * This method may be used by the application to recover from a peer failure\n * or to enable sender-initiated flow-control.\n *\n * Detail spec: https://github.com/bidiweb/webchannel/blob/master/commit.md\n *\n * This is not yet implemented.\n *\n * @return {number} The total number of messages that have not received\n * commit-ack from the server; or if no commit has been issued, the number\n * of messages that have not been delivered to the server application.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.getNonAckedMessageCount =\n goog.abstractMethod;\n\n\n/**\n * A low water-mark message count to notify the application when the\n * flow-control condition is cleared, that is, when the application is\n * able to send more messages.\n *\n * We expect the application to configure a high water-mark message count,\n * which is checked via getNonAckedMessageCount(). When the high water-mark\n * is exceeded, the application should install a callback via this method\n * to be notified when to start to send new messages.\n *\n * This is not yet implemented.\n *\n * @param {number} count The low water-mark count. It is an error to pass\n * a non-positive value.\n * @param {function()} callback The call back to notify the application\n * when NonAckedMessageCount is below the specified low water-mark count.\n * Any previously registered callback is cleared. This new callback will\n * be cleared once it has been fired, or when the channel is closed or aborted.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.notifyNonAckedMessageCount =\n goog.abstractMethod;\n\n\n/**\n * Experimental API.\n *\n * This method registers a callback to handle the commit request sent\n * by the server. Commit protocol spec:\n * https://github.com/bidiweb/webchannel/blob/master/commit.md\n *\n * This is not yet implemented.\n *\n * @param {function(!Object)} callback The callback will take an opaque\n * commitId which needs be passed back to the server when an ack-commit\n * response is generated by the client application, via ackCommit().\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.onCommit = goog.abstractMethod;\n\n\n/**\n * Experimental API.\n *\n * This method is used by the application to generate an ack-commit response\n * for the given commitId. Commit protocol spec:\n * https://github.com/bidiweb/webchannel/blob/master/commit.md\n *\n * This is not yet implemented.\n *\n * @param {!Object} commitId The commitId which denotes the commit request\n * from the server that needs be ack'ed.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.ackCommit = goog.abstractMethod;\n\n\n/**\n * @return {number} The last HTTP status code received by the channel.\n */\ngoog.net.WebChannel.RuntimeProperties.prototype.getLastStatusCode =\n goog.abstractMethod;\n\n\n/**\n * Enum to indicate the current recovery state.\n *\n * @enum {string}\n */\ngoog.net.WebChannel.FailureRecovery.State = {\n /** Initial state. */\n INIT: 'init',\n\n /** Once a failure has been detected. */\n FAILED: 'failed',\n\n /**\n * Once a recovery operation has been issued, e.g. a new request to resume\n * communication.\n */\n RECOVERING: 'recovering',\n\n /** The channel has been closed. */\n CLOSED: 'closed'\n};\n\n\n/**\n * Enum to indicate different failure conditions as detected by the webchannel\n * runtime.\n *\n * This enum is to be used only between the runtime and FailureRecovery module,\n * and new states are expected to be introduced in future.\n *\n * @enum {string}\n */\ngoog.net.WebChannel.FailureRecovery.FailureCondition = {\n /**\n * The HTTP response returned a non-successful http status code.\n */\n HTTP_ERROR: 'http_error',\n\n /**\n * The request was aborted.\n */\n ABORT: 'abort',\n\n /**\n * The request timed out.\n */\n TIMEOUT: 'timeout',\n\n /**\n * Exception was thrown while processing the request/response.\n */\n EXCEPTION: 'exception'\n};\n\n\n/**\n * @return {!goog.net.WebChannel.FailureRecovery.State} the current state,\n * mainly for debugging use.\n */\ngoog.net.WebChannel.FailureRecovery.prototype.getState = goog.abstractMethod;\n\n\n/**\n * This method is for WebChannel runtime to set the current failure condition\n * and to provide a callback for the algorithm to signal to the runtime\n * when it is time to issue a recovery operation, e.g. a new request to the\n * server.\n *\n * Supported transitions include:\n * INIT->FAILED\n * FAILED->FAILED (re-entry ok)\n * RECOVERY->FAILED.\n *\n * Ignored if state == CLOSED.\n *\n * Advanced implementations are expected to track all the state transitions\n * and their timestamps for monitoring purposes.\n *\n * @param {!goog.net.WebChannel.FailureRecovery.FailureCondition} failure The\n * new failure condition generated by the WebChannel runtime.\n * @param {!Function} operation The callback function to the WebChannel\n * runtime to issue a recovery operation, e.g. a new request. E.g. the default\n * recovery algorithm will issue timeout-based recovery operations.\n * Post-condition for the callback: state transition to RECOVERING.\n *\n * @return {!goog.net.WebChannel.FailureRecovery.State} The updated state\n * as decided by the failure recovery module. Upon a recoverable failure event,\n * the state is transitioned to RECOVERING; or the state is transitioned to\n * FAILED which indicates a fail-fast decision for the runtime to execute.\n */\ngoog.net.WebChannel.FailureRecovery.prototype.setFailure = goog.abstractMethod;\n\n\n/**\n * The Webchannel runtime needs call this method when webchannel is closed or\n * aborted.\n *\n * Once the instance is closed, any access to the instance will be a no-op.\n */\ngoog.net.WebChannel.FailureRecovery.prototype.close = goog.abstractMethod;\n\n\n/**\n * A request header to indicate to the server the messaging protocol\n * each HTTP message is speaking.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_CLIENT_PROTOCOL = 'X-Client-Protocol';\n\n\n/**\n * The value for x-client-protocol when the messaging protocol is WebChannel.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL = 'webchannel';\n\n\n/**\n * A response header for the server to signal the wire-protocol that\n * the browser establishes with the server (or proxy), e.g. \"spdy\" (aka http/2)\n * \"quic\". This information avoids the need to use private APIs to decide if\n * HTTP requests are multiplexed etc.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_CLIENT_WIRE_PROTOCOL = 'X-Client-Wire-Protocol';\n\n\n/**\n * A response header for the server to send back the HTTP session id as part of\n * the initial handshake. The value of the HTTP session id is opaque to the\n * WebChannel protocol.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_HTTP_SESSION_ID = 'X-HTTP-Session-Id';\n\n\n/**\n * A response header for the server to send back any initial response data as a\n * header to avoid any possible buffering by an intermediary, which may\n * be undesired during the handshake.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_HTTP_INITIAL_RESPONSE = 'X-HTTP-Initial-Response';\n\n\n/**\n * A request header for specifying the content-type of WebChannel messages,\n * e.g. application-defined JSON encoding styles. Currently this header\n * is sent by the client via initMessageHeaders when the channel is opened.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_WEBCHANNEL_CONTENT_TYPE = 'X-WebChannel-Content-Type';\n\n\n/**\n * A request header for specifying the client profile in order to apply\n * customized config params on the server side, e.g. timeouts.\n *\n * @type {string}\n */\ngoog.net.WebChannel.X_WEBCHANNEL_CLIENT_PROFILE = 'X-WebChannel-Client-Profile';\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Definition of the ChannelRequest class. The request\n * object encapsulates the logic for making a single request, either for the\n * forward channel, back channel, or test channel, to the server. It contains\n * the logic for the two types of transports we use:\n * XMLHTTP and Image request. It provides timeout detection. More transports\n * to be added in future, such as Fetch, WebSocket.\n *\n */\n\n\ngoog.provide('goog.labs.net.webChannel.ChannelRequest');\n\ngoog.require('goog.Timer');\ngoog.require('goog.async.Throttle');\ngoog.require('goog.events.EventHandler');\ngoog.require('goog.labs.net.webChannel.Channel');\ngoog.require('goog.labs.net.webChannel.WebChannelDebug');\ngoog.require('goog.labs.net.webChannel.environment');\ngoog.require('goog.labs.net.webChannel.requestStats');\ngoog.require('goog.net.ErrorCode');\ngoog.require('goog.net.EventType');\ngoog.require('goog.net.WebChannel');\ngoog.require('goog.net.XmlHttp');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.userAgent');\ngoog.requireType('goog.Uri');\ngoog.requireType('goog.net.XhrIo');\n\n\n\n/**\n * A new ChannelRequest is created for each request to the server.\n *\n * @param {goog.labs.net.webChannel.Channel} channel\n * The channel that owns this request.\n * @param {goog.labs.net.webChannel.WebChannelDebug} channelDebug A\n * WebChannelDebug to use for logging.\n * @param {string=} opt_sessionId The session id for the channel.\n * @param {string|number=} opt_requestId The request id for this request.\n * @param {number=} opt_retryId The retry id for this request.\n * @constructor\n * @struct\n * @final\n */\ngoog.labs.net.webChannel.ChannelRequest = function(\n channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId) {\n /**\n * The channel object that owns the request.\n * @private {goog.labs.net.webChannel.Channel}\n */\n this.channel_ = channel;\n\n /**\n * The channel debug to use for logging\n * @private {goog.labs.net.webChannel.WebChannelDebug}\n */\n this.channelDebug_ = channelDebug;\n\n /**\n * The Session ID for the channel.\n * @private {string|undefined}\n */\n this.sid_ = opt_sessionId;\n\n /**\n * The RID (request ID) for the request.\n * @private {string|number|undefined}\n */\n this.rid_ = opt_requestId;\n\n /**\n * The attempt number of the current request.\n * @private {number}\n */\n this.retryId_ = opt_retryId || 1;\n\n /**\n * An object to keep track of the channel request event listeners.\n * @private {!goog.events.EventHandler<\n * !goog.labs.net.webChannel.ChannelRequest>}\n */\n this.eventHandler_ = new goog.events.EventHandler(this);\n\n /**\n * The timeout in ms before failing the request.\n * @private {number}\n */\n this.timeout_ = goog.labs.net.webChannel.ChannelRequest.TIMEOUT_MS_;\n\n /**\n * A timer for polling responseText in browsers that don't fire\n * onreadystatechange during incremental loading of responseText.\n * @private {goog.Timer}\n */\n this.pollingTimer_ =\n new goog.Timer(goog.labs.net.webChannel.environment.getPollingInterval());\n\n /**\n * Extra HTTP headers to add to all the requests sent to the server.\n * @private {?Object}\n */\n this.extraHeaders_ = null;\n\n\n /**\n * Whether the request was successful. This is only set to true after the\n * request successfully completes.\n * @private {boolean}\n */\n this.successful_ = false;\n\n\n /**\n * The TimerID of the timer used to detect if the request has timed-out.\n * @type {?number}\n * @private\n */\n this.watchDogTimerId_ = null;\n\n /**\n * The time in the future when the request will timeout.\n * @private {?number}\n */\n this.watchDogTimeoutTime_ = null;\n\n /**\n * The time the request started.\n * @private {?number}\n */\n this.requestStartTime_ = null;\n\n /**\n * The type of request (XMLHTTP, IMG)\n * @private {?number}\n */\n this.type_ = null;\n\n /**\n * The base Uri for the request. The includes all the parameters except the\n * one that indicates the retry number.\n * @private {?goog.Uri}\n */\n this.baseUri_ = null;\n\n /**\n * The request Uri that was actually used for the most recent request attempt.\n * @private {?goog.Uri}\n */\n this.requestUri_ = null;\n\n /**\n * The post data, if the request is a post.\n * @private {?string}\n */\n this.postData_ = null;\n\n /**\n * An array of pending messages that we have either received a non-successful\n * response for, or no response at all, and which therefore may or may not\n * have been received by the server.\n * @private {!Array<goog.labs.net.webChannel.Wire.QueuedMap>}\n */\n this.pendingMessages_ = [];\n\n /**\n * The XhrLte request if the request is using XMLHTTP\n * @private {?goog.net.XhrIo}\n */\n this.xmlHttp_ = null;\n\n /**\n * The position of where the next unprocessed chunk starts in the response\n * text.\n * @private {number}\n */\n this.xmlHttpChunkStart_ = 0;\n\n /**\n * The verb (Get or Post) for the request.\n * @private {?string}\n */\n this.verb_ = null;\n\n /**\n * The last error if the request failed.\n * @private {?goog.labs.net.webChannel.ChannelRequest.Error}\n */\n this.lastError_ = null;\n\n /**\n * The last status code received.\n * @private {number}\n */\n this.lastStatusCode_ = -1;\n\n /**\n * Whether the request has been cancelled due to a call to cancel.\n * @private {boolean}\n */\n this.cancelled_ = false;\n\n /**\n * A throttle time in ms for readystatechange events for the backchannel.\n * Useful for throttling when ready state is INTERACTIVE (partial data).\n * If set to zero no throttle is used.\n *\n * See WebChannelBase.prototype.readyStateChangeThrottleMs_\n *\n * @private {number}\n */\n this.readyStateChangeThrottleMs_ = 0;\n\n /**\n * The throttle for readystatechange events for the current request, or null\n * if there is none.\n * @private {?goog.async.Throttle}\n */\n this.readyStateChangeThrottle_ = null;\n\n /**\n * Whether to the result is expected to be encoded for chunking and thus\n * requires decoding.\n * @private {boolean}\n */\n this.decodeChunks_ = false;\n\n /**\n * Whether to decode x-http-initial-response.\n * @private {boolean}\n */\n this.decodeInitialResponse_ = false;\n\n /**\n * Whether x-http-initial-response has been decoded (dispatched).\n * @private {boolean}\n */\n this.initialResponseDecoded_ = false;\n};\n\n\ngoog.scope(function() {\nvar WebChannel = goog.net.WebChannel;\nvar Channel = goog.labs.net.webChannel.Channel;\nvar ChannelRequest = goog.labs.net.webChannel.ChannelRequest;\nvar requestStats = goog.labs.net.webChannel.requestStats;\nvar WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;\nvar environment = goog.labs.net.webChannel.environment;\n\n\n/**\n * Default timeout in MS for a request. The server must return data within this\n * time limit for the request to not timeout.\n * @private {number}\n */\nChannelRequest.TIMEOUT_MS_ = 45 * 1000;\n\n\n/**\n * Enum for channel requests type\n * @enum {number}\n * @private\n */\nChannelRequest.Type_ = {\n /**\n * XMLHTTP requests.\n */\n XML_HTTP: 1,\n\n /**\n * IMG requests.\n */\n CLOSE_REQUEST: 2\n};\n\n\n/**\n * Enum type for identifying an error.\n * @enum {number}\n */\nChannelRequest.Error = {\n /**\n * Errors due to a non-200 status code.\n */\n STATUS: 0,\n\n /**\n * Errors due to no data being returned.\n */\n NO_DATA: 1,\n\n /**\n * Errors due to a timeout.\n */\n TIMEOUT: 2,\n\n /**\n * Errors due to the server returning an unknown.\n */\n UNKNOWN_SESSION_ID: 3,\n\n /**\n * Errors due to bad data being received.\n */\n BAD_DATA: 4,\n\n /**\n * Errors due to the handler throwing an exception.\n */\n HANDLER_EXCEPTION: 5,\n\n /**\n * The browser declared itself offline during the request.\n */\n BROWSER_OFFLINE: 6\n};\n\n\n/**\n * Returns a useful error string for debugging based on the specified error\n * code.\n * @param {?ChannelRequest.Error} errorCode The error code.\n * @param {number} statusCode The HTTP status code.\n * @return {string} The error string for the given code combination.\n */\nChannelRequest.errorStringFromCode = function(errorCode, statusCode) {\n switch (errorCode) {\n case ChannelRequest.Error.STATUS:\n return 'Non-200 return code (' + statusCode + ')';\n case ChannelRequest.Error.NO_DATA:\n return 'XMLHTTP failure (no data)';\n case ChannelRequest.Error.TIMEOUT:\n return 'HttpConnection timeout';\n default:\n return 'Unknown error';\n }\n};\n\n\n/**\n * Sentinel value used to indicate an invalid chunk in a multi-chunk response.\n * @private {Object}\n */\nChannelRequest.INVALID_CHUNK_ = {};\n\n\n/**\n * Sentinel value used to indicate an incomplete chunk in a multi-chunk\n * response.\n * @private {Object}\n */\nChannelRequest.INCOMPLETE_CHUNK_ = {};\n\n\n/**\n * Returns whether XHR streaming is supported on this browser.\n *\n * @return {boolean} Whether XHR streaming is supported.\n * @see http://code.google.com/p/closure-library/issues/detail?id=346\n */\nChannelRequest.supportsXhrStreaming = function() {\n return !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(10);\n};\n\n\n/**\n * Sets extra HTTP headers to add to all the requests sent to the server.\n *\n * @param {Object} extraHeaders The HTTP headers.\n */\nChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {\n this.extraHeaders_ = extraHeaders;\n};\n\n\n/**\n * Overrides the default HTTP method.\n *\n * @param {string} verb The HTTP method\n */\nChannelRequest.prototype.setVerb = function(verb) {\n this.verb_ = verb;\n};\n\n\n/**\n * Sets the timeout for a request\n *\n * @param {number} timeout The timeout in MS for when we fail the request.\n */\nChannelRequest.prototype.setTimeout = function(timeout) {\n this.timeout_ = timeout;\n};\n\n\n/**\n * Sets the throttle for handling onreadystatechange events for the request.\n *\n * @param {number} throttle The throttle in ms. A value of zero indicates\n * no throttle.\n */\nChannelRequest.prototype.setReadyStateChangeThrottle = function(throttle) {\n this.readyStateChangeThrottleMs_ = throttle;\n};\n\n\n/**\n * Sets the pending messages that this request is handling.\n *\n * @param {!Array<goog.labs.net.webChannel.Wire.QueuedMap>} pendingMessages\n * The pending messages for this request.\n */\nChannelRequest.prototype.setPendingMessages = function(pendingMessages) {\n this.pendingMessages_ = pendingMessages;\n};\n\n\n/**\n * Gets the pending messages that this request is handling, in case of a retry.\n *\n * @return {!Array<goog.labs.net.webChannel.Wire.QueuedMap>} The pending\n * messages for this request.\n */\nChannelRequest.prototype.getPendingMessages = function() {\n return this.pendingMessages_;\n};\n\n\n/**\n * Uses XMLHTTP to send an HTTP POST to the server.\n *\n * @param {goog.Uri} uri The uri of the request.\n * @param {?string} postData The data for the post body.\n * @param {boolean} decodeChunks Whether to the result is expected to be\n * encoded for chunking and thus requires decoding.\n */\nChannelRequest.prototype.xmlHttpPost = function(uri, postData, decodeChunks) {\n this.type_ = ChannelRequest.Type_.XML_HTTP;\n this.baseUri_ = uri.clone().makeUnique();\n this.postData_ = postData;\n this.decodeChunks_ = decodeChunks;\n this.sendXmlHttp_(null /* hostPrefix */);\n};\n\n\n/**\n * Uses XMLHTTP to send an HTTP GET to the server.\n *\n * @param {goog.Uri} uri The uri of the request.\n * @param {boolean} decodeChunks Whether to the result is expected to be\n * encoded for chunking and thus requires decoding.\n * @param {?string} hostPrefix The host prefix, if we might be using a\n * secondary domain. Note that it should also be in the URL, adding this\n * won't cause it to be added to the URL.\n */\nChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks, hostPrefix) {\n this.type_ = ChannelRequest.Type_.XML_HTTP;\n this.baseUri_ = uri.clone().makeUnique();\n this.postData_ = null;\n this.decodeChunks_ = decodeChunks;\n\n this.sendXmlHttp_(hostPrefix);\n};\n\n\n/**\n * Sends a request via XMLHTTP according to the current state of the request\n * object.\n *\n * @param {?string} hostPrefix The host prefix, if we might be using a secondary\n * domain.\n * @private\n */\nChannelRequest.prototype.sendXmlHttp_ = function(hostPrefix) {\n this.requestStartTime_ = goog.now();\n this.ensureWatchDogTimer_();\n\n // clone the base URI to create the request URI. The request uri has the\n // attempt number as a parameter which helps in debugging.\n this.requestUri_ = this.baseUri_.clone();\n this.requestUri_.setParameterValues('t', this.retryId_);\n\n // send the request either as a POST or GET\n this.xmlHttpChunkStart_ = 0;\n var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains();\n this.xmlHttp_ =\n this.channel_.createXhrIo(useSecondaryDomains ? hostPrefix : null);\n\n if (this.readyStateChangeThrottleMs_ > 0) {\n this.readyStateChangeThrottle_ = new goog.async.Throttle(\n goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_),\n this.readyStateChangeThrottleMs_);\n }\n\n this.eventHandler_.listen(\n this.xmlHttp_, goog.net.EventType.READY_STATE_CHANGE,\n this.readyStateChangeHandler_);\n\n var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {};\n if (this.postData_) {\n if (!this.verb_) {\n this.verb_ = 'POST';\n }\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n this.xmlHttp_.send(this.requestUri_, this.verb_, this.postData_, headers);\n } else {\n this.verb_ = 'GET';\n this.xmlHttp_.send(this.requestUri_, this.verb_, null, headers);\n }\n requestStats.notifyServerReachabilityEvent(\n requestStats.ServerReachability.REQUEST_MADE);\n this.channelDebug_.xmlHttpChannelRequest(\n this.verb_, this.requestUri_, this.rid_, this.retryId_, this.postData_);\n};\n\n\n/**\n * Handles a readystatechange event.\n * @param {goog.events.Event} evt The event.\n * @private\n */\nChannelRequest.prototype.readyStateChangeHandler_ = function(evt) {\n var xhr = /** @type {goog.net.XhrIo} */ (evt.target);\n var throttle = this.readyStateChangeThrottle_;\n if (throttle &&\n xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) {\n // Only throttle in the partial data case.\n this.channelDebug_.debug('Throttling readystatechange.');\n throttle.fire();\n } else {\n // If we haven't throttled, just handle response directly.\n this.xmlHttpHandler_(xhr);\n }\n};\n\n\n/**\n * XmlHttp handler\n * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request.\n * @private\n */\nChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) {\n requestStats.onStartExecution();\n\n\n try {\n if (xmlhttp == this.xmlHttp_) {\n this.onXmlHttpReadyStateChanged_();\n } else {\n this.channelDebug_.warning(\n 'Called back with an ' +\n 'unexpected xmlhttp');\n }\n } catch (ex) {\n this.channelDebug_.debug('Failed call to OnXmlHttpReadyStateChanged_');\n if (this.xmlHttp_ && this.xmlHttp_.getResponseText()) {\n var channelRequest = this;\n this.channelDebug_.dumpException(ex, function() {\n return 'ResponseText: ' + channelRequest.xmlHttp_.getResponseText();\n });\n } else {\n this.channelDebug_.dumpException(ex, 'No response text');\n }\n } finally {\n requestStats.onEndExecution();\n }\n};\n\n\n/**\n * Called by the readystate handler for XMLHTTP requests.\n *\n * @private\n */\nChannelRequest.prototype.onXmlHttpReadyStateChanged_ = function() {\n var readyState = this.xmlHttp_.getReadyState();\n var errorCode = this.xmlHttp_.getLastErrorCode();\n var statusCode = this.xmlHttp_.getStatus();\n\n // we get partial results in browsers that support ready state interactive.\n // We also make sure that getResponseText is not null in interactive mode\n // before we continue.\n if (readyState < goog.net.XmlHttp.ReadyState.INTERACTIVE ||\n (readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&\n !environment.isPollingRequired() && // otherwise, go on to startPolling\n !this.xmlHttp_.getResponseText())) {\n return; // not yet ready\n }\n\n // Dispatch any appropriate network events.\n if (!this.cancelled_ && readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&\n errorCode != goog.net.ErrorCode.ABORT) {\n // Pretty conservative, these are the only known scenarios which we'd\n // consider indicative of a truly non-functional network connection.\n if (errorCode == goog.net.ErrorCode.TIMEOUT || statusCode <= 0) {\n requestStats.notifyServerReachabilityEvent(\n requestStats.ServerReachability.REQUEST_FAILED);\n } else {\n requestStats.notifyServerReachabilityEvent(\n requestStats.ServerReachability.REQUEST_SUCCEEDED);\n }\n }\n\n // got some data so cancel the watchdog timer\n this.cancelWatchDogTimer_();\n\n var status = this.xmlHttp_.getStatus();\n this.lastStatusCode_ = status;\n var responseText = this.xmlHttp_.getResponseText();\n if (!responseText) {\n var channelRequest = this;\n this.channelDebug_.debug(function() {\n return 'No response text for uri ' + channelRequest.requestUri_ +\n ' status ' + status;\n });\n }\n this.successful_ = (status == 200);\n\n this.channelDebug_.xmlHttpChannelResponseMetaData(\n /** @type {string} */ (this.verb_), this.requestUri_, this.rid_,\n this.retryId_, readyState, status);\n\n if (!this.successful_) {\n if (status == 400 && responseText.indexOf('Unknown SID') > 0) {\n // the server error string will include 'Unknown SID' which indicates the\n // server doesn't know about the session (maybe it got restarted, maybe\n // the user got moved to another server, etc.,). Handlers can special\n // case this error\n this.lastError_ = ChannelRequest.Error.UNKNOWN_SESSION_ID;\n requestStats.notifyStatEvent(\n requestStats.Stat.REQUEST_UNKNOWN_SESSION_ID);\n this.channelDebug_.warning('XMLHTTP Unknown SID (' + this.rid_ + ')');\n } else {\n this.lastError_ = ChannelRequest.Error.STATUS;\n requestStats.notifyStatEvent(requestStats.Stat.REQUEST_BAD_STATUS);\n this.channelDebug_.warning(\n 'XMLHTTP Bad status ' + status + ' (' + this.rid_ + ')');\n }\n this.cleanup_();\n this.dispatchFailure_();\n return;\n }\n\n if (this.shouldCheckInitialResponse_()) {\n var initialResponse = this.getInitialResponse_();\n if (initialResponse) {\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, initialResponse,\n 'Initial handshake response via ' +\n WebChannel.X_HTTP_INITIAL_RESPONSE);\n this.initialResponseDecoded_ = true;\n this.safeOnRequestData_(initialResponse);\n } else {\n this.successful_ = false;\n this.lastError_ = ChannelRequest.Error.UNKNOWN_SESSION_ID; // fail-fast\n requestStats.notifyStatEvent(\n requestStats.Stat.REQUEST_UNKNOWN_SESSION_ID);\n this.channelDebug_.warning(\n 'XMLHTTP Missing X_HTTP_INITIAL_RESPONSE' +\n ' (' + this.rid_ + ')');\n this.cleanup_();\n this.dispatchFailure_();\n return;\n }\n }\n\n if (this.decodeChunks_) {\n this.decodeNextChunks_(readyState, responseText);\n if (environment.isPollingRequired() && this.successful_ &&\n readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE) {\n this.startPolling_();\n }\n } else {\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, responseText, null);\n this.safeOnRequestData_(responseText);\n }\n\n if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {\n this.cleanup_();\n }\n\n if (!this.successful_) {\n return;\n }\n\n if (!this.cancelled_) {\n if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {\n this.channel_.onRequestComplete(this);\n } else {\n // The default is false, the result from this callback shouldn't carry\n // over to the next callback, otherwise the request looks successful if\n // the watchdog timer gets called\n this.successful_ = false;\n this.ensureWatchDogTimer_();\n }\n }\n};\n\n\n/**\n * Whether we need check the initial-response header that is sent during the\n * fast handshake.\n *\n * @return {boolean} true if the initial-response header is yet to be processed.\n * @private\n */\nChannelRequest.prototype.shouldCheckInitialResponse_ = function() {\n return this.decodeInitialResponse_ && !this.initialResponseDecoded_;\n};\n\n\n/**\n * Queries the initial response header that is sent during the handshake.\n *\n * @return {?string} The non-empty header value or null.\n * @private\n */\nChannelRequest.prototype.getInitialResponse_ = function() {\n if (this.xmlHttp_) {\n var value = this.xmlHttp_.getStreamingResponseHeader(\n WebChannel.X_HTTP_INITIAL_RESPONSE);\n if (value && !goog.string.isEmptyOrWhitespace(value)) {\n return value;\n }\n }\n\n return null;\n};\n\n\n/**\n * Check if the initial response header has been handled.\n *\n * @return {boolean} true if X_HTTP_INITIAL_RESPONSE has been handled.\n */\nChannelRequest.prototype.isInitialResponseDecoded = function() {\n return this.initialResponseDecoded_;\n};\n\n\n/**\n * Decodes X_HTTP_INITIAL_RESPONSE if present.\n */\nChannelRequest.prototype.setDecodeInitialResponse = function() {\n this.decodeInitialResponse_ = true;\n};\n\n\n/**\n * Decodes the next set of available chunks in the response.\n * @param {number} readyState The value of readyState.\n * @param {string} responseText The value of responseText.\n * @private\n */\nChannelRequest.prototype.decodeNextChunks_ = function(\n readyState, responseText) {\n var decodeNextChunksSuccessful = true;\n while (!this.cancelled_ && this.xmlHttpChunkStart_ < responseText.length) {\n var chunkText = this.getNextChunk_(responseText);\n if (chunkText == ChannelRequest.INCOMPLETE_CHUNK_) {\n if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {\n // should have consumed entire response when the request is done\n this.lastError_ = ChannelRequest.Error.BAD_DATA;\n requestStats.notifyStatEvent(requestStats.Stat.REQUEST_INCOMPLETE_DATA);\n decodeNextChunksSuccessful = false;\n }\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, null, '[Incomplete Response]');\n break;\n } else if (chunkText == ChannelRequest.INVALID_CHUNK_) {\n this.lastError_ = ChannelRequest.Error.BAD_DATA;\n requestStats.notifyStatEvent(requestStats.Stat.REQUEST_BAD_DATA);\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, responseText, '[Invalid Chunk]');\n decodeNextChunksSuccessful = false;\n break;\n } else {\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, /** @type {string} */ (chunkText), null);\n this.safeOnRequestData_(/** @type {string} */ (chunkText));\n }\n }\n if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&\n responseText.length == 0) {\n // also an error if we didn't get any response\n this.lastError_ = ChannelRequest.Error.NO_DATA;\n requestStats.notifyStatEvent(requestStats.Stat.REQUEST_NO_DATA);\n decodeNextChunksSuccessful = false;\n }\n this.successful_ = this.successful_ && decodeNextChunksSuccessful;\n if (!decodeNextChunksSuccessful) {\n // malformed response - we make this trigger retry logic\n this.channelDebug_.xmlHttpChannelResponseText(\n this.rid_, responseText, '[Invalid Chunked Response]');\n this.cleanup_();\n this.dispatchFailure_();\n }\n};\n\n\n/**\n * Polls the response for new data.\n * @private\n */\nChannelRequest.prototype.pollResponse_ = function() {\n if (!this.xmlHttp_) {\n return; // already closed\n }\n var readyState = this.xmlHttp_.getReadyState();\n var responseText = this.xmlHttp_.getResponseText();\n if (this.xmlHttpChunkStart_ < responseText.length) {\n this.cancelWatchDogTimer_();\n this.decodeNextChunks_(readyState, responseText);\n if (this.successful_ &&\n readyState != goog.net.XmlHttp.ReadyState.COMPLETE) {\n this.ensureWatchDogTimer_();\n }\n }\n};\n\n\n/**\n * Starts a polling interval for changes to responseText of the\n * XMLHttpRequest, for browsers that don't fire onreadystatechange\n * as data comes in incrementally. This timer is disabled in\n * cleanup_().\n * @private\n */\nChannelRequest.prototype.startPolling_ = function() {\n this.eventHandler_.listen(\n this.pollingTimer_, goog.Timer.TICK, this.pollResponse_);\n this.pollingTimer_.start();\n};\n\n\n/**\n * Returns the next chunk of a chunk-encoded response. This is not standard\n * HTTP chunked encoding because browsers don't expose the chunk boundaries to\n * the application through XMLHTTP. So we have an additional chunk encoding at\n * the application level that lets us tell where the beginning and end of\n * individual responses are so that we can only try to eval a complete JS array.\n *\n * The encoding is the size of the chunk encoded as a decimal string followed\n * by a newline followed by the data.\n *\n * @param {string} responseText The response text from the XMLHTTP response.\n * @return {string|Object} The next chunk string or a sentinel object\n * indicating a special condition.\n * @private\n */\nChannelRequest.prototype.getNextChunk_ = function(responseText) {\n var sizeStartIndex = this.xmlHttpChunkStart_;\n var sizeEndIndex = responseText.indexOf('\\n', sizeStartIndex);\n if (sizeEndIndex == -1) {\n return ChannelRequest.INCOMPLETE_CHUNK_;\n }\n\n var sizeAsString = responseText.substring(sizeStartIndex, sizeEndIndex);\n var size = Number(sizeAsString);\n if (isNaN(size)) {\n return ChannelRequest.INVALID_CHUNK_;\n }\n\n var chunkStartIndex = sizeEndIndex + 1;\n if (chunkStartIndex + size > responseText.length) {\n return ChannelRequest.INCOMPLETE_CHUNK_;\n }\n\n var chunkText = responseText.substr(chunkStartIndex, size);\n this.xmlHttpChunkStart_ = chunkStartIndex + size;\n return chunkText;\n};\n\n\n/**\n * Uses an IMG tag or navigator.sendBeacon to send an HTTP get to the server.\n *\n * This is only currently used to terminate the connection, as an IMG tag is\n * the most reliable way to send something to the server while the page\n * is getting torn down.\n *\n * Navigator.sendBeacon is available on Chrome and Firefox as a formal\n * solution to ensure delivery without blocking window close. See\n * https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon\n *\n * For Chrome Apps, sendBeacon is always necessary due to Content Security\n * Policy (CSP) violation of using an IMG tag.\n *\n * For react-native, we use xhr to send the actual close request, and assume\n * there is no page-close issue with react-native.\n *\n * @param {goog.Uri} uri The uri to send a request to.\n */\nChannelRequest.prototype.sendCloseRequest = function(uri) {\n this.type_ = ChannelRequest.Type_.CLOSE_REQUEST;\n this.baseUri_ = uri.clone().makeUnique();\n\n var requestSent = false;\n\n if (goog.global.navigator && goog.global.navigator.sendBeacon) {\n // empty string body to avoid 413 error on chrome < 41\n requestSent =\n goog.global.navigator.sendBeacon(this.baseUri_.toString(), '');\n }\n\n if (!requestSent && goog.global.Image) {\n var eltImg = new Image();\n eltImg.src = this.baseUri_;\n requestSent = true;\n }\n\n if (!requestSent) {\n // no handler is set to match the sendBeacon/Image behavior\n this.xmlHttp_ = this.channel_.createXhrIo(null);\n this.xmlHttp_.send(this.baseUri_);\n }\n\n this.requestStartTime_ = goog.now();\n this.ensureWatchDogTimer_();\n};\n\n\n/**\n * Cancels the request no matter what the underlying transport is.\n */\nChannelRequest.prototype.cancel = function() {\n this.cancelled_ = true;\n this.cleanup_();\n};\n\n\n/**\n * Resets the timeout.\n *\n * @param {number=} opt_timeout The new timeout\n */\nChannelRequest.prototype.resetTimeout = function(opt_timeout) {\n if (opt_timeout) {\n this.setTimeout(opt_timeout);\n }\n // restart only if a timer is currently set\n if (this.watchDogTimerId_) {\n this.cancelWatchDogTimer_();\n this.ensureWatchDogTimer_();\n }\n};\n\n\n/**\n * Ensures that there is watchdog timeout which is used to ensure that\n * the connection completes in time.\n *\n * @private\n */\nChannelRequest.prototype.ensureWatchDogTimer_ = function() {\n this.watchDogTimeoutTime_ = goog.now() + this.timeout_;\n this.startWatchDogTimer_(this.timeout_);\n};\n\n\n/**\n * Starts the watchdog timer which is used to ensure that the connection\n * completes in time.\n * @param {number} time The number of milliseconds to wait.\n * @private\n */\nChannelRequest.prototype.startWatchDogTimer_ = function(time) {\n if (this.watchDogTimerId_ != null) {\n // assertion\n throw new Error('WatchDog timer not null');\n }\n this.watchDogTimerId_ =\n requestStats.setTimeout(goog.bind(this.onWatchDogTimeout_, this), time);\n};\n\n\n/**\n * Cancels the watchdog timer if it has been started.\n *\n * @private\n */\nChannelRequest.prototype.cancelWatchDogTimer_ = function() {\n if (this.watchDogTimerId_) {\n goog.global.clearTimeout(this.watchDogTimerId_);\n this.watchDogTimerId_ = null;\n }\n};\n\n\n/**\n * Called when the watchdog timer is triggered. It also handles a case where it\n * is called too early which we suspect may be happening sometimes\n * (not sure why)\n *\n * @private\n */\nChannelRequest.prototype.onWatchDogTimeout_ = function() {\n this.watchDogTimerId_ = null;\n var now = goog.now();\n if (now - this.watchDogTimeoutTime_ >= 0) {\n this.handleTimeout_();\n } else {\n // got called too early for some reason\n this.channelDebug_.warning('WatchDog timer called too early');\n this.startWatchDogTimer_(this.watchDogTimeoutTime_ - now);\n }\n};\n\n\n/**\n * Called when the request has actually timed out. Will cleanup and notify the\n * channel of the failure.\n *\n * @private\n */\nChannelRequest.prototype.handleTimeout_ = function() {\n if (this.successful_) {\n // Should never happen.\n this.channelDebug_.severe(\n 'Received watchdog timeout even though request loaded successfully');\n }\n\n this.channelDebug_.timeoutResponse(this.requestUri_);\n\n // IMG or SendBeacon requests never notice if they were successful,\n // and always 'time out'. This fact says nothing about reachability.\n if (this.type_ != ChannelRequest.Type_.CLOSE_REQUEST) {\n requestStats.notifyServerReachabilityEvent(\n requestStats.ServerReachability.REQUEST_FAILED);\n requestStats.notifyStatEvent(requestStats.Stat.REQUEST_TIMEOUT);\n }\n\n this.cleanup_();\n\n // Set error and dispatch failure.\n // This is called for CLOSE_REQUEST too to ensure channel_.onRequestComplete.\n this.lastError_ = ChannelRequest.Error.TIMEOUT;\n this.dispatchFailure_();\n};\n\n\n/**\n * Notifies the channel that this request failed.\n * @private\n */\nChannelRequest.prototype.dispatchFailure_ = function() {\n if (this.channel_.isClosed() || this.cancelled_) {\n return;\n }\n\n this.channel_.onRequestComplete(this);\n};\n\n\n/**\n * Cleans up the objects used to make the request. This function is\n * idempotent.\n *\n * @private\n */\nChannelRequest.prototype.cleanup_ = function() {\n this.cancelWatchDogTimer_();\n\n goog.dispose(this.readyStateChangeThrottle_);\n this.readyStateChangeThrottle_ = null;\n\n // Stop the polling timer, if necessary.\n this.pollingTimer_.stop();\n\n // Unhook all event handlers.\n this.eventHandler_.removeAll();\n\n if (this.xmlHttp_) {\n // clear out this.xmlHttp_ before aborting so we handle getting reentered\n // inside abort\n var xmlhttp = this.xmlHttp_;\n this.xmlHttp_ = null;\n xmlhttp.abort();\n xmlhttp.dispose();\n }\n};\n\n\n/**\n * Indicates whether the request was successful. Only valid after the handler\n * is called to indicate completion of the request.\n *\n * @return {boolean} True if the request succeeded.\n */\nChannelRequest.prototype.getSuccess = function() {\n return this.successful_;\n};\n\n\n/**\n * If the request was not successful, returns the reason.\n *\n * @return {?ChannelRequest.Error} The last error.\n */\nChannelRequest.prototype.getLastError = function() {\n return this.lastError_;\n};\n\n\n/**\n * Returns the status code of the last request.\n * @return {number} The status code of the last request.\n */\nChannelRequest.prototype.getLastStatusCode = function() {\n return this.lastStatusCode_;\n};\n\n\n/**\n * Returns the session id for this channel.\n *\n * @return {string|undefined} The session ID.\n */\nChannelRequest.prototype.getSessionId = function() {\n return this.sid_;\n};\n\n\n/**\n * Returns the request id for this request. Each request has a unique request\n * id and the request IDs are a sequential increasing count.\n *\n * @return {string|number|undefined} The request ID.\n */\nChannelRequest.prototype.getRequestId = function() {\n return this.rid_;\n};\n\n\n/**\n * Returns the data for a post, if this request is a post.\n *\n * @return {?string} The POST data provided by the request initiator.\n */\nChannelRequest.prototype.getPostData = function() {\n return this.postData_;\n};\n\n\n/**\n * Returns the XhrIo request object.\n *\n * @return {?goog.net.XhrIo} Any XhrIo request created for this object.\n */\nChannelRequest.prototype.getXhr = function() {\n return this.xmlHttp_;\n};\n\n\n/**\n * Returns the time that the request started, if it has started.\n *\n * @return {?number} The time the request started, as returned by goog.now().\n */\nChannelRequest.prototype.getRequestStartTime = function() {\n return this.requestStartTime_;\n};\n\n\n/**\n * Helper to call the callback's onRequestData, which catches any\n * exception.\n * @param {string} data The request data.\n * @private\n */\nChannelRequest.prototype.safeOnRequestData_ = function(data) {\n try {\n this.channel_.onRequestData(this, data);\n var stats = requestStats.ServerReachability;\n requestStats.notifyServerReachabilityEvent(stats.BACK_CHANNEL_ACTIVITY);\n } catch (e) {\n // Dump debug info, but keep going without closing the channel.\n this.channelDebug_.dumpException(e, 'Error in httprequest callback');\n }\n};\n\n\n/**\n * Convenience factory method.\n *\n * @param {Channel} channel The channel object that owns this request.\n * @param {WebChannelDebug} channelDebug A WebChannelDebug to use for logging.\n * @param {string=} opt_sessionId The session id for the channel.\n * @param {string|number=} opt_requestId The request id for this request.\n * @param {number=} opt_retryId The retry id for this request.\n * @return {!ChannelRequest} The created channel request.\n */\nChannelRequest.createChannelRequest = function(\n channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId) {\n return new ChannelRequest(\n channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId);\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A single module to define user-agent specific environment\n * details.\n *\n */\n\ngoog.module('goog.labs.net.webChannel.environment');\n\ngoog.module.declareLegacyNamespace();\n\nvar userAgent = goog.require('goog.userAgent');\n\n\n/**\n * The default polling interval in millis for Edge.\n *\n * Currently on edge, new-chunk events may be not be fired (at all) if a new\n * chunk arrives within 50ms following the previous chunk. This may be fixed\n * in future, which requires changes to the whatwg spec too.\n *\n * @private @const {number}\n */\nvar EDGE_POLLING_INTERVAL_ = 125;\n\n\n/**\n * History:\n *\n * IE11 is still using Trident, the traditional engine for IE.\n * Edge is using EdgeHTML, a fork of Trident. We are seeing the same issue\n * on IE-11 (reported in 2017), so treat IE the same as Edge for now.\n *\n * We used to do polling for Opera (only) with an 250ms interval, because Opera\n * only fires readyState == INTERACTIVE once. Opera switched to WebKit in 2013,\n * and then to Blink (chrome).\n *\n * TODO(user): check the raw UA string to keep polling for old, mobile operas\n * that may still be affected. For old Opera, double the polling interval\n * to 250ms.\n *\n * @return {boolean} True if polling is required with XHR.\n */\nexports.isPollingRequired = function() {\n return userAgent.EDGE_OR_IE;\n};\n\n\n/**\n * How often to poll (in MS) for changes to responseText in browsers that don't\n * fire onreadystatechange during incremental loading of the response body.\n *\n * @return {number|undefined} The polling interval (MS) for the current U-A;\n * or undefined if polling is not supposed to be enabled.\n */\nexports.getPollingInterval = function() {\n if (userAgent.EDGE_OR_IE) {\n return EDGE_POLLING_INTERVAL_;\n }\n\n return undefined;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Base WebChannel implementation.\n */\n\n\ngoog.provide('goog.labs.net.webChannel.WebChannelBase');\n\ngoog.require('goog.Uri');\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.async.run');\ngoog.require('goog.json');\ngoog.require('goog.labs.net.webChannel.Channel');\ngoog.require('goog.labs.net.webChannel.ChannelRequest');\ngoog.require('goog.labs.net.webChannel.ConnectionState');\ngoog.require('goog.labs.net.webChannel.ForwardChannelRequestPool');\ngoog.require('goog.labs.net.webChannel.WebChannelDebug');\ngoog.require('goog.labs.net.webChannel.Wire');\ngoog.require('goog.labs.net.webChannel.WireV8');\ngoog.require('goog.labs.net.webChannel.netUtils');\ngoog.require('goog.labs.net.webChannel.requestStats');\ngoog.require('goog.net.WebChannel');\ngoog.require('goog.net.XhrIo');\ngoog.require('goog.net.XmlHttpFactory');\ngoog.require('goog.net.rpc.HttpCors');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.structs');\n\ngoog.scope(function() {\nvar WebChannel = goog.net.WebChannel;\nvar ChannelRequest = goog.labs.net.webChannel.ChannelRequest;\nvar ConnectionState = goog.labs.net.webChannel.ConnectionState;\nvar ForwardChannelRequestPool =\n goog.labs.net.webChannel.ForwardChannelRequestPool;\nvar WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;\nvar Wire = goog.labs.net.webChannel.Wire;\nvar WireV8 = goog.labs.net.webChannel.WireV8;\nvar netUtils = goog.labs.net.webChannel.netUtils;\nvar requestStats = goog.labs.net.webChannel.requestStats;\n\nvar httpCors = goog.module.get('goog.net.rpc.HttpCors');\n\n/**\n * Gets an internal channel parameter in a type-safe way.\n *\n * @param {string} paramName the key of the parameter to fetch.\n * @param {!T} defaultValue the default value to return\n * @param {!goog.net.WebChannel.Options=} options Configuration for the\n * WebChannel instance.\n * @return {T}\n * @template T\n */\nfunction getInternalChannelParam(paramName, defaultValue, options) {\n if (!options || !options.internalChannelParams) {\n return defaultValue;\n }\n return /** @type {T} */ (options.internalChannelParams[paramName]) ||\n defaultValue;\n}\n\n/**\n * This WebChannel implementation is branched off goog.net.BrowserChannel\n * for now. Ongoing changes to goog.net.BrowserChannel will be back\n * ported to this implementation as needed.\n *\n * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the\n * WebChannel instance.\n * @param {number=} opt_clientVersion An application-specific version number\n * that is sent to the server when connected.\n * @param {!ConnectionState=} opt_conn Previously determined connection\n * conditions.\n * @constructor\n * @struct\n * @implements {goog.labs.net.webChannel.Channel}\n */\ngoog.labs.net.webChannel.WebChannelBase = function(\n opt_options, opt_clientVersion, opt_conn) {\n /**\n * The client library version (capabilities).\n * @private {number}\n */\n this.clientVersion_ = opt_clientVersion || 0;\n\n /**\n * The server library version (capabilities).\n * @private {number}\n */\n this.serverVersion_ = 0;\n\n\n /**\n * An array of queued maps that need to be sent to the server.\n * @private {!Array<Wire.QueuedMap>}\n */\n this.outgoingMaps_ = [];\n\n /**\n * The channel debug used for logging\n * @private {!WebChannelDebug}\n */\n this.channelDebug_ = new WebChannelDebug();\n\n /**\n * Connectivity state.\n * @private {!ConnectionState}\n */\n this.connState_ = opt_conn || new ConnectionState();\n\n /**\n * Extra HTTP headers to add to all the requests sent to the server.\n * @private {?Object}\n */\n this.extraHeaders_ = null;\n\n /**\n * Extra HTTP headers to add to the init request(s) sent to the server.\n * @private {?Object}\n */\n this.initHeaders_ = null;\n\n /**\n * @private {?string} The URL param name to overwrite custom HTTP headers\n * to bypass CORS preflight.\n */\n this.httpHeadersOverwriteParam_ = null;\n\n /**\n * Extra parameters to add to all the requests sent to the server.\n * @private {?Object}\n */\n this.extraParams_ = null;\n\n /**\n * Parameter name for the http session id.\n * @private {?string}\n */\n this.httpSessionIdParam_ = null;\n\n /**\n * The http session id, to be sent with httpSessionIdParam_ with each\n * request after the initial handshake.\n * @private {?string}\n */\n this.httpSessionId_ = null;\n\n /**\n * The ChannelRequest object for the backchannel.\n * @private {?ChannelRequest}\n */\n this.backChannelRequest_ = null;\n\n /**\n * The relative path (in the context of the the page hosting the browser\n * channel) for making requests to the server.\n * @private {?string}\n */\n this.path_ = null;\n\n /**\n * The absolute URI for the forwardchannel request.\n * @private {?goog.Uri}\n */\n this.forwardChannelUri_ = null;\n\n /**\n * The absolute URI for the backchannel request.\n * @private {?goog.Uri}\n */\n this.backChannelUri_ = null;\n\n /**\n * A subdomain prefix for using a subdomain in IE for the backchannel\n * requests.\n * @private {?string}\n */\n this.hostPrefix_ = null;\n\n /**\n * Whether we allow the use of a subdomain in IE for the backchannel requests.\n * @private {boolean}\n */\n this.allowHostPrefix_ = true;\n\n /**\n * The next id to use for the RID (request identifier) parameter. This\n * identifier uniquely identifies the forward channel request.\n * @private {number}\n */\n this.nextRid_ = 0;\n\n /**\n * The id to use for the next outgoing map. This identifier uniquely\n * identifies a sent map.\n * @private {number}\n */\n this.nextMapId_ = 0;\n\n /**\n * Whether to fail forward-channel requests after one try or a few tries.\n * @private {boolean}\n */\n this.failFast_ = getInternalChannelParam('failFast', false, opt_options);\n\n /**\n * The handler that receive callbacks for state changes and data.\n * @private {?goog.labs.net.webChannel.WebChannelBase.Handler}\n */\n this.handler_ = null;\n\n /**\n * Timer identifier for asynchronously making a forward channel request.\n * This is set to true if the func is scheduled with async.run, which\n * is equivalent to setTimeout(0).\n * @private {?number|?boolean}\n */\n this.forwardChannelTimerId_ = null;\n\n /**\n * Timer identifier for asynchronously making a back channel request.\n * @private {?number}\n */\n this.backChannelTimerId_ = null;\n\n /**\n * Timer identifier for the timer that waits for us to retry the backchannel\n * in the case where it is dead and no longer receiving data.\n * @private {?number}\n */\n this.deadBackChannelTimerId_ = null;\n\n /**\n * Whether the client's network conditions can support chunked responses.\n * @private {?boolean}\n */\n this.useChunked_ = null;\n\n /**\n * Whether chunked mode is allowed. In certain debugging situations, it's\n * useful to disable this.\n * @private {boolean}\n */\n this.allowChunkedMode_ = true;\n\n /**\n * The array identifier of the last array received from the server for the\n * backchannel request.\n * @private {number}\n */\n this.lastArrayId_ = -1;\n\n /**\n * The array id of the last array sent by the server that we know about.\n * @private {number}\n */\n this.lastPostResponseArrayId_ = -1;\n\n /**\n * The last status code received.\n * @private {number}\n */\n this.lastStatusCode_ = -1;\n\n /**\n * Number of times we have retried the current forward channel request.\n * @private {number}\n */\n this.forwardChannelRetryCount_ = 0;\n\n /**\n * Number of times in a row that we have retried the current back channel\n * request and received no data.\n * @private {number}\n */\n this.backChannelRetryCount_ = 0;\n\n /**\n * The attempt id for the current back channel request. Starts at 1 and\n * increments for each reconnect. The server uses this to log if our\n * connection is flaky or not.\n * @private {number}\n */\n this.backChannelAttemptId_ = 0;\n\n /**\n * The base part of the time before firing next retry request. Default is 5\n * seconds. Note that a random delay is added (see {@link retryDelaySeedMs_})\n * for all retries, and linear backoff is applied to the sum for subsequent\n * retries.\n * @private {number}\n */\n this.baseRetryDelayMs_ =\n getInternalChannelParam('baseRetryDelayMs', 5 * 1000, opt_options);\n\n /**\n * A random time between 0 and this number of MS is added to the\n * {@link baseRetryDelayMs_}. Default is 10 seconds.\n * @private {number}\n */\n this.retryDelaySeedMs_ =\n getInternalChannelParam('retryDelaySeedMs', 10 * 1000, opt_options);\n\n /**\n * Maximum number of attempts to connect to the server for forward channel\n * requests. Defaults to 2.\n * @private {number}\n */\n this.forwardChannelMaxRetries_ =\n getInternalChannelParam('forwardChannelMaxRetries', 2, opt_options);\n\n /**\n * The timeout in milliseconds for a forward channel request. Defaults to 20\n * seconds. Note that part of this timeout can be randomized.\n * @private {number}\n */\n this.forwardChannelRequestTimeoutMs_ = getInternalChannelParam(\n 'forwardChannelRequestTimeoutMs', 20 * 1000, opt_options);\n\n /**\n * The custom factory used to create XMLHttpRequest objects.\n * @private {!goog.net.XmlHttpFactory | undefined}\n */\n this.xmlHttpFactory_ =\n (opt_options && opt_options.xmlHttpFactory) || undefined;\n\n /**\n * The timeout in milliseconds for a back channel request. Defaults to using\n * the timeout configured in ChannelRequest (45s). If server-side\n * keepaliveInterval is known to the client, set the backchannel request\n * timeout to 1.5 * keepaliveInterval (ms).\n *\n * @private {number|undefined}\n */\n this.backChannelRequestTimeoutMs_ = undefined;\n\n /**\n * A throttle time in ms for readystatechange events for the backchannel.\n * Useful for throttling when ready state is INTERACTIVE (partial data).\n *\n * This throttle is useful if the server sends large data chunks down the\n * backchannel. It prevents examining XHR partial data on every readystate\n * change event. This is useful because large chunks can trigger hundreds\n * of readystatechange events, each of which takes ~5ms or so to handle,\n * in turn making the UI unresponsive for a significant period.\n *\n * If set to zero no throttle is used.\n * @private {number}\n */\n this.readyStateChangeThrottleMs_ = 0;\n\n /**\n * Whether cross origin requests are supported for the channel.\n *\n * See {@link goog.net.XhrIo#setWithCredentials}.\n * @private {boolean}\n */\n this.supportsCrossDomainXhrs_ =\n (opt_options && opt_options.supportsCrossDomainXhr) || false;\n\n /**\n * The current session id.\n * @private {string}\n */\n this.sid_ = '';\n\n /**\n * The current ChannelRequest pool for the forward channel.\n * @private {!ForwardChannelRequestPool}\n */\n this.forwardChannelRequestPool_ = new ForwardChannelRequestPool(\n opt_options && opt_options.concurrentRequestLimit);\n\n /**\n * The V8 codec.\n * @private {!WireV8}\n */\n this.wireCodec_ = new WireV8();\n\n /**\n * Whether to turn on the fast handshake behavior.\n *\n * @private {boolean}\n */\n this.fastHandshake_ = (opt_options && opt_options.fastHandshake) || false;\n\n if (opt_options && opt_options.disableRedact) {\n this.channelDebug_.disableRedact();\n }\n\n if (opt_options && opt_options.forceLongPolling) {\n this.allowChunkedMode_ = false;\n }\n\n /**\n * Callback when all the pending client-sent messages have been flushed.\n *\n * @private {function()|undefined}\n */\n this.forwardChannelFlushedCallback_ = undefined;\n};\n\nvar WebChannelBase = goog.labs.net.webChannel.WebChannelBase;\n\n\n/**\n * The channel version that we negotiated with the server for this session.\n * Starts out as the version we request, and then is changed to the negotiated\n * version after the initial open.\n * @private {number}\n */\nWebChannelBase.prototype.channelVersion_ = Wire.LATEST_CHANNEL_VERSION;\n\n\n/**\n * Enum type for the channel state machine.\n * @enum {number}\n */\nWebChannelBase.State = {\n /** The channel is closed. */\n CLOSED: 0,\n\n /** The channel has been initialized but hasn't yet initiated a connection. */\n INIT: 1,\n\n /** The channel is in the process of opening a connection to the server. */\n OPENING: 2,\n\n /** The channel is open. */\n OPENED: 3\n};\n\n\n/**\n * The current state of the WebChannel.\n * @private {!WebChannelBase.State}\n */\nWebChannelBase.prototype.state_ = WebChannelBase.State.INIT;\n\n\n/**\n * The timeout in milliseconds for a forward channel request.\n * @type {number}\n */\nWebChannelBase.FORWARD_CHANNEL_RETRY_TIMEOUT = 20 * 1000;\n\n\n/**\n * Maximum number of attempts to connect to the server for back channel\n * requests.\n * @type {number}\n */\nWebChannelBase.BACK_CHANNEL_MAX_RETRIES = 3;\n\n\n/**\n * A number in MS of how long we guess the maxmium amount of time a round trip\n * to the server should take. In the future this could be substituted with a\n * real measurement of the RTT.\n * @type {number}\n */\nWebChannelBase.RTT_ESTIMATE = 3 * 1000;\n\n\n/**\n * When retrying for an inactive channel, we will multiply the total delay by\n * this number.\n * @type {number}\n */\nWebChannelBase.INACTIVE_CHANNEL_RETRY_FACTOR = 2;\n\n\n/**\n * Enum type for identifying an error.\n * @enum {number}\n */\nWebChannelBase.Error = {\n /** Value that indicates no error has occurred. */\n OK: 0,\n\n /** An error due to a request failing. */\n REQUEST_FAILED: 2,\n\n /** An error due to the user being logged out. */\n LOGGED_OUT: 4,\n\n /** An error due to server response which contains no data. */\n NO_DATA: 5,\n\n /** An error due to a server response indicating an unknown session id */\n UNKNOWN_SESSION_ID: 6,\n\n /** An error due to a server response requesting to stop the channel. */\n STOP: 7,\n\n /** A general network error. */\n NETWORK: 8,\n\n /** An error due to bad data being returned from the server. */\n BAD_DATA: 10,\n\n /** An error due to a response that is not parsable. */\n BAD_RESPONSE: 11\n};\n\n\n/**\n * Internal enum type for the two channel types.\n * @enum {number}\n * @private\n */\nWebChannelBase.ChannelType_ = {\n FORWARD_CHANNEL: 1,\n\n BACK_CHANNEL: 2\n};\n\n\n/**\n * The maximum number of maps that can be sent in one POST. Should match\n * MAX_MAPS_PER_REQUEST on the server code.\n * @type {number}\n * @private\n */\nWebChannelBase.MAX_MAPS_PER_REQUEST_ = 1000;\n\n\n/**\n * The maximum number of utf-8 chars that can be sent in one GET to enable 0-RTT\n * handshake.\n *\n * @const @private {number}\n */\nWebChannelBase.MAX_CHARS_PER_GET_ = 4 * 1024;\n\n\n/**\n * A guess at a cutoff at which to no longer assume the backchannel is dead\n * when we are slow to receive data. Number in bytes.\n *\n * Assumption: The worst bandwidth we work on is 50 kilobits/sec\n * 50kbits/sec * (1 byte / 8 bits) * 6 sec dead backchannel timeout\n * @type {number}\n */\nWebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF = 37500;\n\n\n/**\n * @return {number} The server version or 0 if undefined\n */\nWebChannelBase.prototype.getServerVersion = function() {\n return this.serverVersion_;\n};\n\n\n/**\n * @return {!ForwardChannelRequestPool} The forward channel request pool.\n */\nWebChannelBase.prototype.getForwardChannelRequestPool = function() {\n return this.forwardChannelRequestPool_;\n};\n\n\n/**\n * @return {!Object} The codec object.\n */\nWebChannelBase.prototype.getWireCodec = function() {\n return this.wireCodec_;\n};\n\n\n/**\n * Returns the logger.\n *\n * @return {!WebChannelDebug} The channel debug object.\n */\nWebChannelBase.prototype.getChannelDebug = function() {\n return this.channelDebug_;\n};\n\n\n/**\n * Sets the logger.\n *\n * @param {!WebChannelDebug} channelDebug The channel debug object.\n */\nWebChannelBase.prototype.setChannelDebug = function(channelDebug) {\n this.channelDebug_ = channelDebug;\n};\n\n\n/**\n * Starts the channel. This initiates connections to the server.\n *\n * @param {string} channelPath The path for the channel connection.\n * @param {!Object=} opt_extraParams Extra parameter keys and values to add to\n * the requests.\n * @param {string=} opt_oldSessionId Session ID from a previous session.\n * @param {number=} opt_oldArrayId The last array ID from a previous session.\n */\nWebChannelBase.prototype.connect = function(\n channelPath, opt_extraParams, opt_oldSessionId, opt_oldArrayId) {\n this.channelDebug_.debug('connect()');\n\n requestStats.notifyStatEvent(requestStats.Stat.CONNECT_ATTEMPT);\n\n this.path_ = channelPath;\n this.extraParams_ = opt_extraParams || {};\n\n // Attach parameters about the previous session if reconnecting.\n if (opt_oldSessionId && opt_oldArrayId !== undefined) {\n this.extraParams_['OSID'] = opt_oldSessionId;\n this.extraParams_['OAID'] = opt_oldArrayId;\n }\n\n this.useChunked_ = this.allowChunkedMode_;\n this.connectChannel_();\n};\n\n\n/**\n * Disconnects and closes the channel.\n */\nWebChannelBase.prototype.disconnect = function() {\n this.channelDebug_.debug('disconnect()');\n\n this.cancelRequests_();\n\n if (this.state_ == WebChannelBase.State.OPENED) {\n var rid = this.nextRid_++;\n var uri = this.forwardChannelUri_.clone();\n uri.setParameterValue('SID', this.sid_);\n uri.setParameterValue('RID', rid);\n uri.setParameterValue('TYPE', 'terminate');\n\n // Add the reconnect parameters.\n this.addAdditionalParams_(uri);\n\n var request = ChannelRequest.createChannelRequest(\n this, this.channelDebug_, this.sid_, rid);\n request.sendCloseRequest(uri);\n }\n\n this.onClose_();\n};\n\n\n/**\n * Returns the session id of the channel. Only available after the\n * channel has been opened.\n * @return {string} Session ID.\n */\nWebChannelBase.prototype.getSessionId = function() {\n return this.sid_;\n};\n\n\n/**\n * Starts the connection.\n * @private\n */\nWebChannelBase.prototype.connectChannel_ = function() {\n this.channelDebug_.debug('connectChannel_()');\n this.ensureInState_(WebChannelBase.State.INIT, WebChannelBase.State.CLOSED);\n this.forwardChannelUri_ =\n this.getForwardChannelUri(/** @type {string} */ (this.path_));\n this.ensureForwardChannel_();\n};\n\n\n/**\n * Cancels all outstanding requests.\n * @private\n */\nWebChannelBase.prototype.cancelRequests_ = function() {\n if (this.backChannelRequest_) {\n this.backChannelRequest_.cancel();\n this.backChannelRequest_ = null;\n }\n\n if (this.backChannelTimerId_) {\n goog.global.clearTimeout(this.backChannelTimerId_);\n this.backChannelTimerId_ = null;\n }\n\n this.clearDeadBackchannelTimer_();\n\n this.forwardChannelRequestPool_.cancel();\n\n if (this.forwardChannelTimerId_) {\n this.clearForwardChannelTimer_();\n }\n};\n\n\n/**\n * Clears the forward channel timer.\n * @private\n */\nWebChannelBase.prototype.clearForwardChannelTimer_ = function() {\n if (typeof this.forwardChannelTimerId_ === 'number') {\n goog.global.clearTimeout(this.forwardChannelTimerId_);\n }\n\n this.forwardChannelTimerId_ = null;\n};\n\n\n/**\n * Returns the extra HTTP headers to add to all the requests sent to the server.\n *\n * @return {Object} The HTTP headers, or null.\n */\nWebChannelBase.prototype.getExtraHeaders = function() {\n return this.extraHeaders_;\n};\n\n\n/**\n * Sets extra HTTP headers to add to all the requests sent to the server.\n *\n * @param {Object} extraHeaders The HTTP headers, or null.\n */\nWebChannelBase.prototype.setExtraHeaders = function(extraHeaders) {\n this.extraHeaders_ = extraHeaders;\n};\n\n\n/**\n * Returns the extra HTTP headers to add to the init requests\n * sent to the server.\n *\n * @return {Object} The HTTP headers, or null.\n */\nWebChannelBase.prototype.getInitHeaders = function() {\n return this.initHeaders_;\n};\n\n\n/**\n * Sets extra HTTP headers to add to the init requests sent to the server.\n *\n * @param {Object} initHeaders The HTTP headers, or null.\n */\nWebChannelBase.prototype.setInitHeaders = function(initHeaders) {\n this.initHeaders_ = initHeaders;\n};\n\n\n/**\n * Sets the URL param name to overwrite custom HTTP headers.\n *\n * @param {string} httpHeadersOverwriteParam The URL param name.\n */\nWebChannelBase.prototype.setHttpHeadersOverwriteParam = function(\n httpHeadersOverwriteParam) {\n this.httpHeadersOverwriteParam_ = httpHeadersOverwriteParam;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.setHttpSessionIdParam = function(httpSessionIdParam) {\n this.httpSessionIdParam_ = httpSessionIdParam;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.getHttpSessionIdParam = function() {\n return this.httpSessionIdParam_;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.setHttpSessionId = function(httpSessionId) {\n this.httpSessionId_ = httpSessionId;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.getHttpSessionId = function() {\n return this.httpSessionId_;\n};\n\n\n/**\n * Sets the throttle for handling onreadystatechange events for the request.\n *\n * @param {number} throttle The throttle in ms. A value of zero indicates\n * no throttle.\n */\nWebChannelBase.prototype.setReadyStateChangeThrottle = function(throttle) {\n this.readyStateChangeThrottleMs_ = throttle;\n};\n\n\n/**\n * Sets whether cross origin requests are supported for the channel.\n *\n * Setting this allows the creation of requests to secondary domains and\n * sends XHRs with the CORS withCredentials bit set to true.\n *\n * In order for cross-origin requests to work, the server will also need to set\n * CORS response headers as per:\n * https://developer.mozilla.org/en-US/docs/HTTP_access_control\n *\n * See {@link goog.net.XhrIo#setWithCredentials}.\n * @param {boolean} supportCrossDomain Whether cross domain XHRs are supported.\n */\nWebChannelBase.prototype.setSupportsCrossDomainXhrs = function(\n supportCrossDomain) {\n this.supportsCrossDomainXhrs_ = supportCrossDomain;\n};\n\n\n/**\n * Returns the handler used for channel callback events.\n *\n * @return {WebChannelBase.Handler} The handler.\n */\nWebChannelBase.prototype.getHandler = function() {\n return this.handler_;\n};\n\n\n/**\n * Sets the handler used for channel callback events.\n * @param {WebChannelBase.Handler} handler The handler to set.\n */\nWebChannelBase.prototype.setHandler = function(handler) {\n this.handler_ = handler;\n};\n\n\n/**\n * Returns whether the channel allows the use of a subdomain. There may be\n * cases where this isn't allowed.\n * @return {boolean} Whether a host prefix is allowed.\n */\nWebChannelBase.prototype.getAllowHostPrefix = function() {\n return this.allowHostPrefix_;\n};\n\n\n/**\n * Sets whether the channel allows the use of a subdomain. There may be cases\n * where this isn't allowed, for example, logging in with troutboard where\n * using a subdomain causes Apache to force the user to authenticate twice.\n * @param {boolean} allowHostPrefix Whether a host prefix is allowed.\n */\nWebChannelBase.prototype.setAllowHostPrefix = function(allowHostPrefix) {\n this.allowHostPrefix_ = allowHostPrefix;\n};\n\n\n/**\n * Returns whether the channel is buffered or not. This may be\n * queried in the WebChannelBase.okToMakeRequest() callback.\n *\n * @return {boolean} Whether the channel is buffered.\n */\nWebChannelBase.prototype.isBuffered = function() {\n return !this.useChunked_;\n};\n\n\n/**\n * Returns whether chunked mode is allowed. In certain debugging situations,\n * it's useful for the application to have a way to disable chunked mode for a\n * user.\n\n * @return {boolean} Whether chunked mode is allowed.\n */\nWebChannelBase.prototype.getAllowChunkedMode = function() {\n return this.allowChunkedMode_;\n};\n\n\n/**\n * Sets whether chunked mode is allowed. In certain debugging situations, it's\n * useful for the application to have a way to disable chunked mode for a user.\n * @param {boolean} allowChunkedMode Whether chunked mode is allowed.\n */\nWebChannelBase.prototype.setAllowChunkedMode = function(allowChunkedMode) {\n this.allowChunkedMode_ = allowChunkedMode;\n};\n\n\n/**\n * Sends a request to the server. The format of the request is a Map data\n * structure of key/value pairs. These maps are then encoded in a format\n * suitable for the wire and then reconstituted as a Map data structure that\n * the server can process.\n * @param {!Object|!goog.structs.Map} map The map to send.\n * @param {!Object=} opt_context The context associated with the map.\n */\nWebChannelBase.prototype.sendMap = function(map, opt_context) {\n goog.asserts.assert(\n this.state_ != WebChannelBase.State.CLOSED,\n 'Invalid operation: sending map when state is closed');\n\n // We can only send 1000 maps per POST, but typically we should never have\n // that much to send, so warn if we exceed that (we still send all the maps).\n if (this.outgoingMaps_.length == WebChannelBase.MAX_MAPS_PER_REQUEST_) {\n // severe() is temporary so that we get these uploaded and can figure out\n // what's causing them. Afterwards can change to warning().\n this.channelDebug_.severe(function() {\n return 'Already have ' + WebChannelBase.MAX_MAPS_PER_REQUEST_ +\n ' queued maps upon queueing ' + goog.json.serialize(map);\n });\n }\n\n this.outgoingMaps_.push(\n new Wire.QueuedMap(this.nextMapId_++, map, opt_context));\n\n // Messages need be buffered during OPENING to avoid server-side race\n if (this.state_ == WebChannelBase.State.OPENED) {\n this.ensureForwardChannel_();\n }\n};\n\n\n/**\n * When set to true, this changes the behavior of the forward channel so it\n * will not retry requests; it will fail after one network failure, and if\n * there was already one network failure, the request will fail immediately.\n * @param {boolean} failFast Whether or not to fail fast.\n */\nWebChannelBase.prototype.setFailFast = function(failFast) {\n this.failFast_ = failFast;\n this.channelDebug_.info('setFailFast: ' + failFast);\n if ((this.forwardChannelRequestPool_.hasPendingRequest() ||\n this.forwardChannelTimerId_) &&\n this.forwardChannelRetryCount_ > this.getForwardChannelMaxRetries()) {\n var self = this;\n this.channelDebug_.info(function() {\n return 'Retry count ' + self.forwardChannelRetryCount_ +\n ' > new maxRetries ' + self.getForwardChannelMaxRetries() +\n '. Fail immediately!';\n });\n\n if (!this.forwardChannelRequestPool_.forceComplete(\n goog.bind(this.onRequestComplete, this))) {\n // i.e., this.forwardChannelTimerId_\n this.clearForwardChannelTimer_();\n // The error code from the last failed request is gone, so just use a\n // generic one.\n this.signalError_(WebChannelBase.Error.REQUEST_FAILED);\n }\n }\n};\n\n\n/**\n * @return {number} The max number of forward-channel retries, which will be 0\n * in fail-fast mode.\n */\nWebChannelBase.prototype.getForwardChannelMaxRetries = function() {\n return this.failFast_ ? 0 : this.forwardChannelMaxRetries_;\n};\n\n\n/**\n * Sets the maximum number of attempts to connect to the server for forward\n * channel requests.\n * @param {number} retries The maximum number of attempts.\n */\nWebChannelBase.prototype.setForwardChannelMaxRetries = function(retries) {\n this.forwardChannelMaxRetries_ = retries;\n};\n\n\n/**\n * Sets the timeout for a forward channel request.\n * @param {number} timeoutMs The timeout in milliseconds.\n */\nWebChannelBase.prototype.setForwardChannelRequestTimeout = function(timeoutMs) {\n this.forwardChannelRequestTimeoutMs_ = timeoutMs;\n};\n\n\n/**\n * @return {number} The max number of back-channel retries, which is a constant.\n */\nWebChannelBase.prototype.getBackChannelMaxRetries = function() {\n // Back-channel retries is a constant.\n return WebChannelBase.BACK_CHANNEL_MAX_RETRIES;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.isClosed = function() {\n return this.state_ == WebChannelBase.State.CLOSED;\n};\n\n\n/**\n * Returns the channel state.\n * @return {WebChannelBase.State} The current state of the channel.\n */\nWebChannelBase.prototype.getState = function() {\n return this.state_;\n};\n\n\n/**\n * Return the last status code received for a request.\n * @return {number} The last status code received for a request.\n */\nWebChannelBase.prototype.getLastStatusCode = function() {\n return this.lastStatusCode_;\n};\n\n\n/**\n * @return {number} The last array id received.\n */\nWebChannelBase.prototype.getLastArrayId = function() {\n return this.lastArrayId_;\n};\n\n\n/**\n * Returns whether there are outstanding requests servicing the channel.\n * @return {boolean} true if there are outstanding requests.\n */\nWebChannelBase.prototype.hasOutstandingRequests = function() {\n return this.getOutstandingRequests_() != 0;\n};\n\n\n/**\n * Returns the number of outstanding requests.\n * @return {number} The number of outstanding requests to the server.\n * @private\n */\nWebChannelBase.prototype.getOutstandingRequests_ = function() {\n var count = 0;\n if (this.backChannelRequest_) {\n count++;\n }\n count += this.forwardChannelRequestPool_.getRequestCount();\n return count;\n};\n\n\n/**\n * Ensures that a forward channel request is scheduled.\n * @private\n */\nWebChannelBase.prototype.ensureForwardChannel_ = function() {\n if (this.forwardChannelRequestPool_.isFull()) {\n // enough connection in process - no need to start a new request\n return;\n }\n\n if (this.forwardChannelTimerId_) {\n // no need to start a new request - one is already scheduled\n return;\n }\n\n // Use async.run instead of setTimeout(0) to avoid the 1s message delay\n // from chrome/firefox background tabs\n this.forwardChannelTimerId_ = true;\n goog.async.run(this.onStartForwardChannelTimer_, this);\n\n this.forwardChannelRetryCount_ = 0;\n};\n\n\n/**\n * Schedules a forward-channel retry for the specified request, unless the max\n * retries has been reached.\n * @param {!ChannelRequest} request The failed request to retry.\n * @return {boolean} true iff a retry was scheduled.\n * @private\n */\nWebChannelBase.prototype.maybeRetryForwardChannel_ = function(request) {\n if (this.forwardChannelRequestPool_.getRequestCount() >=\n this.forwardChannelRequestPool_.getMaxSize() -\n (this.forwardChannelTimerId_ ? 1 : 0)) {\n // Should be impossible to be called in this state.\n this.channelDebug_.severe('Unexpected retry request is scheduled.');\n return false;\n }\n\n if (this.forwardChannelTimerId_) {\n this.channelDebug_.debug(\n 'Use the retry request that is already scheduled.');\n this.outgoingMaps_ =\n request.getPendingMessages().concat(this.outgoingMaps_);\n return true;\n }\n\n // No retry for open_() and fail-fast\n if (this.state_ == WebChannelBase.State.INIT ||\n this.state_ == WebChannelBase.State.OPENING ||\n (this.forwardChannelRetryCount_ >= this.getForwardChannelMaxRetries())) {\n return false;\n }\n\n this.channelDebug_.debug('Going to retry POST');\n\n this.forwardChannelTimerId_ = requestStats.setTimeout(\n goog.bind(this.onStartForwardChannelTimer_, this, request),\n this.getRetryTime_(this.forwardChannelRetryCount_));\n this.forwardChannelRetryCount_++;\n return true;\n};\n\n\n/**\n * Timer callback for ensureForwardChannel\n * @param {ChannelRequest=} opt_retryRequest A failed request\n * to retry.\n * @private\n */\nWebChannelBase.prototype.onStartForwardChannelTimer_ = function(\n opt_retryRequest) {\n // null is possible if scheduled with async.run\n if (this.forwardChannelTimerId_) {\n this.forwardChannelTimerId_ = null;\n this.startForwardChannel_(opt_retryRequest);\n }\n};\n\n\n/**\n * Begins a new forward channel operation to the server.\n * @param {ChannelRequest=} opt_retryRequest A failed request to retry.\n * @private\n */\nWebChannelBase.prototype.startForwardChannel_ = function(opt_retryRequest) {\n this.channelDebug_.debug('startForwardChannel_');\n if (!this.okToMakeRequest_()) {\n return; // channel is cancelled\n } else if (this.state_ == WebChannelBase.State.INIT) {\n if (opt_retryRequest) {\n this.channelDebug_.severe('Not supposed to retry the open');\n return;\n }\n this.open_();\n this.state_ = WebChannelBase.State.OPENING;\n } else if (this.state_ == WebChannelBase.State.OPENED) {\n if (opt_retryRequest) {\n this.makeForwardChannelRequest_(opt_retryRequest);\n return;\n }\n\n if (this.outgoingMaps_.length == 0) {\n this.channelDebug_.debug(\n 'startForwardChannel_ returned: ' +\n 'nothing to send');\n // no need to start a new forward channel request\n return;\n }\n\n if (this.forwardChannelRequestPool_.isFull()) {\n // Should be impossible to be called in this state.\n this.channelDebug_.severe(\n 'startForwardChannel_ returned: ' +\n 'connection already in progress');\n return;\n }\n\n this.makeForwardChannelRequest_();\n this.channelDebug_.debug('startForwardChannel_ finished, sent request');\n }\n};\n\n\n/**\n * Establishes a new channel session with the the server.\n * @private\n */\nWebChannelBase.prototype.open_ = function() {\n this.channelDebug_.debug('open_()');\n this.nextRid_ = Math.floor(Math.random() * 100000);\n\n var rid = this.nextRid_++;\n var request =\n ChannelRequest.createChannelRequest(this, this.channelDebug_, '', rid);\n\n // mix the init headers\n var extraHeaders = this.extraHeaders_;\n if (this.initHeaders_) {\n if (extraHeaders) {\n extraHeaders = goog.object.clone(extraHeaders);\n goog.object.extend(extraHeaders, this.initHeaders_);\n } else {\n extraHeaders = this.initHeaders_;\n }\n }\n\n if (this.httpHeadersOverwriteParam_ === null) {\n request.setExtraHeaders(extraHeaders);\n }\n\n var requestText = this.dequeueOutgoingMaps_(\n request,\n this.fastHandshake_ ? this.getMaxNumMessagesForFastHandshake_() :\n WebChannelBase.MAX_MAPS_PER_REQUEST_);\n\n var uri = this.forwardChannelUri_.clone();\n uri.setParameterValue('RID', rid);\n\n if (this.clientVersion_ > 0) {\n uri.setParameterValue('CVER', this.clientVersion_);\n }\n\n // http-session-id to be generated as the response\n if (this.getHttpSessionIdParam()) {\n uri.setParameterValue(\n WebChannel.X_HTTP_SESSION_ID, this.getHttpSessionIdParam());\n }\n\n // Add the reconnect parameters.\n this.addAdditionalParams_(uri);\n\n if (this.httpHeadersOverwriteParam_ && extraHeaders) {\n httpCors.setHttpHeadersWithOverwriteParam(\n uri, this.httpHeadersOverwriteParam_, extraHeaders);\n }\n\n this.forwardChannelRequestPool_.addRequest(request);\n\n // Check the option and use GET to enable QUIC 0-RTT\n if (this.fastHandshake_) {\n uri.setParameterValue('$req', requestText);\n\n // enable handshake upgrade\n uri.setParameterValue('SID', 'null');\n request.setDecodeInitialResponse();\n\n request.xmlHttpPost(uri, null, true); // Send as a GET\n } else {\n request.xmlHttpPost(uri, requestText, true);\n }\n};\n\n\n/**\n * @return {number} The number of raw JSON messages to be encoded\n * with the fast-handshake (GET) request, including zero. If messages are not\n * encoded as raw JSON data, return WebChannelBase.MAX_MAPS_PER_REQUEST_\n * @private\n */\nWebChannelBase.prototype.getMaxNumMessagesForFastHandshake_ = function() {\n var total = 0;\n for (var i = 0; i < this.outgoingMaps_.length; i++) {\n var map = this.outgoingMaps_[i];\n var size = map.getRawDataSize();\n if (size === undefined) {\n break;\n }\n total += size;\n\n if (total > WebChannelBase.MAX_CHARS_PER_GET_) {\n return i;\n }\n\n if (total === WebChannelBase.MAX_CHARS_PER_GET_ ||\n i === this.outgoingMaps_.length - 1) {\n return i + 1;\n }\n }\n\n return WebChannelBase.MAX_MAPS_PER_REQUEST_;\n};\n\n\n\n/**\n * Makes a forward channel request using XMLHTTP.\n * @param {!ChannelRequest=} opt_retryRequest A failed request to retry.\n * @private\n */\nWebChannelBase.prototype.makeForwardChannelRequest_ = function(\n opt_retryRequest) {\n var rid;\n if (opt_retryRequest) {\n rid = opt_retryRequest.getRequestId(); // Reuse the same RID for a retry\n } else {\n rid = this.nextRid_++;\n }\n\n var uri = this.forwardChannelUri_.clone();\n uri.setParameterValue('SID', this.sid_);\n uri.setParameterValue('RID', rid);\n uri.setParameterValue('AID', this.lastArrayId_);\n // Add the additional reconnect parameters.\n this.addAdditionalParams_(uri);\n\n if (this.httpHeadersOverwriteParam_ && this.extraHeaders_) {\n httpCors.setHttpHeadersWithOverwriteParam(\n uri, this.httpHeadersOverwriteParam_, this.extraHeaders_);\n }\n\n var request = ChannelRequest.createChannelRequest(\n this, this.channelDebug_, this.sid_, rid,\n this.forwardChannelRetryCount_ + 1);\n\n if (this.httpHeadersOverwriteParam_ === null) {\n request.setExtraHeaders(this.extraHeaders_);\n }\n\n var requestText;\n if (opt_retryRequest) {\n this.requeuePendingMaps_(opt_retryRequest);\n }\n requestText =\n this.dequeueOutgoingMaps_(request, WebChannelBase.MAX_MAPS_PER_REQUEST_);\n\n // Randomize from 50%-100% of the forward channel timeout to avoid\n // a big hit if servers happen to die at once.\n request.setTimeout(\n Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50) +\n Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50 * Math.random()));\n this.forwardChannelRequestPool_.addRequest(request);\n request.xmlHttpPost(uri, requestText, true);\n};\n\n\n/**\n * Adds the additional parameters from the handler to the given URI.\n * @param {!goog.Uri} uri The URI to add the parameters to.\n * @private\n */\nWebChannelBase.prototype.addAdditionalParams_ = function(uri) {\n // Add the additional reconnect parameters as needed.\n if (this.handler_) {\n var params = this.handler_.getAdditionalParams(this);\n if (params) {\n goog.structs.forEach(params, function(value, key, coll) {\n uri.setParameterValue(key, value);\n });\n }\n }\n};\n\n\n/**\n * Returns the request text from the outgoing maps and resets it.\n * @param {!ChannelRequest} request The new request for sending the messages.\n * @param {number} maxNum The maximum number of messages to be encoded\n * @return {string} The encoded request text created from all the currently\n * queued outgoing maps.\n * @private\n */\nWebChannelBase.prototype.dequeueOutgoingMaps_ = function(request, maxNum) {\n var count = Math.min(this.outgoingMaps_.length, maxNum);\n\n var badMapHandler = this.handler_ ?\n goog.bind(this.handler_.badMapError, this.handler_, this) :\n null;\n var result = this.wireCodec_.encodeMessageQueue(\n this.outgoingMaps_, count, badMapHandler);\n\n request.setPendingMessages(this.outgoingMaps_.splice(0, count));\n\n return result;\n};\n\n\n/**\n * Requeues unacknowledged sent arrays for retransmission in the next forward\n * channel request.\n * @param {!ChannelRequest} retryRequest A failed request to retry.\n * @private\n */\nWebChannelBase.prototype.requeuePendingMaps_ = function(retryRequest) {\n this.outgoingMaps_ =\n retryRequest.getPendingMessages().concat(this.outgoingMaps_);\n};\n\n\n/**\n * Ensures there is a backchannel request for receiving data from the server.\n * @private\n */\nWebChannelBase.prototype.ensureBackChannel_ = function() {\n if (this.backChannelRequest_) {\n // already have one\n return;\n }\n\n if (this.backChannelTimerId_) {\n // no need to start a new request - one is already scheduled\n return;\n }\n\n this.backChannelAttemptId_ = 1;\n\n // Use async.run instead of setTimeout(0) to avoid the 1s message delay\n // from chrome/firefox background tabs\n // backChannelTimerId_ stays unset, as with setTimeout(0)\n goog.async.run(this.onStartBackChannelTimer_, this);\n\n this.backChannelRetryCount_ = 0;\n};\n\n\n/**\n * Schedules a back-channel retry, unless the max retries has been reached.\n * @return {boolean} true iff a retry was scheduled.\n * @private\n */\nWebChannelBase.prototype.maybeRetryBackChannel_ = function() {\n if (this.backChannelRequest_ || this.backChannelTimerId_) {\n // Should be impossible to be called in this state.\n this.channelDebug_.severe('Request already in progress');\n return false;\n }\n\n if (this.backChannelRetryCount_ >= this.getBackChannelMaxRetries()) {\n return false;\n }\n\n this.channelDebug_.debug('Going to retry GET');\n\n this.backChannelAttemptId_++;\n this.backChannelTimerId_ = requestStats.setTimeout(\n goog.bind(this.onStartBackChannelTimer_, this),\n this.getRetryTime_(this.backChannelRetryCount_));\n this.backChannelRetryCount_++;\n return true;\n};\n\n\n/**\n * Timer callback for ensureBackChannel_.\n * @private\n */\nWebChannelBase.prototype.onStartBackChannelTimer_ = function() {\n this.backChannelTimerId_ = null;\n this.startBackChannel_();\n};\n\n\n/**\n * Begins a new back channel operation to the server.\n * @private\n */\nWebChannelBase.prototype.startBackChannel_ = function() {\n if (!this.okToMakeRequest_()) {\n // channel is cancelled\n return;\n }\n\n this.channelDebug_.debug('Creating new HttpRequest');\n this.backChannelRequest_ = ChannelRequest.createChannelRequest(\n this, this.channelDebug_, this.sid_, 'rpc', this.backChannelAttemptId_);\n\n if (this.httpHeadersOverwriteParam_ === null) {\n this.backChannelRequest_.setExtraHeaders(this.extraHeaders_);\n }\n\n this.backChannelRequest_.setReadyStateChangeThrottle(\n this.readyStateChangeThrottleMs_);\n var uri = this.backChannelUri_.clone();\n uri.setParameterValue('RID', 'rpc');\n uri.setParameterValue('SID', this.sid_);\n uri.setParameterValue('CI', this.useChunked_ ? '0' : '1');\n uri.setParameterValue('AID', this.lastArrayId_);\n\n // Add the reconnect parameters.\n this.addAdditionalParams_(uri);\n\n uri.setParameterValue('TYPE', 'xmlhttp');\n\n if (this.httpHeadersOverwriteParam_ && this.extraHeaders_) {\n httpCors.setHttpHeadersWithOverwriteParam(\n uri, this.httpHeadersOverwriteParam_, this.extraHeaders_);\n }\n\n if (this.backChannelRequestTimeoutMs_) {\n this.backChannelRequest_.setTimeout(this.backChannelRequestTimeoutMs_);\n }\n\n this.backChannelRequest_.xmlHttpGet(\n uri, true /* decodeChunks */, this.hostPrefix_);\n\n this.channelDebug_.debug('New Request created');\n};\n\n\n/**\n * Gives the handler a chance to return an error code and stop channel\n * execution. A handler might want to do this to check that the user is still\n * logged in, for example.\n * @private\n * @return {boolean} If it's OK to make a request.\n */\nWebChannelBase.prototype.okToMakeRequest_ = function() {\n if (this.handler_) {\n var result = this.handler_.okToMakeRequest(this);\n if (result != WebChannelBase.Error.OK) {\n this.channelDebug_.debug(\n 'Handler returned error code from okToMakeRequest');\n this.signalError_(result);\n return false;\n }\n }\n return true;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.onRequestData = function(request, responseText) {\n if (this.state_ == WebChannelBase.State.CLOSED ||\n (this.backChannelRequest_ != request &&\n !this.forwardChannelRequestPool_.hasRequest(request))) {\n // either CLOSED or a request we don't know about (perhaps an old request)\n return;\n }\n this.lastStatusCode_ = request.getLastStatusCode();\n\n // first to check if request has been upgraded to backchannel\n if (!request.isInitialResponseDecoded() &&\n this.forwardChannelRequestPool_.hasRequest(request) &&\n this.state_ == WebChannelBase.State.OPENED) {\n var response;\n try {\n response = this.wireCodec_.decodeMessage(responseText);\n } catch (ex) {\n response = null;\n }\n if (Array.isArray(response) && response.length == 3) {\n this.handlePostResponse_(/** @type {!Array<?>} */ (response), request);\n this.onForwardChannelFlushed_();\n } else {\n this.channelDebug_.debug('Bad POST response data returned');\n this.signalError_(WebChannelBase.Error.BAD_RESPONSE);\n }\n } else {\n if (request.isInitialResponseDecoded() ||\n this.backChannelRequest_ == request) {\n this.clearDeadBackchannelTimer_();\n }\n if (!goog.string.isEmptyOrWhitespace(responseText)) {\n var response = this.wireCodec_.decodeMessage(responseText);\n this.onInput_(/** @type {!Array<?>} */ (response), request);\n }\n }\n};\n\n\n/**\n * Checks if we need call the flush callback.\n *\n * @private\n */\nWebChannelBase.prototype.onForwardChannelFlushed_ = function() {\n if (this.forwardChannelRequestPool_.getRequestCount() <= 1) {\n if (this.forwardChannelFlushedCallback_) {\n try {\n this.forwardChannelFlushedCallback_();\n } catch (ex) {\n this.channelDebug_.dumpException(\n ex, 'Exception from forwardChannelFlushedCallback_ ');\n }\n // reset\n this.forwardChannelFlushedCallback_ = undefined;\n }\n }\n};\n\n\n/**\n * Handles a POST response from the server.\n * @param {Array<number>} responseValues The key value pairs in\n * the POST response.\n * @param {!ChannelRequest} forwardReq The forward channel request that\n * triggers this function call.\n * @private\n */\nWebChannelBase.prototype.handlePostResponse_ = function(\n responseValues, forwardReq) {\n // The first response value is set to 0 if server is missing backchannel.\n if (responseValues[0] == 0) {\n this.handleBackchannelMissing_(forwardReq);\n return;\n }\n this.lastPostResponseArrayId_ = responseValues[1];\n var outstandingArrays = this.lastPostResponseArrayId_ - this.lastArrayId_;\n if (0 < outstandingArrays) {\n var numOutstandingBackchannelBytes = responseValues[2];\n this.channelDebug_.debug(\n numOutstandingBackchannelBytes + ' bytes (in ' + outstandingArrays +\n ' arrays) are outstanding on the BackChannel');\n if (!this.shouldRetryBackChannel_(numOutstandingBackchannelBytes)) {\n return;\n }\n if (!this.deadBackChannelTimerId_) {\n // We expect to receive data within 2 RTTs or we retry the backchannel.\n this.deadBackChannelTimerId_ = requestStats.setTimeout(\n goog.bind(this.onBackChannelDead_, this),\n 2 * WebChannelBase.RTT_ESTIMATE);\n }\n }\n};\n\n\n/**\n * Handles a POST response from the server telling us that it has detected that\n * we have no hanging GET connection.\n * @param {!ChannelRequest} forwardReq The forward channel request that\n * triggers this function call.\n * @private\n */\nWebChannelBase.prototype.handleBackchannelMissing_ = function(forwardReq) {\n // As long as the back channel was started before the POST was sent,\n // we should retry the backchannel. We give a slight buffer of RTT_ESTIMATE\n // so as not to excessively retry the backchannel\n this.channelDebug_.debug('Server claims our backchannel is missing.');\n if (this.backChannelTimerId_) {\n this.channelDebug_.debug('But we are currently starting the request.');\n return;\n } else if (!this.backChannelRequest_) {\n this.channelDebug_.warning('We do not have a BackChannel established');\n } else if (\n this.backChannelRequest_.getRequestStartTime() +\n WebChannelBase.RTT_ESTIMATE <\n forwardReq.getRequestStartTime()) {\n this.clearDeadBackchannelTimer_();\n this.backChannelRequest_.cancel();\n this.backChannelRequest_ = null;\n } else {\n return;\n }\n this.maybeRetryBackChannel_();\n requestStats.notifyStatEvent(requestStats.Stat.BACKCHANNEL_MISSING);\n};\n\n\n/**\n * Determines whether we should start the process of retrying a possibly\n * dead backchannel.\n * @param {number} outstandingBytes The number of bytes for which the server has\n * not yet received acknowledgement.\n * @return {boolean} Whether to start the backchannel retry timer.\n * @private\n */\nWebChannelBase.prototype.shouldRetryBackChannel_ = function(outstandingBytes) {\n // Not too many outstanding bytes, not buffered and not after a retry.\n return outstandingBytes <\n WebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF &&\n !this.isBuffered() && this.backChannelRetryCount_ == 0;\n};\n\n\n/**\n * Decides which host prefix should be used, if any. If there is a handler,\n * allows the handler to validate a host prefix provided by the server, and\n * optionally override it.\n * @param {?string} serverHostPrefix The host prefix provided by the server.\n * @return {?string} The host prefix to actually use, if any. Will return null\n * if the use of host prefixes was disabled via setAllowHostPrefix().\n * @override\n */\nWebChannelBase.prototype.correctHostPrefix = function(serverHostPrefix) {\n if (this.allowHostPrefix_) {\n if (this.handler_) {\n return this.handler_.correctHostPrefix(serverHostPrefix);\n }\n return serverHostPrefix;\n }\n return null;\n};\n\n\n/**\n * Handles the timer that indicates that our backchannel is no longer able to\n * successfully receive data from the server.\n * @private\n */\nWebChannelBase.prototype.onBackChannelDead_ = function() {\n if (this.deadBackChannelTimerId_ != null) {\n this.deadBackChannelTimerId_ = null;\n this.backChannelRequest_.cancel();\n this.backChannelRequest_ = null;\n this.maybeRetryBackChannel_();\n requestStats.notifyStatEvent(requestStats.Stat.BACKCHANNEL_DEAD);\n }\n};\n\n\n/**\n * Clears the timer that indicates that our backchannel is no longer able to\n * successfully receive data from the server.\n * @private\n */\nWebChannelBase.prototype.clearDeadBackchannelTimer_ = function() {\n if (this.deadBackChannelTimerId_ != null) {\n goog.global.clearTimeout(this.deadBackChannelTimerId_);\n this.deadBackChannelTimerId_ = null;\n }\n};\n\n\n/**\n * Returns whether or not the given error/status combination is fatal or not.\n * On fatal errors we immediately close the session rather than retrying the\n * failed request.\n * @param {?ChannelRequest.Error} error The error code for the\n * failed request.\n * @param {number} statusCode The last HTTP status code.\n * @return {boolean} Whether or not the error is fatal.\n * @private\n */\nWebChannelBase.isFatalError_ = function(error, statusCode) {\n return error == ChannelRequest.Error.UNKNOWN_SESSION_ID ||\n (error == ChannelRequest.Error.STATUS && statusCode > 0);\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.onRequestComplete = function(request) {\n this.channelDebug_.debug('Request complete');\n var type;\n var pendingMessages = null;\n if (this.backChannelRequest_ == request) {\n this.clearDeadBackchannelTimer_();\n this.backChannelRequest_ = null;\n type = WebChannelBase.ChannelType_.BACK_CHANNEL;\n } else if (this.forwardChannelRequestPool_.hasRequest(request)) {\n pendingMessages = request.getPendingMessages();\n this.forwardChannelRequestPool_.removeRequest(request);\n type = WebChannelBase.ChannelType_.FORWARD_CHANNEL;\n } else {\n // return if it was an old request from a previous session\n return;\n }\n\n this.lastStatusCode_ = request.getLastStatusCode();\n\n if (this.state_ == WebChannelBase.State.CLOSED) {\n return;\n }\n\n if (request.getSuccess()) {\n // Yay!\n if (type == WebChannelBase.ChannelType_.FORWARD_CHANNEL) {\n var size = request.getPostData() ? request.getPostData().length : 0;\n requestStats.notifyTimingEvent(\n size, goog.now() - request.getRequestStartTime(),\n this.forwardChannelRetryCount_);\n this.ensureForwardChannel_();\n this.onSuccess_(request);\n } else { // i.e., back-channel\n this.ensureBackChannel_();\n }\n return;\n }\n // Else unsuccessful. Fall through.\n\n var lastError = request.getLastError();\n if (!WebChannelBase.isFatalError_(lastError, this.lastStatusCode_)) {\n // Maybe retry.\n var self = this;\n this.channelDebug_.debug(function() {\n return 'Maybe retrying, last error: ' +\n ChannelRequest.errorStringFromCode(lastError, self.lastStatusCode_);\n });\n if (type == WebChannelBase.ChannelType_.FORWARD_CHANNEL) {\n if (this.maybeRetryForwardChannel_(request)) {\n return;\n }\n }\n if (type == WebChannelBase.ChannelType_.BACK_CHANNEL) {\n if (this.maybeRetryBackChannel_()) {\n return;\n }\n }\n // Else exceeded max retries. Fall through.\n this.channelDebug_.debug('Exceeded max number of retries');\n } else {\n // Else fatal error. Fall through and mark the pending maps as failed.\n this.channelDebug_.debug('Not retrying due to error type');\n }\n\n\n // Abort the channel now\n\n // Record pending messages from the failed request\n if (pendingMessages && pendingMessages.length > 0) {\n this.forwardChannelRequestPool_.addPendingMessages(pendingMessages);\n }\n\n this.channelDebug_.debug('Error: HTTP request failed');\n switch (lastError) {\n case ChannelRequest.Error.NO_DATA:\n this.signalError_(WebChannelBase.Error.NO_DATA);\n break;\n case ChannelRequest.Error.BAD_DATA:\n this.signalError_(WebChannelBase.Error.BAD_DATA);\n break;\n case ChannelRequest.Error.UNKNOWN_SESSION_ID:\n this.signalError_(WebChannelBase.Error.UNKNOWN_SESSION_ID);\n break;\n default:\n this.signalError_(WebChannelBase.Error.REQUEST_FAILED);\n break;\n }\n};\n\n\n/**\n * @param {number} retryCount Number of retries so far.\n * @return {number} Time in ms before firing next retry request.\n * @private\n */\nWebChannelBase.prototype.getRetryTime_ = function(retryCount) {\n var retryTime = this.baseRetryDelayMs_ +\n Math.floor(Math.random() * this.retryDelaySeedMs_);\n if (!this.isActive()) {\n this.channelDebug_.debug('Inactive channel');\n retryTime = retryTime * WebChannelBase.INACTIVE_CHANNEL_RETRY_FACTOR;\n }\n // Backoff for subsequent retries\n retryTime *= retryCount;\n return retryTime;\n};\n\n\n/**\n * @param {number} baseDelayMs The base part of the retry delay, in ms.\n * @param {number} delaySeedMs A random delay between 0 and this is added to\n * the base part.\n */\nWebChannelBase.prototype.setRetryDelay = function(baseDelayMs, delaySeedMs) {\n this.baseRetryDelayMs_ = baseDelayMs;\n this.retryDelaySeedMs_ = delaySeedMs;\n};\n\n\n/**\n * Apply any handshake control headers.\n * @param {!ChannelRequest} request The underlying request object\n * @private\n */\nWebChannelBase.prototype.applyControlHeaders_ = function(request) {\n var xhr = request.getXhr();\n if (xhr) {\n var clientProtocol =\n xhr.getStreamingResponseHeader(WebChannel.X_CLIENT_WIRE_PROTOCOL);\n if (clientProtocol) {\n this.forwardChannelRequestPool_.applyClientProtocol(clientProtocol);\n }\n\n if (this.getHttpSessionIdParam()) {\n var httpSessionIdHeader =\n xhr.getStreamingResponseHeader(WebChannel.X_HTTP_SESSION_ID);\n if (httpSessionIdHeader) {\n this.setHttpSessionId(httpSessionIdHeader);\n // update the cached uri\n var httpSessionIdParam = this.getHttpSessionIdParam();\n\n this.forwardChannelUri_.setParameterValue(\n /** @type {string} */ (httpSessionIdParam), // never null\n httpSessionIdHeader);\n } else {\n this.channelDebug_.warning(\n 'Missing X_HTTP_SESSION_ID in the handshake response');\n }\n }\n }\n};\n\n\n/**\n * Processes the data returned by the server.\n * @param {!Array<!Array<?>>} respArray The response array returned\n * by the server.\n * @param {!ChannelRequest} request The underlying request object\n * @private\n */\nWebChannelBase.prototype.onInput_ = function(respArray, request) {\n var batch =\n this.handler_ && this.handler_.channelHandleMultipleArrays ? [] : null;\n for (var i = 0; i < respArray.length; i++) {\n var nextArray = respArray[i];\n this.lastArrayId_ = nextArray[0];\n nextArray = nextArray[1];\n if (this.state_ == WebChannelBase.State.OPENING) {\n if (nextArray[0] == 'c') {\n this.sid_ = nextArray[1];\n this.hostPrefix_ = this.correctHostPrefix(nextArray[2]);\n\n var negotiatedVersion = nextArray[3];\n if (negotiatedVersion != null) {\n this.channelVersion_ = negotiatedVersion;\n this.channelDebug_.info('VER=' + this.channelVersion_);\n }\n\n var negotiatedServerVersion = nextArray[4];\n if (negotiatedServerVersion != null) {\n this.serverVersion_ = negotiatedServerVersion;\n this.channelDebug_.info('SVER=' + this.serverVersion_);\n }\n\n // CVER=22\n var serverKeepaliveMs = nextArray[5];\n if (serverKeepaliveMs != null &&\n typeof serverKeepaliveMs === 'number' && serverKeepaliveMs > 0) {\n var timeout = 1.5 * serverKeepaliveMs;\n this.backChannelRequestTimeoutMs_ = timeout;\n this.channelDebug_.info('backChannelRequestTimeoutMs_=' + timeout);\n }\n\n this.applyControlHeaders_(request);\n\n this.state_ = WebChannelBase.State.OPENED;\n if (this.handler_) {\n this.handler_.channelOpened(this);\n }\n\n this.startBackchannelAfterHandshake_(request);\n\n if (this.outgoingMaps_.length > 0) {\n this.ensureForwardChannel_();\n }\n } else if (nextArray[0] == 'stop' || nextArray[0] == 'close') {\n // treat close also as an abort\n this.signalError_(WebChannelBase.Error.STOP);\n }\n } else if (this.state_ == WebChannelBase.State.OPENED) {\n if (nextArray[0] == 'stop' || nextArray[0] == 'close') {\n if (batch && !goog.array.isEmpty(batch)) {\n this.handler_.channelHandleMultipleArrays(this, batch);\n batch.length = 0;\n }\n if (nextArray[0] == 'stop') {\n this.signalError_(WebChannelBase.Error.STOP);\n } else {\n this.disconnect();\n }\n } else if (nextArray[0] == 'noop') {\n // ignore - noop to keep connection happy\n } else {\n if (batch) {\n batch.push(nextArray);\n } else if (this.handler_) {\n this.handler_.channelHandleArray(this, nextArray);\n }\n }\n // We have received useful data on the back-channel, so clear its retry\n // count. We do this because back-channels by design do not complete\n // quickly, so on a flaky connection we could have many fail to complete\n // fully but still deliver a lot of data before they fail. We don't want\n // to count such failures towards the retry limit, because we don't want\n // to give up on a session if we can still receive data.\n this.backChannelRetryCount_ = 0;\n }\n }\n if (batch && !goog.array.isEmpty(batch)) {\n this.handler_.channelHandleMultipleArrays(this, batch);\n }\n};\n\n\n/**\n * Starts the backchannel after the handshake.\n *\n * @param {!ChannelRequest} request The underlying request object\n * @private\n */\nWebChannelBase.prototype.startBackchannelAfterHandshake_ = function(request) {\n this.backChannelUri_ = this.getBackChannelUri(\n this.hostPrefix_, /** @type {string} */ (this.path_));\n\n if (request.isInitialResponseDecoded()) {\n this.channelDebug_.debug('Upgrade the handshake request to a backchannel.');\n this.forwardChannelRequestPool_.removeRequest(request);\n request.resetTimeout(this.backChannelRequestTimeoutMs_);\n this.backChannelRequest_ = request;\n } else {\n // Open connection to receive data\n this.ensureBackChannel_();\n }\n};\n\n\n/**\n * Helper to ensure the channel is in the expected state.\n * @param {...number} var_args The channel must be in one of the indicated\n * states.\n * @private\n */\nWebChannelBase.prototype.ensureInState_ = function(var_args) {\n goog.asserts.assert(\n goog.array.contains(arguments, this.state_),\n 'Unexpected channel state: %s', this.state_);\n};\n\n\n/**\n * Signals an error has occurred.\n * @param {WebChannelBase.Error} error The error code for the failure.\n * @private\n */\nWebChannelBase.prototype.signalError_ = function(error) {\n this.channelDebug_.info('Error code ' + error);\n if (error == WebChannelBase.Error.REQUEST_FAILED) {\n // Create a separate Internet connection to check\n // if it's a server error or user's network error.\n var imageUri = null;\n if (this.handler_) {\n imageUri = this.handler_.getNetworkTestImageUri(this);\n }\n netUtils.testNetwork(goog.bind(this.testNetworkCallback_, this), imageUri);\n } else {\n requestStats.notifyStatEvent(requestStats.Stat.ERROR_OTHER);\n }\n this.onError_(error);\n};\n\n\n/**\n * Callback for netUtils.testNetwork during error handling.\n * @param {boolean} networkUp Whether the network is up.\n * @private\n */\nWebChannelBase.prototype.testNetworkCallback_ = function(networkUp) {\n if (networkUp) {\n this.channelDebug_.info('Successfully pinged google.com');\n requestStats.notifyStatEvent(requestStats.Stat.ERROR_OTHER);\n } else {\n this.channelDebug_.info('Failed to ping google.com');\n requestStats.notifyStatEvent(requestStats.Stat.ERROR_NETWORK);\n // Do not call onError_ again to eliminate duplicated Error events.\n }\n};\n\n\n/**\n * Called when messages have been successfully sent from the queue.\n * @param {!ChannelRequest} request The request object\n * @private\n */\nWebChannelBase.prototype.onSuccess_ = function(request) {\n if (this.handler_) {\n this.handler_.channelSuccess(this, request);\n }\n};\n\n\n/**\n * Called when we've determined the final error for a channel. It closes the\n * notifiers the handler of the error and closes the channel.\n * @param {WebChannelBase.Error} error The error code for the failure.\n * @private\n */\nWebChannelBase.prototype.onError_ = function(error) {\n this.channelDebug_.debug('HttpChannel: error - ' + error);\n this.state_ = WebChannelBase.State.CLOSED;\n if (this.handler_) {\n this.handler_.channelError(this, error);\n }\n this.onClose_();\n this.cancelRequests_();\n};\n\n\n/**\n * Called when the channel has been closed. It notifiers the handler of the\n * event, and reports any pending or undelivered maps.\n * @private\n */\nWebChannelBase.prototype.onClose_ = function() {\n this.state_ = WebChannelBase.State.CLOSED;\n this.lastStatusCode_ = -1;\n if (this.handler_) {\n var pendingMessages = this.forwardChannelRequestPool_.getPendingMessages();\n\n if (pendingMessages.length == 0 && this.outgoingMaps_.length == 0) {\n this.handler_.channelClosed(this);\n } else {\n var self = this;\n this.channelDebug_.debug(function() {\n return 'Number of undelivered maps' +\n ', pending: ' + pendingMessages.length +\n ', outgoing: ' + self.outgoingMaps_.length;\n });\n\n this.forwardChannelRequestPool_.clearPendingMessages();\n\n var copyOfUndeliveredMaps = goog.array.clone(this.outgoingMaps_);\n this.outgoingMaps_.length = 0;\n\n this.handler_.channelClosed(this, pendingMessages, copyOfUndeliveredMaps);\n }\n }\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.getForwardChannelUri = function(path) {\n var uri = this.createDataUri(null, path);\n this.channelDebug_.debug('GetForwardChannelUri: ' + uri);\n return uri;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.getConnectionState = function() {\n return this.connState_;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.getBackChannelUri = function(hostPrefix, path) {\n var uri = this.createDataUri(\n this.shouldUseSecondaryDomains() ? hostPrefix : null, path);\n this.channelDebug_.debug('GetBackChannelUri: ' + uri);\n return uri;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.createDataUri = function(\n hostPrefix, path, opt_overridePort) {\n var uri = goog.Uri.parse(path);\n var uriAbsolute = (uri.getDomain() != '');\n if (uriAbsolute) {\n if (hostPrefix) {\n uri.setDomain(hostPrefix + '.' + uri.getDomain());\n }\n\n uri.setPort(opt_overridePort || uri.getPort());\n } else {\n var locationPage = goog.global.location;\n var hostName;\n if (hostPrefix) {\n hostName = hostPrefix + '.' + locationPage.hostname;\n } else {\n hostName = locationPage.hostname;\n }\n\n var port = opt_overridePort || +locationPage.port;\n\n uri = goog.Uri.create(locationPage.protocol, null, hostName, port, path);\n }\n\n if (this.extraParams_) {\n goog.object.forEach(this.extraParams_, function(value, key) {\n uri.setParameterValue(key, value);\n });\n }\n\n var param = this.getHttpSessionIdParam();\n var value = this.getHttpSessionId();\n if (param && value) {\n uri.setParameterValue(param, value);\n }\n\n // Add the protocol version to the URI.\n uri.setParameterValue('VER', this.channelVersion_);\n\n // Add the reconnect parameters.\n this.addAdditionalParams_(uri);\n\n return uri;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.createXhrIo = function(hostPrefix) {\n if (hostPrefix && !this.supportsCrossDomainXhrs_) {\n throw new Error('Can\\'t create secondary domain capable XhrIo object.');\n }\n var xhr = new goog.net.XhrIo(this.xmlHttpFactory_);\n xhr.setWithCredentials(this.supportsCrossDomainXhrs_);\n return xhr;\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.isActive = function() {\n return !!this.handler_ && this.handler_.isActive(this);\n};\n\n\n/**\n * @override\n */\nWebChannelBase.prototype.shouldUseSecondaryDomains = function() {\n return this.supportsCrossDomainXhrs_;\n};\n\n\n/**\n * Sets (overwrites) the forward channel flush callback.\n *\n * @param {function()} callback The callback to be invoked.\n */\nWebChannelBase.prototype.setForwardChannelFlushCallback = function(callback) {\n this.forwardChannelFlushedCallback_ = callback;\n};\n\n\n/**\n * Abstract base class for the channel handler\n * @constructor\n * @struct\n */\nWebChannelBase.Handler = function() {};\n\n\n/**\n * Callback handler for when a batch of response arrays is received from the\n * server. When null, batched dispatching is disabled.\n * @type {?function(!WebChannelBase, !Array<!Array<?>>)}\n */\nWebChannelBase.Handler.prototype.channelHandleMultipleArrays = null;\n\n\n/**\n * Whether it's okay to make a request to the server. A handler can return\n * false if the channel should fail. For example, if the user has logged out,\n * the handler may want all requests to fail immediately.\n * @param {WebChannelBase} channel The channel.\n * @return {WebChannelBase.Error} An error code. The code should\n * return WebChannelBase.Error.OK to indicate it's okay. Any other\n * error code will cause a failure.\n */\nWebChannelBase.Handler.prototype.okToMakeRequest = function(channel) {\n return WebChannelBase.Error.OK;\n};\n\n\n/**\n * Indicates the WebChannel has successfully negotiated with the server\n * and can now send and receive data.\n * @param {WebChannelBase} channel The channel.\n */\nWebChannelBase.Handler.prototype.channelOpened = function(channel) {};\n\n\n/**\n * New input is available for the application to process.\n *\n * @param {WebChannelBase} channel The channel.\n * @param {Array<?>} array The data array.\n */\nWebChannelBase.Handler.prototype.channelHandleArray = function(\n channel, array) {};\n\n\n/**\n * Indicates messages that have been successfully sent on the channel.\n *\n * @param {WebChannelBase} channel The channel.\n * @param {!ChannelRequest} request The request object that contains\n * the pending messages that have been successfully delivered to the server.\n */\nWebChannelBase.Handler.prototype.channelSuccess = function(channel, request) {};\n\n\n/**\n * Indicates an error occurred on the WebChannel.\n *\n * @param {WebChannelBase} channel The channel.\n * @param {WebChannelBase.Error} error The error code.\n */\nWebChannelBase.Handler.prototype.channelError = function(channel, error) {};\n\n\n/**\n * Indicates the WebChannel is closed. Also notifies about which maps,\n * if any, that may not have been delivered to the server.\n * @param {WebChannelBase} channel The channel.\n * @param {Array<Wire.QueuedMap>=} opt_pendingMaps The\n * array of pending maps, which may or may not have been delivered to the\n * server.\n * @param {Array<Wire.QueuedMap>=} opt_undeliveredMaps\n * The array of undelivered maps, which have definitely not been delivered\n * to the server.\n */\nWebChannelBase.Handler.prototype.channelClosed = function(\n channel, opt_pendingMaps, opt_undeliveredMaps) {};\n\n\n/**\n * Gets any parameters that should be added at the time another connection is\n * made to the server.\n * @param {WebChannelBase} channel The channel.\n * @return {!Object} Extra parameter keys and values to add to the requests.\n */\nWebChannelBase.Handler.prototype.getAdditionalParams = function(channel) {\n return {};\n};\n\n\n/**\n * Gets the URI of an image that can be used to test network connectivity.\n * @param {WebChannelBase} channel The channel.\n * @return {goog.Uri?} A custom URI to load for the network test.\n */\nWebChannelBase.Handler.prototype.getNetworkTestImageUri = function(channel) {\n return null;\n};\n\n\n/**\n * Gets whether this channel is currently active. This is used to determine the\n * length of time to wait before retrying.\n * @param {WebChannelBase} channel The channel.\n * @return {boolean} Whether the channel is currently active.\n */\nWebChannelBase.Handler.prototype.isActive = function(channel) {\n return true;\n};\n\n\n/**\n * Called by the channel if enumeration of the map throws an exception.\n * @param {WebChannelBase} channel The channel.\n * @param {Object} map The map that can't be enumerated.\n */\nWebChannelBase.Handler.prototype.badMapError = function(channel, map) {};\n\n\n/**\n * Allows the handler to override a host prefix provided by the server. Will\n * be called whenever the channel has received such a prefix and is considering\n * its use.\n * @param {?string} serverHostPrefix The host prefix provided by the server.\n * @return {?string} The host prefix the client should use.\n */\nWebChannelBase.Handler.prototype.correctHostPrefix = function(\n serverHostPrefix) {\n return serverHostPrefix;\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Class for parsing and formatting URIs.\n *\n * Use goog.Uri(string) to parse a URI string. Use goog.Uri.create(...) to\n * create a new instance of the goog.Uri object from Uri parts.\n *\n * e.g: <code>var myUri = new goog.Uri(window.location);</code>\n *\n * Implements RFC 3986 for parsing/formatting URIs.\n * http://www.ietf.org/rfc/rfc3986.txt\n *\n * Some changes have been made to the interface (more like .NETs), though the\n * internal representation is now of un-encoded parts, this will change the\n * behavior slightly.\n */\n\ngoog.provide('goog.Uri');\ngoog.provide('goog.Uri.QueryData');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.string');\ngoog.require('goog.structs');\ngoog.require('goog.structs.Map');\ngoog.require('goog.uri.utils');\ngoog.require('goog.uri.utils.ComponentIndex');\ngoog.require('goog.uri.utils.StandardQueryParam');\n\n\n\n/**\n * This class contains setters and getters for the parts of the URI.\n * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part\n * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the\n * decoded path, <code>/foo bar</code>.\n *\n * Reserved characters (see RFC 3986 section 2.2) can be present in\n * their percent-encoded form in scheme, domain, and path URI components and\n * will not be auto-decoded. For example:\n * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will\n * return <code>relative/path%2fto/resource</code>.\n *\n * The constructor accepts an optional unparsed, raw URI string. The parser\n * is relaxed, so special characters that aren't escaped but don't cause\n * ambiguities will not cause parse failures.\n *\n * All setters return <code>this</code> and so may be chained, a la\n * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>.\n *\n * @param {*=} opt_uri Optional string URI to parse\n * (use goog.Uri.create() to create a URI from parts), or if\n * a goog.Uri is passed, a clone is created.\n * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore\n * the case of the parameter name.\n *\n * @throws URIError If opt_uri is provided and URI is malformed (that is,\n * if decodeURIComponent fails on any of the URI components).\n * @constructor\n * @struct\n */\ngoog.Uri = function(opt_uri, opt_ignoreCase) {\n /**\n * Scheme such as \"http\".\n * @private {string}\n */\n this.scheme_ = '';\n\n /**\n * User credentials in the form \"username:password\".\n * @private {string}\n */\n this.userInfo_ = '';\n\n /**\n * Domain part, e.g. \"www.google.com\".\n * @private {string}\n */\n this.domain_ = '';\n\n /**\n * Port, e.g. 8080.\n * @private {?number}\n */\n this.port_ = null;\n\n /**\n * Path, e.g. \"/tests/img.png\".\n * @private {string}\n */\n this.path_ = '';\n\n /**\n * The fragment without the #.\n * @private {string}\n */\n this.fragment_ = '';\n\n /**\n * Whether or not this Uri should be treated as Read Only.\n * @private {boolean}\n */\n this.isReadOnly_ = false;\n\n /**\n * Whether or not to ignore case when comparing query params.\n * @private {boolean}\n */\n this.ignoreCase_ = false;\n\n /**\n * Object representing query data.\n * @private {!goog.Uri.QueryData}\n */\n this.queryData_;\n\n // Parse in the uri string\n var m;\n if (opt_uri instanceof goog.Uri) {\n this.ignoreCase_ = (opt_ignoreCase !== undefined) ? opt_ignoreCase :\n opt_uri.getIgnoreCase();\n this.setScheme(opt_uri.getScheme());\n this.setUserInfo(opt_uri.getUserInfo());\n this.setDomain(opt_uri.getDomain());\n this.setPort(opt_uri.getPort());\n this.setPath(opt_uri.getPath());\n this.setQueryData(opt_uri.getQueryData().clone());\n this.setFragment(opt_uri.getFragment());\n } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {\n this.ignoreCase_ = !!opt_ignoreCase;\n\n // Set the parts -- decoding as we do so.\n // COMPATIBILITY NOTE - In IE, unmatched fields may be empty strings,\n // whereas in other browsers they will be undefined.\n this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);\n this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);\n this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);\n this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);\n this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);\n this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);\n this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);\n\n } else {\n this.ignoreCase_ = !!opt_ignoreCase;\n this.queryData_ = new goog.Uri.QueryData(null, this.ignoreCase_);\n }\n};\n\n\n/**\n * Parameter name added to stop caching.\n * @type {string}\n */\ngoog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;\n\n\n/**\n * @return {string} The string form of the url.\n * @override\n */\ngoog.Uri.prototype.toString = function() {\n var out = [];\n\n var scheme = this.getScheme();\n if (scheme) {\n out.push(\n goog.Uri.encodeSpecialChars_(\n scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),\n ':');\n }\n\n var domain = this.getDomain();\n if (domain || scheme == 'file') {\n out.push('//');\n\n var userInfo = this.getUserInfo();\n if (userInfo) {\n out.push(\n goog.Uri.encodeSpecialChars_(\n userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),\n '@');\n }\n\n out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));\n\n var port = this.getPort();\n if (port != null) {\n out.push(':', String(port));\n }\n }\n\n var path = this.getPath();\n if (path) {\n if (this.hasDomain() && path.charAt(0) != '/') {\n out.push('/');\n }\n out.push(goog.Uri.encodeSpecialChars_(\n path,\n path.charAt(0) == '/' ? goog.Uri.reDisallowedInAbsolutePath_ :\n goog.Uri.reDisallowedInRelativePath_,\n true));\n }\n\n var query = this.getEncodedQuery();\n if (query) {\n out.push('?', query);\n }\n\n var fragment = this.getFragment();\n if (fragment) {\n out.push(\n '#',\n goog.Uri.encodeSpecialChars_(\n fragment, goog.Uri.reDisallowedInFragment_));\n }\n return out.join('');\n};\n\n\n/**\n * Resolves the given relative URI (a goog.Uri object), using the URI\n * represented by this instance as the base URI.\n *\n * There are several kinds of relative URIs:<br>\n * 1. foo - replaces the last part of the path, the whole query and fragment<br>\n * 2. /foo - replaces the path, the query and fragment<br>\n * 3. //foo - replaces everything from the domain on. foo is a domain name<br>\n * 4. ?foo - replace the query and fragment<br>\n * 5. #foo - replace the fragment only\n *\n * Additionally, if relative URI has a non-empty path, all \"..\" and \".\"\n * segments will be resolved, as described in RFC 3986.\n *\n * @param {!goog.Uri} relativeUri The relative URI to resolve.\n * @return {!goog.Uri} The resolved URI.\n */\ngoog.Uri.prototype.resolve = function(relativeUri) {\n var absoluteUri = this.clone();\n\n // we satisfy these conditions by looking for the first part of relativeUri\n // that is not blank and applying defaults to the rest\n\n var overridden = relativeUri.hasScheme();\n\n if (overridden) {\n absoluteUri.setScheme(relativeUri.getScheme());\n } else {\n overridden = relativeUri.hasUserInfo();\n }\n\n if (overridden) {\n absoluteUri.setUserInfo(relativeUri.getUserInfo());\n } else {\n overridden = relativeUri.hasDomain();\n }\n\n if (overridden) {\n absoluteUri.setDomain(relativeUri.getDomain());\n } else {\n overridden = relativeUri.hasPort();\n }\n\n var path = relativeUri.getPath();\n if (overridden) {\n absoluteUri.setPort(relativeUri.getPort());\n } else {\n overridden = relativeUri.hasPath();\n if (overridden) {\n // resolve path properly\n if (path.charAt(0) != '/') {\n // path is relative\n if (this.hasDomain() && !this.hasPath()) {\n // RFC 3986, section 5.2.3, case 1\n path = '/' + path;\n } else {\n // RFC 3986, section 5.2.3, case 2\n var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');\n if (lastSlashIndex != -1) {\n path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;\n }\n }\n }\n path = goog.Uri.removeDotSegments(path);\n }\n }\n\n if (overridden) {\n absoluteUri.setPath(path);\n } else {\n overridden = relativeUri.hasQuery();\n }\n\n if (overridden) {\n absoluteUri.setQueryData(relativeUri.getQueryData().clone());\n } else {\n overridden = relativeUri.hasFragment();\n }\n\n if (overridden) {\n absoluteUri.setFragment(relativeUri.getFragment());\n }\n\n return absoluteUri;\n};\n\n\n/**\n * Clones the URI instance.\n * @return {!goog.Uri} New instance of the URI object.\n */\ngoog.Uri.prototype.clone = function() {\n return new goog.Uri(this);\n};\n\n\n/**\n * @return {string} The encoded scheme/protocol for the URI.\n */\ngoog.Uri.prototype.getScheme = function() {\n return this.scheme_;\n};\n\n\n/**\n * Sets the scheme/protocol.\n * @throws URIError If opt_decode is true and newScheme is malformed (that is,\n * if decodeURIComponent fails).\n * @param {string} newScheme New scheme value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setScheme = function(newScheme, opt_decode) {\n this.enforceReadOnly();\n this.scheme_ =\n opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) : newScheme;\n\n // remove an : at the end of the scheme so somebody can pass in\n // window.location.protocol\n if (this.scheme_) {\n this.scheme_ = this.scheme_.replace(/:$/, '');\n }\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the scheme has been set.\n */\ngoog.Uri.prototype.hasScheme = function() {\n return !!this.scheme_;\n};\n\n\n/**\n * @return {string} The decoded user info.\n */\ngoog.Uri.prototype.getUserInfo = function() {\n return this.userInfo_;\n};\n\n\n/**\n * Sets the userInfo.\n * @throws URIError If opt_decode is true and newUserInfo is malformed (that is,\n * if decodeURIComponent fails).\n * @param {string} newUserInfo New userInfo value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {\n this.enforceReadOnly();\n this.userInfo_ =\n opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) : newUserInfo;\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the user info has been set.\n */\ngoog.Uri.prototype.hasUserInfo = function() {\n return !!this.userInfo_;\n};\n\n\n/**\n * @return {string} The decoded domain.\n */\ngoog.Uri.prototype.getDomain = function() {\n return this.domain_;\n};\n\n\n/**\n * Sets the domain.\n * @throws URIError If opt_decode is true and newDomain is malformed (that is,\n * if decodeURIComponent fails).\n * @param {string} newDomain New domain value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setDomain = function(newDomain, opt_decode) {\n this.enforceReadOnly();\n this.domain_ =\n opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) : newDomain;\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the domain has been set.\n */\ngoog.Uri.prototype.hasDomain = function() {\n return !!this.domain_;\n};\n\n\n/**\n * @return {?number} The port number.\n */\ngoog.Uri.prototype.getPort = function() {\n return this.port_;\n};\n\n\n/**\n * Sets the port number.\n * @param {*} newPort Port number. Will be explicitly casted to a number.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setPort = function(newPort) {\n this.enforceReadOnly();\n\n if (newPort) {\n newPort = Number(newPort);\n if (isNaN(newPort) || newPort < 0) {\n throw new Error('Bad port number ' + newPort);\n }\n this.port_ = newPort;\n } else {\n this.port_ = null;\n }\n\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the port has been set.\n */\ngoog.Uri.prototype.hasPort = function() {\n return this.port_ != null;\n};\n\n\n/**\n * @return {string} The decoded path.\n */\ngoog.Uri.prototype.getPath = function() {\n return this.path_;\n};\n\n\n/**\n * Sets the path.\n * @throws URIError If opt_decode is true and newPath is malformed (that is,\n * if decodeURIComponent fails).\n * @param {string} newPath New path value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setPath = function(newPath, opt_decode) {\n this.enforceReadOnly();\n this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the path has been set.\n */\ngoog.Uri.prototype.hasPath = function() {\n return !!this.path_;\n};\n\n\n/**\n * @return {boolean} Whether the query string has been set.\n */\ngoog.Uri.prototype.hasQuery = function() {\n return this.queryData_.toString() !== '';\n};\n\n\n/**\n * Sets the query data.\n * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * Applies only if queryData is a string.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setQueryData = function(queryData, opt_decode) {\n this.enforceReadOnly();\n\n if (queryData instanceof goog.Uri.QueryData) {\n this.queryData_ = queryData;\n this.queryData_.setIgnoreCase(this.ignoreCase_);\n } else {\n if (!opt_decode) {\n // QueryData accepts encoded query string, so encode it if\n // opt_decode flag is not true.\n queryData = goog.Uri.encodeSpecialChars_(\n queryData, goog.Uri.reDisallowedInQuery_);\n }\n this.queryData_ = new goog.Uri.QueryData(queryData, this.ignoreCase_);\n }\n\n return this;\n};\n\n\n/**\n * Sets the URI query.\n * @param {string} newQuery New query value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setQuery = function(newQuery, opt_decode) {\n return this.setQueryData(newQuery, opt_decode);\n};\n\n\n/**\n * @return {string} The encoded URI query, not including the ?.\n */\ngoog.Uri.prototype.getEncodedQuery = function() {\n return this.queryData_.toString();\n};\n\n\n/**\n * @return {string} The decoded URI query, not including the ?.\n */\ngoog.Uri.prototype.getDecodedQuery = function() {\n return this.queryData_.toDecodedString();\n};\n\n\n/**\n * Returns the query data.\n * @return {!goog.Uri.QueryData} QueryData object.\n */\ngoog.Uri.prototype.getQueryData = function() {\n return this.queryData_;\n};\n\n\n/**\n * @return {string} The encoded URI query, not including the ?.\n *\n * Warning: This method, unlike other getter methods, returns encoded\n * value, instead of decoded one.\n */\ngoog.Uri.prototype.getQuery = function() {\n return this.getEncodedQuery();\n};\n\n\n/**\n * Sets the value of the named query parameters, clearing previous values for\n * that key.\n *\n * @param {string} key The parameter to set.\n * @param {*} value The new value. Value does not need to be encoded.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setParameterValue = function(key, value) {\n this.enforceReadOnly();\n this.queryData_.set(key, value);\n return this;\n};\n\n\n/**\n * Sets the values of the named query parameters, clearing previous values for\n * that key. Not new values will currently be moved to the end of the query\n * string.\n *\n * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])\n * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>\n *\n * @param {string} key The parameter to set.\n * @param {*} values The new values. If values is a single\n * string then it will be treated as the sole value. Values do not need to\n * be encoded.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setParameterValues = function(key, values) {\n this.enforceReadOnly();\n\n if (!Array.isArray(values)) {\n values = [String(values)];\n }\n\n this.queryData_.setValues(key, values);\n\n return this;\n};\n\n\n/**\n * Returns the value<b>s</b> for a given cgi parameter as a list of decoded\n * query parameter values.\n * @param {string} name The parameter to get values for.\n * @return {!Array<?>} The values for a given cgi parameter as a list of\n * decoded query parameter values.\n */\ngoog.Uri.prototype.getParameterValues = function(name) {\n return this.queryData_.getValues(name);\n};\n\n\n/**\n * Returns the first value for a given cgi parameter or undefined if the given\n * parameter name does not appear in the query string.\n * @param {string} paramName Unescaped parameter name.\n * @return {string|undefined} The first value for a given cgi parameter or\n * undefined if the given parameter name does not appear in the query\n * string.\n */\ngoog.Uri.prototype.getParameterValue = function(paramName) {\n return /** @type {string|undefined} */ (this.queryData_.get(paramName));\n};\n\n\n/**\n * @return {string} The URI fragment, not including the #.\n */\ngoog.Uri.prototype.getFragment = function() {\n return this.fragment_;\n};\n\n\n/**\n * Sets the URI fragment.\n * @throws URIError If opt_decode is true and newFragment is malformed (that is,\n * if decodeURIComponent fails).\n * @param {string} newFragment New fragment value.\n * @param {boolean=} opt_decode Optional param for whether to decode new value.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.setFragment = function(newFragment, opt_decode) {\n this.enforceReadOnly();\n this.fragment_ =\n opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) : newFragment;\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the URI has a fragment set.\n */\ngoog.Uri.prototype.hasFragment = function() {\n return !!this.fragment_;\n};\n\n\n/**\n * Returns true if this has the same domain as that of uri2.\n * @param {!goog.Uri} uri2 The URI object to compare to.\n * @return {boolean} true if same domain; false otherwise.\n */\ngoog.Uri.prototype.hasSameDomainAs = function(uri2) {\n return ((!this.hasDomain() && !uri2.hasDomain()) ||\n this.getDomain() == uri2.getDomain()) &&\n ((!this.hasPort() && !uri2.hasPort()) ||\n this.getPort() == uri2.getPort());\n};\n\n\n/**\n * Adds a random parameter to the Uri.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.makeUnique = function() {\n this.enforceReadOnly();\n this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());\n\n return this;\n};\n\n\n/**\n * Removes the named query parameter.\n *\n * @param {string} key The parameter to remove.\n * @return {!goog.Uri} Reference to this URI object.\n */\ngoog.Uri.prototype.removeParameter = function(key) {\n this.enforceReadOnly();\n this.queryData_.remove(key);\n return this;\n};\n\n\n/**\n * Sets whether Uri is read only. If this goog.Uri is read-only,\n * enforceReadOnly_ will be called at the start of any function that may modify\n * this Uri.\n * @param {boolean} isReadOnly whether this goog.Uri should be read only.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.setReadOnly = function(isReadOnly) {\n this.isReadOnly_ = isReadOnly;\n return this;\n};\n\n\n/**\n * @return {boolean} Whether the URI is read only.\n */\ngoog.Uri.prototype.isReadOnly = function() {\n return this.isReadOnly_;\n};\n\n\n/**\n * Checks if this Uri has been marked as read only, and if so, throws an error.\n * This should be called whenever any modifying function is called.\n */\ngoog.Uri.prototype.enforceReadOnly = function() {\n if (this.isReadOnly_) {\n throw new Error('Tried to modify a read-only Uri');\n }\n};\n\n\n/**\n * Sets whether to ignore case.\n * NOTE: If there are already key/value pairs in the QueryData, and\n * ignoreCase_ is set to false, the keys will all be lower-cased.\n * @param {boolean} ignoreCase whether this goog.Uri should ignore case.\n * @return {!goog.Uri} Reference to this Uri object.\n */\ngoog.Uri.prototype.setIgnoreCase = function(ignoreCase) {\n this.ignoreCase_ = ignoreCase;\n if (this.queryData_) {\n this.queryData_.setIgnoreCase(ignoreCase);\n }\n return this;\n};\n\n\n/**\n * @return {boolean} Whether to ignore case.\n */\ngoog.Uri.prototype.getIgnoreCase = function() {\n return this.ignoreCase_;\n};\n\n\n//==============================================================================\n// Static members\n//==============================================================================\n\n\n/**\n * Creates a uri from the string form. Basically an alias of new goog.Uri().\n * If a Uri object is passed to parse then it will return a clone of the object.\n *\n * @throws URIError If parsing the URI is malformed. The passed URI components\n * should all be parseable by decodeURIComponent.\n * @param {*} uri Raw URI string or instance of Uri\n * object.\n * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter\n * names in #getParameterValue.\n * @return {!goog.Uri} The new URI object.\n */\ngoog.Uri.parse = function(uri, opt_ignoreCase) {\n return uri instanceof goog.Uri ? uri.clone() :\n new goog.Uri(uri, opt_ignoreCase);\n};\n\n\n/**\n * Creates a new goog.Uri object from unencoded parts.\n *\n * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.\n * @param {?string=} opt_userInfo username:password.\n * @param {?string=} opt_domain www.google.com.\n * @param {?number=} opt_port 9830.\n * @param {?string=} opt_path /some/path/to/a/file.html.\n * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.\n * @param {?string=} opt_fragment The fragment without the #.\n * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in\n * #getParameterValue.\n *\n * @return {!goog.Uri} The new URI object.\n */\ngoog.Uri.create = function(\n opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query,\n opt_fragment, opt_ignoreCase) {\n var uri = new goog.Uri(null, opt_ignoreCase);\n\n // Only set the parts if they are defined and not empty strings.\n opt_scheme && uri.setScheme(opt_scheme);\n opt_userInfo && uri.setUserInfo(opt_userInfo);\n opt_domain && uri.setDomain(opt_domain);\n opt_port && uri.setPort(opt_port);\n opt_path && uri.setPath(opt_path);\n opt_query && uri.setQueryData(opt_query);\n opt_fragment && uri.setFragment(opt_fragment);\n\n return uri;\n};\n\n\n/**\n * Resolves a relative Uri against a base Uri, accepting both strings and\n * Uri objects.\n *\n * @param {*} base Base Uri.\n * @param {*} rel Relative Uri.\n * @return {!goog.Uri} Resolved uri.\n */\ngoog.Uri.resolve = function(base, rel) {\n if (!(base instanceof goog.Uri)) {\n base = goog.Uri.parse(base);\n }\n\n if (!(rel instanceof goog.Uri)) {\n rel = goog.Uri.parse(rel);\n }\n\n return base.resolve(rel);\n};\n\n\n/**\n * Removes dot segments in given path component, as described in\n * RFC 3986, section 5.2.4.\n *\n * @param {string} path A non-empty path component.\n * @return {string} Path component with removed dot segments.\n */\ngoog.Uri.removeDotSegments = function(path) {\n if (path == '..' || path == '.') {\n return '';\n\n } else if (\n !goog.string.contains(path, './') && !goog.string.contains(path, '/.')) {\n // This optimization detects uris which do not contain dot-segments,\n // and as a consequence do not require any processing.\n return path;\n\n } else {\n var leadingSlash = goog.string.startsWith(path, '/');\n var segments = path.split('/');\n var out = [];\n\n for (var pos = 0; pos < segments.length;) {\n var segment = segments[pos++];\n\n if (segment == '.') {\n if (leadingSlash && pos == segments.length) {\n out.push('');\n }\n } else if (segment == '..') {\n if (out.length > 1 || out.length == 1 && out[0] != '') {\n out.pop();\n }\n if (leadingSlash && pos == segments.length) {\n out.push('');\n }\n } else {\n out.push(segment);\n leadingSlash = true;\n }\n }\n\n return out.join('/');\n }\n};\n\n\n/**\n * Decodes a value or returns the empty string if it isn't defined or empty.\n * @throws URIError If decodeURIComponent fails to decode val.\n * @param {string|undefined} val Value to decode.\n * @param {boolean=} opt_preserveReserved If true, restricted characters will\n * not be decoded.\n * @return {string} Decoded value.\n * @private\n */\ngoog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {\n // Don't use UrlDecode() here because val is not a query parameter.\n if (!val) {\n return '';\n }\n\n // decodeURI has the same output for '%2f' and '%252f'. We double encode %25\n // so that we can distinguish between the 2 inputs. This is later undone by\n // removeDoubleEncoding_.\n return opt_preserveReserved ? decodeURI(val.replace(/%25/g, '%2525')) :\n decodeURIComponent(val);\n};\n\n\n/**\n * If unescapedPart is non null, then escapes any characters in it that aren't\n * valid characters in a url and also escapes any special characters that\n * appear in extra.\n *\n * @param {*} unescapedPart The string to encode.\n * @param {RegExp} extra A character set of characters in [\\01-\\177].\n * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent\n * encoding.\n * @return {?string} null iff unescapedPart == null.\n * @private\n */\ngoog.Uri.encodeSpecialChars_ = function(\n unescapedPart, extra, opt_removeDoubleEncoding) {\n if (typeof unescapedPart === 'string') {\n var encoded = encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_);\n if (opt_removeDoubleEncoding) {\n // encodeURI double-escapes %XX sequences used to represent restricted\n // characters in some URI components, remove the double escaping here.\n encoded = goog.Uri.removeDoubleEncoding_(encoded);\n }\n return encoded;\n }\n return null;\n};\n\n\n/**\n * Converts a character in [\\01-\\177] to its unicode character equivalent.\n * @param {string} ch One character string.\n * @return {string} Encoded string.\n * @private\n */\ngoog.Uri.encodeChar_ = function(ch) {\n var n = ch.charCodeAt(0);\n return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);\n};\n\n\n/**\n * Removes double percent-encoding from a string.\n * @param {string} doubleEncodedString String\n * @return {string} String with double encoding removed.\n * @private\n */\ngoog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {\n return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');\n};\n\n\n/**\n * Regular expression for characters that are disallowed in the scheme or\n * userInfo part of the URI.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\\/\\?@]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in a relative path.\n * Colon is included due to RFC 3986 3.3.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInRelativePath_ = /[\\#\\?:]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in an absolute path.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInAbsolutePath_ = /[\\#\\?]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in the query.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInQuery_ = /[\\#\\?@]/g;\n\n\n/**\n * Regular expression for characters that are disallowed in the fragment.\n * @type {RegExp}\n * @private\n */\ngoog.Uri.reDisallowedInFragment_ = /#/g;\n\n\n/**\n * Checks whether two URIs have the same domain.\n * @param {string} uri1String First URI string.\n * @param {string} uri2String Second URI string.\n * @return {boolean} true if the two URIs have the same domain; false otherwise.\n */\ngoog.Uri.haveSameDomain = function(uri1String, uri2String) {\n // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.\n // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.\n var pieces1 = goog.uri.utils.split(uri1String);\n var pieces2 = goog.uri.utils.split(uri2String);\n return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==\n pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&\n pieces1[goog.uri.utils.ComponentIndex.PORT] ==\n pieces2[goog.uri.utils.ComponentIndex.PORT];\n};\n\n\n\n/**\n * Class used to represent URI query parameters. It is essentially a hash of\n * name-value pairs, though a name can be present more than once.\n *\n * Has the same interface as the collections in goog.structs.\n *\n * @param {?string=} opt_query Optional encoded query string to parse into\n * the object.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n * name in #get.\n * @constructor\n * @struct\n * @final\n */\ngoog.Uri.QueryData = function(opt_query, opt_ignoreCase) {\n /**\n * The map containing name/value or name/array-of-values pairs.\n * May be null if it requires parsing from the query string.\n *\n * We need to use a Map because we cannot guarantee that the key names will\n * not be problematic for IE.\n *\n * @private {?goog.structs.Map<string, !Array<*>>}\n */\n this.keyMap_ = null;\n\n /**\n * The number of params, or null if it requires computing.\n * @private {?number}\n */\n this.count_ = null;\n\n /**\n * Encoded query string, or null if it requires computing from the key map.\n * @private {?string}\n */\n this.encodedQuery_ = opt_query || null;\n\n /**\n * If true, ignore the case of the parameter name in #get.\n * @private {boolean}\n */\n this.ignoreCase_ = !!opt_ignoreCase;\n};\n\n\n/**\n * If the underlying key map is not yet initialized, it parses the\n * query string and fills the map with parsed data.\n * @private\n */\ngoog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {\n if (!this.keyMap_) {\n this.keyMap_ = new goog.structs.Map();\n this.count_ = 0;\n if (this.encodedQuery_) {\n var self = this;\n goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {\n self.add(goog.string.urlDecode(name), value);\n });\n }\n }\n};\n\n\n/**\n * Creates a new query data instance from a map of names and values.\n *\n * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter\n * names to parameter value. If parameter value is an array, it is\n * treated as if the key maps to each individual value in the\n * array.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n * name in #get.\n * @return {!goog.Uri.QueryData} The populated query data instance.\n */\ngoog.Uri.QueryData.createFromMap = function(map, opt_ignoreCase) {\n var keys = goog.structs.getKeys(map);\n if (typeof keys == 'undefined') {\n throw new Error('Keys are undefined');\n }\n\n var queryData = new goog.Uri.QueryData(null, opt_ignoreCase);\n var values = goog.structs.getValues(map);\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var value = values[i];\n if (!Array.isArray(value)) {\n queryData.add(key, value);\n } else {\n queryData.setValues(key, value);\n }\n }\n return queryData;\n};\n\n\n/**\n * Creates a new query data instance from parallel arrays of parameter names\n * and values. Allows for duplicate parameter names. Throws an error if the\n * lengths of the arrays differ.\n *\n * @param {!Array<string>} keys Parameter names.\n * @param {!Array<?>} values Parameter values.\n * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter\n * name in #get.\n * @return {!goog.Uri.QueryData} The populated query data instance.\n */\ngoog.Uri.QueryData.createFromKeysValues = function(\n keys, values, opt_ignoreCase) {\n if (keys.length != values.length) {\n throw new Error('Mismatched lengths for keys/values');\n }\n var queryData = new goog.Uri.QueryData(null, opt_ignoreCase);\n for (var i = 0; i < keys.length; i++) {\n queryData.add(keys[i], values[i]);\n }\n return queryData;\n};\n\n\n/**\n * @return {?number} The number of parameters.\n */\ngoog.Uri.QueryData.prototype.getCount = function() {\n this.ensureKeyMapInitialized_();\n return this.count_;\n};\n\n\n/**\n * Adds a key value pair.\n * @param {string} key Name.\n * @param {*} value Value.\n * @return {!goog.Uri.QueryData} Instance of this object.\n */\ngoog.Uri.QueryData.prototype.add = function(key, value) {\n this.ensureKeyMapInitialized_();\n this.invalidateCache_();\n\n key = this.getKeyName_(key);\n var values = this.keyMap_.get(key);\n if (!values) {\n this.keyMap_.set(key, (values = []));\n }\n values.push(value);\n this.count_ = goog.asserts.assertNumber(this.count_) + 1;\n return this;\n};\n\n\n/**\n * Removes all the params with the given key.\n * @param {string} key Name.\n * @return {boolean} Whether any parameter was removed.\n */\ngoog.Uri.QueryData.prototype.remove = function(key) {\n this.ensureKeyMapInitialized_();\n\n key = this.getKeyName_(key);\n if (this.keyMap_.containsKey(key)) {\n this.invalidateCache_();\n\n // Decrement parameter count.\n this.count_ =\n goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;\n return this.keyMap_.remove(key);\n }\n return false;\n};\n\n\n/**\n * Clears the parameters.\n */\ngoog.Uri.QueryData.prototype.clear = function() {\n this.invalidateCache_();\n this.keyMap_ = null;\n this.count_ = 0;\n};\n\n\n/**\n * @return {boolean} Whether we have any parameters.\n */\ngoog.Uri.QueryData.prototype.isEmpty = function() {\n this.ensureKeyMapInitialized_();\n return this.count_ == 0;\n};\n\n\n/**\n * Whether there is a parameter with the given name\n * @param {string} key The parameter name to check for.\n * @return {boolean} Whether there is a parameter with the given name.\n */\ngoog.Uri.QueryData.prototype.containsKey = function(key) {\n this.ensureKeyMapInitialized_();\n key = this.getKeyName_(key);\n return this.keyMap_.containsKey(key);\n};\n\n\n/**\n * Whether there is a parameter with the given value.\n * @param {*} value The value to check for.\n * @return {boolean} Whether there is a parameter with the given value.\n */\ngoog.Uri.QueryData.prototype.containsValue = function(value) {\n // NOTE(arv): This solution goes through all the params even if it was the\n // first param. We can get around this by not reusing code or by switching to\n // iterators.\n var vals = this.getValues();\n return goog.array.contains(vals, value);\n};\n\n\n/**\n * Runs a callback on every key-value pair in the map, including duplicate keys.\n * This won't maintain original order when duplicate keys are interspersed (like\n * getKeys() / getValues()).\n * @param {function(this:SCOPE, ?, string, !goog.Uri.QueryData)} f\n * @param {SCOPE=} opt_scope The value of \"this\" inside f.\n * @template SCOPE\n */\ngoog.Uri.QueryData.prototype.forEach = function(f, opt_scope) {\n this.ensureKeyMapInitialized_();\n this.keyMap_.forEach(function(values, key) {\n goog.array.forEach(values, function(value) {\n f.call(opt_scope, value, key, this);\n }, this);\n }, this);\n};\n\n\n/**\n * Returns all the keys of the parameters. If a key is used multiple times\n * it will be included multiple times in the returned array\n * @return {!Array<string>} All the keys of the parameters.\n */\ngoog.Uri.QueryData.prototype.getKeys = function() {\n this.ensureKeyMapInitialized_();\n // We need to get the values to know how many keys to add.\n var vals = this.keyMap_.getValues();\n var keys = this.keyMap_.getKeys();\n var rv = [];\n for (var i = 0; i < keys.length; i++) {\n var val = vals[i];\n for (var j = 0; j < val.length; j++) {\n rv.push(keys[i]);\n }\n }\n return rv;\n};\n\n\n/**\n * Returns all the values of the parameters with the given name. If the query\n * data has no such key this will return an empty array. If no key is given\n * all values wil be returned.\n * @param {string=} opt_key The name of the parameter to get the values for.\n * @return {!Array<?>} All the values of the parameters with the given name.\n */\ngoog.Uri.QueryData.prototype.getValues = function(opt_key) {\n this.ensureKeyMapInitialized_();\n var rv = [];\n if (typeof opt_key === 'string') {\n if (this.containsKey(opt_key)) {\n rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));\n }\n } else {\n // Return all values.\n var values = this.keyMap_.getValues();\n for (var i = 0; i < values.length; i++) {\n rv = goog.array.concat(rv, values[i]);\n }\n }\n return rv;\n};\n\n\n/**\n * Sets a key value pair and removes all other keys with the same value.\n *\n * @param {string} key Name.\n * @param {*} value Value.\n * @return {!goog.Uri.QueryData} Instance of this object.\n */\ngoog.Uri.QueryData.prototype.set = function(key, value) {\n this.ensureKeyMapInitialized_();\n this.invalidateCache_();\n\n // TODO(chrishenry): This could be better written as\n // this.remove(key), this.add(key, value), but that would reorder\n // the key (since the key is first removed and then added at the\n // end) and we would have to fix unit tests that depend on key\n // ordering.\n key = this.getKeyName_(key);\n if (this.containsKey(key)) {\n this.count_ =\n goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;\n }\n this.keyMap_.set(key, [value]);\n this.count_ = goog.asserts.assertNumber(this.count_) + 1;\n return this;\n};\n\n\n/**\n * Returns the first value associated with the key. If the query data has no\n * such key this will return undefined or the optional default.\n * @param {string} key The name of the parameter to get the value for.\n * @param {*=} opt_default The default value to return if the query data\n * has no such key.\n * @return {*} The first string value associated with the key, or opt_default\n * if there's no value.\n */\ngoog.Uri.QueryData.prototype.get = function(key, opt_default) {\n if (!key) {\n return opt_default;\n }\n var values = this.getValues(key);\n return values.length > 0 ? String(values[0]) : opt_default;\n};\n\n\n/**\n * Sets the values for a key. If the key already exists, this will\n * override all of the existing values that correspond to the key.\n * @param {string} key The key to set values for.\n * @param {!Array<?>} values The values to set.\n */\ngoog.Uri.QueryData.prototype.setValues = function(key, values) {\n this.remove(key);\n\n if (values.length > 0) {\n this.invalidateCache_();\n this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));\n this.count_ = goog.asserts.assertNumber(this.count_) + values.length;\n }\n};\n\n\n/**\n * @return {string} Encoded query string.\n * @override\n */\ngoog.Uri.QueryData.prototype.toString = function() {\n if (this.encodedQuery_) {\n return this.encodedQuery_;\n }\n\n if (!this.keyMap_) {\n return '';\n }\n\n var sb = [];\n\n // In the past, we use this.getKeys() and this.getVals(), but that\n // generates a lot of allocations as compared to simply iterating\n // over the keys.\n var keys = this.keyMap_.getKeys();\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var encodedKey = goog.string.urlEncode(key);\n var val = this.getValues(key);\n for (var j = 0; j < val.length; j++) {\n var param = encodedKey;\n // Ensure that null and undefined are encoded into the url as\n // literal strings.\n if (val[j] !== '') {\n param += '=' + goog.string.urlEncode(val[j]);\n }\n sb.push(param);\n }\n }\n\n return this.encodedQuery_ = sb.join('&');\n};\n\n\n/**\n * @throws URIError If URI is malformed (that is, if decodeURIComponent fails on\n * any of the URI components).\n * @return {string} Decoded query string.\n */\ngoog.Uri.QueryData.prototype.toDecodedString = function() {\n return goog.Uri.decodeOrEmpty_(this.toString());\n};\n\n\n/**\n * Invalidate the cache.\n * @private\n */\ngoog.Uri.QueryData.prototype.invalidateCache_ = function() {\n this.encodedQuery_ = null;\n};\n\n\n/**\n * Removes all keys that are not in the provided list. (Modifies this object.)\n * @param {Array<string>} keys The desired keys.\n * @return {!goog.Uri.QueryData} a reference to this object.\n */\ngoog.Uri.QueryData.prototype.filterKeys = function(keys) {\n this.ensureKeyMapInitialized_();\n this.keyMap_.forEach(function(value, key) {\n if (!goog.array.contains(keys, key)) {\n this.remove(key);\n }\n }, this);\n return this;\n};\n\n\n/**\n * Clone the query data instance.\n * @return {!goog.Uri.QueryData} New instance of the QueryData object.\n */\ngoog.Uri.QueryData.prototype.clone = function() {\n var rv = new goog.Uri.QueryData();\n rv.encodedQuery_ = this.encodedQuery_;\n if (this.keyMap_) {\n rv.keyMap_ = this.keyMap_.clone();\n rv.count_ = this.count_;\n }\n return rv;\n};\n\n\n/**\n * Helper function to get the key name from a JavaScript object. Converts\n * the object to a string, and to lower case if necessary.\n * @private\n * @param {*} arg The object to get a key name from.\n * @return {string} valid key name which can be looked up in #keyMap_.\n */\ngoog.Uri.QueryData.prototype.getKeyName_ = function(arg) {\n var keyName = String(arg);\n if (this.ignoreCase_) {\n keyName = keyName.toLowerCase();\n }\n return keyName;\n};\n\n\n/**\n * Ignore case in parameter names.\n * NOTE: If there are already key/value pairs in the QueryData, and\n * ignoreCase_ is set to false, the keys will all be lower-cased.\n * @param {boolean} ignoreCase whether this goog.Uri should ignore case.\n */\ngoog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {\n var resetKeys = ignoreCase && !this.ignoreCase_;\n if (resetKeys) {\n this.ensureKeyMapInitialized_();\n this.invalidateCache_();\n this.keyMap_.forEach(function(value, key) {\n var lowerCase = key.toLowerCase();\n if (key != lowerCase) {\n this.remove(key);\n this.setValues(lowerCase, value);\n }\n }, this);\n }\n this.ignoreCase_ = ignoreCase;\n};\n\n\n/**\n * Extends a query data object with another query data or map like object. This\n * operates 'in-place', it does not create a new QueryData object.\n *\n * @param {...(?goog.Uri.QueryData|?goog.structs.Map<?, ?>|?Object)} var_args\n * The object from which key value pairs will be copied. Note: does not\n * accept null.\n * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different\n * types of parameters.\n */\ngoog.Uri.QueryData.prototype.extend = function(var_args) {\n for (var i = 0; i < arguments.length; i++) {\n var data = arguments[i];\n goog.structs.forEach(data, function(value, key) {\n this.add(key, value);\n }, this);\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utilities for string manipulation.\n */\n\n\n/**\n * Namespace for string utilities\n */\ngoog.provide('goog.string');\ngoog.provide('goog.string.Unicode');\n\ngoog.require('goog.dom.safe');\ngoog.require('goog.html.uncheckedconversions');\ngoog.require('goog.string.Const');\ngoog.require('goog.string.internal');\n\n\n/**\n * @define {boolean} Enables HTML escaping of lowercase letter \"e\" which helps\n * with detection of double-escaping as this letter is frequently used.\n */\ngoog.string.DETECT_DOUBLE_ESCAPING =\n goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);\n\n\n/**\n * @define {boolean} Whether to force non-dom html unescaping.\n */\ngoog.string.FORCE_NON_DOM_HTML_UNESCAPING =\n goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);\n\n\n/**\n * Common Unicode string characters.\n * @enum {string}\n */\ngoog.string.Unicode = {\n NBSP: '\\xa0'\n};\n\n\n/**\n * Fast prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the start of `str`.\n * @return {boolean} True if `str` begins with `prefix`.\n */\ngoog.string.startsWith = goog.string.internal.startsWith;\n\n\n/**\n * Fast suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix`.\n */\ngoog.string.endsWith = goog.string.internal.endsWith;\n\n\n/**\n * Case-insensitive prefix-checker.\n * @param {string} str The string to check.\n * @param {string} prefix A string to look for at the end of `str`.\n * @return {boolean} True if `str` begins with `prefix` (ignoring\n * case).\n */\ngoog.string.caseInsensitiveStartsWith =\n goog.string.internal.caseInsensitiveStartsWith;\n\n\n/**\n * Case-insensitive suffix-checker.\n * @param {string} str The string to check.\n * @param {string} suffix A string to look for at the end of `str`.\n * @return {boolean} True if `str` ends with `suffix` (ignoring\n * case).\n */\ngoog.string.caseInsensitiveEndsWith =\n goog.string.internal.caseInsensitiveEndsWith;\n\n\n/**\n * Case-insensitive equality checker.\n * @param {string} str1 First string to check.\n * @param {string} str2 Second string to check.\n * @return {boolean} True if `str1` and `str2` are the same string,\n * ignoring case.\n */\ngoog.string.caseInsensitiveEquals = goog.string.internal.caseInsensitiveEquals;\n\n\n/**\n * Does simple python-style string substitution.\n * subs(\"foo%s hot%s\", \"bar\", \"dog\") becomes \"foobar hotdog\".\n * @param {string} str The string containing the pattern.\n * @param {...*} var_args The items to substitute into the pattern.\n * @return {string} A copy of `str` in which each occurrence of\n * {@code %s} has been replaced an argument from `var_args`.\n */\ngoog.string.subs = function(str, var_args) {\n var splitParts = str.split('%s');\n var returnString = '';\n\n var subsArguments = Array.prototype.slice.call(arguments, 1);\n while (subsArguments.length &&\n // Replace up to the last split part. We are inserting in the\n // positions between split parts.\n splitParts.length > 1) {\n returnString += splitParts.shift() + subsArguments.shift();\n }\n\n return returnString + splitParts.join('%s'); // Join unused '%s'\n};\n\n\n/**\n * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines\n * and tabs) to a single space, and strips leading and trailing whitespace.\n * @param {string} str Input string.\n * @return {string} A copy of `str` with collapsed whitespace.\n */\ngoog.string.collapseWhitespace = function(str) {\n // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n // include it in the regexp to enforce consistent cross-browser behavior.\n return str.replace(/[\\s\\xa0]+/g, ' ').replace(/^\\s+|\\s+$/g, '');\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n */\ngoog.string.isEmptyOrWhitespace = goog.string.internal.isEmptyOrWhitespace;\n\n\n/**\n * Checks if a string is empty.\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty.\n */\ngoog.string.isEmptyString = function(str) {\n return str.length == 0;\n};\n\n\n/**\n * Checks if a string is empty or contains only whitespaces.\n *\n * @param {string} str The string to check.\n * @return {boolean} Whether `str` is empty or whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace instead.\n */\ngoog.string.isEmpty = goog.string.isEmptyOrWhitespace;\n\n\n/**\n * Checks if a string is null, undefined, empty or contains only whitespaces.\n * @param {*} str The string to check.\n * @return {boolean} Whether `str` is null, undefined, empty, or\n * whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))\n * instead.\n */\ngoog.string.isEmptyOrWhitespaceSafe = function(str) {\n return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));\n};\n\n\n/**\n * Checks if a string is null, undefined, empty or contains only whitespaces.\n *\n * @param {*} str The string to check.\n * @return {boolean} Whether `str` is null, undefined, empty, or\n * whitespace only.\n * @deprecated Use goog.string.isEmptyOrWhitespace instead.\n */\ngoog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;\n\n\n/**\n * Checks if a string is all breaking whitespace.\n * @param {string} str The string to check.\n * @return {boolean} Whether the string is all breaking whitespace.\n */\ngoog.string.isBreakingWhitespace = function(str) {\n return !/[^\\t\\n\\r ]/.test(str);\n};\n\n\n/**\n * Checks if a string contains all letters.\n * @param {string} str string to check.\n * @return {boolean} True if `str` consists entirely of letters.\n */\ngoog.string.isAlpha = function(str) {\n return !/[^a-zA-Z]/.test(str);\n};\n\n\n/**\n * Checks if a string contains only numbers.\n * @param {*} str string to check. If not a string, it will be\n * casted to one.\n * @return {boolean} True if `str` is numeric.\n */\ngoog.string.isNumeric = function(str) {\n return !/[^0-9]/.test(str);\n};\n\n\n/**\n * Checks if a string contains only numbers or letters.\n * @param {string} str string to check.\n * @return {boolean} True if `str` is alphanumeric.\n */\ngoog.string.isAlphaNumeric = function(str) {\n return !/[^a-zA-Z0-9]/.test(str);\n};\n\n\n/**\n * Checks if a character is a space character.\n * @param {string} ch Character to check.\n * @return {boolean} True if `ch` is a space.\n */\ngoog.string.isSpace = function(ch) {\n return ch == ' ';\n};\n\n\n/**\n * Checks if a character is a valid unicode character.\n * @param {string} ch Character to check.\n * @return {boolean} True if `ch` is a valid unicode character.\n */\ngoog.string.isUnicodeChar = function(ch) {\n return ch.length == 1 && ch >= ' ' && ch <= '~' ||\n ch >= '\\u0080' && ch <= '\\uFFFD';\n};\n\n\n/**\n * Takes a string and replaces newlines with a space. Multiple lines are\n * replaced with a single space.\n * @param {string} str The string from which to strip newlines.\n * @return {string} A copy of `str` stripped of newlines.\n */\ngoog.string.stripNewlines = function(str) {\n return str.replace(/(\\r\\n|\\r|\\n)+/g, ' ');\n};\n\n\n/**\n * Replaces Windows and Mac new lines with unix style: \\r or \\r\\n with \\n.\n * @param {string} str The string to in which to canonicalize newlines.\n * @return {string} `str` A copy of {@code} with canonicalized newlines.\n */\ngoog.string.canonicalizeNewlines = function(str) {\n return str.replace(/(\\r\\n|\\r|\\n)/g, '\\n');\n};\n\n\n/**\n * Normalizes whitespace in a string, replacing all whitespace chars with\n * a space.\n * @param {string} str The string in which to normalize whitespace.\n * @return {string} A copy of `str` with all whitespace normalized.\n */\ngoog.string.normalizeWhitespace = function(str) {\n return str.replace(/\\xa0|\\s/g, ' ');\n};\n\n\n/**\n * Normalizes spaces in a string, replacing all consecutive spaces and tabs\n * with a single space. Replaces non-breaking space with a space.\n * @param {string} str The string in which to normalize spaces.\n * @return {string} A copy of `str` with all consecutive spaces and tabs\n * replaced with a single space.\n */\ngoog.string.normalizeSpaces = function(str) {\n return str.replace(/\\xa0|[ \\t]+/g, ' ');\n};\n\n\n/**\n * Removes the breaking spaces from the left and right of the string and\n * collapses the sequences of breaking spaces in the middle into single spaces.\n * The original and the result strings render the same way in HTML.\n * @param {string} str A string in which to collapse spaces.\n * @return {string} Copy of the string with normalized breaking spaces.\n */\ngoog.string.collapseBreakingSpaces = function(str) {\n return str.replace(/[\\t\\r\\n ]+/g, ' ')\n .replace(/^[\\t\\r\\n ]+|[\\t\\r\\n ]+$/g, '');\n};\n\n\n/**\n * Trims white spaces to the left and right of a string.\n * @param {string} str The string to trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trim = goog.string.internal.trim;\n\n\n/**\n * Trims whitespaces at the left end of a string.\n * @param {string} str The string to left trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trimLeft = function(str) {\n // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n // include it in the regexp to enforce consistent cross-browser behavior.\n return str.replace(/^[\\s\\xa0]+/, '');\n};\n\n\n/**\n * Trims whitespaces at the right end of a string.\n * @param {string} str The string to right trim.\n * @return {string} A trimmed copy of `str`.\n */\ngoog.string.trimRight = function(str) {\n // Since IE doesn't include non-breaking-space (0xa0) in their \\s character\n // class (as required by section 7.2 of the ECMAScript spec), we explicitly\n // include it in the regexp to enforce consistent cross-browser behavior.\n return str.replace(/[\\s\\xa0]+$/, '');\n};\n\n\n/**\n * A string comparator that ignores case.\n * -1 = str1 less than str2\n * 0 = str1 equals str2\n * 1 = str1 greater than str2\n *\n * @param {string} str1 The string to compare.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} The comparator result, as described above.\n */\ngoog.string.caseInsensitiveCompare =\n goog.string.internal.caseInsensitiveCompare;\n\n\n/**\n * Compares two strings interpreting their numeric substrings as numbers.\n *\n * @param {string} str1 First string.\n * @param {string} str2 Second string.\n * @param {!RegExp} tokenizerRegExp Splits a string into substrings of\n * non-negative integers, non-numeric characters and optionally fractional\n * numbers starting with a decimal point.\n * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if\n * str1 > str2.\n * @private\n */\ngoog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) {\n if (str1 == str2) {\n return 0;\n }\n if (!str1) {\n return -1;\n }\n if (!str2) {\n return 1;\n }\n\n // Using match to split the entire string ahead of time turns out to be faster\n // for most inputs than using RegExp.exec or iterating over each character.\n var tokens1 = str1.toLowerCase().match(tokenizerRegExp);\n var tokens2 = str2.toLowerCase().match(tokenizerRegExp);\n\n var count = Math.min(tokens1.length, tokens2.length);\n\n for (var i = 0; i < count; i++) {\n var a = tokens1[i];\n var b = tokens2[i];\n\n // Compare pairs of tokens, returning if one token sorts before the other.\n if (a != b) {\n // Only if both tokens are integers is a special comparison required.\n // Decimal numbers are sorted as strings (e.g., '.09' < '.1').\n var num1 = parseInt(a, 10);\n if (!isNaN(num1)) {\n var num2 = parseInt(b, 10);\n if (!isNaN(num2) && num1 - num2) {\n return num1 - num2;\n }\n }\n return a < b ? -1 : 1;\n }\n }\n\n // If one string is a substring of the other, the shorter string sorts first.\n if (tokens1.length != tokens2.length) {\n return tokens1.length - tokens2.length;\n }\n\n // The two strings must be equivalent except for case (perfect equality is\n // tested at the head of the function.) Revert to default ASCII string\n // comparison to stabilize the sort.\n return str1 < str2 ? -1 : 1;\n};\n\n\n/**\n * String comparison function that handles non-negative integer numbers in a\n * way humans might expect. Using this function, the string 'File 2.jpg' sorts\n * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison\n * is mostly case-insensitive, though strings that are identical except for case\n * are sorted with the upper-case strings before lower-case.\n *\n * This comparison function is up to 50x slower than either the default or the\n * case-insensitive compare. It should not be used in time-critical code, but\n * should be fast enough to sort several hundred short strings (like filenames)\n * with a reasonable delay.\n *\n * @param {string} str1 The string to compare in a numerically sensitive way.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than\n * 0 if str1 > str2.\n */\ngoog.string.intAwareCompare = function(str1, str2) {\n return goog.string.numberAwareCompare_(str1, str2, /\\d+|\\D+/g);\n};\n\n\n/**\n * String comparison function that handles non-negative integer and fractional\n * numbers in a way humans might expect. Using this function, the string\n * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent\n * to {@link goog.string.intAwareCompare} apart from the way how it interprets\n * dots.\n *\n * @param {string} str1 The string to compare in a numerically sensitive way.\n * @param {string} str2 The string to compare `str1` to.\n * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than\n * 0 if str1 > str2.\n */\ngoog.string.floatAwareCompare = function(str1, str2) {\n return goog.string.numberAwareCompare_(str1, str2, /\\d+|\\.\\d+|\\D+/g);\n};\n\n\n/**\n * Alias for {@link goog.string.floatAwareCompare}.\n *\n * @param {string} str1\n * @param {string} str2\n * @return {number}\n */\ngoog.string.numerateCompare = goog.string.floatAwareCompare;\n\n\n/**\n * URL-encodes a string\n * @param {*} str The string to url-encode.\n * @return {string} An encoded copy of `str` that is safe for urls.\n * Note that '#', ':', and other characters used to delimit portions\n * of URLs *will* be encoded.\n */\ngoog.string.urlEncode = function(str) {\n return encodeURIComponent(String(str));\n};\n\n\n/**\n * URL-decodes the string. We need to specially handle '+'s because\n * the javascript library doesn't convert them to spaces.\n * @param {string} str The string to url decode.\n * @return {string} The decoded `str`.\n */\ngoog.string.urlDecode = function(str) {\n return decodeURIComponent(str.replace(/\\+/g, ' '));\n};\n\n\n/**\n * Converts \\n to <br>s or <br />s.\n * @param {string} str The string in which to convert newlines.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} A copy of `str` with converted newlines.\n */\ngoog.string.newLineToBr = goog.string.internal.newLineToBr;\n\n\n/**\n * Escapes double quote '\"' and single quote '\\'' characters in addition to\n * '&', '<', and '>' so that a string can be included in an HTML tag attribute\n * value within double or single quotes.\n *\n * It should be noted that > doesn't need to be escaped for the HTML or XML to\n * be valid, but it has been decided to escape it for consistency with other\n * implementations.\n *\n * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the\n * lowercase letter \"e\".\n *\n * NOTE(user):\n * HtmlEscape is often called during the generation of large blocks of HTML.\n * Using statics for the regular expressions and strings is an optimization\n * that can more than half the amount of time IE spends in this function for\n * large apps, since strings and regexes both contribute to GC allocations.\n *\n * Testing for the presence of a character before escaping increases the number\n * of function calls, but actually provides a speed increase for the average\n * case -- since the average case often doesn't require the escaping of all 4\n * characters and indexOf() is much cheaper than replace().\n * The worst case does suffer slightly from the additional calls, therefore the\n * opt_isLikelyToContainHtmlChars option has been included for situations\n * where all 4 HTML entities are very likely to be present and need escaping.\n *\n * Some benchmarks (times tended to fluctuate +-0.05ms):\n * FireFox IE6\n * (no chars / average (mix of cases) / all 4 chars)\n * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80\n * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84\n * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85\n *\n * An additional advantage of checking if replace actually needs to be called\n * is a reduction in the number of object allocations, so as the size of the\n * application grows the difference between the various methods would increase.\n *\n * @param {string} str string to be escaped.\n * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see\n * if the character needs replacing - use this option if you expect each of\n * the characters to appear often. Leave false if you expect few html\n * characters to occur in your strings, such as if you are escaping HTML.\n * @return {string} An escaped copy of `str`.\n */\ngoog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {\n str = goog.string.internal.htmlEscape(str, opt_isLikelyToContainHtmlChars);\n if (goog.string.DETECT_DOUBLE_ESCAPING) {\n str = str.replace(goog.string.E_RE_, 'e');\n }\n return str;\n};\n\n\n/**\n * Regular expression that matches a lowercase letter \"e\", for use in escaping.\n * @const {!RegExp}\n * @private\n */\ngoog.string.E_RE_ = /e/g;\n\n\n/**\n * Unescapes an HTML string.\n *\n * @param {string} str The string to unescape.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapeEntities = function(str) {\n if (goog.string.contains(str, '&')) {\n // We are careful not to use a DOM if we do not have one or we explicitly\n // requested non-DOM html unescaping.\n if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&\n 'document' in goog.global) {\n return goog.string.unescapeEntitiesUsingDom_(str);\n } else {\n // Fall back on pure XML entities\n return goog.string.unescapePureXmlEntities_(str);\n }\n }\n return str;\n};\n\n\n/**\n * Unescapes a HTML string using the provided document.\n *\n * @param {string} str The string to unescape.\n * @param {!Document} document A document to use in escaping the string.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapeEntitiesWithDocument = function(str, document) {\n if (goog.string.contains(str, '&')) {\n return goog.string.unescapeEntitiesUsingDom_(str, document);\n }\n return str;\n};\n\n\n/**\n * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric\n * entities. This function is XSS-safe and whitespace-preserving.\n * @private\n * @param {string} str The string to unescape.\n * @param {Document=} opt_document An optional document to use for creating\n * elements. If this is not specified then the default window.document\n * will be used.\n * @return {string} The unescaped `str` string.\n */\ngoog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {\n /** @type {!Object<string, string>} */\n var seen = {'&': '&', '<': '<', '>': '>', '"': '\"'};\n /** @type {!Element} */\n var div;\n if (opt_document) {\n div = opt_document.createElement('div');\n } else {\n div = goog.global.document.createElement('div');\n }\n // Match as many valid entity characters as possible. If the actual entity\n // happens to be shorter, it will still work as innerHTML will return the\n // trailing characters unchanged. Since the entity characters do not include\n // open angle bracket, there is no chance of XSS from the innerHTML use.\n // Since no whitespace is passed to innerHTML, whitespace is preserved.\n return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {\n // Check for cached entity.\n var value = seen[s];\n if (value) {\n return value;\n }\n // Check for numeric entity.\n if (entity.charAt(0) == '#') {\n // Prefix with 0 so that hex entities (e.g. ) parse as hex numbers.\n var n = Number('0' + entity.substr(1));\n if (!isNaN(n)) {\n value = String.fromCharCode(n);\n }\n }\n // Fall back to innerHTML otherwise.\n if (!value) {\n // Append a non-entity character to avoid a bug in Webkit that parses\n // an invalid entity at the end of innerHTML text as the empty string.\n goog.dom.safe.setInnerHtml(\n div,\n goog.html.uncheckedconversions\n .safeHtmlFromStringKnownToSatisfyTypeContract(\n goog.string.Const.from('Single HTML entity.'), s + ' '));\n // Then remove the trailing character from the result.\n value = div.firstChild.nodeValue.slice(0, -1);\n }\n // Cache and return.\n return seen[s] = value;\n });\n};\n\n\n/**\n * Unescapes XML entities.\n * @private\n * @param {string} str The string to unescape.\n * @return {string} An unescaped copy of `str`.\n */\ngoog.string.unescapePureXmlEntities_ = function(str) {\n return str.replace(/&([^;]+);/g, function(s, entity) {\n switch (entity) {\n case 'amp':\n return '&';\n case 'lt':\n return '<';\n case 'gt':\n return '>';\n case 'quot':\n return '\"';\n default:\n if (entity.charAt(0) == '#') {\n // Prefix with 0 so that hex entities (e.g. ) parse as hex.\n var n = Number('0' + entity.substr(1));\n if (!isNaN(n)) {\n return String.fromCharCode(n);\n }\n }\n // For invalid entities we just return the entity\n return s;\n }\n });\n};\n\n\n/**\n * Regular expression that matches an HTML entity.\n * See also HTML5: Tokenization / Tokenizing character references.\n * @private\n * @type {!RegExp}\n */\ngoog.string.HTML_ENTITY_PATTERN_ = /&([^;\\s<&]+);?/g;\n\n\n/**\n * Do escaping of whitespace to preserve spatial formatting. We use character\n * entity #160 to make it safer for xml.\n * @param {string} str The string in which to escape whitespace.\n * @param {boolean=} opt_xml Whether to use XML compatible tags.\n * @return {string} An escaped copy of `str`.\n */\ngoog.string.whitespaceEscape = function(str, opt_xml) {\n // This doesn't use goog.string.preserveSpaces for backwards compatibility.\n return goog.string.newLineToBr(str.replace(/ /g, '  '), opt_xml);\n};\n\n\n/**\n * Preserve spaces that would be otherwise collapsed in HTML by replacing them\n * with non-breaking space Unicode characters.\n * @param {string} str The string in which to preserve whitespace.\n * @return {string} A copy of `str` with preserved whitespace.\n */\ngoog.string.preserveSpaces = function(str) {\n return str.replace(/(^|[\\n ]) /g, '$1' + goog.string.Unicode.NBSP);\n};\n\n\n/**\n * Strip quote characters around a string. The second argument is a string of\n * characters to treat as quotes. This can be a single character or a string of\n * multiple character and in that case each of those are treated as possible\n * quote characters. For example:\n *\n * <pre>\n * goog.string.stripQuotes('\"abc\"', '\"`') --> 'abc'\n * goog.string.stripQuotes('`abc`', '\"`') --> 'abc'\n * </pre>\n *\n * @param {string} str The string to strip.\n * @param {string} quoteChars The quote characters to strip.\n * @return {string} A copy of `str` without the quotes.\n */\ngoog.string.stripQuotes = function(str, quoteChars) {\n var length = quoteChars.length;\n for (var i = 0; i < length; i++) {\n var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);\n if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {\n return str.substring(1, str.length - 1);\n }\n }\n return str;\n};\n\n\n/**\n * Truncates a string to a certain length and adds '...' if necessary. The\n * length also accounts for the ellipsis, so a maximum length of 10 and a string\n * 'Hello World!' produces 'Hello W...'.\n * @param {string} str The string to truncate.\n * @param {number} chars Max number of characters.\n * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped\n * characters from being cut off in the middle.\n * @return {string} The truncated `str` string.\n */\ngoog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {\n if (opt_protectEscapedCharacters) {\n str = goog.string.unescapeEntities(str);\n }\n\n if (str.length > chars) {\n str = str.substring(0, chars - 3) + '...';\n }\n\n if (opt_protectEscapedCharacters) {\n str = goog.string.htmlEscape(str);\n }\n\n return str;\n};\n\n\n/**\n * Truncate a string in the middle, adding \"...\" if necessary,\n * and favoring the beginning of the string.\n * @param {string} str The string to truncate the middle of.\n * @param {number} chars Max number of characters.\n * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped\n * characters from being cutoff in the middle.\n * @param {number=} opt_trailingChars Optional number of trailing characters to\n * leave at the end of the string, instead of truncating as close to the\n * middle as possible.\n * @return {string} A truncated copy of `str`.\n */\ngoog.string.truncateMiddle = function(\n str, chars, opt_protectEscapedCharacters, opt_trailingChars) {\n if (opt_protectEscapedCharacters) {\n str = goog.string.unescapeEntities(str);\n }\n\n if (opt_trailingChars && str.length > chars) {\n if (opt_trailingChars > chars) {\n opt_trailingChars = chars;\n }\n var endPoint = str.length - opt_trailingChars;\n var startPoint = chars - opt_trailingChars;\n str = str.substring(0, startPoint) + '...' + str.substring(endPoint);\n } else if (str.length > chars) {\n // Favor the beginning of the string:\n var half = Math.floor(chars / 2);\n var endPos = str.length - half;\n half += chars % 2;\n str = str.substring(0, half) + '...' + str.substring(endPos);\n }\n\n if (opt_protectEscapedCharacters) {\n str = goog.string.htmlEscape(str);\n }\n\n return str;\n};\n\n\n/**\n * Special chars that need to be escaped for goog.string.quote.\n * @private {!Object<string, string>}\n */\ngoog.string.specialEscapeChars_ = {\n '\\0': '\\\\0',\n '\\b': '\\\\b',\n '\\f': '\\\\f',\n '\\n': '\\\\n',\n '\\r': '\\\\r',\n '\\t': '\\\\t',\n '\\x0B': '\\\\x0B', // '\\v' is not supported in JScript\n '\"': '\\\\\"',\n '\\\\': '\\\\\\\\',\n // To support the use case of embedding quoted strings inside of script\n // tags, we have to make sure HTML comments and opening/closing script tags do\n // not appear in the resulting string. The specific strings that must be\n // escaped are documented at:\n // https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements\n '<': '\\\\u003C' // NOTE: JSON.parse crashes on '\\\\x3c'.\n};\n\n\n/**\n * Character mappings used internally for goog.string.escapeChar.\n * @private {!Object<string, string>}\n */\ngoog.string.jsEscapeCache_ = {\n '\\'': '\\\\\\''\n};\n\n\n/**\n * Encloses a string in double quotes and escapes characters so that the\n * string is a valid JS string. The resulting string is safe to embed in\n * `<script>` tags as \"<\" is escaped.\n * @param {string} s The string to quote.\n * @return {string} A copy of `s` surrounded by double quotes.\n */\ngoog.string.quote = function(s) {\n s = String(s);\n var sb = ['\"'];\n for (var i = 0; i < s.length; i++) {\n var ch = s.charAt(i);\n var cc = ch.charCodeAt(0);\n sb[i + 1] = goog.string.specialEscapeChars_[ch] ||\n ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));\n }\n sb.push('\"');\n return sb.join('');\n};\n\n\n/**\n * Takes a string and returns the escaped string for that input string.\n * @param {string} str The string to escape.\n * @return {string} An escaped string representing `str`.\n */\ngoog.string.escapeString = function(str) {\n var sb = [];\n for (var i = 0; i < str.length; i++) {\n sb[i] = goog.string.escapeChar(str.charAt(i));\n }\n return sb.join('');\n};\n\n\n/**\n * Takes a character and returns the escaped string for that character. For\n * example escapeChar(String.fromCharCode(15)) -> \"\\\\x0E\".\n * @param {string} c The character to escape.\n * @return {string} An escaped string representing `c`.\n */\ngoog.string.escapeChar = function(c) {\n if (c in goog.string.jsEscapeCache_) {\n return goog.string.jsEscapeCache_[c];\n }\n\n if (c in goog.string.specialEscapeChars_) {\n return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];\n }\n\n var rv = c;\n var cc = c.charCodeAt(0);\n if (cc > 31 && cc < 127) {\n rv = c;\n } else {\n // tab is 9 but handled above\n if (cc < 256) {\n rv = '\\\\x';\n if (cc < 16 || cc > 256) {\n rv += '0';\n }\n } else {\n rv = '\\\\u';\n if (cc < 4096) { // \\u1000\n rv += '0';\n }\n }\n rv += cc.toString(16).toUpperCase();\n }\n\n return goog.string.jsEscapeCache_[c] = rv;\n};\n\n\n/**\n * Determines whether a string contains a substring.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n */\ngoog.string.contains = goog.string.internal.contains;\n\n\n/**\n * Determines whether a string contains a substring, ignoring case.\n * @param {string} str The string to search.\n * @param {string} subString The substring to search for.\n * @return {boolean} Whether `str` contains `subString`.\n */\ngoog.string.caseInsensitiveContains =\n goog.string.internal.caseInsensitiveContains;\n\n\n/**\n * Returns the non-overlapping occurrences of ss in s.\n * If either s or ss evalutes to false, then returns zero.\n * @param {string} s The string to look in.\n * @param {string} ss The string to look for.\n * @return {number} Number of occurrences of ss in s.\n */\ngoog.string.countOf = function(s, ss) {\n return s && ss ? s.split(ss).length - 1 : 0;\n};\n\n\n/**\n * Removes a substring of a specified length at a specific\n * index in a string.\n * @param {string} s The base string from which to remove.\n * @param {number} index The index at which to remove the substring.\n * @param {number} stringLength The length of the substring to remove.\n * @return {string} A copy of `s` with the substring removed or the full\n * string if nothing is removed or the input is invalid.\n */\ngoog.string.removeAt = function(s, index, stringLength) {\n var resultStr = s;\n // If the index is greater or equal to 0 then remove substring\n if (index >= 0 && index < s.length && stringLength > 0) {\n resultStr = s.substr(0, index) +\n s.substr(index + stringLength, s.length - index - stringLength);\n }\n return resultStr;\n};\n\n\n/**\n * Removes the first occurrence of a substring from a string.\n * @param {string} str The base string from which to remove.\n * @param {string} substr The string to remove.\n * @return {string} A copy of `str` with `substr` removed or the\n * full string if nothing is removed.\n */\ngoog.string.remove = function(str, substr) {\n return str.replace(substr, '');\n};\n\n\n/**\n * Removes all occurrences of a substring from a string.\n * @param {string} s The base string from which to remove.\n * @param {string} ss The string to remove.\n * @return {string} A copy of `s` with `ss` removed or the full\n * string if nothing is removed.\n */\ngoog.string.removeAll = function(s, ss) {\n var re = new RegExp(goog.string.regExpEscape(ss), 'g');\n return s.replace(re, '');\n};\n\n\n/**\n * Replaces all occurrences of a substring of a string with a new substring.\n * @param {string} s The base string from which to remove.\n * @param {string} ss The string to replace.\n * @param {string} replacement The replacement string.\n * @return {string} A copy of `s` with `ss` replaced by\n * `replacement` or the original string if nothing is replaced.\n */\ngoog.string.replaceAll = function(s, ss, replacement) {\n var re = new RegExp(goog.string.regExpEscape(ss), 'g');\n return s.replace(re, replacement.replace(/\\$/g, '$$$$'));\n};\n\n\n/**\n * Escapes characters in the string that are not safe to use in a RegExp.\n * @param {*} s The string to escape. If not a string, it will be casted\n * to one.\n * @return {string} A RegExp safe, escaped copy of `s`.\n */\ngoog.string.regExpEscape = function(s) {\n return String(s)\n .replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g, '\\\\$1')\n .replace(/\\x08/g, '\\\\x08');\n};\n\n\n/**\n * Repeats a string n times.\n * @param {string} string The string to repeat.\n * @param {number} length The number of times to repeat.\n * @return {string} A string containing `length` repetitions of\n * `string`.\n */\ngoog.string.repeat = (String.prototype.repeat) ? function(string, length) {\n // The native method is over 100 times faster than the alternative.\n return string.repeat(length);\n} : function(string, length) {\n return new Array(length + 1).join(string);\n};\n\n\n/**\n * Pads number to given length and optionally rounds it to a given precision.\n * For example:\n * <pre>padNumber(1.25, 2, 3) -> '01.250'\n * padNumber(1.25, 2) -> '01.25'\n * padNumber(1.25, 2, 1) -> '01.3'\n * padNumber(1.25, 0) -> '1.25'</pre>\n *\n * @param {number} num The number to pad.\n * @param {number} length The desired length.\n * @param {number=} opt_precision The desired precision.\n * @return {string} `num` as a string with the given options.\n */\ngoog.string.padNumber = function(num, length, opt_precision) {\n var s =\n (opt_precision !== undefined) ? num.toFixed(opt_precision) : String(num);\n var index = s.indexOf('.');\n if (index == -1) {\n index = s.length;\n }\n return goog.string.repeat('0', Math.max(0, length - index)) + s;\n};\n\n\n/**\n * Returns a string representation of the given object, with\n * null and undefined being returned as the empty string.\n *\n * @param {*} obj The object to convert.\n * @return {string} A string representation of the `obj`.\n */\ngoog.string.makeSafe = function(obj) {\n return obj == null ? '' : String(obj);\n};\n\n\n/**\n * Concatenates string expressions. This is useful\n * since some browsers are very inefficient when it comes to using plus to\n * concat strings. Be careful when using null and undefined here since\n * these will not be included in the result. If you need to represent these\n * be sure to cast the argument to a String first.\n * For example:\n * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'\n * buildString(null, undefined) -> ''\n * </pre>\n * @param {...*} var_args A list of strings to concatenate. If not a string,\n * it will be casted to one.\n * @return {string} The concatenation of `var_args`.\n */\ngoog.string.buildString = function(var_args) {\n return Array.prototype.join.call(arguments, '');\n};\n\n\n/**\n * Returns a string with at least 64-bits of randomness.\n *\n * Doesn't trust JavaScript's random function entirely. Uses a combination of\n * random and current timestamp, and then encodes the string in base-36 to\n * make it shorter.\n *\n * @return {string} A random string, e.g. sn1s7vb4gcic.\n */\ngoog.string.getRandomString = function() {\n var x = 2147483648;\n return Math.floor(Math.random() * x).toString(36) +\n Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);\n};\n\n\n/**\n * Compares two version numbers.\n *\n * @param {string|number} version1 Version of first item.\n * @param {string|number} version2 Version of second item.\n *\n * @return {number} 1 if `version1` is higher.\n * 0 if arguments are equal.\n * -1 if `version2` is higher.\n */\ngoog.string.compareVersions = goog.string.internal.compareVersions;\n\n\n/**\n * String hash function similar to java.lang.String.hashCode().\n * The hash code for a string is computed as\n * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],\n * where s[i] is the ith character of the string and n is the length of\n * the string. We mod the result to make it between 0 (inclusive) and 2^32\n * (exclusive).\n * @param {string} str A string.\n * @return {number} Hash value for `str`, between 0 (inclusive) and 2^32\n * (exclusive). The empty string returns 0.\n */\ngoog.string.hashCode = function(str) {\n var result = 0;\n for (var i = 0; i < str.length; ++i) {\n // Normalize to 4 byte range, 0 ... 2^32.\n result = (31 * result + str.charCodeAt(i)) >>> 0;\n }\n return result;\n};\n\n\n/**\n * The most recent unique ID. |0 is equivalent to Math.floor in this case.\n * @type {number}\n * @private\n */\ngoog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;\n\n\n/**\n * Generates and returns a string which is unique in the current document.\n * This is useful, for example, to create unique IDs for DOM elements.\n * @return {string} A unique id.\n */\ngoog.string.createUniqueString = function() {\n return 'goog_' + goog.string.uniqueStringCounter_++;\n};\n\n\n/**\n * Converts the supplied string to a number, which may be Infinity or NaN.\n * This function strips whitespace: (toNumber(' 123') === 123)\n * This function accepts scientific notation: (toNumber('1e1') === 10)\n *\n * This is better than JavaScript's built-in conversions because, sadly:\n * (Number(' ') === 0) and (parseFloat('123a') === 123)\n *\n * @param {string} str The string to convert.\n * @return {number} The number the supplied string represents, or NaN.\n */\ngoog.string.toNumber = function(str) {\n var num = Number(str);\n if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {\n return NaN;\n }\n return num;\n};\n\n\n/**\n * Returns whether the given string is lower camel case (e.g. \"isFooBar\").\n *\n * Note that this assumes the string is entirely letters.\n * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms\n *\n * @param {string} str String to test.\n * @return {boolean} Whether the string is lower camel case.\n */\ngoog.string.isLowerCamelCase = function(str) {\n return /^[a-z]+([A-Z][a-z]*)*$/.test(str);\n};\n\n\n/**\n * Returns whether the given string is upper camel case (e.g. \"FooBarBaz\").\n *\n * Note that this assumes the string is entirely letters.\n * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms\n *\n * @param {string} str String to test.\n * @return {boolean} Whether the string is upper camel case.\n */\ngoog.string.isUpperCamelCase = function(str) {\n return /^([A-Z][a-z]*)+$/.test(str);\n};\n\n\n/**\n * Converts a string from selector-case to camelCase (e.g. from\n * \"multi-part-string\" to \"multiPartString\"), useful for converting\n * CSS selectors and HTML dataset keys to their equivalent JS properties.\n * @param {string} str The string in selector-case form.\n * @return {string} The string in camelCase form.\n */\ngoog.string.toCamelCase = function(str) {\n return String(str).replace(/\\-([a-z])/g, function(all, match) {\n return match.toUpperCase();\n });\n};\n\n\n/**\n * Converts a string from camelCase to selector-case (e.g. from\n * \"multiPartString\" to \"multi-part-string\"), useful for converting JS\n * style and dataset properties to equivalent CSS selectors and HTML keys.\n * @param {string} str The string in camelCase form.\n * @return {string} The string in selector-case form.\n */\ngoog.string.toSelectorCase = function(str) {\n return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();\n};\n\n\n/**\n * Converts a string into TitleCase. First character of the string is always\n * capitalized in addition to the first letter of every subsequent word.\n * Words are delimited by one or more whitespaces by default. Custom delimiters\n * can optionally be specified to replace the default, which doesn't preserve\n * whitespace delimiters and instead must be explicitly included if needed.\n *\n * Default delimiter => \" \":\n * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'\n * goog.string.toTitleCase('one two three') => 'One Two Three'\n * goog.string.toTitleCase(' one two ') => ' One Two '\n * goog.string.toTitleCase('one_two_three') => 'One_two_three'\n * goog.string.toTitleCase('one-two-three') => 'One-two-three'\n *\n * Custom delimiter => \"_-.\":\n * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'\n * goog.string.toTitleCase('one two three', '_-.') => 'One two three'\n * goog.string.toTitleCase(' one two ', '_-.') => ' one two '\n * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'\n * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'\n * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'\n * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'\n * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'\n *\n * @param {string} str String value in camelCase form.\n * @param {string=} opt_delimiters Custom delimiter character set used to\n * distinguish words in the string value. Each character represents a\n * single delimiter. When provided, default whitespace delimiter is\n * overridden and must be explicitly included if needed.\n * @return {string} String value in TitleCase form.\n */\ngoog.string.toTitleCase = function(str, opt_delimiters) {\n var delimiters = (typeof opt_delimiters === 'string') ?\n goog.string.regExpEscape(opt_delimiters) :\n '\\\\s';\n\n // For IE8, we need to prevent using an empty character set. Otherwise,\n // incorrect matching will occur.\n delimiters = delimiters ? '|[' + delimiters + ']+' : '';\n\n var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');\n return str.replace(regexp, function(all, p1, p2) {\n return p1 + p2.toUpperCase();\n });\n};\n\n\n/**\n * Capitalizes a string, i.e. converts the first letter to uppercase\n * and all other letters to lowercase, e.g.:\n *\n * goog.string.capitalize('one') => 'One'\n * goog.string.capitalize('ONE') => 'One'\n * goog.string.capitalize('one two') => 'One two'\n *\n * Note that this function does not trim initial whitespace.\n *\n * @param {string} str String value to capitalize.\n * @return {string} String value with first letter in uppercase.\n */\ngoog.string.capitalize = function(str) {\n return String(str.charAt(0)).toUpperCase() +\n String(str.substr(1)).toLowerCase();\n};\n\n\n/**\n * Parse a string in decimal or hexidecimal ('0xFFFF') form.\n *\n * To parse a particular radix, please use parseInt(string, radix) directly. See\n * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt\n *\n * This is a wrapper for the built-in parseInt function that will only parse\n * numbers as base 10 or base 16. Some JS implementations assume strings\n * starting with \"0\" are intended to be octal. ES3 allowed but discouraged\n * this behavior. ES5 forbids it. This function emulates the ES5 behavior.\n *\n * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj\n *\n * @param {string|number|null|undefined} value The value to be parsed.\n * @return {number} The number, parsed. If the string failed to parse, this\n * will be NaN.\n */\ngoog.string.parseInt = function(value) {\n // Force finite numbers to strings.\n if (isFinite(value)) {\n value = String(value);\n }\n\n if (typeof value === 'string') {\n // If the string starts with '0x' or '-0x', parse as hex.\n return /^\\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10);\n }\n\n return NaN;\n};\n\n\n/**\n * Splits a string on a separator a limited number of times.\n *\n * This implementation is more similar to Python or Java, where the limit\n * parameter specifies the maximum number of splits rather than truncating\n * the number of results.\n *\n * See http://docs.python.org/2/library/stdtypes.html#str.split\n * See JavaDoc: http://goo.gl/F2AsY\n * See Mozilla reference: http://goo.gl/dZdZs\n *\n * @param {string} str String to split.\n * @param {string} separator The separator.\n * @param {number} limit The limit to the number of splits. The resulting array\n * will have a maximum length of limit+1. Negative numbers are the same\n * as zero.\n * @return {!Array<string>} The string, split.\n */\ngoog.string.splitLimit = function(str, separator, limit) {\n var parts = str.split(separator);\n var returnVal = [];\n\n // Only continue doing this while we haven't hit the limit and we have\n // parts left.\n while (limit > 0 && parts.length) {\n returnVal.push(parts.shift());\n limit--;\n }\n\n // If there are remaining parts, append them to the end.\n if (parts.length) {\n returnVal.push(parts.join(separator));\n }\n\n return returnVal;\n};\n\n\n/**\n * Finds the characters to the right of the last instance of any separator\n *\n * This function is similar to goog.string.path.baseName, except it can take a\n * list of characters to split the string on. It will return the rightmost\n * grouping of characters to the right of any separator as a left-to-right\n * oriented string.\n *\n * @see goog.string.path.baseName\n * @param {string} str The string\n * @param {string|!Array<string>} separators A list of separator characters\n * @return {string} The last part of the string with respect to the separators\n */\ngoog.string.lastComponent = function(str, separators) {\n if (!separators) {\n return str;\n } else if (typeof separators == 'string') {\n separators = [separators];\n }\n\n var lastSeparatorIndex = -1;\n for (var i = 0; i < separators.length; i++) {\n if (separators[i] == '') {\n continue;\n }\n var currentSeparatorIndex = str.lastIndexOf(separators[i]);\n if (currentSeparatorIndex > lastSeparatorIndex) {\n lastSeparatorIndex = currentSeparatorIndex;\n }\n }\n if (lastSeparatorIndex == -1) {\n return str;\n }\n return str.slice(lastSeparatorIndex + 1);\n};\n\n\n/**\n * Computes the Levenshtein edit distance between two strings.\n * @param {string} a\n * @param {string} b\n * @return {number} The edit distance between the two strings.\n */\ngoog.string.editDistance = function(a, b) {\n var v0 = [];\n var v1 = [];\n\n if (a == b) {\n return 0;\n }\n\n if (!a.length || !b.length) {\n return Math.max(a.length, b.length);\n }\n\n for (var i = 0; i < b.length + 1; i++) {\n v0[i] = i;\n }\n\n for (var i = 0; i < a.length; i++) {\n v1[0] = i + 1;\n\n for (var j = 0; j < b.length; j++) {\n var cost = Number(a[i] != b[j]);\n // Cost for the substring is the minimum of adding one character, removing\n // one character, or a swap.\n v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);\n }\n\n for (var j = 0; j < v0.length; j++) {\n v0[j] = v1[j];\n }\n }\n\n return v1[b.length];\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Wrapper class for handling XmlHttpRequests.\n *\n * One off requests can be sent through goog.net.XhrIo.send() or an\n * instance can be created to send multiple requests. Each request uses its\n * own XmlHttpRequest object and handles clearing of the event callback to\n * ensure no leaks.\n *\n * XhrIo is event based, it dispatches events on success, failure, finishing,\n * ready-state change, or progress (download and upload).\n *\n * The ready-state or timeout event fires first, followed by\n * a generic completed event. Then the abort, error, or success event\n * is fired as appropriate. Progress events are fired as they are\n * received. Lastly, the ready event will fire to indicate that the\n * object may be used to make another request.\n *\n * The error event may also be called before completed and\n * ready-state-change if the XmlHttpRequest.open() or .send() methods throw.\n *\n * This class does not support multiple requests, queuing, or prioritization.\n *\n * When progress events are supported by the browser, and progress is\n * enabled via .setProgressEventsEnabled(true), the\n * goog.net.EventType.PROGRESS event will be the re-dispatched browser\n * progress event. Additionally, a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event\n * will be fired for download and upload progress respectively.\n */\n\n\ngoog.provide('goog.net.XhrIo');\ngoog.provide('goog.net.XhrIo.ResponseType');\n\ngoog.require('goog.Timer');\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.debug.entryPointRegistry');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.json.hybrid');\ngoog.require('goog.log');\ngoog.require('goog.net.ErrorCode');\ngoog.require('goog.net.EventType');\ngoog.require('goog.net.HttpStatus');\ngoog.require('goog.net.XmlHttp');\ngoog.require('goog.object');\ngoog.require('goog.string');\ngoog.require('goog.structs');\ngoog.require('goog.structs.Map');\ngoog.require('goog.uri.utils');\ngoog.require('goog.userAgent');\ngoog.requireType('goog.Uri');\n\ngoog.scope(function() {\n\n/**\n * Basic class for handling XMLHttpRequests.\n * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when\n * creating XMLHttpRequest objects.\n * @constructor\n * @extends {goog.events.EventTarget}\n */\ngoog.net.XhrIo = function(opt_xmlHttpFactory) {\n XhrIo.base(this, 'constructor');\n\n /**\n * Map of default headers to add to every request, use:\n * XhrIo.headers.set(name, value)\n * @type {!goog.structs.Map}\n */\n this.headers = new goog.structs.Map();\n\n /**\n * Optional XmlHttpFactory\n * @private {goog.net.XmlHttpFactory}\n */\n this.xmlHttpFactory_ = opt_xmlHttpFactory || null;\n\n /**\n * Whether XMLHttpRequest is active. A request is active from the time send()\n * is called until onReadyStateChange() is complete, or error() or abort()\n * is called.\n * @private {boolean}\n */\n this.active_ = false;\n\n /**\n * The XMLHttpRequest object that is being used for the transfer.\n * @private {?goog.net.XhrLike.OrNative}\n */\n this.xhr_ = null;\n\n /**\n * The options to use with the current XMLHttpRequest object.\n * @private {?Object}\n */\n this.xhrOptions_ = null;\n\n /**\n * Last URL that was requested.\n * @private {string|goog.Uri}\n */\n this.lastUri_ = '';\n\n /**\n * Method for the last request.\n * @private {string}\n */\n this.lastMethod_ = '';\n\n /**\n * Last error code.\n * @private {!goog.net.ErrorCode}\n */\n this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;\n\n /**\n * Last error message.\n * @private {Error|string}\n */\n this.lastError_ = '';\n\n /**\n * Used to ensure that we don't dispatch an multiple ERROR events. This can\n * happen in IE when it does a synchronous load and one error is handled in\n * the ready statte change and one is handled due to send() throwing an\n * exception.\n * @private {boolean}\n */\n this.errorDispatched_ = false;\n\n /**\n * Used to make sure we don't fire the complete event from inside a send call.\n * @private {boolean}\n */\n this.inSend_ = false;\n\n /**\n * Used in determining if a call to {@link #onReadyStateChange_} is from\n * within a call to this.xhr_.open.\n * @private {boolean}\n */\n this.inOpen_ = false;\n\n /**\n * Used in determining if a call to {@link #onReadyStateChange_} is from\n * within a call to this.xhr_.abort.\n * @private {boolean}\n */\n this.inAbort_ = false;\n\n /**\n * Number of milliseconds after which an incomplete request will be aborted\n * and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout\n * is set.\n * @private {number}\n */\n this.timeoutInterval_ = 0;\n\n /**\n * Timer to track request timeout.\n * @private {?number}\n */\n this.timeoutId_ = null;\n\n /**\n * The requested type for the response. The empty string means use the default\n * XHR behavior.\n * @private {goog.net.XhrIo.ResponseType}\n */\n this.responseType_ = ResponseType.DEFAULT;\n\n /**\n * Whether a \"credentialed\" request is to be sent (one that is aware of\n * cookies and authentication). This is applicable only for cross-domain\n * requests and more recent browsers that support this part of the HTTP Access\n * Control standard.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute\n *\n * @private {boolean}\n */\n this.withCredentials_ = false;\n\n /**\n * Whether progress events are enabled for this request. This is\n * disabled by default because setting a progress event handler\n * causes pre-flight OPTIONS requests to be sent for CORS requests,\n * even in cases where a pre-flight request would not otherwise be\n * sent.\n *\n * @see http://xhr.spec.whatwg.org/#security-considerations\n *\n * Note that this can cause problems for Firefox 22 and below, as an\n * older \"LSProgressEvent\" will be dispatched by the browser. That\n * progress event is no longer supported, and can lead to failures,\n * including throwing exceptions.\n *\n * @see http://bugzilla.mozilla.org/show_bug.cgi?id=845631\n * @see b/23469793\n *\n * @private {boolean}\n */\n this.progressEventsEnabled_ = false;\n\n /**\n * True if we can use XMLHttpRequest's timeout directly.\n * @private {boolean}\n */\n this.useXhr2Timeout_ = false;\n};\ngoog.inherits(goog.net.XhrIo, goog.events.EventTarget);\n\nvar XhrIo = goog.net.XhrIo;\n\n/**\n * Response types that may be requested for XMLHttpRequests.\n * @enum {string}\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetype-attribute\n */\ngoog.net.XhrIo.ResponseType = {\n DEFAULT: '',\n TEXT: 'text',\n DOCUMENT: 'document',\n // Not supported as of Chrome 10.0.612.1 dev\n BLOB: 'blob',\n ARRAY_BUFFER: 'arraybuffer',\n};\n\nvar ResponseType = goog.net.XhrIo.ResponseType;\n\n\n/**\n * A reference to the XhrIo logger\n * @private {?goog.log.Logger}\n * @const\n */\ngoog.net.XhrIo.prototype.logger_ = goog.log.getLogger('goog.net.XhrIo');\n\n\n/**\n * The Content-Type HTTP header name\n * @type {string}\n */\ngoog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type';\n\n\n/**\n * The Content-Transfer-Encoding HTTP header name\n * @type {string}\n */\ngoog.net.XhrIo.CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding';\n\n\n/**\n * The pattern matching the 'http' and 'https' URI schemes\n * @type {!RegExp}\n */\ngoog.net.XhrIo.HTTP_SCHEME_PATTERN = /^https?$/i;\n\n\n/**\n * The methods that typically come along with form data. We set different\n * headers depending on whether the HTTP action is one of these.\n * @type {!Array<string>}\n */\ngoog.net.XhrIo.METHODS_WITH_FORM_DATA = ['POST', 'PUT'];\n\n\n/**\n * The Content-Type HTTP header value for a url-encoded form\n * @type {string}\n */\ngoog.net.XhrIo.FORM_CONTENT_TYPE =\n 'application/x-www-form-urlencoded;charset=utf-8';\n\n\n/**\n * The XMLHttpRequest Level two timeout delay ms property name.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n *\n * @private {string}\n * @const\n */\ngoog.net.XhrIo.XHR2_TIMEOUT_ = 'timeout';\n\n\n/**\n * The XMLHttpRequest Level two ontimeout handler property name.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n *\n * @private {string}\n * @const\n */\ngoog.net.XhrIo.XHR2_ON_TIMEOUT_ = 'ontimeout';\n\n\n/**\n * All non-disposed instances of goog.net.XhrIo created\n * by {@link goog.net.XhrIo.send} are in this Array.\n * @see goog.net.XhrIo.cleanup\n * @private {!Array<!goog.net.XhrIo>}\n */\ngoog.net.XhrIo.sendInstances_ = [];\n\n\n/**\n * Static send that creates a short lived instance of XhrIo to send the\n * request.\n * @see goog.net.XhrIo.cleanup\n * @param {string|goog.Uri} url Uri to make request to.\n * @param {?function(this:goog.net.XhrIo, ?)=} opt_callback Callback function\n * for when request is complete.\n * @param {string=} opt_method Send method, default: GET.\n * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}\n * opt_content Body data.\n * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the\n * request.\n * @param {number=} opt_timeoutInterval Number of milliseconds after which an\n * incomplete request will be aborted; 0 means no timeout is set.\n * @param {boolean=} opt_withCredentials Whether to send credentials with the\n * request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.\n * @return {!goog.net.XhrIo} The sent XhrIo.\n */\ngoog.net.XhrIo.send = function(\n url, opt_callback, opt_method, opt_content, opt_headers,\n opt_timeoutInterval, opt_withCredentials) {\n var x = new goog.net.XhrIo();\n goog.net.XhrIo.sendInstances_.push(x);\n if (opt_callback) {\n x.listen(goog.net.EventType.COMPLETE, opt_callback);\n }\n x.listenOnce(goog.net.EventType.READY, x.cleanupSend_);\n if (opt_timeoutInterval) {\n x.setTimeoutInterval(opt_timeoutInterval);\n }\n if (opt_withCredentials) {\n x.setWithCredentials(opt_withCredentials);\n }\n x.send(url, opt_method, opt_content, opt_headers);\n return x;\n};\n\n\n/**\n * Disposes all non-disposed instances of goog.net.XhrIo created by\n * {@link goog.net.XhrIo.send}.\n * {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance\n * it creates when the request completes or fails. However, if\n * the request never completes, then the goog.net.XhrIo is not disposed.\n * This can occur if the window is unloaded before the request completes.\n * We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo\n * it creates and make the client of {@link goog.net.XhrIo.send} be\n * responsible for disposing it in this case. However, this makes things\n * significantly more complicated for the client, and the whole point\n * of {@link goog.net.XhrIo.send} is that it's simple and easy to use.\n * Clients of {@link goog.net.XhrIo.send} should call\n * {@link goog.net.XhrIo.cleanup} when doing final\n * cleanup on window unload.\n */\ngoog.net.XhrIo.cleanup = function() {\n var instances = goog.net.XhrIo.sendInstances_;\n while (instances.length) {\n instances.pop().dispose();\n }\n};\n\n\n/**\n * Installs exception protection for all entry point introduced by\n * goog.net.XhrIo instances which are not protected by\n * {@link goog.debug.ErrorHandler#protectWindowSetTimeout},\n * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or\n * {@link goog.events.protectBrowserEventEntryPoint}.\n *\n * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to\n * protect the entry point(s).\n */\ngoog.net.XhrIo.protectEntryPoints = function(errorHandler) {\n goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =\n errorHandler.protectEntryPoint(\n goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);\n};\n\n\n/**\n * Disposes of the specified goog.net.XhrIo created by\n * {@link goog.net.XhrIo.send} and removes it from\n * {@link goog.net.XhrIo.pendingStaticSendInstances_}.\n * @private\n */\ngoog.net.XhrIo.prototype.cleanupSend_ = function() {\n this.dispose();\n goog.array.remove(goog.net.XhrIo.sendInstances_, this);\n};\n\n\n/**\n * Returns the number of milliseconds after which an incomplete request will be\n * aborted, or 0 if no timeout is set.\n * @return {number} Timeout interval in milliseconds.\n */\ngoog.net.XhrIo.prototype.getTimeoutInterval = function() {\n return this.timeoutInterval_;\n};\n\n\n/**\n * Sets the number of milliseconds after which an incomplete request will be\n * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no\n * timeout is set.\n * @param {number} ms Timeout interval in milliseconds; 0 means none.\n */\ngoog.net.XhrIo.prototype.setTimeoutInterval = function(ms) {\n this.timeoutInterval_ = Math.max(0, ms);\n};\n\n\n/**\n * Sets the desired type for the response. At time of writing, this is only\n * supported in very recent versions of WebKit (10.0.612.1 dev and later).\n *\n * If this is used, the response may only be accessed via {@link #getResponse}.\n *\n * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.\n */\ngoog.net.XhrIo.prototype.setResponseType = function(type) {\n this.responseType_ = type;\n};\n\n\n/**\n * Gets the desired type for the response.\n * @return {goog.net.XhrIo.ResponseType} The desired type for the response.\n */\ngoog.net.XhrIo.prototype.getResponseType = function() {\n return this.responseType_;\n};\n\n\n/**\n * Sets whether a \"credentialed\" request that is aware of cookie and\n * authentication information should be made. This option is only supported by\n * browsers that support HTTP Access Control. As of this writing, this option\n * is not supported in IE.\n *\n * @param {boolean} withCredentials Whether this should be a \"credentialed\"\n * request.\n */\ngoog.net.XhrIo.prototype.setWithCredentials = function(withCredentials) {\n this.withCredentials_ = withCredentials;\n};\n\n\n/**\n * Gets whether a \"credentialed\" request is to be sent.\n * @return {boolean} The desired type for the response.\n */\ngoog.net.XhrIo.prototype.getWithCredentials = function() {\n return this.withCredentials_;\n};\n\n\n/**\n * Sets whether progress events are enabled for this request. Note\n * that progress events require pre-flight OPTIONS request handling\n * for CORS requests, and may cause trouble with older browsers. See\n * progressEventsEnabled_ for details.\n * @param {boolean} enabled Whether progress events should be enabled.\n */\ngoog.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) {\n this.progressEventsEnabled_ = enabled;\n};\n\n\n/**\n * Gets whether progress events are enabled.\n * @return {boolean} Whether progress events are enabled for this request.\n */\ngoog.net.XhrIo.prototype.getProgressEventsEnabled = function() {\n return this.progressEventsEnabled_;\n};\n\n\n/**\n * Instance send that actually uses XMLHttpRequest to make a server call.\n * @param {string|goog.Uri} url Uri to make request to.\n * @param {string=} opt_method Send method, default: GET.\n * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}\n * opt_content Body data.\n * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the\n * request.\n * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different\n * types of parameters for opt_headers.\n */\ngoog.net.XhrIo.prototype.send = function(\n url, opt_method, opt_content, opt_headers) {\n if (this.xhr_) {\n throw new Error(\n '[goog.net.XhrIo] Object is active with another request=' +\n this.lastUri_ + '; newUri=' + url);\n }\n\n var method = opt_method ? opt_method.toUpperCase() : 'GET';\n\n this.lastUri_ = url;\n this.lastError_ = '';\n this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;\n this.lastMethod_ = method;\n this.errorDispatched_ = false;\n this.active_ = true;\n\n // Use the factory to create the XHR object and options\n this.xhr_ = this.createXhr();\n this.xhrOptions_ = this.xmlHttpFactory_ ? this.xmlHttpFactory_.getOptions() :\n goog.net.XmlHttp.getOptions();\n\n // Set up the onreadystatechange callback\n this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this);\n\n // Set up upload/download progress events, if progress events are supported.\n if (this.getProgressEventsEnabled() && 'onprogress' in this.xhr_) {\n this.xhr_.onprogress =\n goog.bind(function(e) { this.onProgressHandler_(e, true); }, this);\n if (this.xhr_.upload) {\n this.xhr_.upload.onprogress = goog.bind(this.onProgressHandler_, this);\n }\n }\n\n /**\n * Try to open the XMLHttpRequest (always async), if an error occurs here it\n * is generally permission denied\n */\n try {\n goog.log.fine(this.logger_, this.formatMsg_('Opening Xhr'));\n this.inOpen_ = true;\n this.xhr_.open(method, String(url), true); // Always async!\n this.inOpen_ = false;\n } catch (err) {\n goog.log.fine(\n this.logger_, this.formatMsg_('Error opening Xhr: ' + err.message));\n this.error_(goog.net.ErrorCode.EXCEPTION, err);\n return;\n }\n\n // We can't use null since this won't allow requests with form data to have a\n // content length specified which will cause some proxies to return a 411\n // error.\n var content = opt_content || '';\n\n var headers = this.headers.clone();\n\n // Add headers specific to this request\n if (opt_headers) {\n goog.structs.forEach(\n opt_headers, function(value, key) { headers.set(key, value); });\n }\n\n // Find whether a content type header is set, ignoring case.\n // HTTP header names are case-insensitive. See:\n // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2\n var contentTypeKey =\n goog.array.find(headers.getKeys(), goog.net.XhrIo.isContentTypeHeader_);\n\n var contentIsFormData =\n (goog.global['FormData'] && (content instanceof goog.global['FormData']));\n if (goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA, method) &&\n !contentTypeKey && !contentIsFormData) {\n // For requests typically with form data, default to the url-encoded form\n // content type unless this is a FormData request. For FormData,\n // the browser will automatically add a multipart/form-data content type\n // with an appropriate multipart boundary.\n headers.set(\n goog.net.XhrIo.CONTENT_TYPE_HEADER, goog.net.XhrIo.FORM_CONTENT_TYPE);\n }\n\n // Add the headers to the Xhr object\n headers.forEach(function(value, key) {\n this.xhr_.setRequestHeader(key, value);\n }, this);\n\n if (this.responseType_) {\n this.xhr_.responseType = this.responseType_;\n }\n // Set xhr_.withCredentials only when the value is different, or else in\n // synchronous XMLHtppRequest.open Firefox will throw an exception.\n // https://bugzilla.mozilla.org/show_bug.cgi?id=736340\n if ('withCredentials' in this.xhr_ &&\n this.xhr_.withCredentials !== this.withCredentials_) {\n this.xhr_.withCredentials = this.withCredentials_;\n }\n\n /**\n * Try to send the request, or other wise report an error (404 not found).\n */\n try {\n this.cleanUpTimeoutTimer_(); // Paranoid, should never be running.\n if (this.timeoutInterval_ > 0) {\n this.useXhr2Timeout_ = goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_);\n goog.log.fine(\n this.logger_, this.formatMsg_(\n 'Will abort after ' + this.timeoutInterval_ +\n 'ms if incomplete, xhr2 ' + this.useXhr2Timeout_));\n if (this.useXhr2Timeout_) {\n this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_] = this.timeoutInterval_;\n this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] =\n goog.bind(this.timeout_, this);\n } else {\n this.timeoutId_ =\n goog.Timer.callOnce(this.timeout_, this.timeoutInterval_, this);\n }\n }\n goog.log.fine(this.logger_, this.formatMsg_('Sending request'));\n this.inSend_ = true;\n this.xhr_.send(content);\n this.inSend_ = false;\n\n } catch (err) {\n goog.log.fine(this.logger_, this.formatMsg_('Send error: ' + err.message));\n this.error_(goog.net.ErrorCode.EXCEPTION, err);\n }\n};\n\n\n/**\n * Determines if the argument is an XMLHttpRequest that supports the level 2\n * timeout value and event.\n *\n * Currently, FF 21.0 OS X has the fields but won't actually call the timeout\n * handler. Perhaps the confusion in the bug referenced below hasn't\n * entirely been resolved.\n *\n * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute\n * @see https://bugzilla.mozilla.org/show_bug.cgi?id=525816\n *\n * @param {!goog.net.XhrLike.OrNative} xhr The request.\n * @return {boolean} True if the request supports level 2 timeout.\n * @private\n */\ngoog.net.XhrIo.shouldUseXhr2Timeout_ = function(xhr) {\n return goog.userAgent.IE && goog.userAgent.isVersionOrHigher(9) &&\n typeof xhr[goog.net.XhrIo.XHR2_TIMEOUT_] === 'number' &&\n xhr[goog.net.XhrIo.XHR2_ON_TIMEOUT_] !== undefined;\n};\n\n\n/**\n * @param {string} header An HTTP header key.\n * @return {boolean} Whether the key is a content type header (ignoring\n * case.\n * @private\n */\ngoog.net.XhrIo.isContentTypeHeader_ = function(header) {\n return goog.string.caseInsensitiveEquals(\n goog.net.XhrIo.CONTENT_TYPE_HEADER, header);\n};\n\n\n/**\n * Creates a new XHR object.\n * @return {!goog.net.XhrLike.OrNative} The newly created XHR object.\n * @protected\n */\ngoog.net.XhrIo.prototype.createXhr = function() {\n return this.xmlHttpFactory_ ? this.xmlHttpFactory_.createInstance() :\n goog.net.XmlHttp();\n};\n\n\n/**\n * The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_}\n * milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts\n * the request.\n * @private\n */\ngoog.net.XhrIo.prototype.timeout_ = function() {\n if (typeof goog == 'undefined') {\n // If goog is undefined then the callback has occurred as the application\n // is unloading and will error. Thus we let it silently fail.\n } else if (this.xhr_) {\n this.lastError_ =\n 'Timed out after ' + this.timeoutInterval_ + 'ms, aborting';\n this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;\n goog.log.fine(this.logger_, this.formatMsg_(this.lastError_));\n this.dispatchEvent(goog.net.EventType.TIMEOUT);\n this.abort(goog.net.ErrorCode.TIMEOUT);\n }\n};\n\n\n/**\n * Something errorred, so inactivate, fire error callback and clean up\n * @param {goog.net.ErrorCode} errorCode The error code.\n * @param {Error} err The error object.\n * @private\n */\ngoog.net.XhrIo.prototype.error_ = function(errorCode, err) {\n this.active_ = false;\n if (this.xhr_) {\n this.inAbort_ = true;\n this.xhr_.abort(); // Ensures XHR isn't hung (FF)\n this.inAbort_ = false;\n }\n this.lastError_ = err;\n this.lastErrorCode_ = errorCode;\n this.dispatchErrors_();\n this.cleanUpXhr_();\n};\n\n\n/**\n * Dispatches COMPLETE and ERROR in case of an error. This ensures that we do\n * not dispatch multiple error events.\n * @private\n */\ngoog.net.XhrIo.prototype.dispatchErrors_ = function() {\n if (!this.errorDispatched_) {\n this.errorDispatched_ = true;\n this.dispatchEvent(goog.net.EventType.COMPLETE);\n this.dispatchEvent(goog.net.EventType.ERROR);\n }\n};\n\n\n/**\n * Abort the current XMLHttpRequest\n * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -\n * defaults to ABORT.\n */\ngoog.net.XhrIo.prototype.abort = function(opt_failureCode) {\n if (this.xhr_ && this.active_) {\n goog.log.fine(this.logger_, this.formatMsg_('Aborting'));\n this.active_ = false;\n this.inAbort_ = true;\n this.xhr_.abort();\n this.inAbort_ = false;\n this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;\n this.dispatchEvent(goog.net.EventType.COMPLETE);\n this.dispatchEvent(goog.net.EventType.ABORT);\n this.cleanUpXhr_();\n }\n};\n\n\n/**\n * Nullifies all callbacks to reduce risks of leaks.\n * @override\n * @protected\n */\ngoog.net.XhrIo.prototype.disposeInternal = function() {\n if (this.xhr_) {\n // We explicitly do not call xhr_.abort() unless active_ is still true.\n // This is to avoid unnecessarily aborting a successful request when\n // dispose() is called in a callback triggered by a complete response, but\n // in which browser cleanup has not yet finished.\n // (See http://b/issue?id=1684217.)\n if (this.active_) {\n this.active_ = false;\n this.inAbort_ = true;\n this.xhr_.abort();\n this.inAbort_ = false;\n }\n this.cleanUpXhr_(true);\n }\n\n XhrIo.base(this, 'disposeInternal');\n};\n\n\n/**\n * Internal handler for the XHR object's readystatechange event. This method\n * checks the status and the readystate and fires the correct callbacks.\n * If the request has ended, the handlers are cleaned up and the XHR object is\n * nullified.\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChange_ = function() {\n if (this.isDisposed()) {\n // This method is the target of an untracked goog.Timer.callOnce().\n return;\n }\n if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) {\n // Were not being called from within a call to this.xhr_.send\n // this.xhr_.abort, or this.xhr_.open, so this is an entry point\n this.onReadyStateChangeEntryPoint_();\n } else {\n this.onReadyStateChangeHelper_();\n }\n};\n\n\n/**\n * Used to protect the onreadystatechange handler entry point. Necessary\n * as {#onReadyStateChange_} maybe called from within send or abort, this\n * method is only called when {#onReadyStateChange_} is called as an\n * entry point.\n * {@see #protectEntryPoints}\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {\n this.onReadyStateChangeHelper_();\n};\n\n\n/**\n * Helper for {@link #onReadyStateChange_}. This is used so that\n * entry point calls to {@link #onReadyStateChange_} can be routed through\n * {@link #onReadyStateChangeEntryPoint_}.\n * @private\n */\ngoog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() {\n if (!this.active_) {\n // can get called inside abort call\n return;\n }\n\n if (typeof goog == 'undefined') {\n // NOTE(user): If goog is undefined then the callback has occurred as the\n // application is unloading and will error. Thus we let it silently fail.\n\n } else if (\n this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] &&\n this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE &&\n this.getStatus() == 2) {\n // NOTE(user): In IE if send() errors on a *local* request the readystate\n // is still changed to COMPLETE. We need to ignore it and allow the\n // try/catch around send() to pick up the error.\n goog.log.fine(\n this.logger_,\n this.formatMsg_('Local request error detected and ignored'));\n\n } else {\n // In IE when the response has been cached we sometimes get the callback\n // from inside the send call and this usually breaks code that assumes that\n // XhrIo is asynchronous. If that is the case we delay the callback\n // using a timer.\n if (this.inSend_ &&\n this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) {\n goog.Timer.callOnce(this.onReadyStateChange_, 0, this);\n return;\n }\n\n this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);\n\n // readyState indicates the transfer has finished\n if (this.isComplete()) {\n goog.log.fine(this.logger_, this.formatMsg_('Request complete'));\n\n this.active_ = false;\n\n try {\n // Call the specific callbacks for success or failure. Only call the\n // success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED)\n if (this.isSuccess()) {\n this.dispatchEvent(goog.net.EventType.COMPLETE);\n this.dispatchEvent(goog.net.EventType.SUCCESS);\n } else {\n this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;\n this.lastError_ =\n this.getStatusText() + ' [' + this.getStatus() + ']';\n this.dispatchErrors_();\n }\n } finally {\n this.cleanUpXhr_();\n }\n }\n }\n};\n\n\n/**\n * Internal handler for the XHR object's onprogress event. Fires both a generic\n * PROGRESS event and either a DOWNLOAD_PROGRESS or UPLOAD_PROGRESS event to\n * allow specific binding for each XHR progress event.\n * @param {!ProgressEvent} e XHR progress event.\n * @param {boolean=} opt_isDownload Whether the current progress event is from a\n * download. Used to determine whether DOWNLOAD_PROGRESS or UPLOAD_PROGRESS\n * event should be dispatched.\n * @private\n */\ngoog.net.XhrIo.prototype.onProgressHandler_ = function(e, opt_isDownload) {\n goog.asserts.assert(\n e.type === goog.net.EventType.PROGRESS,\n 'goog.net.EventType.PROGRESS is of the same type as raw XHR progress.');\n this.dispatchEvent(\n goog.net.XhrIo.buildProgressEvent_(e, goog.net.EventType.PROGRESS));\n this.dispatchEvent(\n goog.net.XhrIo.buildProgressEvent_(\n e, opt_isDownload ? goog.net.EventType.DOWNLOAD_PROGRESS :\n goog.net.EventType.UPLOAD_PROGRESS));\n};\n\n\n/**\n * Creates a representation of the native ProgressEvent. IE doesn't support\n * constructing ProgressEvent via \"new\", and the alternatives (e.g.,\n * ProgressEvent.initProgressEvent) are non-standard or deprecated.\n * @param {!ProgressEvent} e XHR progress event.\n * @param {!goog.net.EventType} eventType The type of the event.\n * @return {!ProgressEvent} The progress event.\n * @private\n */\ngoog.net.XhrIo.buildProgressEvent_ = function(e, eventType) {\n return /** @type {!ProgressEvent} */ ({\n type: eventType,\n lengthComputable: e.lengthComputable,\n loaded: e.loaded,\n total: e.total,\n });\n};\n\n\n/**\n * Remove the listener to protect against leaks, and nullify the XMLHttpRequest\n * object.\n * @param {boolean=} opt_fromDispose If this is from the dispose (don't want to\n * fire any events).\n * @private\n */\ngoog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) {\n if (this.xhr_) {\n // Cancel any pending timeout event handler.\n this.cleanUpTimeoutTimer_();\n\n // Save reference so we can mark it as closed after the READY event. The\n // READY event may trigger another request, thus we must nullify this.xhr_\n var xhr = this.xhr_;\n var clearedOnReadyStateChange =\n this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ?\n goog.nullFunction :\n null;\n this.xhr_ = null;\n this.xhrOptions_ = null;\n\n if (!opt_fromDispose) {\n this.dispatchEvent(goog.net.EventType.READY);\n }\n\n try {\n // NOTE(user): Not nullifying in FireFox can still leak if the callbacks\n // are defined in the same scope as the instance of XhrIo. But, IE doesn't\n // allow you to set the onreadystatechange to NULL so nullFunction is\n // used.\n xhr.onreadystatechange = clearedOnReadyStateChange;\n } catch (e) {\n // This seems to occur with a Gears HTTP request. Delayed the setting of\n // this onreadystatechange until after READY is sent out and catching the\n // error to see if we can track down the problem.\n goog.log.error(\n this.logger_,\n 'Problem encountered resetting onreadystatechange: ' + e.message);\n }\n }\n};\n\n\n/**\n * Make sure the timeout timer isn't running.\n * @private\n */\ngoog.net.XhrIo.prototype.cleanUpTimeoutTimer_ = function() {\n if (this.xhr_ && this.useXhr2Timeout_) {\n this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] = null;\n }\n if (this.timeoutId_) {\n goog.Timer.clear(this.timeoutId_);\n this.timeoutId_ = null;\n }\n};\n\n\n/**\n * @return {boolean} Whether there is an active request.\n */\ngoog.net.XhrIo.prototype.isActive = function() {\n return !!this.xhr_;\n};\n\n\n/**\n * @return {boolean} Whether the request has completed.\n */\ngoog.net.XhrIo.prototype.isComplete = function() {\n return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE;\n};\n\n\n/**\n * @return {boolean} Whether the request completed with a success.\n */\ngoog.net.XhrIo.prototype.isSuccess = function() {\n var status = this.getStatus();\n // A zero status code is considered successful for local files.\n return goog.net.HttpStatus.isSuccess(status) ||\n status === 0 && !this.isLastUriEffectiveSchemeHttp_();\n};\n\n\n/**\n * @return {boolean} whether the effective scheme of the last URI that was\n * fetched was 'http' or 'https'.\n * @private\n */\ngoog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {\n var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));\n return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(scheme);\n};\n\n\n/**\n * Get the readystate from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*.\n */\ngoog.net.XhrIo.prototype.getReadyState = function() {\n return this.xhr_ ?\n /** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) :\n goog.net.XmlHttp.ReadyState\n .UNINITIALIZED;\n};\n\n\n/**\n * Get the status from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {number} Http status.\n */\ngoog.net.XhrIo.prototype.getStatus = function() {\n /**\n * IE doesn't like you checking status until the readystate is greater than 2\n * (i.e. it is receiving or complete). The try/catch is used for when the\n * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.\n */\n try {\n return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?\n this.xhr_.status :\n -1;\n } catch (e) {\n return -1;\n }\n};\n\n\n/**\n * Get the status text from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @return {string} Status text.\n */\ngoog.net.XhrIo.prototype.getStatusText = function() {\n /**\n * IE doesn't like you checking status until the readystate is greater than 2\n * (i.e. it is receiving or complete). The try/catch is used for when the\n * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.\n */\n try {\n return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?\n this.xhr_.statusText :\n '';\n } catch (e) {\n goog.log.fine(this.logger_, 'Can not get status: ' + e.message);\n return '';\n }\n};\n\n\n/**\n * Get the last Uri that was requested\n * @return {string} Last Uri.\n */\ngoog.net.XhrIo.prototype.getLastUri = function() {\n return String(this.lastUri_);\n};\n\n\n/**\n * Get the response text from the Xhr object\n * Will only return correct result when called from the context of a callback.\n * @return {string} Result from the server, or '' if no result available.\n */\ngoog.net.XhrIo.prototype.getResponseText = function() {\n try {\n return this.xhr_ ? this.xhr_.responseText : '';\n } catch (e) {\n // http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute\n // states that responseText should return '' (and responseXML null)\n // when the state is not LOADING or DONE. Instead, IE can\n // throw unexpected exceptions, for example when a request is aborted\n // or no data is available yet.\n goog.log.fine(this.logger_, 'Can not get responseText: ' + e.message);\n return '';\n }\n};\n\n\n/**\n * Get the response body from the Xhr object. This property is only available\n * in IE since version 7 according to MSDN:\n * http://msdn.microsoft.com/en-us/library/ie/ms534368(v=vs.85).aspx\n * Will only return correct result when called from the context of a callback.\n *\n * One option is to construct a VBArray from the returned object and convert\n * it to a JavaScript array using the toArray method:\n * `(new window['VBArray'](xhrIo.getResponseBody())).toArray()`\n * This will result in an array of numbers in the range of [0..255]\n *\n * Another option is to use the VBScript CStr method to convert it into a\n * string as outlined in http://stackoverflow.com/questions/1919972\n *\n * @return {Object} Binary result from the server or null if not available.\n */\ngoog.net.XhrIo.prototype.getResponseBody = function() {\n try {\n if (this.xhr_ && 'responseBody' in this.xhr_) {\n return this.xhr_['responseBody'];\n }\n } catch (e) {\n // IE can throw unexpected exceptions, for example when a request is aborted\n // or no data is yet available.\n goog.log.fine(this.logger_, 'Can not get responseBody: ' + e.message);\n }\n return null;\n};\n\n\n/**\n * Get the response XML from the Xhr object\n * Will only return correct result when called from the context of a callback.\n * @return {Document} The DOM Document representing the XML file, or null\n * if no result available.\n */\ngoog.net.XhrIo.prototype.getResponseXml = function() {\n try {\n return this.xhr_ ? this.xhr_.responseXML : null;\n } catch (e) {\n goog.log.fine(this.logger_, 'Can not get responseXML: ' + e.message);\n return null;\n }\n};\n\n\n/**\n * Get the response and evaluates it as JSON from the Xhr object\n * Will only return correct result when called from the context of a callback\n * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for\n * stripping of the response before parsing. This needs to be set only if\n * your backend server prepends the same prefix string to the JSON response.\n * @throws Error if the response text is invalid JSON.\n * @return {Object|undefined} JavaScript object.\n */\ngoog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {\n if (!this.xhr_) {\n return undefined;\n }\n\n var responseText = this.xhr_.responseText;\n if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {\n responseText = responseText.substring(opt_xssiPrefix.length);\n }\n\n return goog.json.hybrid.parse(responseText);\n};\n\n\n/**\n * Get the response as the type specificed by {@link #setResponseType}. At time\n * of writing, this is only directly supported in very recent versions of WebKit\n * (10.0.612.1 dev and later). If the field is not supported directly, we will\n * try to emulate it.\n *\n * Emulating the response means following the rules laid out at\n * http://www.w3.org/TR/XMLHttpRequest/#the-response-attribute\n *\n * On browsers with no support for this (Chrome < 10, Firefox < 4, etc), only\n * response types of DEFAULT or TEXT may be used, and the response returned will\n * be the text response.\n *\n * On browsers with Mozilla's draft support for array buffers (Firefox 4, 5),\n * only response types of DEFAULT, TEXT, and ARRAY_BUFFER may be used, and the\n * response returned will be either the text response or the Mozilla\n * implementation of the array buffer response.\n *\n * On browsers will full support, any valid response type supported by the\n * browser may be used, and the response provided by the browser will be\n * returned.\n *\n * @return {*} The response.\n */\ngoog.net.XhrIo.prototype.getResponse = function() {\n try {\n if (!this.xhr_) {\n return null;\n }\n if ('response' in this.xhr_) {\n return this.xhr_.response;\n }\n switch (this.responseType_) {\n case ResponseType.DEFAULT:\n case ResponseType.TEXT:\n return this.xhr_.responseText;\n // DOCUMENT and BLOB don't need to be handled here because they are\n // introduced in the same spec that adds the .response field, and would\n // have been caught above.\n // ARRAY_BUFFER needs an implementation for Firefox 4, where it was\n // implemented using a draft spec rather than the final spec.\n case ResponseType.ARRAY_BUFFER:\n if ('mozResponseArrayBuffer' in this.xhr_) {\n return this.xhr_.mozResponseArrayBuffer;\n }\n }\n // Fell through to a response type that is not supported on this browser.\n goog.log.error(\n this.logger_, 'Response type ' + this.responseType_ + ' is not ' +\n 'supported on this browser');\n return null;\n } catch (e) {\n goog.log.fine(this.logger_, 'Can not get response: ' + e.message);\n return null;\n }\n};\n\n\n/**\n * Get the value of the response-header with the given name from the Xhr object\n * Will only return correct result when called from the context of a callback\n * and the request has completed\n * @param {string} key The name of the response-header to retrieve.\n * @return {string|undefined} The value of the response-header named key.\n */\ngoog.net.XhrIo.prototype.getResponseHeader = function(key) {\n if (!this.xhr_ || !this.isComplete()) {\n return undefined;\n }\n\n var value = this.xhr_.getResponseHeader(key);\n return value === null ? undefined : value;\n};\n\n\n/**\n * Gets the text of all the headers in the response.\n * Will only return correct result when called from the context of a callback\n * and the request has completed.\n * @return {string} The value of the response headers or empty string.\n */\ngoog.net.XhrIo.prototype.getAllResponseHeaders = function() {\n // getAllResponseHeaders can return null if no response has been received,\n // ensure we always return an empty string.\n return this.xhr_ && this.isComplete() ?\n (this.xhr_.getAllResponseHeaders() || '') :\n '';\n};\n\n\n/**\n * Returns all response headers as a key-value map.\n * Multiple values for the same header key can be combined into one,\n * separated by a comma and a space.\n * Note that the native getResponseHeader method for retrieving a single header\n * does a case insensitive match on the header name. This method does not\n * include any case normalization logic, it will just return a key-value\n * representation of the headers.\n * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method\n * @return {!Object<string, string>} An object with the header keys as keys\n * and header values as values.\n */\ngoog.net.XhrIo.prototype.getResponseHeaders = function() {\n // TODO(user): Make this function parse headers as per the spec\n // (https://tools.ietf.org/html/rfc2616#section-4.2).\n\n var headersObject = {};\n var headersArray = this.getAllResponseHeaders().split('\\r\\n');\n for (var i = 0; i < headersArray.length; i++) {\n if (goog.string.isEmptyOrWhitespace(headersArray[i])) {\n continue;\n }\n var keyValue =\n goog.string.splitLimit(headersArray[i], ':', /* maxSplitCount= */ 1);\n var key = keyValue[0];\n var value = keyValue[1];\n\n if (typeof value !== 'string') {\n // There must be a value but it can be the empty string.\n continue;\n }\n\n // Whitespace at the start and end of the value is meaningless.\n value = value.trim();\n // The key should not contain whitespace but we currently ignore that.\n\n var values = headersObject[key] || [];\n headersObject[key] = values;\n values.push(value);\n }\n\n return goog.object.map(headersObject, function(values) {\n return values.join(', ');\n });\n};\n\n\n/**\n * Get the value of the response-header with the given name from the Xhr object.\n * As opposed to {@link #getResponseHeader}, this method does not require that\n * the request has completed.\n * @param {string} key The name of the response-header to retrieve.\n * @return {?string} The value of the response-header, or null if it is\n * unavailable.\n */\ngoog.net.XhrIo.prototype.getStreamingResponseHeader = function(key) {\n return this.xhr_ ? this.xhr_.getResponseHeader(key) : null;\n};\n\n\n/**\n * Gets the text of all the headers in the response. As opposed to\n * {@link #getAllResponseHeaders}, this method does not require that the request\n * has completed.\n * @return {string} The value of the response headers or empty string.\n */\ngoog.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() {\n return this.xhr_ ? this.xhr_.getAllResponseHeaders() : '';\n};\n\n\n/**\n * Get the last error message\n * @return {!goog.net.ErrorCode} Last error code.\n */\ngoog.net.XhrIo.prototype.getLastErrorCode = function() {\n return this.lastErrorCode_;\n};\n\n\n/**\n * Get the last error message\n * @return {string} Last error message.\n */\ngoog.net.XhrIo.prototype.getLastError = function() {\n return typeof this.lastError_ === 'string' ? this.lastError_ :\n String(this.lastError_);\n};\n\n\n/**\n * Adds the last method, status and URI to the message. This is used to add\n * this information to the logging calls.\n * @param {string} msg The message text that we want to add the extra text to.\n * @return {string} The message with the extra text appended.\n * @private\n */\ngoog.net.XhrIo.prototype.formatMsg_ = function(msg) {\n return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' +\n this.getStatus() + ']';\n};\n\n\n// Register the xhr handler as an entry point, so that\n// it can be monitored for exception handling, etc.\ngoog.debug.entryPointRegistry.register(\n /**\n * @param {function(!Function): !Function} transformer The transforming\n * function.\n */\n function(transformer) {\n goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =\n transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);\n });\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview A pool of forward channel requests to enable real-time\n * messaging from the client to server.\n *\n */\n\ngoog.module('goog.labs.net.webChannel.ForwardChannelRequestPool');\n\ngoog.module.declareLegacyNamespace();\n\nvar ChannelRequest = goog.require('goog.labs.net.webChannel.ChannelRequest');\nvar Wire = goog.require('goog.labs.net.webChannel.Wire');\nvar array = goog.require('goog.array');\nvar googString = goog.require('goog.string');\n\n\n/**\n * This class represents the state of all forward channel requests.\n *\n * @param {number=} opt_maxPoolSize The maximum pool size.\n *\n * @struct @constructor @final\n */\nvar ForwardChannelRequestPool = function(opt_maxPoolSize) {\n /**\n * The max pool size as configured.\n *\n * @private {number}\n */\n this.maxPoolSizeConfigured_ =\n opt_maxPoolSize || ForwardChannelRequestPool.MAX_POOL_SIZE_;\n\n /**\n * The current size limit of the request pool. This limit is meant to be\n * read-only after the channel is fully opened.\n *\n * If SPDY or HTTP2 is enabled, set it to the max pool size, which is also\n * configurable.\n *\n * @private {number}\n */\n this.maxSize_ = ForwardChannelRequestPool.isSpdyOrHttp2Enabled_() ?\n this.maxPoolSizeConfigured_ :\n 1;\n\n /**\n * The container for all the pending request objects.\n *\n * @private {?Set<?ChannelRequest>}\n */\n this.requestPool_ = null;\n\n if (this.maxSize_ > 1) {\n this.requestPool_ = new Set();\n }\n\n /**\n * The single request object when the pool size is limited to one.\n *\n * @private {?ChannelRequest}\n */\n this.request_ = null;\n\n /**\n * Saved pending messages when the pool is cancelled.\n *\n * @private {!Array<Wire.QueuedMap>}\n */\n this.pendingMessages_ = [];\n};\n\n\n/**\n * The default size limit of the request pool.\n *\n * @private {number}\n */\nForwardChannelRequestPool.MAX_POOL_SIZE_ = 10;\n\n\n/**\n * @return {boolean} True if SPDY or HTTP2 is enabled. Uses chrome-specific APIs\n * as a fallback and will always return false for other browsers where\n * PerformanceNavigationTiming is not available.\n * @private\n */\nForwardChannelRequestPool.isSpdyOrHttp2Enabled_ = function() {\n if (goog.global.PerformanceNavigationTiming) {\n var entrys = /** @type {!Array<!PerformanceNavigationTiming>} */ (\n goog.global.performance.getEntriesByType('navigation'));\n return entrys.length > 0 &&\n (entrys[0].nextHopProtocol == 'hq' ||\n entrys[0].nextHopProtocol == 'h2');\n }\n return !!(\n goog.global.chrome && goog.global.chrome.loadTimes &&\n goog.global.chrome.loadTimes() &&\n goog.global.chrome.loadTimes().wasFetchedViaSpdy);\n};\n\n\n/**\n * Once we know the client protocol (from the handshake), check if we need\n * enable the request pool accordingly. This is more robust than using\n * browser-internal APIs (specific to Chrome).\n *\n * @param {string} clientProtocol The client protocol\n */\nForwardChannelRequestPool.prototype.applyClientProtocol = function(\n clientProtocol) {\n if (this.requestPool_) {\n return;\n }\n\n if (googString.contains(clientProtocol, 'spdy') ||\n googString.contains(clientProtocol, 'quic') ||\n googString.contains(clientProtocol, 'h2')) {\n this.maxSize_ = this.maxPoolSizeConfigured_;\n this.requestPool_ = new Set();\n if (this.request_) {\n this.addRequest(this.request_);\n this.request_ = null;\n }\n }\n};\n\n\n/**\n * @return {boolean} True if the pool is full.\n */\nForwardChannelRequestPool.prototype.isFull = function() {\n if (this.request_) {\n return true;\n }\n\n if (this.requestPool_) {\n return this.requestPool_.size >= this.maxSize_;\n }\n\n return false;\n};\n\n\n/**\n * @return {number} The current size limit.\n */\nForwardChannelRequestPool.prototype.getMaxSize = function() {\n return this.maxSize_;\n};\n\n\n/**\n * @return {number} The number of pending requests in the pool.\n */\nForwardChannelRequestPool.prototype.getRequestCount = function() {\n if (this.request_) {\n return 1;\n }\n\n if (this.requestPool_) {\n return this.requestPool_.size;\n }\n\n return 0;\n};\n\n\n/**\n * @param {ChannelRequest} req The channel request.\n * @return {boolean} True if the request is a included inside the pool.\n */\nForwardChannelRequestPool.prototype.hasRequest = function(req) {\n if (this.request_) {\n return this.request_ == req;\n }\n\n if (this.requestPool_) {\n return this.requestPool_.has(req);\n }\n\n return false;\n};\n\n\n/**\n * Adds a new request to the pool.\n *\n * @param {!ChannelRequest} req The new channel request.\n */\nForwardChannelRequestPool.prototype.addRequest = function(req) {\n if (this.requestPool_) {\n this.requestPool_.add(req);\n } else {\n this.request_ = req;\n }\n};\n\n\n/**\n * Removes the given request from the pool.\n *\n * @param {ChannelRequest} req The channel request.\n * @return {boolean} Whether the request has been removed from the pool.\n */\nForwardChannelRequestPool.prototype.removeRequest = function(req) {\n if (this.request_ && this.request_ == req) {\n this.request_ = null;\n return true;\n }\n\n if (this.requestPool_ && this.requestPool_.has(req)) {\n this.requestPool_.delete(req);\n return true;\n }\n\n return false;\n};\n\n\n/**\n * Clears the pool and cancel all the pending requests.\n */\nForwardChannelRequestPool.prototype.cancel = function() {\n // save any pending messages\n this.pendingMessages_ = this.getPendingMessages();\n\n if (this.request_) {\n this.request_.cancel();\n this.request_ = null;\n return;\n }\n\n if (this.requestPool_ && this.requestPool_.size !== 0) {\n for (const val of this.requestPool_.values()) {\n val.cancel();\n }\n this.requestPool_.clear();\n }\n};\n\n\n/**\n * @return {boolean} Whether there are any pending requests.\n */\nForwardChannelRequestPool.prototype.hasPendingRequest = function() {\n return (this.request_ != null) ||\n (this.requestPool_ != null && this.requestPool_.size !== 0);\n};\n\n\n/**\n * @return {!Array<Wire.QueuedMap>} All the pending messages from the pool,\n * as a new array.\n */\nForwardChannelRequestPool.prototype.getPendingMessages = function() {\n if (this.request_ != null) {\n return this.pendingMessages_.concat(this.request_.getPendingMessages());\n }\n\n if (this.requestPool_ != null && this.requestPool_.size !== 0) {\n let result = this.pendingMessages_;\n for (const val of this.requestPool_.values()) {\n result = result.concat(val.getPendingMessages());\n }\n return result;\n }\n\n return array.clone(this.pendingMessages_);\n};\n\n\n/**\n * Records pending messages, e.g. when a request receives a failed response.\n *\n * @param {!Array<Wire.QueuedMap>} messages Pending messages.\n */\nForwardChannelRequestPool.prototype.addPendingMessages = function(messages) {\n this.pendingMessages_ = this.pendingMessages_.concat(messages);\n};\n\n\n/**\n * Clears any recorded pending messages.\n */\nForwardChannelRequestPool.prototype.clearPendingMessages = function() {\n this.pendingMessages_.length = 0;\n};\n\n\n/**\n * Cancels all pending requests and force the completion of channel requests.\n *\n * Need go through the standard onRequestComplete logic to expose the max-retry\n * failure in the standard way.\n *\n * @param {function(!ChannelRequest)} onComplete The completion callback.\n * @return {boolean} true if any request has been forced to complete.\n */\nForwardChannelRequestPool.prototype.forceComplete = function(onComplete) {\n if (this.request_ != null) {\n this.request_.cancel();\n onComplete(this.request_);\n return true;\n }\n\n if (this.requestPool_ && this.requestPool_.size !== 0) {\n for (const val of this.requestPool_.values()) {\n val.cancel();\n onComplete(val);\n }\n return true;\n }\n\n return false;\n};\n\nexports = ForwardChannelRequestPool;\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Generics method for collection-like classes and objects.\n *\n *\n * This file contains functions to work with collections. It supports using\n * Map, Set, Array and Object and other classes that implement collection-like\n * methods.\n * @suppress {strictMissingProperties}\n */\n\n\ngoog.provide('goog.structs');\n\ngoog.require('goog.array');\ngoog.require('goog.object');\n\n\n// We treat an object as a dictionary if it has getKeys or it is an object that\n// isn't arrayLike.\n\n\n/**\n * Returns the number of values in the collection-like object.\n * @param {Object} col The collection-like object.\n * @return {number} The number of values in the collection-like object.\n */\ngoog.structs.getCount = function(col) {\n if (col.getCount && typeof col.getCount == 'function') {\n return col.getCount();\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return col.length;\n }\n return goog.object.getCount(col);\n};\n\n\n/**\n * Returns the values of the collection-like object.\n * @param {Object} col The collection-like object.\n * @return {!Array<?>} The values in the collection-like object.\n */\ngoog.structs.getValues = function(col) {\n if (col.getValues && typeof col.getValues == 'function') {\n return col.getValues();\n }\n if (typeof col === 'string') {\n return col.split('');\n }\n if (goog.isArrayLike(col)) {\n var rv = [];\n var l = col.length;\n for (var i = 0; i < l; i++) {\n rv.push(col[i]);\n }\n return rv;\n }\n return goog.object.getValues(col);\n};\n\n\n/**\n * Returns the keys of the collection. Some collections have no notion of\n * keys/indexes and this function will return undefined in those cases.\n * @param {Object} col The collection-like object.\n * @return {!Array|undefined} The keys in the collection.\n */\ngoog.structs.getKeys = function(col) {\n if (col.getKeys && typeof col.getKeys == 'function') {\n return col.getKeys();\n }\n // if we have getValues but no getKeys we know this is a key-less collection\n if (col.getValues && typeof col.getValues == 'function') {\n return undefined;\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n var rv = [];\n var l = col.length;\n for (var i = 0; i < l; i++) {\n rv.push(i);\n }\n return rv;\n }\n\n return goog.object.getKeys(col);\n};\n\n\n/**\n * Whether the collection contains the given value. This is O(n) and uses\n * equals (==) to test the existence.\n * @param {Object} col The collection-like object.\n * @param {*} val The value to check for.\n * @return {boolean} True if the map contains the value.\n */\ngoog.structs.contains = function(col, val) {\n if (col.contains && typeof col.contains == 'function') {\n return col.contains(val);\n }\n if (col.containsValue && typeof col.containsValue == 'function') {\n return col.containsValue(val);\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.contains(/** @type {!Array<?>} */ (col), val);\n }\n return goog.object.containsValue(col, val);\n};\n\n\n/**\n * Whether the collection is empty.\n * @param {Object} col The collection-like object.\n * @return {boolean} True if empty.\n */\ngoog.structs.isEmpty = function(col) {\n if (col.isEmpty && typeof col.isEmpty == 'function') {\n return col.isEmpty();\n }\n\n // We do not use goog.string.isEmptyOrWhitespace because here we treat the\n // string as\n // collection and as such even whitespace matters\n\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.isEmpty(/** @type {!Array<?>} */ (col));\n }\n return goog.object.isEmpty(col);\n};\n\n\n/**\n * Removes all the elements from the collection.\n * @param {Object} col The collection-like object.\n */\ngoog.structs.clear = function(col) {\n // NOTE(arv): This should not contain strings because strings are immutable\n if (col.clear && typeof col.clear == 'function') {\n col.clear();\n } else if (goog.isArrayLike(col)) {\n goog.array.clear(/** @type {IArrayLike<?>} */ (col));\n } else {\n goog.object.clear(col);\n }\n};\n\n\n/**\n * Calls a function for each value in a collection. The function takes\n * three arguments; the value, the key and the collection.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):?} f The function to call for every value.\n * This function takes\n * 3 arguments (the value, the key or undefined if the collection has no\n * notion of keys, and the collection) and the return value is irrelevant.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n * within `f`.\n * @template T,S\n * @deprecated Use a more specific method, e.g. goog.array.forEach,\n * goog.object.forEach, or for-of.\n */\ngoog.structs.forEach = function(col, f, opt_obj) {\n if (col.forEach && typeof col.forEach == 'function') {\n col.forEach(f, opt_obj);\n } else if (goog.isArrayLike(col) || typeof col === 'string') {\n goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj);\n } else {\n var keys = goog.structs.getKeys(col);\n var values = goog.structs.getValues(col);\n var l = values.length;\n for (var i = 0; i < l; i++) {\n f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);\n }\n }\n};\n\n\n/**\n * Calls a function for every value in the collection. When a call returns true,\n * adds the value to a new collection (Array is returned by default).\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n * value. This function takes\n * 3 arguments (the value, the key or undefined if the collection has no\n * notion of keys, and the collection) and should return a Boolean. If the\n * return value is true the value is added to the result collection. If it\n * is false the value is not included.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n * within `f`.\n * @return {!Object|!Array<?>} A new collection where the passed values are\n * present. If col is a key-less collection an array is returned. If col\n * has keys and values a plain old JS object is returned.\n * @template T,S\n */\ngoog.structs.filter = function(col, f, opt_obj) {\n if (typeof col.filter == 'function') {\n return col.filter(f, opt_obj);\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj);\n }\n\n var rv;\n var keys = goog.structs.getKeys(col);\n var values = goog.structs.getValues(col);\n var l = values.length;\n if (keys) {\n rv = {};\n for (var i = 0; i < l; i++) {\n if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {\n rv[keys[i]] = values[i];\n }\n }\n } else {\n // We should not use goog.array.filter here since we want to make sure that\n // the index is undefined as well as make sure that col is passed to the\n // function.\n rv = [];\n for (var i = 0; i < l; i++) {\n if (f.call(opt_obj, values[i], undefined, col)) {\n rv.push(values[i]);\n }\n }\n }\n return rv;\n};\n\n\n/**\n * Calls a function for every value in the collection and adds the result into a\n * new collection (defaults to creating a new Array).\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):V} f The function to call for every value.\n * This function takes 3 arguments (the value, the key or undefined if the\n * collection has no notion of keys, and the collection) and should return\n * something. The result will be used as the value in the new collection.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n * within `f`.\n * @return {!Object<V>|!Array<V>} A new collection with the new values. If\n * col is a key-less collection an array is returned. If col has keys and\n * values a plain old JS object is returned.\n * @template T,S,V\n */\ngoog.structs.map = function(col, f, opt_obj) {\n if (typeof col.map == 'function') {\n return col.map(f, opt_obj);\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj);\n }\n\n var rv;\n var keys = goog.structs.getKeys(col);\n var values = goog.structs.getValues(col);\n var l = values.length;\n if (keys) {\n rv = {};\n for (var i = 0; i < l; i++) {\n rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);\n }\n } else {\n // We should not use goog.array.map here since we want to make sure that\n // the index is undefined as well as make sure that col is passed to the\n // function.\n rv = [];\n for (var i = 0; i < l; i++) {\n rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);\n }\n }\n return rv;\n};\n\n\n/**\n * Calls f for each value in a collection. If any call returns true this returns\n * true (without checking the rest). If all returns false this returns false.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n * value. This function takes 3 arguments (the value, the key or undefined\n * if the collection has no notion of keys, and the collection) and should\n * return a boolean.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n * within `f`.\n * @return {boolean} True if any value passes the test.\n * @template T,S\n */\ngoog.structs.some = function(col, f, opt_obj) {\n if (typeof col.some == 'function') {\n return col.some(f, opt_obj);\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj);\n }\n var keys = goog.structs.getKeys(col);\n var values = goog.structs.getValues(col);\n var l = values.length;\n for (var i = 0; i < l; i++) {\n if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {\n return true;\n }\n }\n return false;\n};\n\n\n/**\n * Calls f for each value in a collection. If all calls return true this return\n * true this returns true. If any returns false this returns false at this point\n * and does not continue to check the remaining values.\n *\n * @param {S} col The collection-like object.\n * @param {function(this:T,?,?,S):boolean} f The function to call for every\n * value. This function takes 3 arguments (the value, the key or\n * undefined if the collection has no notion of keys, and the collection)\n * and should return a boolean.\n * @param {T=} opt_obj The object to be used as the value of 'this'\n * within `f`.\n * @return {boolean} True if all key-value pairs pass the test.\n * @template T,S\n */\ngoog.structs.every = function(col, f, opt_obj) {\n if (typeof col.every == 'function') {\n return col.every(f, opt_obj);\n }\n if (goog.isArrayLike(col) || typeof col === 'string') {\n return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj);\n }\n var keys = goog.structs.getKeys(col);\n var values = goog.structs.getValues(col);\n var l = values.length;\n for (var i = 0; i < l; i++) {\n if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {\n return false;\n }\n }\n return true;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Datastructure: Hash Map.\n *\n *\n * This file contains an implementation of a Map structure. It implements a lot\n * of the methods used in goog.structs so those functions work on hashes. This\n * is best suited for complex key types. For simple keys such as numbers and\n * strings consider using the lighter-weight utilities in goog.object.\n */\n\n\ngoog.provide('goog.structs.Map');\n\ngoog.require('goog.iter.Iterator');\ngoog.require('goog.iter.StopIteration');\n\n\n\n/**\n * Class for Hash Map datastructure.\n * @param {*=} opt_map Map or Object to initialize the map with.\n * @param {...*} var_args If 2 or more arguments are present then they\n * will be used as key-value pairs.\n * @constructor\n * @template K, V\n * @deprecated This type is misleading: use ES6 Map instead.\n */\ngoog.structs.Map = function(opt_map, var_args) {\n\n /**\n * Underlying JS object used to implement the map.\n * @private {!Object}\n */\n this.map_ = {};\n\n /**\n * An array of keys. This is necessary for two reasons:\n * 1. Iterating the keys using for (var key in this.map_) allocates an\n * object for every key in IE which is really bad for IE6 GC perf.\n * 2. Without a side data structure, we would need to escape all the keys\n * as that would be the only way we could tell during iteration if the\n * key was an internal key or a property of the object.\n *\n * This array can contain deleted keys so it's necessary to check the map\n * as well to see if the key is still in the map (this doesn't require a\n * memory allocation in IE).\n * @private {!Array<string>}\n */\n this.keys_ = [];\n\n /**\n * The number of key value pairs in the map.\n * @private {number}\n */\n this.count_ = 0;\n\n /**\n * Version used to detect changes while iterating.\n * @private {number}\n */\n this.version_ = 0;\n\n var argLength = arguments.length;\n\n if (argLength > 1) {\n if (argLength % 2) {\n throw new Error('Uneven number of arguments');\n }\n for (var i = 0; i < argLength; i += 2) {\n this.set(arguments[i], arguments[i + 1]);\n }\n } else if (opt_map) {\n this.addAll(/** @type {!Object} */ (opt_map));\n }\n};\n\n\n/**\n * @return {number} The number of key-value pairs in the map.\n */\ngoog.structs.Map.prototype.getCount = function() {\n return this.count_;\n};\n\n\n/**\n * Returns the values of the map.\n * @return {!Array<V>} The values in the map.\n */\ngoog.structs.Map.prototype.getValues = function() {\n this.cleanupKeysArray_();\n\n var rv = [];\n for (var i = 0; i < this.keys_.length; i++) {\n var key = this.keys_[i];\n rv.push(this.map_[key]);\n }\n return rv;\n};\n\n\n/**\n * Returns the keys of the map.\n * @return {!Array<string>} Array of string values.\n */\ngoog.structs.Map.prototype.getKeys = function() {\n this.cleanupKeysArray_();\n return /** @type {!Array<string>} */ (this.keys_.concat());\n};\n\n\n/**\n * Whether the map contains the given key.\n * @param {*} key The key to check for.\n * @return {boolean} Whether the map contains the key.\n */\ngoog.structs.Map.prototype.containsKey = function(key) {\n return goog.structs.Map.hasKey_(this.map_, key);\n};\n\n\n/**\n * Whether the map contains the given value. This is O(n).\n * @param {V} val The value to check for.\n * @return {boolean} Whether the map contains the value.\n */\ngoog.structs.Map.prototype.containsValue = function(val) {\n for (var i = 0; i < this.keys_.length; i++) {\n var key = this.keys_[i];\n if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {\n return true;\n }\n }\n return false;\n};\n\n\n/**\n * Whether this map is equal to the argument map.\n * @param {goog.structs.Map} otherMap The map against which to test equality.\n * @param {function(V, V): boolean=} opt_equalityFn Optional equality function\n * to test equality of values. If not specified, this will test whether\n * the values contained in each map are identical objects.\n * @return {boolean} Whether the maps are equal.\n */\ngoog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {\n if (this === otherMap) {\n return true;\n }\n\n if (this.count_ != otherMap.getCount()) {\n return false;\n }\n\n var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;\n\n this.cleanupKeysArray_();\n for (var key, i = 0; key = this.keys_[i]; i++) {\n if (!equalityFn(this.get(key), otherMap.get(key))) {\n return false;\n }\n }\n\n return true;\n};\n\n\n/**\n * Default equality test for values.\n * @param {*} a The first value.\n * @param {*} b The second value.\n * @return {boolean} Whether a and b reference the same object.\n */\ngoog.structs.Map.defaultEquals = function(a, b) {\n return a === b;\n};\n\n\n/**\n * @return {boolean} Whether the map is empty.\n */\ngoog.structs.Map.prototype.isEmpty = function() {\n return this.count_ == 0;\n};\n\n\n/**\n * Removes all key-value pairs from the map.\n */\ngoog.structs.Map.prototype.clear = function() {\n this.map_ = {};\n this.keys_.length = 0;\n this.count_ = 0;\n this.version_ = 0;\n};\n\n\n/**\n * Removes a key-value pair based on the key. This is O(logN) amortized due to\n * updating the keys array whenever the count becomes half the size of the keys\n * in the keys array.\n * @param {*} key The key to remove.\n * @return {boolean} Whether object was removed.\n */\ngoog.structs.Map.prototype.remove = function(key) {\n if (goog.structs.Map.hasKey_(this.map_, key)) {\n delete this.map_[key];\n this.count_--;\n this.version_++;\n\n // clean up the keys array if the threshold is hit\n if (this.keys_.length > 2 * this.count_) {\n this.cleanupKeysArray_();\n }\n\n return true;\n }\n return false;\n};\n\n\n/**\n * Cleans up the temp keys array by removing entries that are no longer in the\n * map.\n * @private\n */\ngoog.structs.Map.prototype.cleanupKeysArray_ = function() {\n if (this.count_ != this.keys_.length) {\n // First remove keys that are no longer in the map.\n var srcIndex = 0;\n var destIndex = 0;\n while (srcIndex < this.keys_.length) {\n var key = this.keys_[srcIndex];\n if (goog.structs.Map.hasKey_(this.map_, key)) {\n this.keys_[destIndex++] = key;\n }\n srcIndex++;\n }\n this.keys_.length = destIndex;\n }\n\n if (this.count_ != this.keys_.length) {\n // If the count still isn't correct, that means we have duplicates. This can\n // happen when the same key is added and removed multiple times. Now we have\n // to allocate one extra Object to remove the duplicates. This could have\n // been done in the first pass, but in the common case, we can avoid\n // allocating an extra object by only doing this when necessary.\n var seen = {};\n var srcIndex = 0;\n var destIndex = 0;\n while (srcIndex < this.keys_.length) {\n var key = this.keys_[srcIndex];\n if (!(goog.structs.Map.hasKey_(seen, key))) {\n this.keys_[destIndex++] = key;\n seen[key] = 1;\n }\n srcIndex++;\n }\n this.keys_.length = destIndex;\n }\n};\n\n\n/**\n * Returns the value for the given key. If the key is not found and the default\n * value is not given this will return `undefined`.\n * @param {*} key The key to get the value for.\n * @param {DEFAULT=} opt_val The value to return if no item is found for the\n * given key, defaults to undefined.\n * @return {V|DEFAULT} The value for the given key.\n * @template DEFAULT\n */\ngoog.structs.Map.prototype.get = function(key, opt_val) {\n if (goog.structs.Map.hasKey_(this.map_, key)) {\n return this.map_[key];\n }\n return opt_val;\n};\n\n\n/**\n * Adds a key-value pair to the map.\n * @param {*} key The key.\n * @param {V} value The value to add.\n * @return {*} Some subclasses return a value.\n */\ngoog.structs.Map.prototype.set = function(key, value) {\n if (!(goog.structs.Map.hasKey_(this.map_, key))) {\n this.count_++;\n // TODO(johnlenz): This class lies, it claims to return an array of string\n // keys, but instead returns the original object used.\n this.keys_.push(/** @type {?} */ (key));\n // Only change the version if we add a new key.\n this.version_++;\n }\n this.map_[key] = value;\n};\n\n\n/**\n * Adds multiple key-value pairs from another goog.structs.Map or Object.\n * @param {?Object} map Object containing the data to add.\n */\ngoog.structs.Map.prototype.addAll = function(map) {\n if (map instanceof goog.structs.Map) {\n var keys = map.getKeys();\n for (var i = 0; i < keys.length; i++) {\n this.set(keys[i], map.get(keys[i]));\n }\n } else {\n for (var key in map) {\n this.set(key, map[key]);\n }\n }\n};\n\n\n/**\n * Calls the given function on each entry in the map.\n * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f\n * @param {T=} opt_obj The value of \"this\" inside f.\n * @template T\n */\ngoog.structs.Map.prototype.forEach = function(f, opt_obj) {\n var keys = this.getKeys();\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var value = this.get(key);\n f.call(opt_obj, value, key, this);\n }\n};\n\n\n/**\n * Clones a map and returns a new map.\n * @return {!goog.structs.Map} A new map with the same key-value pairs.\n */\ngoog.structs.Map.prototype.clone = function() {\n return new goog.structs.Map(this);\n};\n\n\n/**\n * Returns a new map in which all the keys and values are interchanged\n * (keys become values and values become keys). If multiple keys map to the\n * same value, the chosen transposed value is implementation-dependent.\n *\n * It acts very similarly to {goog.object.transpose(Object)}.\n *\n * @return {!goog.structs.Map} The transposed map.\n */\ngoog.structs.Map.prototype.transpose = function() {\n var transposed = new goog.structs.Map();\n for (var i = 0; i < this.keys_.length; i++) {\n var key = this.keys_[i];\n var value = this.map_[key];\n transposed.set(value, key);\n }\n\n return transposed;\n};\n\n\n/**\n * @return {!Object} Object representation of the map.\n */\ngoog.structs.Map.prototype.toObject = function() {\n this.cleanupKeysArray_();\n var obj = {};\n for (var i = 0; i < this.keys_.length; i++) {\n var key = this.keys_[i];\n obj[key] = this.map_[key];\n }\n return obj;\n};\n\n\n/**\n * Returns an iterator that iterates over the keys in the map. Removal of keys\n * while iterating might have undesired side effects.\n * @return {!goog.iter.Iterator} An iterator over the keys in the map.\n */\ngoog.structs.Map.prototype.getKeyIterator = function() {\n return this.__iterator__(true);\n};\n\n\n/**\n * Returns an iterator that iterates over the values in the map. Removal of\n * keys while iterating might have undesired side effects.\n * @return {!goog.iter.Iterator} An iterator over the values in the map.\n */\ngoog.structs.Map.prototype.getValueIterator = function() {\n return this.__iterator__(false);\n};\n\n\n/**\n * Returns an iterator that iterates over the values or the keys in the map.\n * This throws an exception if the map was mutated since the iterator was\n * created.\n * @param {boolean=} opt_keys True to iterate over the keys. False to iterate\n * over the values. The default value is false.\n * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.\n */\ngoog.structs.Map.prototype.__iterator__ = function(opt_keys) {\n // Clean up keys to minimize the risk of iterating over dead keys.\n this.cleanupKeysArray_();\n\n var i = 0;\n var version = this.version_;\n var selfObj = this;\n\n var newIter = new goog.iter.Iterator;\n newIter.next = function() {\n if (version != selfObj.version_) {\n throw new Error('The map has changed since the iterator was created');\n }\n if (i >= selfObj.keys_.length) {\n throw goog.iter.StopIteration;\n }\n var key = selfObj.keys_[i++];\n return opt_keys ? key : selfObj.map_[key];\n };\n return newIter;\n};\n\n\n/**\n * Safe way to test for hasOwnProperty. It even allows testing for\n * 'hasOwnProperty'.\n * @param {!Object} obj The object to test for presence of the given key.\n * @param {*} key The key to check for.\n * @return {boolean} Whether the object has the key.\n * @private\n */\ngoog.structs.Map.hasKey_ = function(obj, key) {\n return Object.prototype.hasOwnProperty.call(obj, key);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Simple utilities for dealing with URI strings.\n *\n * This is intended to be a lightweight alternative to constructing goog.Uri\n * objects. Whereas goog.Uri adds several kilobytes to the binary regardless\n * of how much of its functionality you use, this is designed to be a set of\n * mostly-independent utilities so that the compiler includes only what is\n * necessary for the task. Estimated savings of porting is 5k pre-gzip and\n * 1.5k post-gzip. To ensure the savings remain, future developers should\n * avoid adding new functionality to existing functions, but instead create\n * new ones and factor out shared code.\n *\n * Many of these utilities have limited functionality, tailored to common\n * cases. The query parameter utilities assume that the parameter keys are\n * already encoded, since most keys are compile-time alphanumeric strings. The\n * query parameter mutation utilities also do not tolerate fragment identifiers.\n *\n * By design, these functions can be slower than goog.Uri equivalents.\n * Repeated calls to some of functions may be quadratic in behavior for IE,\n * although the effect is somewhat limited given the 2kb limit.\n *\n * One advantage of the limited functionality here is that this approach is\n * less sensitive to differences in URI encodings than goog.Uri, since these\n * functions operate on strings directly, rather than decoding them and\n * then re-encoding.\n *\n * Uses features of RFC 3986 for parsing/formatting URIs:\n * http://www.ietf.org/rfc/rfc3986.txt\n */\n\ngoog.provide('goog.uri.utils');\ngoog.provide('goog.uri.utils.ComponentIndex');\ngoog.provide('goog.uri.utils.QueryArray');\ngoog.provide('goog.uri.utils.QueryValue');\ngoog.provide('goog.uri.utils.StandardQueryParam');\n\ngoog.require('goog.array');\ngoog.require('goog.asserts');\ngoog.require('goog.string');\n\n\n/**\n * Character codes inlined to avoid object allocations due to charCode.\n * @enum {number}\n * @private\n */\ngoog.uri.utils.CharCode_ = {\n AMPERSAND: 38,\n EQUAL: 61,\n HASH: 35,\n QUESTION: 63\n};\n\n\n/**\n * Builds a URI string from already-encoded parts.\n *\n * No encoding is performed. Any component may be omitted as either null or\n * undefined.\n *\n * @param {?string=} opt_scheme The scheme such as 'http'.\n * @param {?string=} opt_userInfo The user name before the '@'.\n * @param {?string=} opt_domain The domain such as 'www.google.com', already\n * URI-encoded.\n * @param {(string|number|null)=} opt_port The port number.\n * @param {?string=} opt_path The path, already URI-encoded. If it is not\n * empty, it must begin with a slash.\n * @param {?string=} opt_queryData The URI-encoded query data.\n * @param {?string=} opt_fragment The URI-encoded fragment identifier.\n * @return {string} The fully combined URI.\n */\ngoog.uri.utils.buildFromEncodedParts = function(\n opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_queryData,\n opt_fragment) {\n var out = '';\n\n if (opt_scheme) {\n out += opt_scheme + ':';\n }\n\n if (opt_domain) {\n out += '//';\n\n if (opt_userInfo) {\n out += opt_userInfo + '@';\n }\n\n out += opt_domain;\n\n if (opt_port) {\n out += ':' + opt_port;\n }\n }\n\n if (opt_path) {\n out += opt_path;\n }\n\n if (opt_queryData) {\n out += '?' + opt_queryData;\n }\n\n if (opt_fragment) {\n out += '#' + opt_fragment;\n }\n\n return out;\n};\n\n\n/**\n * A regular expression for breaking a URI into its component parts.\n *\n * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B\n * As the \"first-match-wins\" algorithm is identical to the \"greedy\"\n * disambiguation method used by POSIX regular expressions, it is natural and\n * commonplace to use a regular expression for parsing the potential five\n * components of a URI reference.\n *\n * The following line is the regular expression for breaking-down a\n * well-formed URI reference into its components.\n *\n * <pre>\n * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?\n * 12 3 4 5 6 7 8 9\n * </pre>\n *\n * The numbers in the second line above are only to assist readability; they\n * indicate the reference points for each subexpression (i.e., each paired\n * parenthesis). We refer to the value matched for subexpression <n> as $<n>.\n * For example, matching the above expression to\n * <pre>\n * http://www.ics.uci.edu/pub/ietf/uri/#Related\n * </pre>\n * results in the following subexpression matches:\n * <pre>\n * $1 = http:\n * $2 = http\n * $3 = //www.ics.uci.edu\n * $4 = www.ics.uci.edu\n * $5 = /pub/ietf/uri/\n * $6 = <undefined>\n * $7 = <undefined>\n * $8 = #Related\n * $9 = Related\n * </pre>\n * where <undefined> indicates that the component is not present, as is the\n * case for the query component in the above example. Therefore, we can\n * determine the value of the five components as\n * <pre>\n * scheme = $2\n * authority = $4\n * path = $5\n * query = $7\n * fragment = $9\n * </pre>\n *\n * The regular expression has been modified slightly to expose the\n * userInfo, domain, and port separately from the authority.\n * The modified version yields\n * <pre>\n * $1 = http scheme\n * $2 = <undefined> userInfo -\\\n * $3 = www.ics.uci.edu domain | authority\n * $4 = <undefined> port -/\n * $5 = /pub/ietf/uri/ path\n * $6 = <undefined> query without ?\n * $7 = Related fragment without #\n * </pre>\n *\n * TODO(user): separate out the authority terminating characters once this\n * file is moved to ES6.\n * @type {!RegExp}\n * @private\n */\ngoog.uri.utils.splitRe_ = new RegExp(\n '^' + // Anchor against the entire string.\n '(?:' +\n '([^:/?#.]+)' + // scheme - ignore special characters\n // used by other URL parts such as :,\n // ?, /, #, and .\n ':)?' +\n '(?://' +\n '(?:([^\\\\\\\\/?#]*)@)?' + // userInfo\n '([^\\\\\\\\/?#]*?)' + // domain\n '(?::([0-9]+))?' + // port\n '(?=[\\\\\\\\/?#]|$)' + // authority-terminating character.\n ')?' +\n '([^?#]+)?' + // path\n '(?:\\\\?([^#]*))?' + // query\n '(?:#([\\\\s\\\\S]*))?' + // fragment. Can't use '.*' with 's' flag as Firefox\n // doesn't support the flag, and can't use an\n // \"everything set\" ([^]) as IE10 doesn't match any\n // characters with it.\n '$');\n\n\n/**\n * The index of each URI component in the return value of goog.uri.utils.split.\n * @enum {number}\n */\ngoog.uri.utils.ComponentIndex = {\n SCHEME: 1,\n USER_INFO: 2,\n DOMAIN: 3,\n PORT: 4,\n PATH: 5,\n QUERY_DATA: 6,\n FRAGMENT: 7\n};\n\n\n/**\n * Splits a URI into its component parts.\n *\n * Each component can be accessed via the component indices; for example:\n * <pre>\n * goog.uri.utils.split(someStr)[goog.uri.utils.ComponentIndex.QUERY_DATA];\n * </pre>\n *\n * @param {string} uri The URI string to examine.\n * @return {!Array<string|undefined>} Each component still URI-encoded.\n * Each component that is present will contain the encoded value, whereas\n * components that are not present will be undefined or empty, depending\n * on the browser's regular expression implementation. Never null, since\n * arbitrary strings may still look like path names.\n */\ngoog.uri.utils.split = function(uri) {\n // See @return comment -- never null.\n return /** @type {!Array<string|undefined>} */ (\n uri.match(goog.uri.utils.splitRe_));\n};\n\n\n/**\n * @param {?string} uri A possibly null string.\n * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986\n * reserved characters will not be removed.\n * @return {?string} The string URI-decoded, or null if uri is null.\n * @private\n */\ngoog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {\n if (!uri) {\n return uri;\n }\n\n return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);\n};\n\n\n/**\n * Gets a URI component by index.\n *\n * It is preferred to use the getPathEncoded() variety of functions ahead,\n * since they are more readable.\n *\n * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.\n * @param {string} uri The URI to examine.\n * @return {?string} The still-encoded component, or null if the component\n * is not present.\n * @private\n */\ngoog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {\n // Convert undefined, null, and empty string into null.\n return goog.uri.utils.split(uri)[componentIndex] || null;\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The protocol or scheme, or null if none. Does not\n * include trailing colons or slashes.\n */\ngoog.uri.utils.getScheme = function(uri) {\n return goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.SCHEME, uri);\n};\n\n\n/**\n * Gets the effective scheme for the URL. If the URL is relative then the\n * scheme is derived from the page's location.\n * @param {string} uri The URI to examine.\n * @return {string} The protocol or scheme, always lower case.\n */\ngoog.uri.utils.getEffectiveScheme = function(uri) {\n var scheme = goog.uri.utils.getScheme(uri);\n if (!scheme && goog.global.self && goog.global.self.location) {\n var protocol = goog.global.self.location.protocol;\n scheme = protocol.substr(0, protocol.length - 1);\n }\n // NOTE: When called from a web worker in Firefox 3.5, location may be null.\n // All other browsers with web workers support self.location from the worker.\n return scheme ? scheme.toLowerCase() : '';\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The user name still encoded, or null if none.\n */\ngoog.uri.utils.getUserInfoEncoded = function(uri) {\n return goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.USER_INFO, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded user info, or null if none.\n */\ngoog.uri.utils.getUserInfo = function(uri) {\n return goog.uri.utils.decodeIfPossible_(\n goog.uri.utils.getUserInfoEncoded(uri));\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The domain name still encoded, or null if none.\n */\ngoog.uri.utils.getDomainEncoded = function(uri) {\n return goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.DOMAIN, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded domain, or null if none.\n */\ngoog.uri.utils.getDomain = function(uri) {\n return goog.uri.utils.decodeIfPossible_(\n goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?number} The port number, or null if none.\n */\ngoog.uri.utils.getPort = function(uri) {\n // Coerce to a number. If the result of getComponentByIndex_ is null or\n // non-numeric, the number coersion yields NaN. This will then return\n // null for all non-numeric cases (though also zero, which isn't a relevant\n // port number).\n return Number(\n goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.PORT, uri)) ||\n null;\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The path still encoded, or null if none. Includes the\n * leading slash, if any.\n */\ngoog.uri.utils.getPathEncoded = function(uri) {\n return goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.PATH, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded path, or null if none. Includes the leading\n * slash, if any.\n */\ngoog.uri.utils.getPath = function(uri) {\n return goog.uri.utils.decodeIfPossible_(\n goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The query data still encoded, or null if none. Does not\n * include the question mark itself.\n */\ngoog.uri.utils.getQueryData = function(uri) {\n return goog.uri.utils.getComponentByIndex_(\n goog.uri.utils.ComponentIndex.QUERY_DATA, uri);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The fragment identifier, or null if none. Does not\n * include the hash mark itself.\n */\ngoog.uri.utils.getFragmentEncoded = function(uri) {\n // The hash mark may not appear in any other part of the URL.\n var hashIndex = uri.indexOf('#');\n return hashIndex < 0 ? null : uri.substr(hashIndex + 1);\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @param {?string} fragment The encoded fragment identifier, or null if none.\n * Does not include the hash mark itself.\n * @return {string} The URI with the fragment set.\n */\ngoog.uri.utils.setFragmentEncoded = function(uri, fragment) {\n return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');\n};\n\n\n/**\n * @param {string} uri The URI to examine.\n * @return {?string} The decoded fragment identifier, or null if none. Does\n * not include the hash mark.\n */\ngoog.uri.utils.getFragment = function(uri) {\n return goog.uri.utils.decodeIfPossible_(\n goog.uri.utils.getFragmentEncoded(uri));\n};\n\n\n/**\n * Extracts everything up to the port of the URI.\n * @param {string} uri The URI string.\n * @return {string} Everything up to and including the port.\n */\ngoog.uri.utils.getHost = function(uri) {\n var pieces = goog.uri.utils.split(uri);\n return goog.uri.utils.buildFromEncodedParts(\n pieces[goog.uri.utils.ComponentIndex.SCHEME],\n pieces[goog.uri.utils.ComponentIndex.USER_INFO],\n pieces[goog.uri.utils.ComponentIndex.DOMAIN],\n pieces[goog.uri.utils.ComponentIndex.PORT]);\n};\n\n\n/**\n * Returns the origin for a given URL.\n * @param {string} uri The URI string.\n * @return {string} Everything up to and including the port.\n */\ngoog.uri.utils.getOrigin = function(uri) {\n var pieces = goog.uri.utils.split(uri);\n return goog.uri.utils.buildFromEncodedParts(\n pieces[goog.uri.utils.ComponentIndex.SCHEME], null /* opt_userInfo */,\n pieces[goog.uri.utils.ComponentIndex.DOMAIN],\n pieces[goog.uri.utils.ComponentIndex.PORT]);\n};\n\n\n/**\n * Extracts the path of the URL and everything after.\n * @param {string} uri The URI string.\n * @return {string} The URI, starting at the path and including the query\n * parameters and fragment identifier.\n */\ngoog.uri.utils.getPathAndAfter = function(uri) {\n var pieces = goog.uri.utils.split(uri);\n return goog.uri.utils.buildFromEncodedParts(\n null, null, null, null, pieces[goog.uri.utils.ComponentIndex.PATH],\n pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],\n pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);\n};\n\n\n/**\n * Gets the URI with the fragment identifier removed.\n * @param {string} uri The URI to examine.\n * @return {string} Everything preceding the hash mark.\n */\ngoog.uri.utils.removeFragment = function(uri) {\n // The hash mark may not appear in any other part of the URL.\n var hashIndex = uri.indexOf('#');\n return hashIndex < 0 ? uri : uri.substr(0, hashIndex);\n};\n\n\n/**\n * Ensures that two URI's have the exact same domain, scheme, and port.\n *\n * Unlike the version in goog.Uri, this checks protocol, and therefore is\n * suitable for checking against the browser's same-origin policy.\n *\n * @param {string} uri1 The first URI.\n * @param {string} uri2 The second URI.\n * @return {boolean} Whether they have the same scheme, domain and port.\n */\ngoog.uri.utils.haveSameDomain = function(uri1, uri2) {\n var pieces1 = goog.uri.utils.split(uri1);\n var pieces2 = goog.uri.utils.split(uri2);\n return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==\n pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&\n pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==\n pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&\n pieces1[goog.uri.utils.ComponentIndex.PORT] ==\n pieces2[goog.uri.utils.ComponentIndex.PORT];\n};\n\n\n/**\n * Asserts that there are no fragment or query identifiers, only in uncompiled\n * mode.\n * @param {string} uri The URI to examine.\n * @private\n */\ngoog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {\n goog.asserts.assert(\n uri.indexOf('#') < 0 && uri.indexOf('?') < 0,\n 'goog.uri.utils: Fragment or query identifiers are not supported: [%s]',\n uri);\n};\n\n\n/**\n * Supported query parameter values by the parameter serializing utilities.\n *\n * If a value is null or undefined, the key-value pair is skipped, as an easy\n * way to omit parameters conditionally. Non-array parameters are converted\n * to a string and URI encoded. Array values are expanded into multiple\n * &key=value pairs, with each element stringized and URI-encoded.\n *\n * @typedef {*}\n */\ngoog.uri.utils.QueryValue;\n\n\n/**\n * An array representing a set of query parameters with alternating keys\n * and values.\n *\n * Keys are assumed to be URI encoded already and live at even indices. See\n * goog.uri.utils.QueryValue for details on how parameter values are encoded.\n *\n * Example:\n * <pre>\n * var data = [\n * // Simple param: ?name=BobBarker\n * 'name', 'BobBarker',\n * // Conditional param -- may be omitted entirely.\n * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,\n * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null\n * 'house', ['LosAngeles', 'NewYork', null]\n * ];\n * </pre>\n *\n * @typedef {!Array<string|goog.uri.utils.QueryValue>}\n */\ngoog.uri.utils.QueryArray;\n\n\n/**\n * Parses encoded query parameters and calls callback function for every\n * parameter found in the string.\n *\n * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an\n * empty string. Keys may be empty strings (e.g. “…&=value&…”) which also means\n * that “…&=&…” and “…&&…” will result in an empty key and value.\n *\n * @param {string} encodedQuery Encoded query string excluding question mark at\n * the beginning.\n * @param {function(string, string)} callback Function called for every\n * parameter found in query string. The first argument (name) will not be\n * urldecoded (so the function is consistent with buildQueryData), but the\n * second will. If the parameter has no value (i.e. “=” was not present)\n * the second argument (value) will be an empty string.\n */\ngoog.uri.utils.parseQueryData = function(encodedQuery, callback) {\n if (!encodedQuery) {\n return;\n }\n var pairs = encodedQuery.split('&');\n for (var i = 0; i < pairs.length; i++) {\n var indexOfEquals = pairs[i].indexOf('=');\n var name = null;\n var value = null;\n if (indexOfEquals >= 0) {\n name = pairs[i].substring(0, indexOfEquals);\n value = pairs[i].substring(indexOfEquals + 1);\n } else {\n name = pairs[i];\n }\n callback(name, value ? goog.string.urlDecode(value) : '');\n }\n};\n\n\n/**\n * Split the URI into 3 parts where the [1] is the queryData without a leading\n * '?'. For example, the URI http://foo.com/bar?a=b#abc returns\n * ['http://foo.com/bar','a=b','#abc'].\n * @param {string} uri The URI to parse.\n * @return {!Array<string>} An array representation of uri of length 3 where the\n * middle value is the queryData without a leading '?'.\n * @private\n */\ngoog.uri.utils.splitQueryData_ = function(uri) {\n // Find the query data and hash.\n var hashIndex = uri.indexOf('#');\n if (hashIndex < 0) {\n hashIndex = uri.length;\n }\n var questionIndex = uri.indexOf('?');\n var queryData;\n if (questionIndex < 0 || questionIndex > hashIndex) {\n questionIndex = hashIndex;\n queryData = '';\n } else {\n queryData = uri.substring(questionIndex + 1, hashIndex);\n }\n return [uri.substr(0, questionIndex), queryData, uri.substr(hashIndex)];\n};\n\n\n/**\n * Join an array created by splitQueryData_ back into a URI.\n * @param {!Array<string>} parts A URI in the form generated by splitQueryData_.\n * @return {string} The joined URI.\n * @private\n */\ngoog.uri.utils.joinQueryData_ = function(parts) {\n return parts[0] + (parts[1] ? '?' + parts[1] : '') + parts[2];\n};\n\n\n/**\n * @param {string} queryData\n * @param {string} newData\n * @return {string}\n * @private\n */\ngoog.uri.utils.appendQueryData_ = function(queryData, newData) {\n if (!newData) {\n return queryData;\n }\n return queryData ? queryData + '&' + newData : newData;\n};\n\n\n/**\n * @param {string} uri\n * @param {string} queryData\n * @return {string}\n * @private\n */\ngoog.uri.utils.appendQueryDataToUri_ = function(uri, queryData) {\n if (!queryData) {\n return uri;\n }\n var parts = goog.uri.utils.splitQueryData_(uri);\n parts[1] = goog.uri.utils.appendQueryData_(parts[1], queryData);\n return goog.uri.utils.joinQueryData_(parts);\n};\n\n\n/**\n * Appends key=value pairs to an array, supporting multi-valued objects.\n * @param {*} key The key prefix.\n * @param {goog.uri.utils.QueryValue} value The value to serialize.\n * @param {!Array<string>} pairs The array to which the 'key=value' strings\n * should be appended.\n * @private\n */\ngoog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {\n goog.asserts.assertString(key);\n if (Array.isArray(value)) {\n // Convince the compiler it's an array.\n goog.asserts.assertArray(value);\n for (var j = 0; j < value.length; j++) {\n // Convert to string explicitly, to short circuit the null and array\n // logic in this function -- this ensures that null and undefined get\n // written as literal 'null' and 'undefined', and arrays don't get\n // expanded out but instead encoded in the default way.\n goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);\n }\n } else if (value != null) {\n // Skip a top-level null or undefined entirely.\n pairs.push(\n key +\n // Check for empty string. Zero gets encoded into the url as literal\n // strings. For empty string, skip the equal sign, to be consistent\n // with UriBuilder.java.\n (value === '' ? '' : '=' + goog.string.urlEncode(value)));\n }\n};\n\n\n/**\n * Builds a query data string from a sequence of alternating keys and values.\n * Currently generates \"&key&\" for empty args.\n *\n * @param {!IArrayLike<string|goog.uri.utils.QueryValue>} keysAndValues\n * Alternating keys and values. See the QueryArray typedef.\n * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.\n * @return {string} The encoded query string, in the form 'a=1&b=2'.\n */\ngoog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {\n goog.asserts.assert(\n Math.max(keysAndValues.length - (opt_startIndex || 0), 0) % 2 == 0,\n 'goog.uri.utils: Key/value lists must be even in length.');\n\n var params = [];\n for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {\n var key = /** @type {string} */ (keysAndValues[i]);\n goog.uri.utils.appendKeyValuePairs_(key, keysAndValues[i + 1], params);\n }\n return params.join('&');\n};\n\n\n/**\n * Builds a query data string from a map.\n * Currently generates \"&key&\" for empty args.\n *\n * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys\n * are URI-encoded parameter keys, and the values are arbitrary types\n * or arrays. Keys with a null value are dropped.\n * @return {string} The encoded query string, in the form 'a=1&b=2'.\n */\ngoog.uri.utils.buildQueryDataFromMap = function(map) {\n var params = [];\n for (var key in map) {\n goog.uri.utils.appendKeyValuePairs_(key, map[key], params);\n }\n return params.join('&');\n};\n\n\n/**\n * Appends URI parameters to an existing URI.\n *\n * The variable arguments may contain alternating keys and values. Keys are\n * assumed to be already URI encoded. The values should not be URI-encoded,\n * and will instead be encoded by this function.\n * <pre>\n * appendParams('http://www.foo.com?existing=true',\n * 'key1', 'value1',\n * 'key2', 'value?willBeEncoded',\n * 'key3', ['valueA', 'valueB', 'valueC'],\n * 'key4', null);\n * result: 'http://www.foo.com?existing=true&' +\n * 'key1=value1&' +\n * 'key2=value%3FwillBeEncoded&' +\n * 'key3=valueA&key3=valueB&key3=valueC'\n * </pre>\n *\n * A single call to this function will not exhibit quadratic behavior in IE,\n * whereas multiple repeated calls may, although the effect is limited by\n * fact that URL's generally can't exceed 2kb.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {...(goog.uri.utils.QueryArray|goog.uri.utils.QueryValue)}\n * var_args\n * An array or argument list conforming to goog.uri.utils.QueryArray.\n * @return {string} The URI with all query parameters added.\n */\ngoog.uri.utils.appendParams = function(uri, var_args) {\n var queryData = arguments.length == 2 ?\n goog.uri.utils.buildQueryData(arguments[1], 0) :\n goog.uri.utils.buildQueryData(arguments, 1);\n return goog.uri.utils.appendQueryDataToUri_(uri, queryData);\n};\n\n\n/**\n * Appends query parameters from a map.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are\n * URI-encoded parameter keys, and the values are arbitrary types or arrays.\n * Keys with a null value are dropped.\n * @return {string} The new parameters.\n */\ngoog.uri.utils.appendParamsFromMap = function(uri, map) {\n var queryData = goog.uri.utils.buildQueryDataFromMap(map);\n return goog.uri.utils.appendQueryDataToUri_(uri, queryData);\n};\n\n\n/**\n * Appends a single URI parameter.\n *\n * Repeated calls to this can exhibit quadratic behavior in IE6 due to the\n * way string append works, though it should be limited given the 2kb limit.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {string} key The key, which must already be URI encoded.\n * @param {*=} opt_value The value, which will be stringized and encoded\n * (assumed not already to be encoded). If omitted, undefined, or null, the\n * key will be added as a valueless parameter.\n * @return {string} The URI with the query parameter added.\n */\ngoog.uri.utils.appendParam = function(uri, key, opt_value) {\n var value = (opt_value != null) ? '=' + goog.string.urlEncode(opt_value) : '';\n return goog.uri.utils.appendQueryDataToUri_(uri, key + value);\n};\n\n\n/**\n * Finds the next instance of a query parameter with the specified name.\n *\n * Does not instantiate any objects.\n *\n * @param {string} uri The URI to search. May contain a fragment identifier\n * if opt_hashIndex is specified.\n * @param {number} startIndex The index to begin searching for the key at. A\n * match may be found even if this is one character after the ampersand.\n * @param {string} keyEncoded The URI-encoded key.\n * @param {number} hashOrEndIndex Index to stop looking at. If a hash\n * mark is present, it should be its index, otherwise it should be the\n * length of the string.\n * @return {number} The position of the first character in the key's name,\n * immediately after either a question mark or a dot.\n * @private\n */\ngoog.uri.utils.findParam_ = function(\n uri, startIndex, keyEncoded, hashOrEndIndex) {\n var index = startIndex;\n var keyLength = keyEncoded.length;\n\n // Search for the key itself and post-filter for surronuding punctuation,\n // rather than expensively building a regexp.\n while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&\n index < hashOrEndIndex) {\n var precedingChar = uri.charCodeAt(index - 1);\n // Ensure that the preceding character is '&' or '?'.\n if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||\n precedingChar == goog.uri.utils.CharCode_.QUESTION) {\n // Ensure the following character is '&', '=', '#', or NaN\n // (end of string).\n var followingChar = uri.charCodeAt(index + keyLength);\n if (!followingChar || followingChar == goog.uri.utils.CharCode_.EQUAL ||\n followingChar == goog.uri.utils.CharCode_.AMPERSAND ||\n followingChar == goog.uri.utils.CharCode_.HASH) {\n return index;\n }\n }\n index += keyLength + 1;\n }\n\n return -1;\n};\n\n\n/**\n * Regular expression for finding a hash mark or end of string.\n * @type {RegExp}\n * @private\n */\ngoog.uri.utils.hashOrEndRe_ = /#|$/;\n\n\n/**\n * Determines if the URI contains a specific key.\n *\n * Performs no object instantiations.\n *\n * @param {string} uri The URI to process. May contain a fragment\n * identifier.\n * @param {string} keyEncoded The URI-encoded key. Case-sensitive.\n * @return {boolean} Whether the key is present.\n */\ngoog.uri.utils.hasParam = function(uri, keyEncoded) {\n return goog.uri.utils.findParam_(\n uri, 0, keyEncoded, uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;\n};\n\n\n/**\n * Gets the first value of a query parameter.\n * @param {string} uri The URI to process. May contain a fragment.\n * @param {string} keyEncoded The URI-encoded key. Case-sensitive.\n * @return {?string} The first value of the parameter (URI-decoded), or null\n * if the parameter is not found.\n */\ngoog.uri.utils.getParamValue = function(uri, keyEncoded) {\n var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n var foundIndex =\n goog.uri.utils.findParam_(uri, 0, keyEncoded, hashOrEndIndex);\n\n if (foundIndex < 0) {\n return null;\n } else {\n var endPosition = uri.indexOf('&', foundIndex);\n if (endPosition < 0 || endPosition > hashOrEndIndex) {\n endPosition = hashOrEndIndex;\n }\n // Progress forth to the end of the \"key=\" or \"key&\" substring.\n foundIndex += keyEncoded.length + 1;\n // Use substr, because it (unlike substring) will return empty string\n // if foundIndex > endPosition.\n return goog.string.urlDecode(\n uri.substr(foundIndex, endPosition - foundIndex));\n }\n};\n\n\n/**\n * Gets all values of a query parameter.\n * @param {string} uri The URI to process. May contain a fragment.\n * @param {string} keyEncoded The URI-encoded key. Case-sensitive.\n * @return {!Array<string>} All URI-decoded values with the given key.\n * If the key is not found, this will have length 0, but never be null.\n */\ngoog.uri.utils.getParamValues = function(uri, keyEncoded) {\n var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n var position = 0;\n var foundIndex;\n var result = [];\n\n while ((foundIndex = goog.uri.utils.findParam_(\n uri, position, keyEncoded, hashOrEndIndex)) >= 0) {\n // Find where this parameter ends, either the '&' or the end of the\n // query parameters.\n position = uri.indexOf('&', foundIndex);\n if (position < 0 || position > hashOrEndIndex) {\n position = hashOrEndIndex;\n }\n\n // Progress forth to the end of the \"key=\" or \"key&\" substring.\n foundIndex += keyEncoded.length + 1;\n // Use substr, because it (unlike substring) will return empty string\n // if foundIndex > position.\n result.push(\n goog.string.urlDecode(uri.substr(foundIndex, position - foundIndex)));\n }\n\n return result;\n};\n\n\n/**\n * Regexp to find trailing question marks and ampersands.\n * @type {RegExp}\n * @private\n */\ngoog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;\n\n\n/**\n * Removes all instances of a query parameter.\n * @param {string} uri The URI to process. Must not contain a fragment.\n * @param {string} keyEncoded The URI-encoded key.\n * @return {string} The URI with all instances of the parameter removed.\n */\ngoog.uri.utils.removeParam = function(uri, keyEncoded) {\n var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);\n var position = 0;\n var foundIndex;\n var buffer = [];\n\n // Look for a query parameter.\n while ((foundIndex = goog.uri.utils.findParam_(\n uri, position, keyEncoded, hashOrEndIndex)) >= 0) {\n // Get the portion of the query string up to, but not including, the ?\n // or & starting the parameter.\n buffer.push(uri.substring(position, foundIndex));\n // Progress to immediately after the '&'. If not found, go to the end.\n // Avoid including the hash mark.\n position = Math.min(\n (uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex, hashOrEndIndex);\n }\n\n // Append everything that is remaining.\n buffer.push(uri.substr(position));\n\n // Join the buffer, and remove trailing punctuation that remains.\n return buffer.join('').replace(\n goog.uri.utils.trailingQueryPunctuationRe_, '$1');\n};\n\n\n/**\n * Replaces all existing definitions of a parameter with a single definition.\n *\n * Repeated calls to this can exhibit quadratic behavior due to the need to\n * find existing instances and reconstruct the string, though it should be\n * limited given the 2kb limit. Consider using appendParams or setParamsFromMap\n * to update multiple parameters in bulk.\n *\n * @param {string} uri The original URI, which may already have query data.\n * @param {string} keyEncoded The key, which must already be URI encoded.\n * @param {*} value The value, which will be stringized and encoded (assumed\n * not already to be encoded).\n * @return {string} The URI with the query parameter added.\n */\ngoog.uri.utils.setParam = function(uri, keyEncoded, value) {\n return goog.uri.utils.appendParam(\n goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);\n};\n\n\n/**\n * Effeciently set or remove multiple query parameters in a URI. Order of\n * unchanged parameters will not be modified, all updated parameters will be\n * appended to the end of the query. Params with values of null or undefined are\n * removed.\n *\n * @param {string} uri The URI to process.\n * @param {!Object<string, goog.uri.utils.QueryValue>} params A list of\n * parameters to update. If null or undefined, the param will be removed.\n * @return {string} An updated URI where the query data has been updated with\n * the params.\n */\ngoog.uri.utils.setParamsFromMap = function(uri, params) {\n var parts = goog.uri.utils.splitQueryData_(uri);\n var queryData = parts[1];\n var buffer = [];\n if (queryData) {\n goog.array.forEach(queryData.split('&'), function(pair) {\n var indexOfEquals = pair.indexOf('=');\n var name = indexOfEquals >= 0 ? pair.substr(0, indexOfEquals) : pair;\n if (!params.hasOwnProperty(name)) {\n buffer.push(pair);\n }\n });\n }\n parts[1] = goog.uri.utils.appendQueryData_(\n buffer.join('&'), goog.uri.utils.buildQueryDataFromMap(params));\n return goog.uri.utils.joinQueryData_(parts);\n};\n\n\n/**\n * Generates a URI path using a given URI and a path with checks to\n * prevent consecutive \"//\". The baseUri passed in must not contain\n * query or fragment identifiers. The path to append may not contain query or\n * fragment identifiers.\n *\n * @param {string} baseUri URI to use as the base.\n * @param {string} path Path to append.\n * @return {string} Updated URI.\n */\ngoog.uri.utils.appendPath = function(baseUri, path) {\n goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);\n\n // Remove any trailing '/'\n if (goog.string.endsWith(baseUri, '/')) {\n baseUri = baseUri.substr(0, baseUri.length - 1);\n }\n // Remove any leading '/'\n if (goog.string.startsWith(path, '/')) {\n path = path.substr(1);\n }\n return goog.string.buildString(baseUri, '/', path);\n};\n\n\n/**\n * Replaces the path.\n * @param {string} uri URI to use as the base.\n * @param {string} path New path.\n * @return {string} Updated URI.\n */\ngoog.uri.utils.setPath = function(uri, path) {\n // Add any missing '/'.\n if (!goog.string.startsWith(path, '/')) {\n path = '/' + path;\n }\n var parts = goog.uri.utils.split(uri);\n return goog.uri.utils.buildFromEncodedParts(\n parts[goog.uri.utils.ComponentIndex.SCHEME],\n parts[goog.uri.utils.ComponentIndex.USER_INFO],\n parts[goog.uri.utils.ComponentIndex.DOMAIN],\n parts[goog.uri.utils.ComponentIndex.PORT], path,\n parts[goog.uri.utils.ComponentIndex.QUERY_DATA],\n parts[goog.uri.utils.ComponentIndex.FRAGMENT]);\n};\n\n\n/**\n * Standard supported query parameters.\n * @enum {string}\n */\ngoog.uri.utils.StandardQueryParam = {\n\n /** Unused parameter for unique-ifying. */\n RANDOM: 'zx'\n};\n\n\n/**\n * Sets the zx parameter of a URI to a random value.\n * @param {string} uri Any URI.\n * @return {string} That URI with the \"zx\" parameter added or replaced to\n * contain a random string.\n */\ngoog.uri.utils.makeUnique = function(uri) {\n return goog.uri.utils.setParam(\n uri, goog.uri.utils.StandardQueryParam.RANDOM,\n goog.string.getRandomString());\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Interface and shared data structures for implementing\n * different wire protocol versions.\n */\ngoog.provide('goog.labs.net.webChannel.Wire');\n\ngoog.requireType('goog.structs.Map');\n\n\n\n/**\n * The interface class.\n *\n * @interface\n */\ngoog.labs.net.webChannel.Wire = function() {};\n\n\n/**\n * The latest protocol version that this class supports. We request this version\n * from the server when opening the connection. Should match\n * LATEST_CHANNEL_VERSION on the server code.\n * @type {number}\n */\ngoog.labs.net.webChannel.Wire.LATEST_CHANNEL_VERSION = 8;\n\n\n/**\n * The JSON field key for the raw data wrapper object.\n * @type {string}\n */\ngoog.labs.net.webChannel.Wire.RAW_DATA_KEY = '__data__';\n\n\n\n/**\n * Simple container class for a (mapId, map) pair.\n * @param {number} mapId The id for this map.\n * @param {!Object|!goog.structs.Map} map The map itself.\n * @param {!Object=} opt_context The context associated with the map.\n * @constructor\n * @struct\n */\ngoog.labs.net.webChannel.Wire.QueuedMap = function(mapId, map, opt_context) {\n /**\n * The id for this map.\n * @type {number}\n */\n this.mapId = mapId;\n\n /**\n * The map itself.\n * @type {!Object|!goog.structs.Map}\n */\n this.map = map;\n\n /**\n * The context for the map.\n * @type {Object}\n */\n this.context = opt_context || null;\n};\n\n\n/**\n * @return {number|undefined} the size of the raw JSON message or\n * undefined if the message is not encoded as a raw JSON message\n */\ngoog.labs.net.webChannel.Wire.QueuedMap.prototype.getRawDataSize = function() {\n if (goog.labs.net.webChannel.Wire.RAW_DATA_KEY in this.map) {\n const data = this.map[goog.labs.net.webChannel.Wire.RAW_DATA_KEY];\n if (typeof data === 'string') {\n return data.length;\n }\n }\n\n return undefined;\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n\n/**\n * @fileoverview Defines a class for parsing JSON using the browser's built in\n * JSON library.\n */\n\ngoog.provide('goog.json.NativeJsonProcessor');\n\ngoog.require('goog.asserts');\ngoog.require('goog.json.Processor');\n\n\n\n/**\n * A class that parses and stringifies JSON using the browser's built-in JSON\n * library, if it is available.\n *\n * Note that the native JSON api has subtle differences across browsers, so\n * use this implementation with care. See json_test#assertSerialize\n * for details on the differences from goog.json.\n *\n * This implementation is signficantly faster than goog.json, at least on\n * Chrome. See json_perf.html for a perf test showing the difference.\n *\n * @param {?goog.json.Replacer=} opt_replacer An optional replacer to use during\n * serialization.\n * @param {?goog.json.Reviver=} opt_reviver An optional reviver to use during\n * parsing.\n * @constructor\n * @implements {goog.json.Processor}\n * @final\n */\ngoog.json.NativeJsonProcessor = function(opt_replacer, opt_reviver) {\n goog.asserts.assert(goog.global['JSON'] !== undefined, 'JSON not defined');\n\n /**\n * @type {goog.json.Replacer|null|undefined}\n * @private\n */\n this.replacer_ = opt_replacer;\n\n /**\n * @type {goog.json.Reviver|null|undefined}\n * @private\n */\n this.reviver_ = opt_reviver;\n};\n\n\n/** @override */\ngoog.json.NativeJsonProcessor.prototype.stringify = function(object) {\n return goog.global['JSON'].stringify(object, this.replacer_);\n};\n\n\n/** @override */\ngoog.json.NativeJsonProcessor.prototype.parse = function(s) {\n return goog.global['JSON'].parse(s, this.reviver_);\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Codec functions of the v8 wire protocol. Eventually we'd want\n * to support pluggable wire-format to improve wire efficiency and to enable\n * binary encoding. Such support will require an interface class, which\n * will be added later.\n *\n */\n\n\ngoog.provide('goog.labs.net.webChannel.WireV8');\n\ngoog.require('goog.asserts');\ngoog.require('goog.json');\ngoog.require('goog.json.NativeJsonProcessor');\ngoog.require('goog.labs.net.webChannel.Wire');\ngoog.require('goog.structs');\ngoog.requireType('goog.structs.Map');\n\n\n\n/**\n * The v8 codec class.\n *\n * @constructor\n * @struct\n */\ngoog.labs.net.webChannel.WireV8 = function() {\n /**\n * Parser for a response payload. The parser should return an array.\n * @private {!goog.string.Parser}\n */\n this.parser_ = new goog.json.NativeJsonProcessor();\n};\n\n\ngoog.scope(function() {\nvar WireV8 = goog.labs.net.webChannel.WireV8;\nvar Wire = goog.labs.net.webChannel.Wire;\n\n\n/**\n * Encodes a standalone message into the wire format.\n *\n * May throw exception if the message object contains any invalid elements.\n *\n * @param {!Object|!goog.structs.Map} message The message data.\n * V8 only support JS objects (or Map).\n * @param {!Array<string>} buffer The text buffer to write the message to.\n * @param {string=} opt_prefix The prefix for each field of the object.\n */\nWireV8.prototype.encodeMessage = function(message, buffer, opt_prefix) {\n var prefix = opt_prefix || '';\n try {\n goog.structs.forEach(message, function(value, key) {\n var encodedValue = value;\n if (goog.isObject(value)) {\n encodedValue = goog.json.serialize(value);\n } // keep the fast-path for primitive types\n buffer.push(prefix + key + '=' + encodeURIComponent(encodedValue));\n });\n } catch (ex) {\n // We send a map here because lots of the retry logic relies on map IDs,\n // so we have to send something (possibly redundant).\n buffer.push(\n prefix + 'type' +\n '=' + encodeURIComponent('_badmap'));\n throw ex;\n }\n};\n\n\n/**\n * Encodes all the buffered messages of the forward channel.\n *\n * @param {!Array<Wire.QueuedMap>} messageQueue The message data.\n * V8 only support JS objects.\n * @param {number} count The number of messages to be encoded.\n * @param {?function(!Object)} badMapHandler Callback for bad messages.\n * @return {string} the encoded messages\n */\nWireV8.prototype.encodeMessageQueue = function(\n messageQueue, count, badMapHandler) {\n var offset = -1;\n while (true) {\n var sb = ['count=' + count];\n // To save a bit of bandwidth, specify the base mapId and the rest as\n // offsets from it.\n if (offset == -1) {\n if (count > 0) {\n offset = messageQueue[0].mapId;\n sb.push('ofs=' + offset);\n } else {\n offset = 0;\n }\n } else {\n sb.push('ofs=' + offset);\n }\n var done = true;\n for (var i = 0; i < count; i++) {\n var mapId = messageQueue[i].mapId;\n var map = messageQueue[i].map;\n mapId -= offset;\n if (mapId < 0) {\n // redo the encoding in case of retry/reordering, plus extra space\n offset = Math.max(0, messageQueue[i].mapId - 100);\n done = false;\n continue;\n }\n try {\n this.encodeMessage(map, sb, 'req' + mapId + '_');\n } catch (ex) {\n if (badMapHandler) {\n badMapHandler(map);\n }\n }\n }\n if (done) {\n return sb.join('&');\n }\n }\n};\n\n\n/**\n * Decodes a standalone message received from the wire. May throw exception\n * if text is ill-formatted.\n *\n * Must be valid JSON as it is insecure to use eval() to decode JS literals;\n * and eval() is disallowed in Chrome apps too.\n *\n * Invalid JS literals include null array elements, quotas etc.\n *\n * @param {string} messageText The string content as received from the wire.\n * @return {*} The decoded message object.\n */\nWireV8.prototype.decodeMessage = function(messageText) {\n var response = this.parser_.parse(messageText);\n goog.asserts.assert(Array.isArray(response)); // throw exception\n return response;\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Utility functions for managing networking, such as\n * testing network connectivity.\n *\n */\n\n\ngoog.provide('goog.labs.net.webChannel.netUtils');\n\ngoog.require('goog.Uri');\ngoog.require('goog.labs.net.webChannel.WebChannelDebug');\n\ngoog.scope(function() {\nvar netUtils = goog.labs.net.webChannel.netUtils;\nvar WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;\n\n\n/**\n * Default timeout to allow for URI pings.\n * @type {number}\n */\nnetUtils.NETWORK_TIMEOUT = 10000;\n\n\n/**\n * Pings the network with an image URI to check if an error is a server error\n * or user's network error.\n *\n * The caller needs to add a 'rand' parameter to make sure the response is\n * not fulfilled by browser cache.\n *\n * @param {function(boolean)} callback The function to call back with results.\n * @param {goog.Uri=} opt_imageUri The URI (of an image) to use for the network\n * test.\n */\nnetUtils.testNetwork = function(callback, opt_imageUri) {\n var uri = opt_imageUri;\n if (!uri) {\n // default google.com image\n uri = new goog.Uri('//www.google.com/images/cleardot.gif');\n\n if (!(goog.global.location && goog.global.location.protocol == 'http')) {\n uri.setScheme('https'); // e.g. chrome-extension\n }\n uri.makeUnique();\n }\n\n netUtils.testLoadImage(uri.toString(), netUtils.NETWORK_TIMEOUT, callback);\n};\n\n\n/**\n * Test loading the given image, retrying if necessary.\n * @param {string} url URL to the image.\n * @param {number} timeout Milliseconds before giving up.\n * @param {function(boolean)} callback Function to call with results.\n * @param {number} retries The number of times to retry.\n * @param {!WebChannelDebug} channelDebug The debug object\n * @param {number=} opt_pauseBetweenRetriesMS Optional number of milliseconds\n * between retries - defaults to 0.\n */\nnetUtils.testLoadImageWithRetries = function(\n url, timeout, callback, retries, channelDebug, opt_pauseBetweenRetriesMS) {\n channelDebug.debug('TestLoadImageWithRetries: ' + opt_pauseBetweenRetriesMS);\n if (retries == 0) {\n // no more retries, give up\n callback(false);\n return;\n }\n\n var pauseBetweenRetries = opt_pauseBetweenRetriesMS || 0;\n retries--;\n netUtils.testLoadImage(url, timeout, function(succeeded) {\n if (succeeded) {\n callback(true);\n } else {\n // try again\n goog.global.setTimeout(function() {\n netUtils.testLoadImageWithRetries(\n url, timeout, callback, retries, channelDebug, pauseBetweenRetries);\n }, pauseBetweenRetries);\n }\n });\n};\n\n\n/**\n * Test loading the given image.\n * @param {string} url URL to the image.\n * @param {number} timeout Milliseconds before giving up.\n * @param {function(boolean)} callback Function to call with results.\n * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration\n */\nnetUtils.testLoadImage = function(url, timeout, callback) {\n var channelDebug = new WebChannelDebug();\n channelDebug.debug('TestLoadImage: loading ' + url);\n if (goog.global.Image) {\n var img = new Image();\n img.onload = goog.partial(\n netUtils.imageCallback_, channelDebug, img, 'TestLoadImage: loaded',\n true, callback);\n img.onerror = goog.partial(\n netUtils.imageCallback_, channelDebug, img, 'TestLoadImage: error',\n false, callback);\n img.onabort = goog.partial(\n netUtils.imageCallback_, channelDebug, img, 'TestLoadImage: abort',\n false, callback);\n img.ontimeout = goog.partial(\n netUtils.imageCallback_, channelDebug, img, 'TestLoadImage: timeout',\n false, callback);\n\n goog.global.setTimeout(function() {\n if (img.ontimeout) {\n img.ontimeout();\n }\n }, timeout);\n img.src = url;\n } else {\n // log ERROR_OTHER from environements where Image is not supported\n callback(false);\n }\n};\n\n\n/**\n * Wrap the image callback with debug and cleanup logic.\n * @param {!WebChannelDebug} channelDebug The WebChannelDebug object.\n * @param {!Image} img The image element.\n * @param {string} debugText The debug text.\n * @param {boolean} result The result of image loading.\n * @param {function(boolean)} callback The image callback.\n * @private\n */\nnetUtils.imageCallback_ = function(\n channelDebug, img, debugText, result, callback) {\n try {\n channelDebug.debug(debugText);\n netUtils.clearImageCallbacks_(img);\n callback(result);\n } catch (e) {\n channelDebug.dumpException(e);\n }\n};\n\n\n/**\n * Clears handlers to avoid memory leaks.\n * @param {Image} img The image to clear handlers from.\n * @private\n * @suppress {strictMissingProperties} Part of the go/strict_warnings_migration\n */\nnetUtils.clearImageCallbacks_ = function(img) {\n img.onload = null;\n img.onerror = null;\n img.onabort = null;\n img.ontimeout = null;\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n\n/**\n * @fileoverview Utility to attempt native JSON processing, falling back to\n * goog.json if not available.\n *\n * This is intended as a drop-in for current users of goog.json who want\n * to take advantage of native JSON if present.\n */\n\ngoog.provide('goog.json.hybrid');\n\ngoog.require('goog.asserts');\ngoog.require('goog.json');\n\n\n/**\n * Attempts to serialize the JSON string natively, falling back to\n * `goog.json.serialize` if unsuccessful.\n * @param {!Object} obj JavaScript object to serialize to JSON.\n * @return {string} Resulting JSON string.\n */\ngoog.json.hybrid.stringify =\n goog.json.USE_NATIVE_JSON ? goog.global['JSON']['stringify'] : function(\n obj) {\n if (goog.global.JSON) {\n try {\n return goog.global.JSON.stringify(obj);\n } catch (e) {\n // Native serialization failed. Fall through to retry with\n // goog.json.serialize.\n }\n }\n\n return goog.json.serialize(obj);\n };\n\n\n/**\n * Attempts to parse the JSON string natively, falling back to\n * the supplied `fallbackParser` if unsuccessful.\n * @param {string} jsonString JSON string to parse.\n * @param {function(string):Object} fallbackParser Fallback JSON parser used\n * if native\n * @return {?Object} Resulting JSON object.\n * @private\n */\ngoog.json.hybrid.parse_ = function(jsonString, fallbackParser) {\n if (goog.global.JSON) {\n try {\n var obj = goog.global.JSON.parse(jsonString);\n goog.asserts.assert(typeof obj == 'object');\n return /** @type {?Object} */ (obj);\n } catch (e) {\n // Native parse failed. Fall through to retry with goog.json.parse.\n }\n }\n\n return fallbackParser(jsonString);\n};\n\n\n/**\n * Attempts to parse the JSON string natively, falling back to\n * `goog.json.parse` if unsuccessful.\n * @param {string} jsonString JSON string to parse.\n * @return {?Object} Resulting JSON object.\n */\ngoog.json.hybrid.parse =\n goog.json.USE_NATIVE_JSON ? goog.global['JSON']['parse'] : function(\n jsonString) {\n return goog.json.hybrid.parse_(jsonString, goog.json.parse);\n };\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Constants for HTTP status codes.\n */\n\ngoog.provide('goog.net.HttpStatus');\n\n\n/**\n * HTTP Status Codes defined in RFC 2616, RFC 6585, RFC 4918 and RFC 7538.\n * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n * @see http://tools.ietf.org/html/rfc6585\n * @see https://tools.ietf.org/html/rfc4918\n * @see https://tools.ietf.org/html/rfc7538\n * @enum {number}\n */\ngoog.net.HttpStatus = {\n // Informational 1xx\n CONTINUE: 100,\n SWITCHING_PROTOCOLS: 101,\n\n // Successful 2xx\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NON_AUTHORITATIVE_INFORMATION: 203,\n NO_CONTENT: 204,\n RESET_CONTENT: 205,\n PARTIAL_CONTENT: 206,\n MULTI_STATUS: 207,\n\n // Redirection 3xx\n MULTIPLE_CHOICES: 300,\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n SEE_OTHER: 303,\n NOT_MODIFIED: 304,\n USE_PROXY: 305,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // Client Error 4xx\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n PROXY_AUTHENTICATION_REQUIRED: 407,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n LENGTH_REQUIRED: 411,\n PRECONDITION_FAILED: 412,\n REQUEST_ENTITY_TOO_LARGE: 413,\n REQUEST_URI_TOO_LONG: 414,\n UNSUPPORTED_MEDIA_TYPE: 415,\n REQUEST_RANGE_NOT_SATISFIABLE: 416,\n EXPECTATION_FAILED: 417,\n UNPROCESSABLE_ENTITY: 422,\n LOCKED: 423,\n FAILED_DEPENDENCY: 424,\n PRECONDITION_REQUIRED: 428,\n TOO_MANY_REQUESTS: 429,\n REQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n\n // Server Error 5xx\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n HTTP_VERSION_NOT_SUPPORTED: 505,\n INSUFFICIENT_STORAGE: 507,\n NETWORK_AUTHENTICATION_REQUIRED: 511,\n\n /*\n * IE returns this code for 204 due to its use of URLMon, which returns this\n * code for 'Operation Aborted'. The status text is 'Unknown', the response\n * headers are ''. Known to occur on IE 6 on XP through IE9 on Win7.\n */\n QUIRK_IE_NO_CONTENT: 1223,\n};\n\n\n/**\n * Returns whether the given status should be considered successful.\n *\n * Successful codes are OK (200), CREATED (201), ACCEPTED (202),\n * NO CONTENT (204), PARTIAL CONTENT (206), NOT MODIFIED (304),\n * and IE's no content code (1223).\n *\n * @param {number} status The status code to test.\n * @return {boolean} Whether the status code should be considered successful.\n */\ngoog.net.HttpStatus.isSuccess = function(status) {\n switch (status) {\n case goog.net.HttpStatus.OK:\n case goog.net.HttpStatus.CREATED:\n case goog.net.HttpStatus.ACCEPTED:\n case goog.net.HttpStatus.NO_CONTENT:\n case goog.net.HttpStatus.PARTIAL_CONTENT:\n case goog.net.HttpStatus.NOT_MODIFIED:\n case goog.net.HttpStatus.QUIRK_IE_NO_CONTENT:\n return true;\n\n default:\n return false;\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Provides CORS support for HTTP based RPC requests.\n *\n * As part of net.rpc package, CORS features provided by this class\n * depend on the server support. Please check related specs to decide how\n * to enable any of the features provided by this class.\n */\n\ngoog.module('goog.net.rpc.HttpCors');\n\nvar GoogUri = goog.require('goog.Uri');\nvar googObject = goog.require('goog.object');\nvar googString = goog.require('goog.string');\nvar googUriUtils = goog.require('goog.uri.utils');\n\n\n/**\n * The default URL parameter name to overwrite http headers with a URL param\n * to avoid CORS preflight.\n *\n * See https://github.com/whatwg/fetch/issues/210#issue-129531743 for the spec.\n *\n * @type {string}\n */\nexports.HTTP_HEADERS_PARAM_NAME = '$httpHeaders';\n\n\n/**\n * The default URL parameter name to overwrite http method with a URL param\n * to avoid CORS preflight.\n *\n * See https://github.com/whatwg/fetch/issues/210#issue-129531743 for the spec.\n *\n * @type {string}\n */\nexports.HTTP_METHOD_PARAM_NAME = '$httpMethod';\n\n\n/**\n * Generates the URL parameter value with custom headers encoded as\n * HTTP/1.1 headers block.\n *\n * @param {!Object<string, string>} headers The custom headers.\n * @return {string} The URL param to overwrite custom HTTP headers.\n */\nexports.generateHttpHeadersOverwriteParam = function(headers) {\n var result = '';\n googObject.forEach(headers, function(value, key) {\n result += key;\n result += ':';\n result += value;\n result += '\\r\\n';\n });\n return result;\n};\n\n\n/**\n * Generates the URL-encoded URL parameter value with custom headers encoded as\n * HTTP/1.1 headers block.\n *\n * @param {!Object<string, string>} headers The custom headers.\n * @return {string} The URL param to overwrite custom HTTP headers.\n */\nexports.generateEncodedHttpHeadersOverwriteParam = function(headers) {\n return googString.urlEncode(\n exports.generateHttpHeadersOverwriteParam(headers));\n};\n\n\n/**\n * Sets custom HTTP headers via an overwrite URL param.\n *\n * @param {!GoogUri|string} url The URI object or a string path.\n * @param {string} urlParam The URL param name.\n * @param {!Object<string, string>} extraHeaders The HTTP headers.\n * @return {!GoogUri|string} The URI object or a string path with headers\n * encoded as a url param.\n */\nexports.setHttpHeadersWithOverwriteParam = function(\n url, urlParam, extraHeaders) {\n if (googObject.isEmpty(extraHeaders)) {\n return url;\n }\n var httpHeaders = exports.generateHttpHeadersOverwriteParam(extraHeaders);\n if (typeof url === 'string') {\n return googUriUtils.appendParam(\n url, googString.urlEncode(urlParam), httpHeaders);\n } else {\n url.setParameterValue(urlParam, httpHeaders); // duplicate removed!\n return url;\n }\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Implementation of a WebChannel transport using WebChannelBase.\n *\n * When WebChannelBase is used as the underlying transport, the capabilities\n * of the WebChannel are limited to what's supported by the implementation.\n * Particularly, multiplexing is not possible, and only strings are\n * supported as message types.\n */\n\ngoog.provide('goog.labs.net.webChannel.WebChannelBaseTransport');\n\ngoog.require('goog.asserts');\ngoog.require('goog.events.EventTarget');\ngoog.require('goog.json');\ngoog.require('goog.labs.net.webChannel.ChannelRequest');\ngoog.require('goog.labs.net.webChannel.WebChannelBase');\ngoog.require('goog.labs.net.webChannel.Wire');\ngoog.require('goog.log');\ngoog.require('goog.net.WebChannel');\ngoog.require('goog.net.WebChannelTransport');\ngoog.require('goog.object');\ngoog.require('goog.string');\n\n\n\n/**\n * Implementation of {@link goog.net.WebChannelTransport} with\n * {@link goog.labs.net.webChannel.WebChannelBase} as the underlying channel\n * implementation.\n *\n * @constructor\n * @struct\n * @implements {goog.net.WebChannelTransport}\n * @final\n */\ngoog.labs.net.webChannel.WebChannelBaseTransport = function() {\n if (!goog.labs.net.webChannel.ChannelRequest.supportsXhrStreaming()) {\n throw new Error('Environmental error: no available transport.');\n }\n};\n\n\ngoog.scope(function() {\nvar WebChannelBaseTransport = goog.labs.net.webChannel.WebChannelBaseTransport;\nvar WebChannelBase = goog.labs.net.webChannel.WebChannelBase;\nvar Wire = goog.labs.net.webChannel.Wire;\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.prototype.createWebChannel = function(\n url, opt_options) {\n return new WebChannelBaseTransport.Channel(url, opt_options);\n};\n\n\n\n/**\n * Implementation of the {@link goog.net.WebChannel} interface.\n *\n * @param {string} url The URL path for the new WebChannel instance.\n * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the\n * new WebChannel instance.\n *\n * @constructor\n * @implements {goog.net.WebChannel}\n * @extends {goog.events.EventTarget}\n * @final\n */\nWebChannelBaseTransport.Channel = function(url, opt_options) {\n WebChannelBaseTransport.Channel.base(this, 'constructor');\n\n /**\n * @private {!WebChannelBase} The underlying channel object.\n */\n this.channel_ = new WebChannelBase(\n opt_options, goog.net.WebChannelTransport.CLIENT_VERSION);\n\n /**\n * @private {string} The URL of the target server end-point.\n */\n this.url_ = url;\n\n /**\n * @private {goog.log.Logger} The logger for this class.\n */\n this.logger_ =\n goog.log.getLogger('goog.labs.net.webChannel.WebChannelBaseTransport');\n\n /**\n * @private {Object<string, string>} Extra URL parameters\n * to be added to each HTTP request.\n */\n this.messageUrlParams_ =\n (opt_options && opt_options.messageUrlParams) || null;\n\n var messageHeaders = (opt_options && opt_options.messageHeaders) || null;\n\n // default is false\n if (opt_options && opt_options.clientProtocolHeaderRequired) {\n if (messageHeaders) {\n goog.object.set(\n messageHeaders, goog.net.WebChannel.X_CLIENT_PROTOCOL,\n goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);\n } else {\n messageHeaders = goog.object.create(\n goog.net.WebChannel.X_CLIENT_PROTOCOL,\n goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);\n }\n }\n\n this.channel_.setExtraHeaders(messageHeaders);\n\n var initHeaders = (opt_options && opt_options.initMessageHeaders) || null;\n\n if (opt_options && opt_options.messageContentType) {\n if (initHeaders) {\n goog.object.set(\n initHeaders, goog.net.WebChannel.X_WEBCHANNEL_CONTENT_TYPE,\n opt_options.messageContentType);\n } else {\n initHeaders = goog.object.create(\n goog.net.WebChannel.X_WEBCHANNEL_CONTENT_TYPE,\n opt_options.messageContentType);\n }\n }\n\n if (opt_options && opt_options.clientProfile) {\n if (initHeaders) {\n goog.object.set(\n initHeaders, goog.net.WebChannel.X_WEBCHANNEL_CLIENT_PROFILE,\n opt_options.clientProfile);\n } else {\n initHeaders = goog.object.create(\n goog.net.WebChannel.X_WEBCHANNEL_CLIENT_PROFILE,\n opt_options.clientProfile);\n }\n }\n\n this.channel_.setInitHeaders(initHeaders);\n\n var httpHeadersOverwriteParam =\n opt_options && opt_options.httpHeadersOverwriteParam;\n if (httpHeadersOverwriteParam &&\n !goog.string.isEmptyOrWhitespace(httpHeadersOverwriteParam)) {\n this.channel_.setHttpHeadersOverwriteParam(httpHeadersOverwriteParam);\n }\n\n /**\n * @private {boolean} Whether to enable CORS.\n */\n this.supportsCrossDomainXhr_ =\n (opt_options && opt_options.supportsCrossDomainXhr) || false;\n\n /**\n * @private {boolean} Whether to send raw Json and bypass v8 wire format.\n */\n this.sendRawJson_ = (opt_options && opt_options.sendRawJson) || false;\n\n // Note that httpSessionIdParam will be ignored if the same parameter name\n // has already been specified with messageUrlParams\n var httpSessionIdParam = opt_options && opt_options.httpSessionIdParam;\n if (httpSessionIdParam &&\n !goog.string.isEmptyOrWhitespace(httpSessionIdParam)) {\n this.channel_.setHttpSessionIdParam(httpSessionIdParam);\n if (goog.object.containsKey(this.messageUrlParams_, httpSessionIdParam)) {\n goog.object.remove(this.messageUrlParams_, httpSessionIdParam);\n goog.log.warning(this.logger_,\n 'Ignore httpSessionIdParam also specified with messageUrlParams: '\n + httpSessionIdParam);\n }\n }\n\n /**\n * The channel handler.\n *\n * @private {!WebChannelBaseTransport.Channel.Handler_}\n */\n this.channelHandler_ = new WebChannelBaseTransport.Channel.Handler_(this);\n};\ngoog.inherits(WebChannelBaseTransport.Channel, goog.events.EventTarget);\n\n\n/**\n * @override\n * @suppress {checkTypes}\n */\nWebChannelBaseTransport.Channel.prototype.addEventListener = function(\n type, handler, /** boolean= */ opt_capture, opt_handlerScope) {\n WebChannelBaseTransport.Channel.base(\n this, 'addEventListener', type, handler, opt_capture, opt_handlerScope);\n};\n\n\n/**\n * @override\n * @suppress {checkTypes}\n */\nWebChannelBaseTransport.Channel.prototype.removeEventListener = function(\n type, handler, /** boolean= */ opt_capture, opt_handlerScope) {\n WebChannelBaseTransport.Channel.base(\n this, 'removeEventListener', type, handler, opt_capture,\n opt_handlerScope);\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.open = function() {\n this.channel_.setHandler(this.channelHandler_);\n if (this.supportsCrossDomainXhr_) {\n this.channel_.setSupportsCrossDomainXhrs(true);\n }\n this.channel_.connect(this.url_, (this.messageUrlParams_ || undefined));\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.close = function() {\n this.channel_.disconnect();\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.halfClose = function() {\n // to be implemented\n throw new Error('Not implemented');\n};\n\n\n/**\n * The WebChannelBase only supports object types.\n *\n * @param {!goog.net.WebChannel.MessageData} message The message to send.\n *\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.send = function(message) {\n goog.asserts.assert(\n goog.isObject(message) || typeof message === 'string',\n 'only object type or raw string is supported');\n\n if (typeof message === 'string') {\n var rawJson = {};\n rawJson[Wire.RAW_DATA_KEY] = message;\n this.channel_.sendMap(rawJson);\n } else if (this.sendRawJson_) {\n var rawJson = {};\n rawJson[Wire.RAW_DATA_KEY] = goog.json.serialize(message);\n this.channel_.sendMap(rawJson);\n } else {\n this.channel_.sendMap(message);\n }\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.disposeInternal = function() {\n this.channel_.setHandler(null);\n delete this.channelHandler_;\n this.channel_.disconnect();\n delete this.channel_;\n\n WebChannelBaseTransport.Channel.base(this, 'disposeInternal');\n};\n\n\n\n/**\n * The message event.\n *\n * @param {!Array<?>|!Object} array The data array from the underlying channel.\n * @constructor\n * @extends {goog.net.WebChannel.MessageEvent}\n * @final\n */\nWebChannelBaseTransport.Channel.MessageEvent = function(array) {\n WebChannelBaseTransport.Channel.MessageEvent.base(this, 'constructor');\n\n // single-metadata only\n var metadata = array['__sm__'];\n if (metadata) {\n this.metadataKey = goog.object.getAnyKey(metadata);\n if (this.metadataKey) {\n this.data = goog.object.get(metadata, this.metadataKey);\n } else {\n this.data = metadata; // empty\n }\n } else {\n this.data = array;\n }\n};\ngoog.inherits(\n WebChannelBaseTransport.Channel.MessageEvent,\n goog.net.WebChannel.MessageEvent);\n\n\n\n/**\n * The error event.\n *\n * @param {WebChannelBase.Error} error The error code.\n * @constructor\n * @extends {goog.net.WebChannel.ErrorEvent}\n * @final\n */\nWebChannelBaseTransport.Channel.ErrorEvent = function(error) {\n WebChannelBaseTransport.Channel.ErrorEvent.base(this, 'constructor');\n\n /**\n * High-level status code.\n */\n this.status = goog.net.WebChannel.ErrorStatus.NETWORK_ERROR;\n\n /**\n * @const {WebChannelBase.Error} Internal error code, for debugging use only.\n */\n this.errorCode = error;\n};\ngoog.inherits(\n WebChannelBaseTransport.Channel.ErrorEvent, goog.net.WebChannel.ErrorEvent);\n\n\n\n/**\n * Implementation of {@link WebChannelBase.Handler} interface.\n *\n * @param {!WebChannelBaseTransport.Channel} channel The enclosing WebChannel.\n *\n * @constructor\n * @extends {WebChannelBase.Handler}\n * @private\n */\nWebChannelBaseTransport.Channel.Handler_ = function(channel) {\n WebChannelBaseTransport.Channel.Handler_.base(this, 'constructor');\n\n /**\n * @type {!WebChannelBaseTransport.Channel}\n * @private\n */\n this.channel_ = channel;\n};\ngoog.inherits(WebChannelBaseTransport.Channel.Handler_, WebChannelBase.Handler);\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.Handler_.prototype.channelOpened = function(\n channel) {\n goog.log.info(\n this.channel_.logger_, 'WebChannel opened on ' + this.channel_.url_);\n this.channel_.dispatchEvent(goog.net.WebChannel.EventType.OPEN);\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.Handler_.prototype.channelHandleArray =\n function(channel, array) {\n goog.asserts.assert(array, 'array expected to be defined');\n this.channel_.dispatchEvent(\n new WebChannelBaseTransport.Channel.MessageEvent(array));\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.Handler_.prototype.channelError = function(\n channel, error) {\n goog.log.info(\n this.channel_.logger_, 'WebChannel aborted on ' + this.channel_.url_ +\n ' due to channel error: ' + error);\n this.channel_.dispatchEvent(\n new WebChannelBaseTransport.Channel.ErrorEvent(error));\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.Handler_.prototype.channelClosed = function(\n channel, opt_pendingMaps, opt_undeliveredMaps) {\n goog.log.info(\n this.channel_.logger_, 'WebChannel closed on ' + this.channel_.url_);\n this.channel_.dispatchEvent(goog.net.WebChannel.EventType.CLOSE);\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.Channel.prototype.getRuntimeProperties = function() {\n return new WebChannelBaseTransport.ChannelProperties(this.channel_);\n};\n\n\n\n/**\n * Implementation of the {@link goog.net.WebChannel.RuntimeProperties}.\n *\n * @param {!WebChannelBase} channel The underlying channel object.\n *\n * @constructor\n * @implements {goog.net.WebChannel.RuntimeProperties}\n * @final\n */\nWebChannelBaseTransport.ChannelProperties = function(channel) {\n /**\n * The underlying channel object.\n *\n * @private {!WebChannelBase}\n */\n this.channel_ = channel;\n\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.getConcurrentRequestLimit =\n function() {\n return this.channel_.getForwardChannelRequestPool().getMaxSize();\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.isSpdyEnabled = function() {\n return this.getConcurrentRequestLimit() > 1;\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.getPendingRequestCount =\n function() {\n return this.channel_.getForwardChannelRequestPool().getRequestCount();\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.getHttpSessionId =\n function() {\n return this.channel_.getHttpSessionId();\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.commit = function(\n callback) {\n this.channel_.setForwardChannelFlushCallback(callback);\n};\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.getNonAckedMessageCount =\n goog.abstractMethod;\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.notifyNonAckedMessageCount =\n goog.abstractMethod;\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.onCommit =\n goog.abstractMethod;\n\n\n/**\n * @override\n */\nWebChannelBaseTransport.ChannelProperties.prototype.ackCommit =\n goog.abstractMethod;\n\n\n/** @override */\nWebChannelBaseTransport.ChannelProperties.prototype.getLastStatusCode =\n function() {\n return this.channel_.getLastStatusCode();\n};\n}); // goog.scope\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Transport support for WebChannel.\n *\n * The <code>WebChannelTransport</code> implementation serves as the factory\n * for <code>WebChannel</code>, which offers an abstraction for\n * point-to-point socket-like communication similar to what BrowserChannel\n * or HTML5 WebSocket offers.\n */\n\ngoog.provide('goog.net.WebChannelTransport');\n\ngoog.forwardDeclare('goog.net.WebChannel.Options');\ngoog.requireType('goog.net.WebChannel');\n\n\n\n/**\n * A WebChannelTransport instance represents a shared context of logical\n * connectivity between a browser client and a remote origin.\n *\n * Over a single WebChannelTransport instance, multiple WebChannels may be\n * created against different URLs, which may all share the same\n * underlying connectivity (i.e. TCP connection) whenever possible.\n *\n * When multi-domains are supported, such as CORS, multiple origins may be\n * supported over a single WebChannelTransport instance at the same time.\n *\n * Sharing between different window contexts such as tabs is not addressed\n * by WebChannelTransport. Applications may choose HTML5 shared workers\n * or other techniques to access the same transport instance\n * across different window contexts.\n *\n * @interface\n */\ngoog.net.WebChannelTransport = function() {};\n\n\n/**\n * The client version. This integer value will be passed to the server\n * when a channel is opened to inform the server the client \"capabilities\".\n *\n * Wire protocol version is a different concept and is internal to the\n * transport implementation.\n *\n * @const\n * @type {number}\n */\ngoog.net.WebChannelTransport.CLIENT_VERSION = 22;\n\n\n/**\n * Create a new WebChannel instance.\n *\n * The new WebChannel is to be opened against the server-side resource\n * as specified by the given URL. See {@link goog.net.WebChannel} for detailed\n * semantics.\n *\n * @param {string} url The URL path for the new WebChannel instance.\n * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the\n * new WebChannel instance. The configuration object is reusable after\n * the new channel instance is created.\n * @return {!goog.net.WebChannel} the newly created WebChannel instance.\n */\ngoog.net.WebChannelTransport.prototype.createWebChannel = goog.abstractMethod;\n","/**\n * @license\n * Copyright 2017 Google Inc.\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/**\n * Bring in closure-library dependencies\n */\n\ngoog.provide('firebase.webchannel.wrapper');\n\n// goog.net.WebChannelTransport\ngoog.require('goog.net.createWebChannelTransport');\ngoog.require('goog.labs.net.webChannel.WebChannelBaseTransport');\n/**\n * NOTE: The `createWebChannel` function takes an options object as a second param\n * whose properties are typically mangled. We override these in externs/overrides.js\n * Without those externs, this does not function properly.\n */\ngoog.labs.net.webChannel.WebChannelBaseTransport.prototype['createWebChannel'] =\n goog.labs.net.webChannel.WebChannelBaseTransport.prototype.createWebChannel;\ngoog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype['send'] =\n goog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype.send;\ngoog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype['open'] =\n goog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype.open;\ngoog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype['close'] =\n goog.labs.net.webChannel.WebChannelBaseTransport.Channel.prototype.close;\n\n// goog.net.ErrorCode\ngoog.require('goog.net.ErrorCode');\ngoog.net.ErrorCode['NO_ERROR'] = goog.net.ErrorCode.NO_ERROR;\ngoog.net.ErrorCode['TIMEOUT'] = goog.net.ErrorCode.TIMEOUT;\ngoog.net.ErrorCode['HTTP_ERROR'] = goog.net.ErrorCode.HTTP_ERROR;\n\n// goog.net.ErrorType\ngoog.require('goog.net.EventType');\ngoog.net.EventType['COMPLETE'] = goog.net.EventType.COMPLETE;\n\n// goog.net.WebChannel\ngoog.require('goog.net.WebChannel');\ngoog.require('goog.events.EventTarget');\ngoog.net.WebChannel['EventType'] = goog.net.WebChannel.EventType;\ngoog.net.WebChannel.EventType['OPEN'] = goog.net.WebChannel.EventType.OPEN;\ngoog.net.WebChannel.EventType['CLOSE'] = goog.net.WebChannel.EventType.CLOSE;\ngoog.net.WebChannel.EventType['ERROR'] = goog.net.WebChannel.EventType.ERROR;\ngoog.net.WebChannel.EventType['MESSAGE'] =\n goog.net.WebChannel.EventType.MESSAGE;\ngoog.events.EventTarget.prototype['listen'] =\n goog.events.EventTarget.prototype.listen;\n\n// goog.net.XhrIo\ngoog.require('goog.net.XhrIo');\ngoog.net.XhrIo.prototype['listenOnce'] = goog.net.XhrIo.prototype.listenOnce;\ngoog.net.XhrIo.prototype['getLastError'] =\n goog.net.XhrIo.prototype.getLastError;\ngoog.net.XhrIo.prototype['getLastErrorCode'] =\n goog.net.XhrIo.prototype.getLastErrorCode;\ngoog.net.XhrIo.prototype['getStatus'] = goog.net.XhrIo.prototype.getStatus;\ngoog.net.XhrIo.prototype['getResponseJson'] =\n goog.net.XhrIo.prototype.getResponseJson;\ngoog.net.XhrIo.prototype['getResponseText'] =\n goog.net.XhrIo.prototype.getResponseText;\ngoog.net.XhrIo.prototype['send'] = goog.net.XhrIo.prototype.send;\n\n/**\n * This ignore statement is required as closure will minify these properties\n * if done in the typical prettier format\n */\n// prettier-ignore\nmodule['exports'] = {\n 'createWebChannelTransport': goog.net.createWebChannelTransport,\n 'ErrorCode': goog.net.ErrorCode,\n 'EventType': goog.net.EventType,\n 'WebChannel': goog.net.WebChannel,\n 'XhrIo': goog.net.XhrIo\n};\n","/**\n * @license\n * Copyright The Closure Library Authors.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * @fileoverview Default factory for <code>WebChannelTransport</code> to\n * avoid exposing concrete classes to clients.\n */\n\ngoog.provide('goog.net.createWebChannelTransport');\n\ngoog.require('goog.labs.net.webChannel.WebChannelBaseTransport');\n\n\n/**\n * Create a new WebChannelTransport instance using the default implementation.\n * Throws an error message if no default transport available in the current\n * environment.\n *\n * @return {!goog.net.WebChannelTransport} the newly created transport instance.\n */\ngoog.net.createWebChannelTransport = function() {\n return new goog.labs.net.webChannel.WebChannelBaseTransport();\n};\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport firebase from '@firebase/app';\n\n/** The semver (www.semver.org) version of the SDK. */\nexport const SDK_VERSION = firebase.SDK_VERSION;\n","/**\n * @license\n * Copyright 2017 Google LLC\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/**\n * Simple wrapper around a nullable UID. Mostly exists to make code more\n * readable.\n */\nexport class User {\n /** A user with a null UID. */\n static readonly UNAUTHENTICATED = new User(null);\n\n // TODO(mikelehen): Look into getting a proper uid-equivalent for\n // non-FirebaseAuth providers.\n static readonly GOOGLE_CREDENTIALS = new User('google-credentials-uid');\n static readonly FIRST_PARTY = new User('first-party-uid');\n\n constructor(readonly uid: string | null) {}\n\n isAuthenticated(): boolean {\n return this.uid != null;\n }\n\n /**\n * Returns a key representing this user, suitable for inclusion in a\n * dictionary.\n */\n toKey(): string {\n if (this.isAuthenticated()) {\n return 'uid:' + this.uid;\n } else {\n return 'anonymous-user';\n }\n }\n\n isEqual(otherUser: User): boolean {\n return otherUser.uid === this.uid;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { User } from '../auth/user';\nimport { hardAssert, debugAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport {\n FirebaseAuthInternal,\n FirebaseAuthInternalName\n} from '@firebase/auth-interop-types';\nimport { Provider } from '@firebase/component';\n\n// TODO(mikelehen): This should be split into multiple files and probably\n// moved to an auth/ folder to match other platforms.\n\nexport interface FirstPartyCredentialsSettings {\n type: 'gapi';\n client: unknown;\n sessionIndex: string;\n}\n\nexport interface ProviderCredentialsSettings {\n type: 'provider';\n client: CredentialsProvider;\n}\n\n/** Settings for private credentials */\nexport type CredentialsSettings =\n | FirstPartyCredentialsSettings\n | ProviderCredentialsSettings;\n\nexport type TokenType = 'OAuth' | 'FirstParty';\nexport interface Token {\n /** Type of token. */\n type: TokenType;\n\n /**\n * The user with which the token is associated (used for persisting user\n * state on disk, etc.).\n */\n user: User;\n\n /** Extra header values to be passed along with a request */\n authHeaders: { [header: string]: string };\n}\n\nexport class OAuthToken implements Token {\n type = 'OAuth' as TokenType;\n authHeaders: { [header: string]: string };\n constructor(value: string, public user: User) {\n this.authHeaders = {};\n // Set the headers using Object Literal notation to avoid minification\n this.authHeaders['Authorization'] = `Bearer ${value}`;\n }\n}\n\n/**\n * A Listener for credential change events. The listener should fetch a new\n * token and may need to invalidate other state if the current user has also\n * changed.\n */\nexport type CredentialChangeListener = (user: User) => void;\n\n/**\n * Provides methods for getting the uid and token for the current user and\n * listening for changes.\n */\nexport interface CredentialsProvider {\n /** Requests a token for the current user. */\n getToken(): Promise<Token | null>;\n\n /**\n * Marks the last retrieved token as invalid, making the next GetToken request\n * force-refresh the token.\n */\n invalidateToken(): void;\n\n /**\n * Specifies a listener to be notified of credential changes\n * (sign-in / sign-out, token changes). It is immediately called once with the\n * initial user.\n */\n setChangeListener(changeListener: CredentialChangeListener): void;\n\n /** Removes the previously-set change listener. */\n removeChangeListener(): void;\n}\n\n/** A CredentialsProvider that always yields an empty token. */\nexport class EmptyCredentialsProvider implements CredentialsProvider {\n /**\n * Stores the listener registered with setChangeListener()\n * This isn't actually necessary since the UID never changes, but we use this\n * to verify the listen contract is adhered to in tests.\n */\n private changeListener: CredentialChangeListener | null = null;\n\n getToken(): Promise<Token | null> {\n return Promise.resolve<Token | null>(null);\n }\n\n invalidateToken(): void {}\n\n setChangeListener(changeListener: CredentialChangeListener): void {\n debugAssert(\n !this.changeListener,\n 'Can only call setChangeListener() once.'\n );\n this.changeListener = changeListener;\n // Fire with initial user.\n changeListener(User.UNAUTHENTICATED);\n }\n\n removeChangeListener(): void {\n debugAssert(\n this.changeListener !== null,\n 'removeChangeListener() when no listener registered'\n );\n this.changeListener = null;\n }\n}\n\nexport class FirebaseCredentialsProvider implements CredentialsProvider {\n /**\n * The auth token listener registered with FirebaseApp, retained here so we\n * can unregister it.\n */\n private tokenListener: ((token: string | null) => void) | null = null;\n\n /** Tracks the current User. */\n private currentUser: User = User.UNAUTHENTICATED;\n private receivedInitialUser: boolean = false;\n\n /**\n * Counter used to detect if the token changed while a getToken request was\n * outstanding.\n */\n private tokenCounter = 0;\n\n /** The listener registered with setChangeListener(). */\n private changeListener: CredentialChangeListener | null = null;\n\n private forceRefresh = false;\n\n private auth: FirebaseAuthInternal | null;\n\n constructor(authProvider: Provider<FirebaseAuthInternalName>) {\n this.tokenListener = () => {\n this.tokenCounter++;\n this.currentUser = this.getUser();\n this.receivedInitialUser = true;\n if (this.changeListener) {\n this.changeListener(this.currentUser);\n }\n };\n\n this.tokenCounter = 0;\n\n this.auth = authProvider.getImmediate({ optional: true });\n\n if (this.auth) {\n this.auth.addAuthTokenListener(this.tokenListener!);\n } else {\n // if auth is not available, invoke tokenListener once with null token\n this.tokenListener(null);\n authProvider.get().then(\n auth => {\n this.auth = auth;\n if (this.tokenListener) {\n // tokenListener can be removed by removeChangeListener()\n this.auth.addAuthTokenListener(this.tokenListener);\n }\n },\n () => {\n /* this.authProvider.get() never rejects */\n }\n );\n }\n }\n\n getToken(): Promise<Token | null> {\n debugAssert(\n this.tokenListener != null,\n 'getToken cannot be called after listener removed.'\n );\n\n // Take note of the current value of the tokenCounter so that this method\n // can fail (with an ABORTED error) if there is a token change while the\n // request is outstanding.\n const initialTokenCounter = this.tokenCounter;\n const forceRefresh = this.forceRefresh;\n this.forceRefresh = false;\n\n if (!this.auth) {\n return Promise.resolve(null);\n }\n\n return this.auth.getToken(forceRefresh).then(tokenData => {\n // Cancel the request since the token changed while the request was\n // outstanding so the response is potentially for a previous user (which\n // user, we can't be sure).\n if (this.tokenCounter !== initialTokenCounter) {\n throw new FirestoreError(\n Code.ABORTED,\n 'getToken aborted due to token change.'\n );\n } else {\n if (tokenData) {\n hardAssert(\n typeof tokenData.accessToken === 'string',\n 'Invalid tokenData returned from getToken():' + tokenData\n );\n return new OAuthToken(tokenData.accessToken, this.currentUser);\n } else {\n return null;\n }\n }\n });\n }\n\n invalidateToken(): void {\n this.forceRefresh = true;\n }\n\n setChangeListener(changeListener: CredentialChangeListener): void {\n debugAssert(\n !this.changeListener,\n 'Can only call setChangeListener() once.'\n );\n this.changeListener = changeListener;\n\n // Fire the initial event\n if (this.receivedInitialUser) {\n changeListener(this.currentUser);\n }\n }\n\n removeChangeListener(): void {\n debugAssert(\n this.tokenListener != null,\n 'removeChangeListener() called twice'\n );\n debugAssert(\n this.changeListener !== null,\n 'removeChangeListener() called when no listener registered'\n );\n\n if (this.auth) {\n this.auth.removeAuthTokenListener(this.tokenListener!);\n }\n this.tokenListener = null;\n this.changeListener = null;\n }\n\n // Auth.getUid() can return null even with a user logged in. It is because\n // getUid() is synchronous, but the auth code populating Uid is asynchronous.\n // This method should only be called in the AuthTokenListener callback\n // to guarantee to get the actual user.\n private getUser(): User {\n const currentUid = this.auth && this.auth.getUid();\n hardAssert(\n currentUid === null || typeof currentUid === 'string',\n 'Received invalid UID: ' + currentUid\n );\n return new User(currentUid);\n }\n}\n\n// Manual type definition for the subset of Gapi we use.\ninterface Gapi {\n auth: {\n getAuthHeaderValueForFirstParty: (\n userIdentifiers: Array<{ [key: string]: string }>\n ) => string | null;\n };\n}\n\n/*\n * FirstPartyToken provides a fresh token each time its value\n * is requested, because if the token is too old, requests will be rejected.\n * Technically this may no longer be necessary since the SDK should gracefully\n * recover from unauthenticated errors (see b/33147818 for context), but it's\n * safer to keep the implementation as-is.\n */\nexport class FirstPartyToken implements Token {\n type = 'FirstParty' as TokenType;\n user = User.FIRST_PARTY;\n\n constructor(private gapi: Gapi, private sessionIndex: string) {}\n\n get authHeaders(): { [header: string]: string } {\n const headers: { [header: string]: string } = {\n 'X-Goog-AuthUser': this.sessionIndex\n };\n const authHeader = this.gapi.auth.getAuthHeaderValueForFirstParty([]);\n if (authHeader) {\n headers['Authorization'] = authHeader;\n }\n return headers;\n }\n}\n\n/*\n * Provides user credentials required for the Firestore JavaScript SDK\n * to authenticate the user, using technique that is only available\n * to applications hosted by Google.\n */\nexport class FirstPartyCredentialsProvider implements CredentialsProvider {\n constructor(private gapi: Gapi, private sessionIndex: string) {}\n\n getToken(): Promise<Token | null> {\n return Promise.resolve(new FirstPartyToken(this.gapi, this.sessionIndex));\n }\n\n setChangeListener(changeListener: CredentialChangeListener): void {\n // Fire with initial uid.\n changeListener(User.FIRST_PARTY);\n }\n\n removeChangeListener(): void {}\n\n invalidateToken(): void {}\n}\n\n/**\n * Builds a CredentialsProvider depending on the type of\n * the credentials passed in.\n */\nexport function makeCredentialsProvider(\n credentials?: CredentialsSettings\n): CredentialsProvider {\n if (!credentials) {\n return new EmptyCredentialsProvider();\n }\n\n switch (credentials.type) {\n case 'gapi':\n const client = credentials.client as Gapi;\n // Make sure this really is a Gapi client.\n hardAssert(\n !!(\n typeof client === 'object' &&\n client !== null &&\n client['auth'] &&\n client['auth']['getAuthHeaderValueForFirstParty']\n ),\n 'unexpected gapi interface'\n );\n return new FirstPartyCredentialsProvider(\n client,\n credentials.sessionIndex || '0'\n );\n\n case 'provider':\n return credentials.client;\n\n default:\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'makeCredentialsProvider failed due to invalid credential type'\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\n\n/**\n * Error Codes describing the different ways Firestore can fail. These come\n * directly from GRPC.\n */\nexport type Code = firestore.FirestoreErrorCode;\n\nexport const Code = {\n // Causes are copied from:\n // https://github.com/grpc/grpc/blob/bceec94ea4fc5f0085d81235d8e1c06798dc341a/include/grpc%2B%2B/impl/codegen/status_code_enum.h\n /** Not an error; returned on success. */\n OK: 'ok' as Code,\n\n /** The operation was cancelled (typically by the caller). */\n CANCELLED: 'cancelled' as Code,\n\n /** Unknown error or an error from a different error domain. */\n UNKNOWN: 'unknown' as Code,\n\n /**\n * Client specified an invalid argument. Note that this differs from\n * FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are\n * problematic regardless of the state of the system (e.g., a malformed file\n * name).\n */\n INVALID_ARGUMENT: 'invalid-argument' as Code,\n\n /**\n * Deadline expired before operation could complete. For operations that\n * change the state of the system, this error may be returned even if the\n * operation has completed successfully. For example, a successful response\n * from a server could have been delayed long enough for the deadline to\n * expire.\n */\n DEADLINE_EXCEEDED: 'deadline-exceeded' as Code,\n\n /** Some requested entity (e.g., file or directory) was not found. */\n NOT_FOUND: 'not-found' as Code,\n\n /**\n * Some entity that we attempted to create (e.g., file or directory) already\n * exists.\n */\n ALREADY_EXISTS: 'already-exists' as Code,\n\n /**\n * The caller does not have permission to execute the specified operation.\n * PERMISSION_DENIED must not be used for rejections caused by exhausting\n * some resource (use RESOURCE_EXHAUSTED instead for those errors).\n * PERMISSION_DENIED must not be used if the caller can not be identified\n * (use UNAUTHENTICATED instead for those errors).\n */\n PERMISSION_DENIED: 'permission-denied' as Code,\n\n /**\n * The request does not have valid authentication credentials for the\n * operation.\n */\n UNAUTHENTICATED: 'unauthenticated' as Code,\n\n /**\n * Some resource has been exhausted, perhaps a per-user quota, or perhaps the\n * entire file system is out of space.\n */\n RESOURCE_EXHAUSTED: 'resource-exhausted' as Code,\n\n /**\n * Operation was rejected because the system is not in a state required for\n * the operation's execution. For example, directory to be deleted may be\n * non-empty, an rmdir operation is applied to a non-directory, etc.\n *\n * A litmus test that may help a service implementor in deciding\n * between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:\n * (a) Use UNAVAILABLE if the client can retry just the failing call.\n * (b) Use ABORTED if the client should retry at a higher-level\n * (e.g., restarting a read-modify-write sequence).\n * (c) Use FAILED_PRECONDITION if the client should not retry until\n * the system state has been explicitly fixed. E.g., if an \"rmdir\"\n * fails because the directory is non-empty, FAILED_PRECONDITION\n * should be returned since the client should not retry unless\n * they have first fixed up the directory by deleting files from it.\n * (d) Use FAILED_PRECONDITION if the client performs conditional\n * REST Get/Update/Delete on a resource and the resource on the\n * server does not match the condition. E.g., conflicting\n * read-modify-write on the same resource.\n */\n FAILED_PRECONDITION: 'failed-precondition' as Code,\n\n /**\n * The operation was aborted, typically due to a concurrency issue like\n * sequencer check failures, transaction aborts, etc.\n *\n * See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,\n * and UNAVAILABLE.\n */\n ABORTED: 'aborted' as Code,\n\n /**\n * Operation was attempted past the valid range. E.g., seeking or reading\n * past end of file.\n *\n * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed\n * if the system state changes. For example, a 32-bit file system will\n * generate INVALID_ARGUMENT if asked to read at an offset that is not in the\n * range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from\n * an offset past the current file size.\n *\n * There is a fair bit of overlap between FAILED_PRECONDITION and\n * OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)\n * when it applies so that callers who are iterating through a space can\n * easily look for an OUT_OF_RANGE error to detect when they are done.\n */\n OUT_OF_RANGE: 'out-of-range' as Code,\n\n /** Operation is not implemented or not supported/enabled in this service. */\n UNIMPLEMENTED: 'unimplemented' as Code,\n\n /**\n * Internal errors. Means some invariants expected by underlying System has\n * been broken. If you see one of these errors, Something is very broken.\n */\n INTERNAL: 'internal' as Code,\n\n /**\n * The service is currently unavailable. This is a most likely a transient\n * condition and may be corrected by retrying with a backoff.\n *\n * See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,\n * and UNAVAILABLE.\n */\n UNAVAILABLE: 'unavailable' as Code,\n\n /** Unrecoverable data loss or corruption. */\n DATA_LOSS: 'data-loss' as Code\n};\n\n/**\n * An error class used for Firestore-generated errors. Ideally we should be\n * using FirebaseError, but integrating with it is overly arduous at the moment,\n * so we define our own compatible error class (with a `name` of 'FirebaseError'\n * and compatible `code` and `message` fields.)\n */\nexport class FirestoreError extends Error implements firestore.FirestoreError {\n name = 'FirebaseError';\n stack?: string;\n\n constructor(readonly code: Code, readonly message: string) {\n super(message);\n\n // HACK: We write a toString property directly because Error is not a real\n // class and so inheritance does not work correctly. We could alternatively\n // do the same \"back-door inheritance\" trick that FirebaseError does.\n this.toString = () => `${this.name}: [code=${this.code}]: ${this.message}`;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Code, FirestoreError } from '../util/error';\nimport { primitiveComparator } from '../util/misc';\n\n// The earlist date supported by Firestore timestamps (0001-01-01T00:00:00Z).\nconst MIN_SECONDS = -62135596800;\n\nexport class Timestamp {\n static now(): Timestamp {\n return Timestamp.fromMillis(Date.now());\n }\n\n static fromDate(date: Date): Timestamp {\n return Timestamp.fromMillis(date.getTime());\n }\n\n static fromMillis(milliseconds: number): Timestamp {\n const seconds = Math.floor(milliseconds / 1000);\n const nanos = (milliseconds - seconds * 1000) * 1e6;\n return new Timestamp(seconds, nanos);\n }\n\n constructor(readonly seconds: number, readonly nanoseconds: number) {\n if (nanoseconds < 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Timestamp nanoseconds out of range: ' + nanoseconds\n );\n }\n if (nanoseconds >= 1e9) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Timestamp nanoseconds out of range: ' + nanoseconds\n );\n }\n if (seconds < MIN_SECONDS) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Timestamp seconds out of range: ' + seconds\n );\n }\n // This will break in the year 10,000.\n if (seconds >= 253402300800) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Timestamp seconds out of range: ' + seconds\n );\n }\n }\n\n toDate(): Date {\n return new Date(this.toMillis());\n }\n\n toMillis(): number {\n return this.seconds * 1000 + this.nanoseconds / 1e6;\n }\n\n _compareTo(other: Timestamp): number {\n if (this.seconds === other.seconds) {\n return primitiveComparator(this.nanoseconds, other.nanoseconds);\n }\n return primitiveComparator(this.seconds, other.seconds);\n }\n\n isEqual(other: Timestamp): boolean {\n return (\n other.seconds === this.seconds && other.nanoseconds === this.nanoseconds\n );\n }\n\n toString(): string {\n return (\n 'Timestamp(seconds=' +\n this.seconds +\n ', nanoseconds=' +\n this.nanoseconds +\n ')'\n );\n }\n\n valueOf(): string {\n // This method returns a string of the form <seconds>.<nanoseconds> where <seconds> is\n // translated to have a non-negative value and both <seconds> and <nanoseconds> are left-padded\n // with zeroes to be a consistent length. Strings with this format then have a lexiographical\n // ordering that matches the expected ordering. The <seconds> translation is done to avoid\n // having a leading negative sign (i.e. a leading '-' character) in its string representation,\n // which would affect its lexiographical ordering.\n const adjustedSeconds = this.seconds - MIN_SECONDS;\n // Note: Up to 12 decimal digits are required to represent all valid 'seconds' values.\n const formattedSeconds = String(adjustedSeconds).padStart(12, '0');\n const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');\n return formattedSeconds + '.' + formattedNanoseconds;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Timestamp } from '../api/timestamp';\n\n/**\n * A version of a document in Firestore. This corresponds to the version\n * timestamp, such as update_time or read_time.\n */\nexport class SnapshotVersion {\n static fromTimestamp(value: Timestamp): SnapshotVersion {\n return new SnapshotVersion(value);\n }\n\n static min(): SnapshotVersion {\n return new SnapshotVersion(new Timestamp(0, 0));\n }\n\n private constructor(private timestamp: Timestamp) {}\n\n compareTo(other: SnapshotVersion): number {\n return this.timestamp._compareTo(other.timestamp);\n }\n\n isEqual(other: SnapshotVersion): boolean {\n return this.timestamp.isEqual(other.timestamp);\n }\n\n /** Returns a number representation of the version for use in spec tests. */\n toMicroseconds(): number {\n // Convert to microseconds.\n return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;\n }\n\n toString(): string {\n return 'SnapshotVersion(' + this.timestamp.toString() + ')';\n }\n\n toTimestamp(): Timestamp {\n return this.timestamp;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert, fail } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\n\nexport const DOCUMENT_KEY_NAME = '__name__';\n\n/**\n * Path represents an ordered sequence of string segments.\n */\nabstract class BasePath<B extends BasePath<B>> {\n private segments: string[];\n private offset: number;\n private len: number;\n\n constructor(segments: string[], offset?: number, length?: number) {\n if (offset === undefined) {\n offset = 0;\n } else if (offset > segments.length) {\n fail('offset ' + offset + ' out of range ' + segments.length);\n }\n\n if (length === undefined) {\n length = segments.length - offset;\n } else if (length > segments.length - offset) {\n fail('length ' + length + ' out of range ' + (segments.length - offset));\n }\n this.segments = segments;\n this.offset = offset;\n this.len = length;\n }\n\n /**\n * Abstract constructor method to construct an instance of B with the given\n * parameters.\n */\n protected abstract construct(\n segments: string[],\n offset?: number,\n length?: number\n ): B;\n\n /**\n * Returns a String representation.\n *\n * Implementing classes are required to provide deterministic implementations as\n * the String representation is used to obtain canonical Query IDs.\n */\n abstract toString(): string;\n\n get length(): number {\n return this.len;\n }\n\n isEqual(other: B): boolean {\n return BasePath.comparator(this, other) === 0;\n }\n\n child(nameOrPath: string | B): B {\n const segments = this.segments.slice(this.offset, this.limit());\n if (nameOrPath instanceof BasePath) {\n nameOrPath.forEach(segment => {\n segments.push(segment);\n });\n } else {\n segments.push(nameOrPath);\n }\n return this.construct(segments);\n }\n\n /** The index of one past the last segment of the path. */\n private limit(): number {\n return this.offset + this.length;\n }\n\n popFirst(size?: number): B {\n size = size === undefined ? 1 : size;\n debugAssert(\n this.length >= size,\n \"Can't call popFirst() with less segments\"\n );\n return this.construct(\n this.segments,\n this.offset + size,\n this.length - size\n );\n }\n\n popLast(): B {\n debugAssert(!this.isEmpty(), \"Can't call popLast() on empty path\");\n return this.construct(this.segments, this.offset, this.length - 1);\n }\n\n firstSegment(): string {\n debugAssert(!this.isEmpty(), \"Can't call firstSegment() on empty path\");\n return this.segments[this.offset];\n }\n\n lastSegment(): string {\n return this.get(this.length - 1);\n }\n\n get(index: number): string {\n debugAssert(index < this.length, 'Index out of range');\n return this.segments[this.offset + index];\n }\n\n isEmpty(): boolean {\n return this.length === 0;\n }\n\n isPrefixOf(other: this): boolean {\n if (other.length < this.length) {\n return false;\n }\n\n for (let i = 0; i < this.length; i++) {\n if (this.get(i) !== other.get(i)) {\n return false;\n }\n }\n\n return true;\n }\n\n isImmediateParentOf(potentialChild: this): boolean {\n if (this.length + 1 !== potentialChild.length) {\n return false;\n }\n\n for (let i = 0; i < this.length; i++) {\n if (this.get(i) !== potentialChild.get(i)) {\n return false;\n }\n }\n\n return true;\n }\n\n forEach(fn: (segment: string) => void): void {\n for (let i = this.offset, end = this.limit(); i < end; i++) {\n fn(this.segments[i]);\n }\n }\n\n toArray(): string[] {\n return this.segments.slice(this.offset, this.limit());\n }\n\n static comparator<T extends BasePath<T>>(\n p1: BasePath<T>,\n p2: BasePath<T>\n ): number {\n const len = Math.min(p1.length, p2.length);\n for (let i = 0; i < len; i++) {\n const left = p1.get(i);\n const right = p2.get(i);\n if (left < right) {\n return -1;\n }\n if (left > right) {\n return 1;\n }\n }\n if (p1.length < p2.length) {\n return -1;\n }\n if (p1.length > p2.length) {\n return 1;\n }\n return 0;\n }\n}\n\n/**\n * A slash-separated path for navigating resources (documents and collections)\n * within Firestore.\n */\nexport class ResourcePath extends BasePath<ResourcePath> {\n protected construct(\n segments: string[],\n offset?: number,\n length?: number\n ): ResourcePath {\n return new ResourcePath(segments, offset, length);\n }\n\n canonicalString(): string {\n // NOTE: The client is ignorant of any path segments containing escape\n // sequences (e.g. __id123__) and just passes them through raw (they exist\n // for legacy reasons and should not be used frequently).\n\n return this.toArray().join('/');\n }\n\n toString(): string {\n return this.canonicalString();\n }\n\n /**\n * Creates a resource path from the given slash-delimited string.\n */\n static fromString(path: string): ResourcePath {\n // NOTE: The client is ignorant of any path segments containing escape\n // sequences (e.g. __id123__) and just passes them through raw (they exist\n // for legacy reasons and should not be used frequently).\n\n if (path.indexOf('//') >= 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid path (${path}). Paths must not contain // in them.`\n );\n }\n\n // We may still have an empty segment at the beginning or end if they had a\n // leading or trailing slash (which we allow).\n const segments = path.split('/').filter(segment => segment.length > 0);\n\n return new ResourcePath(segments);\n }\n\n static EMPTY_PATH = new ResourcePath([]);\n}\n\nconst identifierRegExp = /^[_a-zA-Z][_a-zA-Z0-9]*$/;\n\n/** A dot-separated path for navigating sub-objects within a document. */\nexport class FieldPath extends BasePath<FieldPath> {\n protected construct(\n segments: string[],\n offset?: number,\n length?: number\n ): FieldPath {\n return new FieldPath(segments, offset, length);\n }\n\n /**\n * Returns true if the string could be used as a segment in a field path\n * without escaping.\n */\n private static isValidIdentifier(segment: string): boolean {\n return identifierRegExp.test(segment);\n }\n\n canonicalString(): string {\n return this.toArray()\n .map(str => {\n str = str.replace('\\\\', '\\\\\\\\').replace('`', '\\\\`');\n if (!FieldPath.isValidIdentifier(str)) {\n str = '`' + str + '`';\n }\n return str;\n })\n .join('.');\n }\n\n toString(): string {\n return this.canonicalString();\n }\n\n /**\n * Returns true if this field references the key of a document.\n */\n isKeyField(): boolean {\n return this.length === 1 && this.get(0) === DOCUMENT_KEY_NAME;\n }\n\n /**\n * The field designating the key of a document.\n */\n static keyField(): FieldPath {\n return new FieldPath([DOCUMENT_KEY_NAME]);\n }\n\n /**\n * Parses a field string from the given server-formatted string.\n *\n * - Splitting the empty string is not allowed (for now at least).\n * - Empty segments within the string (e.g. if there are two consecutive\n * separators) are not allowed.\n *\n * TODO(b/37244157): we should make this more strict. Right now, it allows\n * non-identifier path components, even if they aren't escaped.\n */\n static fromServerFormat(path: string): FieldPath {\n const segments: string[] = [];\n let current = '';\n let i = 0;\n\n const addCurrentSegment = (): void => {\n if (current.length === 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid field path (${path}). Paths must not be empty, begin ` +\n `with '.', end with '.', or contain '..'`\n );\n }\n segments.push(current);\n current = '';\n };\n\n let inBackticks = false;\n\n while (i < path.length) {\n const c = path[i];\n if (c === '\\\\') {\n if (i + 1 === path.length) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Path has trailing escape character: ' + path\n );\n }\n const next = path[i + 1];\n if (!(next === '\\\\' || next === '.' || next === '`')) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Path has invalid escape sequence: ' + path\n );\n }\n current += next;\n i += 2;\n } else if (c === '`') {\n inBackticks = !inBackticks;\n i++;\n } else if (c === '.' && !inBackticks) {\n addCurrentSegment();\n i++;\n } else {\n current += c;\n i++;\n }\n }\n addCurrentSegment();\n\n if (inBackticks) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Unterminated ` in path: ' + path\n );\n }\n\n return new FieldPath(segments);\n }\n\n static EMPTY_PATH = new FieldPath([]);\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert } from '../util/assert';\n\nimport { ResourcePath } from './path';\n\nexport class DocumentKey {\n constructor(readonly path: ResourcePath) {\n debugAssert(\n DocumentKey.isDocumentKey(path),\n 'Invalid DocumentKey with an odd number of segments: ' +\n path.toArray().join('/')\n );\n }\n\n static fromName(name: string): DocumentKey {\n return new DocumentKey(ResourcePath.fromString(name).popFirst(5));\n }\n\n /** Returns true if the document is in the specified collectionId. */\n hasCollectionId(collectionId: string): boolean {\n return (\n this.path.length >= 2 &&\n this.path.get(this.path.length - 2) === collectionId\n );\n }\n\n isEqual(other: DocumentKey | null): boolean {\n return (\n other !== null && ResourcePath.comparator(this.path, other.path) === 0\n );\n }\n\n toString(): string {\n return this.path.toString();\n }\n\n static EMPTY = new DocumentKey(new ResourcePath([]));\n\n static comparator(k1: DocumentKey, k2: DocumentKey): number {\n return ResourcePath.comparator(k1.path, k2.path);\n }\n\n static isDocumentKey(path: ResourcePath): boolean {\n return path.length % 2 === 0;\n }\n\n /**\n * Creates and returns a new document key with the given segments.\n *\n * @param segments The segments of the path to the document\n * @return A new instance of DocumentKey\n */\n static fromSegments(segments: string[]): DocumentKey {\n return new DocumentKey(new ResourcePath(segments.slice()));\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert } from './assert';\n\nexport interface Dict<V> {\n [stringKey: string]: V;\n}\n\nexport function objectSize<V>(obj: object): number {\n let count = 0;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n count++;\n }\n }\n return count;\n}\n\nexport function forEach<V>(\n obj: Dict<V>,\n fn: (key: string, val: V) => void\n): void {\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n fn(key, obj[key]);\n }\n }\n}\n\nexport function isEmpty<V>(obj: Dict<V>): boolean {\n debugAssert(\n obj != null && typeof obj === 'object',\n 'isEmpty() expects object parameter.'\n );\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n return false;\n }\n }\n return true;\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport { PlatformSupport } from '../platform/platform';\nimport { primitiveComparator } from './misc';\n\n/**\n * Immutable class that represents a \"proto\" byte string.\n *\n * Proto byte strings can either be Base64-encoded strings or Uint8Arrays when\n * sent on the wire. This class abstracts away this differentiation by holding\n * the proto byte string in a common class that must be converted into a string\n * before being sent as a proto.\n */\nexport class ByteString {\n static readonly EMPTY_BYTE_STRING = new ByteString('');\n\n private constructor(private readonly binaryString: string) {}\n\n static fromBase64String(base64: string): ByteString {\n const binaryString = PlatformSupport.getPlatform().atob(base64);\n return new ByteString(binaryString);\n }\n\n static fromUint8Array(array: Uint8Array): ByteString {\n const binaryString = binaryStringFromUint8Array(array);\n return new ByteString(binaryString);\n }\n\n toBase64(): string {\n return PlatformSupport.getPlatform().btoa(this.binaryString);\n }\n\n toUint8Array(): Uint8Array {\n return uint8ArrayFromBinaryString(this.binaryString);\n }\n\n approximateByteSize(): number {\n return this.binaryString.length * 2;\n }\n\n compareTo(other: ByteString): number {\n return primitiveComparator(this.binaryString, other.binaryString);\n }\n\n isEqual(other: ByteString): boolean {\n return this.binaryString === other.binaryString;\n }\n}\n\n/**\n * Helper function to convert an Uint8array to a binary string.\n */\nexport function binaryStringFromUint8Array(array: Uint8Array): string {\n let binaryString = '';\n for (let i = 0; i < array.length; ++i) {\n binaryString += String.fromCharCode(array[i]);\n }\n return binaryString;\n}\n\n/**\n * Helper function to convert a binary string to an Uint8Array.\n */\nexport function uint8ArrayFromBinaryString(binaryString: string): Uint8Array {\n const buffer = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n buffer[i] = binaryString.charCodeAt(i);\n }\n return buffer;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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// An Object whose keys and values are strings.\nexport interface StringMap {\n [key: string]: string;\n}\n\n/**\n * Returns whether a variable is either undefined or null.\n */\nexport function isNullOrUndefined(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\n/** Returns whether the value represents -0. */\nexport function isNegativeZero(value: number): boolean {\n // Detect if the value is -0.0. Based on polyfill from\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\n return value === -0 && 1 / value === 1 / -0;\n}\n\n/**\n * Returns whether a value is an integer and in the safe integer range\n * @param value The value to test for being an integer and in the safe range\n */\nexport function isSafeInteger(value: unknown): boolean {\n return (\n typeof value === 'number' &&\n Number.isInteger(value) &&\n !isNegativeZero(value) &&\n value <= Number.MAX_SAFE_INTEGER &&\n value >= Number.MIN_SAFE_INTEGER\n );\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\nimport { Timestamp } from '../api/timestamp';\nimport { normalizeTimestamp } from './values';\n\n/**\n * Represents a locally-applied ServerTimestamp.\n *\n * Server Timestamps are backed by MapValues that contain an internal field\n * `__type__` with a value of `server_timestamp`. The previous value and local\n * write time are stored in its `__previous_value__` and `__local_write_time__`\n * fields respectively.\n *\n * Notes:\n * - ServerTimestampValue instances are created as the result of applying a\n * TransformMutation (see TransformMutation.applyTo()). They can only exist in\n * the local view of a document. Therefore they do not need to be parsed or\n * serialized.\n * - When evaluated locally (e.g. for snapshot.data()), they by default\n * evaluate to `null`. This behavior can be configured by passing custom\n * FieldValueOptions to value().\n * - With respect to other ServerTimestampValues, they sort by their\n * localWriteTime.\n */\n\nconst SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';\nconst TYPE_KEY = '__type__';\nconst PREVIOUS_VALUE_KEY = '__previous_value__';\nconst LOCAL_WRITE_TIME_KEY = '__local_write_time__';\n\nexport function isServerTimestamp(value: api.Value | null): boolean {\n const type = (value?.mapValue?.fields || {})[TYPE_KEY]?.stringValue;\n return type === SERVER_TIMESTAMP_SENTINEL;\n}\n\n/**\n * Creates a new ServerTimestamp proto value (using the internal format).\n */\nexport function serverTimestamp(\n localWriteTime: Timestamp,\n previousValue: api.Value | null\n): api.Value {\n const mapValue: api.MapValue = {\n fields: {\n [TYPE_KEY]: {\n stringValue: SERVER_TIMESTAMP_SENTINEL\n },\n [LOCAL_WRITE_TIME_KEY]: {\n timestampValue: {\n seconds: localWriteTime.seconds,\n nanos: localWriteTime.nanoseconds\n }\n }\n }\n };\n\n if (previousValue) {\n mapValue.fields![PREVIOUS_VALUE_KEY] = previousValue;\n }\n\n return { mapValue };\n}\n\n/**\n * Returns the value of the field before this ServerTimestamp was set.\n *\n * Preserving the previous values allows the user to display the last resoled\n * value until the backend responds with the timestamp.\n */\nexport function getPreviousValue(value: api.Value): api.Value | null {\n const previousValue = value.mapValue!.fields![PREVIOUS_VALUE_KEY];\n\n if (isServerTimestamp(previousValue)) {\n return getPreviousValue(previousValue);\n }\n return previousValue;\n}\n\n/**\n * Returns the local time at which this timestamp was first set.\n */\nexport function getLocalWriteTime(value: api.Value): Timestamp {\n const localWriteTime = normalizeTimestamp(\n value.mapValue!.fields![LOCAL_WRITE_TIME_KEY].timestampValue!\n );\n return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { TypeOrder } from './object_value';\nimport { fail, hardAssert } from '../util/assert';\nimport { forEach, objectSize } from '../util/obj';\nimport { ByteString } from '../util/byte_string';\nimport { isNegativeZero } from '../util/types';\nimport { DocumentKey } from './document_key';\nimport { arrayEquals, primitiveComparator } from '../util/misc';\nimport { DatabaseId } from '../core/database_info';\nimport {\n getLocalWriteTime,\n getPreviousValue,\n isServerTimestamp\n} from './server_timestamps';\n\n// A RegExp matching ISO 8601 UTC timestamps with optional fraction.\nconst ISO_TIMESTAMP_REG_EXP = new RegExp(\n /^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d(?:\\.(\\d+))?Z$/\n);\n\n/** Extracts the backend's type order for the provided value. */\nexport function typeOrder(value: api.Value): TypeOrder {\n if ('nullValue' in value) {\n return TypeOrder.NullValue;\n } else if ('booleanValue' in value) {\n return TypeOrder.BooleanValue;\n } else if ('integerValue' in value || 'doubleValue' in value) {\n return TypeOrder.NumberValue;\n } else if ('timestampValue' in value) {\n return TypeOrder.TimestampValue;\n } else if ('stringValue' in value) {\n return TypeOrder.StringValue;\n } else if ('bytesValue' in value) {\n return TypeOrder.BlobValue;\n } else if ('referenceValue' in value) {\n return TypeOrder.RefValue;\n } else if ('geoPointValue' in value) {\n return TypeOrder.GeoPointValue;\n } else if ('arrayValue' in value) {\n return TypeOrder.ArrayValue;\n } else if ('mapValue' in value) {\n if (isServerTimestamp(value)) {\n return TypeOrder.ServerTimestampValue;\n }\n return TypeOrder.ObjectValue;\n } else {\n return fail('Invalid value type: ' + JSON.stringify(value));\n }\n}\n\n/** Tests `left` and `right` for equality based on the backend semantics. */\nexport function valueEquals(left: api.Value, right: api.Value): boolean {\n const leftType = typeOrder(left);\n const rightType = typeOrder(right);\n if (leftType !== rightType) {\n return false;\n }\n\n switch (leftType) {\n case TypeOrder.NullValue:\n return true;\n case TypeOrder.BooleanValue:\n return left.booleanValue === right.booleanValue;\n case TypeOrder.ServerTimestampValue:\n return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));\n case TypeOrder.TimestampValue:\n return timestampEquals(left, right);\n case TypeOrder.StringValue:\n return left.stringValue === right.stringValue;\n case TypeOrder.BlobValue:\n return blobEquals(left, right);\n case TypeOrder.RefValue:\n return left.referenceValue === right.referenceValue;\n case TypeOrder.GeoPointValue:\n return geoPointEquals(left, right);\n case TypeOrder.NumberValue:\n return numberEquals(left, right);\n case TypeOrder.ArrayValue:\n return arrayEquals(\n left.arrayValue!.values || [],\n right.arrayValue!.values || [],\n valueEquals\n );\n case TypeOrder.ObjectValue:\n return objectEquals(left, right);\n default:\n return fail('Unexpected value type: ' + JSON.stringify(left));\n }\n}\n\nfunction timestampEquals(left: api.Value, right: api.Value): boolean {\n if (\n typeof left.timestampValue === 'string' &&\n typeof right.timestampValue === 'string' &&\n left.timestampValue.length === right.timestampValue.length\n ) {\n // Use string equality for ISO 8601 timestamps\n return left.timestampValue === right.timestampValue;\n }\n\n const leftTimestamp = normalizeTimestamp(left.timestampValue!);\n const rightTimestamp = normalizeTimestamp(right.timestampValue!);\n return (\n leftTimestamp.seconds === rightTimestamp.seconds &&\n leftTimestamp.nanos === rightTimestamp.nanos\n );\n}\n\nfunction geoPointEquals(left: api.Value, right: api.Value): boolean {\n return (\n normalizeNumber(left.geoPointValue!.latitude) ===\n normalizeNumber(right.geoPointValue!.latitude) &&\n normalizeNumber(left.geoPointValue!.longitude) ===\n normalizeNumber(right.geoPointValue!.longitude)\n );\n}\n\nfunction blobEquals(left: api.Value, right: api.Value): boolean {\n return normalizeByteString(left.bytesValue!).isEqual(\n normalizeByteString(right.bytesValue!)\n );\n}\n\nexport function numberEquals(left: api.Value, right: api.Value): boolean {\n if ('integerValue' in left && 'integerValue' in right) {\n return (\n normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue)\n );\n } else if ('doubleValue' in left && 'doubleValue' in right) {\n const n1 = normalizeNumber(left.doubleValue!);\n const n2 = normalizeNumber(right.doubleValue!);\n\n if (n1 === n2) {\n return isNegativeZero(n1) === isNegativeZero(n2);\n } else {\n return isNaN(n1) && isNaN(n2);\n }\n }\n\n return false;\n}\n\nfunction objectEquals(left: api.Value, right: api.Value): boolean {\n const leftMap = left.mapValue!.fields || {};\n const rightMap = right.mapValue!.fields || {};\n\n if (objectSize(leftMap) !== objectSize(rightMap)) {\n return false;\n }\n\n for (const key in leftMap) {\n if (leftMap.hasOwnProperty(key)) {\n if (\n rightMap[key] === undefined ||\n !valueEquals(leftMap[key], rightMap[key])\n ) {\n return false;\n }\n }\n }\n return true;\n}\n\n/** Returns true if the ArrayValue contains the specified element. */\nexport function arrayValueContains(\n haystack: api.ArrayValue,\n needle: api.Value\n): boolean {\n return (\n (haystack.values || []).find(v => valueEquals(v, needle)) !== undefined\n );\n}\n\nexport function valueCompare(left: api.Value, right: api.Value): number {\n const leftType = typeOrder(left);\n const rightType = typeOrder(right);\n\n if (leftType !== rightType) {\n return primitiveComparator(leftType, rightType);\n }\n\n switch (leftType) {\n case TypeOrder.NullValue:\n return 0;\n case TypeOrder.BooleanValue:\n return primitiveComparator(left.booleanValue!, right.booleanValue!);\n case TypeOrder.NumberValue:\n return compareNumbers(left, right);\n case TypeOrder.TimestampValue:\n return compareTimestamps(left.timestampValue!, right.timestampValue!);\n case TypeOrder.ServerTimestampValue:\n return compareTimestamps(\n getLocalWriteTime(left),\n getLocalWriteTime(right)\n );\n case TypeOrder.StringValue:\n return primitiveComparator(left.stringValue!, right.stringValue!);\n case TypeOrder.BlobValue:\n return compareBlobs(left.bytesValue!, right.bytesValue!);\n case TypeOrder.RefValue:\n return compareReferences(left.referenceValue!, right.referenceValue!);\n case TypeOrder.GeoPointValue:\n return compareGeoPoints(left.geoPointValue!, right.geoPointValue!);\n case TypeOrder.ArrayValue:\n return compareArrays(left.arrayValue!, right.arrayValue!);\n case TypeOrder.ObjectValue:\n return compareMaps(left.mapValue!, right.mapValue!);\n default:\n throw fail('Invalid value type: ' + leftType);\n }\n}\n\nfunction compareNumbers(left: api.Value, right: api.Value): number {\n const leftNumber = normalizeNumber(left.integerValue || left.doubleValue);\n const rightNumber = normalizeNumber(right.integerValue || right.doubleValue);\n\n if (leftNumber < rightNumber) {\n return -1;\n } else if (leftNumber > rightNumber) {\n return 1;\n } else if (leftNumber === rightNumber) {\n return 0;\n } else {\n // one or both are NaN.\n if (isNaN(leftNumber)) {\n return isNaN(rightNumber) ? 0 : -1;\n } else {\n return 1;\n }\n }\n}\n\nfunction compareTimestamps(left: api.Timestamp, right: api.Timestamp): number {\n if (\n typeof left === 'string' &&\n typeof right === 'string' &&\n left.length === right.length\n ) {\n return primitiveComparator(left, right);\n }\n\n const leftTimestamp = normalizeTimestamp(left);\n const rightTimestamp = normalizeTimestamp(right);\n\n const comparison = primitiveComparator(\n leftTimestamp.seconds,\n rightTimestamp.seconds\n );\n if (comparison !== 0) {\n return comparison;\n }\n return primitiveComparator(leftTimestamp.nanos, rightTimestamp.nanos);\n}\n\nfunction compareReferences(leftPath: string, rightPath: string): number {\n const leftSegments = leftPath.split('/');\n const rightSegments = rightPath.split('/');\n for (let i = 0; i < leftSegments.length && i < rightSegments.length; i++) {\n const comparison = primitiveComparator(leftSegments[i], rightSegments[i]);\n if (comparison !== 0) {\n return comparison;\n }\n }\n return primitiveComparator(leftSegments.length, rightSegments.length);\n}\n\nfunction compareGeoPoints(left: api.LatLng, right: api.LatLng): number {\n const comparison = primitiveComparator(\n normalizeNumber(left.latitude),\n normalizeNumber(right.latitude)\n );\n if (comparison !== 0) {\n return comparison;\n }\n return primitiveComparator(\n normalizeNumber(left.longitude),\n normalizeNumber(right.longitude)\n );\n}\n\nfunction compareBlobs(\n left: string | Uint8Array,\n right: string | Uint8Array\n): number {\n const leftBytes = normalizeByteString(left);\n const rightBytes = normalizeByteString(right);\n return leftBytes.compareTo(rightBytes);\n}\n\nfunction compareArrays(left: api.ArrayValue, right: api.ArrayValue): number {\n const leftArray = left.values || [];\n const rightArray = right.values || [];\n\n for (let i = 0; i < leftArray.length && i < rightArray.length; ++i) {\n const compare = valueCompare(leftArray[i], rightArray[i]);\n if (compare) {\n return compare;\n }\n }\n return primitiveComparator(leftArray.length, rightArray.length);\n}\n\nfunction compareMaps(left: api.MapValue, right: api.MapValue): number {\n const leftMap = left.fields || {};\n const leftKeys = Object.keys(leftMap);\n const rightMap = right.fields || {};\n const rightKeys = Object.keys(rightMap);\n\n // Even though MapValues are likely sorted correctly based on their insertion\n // order (e.g. when received from the backend), local modifications can bring\n // elements out of order. We need to re-sort the elements to ensure that\n // canonical IDs are independent of insertion order.\n leftKeys.sort();\n rightKeys.sort();\n\n for (let i = 0; i < leftKeys.length && i < rightKeys.length; ++i) {\n const keyCompare = primitiveComparator(leftKeys[i], rightKeys[i]);\n if (keyCompare !== 0) {\n return keyCompare;\n }\n const compare = valueCompare(leftMap[leftKeys[i]], rightMap[rightKeys[i]]);\n if (compare !== 0) {\n return compare;\n }\n }\n\n return primitiveComparator(leftKeys.length, rightKeys.length);\n}\n\n/**\n * Generates the canonical ID for the provided field value (as used in Target\n * serialization).\n */\nexport function canonicalId(value: api.Value): string {\n return canonifyValue(value);\n}\n\nfunction canonifyValue(value: api.Value): string {\n if ('nullValue' in value) {\n return 'null';\n } else if ('booleanValue' in value) {\n return '' + value.booleanValue!;\n } else if ('integerValue' in value) {\n return '' + value.integerValue!;\n } else if ('doubleValue' in value) {\n return '' + value.doubleValue!;\n } else if ('timestampValue' in value) {\n return canonifyTimestamp(value.timestampValue!);\n } else if ('stringValue' in value) {\n return value.stringValue!;\n } else if ('bytesValue' in value) {\n return canonifyByteString(value.bytesValue!);\n } else if ('referenceValue' in value) {\n return canonifyReference(value.referenceValue!);\n } else if ('geoPointValue' in value) {\n return canonifyGeoPoint(value.geoPointValue!);\n } else if ('arrayValue' in value) {\n return canonifyArray(value.arrayValue!);\n } else if ('mapValue' in value) {\n return canonifyMap(value.mapValue!);\n } else {\n return fail('Invalid value type: ' + JSON.stringify(value));\n }\n}\n\nfunction canonifyByteString(byteString: string | Uint8Array): string {\n return normalizeByteString(byteString).toBase64();\n}\n\nfunction canonifyTimestamp(timestamp: api.Timestamp): string {\n const normalizedTimestamp = normalizeTimestamp(timestamp);\n return `time(${normalizedTimestamp.seconds},${normalizedTimestamp.nanos})`;\n}\n\nfunction canonifyGeoPoint(geoPoint: api.LatLng): string {\n return `geo(${geoPoint.latitude},${geoPoint.longitude})`;\n}\n\nfunction canonifyReference(referenceValue: string): string {\n return DocumentKey.fromName(referenceValue).toString();\n}\n\nfunction canonifyMap(mapValue: api.MapValue): string {\n // Iteration order in JavaScript is not guaranteed. To ensure that we generate\n // matching canonical IDs for identical maps, we need to sort the keys.\n const sortedKeys = Object.keys(mapValue.fields || {}).sort();\n\n let result = '{';\n let first = true;\n for (const key of sortedKeys) {\n if (!first) {\n result += ',';\n } else {\n first = false;\n }\n result += `${key}:${canonifyValue(mapValue.fields![key])}`;\n }\n return result + '}';\n}\n\nfunction canonifyArray(arrayValue: api.ArrayValue): string {\n let result = '[';\n let first = true;\n for (const value of arrayValue.values || []) {\n if (!first) {\n result += ',';\n } else {\n first = false;\n }\n result += canonifyValue(value);\n }\n return result + ']';\n}\n\n/**\n * Returns an approximate (and wildly inaccurate) in-memory size for the field\n * value.\n *\n * The memory size takes into account only the actual user data as it resides\n * in memory and ignores object overhead.\n */\nexport function estimateByteSize(value: api.Value): number {\n switch (typeOrder(value)) {\n case TypeOrder.NullValue:\n return 4;\n case TypeOrder.BooleanValue:\n return 4;\n case TypeOrder.NumberValue:\n return 8;\n case TypeOrder.TimestampValue:\n // Timestamps are made up of two distinct numbers (seconds + nanoseconds)\n return 16;\n case TypeOrder.ServerTimestampValue:\n const previousValue = getPreviousValue(value);\n return previousValue ? 16 + estimateByteSize(previousValue) : 16;\n case TypeOrder.StringValue:\n // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures:\n // \"JavaScript's String type is [...] a set of elements of 16-bit unsigned\n // integer values\"\n return value.stringValue!.length * 2;\n case TypeOrder.BlobValue:\n return normalizeByteString(value.bytesValue!).approximateByteSize();\n case TypeOrder.RefValue:\n return value.referenceValue!.length;\n case TypeOrder.GeoPointValue:\n // GeoPoints are made up of two distinct numbers (latitude + longitude)\n return 16;\n case TypeOrder.ArrayValue:\n return estimateArrayByteSize(value.arrayValue!);\n case TypeOrder.ObjectValue:\n return estimateMapByteSize(value.mapValue!);\n default:\n throw fail('Invalid value type: ' + JSON.stringify(value));\n }\n}\n\nfunction estimateMapByteSize(mapValue: api.MapValue): number {\n let size = 0;\n forEach(mapValue.fields || {}, (key, val) => {\n size += key.length + estimateByteSize(val);\n });\n return size;\n}\n\nfunction estimateArrayByteSize(arrayValue: api.ArrayValue): number {\n return (arrayValue.values || []).reduce(\n (previousSize, value) => previousSize + estimateByteSize(value),\n 0\n );\n}\n\n/**\n * Converts the possible Proto values for a timestamp value into a \"seconds and\n * nanos\" representation.\n */\nexport function normalizeTimestamp(\n date: api.Timestamp\n): { seconds: number; nanos: number } {\n hardAssert(!!date, 'Cannot normalize null or undefined timestamp.');\n\n // The json interface (for the browser) will return an iso timestamp string,\n // while the proto js library (for node) will return a\n // google.protobuf.Timestamp instance.\n if (typeof date === 'string') {\n // The date string can have higher precision (nanos) than the Date class\n // (millis), so we do some custom parsing here.\n\n // Parse the nanos right out of the string.\n let nanos = 0;\n const fraction = ISO_TIMESTAMP_REG_EXP.exec(date);\n hardAssert(!!fraction, 'invalid timestamp: ' + date);\n if (fraction[1]) {\n // Pad the fraction out to 9 digits (nanos).\n let nanoStr = fraction[1];\n nanoStr = (nanoStr + '000000000').substr(0, 9);\n nanos = Number(nanoStr);\n }\n\n // Parse the date to get the seconds.\n const parsedDate = new Date(date);\n const seconds = Math.floor(parsedDate.getTime() / 1000);\n\n return { seconds, nanos };\n } else {\n // TODO(b/37282237): Use strings for Proto3 timestamps\n // assert(!this.options.useProto3Json,\n // 'The timestamp instance format requires Proto JS.');\n const seconds = normalizeNumber(date.seconds);\n const nanos = normalizeNumber(date.nanos);\n return { seconds, nanos };\n }\n}\n\n/**\n * Converts the possible Proto types for numbers into a JavaScript number.\n * Returns 0 if the value is not numeric.\n */\nexport function normalizeNumber(value: number | string | undefined): number {\n // TODO(bjornick): Handle int64 greater than 53 bits.\n if (typeof value === 'number') {\n return value;\n } else if (typeof value === 'string') {\n return Number(value);\n } else {\n return 0;\n }\n}\n\n/** Converts the possible Proto types for Blobs into a ByteString. */\nexport function normalizeByteString(blob: string | Uint8Array): ByteString {\n if (typeof blob === 'string') {\n return ByteString.fromBase64String(blob);\n } else {\n return ByteString.fromUint8Array(blob);\n }\n}\n\n/** Returns a reference value for the provided database and key. */\nexport function refValue(databaseId: DatabaseId, key: DocumentKey): api.Value {\n return {\n referenceValue: `projects/${databaseId.projectId}/databases/${\n databaseId.database\n }/documents/${key.path.canonicalString()}`\n };\n}\n\n/** Returns true if `value` is an IntegerValue . */\nexport function isInteger(\n value?: api.Value | null\n): value is { integerValue: string | number } {\n return !!value && 'integerValue' in value;\n}\n\n/** Returns true if `value` is a DoubleValue. */\nexport function isDouble(\n value?: api.Value | null\n): value is { doubleValue: string | number } {\n return !!value && 'doubleValue' in value;\n}\n\n/** Returns true if `value` is either an IntegerValue or a DoubleValue. */\nexport function isNumber(value?: api.Value | null): boolean {\n return isInteger(value) || isDouble(value);\n}\n\n/** Returns true if `value` is an ArrayValue. */\nexport function isArray(\n value?: api.Value | null\n): value is { arrayValue: api.ArrayValue } {\n return !!value && 'arrayValue' in value;\n}\n\n/** Returns true if `value` is a ReferenceValue. */\nexport function isReferenceValue(\n value?: api.Value | null\n): value is { referenceValue: string } {\n return !!value && 'referenceValue' in value;\n}\n\n/** Returns true if `value` is a NullValue. */\nexport function isNullValue(\n value?: api.Value | null\n): value is { nullValue: 'NULL_VALUE' } {\n return !!value && 'nullValue' in value;\n}\n\n/** Returns true if `value` is NaN. */\nexport function isNanValue(\n value?: api.Value | null\n): value is { doubleValue: 'NaN' | number } {\n return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue));\n}\n\n/** Returns true if `value` is a MapValue. */\nexport function isMapValue(\n value?: api.Value | null\n): value is { mapValue: api.MapValue } {\n return !!value && 'mapValue' in value;\n}\n","/**\n * @license\n * Copyright 2018 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { Timestamp } from '../api/timestamp';\nimport { debugAssert } from '../util/assert';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport {\n valueEquals,\n isArray,\n isInteger,\n isNumber,\n normalizeNumber\n} from './values';\nimport { serverTimestamp } from './server_timestamps';\nimport { arrayEquals } from '../util/misc';\n\n/** Represents a transform within a TransformMutation. */\nexport interface TransformOperation {\n /**\n * Computes the local transform result against the provided `previousValue`,\n * optionally using the provided localWriteTime.\n */\n applyToLocalView(\n previousValue: api.Value | null,\n localWriteTime: Timestamp\n ): api.Value;\n\n /**\n * Computes a final transform result after the transform has been acknowledged\n * by the server, potentially using the server-provided transformResult.\n */\n applyToRemoteDocument(\n previousValue: api.Value | null,\n transformResult: api.Value | null\n ): api.Value;\n\n /**\n * If this transform operation is not idempotent, returns the base value to\n * persist for this transform. If a base value is returned, the transform\n * operation is always applied to this base value, even if document has\n * already been updated.\n *\n * Base values provide consistent behavior for non-idempotent transforms and\n * allow us to return the same latency-compensated value even if the backend\n * has already applied the transform operation. The base value is null for\n * idempotent transforms, as they can be re-played even if the backend has\n * already applied them.\n *\n * @return a base value to store along with the mutation, or null for\n * idempotent transforms.\n */\n computeBaseValue(previousValue: api.Value | null): api.Value | null;\n\n isEqual(other: TransformOperation): boolean;\n}\n\n/** Transforms a value into a server-generated timestamp. */\nexport class ServerTimestampTransform implements TransformOperation {\n private constructor() {}\n static instance = new ServerTimestampTransform();\n\n applyToLocalView(\n previousValue: api.Value | null,\n localWriteTime: Timestamp\n ): api.Value {\n return serverTimestamp(localWriteTime!, previousValue);\n }\n\n applyToRemoteDocument(\n previousValue: api.Value | null,\n transformResult: api.Value | null\n ): api.Value {\n return transformResult!;\n }\n\n computeBaseValue(previousValue: api.Value | null): api.Value | null {\n return null; // Server timestamps are idempotent and don't require a base value.\n }\n\n isEqual(other: TransformOperation): boolean {\n return other instanceof ServerTimestampTransform;\n }\n}\n\n/** Transforms an array value via a union operation. */\nexport class ArrayUnionTransformOperation implements TransformOperation {\n constructor(readonly elements: api.Value[]) {}\n\n applyToLocalView(\n previousValue: api.Value | null,\n localWriteTime: Timestamp\n ): api.Value {\n return this.apply(previousValue);\n }\n\n applyToRemoteDocument(\n previousValue: api.Value | null,\n transformResult: api.Value | null\n ): api.Value {\n // The server just sends null as the transform result for array operations,\n // so we have to calculate a result the same as we do for local\n // applications.\n return this.apply(previousValue);\n }\n\n private apply(previousValue: api.Value | null): api.Value {\n const values = coercedFieldValuesArray(previousValue);\n for (const toUnion of this.elements) {\n if (!values.some(element => valueEquals(element, toUnion))) {\n values.push(toUnion);\n }\n }\n return { arrayValue: { values } };\n }\n\n computeBaseValue(previousValue: api.Value | null): api.Value | null {\n return null; // Array transforms are idempotent and don't require a base value.\n }\n\n isEqual(other: TransformOperation): boolean {\n return (\n other instanceof ArrayUnionTransformOperation &&\n arrayEquals(this.elements, other.elements, valueEquals)\n );\n }\n}\n\n/** Transforms an array value via a remove operation. */\nexport class ArrayRemoveTransformOperation implements TransformOperation {\n constructor(readonly elements: api.Value[]) {}\n\n applyToLocalView(\n previousValue: api.Value | null,\n localWriteTime: Timestamp\n ): api.Value {\n return this.apply(previousValue);\n }\n\n applyToRemoteDocument(\n previousValue: api.Value | null,\n transformResult: api.Value | null\n ): api.Value {\n // The server just sends null as the transform result for array operations,\n // so we have to calculate a result the same as we do for local\n // applications.\n return this.apply(previousValue);\n }\n\n private apply(previousValue: api.Value | null): api.Value {\n let values = coercedFieldValuesArray(previousValue);\n for (const toRemove of this.elements) {\n values = values.filter(element => !valueEquals(element, toRemove));\n }\n return { arrayValue: { values } };\n }\n\n computeBaseValue(previousValue: api.Value | null): api.Value | null {\n return null; // Array transforms are idempotent and don't require a base value.\n }\n\n isEqual(other: TransformOperation): boolean {\n return (\n other instanceof ArrayRemoveTransformOperation &&\n arrayEquals(this.elements, other.elements, valueEquals)\n );\n }\n}\n\n/**\n * Implements the backend semantics for locally computed NUMERIC_ADD (increment)\n * transforms. Converts all field values to integers or doubles, but unlike the\n * backend does not cap integer values at 2^63. Instead, JavaScript number\n * arithmetic is used and precision loss can occur for values greater than 2^53.\n */\nexport class NumericIncrementTransformOperation implements TransformOperation {\n constructor(\n private readonly serializer: JsonProtoSerializer,\n readonly operand: api.Value\n ) {\n debugAssert(\n isNumber(operand),\n 'NumericIncrementTransform transform requires a NumberValue'\n );\n }\n\n applyToLocalView(\n previousValue: api.Value | null,\n localWriteTime: Timestamp\n ): api.Value {\n // PORTING NOTE: Since JavaScript's integer arithmetic is limited to 53 bit\n // precision and resolves overflows by reducing precision, we do not\n // manually cap overflows at 2^63.\n const baseValue = this.computeBaseValue(previousValue);\n const sum = this.asNumber(baseValue) + this.asNumber(this.operand);\n if (isInteger(baseValue) && isInteger(this.operand)) {\n return this.serializer.toInteger(sum);\n } else {\n return this.serializer.toDouble(sum);\n }\n }\n\n applyToRemoteDocument(\n previousValue: api.Value | null,\n transformResult: api.Value | null\n ): api.Value {\n debugAssert(\n transformResult !== null,\n \"Didn't receive transformResult for NUMERIC_ADD transform\"\n );\n return transformResult;\n }\n\n /**\n * Inspects the provided value, returning the provided value if it is already\n * a NumberValue, otherwise returning a coerced value of 0.\n */\n computeBaseValue(previousValue: api.Value | null): api.Value {\n return isNumber(previousValue) ? previousValue! : { integerValue: 0 };\n }\n\n isEqual(other: TransformOperation): boolean {\n return (\n other instanceof NumericIncrementTransformOperation &&\n valueEquals(this.operand, other.operand)\n );\n }\n\n private asNumber(value: api.Value): number {\n return normalizeNumber(value.integerValue || value.doubleValue);\n }\n}\n\nfunction coercedFieldValuesArray(value: api.Value | null): api.Value[] {\n return isArray(value) && value.arrayValue.values\n ? value.arrayValue.values.slice()\n : [];\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { Timestamp } from '../api/timestamp';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\n\nimport {\n Document,\n MaybeDocument,\n NoDocument,\n UnknownDocument\n} from './document';\nimport { DocumentKey } from './document_key';\nimport { ObjectValue, ObjectValueBuilder } from './object_value';\nimport { FieldPath } from './path';\nimport { TransformOperation } from './transform_operation';\nimport { arrayEquals } from '../util/misc';\n\n/**\n * Provides a set of fields that can be used to partially patch a document.\n * FieldMask is used in conjunction with ObjectValue.\n * Examples:\n * foo - Overwrites foo entirely with the provided value. If foo is not\n * present in the companion ObjectValue, the field is deleted.\n * foo.bar - Overwrites only the field bar of the object foo.\n * If foo is not an object, foo is replaced with an object\n * containing foo\n */\nexport class FieldMask {\n constructor(readonly fields: FieldPath[]) {\n // TODO(dimond): validation of FieldMask\n // Sort the field mask to support `FieldMask.isEqual()` and assert below.\n fields.sort(FieldPath.comparator);\n debugAssert(\n !fields.some((v, i) => i !== 0 && v.isEqual(fields[i - 1])),\n 'FieldMask contains field that is not unique: ' +\n fields.find((v, i) => i !== 0 && v.isEqual(fields[i - 1]))!\n );\n }\n\n /**\n * Verifies that `fieldPath` is included by at least one field in this field\n * mask.\n *\n * This is an O(n) operation, where `n` is the size of the field mask.\n */\n covers(fieldPath: FieldPath): boolean {\n for (const fieldMaskPath of this.fields) {\n if (fieldMaskPath.isPrefixOf(fieldPath)) {\n return true;\n }\n }\n return false;\n }\n\n isEqual(other: FieldMask): boolean {\n return arrayEquals(this.fields, other.fields, (l, r) => l.isEqual(r));\n }\n}\n\n/** A field path and the TransformOperation to perform upon it. */\nexport class FieldTransform {\n constructor(\n readonly field: FieldPath,\n readonly transform: TransformOperation\n ) {}\n\n isEqual(other: FieldTransform): boolean {\n return (\n this.field.isEqual(other.field) && this.transform.isEqual(other.transform)\n );\n }\n}\n\n/** The result of successfully applying a mutation to the backend. */\nexport class MutationResult {\n constructor(\n /**\n * The version at which the mutation was committed:\n *\n * - For most operations, this is the updateTime in the WriteResult.\n * - For deletes, the commitTime of the WriteResponse (because deletes are\n * not stored and have no updateTime).\n *\n * Note that these versions can be different: No-op writes will not change\n * the updateTime even though the commitTime advances.\n */\n readonly version: SnapshotVersion,\n /**\n * The resulting fields returned from the backend after a\n * TransformMutation has been committed. Contains one FieldValue for each\n * FieldTransform that was in the mutation.\n *\n * Will be null if the mutation was not a TransformMutation.\n */\n readonly transformResults: Array<api.Value | null> | null\n ) {}\n}\n\nexport const enum MutationType {\n Set,\n Patch,\n Transform,\n Delete,\n Verify\n}\n\n/**\n * Encodes a precondition for a mutation. This follows the model that the\n * backend accepts with the special case of an explicit \"empty\" precondition\n * (meaning no precondition).\n */\nexport class Precondition {\n private constructor(\n readonly updateTime?: SnapshotVersion,\n readonly exists?: boolean\n ) {\n debugAssert(\n updateTime === undefined || exists === undefined,\n 'Precondition can specify \"exists\" or \"updateTime\" but not both'\n );\n }\n\n /** Creates a new empty Precondition. */\n static none(): Precondition {\n return new Precondition();\n }\n\n /** Creates a new Precondition with an exists flag. */\n static exists(exists: boolean): Precondition {\n return new Precondition(undefined, exists);\n }\n\n /** Creates a new Precondition based on a version a document exists at. */\n static updateTime(version: SnapshotVersion): Precondition {\n return new Precondition(version);\n }\n\n /** Returns whether this Precondition is empty. */\n get isNone(): boolean {\n return this.updateTime === undefined && this.exists === undefined;\n }\n\n /**\n * Returns true if the preconditions is valid for the given document\n * (or null if no document is available).\n */\n isValidFor(maybeDoc: MaybeDocument | null): boolean {\n if (this.updateTime !== undefined) {\n return (\n maybeDoc instanceof Document &&\n maybeDoc.version.isEqual(this.updateTime)\n );\n } else if (this.exists !== undefined) {\n return this.exists === maybeDoc instanceof Document;\n } else {\n debugAssert(this.isNone, 'Precondition should be empty');\n return true;\n }\n }\n\n isEqual(other: Precondition): boolean {\n return (\n this.exists === other.exists &&\n (this.updateTime\n ? !!other.updateTime && this.updateTime.isEqual(other.updateTime)\n : !other.updateTime)\n );\n }\n}\n\n/**\n * A mutation describes a self-contained change to a document. Mutations can\n * create, replace, delete, and update subsets of documents.\n *\n * Mutations not only act on the value of the document but also its version.\n *\n * For local mutations (mutations that haven't been committed yet), we preserve\n * the existing version for Set, Patch, and Transform mutations. For Delete\n * mutations, we reset the version to 0.\n *\n * Here's the expected transition table.\n *\n * MUTATION APPLIED TO RESULTS IN\n *\n * SetMutation Document(v3) Document(v3)\n * SetMutation NoDocument(v3) Document(v0)\n * SetMutation null Document(v0)\n * PatchMutation Document(v3) Document(v3)\n * PatchMutation NoDocument(v3) NoDocument(v3)\n * PatchMutation null null\n * TransformMutation Document(v3) Document(v3)\n * TransformMutation NoDocument(v3) NoDocument(v3)\n * TransformMutation null null\n * DeleteMutation Document(v3) NoDocument(v0)\n * DeleteMutation NoDocument(v3) NoDocument(v0)\n * DeleteMutation null NoDocument(v0)\n *\n * For acknowledged mutations, we use the updateTime of the WriteResponse as\n * the resulting version for Set, Patch, and Transform mutations. As deletes\n * have no explicit update time, we use the commitTime of the WriteResponse for\n * Delete mutations.\n *\n * If a mutation is acknowledged by the backend but fails the precondition check\n * locally, we return an `UnknownDocument` and rely on Watch to send us the\n * updated version.\n *\n * Note that TransformMutations don't create Documents (in the case of being\n * applied to a NoDocument), even though they would on the backend. This is\n * because the client always combines the TransformMutation with a SetMutation\n * or PatchMutation and we only want to apply the transform if the prior\n * mutation resulted in a Document (always true for a SetMutation, but not\n * necessarily for a PatchMutation).\n *\n * ## Subclassing Notes\n *\n * Subclasses of Mutation need to implement applyToRemoteDocument() and\n * applyToLocalView() to implement the actual behavior of applying the mutation\n * to some source document.\n */\nexport abstract class Mutation {\n abstract readonly type: MutationType;\n abstract readonly key: DocumentKey;\n abstract readonly precondition: Precondition;\n\n /**\n * Applies this mutation to the given MaybeDocument or null for the purposes\n * of computing a new remote document. If the input document doesn't match the\n * expected state (e.g. it is null or outdated), an `UnknownDocument` can be\n * returned.\n *\n * @param maybeDoc The document to mutate. The input document can be null if\n * the client has no knowledge of the pre-mutation state of the document.\n * @param mutationResult The result of applying the mutation from the backend.\n * @return The mutated document. The returned document may be an\n * UnknownDocument if the mutation could not be applied to the locally\n * cached base document.\n */\n abstract applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument;\n\n /**\n * Applies this mutation to the given MaybeDocument or null for the purposes\n * of computing the new local view of a document. Both the input and returned\n * documents can be null.\n *\n * @param maybeDoc The document to mutate. The input document can be null if\n * the client has no knowledge of the pre-mutation state of the document.\n * @param baseDoc The state of the document prior to this mutation batch. The\n * input document can be null if the client has no knowledge of the\n * pre-mutation state of the document.\n * @param localWriteTime A timestamp indicating the local write time of the\n * batch this mutation is a part of.\n * @return The mutated document. The returned document may be null, but only\n * if maybeDoc was null and the mutation would not create a new document.\n */\n abstract applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null;\n\n /**\n * If this mutation is not idempotent, returns the base value to persist with\n * this mutation. If a base value is returned, the mutation is always applied\n * to this base value, even if document has already been updated.\n *\n * The base value is a sparse object that consists of only the document\n * fields for which this mutation contains a non-idempotent transformation\n * (e.g. a numeric increment). The provided value guarantees consistent\n * behavior for non-idempotent transforms and allow us to return the same\n * latency-compensated value even if the backend has already applied the\n * mutation. The base value is null for idempotent mutations, as they can be\n * re-played even if the backend has already applied them.\n *\n * @return a base value to store along with the mutation, or null for\n * idempotent mutations.\n */\n abstract extractBaseValue(maybeDoc: MaybeDocument | null): ObjectValue | null;\n\n abstract isEqual(other: Mutation): boolean;\n\n protected verifyKeyMatches(maybeDoc: MaybeDocument | null): void {\n if (maybeDoc != null) {\n debugAssert(\n maybeDoc.key.isEqual(this.key),\n 'Can only apply a mutation to a document with the same key'\n );\n }\n }\n\n /**\n * Returns the version from the given document for use as the result of a\n * mutation. Mutations are defined to return the version of the base document\n * only if it is an existing document. Deleted and unknown documents have a\n * post-mutation version of SnapshotVersion.min().\n */\n protected static getPostMutationVersion(\n maybeDoc: MaybeDocument | null\n ): SnapshotVersion {\n if (maybeDoc instanceof Document) {\n return maybeDoc.version;\n } else {\n return SnapshotVersion.min();\n }\n }\n}\n\n/**\n * A mutation that creates or replaces the document at the given key with the\n * object value contents.\n */\nexport class SetMutation extends Mutation {\n constructor(\n readonly key: DocumentKey,\n readonly value: ObjectValue,\n readonly precondition: Precondition\n ) {\n super();\n }\n\n readonly type: MutationType = MutationType.Set;\n\n applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument {\n this.verifyKeyMatches(maybeDoc);\n\n debugAssert(\n mutationResult.transformResults == null,\n 'Transform results received by SetMutation.'\n );\n\n // Unlike applyToLocalView, if we're applying a mutation to a remote\n // document the server has accepted the mutation so the precondition must\n // have held.\n\n const version = mutationResult.version;\n return new Document(this.key, version, this.value, {\n hasCommittedMutations: true\n });\n }\n\n applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null {\n this.verifyKeyMatches(maybeDoc);\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n return maybeDoc;\n }\n\n const version = Mutation.getPostMutationVersion(maybeDoc);\n return new Document(this.key, version, this.value, {\n hasLocalMutations: true\n });\n }\n\n extractBaseValue(maybeDoc: MaybeDocument | null): null {\n return null;\n }\n\n isEqual(other: Mutation): boolean {\n return (\n other instanceof SetMutation &&\n this.key.isEqual(other.key) &&\n this.value.isEqual(other.value) &&\n this.precondition.isEqual(other.precondition)\n );\n }\n}\n\n/**\n * A mutation that modifies fields of the document at the given key with the\n * given values. The values are applied through a field mask:\n *\n * * When a field is in both the mask and the values, the corresponding field\n * is updated.\n * * When a field is in neither the mask nor the values, the corresponding\n * field is unmodified.\n * * When a field is in the mask but not in the values, the corresponding field\n * is deleted.\n * * When a field is not in the mask but is in the values, the values map is\n * ignored.\n */\nexport class PatchMutation extends Mutation {\n constructor(\n readonly key: DocumentKey,\n readonly data: ObjectValue,\n readonly fieldMask: FieldMask,\n readonly precondition: Precondition\n ) {\n super();\n }\n\n readonly type: MutationType = MutationType.Patch;\n\n applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument {\n this.verifyKeyMatches(maybeDoc);\n\n debugAssert(\n mutationResult.transformResults == null,\n 'Transform results received by PatchMutation.'\n );\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n // Since the mutation was not rejected, we know that the precondition\n // matched on the backend. We therefore must not have the expected version\n // of the document in our cache and return an UnknownDocument with the\n // known updateTime.\n return new UnknownDocument(this.key, mutationResult.version);\n }\n\n const newData = this.patchDocument(maybeDoc);\n return new Document(this.key, mutationResult.version, newData, {\n hasCommittedMutations: true\n });\n }\n\n applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null {\n this.verifyKeyMatches(maybeDoc);\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n return maybeDoc;\n }\n\n const version = Mutation.getPostMutationVersion(maybeDoc);\n const newData = this.patchDocument(maybeDoc);\n return new Document(this.key, version, newData, {\n hasLocalMutations: true\n });\n }\n\n extractBaseValue(maybeDoc: MaybeDocument | null): null {\n return null;\n }\n\n isEqual(other: Mutation): boolean {\n return (\n other instanceof PatchMutation &&\n this.key.isEqual(other.key) &&\n this.fieldMask.isEqual(other.fieldMask) &&\n this.precondition.isEqual(other.precondition)\n );\n }\n\n /**\n * Patches the data of document if available or creates a new document. Note\n * that this does not check whether or not the precondition of this patch\n * holds.\n */\n private patchDocument(maybeDoc: MaybeDocument | null): ObjectValue {\n let data: ObjectValue;\n if (maybeDoc instanceof Document) {\n data = maybeDoc.data();\n } else {\n data = ObjectValue.empty();\n }\n return this.patchObject(data);\n }\n\n private patchObject(data: ObjectValue): ObjectValue {\n const builder = new ObjectValueBuilder(data);\n this.fieldMask.fields.forEach(fieldPath => {\n if (!fieldPath.isEmpty()) {\n const newValue = this.data.field(fieldPath);\n if (newValue !== null) {\n builder.set(fieldPath, newValue);\n } else {\n builder.delete(fieldPath);\n }\n }\n });\n return builder.build();\n }\n}\n\n/**\n * A mutation that modifies specific fields of the document with transform\n * operations. Currently the only supported transform is a server timestamp, but\n * IP Address, increment(n), etc. could be supported in the future.\n *\n * It is somewhat similar to a PatchMutation in that it patches specific fields\n * and has no effect when applied to a null or NoDocument (see comment on\n * Mutation for rationale).\n */\nexport class TransformMutation extends Mutation {\n readonly type: MutationType = MutationType.Transform;\n\n // NOTE: We set a precondition of exists: true as a safety-check, since we\n // always combine TransformMutations with a SetMutation or PatchMutation which\n // (if successful) should end up with an existing document.\n readonly precondition = Precondition.exists(true);\n\n constructor(\n readonly key: DocumentKey,\n readonly fieldTransforms: FieldTransform[]\n ) {\n super();\n }\n\n applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument {\n this.verifyKeyMatches(maybeDoc);\n\n hardAssert(\n mutationResult.transformResults != null,\n 'Transform results missing for TransformMutation.'\n );\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n // Since the mutation was not rejected, we know that the precondition\n // matched on the backend. We therefore must not have the expected version\n // of the document in our cache and return an UnknownDocument with the\n // known updateTime.\n return new UnknownDocument(this.key, mutationResult.version);\n }\n\n const doc = this.requireDocument(maybeDoc);\n const transformResults = this.serverTransformResults(\n maybeDoc,\n mutationResult.transformResults!\n );\n\n const version = mutationResult.version;\n const newData = this.transformObject(doc.data(), transformResults);\n return new Document(this.key, version, newData, {\n hasCommittedMutations: true\n });\n }\n\n applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null {\n this.verifyKeyMatches(maybeDoc);\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n return maybeDoc;\n }\n\n const doc = this.requireDocument(maybeDoc);\n const transformResults = this.localTransformResults(\n localWriteTime,\n maybeDoc,\n baseDoc\n );\n const newData = this.transformObject(doc.data(), transformResults);\n return new Document(this.key, doc.version, newData, {\n hasLocalMutations: true\n });\n }\n\n extractBaseValue(maybeDoc: MaybeDocument | null): ObjectValue | null {\n let baseObject: ObjectValueBuilder | null = null;\n for (const fieldTransform of this.fieldTransforms) {\n const existingValue =\n maybeDoc instanceof Document\n ? maybeDoc.field(fieldTransform.field)\n : undefined;\n const coercedValue = fieldTransform.transform.computeBaseValue(\n existingValue || null\n );\n\n if (coercedValue != null) {\n if (baseObject == null) {\n baseObject = new ObjectValueBuilder().set(\n fieldTransform.field,\n coercedValue\n );\n } else {\n baseObject = baseObject.set(fieldTransform.field, coercedValue);\n }\n }\n }\n return baseObject ? baseObject.build() : null;\n }\n\n isEqual(other: Mutation): boolean {\n return (\n other instanceof TransformMutation &&\n this.key.isEqual(other.key) &&\n arrayEquals(this.fieldTransforms, other.fieldTransforms, (l, r) =>\n l.isEqual(r)\n ) &&\n this.precondition.isEqual(other.precondition)\n );\n }\n\n /**\n * Asserts that the given MaybeDocument is actually a Document and verifies\n * that it matches the key for this mutation. Since we only support\n * transformations with precondition exists this method is guaranteed to be\n * safe.\n */\n private requireDocument(maybeDoc: MaybeDocument | null): Document {\n debugAssert(\n maybeDoc instanceof Document,\n 'Unknown MaybeDocument type ' + maybeDoc\n );\n debugAssert(\n maybeDoc.key.isEqual(this.key),\n 'Can only transform a document with the same key'\n );\n return maybeDoc;\n }\n\n /**\n * Creates a list of \"transform results\" (a transform result is a field value\n * representing the result of applying a transform) for use after a\n * TransformMutation has been acknowledged by the server.\n *\n * @param baseDoc The document prior to applying this mutation batch.\n * @param serverTransformResults The transform results received by the server.\n * @return The transform results list.\n */\n private serverTransformResults(\n baseDoc: MaybeDocument | null,\n serverTransformResults: Array<api.Value | null>\n ): api.Value[] {\n const transformResults: api.Value[] = [];\n hardAssert(\n this.fieldTransforms.length === serverTransformResults.length,\n `server transform result count (${serverTransformResults.length}) ` +\n `should match field transform count (${this.fieldTransforms.length})`\n );\n\n for (let i = 0; i < serverTransformResults.length; i++) {\n const fieldTransform = this.fieldTransforms[i];\n const transform = fieldTransform.transform;\n let previousValue: api.Value | null = null;\n if (baseDoc instanceof Document) {\n previousValue = baseDoc.field(fieldTransform.field);\n }\n transformResults.push(\n transform.applyToRemoteDocument(\n previousValue,\n serverTransformResults[i]\n )\n );\n }\n return transformResults;\n }\n\n /**\n * Creates a list of \"transform results\" (a transform result is a field value\n * representing the result of applying a transform) for use when applying a\n * TransformMutation locally.\n *\n * @param localWriteTime The local time of the transform mutation (used to\n * generate ServerTimestampValues).\n * @param maybeDoc The current state of the document after applying all\n * previous mutations.\n * @param baseDoc The document prior to applying this mutation batch.\n * @return The transform results list.\n */\n private localTransformResults(\n localWriteTime: Timestamp,\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null\n ): api.Value[] {\n const transformResults: api.Value[] = [];\n for (const fieldTransform of this.fieldTransforms) {\n const transform = fieldTransform.transform;\n\n let previousValue: api.Value | null = null;\n if (maybeDoc instanceof Document) {\n previousValue = maybeDoc.field(fieldTransform.field);\n }\n\n if (previousValue === null && baseDoc instanceof Document) {\n // If the current document does not contain a value for the mutated\n // field, use the value that existed before applying this mutation\n // batch. This solves an edge case where a PatchMutation clears the\n // values in a nested map before the TransformMutation is applied.\n previousValue = baseDoc.field(fieldTransform.field);\n }\n\n transformResults.push(\n transform.applyToLocalView(previousValue, localWriteTime)\n );\n }\n return transformResults;\n }\n\n private transformObject(\n data: ObjectValue,\n transformResults: api.Value[]\n ): ObjectValue {\n debugAssert(\n transformResults.length === this.fieldTransforms.length,\n 'TransformResults length mismatch.'\n );\n\n const builder = new ObjectValueBuilder(data);\n for (let i = 0; i < this.fieldTransforms.length; i++) {\n const fieldTransform = this.fieldTransforms[i];\n const fieldPath = fieldTransform.field;\n builder.set(fieldPath, transformResults[i]);\n }\n return builder.build();\n }\n}\n\n/** A mutation that deletes the document at the given key. */\nexport class DeleteMutation extends Mutation {\n constructor(readonly key: DocumentKey, readonly precondition: Precondition) {\n super();\n }\n\n readonly type: MutationType = MutationType.Delete;\n\n applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument {\n this.verifyKeyMatches(maybeDoc);\n\n debugAssert(\n mutationResult.transformResults == null,\n 'Transform results received by DeleteMutation.'\n );\n\n // Unlike applyToLocalView, if we're applying a mutation to a remote\n // document the server has accepted the mutation so the precondition must\n // have held.\n\n return new NoDocument(this.key, mutationResult.version, {\n hasCommittedMutations: true\n });\n }\n\n applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null {\n this.verifyKeyMatches(maybeDoc);\n\n if (!this.precondition.isValidFor(maybeDoc)) {\n return maybeDoc;\n }\n\n if (maybeDoc) {\n debugAssert(\n maybeDoc.key.isEqual(this.key),\n 'Can only apply mutation to document with same key'\n );\n }\n return new NoDocument(this.key, SnapshotVersion.min());\n }\n\n extractBaseValue(maybeDoc: MaybeDocument | null): null {\n return null;\n }\n\n isEqual(other: Mutation): boolean {\n return (\n other instanceof DeleteMutation &&\n this.key.isEqual(other.key) &&\n this.precondition.isEqual(other.precondition)\n );\n }\n}\n\n/**\n * A mutation that verifies the existence of the document at the given key with\n * the provided precondition.\n *\n * The `verify` operation is only used in Transactions, and this class serves\n * primarily to facilitate serialization into protos.\n */\nexport class VerifyMutation extends Mutation {\n constructor(readonly key: DocumentKey, readonly precondition: Precondition) {\n super();\n }\n\n readonly type: MutationType = MutationType.Verify;\n\n applyToRemoteDocument(\n maybeDoc: MaybeDocument | null,\n mutationResult: MutationResult\n ): MaybeDocument {\n fail('VerifyMutation should only be used in Transactions.');\n }\n\n applyToLocalView(\n maybeDoc: MaybeDocument | null,\n baseDoc: MaybeDocument | null,\n localWriteTime: Timestamp\n ): MaybeDocument | null {\n fail('VerifyMutation should only be used in Transactions.');\n }\n\n extractBaseValue(maybeDoc: MaybeDocument | null): null {\n fail('VerifyMutation should only be used in Transactions.');\n }\n\n isEqual(other: Mutation): boolean {\n return (\n other instanceof VerifyMutation &&\n this.key.isEqual(other.key) &&\n this.precondition.isEqual(other.precondition)\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { debugAssert } from '../util/assert';\nimport { FieldMask } from './mutation';\nimport { FieldPath } from './path';\nimport { isServerTimestamp } from './server_timestamps';\nimport { valueEquals, isMapValue, typeOrder } from './values';\nimport { forEach } from '../util/obj';\n\nexport interface JsonObject<T> {\n [name: string]: T;\n}\n\nexport const enum TypeOrder {\n // This order is based on the backend's ordering, but modified to support\n // server timestamps.\n NullValue = 0,\n BooleanValue = 1,\n NumberValue = 2,\n TimestampValue = 3,\n ServerTimestampValue = 4,\n StringValue = 5,\n BlobValue = 6,\n RefValue = 7,\n GeoPointValue = 8,\n ArrayValue = 9,\n ObjectValue = 10\n}\n\n/**\n * An ObjectValue represents a MapValue in the Firestore Proto and offers the\n * ability to add and remove fields (via the ObjectValueBuilder).\n */\nexport class ObjectValue {\n constructor(public readonly proto: { mapValue: api.MapValue }) {\n debugAssert(\n !isServerTimestamp(proto),\n 'ServerTimestamps should be converted to ServerTimestampValue'\n );\n }\n\n static empty(): ObjectValue {\n return new ObjectValue({ mapValue: {} });\n }\n\n /**\n * Returns the value at the given path or null.\n *\n * @param path the path to search\n * @return The value at the path or if there it doesn't exist.\n */\n field(path: FieldPath): api.Value | null {\n if (path.isEmpty()) {\n return this.proto;\n } else {\n let value: api.Value = this.proto;\n for (let i = 0; i < path.length - 1; ++i) {\n if (!value.mapValue!.fields) {\n return null;\n }\n value = value.mapValue!.fields[path.get(i)];\n if (!isMapValue(value)) {\n return null;\n }\n }\n\n value = (value.mapValue!.fields || {})[path.lastSegment()];\n return value || null;\n }\n }\n\n isEqual(other: ObjectValue): boolean {\n return valueEquals(this.proto, other.proto);\n }\n}\n\n/**\n * An Overlay, which contains an update to apply. Can either be Value proto, a\n * map of Overlay values (to represent additional nesting at the given key) or\n * `null` (to represent field deletes).\n */\ntype Overlay = Map<string, Overlay> | api.Value | null;\n\n/**\n * An ObjectValueBuilder provides APIs to set and delete fields from an\n * ObjectValue.\n */\nexport class ObjectValueBuilder {\n /** A map that contains the accumulated changes in this builder. */\n private overlayMap = new Map<string, Overlay>();\n\n /**\n * @param baseObject The object to mutate.\n */\n constructor(private readonly baseObject: ObjectValue = ObjectValue.empty()) {}\n\n /**\n * Sets the field to the provided value.\n *\n * @param path The field path to set.\n * @param value The value to set.\n * @return The current Builder instance.\n */\n set(path: FieldPath, value: api.Value): ObjectValueBuilder {\n debugAssert(\n !path.isEmpty(),\n 'Cannot set field for empty path on ObjectValue'\n );\n this.setOverlay(path, value);\n return this;\n }\n\n /**\n * Removes the field at the specified path. If there is no field at the\n * specified path, nothing is changed.\n *\n * @param path The field path to remove.\n * @return The current Builder instance.\n */\n delete(path: FieldPath): ObjectValueBuilder {\n debugAssert(\n !path.isEmpty(),\n 'Cannot delete field for empty path on ObjectValue'\n );\n this.setOverlay(path, null);\n return this;\n }\n\n /**\n * Adds `value` to the overlay map at `path`. Creates nested map entries if\n * needed.\n */\n private setOverlay(path: FieldPath, value: api.Value | null): void {\n let currentLevel = this.overlayMap;\n\n for (let i = 0; i < path.length - 1; ++i) {\n const currentSegment = path.get(i);\n let currentValue = currentLevel.get(currentSegment);\n\n if (currentValue instanceof Map) {\n // Re-use a previously created map\n currentLevel = currentValue;\n } else if (\n currentValue &&\n typeOrder(currentValue) === TypeOrder.ObjectValue\n ) {\n // Convert the existing Protobuf MapValue into a map\n currentValue = new Map<string, Overlay>(\n Object.entries(currentValue.mapValue!.fields || {})\n );\n currentLevel.set(currentSegment, currentValue);\n currentLevel = currentValue;\n } else {\n // Create an empty map to represent the current nesting level\n currentValue = new Map<string, Overlay>();\n currentLevel.set(currentSegment, currentValue);\n currentLevel = currentValue;\n }\n }\n\n currentLevel.set(path.lastSegment(), value);\n }\n\n /** Returns an ObjectValue with all mutations applied. */\n build(): ObjectValue {\n const mergedResult = this.applyOverlay(\n FieldPath.EMPTY_PATH,\n this.overlayMap\n );\n if (mergedResult != null) {\n return new ObjectValue(mergedResult);\n } else {\n return this.baseObject;\n }\n }\n\n /**\n * Applies any overlays from `currentOverlays` that exist at `currentPath`\n * and returns the merged data at `currentPath` (or null if there were no\n * changes).\n *\n * @param currentPath The path at the current nesting level. Can be set to\n * FieldValue.EMPTY_PATH to represent the root.\n * @param currentOverlays The overlays at the current nesting level in the\n * same format as `overlayMap`.\n * @return The merged data at `currentPath` or null if no modifications\n * were applied.\n */\n private applyOverlay(\n currentPath: FieldPath,\n currentOverlays: Map<string, Overlay>\n ): { mapValue: api.MapValue } | null {\n let modified = false;\n\n const existingValue = this.baseObject.field(currentPath);\n const resultAtPath = isMapValue(existingValue)\n ? // If there is already data at the current path, base our\n // modifications on top of the existing data.\n { ...existingValue.mapValue.fields }\n : {};\n\n currentOverlays.forEach((value, pathSegment) => {\n if (value instanceof Map) {\n const nested = this.applyOverlay(currentPath.child(pathSegment), value);\n if (nested != null) {\n resultAtPath[pathSegment] = nested;\n modified = true;\n }\n } else if (value !== null) {\n resultAtPath[pathSegment] = value;\n modified = true;\n } else if (resultAtPath.hasOwnProperty(pathSegment)) {\n delete resultAtPath[pathSegment];\n modified = true;\n }\n });\n\n return modified ? { mapValue: { fields: resultAtPath } } : null;\n }\n}\n\n/**\n * Returns a FieldMask built from all fields in a MapValue.\n */\nexport function extractFieldMask(value: api.MapValue): FieldMask {\n const fields: FieldPath[] = [];\n forEach(value!.fields || {}, (key, value) => {\n const currentPath = new FieldPath([key]);\n if (isMapValue(value)) {\n const nestedMask = extractFieldMask(value.mapValue!);\n const nestedFields = nestedMask.fields;\n if (nestedFields.length === 0) {\n // Preserve the empty map by adding it to the FieldMask.\n fields.push(currentPath);\n } else {\n // For nested and non-empty ObjectValues, add the FieldPath of the\n // leaf nodes.\n for (const nestedPath of nestedFields) {\n fields.push(currentPath.child(nestedPath));\n }\n }\n } else {\n // For nested and non-empty ObjectValues, add the FieldPath of the leaf\n // nodes.\n fields.push(currentPath);\n }\n });\n return new FieldMask(fields);\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { fail } from '../util/assert';\n\nimport { DocumentKey } from './document_key';\nimport { ObjectValue } from './object_value';\nimport { FieldPath } from './path';\nimport { valueCompare } from './values';\n\nexport interface DocumentOptions {\n hasLocalMutations?: boolean;\n hasCommittedMutations?: boolean;\n}\n\n/**\n * The result of a lookup for a given path may be an existing document or a\n * marker that this document does not exist at a given version.\n */\nexport abstract class MaybeDocument {\n constructor(readonly key: DocumentKey, readonly version: SnapshotVersion) {}\n\n /**\n * Whether this document had a local mutation applied that has not yet been\n * acknowledged by Watch.\n */\n abstract get hasPendingWrites(): boolean;\n\n abstract isEqual(other: MaybeDocument | null | undefined): boolean;\n\n abstract toString(): string;\n}\n\n/**\n * Represents a document in Firestore with a key, version, data and whether the\n * data has local mutations applied to it.\n */\nexport class Document extends MaybeDocument {\n readonly hasLocalMutations: boolean;\n readonly hasCommittedMutations: boolean;\n\n constructor(\n key: DocumentKey,\n version: SnapshotVersion,\n private readonly objectValue: ObjectValue,\n options: DocumentOptions\n ) {\n super(key, version);\n this.hasLocalMutations = !!options.hasLocalMutations;\n this.hasCommittedMutations = !!options.hasCommittedMutations;\n }\n\n field(path: FieldPath): api.Value | null {\n return this.objectValue.field(path);\n }\n\n data(): ObjectValue {\n return this.objectValue;\n }\n\n toProto(): { mapValue: api.MapValue } {\n return this.objectValue.proto;\n }\n\n isEqual(other: MaybeDocument | null | undefined): boolean {\n return (\n other instanceof Document &&\n this.key.isEqual(other.key) &&\n this.version.isEqual(other.version) &&\n this.hasLocalMutations === other.hasLocalMutations &&\n this.hasCommittedMutations === other.hasCommittedMutations &&\n this.objectValue.isEqual(other.objectValue)\n );\n }\n\n toString(): string {\n return (\n `Document(${this.key}, ${\n this.version\n }, ${this.objectValue.toString()}, ` +\n `{hasLocalMutations: ${this.hasLocalMutations}}), ` +\n `{hasCommittedMutations: ${this.hasCommittedMutations}})`\n );\n }\n\n get hasPendingWrites(): boolean {\n return this.hasLocalMutations || this.hasCommittedMutations;\n }\n}\n\n/**\n * Compares the value for field `field` in the provided documents. Throws if\n * the field does not exist in both documents.\n */\nexport function compareDocumentsByField(\n field: FieldPath,\n d1: Document,\n d2: Document\n): number {\n const v1 = d1.field(field);\n const v2 = d2.field(field);\n if (v1 !== null && v2 !== null) {\n return valueCompare(v1, v2);\n } else {\n return fail(\"Trying to compare documents on fields that don't exist\");\n }\n}\n\n/**\n * A class representing a deleted document.\n * Version is set to 0 if we don't point to any specific time, otherwise it\n * denotes time we know it didn't exist at.\n */\nexport class NoDocument extends MaybeDocument {\n readonly hasCommittedMutations: boolean;\n\n constructor(\n key: DocumentKey,\n version: SnapshotVersion,\n options?: DocumentOptions\n ) {\n super(key, version);\n this.hasCommittedMutations = !!(options && options.hasCommittedMutations);\n }\n\n toString(): string {\n return `NoDocument(${this.key}, ${this.version})`;\n }\n\n get hasPendingWrites(): boolean {\n return this.hasCommittedMutations;\n }\n\n isEqual(other: MaybeDocument | null | undefined): boolean {\n return (\n other instanceof NoDocument &&\n other.hasCommittedMutations === this.hasCommittedMutations &&\n other.version.isEqual(this.version) &&\n other.key.isEqual(this.key)\n );\n }\n}\n\n/**\n * A class representing an existing document whose data is unknown (e.g. a\n * document that was updated without a known base document).\n */\nexport class UnknownDocument extends MaybeDocument {\n toString(): string {\n return `UnknownDocument(${this.key}, ${this.version})`;\n }\n\n get hasPendingWrites(): boolean {\n return true;\n }\n\n isEqual(other: MaybeDocument | null | undefined): boolean {\n return (\n other instanceof UnknownDocument &&\n other.version.isEqual(this.version) &&\n other.key.isEqual(this.key)\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nexport class ExistenceFilter {\n // TODO(b/33078163): just use simplest form of existence filter for now\n constructor(public count: number) {}\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { fail } from '../util/assert';\nimport { Code } from '../util/error';\nimport { logError } from '../util/log';\n\n/**\n * Error Codes describing the different ways GRPC can fail. These are copied\n * directly from GRPC's sources here:\n *\n * https://github.com/grpc/grpc/blob/bceec94ea4fc5f0085d81235d8e1c06798dc341a/include/grpc%2B%2B/impl/codegen/status_code_enum.h\n *\n * Important! The names of these identifiers matter because the string forms\n * are used for reverse lookups from the webchannel stream. Do NOT change the\n * names of these identifiers or change this into a const enum.\n */\nenum RpcCode {\n OK = 0,\n CANCELLED = 1,\n UNKNOWN = 2,\n INVALID_ARGUMENT = 3,\n DEADLINE_EXCEEDED = 4,\n NOT_FOUND = 5,\n ALREADY_EXISTS = 6,\n PERMISSION_DENIED = 7,\n UNAUTHENTICATED = 16,\n RESOURCE_EXHAUSTED = 8,\n FAILED_PRECONDITION = 9,\n ABORTED = 10,\n OUT_OF_RANGE = 11,\n UNIMPLEMENTED = 12,\n INTERNAL = 13,\n UNAVAILABLE = 14,\n DATA_LOSS = 15\n}\n\n/**\n * Determines whether an error code represents a permanent error when received\n * in response to a non-write operation.\n *\n * See isPermanentWriteError for classifying write errors.\n */\nexport function isPermanentError(code: Code): boolean {\n switch (code) {\n case Code.OK:\n return fail('Treated status OK as error');\n case Code.CANCELLED:\n case Code.UNKNOWN:\n case Code.DEADLINE_EXCEEDED:\n case Code.RESOURCE_EXHAUSTED:\n case Code.INTERNAL:\n case Code.UNAVAILABLE:\n // Unauthenticated means something went wrong with our token and we need\n // to retry with new credentials which will happen automatically.\n case Code.UNAUTHENTICATED:\n return false;\n case Code.INVALID_ARGUMENT:\n case Code.NOT_FOUND:\n case Code.ALREADY_EXISTS:\n case Code.PERMISSION_DENIED:\n case Code.FAILED_PRECONDITION:\n // Aborted might be retried in some scenarios, but that is dependant on\n // the context and should handled individually by the calling code.\n // See https://cloud.google.com/apis/design/errors.\n case Code.ABORTED:\n case Code.OUT_OF_RANGE:\n case Code.UNIMPLEMENTED:\n case Code.DATA_LOSS:\n return true;\n default:\n return fail('Unknown status code: ' + code);\n }\n}\n\n/**\n * Determines whether an error code represents a permanent error when received\n * in response to a write operation.\n *\n * Write operations must be handled specially because as of b/119437764, ABORTED\n * errors on the write stream should be retried too (even though ABORTED errors\n * are not generally retryable).\n *\n * Note that during the initial handshake on the write stream an ABORTED error\n * signals that we should discard our stream token (i.e. it is permanent). This\n * means a handshake error should be classified with isPermanentError, above.\n */\nexport function isPermanentWriteError(code: Code): boolean {\n return isPermanentError(code) && code !== Code.ABORTED;\n}\n\n/**\n * Maps an error Code from a GRPC status identifier like 'NOT_FOUND'.\n *\n * @returns The Code equivalent to the given status string or undefined if\n * there is no match.\n */\nexport function mapCodeFromRpcStatus(status: string): Code | undefined {\n // lookup by string\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const code: RpcCode = RpcCode[status as any] as any;\n if (code === undefined) {\n return undefined;\n }\n\n return mapCodeFromRpcCode(code);\n}\n\n/**\n * Maps an error Code from GRPC status code number, like 0, 1, or 14. These\n * are not the same as HTTP status codes.\n *\n * @returns The Code equivalent to the given GRPC status code. Fails if there\n * is no match.\n */\nexport function mapCodeFromRpcCode(code: number | undefined): Code {\n if (code === undefined) {\n // This shouldn't normally happen, but in certain error cases (like trying\n // to send invalid proto messages) we may get an error with no GRPC code.\n logError('GRPC error has no .code');\n return Code.UNKNOWN;\n }\n\n switch (code) {\n case RpcCode.OK:\n return Code.OK;\n case RpcCode.CANCELLED:\n return Code.CANCELLED;\n case RpcCode.UNKNOWN:\n return Code.UNKNOWN;\n case RpcCode.DEADLINE_EXCEEDED:\n return Code.DEADLINE_EXCEEDED;\n case RpcCode.RESOURCE_EXHAUSTED:\n return Code.RESOURCE_EXHAUSTED;\n case RpcCode.INTERNAL:\n return Code.INTERNAL;\n case RpcCode.UNAVAILABLE:\n return Code.UNAVAILABLE;\n case RpcCode.UNAUTHENTICATED:\n return Code.UNAUTHENTICATED;\n case RpcCode.INVALID_ARGUMENT:\n return Code.INVALID_ARGUMENT;\n case RpcCode.NOT_FOUND:\n return Code.NOT_FOUND;\n case RpcCode.ALREADY_EXISTS:\n return Code.ALREADY_EXISTS;\n case RpcCode.PERMISSION_DENIED:\n return Code.PERMISSION_DENIED;\n case RpcCode.FAILED_PRECONDITION:\n return Code.FAILED_PRECONDITION;\n case RpcCode.ABORTED:\n return Code.ABORTED;\n case RpcCode.OUT_OF_RANGE:\n return Code.OUT_OF_RANGE;\n case RpcCode.UNIMPLEMENTED:\n return Code.UNIMPLEMENTED;\n case RpcCode.DATA_LOSS:\n return Code.DATA_LOSS;\n default:\n return fail('Unknown status code: ' + code);\n }\n}\n\n/**\n * Maps an RPC code from a Code. This is the reverse operation from\n * mapCodeFromRpcCode and should really only be used in tests.\n */\nexport function mapRpcCodeFromCode(code: Code | undefined): number {\n if (code === undefined) {\n return RpcCode.OK;\n }\n\n switch (code) {\n case Code.OK:\n return RpcCode.OK;\n case Code.CANCELLED:\n return RpcCode.CANCELLED;\n case Code.UNKNOWN:\n return RpcCode.UNKNOWN;\n case Code.DEADLINE_EXCEEDED:\n return RpcCode.DEADLINE_EXCEEDED;\n case Code.RESOURCE_EXHAUSTED:\n return RpcCode.RESOURCE_EXHAUSTED;\n case Code.INTERNAL:\n return RpcCode.INTERNAL;\n case Code.UNAVAILABLE:\n return RpcCode.UNAVAILABLE;\n case Code.UNAUTHENTICATED:\n return RpcCode.UNAUTHENTICATED;\n case Code.INVALID_ARGUMENT:\n return RpcCode.INVALID_ARGUMENT;\n case Code.NOT_FOUND:\n return RpcCode.NOT_FOUND;\n case Code.ALREADY_EXISTS:\n return RpcCode.ALREADY_EXISTS;\n case Code.PERMISSION_DENIED:\n return RpcCode.PERMISSION_DENIED;\n case Code.FAILED_PRECONDITION:\n return RpcCode.FAILED_PRECONDITION;\n case Code.ABORTED:\n return RpcCode.ABORTED;\n case Code.OUT_OF_RANGE:\n return RpcCode.OUT_OF_RANGE;\n case Code.UNIMPLEMENTED:\n return RpcCode.UNIMPLEMENTED;\n case Code.DATA_LOSS:\n return RpcCode.DATA_LOSS;\n default:\n return fail('Unknown status code: ' + code);\n }\n}\n\n/**\n * Converts an HTTP Status Code to the equivalent error code.\n *\n * @param status An HTTP Status Code, like 200, 404, 503, etc.\n * @returns The equivalent Code. Unknown status codes are mapped to\n * Code.UNKNOWN.\n */\nexport function mapCodeFromHttpStatus(status: number): Code {\n // The canonical error codes for Google APIs [1] specify mapping onto HTTP\n // status codes but the mapping is not bijective. In each case of ambiguity\n // this function chooses a primary error.\n //\n // [1]\n // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\n switch (status) {\n case 200: // OK\n return Code.OK;\n\n case 400: // Bad Request\n return Code.INVALID_ARGUMENT;\n // Other possibilities based on the forward mapping\n // return Code.FAILED_PRECONDITION;\n // return Code.OUT_OF_RANGE;\n\n case 401: // Unauthorized\n return Code.UNAUTHENTICATED;\n\n case 403: // Forbidden\n return Code.PERMISSION_DENIED;\n\n case 404: // Not Found\n return Code.NOT_FOUND;\n\n case 409: // Conflict\n return Code.ABORTED;\n // Other possibilities:\n // return Code.ALREADY_EXISTS;\n\n case 416: // Range Not Satisfiable\n return Code.OUT_OF_RANGE;\n\n case 429: // Too Many Requests\n return Code.RESOURCE_EXHAUSTED;\n\n case 499: // Client Closed Request\n return Code.CANCELLED;\n\n case 500: // Internal Server Error\n return Code.UNKNOWN;\n // Other possibilities:\n // return Code.INTERNAL;\n // return Code.DATA_LOSS;\n\n case 501: // Unimplemented\n return Code.UNIMPLEMENTED;\n\n case 503: // Service Unavailable\n return Code.UNAVAILABLE;\n\n case 504: // Gateway Timeout\n return Code.DEADLINE_EXCEEDED;\n\n default:\n if (status >= 200 && status < 300) {\n return Code.OK;\n }\n if (status >= 400 && status < 500) {\n return Code.FAILED_PRECONDITION;\n }\n if (status >= 500 && status < 600) {\n return Code.INTERNAL;\n }\n return Code.UNKNOWN;\n }\n}\n\n/**\n * Converts an HTTP response's error status to the equivalent error code.\n *\n * @param status An HTTP error response status (\"FAILED_PRECONDITION\",\n * \"UNKNOWN\", etc.)\n * @returns The equivalent Code. Non-matching responses are mapped to\n * Code.UNKNOWN.\n */\nexport function mapCodeFromHttpResponseErrorStatus(status: string): Code {\n const serverError = status.toLowerCase().replace('_', '-');\n return Object.values(Code).indexOf(serverError as Code) >= 0\n ? (serverError as Code)\n : Code.UNKNOWN;\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { DocumentKey } from '../model/document_key';\nimport { ResourcePath } from '../model/path';\nimport { isNullOrUndefined } from '../util/types';\nimport { Bound, Filter, OrderBy } from './query';\n\n/**\n * A Target represents the WatchTarget representation of a Query, which is used\n * by the LocalStore and the RemoteStore to keep track of and to execute\n * backend queries. While a Query can represent multiple Targets, each Targets\n * maps to a single WatchTarget in RemoteStore and a single TargetData entry\n * in persistence.\n */\nexport class Target {\n private memoizedCanonicalId: string | null = null;\n\n /**\n * Initializes a Target with a path and optional additional query constraints.\n * Path must currently be empty if this is a collection group query.\n *\n * NOTE: you should always construct `Target` from `Query.toTarget` instead of\n * using this constructor, because `Query` provides an implicit `orderBy`\n * property.\n */\n constructor(\n readonly path: ResourcePath,\n readonly collectionGroup: string | null = null,\n readonly orderBy: OrderBy[] = [],\n readonly filters: Filter[] = [],\n readonly limit: number | null = null,\n readonly startAt: Bound | null = null,\n readonly endAt: Bound | null = null\n ) {}\n\n canonicalId(): string {\n if (this.memoizedCanonicalId === null) {\n let canonicalId = this.path.canonicalString();\n if (this.collectionGroup !== null) {\n canonicalId += '|cg:' + this.collectionGroup;\n }\n canonicalId += '|f:';\n canonicalId += this.filters.map(f => f.canonicalId()).join(',');\n canonicalId += '|ob:';\n canonicalId += this.orderBy.map(o => o.canonicalId()).join(',');\n\n if (!isNullOrUndefined(this.limit)) {\n canonicalId += '|l:';\n canonicalId += this.limit!;\n }\n if (this.startAt) {\n canonicalId += '|lb:';\n canonicalId += this.startAt.canonicalId();\n }\n if (this.endAt) {\n canonicalId += '|ub:';\n canonicalId += this.endAt.canonicalId();\n }\n this.memoizedCanonicalId = canonicalId;\n }\n return this.memoizedCanonicalId;\n }\n\n toString(): string {\n let str = this.path.canonicalString();\n if (this.collectionGroup !== null) {\n str += ' collectionGroup=' + this.collectionGroup;\n }\n if (this.filters.length > 0) {\n str += `, filters: [${this.filters.join(', ')}]`;\n }\n if (!isNullOrUndefined(this.limit)) {\n str += ', limit: ' + this.limit;\n }\n if (this.orderBy.length > 0) {\n str += `, orderBy: [${this.orderBy.join(', ')}]`;\n }\n if (this.startAt) {\n str += ', startAt: ' + this.startAt.canonicalId();\n }\n if (this.endAt) {\n str += ', endAt: ' + this.endAt.canonicalId();\n }\n return `Target(${str})`;\n }\n\n isEqual(other: Target): boolean {\n if (this.limit !== other.limit) {\n return false;\n }\n\n if (this.orderBy.length !== other.orderBy.length) {\n return false;\n }\n\n for (let i = 0; i < this.orderBy.length; i++) {\n if (!this.orderBy[i].isEqual(other.orderBy[i])) {\n return false;\n }\n }\n\n if (this.filters.length !== other.filters.length) {\n return false;\n }\n\n for (let i = 0; i < this.filters.length; i++) {\n if (!this.filters[i].isEqual(other.filters[i])) {\n return false;\n }\n }\n\n if (this.collectionGroup !== other.collectionGroup) {\n return false;\n }\n\n if (!this.path.isEqual(other.path)) {\n return false;\n }\n\n if (\n this.startAt !== null\n ? !this.startAt.isEqual(other.startAt)\n : other.startAt !== null\n ) {\n return false;\n }\n\n return this.endAt !== null\n ? this.endAt.isEqual(other.endAt)\n : other.endAt === null;\n }\n\n isDocumentQuery(): boolean {\n return (\n DocumentKey.isDocumentKey(this.path) &&\n this.collectionGroup === null &&\n this.filters.length === 0\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as api from '../protos/firestore_proto_api';\n\nimport { compareDocumentsByField, Document } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport {\n canonicalId,\n valueCompare,\n arrayValueContains,\n valueEquals,\n isArray,\n isNanValue,\n isNullValue,\n isReferenceValue,\n typeOrder\n} from '../model/values';\nimport { FieldPath, ResourcePath } from '../model/path';\nimport { debugAssert, fail } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { isNullOrUndefined } from '../util/types';\nimport { Target } from './target';\n\nexport const enum LimitType {\n First = 'F',\n Last = 'L'\n}\n\n/**\n * Query encapsulates all the query attributes we support in the SDK. It can\n * be run against the LocalStore, as well as be converted to a `Target` to\n * query the RemoteStore results.\n */\nexport class Query {\n static atPath(path: ResourcePath): Query {\n return new Query(path);\n }\n\n private memoizedOrderBy: OrderBy[] | null = null;\n\n // The corresponding `Target` of this `Query` instance.\n private memoizedTarget: Target | null = null;\n\n /**\n * Initializes a Query with a path and optional additional query constraints.\n * Path must currently be empty if this is a collection group query.\n */\n constructor(\n readonly path: ResourcePath,\n readonly collectionGroup: string | null = null,\n readonly explicitOrderBy: OrderBy[] = [],\n readonly filters: Filter[] = [],\n readonly limit: number | null = null,\n readonly limitType: LimitType = LimitType.First,\n readonly startAt: Bound | null = null,\n readonly endAt: Bound | null = null\n ) {\n if (this.startAt) {\n this.assertValidBound(this.startAt);\n }\n if (this.endAt) {\n this.assertValidBound(this.endAt);\n }\n }\n\n get orderBy(): OrderBy[] {\n if (this.memoizedOrderBy === null) {\n this.memoizedOrderBy = [];\n\n const inequalityField = this.getInequalityFilterField();\n const firstOrderByField = this.getFirstOrderByField();\n if (inequalityField !== null && firstOrderByField === null) {\n // In order to implicitly add key ordering, we must also add the\n // inequality filter field for it to be a valid query.\n // Note that the default inequality field and key ordering is ascending.\n if (!inequalityField.isKeyField()) {\n this.memoizedOrderBy.push(new OrderBy(inequalityField));\n }\n this.memoizedOrderBy.push(\n new OrderBy(FieldPath.keyField(), Direction.ASCENDING)\n );\n } else {\n debugAssert(\n inequalityField === null ||\n (firstOrderByField !== null &&\n inequalityField.isEqual(firstOrderByField)),\n 'First orderBy should match inequality field.'\n );\n let foundKeyOrdering = false;\n for (const orderBy of this.explicitOrderBy) {\n this.memoizedOrderBy.push(orderBy);\n if (orderBy.field.isKeyField()) {\n foundKeyOrdering = true;\n }\n }\n if (!foundKeyOrdering) {\n // The order of the implicit key ordering always matches the last\n // explicit order by\n const lastDirection =\n this.explicitOrderBy.length > 0\n ? this.explicitOrderBy[this.explicitOrderBy.length - 1].dir\n : Direction.ASCENDING;\n this.memoizedOrderBy.push(\n new OrderBy(FieldPath.keyField(), lastDirection)\n );\n }\n }\n }\n return this.memoizedOrderBy;\n }\n\n addFilter(filter: Filter): Query {\n debugAssert(\n this.getInequalityFilterField() == null ||\n !(filter instanceof FieldFilter) ||\n !filter.isInequality() ||\n filter.field.isEqual(this.getInequalityFilterField()!),\n 'Query must only have one inequality field.'\n );\n\n debugAssert(\n !this.isDocumentQuery(),\n 'No filtering allowed for document query'\n );\n\n const newFilters = this.filters.concat([filter]);\n return new Query(\n this.path,\n this.collectionGroup,\n this.explicitOrderBy.slice(),\n newFilters,\n this.limit,\n this.limitType,\n this.startAt,\n this.endAt\n );\n }\n\n addOrderBy(orderBy: OrderBy): Query {\n debugAssert(\n !this.startAt && !this.endAt,\n 'Bounds must be set after orderBy'\n );\n // TODO(dimond): validate that orderBy does not list the same key twice.\n const newOrderBy = this.explicitOrderBy.concat([orderBy]);\n return new Query(\n this.path,\n this.collectionGroup,\n newOrderBy,\n this.filters.slice(),\n this.limit,\n this.limitType,\n this.startAt,\n this.endAt\n );\n }\n\n withLimitToFirst(limit: number | null): Query {\n return new Query(\n this.path,\n this.collectionGroup,\n this.explicitOrderBy.slice(),\n this.filters.slice(),\n limit,\n LimitType.First,\n this.startAt,\n this.endAt\n );\n }\n\n withLimitToLast(limit: number | null): Query {\n return new Query(\n this.path,\n this.collectionGroup,\n this.explicitOrderBy.slice(),\n this.filters.slice(),\n limit,\n LimitType.Last,\n this.startAt,\n this.endAt\n );\n }\n\n withStartAt(bound: Bound): Query {\n return new Query(\n this.path,\n this.collectionGroup,\n this.explicitOrderBy.slice(),\n this.filters.slice(),\n this.limit,\n this.limitType,\n bound,\n this.endAt\n );\n }\n\n withEndAt(bound: Bound): Query {\n return new Query(\n this.path,\n this.collectionGroup,\n this.explicitOrderBy.slice(),\n this.filters.slice(),\n this.limit,\n this.limitType,\n this.startAt,\n bound\n );\n }\n\n /**\n * Helper to convert a collection group query into a collection query at a\n * specific path. This is used when executing collection group queries, since\n * we have to split the query into a set of collection queries at multiple\n * paths.\n */\n asCollectionQueryAtPath(path: ResourcePath): Query {\n return new Query(\n path,\n /*collectionGroup=*/ null,\n this.explicitOrderBy.slice(),\n this.filters.slice(),\n this.limit,\n this.limitType,\n this.startAt,\n this.endAt\n );\n }\n\n /**\n * Returns true if this query does not specify any query constraints that\n * could remove results.\n */\n matchesAllDocuments(): boolean {\n return (\n this.filters.length === 0 &&\n this.limit === null &&\n this.startAt == null &&\n this.endAt == null &&\n (this.explicitOrderBy.length === 0 ||\n (this.explicitOrderBy.length === 1 &&\n this.explicitOrderBy[0].field.isKeyField()))\n );\n }\n\n // TODO(b/29183165): This is used to get a unique string from a query to, for\n // example, use as a dictionary key, but the implementation is subject to\n // collisions. Make it collision-free.\n canonicalId(): string {\n return `${this.toTarget().canonicalId()}|lt:${this.limitType}`;\n }\n\n toString(): string {\n return `Query(target=${this.toTarget().toString()}; limitType=${\n this.limitType\n })`;\n }\n\n isEqual(other: Query): boolean {\n return (\n this.toTarget().isEqual(other.toTarget()) &&\n this.limitType === other.limitType\n );\n }\n\n docComparator(d1: Document, d2: Document): number {\n let comparedOnKeyField = false;\n for (const orderBy of this.orderBy) {\n const comp = orderBy.compare(d1, d2);\n if (comp !== 0) {\n return comp;\n }\n comparedOnKeyField = comparedOnKeyField || orderBy.field.isKeyField();\n }\n // Assert that we actually compared by key\n debugAssert(\n comparedOnKeyField,\n \"orderBy used that doesn't compare on key field\"\n );\n return 0;\n }\n\n matches(doc: Document): boolean {\n return (\n this.matchesPathAndCollectionGroup(doc) &&\n this.matchesOrderBy(doc) &&\n this.matchesFilters(doc) &&\n this.matchesBounds(doc)\n );\n }\n\n hasLimitToFirst(): boolean {\n return !isNullOrUndefined(this.limit) && this.limitType === LimitType.First;\n }\n\n hasLimitToLast(): boolean {\n return !isNullOrUndefined(this.limit) && this.limitType === LimitType.Last;\n }\n\n getFirstOrderByField(): FieldPath | null {\n return this.explicitOrderBy.length > 0\n ? this.explicitOrderBy[0].field\n : null;\n }\n\n getInequalityFilterField(): FieldPath | null {\n for (const filter of this.filters) {\n if (filter instanceof FieldFilter && filter.isInequality()) {\n return filter.field;\n }\n }\n return null;\n }\n\n // Checks if any of the provided Operators are included in the query and\n // returns the first one that is, or null if none are.\n findFilterOperator(operators: Operator[]): Operator | null {\n for (const filter of this.filters) {\n if (filter instanceof FieldFilter) {\n if (operators.indexOf(filter.op) >= 0) {\n return filter.op;\n }\n }\n }\n return null;\n }\n\n isDocumentQuery(): boolean {\n return this.toTarget().isDocumentQuery();\n }\n\n isCollectionGroupQuery(): boolean {\n return this.collectionGroup !== null;\n }\n\n /**\n * Converts this `Query` instance to it's corresponding `Target`\n * representation.\n */\n toTarget(): Target {\n if (!this.memoizedTarget) {\n if (this.limitType === LimitType.First) {\n this.memoizedTarget = new Target(\n this.path,\n this.collectionGroup,\n this.orderBy,\n this.filters,\n this.limit,\n this.startAt,\n this.endAt\n );\n } else {\n // Flip the orderBy directions since we want the last results\n const orderBys = [] as OrderBy[];\n for (const orderBy of this.orderBy) {\n const dir =\n orderBy.dir === Direction.DESCENDING\n ? Direction.ASCENDING\n : Direction.DESCENDING;\n orderBys.push(new OrderBy(orderBy.field, dir));\n }\n\n // We need to swap the cursors to match the now-flipped query ordering.\n const startAt = this.endAt\n ? new Bound(this.endAt.position, !this.endAt.before)\n : null;\n const endAt = this.startAt\n ? new Bound(this.startAt.position, !this.startAt.before)\n : null;\n\n // Now return as a LimitType.First query.\n this.memoizedTarget = new Target(\n this.path,\n this.collectionGroup,\n orderBys,\n this.filters,\n this.limit,\n startAt,\n endAt\n );\n }\n }\n return this.memoizedTarget!;\n }\n\n private matchesPathAndCollectionGroup(doc: Document): boolean {\n const docPath = doc.key.path;\n if (this.collectionGroup !== null) {\n // NOTE: this.path is currently always empty since we don't expose Collection\n // Group queries rooted at a document path yet.\n return (\n doc.key.hasCollectionId(this.collectionGroup) &&\n this.path.isPrefixOf(docPath)\n );\n } else if (DocumentKey.isDocumentKey(this.path)) {\n // exact match for document queries\n return this.path.isEqual(docPath);\n } else {\n // shallow ancestor queries by default\n return this.path.isImmediateParentOf(docPath);\n }\n }\n\n /**\n * A document must have a value for every ordering clause in order to show up\n * in the results.\n */\n private matchesOrderBy(doc: Document): boolean {\n for (const orderBy of this.explicitOrderBy) {\n // order by key always matches\n if (!orderBy.field.isKeyField() && doc.field(orderBy.field) === null) {\n return false;\n }\n }\n return true;\n }\n\n private matchesFilters(doc: Document): boolean {\n for (const filter of this.filters) {\n if (!filter.matches(doc)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Makes sure a document is within the bounds, if provided.\n */\n private matchesBounds(doc: Document): boolean {\n if (this.startAt && !this.startAt.sortsBeforeDocument(this.orderBy, doc)) {\n return false;\n }\n if (this.endAt && this.endAt.sortsBeforeDocument(this.orderBy, doc)) {\n return false;\n }\n return true;\n }\n\n private assertValidBound(bound: Bound): void {\n debugAssert(\n bound.position.length <= this.orderBy.length,\n 'Bound is longer than orderBy'\n );\n }\n}\n\nexport abstract class Filter {\n abstract matches(doc: Document): boolean;\n abstract canonicalId(): string;\n abstract isEqual(filter: Filter): boolean;\n}\n\nexport const enum Operator {\n LESS_THAN = '<',\n LESS_THAN_OR_EQUAL = '<=',\n EQUAL = '==',\n GREATER_THAN = '>',\n GREATER_THAN_OR_EQUAL = '>=',\n ARRAY_CONTAINS = 'array-contains',\n IN = 'in',\n ARRAY_CONTAINS_ANY = 'array-contains-any'\n}\n\nexport class FieldFilter extends Filter {\n protected constructor(\n public field: FieldPath,\n public op: Operator,\n public value: api.Value\n ) {\n super();\n }\n\n /**\n * Creates a filter based on the provided arguments.\n */\n static create(field: FieldPath, op: Operator, value: api.Value): FieldFilter {\n if (field.isKeyField()) {\n if (op === Operator.IN) {\n debugAssert(\n isArray(value),\n 'Comparing on key with IN, but filter value not an ArrayValue'\n );\n debugAssert(\n (value.arrayValue.values || []).every(elem => isReferenceValue(elem)),\n 'Comparing on key with IN, but an array value was not a RefValue'\n );\n return new KeyFieldInFilter(field, value);\n } else {\n debugAssert(\n isReferenceValue(value),\n 'Comparing on key, but filter value not a RefValue'\n );\n debugAssert(\n op !== Operator.ARRAY_CONTAINS && op !== Operator.ARRAY_CONTAINS_ANY,\n `'${op.toString()}' queries don't make sense on document keys.`\n );\n return new KeyFieldFilter(field, op, value);\n }\n } else if (isNullValue(value)) {\n if (op !== Operator.EQUAL) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. Null supports only equality comparisons.'\n );\n }\n return new FieldFilter(field, op, value);\n } else if (isNanValue(value)) {\n if (op !== Operator.EQUAL) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. NaN supports only equality comparisons.'\n );\n }\n return new FieldFilter(field, op, value);\n } else if (op === Operator.ARRAY_CONTAINS) {\n return new ArrayContainsFilter(field, value);\n } else if (op === Operator.IN) {\n debugAssert(\n isArray(value),\n 'IN filter has invalid value: ' + value.toString()\n );\n return new InFilter(field, value);\n } else if (op === Operator.ARRAY_CONTAINS_ANY) {\n debugAssert(\n isArray(value),\n 'ARRAY_CONTAINS_ANY filter has invalid value: ' + value.toString()\n );\n return new ArrayContainsAnyFilter(field, value);\n } else {\n return new FieldFilter(field, op, value);\n }\n }\n\n matches(doc: Document): boolean {\n const other = doc.field(this.field);\n\n // Only compare types with matching backend order (such as double and int).\n return (\n other !== null &&\n typeOrder(this.value) === typeOrder(other) &&\n this.matchesComparison(valueCompare(other, this.value))\n );\n }\n\n protected matchesComparison(comparison: number): boolean {\n switch (this.op) {\n case Operator.LESS_THAN:\n return comparison < 0;\n case Operator.LESS_THAN_OR_EQUAL:\n return comparison <= 0;\n case Operator.EQUAL:\n return comparison === 0;\n case Operator.GREATER_THAN:\n return comparison > 0;\n case Operator.GREATER_THAN_OR_EQUAL:\n return comparison >= 0;\n default:\n return fail('Unknown FieldFilter operator: ' + this.op);\n }\n }\n\n isInequality(): boolean {\n return (\n [\n Operator.LESS_THAN,\n Operator.LESS_THAN_OR_EQUAL,\n Operator.GREATER_THAN,\n Operator.GREATER_THAN_OR_EQUAL\n ].indexOf(this.op) >= 0\n );\n }\n\n canonicalId(): string {\n // TODO(b/29183165): Technically, this won't be unique if two values have\n // the same description, such as the int 3 and the string \"3\". So we should\n // add the types in here somehow, too.\n return (\n this.field.canonicalString() +\n this.op.toString() +\n canonicalId(this.value)\n );\n }\n\n isEqual(other: Filter): boolean {\n if (other instanceof FieldFilter) {\n return (\n this.op === other.op &&\n this.field.isEqual(other.field) &&\n valueEquals(this.value, other.value)\n );\n } else {\n return false;\n }\n }\n\n toString(): string {\n return `${this.field.canonicalString()} ${this.op} ${canonicalId(\n this.value\n )}`;\n }\n}\n\n/** Filter that matches on key fields (i.e. '__name__'). */\nexport class KeyFieldFilter extends FieldFilter {\n private readonly key: DocumentKey;\n\n constructor(field: FieldPath, op: Operator, value: api.Value) {\n super(field, op, value);\n debugAssert(\n isReferenceValue(value),\n 'KeyFieldFilter expects a ReferenceValue'\n );\n this.key = DocumentKey.fromName(value.referenceValue);\n }\n\n matches(doc: Document): boolean {\n const comparison = DocumentKey.comparator(doc.key, this.key);\n return this.matchesComparison(comparison);\n }\n}\n\n/** Filter that matches on key fields within an array. */\nexport class KeyFieldInFilter extends FieldFilter {\n private readonly keys: DocumentKey[];\n\n constructor(field: FieldPath, value: api.Value) {\n super(field, Operator.IN, value);\n debugAssert(isArray(value), 'KeyFieldInFilter expects an ArrayValue');\n this.keys = (value.arrayValue.values || []).map(v => {\n debugAssert(\n isReferenceValue(v),\n 'Comparing on key with IN, but an array value was not a ReferenceValue'\n );\n return DocumentKey.fromName(v.referenceValue);\n });\n }\n\n matches(doc: Document): boolean {\n return this.keys.some(key => key.isEqual(doc.key));\n }\n}\n\n/** A Filter that implements the array-contains operator. */\nexport class ArrayContainsFilter extends FieldFilter {\n constructor(field: FieldPath, value: api.Value) {\n super(field, Operator.ARRAY_CONTAINS, value);\n }\n\n matches(doc: Document): boolean {\n const other = doc.field(this.field);\n return isArray(other) && arrayValueContains(other.arrayValue, this.value);\n }\n}\n\n/** A Filter that implements the IN operator. */\nexport class InFilter extends FieldFilter {\n constructor(field: FieldPath, value: api.Value) {\n super(field, Operator.IN, value);\n debugAssert(isArray(value), 'InFilter expects an ArrayValue');\n }\n\n matches(doc: Document): boolean {\n const other = doc.field(this.field);\n return other !== null && arrayValueContains(this.value.arrayValue!, other);\n }\n}\n\n/** A Filter that implements the array-contains-any operator. */\nexport class ArrayContainsAnyFilter extends FieldFilter {\n constructor(field: FieldPath, value: api.Value) {\n super(field, Operator.ARRAY_CONTAINS_ANY, value);\n debugAssert(isArray(value), 'ArrayContainsAnyFilter expects an ArrayValue');\n }\n\n matches(doc: Document): boolean {\n const other = doc.field(this.field);\n if (!isArray(other) || !other.arrayValue.values) {\n return false;\n }\n return other.arrayValue.values.some(val =>\n arrayValueContains(this.value.arrayValue!, val)\n );\n }\n}\n\n/**\n * The direction of sorting in an order by.\n */\nexport const enum Direction {\n ASCENDING = 'asc',\n DESCENDING = 'desc'\n}\n\n/**\n * Represents a bound of a query.\n *\n * The bound is specified with the given components representing a position and\n * whether it's just before or just after the position (relative to whatever the\n * query order is).\n *\n * The position represents a logical index position for a query. It's a prefix\n * of values for the (potentially implicit) order by clauses of a query.\n *\n * Bound provides a function to determine whether a document comes before or\n * after a bound. This is influenced by whether the position is just before or\n * just after the provided values.\n */\nexport class Bound {\n constructor(readonly position: api.Value[], readonly before: boolean) {}\n\n canonicalId(): string {\n // TODO(b/29183165): Make this collision robust.\n return `${this.before ? 'b' : 'a'}:${this.position\n .map(p => canonicalId(p))\n .join(',')}`;\n }\n\n /**\n * Returns true if a document sorts before a bound using the provided sort\n * order.\n */\n sortsBeforeDocument(orderBy: OrderBy[], doc: Document): boolean {\n debugAssert(\n this.position.length <= orderBy.length,\n \"Bound has more components than query's orderBy\"\n );\n let comparison = 0;\n for (let i = 0; i < this.position.length; i++) {\n const orderByComponent = orderBy[i];\n const component = this.position[i];\n if (orderByComponent.field.isKeyField()) {\n debugAssert(\n isReferenceValue(component),\n 'Bound has a non-key value where the key path is being used.'\n );\n comparison = DocumentKey.comparator(\n DocumentKey.fromName(component.referenceValue),\n doc.key\n );\n } else {\n const docValue = doc.field(orderByComponent.field);\n debugAssert(\n docValue !== null,\n 'Field should exist since document matched the orderBy already.'\n );\n comparison = valueCompare(component, docValue);\n }\n if (orderByComponent.dir === Direction.DESCENDING) {\n comparison = comparison * -1;\n }\n if (comparison !== 0) {\n break;\n }\n }\n return this.before ? comparison <= 0 : comparison < 0;\n }\n\n isEqual(other: Bound | null): boolean {\n if (other === null) {\n return false;\n }\n if (\n this.before !== other.before ||\n this.position.length !== other.position.length\n ) {\n return false;\n }\n for (let i = 0; i < this.position.length; i++) {\n const thisPosition = this.position[i];\n const otherPosition = other.position[i];\n if (!valueEquals(thisPosition, otherPosition)) {\n return false;\n }\n }\n return true;\n }\n}\n\n/**\n * An ordering on a field, in some Direction. Direction defaults to ASCENDING.\n */\nexport class OrderBy {\n readonly dir: Direction;\n private readonly isKeyOrderBy: boolean;\n\n constructor(readonly field: FieldPath, dir?: Direction) {\n if (dir === undefined) {\n dir = Direction.ASCENDING;\n }\n this.dir = dir;\n this.isKeyOrderBy = field.isKeyField();\n }\n\n compare(d1: Document, d2: Document): number {\n const comparison = this.isKeyOrderBy\n ? DocumentKey.comparator(d1.key, d2.key)\n : compareDocumentsByField(this.field, d1, d2);\n switch (this.dir) {\n case Direction.ASCENDING:\n return comparison;\n case Direction.DESCENDING:\n return -1 * comparison;\n default:\n return fail('Unknown direction: ' + this.dir);\n }\n }\n\n canonicalId(): string {\n // TODO(b/29183165): Make this collision robust.\n return this.field.canonicalString() + this.dir.toString();\n }\n\n toString(): string {\n return `${this.field.canonicalString()} (${this.dir})`;\n }\n\n isEqual(other: OrderBy): boolean {\n return this.dir === other.dir && this.field.isEqual(other.field);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { Target } from '../core/target';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { ByteString } from '../util/byte_string';\n\n/** An enumeration of the different purposes we have for targets. */\nexport const enum TargetPurpose {\n /** A regular, normal query target. */\n Listen,\n\n /**\n * The query target was used to refill a query after an existence filter mismatch.\n */\n ExistenceFilterMismatch,\n\n /** The query target was used to resolve a limbo document. */\n LimboResolution\n}\n\n/**\n * An immutable set of metadata that the local store tracks for each target.\n */\nexport class TargetData {\n constructor(\n /** The target being listened to. */\n readonly target: Target,\n /**\n * The target ID to which the target corresponds; Assigned by the\n * LocalStore for user listens and by the SyncEngine for limbo watches.\n */\n readonly targetId: TargetId,\n /** The purpose of the target. */\n readonly purpose: TargetPurpose,\n /**\n * The sequence number of the last transaction during which this target data\n * was modified.\n */\n readonly sequenceNumber: ListenSequenceNumber,\n /** The latest snapshot version seen for this target. */\n readonly snapshotVersion: SnapshotVersion = SnapshotVersion.min(),\n /**\n * The maximum snapshot version at which the associated view\n * contained no limbo documents.\n */\n readonly lastLimboFreeSnapshotVersion: SnapshotVersion = SnapshotVersion.min(),\n /**\n * An opaque, server-assigned token that allows watching a target to be\n * resumed after disconnecting without retransmitting all the data that\n * matches the target. The resume token essentially identifies a point in\n * time from which the server should resume sending results.\n */\n readonly resumeToken: ByteString = ByteString.EMPTY_BYTE_STRING\n ) {}\n\n /** Creates a new target data instance with an updated sequence number. */\n withSequenceNumber(sequenceNumber: number): TargetData {\n return new TargetData(\n this.target,\n this.targetId,\n this.purpose,\n sequenceNumber,\n this.snapshotVersion,\n this.lastLimboFreeSnapshotVersion,\n this.resumeToken\n );\n }\n\n /**\n * Creates a new target data instance with an updated resume token and\n * snapshot version.\n */\n withResumeToken(\n resumeToken: ByteString,\n snapshotVersion: SnapshotVersion\n ): TargetData {\n return new TargetData(\n this.target,\n this.targetId,\n this.purpose,\n this.sequenceNumber,\n snapshotVersion,\n this.lastLimboFreeSnapshotVersion,\n resumeToken\n );\n }\n\n /**\n * Creates a new target data instance with an updated last limbo free\n * snapshot version number.\n */\n withLastLimboFreeSnapshotVersion(\n lastLimboFreeSnapshotVersion: SnapshotVersion\n ): TargetData {\n return new TargetData(\n this.target,\n this.targetId,\n this.purpose,\n this.sequenceNumber,\n this.snapshotVersion,\n lastLimboFreeSnapshotVersion,\n this.resumeToken\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert, fail } from './assert';\n\n/*\n * Implementation of an immutable SortedMap using a Left-leaning\n * Red-Black Tree, adapted from the implementation in Mugs\n * (http://mads379.github.com/mugs/) by Mads Hartmann Jensen\n * (mads379@gmail.com).\n *\n * Original paper on Left-leaning Red-Black Trees:\n * http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf\n *\n * Invariant 1: No red node has a red child\n * Invariant 2: Every leaf path has the same number of black nodes\n * Invariant 3: Only the left child can be red (left leaning)\n */\n\nexport type Comparator<K> = (key1: K, key2: K) => number;\n\nexport interface Entry<K, V> {\n key: K;\n value: V;\n}\n\n// An immutable sorted map implementation, based on a Left-leaning Red-Black\n// tree.\nexport class SortedMap<K, V> {\n // visible for testing\n root: LLRBNode<K, V> | LLRBEmptyNode<K, V>;\n\n constructor(\n public comparator: Comparator<K>,\n root?: LLRBNode<K, V> | LLRBEmptyNode<K, V>\n ) {\n this.root = root ? root : LLRBNode.EMPTY;\n }\n\n // Returns a copy of the map, with the specified key/value added or replaced.\n insert(key: K, value: V): SortedMap<K, V> {\n return new SortedMap<K, V>(\n this.comparator,\n this.root\n .insert(key, value, this.comparator)\n .copy(null, null, LLRBNode.BLACK, null, null)\n );\n }\n\n // Returns a copy of the map, with the specified key removed.\n remove(key: K): SortedMap<K, V> {\n return new SortedMap<K, V>(\n this.comparator,\n this.root\n .remove(key, this.comparator)\n .copy(null, null, LLRBNode.BLACK, null, null)\n );\n }\n\n // Returns the value of the node with the given key, or null.\n get(key: K): V | null {\n let node = this.root;\n while (!node.isEmpty()) {\n const cmp = this.comparator(key, node.key);\n if (cmp === 0) {\n return node.value;\n } else if (cmp < 0) {\n node = node.left;\n } else if (cmp > 0) {\n node = node.right;\n }\n }\n return null;\n }\n\n // Returns the index of the element in this sorted map, or -1 if it doesn't\n // exist.\n indexOf(key: K): number {\n // Number of nodes that were pruned when descending right\n let prunedNodes = 0;\n let node = this.root;\n while (!node.isEmpty()) {\n const cmp = this.comparator(key, node.key);\n if (cmp === 0) {\n return prunedNodes + node.left.size;\n } else if (cmp < 0) {\n node = node.left;\n } else {\n // Count all nodes left of the node plus the node itself\n prunedNodes += node.left.size + 1;\n node = node.right;\n }\n }\n // Node not found\n return -1;\n }\n\n isEmpty(): boolean {\n return this.root.isEmpty();\n }\n\n // Returns the total number of nodes in the map.\n get size(): number {\n return this.root.size;\n }\n\n // Returns the minimum key in the map.\n minKey(): K | null {\n return this.root.minKey();\n }\n\n // Returns the maximum key in the map.\n maxKey(): K | null {\n return this.root.maxKey();\n }\n\n // Traverses the map in key order and calls the specified action function\n // for each key/value pair. If action returns true, traversal is aborted.\n // Returns the first truthy value returned by action, or the last falsey\n // value returned by action.\n inorderTraversal<T>(action: (k: K, v: V) => T): T {\n return (this.root as LLRBNode<K, V>).inorderTraversal(action);\n }\n\n forEach(fn: (k: K, v: V) => void): void {\n this.inorderTraversal((k, v) => {\n fn(k, v);\n return false;\n });\n }\n\n toString(): string {\n const descriptions: string[] = [];\n this.inorderTraversal((k, v) => {\n descriptions.push(`${k}:${v}`);\n return false;\n });\n return `{${descriptions.join(', ')}}`;\n }\n\n // Traverses the map in reverse key order and calls the specified action\n // function for each key/value pair. If action returns true, traversal is\n // aborted.\n // Returns the first truthy value returned by action, or the last falsey\n // value returned by action.\n reverseTraversal<T>(action: (k: K, v: V) => T): T {\n return (this.root as LLRBNode<K, V>).reverseTraversal(action);\n }\n\n // Returns an iterator over the SortedMap.\n getIterator(): SortedMapIterator<K, V> {\n return new SortedMapIterator<K, V>(this.root, null, this.comparator, false);\n }\n\n getIteratorFrom(key: K): SortedMapIterator<K, V> {\n return new SortedMapIterator<K, V>(this.root, key, this.comparator, false);\n }\n\n getReverseIterator(): SortedMapIterator<K, V> {\n return new SortedMapIterator<K, V>(this.root, null, this.comparator, true);\n }\n\n getReverseIteratorFrom(key: K): SortedMapIterator<K, V> {\n return new SortedMapIterator<K, V>(this.root, key, this.comparator, true);\n }\n} // end SortedMap\n\n// An iterator over an LLRBNode.\nexport class SortedMapIterator<K, V> {\n private isReverse: boolean;\n private nodeStack: Array<LLRBNode<K, V> | LLRBEmptyNode<K, V>>;\n\n constructor(\n node: LLRBNode<K, V> | LLRBEmptyNode<K, V>,\n startKey: K | null,\n comparator: Comparator<K>,\n isReverse: boolean\n ) {\n this.isReverse = isReverse;\n this.nodeStack = [];\n\n let cmp = 1;\n while (!node.isEmpty()) {\n cmp = startKey ? comparator(node.key, startKey) : 1;\n // flip the comparison if we're going in reverse\n if (isReverse) {\n cmp *= -1;\n }\n\n if (cmp < 0) {\n // This node is less than our start key. ignore it\n if (this.isReverse) {\n node = node.left;\n } else {\n node = node.right;\n }\n } else if (cmp === 0) {\n // This node is exactly equal to our start key. Push it on the stack,\n // but stop iterating;\n this.nodeStack.push(node);\n break;\n } else {\n // This node is greater than our start key, add it to the stack and move\n // to the next one\n this.nodeStack.push(node);\n if (this.isReverse) {\n node = node.right;\n } else {\n node = node.left;\n }\n }\n }\n }\n\n getNext(): Entry<K, V> {\n debugAssert(\n this.nodeStack.length > 0,\n 'getNext() called on iterator when hasNext() is false.'\n );\n\n let node = this.nodeStack.pop()!;\n const result = { key: node.key, value: node.value };\n\n if (this.isReverse) {\n node = node.left;\n while (!node.isEmpty()) {\n this.nodeStack.push(node);\n node = node.right;\n }\n } else {\n node = node.right;\n while (!node.isEmpty()) {\n this.nodeStack.push(node);\n node = node.left;\n }\n }\n\n return result;\n }\n\n hasNext(): boolean {\n return this.nodeStack.length > 0;\n }\n\n peek(): Entry<K, V> | null {\n if (this.nodeStack.length === 0) {\n return null;\n }\n\n const node = this.nodeStack[this.nodeStack.length - 1];\n return { key: node.key, value: node.value };\n }\n} // end SortedMapIterator\n\n// Represents a node in a Left-leaning Red-Black tree.\nexport class LLRBNode<K, V> {\n readonly color: boolean;\n readonly left: LLRBNode<K, V> | LLRBEmptyNode<K, V>;\n readonly right: LLRBNode<K, V> | LLRBEmptyNode<K, V>;\n readonly size: number;\n\n // Empty node is shared between all LLRB trees.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n static EMPTY: LLRBEmptyNode<any, any> = null as any;\n\n static RED = true;\n static BLACK = false;\n\n constructor(\n public key: K,\n public value: V,\n color?: boolean,\n left?: LLRBNode<K, V> | LLRBEmptyNode<K, V>,\n right?: LLRBNode<K, V> | LLRBEmptyNode<K, V>\n ) {\n this.color = color != null ? color : LLRBNode.RED;\n this.left = left != null ? left : LLRBNode.EMPTY;\n this.right = right != null ? right : LLRBNode.EMPTY;\n this.size = this.left.size + 1 + this.right.size;\n }\n\n // Returns a copy of the current node, optionally replacing pieces of it.\n copy(\n key: K | null,\n value: V | null,\n color: boolean | null,\n left: LLRBNode<K, V> | LLRBEmptyNode<K, V> | null,\n right: LLRBNode<K, V> | LLRBEmptyNode<K, V> | null\n ): LLRBNode<K, V> {\n return new LLRBNode<K, V>(\n key != null ? key : this.key,\n value != null ? value : this.value,\n color != null ? color : this.color,\n left != null ? left : this.left,\n right != null ? right : this.right\n );\n }\n\n isEmpty(): boolean {\n return false;\n }\n\n // Traverses the tree in key order and calls the specified action function\n // for each node. If action returns true, traversal is aborted.\n // Returns the first truthy value returned by action, or the last falsey\n // value returned by action.\n inorderTraversal<T>(action: (k: K, v: V) => T): T {\n return (\n (this.left as LLRBNode<K, V>).inorderTraversal(action) ||\n action(this.key, this.value) ||\n (this.right as LLRBNode<K, V>).inorderTraversal(action)\n );\n }\n\n // Traverses the tree in reverse key order and calls the specified action\n // function for each node. If action returns true, traversal is aborted.\n // Returns the first truthy value returned by action, or the last falsey\n // value returned by action.\n reverseTraversal<T>(action: (k: K, v: V) => T): T {\n return (\n (this.right as LLRBNode<K, V>).reverseTraversal(action) ||\n action(this.key, this.value) ||\n (this.left as LLRBNode<K, V>).reverseTraversal(action)\n );\n }\n\n // Returns the minimum node in the tree.\n private min(): LLRBNode<K, V> {\n if (this.left.isEmpty()) {\n return this;\n } else {\n return (this.left as LLRBNode<K, V>).min();\n }\n }\n\n // Returns the maximum key in the tree.\n minKey(): K | null {\n return this.min().key;\n }\n\n // Returns the maximum key in the tree.\n maxKey(): K | null {\n if (this.right.isEmpty()) {\n return this.key;\n } else {\n return this.right.maxKey();\n }\n }\n\n // Returns new tree, with the key/value added.\n insert(key: K, value: V, comparator: Comparator<K>): LLRBNode<K, V> {\n let n: LLRBNode<K, V> = this;\n const cmp = comparator(key, n.key);\n if (cmp < 0) {\n n = n.copy(null, null, null, n.left.insert(key, value, comparator), null);\n } else if (cmp === 0) {\n n = n.copy(null, value, null, null, null);\n } else {\n n = n.copy(\n null,\n null,\n null,\n null,\n n.right.insert(key, value, comparator)\n );\n }\n return n.fixUp();\n }\n\n private removeMin(): LLRBNode<K, V> | LLRBEmptyNode<K, V> {\n if (this.left.isEmpty()) {\n return LLRBNode.EMPTY;\n }\n let n: LLRBNode<K, V> = this;\n if (!n.left.isRed() && !n.left.left.isRed()) {\n n = n.moveRedLeft();\n }\n n = n.copy(null, null, null, (n.left as LLRBNode<K, V>).removeMin(), null);\n return n.fixUp();\n }\n\n // Returns new tree, with the specified item removed.\n remove(\n key: K,\n comparator: Comparator<K>\n ): LLRBNode<K, V> | LLRBEmptyNode<K, V> {\n let smallest: LLRBNode<K, V>;\n let n: LLRBNode<K, V> = this;\n if (comparator(key, n.key) < 0) {\n if (!n.left.isEmpty() && !n.left.isRed() && !n.left.left.isRed()) {\n n = n.moveRedLeft();\n }\n n = n.copy(null, null, null, n.left.remove(key, comparator), null);\n } else {\n if (n.left.isRed()) {\n n = n.rotateRight();\n }\n if (!n.right.isEmpty() && !n.right.isRed() && !n.right.left.isRed()) {\n n = n.moveRedRight();\n }\n if (comparator(key, n.key) === 0) {\n if (n.right.isEmpty()) {\n return LLRBNode.EMPTY;\n } else {\n smallest = (n.right as LLRBNode<K, V>).min();\n n = n.copy(\n smallest.key,\n smallest.value,\n null,\n null,\n (n.right as LLRBNode<K, V>).removeMin()\n );\n }\n }\n n = n.copy(null, null, null, null, n.right.remove(key, comparator));\n }\n return n.fixUp();\n }\n\n isRed(): boolean {\n return this.color;\n }\n\n // Returns new tree after performing any needed rotations.\n private fixUp(): LLRBNode<K, V> {\n let n: LLRBNode<K, V> = this;\n if (n.right.isRed() && !n.left.isRed()) {\n n = n.rotateLeft();\n }\n if (n.left.isRed() && n.left.left.isRed()) {\n n = n.rotateRight();\n }\n if (n.left.isRed() && n.right.isRed()) {\n n = n.colorFlip();\n }\n return n;\n }\n\n private moveRedLeft(): LLRBNode<K, V> {\n let n = this.colorFlip();\n if (n.right.left.isRed()) {\n n = n.copy(\n null,\n null,\n null,\n null,\n (n.right as LLRBNode<K, V>).rotateRight()\n );\n n = n.rotateLeft();\n n = n.colorFlip();\n }\n return n;\n }\n\n private moveRedRight(): LLRBNode<K, V> {\n let n = this.colorFlip();\n if (n.left.left.isRed()) {\n n = n.rotateRight();\n n = n.colorFlip();\n }\n return n;\n }\n\n private rotateLeft(): LLRBNode<K, V> {\n const nl = this.copy(null, null, LLRBNode.RED, null, this.right.left);\n return (this.right as LLRBNode<K, V>).copy(\n null,\n null,\n this.color,\n nl,\n null\n );\n }\n\n private rotateRight(): LLRBNode<K, V> {\n const nr = this.copy(null, null, LLRBNode.RED, this.left.right, null);\n return (this.left as LLRBNode<K, V>).copy(null, null, this.color, null, nr);\n }\n\n private colorFlip(): LLRBNode<K, V> {\n const left = this.left.copy(null, null, !this.left.color, null, null);\n const right = this.right.copy(null, null, !this.right.color, null, null);\n return this.copy(null, null, !this.color, left, right);\n }\n\n // For testing.\n checkMaxDepth(): boolean {\n const blackDepth = this.check();\n if (Math.pow(2.0, blackDepth) <= this.size + 1) {\n return true;\n } else {\n return false;\n }\n }\n\n // In a balanced RB tree, the black-depth (number of black nodes) from root to\n // leaves is equal on both sides. This function verifies that or asserts.\n protected check(): number {\n if (this.isRed() && this.left.isRed()) {\n throw fail('Red node has red child(' + this.key + ',' + this.value + ')');\n }\n if (this.right.isRed()) {\n throw fail('Right child of (' + this.key + ',' + this.value + ') is red');\n }\n const blackDepth = (this.left as LLRBNode<K, V>).check();\n if (blackDepth !== (this.right as LLRBNode<K, V>).check()) {\n throw fail('Black depths differ');\n } else {\n return blackDepth + (this.isRed() ? 0 : 1);\n }\n }\n} // end LLRBNode\n\n// Represents an empty node (a leaf node in the Red-Black Tree).\nexport class LLRBEmptyNode<K, V> {\n get key(): never {\n throw fail('LLRBEmptyNode has no key.');\n }\n get value(): never {\n throw fail('LLRBEmptyNode has no value.');\n }\n get color(): never {\n throw fail('LLRBEmptyNode has no color.');\n }\n get left(): never {\n throw fail('LLRBEmptyNode has no left child.');\n }\n get right(): never {\n throw fail('LLRBEmptyNode has no right child.');\n }\n size = 0;\n\n // Returns a copy of the current node.\n copy(\n key: K | null,\n value: V | null,\n color: boolean | null,\n left: LLRBNode<K, V> | LLRBEmptyNode<K, V> | null,\n right: LLRBNode<K, V> | LLRBEmptyNode<K, V> | null\n ): LLRBEmptyNode<K, V> {\n return this;\n }\n\n // Returns a copy of the tree, with the specified key/value added.\n insert(key: K, value: V, comparator: Comparator<K>): LLRBNode<K, V> {\n return new LLRBNode<K, V>(key, value);\n }\n\n // Returns a copy of the tree, with the specified key removed.\n remove(key: K, comparator: Comparator<K>): LLRBEmptyNode<K, V> {\n return this;\n }\n\n isEmpty(): boolean {\n return true;\n }\n\n inorderTraversal(action: (k: K, v: V) => boolean): boolean {\n return false;\n }\n\n reverseTraversal(action: (k: K, v: V) => boolean): boolean {\n return false;\n }\n\n minKey(): K | null {\n return null;\n }\n\n maxKey(): K | null {\n return null;\n }\n\n isRed(): boolean {\n return false;\n }\n\n // For testing.\n checkMaxDepth(): boolean {\n return true;\n }\n\n protected check(): 0 {\n return 0;\n }\n} // end LLRBEmptyNode\n\nLLRBNode.EMPTY = new LLRBEmptyNode<unknown, unknown>();\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SortedMap, SortedMapIterator } from './sorted_map';\n\n/**\n * SortedSet is an immutable (copy-on-write) collection that holds elements\n * in order specified by the provided comparator.\n *\n * NOTE: if provided comparator returns 0 for two elements, we consider them to\n * be equal!\n */\nexport class SortedSet<T> {\n private data: SortedMap<T, boolean>;\n\n constructor(private comparator: (left: T, right: T) => number) {\n this.data = new SortedMap<T, boolean>(this.comparator);\n }\n\n has(elem: T): boolean {\n return this.data.get(elem) !== null;\n }\n\n first(): T | null {\n return this.data.minKey();\n }\n\n last(): T | null {\n return this.data.maxKey();\n }\n\n get size(): number {\n return this.data.size;\n }\n\n indexOf(elem: T): number {\n return this.data.indexOf(elem);\n }\n\n /** Iterates elements in order defined by \"comparator\" */\n forEach(cb: (elem: T) => void): void {\n this.data.inorderTraversal((k: T, v: boolean) => {\n cb(k);\n return false;\n });\n }\n\n /** Iterates over `elem`s such that: range[0] <= elem < range[1]. */\n forEachInRange(range: [T, T], cb: (elem: T) => void): void {\n const iter = this.data.getIteratorFrom(range[0]);\n while (iter.hasNext()) {\n const elem = iter.getNext();\n if (this.comparator(elem.key, range[1]) >= 0) {\n return;\n }\n cb(elem.key);\n }\n }\n\n /**\n * Iterates over `elem`s such that: start <= elem until false is returned.\n */\n forEachWhile(cb: (elem: T) => boolean, start?: T): void {\n let iter: SortedMapIterator<T, boolean>;\n if (start !== undefined) {\n iter = this.data.getIteratorFrom(start);\n } else {\n iter = this.data.getIterator();\n }\n while (iter.hasNext()) {\n const elem = iter.getNext();\n const result = cb(elem.key);\n if (!result) {\n return;\n }\n }\n }\n\n /** Finds the least element greater than or equal to `elem`. */\n firstAfterOrEqual(elem: T): T | null {\n const iter = this.data.getIteratorFrom(elem);\n return iter.hasNext() ? iter.getNext().key : null;\n }\n\n getIterator(): SortedSetIterator<T> {\n return new SortedSetIterator<T>(this.data.getIterator());\n }\n\n getIteratorFrom(key: T): SortedSetIterator<T> {\n return new SortedSetIterator<T>(this.data.getIteratorFrom(key));\n }\n\n /** Inserts or updates an element */\n add(elem: T): SortedSet<T> {\n return this.copy(this.data.remove(elem).insert(elem, true));\n }\n\n /** Deletes an element */\n delete(elem: T): SortedSet<T> {\n if (!this.has(elem)) {\n return this;\n }\n return this.copy(this.data.remove(elem));\n }\n\n isEmpty(): boolean {\n return this.data.isEmpty();\n }\n\n unionWith(other: SortedSet<T>): SortedSet<T> {\n let result: SortedSet<T> = this;\n\n // Make sure `result` always refers to the larger one of the two sets.\n if (result.size < other.size) {\n result = other;\n other = this;\n }\n\n other.forEach(elem => {\n result = result.add(elem);\n });\n return result;\n }\n\n isEqual(other: SortedSet<T>): boolean {\n if (!(other instanceof SortedSet)) {\n return false;\n }\n if (this.size !== other.size) {\n return false;\n }\n\n const thisIt = this.data.getIterator();\n const otherIt = other.data.getIterator();\n while (thisIt.hasNext()) {\n const thisElem = thisIt.getNext().key;\n const otherElem = otherIt.getNext().key;\n if (this.comparator(thisElem, otherElem) !== 0) {\n return false;\n }\n }\n return true;\n }\n\n toArray(): T[] {\n const res: T[] = [];\n this.forEach(targetId => {\n res.push(targetId);\n });\n return res;\n }\n\n toString(): string {\n const result: T[] = [];\n this.forEach(elem => result.push(elem));\n return 'SortedSet(' + result.toString() + ')';\n }\n\n private copy(data: SortedMap<T, boolean>): SortedSet<T> {\n const result = new SortedSet(this.comparator);\n result.data = data;\n return result;\n }\n}\n\nexport class SortedSetIterator<T> {\n constructor(private iter: SortedMapIterator<T, boolean>) {}\n\n getNext(): T {\n return this.iter.getNext().key;\n }\n\n hasNext(): boolean {\n return this.iter.hasNext();\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { SortedMap } from '../util/sorted_map';\nimport { SortedSet } from '../util/sorted_set';\n\nimport { TargetId } from '../core/types';\nimport { primitiveComparator } from '../util/misc';\nimport { Document, MaybeDocument } from './document';\nimport { DocumentKey } from './document_key';\n\n/** Miscellaneous collection types / constants. */\nexport interface DocumentSizeEntry {\n maybeDocument: MaybeDocument;\n size: number;\n}\n\nexport type MaybeDocumentMap = SortedMap<DocumentKey, MaybeDocument>;\nconst EMPTY_MAYBE_DOCUMENT_MAP = new SortedMap<DocumentKey, MaybeDocument>(\n DocumentKey.comparator\n);\nexport function maybeDocumentMap(): MaybeDocumentMap {\n return EMPTY_MAYBE_DOCUMENT_MAP;\n}\n\nexport type NullableMaybeDocumentMap = SortedMap<\n DocumentKey,\n MaybeDocument | null\n>;\n\nexport function nullableMaybeDocumentMap(): NullableMaybeDocumentMap {\n return maybeDocumentMap();\n}\n\nexport interface DocumentSizeEntries {\n maybeDocuments: NullableMaybeDocumentMap;\n sizeMap: SortedMap<DocumentKey, number>;\n}\n\nexport type DocumentMap = SortedMap<DocumentKey, Document>;\nconst EMPTY_DOCUMENT_MAP = new SortedMap<DocumentKey, Document>(\n DocumentKey.comparator\n);\nexport function documentMap(): DocumentMap {\n return EMPTY_DOCUMENT_MAP;\n}\n\nexport type DocumentVersionMap = SortedMap<DocumentKey, SnapshotVersion>;\nconst EMPTY_DOCUMENT_VERSION_MAP = new SortedMap<DocumentKey, SnapshotVersion>(\n DocumentKey.comparator\n);\nexport function documentVersionMap(): DocumentVersionMap {\n return EMPTY_DOCUMENT_VERSION_MAP;\n}\n\nexport type DocumentKeySet = SortedSet<DocumentKey>;\nconst EMPTY_DOCUMENT_KEY_SET = new SortedSet(DocumentKey.comparator);\nexport function documentKeySet(...keys: DocumentKey[]): DocumentKeySet {\n let set = EMPTY_DOCUMENT_KEY_SET;\n for (const key of keys) {\n set = set.add(key);\n }\n return set;\n}\n\nexport type TargetIdSet = SortedSet<TargetId>;\nconst EMPTY_TARGET_ID_SET = new SortedSet<TargetId>(primitiveComparator);\nexport function targetIdSet(): SortedSet<TargetId> {\n return EMPTY_TARGET_ID_SET;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { TargetId } from '../core/types';\nimport { ChangeType } from '../core/view_snapshot';\nimport { TargetData, TargetPurpose } from '../local/target_data';\nimport {\n documentKeySet,\n DocumentKeySet,\n maybeDocumentMap\n} from '../model/collections';\nimport { Document, MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\nimport { FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { primitiveComparator } from '../util/misc';\nimport { SortedMap } from '../util/sorted_map';\nimport { SortedSet } from '../util/sorted_set';\nimport { ExistenceFilter } from './existence_filter';\nimport { RemoteEvent, TargetChange } from './remote_event';\nimport { ByteString } from '../util/byte_string';\n\n/**\n * Internal representation of the watcher API protocol buffers.\n */\nexport type WatchChange =\n | DocumentWatchChange\n | WatchTargetChange\n | ExistenceFilterChange;\n\n/**\n * Represents a changed document and a list of target ids to which this change\n * applies.\n *\n * If document has been deleted NoDocument will be provided.\n */\nexport class DocumentWatchChange {\n constructor(\n /** The new document applies to all of these targets. */\n public updatedTargetIds: TargetId[],\n /** The new document is removed from all of these targets. */\n public removedTargetIds: TargetId[],\n /** The key of the document for this change. */\n public key: DocumentKey,\n /**\n * The new document or NoDocument if it was deleted. Is null if the\n * document went out of view without the server sending a new document.\n */\n public newDoc: MaybeDocument | null\n ) {}\n}\n\nexport class ExistenceFilterChange {\n constructor(\n public targetId: TargetId,\n public existenceFilter: ExistenceFilter\n ) {}\n}\n\nexport const enum WatchTargetChangeState {\n NoChange,\n Added,\n Removed,\n Current,\n Reset\n}\n\nexport class WatchTargetChange {\n constructor(\n /** What kind of change occurred to the watch target. */\n public state: WatchTargetChangeState,\n /** The target IDs that were added/removed/set. */\n public targetIds: TargetId[],\n /**\n * An opaque, server-assigned token that allows watching a target to be\n * resumed after disconnecting without retransmitting all the data that\n * matches the target. The resume token essentially identifies a point in\n * time from which the server should resume sending results.\n */\n public resumeToken: ByteString = ByteString.EMPTY_BYTE_STRING,\n /** An RPC error indicating why the watch failed. */\n public cause: FirestoreError | null = null\n ) {}\n}\n\n/** Tracks the internal state of a Watch target. */\nclass TargetState {\n /**\n * The number of pending responses (adds or removes) that we are waiting on.\n * We only consider targets active that have no pending responses.\n */\n private pendingResponses = 0;\n\n /**\n * Keeps track of the document changes since the last raised snapshot.\n *\n * These changes are continuously updated as we receive document updates and\n * always reflect the current set of changes against the last issued snapshot.\n */\n private documentChanges: SortedMap<\n DocumentKey,\n ChangeType\n > = snapshotChangesMap();\n\n /** See public getters for explanations of these fields. */\n private _resumeToken: ByteString = ByteString.EMPTY_BYTE_STRING;\n private _current = false;\n\n /**\n * Whether this target state should be included in the next snapshot. We\n * initialize to true so that newly-added targets are included in the next\n * RemoteEvent.\n */\n private _hasPendingChanges = true;\n\n /**\n * Whether this target has been marked 'current'.\n *\n * 'Current' has special meaning in the RPC protocol: It implies that the\n * Watch backend has sent us all changes up to the point at which the target\n * was added and that the target is consistent with the rest of the watch\n * stream.\n */\n get current(): boolean {\n return this._current;\n }\n\n /** The last resume token sent to us for this target. */\n get resumeToken(): ByteString {\n return this._resumeToken;\n }\n\n /** Whether this target has pending target adds or target removes. */\n get isPending(): boolean {\n return this.pendingResponses !== 0;\n }\n\n /** Whether we have modified any state that should trigger a snapshot. */\n get hasPendingChanges(): boolean {\n return this._hasPendingChanges;\n }\n\n /**\n * Applies the resume token to the TargetChange, but only when it has a new\n * value. Empty resumeTokens are discarded.\n */\n updateResumeToken(resumeToken: ByteString): void {\n if (resumeToken.approximateByteSize() > 0) {\n this._hasPendingChanges = true;\n this._resumeToken = resumeToken;\n }\n }\n\n /**\n * Creates a target change from the current set of changes.\n *\n * To reset the document changes after raising this snapshot, call\n * `clearPendingChanges()`.\n */\n toTargetChange(): TargetChange {\n let addedDocuments = documentKeySet();\n let modifiedDocuments = documentKeySet();\n let removedDocuments = documentKeySet();\n\n this.documentChanges.forEach((key, changeType) => {\n switch (changeType) {\n case ChangeType.Added:\n addedDocuments = addedDocuments.add(key);\n break;\n case ChangeType.Modified:\n modifiedDocuments = modifiedDocuments.add(key);\n break;\n case ChangeType.Removed:\n removedDocuments = removedDocuments.add(key);\n break;\n default:\n fail('Encountered invalid change type: ' + changeType);\n }\n });\n\n return new TargetChange(\n this._resumeToken,\n this._current,\n addedDocuments,\n modifiedDocuments,\n removedDocuments\n );\n }\n\n /**\n * Resets the document changes and sets `hasPendingChanges` to false.\n */\n clearPendingChanges(): void {\n this._hasPendingChanges = false;\n this.documentChanges = snapshotChangesMap();\n }\n\n addDocumentChange(key: DocumentKey, changeType: ChangeType): void {\n this._hasPendingChanges = true;\n this.documentChanges = this.documentChanges.insert(key, changeType);\n }\n\n removeDocumentChange(key: DocumentKey): void {\n this._hasPendingChanges = true;\n this.documentChanges = this.documentChanges.remove(key);\n }\n\n recordPendingTargetRequest(): void {\n this.pendingResponses += 1;\n }\n\n recordTargetResponse(): void {\n this.pendingResponses -= 1;\n }\n\n markCurrent(): void {\n this._hasPendingChanges = true;\n this._current = true;\n }\n}\n\n/**\n * Interface implemented by RemoteStore to expose target metadata to the\n * WatchChangeAggregator.\n */\nexport interface TargetMetadataProvider {\n /**\n * Returns the set of remote document keys for the given target ID as of the\n * last raised snapshot.\n */\n getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet;\n\n /**\n * Returns the TargetData for an active target ID or 'null' if this target\n * has become inactive\n */\n getTargetDataForTarget(targetId: TargetId): TargetData | null;\n}\n\nconst LOG_TAG = 'WatchChangeAggregator';\n\n/**\n * A helper class to accumulate watch changes into a RemoteEvent.\n */\nexport class WatchChangeAggregator {\n constructor(private metadataProvider: TargetMetadataProvider) {}\n\n /** The internal state of all tracked targets. */\n private targetStates = new Map<TargetId, TargetState>();\n\n /** Keeps track of the documents to update since the last raised snapshot. */\n private pendingDocumentUpdates = maybeDocumentMap();\n\n /** A mapping of document keys to their set of target IDs. */\n private pendingDocumentTargetMapping = documentTargetMap();\n\n /**\n * A list of targets with existence filter mismatches. These targets are\n * known to be inconsistent and their listens needs to be re-established by\n * RemoteStore.\n */\n private pendingTargetResets = new SortedSet<TargetId>(primitiveComparator);\n\n /**\n * Processes and adds the DocumentWatchChange to the current set of changes.\n */\n handleDocumentChange(docChange: DocumentWatchChange): void {\n for (const targetId of docChange.updatedTargetIds) {\n if (docChange.newDoc instanceof Document) {\n this.addDocumentToTarget(targetId, docChange.newDoc);\n } else if (docChange.newDoc instanceof NoDocument) {\n this.removeDocumentFromTarget(\n targetId,\n docChange.key,\n docChange.newDoc\n );\n }\n }\n\n for (const targetId of docChange.removedTargetIds) {\n this.removeDocumentFromTarget(targetId, docChange.key, docChange.newDoc);\n }\n }\n\n /** Processes and adds the WatchTargetChange to the current set of changes. */\n handleTargetChange(targetChange: WatchTargetChange): void {\n this.forEachTarget(targetChange, targetId => {\n const targetState = this.ensureTargetState(targetId);\n switch (targetChange.state) {\n case WatchTargetChangeState.NoChange:\n if (this.isActiveTarget(targetId)) {\n targetState.updateResumeToken(targetChange.resumeToken);\n }\n break;\n case WatchTargetChangeState.Added:\n // We need to decrement the number of pending acks needed from watch\n // for this targetId.\n targetState.recordTargetResponse();\n if (!targetState.isPending) {\n // We have a freshly added target, so we need to reset any state\n // that we had previously. This can happen e.g. when remove and add\n // back a target for existence filter mismatches.\n targetState.clearPendingChanges();\n }\n targetState.updateResumeToken(targetChange.resumeToken);\n break;\n case WatchTargetChangeState.Removed:\n // We need to keep track of removed targets to we can post-filter and\n // remove any target changes.\n // We need to decrement the number of pending acks needed from watch\n // for this targetId.\n targetState.recordTargetResponse();\n if (!targetState.isPending) {\n this.removeTarget(targetId);\n }\n debugAssert(\n !targetChange.cause,\n 'WatchChangeAggregator does not handle errored targets'\n );\n break;\n case WatchTargetChangeState.Current:\n if (this.isActiveTarget(targetId)) {\n targetState.markCurrent();\n targetState.updateResumeToken(targetChange.resumeToken);\n }\n break;\n case WatchTargetChangeState.Reset:\n if (this.isActiveTarget(targetId)) {\n // Reset the target and synthesizes removes for all existing\n // documents. The backend will re-add any documents that still\n // match the target before it sends the next global snapshot.\n this.resetTarget(targetId);\n targetState.updateResumeToken(targetChange.resumeToken);\n }\n break;\n default:\n fail('Unknown target watch change state: ' + targetChange.state);\n }\n });\n }\n\n /**\n * Iterates over all targetIds that the watch change applies to: either the\n * targetIds explicitly listed in the change or the targetIds of all currently\n * active targets.\n */\n forEachTarget(\n targetChange: WatchTargetChange,\n fn: (targetId: TargetId) => void\n ): void {\n if (targetChange.targetIds.length > 0) {\n targetChange.targetIds.forEach(fn);\n } else {\n this.targetStates.forEach((_, targetId) => {\n if (this.isActiveTarget(targetId)) {\n fn(targetId);\n }\n });\n }\n }\n\n /**\n * Handles existence filters and synthesizes deletes for filter mismatches.\n * Targets that are invalidated by filter mismatches are added to\n * `pendingTargetResets`.\n */\n handleExistenceFilter(watchChange: ExistenceFilterChange): void {\n const targetId = watchChange.targetId;\n const expectedCount = watchChange.existenceFilter.count;\n\n const targetData = this.targetDataForActiveTarget(targetId);\n if (targetData) {\n const target = targetData.target;\n if (target.isDocumentQuery()) {\n if (expectedCount === 0) {\n // The existence filter told us the document does not exist. We deduce\n // that this document does not exist and apply a deleted document to\n // our updates. Without applying this deleted document there might be\n // another query that will raise this document as part of a snapshot\n // until it is resolved, essentially exposing inconsistency between\n // queries.\n const key = new DocumentKey(target.path);\n this.removeDocumentFromTarget(\n targetId,\n key,\n new NoDocument(key, SnapshotVersion.min())\n );\n } else {\n hardAssert(\n expectedCount === 1,\n 'Single document existence filter with count: ' + expectedCount\n );\n }\n } else {\n const currentSize = this.getCurrentDocumentCountForTarget(targetId);\n if (currentSize !== expectedCount) {\n // Existence filter mismatch: We reset the mapping and raise a new\n // snapshot with `isFromCache:true`.\n this.resetTarget(targetId);\n this.pendingTargetResets = this.pendingTargetResets.add(targetId);\n }\n }\n }\n }\n\n /**\n * Converts the currently accumulated state into a remote event at the\n * provided snapshot version. Resets the accumulated changes before returning.\n */\n createRemoteEvent(snapshotVersion: SnapshotVersion): RemoteEvent {\n const targetChanges = new Map<TargetId, TargetChange>();\n\n this.targetStates.forEach((targetState, targetId) => {\n const targetData = this.targetDataForActiveTarget(targetId);\n if (targetData) {\n if (targetState.current && targetData.target.isDocumentQuery()) {\n // Document queries for document that don't exist can produce an empty\n // result set. To update our local cache, we synthesize a document\n // delete if we have not previously received the document. This\n // resolves the limbo state of the document, removing it from\n // limboDocumentRefs.\n //\n // TODO(dimond): Ideally we would have an explicit lookup target\n // instead resulting in an explicit delete message and we could\n // remove this special logic.\n const key = new DocumentKey(targetData.target.path);\n if (\n this.pendingDocumentUpdates.get(key) === null &&\n !this.targetContainsDocument(targetId, key)\n ) {\n this.removeDocumentFromTarget(\n targetId,\n key,\n new NoDocument(key, snapshotVersion)\n );\n }\n }\n\n if (targetState.hasPendingChanges) {\n targetChanges.set(targetId, targetState.toTargetChange());\n targetState.clearPendingChanges();\n }\n }\n });\n\n let resolvedLimboDocuments = documentKeySet();\n\n // We extract the set of limbo-only document updates as the GC logic\n // special-cases documents that do not appear in the target cache.\n //\n // TODO(gsoltis): Expand on this comment once GC is available in the JS\n // client.\n this.pendingDocumentTargetMapping.forEach((key, targets) => {\n let isOnlyLimboTarget = true;\n\n targets.forEachWhile(targetId => {\n const targetData = this.targetDataForActiveTarget(targetId);\n if (\n targetData &&\n targetData.purpose !== TargetPurpose.LimboResolution\n ) {\n isOnlyLimboTarget = false;\n return false;\n }\n\n return true;\n });\n\n if (isOnlyLimboTarget) {\n resolvedLimboDocuments = resolvedLimboDocuments.add(key);\n }\n });\n\n const remoteEvent = new RemoteEvent(\n snapshotVersion,\n targetChanges,\n this.pendingTargetResets,\n this.pendingDocumentUpdates,\n resolvedLimboDocuments\n );\n\n this.pendingDocumentUpdates = maybeDocumentMap();\n this.pendingDocumentTargetMapping = documentTargetMap();\n this.pendingTargetResets = new SortedSet<TargetId>(primitiveComparator);\n\n return remoteEvent;\n }\n\n /**\n * Adds the provided document to the internal list of document updates and\n * its document key to the given target's mapping.\n */\n // Visible for testing.\n addDocumentToTarget(targetId: TargetId, document: MaybeDocument): void {\n if (!this.isActiveTarget(targetId)) {\n return;\n }\n\n const changeType = this.targetContainsDocument(targetId, document.key)\n ? ChangeType.Modified\n : ChangeType.Added;\n\n const targetState = this.ensureTargetState(targetId);\n targetState.addDocumentChange(document.key, changeType);\n\n this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(\n document.key,\n document\n );\n\n this.pendingDocumentTargetMapping = this.pendingDocumentTargetMapping.insert(\n document.key,\n this.ensureDocumentTargetMapping(document.key).add(targetId)\n );\n }\n\n /**\n * Removes the provided document from the target mapping. If the\n * document no longer matches the target, but the document's state is still\n * known (e.g. we know that the document was deleted or we received the change\n * that caused the filter mismatch), the new document can be provided\n * to update the remote document cache.\n */\n // Visible for testing.\n removeDocumentFromTarget(\n targetId: TargetId,\n key: DocumentKey,\n updatedDocument: MaybeDocument | null\n ): void {\n if (!this.isActiveTarget(targetId)) {\n return;\n }\n\n const targetState = this.ensureTargetState(targetId);\n if (this.targetContainsDocument(targetId, key)) {\n targetState.addDocumentChange(key, ChangeType.Removed);\n } else {\n // The document may have entered and left the target before we raised a\n // snapshot, so we can just ignore the change.\n targetState.removeDocumentChange(key);\n }\n\n this.pendingDocumentTargetMapping = this.pendingDocumentTargetMapping.insert(\n key,\n this.ensureDocumentTargetMapping(key).delete(targetId)\n );\n\n if (updatedDocument) {\n this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(\n key,\n updatedDocument\n );\n }\n }\n\n removeTarget(targetId: TargetId): void {\n this.targetStates.delete(targetId);\n }\n\n /**\n * Returns the current count of documents in the target. This includes both\n * the number of documents that the LocalStore considers to be part of the\n * target as well as any accumulated changes.\n */\n private getCurrentDocumentCountForTarget(targetId: TargetId): number {\n const targetState = this.ensureTargetState(targetId);\n const targetChange = targetState.toTargetChange();\n return (\n this.metadataProvider.getRemoteKeysForTarget(targetId).size +\n targetChange.addedDocuments.size -\n targetChange.removedDocuments.size\n );\n }\n\n /**\n * Increment the number of acks needed from watch before we can consider the\n * server to be 'in-sync' with the client's active targets.\n */\n recordPendingTargetRequest(targetId: TargetId): void {\n // For each request we get we need to record we need a response for it.\n const targetState = this.ensureTargetState(targetId);\n targetState.recordPendingTargetRequest();\n }\n\n private ensureTargetState(targetId: TargetId): TargetState {\n let result = this.targetStates.get(targetId);\n if (!result) {\n result = new TargetState();\n this.targetStates.set(targetId, result);\n }\n return result;\n }\n\n private ensureDocumentTargetMapping(key: DocumentKey): SortedSet<TargetId> {\n let targetMapping = this.pendingDocumentTargetMapping.get(key);\n\n if (!targetMapping) {\n targetMapping = new SortedSet<TargetId>(primitiveComparator);\n this.pendingDocumentTargetMapping = this.pendingDocumentTargetMapping.insert(\n key,\n targetMapping\n );\n }\n\n return targetMapping;\n }\n\n /**\n * Verifies that the user is still interested in this target (by calling\n * `getTargetDataForTarget()`) and that we are not waiting for pending ADDs\n * from watch.\n */\n protected isActiveTarget(targetId: TargetId): boolean {\n const targetActive = this.targetDataForActiveTarget(targetId) !== null;\n if (!targetActive) {\n logDebug(LOG_TAG, 'Detected inactive target', targetId);\n }\n return targetActive;\n }\n\n /**\n * Returns the TargetData for an active target (i.e. a target that the user\n * is still interested in that has no outstanding target change requests).\n */\n protected targetDataForActiveTarget(targetId: TargetId): TargetData | null {\n const targetState = this.targetStates.get(targetId);\n return targetState && targetState.isPending\n ? null\n : this.metadataProvider.getTargetDataForTarget(targetId);\n }\n\n /**\n * Resets the state of a Watch target to its initial state (e.g. sets\n * 'current' to false, clears the resume token and removes its target mapping\n * from all documents).\n */\n private resetTarget(targetId: TargetId): void {\n debugAssert(\n !this.targetStates.get(targetId)!.isPending,\n 'Should only reset active targets'\n );\n this.targetStates.set(targetId, new TargetState());\n\n // Trigger removal for any documents currently mapped to this target.\n // These removals will be part of the initial snapshot if Watch does not\n // resend these documents.\n const existingKeys = this.metadataProvider.getRemoteKeysForTarget(targetId);\n existingKeys.forEach(key => {\n this.removeDocumentFromTarget(targetId, key, /*updatedDocument=*/ null);\n });\n }\n /**\n * Returns whether the LocalStore considers the document to be part of the\n * specified target.\n */\n private targetContainsDocument(\n targetId: TargetId,\n key: DocumentKey\n ): boolean {\n const existingKeys = this.metadataProvider.getRemoteKeysForTarget(targetId);\n return existingKeys.has(key);\n }\n}\n\nfunction documentTargetMap(): SortedMap<DocumentKey, SortedSet<TargetId>> {\n return new SortedMap<DocumentKey, SortedSet<TargetId>>(\n DocumentKey.comparator\n );\n}\n\nfunction snapshotChangesMap(): SortedMap<DocumentKey, ChangeType> {\n return new SortedMap<DocumentKey, ChangeType>(DocumentKey.comparator);\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SortedMap } from '../util/sorted_map';\n\nimport { documentMap } from './collections';\nimport { Document } from './document';\nimport { DocumentComparator } from './document_comparator';\nimport { DocumentKey } from './document_key';\n\n/**\n * DocumentSet is an immutable (copy-on-write) collection that holds documents\n * in order specified by the provided comparator. We always add a document key\n * comparator on top of what is provided to guarantee document equality based on\n * the key.\n */\n\nexport class DocumentSet {\n /**\n * Returns an empty copy of the existing DocumentSet, using the same\n * comparator.\n */\n static emptySet(oldSet: DocumentSet): DocumentSet {\n return new DocumentSet(oldSet.comparator);\n }\n\n private comparator: DocumentComparator;\n private keyedMap: SortedMap<DocumentKey, Document>;\n private sortedSet: SortedMap<Document, null>;\n\n /** The default ordering is by key if the comparator is omitted */\n constructor(comp?: DocumentComparator) {\n // We are adding document key comparator to the end as it's the only\n // guaranteed unique property of a document.\n if (comp) {\n this.comparator = (d1: Document, d2: Document) =>\n comp(d1, d2) || DocumentKey.comparator(d1.key, d2.key);\n } else {\n this.comparator = (d1: Document, d2: Document) =>\n DocumentKey.comparator(d1.key, d2.key);\n }\n\n this.keyedMap = documentMap();\n this.sortedSet = new SortedMap<Document, null>(this.comparator);\n }\n\n has(key: DocumentKey): boolean {\n return this.keyedMap.get(key) != null;\n }\n\n get(key: DocumentKey): Document | null {\n return this.keyedMap.get(key);\n }\n\n first(): Document | null {\n return this.sortedSet.minKey();\n }\n\n last(): Document | null {\n return this.sortedSet.maxKey();\n }\n\n isEmpty(): boolean {\n return this.sortedSet.isEmpty();\n }\n\n /**\n * Returns the index of the provided key in the document set, or -1 if the\n * document key is not present in the set;\n */\n indexOf(key: DocumentKey): number {\n const doc = this.keyedMap.get(key);\n return doc ? this.sortedSet.indexOf(doc) : -1;\n }\n\n get size(): number {\n return this.sortedSet.size;\n }\n\n /** Iterates documents in order defined by \"comparator\" */\n forEach(cb: (doc: Document) => void): void {\n this.sortedSet.inorderTraversal((k, v) => {\n cb(k);\n return false;\n });\n }\n\n /** Inserts or updates a document with the same key */\n add(doc: Document): DocumentSet {\n // First remove the element if we have it.\n const set = this.delete(doc.key);\n return set.copy(\n set.keyedMap.insert(doc.key, doc),\n set.sortedSet.insert(doc, null)\n );\n }\n\n /** Deletes a document with a given key */\n delete(key: DocumentKey): DocumentSet {\n const doc = this.get(key);\n if (!doc) {\n return this;\n }\n\n return this.copy(this.keyedMap.remove(key), this.sortedSet.remove(doc));\n }\n\n isEqual(other: DocumentSet | null | undefined): boolean {\n if (!(other instanceof DocumentSet)) {\n return false;\n }\n if (this.size !== other.size) {\n return false;\n }\n\n const thisIt = this.sortedSet.getIterator();\n const otherIt = other.sortedSet.getIterator();\n while (thisIt.hasNext()) {\n const thisDoc = thisIt.getNext().key;\n const otherDoc = otherIt.getNext().key;\n if (!thisDoc.isEqual(otherDoc)) {\n return false;\n }\n }\n return true;\n }\n\n toString(): string {\n const docStrings: string[] = [];\n this.forEach(doc => {\n docStrings.push(doc.toString());\n });\n if (docStrings.length === 0) {\n return 'DocumentSet ()';\n } else {\n return 'DocumentSet (\\n ' + docStrings.join(' \\n') + '\\n)';\n }\n }\n\n private copy(\n keyedMap: SortedMap<DocumentKey, Document>,\n sortedSet: SortedMap<Document, null>\n ): DocumentSet {\n const newSet = new DocumentSet();\n newSet.comparator = this.comparator;\n newSet.keyedMap = keyedMap;\n newSet.sortedSet = sortedSet;\n return newSet;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Document } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { DocumentSet } from '../model/document_set';\nimport { fail } from '../util/assert';\nimport { SortedMap } from '../util/sorted_map';\n\nimport { DocumentKeySet } from '../model/collections';\nimport { Query } from './query';\n\nexport const enum ChangeType {\n Added,\n Removed,\n Modified,\n Metadata\n}\n\nexport interface DocumentViewChange {\n type: ChangeType;\n doc: Document;\n}\n\nexport const enum SyncState {\n Local,\n Synced\n}\n\n/**\n * DocumentChangeSet keeps track of a set of changes to docs in a query, merging\n * duplicate events for the same doc.\n */\nexport class DocumentChangeSet {\n private changeMap = new SortedMap<DocumentKey, DocumentViewChange>(\n DocumentKey.comparator\n );\n\n track(change: DocumentViewChange): void {\n const key = change.doc.key;\n const oldChange = this.changeMap.get(key);\n if (!oldChange) {\n this.changeMap = this.changeMap.insert(key, change);\n return;\n }\n\n // Merge the new change with the existing change.\n if (\n change.type !== ChangeType.Added &&\n oldChange.type === ChangeType.Metadata\n ) {\n this.changeMap = this.changeMap.insert(key, change);\n } else if (\n change.type === ChangeType.Metadata &&\n oldChange.type !== ChangeType.Removed\n ) {\n this.changeMap = this.changeMap.insert(key, {\n type: oldChange.type,\n doc: change.doc\n });\n } else if (\n change.type === ChangeType.Modified &&\n oldChange.type === ChangeType.Modified\n ) {\n this.changeMap = this.changeMap.insert(key, {\n type: ChangeType.Modified,\n doc: change.doc\n });\n } else if (\n change.type === ChangeType.Modified &&\n oldChange.type === ChangeType.Added\n ) {\n this.changeMap = this.changeMap.insert(key, {\n type: ChangeType.Added,\n doc: change.doc\n });\n } else if (\n change.type === ChangeType.Removed &&\n oldChange.type === ChangeType.Added\n ) {\n this.changeMap = this.changeMap.remove(key);\n } else if (\n change.type === ChangeType.Removed &&\n oldChange.type === ChangeType.Modified\n ) {\n this.changeMap = this.changeMap.insert(key, {\n type: ChangeType.Removed,\n doc: oldChange.doc\n });\n } else if (\n change.type === ChangeType.Added &&\n oldChange.type === ChangeType.Removed\n ) {\n this.changeMap = this.changeMap.insert(key, {\n type: ChangeType.Modified,\n doc: change.doc\n });\n } else {\n // This includes these cases, which don't make sense:\n // Added->Added\n // Removed->Removed\n // Modified->Added\n // Removed->Modified\n // Metadata->Added\n // Removed->Metadata\n fail(\n 'unsupported combination of changes: ' +\n JSON.stringify(change) +\n ' after ' +\n JSON.stringify(oldChange)\n );\n }\n }\n\n getChanges(): DocumentViewChange[] {\n const changes: DocumentViewChange[] = [];\n this.changeMap.inorderTraversal(\n (key: DocumentKey, change: DocumentViewChange) => {\n changes.push(change);\n }\n );\n return changes;\n }\n}\n\nexport class ViewSnapshot {\n constructor(\n readonly query: Query,\n readonly docs: DocumentSet,\n readonly oldDocs: DocumentSet,\n readonly docChanges: DocumentViewChange[],\n readonly mutatedKeys: DocumentKeySet,\n readonly fromCache: boolean,\n readonly syncStateChanged: boolean,\n readonly excludesMetadataChanges: boolean\n ) {}\n\n /** Returns a view snapshot as if all documents in the snapshot were added. */\n static fromInitialDocuments(\n query: Query,\n documents: DocumentSet,\n mutatedKeys: DocumentKeySet,\n fromCache: boolean\n ): ViewSnapshot {\n const changes: DocumentViewChange[] = [];\n documents.forEach(doc => {\n changes.push({ type: ChangeType.Added, doc });\n });\n\n return new ViewSnapshot(\n query,\n documents,\n DocumentSet.emptySet(documents),\n changes,\n mutatedKeys,\n fromCache,\n /* syncStateChanged= */ true,\n /* excludesMetadataChanges= */ false\n );\n }\n\n get hasPendingWrites(): boolean {\n return !this.mutatedKeys.isEmpty();\n }\n\n isEqual(other: ViewSnapshot): boolean {\n if (\n this.fromCache !== other.fromCache ||\n this.syncStateChanged !== other.syncStateChanged ||\n !this.mutatedKeys.isEqual(other.mutatedKeys) ||\n !this.query.isEqual(other.query) ||\n !this.docs.isEqual(other.docs) ||\n !this.oldDocs.isEqual(other.oldDocs)\n ) {\n return false;\n }\n const changes: DocumentViewChange[] = this.docChanges;\n const otherChanges: DocumentViewChange[] = other.docChanges;\n if (changes.length !== otherChanges.length) {\n return false;\n }\n for (let i = 0; i < changes.length; i++) {\n if (\n changes[i].type !== otherChanges[i].type ||\n !changes[i].doc.isEqual(otherChanges[i].doc)\n ) {\n return false;\n }\n }\n return true;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { TargetId } from '../core/types';\nimport {\n documentKeySet,\n DocumentKeySet,\n maybeDocumentMap,\n MaybeDocumentMap,\n targetIdSet\n} from '../model/collections';\nimport { SortedSet } from '../util/sorted_set';\nimport { ByteString } from '../util/byte_string';\n\n/**\n * An event from the RemoteStore. It is split into targetChanges (changes to the\n * state or the set of documents in our watched targets) and documentUpdates\n * (changes to the actual documents).\n */\nexport class RemoteEvent {\n constructor(\n /**\n * The snapshot version this event brings us up to, or MIN if not set.\n */\n readonly snapshotVersion: SnapshotVersion,\n /**\n * A map from target to changes to the target. See TargetChange.\n */\n readonly targetChanges: Map<TargetId, TargetChange>,\n /**\n * A set of targets that is known to be inconsistent. Listens for these\n * targets should be re-established without resume tokens.\n */\n readonly targetMismatches: SortedSet<TargetId>,\n /**\n * A set of which documents have changed or been deleted, along with the\n * doc's new values (if not deleted).\n */\n readonly documentUpdates: MaybeDocumentMap,\n /**\n * A set of which document updates are due only to limbo resolution targets.\n */\n readonly resolvedLimboDocuments: DocumentKeySet\n ) {}\n\n /**\n * HACK: Views require RemoteEvents in order to determine whether the view is\n * CURRENT, but secondary tabs don't receive remote events. So this method is\n * used to create a synthesized RemoteEvent that can be used to apply a\n * CURRENT status change to a View, for queries executed in a different tab.\n */\n // PORTING NOTE: Multi-tab only\n static createSynthesizedRemoteEventForCurrentChange(\n targetId: TargetId,\n current: boolean\n ): RemoteEvent {\n const targetChanges = new Map<TargetId, TargetChange>();\n targetChanges.set(\n targetId,\n TargetChange.createSynthesizedTargetChangeForCurrentChange(\n targetId,\n current\n )\n );\n return new RemoteEvent(\n SnapshotVersion.min(),\n targetChanges,\n targetIdSet(),\n maybeDocumentMap(),\n documentKeySet()\n );\n }\n}\n\n/**\n * A TargetChange specifies the set of changes for a specific target as part of\n * a RemoteEvent. These changes track which documents are added, modified or\n * removed, as well as the target's resume token and whether the target is\n * marked CURRENT.\n * The actual changes *to* documents are not part of the TargetChange since\n * documents may be part of multiple targets.\n */\nexport class TargetChange {\n constructor(\n /**\n * An opaque, server-assigned token that allows watching a query to be resumed\n * after disconnecting without retransmitting all the data that matches the\n * query. The resume token essentially identifies a point in time from which\n * the server should resume sending results.\n */\n readonly resumeToken: ByteString,\n /**\n * The \"current\" (synced) status of this target. Note that \"current\"\n * has special meaning in the RPC protocol that implies that a target is\n * both up-to-date and consistent with the rest of the watch stream.\n */\n readonly current: boolean,\n /**\n * The set of documents that were newly assigned to this target as part of\n * this remote event.\n */\n readonly addedDocuments: DocumentKeySet,\n /**\n * The set of documents that were already assigned to this target but received\n * an update during this remote event.\n */\n readonly modifiedDocuments: DocumentKeySet,\n /**\n * The set of documents that were removed from this target as part of this\n * remote event.\n */\n readonly removedDocuments: DocumentKeySet\n ) {}\n\n /**\n * This method is used to create a synthesized TargetChanges that can be used to\n * apply a CURRENT status change to a View (for queries executed in a different\n * tab) or for new queries (to raise snapshots with correct CURRENT status).\n */\n static createSynthesizedTargetChangeForCurrentChange(\n targetId: TargetId,\n current: boolean\n ): TargetChange {\n return new TargetChange(\n ByteString.EMPTY_BYTE_STRING,\n current,\n documentKeySet(),\n documentKeySet(),\n documentKeySet()\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Blob } from '../api/blob';\nimport { Timestamp } from '../api/timestamp';\nimport { DatabaseId } from '../core/database_info';\nimport {\n Bound,\n Direction,\n FieldFilter,\n Filter,\n LimitType,\n Operator,\n OrderBy,\n Query\n} from '../core/query';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { Target } from '../core/target';\nimport { TargetId } from '../core/types';\nimport { TargetData, TargetPurpose } from '../local/target_data';\nimport { Document, MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { ObjectValue } from '../model/object_value';\nimport {\n DeleteMutation,\n FieldMask,\n FieldTransform,\n Mutation,\n MutationResult,\n PatchMutation,\n Precondition,\n SetMutation,\n TransformMutation,\n VerifyMutation\n} from '../model/mutation';\nimport { FieldPath, ResourcePath } from '../model/path';\nimport * as api from '../protos/firestore_proto_api';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { ByteString } from '../util/byte_string';\nimport {\n isNegativeZero,\n isNullOrUndefined,\n isSafeInteger\n} from '../util/types';\nimport {\n ArrayRemoveTransformOperation,\n ArrayUnionTransformOperation,\n NumericIncrementTransformOperation,\n ServerTimestampTransform,\n TransformOperation\n} from '../model/transform_operation';\nimport { ExistenceFilter } from './existence_filter';\nimport { mapCodeFromRpcCode } from './rpc_error';\nimport {\n DocumentWatchChange,\n ExistenceFilterChange,\n WatchChange,\n WatchTargetChange,\n WatchTargetChangeState\n} from './watch_change';\nimport { isNanValue, isNullValue, normalizeTimestamp } from '../model/values';\n\nconst DIRECTIONS = (() => {\n const dirs: { [dir: string]: api.OrderDirection } = {};\n dirs[Direction.ASCENDING] = 'ASCENDING';\n dirs[Direction.DESCENDING] = 'DESCENDING';\n return dirs;\n})();\n\nconst OPERATORS = (() => {\n const ops: { [op: string]: api.FieldFilterOp } = {};\n ops[Operator.LESS_THAN] = 'LESS_THAN';\n ops[Operator.LESS_THAN_OR_EQUAL] = 'LESS_THAN_OR_EQUAL';\n ops[Operator.GREATER_THAN] = 'GREATER_THAN';\n ops[Operator.GREATER_THAN_OR_EQUAL] = 'GREATER_THAN_OR_EQUAL';\n ops[Operator.EQUAL] = 'EQUAL';\n ops[Operator.ARRAY_CONTAINS] = 'ARRAY_CONTAINS';\n ops[Operator.IN] = 'IN';\n ops[Operator.ARRAY_CONTAINS_ANY] = 'ARRAY_CONTAINS_ANY';\n return ops;\n})();\n\nfunction assertPresent(value: unknown, description: string): asserts value {\n debugAssert(!isNullOrUndefined(value), description + ' is missing');\n}\n\nexport interface SerializerOptions {\n /**\n * The serializer supports both Protobuf.js and Proto3 JSON formats. By\n * setting this flag to true, the serializer will use the Proto3 JSON format.\n *\n * For a description of the Proto3 JSON format check\n * https://developers.google.com/protocol-buffers/docs/proto3#json\n */\n useProto3Json: boolean;\n}\n\n/**\n * Generates JsonObject values for the Datastore API suitable for sending to\n * either GRPC stub methods or via the JSON/HTTP REST API.\n * TODO(klimt): We can remove the databaseId argument if we keep the full\n * resource name in documents.\n */\nexport class JsonProtoSerializer {\n constructor(\n private databaseId: DatabaseId,\n private options: SerializerOptions\n ) {}\n\n fromRpcStatus(status: api.Status): FirestoreError {\n const code =\n status.code === undefined\n ? Code.UNKNOWN\n : mapCodeFromRpcCode(status.code);\n return new FirestoreError(code, status.message || '');\n }\n\n /**\n * Returns a value for a number (or null) that's appropriate to put into\n * a google.protobuf.Int32Value proto.\n * DO NOT USE THIS FOR ANYTHING ELSE.\n * This method cheats. It's typed as returning \"number\" because that's what\n * our generated proto interfaces say Int32Value must be. But GRPC actually\n * expects a { value: <number> } struct.\n */\n private toInt32Proto(val: number | null): number | { value: number } | null {\n if (this.options.useProto3Json || isNullOrUndefined(val)) {\n return val;\n } else {\n return { value: val };\n }\n }\n\n /**\n * Returns a number (or null) from a google.protobuf.Int32Value proto.\n */\n private fromInt32Proto(\n val: number | { value: number } | undefined\n ): number | null {\n let result;\n if (typeof val === 'object') {\n result = val.value;\n } else {\n result = val;\n }\n return isNullOrUndefined(result) ? null : result;\n }\n\n /**\n * Returns an IntegerValue for `value`.\n */\n toInteger(value: number): api.Value {\n return { integerValue: '' + value };\n }\n\n /**\n * Returns an DoubleValue for `value` that is encoded based the serializer's\n * `useProto3Json` setting.\n */\n toDouble(value: number): api.Value {\n if (this.options.useProto3Json) {\n if (isNaN(value)) {\n return { doubleValue: 'NaN' };\n } else if (value === Infinity) {\n return { doubleValue: 'Infinity' };\n } else if (value === -Infinity) {\n return { doubleValue: '-Infinity' };\n }\n }\n return { doubleValue: isNegativeZero(value) ? '-0' : value };\n }\n\n /**\n * Returns a value for a number that's appropriate to put into a proto.\n * The return value is an IntegerValue if it can safely represent the value,\n * otherwise a DoubleValue is returned.\n */\n toNumber(value: number): api.Value {\n return isSafeInteger(value) ? this.toInteger(value) : this.toDouble(value);\n }\n\n /**\n * Returns a value for a Date that's appropriate to put into a proto.\n */\n toTimestamp(timestamp: Timestamp): api.Timestamp {\n if (this.options.useProto3Json) {\n // Serialize to ISO-8601 date format, but with full nano resolution.\n // Since JS Date has only millis, let's only use it for the seconds and\n // then manually add the fractions to the end.\n const jsDateStr = new Date(timestamp.seconds * 1000).toISOString();\n // Remove .xxx frac part and Z in the end.\n const strUntilSeconds = jsDateStr.replace(/\\.\\d*/, '').replace('Z', '');\n // Pad the fraction out to 9 digits (nanos).\n const nanoStr = ('000000000' + timestamp.nanoseconds).slice(-9);\n\n return `${strUntilSeconds}.${nanoStr}Z`;\n } else {\n return {\n seconds: '' + timestamp.seconds,\n nanos: timestamp.nanoseconds\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n }\n }\n\n private fromTimestamp(date: api.Timestamp): Timestamp {\n const timestamp = normalizeTimestamp(date);\n return new Timestamp(timestamp.seconds, timestamp.nanos);\n }\n\n /**\n * Returns a value for bytes that's appropriate to put in a proto.\n *\n * Visible for testing.\n */\n toBytes(bytes: Blob | ByteString): string | Uint8Array {\n if (this.options.useProto3Json) {\n return bytes.toBase64();\n } else {\n return bytes.toUint8Array();\n }\n }\n\n /**\n * Returns a ByteString based on the proto string value.\n */\n fromBytes(value: string | Uint8Array | undefined): ByteString {\n if (this.options.useProto3Json) {\n hardAssert(\n value === undefined || typeof value === 'string',\n 'value must be undefined or a string when using proto3 Json'\n );\n return ByteString.fromBase64String(value ? value : '');\n } else {\n hardAssert(\n value === undefined || value instanceof Uint8Array,\n 'value must be undefined or Uint8Array'\n );\n return ByteString.fromUint8Array(value ? value : new Uint8Array());\n }\n }\n\n toVersion(version: SnapshotVersion): api.Timestamp {\n return this.toTimestamp(version.toTimestamp());\n }\n\n fromVersion(version: api.Timestamp): SnapshotVersion {\n hardAssert(!!version, \"Trying to deserialize version that isn't set\");\n return SnapshotVersion.fromTimestamp(this.fromTimestamp(version));\n }\n\n toResourceName(path: ResourcePath, databaseId?: DatabaseId): string {\n return this.fullyQualifiedPrefixPath(databaseId || this.databaseId)\n .child('documents')\n .child(path)\n .canonicalString();\n }\n\n fromResourceName(name: string): ResourcePath {\n const resource = ResourcePath.fromString(name);\n hardAssert(\n isValidResourceName(resource),\n 'Tried to deserialize invalid key ' + resource.toString()\n );\n return resource;\n }\n\n toName(key: DocumentKey): string {\n return this.toResourceName(key.path);\n }\n\n fromName(name: string): DocumentKey {\n const resource = this.fromResourceName(name);\n hardAssert(\n resource.get(1) === this.databaseId.projectId,\n 'Tried to deserialize key from different project: ' +\n resource.get(1) +\n ' vs ' +\n this.databaseId.projectId\n );\n hardAssert(\n (!resource.get(3) && !this.databaseId.database) ||\n resource.get(3) === this.databaseId.database,\n 'Tried to deserialize key from different database: ' +\n resource.get(3) +\n ' vs ' +\n this.databaseId.database\n );\n return new DocumentKey(this.extractLocalPathFromResourceName(resource));\n }\n\n toQueryPath(path: ResourcePath): string {\n return this.toResourceName(path);\n }\n\n fromQueryPath(name: string): ResourcePath {\n const resourceName = this.fromResourceName(name);\n // In v1beta1 queries for collections at the root did not have a trailing\n // \"/documents\". In v1 all resource paths contain \"/documents\". Preserve the\n // ability to read the v1beta1 form for compatibility with queries persisted\n // in the local target cache.\n if (resourceName.length === 4) {\n return ResourcePath.EMPTY_PATH;\n }\n return this.extractLocalPathFromResourceName(resourceName);\n }\n\n get encodedDatabaseId(): string {\n const path = new ResourcePath([\n 'projects',\n this.databaseId.projectId,\n 'databases',\n this.databaseId.database\n ]);\n return path.canonicalString();\n }\n\n private fullyQualifiedPrefixPath(databaseId: DatabaseId): ResourcePath {\n return new ResourcePath([\n 'projects',\n databaseId.projectId,\n 'databases',\n databaseId.database\n ]);\n }\n\n private extractLocalPathFromResourceName(\n resourceName: ResourcePath\n ): ResourcePath {\n hardAssert(\n resourceName.length > 4 && resourceName.get(4) === 'documents',\n 'tried to deserialize invalid key ' + resourceName.toString()\n );\n return resourceName.popFirst(5);\n }\n\n /** Creates an api.Document from key and fields (but no create/update time) */\n toMutationDocument(key: DocumentKey, fields: ObjectValue): api.Document {\n return {\n name: this.toName(key),\n fields: fields.proto.mapValue.fields\n };\n }\n\n toDocument(document: Document): api.Document {\n debugAssert(\n !document.hasLocalMutations,\n \"Can't serialize documents with mutations.\"\n );\n return {\n name: this.toName(document.key),\n fields: document.toProto().mapValue.fields,\n updateTime: this.toTimestamp(document.version.toTimestamp())\n };\n }\n\n fromDocument(\n document: api.Document,\n hasCommittedMutations?: boolean\n ): Document {\n const key = this.fromName(document.name!);\n const version = this.fromVersion(document.updateTime!);\n const data = new ObjectValue({ mapValue: { fields: document.fields } });\n return new Document(key, version, data, {\n hasCommittedMutations: !!hasCommittedMutations\n });\n }\n\n private fromFound(doc: api.BatchGetDocumentsResponse): Document {\n hardAssert(\n !!doc.found,\n 'Tried to deserialize a found document from a missing document.'\n );\n assertPresent(doc.found.name, 'doc.found.name');\n assertPresent(doc.found.updateTime, 'doc.found.updateTime');\n const key = this.fromName(doc.found.name);\n const version = this.fromVersion(doc.found.updateTime);\n const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });\n return new Document(key, version, data, {});\n }\n\n private fromMissing(result: api.BatchGetDocumentsResponse): NoDocument {\n hardAssert(\n !!result.missing,\n 'Tried to deserialize a missing document from a found document.'\n );\n hardAssert(\n !!result.readTime,\n 'Tried to deserialize a missing document without a read time.'\n );\n const key = this.fromName(result.missing);\n const version = this.fromVersion(result.readTime);\n return new NoDocument(key, version);\n }\n\n fromMaybeDocument(result: api.BatchGetDocumentsResponse): MaybeDocument {\n if ('found' in result) {\n return this.fromFound(result);\n } else if ('missing' in result) {\n return this.fromMissing(result);\n }\n return fail('invalid batch get response: ' + JSON.stringify(result));\n }\n\n fromWatchChange(change: api.ListenResponse): WatchChange {\n let watchChange: WatchChange;\n if ('targetChange' in change) {\n assertPresent(change.targetChange, 'targetChange');\n // proto3 default value is unset in JSON (undefined), so use 'NO_CHANGE'\n // if unset\n const state = this.fromWatchTargetChangeState(\n change.targetChange.targetChangeType || 'NO_CHANGE'\n );\n const targetIds: TargetId[] = change.targetChange.targetIds || [];\n\n const resumeToken = this.fromBytes(change.targetChange.resumeToken);\n const causeProto = change.targetChange!.cause;\n const cause = causeProto && this.fromRpcStatus(causeProto);\n watchChange = new WatchTargetChange(\n state,\n targetIds,\n resumeToken,\n cause || null\n );\n } else if ('documentChange' in change) {\n assertPresent(change.documentChange, 'documentChange');\n const entityChange = change.documentChange;\n assertPresent(entityChange.document, 'documentChange.name');\n assertPresent(entityChange.document.name, 'documentChange.document.name');\n assertPresent(\n entityChange.document.updateTime,\n 'documentChange.document.updateTime'\n );\n const key = this.fromName(entityChange.document.name);\n const version = this.fromVersion(entityChange.document.updateTime);\n const data = new ObjectValue({\n mapValue: { fields: entityChange.document.fields }\n });\n const doc = new Document(key, version, data, {});\n const updatedTargetIds = entityChange.targetIds || [];\n const removedTargetIds = entityChange.removedTargetIds || [];\n watchChange = new DocumentWatchChange(\n updatedTargetIds,\n removedTargetIds,\n doc.key,\n doc\n );\n } else if ('documentDelete' in change) {\n assertPresent(change.documentDelete, 'documentDelete');\n const docDelete = change.documentDelete;\n assertPresent(docDelete.document, 'documentDelete.document');\n const key = this.fromName(docDelete.document);\n const version = docDelete.readTime\n ? this.fromVersion(docDelete.readTime)\n : SnapshotVersion.min();\n const doc = new NoDocument(key, version);\n const removedTargetIds = docDelete.removedTargetIds || [];\n watchChange = new DocumentWatchChange([], removedTargetIds, doc.key, doc);\n } else if ('documentRemove' in change) {\n assertPresent(change.documentRemove, 'documentRemove');\n const docRemove = change.documentRemove;\n assertPresent(docRemove.document, 'documentRemove');\n const key = this.fromName(docRemove.document);\n const removedTargetIds = docRemove.removedTargetIds || [];\n watchChange = new DocumentWatchChange([], removedTargetIds, key, null);\n } else if ('filter' in change) {\n // TODO(dimond): implement existence filter parsing with strategy.\n assertPresent(change.filter, 'filter');\n const filter = change.filter;\n assertPresent(filter.targetId, 'filter.targetId');\n const count = filter.count || 0;\n const existenceFilter = new ExistenceFilter(count);\n const targetId = filter.targetId;\n watchChange = new ExistenceFilterChange(targetId, existenceFilter);\n } else {\n return fail('Unknown change type ' + JSON.stringify(change));\n }\n return watchChange;\n }\n\n fromWatchTargetChangeState(\n state: api.TargetChangeTargetChangeType\n ): WatchTargetChangeState {\n if (state === 'NO_CHANGE') {\n return WatchTargetChangeState.NoChange;\n } else if (state === 'ADD') {\n return WatchTargetChangeState.Added;\n } else if (state === 'REMOVE') {\n return WatchTargetChangeState.Removed;\n } else if (state === 'CURRENT') {\n return WatchTargetChangeState.Current;\n } else if (state === 'RESET') {\n return WatchTargetChangeState.Reset;\n } else {\n return fail('Got unexpected TargetChange.state: ' + state);\n }\n }\n\n versionFromListenResponse(change: api.ListenResponse): SnapshotVersion {\n // We have only reached a consistent snapshot for the entire stream if there\n // is a read_time set and it applies to all targets (i.e. the list of\n // targets is empty). The backend is guaranteed to send such responses.\n if (!('targetChange' in change)) {\n return SnapshotVersion.min();\n }\n const targetChange = change.targetChange!;\n if (targetChange.targetIds && targetChange.targetIds.length) {\n return SnapshotVersion.min();\n }\n if (!targetChange.readTime) {\n return SnapshotVersion.min();\n }\n return this.fromVersion(targetChange.readTime);\n }\n\n toMutation(mutation: Mutation): api.Write {\n let result: api.Write;\n if (mutation instanceof SetMutation) {\n result = {\n update: this.toMutationDocument(mutation.key, mutation.value)\n };\n } else if (mutation instanceof DeleteMutation) {\n result = { delete: this.toName(mutation.key) };\n } else if (mutation instanceof PatchMutation) {\n result = {\n update: this.toMutationDocument(mutation.key, mutation.data),\n updateMask: this.toDocumentMask(mutation.fieldMask)\n };\n } else if (mutation instanceof TransformMutation) {\n result = {\n transform: {\n document: this.toName(mutation.key),\n fieldTransforms: mutation.fieldTransforms.map(transform =>\n this.toFieldTransform(transform)\n )\n }\n };\n } else if (mutation instanceof VerifyMutation) {\n result = {\n verify: this.toName(mutation.key)\n };\n } else {\n return fail('Unknown mutation type ' + mutation.type);\n }\n\n if (!mutation.precondition.isNone) {\n result.currentDocument = this.toPrecondition(mutation.precondition);\n }\n\n return result;\n }\n\n fromMutation(proto: api.Write): Mutation {\n const precondition = proto.currentDocument\n ? this.fromPrecondition(proto.currentDocument)\n : Precondition.none();\n\n if (proto.update) {\n assertPresent(proto.update.name, 'name');\n const key = this.fromName(proto.update.name);\n const value = new ObjectValue({\n mapValue: { fields: proto.update.fields }\n });\n if (proto.updateMask) {\n const fieldMask = this.fromDocumentMask(proto.updateMask);\n return new PatchMutation(key, value, fieldMask, precondition);\n } else {\n return new SetMutation(key, value, precondition);\n }\n } else if (proto.delete) {\n const key = this.fromName(proto.delete);\n return new DeleteMutation(key, precondition);\n } else if (proto.transform) {\n const key = this.fromName(proto.transform.document!);\n const fieldTransforms = proto.transform.fieldTransforms!.map(transform =>\n this.fromFieldTransform(transform)\n );\n hardAssert(\n precondition.exists === true,\n 'Transforms only support precondition \"exists == true\"'\n );\n return new TransformMutation(key, fieldTransforms);\n } else if (proto.verify) {\n const key = this.fromName(proto.verify);\n return new VerifyMutation(key, precondition);\n } else {\n return fail('unknown mutation proto: ' + JSON.stringify(proto));\n }\n }\n\n private toPrecondition(precondition: Precondition): api.Precondition {\n debugAssert(!precondition.isNone, \"Can't serialize an empty precondition\");\n if (precondition.updateTime !== undefined) {\n return {\n updateTime: this.toVersion(precondition.updateTime)\n };\n } else if (precondition.exists !== undefined) {\n return { exists: precondition.exists };\n } else {\n return fail('Unknown precondition');\n }\n }\n\n private fromPrecondition(precondition: api.Precondition): Precondition {\n if (precondition.updateTime !== undefined) {\n return Precondition.updateTime(this.fromVersion(precondition.updateTime));\n } else if (precondition.exists !== undefined) {\n return Precondition.exists(precondition.exists);\n } else {\n return Precondition.none();\n }\n }\n\n private fromWriteResult(\n proto: api.WriteResult,\n commitTime: api.Timestamp\n ): MutationResult {\n // NOTE: Deletes don't have an updateTime.\n let version = proto.updateTime\n ? this.fromVersion(proto.updateTime)\n : this.fromVersion(commitTime);\n\n if (version.isEqual(SnapshotVersion.min())) {\n // The Firestore Emulator currently returns an update time of 0 for\n // deletes of non-existing documents (rather than null). This breaks the\n // test \"get deleted doc while offline with source=cache\" as NoDocuments\n // with version 0 are filtered by IndexedDb's RemoteDocumentCache.\n // TODO(#2149): Remove this when Emulator is fixed\n version = this.fromVersion(commitTime);\n }\n\n let transformResults: api.Value[] | null = null;\n if (proto.transformResults && proto.transformResults.length > 0) {\n transformResults = proto.transformResults;\n }\n return new MutationResult(version, transformResults);\n }\n\n fromWriteResults(\n protos: api.WriteResult[] | undefined,\n commitTime?: api.Timestamp\n ): MutationResult[] {\n if (protos && protos.length > 0) {\n hardAssert(\n commitTime !== undefined,\n 'Received a write result without a commit time'\n );\n return protos.map(proto => this.fromWriteResult(proto, commitTime));\n } else {\n return [];\n }\n }\n\n private toFieldTransform(fieldTransform: FieldTransform): api.FieldTransform {\n const transform = fieldTransform.transform;\n if (transform instanceof ServerTimestampTransform) {\n return {\n fieldPath: fieldTransform.field.canonicalString(),\n setToServerValue: 'REQUEST_TIME'\n };\n } else if (transform instanceof ArrayUnionTransformOperation) {\n return {\n fieldPath: fieldTransform.field.canonicalString(),\n appendMissingElements: {\n values: transform.elements\n }\n };\n } else if (transform instanceof ArrayRemoveTransformOperation) {\n return {\n fieldPath: fieldTransform.field.canonicalString(),\n removeAllFromArray: {\n values: transform.elements\n }\n };\n } else if (transform instanceof NumericIncrementTransformOperation) {\n return {\n fieldPath: fieldTransform.field.canonicalString(),\n increment: transform.operand\n };\n } else {\n throw fail('Unknown transform: ' + fieldTransform.transform);\n }\n }\n\n private fromFieldTransform(proto: api.FieldTransform): FieldTransform {\n let transform: TransformOperation | null = null;\n if ('setToServerValue' in proto) {\n hardAssert(\n proto.setToServerValue === 'REQUEST_TIME',\n 'Unknown server value transform proto: ' + JSON.stringify(proto)\n );\n transform = ServerTimestampTransform.instance;\n } else if ('appendMissingElements' in proto) {\n const values = proto.appendMissingElements!.values || [];\n transform = new ArrayUnionTransformOperation(values);\n } else if ('removeAllFromArray' in proto) {\n const values = proto.removeAllFromArray!.values || [];\n transform = new ArrayRemoveTransformOperation(values);\n } else if ('increment' in proto) {\n transform = new NumericIncrementTransformOperation(\n this,\n proto.increment!\n );\n } else {\n fail('Unknown transform proto: ' + JSON.stringify(proto));\n }\n const fieldPath = FieldPath.fromServerFormat(proto.fieldPath!);\n return new FieldTransform(fieldPath, transform!);\n }\n\n toDocumentsTarget(target: Target): api.DocumentsTarget {\n return { documents: [this.toQueryPath(target.path)] };\n }\n\n fromDocumentsTarget(documentsTarget: api.DocumentsTarget): Target {\n const count = documentsTarget.documents!.length;\n hardAssert(\n count === 1,\n 'DocumentsTarget contained other than 1 document: ' + count\n );\n const name = documentsTarget.documents![0];\n return Query.atPath(this.fromQueryPath(name)).toTarget();\n }\n\n toQueryTarget(target: Target): api.QueryTarget {\n // Dissect the path into parent, collectionId, and optional key filter.\n const result: api.QueryTarget = { structuredQuery: {} };\n const path = target.path;\n if (target.collectionGroup !== null) {\n debugAssert(\n path.length % 2 === 0,\n 'Collection Group queries should be within a document path or root.'\n );\n result.parent = this.toQueryPath(path);\n result.structuredQuery!.from = [\n {\n collectionId: target.collectionGroup,\n allDescendants: true\n }\n ];\n } else {\n debugAssert(\n path.length % 2 !== 0,\n 'Document queries with filters are not supported.'\n );\n result.parent = this.toQueryPath(path.popLast());\n result.structuredQuery!.from = [{ collectionId: path.lastSegment() }];\n }\n\n const where = this.toFilter(target.filters);\n if (where) {\n result.structuredQuery!.where = where;\n }\n\n const orderBy = this.toOrder(target.orderBy);\n if (orderBy) {\n result.structuredQuery!.orderBy = orderBy;\n }\n\n const limit = this.toInt32Proto(target.limit);\n if (limit !== null) {\n result.structuredQuery!.limit = limit;\n }\n\n if (target.startAt) {\n result.structuredQuery!.startAt = this.toCursor(target.startAt);\n }\n if (target.endAt) {\n result.structuredQuery!.endAt = this.toCursor(target.endAt);\n }\n\n return result;\n }\n\n fromQueryTarget(target: api.QueryTarget): Target {\n let path = this.fromQueryPath(target.parent!);\n\n const query = target.structuredQuery!;\n const fromCount = query.from ? query.from.length : 0;\n let collectionGroup: string | null = null;\n if (fromCount > 0) {\n hardAssert(\n fromCount === 1,\n 'StructuredQuery.from with more than one collection is not supported.'\n );\n const from = query.from![0];\n if (from.allDescendants) {\n collectionGroup = from.collectionId!;\n } else {\n path = path.child(from.collectionId!);\n }\n }\n\n let filterBy: Filter[] = [];\n if (query.where) {\n filterBy = this.fromFilter(query.where);\n }\n\n let orderBy: OrderBy[] = [];\n if (query.orderBy) {\n orderBy = this.fromOrder(query.orderBy);\n }\n\n let limit: number | null = null;\n if (query.limit) {\n limit = this.fromInt32Proto(query.limit);\n }\n\n let startAt: Bound | null = null;\n if (query.startAt) {\n startAt = this.fromCursor(query.startAt);\n }\n\n let endAt: Bound | null = null;\n if (query.endAt) {\n endAt = this.fromCursor(query.endAt);\n }\n\n return new Query(\n path,\n collectionGroup,\n orderBy,\n filterBy,\n limit,\n LimitType.First,\n startAt,\n endAt\n ).toTarget();\n }\n\n toListenRequestLabels(\n targetData: TargetData\n ): api.ApiClientObjectMap<string> | null {\n const value = this.toLabel(targetData.purpose);\n if (value == null) {\n return null;\n } else {\n return {\n 'goog-listen-tags': value\n };\n }\n }\n\n private toLabel(purpose: TargetPurpose): string | null {\n switch (purpose) {\n case TargetPurpose.Listen:\n return null;\n case TargetPurpose.ExistenceFilterMismatch:\n return 'existence-filter-mismatch';\n case TargetPurpose.LimboResolution:\n return 'limbo-document';\n default:\n return fail('Unrecognized query purpose: ' + purpose);\n }\n }\n\n toTarget(targetData: TargetData): api.Target {\n let result: api.Target;\n const target = targetData.target;\n\n if (target.isDocumentQuery()) {\n result = { documents: this.toDocumentsTarget(target) };\n } else {\n result = { query: this.toQueryTarget(target) };\n }\n\n result.targetId = targetData.targetId;\n\n if (targetData.resumeToken.approximateByteSize() > 0) {\n result.resumeToken = this.toBytes(targetData.resumeToken);\n }\n\n return result;\n }\n\n private toFilter(filters: Filter[]): api.Filter | undefined {\n if (filters.length === 0) {\n return;\n }\n const protos = filters.map(filter => {\n if (filter instanceof FieldFilter) {\n return this.toUnaryOrFieldFilter(filter);\n } else {\n return fail('Unrecognized filter: ' + JSON.stringify(filter));\n }\n });\n if (protos.length === 1) {\n return protos[0];\n }\n return { compositeFilter: { op: 'AND', filters: protos } };\n }\n\n private fromFilter(filter: api.Filter | undefined): Filter[] {\n if (!filter) {\n return [];\n } else if (filter.unaryFilter !== undefined) {\n return [this.fromUnaryFilter(filter)];\n } else if (filter.fieldFilter !== undefined) {\n return [this.fromFieldFilter(filter)];\n } else if (filter.compositeFilter !== undefined) {\n return filter.compositeFilter\n .filters!.map(f => this.fromFilter(f))\n .reduce((accum, current) => accum.concat(current));\n } else {\n return fail('Unknown filter: ' + JSON.stringify(filter));\n }\n }\n\n private toOrder(orderBys: OrderBy[]): api.Order[] | undefined {\n if (orderBys.length === 0) {\n return;\n }\n return orderBys.map(order => this.toPropertyOrder(order));\n }\n\n private fromOrder(orderBys: api.Order[]): OrderBy[] {\n return orderBys.map(order => this.fromPropertyOrder(order));\n }\n\n private toCursor(cursor: Bound): api.Cursor {\n return {\n before: cursor.before,\n values: cursor.position\n };\n }\n\n private fromCursor(cursor: api.Cursor): Bound {\n const before = !!cursor.before;\n const position = cursor.values || [];\n return new Bound(position, before);\n }\n\n // visible for testing\n toDirection(dir: Direction): api.OrderDirection {\n return DIRECTIONS[dir];\n }\n\n // visible for testing\n fromDirection(dir: api.OrderDirection | undefined): Direction | undefined {\n switch (dir) {\n case 'ASCENDING':\n return Direction.ASCENDING;\n case 'DESCENDING':\n return Direction.DESCENDING;\n default:\n return undefined;\n }\n }\n\n // visible for testing\n toOperatorName(op: Operator): api.FieldFilterOp {\n return OPERATORS[op];\n }\n\n fromOperatorName(op: api.FieldFilterOp): Operator {\n switch (op) {\n case 'EQUAL':\n return Operator.EQUAL;\n case 'GREATER_THAN':\n return Operator.GREATER_THAN;\n case 'GREATER_THAN_OR_EQUAL':\n return Operator.GREATER_THAN_OR_EQUAL;\n case 'LESS_THAN':\n return Operator.LESS_THAN;\n case 'LESS_THAN_OR_EQUAL':\n return Operator.LESS_THAN_OR_EQUAL;\n case 'ARRAY_CONTAINS':\n return Operator.ARRAY_CONTAINS;\n case 'IN':\n return Operator.IN;\n case 'ARRAY_CONTAINS_ANY':\n return Operator.ARRAY_CONTAINS_ANY;\n case 'OPERATOR_UNSPECIFIED':\n return fail('Unspecified operator');\n default:\n return fail('Unknown operator');\n }\n }\n\n toFieldPathReference(path: FieldPath): api.FieldReference {\n return { fieldPath: path.canonicalString() };\n }\n\n fromFieldPathReference(fieldReference: api.FieldReference): FieldPath {\n return FieldPath.fromServerFormat(fieldReference.fieldPath!);\n }\n\n // visible for testing\n toPropertyOrder(orderBy: OrderBy): api.Order {\n return {\n field: this.toFieldPathReference(orderBy.field),\n direction: this.toDirection(orderBy.dir)\n };\n }\n\n fromPropertyOrder(orderBy: api.Order): OrderBy {\n return new OrderBy(\n this.fromFieldPathReference(orderBy.field!),\n this.fromDirection(orderBy.direction)\n );\n }\n\n fromFieldFilter(filter: api.Filter): Filter {\n return FieldFilter.create(\n this.fromFieldPathReference(filter.fieldFilter!.field!),\n this.fromOperatorName(filter.fieldFilter!.op!),\n filter.fieldFilter!.value!\n );\n }\n\n // visible for testing\n toUnaryOrFieldFilter(filter: FieldFilter): api.Filter {\n if (filter.op === Operator.EQUAL) {\n if (isNanValue(filter.value)) {\n return {\n unaryFilter: {\n field: this.toFieldPathReference(filter.field),\n op: 'IS_NAN'\n }\n };\n } else if (isNullValue(filter.value)) {\n return {\n unaryFilter: {\n field: this.toFieldPathReference(filter.field),\n op: 'IS_NULL'\n }\n };\n }\n }\n return {\n fieldFilter: {\n field: this.toFieldPathReference(filter.field),\n op: this.toOperatorName(filter.op),\n value: filter.value\n }\n };\n }\n\n fromUnaryFilter(filter: api.Filter): Filter {\n switch (filter.unaryFilter!.op!) {\n case 'IS_NAN':\n const nanField = this.fromFieldPathReference(\n filter.unaryFilter!.field!\n );\n return FieldFilter.create(nanField, Operator.EQUAL, {\n doubleValue: NaN\n });\n case 'IS_NULL':\n const nullField = this.fromFieldPathReference(\n filter.unaryFilter!.field!\n );\n return FieldFilter.create(nullField, Operator.EQUAL, {\n nullValue: 'NULL_VALUE'\n });\n case 'OPERATOR_UNSPECIFIED':\n return fail('Unspecified filter');\n default:\n return fail('Unknown filter');\n }\n }\n\n toDocumentMask(fieldMask: FieldMask): api.DocumentMask {\n const canonicalFields: string[] = [];\n fieldMask.fields.forEach(field =>\n canonicalFields.push(field.canonicalString())\n );\n return {\n fieldPaths: canonicalFields\n };\n }\n\n fromDocumentMask(proto: api.DocumentMask): FieldMask {\n const paths = proto.fieldPaths || [];\n return new FieldMask(paths.map(path => FieldPath.fromServerFormat(path)));\n }\n}\n\nexport function isValidResourceName(path: ResourcePath): boolean {\n // Resource names have at least 4 components (project ID, database ID)\n return (\n path.length >= 4 &&\n path.get(0) === 'projects' &&\n path.get(2) === 'databases'\n );\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { DatabaseId, DatabaseInfo } from '../core/database_info';\nimport { Connection } from '../remote/connection';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport { fail } from '../util/assert';\nimport { ConnectivityMonitor } from './../remote/connectivity_monitor';\n\n/**\n * Provides a common interface to load anything platform dependent, e.g.\n * the connection implementation.\n *\n * An implementation of this must be provided at compile time for the platform.\n */\n// TODO: Consider only exposing the APIs of 'document' and 'window' that we\n// use in our client.\nexport interface Platform {\n loadConnection(databaseInfo: DatabaseInfo): Promise<Connection>;\n newConnectivityMonitor(): ConnectivityMonitor;\n newSerializer(databaseId: DatabaseId): JsonProtoSerializer;\n\n /** Formats an object as a JSON string, suitable for logging. */\n formatJSON(value: unknown): string;\n\n /** Converts a Base64 encoded string to a binary string. */\n atob(encoded: string): string;\n\n /** Converts a binary string to a Base64 encoded string. */\n btoa(raw: string): string;\n\n /**\n * Generates `nBytes` of random bytes.\n *\n * If `nBytes < 0` , an error will be thrown.\n */\n randomBytes(nBytes: number): Uint8Array;\n\n /** The Platform's 'window' implementation or null if not available. */\n readonly window: Window | null;\n\n /** The Platform's 'document' implementation or null if not available. */\n readonly document: Document | null;\n\n /** True if and only if the Base64 conversion functions are available. */\n readonly base64Available: boolean;\n}\n\n/**\n * Provides singleton helpers where setup code can inject a platform at runtime.\n * setPlatform needs to be set before Firestore is used and must be set exactly\n * once.\n */\nexport class PlatformSupport {\n private static platform: Platform;\n static setPlatform(platform: Platform): void {\n if (PlatformSupport.platform) {\n fail('Platform already defined');\n }\n PlatformSupport.platform = platform;\n }\n\n static getPlatform(): Platform {\n if (!PlatformSupport.platform) {\n fail('Platform not set');\n }\n return PlatformSupport.platform;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Logger, LogLevel } from '@firebase/logger';\nimport { SDK_VERSION } from '../core/version';\nimport { PlatformSupport } from '../platform/platform';\n\nexport { LogLevel };\n\nconst logClient = new Logger('@firebase/firestore');\n\n// Helper methods are needed because variables can't be exported as read/write\nexport function getLogLevel(): LogLevel {\n return logClient.logLevel;\n}\n\nexport function setLogLevel(newLevel: LogLevel): void {\n logClient.logLevel = newLevel;\n}\n\nexport function logDebug(msg: string, ...obj: unknown[]): void {\n if (logClient.logLevel <= LogLevel.DEBUG) {\n const args = obj.map(argToString);\n logClient.debug(`Firestore (${SDK_VERSION}): ${msg}`, ...args);\n }\n}\n\nexport function logError(msg: string, ...obj: unknown[]): void {\n if (logClient.logLevel <= LogLevel.ERROR) {\n const args = obj.map(argToString);\n logClient.error(`Firestore (${SDK_VERSION}): ${msg}`, ...args);\n }\n}\n\n/**\n * Converts an additional log parameter to a string representation.\n */\nfunction argToString(obj: unknown): string | unknown {\n if (typeof obj === 'string') {\n return obj;\n } else {\n const platform = PlatformSupport.getPlatform();\n try {\n return platform.formatJSON(obj);\n } catch (e) {\n // Converting to JSON failed, just log the object directly\n return obj;\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SDK_VERSION } from '../core/version';\nimport { logError } from './log';\n\n/**\n * Unconditionally fails, throwing an Error with the given message.\n * Messages are stripped in production builds.\n *\n * Returns `never` and can be used in expressions:\n * @example\n * let futureVar = fail('not implemented yet');\n */\nexport function fail(failure: string = 'Unexpected state'): never {\n // Log the failure in addition to throw an exception, just in case the\n // exception is swallowed.\n const message =\n `FIRESTORE (${SDK_VERSION}) INTERNAL ASSERTION FAILED: ` + failure;\n logError(message);\n\n // NOTE: We don't use FirestoreError here because these are internal failures\n // that cannot be handled by the user. (Also it would create a circular\n // dependency between the error and assert modules which doesn't work.)\n throw new Error(message);\n}\n\n/**\n * Fails if the given assertion condition is false, throwing an Error with the\n * given message if it did.\n *\n * Messages are stripped in production builds.\n */\nexport function hardAssert(\n assertion: boolean,\n message?: string\n): asserts assertion {\n if (!assertion) {\n fail(message);\n }\n}\n\n/**\n * Fails if the given assertion condition is false, throwing an Error with the\n * given message if it did.\n *\n * The code of callsites invoking this function are stripped out in production\n * builds. Any side-effects of code within the debugAssert() invocation will not\n * happen in this case.\n */\nexport function debugAssert(\n assertion: boolean,\n message: string\n): asserts assertion {\n if (!assertion) {\n fail(message);\n }\n}\n\n/**\n * Casts `obj` to `T`. In non-production builds, verifies that `obj` is an\n * instance of `T` before casting.\n */\nexport function debugCast<T>(\n obj: object,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n constructor: { new (...args: any[]): T }\n): T {\n debugAssert(\n obj instanceof constructor,\n `Expected type '${constructor.name}', but was '${obj.constructor.name}'`\n );\n return obj as T;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert } from './assert';\nimport { PlatformSupport } from '../platform/platform';\n\nexport type EventHandler<E> = (value: E) => void;\nexport interface Indexable {\n [k: string]: unknown;\n}\n\nexport class AutoId {\n static newId(): string {\n // Alphanumeric characters\n const chars =\n 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n // The largest byte value that is a multiple of `char.length`.\n const maxMultiple = Math.floor(256 / chars.length) * chars.length;\n debugAssert(\n 0 < maxMultiple && maxMultiple < 256,\n `Expect maxMultiple to be (0, 256), but got ${maxMultiple}`\n );\n\n let autoId = '';\n const targetLength = 20;\n while (autoId.length < targetLength) {\n const bytes = PlatformSupport.getPlatform().randomBytes(40);\n for (let i = 0; i < bytes.length; ++i) {\n // Only accept values that are [0, maxMultiple), this ensures they can\n // be evenly mapped to indices of `chars` via a modulo operation.\n if (autoId.length < targetLength && bytes[i] < maxMultiple) {\n autoId += chars.charAt(bytes[i] % chars.length);\n }\n }\n }\n debugAssert(autoId.length === targetLength, 'Invalid auto ID: ' + autoId);\n\n return autoId;\n }\n}\n\nexport function primitiveComparator<T>(left: T, right: T): number {\n if (left < right) {\n return -1;\n }\n if (left > right) {\n return 1;\n }\n return 0;\n}\n\nexport interface Equatable<T> {\n isEqual(other: T): boolean;\n}\n\n/** Helper to compare arrays using isEqual(). */\nexport function arrayEquals<T>(\n left: T[],\n right: T[],\n comparator: (l: T, r: T) => boolean\n): boolean {\n if (left.length !== right.length) {\n return false;\n }\n return left.every((value, index) => comparator(value, right[index]));\n}\n/**\n * Returns the immediate lexicographically-following string. This is useful to\n * construct an inclusive range for indexeddb iterators.\n */\nexport function immediateSuccessor(s: string): string {\n // Return the input string, with an additional NUL byte appended.\n return s + '\\0';\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { primitiveComparator } from '../util/misc';\n\nexport class DatabaseInfo {\n /**\n * Constructs a DatabaseInfo using the provided host, databaseId and\n * persistenceKey.\n *\n * @param databaseId The database to use.\n * @param persistenceKey A unique identifier for this Firestore's local\n * storage (used in conjunction with the databaseId).\n * @param host The Firestore backend host to connect to.\n * @param ssl Whether to use SSL when connecting.\n * @param forceLongPolling Whether to use the forceLongPolling option\n * when using WebChannel as the network transport.\n */\n constructor(\n readonly databaseId: DatabaseId,\n readonly persistenceKey: string,\n readonly host: string,\n readonly ssl: boolean,\n readonly forceLongPolling: boolean\n ) {}\n}\n\n/** The default database name for a project. */\nconst DEFAULT_DATABASE_NAME = '(default)';\n\n/** Represents the database ID a Firestore client is associated with. */\nexport class DatabaseId {\n readonly database: string;\n constructor(readonly projectId: string, database?: string) {\n this.database = database ? database : DEFAULT_DATABASE_NAME;\n }\n\n get isDefaultDatabase(): boolean {\n return this.database === DEFAULT_DATABASE_NAME;\n }\n\n isEqual(other: {}): boolean {\n return (\n other instanceof DatabaseId &&\n other.projectId === this.projectId &&\n other.database === this.database\n );\n }\n\n compareTo(other: DatabaseId): number {\n return (\n primitiveComparator(this.projectId, other.projectId) ||\n primitiveComparator(this.database, other.database)\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Equatable } from './misc';\nimport { forEach, isEmpty } from './obj';\n\ntype Entry<K, V> = [K, V];\n\n/**\n * A map implementation that uses objects as keys. Objects must implement the\n * Equatable interface and must be immutable. Entries in the map are stored\n * together with the key being produced from the mapKeyFn. This map\n * automatically handles collisions of keys.\n */\nexport class ObjectMap<KeyType extends Equatable<KeyType>, ValueType> {\n /**\n * The inner map for a key -> value pair. Due to the possibility of\n * collisions we keep a list of entries that we do a linear search through\n * to find an actual match. Note that collisions should be rare, so we still\n * expect near constant time lookups in practice.\n */\n private inner: {\n [canonicalId: string]: Array<Entry<KeyType, ValueType>>;\n } = {};\n\n constructor(private mapKeyFn: (key: KeyType) => string) {}\n\n /** Get a value for this key, or undefined if it does not exist. */\n get(key: KeyType): ValueType | undefined {\n const id = this.mapKeyFn(key);\n const matches = this.inner[id];\n if (matches === undefined) {\n return undefined;\n }\n for (const [otherKey, value] of matches) {\n if (otherKey.isEqual(key)) {\n return value;\n }\n }\n return undefined;\n }\n\n has(key: KeyType): boolean {\n return this.get(key) !== undefined;\n }\n\n /** Put this key and value in the map. */\n set(key: KeyType, value: ValueType): void {\n const id = this.mapKeyFn(key);\n const matches = this.inner[id];\n if (matches === undefined) {\n this.inner[id] = [[key, value]];\n return;\n }\n for (let i = 0; i < matches.length; i++) {\n if (matches[i][0].isEqual(key)) {\n matches[i] = [key, value];\n return;\n }\n }\n matches.push([key, value]);\n }\n\n /**\n * Remove this key from the map. Returns a boolean if anything was deleted.\n */\n delete(key: KeyType): boolean {\n const id = this.mapKeyFn(key);\n const matches = this.inner[id];\n if (matches === undefined) {\n return false;\n }\n for (let i = 0; i < matches.length; i++) {\n if (matches[i][0].isEqual(key)) {\n if (matches.length === 1) {\n delete this.inner[id];\n } else {\n matches.splice(i, 1);\n }\n return true;\n }\n }\n return false;\n }\n\n forEach(fn: (key: KeyType, val: ValueType) => void): void {\n forEach(this.inner, (_, entries) => {\n for (const [k, v] of entries) {\n fn(k, v);\n }\n });\n }\n\n isEmpty(): boolean {\n return isEmpty(this.inner);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Timestamp } from '../api/timestamp';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { BatchId } from '../core/types';\nimport { hardAssert, debugAssert } from '../util/assert';\nimport { arrayEquals } from '../util/misc';\nimport { ByteString } from '../util/byte_string';\nimport {\n documentKeySet,\n DocumentKeySet,\n DocumentVersionMap,\n documentVersionMap,\n MaybeDocumentMap\n} from './collections';\nimport { MaybeDocument } from './document';\nimport { DocumentKey } from './document_key';\nimport { Mutation, MutationResult } from './mutation';\n\nexport const BATCHID_UNKNOWN = -1;\n\n/**\n * A batch of mutations that will be sent as one unit to the backend.\n */\nexport class MutationBatch {\n /**\n * @param batchId The unique ID of this mutation batch.\n * @param localWriteTime The original write time of this mutation.\n * @param baseMutations Mutations that are used to populate the base\n * values when this mutation is applied locally. This can be used to locally\n * overwrite values that are persisted in the remote document cache. Base\n * mutations are never sent to the backend.\n * @param mutations The user-provided mutations in this mutation batch.\n * User-provided mutations are applied both locally and remotely on the\n * backend.\n */\n constructor(\n public batchId: BatchId,\n public localWriteTime: Timestamp,\n public baseMutations: Mutation[],\n public mutations: Mutation[]\n ) {\n debugAssert(mutations.length > 0, 'Cannot create an empty mutation batch');\n }\n\n /**\n * Applies all the mutations in this MutationBatch to the specified document\n * to create a new remote document\n *\n * @param docKey The key of the document to apply mutations to.\n * @param maybeDoc The document to apply mutations to.\n * @param batchResult The result of applying the MutationBatch to the\n * backend.\n */\n applyToRemoteDocument(\n docKey: DocumentKey,\n maybeDoc: MaybeDocument | null,\n batchResult: MutationBatchResult\n ): MaybeDocument | null {\n if (maybeDoc) {\n debugAssert(\n maybeDoc.key.isEqual(docKey),\n `applyToRemoteDocument: key ${docKey} should match maybeDoc key\n ${maybeDoc.key}`\n );\n }\n\n const mutationResults = batchResult.mutationResults;\n debugAssert(\n mutationResults.length === this.mutations.length,\n `Mismatch between mutations length\n (${this.mutations.length}) and mutation results length\n (${mutationResults.length}).`\n );\n\n for (let i = 0; i < this.mutations.length; i++) {\n const mutation = this.mutations[i];\n if (mutation.key.isEqual(docKey)) {\n const mutationResult = mutationResults[i];\n maybeDoc = mutation.applyToRemoteDocument(maybeDoc, mutationResult);\n }\n }\n return maybeDoc;\n }\n\n /**\n * Computes the local view of a document given all the mutations in this\n * batch.\n *\n * @param docKey The key of the document to apply mutations to.\n * @param maybeDoc The document to apply mutations to.\n */\n applyToLocalView(\n docKey: DocumentKey,\n maybeDoc: MaybeDocument | null\n ): MaybeDocument | null {\n if (maybeDoc) {\n debugAssert(\n maybeDoc.key.isEqual(docKey),\n `applyToLocalDocument: key ${docKey} should match maybeDoc key\n ${maybeDoc.key}`\n );\n }\n\n // First, apply the base state. This allows us to apply non-idempotent\n // transform against a consistent set of values.\n for (const mutation of this.baseMutations) {\n if (mutation.key.isEqual(docKey)) {\n maybeDoc = mutation.applyToLocalView(\n maybeDoc,\n maybeDoc,\n this.localWriteTime\n );\n }\n }\n\n const baseDoc = maybeDoc;\n\n // Second, apply all user-provided mutations.\n for (const mutation of this.mutations) {\n if (mutation.key.isEqual(docKey)) {\n maybeDoc = mutation.applyToLocalView(\n maybeDoc,\n baseDoc,\n this.localWriteTime\n );\n }\n }\n return maybeDoc;\n }\n\n /**\n * Computes the local view for all provided documents given the mutations in\n * this batch.\n */\n applyToLocalDocumentSet(maybeDocs: MaybeDocumentMap): MaybeDocumentMap {\n // TODO(mrschmidt): This implementation is O(n^2). If we apply the mutations\n // directly (as done in `applyToLocalView()`), we can reduce the complexity\n // to O(n).\n let mutatedDocuments = maybeDocs;\n this.mutations.forEach(m => {\n const mutatedDocument = this.applyToLocalView(\n m.key,\n maybeDocs.get(m.key)\n );\n if (mutatedDocument) {\n mutatedDocuments = mutatedDocuments.insert(m.key, mutatedDocument);\n }\n });\n return mutatedDocuments;\n }\n\n keys(): DocumentKeySet {\n return this.mutations.reduce(\n (keys, m) => keys.add(m.key),\n documentKeySet()\n );\n }\n\n isEqual(other: MutationBatch): boolean {\n return (\n this.batchId === other.batchId &&\n arrayEquals(this.mutations, other.mutations, (l, r) => l.isEqual(r)) &&\n arrayEquals(this.baseMutations, other.baseMutations, (l, r) =>\n l.isEqual(r)\n )\n );\n }\n}\n\n/** The result of applying a mutation batch to the backend. */\nexport class MutationBatchResult {\n private constructor(\n readonly batch: MutationBatch,\n readonly commitVersion: SnapshotVersion,\n readonly mutationResults: MutationResult[],\n readonly streamToken: ByteString,\n /**\n * A pre-computed mapping from each mutated document to the resulting\n * version.\n */\n readonly docVersions: DocumentVersionMap\n ) {}\n\n /**\n * Creates a new MutationBatchResult for the given batch and results. There\n * must be one result for each mutation in the batch. This static factory\n * caches a document=>version mapping (docVersions).\n */\n static from(\n batch: MutationBatch,\n commitVersion: SnapshotVersion,\n results: MutationResult[],\n streamToken: ByteString\n ): MutationBatchResult {\n hardAssert(\n batch.mutations.length === results.length,\n 'Mutations sent ' +\n batch.mutations.length +\n ' must equal results received ' +\n results.length\n );\n\n let versionMap = documentVersionMap();\n const mutations = batch.mutations;\n for (let i = 0; i < mutations.length; i++) {\n versionMap = versionMap.insert(mutations[i].key, results[i].version);\n }\n\n return new MutationBatchResult(\n batch,\n commitVersion,\n results,\n streamToken,\n versionMap\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { fail } from '../util/assert';\n\nexport type FulfilledHandler<T, R> =\n | ((result: T) => R | PersistencePromise<R>)\n | null;\nexport type RejectedHandler<R> =\n | ((reason: Error) => R | PersistencePromise<R>)\n | null;\nexport type Resolver<T> = (value?: T) => void;\nexport type Rejector = (error: Error) => void;\n\n/**\n * PersistencePromise<> is essentially a re-implementation of Promise<> except\n * it has a .next() method instead of .then() and .next() and .catch() callbacks\n * are executed synchronously when a PersistencePromise resolves rather than\n * asynchronously (Promise<> implementations use setImmediate() or similar).\n *\n * This is necessary to interoperate with IndexedDB which will automatically\n * commit transactions if control is returned to the event loop without\n * synchronously initiating another operation on the transaction.\n *\n * NOTE: .then() and .catch() only allow a single consumer, unlike normal\n * Promises.\n */\nexport class PersistencePromise<T> {\n // NOTE: next/catchCallback will always point to our own wrapper functions,\n // not the user's raw next() or catch() callbacks.\n private nextCallback: FulfilledHandler<T, unknown> = null;\n private catchCallback: RejectedHandler<unknown> = null;\n\n // When the operation resolves, we'll set result or error and mark isDone.\n private result: T | undefined = undefined;\n private error: Error | undefined = undefined;\n private isDone = false;\n\n // Set to true when .then() or .catch() are called and prevents additional\n // chaining.\n private callbackAttached = false;\n\n constructor(callback: (resolve: Resolver<T>, reject: Rejector) => void) {\n callback(\n value => {\n this.isDone = true;\n this.result = value;\n if (this.nextCallback) {\n // value should be defined unless T is Void, but we can't express\n // that in the type system.\n this.nextCallback(value!);\n }\n },\n error => {\n this.isDone = true;\n this.error = error;\n if (this.catchCallback) {\n this.catchCallback(error);\n }\n }\n );\n }\n\n catch<R>(\n fn: (error: Error) => R | PersistencePromise<R>\n ): PersistencePromise<R> {\n return this.next(undefined, fn);\n }\n\n next<R>(\n nextFn?: FulfilledHandler<T, R>,\n catchFn?: RejectedHandler<R>\n ): PersistencePromise<R> {\n if (this.callbackAttached) {\n fail('Called next() or catch() twice for PersistencePromise');\n }\n this.callbackAttached = true;\n if (this.isDone) {\n if (!this.error) {\n return this.wrapSuccess(nextFn, this.result!);\n } else {\n return this.wrapFailure(catchFn, this.error);\n }\n } else {\n return new PersistencePromise<R>((resolve, reject) => {\n this.nextCallback = (value: T) => {\n this.wrapSuccess(nextFn, value).next(resolve, reject);\n };\n this.catchCallback = (error: Error) => {\n this.wrapFailure(catchFn, error).next(resolve, reject);\n };\n });\n }\n }\n\n toPromise(): Promise<T> {\n return new Promise((resolve, reject) => {\n this.next(resolve, reject);\n });\n }\n\n private wrapUserFunction<R>(\n fn: () => R | PersistencePromise<R>\n ): PersistencePromise<R> {\n try {\n const result = fn();\n if (result instanceof PersistencePromise) {\n return result;\n } else {\n return PersistencePromise.resolve(result);\n }\n } catch (e) {\n return PersistencePromise.reject<R>(e);\n }\n }\n\n private wrapSuccess<R>(\n nextFn: FulfilledHandler<T, R> | undefined,\n value: T\n ): PersistencePromise<R> {\n if (nextFn) {\n return this.wrapUserFunction(() => nextFn(value));\n } else {\n // If there's no nextFn, then R must be the same as T\n return PersistencePromise.resolve<R>((value as unknown) as R);\n }\n }\n\n private wrapFailure<R>(\n catchFn: RejectedHandler<R> | undefined,\n error: Error\n ): PersistencePromise<R> {\n if (catchFn) {\n return this.wrapUserFunction(() => catchFn(error));\n } else {\n return PersistencePromise.reject<R>(error);\n }\n }\n\n static resolve(): PersistencePromise<void>;\n static resolve<R>(result: R): PersistencePromise<R>;\n static resolve<R>(result?: R): PersistencePromise<R | void> {\n return new PersistencePromise<R | void>((resolve, reject) => {\n resolve(result);\n });\n }\n\n static reject<R>(error: Error): PersistencePromise<R> {\n return new PersistencePromise<R>((resolve, reject) => {\n reject(error);\n });\n }\n\n static waitFor(\n // Accept all Promise types in waitFor().\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n all: { forEach: (cb: (el: PersistencePromise<any>) => void) => void }\n ): PersistencePromise<void> {\n return new PersistencePromise<void>((resolve, reject) => {\n let expectedCount = 0;\n let resolvedCount = 0;\n let done = false;\n\n all.forEach(element => {\n ++expectedCount;\n element.next(\n () => {\n ++resolvedCount;\n if (done && resolvedCount === expectedCount) {\n resolve();\n }\n },\n err => reject(err)\n );\n });\n\n done = true;\n if (resolvedCount === expectedCount) {\n resolve();\n }\n });\n }\n\n /**\n * Given an array of predicate functions that asynchronously evaluate to a\n * boolean, implements a short-circuiting `or` between the results. Predicates\n * will be evaluated until one of them returns `true`, then stop. The final\n * result will be whether any of them returned `true`.\n */\n static or(\n predicates: Array<() => PersistencePromise<boolean>>\n ): PersistencePromise<boolean> {\n let p: PersistencePromise<boolean> = PersistencePromise.resolve<boolean>(\n false\n );\n for (const predicate of predicates) {\n p = p.next(isTrue => {\n if (isTrue) {\n return PersistencePromise.resolve<boolean>(isTrue);\n } else {\n return predicate();\n }\n });\n }\n return p;\n }\n\n /**\n * Given an iterable, call the given function on each element in the\n * collection and wait for all of the resulting concurrent PersistencePromises\n * to resolve.\n */\n static forEach<R, S>(\n collection: { forEach: (cb: (r: R, s: S) => void) => void },\n f:\n | ((r: R, s: S) => PersistencePromise<void>)\n | ((r: R) => PersistencePromise<void>)\n ): PersistencePromise<void>;\n static forEach<R>(\n collection: { forEach: (cb: (r: R) => void) => void },\n f: (r: R) => PersistencePromise<void>\n ): PersistencePromise<void>;\n static forEach<R, S>(\n collection: { forEach: (cb: (r: R, s?: S) => void) => void },\n f: (r: R, s?: S) => PersistencePromise<void>\n ): PersistencePromise<void> {\n const promises: Array<PersistencePromise<void>> = [];\n collection.forEach((r, s) => {\n promises.push(f.call(this, r, s));\n });\n return this.waitFor(promises);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Query } from '../core/query';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport {\n DocumentKeySet,\n documentKeySet,\n DocumentMap,\n documentMap,\n MaybeDocumentMap,\n maybeDocumentMap,\n NullableMaybeDocumentMap,\n nullableMaybeDocumentMap\n} from '../model/collections';\nimport { Document, MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { MutationBatch } from '../model/mutation_batch';\nimport { ResourcePath } from '../model/path';\n\nimport { debugAssert } from '../util/assert';\nimport { IndexManager } from './index_manager';\nimport { MutationQueue } from './mutation_queue';\nimport { PatchMutation } from '../model/mutation';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { RemoteDocumentCache } from './remote_document_cache';\n\n/**\n * A readonly view of the local state of all documents we're tracking (i.e. we\n * have a cached version in remoteDocumentCache or local mutations for the\n * document). The view is computed by applying the mutations in the\n * MutationQueue to the RemoteDocumentCache.\n */\nexport class LocalDocumentsView {\n constructor(\n readonly remoteDocumentCache: RemoteDocumentCache,\n readonly mutationQueue: MutationQueue,\n readonly indexManager: IndexManager\n ) {}\n\n /**\n * Get the local view of the document identified by `key`.\n *\n * @return Local view of the document or null if we don't have any cached\n * state for it.\n */\n getDocument(\n transaction: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n return this.mutationQueue\n .getAllMutationBatchesAffectingDocumentKey(transaction, key)\n .next(batches => this.getDocumentInternal(transaction, key, batches));\n }\n\n /** Internal version of `getDocument` that allows reusing batches. */\n private getDocumentInternal(\n transaction: PersistenceTransaction,\n key: DocumentKey,\n inBatches: MutationBatch[]\n ): PersistencePromise<MaybeDocument | null> {\n return this.remoteDocumentCache.getEntry(transaction, key).next(doc => {\n for (const batch of inBatches) {\n doc = batch.applyToLocalView(key, doc);\n }\n return doc;\n });\n }\n\n // Returns the view of the given `docs` as they would appear after applying\n // all mutations in the given `batches`.\n private applyLocalMutationsToDocuments(\n transaction: PersistenceTransaction,\n docs: NullableMaybeDocumentMap,\n batches: MutationBatch[]\n ): NullableMaybeDocumentMap {\n let results = nullableMaybeDocumentMap();\n docs.forEach((key, localView) => {\n for (const batch of batches) {\n localView = batch.applyToLocalView(key, localView);\n }\n results = results.insert(key, localView);\n });\n return results;\n }\n\n /**\n * Gets the local view of the documents identified by `keys`.\n *\n * If we don't have cached state for a document in `keys`, a NoDocument will\n * be stored for that key in the resulting set.\n */\n getDocuments(\n transaction: PersistenceTransaction,\n keys: DocumentKeySet\n ): PersistencePromise<MaybeDocumentMap> {\n return this.remoteDocumentCache\n .getEntries(transaction, keys)\n .next(docs => this.getLocalViewOfDocuments(transaction, docs));\n }\n\n /**\n * Similar to `getDocuments`, but creates the local view from the given\n * `baseDocs` without retrieving documents from the local store.\n */\n getLocalViewOfDocuments(\n transaction: PersistenceTransaction,\n baseDocs: NullableMaybeDocumentMap\n ): PersistencePromise<MaybeDocumentMap> {\n return this.mutationQueue\n .getAllMutationBatchesAffectingDocumentKeys(transaction, baseDocs)\n .next(batches => {\n const docs = this.applyLocalMutationsToDocuments(\n transaction,\n baseDocs,\n batches\n );\n let results = maybeDocumentMap();\n docs.forEach((key, maybeDoc) => {\n // TODO(http://b/32275378): Don't conflate missing / deleted.\n if (!maybeDoc) {\n maybeDoc = new NoDocument(key, SnapshotVersion.min());\n }\n results = results.insert(key, maybeDoc);\n });\n\n return results;\n });\n }\n\n /**\n * Performs a query against the local view of all documents.\n *\n * @param transaction The persistence transaction.\n * @param query The query to match documents against.\n * @param sinceReadTime If not set to SnapshotVersion.min(), return only\n * documents that have been read since this snapshot version (exclusive).\n */\n getDocumentsMatchingQuery(\n transaction: PersistenceTransaction,\n query: Query,\n sinceReadTime: SnapshotVersion\n ): PersistencePromise<DocumentMap> {\n if (query.isDocumentQuery()) {\n return this.getDocumentsMatchingDocumentQuery(transaction, query.path);\n } else if (query.isCollectionGroupQuery()) {\n return this.getDocumentsMatchingCollectionGroupQuery(\n transaction,\n query,\n sinceReadTime\n );\n } else {\n return this.getDocumentsMatchingCollectionQuery(\n transaction,\n query,\n sinceReadTime\n );\n }\n }\n\n private getDocumentsMatchingDocumentQuery(\n transaction: PersistenceTransaction,\n docPath: ResourcePath\n ): PersistencePromise<DocumentMap> {\n // Just do a simple document lookup.\n return this.getDocument(transaction, new DocumentKey(docPath)).next(\n maybeDoc => {\n let result = documentMap();\n if (maybeDoc instanceof Document) {\n result = result.insert(maybeDoc.key, maybeDoc);\n }\n return result;\n }\n );\n }\n\n private getDocumentsMatchingCollectionGroupQuery(\n transaction: PersistenceTransaction,\n query: Query,\n sinceReadTime: SnapshotVersion\n ): PersistencePromise<DocumentMap> {\n debugAssert(\n query.path.isEmpty(),\n 'Currently we only support collection group queries at the root.'\n );\n const collectionId = query.collectionGroup!;\n let results = documentMap();\n return this.indexManager\n .getCollectionParents(transaction, collectionId)\n .next(parents => {\n // Perform a collection query against each parent that contains the\n // collectionId and aggregate the results.\n return PersistencePromise.forEach(parents, (parent: ResourcePath) => {\n const collectionQuery = query.asCollectionQueryAtPath(\n parent.child(collectionId)\n );\n return this.getDocumentsMatchingCollectionQuery(\n transaction,\n collectionQuery,\n sinceReadTime\n ).next(r => {\n r.forEach((key, doc) => {\n results = results.insert(key, doc);\n });\n });\n }).next(() => results);\n });\n }\n\n private getDocumentsMatchingCollectionQuery(\n transaction: PersistenceTransaction,\n query: Query,\n sinceReadTime: SnapshotVersion\n ): PersistencePromise<DocumentMap> {\n // Query the remote documents and overlay mutations.\n let results: DocumentMap;\n let mutationBatches: MutationBatch[];\n return this.remoteDocumentCache\n .getDocumentsMatchingQuery(transaction, query, sinceReadTime)\n .next(queryResults => {\n results = queryResults;\n return this.mutationQueue.getAllMutationBatchesAffectingQuery(\n transaction,\n query\n );\n })\n .next(matchingMutationBatches => {\n mutationBatches = matchingMutationBatches;\n // It is possible that a PatchMutation can make a document match a query, even if\n // the version in the RemoteDocumentCache is not a match yet (waiting for server\n // to ack). To handle this, we find all document keys affected by the PatchMutations\n // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,\n // otherwise those `PatchMutations` will be ignored because no base document can be found,\n // and lead to missing result for the query.\n return this.addMissingBaseDocuments(\n transaction,\n mutationBatches,\n results\n ).next(mergedDocuments => {\n results = mergedDocuments;\n\n for (const batch of mutationBatches) {\n for (const mutation of batch.mutations) {\n const key = mutation.key;\n const baseDoc = results.get(key);\n const mutatedDoc = mutation.applyToLocalView(\n baseDoc,\n baseDoc,\n batch.localWriteTime\n );\n if (mutatedDoc instanceof Document) {\n results = results.insert(key, mutatedDoc);\n } else {\n results = results.remove(key);\n }\n }\n }\n });\n })\n .next(() => {\n // Finally, filter out any documents that don't actually match\n // the query.\n results.forEach((key, doc) => {\n if (!query.matches(doc)) {\n results = results.remove(key);\n }\n });\n\n return results;\n });\n }\n\n private addMissingBaseDocuments(\n transaction: PersistenceTransaction,\n matchingMutationBatches: MutationBatch[],\n existingDocuments: DocumentMap\n ): PersistencePromise<DocumentMap> {\n let missingBaseDocEntriesForPatching = documentKeySet();\n for (const batch of matchingMutationBatches) {\n for (const mutation of batch.mutations) {\n if (\n mutation instanceof PatchMutation &&\n existingDocuments.get(mutation.key) === null\n ) {\n missingBaseDocEntriesForPatching = missingBaseDocEntriesForPatching.add(\n mutation.key\n );\n }\n }\n }\n\n let mergedDocuments = existingDocuments;\n return this.remoteDocumentCache\n .getEntries(transaction, missingBaseDocEntriesForPatching)\n .next(missingBaseDocs => {\n missingBaseDocs.forEach((key, doc) => {\n if (doc !== null && doc instanceof Document) {\n mergedDocuments = mergedDocuments.insert(key, doc);\n }\n });\n return mergedDocuments;\n });\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { TargetId } from '../core/types';\nimport { ChangeType, ViewSnapshot } from '../core/view_snapshot';\nimport { documentKeySet, DocumentKeySet } from '../model/collections';\n\n/**\n * A set of changes to what documents are currently in view and out of view for\n * a given query. These changes are sent to the LocalStore by the View (via\n * the SyncEngine) and are used to pin / unpin documents as appropriate.\n */\nexport class LocalViewChanges {\n constructor(\n readonly targetId: TargetId,\n readonly fromCache: boolean,\n readonly addedKeys: DocumentKeySet,\n readonly removedKeys: DocumentKeySet\n ) {}\n\n static fromSnapshot(\n targetId: TargetId,\n viewSnapshot: ViewSnapshot\n ): LocalViewChanges {\n let addedKeys = documentKeySet();\n let removedKeys = documentKeySet();\n\n for (const docChange of viewSnapshot.docChanges) {\n switch (docChange.type) {\n case ChangeType.Added:\n addedKeys = addedKeys.add(docChange.doc.key);\n break;\n case ChangeType.Removed:\n removedKeys = removedKeys.add(docChange.doc.key);\n break;\n default:\n // do nothing\n }\n }\n\n return new LocalViewChanges(\n targetId,\n viewSnapshot.fromCache,\n addedKeys,\n removedKeys\n );\n }\n}\n","/**\n * @license\n * Copyright 2018 Google LLC\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\nimport { ListenSequenceNumber } from './types';\n\n/**\n * `SequenceNumberSyncer` defines the methods required to keep multiple instances of a\n * `ListenSequence` in sync.\n */\nexport interface SequenceNumberSyncer {\n // Notify the syncer that a new sequence number has been used.\n writeSequenceNumber(sequenceNumber: ListenSequenceNumber): void;\n // Setting this property allows the syncer to notify when a sequence number has been used, and\n // and lets the ListenSequence adjust its internal previous value accordingly.\n sequenceNumberHandler:\n | ((sequenceNumber: ListenSequenceNumber) => void)\n | null;\n}\n\n/**\n * `ListenSequence` is a monotonic sequence. It is initialized with a minimum value to\n * exceed. All subsequent calls to next will return increasing values. If provided with a\n * `SequenceNumberSyncer`, it will additionally bump its next value when told of a new value, as\n * well as write out sequence numbers that it produces via `next()`.\n */\nexport class ListenSequence {\n static readonly INVALID: ListenSequenceNumber = -1;\n\n private writeNewSequenceNumber?: (\n newSequenceNumber: ListenSequenceNumber\n ) => void;\n\n constructor(\n private previousValue: ListenSequenceNumber,\n sequenceNumberSyncer?: SequenceNumberSyncer\n ) {\n if (sequenceNumberSyncer) {\n sequenceNumberSyncer.sequenceNumberHandler = sequenceNumber =>\n this.setPreviousValue(sequenceNumber);\n this.writeNewSequenceNumber = sequenceNumber =>\n sequenceNumberSyncer.writeSequenceNumber(sequenceNumber);\n }\n }\n\n private setPreviousValue(\n externalPreviousValue: ListenSequenceNumber\n ): ListenSequenceNumber {\n this.previousValue = Math.max(externalPreviousValue, this.previousValue);\n return this.previousValue;\n }\n\n next(): ListenSequenceNumber {\n const nextValue = ++this.previousValue;\n if (this.writeNewSequenceNumber) {\n this.writeNewSequenceNumber(nextValue);\n }\n return nextValue;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nexport interface Resolver<R> {\n (value?: R | Promise<R>): void;\n}\n\nexport interface Rejecter {\n (reason?: Error): void;\n}\n\nexport class Deferred<R> {\n promise: Promise<R>;\n // Assigned synchronously in constructor by Promise constructor callback.\n resolve!: Resolver<R>;\n reject!: Rejecter;\n\n constructor() {\n this.promise = new Promise((resolve: Resolver<R>, reject: Rejecter) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\n\n/**\n * Takes an array of values and a function from a value to a Promise. The function is run on each\n * value sequentially, waiting for the previous promise to resolve before starting the next one.\n * The returned promise resolves once the function has been run on all values.\n */\nexport function sequence<T>(\n values: T[],\n fn: (value: T) => Promise<void>\n): Promise<void> {\n let p = Promise.resolve();\n for (const value of values) {\n p = p.then(() => fn(value));\n }\n return p;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { AsyncQueue, DelayedOperation, TimerId } from '../util/async_queue';\nimport { logDebug } from '../util/log';\n\nconst LOG_TAG = 'ExponentialBackoff';\n\n/**\n * Initial backoff time in milliseconds after an error.\n * Set to 1s according to https://cloud.google.com/apis/design/errors.\n */\nconst DEFAULT_BACKOFF_INITIAL_DELAY_MS = 1000;\n\nconst DEFAULT_BACKOFF_FACTOR = 1.5;\n\n/** Maximum backoff time in milliseconds */\nconst DEFAULT_BACKOFF_MAX_DELAY_MS = 60 * 1000;\n\n/**\n * A helper for running delayed tasks following an exponential backoff curve\n * between attempts.\n *\n * Each delay is made up of a \"base\" delay which follows the exponential\n * backoff curve, and a +/- 50% \"jitter\" that is calculated and added to the\n * base delay. This prevents clients from accidentally synchronizing their\n * delays causing spikes of load to the backend.\n */\nexport class ExponentialBackoff {\n private currentBaseMs: number = 0;\n private timerPromise: DelayedOperation<void> | null = null;\n /** The last backoff attempt, as epoch milliseconds. */\n private lastAttemptTime = Date.now();\n\n constructor(\n /**\n * The AsyncQueue to run backoff operations on.\n */\n private readonly queue: AsyncQueue,\n /**\n * The ID to use when scheduling backoff operations on the AsyncQueue.\n */\n private readonly timerId: TimerId,\n /**\n * The initial delay (used as the base delay on the first retry attempt).\n * Note that jitter will still be applied, so the actual delay could be as\n * little as 0.5*initialDelayMs.\n */\n private readonly initialDelayMs: number = DEFAULT_BACKOFF_INITIAL_DELAY_MS,\n /**\n * The multiplier to use to determine the extended base delay after each\n * attempt.\n */\n private readonly backoffFactor: number = DEFAULT_BACKOFF_FACTOR,\n /**\n * The maximum base delay after which no further backoff is performed.\n * Note that jitter will still be applied, so the actual delay could be as\n * much as 1.5*maxDelayMs.\n */\n private readonly maxDelayMs: number = DEFAULT_BACKOFF_MAX_DELAY_MS\n ) {\n this.reset();\n }\n\n /**\n * Resets the backoff delay.\n *\n * The very next backoffAndWait() will have no delay. If it is called again\n * (i.e. due to an error), initialDelayMs (plus jitter) will be used, and\n * subsequent ones will increase according to the backoffFactor.\n */\n reset(): void {\n this.currentBaseMs = 0;\n }\n\n /**\n * Resets the backoff delay to the maximum delay (e.g. for use after a\n * RESOURCE_EXHAUSTED error).\n */\n resetToMax(): void {\n this.currentBaseMs = this.maxDelayMs;\n }\n\n /**\n * Returns a promise that resolves after currentDelayMs, and increases the\n * delay for any subsequent attempts. If there was a pending backoff operation\n * already, it will be canceled.\n */\n backoffAndRun(op: () => Promise<void>): void {\n // Cancel any pending backoff operation.\n this.cancel();\n\n // First schedule using the current base (which may be 0 and should be\n // honored as such).\n const desiredDelayWithJitterMs = Math.floor(\n this.currentBaseMs + this.jitterDelayMs()\n );\n\n // Guard against lastAttemptTime being in the future due to a clock change.\n const delaySoFarMs = Math.max(0, Date.now() - this.lastAttemptTime);\n\n // Guard against the backoff delay already being past.\n const remainingDelayMs = Math.max(\n 0,\n desiredDelayWithJitterMs - delaySoFarMs\n );\n\n if (remainingDelayMs > 0) {\n logDebug(\n LOG_TAG,\n `Backing off for ${remainingDelayMs} ms ` +\n `(base delay: ${this.currentBaseMs} ms, ` +\n `delay with jitter: ${desiredDelayWithJitterMs} ms, ` +\n `last attempt: ${delaySoFarMs} ms ago)`\n );\n }\n\n this.timerPromise = this.queue.enqueueAfterDelay(\n this.timerId,\n remainingDelayMs,\n () => {\n this.lastAttemptTime = Date.now();\n return op();\n }\n );\n\n // Apply backoff factor to determine next delay and ensure it is within\n // bounds.\n this.currentBaseMs *= this.backoffFactor;\n if (this.currentBaseMs < this.initialDelayMs) {\n this.currentBaseMs = this.initialDelayMs;\n }\n if (this.currentBaseMs > this.maxDelayMs) {\n this.currentBaseMs = this.maxDelayMs;\n }\n }\n\n skipBackoff(): void {\n if (this.timerPromise !== null) {\n this.timerPromise.skipDelay();\n this.timerPromise = null;\n }\n }\n\n cancel(): void {\n if (this.timerPromise !== null) {\n this.timerPromise.cancel();\n this.timerPromise = null;\n }\n }\n\n /** Returns a random value in the range [-currentBaseMs/2, currentBaseMs/2] */\n private jitterDelayMs(): number {\n return (Math.random() - 0.5) * this.currentBaseMs;\n }\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { ResourcePath } from '../model/path';\nimport { debugAssert } from '../util/assert';\nimport { SortedSet } from '../util/sorted_set';\nimport { IndexManager } from './index_manager';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\n\n/**\n * An in-memory implementation of IndexManager.\n */\nexport class MemoryIndexManager implements IndexManager {\n private collectionParentIndex = new MemoryCollectionParentIndex();\n\n addToCollectionParentIndex(\n transaction: PersistenceTransaction,\n collectionPath: ResourcePath\n ): PersistencePromise<void> {\n this.collectionParentIndex.add(collectionPath);\n return PersistencePromise.resolve();\n }\n\n getCollectionParents(\n transaction: PersistenceTransaction,\n collectionId: string\n ): PersistencePromise<ResourcePath[]> {\n return PersistencePromise.resolve(\n this.collectionParentIndex.getEntries(collectionId)\n );\n }\n}\n\n/**\n * Internal implementation of the collection-parent index exposed by MemoryIndexManager.\n * Also used for in-memory caching by IndexedDbIndexManager and initial index population\n * in indexeddb_schema.ts\n */\nexport class MemoryCollectionParentIndex {\n private index = {} as {\n [collectionId: string]: SortedSet<ResourcePath>;\n };\n\n // Returns false if the entry already existed.\n add(collectionPath: ResourcePath): boolean {\n debugAssert(collectionPath.length % 2 === 1, 'Expected a collection path.');\n const collectionId = collectionPath.lastSegment();\n const parentPath = collectionPath.popLast();\n const existingParents =\n this.index[collectionId] ||\n new SortedSet<ResourcePath>(ResourcePath.comparator);\n const added = !existingParents.has(parentPath);\n this.index[collectionId] = existingParents.add(parentPath);\n return added;\n }\n\n has(collectionPath: ResourcePath): boolean {\n const collectionId = collectionPath.lastSegment();\n const parentPath = collectionPath.popLast();\n const existingParents = this.index[collectionId];\n return existingParents && existingParents.has(parentPath);\n }\n\n getEntries(collectionId: string): ResourcePath[] {\n const parentPaths =\n this.index[collectionId] ||\n new SortedSet<ResourcePath>(ResourcePath.comparator);\n return parentPaths.toArray();\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { TargetId } from './types';\n\n/** Offset to ensure non-overlapping target ids. */\nconst OFFSET = 2;\n\n/**\n * Generates monotonically increasing target IDs for sending targets to the\n * watch stream.\n *\n * The client constructs two generators, one for the target cache, and one for\n * for the sync engine (to generate limbo documents targets). These\n * generators produce non-overlapping IDs (by using even and odd IDs\n * respectively).\n *\n * By separating the target ID space, the query cache can generate target IDs\n * that persist across client restarts, while sync engine can independently\n * generate in-memory target IDs that are transient and can be reused after a\n * restart.\n */\nexport class TargetIdGenerator {\n constructor(private lastId: number) {}\n\n next(): TargetId {\n this.lastId += OFFSET;\n return this.lastId;\n }\n\n static forTargetCache(): TargetIdGenerator {\n // The target cache generator must return '2' in its first call to `next()`\n // as there is no differentiation in the protocol layer between an unset\n // number and the number '0'. If we were to sent a target with target ID\n // '0', the backend would consider it unset and replace it with its own ID.\n return new TargetIdGenerator(2 - OFFSET);\n }\n\n static forSyncEngine(): TargetIdGenerator {\n // Sync engine assigns target IDs for limbo document detection.\n return new TargetIdGenerator(1 - OFFSET);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { getUA } from '@firebase/util';\nimport { debugAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug, logError } from '../util/log';\nimport { Deferred } from '../util/promise';\nimport { SCHEMA_VERSION } from './indexeddb_schema';\nimport { PersistencePromise } from './persistence_promise';\n\n// References to `window` are guarded by SimpleDb.isAvailable()\n/* eslint-disable no-restricted-globals */\n\nconst LOG_TAG = 'SimpleDb';\n\n/**\n * The maximum number of retry attempts for an IndexedDb transaction that fails\n * with a DOMException.\n */\nconst TRANSACTION_RETRY_COUNT = 3;\n\n// The different modes supported by `SimpleDb.runTransaction()`\ntype SimpleDbTransactionMode = 'readonly' | 'readwrite';\n\nexport interface SimpleDbSchemaConverter {\n createOrUpgrade(\n db: IDBDatabase,\n txn: IDBTransaction,\n fromVersion: number,\n toVersion: number\n ): PersistencePromise<void>;\n}\n\n/**\n * Provides a wrapper around IndexedDb with a simplified interface that uses\n * Promise-like return values to chain operations. Real promises cannot be used\n * since .then() continuations are executed asynchronously (e.g. via\n * .setImmediate), which would cause IndexedDB to end the transaction.\n * See PersistencePromise for more details.\n */\nexport class SimpleDb {\n /**\n * Opens the specified database, creating or upgrading it if necessary.\n *\n * Note that `version` must not be a downgrade. IndexedDB does not support downgrading the schema\n * version. We currently do not support any way to do versioning outside of IndexedDB's versioning\n * mechanism, as only version-upgrade transactions are allowed to do things like create\n * objectstores.\n */\n static openOrCreate(\n name: string,\n version: number,\n schemaConverter: SimpleDbSchemaConverter\n ): Promise<SimpleDb> {\n debugAssert(\n SimpleDb.isAvailable(),\n 'IndexedDB not supported in current environment.'\n );\n logDebug(LOG_TAG, 'Opening database:', name);\n return new PersistencePromise<SimpleDb>((resolve, reject) => {\n // TODO(mikelehen): Investigate browser compatibility.\n // https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB\n // suggests IE9 and older WebKit browsers handle upgrade\n // differently. They expect setVersion, as described here:\n // https://developer.mozilla.org/en-US/docs/Web/API/IDBVersionChangeRequest/setVersion\n const request = window.indexedDB.open(name, version);\n\n request.onsuccess = (event: Event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n resolve(new SimpleDb(db));\n };\n\n request.onblocked = () => {\n reject(\n new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'Cannot upgrade IndexedDB schema while another tab is open. ' +\n 'Close all tabs that access Firestore and reload this page to proceed.'\n )\n );\n };\n\n request.onerror = (event: Event) => {\n const error: DOMException = (event.target as IDBOpenDBRequest).error!;\n if (error.name === 'VersionError') {\n reject(\n new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'A newer version of the Firestore SDK was previously used and so the persisted ' +\n 'data is not compatible with the version of the SDK you are now using. The SDK ' +\n 'will operate with persistence disabled. If you need persistence, please ' +\n 're-upgrade to a newer version of the SDK or else clear the persisted IndexedDB ' +\n 'data for your app to start fresh.'\n )\n );\n } else {\n reject(error);\n }\n };\n\n request.onupgradeneeded = (event: IDBVersionChangeEvent) => {\n logDebug(\n LOG_TAG,\n 'Database \"' + name + '\" requires upgrade from version:',\n event.oldVersion\n );\n const db = (event.target as IDBOpenDBRequest).result;\n schemaConverter\n .createOrUpgrade(\n db,\n request.transaction!,\n event.oldVersion,\n SCHEMA_VERSION\n )\n .next(() => {\n logDebug(\n LOG_TAG,\n 'Database upgrade to version ' + SCHEMA_VERSION + ' complete'\n );\n });\n };\n }).toPromise();\n }\n\n /** Deletes the specified database. */\n static delete(name: string): Promise<void> {\n logDebug(LOG_TAG, 'Removing database:', name);\n return wrapRequest<void>(window.indexedDB.deleteDatabase(name)).toPromise();\n }\n\n /** Returns true if IndexedDB is available in the current environment. */\n static isAvailable(): boolean {\n if (typeof window === 'undefined' || window.indexedDB == null) {\n return false;\n }\n\n if (SimpleDb.isMockPersistence()) {\n return true;\n }\n\n // In some Node environments, `window` is defined, but `window.navigator` is\n // not. We don't support IndexedDB persistence in Node if the\n // isMockPersistence() check above returns false.\n if (window.navigator === undefined) {\n return false;\n }\n\n // We extensively use indexed array values and compound keys,\n // which IE and Edge do not support. However, they still have indexedDB\n // defined on the window, so we need to check for them here and make sure\n // to return that persistence is not enabled for those browsers.\n // For tracking support of this feature, see here:\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/status/indexeddbarraysandmultientrysupport/\n\n // Check the UA string to find out the browser.\n const ua = getUA();\n\n // IE 10\n // ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';\n\n // IE 11\n // ua = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';\n\n // Edge\n // ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML,\n // like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0';\n\n // iOS Safari: Disable for users running iOS version < 10.\n const iOSVersion = SimpleDb.getIOSVersion(ua);\n const isUnsupportedIOS = 0 < iOSVersion && iOSVersion < 10;\n\n // Android browser: Disable for userse running version < 4.5.\n const androidVersion = SimpleDb.getAndroidVersion(ua);\n const isUnsupportedAndroid = 0 < androidVersion && androidVersion < 4.5;\n\n if (\n ua.indexOf('MSIE ') > 0 ||\n ua.indexOf('Trident/') > 0 ||\n ua.indexOf('Edge/') > 0 ||\n isUnsupportedIOS ||\n isUnsupportedAndroid\n ) {\n return false;\n } else {\n return true;\n }\n }\n\n /**\n * Returns true if the backing IndexedDB store is the Node IndexedDBShim\n * (see https://github.com/axemclion/IndexedDBShim).\n */\n static isMockPersistence(): boolean {\n return (\n typeof process !== 'undefined' &&\n process.env?.USE_MOCK_PERSISTENCE === 'YES'\n );\n }\n\n /** Helper to get a typed SimpleDbStore from a transaction. */\n static getStore<KeyType extends IDBValidKey, ValueType extends unknown>(\n txn: SimpleDbTransaction,\n store: string\n ): SimpleDbStore<KeyType, ValueType> {\n return txn.store<KeyType, ValueType>(store);\n }\n\n // visible for testing\n /** Parse User Agent to determine iOS version. Returns -1 if not found. */\n static getIOSVersion(ua: string): number {\n const iOSVersionRegex = ua.match(/i(?:phone|pad|pod) os ([\\d_]+)/i);\n const version = iOSVersionRegex\n ? iOSVersionRegex[1]\n .split('_')\n .slice(0, 2)\n .join('.')\n : '-1';\n return Number(version);\n }\n\n // visible for testing\n /** Parse User Agent to determine Android version. Returns -1 if not found. */\n static getAndroidVersion(ua: string): number {\n const androidVersionRegex = ua.match(/Android ([\\d.]+)/i);\n const version = androidVersionRegex\n ? androidVersionRegex[1]\n .split('.')\n .slice(0, 2)\n .join('.')\n : '-1';\n return Number(version);\n }\n\n constructor(private db: IDBDatabase) {\n const iOSVersion = SimpleDb.getIOSVersion(getUA());\n // NOTE: According to https://bugs.webkit.org/show_bug.cgi?id=197050, the\n // bug we're checking for should exist in iOS >= 12.2 and < 13, but for\n // whatever reason it's much harder to hit after 12.2 so we only proactively\n // log on 12.2.\n if (iOSVersion === 12.2) {\n logError(\n 'Firestore persistence suffers from a bug in iOS 12.2 ' +\n 'Safari that may cause your app to stop working. See ' +\n 'https://stackoverflow.com/q/56496296/110915 for details ' +\n 'and a potential workaround.'\n );\n }\n }\n\n setVersionChangeListener(\n versionChangeListener: (event: IDBVersionChangeEvent) => void\n ): void {\n this.db.onversionchange = (event: IDBVersionChangeEvent) => {\n return versionChangeListener(event);\n };\n }\n\n async runTransaction<T>(\n mode: SimpleDbTransactionMode,\n objectStores: string[],\n transactionFn: (transaction: SimpleDbTransaction) => PersistencePromise<T>\n ): Promise<T> {\n const readonly = mode === 'readonly';\n let attemptNumber = 0;\n\n while (true) {\n ++attemptNumber;\n\n const transaction = SimpleDbTransaction.open(\n this.db,\n readonly ? 'readonly' : 'readwrite',\n objectStores\n );\n try {\n const transactionFnResult = transactionFn(transaction)\n .catch(error => {\n // Abort the transaction if there was an error.\n transaction.abort(error);\n // We cannot actually recover, and calling `abort()` will cause the transaction's\n // completion promise to be rejected. This in turn means that we won't use\n // `transactionFnResult` below. We return a rejection here so that we don't add the\n // possibility of returning `void` to the type of `transactionFnResult`.\n return PersistencePromise.reject<T>(error);\n })\n .toPromise();\n\n // As noted above, errors are propagated by aborting the transaction. So\n // we swallow any error here to avoid the browser logging it as unhandled.\n transactionFnResult.catch(() => {});\n\n // Wait for the transaction to complete (i.e. IndexedDb's onsuccess event to\n // fire), but still return the original transactionFnResult back to the\n // caller.\n await transaction.completionPromise;\n return transactionFnResult;\n } catch (error) {\n // TODO(schmidt-sebastian): We could probably be smarter about this and\n // not retry exceptions that are likely unrecoverable (such as quota\n // exceeded errors).\n\n // Note: We cannot use an instanceof check for FirestoreException, since the\n // exception is wrapped in a generic error by our async/await handling.\n const retryable =\n error.name !== 'FirebaseError' &&\n attemptNumber < TRANSACTION_RETRY_COUNT;\n logDebug(\n LOG_TAG,\n 'Transaction failed with error: %s. Retrying: %s.',\n error.message,\n retryable\n );\n\n if (!retryable) {\n return Promise.reject(error);\n }\n }\n }\n }\n\n close(): void {\n this.db.close();\n }\n}\n\n/**\n * A controller for iterating over a key range or index. It allows an iterate\n * callback to delete the currently-referenced object, or jump to a new key\n * within the key range or index.\n */\nexport class IterationController {\n private shouldStop = false;\n private nextKey: IDBValidKey | null = null;\n\n constructor(private dbCursor: IDBCursorWithValue) {}\n\n get isDone(): boolean {\n return this.shouldStop;\n }\n\n get skipToKey(): IDBValidKey | null {\n return this.nextKey;\n }\n\n set cursor(value: IDBCursorWithValue) {\n this.dbCursor = value;\n }\n\n /**\n * This function can be called to stop iteration at any point.\n */\n done(): void {\n this.shouldStop = true;\n }\n\n /**\n * This function can be called to skip to that next key, which could be\n * an index or a primary key.\n */\n skip(key: IDBValidKey): void {\n this.nextKey = key;\n }\n\n /**\n * Delete the current cursor value from the object store.\n *\n * NOTE: You CANNOT do this with a keysOnly query.\n */\n delete(): PersistencePromise<void> {\n return wrapRequest<void>(this.dbCursor.delete());\n }\n}\n\n/**\n * Callback used with iterate() method.\n */\nexport type IterateCallback<KeyType, ValueType> = (\n key: KeyType,\n value: ValueType,\n control: IterationController\n) => void | PersistencePromise<void>;\n\n/** Options available to the iterate() method. */\nexport interface IterateOptions {\n /** Index to iterate over (else primary keys will be iterated) */\n index?: string;\n\n /** IndxedDB Range to iterate over (else entire store will be iterated) */\n range?: IDBKeyRange;\n\n /** If true, values aren't read while iterating. */\n keysOnly?: boolean;\n\n /** If true, iterate over the store in reverse. */\n reverse?: boolean;\n}\n\n/** An error that wraps exceptions that thrown during IndexedDB execution. */\nexport class IndexedDbTransactionError extends FirestoreError {\n name = 'IndexedDbTransactionError';\n\n constructor(cause: Error) {\n super(Code.UNAVAILABLE, 'IndexedDB transaction failed: ' + cause);\n }\n}\n\n/** Verifies whether `e` is an IndexedDbTransactionError. */\nexport function isIndexedDbTransactionError(e: Error): boolean {\n // Use name equality, as instanceof checks on errors don't work with errors\n // that wrap other errors.\n return e.name === 'IndexedDbTransactionError';\n}\n\n/**\n * Wraps an IDBTransaction and exposes a store() method to get a handle to a\n * specific object store.\n */\nexport class SimpleDbTransaction {\n private aborted = false;\n\n /**\n * A promise that resolves with the result of the IndexedDb transaction.\n */\n private readonly completionDeferred = new Deferred<void>();\n\n static open(\n db: IDBDatabase,\n mode: IDBTransactionMode,\n objectStoreNames: string[]\n ): SimpleDbTransaction {\n return new SimpleDbTransaction(db.transaction(objectStoreNames, mode));\n }\n\n constructor(private readonly transaction: IDBTransaction) {\n this.transaction.oncomplete = () => {\n this.completionDeferred.resolve();\n };\n this.transaction.onabort = () => {\n if (transaction.error) {\n this.completionDeferred.reject(\n new IndexedDbTransactionError(transaction.error)\n );\n } else {\n this.completionDeferred.resolve();\n }\n };\n this.transaction.onerror = (event: Event) => {\n const error = checkForAndReportiOSError(\n (event.target as IDBRequest).error!\n );\n this.completionDeferred.reject(new IndexedDbTransactionError(error));\n };\n }\n\n get completionPromise(): Promise<void> {\n return this.completionDeferred.promise;\n }\n\n abort(error?: Error): void {\n if (error) {\n this.completionDeferred.reject(error);\n }\n\n if (!this.aborted) {\n logDebug(\n LOG_TAG,\n 'Aborting transaction:',\n error ? error.message : 'Client-initiated abort'\n );\n this.aborted = true;\n this.transaction.abort();\n }\n }\n\n /**\n * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All\n * operations performed on the SimpleDbStore happen within the context of this\n * transaction and it cannot be used anymore once the transaction is\n * completed.\n *\n * Note that we can't actually enforce that the KeyType and ValueType are\n * correct, but they allow type safety through the rest of the consuming code.\n */\n store<KeyType extends IDBValidKey, ValueType extends unknown>(\n storeName: string\n ): SimpleDbStore<KeyType, ValueType> {\n const store = this.transaction.objectStore(storeName);\n debugAssert(!!store, 'Object store not part of transaction: ' + storeName);\n return new SimpleDbStore<KeyType, ValueType>(store);\n }\n}\n\n/**\n * A wrapper around an IDBObjectStore providing an API that:\n *\n * 1) Has generic KeyType / ValueType parameters to provide strongly-typed\n * methods for acting against the object store.\n * 2) Deals with IndexedDB's onsuccess / onerror event callbacks, making every\n * method return a PersistencePromise instead.\n * 3) Provides a higher-level API to avoid needing to do excessive wrapping of\n * intermediate IndexedDB types (IDBCursorWithValue, etc.)\n */\nexport class SimpleDbStore<\n KeyType extends IDBValidKey,\n ValueType extends unknown\n> {\n constructor(private store: IDBObjectStore) {}\n\n /**\n * Writes a value into the Object Store.\n *\n * @param key Optional explicit key to use when writing the object, else the\n * key will be auto-assigned (e.g. via the defined keyPath for the store).\n * @param value The object to write.\n */\n put(value: ValueType): PersistencePromise<void>;\n put(key: KeyType, value: ValueType): PersistencePromise<void>;\n put(\n keyOrValue: KeyType | ValueType,\n value?: ValueType\n ): PersistencePromise<void> {\n let request;\n if (value !== undefined) {\n logDebug(LOG_TAG, 'PUT', this.store.name, keyOrValue, value);\n request = this.store.put(value, keyOrValue as KeyType);\n } else {\n logDebug(LOG_TAG, 'PUT', this.store.name, '<auto-key>', keyOrValue);\n request = this.store.put(keyOrValue as ValueType);\n }\n return wrapRequest<void>(request);\n }\n\n /**\n * Adds a new value into an Object Store and returns the new key. Similar to\n * IndexedDb's `add()`, this method will fail on primary key collisions.\n *\n * @param value The object to write.\n * @return The key of the value to add.\n */\n add(value: ValueType): PersistencePromise<KeyType> {\n logDebug(LOG_TAG, 'ADD', this.store.name, value, value);\n const request = this.store.add(value as ValueType);\n return wrapRequest<KeyType>(request);\n }\n\n /**\n * Gets the object with the specified key from the specified store, or null\n * if no object exists with the specified key.\n *\n * @key The key of the object to get.\n * @return The object with the specified key or null if no object exists.\n */\n get(key: KeyType): PersistencePromise<ValueType | null> {\n const request = this.store.get(key);\n // We're doing an unsafe cast to ValueType.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return wrapRequest<any>(request).next(result => {\n // Normalize nonexistence to null.\n if (result === undefined) {\n result = null;\n }\n logDebug(LOG_TAG, 'GET', this.store.name, key, result);\n return result;\n });\n }\n\n delete(key: KeyType | IDBKeyRange): PersistencePromise<void> {\n logDebug(LOG_TAG, 'DELETE', this.store.name, key);\n const request = this.store.delete(key);\n return wrapRequest<void>(request);\n }\n\n /**\n * If we ever need more of the count variants, we can add overloads. For now,\n * all we need is to count everything in a store.\n *\n * Returns the number of rows in the store.\n */\n count(): PersistencePromise<number> {\n logDebug(LOG_TAG, 'COUNT', this.store.name);\n const request = this.store.count();\n return wrapRequest<number>(request);\n }\n\n loadAll(): PersistencePromise<ValueType[]>;\n loadAll(range: IDBKeyRange): PersistencePromise<ValueType[]>;\n loadAll(index: string, range: IDBKeyRange): PersistencePromise<ValueType[]>;\n loadAll(\n indexOrRange?: string | IDBKeyRange,\n range?: IDBKeyRange\n ): PersistencePromise<ValueType[]> {\n const cursor = this.cursor(this.options(indexOrRange, range));\n const results: ValueType[] = [];\n return this.iterateCursor(cursor, (key, value) => {\n results.push(value);\n }).next(() => {\n return results;\n });\n }\n\n deleteAll(): PersistencePromise<void>;\n deleteAll(range: IDBKeyRange): PersistencePromise<void>;\n deleteAll(index: string, range: IDBKeyRange): PersistencePromise<void>;\n deleteAll(\n indexOrRange?: string | IDBKeyRange,\n range?: IDBKeyRange\n ): PersistencePromise<void> {\n logDebug(LOG_TAG, 'DELETE ALL', this.store.name);\n const options = this.options(indexOrRange, range);\n options.keysOnly = false;\n const cursor = this.cursor(options);\n return this.iterateCursor(cursor, (key, value, control) => {\n // NOTE: Calling delete() on a cursor is documented as more efficient than\n // calling delete() on an object store with a single key\n // (https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/delete),\n // however, this requires us *not* to use a keysOnly cursor\n // (https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor/delete). We\n // may want to compare the performance of each method.\n return control.delete();\n });\n }\n\n /**\n * Iterates over keys and values in an object store.\n *\n * @param options Options specifying how to iterate the objects in the store.\n * @param callback will be called for each iterated object. Iteration can be\n * canceled at any point by calling the doneFn passed to the callback.\n * The callback can return a PersistencePromise if it performs async\n * operations but note that iteration will continue without waiting for them\n * to complete.\n * @returns A PersistencePromise that resolves once all PersistencePromises\n * returned by callbacks resolve.\n */\n iterate(\n callback: IterateCallback<KeyType, ValueType>\n ): PersistencePromise<void>;\n iterate(\n options: IterateOptions,\n callback: IterateCallback<KeyType, ValueType>\n ): PersistencePromise<void>;\n iterate(\n optionsOrCallback: IterateOptions | IterateCallback<KeyType, ValueType>,\n callback?: IterateCallback<KeyType, ValueType>\n ): PersistencePromise<void> {\n let options;\n if (!callback) {\n options = {};\n callback = optionsOrCallback as IterateCallback<KeyType, ValueType>;\n } else {\n options = optionsOrCallback as IterateOptions;\n }\n const cursor = this.cursor(options);\n return this.iterateCursor(cursor, callback);\n }\n\n /**\n * Iterates over a store, but waits for the given callback to complete for\n * each entry before iterating the next entry. This allows the callback to do\n * asynchronous work to determine if this iteration should continue.\n *\n * The provided callback should return `true` to continue iteration, and\n * `false` otherwise.\n */\n iterateSerial(\n callback: (k: KeyType, v: ValueType) => PersistencePromise<boolean>\n ): PersistencePromise<void> {\n const cursorRequest = this.cursor({});\n return new PersistencePromise((resolve, reject) => {\n cursorRequest.onerror = (event: Event) => {\n const error = checkForAndReportiOSError(\n (event.target as IDBRequest).error!\n );\n reject(error);\n };\n cursorRequest.onsuccess = (event: Event) => {\n const cursor: IDBCursorWithValue = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n\n callback(cursor.primaryKey as KeyType, cursor.value).next(\n shouldContinue => {\n if (shouldContinue) {\n cursor.continue();\n } else {\n resolve();\n }\n }\n );\n };\n });\n }\n\n private iterateCursor(\n cursorRequest: IDBRequest,\n fn: IterateCallback<KeyType, ValueType>\n ): PersistencePromise<void> {\n const results: Array<PersistencePromise<void>> = [];\n return new PersistencePromise((resolve, reject) => {\n cursorRequest.onerror = (event: Event) => {\n reject((event.target as IDBRequest).error!);\n };\n cursorRequest.onsuccess = (event: Event) => {\n const cursor: IDBCursorWithValue = (event.target as IDBRequest).result;\n if (!cursor) {\n resolve();\n return;\n }\n const controller = new IterationController(cursor);\n const userResult = fn(\n cursor.primaryKey as KeyType,\n cursor.value,\n controller\n );\n if (userResult instanceof PersistencePromise) {\n const userPromise: PersistencePromise<void> = userResult.catch(\n err => {\n controller.done();\n return PersistencePromise.reject(err);\n }\n );\n results.push(userPromise);\n }\n if (controller.isDone) {\n resolve();\n } else if (controller.skipToKey === null) {\n cursor.continue();\n } else {\n cursor.continue(controller.skipToKey);\n }\n };\n }).next(() => {\n return PersistencePromise.waitFor(results);\n });\n }\n\n private options(\n indexOrRange?: string | IDBKeyRange,\n range?: IDBKeyRange\n ): IterateOptions {\n let indexName: string | undefined = undefined;\n if (indexOrRange !== undefined) {\n if (typeof indexOrRange === 'string') {\n indexName = indexOrRange;\n } else {\n debugAssert(\n range === undefined,\n '3rd argument must not be defined if 2nd is a range.'\n );\n range = indexOrRange;\n }\n }\n return { index: indexName, range };\n }\n\n private cursor(options: IterateOptions): IDBRequest {\n let direction: IDBCursorDirection = 'next';\n if (options.reverse) {\n direction = 'prev';\n }\n if (options.index) {\n const index = this.store.index(options.index);\n if (options.keysOnly) {\n return index.openKeyCursor(options.range, direction);\n } else {\n return index.openCursor(options.range, direction);\n }\n } else {\n return this.store.openCursor(options.range, direction);\n }\n }\n}\n\n/**\n * Wraps an IDBRequest in a PersistencePromise, using the onsuccess / onerror\n * handlers to resolve / reject the PersistencePromise as appropriate.\n */\nfunction wrapRequest<R>(request: IDBRequest): PersistencePromise<R> {\n return new PersistencePromise<R>((resolve, reject) => {\n request.onsuccess = (event: Event) => {\n const result = (event.target as IDBRequest).result;\n resolve(result);\n };\n\n request.onerror = (event: Event) => {\n const error = checkForAndReportiOSError(\n (event.target as IDBRequest).error!\n );\n reject(error);\n };\n });\n}\n\n// Guard so we only report the error once.\nlet reportedIOSError = false;\nfunction checkForAndReportiOSError(error: DOMException): Error {\n const iOSVersion = SimpleDb.getIOSVersion(getUA());\n if (iOSVersion >= 12.2 && iOSVersion < 13) {\n const IOS_ERROR =\n 'An internal error was encountered in the Indexed Database server';\n if (error.message.indexOf(IOS_ERROR) >= 0) {\n // Wrap error in a more descriptive one.\n const newError = new FirestoreError(\n 'internal',\n `IOS_INDEXEDDB_BUG1: IndexedDb has thrown '${IOS_ERROR}'. This is likely ` +\n `due to an unavoidable bug in iOS. See https://stackoverflow.com/q/56496296/110915 ` +\n `for details and a potential workaround.`\n );\n if (!reportedIOSError) {\n reportedIOSError = true;\n // Throw a global exception outside of this promise chain, for the user to\n // potentially catch.\n setTimeout(() => {\n throw newError;\n }, 0);\n }\n return newError;\n }\n }\n return error;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert, fail } from './assert';\nimport { Code, FirestoreError } from './error';\nimport { logDebug, logError } from './log';\nimport { Deferred } from './promise';\nimport { ExponentialBackoff } from '../remote/backoff';\nimport { PlatformSupport } from '../platform/platform';\nimport { isIndexedDbTransactionError } from '../local/simple_db';\n\nconst LOG_TAG = 'AsyncQueue';\n\n// Accept any return type from setTimeout().\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype TimerHandle = any;\n\n/**\n * Wellknown \"timer\" IDs used when scheduling delayed operations on the\n * AsyncQueue. These IDs can then be used from tests to check for the presence\n * of operations or to run them early.\n *\n * The string values are used when encoding these timer IDs in JSON spec tests.\n */\nexport const enum TimerId {\n /** All can be used with runDelayedOperationsEarly() to run all timers. */\n All = 'all',\n\n /**\n * The following 4 timers are used in persistent_stream.ts for the listen and\n * write streams. The \"Idle\" timer is used to close the stream due to\n * inactivity. The \"ConnectionBackoff\" timer is used to restart a stream once\n * the appropriate backoff delay has elapsed.\n */\n ListenStreamIdle = 'listen_stream_idle',\n ListenStreamConnectionBackoff = 'listen_stream_connection_backoff',\n WriteStreamIdle = 'write_stream_idle',\n WriteStreamConnectionBackoff = 'write_stream_connection_backoff',\n\n /**\n * A timer used in online_state_tracker.ts to transition from\n * OnlineState.Unknown to Offline after a set timeout, rather than waiting\n * indefinitely for success or failure.\n */\n OnlineStateTimeout = 'online_state_timeout',\n\n /**\n * A timer used to update the client metadata in IndexedDb, which is used\n * to determine the primary leaseholder.\n */\n ClientMetadataRefresh = 'client_metadata_refresh',\n\n /** A timer used to periodically attempt LRU Garbage collection */\n LruGarbageCollection = 'lru_garbage_collection',\n\n /**\n * A timer used to retry transactions. Since there can be multiple concurrent\n * transactions, multiple of these may be in the queue at a given time.\n */\n TransactionRetry = 'transaction_retry',\n\n /**\n * A timer used to retry operations scheduled via retryable AsyncQueue\n * operations.\n */\n AsyncQueueRetry = 'async_queue_retry'\n}\n\n/**\n * Represents an operation scheduled to be run in the future on an AsyncQueue.\n *\n * It is created via DelayedOperation.createAndSchedule().\n *\n * Supports cancellation (via cancel()) and early execution (via skipDelay()).\n *\n * Note: We implement `PromiseLike` instead of `Promise`, as the `Promise` type\n * in newer versions of TypeScript defines `finally`, which is not available in\n * IE.\n */\nexport class DelayedOperation<T extends unknown> implements PromiseLike<T> {\n // handle for use with clearTimeout(), or null if the operation has been\n // executed or canceled already.\n private timerHandle: TimerHandle | null;\n\n private readonly deferred = new Deferred<T>();\n\n private constructor(\n private readonly asyncQueue: AsyncQueue,\n readonly timerId: TimerId,\n readonly targetTimeMs: number,\n private readonly op: () => Promise<T>,\n private readonly removalCallback: (op: DelayedOperation<T>) => void\n ) {\n // It's normal for the deferred promise to be canceled (due to cancellation)\n // and so we attach a dummy catch callback to avoid\n // 'UnhandledPromiseRejectionWarning' log spam.\n this.deferred.promise.catch(err => {});\n }\n\n /**\n * Creates and returns a DelayedOperation that has been scheduled to be\n * executed on the provided asyncQueue after the provided delayMs.\n *\n * @param asyncQueue The queue to schedule the operation on.\n * @param id A Timer ID identifying the type of operation this is.\n * @param delayMs The delay (ms) before the operation should be scheduled.\n * @param op The operation to run.\n * @param removalCallback A callback to be called synchronously once the\n * operation is executed or canceled, notifying the AsyncQueue to remove it\n * from its delayedOperations list.\n * PORTING NOTE: This exists to prevent making removeDelayedOperation() and\n * the DelayedOperation class public.\n */\n static createAndSchedule<R extends unknown>(\n asyncQueue: AsyncQueue,\n timerId: TimerId,\n delayMs: number,\n op: () => Promise<R>,\n removalCallback: (op: DelayedOperation<R>) => void\n ): DelayedOperation<R> {\n const targetTime = Date.now() + delayMs;\n const delayedOp = new DelayedOperation(\n asyncQueue,\n timerId,\n targetTime,\n op,\n removalCallback\n );\n delayedOp.start(delayMs);\n return delayedOp;\n }\n\n /**\n * Starts the timer. This is called immediately after construction by\n * createAndSchedule().\n */\n private start(delayMs: number): void {\n this.timerHandle = setTimeout(() => this.handleDelayElapsed(), delayMs);\n }\n\n /**\n * Queues the operation to run immediately (if it hasn't already been run or\n * canceled).\n */\n skipDelay(): void {\n return this.handleDelayElapsed();\n }\n\n /**\n * Cancels the operation if it hasn't already been executed or canceled. The\n * promise will be rejected.\n *\n * As long as the operation has not yet been run, calling cancel() provides a\n * guarantee that the operation will not be run.\n */\n cancel(reason?: string): void {\n if (this.timerHandle !== null) {\n this.clearTimeout();\n this.deferred.reject(\n new FirestoreError(\n Code.CANCELLED,\n 'Operation cancelled' + (reason ? ': ' + reason : '')\n )\n );\n }\n }\n\n then = this.deferred.promise.then.bind(this.deferred.promise);\n\n private handleDelayElapsed(): void {\n this.asyncQueue.enqueueAndForget(() => {\n if (this.timerHandle !== null) {\n this.clearTimeout();\n return this.op().then(result => {\n return this.deferred.resolve(result);\n });\n } else {\n return Promise.resolve();\n }\n });\n }\n\n private clearTimeout(): void {\n if (this.timerHandle !== null) {\n this.removalCallback(this);\n clearTimeout(this.timerHandle);\n this.timerHandle = null;\n }\n }\n}\n\nexport class AsyncQueue {\n // The last promise in the queue.\n private tail: Promise<unknown> = Promise.resolve();\n\n // The last retryable operation. Retryable operation are run in order and\n // retried with backoff.\n private retryableTail: Promise<void> = Promise.resolve();\n\n // Is this AsyncQueue being shut down? Once it is set to true, it will not\n // be changed again.\n private _isShuttingDown: boolean = false;\n\n // Operations scheduled to be queued in the future. Operations are\n // automatically removed after they are run or canceled.\n private delayedOperations: Array<DelayedOperation<unknown>> = [];\n\n // visible for testing\n failure: Error | null = null;\n\n // Flag set while there's an outstanding AsyncQueue operation, used for\n // assertion sanity-checks.\n private operationInProgress = false;\n\n // List of TimerIds to fast-forward delays for.\n private timerIdsToSkip: TimerId[] = [];\n\n // Backoff timer used to schedule retries for retryable operations\n private backoff = new ExponentialBackoff(this, TimerId.AsyncQueueRetry);\n\n // Visibility handler that triggers an immediate retry of all retryable\n // operations. Meant to speed up recovery when we regain file system access\n // after page comes into foreground.\n private visibilityHandler = (): void => this.backoff.skipBackoff();\n\n constructor() {\n const window = PlatformSupport.getPlatform().window;\n if (window && typeof window.addEventListener === 'function') {\n window.addEventListener('visibilitychange', this.visibilityHandler);\n }\n }\n\n // Is this AsyncQueue being shut down? If true, this instance will not enqueue\n // any new operations, Promises from enqueue requests will not resolve.\n get isShuttingDown(): boolean {\n return this._isShuttingDown;\n }\n\n /**\n * Adds a new operation to the queue without waiting for it to complete (i.e.\n * we ignore the Promise result).\n */\n enqueueAndForget<T extends unknown>(op: () => Promise<T>): void {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.enqueue(op);\n }\n\n /**\n * Regardless if the queue has initialized shutdown, adds a new operation to the\n * queue without waiting for it to complete (i.e. we ignore the Promise result).\n */\n enqueueAndForgetEvenAfterShutdown<T extends unknown>(\n op: () => Promise<T>\n ): void {\n this.verifyNotFailed();\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.enqueueInternal(op);\n }\n\n /**\n * Regardless if the queue has initialized shutdown, adds a new operation to the\n * queue.\n */\n private enqueueEvenAfterShutdown<T extends unknown>(\n op: () => Promise<T>\n ): Promise<T> {\n this.verifyNotFailed();\n return this.enqueueInternal(op);\n }\n\n /**\n * Adds a new operation to the queue and initialize the shut down of this queue.\n * Returns a promise that will be resolved when the promise returned by the new\n * operation is (with its value).\n * Once this method is called, the only possible way to request running an operation\n * is through `enqueueAndForgetEvenAfterShutdown`.\n */\n async enqueueAndInitiateShutdown(op: () => Promise<void>): Promise<void> {\n this.verifyNotFailed();\n if (!this._isShuttingDown) {\n this._isShuttingDown = true;\n const window = PlatformSupport.getPlatform().window;\n if (window) {\n window.removeEventListener('visibilitychange', this.visibilityHandler);\n }\n await this.enqueueEvenAfterShutdown(op);\n }\n }\n\n /**\n * Adds a new operation to the queue. Returns a promise that will be resolved\n * when the promise returned by the new operation is (with its value).\n */\n enqueue<T extends unknown>(op: () => Promise<T>): Promise<T> {\n this.verifyNotFailed();\n if (this._isShuttingDown) {\n // Return a Promise which never resolves.\n return new Promise<T>(resolve => {});\n }\n return this.enqueueInternal(op);\n }\n\n /**\n * Enqueue a retryable operation.\n *\n * A retryable operation is rescheduled with backoff if it fails with a\n * IndexedDbTransactionError (the error type used by SimpleDb). All\n * retryable operations are executed in order and only run if all prior\n * operations were retried successfully.\n */\n enqueueRetryable(op: () => Promise<void>): void {\n this.verifyNotFailed();\n\n if (this._isShuttingDown) {\n return;\n }\n\n this.retryableTail = this.retryableTail.then(() => {\n const deferred = new Deferred<void>();\n const retryingOp = async (): Promise<void> => {\n try {\n await op();\n deferred.resolve();\n this.backoff.reset();\n } catch (e) {\n if (isIndexedDbTransactionError(e)) {\n logDebug(LOG_TAG, 'Operation failed with retryable error: ' + e);\n this.backoff.backoffAndRun(retryingOp);\n } else {\n deferred.resolve();\n throw e; // Failure will be handled by AsyncQueue\n }\n }\n };\n this.enqueueAndForget(retryingOp);\n return deferred.promise;\n });\n }\n\n private enqueueInternal<T extends unknown>(op: () => Promise<T>): Promise<T> {\n const newTail = this.tail.then(() => {\n this.operationInProgress = true;\n return op()\n .catch((error: FirestoreError) => {\n this.failure = error;\n this.operationInProgress = false;\n const message = error.stack || error.message || '';\n logError('INTERNAL UNHANDLED ERROR: ', message);\n\n // Re-throw the error so that this.tail becomes a rejected Promise and\n // all further attempts to chain (via .then) will just short-circuit\n // and return the rejected Promise.\n throw error;\n })\n .then(result => {\n this.operationInProgress = false;\n return result;\n });\n });\n this.tail = newTail;\n return newTail;\n }\n\n /**\n * Schedules an operation to be queued on the AsyncQueue once the specified\n * `delayMs` has elapsed. The returned DelayedOperation can be used to cancel\n * or fast-forward the operation prior to its running.\n */\n enqueueAfterDelay<T extends unknown>(\n timerId: TimerId,\n delayMs: number,\n op: () => Promise<T>\n ): DelayedOperation<T> {\n this.verifyNotFailed();\n\n debugAssert(\n delayMs >= 0,\n `Attempted to schedule an operation with a negative delay of ${delayMs}`\n );\n\n // Fast-forward delays for timerIds that have been overriden.\n if (this.timerIdsToSkip.indexOf(timerId) > -1) {\n delayMs = 0;\n }\n\n const delayedOp = DelayedOperation.createAndSchedule<T>(\n this,\n timerId,\n delayMs,\n op,\n removedOp =>\n this.removeDelayedOperation(removedOp as DelayedOperation<unknown>)\n );\n this.delayedOperations.push(delayedOp as DelayedOperation<unknown>);\n return delayedOp;\n }\n\n private verifyNotFailed(): void {\n if (this.failure) {\n fail(\n 'AsyncQueue is already failed: ' +\n (this.failure.stack || this.failure.message)\n );\n }\n }\n\n /**\n * Verifies there's an operation currently in-progress on the AsyncQueue.\n * Unfortunately we can't verify that the running code is in the promise chain\n * of that operation, so this isn't a foolproof check, but it should be enough\n * to catch some bugs.\n */\n verifyOperationInProgress(): void {\n debugAssert(\n this.operationInProgress,\n 'verifyOpInProgress() called when no op in progress on this queue.'\n );\n }\n\n /**\n * Waits until all currently queued tasks are finished executing. Delayed\n * operations are not run.\n */\n async drain(): Promise<void> {\n // Operations in the queue prior to draining may have enqueued additional\n // operations. Keep draining the queue until the tail is no longer advanced,\n // which indicates that no more new operations were enqueued and that all\n // operations were executed.\n let currentTail: Promise<unknown>;\n do {\n currentTail = this.tail;\n await currentTail;\n } while (currentTail !== this.tail);\n }\n\n /**\n * For Tests: Determine if a delayed operation with a particular TimerId\n * exists.\n */\n containsDelayedOperation(timerId: TimerId): boolean {\n for (const op of this.delayedOperations) {\n if (op.timerId === timerId) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * For Tests: Runs some or all delayed operations early.\n *\n * @param lastTimerId Delayed operations up to and including this TimerId will\n * be drained. Pass TimerId.All to run all delayed operations.\n * @returns a Promise that resolves once all operations have been run.\n */\n runAllDelayedOperationsUntil(lastTimerId: TimerId): Promise<void> {\n // Note that draining may generate more delayed ops, so we do that first.\n return this.drain().then(() => {\n // Run ops in the same order they'd run if they ran naturally.\n this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);\n\n for (const op of this.delayedOperations) {\n op.skipDelay();\n if (lastTimerId !== TimerId.All && op.timerId === lastTimerId) {\n break;\n }\n }\n\n return this.drain();\n });\n }\n\n /**\n * For Tests: Skip all subsequent delays for a timer id.\n */\n skipDelaysForTimerId(timerId: TimerId): void {\n this.timerIdsToSkip.push(timerId);\n }\n\n /** Called once a DelayedOperation is run or canceled. */\n private removeDelayedOperation(op: DelayedOperation<unknown>): void {\n // NOTE: indexOf / slice are O(n), but delayedOperations is expected to be small.\n const index = this.delayedOperations.indexOf(op);\n debugAssert(index >= 0, 'Delayed operation not found.');\n this.delayedOperations.splice(index, 1);\n }\n}\n\n/**\n * Returns a FirestoreError that can be surfaced to the user if the provided\n * error is an IndexedDbTransactionError. Re-throws the error otherwise.\n */\nexport function wrapInUserErrorIfRecoverable(\n e: Error,\n msg: string\n): FirestoreError {\n logError(LOG_TAG, `${msg}: ${e}`);\n if (isIndexedDbTransactionError(e)) {\n return new FirestoreError(Code.UNAVAILABLE, `${msg}: ${e}`);\n } else {\n throw e;\n }\n}\n","/**\n * @license\n * Copyright 2018 Google LLC\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\nimport { ListenSequence } from '../core/listen_sequence';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { debugAssert } from '../util/assert';\nimport { AsyncQueue, DelayedOperation, TimerId } from '../util/async_queue';\nimport { getLogLevel, logDebug, LogLevel } from '../util/log';\nimport { primitiveComparator } from '../util/misc';\nimport { SortedMap } from '../util/sorted_map';\nimport { SortedSet } from '../util/sorted_set';\nimport { ignoreIfPrimaryLeaseLoss, LocalStore } from './local_store';\nimport {\n GarbageCollectionScheduler,\n PersistenceTransaction\n} from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { TargetData } from './target_data';\nimport { isIndexedDbTransactionError } from './simple_db';\n\nconst LOG_TAG = 'LruGarbageCollector';\n\n/**\n * Persistence layers intending to use LRU Garbage collection should have reference delegates that\n * implement this interface. This interface defines the operations that the LRU garbage collector\n * needs from the persistence layer.\n */\nexport interface LruDelegate {\n readonly garbageCollector: LruGarbageCollector;\n\n /** Enumerates all the targets in the TargetCache. */\n forEachTarget(\n txn: PersistenceTransaction,\n f: (target: TargetData) => void\n ): PersistencePromise<void>;\n\n getSequenceNumberCount(\n txn: PersistenceTransaction\n ): PersistencePromise<number>;\n\n /**\n * Enumerates sequence numbers for documents not associated with a target.\n * Note that this may include duplicate sequence numbers.\n */\n forEachOrphanedDocumentSequenceNumber(\n txn: PersistenceTransaction,\n f: (sequenceNumber: ListenSequenceNumber) => void\n ): PersistencePromise<void>;\n\n /**\n * Removes all targets that have a sequence number less than or equal to `upperBound`, and are not\n * present in the `activeTargetIds` set.\n *\n * @return the number of targets removed.\n */\n removeTargets(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number>;\n\n /**\n * Removes all unreferenced documents from the cache that have a sequence number less than or\n * equal to the given `upperBound`.\n *\n * @return the number of documents removed.\n */\n removeOrphanedDocuments(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber\n ): PersistencePromise<number>;\n\n getCacheSize(txn: PersistenceTransaction): PersistencePromise<number>;\n}\n\n/**\n * Describes a map whose keys are active target ids. We do not care about the type of the\n * values.\n */\nexport type ActiveTargets = SortedMap<TargetId, unknown>;\n\n// The type and comparator for the items contained in the SortedSet used in\n// place of a priority queue for the RollingSequenceNumberBuffer.\ntype BufferEntry = [ListenSequenceNumber, number];\nfunction bufferEntryComparator(\n [aSequence, aIndex]: BufferEntry,\n [bSequence, bIndex]: BufferEntry\n): number {\n const seqCmp = primitiveComparator(aSequence, bSequence);\n if (seqCmp === 0) {\n // This order doesn't matter, but we can bias against churn by sorting\n // entries created earlier as less than newer entries.\n return primitiveComparator(aIndex, bIndex);\n } else {\n return seqCmp;\n }\n}\n\n/**\n * Used to calculate the nth sequence number. Keeps a rolling buffer of the\n * lowest n values passed to `addElement`, and finally reports the largest of\n * them in `maxValue`.\n */\nclass RollingSequenceNumberBuffer {\n private buffer: SortedSet<BufferEntry> = new SortedSet<BufferEntry>(\n bufferEntryComparator\n );\n\n private previousIndex = 0;\n\n constructor(private readonly maxElements: number) {}\n\n private nextIndex(): number {\n return ++this.previousIndex;\n }\n\n addElement(sequenceNumber: ListenSequenceNumber): void {\n const entry: BufferEntry = [sequenceNumber, this.nextIndex()];\n if (this.buffer.size < this.maxElements) {\n this.buffer = this.buffer.add(entry);\n } else {\n const highestValue = this.buffer.last()!;\n if (bufferEntryComparator(entry, highestValue) < 0) {\n this.buffer = this.buffer.delete(highestValue).add(entry);\n }\n }\n }\n\n get maxValue(): ListenSequenceNumber {\n // Guaranteed to be non-empty. If we decide we are not collecting any\n // sequence numbers, nthSequenceNumber below short-circuits. If we have\n // decided that we are collecting n sequence numbers, it's because n is some\n // percentage of the existing sequence numbers. That means we should never\n // be in a situation where we are collecting sequence numbers but don't\n // actually have any.\n return this.buffer.last()![0];\n }\n}\n\n/**\n * Describes the results of a garbage collection run. `didRun` will be set to\n * `false` if collection was skipped (either it is disabled or the cache size\n * has not hit the threshold). If collection ran, the other fields will be\n * filled in with the details of the results.\n */\nexport interface LruResults {\n readonly didRun: boolean;\n readonly sequenceNumbersCollected: number;\n readonly targetsRemoved: number;\n readonly documentsRemoved: number;\n}\n\nconst GC_DID_NOT_RUN: LruResults = {\n didRun: false,\n sequenceNumbersCollected: 0,\n targetsRemoved: 0,\n documentsRemoved: 0\n};\n\nexport class LruParams {\n static readonly COLLECTION_DISABLED = -1;\n static readonly MINIMUM_CACHE_SIZE_BYTES = 1 * 1024 * 1024;\n static readonly DEFAULT_CACHE_SIZE_BYTES = 40 * 1024 * 1024;\n private static readonly DEFAULT_COLLECTION_PERCENTILE = 10;\n private static readonly DEFAULT_MAX_SEQUENCE_NUMBERS_TO_COLLECT = 1000;\n\n static withCacheSize(cacheSize: number): LruParams {\n return new LruParams(\n cacheSize,\n LruParams.DEFAULT_COLLECTION_PERCENTILE,\n LruParams.DEFAULT_MAX_SEQUENCE_NUMBERS_TO_COLLECT\n );\n }\n\n static readonly DEFAULT: LruParams = new LruParams(\n LruParams.DEFAULT_CACHE_SIZE_BYTES,\n LruParams.DEFAULT_COLLECTION_PERCENTILE,\n LruParams.DEFAULT_MAX_SEQUENCE_NUMBERS_TO_COLLECT\n );\n\n static readonly DISABLED: LruParams = new LruParams(\n LruParams.COLLECTION_DISABLED,\n 0,\n 0\n );\n\n constructor(\n // When we attempt to collect, we will only do so if the cache size is greater than this\n // threshold. Passing `COLLECTION_DISABLED` here will cause collection to always be skipped.\n readonly cacheSizeCollectionThreshold: number,\n // The percentage of sequence numbers that we will attempt to collect\n readonly percentileToCollect: number,\n // A cap on the total number of sequence numbers that will be collected. This prevents\n // us from collecting a huge number of sequence numbers if the cache has grown very large.\n readonly maximumSequenceNumbersToCollect: number\n ) {}\n}\n\n/** How long we wait to try running LRU GC after SDK initialization. */\nconst INITIAL_GC_DELAY_MS = 1 * 60 * 1000;\n/** Minimum amount of time between GC checks, after the first one. */\nconst REGULAR_GC_DELAY_MS = 5 * 60 * 1000;\n\n/**\n * This class is responsible for the scheduling of LRU garbage collection. It handles checking\n * whether or not GC is enabled, as well as which delay to use before the next run.\n */\nexport class LruScheduler implements GarbageCollectionScheduler {\n private hasRun: boolean = false;\n private gcTask: DelayedOperation<void> | null;\n\n constructor(\n private readonly garbageCollector: LruGarbageCollector,\n private readonly asyncQueue: AsyncQueue\n ) {\n this.gcTask = null;\n }\n\n start(localStore: LocalStore): void {\n debugAssert(\n this.gcTask === null,\n 'Cannot start an already started LruScheduler'\n );\n if (\n this.garbageCollector.params.cacheSizeCollectionThreshold !==\n LruParams.COLLECTION_DISABLED\n ) {\n this.scheduleGC(localStore);\n }\n }\n\n stop(): void {\n if (this.gcTask) {\n this.gcTask.cancel();\n this.gcTask = null;\n }\n }\n\n get started(): boolean {\n return this.gcTask !== null;\n }\n\n private scheduleGC(localStore: LocalStore): void {\n debugAssert(\n this.gcTask === null,\n 'Cannot schedule GC while a task is pending'\n );\n const delay = this.hasRun ? REGULAR_GC_DELAY_MS : INITIAL_GC_DELAY_MS;\n logDebug(\n 'LruGarbageCollector',\n `Garbage collection scheduled in ${delay}ms`\n );\n this.gcTask = this.asyncQueue.enqueueAfterDelay(\n TimerId.LruGarbageCollection,\n delay,\n async () => {\n this.gcTask = null;\n this.hasRun = true;\n try {\n await localStore.collectGarbage(this.garbageCollector);\n } catch (e) {\n if (isIndexedDbTransactionError(e)) {\n logDebug(\n LOG_TAG,\n 'Ignoring IndexedDB error during garbage collection: ',\n e\n );\n } else {\n await ignoreIfPrimaryLeaseLoss(e);\n }\n }\n await this.scheduleGC(localStore);\n }\n );\n }\n}\n\n/** Implements the steps for LRU garbage collection. */\nexport class LruGarbageCollector {\n constructor(\n private readonly delegate: LruDelegate,\n readonly params: LruParams\n ) {}\n\n /** Given a percentile of target to collect, returns the number of targets to collect. */\n calculateTargetCount(\n txn: PersistenceTransaction,\n percentile: number\n ): PersistencePromise<number> {\n return this.delegate.getSequenceNumberCount(txn).next(targetCount => {\n return Math.floor((percentile / 100.0) * targetCount);\n });\n }\n\n /** Returns the nth sequence number, counting in order from the smallest. */\n nthSequenceNumber(\n txn: PersistenceTransaction,\n n: number\n ): PersistencePromise<ListenSequenceNumber> {\n if (n === 0) {\n return PersistencePromise.resolve(ListenSequence.INVALID);\n }\n\n const buffer = new RollingSequenceNumberBuffer(n);\n return this.delegate\n .forEachTarget(txn, target => buffer.addElement(target.sequenceNumber))\n .next(() => {\n return this.delegate.forEachOrphanedDocumentSequenceNumber(\n txn,\n sequenceNumber => buffer.addElement(sequenceNumber)\n );\n })\n .next(() => buffer.maxValue);\n }\n\n /**\n * Removes targets with a sequence number equal to or less than the given upper bound, and removes\n * document associations with those targets.\n */\n removeTargets(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number> {\n return this.delegate.removeTargets(txn, upperBound, activeTargetIds);\n }\n\n /**\n * Removes documents that have a sequence number equal to or less than the upper bound and are not\n * otherwise pinned.\n */\n removeOrphanedDocuments(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber\n ): PersistencePromise<number> {\n return this.delegate.removeOrphanedDocuments(txn, upperBound);\n }\n\n collect(\n txn: PersistenceTransaction,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<LruResults> {\n if (\n this.params.cacheSizeCollectionThreshold === LruParams.COLLECTION_DISABLED\n ) {\n logDebug('LruGarbageCollector', 'Garbage collection skipped; disabled');\n return PersistencePromise.resolve(GC_DID_NOT_RUN);\n }\n\n return this.getCacheSize(txn).next(cacheSize => {\n if (cacheSize < this.params.cacheSizeCollectionThreshold) {\n logDebug(\n 'LruGarbageCollector',\n `Garbage collection skipped; Cache size ${cacheSize} ` +\n `is lower than threshold ${this.params.cacheSizeCollectionThreshold}`\n );\n return GC_DID_NOT_RUN;\n } else {\n return this.runGarbageCollection(txn, activeTargetIds);\n }\n });\n }\n\n getCacheSize(txn: PersistenceTransaction): PersistencePromise<number> {\n return this.delegate.getCacheSize(txn);\n }\n\n private runGarbageCollection(\n txn: PersistenceTransaction,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<LruResults> {\n let upperBoundSequenceNumber: number;\n let sequenceNumbersToCollect: number, targetsRemoved: number;\n // Timestamps for various pieces of the process\n let countedTargetsTs: number,\n foundUpperBoundTs: number,\n removedTargetsTs: number,\n removedDocumentsTs: number;\n const startTs = Date.now();\n return this.calculateTargetCount(txn, this.params.percentileToCollect)\n .next(sequenceNumbers => {\n // Cap at the configured max\n if (sequenceNumbers > this.params.maximumSequenceNumbersToCollect) {\n logDebug(\n 'LruGarbageCollector',\n 'Capping sequence numbers to collect down ' +\n `to the maximum of ${this.params.maximumSequenceNumbersToCollect} ` +\n `from ${sequenceNumbers}`\n );\n sequenceNumbersToCollect = this.params\n .maximumSequenceNumbersToCollect;\n } else {\n sequenceNumbersToCollect = sequenceNumbers;\n }\n countedTargetsTs = Date.now();\n\n return this.nthSequenceNumber(txn, sequenceNumbersToCollect);\n })\n .next(upperBound => {\n upperBoundSequenceNumber = upperBound;\n foundUpperBoundTs = Date.now();\n\n return this.removeTargets(\n txn,\n upperBoundSequenceNumber,\n activeTargetIds\n );\n })\n .next(numTargetsRemoved => {\n targetsRemoved = numTargetsRemoved;\n removedTargetsTs = Date.now();\n\n return this.removeOrphanedDocuments(txn, upperBoundSequenceNumber);\n })\n .next(documentsRemoved => {\n removedDocumentsTs = Date.now();\n\n if (getLogLevel() <= LogLevel.DEBUG) {\n const desc =\n 'LRU Garbage Collection\\n' +\n `\\tCounted targets in ${countedTargetsTs - startTs}ms\\n` +\n `\\tDetermined least recently used ${sequenceNumbersToCollect} in ` +\n `${foundUpperBoundTs - countedTargetsTs}ms\\n` +\n `\\tRemoved ${targetsRemoved} targets in ` +\n `${removedTargetsTs - foundUpperBoundTs}ms\\n` +\n `\\tRemoved ${documentsRemoved} documents in ` +\n `${removedDocumentsTs - removedTargetsTs}ms\\n` +\n `Total Duration: ${removedDocumentsTs - startTs}ms`;\n logDebug('LruGarbageCollector', desc);\n }\n\n return PersistencePromise.resolve<LruResults>({\n didRun: true,\n sequenceNumbersCollected: sequenceNumbersToCollect,\n targetsRemoved,\n documentsRemoved\n });\n });\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Timestamp } from '../api/timestamp';\nimport { User } from '../auth/user';\nimport { Query } from '../core/query';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { Target } from '../core/target';\nimport { BatchId, TargetId } from '../core/types';\nimport {\n DocumentKeySet,\n documentKeySet,\n DocumentMap,\n maybeDocumentMap,\n MaybeDocumentMap\n} from '../model/collections';\nimport { MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation, PatchMutation, Precondition } from '../model/mutation';\nimport {\n BATCHID_UNKNOWN,\n MutationBatch,\n MutationBatchResult\n} from '../model/mutation_batch';\nimport { RemoteEvent, TargetChange } from '../remote/remote_event';\nimport { hardAssert, debugAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { primitiveComparator } from '../util/misc';\nimport { ObjectMap } from '../util/obj_map';\nimport { SortedMap } from '../util/sorted_map';\n\nimport { LocalDocumentsView } from './local_documents_view';\nimport { LocalViewChanges } from './local_view_changes';\nimport { LruGarbageCollector, LruResults } from './lru_garbage_collector';\nimport { MutationQueue } from './mutation_queue';\nimport {\n Persistence,\n PersistenceTransaction,\n PRIMARY_LEASE_LOST_ERROR_MSG\n} from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { TargetCache } from './target_cache';\nimport { QueryEngine } from './query_engine';\nimport { RemoteDocumentCache } from './remote_document_cache';\nimport { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';\nimport { ClientId } from './shared_client_state';\nimport { TargetData, TargetPurpose } from './target_data';\nimport { ByteString } from '../util/byte_string';\nimport { IndexedDbPersistence } from './indexeddb_persistence';\nimport { IndexedDbMutationQueue } from './indexeddb_mutation_queue';\nimport { IndexedDbRemoteDocumentCache } from './indexeddb_remote_document_cache';\nimport { IndexedDbTargetCache } from './indexeddb_target_cache';\nimport { extractFieldMask } from '../model/object_value';\nimport { isIndexedDbTransactionError } from './simple_db';\n\nconst LOG_TAG = 'LocalStore';\n\n/** The result of a write to the local store. */\nexport interface LocalWriteResult {\n batchId: BatchId;\n changes: MaybeDocumentMap;\n}\n\n/** The result of a user-change operation in the local store. */\nexport interface UserChangeResult {\n readonly affectedDocuments: MaybeDocumentMap;\n readonly removedBatchIds: BatchId[];\n readonly addedBatchIds: BatchId[];\n}\n\n/** The result of executing a query against the local store. */\nexport interface QueryResult {\n readonly documents: DocumentMap;\n readonly remoteKeys: DocumentKeySet;\n}\n\n/**\n * Local storage in the Firestore client. Coordinates persistence components\n * like the mutation queue and remote document cache to present a\n * latency-compensated view of stored data.\n *\n * The LocalStore is responsible for accepting mutations from the Sync Engine.\n * Writes from the client are put into a queue as provisional Mutations until\n * they are processed by the RemoteStore and confirmed as having been written\n * to the server.\n *\n * The local store provides the local version of documents that have been\n * modified locally. It maintains the constraint:\n *\n * LocalDocument = RemoteDocument + Active(LocalMutations)\n *\n * (Active mutations are those that are enqueued and have not been previously\n * acknowledged or rejected).\n *\n * The RemoteDocument (\"ground truth\") state is provided via the\n * applyChangeBatch method. It will be some version of a server-provided\n * document OR will be a server-provided document PLUS acknowledged mutations:\n *\n * RemoteDocument' = RemoteDocument + Acknowledged(LocalMutations)\n *\n * Note that this \"dirty\" version of a RemoteDocument will not be identical to a\n * server base version, since it has LocalMutations added to it pending getting\n * an authoritative copy from the server.\n *\n * Since LocalMutations can be rejected by the server, we have to be able to\n * revert a LocalMutation that has already been applied to the LocalDocument\n * (typically done by replaying all remaining LocalMutations to the\n * RemoteDocument to re-apply).\n *\n * The LocalStore is responsible for the garbage collection of the documents it\n * contains. For now, it every doc referenced by a view, the mutation queue, or\n * the RemoteStore.\n *\n * It also maintains the persistence of mapping queries to resume tokens and\n * target ids. It needs to know this data about queries to properly know what\n * docs it would be allowed to garbage collect.\n *\n * The LocalStore must be able to efficiently execute queries against its local\n * cache of the documents, to provide the initial set of results before any\n * remote changes have been received.\n *\n * Note: In TypeScript, most methods return Promises since the implementation\n * may rely on fetching data from IndexedDB which is async.\n * These Promises will only be rejected on an I/O error or other internal\n * (unexpected) failure (e.g. failed assert) and always represent an\n * unrecoverable error (should be caught / reported by the async_queue).\n */\nexport class LocalStore {\n /**\n * The maximum time to leave a resume token buffered without writing it out.\n * This value is arbitrary: it's long enough to avoid several writes\n * (possibly indefinitely if updates come more frequently than this) but\n * short enough that restarting after crashing will still have a pretty\n * recent resume token.\n */\n private static readonly RESUME_TOKEN_MAX_AGE_MICROS = 5 * 60 * 1e6;\n\n /**\n * The set of all mutations that have been sent but not yet been applied to\n * the backend.\n */\n protected mutationQueue: MutationQueue;\n\n /** The set of all cached remote documents. */\n protected remoteDocuments: RemoteDocumentCache;\n\n /**\n * The \"local\" view of all documents (layering mutationQueue on top of\n * remoteDocumentCache).\n */\n protected localDocuments: LocalDocumentsView;\n\n /** Maps a target to its `TargetData`. */\n protected targetCache: TargetCache;\n\n /**\n * Maps a targetID to data about its target.\n *\n * PORTING NOTE: We are using an immutable data structure on Web to make re-runs\n * of `applyRemoteEvent()` idempotent.\n */\n protected targetDataByTarget = new SortedMap<TargetId, TargetData>(\n primitiveComparator\n );\n\n /** Maps a target to its targetID. */\n // TODO(wuandy): Evaluate if TargetId can be part of Target.\n private targetIdByTarget = new ObjectMap<Target, TargetId>(t =>\n t.canonicalId()\n );\n\n /**\n * The read time of the last entry processed by `getNewDocumentChanges()`.\n *\n * PORTING NOTE: This is only used for multi-tab synchronization.\n */\n protected lastDocumentChangeReadTime = SnapshotVersion.min();\n\n constructor(\n /** Manages our in-memory or durable persistence. */\n protected persistence: Persistence,\n private queryEngine: QueryEngine,\n initialUser: User\n ) {\n debugAssert(\n persistence.started,\n 'LocalStore was passed an unstarted persistence implementation'\n );\n this.mutationQueue = persistence.getMutationQueue(initialUser);\n this.remoteDocuments = persistence.getRemoteDocumentCache();\n this.targetCache = persistence.getTargetCache();\n this.localDocuments = new LocalDocumentsView(\n this.remoteDocuments,\n this.mutationQueue,\n this.persistence.getIndexManager()\n );\n this.queryEngine.setLocalDocumentsView(this.localDocuments);\n }\n\n /** Starts the LocalStore. */\n start(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Tells the LocalStore that the currently authenticated user has changed.\n *\n * In response the local store switches the mutation queue to the new user and\n * returns any resulting document changes.\n */\n // PORTING NOTE: Android and iOS only return the documents affected by the\n // change.\n async handleUserChange(user: User): Promise<UserChangeResult> {\n let newMutationQueue = this.mutationQueue;\n let newLocalDocuments = this.localDocuments;\n\n const result = await this.persistence.runTransaction(\n 'Handle user change',\n 'readonly',\n txn => {\n // Swap out the mutation queue, grabbing the pending mutation batches\n // before and after.\n let oldBatches: MutationBatch[];\n return this.mutationQueue\n .getAllMutationBatches(txn)\n .next(promisedOldBatches => {\n oldBatches = promisedOldBatches;\n\n newMutationQueue = this.persistence.getMutationQueue(user);\n\n // Recreate our LocalDocumentsView using the new\n // MutationQueue.\n newLocalDocuments = new LocalDocumentsView(\n this.remoteDocuments,\n newMutationQueue,\n this.persistence.getIndexManager()\n );\n return newMutationQueue.getAllMutationBatches(txn);\n })\n .next(newBatches => {\n const removedBatchIds: BatchId[] = [];\n const addedBatchIds: BatchId[] = [];\n\n // Union the old/new changed keys.\n let changedKeys = documentKeySet();\n\n for (const batch of oldBatches) {\n removedBatchIds.push(batch.batchId);\n for (const mutation of batch.mutations) {\n changedKeys = changedKeys.add(mutation.key);\n }\n }\n\n for (const batch of newBatches) {\n addedBatchIds.push(batch.batchId);\n for (const mutation of batch.mutations) {\n changedKeys = changedKeys.add(mutation.key);\n }\n }\n\n // Return the set of all (potentially) changed documents and the list\n // of mutation batch IDs that were affected by change.\n return newLocalDocuments\n .getDocuments(txn, changedKeys)\n .next(affectedDocuments => {\n return {\n affectedDocuments,\n removedBatchIds,\n addedBatchIds\n };\n });\n });\n }\n );\n\n this.mutationQueue = newMutationQueue;\n this.localDocuments = newLocalDocuments;\n this.queryEngine.setLocalDocumentsView(this.localDocuments);\n\n return result;\n }\n\n /* Accept locally generated Mutations and commit them to storage. */\n localWrite(mutations: Mutation[]): Promise<LocalWriteResult> {\n const localWriteTime = Timestamp.now();\n const keys = mutations.reduce(\n (keys, m) => keys.add(m.key),\n documentKeySet()\n );\n\n let existingDocs: MaybeDocumentMap;\n\n return this.persistence\n .runTransaction('Locally write mutations', 'readwrite', txn => {\n // Load and apply all existing mutations. This lets us compute the\n // current base state for all non-idempotent transforms before applying\n // any additional user-provided writes.\n return this.localDocuments.getDocuments(txn, keys).next(docs => {\n existingDocs = docs;\n\n // For non-idempotent mutations (such as `FieldValue.increment()`),\n // we record the base state in a separate patch mutation. This is\n // later used to guarantee consistent values and prevents flicker\n // even if the backend sends us an update that already includes our\n // transform.\n const baseMutations: Mutation[] = [];\n\n for (const mutation of mutations) {\n const baseValue = mutation.extractBaseValue(\n existingDocs.get(mutation.key)\n );\n if (baseValue != null) {\n // NOTE: The base state should only be applied if there's some\n // existing document to override, so use a Precondition of\n // exists=true\n baseMutations.push(\n new PatchMutation(\n mutation.key,\n baseValue,\n extractFieldMask(baseValue.proto.mapValue!),\n Precondition.exists(true)\n )\n );\n }\n }\n\n return this.mutationQueue.addMutationBatch(\n txn,\n localWriteTime,\n baseMutations,\n mutations\n );\n });\n })\n .then(batch => {\n const changes = batch.applyToLocalDocumentSet(existingDocs);\n return { batchId: batch.batchId, changes };\n });\n }\n\n /**\n * Acknowledge the given batch.\n *\n * On the happy path when a batch is acknowledged, the local store will\n *\n * + remove the batch from the mutation queue;\n * + apply the changes to the remote document cache;\n * + recalculate the latency compensated view implied by those changes (there\n * may be mutations in the queue that affect the documents but haven't been\n * acknowledged yet); and\n * + give the changed documents back the sync engine\n *\n * @returns The resulting (modified) documents.\n */\n acknowledgeBatch(\n batchResult: MutationBatchResult\n ): Promise<MaybeDocumentMap> {\n return this.persistence.runTransaction(\n 'Acknowledge batch',\n 'readwrite-primary',\n txn => {\n const affected = batchResult.batch.keys();\n const documentBuffer = this.remoteDocuments.newChangeBuffer({\n trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`\n });\n return this.mutationQueue\n .acknowledgeBatch(txn, batchResult.batch, batchResult.streamToken)\n .next(() =>\n this.applyWriteToRemoteDocuments(txn, batchResult, documentBuffer)\n )\n .next(() => documentBuffer.apply(txn))\n .next(() => this.mutationQueue.performConsistencyCheck(txn))\n .next(() => this.localDocuments.getDocuments(txn, affected));\n }\n );\n }\n\n /**\n * Remove mutations from the MutationQueue for the specified batch;\n * LocalDocuments will be recalculated.\n *\n * @returns The resulting modified documents.\n */\n rejectBatch(batchId: BatchId): Promise<MaybeDocumentMap> {\n return this.persistence.runTransaction(\n 'Reject batch',\n 'readwrite-primary',\n txn => {\n let affectedKeys: DocumentKeySet;\n return this.mutationQueue\n .lookupMutationBatch(txn, batchId)\n .next((batch: MutationBatch | null) => {\n hardAssert(batch !== null, 'Attempt to reject nonexistent batch!');\n affectedKeys = batch.keys();\n return this.mutationQueue.removeMutationBatch(txn, batch);\n })\n .next(() => {\n return this.mutationQueue.performConsistencyCheck(txn);\n })\n .next(() => {\n return this.localDocuments.getDocuments(txn, affectedKeys);\n });\n }\n );\n }\n\n /**\n * Returns the largest (latest) batch id in mutation queue that is pending server response.\n * Returns `BATCHID_UNKNOWN` if the queue is empty.\n */\n getHighestUnacknowledgedBatchId(): Promise<BatchId> {\n return this.persistence.runTransaction(\n 'Get highest unacknowledged batch id',\n 'readonly',\n txn => {\n return this.mutationQueue.getHighestUnacknowledgedBatchId(txn);\n }\n );\n }\n\n /** Returns the last recorded stream token for the current user. */\n getLastStreamToken(): Promise<ByteString> {\n return this.persistence.runTransaction(\n 'Get last stream token',\n 'readonly',\n txn => {\n return this.mutationQueue.getLastStreamToken(txn);\n }\n );\n }\n\n /**\n * Sets the stream token for the current user without acknowledging any\n * mutation batch. This is usually only useful after a stream handshake or in\n * response to an error that requires clearing the stream token.\n */\n setLastStreamToken(streamToken: ByteString): Promise<void> {\n return this.persistence.runTransaction(\n 'Set last stream token',\n 'readwrite-primary',\n txn => {\n return this.mutationQueue.setLastStreamToken(txn, streamToken);\n }\n );\n }\n\n /**\n * Returns the last consistent snapshot processed (used by the RemoteStore to\n * determine whether to buffer incoming snapshots from the backend).\n */\n getLastRemoteSnapshotVersion(): Promise<SnapshotVersion> {\n return this.persistence.runTransaction(\n 'Get last remote snapshot version',\n 'readonly',\n txn => this.targetCache.getLastRemoteSnapshotVersion(txn)\n );\n }\n\n /**\n * Update the \"ground-state\" (remote) documents. We assume that the remote\n * event reflects any write batches that have been acknowledged or rejected\n * (i.e. we do not re-apply local mutations to updates from this event).\n *\n * LocalDocuments are re-calculated if there are remaining mutations in the\n * queue.\n */\n applyRemoteEvent(remoteEvent: RemoteEvent): Promise<MaybeDocumentMap> {\n const remoteVersion = remoteEvent.snapshotVersion;\n let newTargetDataByTargetMap = this.targetDataByTarget;\n\n return this.persistence\n .runTransaction('Apply remote event', 'readwrite-primary', txn => {\n const documentBuffer = this.remoteDocuments.newChangeBuffer({\n trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`\n });\n\n // Reset newTargetDataByTargetMap in case this transaction gets re-run.\n newTargetDataByTargetMap = this.targetDataByTarget;\n\n const promises = [] as Array<PersistencePromise<void>>;\n remoteEvent.targetChanges.forEach((change, targetId) => {\n const oldTargetData = newTargetDataByTargetMap.get(targetId);\n if (!oldTargetData) {\n return;\n }\n\n // Only update the remote keys if the target is still active. This\n // ensures that we can persist the updated target data along with\n // the updated assignment.\n promises.push(\n this.targetCache\n .removeMatchingKeys(txn, change.removedDocuments, targetId)\n .next(() => {\n return this.targetCache.addMatchingKeys(\n txn,\n change.addedDocuments,\n targetId\n );\n })\n );\n\n const resumeToken = change.resumeToken;\n // Update the resume token if the change includes one.\n if (resumeToken.approximateByteSize() > 0) {\n const newTargetData = oldTargetData\n .withResumeToken(resumeToken, remoteVersion)\n .withSequenceNumber(txn.currentSequenceNumber);\n newTargetDataByTargetMap = newTargetDataByTargetMap.insert(\n targetId,\n newTargetData\n );\n\n // Update the target data if there are target changes (or if\n // sufficient time has passed since the last update).\n if (\n LocalStore.shouldPersistTargetData(\n oldTargetData,\n newTargetData,\n change\n )\n ) {\n promises.push(\n this.targetCache.updateTargetData(txn, newTargetData)\n );\n }\n }\n });\n\n let changedDocs = maybeDocumentMap();\n let updatedKeys = documentKeySet();\n remoteEvent.documentUpdates.forEach((key, doc) => {\n updatedKeys = updatedKeys.add(key);\n });\n\n // Each loop iteration only affects its \"own\" doc, so it's safe to get all the remote\n // documents in advance in a single call.\n promises.push(\n documentBuffer.getEntries(txn, updatedKeys).next(existingDocs => {\n remoteEvent.documentUpdates.forEach((key, doc) => {\n const existingDoc = existingDocs.get(key);\n\n // Note: The order of the steps below is important, since we want\n // to ensure that rejected limbo resolutions (which fabricate\n // NoDocuments with SnapshotVersion.min()) never add documents to\n // cache.\n if (\n doc instanceof NoDocument &&\n doc.version.isEqual(SnapshotVersion.min())\n ) {\n // NoDocuments with SnapshotVersion.min() are used in manufactured\n // events. We remove these documents from cache since we lost\n // access.\n documentBuffer.removeEntry(key, remoteVersion);\n changedDocs = changedDocs.insert(key, doc);\n } else if (\n existingDoc == null ||\n doc.version.compareTo(existingDoc.version) > 0 ||\n (doc.version.compareTo(existingDoc.version) === 0 &&\n existingDoc.hasPendingWrites)\n ) {\n debugAssert(\n !SnapshotVersion.min().isEqual(remoteVersion),\n 'Cannot add a document when the remote version is zero'\n );\n documentBuffer.addEntry(doc, remoteVersion);\n changedDocs = changedDocs.insert(key, doc);\n } else {\n logDebug(\n LOG_TAG,\n 'Ignoring outdated watch update for ',\n key,\n '. Current version:',\n existingDoc.version,\n ' Watch version:',\n doc.version\n );\n }\n\n if (remoteEvent.resolvedLimboDocuments.has(key)) {\n promises.push(\n this.persistence.referenceDelegate.updateLimboDocument(\n txn,\n key\n )\n );\n }\n });\n })\n );\n\n // HACK: The only reason we allow a null snapshot version is so that we\n // can synthesize remote events when we get permission denied errors while\n // trying to resolve the state of a locally cached document that is in\n // limbo.\n if (!remoteVersion.isEqual(SnapshotVersion.min())) {\n const updateRemoteVersion = this.targetCache\n .getLastRemoteSnapshotVersion(txn)\n .next(lastRemoteSnapshotVersion => {\n debugAssert(\n remoteVersion.compareTo(lastRemoteSnapshotVersion) >= 0,\n 'Watch stream reverted to previous snapshot?? ' +\n remoteVersion +\n ' < ' +\n lastRemoteSnapshotVersion\n );\n return this.targetCache.setTargetsMetadata(\n txn,\n txn.currentSequenceNumber,\n remoteVersion\n );\n });\n promises.push(updateRemoteVersion);\n }\n\n return PersistencePromise.waitFor(promises)\n .next(() => documentBuffer.apply(txn))\n .next(() => {\n return this.localDocuments.getLocalViewOfDocuments(\n txn,\n changedDocs\n );\n });\n })\n .then(changedDocs => {\n this.targetDataByTarget = newTargetDataByTargetMap;\n return changedDocs;\n });\n }\n\n /**\n * Returns true if the newTargetData should be persisted during an update of\n * an active target. TargetData should always be persisted when a target is\n * being released and should not call this function.\n *\n * While the target is active, TargetData updates can be omitted when nothing\n * about the target has changed except metadata like the resume token or\n * snapshot version. Occasionally it's worth the extra write to prevent these\n * values from getting too stale after a crash, but this doesn't have to be\n * too frequent.\n */\n private static shouldPersistTargetData(\n oldTargetData: TargetData,\n newTargetData: TargetData,\n change: TargetChange\n ): boolean {\n hardAssert(\n newTargetData.resumeToken.approximateByteSize() > 0,\n 'Attempted to persist target data with no resume token'\n );\n\n // Always persist target data if we don't already have a resume token.\n if (oldTargetData.resumeToken.approximateByteSize() === 0) {\n return true;\n }\n\n // Don't allow resume token changes to be buffered indefinitely. This\n // allows us to be reasonably up-to-date after a crash and avoids needing\n // to loop over all active queries on shutdown. Especially in the browser\n // we may not get time to do anything interesting while the current tab is\n // closing.\n const timeDelta =\n newTargetData.snapshotVersion.toMicroseconds() -\n oldTargetData.snapshotVersion.toMicroseconds();\n if (timeDelta >= this.RESUME_TOKEN_MAX_AGE_MICROS) {\n return true;\n }\n\n // Otherwise if the only thing that has changed about a target is its resume\n // token it's not worth persisting. Note that the RemoteStore keeps an\n // in-memory view of the currently active targets which includes the current\n // resume token, so stream failure or user changes will still use an\n // up-to-date resume token regardless of what we do here.\n const changes =\n change.addedDocuments.size +\n change.modifiedDocuments.size +\n change.removedDocuments.size;\n return changes > 0;\n }\n\n /**\n * Notify local store of the changed views to locally pin documents.\n */\n async notifyLocalViewChanges(viewChanges: LocalViewChanges[]): Promise<void> {\n try {\n await this.persistence.runTransaction(\n 'notifyLocalViewChanges',\n 'readwrite',\n txn => {\n return PersistencePromise.forEach(\n viewChanges,\n (viewChange: LocalViewChanges) => {\n return PersistencePromise.forEach(\n viewChange.addedKeys,\n (key: DocumentKey) =>\n this.persistence.referenceDelegate.addReference(\n txn,\n viewChange.targetId,\n key\n )\n ).next(() =>\n PersistencePromise.forEach(\n viewChange.removedKeys,\n (key: DocumentKey) =>\n this.persistence.referenceDelegate.removeReference(\n txn,\n viewChange.targetId,\n key\n )\n )\n );\n }\n );\n }\n );\n } catch (e) {\n if (isIndexedDbTransactionError(e)) {\n // If `notifyLocalViewChanges` fails, we did not advance the sequence\n // number for the documents that were included in this transaction.\n // This might trigger them to be deleted earlier than they otherwise\n // would have, but it should not invalidate the integrity of the data.\n logDebug(LOG_TAG, 'Failed to update sequence numbers: ' + e);\n } else {\n throw e;\n }\n }\n\n for (const viewChange of viewChanges) {\n const targetId = viewChange.targetId;\n\n if (!viewChange.fromCache) {\n const targetData = this.targetDataByTarget.get(targetId);\n debugAssert(\n targetData !== null,\n `Can't set limbo-free snapshot version for unknown target: ${targetId}`\n );\n\n // Advance the last limbo free snapshot version\n const lastLimboFreeSnapshotVersion = targetData.snapshotVersion;\n const updatedTargetData = targetData.withLastLimboFreeSnapshotVersion(\n lastLimboFreeSnapshotVersion\n );\n this.targetDataByTarget = this.targetDataByTarget.insert(\n targetId,\n updatedTargetData\n );\n }\n }\n }\n\n /**\n * Gets the mutation batch after the passed in batchId in the mutation queue\n * or null if empty.\n * @param afterBatchId If provided, the batch to search after.\n * @returns The next mutation or null if there wasn't one.\n */\n nextMutationBatch(afterBatchId?: BatchId): Promise<MutationBatch | null> {\n return this.persistence.runTransaction(\n 'Get next mutation batch',\n 'readonly',\n txn => {\n if (afterBatchId === undefined) {\n afterBatchId = BATCHID_UNKNOWN;\n }\n return this.mutationQueue.getNextMutationBatchAfterBatchId(\n txn,\n afterBatchId\n );\n }\n );\n }\n\n /**\n * Read the current value of a Document with a given key or null if not\n * found - used for testing.\n */\n readDocument(key: DocumentKey): Promise<MaybeDocument | null> {\n return this.persistence.runTransaction('read document', 'readonly', txn => {\n return this.localDocuments.getDocument(txn, key);\n });\n }\n\n /**\n * Assigns the given target an internal ID so that its results can be pinned so\n * they don't get GC'd. A target must be allocated in the local store before\n * the store can be used to manage its view.\n *\n * Allocating an already allocated `Target` will return the existing `TargetData`\n * for that `Target`.\n */\n allocateTarget(target: Target): Promise<TargetData> {\n return this.persistence\n .runTransaction('Allocate target', 'readwrite', txn => {\n let targetData: TargetData;\n return this.targetCache\n .getTargetData(txn, target)\n .next((cached: TargetData | null) => {\n if (cached) {\n // This target has been listened to previously, so reuse the\n // previous targetID.\n // TODO(mcg): freshen last accessed date?\n targetData = cached;\n return PersistencePromise.resolve(targetData);\n } else {\n return this.targetCache.allocateTargetId(txn).next(targetId => {\n targetData = new TargetData(\n target,\n targetId,\n TargetPurpose.Listen,\n txn.currentSequenceNumber\n );\n return this.targetCache\n .addTargetData(txn, targetData)\n .next(() => targetData);\n });\n }\n });\n })\n .then(targetData => {\n if (this.targetDataByTarget.get(targetData.targetId) === null) {\n this.targetDataByTarget = this.targetDataByTarget.insert(\n targetData.targetId,\n targetData\n );\n this.targetIdByTarget.set(target, targetData.targetId);\n }\n return targetData;\n });\n }\n\n /**\n * Returns the TargetData as seen by the LocalStore, including updates that may\n * have not yet been persisted to the TargetCache.\n */\n // Visible for testing.\n getTargetData(\n transaction: PersistenceTransaction,\n target: Target\n ): PersistencePromise<TargetData | null> {\n const targetId = this.targetIdByTarget.get(target);\n if (targetId !== undefined) {\n return PersistencePromise.resolve<TargetData | null>(\n this.targetDataByTarget.get(targetId)\n );\n } else {\n return this.targetCache.getTargetData(transaction, target);\n }\n }\n\n /**\n * Unpin all the documents associated with the given target. If\n * `keepPersistedTargetData` is set to false and Eager GC enabled, the method\n * directly removes the associated target data from the target cache.\n *\n * Releasing a non-existing `Target` is a no-op.\n */\n // PORTING NOTE: `keepPersistedTargetData` is multi-tab only.\n releaseTarget(\n targetId: number,\n keepPersistedTargetData: boolean\n ): Promise<void> {\n const targetData = this.targetDataByTarget.get(targetId);\n debugAssert(\n targetData !== null,\n `Tried to release nonexistent target: ${targetId}`\n );\n\n const mode = keepPersistedTargetData ? 'readwrite' : 'readwrite-primary';\n return this.persistence\n .runTransaction('Release target', mode, txn => {\n if (!keepPersistedTargetData) {\n return this.persistence.referenceDelegate.removeTarget(\n txn,\n targetData!\n );\n } else {\n return PersistencePromise.resolve();\n }\n })\n .then(() => {\n this.targetDataByTarget = this.targetDataByTarget.remove(targetId);\n this.targetIdByTarget.delete(targetData!.target);\n });\n }\n\n /**\n * Runs the specified query against the local store and returns the results,\n * potentially taking advantage of query data from previous executions (such\n * as the set of remote keys).\n *\n * @param usePreviousResults Whether results from previous executions can\n * be used to optimize this query execution.\n */\n executeQuery(\n query: Query,\n usePreviousResults: boolean\n ): Promise<QueryResult> {\n let lastLimboFreeSnapshotVersion = SnapshotVersion.min();\n let remoteKeys = documentKeySet();\n\n return this.persistence.runTransaction('Execute query', 'readonly', txn => {\n return this.getTargetData(txn, query.toTarget())\n .next(targetData => {\n if (targetData) {\n lastLimboFreeSnapshotVersion =\n targetData.lastLimboFreeSnapshotVersion;\n return this.targetCache\n .getMatchingKeysForTargetId(txn, targetData.targetId)\n .next(result => {\n remoteKeys = result;\n });\n }\n })\n .next(() =>\n this.queryEngine.getDocumentsMatchingQuery(\n txn,\n query,\n usePreviousResults\n ? lastLimboFreeSnapshotVersion\n : SnapshotVersion.min(),\n usePreviousResults ? remoteKeys : documentKeySet()\n )\n )\n .next(documents => {\n return { documents, remoteKeys };\n });\n });\n }\n\n private applyWriteToRemoteDocuments(\n txn: PersistenceTransaction,\n batchResult: MutationBatchResult,\n documentBuffer: RemoteDocumentChangeBuffer\n ): PersistencePromise<void> {\n const batch = batchResult.batch;\n const docKeys = batch.keys();\n let promiseChain = PersistencePromise.resolve();\n docKeys.forEach(docKey => {\n promiseChain = promiseChain\n .next(() => {\n return documentBuffer.getEntry(txn, docKey);\n })\n .next((remoteDoc: MaybeDocument | null) => {\n let doc = remoteDoc;\n const ackVersion = batchResult.docVersions.get(docKey);\n hardAssert(\n ackVersion !== null,\n 'ackVersions should contain every doc in the write.'\n );\n if (!doc || doc.version.compareTo(ackVersion!) < 0) {\n doc = batch.applyToRemoteDocument(docKey, doc, batchResult);\n if (!doc) {\n debugAssert(\n !remoteDoc,\n 'Mutation batch ' +\n batch +\n ' applied to document ' +\n remoteDoc +\n ' resulted in null'\n );\n } else {\n // We use the commitVersion as the readTime rather than the\n // document's updateTime since the updateTime is not advanced\n // for updates that do not modify the underlying document.\n documentBuffer.addEntry(doc, batchResult.commitVersion);\n }\n }\n });\n });\n return promiseChain.next(() =>\n this.mutationQueue.removeMutationBatch(txn, batch)\n );\n }\n\n collectGarbage(garbageCollector: LruGarbageCollector): Promise<LruResults> {\n return this.persistence.runTransaction(\n 'Collect garbage',\n 'readwrite-primary',\n txn => garbageCollector.collect(txn, this.targetDataByTarget)\n );\n }\n}\n\n/**\n * An implementation of LocalStore that provides additional functionality\n * for MultiTabSyncEngine.\n */\n// PORTING NOTE: Web only.\nexport class MultiTabLocalStore extends LocalStore {\n protected mutationQueue: IndexedDbMutationQueue;\n protected remoteDocuments: IndexedDbRemoteDocumentCache;\n protected targetCache: IndexedDbTargetCache;\n\n constructor(\n protected persistence: IndexedDbPersistence,\n queryEngine: QueryEngine,\n initialUser: User\n ) {\n super(persistence, queryEngine, initialUser);\n\n this.mutationQueue = persistence.getMutationQueue(initialUser);\n this.remoteDocuments = persistence.getRemoteDocumentCache();\n this.targetCache = persistence.getTargetCache();\n }\n\n /** Starts the LocalStore. */\n start(): Promise<void> {\n return this.synchronizeLastDocumentChangeReadTime();\n }\n\n /** Returns the local view of the documents affected by a mutation batch. */\n lookupMutationDocuments(batchId: BatchId): Promise<MaybeDocumentMap | null> {\n return this.persistence.runTransaction(\n 'Lookup mutation documents',\n 'readonly',\n txn => {\n return this.mutationQueue\n .lookupMutationKeys(txn, batchId)\n .next(keys => {\n if (keys) {\n return this.localDocuments.getDocuments(\n txn,\n keys\n ) as PersistencePromise<MaybeDocumentMap | null>;\n } else {\n return PersistencePromise.resolve<MaybeDocumentMap | null>(null);\n }\n });\n }\n );\n }\n\n removeCachedMutationBatchMetadata(batchId: BatchId): void {\n this.mutationQueue.removeCachedMutationKeys(batchId);\n }\n\n setNetworkEnabled(networkEnabled: boolean): void {\n this.persistence.setNetworkEnabled(networkEnabled);\n }\n\n getActiveClients(): Promise<ClientId[]> {\n return this.persistence.getActiveClients();\n }\n\n getTarget(targetId: TargetId): Promise<Target | null> {\n const cachedTargetData = this.targetDataByTarget.get(targetId);\n\n if (cachedTargetData) {\n return Promise.resolve(cachedTargetData.target);\n } else {\n return this.persistence.runTransaction(\n 'Get target data',\n 'readonly',\n txn => {\n return this.targetCache\n .getTargetDataForTarget(txn, targetId)\n .next(targetData => (targetData ? targetData.target : null));\n }\n );\n }\n }\n\n /**\n * Returns the set of documents that have been updated since the last call.\n * If this is the first call, returns the set of changes since client\n * initialization. Further invocations will return document changes since\n * the point of rejection.\n */\n getNewDocumentChanges(): Promise<MaybeDocumentMap> {\n return this.persistence\n .runTransaction('Get new document changes', 'readonly', txn =>\n this.remoteDocuments.getNewDocumentChanges(\n txn,\n this.lastDocumentChangeReadTime\n )\n )\n .then(({ changedDocs, readTime }) => {\n this.lastDocumentChangeReadTime = readTime;\n return changedDocs;\n });\n }\n\n /**\n * Reads the newest document change from persistence and forwards the internal\n * synchronization marker so that calls to `getNewDocumentChanges()`\n * only return changes that happened after client initialization.\n */\n async synchronizeLastDocumentChangeReadTime(): Promise<void> {\n this.lastDocumentChangeReadTime = await this.persistence.runTransaction(\n 'Synchronize last document change read time',\n 'readonly',\n txn => this.remoteDocuments.getLastReadTime(txn)\n );\n }\n}\n\n/**\n * Verifies the error thrown by a LocalStore operation. If a LocalStore\n * operation fails because the primary lease has been taken by another client,\n * we ignore the error (the persistence layer will immediately call\n * `applyPrimaryLease` to propagate the primary state change). All other errors\n * are re-thrown.\n *\n * @param err An error returned by a LocalStore operation.\n * @return A Promise that resolves after we recovered, or the original error.\n */\nexport async function ignoreIfPrimaryLeaseLoss(\n err: FirestoreError\n): Promise<void> {\n if (\n err.code === Code.FAILED_PRECONDITION &&\n err.message === PRIMARY_LEASE_LOST_ERROR_MSG\n ) {\n logDebug(LOG_TAG, 'Unexpectedly lost primary lease');\n } else {\n throw err;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { User } from '../auth/user';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { DocumentKey } from '../model/document_key';\nimport { IndexManager } from './index_manager';\nimport { LocalStore } from './local_store';\nimport { MutationQueue } from './mutation_queue';\nimport { PersistencePromise } from './persistence_promise';\nimport { TargetCache } from './target_cache';\nimport { RemoteDocumentCache } from './remote_document_cache';\nimport { TargetData } from './target_data';\n\nexport const PRIMARY_LEASE_LOST_ERROR_MSG =\n 'The current tab is not in the required state to perform this operation. ' +\n 'It might be necessary to refresh the browser tab.';\n\n/**\n * A base class representing a persistence transaction, encapsulating both the\n * transaction's sequence numbers as well as a list of onCommitted listeners.\n *\n * When you call Persistence.runTransaction(), it will create a transaction and\n * pass it to your callback. You then pass it to any method that operates\n * on persistence.\n */\nexport abstract class PersistenceTransaction {\n private readonly onCommittedListeners: Array<() => void> = [];\n\n abstract readonly currentSequenceNumber: ListenSequenceNumber;\n\n addOnCommittedListener(listener: () => void): void {\n this.onCommittedListeners.push(listener);\n }\n\n raiseOnCommittedEvent(): void {\n this.onCommittedListeners.forEach(listener => listener());\n }\n}\n\n/** The different modes supported by `IndexedDbPersistence.runTransaction()`. */\nexport type PersistenceTransactionMode =\n | 'readonly'\n | 'readwrite'\n | 'readwrite-primary';\n\n/**\n * Callback type for primary state notifications. This callback can be\n * registered with the persistence layer to get notified when we transition from\n * primary to secondary state and vice versa.\n *\n * Note: Instances can only toggle between Primary and Secondary state if\n * IndexedDB persistence is enabled and multiple clients are active. If this\n * listener is registered with MemoryPersistence, the callback will be called\n * exactly once marking the current instance as Primary.\n */\nexport type PrimaryStateListener = (isPrimary: boolean) => Promise<void>;\n\n/**\n * A ReferenceDelegate instance handles all of the hooks into the document-reference lifecycle. This\n * includes being added to a target, being removed from a target, being subject to mutation, and\n * being mutated by the user.\n *\n * Different implementations may do different things with each of these events. Not every\n * implementation needs to do something with every lifecycle hook.\n *\n * PORTING NOTE: since sequence numbers are attached to transactions in this\n * client, the ReferenceDelegate does not need to deal in transactional\n * semantics (onTransactionStarted/Committed()), nor does it need to track and\n * generate sequence numbers (getCurrentSequenceNumber()).\n */\nexport interface ReferenceDelegate {\n /** Notify the delegate that the given document was added to a target. */\n addReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n doc: DocumentKey\n ): PersistencePromise<void>;\n\n /** Notify the delegate that the given document was removed from a target. */\n removeReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n doc: DocumentKey\n ): PersistencePromise<void>;\n\n /**\n * Notify the delegate that a target was removed. The delegate may, but is not obligated to,\n * actually delete the target and associated data.\n */\n removeTarget(\n txn: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void>;\n\n /**\n * Notify the delegate that a document may no longer be part of any views or\n * have any mutations associated.\n */\n markPotentiallyOrphaned(\n txn: PersistenceTransaction,\n doc: DocumentKey\n ): PersistencePromise<void>;\n\n /** Notify the delegate that a limbo document was updated. */\n updateLimboDocument(\n txn: PersistenceTransaction,\n doc: DocumentKey\n ): PersistencePromise<void>;\n}\n\n/**\n * Persistence is the lowest-level shared interface to persistent storage in\n * Firestore.\n *\n * Persistence is used to create MutationQueue and RemoteDocumentCache\n * instances backed by persistence (which might be in-memory or LevelDB).\n *\n * Persistence also exposes an API to create and run PersistenceTransactions\n * against persistence. All read / write operations must be wrapped in a\n * transaction. Implementations of PersistenceTransaction / Persistence only\n * need to guarantee that writes made against the transaction are not made to\n * durable storage until the transaction resolves its PersistencePromise.\n * Since memory-only storage components do not alter durable storage, they are\n * free to ignore the transaction.\n *\n * This contract is enough to allow the LocalStore be be written\n * independently of whether or not the stored state actually is durably\n * persisted. If persistent storage is enabled, writes are grouped together to\n * avoid inconsistent state that could cause crashes.\n *\n * Concretely, when persistent storage is enabled, the persistent versions of\n * MutationQueue, RemoteDocumentCache, and others (the mutators) will\n * defer their writes into a transaction. Once the local store has completed\n * one logical operation, it commits the transaction.\n *\n * When persistent storage is disabled, the non-persistent versions of the\n * mutators ignore the transaction. This short-cut is allowed because\n * memory-only storage leaves no state so it cannot be inconsistent.\n *\n * This simplifies the implementations of the mutators and allows memory-only\n * implementations to supplement the persistent ones without requiring any\n * special dual-store implementation of Persistence. The cost is that the\n * LocalStore needs to be slightly careful about the order of its reads and\n * writes in order to avoid relying on being able to read back uncommitted\n * writes.\n */\nexport interface Persistence {\n /**\n * Whether or not this persistence instance has been started.\n */\n readonly started: boolean;\n\n readonly referenceDelegate: ReferenceDelegate;\n\n /** Starts persistence. */\n start(): Promise<void>;\n\n /**\n * Releases any resources held during eager shutdown.\n */\n shutdown(): Promise<void>;\n\n /**\n * Registers a listener that gets called when the database receives a\n * version change event indicating that it has deleted.\n *\n * PORTING NOTE: This is only used for Web multi-tab.\n */\n setDatabaseDeletedListener(\n databaseDeletedListener: () => Promise<void>\n ): void;\n\n /**\n * Returns a MutationQueue representing the persisted mutations for the\n * given user.\n *\n * Note: The implementation is free to return the same instance every time\n * this is called for a given user. In particular, the memory-backed\n * implementation does this to emulate the persisted implementation to the\n * extent possible (e.g. in the case of uid switching from\n * sally=>jack=>sally, sally's mutation queue will be preserved).\n */\n getMutationQueue(user: User): MutationQueue;\n\n /**\n * Returns a TargetCache representing the persisted cache of targets.\n *\n * Note: The implementation is free to return the same instance every time\n * this is called. In particular, the memory-backed implementation does this\n * to emulate the persisted implementation to the extent possible.\n */\n getTargetCache(): TargetCache;\n\n /**\n * Returns a RemoteDocumentCache representing the persisted cache of remote\n * documents.\n *\n * Note: The implementation is free to return the same instance every time\n * this is called. In particular, the memory-backed implementation does this\n * to emulate the persisted implementation to the extent possible.\n */\n getRemoteDocumentCache(): RemoteDocumentCache;\n\n /**\n * Returns an IndexManager instance that manages our persisted query indexes.\n *\n * Note: The implementation is free to return the same instance every time\n * this is called. In particular, the memory-backed implementation does this\n * to emulate the persisted implementation to the extent possible.\n */\n getIndexManager(): IndexManager;\n\n /**\n * Performs an operation inside a persistence transaction. Any reads or writes\n * against persistence must be performed within a transaction. Writes will be\n * committed atomically once the transaction completes.\n *\n * Persistence operations are asynchronous and therefore the provided\n * transactionOperation must return a PersistencePromise. When it is resolved,\n * the transaction will be committed and the Promise returned by this method\n * will resolve.\n *\n * @param action A description of the action performed by this transaction,\n * used for logging.\n * @param mode The underlying mode of the IndexedDb transaction. Can be\n * 'readonly`, 'readwrite' or 'readwrite-primary'. Transactions marked\n * 'readwrite-primary' can only be executed by the primary client. In this\n * mode, the transactionOperation will not be run if the primary lease cannot\n * be acquired and the returned promise will be rejected with a\n * FAILED_PRECONDITION error.\n * @param transactionOperation The operation to run inside a transaction.\n * @return A promise that is resolved once the transaction completes.\n */\n runTransaction<T>(\n action: string,\n mode: PersistenceTransactionMode,\n transactionOperation: (\n transaction: PersistenceTransaction\n ) => PersistencePromise<T>\n ): Promise<T>;\n}\n\n/**\n * Interface implemented by the LRU scheduler to start(), stop() and restart\n * garbage collection.\n */\nexport interface GarbageCollectionScheduler {\n readonly started: boolean;\n start(localStore: LocalStore): void;\n stop(): void;\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { BatchId, TargetId } from '../core/types';\nimport { documentKeySet, DocumentKeySet } from '../model/collections';\nimport { DocumentKey } from '../model/document_key';\nimport { primitiveComparator } from '../util/misc';\nimport { SortedSet } from '../util/sorted_set';\n\n/**\n * A collection of references to a document from some kind of numbered entity\n * (either a target ID or batch ID). As references are added to or removed from\n * the set corresponding events are emitted to a registered garbage collector.\n *\n * Each reference is represented by a DocumentReference object. Each of them\n * contains enough information to uniquely identify the reference. They are all\n * stored primarily in a set sorted by key. A document is considered garbage if\n * there's no references in that set (this can be efficiently checked thanks to\n * sorting by key).\n *\n * ReferenceSet also keeps a secondary set that contains references sorted by\n * IDs. This one is used to efficiently implement removal of all references by\n * some target ID.\n */\nexport class ReferenceSet {\n // A set of outstanding references to a document sorted by key.\n private refsByKey = new SortedSet(DocReference.compareByKey);\n\n // A set of outstanding references to a document sorted by target id.\n private refsByTarget = new SortedSet(DocReference.compareByTargetId);\n\n /** Returns true if the reference set contains no references. */\n isEmpty(): boolean {\n return this.refsByKey.isEmpty();\n }\n\n /** Adds a reference to the given document key for the given ID. */\n addReference(key: DocumentKey, id: TargetId | BatchId): void {\n const ref = new DocReference(key, id);\n this.refsByKey = this.refsByKey.add(ref);\n this.refsByTarget = this.refsByTarget.add(ref);\n }\n\n /** Add references to the given document keys for the given ID. */\n addReferences(keys: DocumentKeySet, id: TargetId | BatchId): void {\n keys.forEach(key => this.addReference(key, id));\n }\n\n /**\n * Removes a reference to the given document key for the given\n * ID.\n */\n removeReference(key: DocumentKey, id: TargetId | BatchId): void {\n this.removeRef(new DocReference(key, id));\n }\n\n removeReferences(keys: DocumentKeySet, id: TargetId | BatchId): void {\n keys.forEach(key => this.removeReference(key, id));\n }\n\n /**\n * Clears all references with a given ID. Calls removeRef() for each key\n * removed.\n */\n removeReferencesForId(id: TargetId | BatchId): DocumentKey[] {\n const emptyKey = DocumentKey.EMPTY;\n const startRef = new DocReference(emptyKey, id);\n const endRef = new DocReference(emptyKey, id + 1);\n const keys: DocumentKey[] = [];\n this.refsByTarget.forEachInRange([startRef, endRef], ref => {\n this.removeRef(ref);\n keys.push(ref.key);\n });\n return keys;\n }\n\n removeAllReferences(): void {\n this.refsByKey.forEach(ref => this.removeRef(ref));\n }\n\n private removeRef(ref: DocReference): void {\n this.refsByKey = this.refsByKey.delete(ref);\n this.refsByTarget = this.refsByTarget.delete(ref);\n }\n\n referencesForId(id: TargetId | BatchId): DocumentKeySet {\n const emptyKey = DocumentKey.EMPTY;\n const startRef = new DocReference(emptyKey, id);\n const endRef = new DocReference(emptyKey, id + 1);\n let keys = documentKeySet();\n this.refsByTarget.forEachInRange([startRef, endRef], ref => {\n keys = keys.add(ref.key);\n });\n return keys;\n }\n\n containsKey(key: DocumentKey): boolean {\n const ref = new DocReference(key, 0);\n const firstRef = this.refsByKey.firstAfterOrEqual(ref);\n return firstRef !== null && key.isEqual(firstRef.key);\n }\n}\n\nexport class DocReference {\n constructor(\n public key: DocumentKey,\n public targetOrBatchId: TargetId | BatchId\n ) {}\n\n /** Compare by key then by ID */\n static compareByKey(left: DocReference, right: DocReference): number {\n return (\n DocumentKey.comparator(left.key, right.key) ||\n primitiveComparator(left.targetOrBatchId, right.targetOrBatchId)\n );\n }\n\n /** Compare by ID then by key */\n static compareByTargetId(left: DocReference, right: DocReference): number {\n return (\n primitiveComparator(left.targetOrBatchId, right.targetOrBatchId) ||\n DocumentKey.comparator(left.key, right.key)\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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 */\nimport { fail } from './assert';\nimport { Code, FirestoreError } from './error';\nimport { Dict, forEach } from './obj';\n\n/** Types accepted by validateType() and related methods for validation. */\nexport type ValidationType =\n | 'undefined'\n | 'object'\n | 'function'\n | 'boolean'\n | 'number'\n | 'string'\n | 'non-empty string';\n\n/**\n * Validates that no arguments were passed in the invocation of functionName.\n *\n * Forward the magic \"arguments\" variable as second parameter on which the\n * parameter validation is performed:\n * validateNoArgs('myFunction', arguments);\n */\nexport function validateNoArgs(functionName: string, args: IArguments): void {\n if (args.length !== 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() does not support arguments, ` +\n 'but was called with ' +\n formatPlural(args.length, 'argument') +\n '.'\n );\n }\n}\n\n/**\n * Validates the invocation of functionName has the exact number of arguments.\n *\n * Forward the magic \"arguments\" variable as second parameter on which the\n * parameter validation is performed:\n * validateExactNumberOfArgs('myFunction', arguments, 2);\n */\nexport function validateExactNumberOfArgs(\n functionName: string,\n args: IArguments,\n numberOfArgs: number\n): void {\n if (args.length !== numberOfArgs) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires ` +\n formatPlural(numberOfArgs, 'argument') +\n ', but was called with ' +\n formatPlural(args.length, 'argument') +\n '.'\n );\n }\n}\n\n/**\n * Validates the invocation of functionName has at least the provided number of\n * arguments (but can have many more).\n *\n * Forward the magic \"arguments\" variable as second parameter on which the\n * parameter validation is performed:\n * validateAtLeastNumberOfArgs('myFunction', arguments, 2);\n */\nexport function validateAtLeastNumberOfArgs(\n functionName: string,\n args: IArguments,\n minNumberOfArgs: number\n): void {\n if (args.length < minNumberOfArgs) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires at least ` +\n formatPlural(minNumberOfArgs, 'argument') +\n ', but was called with ' +\n formatPlural(args.length, 'argument') +\n '.'\n );\n }\n}\n\n/**\n * Validates the invocation of functionName has number of arguments between\n * the values provided.\n *\n * Forward the magic \"arguments\" variable as second parameter on which the\n * parameter validation is performed:\n * validateBetweenNumberOfArgs('myFunction', arguments, 2, 3);\n */\nexport function validateBetweenNumberOfArgs(\n functionName: string,\n args: IArguments,\n minNumberOfArgs: number,\n maxNumberOfArgs: number\n): void {\n if (args.length < minNumberOfArgs || args.length > maxNumberOfArgs) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires between ${minNumberOfArgs} and ` +\n `${maxNumberOfArgs} arguments, but was called with ` +\n formatPlural(args.length, 'argument') +\n '.'\n );\n }\n}\n\n/**\n * Validates the provided argument is an array and has as least the expected\n * number of elements.\n */\nexport function validateNamedArrayAtLeastNumberOfElements<T>(\n functionName: string,\n value: T[],\n name: string,\n minNumberOfElements: number\n): void {\n if (!(value instanceof Array) || value.length < minNumberOfElements) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires its ${name} argument to be an ` +\n 'array with at least ' +\n `${formatPlural(minNumberOfElements, 'element')}.`\n );\n }\n}\n\n/**\n * Validates the provided positional argument has the native JavaScript type\n * using typeof checks.\n */\nexport function validateArgType(\n functionName: string,\n type: ValidationType,\n position: number,\n argument: unknown\n): void {\n validateType(functionName, type, `${ordinal(position)} argument`, argument);\n}\n\n/**\n * Validates the provided argument has the native JavaScript type using\n * typeof checks or is undefined.\n */\nexport function validateOptionalArgType(\n functionName: string,\n type: ValidationType,\n position: number,\n argument: unknown\n): void {\n if (argument !== undefined) {\n validateArgType(functionName, type, position, argument);\n }\n}\n\n/**\n * Validates the provided named option has the native JavaScript type using\n * typeof checks.\n */\nexport function validateNamedType(\n functionName: string,\n type: ValidationType,\n optionName: string,\n argument: unknown\n): void {\n validateType(functionName, type, `${optionName} option`, argument);\n}\n\n/**\n * Validates the provided named option has the native JavaScript type using\n * typeof checks or is undefined.\n */\nexport function validateNamedOptionalType(\n functionName: string,\n type: ValidationType,\n optionName: string,\n argument: unknown\n): void {\n if (argument !== undefined) {\n validateNamedType(functionName, type, optionName, argument);\n }\n}\n\nexport function validateArrayElements<T>(\n functionName: string,\n optionName: string,\n typeDescription: string,\n argument: T[],\n validator: (arg0: T) => boolean\n): void {\n if (!(argument instanceof Array)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires its ${optionName} ` +\n `option to be an array, but it was: ${valueDescription(argument)}`\n );\n }\n\n for (let i = 0; i < argument.length; ++i) {\n if (!validator(argument[i])) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires all ${optionName} ` +\n `elements to be ${typeDescription}, but the value at index ${i} ` +\n `was: ${valueDescription(argument[i])}`\n );\n }\n }\n}\n\nexport function validateOptionalArrayElements<T>(\n functionName: string,\n optionName: string,\n typeDescription: string,\n argument: T[] | undefined,\n validator: (arg0: T) => boolean\n): void {\n if (argument !== undefined) {\n validateArrayElements(\n functionName,\n optionName,\n typeDescription,\n argument,\n validator\n );\n }\n}\n\n/**\n * Validates that the provided named option equals one of the expected values.\n */\nexport function validateNamedPropertyEquals<T>(\n functionName: string,\n inputName: string,\n optionName: string,\n input: T,\n expected: T[]\n): void {\n const expectedDescription: string[] = [];\n\n for (const val of expected) {\n if (val === input) {\n return;\n }\n expectedDescription.push(valueDescription(val));\n }\n\n const actualDescription = valueDescription(input);\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid value ${actualDescription} provided to function ${functionName}() for option ` +\n `\"${optionName}\". Acceptable values: ${expectedDescription.join(', ')}`\n );\n}\n\n/**\n * Validates that the provided named option equals one of the expected values or\n * is undefined.\n */\nexport function validateNamedOptionalPropertyEquals<T>(\n functionName: string,\n inputName: string,\n optionName: string,\n input: T,\n expected: T[]\n): void {\n if (input !== undefined) {\n validateNamedPropertyEquals(\n functionName,\n inputName,\n optionName,\n input,\n expected\n );\n }\n}\n\n/**\n * Validates that the provided argument is a valid enum.\n *\n * @param functionName Function making the validation call.\n * @param enums Array containing all possible values for the enum.\n * @param position Position of the argument in `functionName`.\n * @param argument Argument to validate.\n * @return The value as T if the argument can be converted.\n */\nexport function validateStringEnum<T>(\n functionName: string,\n enums: T[],\n position: number,\n argument: unknown\n): T {\n if (!enums.some(element => element === argument)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid value ${valueDescription(argument)} provided to function ` +\n `${functionName}() for its ${ordinal(position)} argument. Acceptable ` +\n `values: ${enums.join(', ')}`\n );\n }\n return argument as T;\n}\n\n/** Helper to validate the type of a provided input. */\nfunction validateType(\n functionName: string,\n type: ValidationType,\n inputName: string,\n input: unknown\n): void {\n let valid = false;\n if (type === 'object') {\n valid = isPlainObject(input);\n } else if (type === 'non-empty string') {\n valid = typeof input === 'string' && input !== '';\n } else {\n valid = typeof input === type;\n }\n\n if (!valid) {\n const description = valueDescription(input);\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires its ${inputName} ` +\n `to be of type ${type}, but it was: ${description}`\n );\n }\n}\n\n/**\n * Returns true if it's a non-null object without a custom prototype\n * (i.e. excludes Array, Date, etc.).\n */\nexport function isPlainObject(input: unknown): boolean {\n return (\n typeof input === 'object' &&\n input !== null &&\n (Object.getPrototypeOf(input) === Object.prototype ||\n Object.getPrototypeOf(input) === null)\n );\n}\n\n/** Returns a string describing the type / value of the provided input. */\nexport function valueDescription(input: unknown): string {\n if (input === undefined) {\n return 'undefined';\n } else if (input === null) {\n return 'null';\n } else if (typeof input === 'string') {\n if (input.length > 20) {\n input = `${input.substring(0, 20)}...`;\n }\n return JSON.stringify(input);\n } else if (typeof input === 'number' || typeof input === 'boolean') {\n return '' + input;\n } else if (typeof input === 'object') {\n if (input instanceof Array) {\n return 'an array';\n } else {\n const customObjectName = tryGetCustomObjectType(input!);\n if (customObjectName) {\n return `a custom ${customObjectName} object`;\n } else {\n return 'an object';\n }\n }\n } else if (typeof input === 'function') {\n return 'a function';\n } else {\n return fail('Unknown wrong type: ' + typeof input);\n }\n}\n\n/** Hacky method to try to get the constructor name for an object. */\nexport function tryGetCustomObjectType(input: object): string | null {\n if (input.constructor) {\n const funcNameRegex = /function\\s+([^\\s(]+)\\s*\\(/;\n const results = funcNameRegex.exec(input.constructor.toString());\n if (results && results.length > 1) {\n return results[1];\n }\n }\n return null;\n}\n\n/** Validates the provided argument is defined. */\nexport function validateDefined(\n functionName: string,\n position: number,\n argument: unknown\n): void {\n if (argument === undefined) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires a valid ${ordinal(position)} ` +\n `argument, but it was undefined.`\n );\n }\n}\n\n/**\n * Validates the provided positional argument is an object, and its keys and\n * values match the expected keys and types provided in optionTypes.\n */\nexport function validateOptionNames(\n functionName: string,\n options: object,\n optionNames: string[]\n): void {\n forEach(options as Dict<unknown>, (key, _) => {\n if (optionNames.indexOf(key) < 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Unknown option '${key}' passed to function ${functionName}(). ` +\n 'Available options: ' +\n optionNames.join(', ')\n );\n }\n });\n}\n\n/**\n * Helper method to throw an error that the provided argument did not pass\n * an instanceof check.\n */\nexport function invalidClassError(\n functionName: string,\n type: string,\n position: number,\n argument: unknown\n): Error {\n const description = valueDescription(argument);\n return new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires its ${ordinal(position)} ` +\n `argument to be a ${type}, but it was: ${description}`\n );\n}\n\nexport function validatePositiveNumber(\n functionName: string,\n position: number,\n n: number\n): void {\n if (n <= 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${functionName}() requires its ${ordinal(\n position\n )} argument to be a positive number, but it was: ${n}.`\n );\n }\n}\n\n/** Converts a number to its english word representation */\nfunction ordinal(num: number): string {\n switch (num) {\n case 1:\n return 'first';\n case 2:\n return 'second';\n case 3:\n return 'third';\n default:\n return num + 'th';\n }\n}\n\n/**\n * Formats the given word as plural conditionally given the preceding number.\n */\nfunction formatPlural(num: number, str: string): string {\n return `${num} ${str}` + (num === 1 ? '' : 's');\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { PlatformSupport } from '../platform/platform';\nimport { Code, FirestoreError } from '../util/error';\nimport {\n invalidClassError,\n validateArgType,\n validateExactNumberOfArgs\n} from '../util/input_validation';\nimport { ByteString } from '../util/byte_string';\n\n/** Helper function to assert Uint8Array is available at runtime. */\nfunction assertUint8ArrayAvailable(): void {\n if (typeof Uint8Array === 'undefined') {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'Uint8Arrays are not available in this environment.'\n );\n }\n}\n\n/** Helper function to assert Base64 functions are available at runtime. */\nfunction assertBase64Available(): void {\n if (!PlatformSupport.getPlatform().base64Available) {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'Blobs are unavailable in Firestore in this environment.'\n );\n }\n}\n\n/**\n * Immutable class holding a blob (binary data).\n * This class is directly exposed in the public API.\n *\n * Note that while you can't hide the constructor in JavaScript code, we are\n * using the hack above to make sure no-one outside this module can call it.\n */\nexport class Blob {\n // Prefix with underscore to signal that we consider this not part of the\n // public API and to prevent it from showing up for autocompletion.\n _byteString: ByteString;\n\n constructor(byteString: ByteString) {\n assertBase64Available();\n this._byteString = byteString;\n }\n\n static fromBase64String(base64: string): Blob {\n validateExactNumberOfArgs('Blob.fromBase64String', arguments, 1);\n validateArgType('Blob.fromBase64String', 'string', 1, base64);\n assertBase64Available();\n try {\n return new Blob(ByteString.fromBase64String(base64));\n } catch (e) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Failed to construct Blob from Base64 string: ' + e\n );\n }\n }\n\n static fromUint8Array(array: Uint8Array): Blob {\n validateExactNumberOfArgs('Blob.fromUint8Array', arguments, 1);\n assertUint8ArrayAvailable();\n if (!(array instanceof Uint8Array)) {\n throw invalidClassError('Blob.fromUint8Array', 'Uint8Array', 1, array);\n }\n return new Blob(ByteString.fromUint8Array(array));\n }\n\n toBase64(): string {\n validateExactNumberOfArgs('Blob.toBase64', arguments, 0);\n assertBase64Available();\n return this._byteString.toBase64();\n }\n\n toUint8Array(): Uint8Array {\n validateExactNumberOfArgs('Blob.toUint8Array', arguments, 0);\n assertUint8ArrayAvailable();\n return this._byteString.toUint8Array();\n }\n\n toString(): string {\n return 'Blob(base64: ' + this.toBase64() + ')';\n }\n\n isEqual(other: Blob): boolean {\n return this._byteString.isEqual(other._byteString);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\n\nimport { FieldPath as InternalFieldPath } from '../model/path';\nimport { Code, FirestoreError } from '../util/error';\nimport {\n invalidClassError,\n validateArgType,\n validateNamedArrayAtLeastNumberOfElements\n} from '../util/input_validation';\n\n// The objects that are a part of this API are exposed to third-parties as\n// compiled javascript so we want to flag our private members with a leading\n// underscore to discourage their use.\n\n/**\n * A FieldPath refers to a field in a document. The path may consist of a single\n * field name (referring to a top-level field in the document), or a list of\n * field names (referring to a nested field in the document).\n */\nexport class FieldPath implements firestore.FieldPath {\n /** Internal representation of a Firestore field path. */\n _internalPath: InternalFieldPath;\n\n /**\n * Creates a FieldPath from the provided field names. If more than one field\n * name is provided, the path will point to a nested field in a document.\n *\n * @param fieldNames A list of field names.\n */\n constructor(...fieldNames: string[]) {\n validateNamedArrayAtLeastNumberOfElements(\n 'FieldPath',\n fieldNames,\n 'fieldNames',\n 1\n );\n\n for (let i = 0; i < fieldNames.length; ++i) {\n validateArgType('FieldPath', 'string', i, fieldNames[i]);\n if (fieldNames[i].length === 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid field name at argument $(i + 1). ` +\n 'Field names must not be empty.'\n );\n }\n }\n\n this._internalPath = new InternalFieldPath(fieldNames);\n }\n\n /**\n * Internal Note: The backend doesn't technically support querying by\n * document ID. Instead it queries by the entire document name (full path\n * included), but in the cases we currently support documentId(), the net\n * effect is the same.\n */\n private static readonly _DOCUMENT_ID = new FieldPath(\n InternalFieldPath.keyField().canonicalString()\n );\n\n static documentId(): FieldPath {\n return FieldPath._DOCUMENT_ID;\n }\n\n isEqual(other: firestore.FieldPath): boolean {\n if (!(other instanceof FieldPath)) {\n throw invalidClassError('isEqual', 'FieldPath', 1, other);\n }\n return this._internalPath.isEqual(other._internalPath);\n }\n}\n\n/**\n * Matches any characters in a field path string that are reserved.\n */\nconst RESERVED = new RegExp('[~\\\\*/\\\\[\\\\]]');\n\n/**\n * Parses a field path string into a FieldPath, treating dots as separators.\n */\nexport function fromDotSeparatedString(path: string): FieldPath {\n const found = path.search(RESERVED);\n if (found >= 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid field path (${path}). Paths must not contain ` +\n `'~', '*', '/', '[', or ']'`\n );\n }\n try {\n return new FieldPath(...path.split('.'));\n } catch (e) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid field path (${path}). Paths must not be empty, ` +\n `begin with '.', end with '.', or contain '..'`\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\nimport {\n validateArgType,\n validateAtLeastNumberOfArgs,\n validateExactNumberOfArgs,\n validateNoArgs\n} from '../util/input_validation';\nimport { FieldTransform } from '../model/mutation';\nimport {\n ArrayRemoveTransformOperation,\n ArrayUnionTransformOperation,\n NumericIncrementTransformOperation,\n ServerTimestampTransform\n} from '../model/transform_operation';\nimport { ParseContext, parseData, UserDataSource } from './user_data_reader';\nimport { debugAssert } from '../util/assert';\n\n/**\n * An opaque base class for FieldValue sentinel objects in our public API,\n * with public static methods for creating said sentinel objects.\n */\nexport abstract class FieldValueImpl {\n protected constructor(readonly _methodName: string) {}\n\n abstract toFieldTransform(context: ParseContext): FieldTransform | null;\n\n abstract isEqual(other: FieldValue): boolean;\n}\n\nexport class DeleteFieldValueImpl extends FieldValueImpl {\n constructor() {\n super('FieldValue.delete');\n }\n\n toFieldTransform(context: ParseContext): null {\n if (context.dataSource === UserDataSource.MergeSet) {\n // No transform to add for a delete, but we need to add it to our\n // fieldMask so it gets deleted.\n context.fieldMask.push(context.path!);\n } else if (context.dataSource === UserDataSource.Update) {\n debugAssert(\n context.path!.length > 0,\n 'FieldValue.delete() at the top level should have already' +\n ' been handled.'\n );\n throw context.createError(\n 'FieldValue.delete() can only appear at the top level ' +\n 'of your update data'\n );\n } else {\n // We shouldn't encounter delete sentinels for queries or non-merge set() calls.\n throw context.createError(\n 'FieldValue.delete() cannot be used with set() unless you pass ' +\n '{merge:true}'\n );\n }\n return null;\n }\n\n isEqual(other: FieldValue): boolean {\n return other instanceof DeleteFieldValueImpl;\n }\n}\n\nexport class ServerTimestampFieldValueImpl extends FieldValueImpl {\n constructor() {\n super('FieldValue.serverTimestamp');\n }\n\n toFieldTransform(context: ParseContext): FieldTransform {\n return new FieldTransform(context.path!, ServerTimestampTransform.instance);\n }\n\n isEqual(other: FieldValue): boolean {\n return other instanceof ServerTimestampFieldValueImpl;\n }\n}\n\nexport class ArrayUnionFieldValueImpl extends FieldValueImpl {\n constructor(private readonly _elements: unknown[]) {\n super('FieldValue.arrayUnion');\n }\n\n toFieldTransform(context: ParseContext): FieldTransform {\n // Although array transforms are used with writes, the actual elements\n // being uniomed or removed are not considered writes since they cannot\n // contain any FieldValue sentinels, etc.\n const parseContext = new ParseContext(\n {\n dataSource: UserDataSource.Argument,\n methodName: this._methodName,\n arrayElement: true\n },\n context.databaseId,\n context.serializer,\n context.ignoreUndefinedProperties\n );\n const parsedElements = this._elements.map(\n element => parseData(element, parseContext)!\n );\n const arrayUnion = new ArrayUnionTransformOperation(parsedElements);\n return new FieldTransform(context.path!, arrayUnion);\n }\n\n isEqual(other: FieldValue): boolean {\n // TODO(mrschmidt): Implement isEquals\n return this === other;\n }\n}\n\nexport class ArrayRemoveFieldValueImpl extends FieldValueImpl {\n constructor(readonly _elements: unknown[]) {\n super('FieldValue.arrayRemove');\n }\n\n toFieldTransform(context: ParseContext): FieldTransform {\n // Although array transforms are used with writes, the actual elements\n // being unioned or removed are not considered writes since they cannot\n // contain any FieldValue sentinels, etc.\n const parseContext = new ParseContext(\n {\n dataSource: UserDataSource.Argument,\n methodName: this._methodName,\n arrayElement: true\n },\n context.databaseId,\n context.serializer,\n context.ignoreUndefinedProperties\n );\n const parsedElements = this._elements.map(\n element => parseData(element, parseContext)!\n );\n const arrayUnion = new ArrayRemoveTransformOperation(parsedElements);\n return new FieldTransform(context.path!, arrayUnion);\n }\n\n isEqual(other: FieldValue): boolean {\n // TODO(mrschmidt): Implement isEquals\n return this === other;\n }\n}\n\nexport class NumericIncrementFieldValueImpl extends FieldValueImpl {\n constructor(private readonly _operand: number) {\n super('FieldValue.increment');\n }\n\n toFieldTransform(context: ParseContext): FieldTransform {\n const parseContext = new ParseContext(\n {\n dataSource: UserDataSource.Argument,\n methodName: this._methodName\n },\n context.databaseId,\n context.serializer,\n context.ignoreUndefinedProperties\n );\n const operand = parseData(this._operand, parseContext)!;\n const numericIncrement = new NumericIncrementTransformOperation(\n context.serializer,\n operand\n );\n return new FieldTransform(context.path!, numericIncrement);\n }\n\n isEqual(other: FieldValue): boolean {\n // TODO(mrschmidt): Implement isEquals\n return this === other;\n }\n}\n\nexport class FieldValue implements firestore.FieldValue {\n static delete(): FieldValueImpl {\n validateNoArgs('FieldValue.delete', arguments);\n return new DeleteFieldValueImpl();\n }\n\n static serverTimestamp(): FieldValueImpl {\n validateNoArgs('FieldValue.serverTimestamp', arguments);\n return new ServerTimestampFieldValueImpl();\n }\n\n static arrayUnion(...elements: unknown[]): FieldValueImpl {\n validateAtLeastNumberOfArgs('FieldValue.arrayUnion', arguments, 1);\n // NOTE: We don't actually parse the data until it's used in set() or\n // update() since we need access to the Firestore instance.\n return new ArrayUnionFieldValueImpl(elements);\n }\n\n static arrayRemove(...elements: unknown[]): FieldValueImpl {\n validateAtLeastNumberOfArgs('FieldValue.arrayRemove', arguments, 1);\n // NOTE: We don't actually parse the data until it's used in set() or\n // update() since we need access to the Firestore instance.\n return new ArrayRemoveFieldValueImpl(elements);\n }\n\n static increment(n: number): FieldValueImpl {\n validateArgType('FieldValue.increment', 'number', 1, n);\n validateExactNumberOfArgs('FieldValue.increment', arguments, 1);\n return new NumericIncrementFieldValueImpl(n);\n }\n\n isEqual(other: FieldValue): boolean {\n return this === other;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Code, FirestoreError } from '../util/error';\nimport {\n validateArgType,\n validateExactNumberOfArgs\n} from '../util/input_validation';\nimport { primitiveComparator } from '../util/misc';\n\n/**\n * Immutable class representing a geo point as latitude-longitude pair.\n * This class is directly exposed in the public API, including its constructor.\n */\nexport class GeoPoint {\n // Prefix with underscore to signal this is a private variable in JS and\n // prevent it showing up for autocompletion when typing latitude or longitude.\n private _lat: number;\n private _long: number;\n\n constructor(latitude: number, longitude: number) {\n validateExactNumberOfArgs('GeoPoint', arguments, 2);\n validateArgType('GeoPoint', 'number', 1, latitude);\n validateArgType('GeoPoint', 'number', 2, longitude);\n if (!isFinite(latitude) || latitude < -90 || latitude > 90) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Latitude must be a number between -90 and 90, but was: ' + latitude\n );\n }\n if (!isFinite(longitude) || longitude < -180 || longitude > 180) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Longitude must be a number between -180 and 180, but was: ' + longitude\n );\n }\n\n this._lat = latitude;\n this._long = longitude;\n }\n\n /**\n * Returns the latitude of this geo point, a number between -90 and 90.\n */\n get latitude(): number {\n return this._lat;\n }\n\n /**\n * Returns the longitude of this geo point, a number between -180 and 180.\n */\n get longitude(): number {\n return this._long;\n }\n\n isEqual(other: GeoPoint): boolean {\n return this._lat === other._lat && this._long === other._long;\n }\n\n /**\n * Actually private to JS consumers of our API, so this function is prefixed\n * with an underscore.\n */\n _compareTo(other: GeoPoint): number {\n return (\n primitiveComparator(this._lat, other._lat) ||\n primitiveComparator(this._long, other._long)\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\n\nimport * as api from '../protos/firestore_proto_api';\n\nimport { Timestamp } from './timestamp';\nimport { DatabaseId } from '../core/database_info';\nimport { DocumentKey } from '../model/document_key';\nimport {\n FieldMask,\n FieldTransform,\n Mutation,\n PatchMutation,\n Precondition,\n SetMutation,\n TransformMutation\n} from '../model/mutation';\nimport { FieldPath } from '../model/path';\nimport { debugAssert, fail } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { isPlainObject, valueDescription } from '../util/input_validation';\nimport { Dict, forEach, isEmpty } from '../util/obj';\nimport { ObjectValue, ObjectValueBuilder } from '../model/object_value';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport { Blob } from './blob';\nimport {\n FieldPath as ExternalFieldPath,\n fromDotSeparatedString\n} from './field_path';\nimport { DeleteFieldValueImpl, FieldValueImpl } from './field_value';\nimport { GeoPoint } from './geo_point';\nimport { PlatformSupport } from '../platform/platform';\nimport { DocumentReference } from './database';\n\nconst RESERVED_FIELD_REGEX = /^__.*__$/;\n\n/** The result of parsing document data (e.g. for a setData call). */\nexport class ParsedSetData {\n constructor(\n readonly data: ObjectValue,\n readonly fieldMask: FieldMask | null,\n readonly fieldTransforms: FieldTransform[]\n ) {}\n\n toMutations(key: DocumentKey, precondition: Precondition): Mutation[] {\n const mutations = [] as Mutation[];\n if (this.fieldMask !== null) {\n mutations.push(\n new PatchMutation(key, this.data, this.fieldMask, precondition)\n );\n } else {\n mutations.push(new SetMutation(key, this.data, precondition));\n }\n if (this.fieldTransforms.length > 0) {\n mutations.push(new TransformMutation(key, this.fieldTransforms));\n }\n return mutations;\n }\n}\n\n/** The result of parsing \"update\" data (i.e. for an updateData call). */\nexport class ParsedUpdateData {\n constructor(\n readonly data: ObjectValue,\n readonly fieldMask: FieldMask,\n readonly fieldTransforms: FieldTransform[]\n ) {}\n\n toMutations(key: DocumentKey, precondition: Precondition): Mutation[] {\n const mutations = [\n new PatchMutation(key, this.data, this.fieldMask, precondition)\n ] as Mutation[];\n if (this.fieldTransforms.length > 0) {\n mutations.push(new TransformMutation(key, this.fieldTransforms));\n }\n return mutations;\n }\n}\n\n/*\n * Represents what type of API method provided the data being parsed; useful\n * for determining which error conditions apply during parsing and providing\n * better error messages.\n */\nexport const enum UserDataSource {\n Set,\n Update,\n MergeSet,\n /**\n * Indicates the source is a where clause, cursor bound, arrayUnion()\n * element, etc. Of note, isWrite(source) will return false.\n */\n Argument,\n /**\n * Indicates that the source is an Argument that may directly contain nested\n * arrays (e.g. the operand of an `in` query).\n */\n ArrayArgument\n}\n\nfunction isWrite(dataSource: UserDataSource): boolean {\n switch (dataSource) {\n case UserDataSource.Set: // fall through\n case UserDataSource.MergeSet: // fall through\n case UserDataSource.Update:\n return true;\n case UserDataSource.Argument:\n case UserDataSource.ArrayArgument:\n return false;\n default:\n throw fail(`Unexpected case for UserDataSource: ${dataSource}`);\n }\n}\n\n/** Contains the settings that are mutated as we parse user data. */\ninterface ContextSettings {\n /** Indicates what kind of API method this data came from. */\n readonly dataSource: UserDataSource;\n /** The name of the method the user called to create the ParseContext. */\n readonly methodName: string;\n /**\n * A path within the object being parsed. This could be an empty path (in\n * which case the context represents the root of the data being parsed), or a\n * nonempty path (indicating the context represents a nested location within\n * the data).\n */\n readonly path?: FieldPath;\n /**\n * Whether or not this context corresponds to an element of an array.\n * If not set, elements are treated as if they were outside of arrays.\n */\n readonly arrayElement?: boolean;\n}\n\n/** A \"context\" object passed around while parsing user data. */\nexport class ParseContext {\n readonly fieldTransforms: FieldTransform[];\n readonly fieldMask: FieldPath[];\n /**\n * Initializes a ParseContext with the given source and path.\n *\n * @param settings The settings for the parser.\n * @param databaseId The database ID of the Firestore instance.\n * @param serializer The serializer to use to generate the Value proto.\n * @param ignoreUndefinedProperties Whether to ignore undefined properties\n * rather than throw.\n * @param fieldTransforms A mutable list of field transforms encountered while\n * parsing the data.\n * @param fieldMask A mutable list of field paths encountered while parsing\n * the data.\n *\n * TODO(b/34871131): We don't support array paths right now, so path can be\n * null to indicate the context represents any location within an array (in\n * which case certain features will not work and errors will be somewhat\n * compromised).\n */\n constructor(\n readonly settings: ContextSettings,\n readonly databaseId: DatabaseId,\n readonly serializer: JsonProtoSerializer,\n readonly ignoreUndefinedProperties: boolean,\n fieldTransforms?: FieldTransform[],\n fieldMask?: FieldPath[]\n ) {\n // Minor hack: If fieldTransforms is undefined, we assume this is an\n // external call and we need to validate the entire path.\n if (fieldTransforms === undefined) {\n this.validatePath();\n }\n this.fieldTransforms = fieldTransforms || [];\n this.fieldMask = fieldMask || [];\n }\n\n get path(): FieldPath | undefined {\n return this.settings.path;\n }\n\n get dataSource(): UserDataSource {\n return this.settings.dataSource;\n }\n\n /** Returns a new context with the specified settings overwritten. */\n contextWith(configuration: Partial<ContextSettings>): ParseContext {\n return new ParseContext(\n { ...this.settings, ...configuration },\n this.databaseId,\n this.serializer,\n this.ignoreUndefinedProperties,\n this.fieldTransforms,\n this.fieldMask\n );\n }\n\n childContextForField(field: string): ParseContext {\n const childPath = this.path?.child(field);\n const context = this.contextWith({ path: childPath, arrayElement: false });\n context.validatePathSegment(field);\n return context;\n }\n\n childContextForFieldPath(field: FieldPath): ParseContext {\n const childPath = this.path?.child(field);\n const context = this.contextWith({ path: childPath, arrayElement: false });\n context.validatePath();\n return context;\n }\n\n childContextForArray(index: number): ParseContext {\n // TODO(b/34871131): We don't support array paths right now; so make path\n // undefined.\n return this.contextWith({ path: undefined, arrayElement: true });\n }\n\n createError(reason: string): Error {\n const fieldDescription =\n !this.path || this.path.isEmpty()\n ? ''\n : ` (found in field ${this.path.toString()})`;\n return new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${this.settings.methodName}() called with invalid data. ` +\n reason +\n fieldDescription\n );\n }\n\n /** Returns 'true' if 'fieldPath' was traversed when creating this context. */\n contains(fieldPath: FieldPath): boolean {\n return (\n this.fieldMask.find(field => fieldPath.isPrefixOf(field)) !== undefined ||\n this.fieldTransforms.find(transform =>\n fieldPath.isPrefixOf(transform.field)\n ) !== undefined\n );\n }\n\n private validatePath(): void {\n // TODO(b/34871131): Remove null check once we have proper paths for fields\n // within arrays.\n if (!this.path) {\n return;\n }\n for (let i = 0; i < this.path.length; i++) {\n this.validatePathSegment(this.path.get(i));\n }\n }\n\n private validatePathSegment(segment: string): void {\n if (segment.length === 0) {\n throw this.createError('Document fields must not be empty');\n }\n if (isWrite(this.dataSource) && RESERVED_FIELD_REGEX.test(segment)) {\n throw this.createError('Document fields cannot begin and end with \"__\"');\n }\n }\n}\n\n/**\n * Helper for parsing raw user input (provided via the API) into internal model\n * classes.\n */\nexport class UserDataReader {\n private readonly serializer: JsonProtoSerializer;\n\n constructor(\n private readonly databaseId: DatabaseId,\n private readonly ignoreUndefinedProperties: boolean,\n serializer?: JsonProtoSerializer\n ) {\n this.serializer =\n serializer || PlatformSupport.getPlatform().newSerializer(databaseId);\n }\n\n /** Parse document data from a non-merge set() call. */\n parseSetData(methodName: string, input: unknown): ParsedSetData {\n const context = this.createContext(UserDataSource.Set, methodName);\n validatePlainObject('Data must be an object, but it was:', context, input);\n const updateData = parseObject(input, context)!;\n\n return new ParsedSetData(\n new ObjectValue(updateData),\n /* fieldMask= */ null,\n context.fieldTransforms\n );\n }\n\n /** Parse document data from a set() call with '{merge:true}'. */\n parseMergeData(\n methodName: string,\n input: unknown,\n fieldPaths?: Array<string | firestore.FieldPath>\n ): ParsedSetData {\n const context = this.createContext(UserDataSource.MergeSet, methodName);\n validatePlainObject('Data must be an object, but it was:', context, input);\n const updateData = parseObject(input, context);\n\n let fieldMask: FieldMask;\n let fieldTransforms: FieldTransform[];\n\n if (!fieldPaths) {\n fieldMask = new FieldMask(context.fieldMask);\n fieldTransforms = context.fieldTransforms;\n } else {\n const validatedFieldPaths: FieldPath[] = [];\n\n for (const stringOrFieldPath of fieldPaths) {\n let fieldPath: FieldPath;\n\n if (stringOrFieldPath instanceof ExternalFieldPath) {\n fieldPath = stringOrFieldPath._internalPath;\n } else if (typeof stringOrFieldPath === 'string') {\n fieldPath = fieldPathFromDotSeparatedString(\n methodName,\n stringOrFieldPath\n );\n } else {\n throw fail(\n 'Expected stringOrFieldPath to be a string or a FieldPath'\n );\n }\n\n if (!context.contains(fieldPath)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Field '${fieldPath}' is specified in your field mask but missing from your input data.`\n );\n }\n\n if (!fieldMaskContains(validatedFieldPaths, fieldPath)) {\n validatedFieldPaths.push(fieldPath);\n }\n }\n\n fieldMask = new FieldMask(validatedFieldPaths);\n fieldTransforms = context.fieldTransforms.filter(transform =>\n fieldMask.covers(transform.field)\n );\n }\n return new ParsedSetData(\n new ObjectValue(updateData),\n fieldMask,\n fieldTransforms\n );\n }\n\n /** Parse update data from an update() call. */\n parseUpdateData(methodName: string, input: unknown): ParsedUpdateData {\n const context = this.createContext(UserDataSource.Update, methodName);\n validatePlainObject('Data must be an object, but it was:', context, input);\n\n const fieldMaskPaths: FieldPath[] = [];\n const updateData = new ObjectValueBuilder();\n forEach(input as Dict<unknown>, (key, value) => {\n const path = fieldPathFromDotSeparatedString(methodName, key);\n\n const childContext = context.childContextForFieldPath(path);\n if (value instanceof DeleteFieldValueImpl) {\n // Add it to the field mask, but don't add anything to updateData.\n fieldMaskPaths.push(path);\n } else {\n const parsedValue = parseData(value, childContext);\n if (parsedValue != null) {\n fieldMaskPaths.push(path);\n updateData.set(path, parsedValue);\n }\n }\n });\n\n const mask = new FieldMask(fieldMaskPaths);\n return new ParsedUpdateData(\n updateData.build(),\n mask,\n context.fieldTransforms\n );\n }\n\n /** Parse update data from a list of field/value arguments. */\n parseUpdateVarargs(\n methodName: string,\n field: string | ExternalFieldPath,\n value: unknown,\n moreFieldsAndValues: unknown[]\n ): ParsedUpdateData {\n const context = this.createContext(UserDataSource.Update, methodName);\n const keys = [fieldPathFromArgument(methodName, field)];\n const values = [value];\n\n if (moreFieldsAndValues.length % 2 !== 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${methodName}() needs to be called with an even number ` +\n 'of arguments that alternate between field names and values.'\n );\n }\n\n for (let i = 0; i < moreFieldsAndValues.length; i += 2) {\n keys.push(\n fieldPathFromArgument(\n methodName,\n moreFieldsAndValues[i] as string | ExternalFieldPath\n )\n );\n values.push(moreFieldsAndValues[i + 1]);\n }\n\n const fieldMaskPaths: FieldPath[] = [];\n const updateData = new ObjectValueBuilder();\n\n // We iterate in reverse order to pick the last value for a field if the\n // user specified the field multiple times.\n for (let i = keys.length - 1; i >= 0; --i) {\n if (!fieldMaskContains(fieldMaskPaths, keys[i])) {\n const path = keys[i];\n const value = values[i];\n const childContext = context.childContextForFieldPath(path);\n if (value instanceof DeleteFieldValueImpl) {\n // Add it to the field mask, but don't add anything to updateData.\n fieldMaskPaths.push(path);\n } else {\n const parsedValue = parseData(value, childContext);\n if (parsedValue != null) {\n fieldMaskPaths.push(path);\n updateData.set(path, parsedValue);\n }\n }\n }\n }\n\n const mask = new FieldMask(fieldMaskPaths);\n return new ParsedUpdateData(\n updateData.build(),\n mask,\n context.fieldTransforms\n );\n }\n\n /** Creates a new top-level parse context. */\n private createContext(\n dataSource: UserDataSource,\n methodName: string\n ): ParseContext {\n return new ParseContext(\n {\n dataSource,\n methodName,\n path: FieldPath.EMPTY_PATH,\n arrayElement: false\n },\n this.databaseId,\n this.serializer,\n this.ignoreUndefinedProperties\n );\n }\n\n /**\n * Parse a \"query value\" (e.g. value in a where filter or a value in a cursor\n * bound).\n *\n * @param allowArrays Whether the query value is an array that may directly\n * contain additional arrays (e.g. the operand of an `in` query).\n */\n parseQueryValue(\n methodName: string,\n input: unknown,\n allowArrays = false\n ): api.Value {\n const context = this.createContext(\n allowArrays ? UserDataSource.ArrayArgument : UserDataSource.Argument,\n methodName\n );\n const parsed = parseData(input, context);\n debugAssert(parsed != null, 'Parsed data should not be null.');\n debugAssert(\n context.fieldTransforms.length === 0,\n 'Field transforms should have been disallowed.'\n );\n return parsed;\n }\n}\n\n/**\n * Parses user data to Protobuf Values.\n *\n * @param input Data to be parsed.\n * @param context A context object representing the current path being parsed,\n * the source of the data being parsed, etc.\n * @return The parsed value, or null if the value was a FieldValue sentinel\n * that should not be included in the resulting parsed data.\n */\nexport function parseData(\n input: unknown,\n context: ParseContext\n): api.Value | null {\n if (looksLikeJsonObject(input)) {\n validatePlainObject('Unsupported field value:', context, input);\n return parseObject(input, context);\n } else if (input instanceof FieldValueImpl) {\n // FieldValues usually parse into transforms (except FieldValue.delete())\n // in which case we do not want to include this field in our parsed data\n // (as doing so will overwrite the field directly prior to the transform\n // trying to transform it). So we don't add this location to\n // context.fieldMask and we return null as our parsing result.\n parseSentinelFieldValue(input, context);\n return null;\n } else {\n // If context.path is null we are inside an array and we don't support\n // field mask paths more granular than the top-level array.\n if (context.path) {\n context.fieldMask.push(context.path);\n }\n\n if (input instanceof Array) {\n // TODO(b/34871131): Include the path containing the array in the error\n // message.\n // In the case of IN queries, the parsed data is an array (representing\n // the set of values to be included for the IN query) that may directly\n // contain additional arrays (each representing an individual field\n // value), so we disable this validation.\n if (\n context.settings.arrayElement &&\n context.dataSource !== UserDataSource.ArrayArgument\n ) {\n throw context.createError('Nested arrays are not supported');\n }\n return parseArray(input as unknown[], context);\n } else {\n return parseScalarValue(input, context);\n }\n }\n}\n\nfunction parseObject(\n obj: Dict<unknown>,\n context: ParseContext\n): { mapValue: api.MapValue } {\n const fields: Dict<api.Value> = {};\n\n if (isEmpty(obj)) {\n // If we encounter an empty object, we explicitly add it to the update\n // mask to ensure that the server creates a map entry.\n if (context.path && context.path.length > 0) {\n context.fieldMask.push(context.path);\n }\n } else {\n forEach(obj, (key: string, val: unknown) => {\n const parsedValue = parseData(val, context.childContextForField(key));\n if (parsedValue != null) {\n fields[key] = parsedValue;\n }\n });\n }\n\n return { mapValue: { fields } };\n}\n\nfunction parseArray(array: unknown[], context: ParseContext): api.Value {\n const values: api.Value[] = [];\n let entryIndex = 0;\n for (const entry of array) {\n let parsedEntry = parseData(\n entry,\n context.childContextForArray(entryIndex)\n );\n if (parsedEntry == null) {\n // Just include nulls in the array for fields being replaced with a\n // sentinel.\n parsedEntry = { nullValue: 'NULL_VALUE' };\n }\n values.push(parsedEntry);\n entryIndex++;\n }\n return { arrayValue: { values } };\n}\n\n/**\n * \"Parses\" the provided FieldValueImpl, adding any necessary transforms to\n * context.fieldTransforms.\n */\nfunction parseSentinelFieldValue(\n value: FieldValueImpl,\n context: ParseContext\n): void {\n // Sentinels are only supported with writes, and not within arrays.\n if (!isWrite(context.dataSource)) {\n throw context.createError(\n `${value._methodName}() can only be used with update() and set()`\n );\n }\n if (context.path === null) {\n throw context.createError(\n `${value._methodName}() is not currently supported inside arrays`\n );\n }\n\n const fieldTransform = value.toFieldTransform(context);\n if (fieldTransform) {\n context.fieldTransforms.push(fieldTransform);\n }\n}\n\n/**\n * Helper to parse a scalar value (i.e. not an Object, Array, or FieldValue)\n *\n * @return The parsed value\n */\nfunction parseScalarValue(\n value: unknown,\n context: ParseContext\n): api.Value | null {\n if (value === null) {\n return { nullValue: 'NULL_VALUE' };\n } else if (typeof value === 'number') {\n return context.serializer.toNumber(value);\n } else if (typeof value === 'boolean') {\n return { booleanValue: value };\n } else if (typeof value === 'string') {\n return { stringValue: value };\n } else if (value instanceof Date) {\n const timestamp = Timestamp.fromDate(value);\n return { timestampValue: context.serializer.toTimestamp(timestamp) };\n } else if (value instanceof Timestamp) {\n // Firestore backend truncates precision down to microseconds. To ensure\n // offline mode works the same with regards to truncation, perform the\n // truncation immediately without waiting for the backend to do that.\n const timestamp = new Timestamp(\n value.seconds,\n Math.floor(value.nanoseconds / 1000) * 1000\n );\n return { timestampValue: context.serializer.toTimestamp(timestamp) };\n } else if (value instanceof GeoPoint) {\n return {\n geoPointValue: {\n latitude: value.latitude,\n longitude: value.longitude\n }\n };\n } else if (value instanceof Blob) {\n return { bytesValue: context.serializer.toBytes(value) };\n } else if (value instanceof DocumentReference) {\n const thisDb = context.databaseId;\n const otherDb = value.firestore._databaseId;\n if (!otherDb.isEqual(thisDb)) {\n throw context.createError(\n 'Document reference is for database ' +\n `${otherDb.projectId}/${otherDb.database} but should be ` +\n `for database ${thisDb.projectId}/${thisDb.database}`\n );\n }\n return {\n referenceValue: context.serializer.toResourceName(\n value._key.path,\n value.firestore._databaseId\n )\n };\n } else if (value === undefined && context.ignoreUndefinedProperties) {\n return null;\n } else {\n throw context.createError(\n `Unsupported field value: ${valueDescription(value)}`\n );\n }\n}\n\n/**\n * Checks whether an object looks like a JSON object that should be converted\n * into a struct. Normal class/prototype instances are considered to look like\n * JSON objects since they should be converted to a struct value. Arrays, Dates,\n * GeoPoints, etc. are not considered to look like JSON objects since they map\n * to specific FieldValue types other than ObjectValue.\n */\nfunction looksLikeJsonObject(input: unknown): boolean {\n return (\n typeof input === 'object' &&\n input !== null &&\n !(input instanceof Array) &&\n !(input instanceof Date) &&\n !(input instanceof Timestamp) &&\n !(input instanceof GeoPoint) &&\n !(input instanceof Blob) &&\n !(input instanceof DocumentReference) &&\n !(input instanceof FieldValueImpl)\n );\n}\n\nfunction validatePlainObject(\n message: string,\n context: ParseContext,\n input: unknown\n): asserts input is Dict<unknown> {\n if (!looksLikeJsonObject(input) || !isPlainObject(input)) {\n const description = valueDescription(input);\n if (description === 'an object') {\n // Massage the error if it was an object.\n throw context.createError(message + ' a custom object');\n } else {\n throw context.createError(message + ' ' + description);\n }\n }\n}\n\n/**\n * Helper that calls fromDotSeparatedString() but wraps any error thrown.\n */\nexport function fieldPathFromArgument(\n methodName: string,\n path: string | ExternalFieldPath\n): FieldPath {\n if (path instanceof ExternalFieldPath) {\n return path._internalPath;\n } else if (typeof path === 'string') {\n return fieldPathFromDotSeparatedString(methodName, path);\n } else {\n const message = 'Field path arguments must be of type string or FieldPath.';\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${methodName}() called with invalid data. ${message}`\n );\n }\n}\n\n/**\n * Wraps fromDotSeparatedString with an error message about the method that\n * was thrown.\n * @param methodName The publicly visible method name\n * @param path The dot-separated string form of a field path which will be split\n * on dots.\n */\nfunction fieldPathFromDotSeparatedString(\n methodName: string,\n path: string\n): FieldPath {\n try {\n return fromDotSeparatedString(path)._internalPath;\n } catch (e) {\n const message = errorMessage(e);\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function ${methodName}() called with invalid data. ${message}`\n );\n }\n}\n\n/**\n * Extracts the message from a caught exception, which should be an Error object\n * though JS doesn't guarantee that.\n */\nfunction errorMessage(error: Error | object): string {\n return error instanceof Error ? error.message : error.toString();\n}\n\n/** Checks `haystack` if FieldPath `needle` is present. Runs in O(n). */\nfunction fieldMaskContains(haystack: FieldPath[], needle: FieldPath): boolean {\n return haystack.some(v => v.isEqual(needle));\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { QueryResult } from '../local/local_store';\nimport {\n documentKeySet,\n DocumentKeySet,\n MaybeDocumentMap\n} from '../model/collections';\nimport { Document, MaybeDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { DocumentSet } from '../model/document_set';\nimport { TargetChange } from '../remote/remote_event';\nimport { debugAssert, fail } from '../util/assert';\n\nimport { Query } from './query';\nimport { OnlineState } from './types';\nimport {\n ChangeType,\n DocumentChangeSet,\n SyncState,\n ViewSnapshot\n} from './view_snapshot';\n\nexport type LimboDocumentChange = AddedLimboDocument | RemovedLimboDocument;\nexport class AddedLimboDocument {\n constructor(public key: DocumentKey) {}\n}\nexport class RemovedLimboDocument {\n constructor(public key: DocumentKey) {}\n}\n\n/** The result of applying a set of doc changes to a view. */\nexport interface ViewDocumentChanges {\n /** The new set of docs that should be in the view. */\n documentSet: DocumentSet;\n /** The diff of these docs with the previous set of docs. */\n changeSet: DocumentChangeSet;\n /**\n * Whether the set of documents passed in was not sufficient to calculate the\n * new state of the view and there needs to be another pass based on the\n * local cache.\n */\n needsRefill: boolean;\n\n mutatedKeys: DocumentKeySet;\n}\n\nexport interface ViewChange {\n snapshot?: ViewSnapshot;\n limboChanges: LimboDocumentChange[];\n}\n\n/**\n * View is responsible for computing the final merged truth of what docs are in\n * a query. It gets notified of local and remote changes to docs, and applies\n * the query filters and limits to determine the most correct possible results.\n */\nexport class View {\n private syncState: SyncState | null = null;\n /**\n * A flag whether the view is current with the backend. A view is considered\n * current after it has seen the current flag from the backend and did not\n * lose consistency within the watch stream (e.g. because of an existence\n * filter mismatch).\n */\n private current = false;\n private documentSet: DocumentSet;\n /** Documents in the view but not in the remote target */\n private limboDocuments = documentKeySet();\n /** Document Keys that have local changes */\n private mutatedKeys = documentKeySet();\n\n constructor(\n private query: Query,\n /** Documents included in the remote target */\n private _syncedDocuments: DocumentKeySet\n ) {\n this.documentSet = new DocumentSet(query.docComparator.bind(query));\n }\n\n /**\n * The set of remote documents that the server has told us belongs to the target associated with\n * this view.\n */\n get syncedDocuments(): DocumentKeySet {\n return this._syncedDocuments;\n }\n\n /**\n * Iterates over a set of doc changes, applies the query limit, and computes\n * what the new results should be, what the changes were, and whether we may\n * need to go back to the local cache for more results. Does not make any\n * changes to the view.\n * @param docChanges The doc changes to apply to this view.\n * @param previousChanges If this is being called with a refill, then start\n * with this set of docs and changes instead of the current view.\n * @return a new set of docs, changes, and refill flag.\n */\n computeDocChanges(\n docChanges: MaybeDocumentMap,\n previousChanges?: ViewDocumentChanges\n ): ViewDocumentChanges {\n const changeSet = previousChanges\n ? previousChanges.changeSet\n : new DocumentChangeSet();\n const oldDocumentSet = previousChanges\n ? previousChanges.documentSet\n : this.documentSet;\n let newMutatedKeys = previousChanges\n ? previousChanges.mutatedKeys\n : this.mutatedKeys;\n let newDocumentSet = oldDocumentSet;\n let needsRefill = false;\n\n // Track the last doc in a (full) limit. This is necessary, because some\n // update (a delete, or an update moving a doc past the old limit) might\n // mean there is some other document in the local cache that either should\n // come (1) between the old last limit doc and the new last document, in the\n // case of updates, or (2) after the new last document, in the case of\n // deletes. So we keep this doc at the old limit to compare the updates to.\n //\n // Note that this should never get used in a refill (when previousChanges is\n // set), because there will only be adds -- no deletes or updates.\n const lastDocInLimit =\n this.query.hasLimitToFirst() && oldDocumentSet.size === this.query.limit\n ? oldDocumentSet.last()\n : null;\n const firstDocInLimit =\n this.query.hasLimitToLast() && oldDocumentSet.size === this.query.limit\n ? oldDocumentSet.first()\n : null;\n\n docChanges.inorderTraversal(\n (key: DocumentKey, newMaybeDoc: MaybeDocument) => {\n const oldDoc = oldDocumentSet.get(key);\n let newDoc = newMaybeDoc instanceof Document ? newMaybeDoc : null;\n if (newDoc) {\n debugAssert(\n key.isEqual(newDoc.key),\n 'Mismatching keys found in document changes: ' +\n key +\n ' != ' +\n newDoc.key\n );\n newDoc = this.query.matches(newDoc) ? newDoc : null;\n }\n\n const oldDocHadPendingMutations = oldDoc\n ? this.mutatedKeys.has(oldDoc.key)\n : false;\n const newDocHasPendingMutations = newDoc\n ? newDoc.hasLocalMutations ||\n // We only consider committed mutations for documents that were\n // mutated during the lifetime of the view.\n (this.mutatedKeys.has(newDoc.key) && newDoc.hasCommittedMutations)\n : false;\n\n let changeApplied = false;\n\n // Calculate change\n if (oldDoc && newDoc) {\n const docsEqual = oldDoc.data().isEqual(newDoc.data());\n if (!docsEqual) {\n if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {\n changeSet.track({\n type: ChangeType.Modified,\n doc: newDoc\n });\n changeApplied = true;\n\n if (\n (lastDocInLimit &&\n this.query.docComparator(newDoc, lastDocInLimit) > 0) ||\n (firstDocInLimit &&\n this.query.docComparator(newDoc, firstDocInLimit) < 0)\n ) {\n // This doc moved from inside the limit to outside the limit.\n // That means there may be some other doc in the local cache\n // that should be included instead.\n needsRefill = true;\n }\n }\n } else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {\n changeSet.track({ type: ChangeType.Metadata, doc: newDoc });\n changeApplied = true;\n }\n } else if (!oldDoc && newDoc) {\n changeSet.track({ type: ChangeType.Added, doc: newDoc });\n changeApplied = true;\n } else if (oldDoc && !newDoc) {\n changeSet.track({ type: ChangeType.Removed, doc: oldDoc });\n changeApplied = true;\n\n if (lastDocInLimit || firstDocInLimit) {\n // A doc was removed from a full limit query. We'll need to\n // requery from the local cache to see if we know about some other\n // doc that should be in the results.\n needsRefill = true;\n }\n }\n\n if (changeApplied) {\n if (newDoc) {\n newDocumentSet = newDocumentSet.add(newDoc);\n if (newDocHasPendingMutations) {\n newMutatedKeys = newMutatedKeys.add(key);\n } else {\n newMutatedKeys = newMutatedKeys.delete(key);\n }\n } else {\n newDocumentSet = newDocumentSet.delete(key);\n newMutatedKeys = newMutatedKeys.delete(key);\n }\n }\n }\n );\n\n // Drop documents out to meet limit/limitToLast requirement.\n if (this.query.hasLimitToFirst() || this.query.hasLimitToLast()) {\n while (newDocumentSet.size > this.query.limit!) {\n const oldDoc = this.query.hasLimitToFirst()\n ? newDocumentSet.last()\n : newDocumentSet.first();\n newDocumentSet = newDocumentSet.delete(oldDoc!.key);\n newMutatedKeys = newMutatedKeys.delete(oldDoc!.key);\n changeSet.track({ type: ChangeType.Removed, doc: oldDoc! });\n }\n }\n\n debugAssert(\n !needsRefill || !previousChanges,\n 'View was refilled using docs that themselves needed refilling.'\n );\n return {\n documentSet: newDocumentSet,\n changeSet,\n needsRefill,\n mutatedKeys: newMutatedKeys\n };\n }\n\n private shouldWaitForSyncedDocument(\n oldDoc: Document,\n newDoc: Document\n ): boolean {\n // We suppress the initial change event for documents that were modified as\n // part of a write acknowledgment (e.g. when the value of a server transform\n // is applied) as Watch will send us the same document again.\n // By suppressing the event, we only raise two user visible events (one with\n // `hasPendingWrites` and the final state of the document) instead of three\n // (one with `hasPendingWrites`, the modified document with\n // `hasPendingWrites` and the final state of the document).\n return (\n oldDoc.hasLocalMutations &&\n newDoc.hasCommittedMutations &&\n !newDoc.hasLocalMutations\n );\n }\n\n /**\n * Updates the view with the given ViewDocumentChanges and optionally updates\n * limbo docs and sync state from the provided target change.\n * @param docChanges The set of changes to make to the view's docs.\n * @param updateLimboDocuments Whether to update limbo documents based on this\n * change.\n * @param targetChange A target change to apply for computing limbo docs and\n * sync state.\n * @return A new ViewChange with the given docs, changes, and sync state.\n */\n // PORTING NOTE: The iOS/Android clients always compute limbo document changes.\n applyChanges(\n docChanges: ViewDocumentChanges,\n updateLimboDocuments: boolean,\n targetChange?: TargetChange\n ): ViewChange {\n debugAssert(\n !docChanges.needsRefill,\n 'Cannot apply changes that need a refill'\n );\n const oldDocs = this.documentSet;\n this.documentSet = docChanges.documentSet;\n this.mutatedKeys = docChanges.mutatedKeys;\n // Sort changes based on type and query comparator\n const changes = docChanges.changeSet.getChanges();\n changes.sort((c1, c2) => {\n return (\n compareChangeType(c1.type, c2.type) ||\n this.query.docComparator(c1.doc, c2.doc)\n );\n });\n\n this.applyTargetChange(targetChange);\n const limboChanges = updateLimboDocuments\n ? this.updateLimboDocuments()\n : [];\n const synced = this.limboDocuments.size === 0 && this.current;\n const newSyncState = synced ? SyncState.Synced : SyncState.Local;\n const syncStateChanged = newSyncState !== this.syncState;\n this.syncState = newSyncState;\n\n if (changes.length === 0 && !syncStateChanged) {\n // no changes\n return { limboChanges };\n } else {\n const snap: ViewSnapshot = new ViewSnapshot(\n this.query,\n docChanges.documentSet,\n oldDocs,\n changes,\n docChanges.mutatedKeys,\n newSyncState === SyncState.Local,\n syncStateChanged,\n /* excludesMetadataChanges= */ false\n );\n return {\n snapshot: snap,\n limboChanges\n };\n }\n }\n\n /**\n * Applies an OnlineState change to the view, potentially generating a\n * ViewChange if the view's syncState changes as a result.\n */\n applyOnlineStateChange(onlineState: OnlineState): ViewChange {\n if (this.current && onlineState === OnlineState.Offline) {\n // If we're offline, set `current` to false and then call applyChanges()\n // to refresh our syncState and generate a ViewChange as appropriate. We\n // are guaranteed to get a new TargetChange that sets `current` back to\n // true once the client is back online.\n this.current = false;\n return this.applyChanges(\n {\n documentSet: this.documentSet,\n changeSet: new DocumentChangeSet(),\n mutatedKeys: this.mutatedKeys,\n needsRefill: false\n },\n /* updateLimboDocuments= */ false\n );\n } else {\n // No effect, just return a no-op ViewChange.\n return { limboChanges: [] };\n }\n }\n\n /**\n * Returns whether the doc for the given key should be in limbo.\n */\n private shouldBeInLimbo(key: DocumentKey): boolean {\n // If the remote end says it's part of this query, it's not in limbo.\n if (this._syncedDocuments.has(key)) {\n return false;\n }\n // The local store doesn't think it's a result, so it shouldn't be in limbo.\n if (!this.documentSet.has(key)) {\n return false;\n }\n // If there are local changes to the doc, they might explain why the server\n // doesn't know that it's part of the query. So don't put it in limbo.\n // TODO(klimt): Ideally, we would only consider changes that might actually\n // affect this specific query.\n if (this.documentSet.get(key)!.hasLocalMutations) {\n return false;\n }\n // Everything else is in limbo.\n return true;\n }\n\n /**\n * Updates syncedDocuments, current, and limbo docs based on the given change.\n * Returns the list of changes to which docs are in limbo.\n */\n private applyTargetChange(targetChange?: TargetChange): void {\n if (targetChange) {\n targetChange.addedDocuments.forEach(\n key => (this._syncedDocuments = this._syncedDocuments.add(key))\n );\n targetChange.modifiedDocuments.forEach(key => {\n debugAssert(\n this._syncedDocuments.has(key),\n `Modified document ${key} not found in view.`\n );\n });\n targetChange.removedDocuments.forEach(\n key => (this._syncedDocuments = this._syncedDocuments.delete(key))\n );\n this.current = targetChange.current;\n }\n }\n\n private updateLimboDocuments(): LimboDocumentChange[] {\n // We can only determine limbo documents when we're in-sync with the server.\n if (!this.current) {\n return [];\n }\n\n // TODO(klimt): Do this incrementally so that it's not quadratic when\n // updating many documents.\n const oldLimboDocuments = this.limboDocuments;\n this.limboDocuments = documentKeySet();\n this.documentSet.forEach(doc => {\n if (this.shouldBeInLimbo(doc.key)) {\n this.limboDocuments = this.limboDocuments.add(doc.key);\n }\n });\n\n // Diff the new limbo docs with the old limbo docs.\n const changes: LimboDocumentChange[] = [];\n oldLimboDocuments.forEach(key => {\n if (!this.limboDocuments.has(key)) {\n changes.push(new RemovedLimboDocument(key));\n }\n });\n this.limboDocuments.forEach(key => {\n if (!oldLimboDocuments.has(key)) {\n changes.push(new AddedLimboDocument(key));\n }\n });\n return changes;\n }\n\n /**\n * Update the in-memory state of the current view with the state read from\n * persistence.\n *\n * We update the query view whenever a client's primary status changes:\n * - When a client transitions from primary to secondary, it can miss\n * LocalStorage updates and its query views may temporarily not be\n * synchronized with the state on disk.\n * - For secondary to primary transitions, the client needs to update the list\n * of `syncedDocuments` since secondary clients update their query views\n * based purely on synthesized RemoteEvents.\n *\n * @param queryResult.documents - The documents that match the query according\n * to the LocalStore.\n * @param queryResult.remoteKeys - The keys of the documents that match the\n * query according to the backend.\n *\n * @return The ViewChange that resulted from this synchronization.\n */\n // PORTING NOTE: Multi-tab only.\n synchronizeWithPersistedState(queryResult: QueryResult): ViewChange {\n this._syncedDocuments = queryResult.remoteKeys;\n this.limboDocuments = documentKeySet();\n const docChanges = this.computeDocChanges(queryResult.documents);\n return this.applyChanges(docChanges, /*updateLimboDocuments=*/ true);\n }\n\n /**\n * Returns a view snapshot as if this query was just listened to. Contains\n * a document add for every existing document and the `fromCache` and\n * `hasPendingWrites` status of the already established view.\n */\n // PORTING NOTE: Multi-tab only.\n computeInitialSnapshot(): ViewSnapshot {\n return ViewSnapshot.fromInitialDocuments(\n this.query,\n this.documentSet,\n this.mutatedKeys,\n this.syncState === SyncState.Local\n );\n }\n}\n\nfunction compareChangeType(c1: ChangeType, c2: ChangeType): number {\n const order = (change: ChangeType): 0 | 1 | 2 => {\n switch (change) {\n case ChangeType.Added:\n return 1;\n case ChangeType.Modified:\n return 2;\n case ChangeType.Metadata:\n // A metadata change is converted to a modified change at the public\n // api layer. Since we sort by document key and then change type,\n // metadata and modified changes must be sorted equivalently.\n return 2;\n case ChangeType.Removed:\n return 0;\n default:\n return fail('Unknown ChangeType: ' + change);\n }\n };\n\n return order(c1) - order(c2);\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { User } from '../auth/user';\nimport {\n ignoreIfPrimaryLeaseLoss,\n LocalStore,\n MultiTabLocalStore\n} from '../local/local_store';\nimport { LocalViewChanges } from '../local/local_view_changes';\nimport { ReferenceSet } from '../local/reference_set';\nimport { TargetData, TargetPurpose } from '../local/target_data';\nimport {\n documentKeySet,\n DocumentKeySet,\n MaybeDocumentMap\n} from '../model/collections';\nimport { MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation } from '../model/mutation';\nimport { BATCHID_UNKNOWN, MutationBatchResult } from '../model/mutation_batch';\nimport { RemoteEvent, TargetChange } from '../remote/remote_event';\nimport { RemoteStore } from '../remote/remote_store';\nimport { RemoteSyncer } from '../remote/remote_syncer';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { primitiveComparator } from '../util/misc';\nimport { ObjectMap } from '../util/obj_map';\nimport { Deferred } from '../util/promise';\nimport { SortedMap } from '../util/sorted_map';\n\nimport { ClientId, SharedClientState } from '../local/shared_client_state';\nimport {\n QueryTargetState,\n SharedClientStateSyncer\n} from '../local/shared_client_state_syncer';\nimport { SortedSet } from '../util/sorted_set';\nimport { ListenSequence } from './listen_sequence';\nimport { LimitType, Query } from './query';\nimport { SnapshotVersion } from './snapshot_version';\nimport { Target } from './target';\nimport { TargetIdGenerator } from './target_id_generator';\nimport { Transaction } from './transaction';\nimport {\n BatchId,\n MutationBatchState,\n OnlineState,\n OnlineStateSource,\n TargetId\n} from './types';\nimport {\n AddedLimboDocument,\n LimboDocumentChange,\n RemovedLimboDocument,\n View,\n ViewChange,\n ViewDocumentChanges\n} from './view';\nimport { ViewSnapshot } from './view_snapshot';\nimport { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue';\nimport { TransactionRunner } from './transaction_runner';\n\nconst LOG_TAG = 'SyncEngine';\n\n/**\n * QueryView contains all of the data that SyncEngine needs to keep track of for\n * a particular query.\n */\nclass QueryView {\n constructor(\n /**\n * The query itself.\n */\n public query: Query,\n /**\n * The target number created by the client that is used in the watch\n * stream to identify this query.\n */\n public targetId: TargetId,\n /**\n * The view is responsible for computing the final merged truth of what\n * docs are in the query. It gets notified of local and remote changes,\n * and applies the query filters and limits to determine the most correct\n * possible results.\n */\n public view: View\n ) {}\n}\n\n/** Tracks a limbo resolution. */\nclass LimboResolution {\n constructor(public key: DocumentKey) {}\n\n /**\n * Set to true once we've received a document. This is used in\n * getRemoteKeysForTarget() and ultimately used by WatchChangeAggregator to\n * decide whether it needs to manufacture a delete event for the target once\n * the target is CURRENT.\n */\n receivedDocument: boolean = false;\n}\n\n/**\n * Interface implemented by EventManager to handle notifications from\n * SyncEngine.\n */\nexport interface SyncEngineListener {\n /** Handles new view snapshots. */\n onWatchChange(snapshots: ViewSnapshot[]): void;\n\n /** Handles the failure of a query. */\n onWatchError(query: Query, error: Error): void;\n\n /** Handles a change in online state. */\n onOnlineStateChange(onlineState: OnlineState): void;\n}\n\n/**\n * SyncEngine is the central controller in the client SDK architecture. It is\n * the glue code between the EventManager, LocalStore, and RemoteStore. Some of\n * SyncEngine's responsibilities include:\n * 1. Coordinating client requests and remote events between the EventManager\n * and the local and remote data stores.\n * 2. Managing a View object for each query, providing the unified view between\n * the local and remote data stores.\n * 3. Notifying the RemoteStore when the LocalStore has new mutations in its\n * queue that need sending to the backend.\n *\n * The SyncEngine’s methods should only ever be called by methods running in the\n * global async queue.\n */\nexport class SyncEngine implements RemoteSyncer {\n protected syncEngineListener: SyncEngineListener | null = null;\n\n protected queryViewsByQuery = new ObjectMap<Query, QueryView>(q =>\n q.canonicalId()\n );\n protected queriesByTarget = new Map<TargetId, Query[]>();\n /**\n * The keys of documents that are in limbo for which we haven't yet started a\n * limbo resolution query.\n */\n private enqueuedLimboResolutions: DocumentKey[] = [];\n /**\n * Keeps track of the target ID for each document that is in limbo with an\n * active target.\n */\n protected activeLimboTargetsByKey = new SortedMap<DocumentKey, TargetId>(\n DocumentKey.comparator\n );\n /**\n * Keeps track of the information about an active limbo resolution for each\n * active target ID that was started for the purpose of limbo resolution.\n */\n protected activeLimboResolutionsByTarget = new Map<\n TargetId,\n LimboResolution\n >();\n protected limboDocumentRefs = new ReferenceSet();\n /** Stores user completion handlers, indexed by User and BatchId. */\n private mutationUserCallbacks = {} as {\n [uidKey: string]: SortedMap<BatchId, Deferred<void>>;\n };\n /** Stores user callbacks waiting for all pending writes to be acknowledged. */\n private pendingWritesCallbacks = new Map<BatchId, Array<Deferred<void>>>();\n private limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();\n\n private onlineState = OnlineState.Unknown;\n\n constructor(\n protected localStore: LocalStore,\n protected remoteStore: RemoteStore,\n // PORTING NOTE: Manages state synchronization in multi-tab environments.\n protected sharedClientState: SharedClientState,\n private currentUser: User,\n private maxConcurrentLimboResolutions: number\n ) {}\n\n get isPrimaryClient(): boolean {\n return true;\n }\n\n /** Subscribes to SyncEngine notifications. Has to be called exactly once. */\n subscribe(syncEngineListener: SyncEngineListener): void {\n debugAssert(\n syncEngineListener !== null,\n 'SyncEngine listener cannot be null'\n );\n debugAssert(\n this.syncEngineListener === null,\n 'SyncEngine already has a subscriber.'\n );\n\n this.syncEngineListener = syncEngineListener;\n }\n\n /**\n * Initiates the new listen, resolves promise when listen enqueued to the\n * server. All the subsequent view snapshots or errors are sent to the\n * subscribed handlers. Returns the initial snapshot.\n */\n async listen(query: Query): Promise<ViewSnapshot> {\n this.assertSubscribed('listen()');\n\n let targetId;\n let viewSnapshot;\n\n const queryView = this.queryViewsByQuery.get(query);\n if (queryView) {\n // PORTING NOTE: With Multi-Tab Web, it is possible that a query view\n // already exists when EventManager calls us for the first time. This\n // happens when the primary tab is already listening to this query on\n // behalf of another tab and the user of the primary also starts listening\n // to the query. EventManager will not have an assigned target ID in this\n // case and calls `listen` to obtain this ID.\n targetId = queryView.targetId;\n this.sharedClientState.addLocalQueryTarget(targetId);\n viewSnapshot = queryView.view.computeInitialSnapshot();\n } else {\n const targetData = await this.localStore.allocateTarget(query.toTarget());\n\n const status = this.sharedClientState.addLocalQueryTarget(\n targetData.targetId\n );\n targetId = targetData.targetId;\n viewSnapshot = await this.initializeViewAndComputeSnapshot(\n query,\n targetId,\n status === 'current'\n );\n if (this.isPrimaryClient) {\n this.remoteStore.listen(targetData);\n }\n }\n\n return viewSnapshot;\n }\n\n /**\n * Registers a view for a previously unknown query and computes its initial\n * snapshot.\n */\n protected async initializeViewAndComputeSnapshot(\n query: Query,\n targetId: TargetId,\n current: boolean\n ): Promise<ViewSnapshot> {\n const queryResult = await this.localStore.executeQuery(\n query,\n /* usePreviousResults= */ true\n );\n const view = new View(query, queryResult.remoteKeys);\n const viewDocChanges = view.computeDocChanges(queryResult.documents);\n const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(\n targetId,\n current && this.onlineState !== OnlineState.Offline\n );\n const viewChange = view.applyChanges(\n viewDocChanges,\n /* updateLimboDocuments= */ this.isPrimaryClient,\n synthesizedTargetChange\n );\n this.updateTrackedLimbos(targetId, viewChange.limboChanges);\n\n debugAssert(\n !!viewChange.snapshot,\n 'applyChanges for new view should always return a snapshot'\n );\n\n const data = new QueryView(query, targetId, view);\n this.queryViewsByQuery.set(query, data);\n if (this.queriesByTarget.has(targetId)) {\n this.queriesByTarget.get(targetId)!.push(query);\n } else {\n this.queriesByTarget.set(targetId, [query]);\n }\n return viewChange.snapshot!;\n }\n\n /** Stops listening to the query. */\n async unlisten(query: Query): Promise<void> {\n this.assertSubscribed('unlisten()');\n\n const queryView = this.queryViewsByQuery.get(query)!;\n debugAssert(!!queryView, 'Trying to unlisten on query not found:' + query);\n\n // Only clean up the query view and target if this is the only query mapped\n // to the target.\n const queries = this.queriesByTarget.get(queryView.targetId)!;\n if (queries.length > 1) {\n this.queriesByTarget.set(\n queryView.targetId,\n queries.filter(q => !q.isEqual(query))\n );\n this.queryViewsByQuery.delete(query);\n return;\n }\n\n // No other queries are mapped to the target, clean up the query and the target.\n if (this.isPrimaryClient) {\n // We need to remove the local query target first to allow us to verify\n // whether any other client is still interested in this target.\n this.sharedClientState.removeLocalQueryTarget(queryView.targetId);\n const targetRemainsActive = this.sharedClientState.isActiveQueryTarget(\n queryView.targetId\n );\n\n if (!targetRemainsActive) {\n await this.localStore\n .releaseTarget(queryView.targetId, /*keepPersistedTargetData=*/ false)\n .then(() => {\n this.sharedClientState.clearQueryState(queryView.targetId);\n this.remoteStore.unlisten(queryView.targetId);\n this.removeAndCleanupTarget(queryView.targetId);\n })\n .catch(ignoreIfPrimaryLeaseLoss);\n }\n } else {\n this.removeAndCleanupTarget(queryView.targetId);\n await this.localStore.releaseTarget(\n queryView.targetId,\n /*keepPersistedTargetData=*/ true\n );\n }\n }\n\n /**\n * Initiates the write of local mutation batch which involves adding the\n * writes to the mutation queue, notifying the remote store about new\n * mutations and raising events for any changes this write caused.\n *\n * The promise returned by this call is resolved when the above steps\n * have completed, *not* when the write was acked by the backend. The\n * userCallback is resolved once the write was acked/rejected by the\n * backend (or failed locally for any other reason).\n */\n async write(batch: Mutation[], userCallback: Deferred<void>): Promise<void> {\n this.assertSubscribed('write()');\n\n try {\n const result = await this.localStore.localWrite(batch);\n this.sharedClientState.addPendingMutation(result.batchId);\n this.addMutationCallback(result.batchId, userCallback);\n await this.emitNewSnapsAndNotifyLocalStore(result.changes);\n await this.remoteStore.fillWritePipeline();\n } catch (e) {\n // If we can't persist the mutation, we reject the user callback and\n // don't send the mutation. The user can then retry the write.\n const error = wrapInUserErrorIfRecoverable(e, `Failed to persist write`);\n userCallback.reject(error);\n }\n }\n\n /**\n * Takes an updateFunction in which a set of reads and writes can be performed\n * atomically. In the updateFunction, the client can read and write values\n * using the supplied transaction object. After the updateFunction, all\n * changes will be committed. If a retryable error occurs (ex: some other\n * client has changed any of the data referenced), then the updateFunction\n * will be called again after a backoff. If the updateFunction still fails\n * after all retries, then the transaction will be rejected.\n *\n * The transaction object passed to the updateFunction contains methods for\n * accessing documents and collections. Unlike other datastore access, data\n * accessed with the transaction will not reflect local changes that have not\n * been committed. For this reason, it is required that all reads are\n * performed before any writes. Transactions must be performed while online.\n *\n * The Deferred input is resolved when the transaction is fully committed.\n */\n runTransaction<T>(\n asyncQueue: AsyncQueue,\n updateFunction: (transaction: Transaction) => Promise<T>,\n deferred: Deferred<T>\n ): void {\n new TransactionRunner<T>(\n asyncQueue,\n this.remoteStore,\n updateFunction,\n deferred\n ).run();\n }\n\n async applyRemoteEvent(remoteEvent: RemoteEvent): Promise<void> {\n this.assertSubscribed('applyRemoteEvent()');\n try {\n const changes = await this.localStore.applyRemoteEvent(remoteEvent);\n // Update `receivedDocument` as appropriate for any limbo targets.\n remoteEvent.targetChanges.forEach((targetChange, targetId) => {\n const limboResolution = this.activeLimboResolutionsByTarget.get(\n targetId\n );\n if (limboResolution) {\n // Since this is a limbo resolution lookup, it's for a single document\n // and it could be added, modified, or removed, but not a combination.\n hardAssert(\n targetChange.addedDocuments.size +\n targetChange.modifiedDocuments.size +\n targetChange.removedDocuments.size <=\n 1,\n 'Limbo resolution for single document contains multiple changes.'\n );\n if (targetChange.addedDocuments.size > 0) {\n limboResolution.receivedDocument = true;\n } else if (targetChange.modifiedDocuments.size > 0) {\n hardAssert(\n limboResolution.receivedDocument,\n 'Received change for limbo target document without add.'\n );\n } else if (targetChange.removedDocuments.size > 0) {\n hardAssert(\n limboResolution.receivedDocument,\n 'Received remove for limbo target document without add.'\n );\n limboResolution.receivedDocument = false;\n } else {\n // This was probably just a CURRENT targetChange or similar.\n }\n }\n });\n await this.emitNewSnapsAndNotifyLocalStore(changes, remoteEvent);\n } catch (error) {\n await ignoreIfPrimaryLeaseLoss(error);\n }\n }\n\n /**\n * Applies an OnlineState change to the sync engine and notifies any views of\n * the change.\n */\n applyOnlineStateChange(\n onlineState: OnlineState,\n source: OnlineStateSource\n ): void {\n this.assertSubscribed('applyOnlineStateChange()');\n const newViewSnapshots = [] as ViewSnapshot[];\n this.queryViewsByQuery.forEach((query, queryView) => {\n const viewChange = queryView.view.applyOnlineStateChange(onlineState);\n debugAssert(\n viewChange.limboChanges.length === 0,\n 'OnlineState should not affect limbo documents.'\n );\n if (viewChange.snapshot) {\n newViewSnapshots.push(viewChange.snapshot);\n }\n });\n this.syncEngineListener!.onOnlineStateChange(onlineState);\n this.syncEngineListener!.onWatchChange(newViewSnapshots);\n this.onlineState = onlineState;\n }\n\n async rejectListen(targetId: TargetId, err: FirestoreError): Promise<void> {\n this.assertSubscribed('rejectListens()');\n\n // PORTING NOTE: Multi-tab only.\n this.sharedClientState.updateQueryState(targetId, 'rejected', err);\n\n const limboResolution = this.activeLimboResolutionsByTarget.get(targetId);\n const limboKey = limboResolution && limboResolution.key;\n if (limboKey) {\n // TODO(klimt): We really only should do the following on permission\n // denied errors, but we don't have the cause code here.\n\n // It's a limbo doc. Create a synthetic event saying it was deleted.\n // This is kind of a hack. Ideally, we would have a method in the local\n // store to purge a document. However, it would be tricky to keep all of\n // the local store's invariants with another method.\n let documentUpdates = new SortedMap<DocumentKey, MaybeDocument>(\n DocumentKey.comparator\n );\n documentUpdates = documentUpdates.insert(\n limboKey,\n new NoDocument(limboKey, SnapshotVersion.min())\n );\n const resolvedLimboDocuments = documentKeySet().add(limboKey);\n const event = new RemoteEvent(\n SnapshotVersion.min(),\n /* targetChanges= */ new Map<TargetId, TargetChange>(),\n /* targetMismatches= */ new SortedSet<TargetId>(primitiveComparator),\n documentUpdates,\n resolvedLimboDocuments\n );\n\n await this.applyRemoteEvent(event);\n\n // Since this query failed, we won't want to manually unlisten to it.\n // We only remove it from bookkeeping after we successfully applied the\n // RemoteEvent. If `applyRemoteEvent()` throws, we want to re-listen to\n // this query when the RemoteStore restarts the Watch stream, which should\n // re-trigger the target failure.\n this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.remove(\n limboKey\n );\n this.activeLimboResolutionsByTarget.delete(targetId);\n this.pumpEnqueuedLimboResolutions();\n } else {\n await this.localStore\n .releaseTarget(targetId, /* keepPersistedTargetData */ false)\n .then(() => this.removeAndCleanupTarget(targetId, err))\n .catch(ignoreIfPrimaryLeaseLoss);\n }\n }\n\n async applySuccessfulWrite(\n mutationBatchResult: MutationBatchResult\n ): Promise<void> {\n this.assertSubscribed('applySuccessfulWrite()');\n\n const batchId = mutationBatchResult.batch.batchId;\n\n // The local store may or may not be able to apply the write result and\n // raise events immediately (depending on whether the watcher is caught\n // up), so we raise user callbacks first so that they consistently happen\n // before listen events.\n this.processUserCallback(batchId, /*error=*/ null);\n\n this.triggerPendingWritesCallbacks(batchId);\n\n try {\n const changes = await this.localStore.acknowledgeBatch(\n mutationBatchResult\n );\n this.sharedClientState.updateMutationState(batchId, 'acknowledged');\n await this.emitNewSnapsAndNotifyLocalStore(changes);\n } catch (error) {\n await ignoreIfPrimaryLeaseLoss(error);\n }\n }\n\n async rejectFailedWrite(\n batchId: BatchId,\n error: FirestoreError\n ): Promise<void> {\n this.assertSubscribed('rejectFailedWrite()');\n\n // The local store may or may not be able to apply the write result and\n // raise events immediately (depending on whether the watcher is caught up),\n // so we raise user callbacks first so that they consistently happen before\n // listen events.\n this.processUserCallback(batchId, error);\n\n this.triggerPendingWritesCallbacks(batchId);\n\n try {\n const changes = await this.localStore.rejectBatch(batchId);\n this.sharedClientState.updateMutationState(batchId, 'rejected', error);\n await this.emitNewSnapsAndNotifyLocalStore(changes);\n } catch (error) {\n await ignoreIfPrimaryLeaseLoss(error);\n }\n }\n\n /**\n * Registers a user callback that resolves when all pending mutations at the moment of calling\n * are acknowledged .\n */\n async registerPendingWritesCallback(callback: Deferred<void>): Promise<void> {\n if (!this.remoteStore.canUseNetwork()) {\n logDebug(\n LOG_TAG,\n 'The network is disabled. The task returned by ' +\n \"'awaitPendingWrites()' will not complete until the network is enabled.\"\n );\n }\n\n try {\n const highestBatchId = await this.localStore.getHighestUnacknowledgedBatchId();\n if (highestBatchId === BATCHID_UNKNOWN) {\n // Trigger the callback right away if there is no pending writes at the moment.\n callback.resolve();\n return;\n }\n\n const callbacks = this.pendingWritesCallbacks.get(highestBatchId) || [];\n callbacks.push(callback);\n this.pendingWritesCallbacks.set(highestBatchId, callbacks);\n } catch (e) {\n const firestoreError = wrapInUserErrorIfRecoverable(\n e,\n 'Initialization of waitForPendingWrites() operation failed'\n );\n callback.reject(firestoreError);\n }\n }\n\n /**\n * Triggers the callbacks that are waiting for this batch id to get acknowledged by server,\n * if there are any.\n */\n private triggerPendingWritesCallbacks(batchId: BatchId): void {\n (this.pendingWritesCallbacks.get(batchId) || []).forEach(callback => {\n callback.resolve();\n });\n\n this.pendingWritesCallbacks.delete(batchId);\n }\n\n /** Reject all outstanding callbacks waiting for pending writes to complete. */\n private rejectOutstandingPendingWritesCallbacks(errorMessage: string): void {\n this.pendingWritesCallbacks.forEach(callbacks => {\n callbacks.forEach(callback => {\n callback.reject(new FirestoreError(Code.CANCELLED, errorMessage));\n });\n });\n\n this.pendingWritesCallbacks.clear();\n }\n\n private addMutationCallback(\n batchId: BatchId,\n callback: Deferred<void>\n ): void {\n let newCallbacks = this.mutationUserCallbacks[this.currentUser.toKey()];\n if (!newCallbacks) {\n newCallbacks = new SortedMap<BatchId, Deferred<void>>(\n primitiveComparator\n );\n }\n newCallbacks = newCallbacks.insert(batchId, callback);\n this.mutationUserCallbacks[this.currentUser.toKey()] = newCallbacks;\n }\n\n /**\n * Resolves or rejects the user callback for the given batch and then discards\n * it.\n */\n protected processUserCallback(batchId: BatchId, error: Error | null): void {\n let newCallbacks = this.mutationUserCallbacks[this.currentUser.toKey()];\n\n // NOTE: Mutations restored from persistence won't have callbacks, so it's\n // okay for there to be no callback for this ID.\n if (newCallbacks) {\n const callback = newCallbacks.get(batchId);\n if (callback) {\n debugAssert(\n batchId === newCallbacks.minKey(),\n 'Mutation callbacks processed out-of-order?'\n );\n if (error) {\n callback.reject(error);\n } else {\n callback.resolve();\n }\n newCallbacks = newCallbacks.remove(batchId);\n }\n this.mutationUserCallbacks[this.currentUser.toKey()] = newCallbacks;\n }\n }\n\n protected removeAndCleanupTarget(\n targetId: number,\n error: Error | null = null\n ): void {\n this.sharedClientState.removeLocalQueryTarget(targetId);\n\n debugAssert(\n this.queriesByTarget.has(targetId) &&\n this.queriesByTarget.get(targetId)!.length !== 0,\n `There are no queries mapped to target id ${targetId}`\n );\n\n for (const query of this.queriesByTarget.get(targetId)!) {\n this.queryViewsByQuery.delete(query);\n if (error) {\n this.syncEngineListener!.onWatchError(query, error);\n }\n }\n\n this.queriesByTarget.delete(targetId);\n\n if (this.isPrimaryClient) {\n const limboKeys = this.limboDocumentRefs.removeReferencesForId(targetId);\n limboKeys.forEach(limboKey => {\n const isReferenced = this.limboDocumentRefs.containsKey(limboKey);\n if (!isReferenced) {\n // We removed the last reference for this key\n this.removeLimboTarget(limboKey);\n }\n });\n }\n }\n\n private removeLimboTarget(key: DocumentKey): void {\n // It's possible that the target already got removed because the query failed. In that case,\n // the key won't exist in `limboTargetsByKey`. Only do the cleanup if we still have the target.\n const limboTargetId = this.activeLimboTargetsByKey.get(key);\n if (limboTargetId === null) {\n // This target already got removed, because the query failed.\n return;\n }\n\n this.remoteStore.unlisten(limboTargetId);\n this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.remove(key);\n this.activeLimboResolutionsByTarget.delete(limboTargetId);\n this.pumpEnqueuedLimboResolutions();\n }\n\n protected updateTrackedLimbos(\n targetId: TargetId,\n limboChanges: LimboDocumentChange[]\n ): void {\n for (const limboChange of limboChanges) {\n if (limboChange instanceof AddedLimboDocument) {\n this.limboDocumentRefs.addReference(limboChange.key, targetId);\n this.trackLimboChange(limboChange);\n } else if (limboChange instanceof RemovedLimboDocument) {\n logDebug(LOG_TAG, 'Document no longer in limbo: ' + limboChange.key);\n this.limboDocumentRefs.removeReference(limboChange.key, targetId);\n const isReferenced = this.limboDocumentRefs.containsKey(\n limboChange.key\n );\n if (!isReferenced) {\n // We removed the last reference for this key\n this.removeLimboTarget(limboChange.key);\n }\n } else {\n fail('Unknown limbo change: ' + JSON.stringify(limboChange));\n }\n }\n }\n\n private trackLimboChange(limboChange: AddedLimboDocument): void {\n const key = limboChange.key;\n if (!this.activeLimboTargetsByKey.get(key)) {\n logDebug(LOG_TAG, 'New document in limbo: ' + key);\n this.enqueuedLimboResolutions.push(key);\n this.pumpEnqueuedLimboResolutions();\n }\n }\n\n /**\n * Starts listens for documents in limbo that are enqueued for resolution,\n * subject to a maximum number of concurrent resolutions.\n *\n * Without bounding the number of concurrent resolutions, the server can fail\n * with \"resource exhausted\" errors which can lead to pathological client\n * behavior as seen in https://github.com/firebase/firebase-js-sdk/issues/2683.\n */\n private pumpEnqueuedLimboResolutions(): void {\n while (\n this.enqueuedLimboResolutions.length > 0 &&\n this.activeLimboTargetsByKey.size < this.maxConcurrentLimboResolutions\n ) {\n const key = this.enqueuedLimboResolutions.shift()!;\n const limboTargetId = this.limboTargetIdGenerator.next();\n this.activeLimboResolutionsByTarget.set(\n limboTargetId,\n new LimboResolution(key)\n );\n this.activeLimboTargetsByKey = this.activeLimboTargetsByKey.insert(\n key,\n limboTargetId\n );\n this.remoteStore.listen(\n new TargetData(\n Query.atPath(key.path).toTarget(),\n limboTargetId,\n TargetPurpose.LimboResolution,\n ListenSequence.INVALID\n )\n );\n }\n }\n\n // Visible for testing\n activeLimboDocumentResolutions(): SortedMap<DocumentKey, TargetId> {\n return this.activeLimboTargetsByKey;\n }\n\n // Visible for testing\n enqueuedLimboDocumentResolutions(): DocumentKey[] {\n return this.enqueuedLimboResolutions;\n }\n\n protected async emitNewSnapsAndNotifyLocalStore(\n changes: MaybeDocumentMap,\n remoteEvent?: RemoteEvent\n ): Promise<void> {\n const newSnaps: ViewSnapshot[] = [];\n const docChangesInAllViews: LocalViewChanges[] = [];\n const queriesProcessed: Array<Promise<void>> = [];\n\n this.queryViewsByQuery.forEach((_, queryView) => {\n queriesProcessed.push(\n Promise.resolve()\n .then(() => {\n const viewDocChanges = queryView.view.computeDocChanges(changes);\n if (!viewDocChanges.needsRefill) {\n return viewDocChanges;\n }\n // The query has a limit and some docs were removed, so we need\n // to re-run the query against the local store to make sure we\n // didn't lose any good docs that had been past the limit.\n return this.localStore\n .executeQuery(queryView.query, /* usePreviousResults= */ false)\n .then(({ documents }) => {\n return queryView.view.computeDocChanges(\n documents,\n viewDocChanges\n );\n });\n })\n .then((viewDocChanges: ViewDocumentChanges) => {\n const targetChange =\n remoteEvent && remoteEvent.targetChanges.get(queryView.targetId);\n const viewChange = queryView.view.applyChanges(\n viewDocChanges,\n /* updateLimboDocuments= */ this.isPrimaryClient,\n targetChange\n );\n this.updateTrackedLimbos(\n queryView.targetId,\n viewChange.limboChanges\n );\n if (viewChange.snapshot) {\n if (this.isPrimaryClient) {\n this.sharedClientState.updateQueryState(\n queryView.targetId,\n viewChange.snapshot.fromCache ? 'not-current' : 'current'\n );\n }\n\n newSnaps.push(viewChange.snapshot);\n const docChanges = LocalViewChanges.fromSnapshot(\n queryView.targetId,\n viewChange.snapshot\n );\n docChangesInAllViews.push(docChanges);\n }\n })\n );\n });\n\n await Promise.all(queriesProcessed);\n this.syncEngineListener!.onWatchChange(newSnaps);\n await this.localStore.notifyLocalViewChanges(docChangesInAllViews);\n }\n\n protected assertSubscribed(fnName: string): void {\n debugAssert(\n this.syncEngineListener !== null,\n 'Trying to call ' + fnName + ' before calling subscribe().'\n );\n }\n\n async handleCredentialChange(user: User): Promise<void> {\n const userChanged = !this.currentUser.isEqual(user);\n\n if (userChanged) {\n const result = await this.localStore.handleUserChange(user);\n this.currentUser = user;\n\n // Fails tasks waiting for pending writes requested by previous user.\n this.rejectOutstandingPendingWritesCallbacks(\n \"'waitForPendingWrites' promise is rejected due to a user change.\"\n );\n // TODO(b/114226417): Consider calling this only in the primary tab.\n this.sharedClientState.handleUserChange(\n user,\n result.removedBatchIds,\n result.addedBatchIds\n );\n await this.emitNewSnapsAndNotifyLocalStore(result.affectedDocuments);\n }\n\n await this.remoteStore.handleCredentialChange();\n }\n\n enableNetwork(): Promise<void> {\n return this.remoteStore.enableNetwork();\n }\n\n disableNetwork(): Promise<void> {\n return this.remoteStore.disableNetwork();\n }\n\n getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet {\n const limboResolution = this.activeLimboResolutionsByTarget.get(targetId);\n if (limboResolution && limboResolution.receivedDocument) {\n return documentKeySet().add(limboResolution.key);\n } else {\n let keySet = documentKeySet();\n const queries = this.queriesByTarget.get(targetId);\n if (!queries) {\n return keySet;\n }\n for (const query of queries) {\n const queryView = this.queryViewsByQuery.get(query);\n debugAssert(!!queryView, `No query view found for ${query}`);\n keySet = keySet.unionWith(queryView.view.syncedDocuments);\n }\n return keySet;\n }\n }\n}\n\n/**\n * An impplementation of SyncEngine that implement SharedClientStateSyncer for\n * Multi-Tab synchronization.\n */\n// PORTING NOTE: Web only\nexport class MultiTabSyncEngine extends SyncEngine\n implements SharedClientStateSyncer {\n // The primary state is set to `true` or `false` immediately after Firestore\n // startup. In the interim, a client should only be considered primary if\n // `isPrimary` is true.\n private _isPrimaryClient: undefined | boolean = undefined;\n\n constructor(\n protected localStore: MultiTabLocalStore,\n remoteStore: RemoteStore,\n sharedClientState: SharedClientState,\n currentUser: User,\n maxConcurrentLimboResolutions: number\n ) {\n super(\n localStore,\n remoteStore,\n sharedClientState,\n currentUser,\n maxConcurrentLimboResolutions\n );\n }\n\n get isPrimaryClient(): boolean {\n return this._isPrimaryClient === true;\n }\n\n enableNetwork(): Promise<void> {\n this.localStore.setNetworkEnabled(true);\n return super.enableNetwork();\n }\n\n disableNetwork(): Promise<void> {\n this.localStore.setNetworkEnabled(false);\n return super.disableNetwork();\n }\n\n /**\n * Reconcile the list of synced documents in an existing view with those\n * from persistence.\n */\n private async synchronizeViewAndComputeSnapshot(\n queryView: QueryView\n ): Promise<ViewChange> {\n const queryResult = await this.localStore.executeQuery(\n queryView.query,\n /* usePreviousResults= */ true\n );\n const viewSnapshot = queryView.view.synchronizeWithPersistedState(\n queryResult\n );\n if (this._isPrimaryClient) {\n this.updateTrackedLimbos(queryView.targetId, viewSnapshot.limboChanges);\n }\n return viewSnapshot;\n }\n\n applyOnlineStateChange(\n onlineState: OnlineState,\n source: OnlineStateSource\n ): void {\n // If we are the primary client, the online state of all clients only\n // depends on the online state of the local RemoteStore.\n if (this.isPrimaryClient && source === OnlineStateSource.RemoteStore) {\n super.applyOnlineStateChange(onlineState, source);\n this.sharedClientState.setOnlineState(onlineState);\n }\n\n // If we are the secondary client, we explicitly ignore the remote store's\n // online state (the local client may go offline, even though the primary\n // tab remains online) and only apply the primary tab's online state from\n // SharedClientState.\n if (\n !this.isPrimaryClient &&\n source === OnlineStateSource.SharedClientState\n ) {\n super.applyOnlineStateChange(onlineState, source);\n }\n }\n\n async applyBatchState(\n batchId: BatchId,\n batchState: MutationBatchState,\n error?: FirestoreError\n ): Promise<void> {\n this.assertSubscribed('applyBatchState()');\n const documents = await this.localStore.lookupMutationDocuments(batchId);\n\n if (documents === null) {\n // A throttled tab may not have seen the mutation before it was completed\n // and removed from the mutation queue, in which case we won't have cached\n // the affected documents. In this case we can safely ignore the update\n // since that means we didn't apply the mutation locally at all (if we\n // had, we would have cached the affected documents), and so we will just\n // see any resulting document changes via normal remote document updates\n // as applicable.\n logDebug(LOG_TAG, 'Cannot apply mutation batch with id: ' + batchId);\n return;\n }\n\n if (batchState === 'pending') {\n // If we are the primary client, we need to send this write to the\n // backend. Secondary clients will ignore these writes since their remote\n // connection is disabled.\n await this.remoteStore.fillWritePipeline();\n } else if (batchState === 'acknowledged' || batchState === 'rejected') {\n // NOTE: Both these methods are no-ops for batches that originated from\n // other clients.\n this.processUserCallback(batchId, error ? error : null);\n this.localStore.removeCachedMutationBatchMetadata(batchId);\n } else {\n fail(`Unknown batchState: ${batchState}`);\n }\n\n await this.emitNewSnapsAndNotifyLocalStore(documents);\n }\n\n async applyPrimaryState(isPrimary: boolean): Promise<void> {\n if (isPrimary === true && this._isPrimaryClient !== true) {\n // Secondary tabs only maintain Views for their local listeners and the\n // Views internal state may not be 100% populated (in particular\n // secondary tabs don't track syncedDocuments, the set of documents the\n // server considers to be in the target). So when a secondary becomes\n // primary, we need to need to make sure that all views for all targets\n // match the state on disk.\n const activeTargets = this.sharedClientState.getAllActiveQueryTargets();\n const activeQueries = await this.synchronizeQueryViewsAndRaiseSnapshots(\n activeTargets.toArray(),\n /*transitionToPrimary=*/ true\n );\n this._isPrimaryClient = true;\n await this.remoteStore.applyPrimaryState(true);\n for (const targetData of activeQueries) {\n this.remoteStore.listen(targetData);\n }\n } else if (isPrimary === false && this._isPrimaryClient !== false) {\n const activeTargets: TargetId[] = [];\n\n let p = Promise.resolve();\n this.queriesByTarget.forEach((_, targetId) => {\n if (this.sharedClientState.isLocalQueryTarget(targetId)) {\n activeTargets.push(targetId);\n } else {\n p = p.then(() => {\n this.removeAndCleanupTarget(targetId);\n return this.localStore.releaseTarget(\n targetId,\n /*keepPersistedTargetData=*/ true\n );\n });\n }\n this.remoteStore.unlisten(targetId);\n });\n await p;\n\n await this.synchronizeQueryViewsAndRaiseSnapshots(\n activeTargets,\n /*transitionToPrimary=*/ false\n );\n this.resetLimboDocuments();\n this._isPrimaryClient = false;\n await this.remoteStore.applyPrimaryState(false);\n }\n }\n\n private resetLimboDocuments(): void {\n this.activeLimboResolutionsByTarget.forEach((_, targetId) => {\n this.remoteStore.unlisten(targetId);\n });\n this.limboDocumentRefs.removeAllReferences();\n this.activeLimboResolutionsByTarget = new Map<TargetId, LimboResolution>();\n this.activeLimboTargetsByKey = new SortedMap<DocumentKey, TargetId>(\n DocumentKey.comparator\n );\n }\n\n /**\n * Reconcile the query views of the provided query targets with the state from\n * persistence. Raises snapshots for any changes that affect the local\n * client and returns the updated state of all target's query data.\n *\n * @param targets the list of targets with views that need to be recomputed\n * @param transitionToPrimary `true` iff the tab transitions from a secondary\n * tab to a primary tab\n */\n private async synchronizeQueryViewsAndRaiseSnapshots(\n targets: TargetId[],\n transitionToPrimary: boolean\n ): Promise<TargetData[]> {\n const activeQueries: TargetData[] = [];\n const newViewSnapshots: ViewSnapshot[] = [];\n for (const targetId of targets) {\n let targetData: TargetData;\n const queries = this.queriesByTarget.get(targetId);\n\n if (queries && queries.length !== 0) {\n // For queries that have a local View, we need to update their state\n // in LocalStore (as the resume token and the snapshot version\n // might have changed) and reconcile their views with the persisted\n // state (the list of syncedDocuments may have gotten out of sync).\n await this.localStore.releaseTarget(\n targetId,\n /*keepPersistedTargetData=*/ true\n );\n targetData = await this.localStore.allocateTarget(\n queries[0].toTarget()\n );\n\n for (const query of queries) {\n const queryView = this.queryViewsByQuery.get(query);\n debugAssert(!!queryView, `No query view found for ${query}`);\n\n const viewChange = await this.synchronizeViewAndComputeSnapshot(\n queryView\n );\n if (viewChange.snapshot) {\n newViewSnapshots.push(viewChange.snapshot);\n }\n }\n } else {\n debugAssert(\n transitionToPrimary,\n 'A secondary tab should never have an active target without an active query.'\n );\n // For queries that never executed on this client, we need to\n // allocate the target in LocalStore and initialize a new View.\n const target = await this.localStore.getTarget(targetId);\n debugAssert(!!target, `Target for id ${targetId} not found`);\n targetData = await this.localStore.allocateTarget(target);\n await this.initializeViewAndComputeSnapshot(\n this.synthesizeTargetToQuery(target!),\n targetId,\n /*current=*/ false\n );\n }\n\n activeQueries.push(targetData!);\n }\n\n this.syncEngineListener!.onWatchChange(newViewSnapshots);\n return activeQueries;\n }\n\n /**\n * Creates a `Query` object from the specified `Target`. There is no way to\n * obtain the original `Query`, so we synthesize a `Query` from the `Target`\n * object.\n *\n * The synthesized result might be different from the original `Query`, but\n * since the synthesized `Query` should return the same results as the\n * original one (only the presentation of results might differ), the potential\n * difference will not cause issues.\n */\n private synthesizeTargetToQuery(target: Target): Query {\n return new Query(\n target.path,\n target.collectionGroup,\n target.orderBy,\n target.filters,\n target.limit,\n LimitType.First,\n target.startAt,\n target.endAt\n );\n }\n\n getActiveClients(): Promise<ClientId[]> {\n return this.localStore.getActiveClients();\n }\n\n async applyTargetState(\n targetId: TargetId,\n state: QueryTargetState,\n error?: FirestoreError\n ): Promise<void> {\n if (this._isPrimaryClient) {\n // If we receive a target state notification via WebStorage, we are\n // either already secondary or another tab has taken the primary lease.\n logDebug(LOG_TAG, 'Ignoring unexpected query state notification.');\n return;\n }\n\n if (this.queriesByTarget.has(targetId)) {\n switch (state) {\n case 'current':\n case 'not-current': {\n const changes = await this.localStore.getNewDocumentChanges();\n const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(\n targetId,\n state === 'current'\n );\n await this.emitNewSnapsAndNotifyLocalStore(\n changes,\n synthesizedRemoteEvent\n );\n break;\n }\n case 'rejected': {\n await this.localStore.releaseTarget(\n targetId,\n /* keepPersistedTargetData */ true\n );\n this.removeAndCleanupTarget(targetId, error);\n break;\n }\n default:\n fail('Unexpected target state: ' + state);\n }\n }\n }\n\n async applyActiveTargetsChange(\n added: TargetId[],\n removed: TargetId[]\n ): Promise<void> {\n if (!this._isPrimaryClient) {\n return;\n }\n\n for (const targetId of added) {\n if (this.queriesByTarget.has(targetId)) {\n // A target might have been added in a previous attempt\n logDebug(LOG_TAG, 'Adding an already active target ' + targetId);\n continue;\n }\n\n const target = await this.localStore.getTarget(targetId);\n debugAssert(\n !!target,\n `Query data for active target ${targetId} not found`\n );\n const targetData = await this.localStore.allocateTarget(target);\n await this.initializeViewAndComputeSnapshot(\n this.synthesizeTargetToQuery(target),\n targetData.targetId,\n /*current=*/ false\n );\n this.remoteStore.listen(targetData);\n }\n\n for (const targetId of removed) {\n // Check that the target is still active since the target might have been\n // removed if it has been rejected by the backend.\n if (!this.queriesByTarget.has(targetId)) {\n continue;\n }\n\n // Release queries that are still active.\n await this.localStore\n .releaseTarget(targetId, /* keepPersistedTargetData */ false)\n .then(() => {\n this.remoteStore.unlisten(targetId);\n this.removeAndCleanupTarget(targetId);\n })\n .catch(ignoreIfPrimaryLeaseLoss);\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert } from '../util/assert';\nimport { EventHandler } from '../util/misc';\nimport { ObjectMap } from '../util/obj_map';\nimport { Query } from './query';\nimport { SyncEngine, SyncEngineListener } from './sync_engine';\nimport { OnlineState } from './types';\nimport { ChangeType, DocumentViewChange, ViewSnapshot } from './view_snapshot';\nimport { wrapInUserErrorIfRecoverable } from '../util/async_queue';\n\n/**\n * Holds the listeners and the last received ViewSnapshot for a query being\n * tracked by EventManager.\n */\nclass QueryListenersInfo {\n viewSnap: ViewSnapshot | undefined = undefined;\n listeners: QueryListener[] = [];\n}\n\n/**\n * Interface for handling events from the EventManager.\n */\nexport interface Observer<T> {\n next: EventHandler<T>;\n error: EventHandler<Error>;\n}\n\n/**\n * EventManager is responsible for mapping queries to query event emitters.\n * It handles \"fan-out\". -- Identical queries will re-use the same watch on the\n * backend.\n */\nexport class EventManager implements SyncEngineListener {\n private queries = new ObjectMap<Query, QueryListenersInfo>(q =>\n q.canonicalId()\n );\n\n private onlineState = OnlineState.Unknown;\n\n private snapshotsInSyncListeners: Set<Observer<void>> = new Set();\n\n constructor(private syncEngine: SyncEngine) {\n this.syncEngine.subscribe(this);\n }\n\n async listen(listener: QueryListener): Promise<void> {\n const query = listener.query;\n let firstListen = false;\n\n let queryInfo = this.queries.get(query);\n if (!queryInfo) {\n firstListen = true;\n queryInfo = new QueryListenersInfo();\n }\n\n if (firstListen) {\n try {\n queryInfo.viewSnap = await this.syncEngine.listen(query);\n } catch (e) {\n const firestoreError = wrapInUserErrorIfRecoverable(\n e,\n `Initialization of query '${listener.query}' failed`\n );\n listener.onError(firestoreError);\n return;\n }\n }\n\n this.queries.set(query, queryInfo);\n queryInfo.listeners.push(listener);\n\n // Run global snapshot listeners if a consistent snapshot has been emitted.\n const raisedEvent = listener.applyOnlineStateChange(this.onlineState);\n debugAssert(\n !raisedEvent,\n \"applyOnlineStateChange() shouldn't raise an event for brand-new listeners.\"\n );\n\n if (queryInfo.viewSnap) {\n const raisedEvent = listener.onViewSnapshot(queryInfo.viewSnap);\n if (raisedEvent) {\n this.raiseSnapshotsInSyncEvent();\n }\n }\n }\n\n async unlisten(listener: QueryListener): Promise<void> {\n const query = listener.query;\n let lastListen = false;\n\n const queryInfo = this.queries.get(query);\n if (queryInfo) {\n const i = queryInfo.listeners.indexOf(listener);\n if (i >= 0) {\n queryInfo.listeners.splice(i, 1);\n lastListen = queryInfo.listeners.length === 0;\n }\n }\n\n if (lastListen) {\n this.queries.delete(query);\n return this.syncEngine.unlisten(query);\n }\n }\n\n onWatchChange(viewSnaps: ViewSnapshot[]): void {\n let raisedEvent = false;\n for (const viewSnap of viewSnaps) {\n const query = viewSnap.query;\n const queryInfo = this.queries.get(query);\n if (queryInfo) {\n for (const listener of queryInfo.listeners) {\n if (listener.onViewSnapshot(viewSnap)) {\n raisedEvent = true;\n }\n }\n queryInfo.viewSnap = viewSnap;\n }\n }\n if (raisedEvent) {\n this.raiseSnapshotsInSyncEvent();\n }\n }\n\n onWatchError(query: Query, error: Error): void {\n const queryInfo = this.queries.get(query);\n if (queryInfo) {\n for (const listener of queryInfo.listeners) {\n listener.onError(error);\n }\n }\n\n // Remove all listeners. NOTE: We don't need to call syncEngine.unlisten()\n // after an error.\n this.queries.delete(query);\n }\n\n onOnlineStateChange(onlineState: OnlineState): void {\n this.onlineState = onlineState;\n let raisedEvent = false;\n this.queries.forEach((_, queryInfo) => {\n for (const listener of queryInfo.listeners) {\n // Run global snapshot listeners if a consistent snapshot has been emitted.\n if (listener.applyOnlineStateChange(onlineState)) {\n raisedEvent = true;\n }\n }\n });\n if (raisedEvent) {\n this.raiseSnapshotsInSyncEvent();\n }\n }\n\n addSnapshotsInSyncListener(observer: Observer<void>): void {\n this.snapshotsInSyncListeners.add(observer);\n // Immediately fire an initial event, indicating all existing listeners\n // are in-sync.\n observer.next();\n }\n\n removeSnapshotsInSyncListener(observer: Observer<void>): void {\n this.snapshotsInSyncListeners.delete(observer);\n }\n\n // Call all global snapshot listeners that have been set.\n private raiseSnapshotsInSyncEvent(): void {\n this.snapshotsInSyncListeners.forEach(observer => {\n observer.next();\n });\n }\n}\n\nexport interface ListenOptions {\n /** Raise events even when only the metadata changes */\n readonly includeMetadataChanges?: boolean;\n\n /**\n * Wait for a sync with the server when online, but still raise events while\n * offline.\n */\n readonly waitForSyncWhenOnline?: boolean;\n}\n\n/**\n * QueryListener takes a series of internal view snapshots and determines\n * when to raise the event.\n *\n * It uses an Observer to dispatch events.\n */\nexport class QueryListener {\n /**\n * Initial snapshots (e.g. from cache) may not be propagated to the wrapped\n * observer. This flag is set to true once we've actually raised an event.\n */\n private raisedInitialEvent = false;\n\n private options: ListenOptions;\n\n private snap: ViewSnapshot | null = null;\n\n private onlineState = OnlineState.Unknown;\n\n constructor(\n readonly query: Query,\n private queryObserver: Observer<ViewSnapshot>,\n options?: ListenOptions\n ) {\n this.options = options || {};\n }\n\n /**\n * Applies the new ViewSnapshot to this listener, raising a user-facing event\n * if applicable (depending on what changed, whether the user has opted into\n * metadata-only changes, etc.). Returns true if a user-facing event was\n * indeed raised.\n */\n onViewSnapshot(snap: ViewSnapshot): boolean {\n debugAssert(\n snap.docChanges.length > 0 || snap.syncStateChanged,\n 'We got a new snapshot with no changes?'\n );\n\n if (!this.options.includeMetadataChanges) {\n // Remove the metadata only changes.\n const docChanges: DocumentViewChange[] = [];\n for (const docChange of snap.docChanges) {\n if (docChange.type !== ChangeType.Metadata) {\n docChanges.push(docChange);\n }\n }\n snap = new ViewSnapshot(\n snap.query,\n snap.docs,\n snap.oldDocs,\n docChanges,\n snap.mutatedKeys,\n snap.fromCache,\n snap.syncStateChanged,\n /* excludesMetadataChanges= */ true\n );\n }\n let raisedEvent = false;\n if (!this.raisedInitialEvent) {\n if (this.shouldRaiseInitialEvent(snap, this.onlineState)) {\n this.raiseInitialEvent(snap);\n raisedEvent = true;\n }\n } else if (this.shouldRaiseEvent(snap)) {\n this.queryObserver.next(snap);\n raisedEvent = true;\n }\n\n this.snap = snap;\n return raisedEvent;\n }\n\n onError(error: Error): void {\n this.queryObserver.error(error);\n }\n\n /** Returns whether a snapshot was raised. */\n applyOnlineStateChange(onlineState: OnlineState): boolean {\n this.onlineState = onlineState;\n let raisedEvent = false;\n if (\n this.snap &&\n !this.raisedInitialEvent &&\n this.shouldRaiseInitialEvent(this.snap, onlineState)\n ) {\n this.raiseInitialEvent(this.snap);\n raisedEvent = true;\n }\n return raisedEvent;\n }\n\n private shouldRaiseInitialEvent(\n snap: ViewSnapshot,\n onlineState: OnlineState\n ): boolean {\n debugAssert(\n !this.raisedInitialEvent,\n 'Determining whether to raise first event but already had first event'\n );\n\n // Always raise the first event when we're synced\n if (!snap.fromCache) {\n return true;\n }\n\n // NOTE: We consider OnlineState.Unknown as online (it should become Offline\n // or Online if we wait long enough).\n const maybeOnline = onlineState !== OnlineState.Offline;\n // Don't raise the event if we're online, aren't synced yet (checked\n // above) and are waiting for a sync.\n if (this.options.waitForSyncWhenOnline && maybeOnline) {\n debugAssert(\n snap.fromCache,\n 'Waiting for sync, but snapshot is not from cache'\n );\n return false;\n }\n\n // Raise data from cache if we have any documents or we are offline\n return !snap.docs.isEmpty() || onlineState === OnlineState.Offline;\n }\n\n private shouldRaiseEvent(snap: ViewSnapshot): boolean {\n // We don't need to handle includeDocumentMetadataChanges here because\n // the Metadata only changes have already been stripped out if needed.\n // At this point the only changes we will see are the ones we should\n // propagate.\n if (snap.docChanges.length > 0) {\n return true;\n }\n\n const hasPendingWritesChanged =\n this.snap && this.snap.hasPendingWrites !== snap.hasPendingWrites;\n if (snap.syncStateChanged || hasPendingWritesChanged) {\n return this.options.includeMetadataChanges === true;\n }\n\n // Generally we should have hit one of the cases above, but it's possible\n // to get here if there were only metadata docChanges and they got\n // stripped out.\n return false;\n }\n\n private raiseInitialEvent(snap: ViewSnapshot): void {\n debugAssert(\n !this.raisedInitialEvent,\n 'Trying to raise initial events for second time'\n );\n snap = ViewSnapshot.fromInitialDocuments(\n snap.query,\n snap.docs,\n snap.mutatedKeys,\n snap.fromCache\n );\n this.raisedInitialEvent = true;\n this.queryObserver.next(snap);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { CredentialsProvider, Token } from '../api/credentials';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { TargetId } from '../core/types';\nimport { TargetData } from '../local/target_data';\nimport { Mutation, MutationResult } from '../model/mutation';\nimport * as api from '../protos/firestore_proto_api';\nimport { hardAssert, debugAssert } from '../util/assert';\nimport { AsyncQueue, DelayedOperation, TimerId } from '../util/async_queue';\nimport { Code, FirestoreError } from '../util/error';\nimport { logError, logDebug } from '../util/log';\n\nimport { isNullOrUndefined } from '../util/types';\nimport { ExponentialBackoff } from './backoff';\nimport { Connection, Stream } from './connection';\nimport { JsonProtoSerializer } from './serializer';\nimport { WatchChange } from './watch_change';\nimport { ByteString } from '../util/byte_string';\n\nconst LOG_TAG = 'PersistentStream';\n\n// The generated proto interfaces for these class are missing the database\n// field. So we add it here.\n// TODO(b/36015800): Remove this once the api generator is fixed.\ninterface ListenRequest extends api.ListenRequest {\n database?: string;\n}\nexport interface WriteRequest extends api.WriteRequest {\n database?: string;\n}\n/**\n * PersistentStream can be in one of 5 states (each described in detail below)\n * based on the following state transition diagram:\n *\n * start() called auth & connection succeeded\n * INITIAL ----------------> STARTING -----------------------------> OPEN\n * ^ | |\n * | | error occurred |\n * | \\-----------------------------v-----/\n * | |\n * backoff | |\n * elapsed | start() called |\n * \\--- BACKOFF <---------------- ERROR\n *\n * [any state] --------------------------> INITIAL\n * stop() called or\n * idle timer expired\n */\nconst enum PersistentStreamState {\n /**\n * The streaming RPC is not yet running and there's no error condition.\n * Calling start() will start the stream immediately without backoff.\n * While in this state isStarted() will return false.\n */\n Initial,\n\n /**\n * The stream is starting, either waiting for an auth token or for the stream\n * to successfully open. While in this state, isStarted() will return true but\n * isOpen() will return false.\n */\n Starting,\n\n /**\n * The streaming RPC is up and running. Requests and responses can flow\n * freely. Both isStarted() and isOpen() will return true.\n */\n Open,\n\n /**\n * The stream encountered an error. The next start attempt will back off.\n * While in this state isStarted() will return false.\n */\n Error,\n\n /**\n * An in-between state after an error where the stream is waiting before\n * re-starting. After waiting is complete, the stream will try to open.\n * While in this state isStarted() will return true but isOpen() will return\n * false.\n */\n Backoff\n}\n\n/**\n * Provides a common interface that is shared by the listeners for stream\n * events by the concrete implementation classes.\n */\nexport interface PersistentStreamListener {\n /**\n * Called after the stream was established and can accept outgoing\n * messages\n */\n onOpen: () => Promise<void>;\n /**\n * Called after the stream has closed. If there was an error, the\n * FirestoreError will be set.\n */\n onClose: (err?: FirestoreError) => Promise<void>;\n}\n\n/** The time a stream stays open after it is marked idle. */\nconst IDLE_TIMEOUT_MS = 60 * 1000;\n\n/**\n * A PersistentStream is an abstract base class that represents a streaming RPC\n * to the Firestore backend. It's built on top of the connections own support\n * for streaming RPCs, and adds several critical features for our clients:\n *\n * - Exponential backoff on failure\n * - Authentication via CredentialsProvider\n * - Dispatching all callbacks into the shared worker queue\n * - Closing idle streams after 60 seconds of inactivity\n *\n * Subclasses of PersistentStream implement serialization of models to and\n * from the JSON representation of the protocol buffers for a specific\n * streaming RPC.\n *\n * ## Starting and Stopping\n *\n * Streaming RPCs are stateful and need to be start()ed before messages can\n * be sent and received. The PersistentStream will call the onOpen() function\n * of the listener once the stream is ready to accept requests.\n *\n * Should a start() fail, PersistentStream will call the registered onClose()\n * listener with a FirestoreError indicating what went wrong.\n *\n * A PersistentStream can be started and stopped repeatedly.\n *\n * Generic types:\n * SendType: The type of the outgoing message of the underlying\n * connection stream\n * ReceiveType: The type of the incoming message of the underlying\n * connection stream\n * ListenerType: The type of the listener that will be used for callbacks\n */\nexport abstract class PersistentStream<\n SendType,\n ReceiveType,\n ListenerType extends PersistentStreamListener\n> {\n private state = PersistentStreamState.Initial;\n /**\n * A close count that's incremented every time the stream is closed; used by\n * getCloseGuardedDispatcher() to invalidate callbacks that happen after\n * close.\n */\n private closeCount = 0;\n\n private idleTimer: DelayedOperation<void> | null = null;\n private stream: Stream<SendType, ReceiveType> | null = null;\n\n protected backoff: ExponentialBackoff;\n\n constructor(\n private queue: AsyncQueue,\n connectionTimerId: TimerId,\n private idleTimerId: TimerId,\n protected connection: Connection,\n private credentialsProvider: CredentialsProvider,\n protected listener: ListenerType\n ) {\n this.backoff = new ExponentialBackoff(queue, connectionTimerId);\n }\n\n /**\n * Returns true if start() has been called and no error has occurred. True\n * indicates the stream is open or in the process of opening (which\n * encompasses respecting backoff, getting auth tokens, and starting the\n * actual RPC). Use isOpen() to determine if the stream is open and ready for\n * outbound requests.\n */\n isStarted(): boolean {\n return (\n this.state === PersistentStreamState.Starting ||\n this.state === PersistentStreamState.Open ||\n this.state === PersistentStreamState.Backoff\n );\n }\n\n /**\n * Returns true if the underlying RPC is open (the onOpen() listener has been\n * called) and the stream is ready for outbound requests.\n */\n isOpen(): boolean {\n return this.state === PersistentStreamState.Open;\n }\n\n /**\n * Starts the RPC. Only allowed if isStarted() returns false. The stream is\n * not immediately ready for use: onOpen() will be invoked when the RPC is\n * ready for outbound requests, at which point isOpen() will return true.\n *\n * When start returns, isStarted() will return true.\n */\n start(): void {\n if (this.state === PersistentStreamState.Error) {\n this.performBackoff();\n return;\n }\n\n debugAssert(\n this.state === PersistentStreamState.Initial,\n 'Already started'\n );\n this.auth();\n }\n\n /**\n * Stops the RPC. This call is idempotent and allowed regardless of the\n * current isStarted() state.\n *\n * When stop returns, isStarted() and isOpen() will both return false.\n */\n async stop(): Promise<void> {\n if (this.isStarted()) {\n await this.close(PersistentStreamState.Initial);\n }\n }\n\n /**\n * After an error the stream will usually back off on the next attempt to\n * start it. If the error warrants an immediate restart of the stream, the\n * sender can use this to indicate that the receiver should not back off.\n *\n * Each error will call the onClose() listener. That function can decide to\n * inhibit backoff if required.\n */\n inhibitBackoff(): void {\n debugAssert(\n !this.isStarted(),\n 'Can only inhibit backoff in a stopped state'\n );\n\n this.state = PersistentStreamState.Initial;\n this.backoff.reset();\n }\n\n /**\n * Marks this stream as idle. If no further actions are performed on the\n * stream for one minute, the stream will automatically close itself and\n * notify the stream's onClose() handler with Status.OK. The stream will then\n * be in a !isStarted() state, requiring the caller to start the stream again\n * before further use.\n *\n * Only streams that are in state 'Open' can be marked idle, as all other\n * states imply pending network operations.\n */\n markIdle(): void {\n // Starts the idle time if we are in state 'Open' and are not yet already\n // running a timer (in which case the previous idle timeout still applies).\n if (this.isOpen() && this.idleTimer === null) {\n this.idleTimer = this.queue.enqueueAfterDelay(\n this.idleTimerId,\n IDLE_TIMEOUT_MS,\n () => this.handleIdleCloseTimer()\n );\n }\n }\n\n /** Sends a message to the underlying stream. */\n protected sendRequest(msg: SendType): void {\n this.cancelIdleCheck();\n this.stream!.send(msg);\n }\n\n /** Called by the idle timer when the stream should close due to inactivity. */\n private async handleIdleCloseTimer(): Promise<void> {\n if (this.isOpen()) {\n // When timing out an idle stream there's no reason to force the stream into backoff when\n // it restarts so set the stream state to Initial instead of Error.\n return this.close(PersistentStreamState.Initial);\n }\n }\n\n /** Marks the stream as active again. */\n private cancelIdleCheck(): void {\n if (this.idleTimer) {\n this.idleTimer.cancel();\n this.idleTimer = null;\n }\n }\n\n /**\n * Closes the stream and cleans up as necessary:\n *\n * * closes the underlying GRPC stream;\n * * calls the onClose handler with the given 'error';\n * * sets internal stream state to 'finalState';\n * * adjusts the backoff timer based on the error\n *\n * A new stream can be opened by calling start().\n *\n * @param finalState the intended state of the stream after closing.\n * @param error the error the connection was closed with.\n */\n private async close(\n finalState: PersistentStreamState,\n error?: FirestoreError\n ): Promise<void> {\n debugAssert(this.isStarted(), 'Only started streams should be closed.');\n debugAssert(\n finalState === PersistentStreamState.Error || isNullOrUndefined(error),\n \"Can't provide an error when not in an error state.\"\n );\n\n // Cancel any outstanding timers (they're guaranteed not to execute).\n this.cancelIdleCheck();\n this.backoff.cancel();\n\n // Invalidates any stream-related callbacks (e.g. from auth or the\n // underlying stream), guaranteeing they won't execute.\n this.closeCount++;\n\n if (finalState !== PersistentStreamState.Error) {\n // If this is an intentional close ensure we don't delay our next connection attempt.\n this.backoff.reset();\n } else if (error && error.code === Code.RESOURCE_EXHAUSTED) {\n // Log the error. (Probably either 'quota exceeded' or 'max queue length reached'.)\n logError(error.toString());\n logError(\n 'Using maximum backoff delay to prevent overloading the backend.'\n );\n this.backoff.resetToMax();\n } else if (error && error.code === Code.UNAUTHENTICATED) {\n // \"unauthenticated\" error means the token was rejected. Try force refreshing it in case it\n // just expired.\n this.credentialsProvider.invalidateToken();\n }\n\n // Clean up the underlying stream because we are no longer interested in events.\n if (this.stream !== null) {\n this.tearDown();\n this.stream.close();\n this.stream = null;\n }\n\n // This state must be assigned before calling onClose() to allow the callback to\n // inhibit backoff or otherwise manipulate the state in its non-started state.\n this.state = finalState;\n\n // Notify the listener that the stream closed.\n await this.listener.onClose(error);\n }\n\n /**\n * Can be overridden to perform additional cleanup before the stream is closed.\n * Calling super.tearDown() is not required.\n */\n protected tearDown(): void {}\n\n /**\n * Used by subclasses to start the concrete RPC and return the underlying\n * connection stream.\n */\n protected abstract startRpc(\n token: Token | null\n ): Stream<SendType, ReceiveType>;\n\n /**\n * Called after the stream has received a message. The function will be\n * called on the right queue and must return a Promise.\n * @param message The message received from the stream.\n */\n protected abstract onMessage(message: ReceiveType): Promise<void>;\n\n private auth(): void {\n debugAssert(\n this.state === PersistentStreamState.Initial,\n 'Must be in initial state to auth'\n );\n\n this.state = PersistentStreamState.Starting;\n\n const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);\n\n // TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.\n const closeCount = this.closeCount;\n\n this.credentialsProvider.getToken().then(\n token => {\n // Stream can be stopped while waiting for authentication.\n // TODO(mikelehen): We really should just use dispatchIfNotClosed\n // and let this dispatch onto the queue, but that opened a spec test can\n // of worms that I don't want to deal with in this PR.\n if (this.closeCount === closeCount) {\n // Normally we'd have to schedule the callback on the AsyncQueue.\n // However, the following calls are safe to be called outside the\n // AsyncQueue since they don't chain asynchronous calls\n this.startStream(token);\n }\n },\n (error: Error) => {\n dispatchIfNotClosed(() => {\n const rpcError = new FirestoreError(\n Code.UNKNOWN,\n 'Fetching auth token failed: ' + error.message\n );\n return this.handleStreamClose(rpcError);\n });\n }\n );\n }\n\n private startStream(token: Token | null): void {\n debugAssert(\n this.state === PersistentStreamState.Starting,\n 'Trying to start stream in a non-starting state'\n );\n\n const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);\n\n this.stream = this.startRpc(token);\n this.stream.onOpen(() => {\n dispatchIfNotClosed(() => {\n debugAssert(\n this.state === PersistentStreamState.Starting,\n 'Expected stream to be in state Starting, but was ' + this.state\n );\n this.state = PersistentStreamState.Open;\n return this.listener!.onOpen();\n });\n });\n this.stream.onClose((error?: FirestoreError) => {\n dispatchIfNotClosed(() => {\n return this.handleStreamClose(error);\n });\n });\n this.stream.onMessage((msg: ReceiveType) => {\n dispatchIfNotClosed(() => {\n return this.onMessage(msg);\n });\n });\n }\n\n private performBackoff(): void {\n debugAssert(\n this.state === PersistentStreamState.Error,\n 'Should only perform backoff when in Error state'\n );\n this.state = PersistentStreamState.Backoff;\n\n this.backoff.backoffAndRun(async () => {\n debugAssert(\n this.state === PersistentStreamState.Backoff,\n 'Backoff elapsed but state is now: ' + this.state\n );\n\n this.state = PersistentStreamState.Initial;\n this.start();\n debugAssert(this.isStarted(), 'PersistentStream should have started');\n });\n }\n\n // Visible for tests\n handleStreamClose(error?: FirestoreError): Promise<void> {\n debugAssert(\n this.isStarted(),\n \"Can't handle server close on non-started stream\"\n );\n logDebug(LOG_TAG, `close with error: ${error}`);\n\n this.stream = null;\n\n // In theory the stream could close cleanly, however, in our current model\n // we never expect this to happen because if we stop a stream ourselves,\n // this callback will never be called. To prevent cases where we retry\n // without a backoff accidentally, we set the stream to error in all cases.\n return this.close(PersistentStreamState.Error, error);\n }\n\n /**\n * Returns a \"dispatcher\" function that dispatches operations onto the\n * AsyncQueue but only runs them if closeCount remains unchanged. This allows\n * us to turn auth / stream callbacks into no-ops if the stream is closed /\n * re-opened, etc.\n */\n private getCloseGuardedDispatcher(\n startCloseCount: number\n ): (fn: () => Promise<void>) => void {\n return (fn: () => Promise<void>): void => {\n this.queue.enqueueAndForget(() => {\n if (this.closeCount === startCloseCount) {\n return fn();\n } else {\n logDebug(\n LOG_TAG,\n 'stream callback skipped by getCloseGuardedDispatcher.'\n );\n return Promise.resolve();\n }\n });\n };\n }\n}\n\n/** Listener for the PersistentWatchStream */\nexport interface WatchStreamListener extends PersistentStreamListener {\n /**\n * Called on a watchChange. The snapshot parameter will be MIN if the watch\n * change did not have a snapshot associated with it.\n */\n onWatchChange: (\n watchChange: WatchChange,\n snapshot: SnapshotVersion\n ) => Promise<void>;\n}\n\n/**\n * A PersistentStream that implements the Listen RPC.\n *\n * Once the Listen stream has called the onOpen() listener, any number of\n * listen() and unlisten() calls can be made to control what changes will be\n * sent from the server for ListenResponses.\n */\nexport class PersistentListenStream extends PersistentStream<\n api.ListenRequest,\n api.ListenResponse,\n WatchStreamListener\n> {\n constructor(\n queue: AsyncQueue,\n connection: Connection,\n credentials: CredentialsProvider,\n private serializer: JsonProtoSerializer,\n listener: WatchStreamListener\n ) {\n super(\n queue,\n TimerId.ListenStreamConnectionBackoff,\n TimerId.ListenStreamIdle,\n connection,\n credentials,\n listener\n );\n }\n\n protected startRpc(\n token: Token | null\n ): Stream<api.ListenRequest, api.ListenResponse> {\n return this.connection.openStream<api.ListenRequest, api.ListenResponse>(\n 'Listen',\n token\n );\n }\n\n protected onMessage(watchChangeProto: api.ListenResponse): Promise<void> {\n // A successful response means the stream is healthy\n this.backoff.reset();\n\n const watchChange = this.serializer.fromWatchChange(watchChangeProto);\n const snapshot = this.serializer.versionFromListenResponse(\n watchChangeProto\n );\n return this.listener!.onWatchChange(watchChange, snapshot);\n }\n\n /**\n * Registers interest in the results of the given target. If the target\n * includes a resumeToken it will be included in the request. Results that\n * affect the target will be streamed back as WatchChange messages that\n * reference the targetId.\n */\n watch(targetData: TargetData): void {\n const request: ListenRequest = {};\n request.database = this.serializer.encodedDatabaseId;\n request.addTarget = this.serializer.toTarget(targetData);\n\n const labels = this.serializer.toListenRequestLabels(targetData);\n if (labels) {\n request.labels = labels;\n }\n\n this.sendRequest(request);\n }\n\n /**\n * Unregisters interest in the results of the target associated with the\n * given targetId.\n */\n unwatch(targetId: TargetId): void {\n const request: ListenRequest = {};\n request.database = this.serializer.encodedDatabaseId;\n request.removeTarget = targetId;\n this.sendRequest(request);\n }\n}\n\n/** Listener for the PersistentWriteStream */\nexport interface WriteStreamListener extends PersistentStreamListener {\n /**\n * Called by the PersistentWriteStream upon a successful handshake response\n * from the server, which is the receiver's cue to send any pending writes.\n */\n onHandshakeComplete: () => Promise<void>;\n\n /**\n * Called by the PersistentWriteStream upon receiving a StreamingWriteResponse\n * from the server that contains a mutation result.\n */\n onMutationResult: (\n commitVersion: SnapshotVersion,\n results: MutationResult[]\n ) => Promise<void>;\n}\n\n/**\n * A Stream that implements the Write RPC.\n *\n * The Write RPC requires the caller to maintain special streamToken\n * state in between calls, to help the server understand which responses the\n * client has processed by the time the next request is made. Every response\n * will contain a streamToken; this value must be passed to the next\n * request.\n *\n * After calling start() on this stream, the next request must be a handshake,\n * containing whatever streamToken is on hand. Once a response to this\n * request is received, all pending mutations may be submitted. When\n * submitting multiple batches of mutations at the same time, it's\n * okay to use the same streamToken for the calls to writeMutations.\n *\n * TODO(b/33271235): Use proto types\n */\nexport class PersistentWriteStream extends PersistentStream<\n api.WriteRequest,\n api.WriteResponse,\n WriteStreamListener\n> {\n private handshakeComplete_ = false;\n\n constructor(\n queue: AsyncQueue,\n connection: Connection,\n credentials: CredentialsProvider,\n private serializer: JsonProtoSerializer,\n listener: WriteStreamListener\n ) {\n super(\n queue,\n TimerId.WriteStreamConnectionBackoff,\n TimerId.WriteStreamIdle,\n connection,\n credentials,\n listener\n );\n }\n\n /**\n * The last received stream token from the server, used to acknowledge which\n * responses the client has processed. Stream tokens are opaque checkpoint\n * markers whose only real value is their inclusion in the next request.\n *\n * PersistentWriteStream manages propagating this value from responses to the\n * next request.\n */\n lastStreamToken: ByteString = ByteString.EMPTY_BYTE_STRING;\n\n /**\n * Tracks whether or not a handshake has been successfully exchanged and\n * the stream is ready to accept mutations.\n */\n get handshakeComplete(): boolean {\n return this.handshakeComplete_;\n }\n\n // Override of PersistentStream.start\n start(): void {\n this.handshakeComplete_ = false;\n super.start();\n }\n\n protected tearDown(): void {\n if (this.handshakeComplete_) {\n this.writeMutations([]);\n }\n }\n\n protected startRpc(\n token: Token | null\n ): Stream<api.WriteRequest, api.WriteResponse> {\n return this.connection.openStream<api.WriteRequest, api.WriteResponse>(\n 'Write',\n token\n );\n }\n\n protected onMessage(responseProto: api.WriteResponse): Promise<void> {\n // Always capture the last stream token.\n hardAssert(\n !!responseProto.streamToken,\n 'Got a write response without a stream token'\n );\n this.lastStreamToken = this.serializer.fromBytes(responseProto.streamToken);\n\n if (!this.handshakeComplete_) {\n // The first response is always the handshake response\n hardAssert(\n !responseProto.writeResults || responseProto.writeResults.length === 0,\n 'Got mutation results for handshake'\n );\n this.handshakeComplete_ = true;\n return this.listener!.onHandshakeComplete();\n } else {\n // A successful first write response means the stream is healthy,\n // Note, that we could consider a successful handshake healthy, however,\n // the write itself might be causing an error we want to back off from.\n this.backoff.reset();\n\n const results = this.serializer.fromWriteResults(\n responseProto.writeResults,\n responseProto.commitTime\n );\n const commitVersion = this.serializer.fromVersion(\n responseProto.commitTime!\n );\n return this.listener!.onMutationResult(commitVersion, results);\n }\n }\n\n /**\n * Sends an initial streamToken to the server, performing the handshake\n * required to make the StreamingWrite RPC work. Subsequent\n * calls should wait until onHandshakeComplete was called.\n */\n writeHandshake(): void {\n debugAssert(this.isOpen(), 'Writing handshake requires an opened stream');\n debugAssert(!this.handshakeComplete_, 'Handshake already completed');\n // TODO(dimond): Support stream resumption. We intentionally do not set the\n // stream token on the handshake, ignoring any stream token we might have.\n const request: WriteRequest = {};\n request.database = this.serializer.encodedDatabaseId;\n this.sendRequest(request);\n }\n\n /** Sends a group of mutations to the Firestore backend to apply. */\n writeMutations(mutations: Mutation[]): void {\n debugAssert(this.isOpen(), 'Writing mutations requires an opened stream');\n debugAssert(\n this.handshakeComplete_,\n 'Handshake must be complete before writing mutations'\n );\n debugAssert(\n this.lastStreamToken.approximateByteSize() > 0,\n 'Trying to write mutation without a token'\n );\n\n const request: WriteRequest = {\n streamToken: this.serializer.toBytes(this.lastStreamToken),\n writes: mutations.map(mutation => this.serializer.toMutation(mutation))\n };\n\n this.sendRequest(request);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { CredentialsProvider } from '../api/credentials';\nimport { MaybeDocument, Document } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation, MutationResult } from '../model/mutation';\nimport * as api from '../protos/firestore_proto_api';\nimport { debugCast, hardAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { Connection } from './connection';\nimport { JsonProtoSerializer } from './serializer';\nimport {\n PersistentListenStream,\n PersistentWriteStream,\n WatchStreamListener,\n WriteStreamListener\n} from './persistent_stream';\nimport { AsyncQueue } from '../util/async_queue';\nimport { Query } from '../core/query';\n\n/**\n * Datastore and its related methods are a wrapper around the external Google\n * Cloud Datastore grpc API, which provides an interface that is more convenient\n * for the rest of the client SDK architecture to consume.\n */\nexport class Datastore {\n // Make sure that the structural type of `Datastore` is unique.\n // See https://github.com/microsoft/TypeScript/issues/5451\n private _ = undefined;\n}\n\n/**\n * An implementation of Datastore that exposes additional state for internal\n * consumption.\n */\nclass DatastoreImpl extends Datastore {\n constructor(\n public readonly connection: Connection,\n public readonly credentials: CredentialsProvider,\n public readonly serializer: JsonProtoSerializer\n ) {\n super();\n }\n\n /** Gets an auth token and invokes the provided RPC. */\n invokeRPC<Req, Resp>(rpcName: string, request: Req): Promise<Resp> {\n return this.credentials\n .getToken()\n .then(token => {\n return this.connection.invokeRPC<Req, Resp>(rpcName, request, token);\n })\n .catch((error: FirestoreError) => {\n if (error.code === Code.UNAUTHENTICATED) {\n this.credentials.invalidateToken();\n }\n throw error;\n });\n }\n\n /** Gets an auth token and invokes the provided RPC with streamed results. */\n invokeStreamingRPC<Req, Resp>(\n rpcName: string,\n request: Req\n ): Promise<Resp[]> {\n return this.credentials\n .getToken()\n .then(token => {\n return this.connection.invokeStreamingRPC<Req, Resp>(\n rpcName,\n request,\n token\n );\n })\n .catch((error: FirestoreError) => {\n if (error.code === Code.UNAUTHENTICATED) {\n this.credentials.invalidateToken();\n }\n throw error;\n });\n }\n}\n\nexport function newDatastore(\n connection: Connection,\n credentials: CredentialsProvider,\n serializer: JsonProtoSerializer\n): Datastore {\n return new DatastoreImpl(connection, credentials, serializer);\n}\n\nexport async function invokeCommitRpc(\n datastore: Datastore,\n mutations: Mutation[]\n): Promise<MutationResult[]> {\n const datastoreImpl = debugCast(datastore, DatastoreImpl);\n const params = {\n database: datastoreImpl.serializer.encodedDatabaseId,\n writes: mutations.map(m => datastoreImpl.serializer.toMutation(m))\n };\n const response = await datastoreImpl.invokeRPC<\n api.CommitRequest,\n api.CommitResponse\n >('Commit', params);\n return datastoreImpl.serializer.fromWriteResults(\n response.writeResults,\n response.commitTime\n );\n}\n\nexport async function invokeBatchGetDocumentsRpc(\n datastore: Datastore,\n keys: DocumentKey[]\n): Promise<MaybeDocument[]> {\n const datastoreImpl = debugCast(datastore, DatastoreImpl);\n const params = {\n database: datastoreImpl.serializer.encodedDatabaseId,\n documents: keys.map(k => datastoreImpl.serializer.toName(k))\n };\n const response = await datastoreImpl.invokeStreamingRPC<\n api.BatchGetDocumentsRequest,\n api.BatchGetDocumentsResponse\n >('BatchGetDocuments', params);\n\n const docs = new Map<string, MaybeDocument>();\n response.forEach(proto => {\n const doc = datastoreImpl.serializer.fromMaybeDocument(proto);\n docs.set(doc.key.toString(), doc);\n });\n const result: MaybeDocument[] = [];\n keys.forEach(key => {\n const doc = docs.get(key.toString());\n hardAssert(!!doc, 'Missing entity in write response for ' + key);\n result.push(doc);\n });\n return result;\n}\n\nexport async function invokeRunQueryRpc(\n datastore: Datastore,\n query: Query\n): Promise<Document[]> {\n const datastoreImpl = debugCast(datastore, DatastoreImpl);\n const { structuredQuery, parent } = datastoreImpl.serializer.toQueryTarget(\n query.toTarget()\n );\n const params = {\n database: datastoreImpl.serializer.encodedDatabaseId,\n parent,\n structuredQuery\n };\n\n const response = await datastoreImpl.invokeStreamingRPC<\n api.RunQueryRequest,\n api.RunQueryResponse\n >('RunQuery', params);\n\n return (\n response\n // Omit RunQueryResponses that only contain readTimes.\n .filter(proto => !!proto.document)\n .map(proto => datastoreImpl.serializer.fromDocument(proto.document!))\n );\n}\n\nexport function newPersistentWriteStream(\n datastore: Datastore,\n queue: AsyncQueue,\n listener: WriteStreamListener\n): PersistentWriteStream {\n const datastoreImpl = debugCast(datastore, DatastoreImpl);\n return new PersistentWriteStream(\n queue,\n datastoreImpl.connection,\n datastoreImpl.credentials,\n datastoreImpl.serializer,\n listener\n );\n}\n\nexport function newPersistentWatchStream(\n datastore: Datastore,\n queue: AsyncQueue,\n listener: WatchStreamListener\n): PersistentListenStream {\n const datastoreImpl = debugCast(datastore, DatastoreImpl);\n return new PersistentListenStream(\n queue,\n datastoreImpl.connection,\n datastoreImpl.credentials,\n datastoreImpl.serializer,\n listener\n );\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { ParsedSetData, ParsedUpdateData } from '../api/user_data_reader';\nimport { documentVersionMap } from '../model/collections';\nimport { Document, MaybeDocument, NoDocument } from '../model/document';\n\nimport { DocumentKey } from '../model/document_key';\nimport {\n DeleteMutation,\n Mutation,\n Precondition,\n VerifyMutation\n} from '../model/mutation';\nimport {\n Datastore,\n invokeBatchGetDocumentsRpc,\n invokeCommitRpc\n} from '../remote/datastore';\nimport { fail, debugAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { SnapshotVersion } from './snapshot_version';\n\n/**\n * Internal transaction object responsible for accumulating the mutations to\n * perform and the base versions for any documents read.\n */\nexport class Transaction {\n // The version of each document that was read during this transaction.\n private readVersions = documentVersionMap();\n private mutations: Mutation[] = [];\n private committed = false;\n\n /**\n * A deferred usage error that occurred previously in this transaction that\n * will cause the transaction to fail once it actually commits.\n */\n private lastWriteError: FirestoreError | null = null;\n\n /**\n * Set of documents that have been written in the transaction.\n *\n * When there's more than one write to the same key in a transaction, any\n * writes after the first are handled differently.\n */\n private writtenDocs: Set<DocumentKey> = new Set();\n\n constructor(private datastore: Datastore) {}\n\n async lookup(keys: DocumentKey[]): Promise<MaybeDocument[]> {\n this.ensureCommitNotCalled();\n\n if (this.mutations.length > 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Firestore transactions require all reads to be executed before all writes.'\n );\n }\n const docs = await invokeBatchGetDocumentsRpc(this.datastore, keys);\n docs.forEach(doc => {\n if (doc instanceof NoDocument || doc instanceof Document) {\n this.recordVersion(doc);\n } else {\n fail('Document in a transaction was a ' + doc.constructor.name);\n }\n });\n return docs;\n }\n\n set(key: DocumentKey, data: ParsedSetData): void {\n this.write(data.toMutations(key, this.precondition(key)));\n this.writtenDocs.add(key);\n }\n\n update(key: DocumentKey, data: ParsedUpdateData): void {\n try {\n this.write(data.toMutations(key, this.preconditionForUpdate(key)));\n } catch (e) {\n this.lastWriteError = e;\n }\n this.writtenDocs.add(key);\n }\n\n delete(key: DocumentKey): void {\n this.write([new DeleteMutation(key, this.precondition(key))]);\n this.writtenDocs.add(key);\n }\n\n async commit(): Promise<void> {\n this.ensureCommitNotCalled();\n\n if (this.lastWriteError) {\n throw this.lastWriteError;\n }\n let unwritten = this.readVersions;\n // For each mutation, note that the doc was written.\n this.mutations.forEach(mutation => {\n unwritten = unwritten.remove(mutation.key);\n });\n // For each document that was read but not written to, we want to perform\n // a `verify` operation.\n unwritten.forEach((key, _version) => {\n this.mutations.push(new VerifyMutation(key, this.precondition(key)));\n });\n await invokeCommitRpc(this.datastore, this.mutations);\n this.committed = true;\n }\n\n private recordVersion(doc: MaybeDocument): void {\n let docVersion: SnapshotVersion;\n\n if (doc instanceof Document) {\n docVersion = doc.version;\n } else if (doc instanceof NoDocument) {\n // For deleted docs, we must use baseVersion 0 when we overwrite them.\n docVersion = SnapshotVersion.min();\n } else {\n throw fail('Document in a transaction was a ' + doc.constructor.name);\n }\n\n const existingVersion = this.readVersions.get(doc.key);\n if (existingVersion !== null) {\n if (!docVersion.isEqual(existingVersion)) {\n // This transaction will fail no matter what.\n throw new FirestoreError(\n Code.ABORTED,\n 'Document version changed between two reads.'\n );\n }\n } else {\n this.readVersions = this.readVersions.insert(doc.key, docVersion);\n }\n }\n\n /**\n * Returns the version of this document when it was read in this transaction,\n * as a precondition, or no precondition if it was not read.\n */\n private precondition(key: DocumentKey): Precondition {\n const version = this.readVersions.get(key);\n if (!this.writtenDocs.has(key) && version) {\n return Precondition.updateTime(version);\n } else {\n return Precondition.none();\n }\n }\n\n /**\n * Returns the precondition for a document if the operation is an update.\n */\n private preconditionForUpdate(key: DocumentKey): Precondition {\n const version = this.readVersions.get(key);\n // The first time a document is written, we want to take into account the\n // read time and existence\n if (!this.writtenDocs.has(key) && version) {\n if (version.isEqual(SnapshotVersion.min())) {\n // The document doesn't exist, so fail the transaction.\n\n // This has to be validated locally because you can't send a\n // precondition that a document does not exist without changing the\n // semantics of the backend write to be an insert. This is the reverse\n // of what we want, since we want to assert that the document doesn't\n // exist but then send the update and have it fail. Since we can't\n // express that to the backend, we have to validate locally.\n\n // Note: this can change once we can send separate verify writes in the\n // transaction.\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n \"Can't update a document that doesn't exist.\"\n );\n }\n // Document exists, base precondition on document update time.\n return Precondition.updateTime(version);\n } else {\n // Document was not read, so we just use the preconditions for a blind\n // update.\n return Precondition.exists(true);\n }\n }\n\n private write(mutations: Mutation[]): void {\n this.ensureCommitNotCalled();\n this.mutations = this.mutations.concat(mutations);\n }\n\n private ensureCommitNotCalled(): void {\n debugAssert(\n !this.committed,\n 'A transaction object cannot be used after its update callback has been invoked.'\n );\n }\n}\n","/**\n * @license\n * Copyright 2018 Google LLC\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\nimport { OnlineState } from '../core/types';\nimport { debugAssert } from '../util/assert';\nimport { AsyncQueue, DelayedOperation, TimerId } from '../util/async_queue';\nimport { FirestoreError } from '../util/error';\nimport { logError, logDebug } from '../util/log';\n\nconst LOG_TAG = 'OnlineStateTracker';\n\n// To deal with transient failures, we allow multiple stream attempts before\n// giving up and transitioning from OnlineState.Unknown to Offline.\n// TODO(mikelehen): This used to be set to 2 as a mitigation for b/66228394.\n// @jdimond thinks that bug is sufficiently fixed so that we can set this back\n// to 1. If that works okay, we could potentially remove this logic entirely.\nconst MAX_WATCH_STREAM_FAILURES = 1;\n\n// To deal with stream attempts that don't succeed or fail in a timely manner,\n// we have a timeout for OnlineState to reach Online or Offline.\n// If the timeout is reached, we transition to Offline rather than waiting\n// indefinitely.\nconst ONLINE_STATE_TIMEOUT_MS = 10 * 1000;\n\n/**\n * A component used by the RemoteStore to track the OnlineState (that is,\n * whether or not the client as a whole should be considered to be online or\n * offline), implementing the appropriate heuristics.\n *\n * In particular, when the client is trying to connect to the backend, we\n * allow up to MAX_WATCH_STREAM_FAILURES within ONLINE_STATE_TIMEOUT_MS for\n * a connection to succeed. If we have too many failures or the timeout elapses,\n * then we set the OnlineState to Offline, and the client will behave as if\n * it is offline (get()s will return cached data, etc.).\n */\nexport class OnlineStateTracker {\n /** The current OnlineState. */\n private state = OnlineState.Unknown;\n\n /**\n * A count of consecutive failures to open the stream. If it reaches the\n * maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to\n * Offline.\n */\n private watchStreamFailures = 0;\n\n /**\n * A timer that elapses after ONLINE_STATE_TIMEOUT_MS, at which point we\n * transition from OnlineState.Unknown to OnlineState.Offline without waiting\n * for the stream to actually fail (MAX_WATCH_STREAM_FAILURES times).\n */\n private onlineStateTimer: DelayedOperation<void> | null = null;\n\n /**\n * Whether the client should log a warning message if it fails to connect to\n * the backend (initially true, cleared after a successful stream, or if we've\n * logged the message already).\n */\n private shouldWarnClientIsOffline = true;\n\n constructor(\n private asyncQueue: AsyncQueue,\n private onlineStateHandler: (onlineState: OnlineState) => void\n ) {}\n\n /**\n * Called by RemoteStore when a watch stream is started (including on each\n * backoff attempt).\n *\n * If this is the first attempt, it sets the OnlineState to Unknown and starts\n * the onlineStateTimer.\n */\n handleWatchStreamStart(): void {\n if (this.watchStreamFailures === 0) {\n this.setAndBroadcast(OnlineState.Unknown);\n\n debugAssert(\n this.onlineStateTimer === null,\n `onlineStateTimer shouldn't be started yet`\n );\n this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay(\n TimerId.OnlineStateTimeout,\n ONLINE_STATE_TIMEOUT_MS,\n () => {\n this.onlineStateTimer = null;\n debugAssert(\n this.state === OnlineState.Unknown,\n 'Timer should be canceled if we transitioned to a different state.'\n );\n this.logClientOfflineWarningIfNecessary(\n `Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +\n `seconds.`\n );\n this.setAndBroadcast(OnlineState.Offline);\n\n // NOTE: handleWatchStreamFailure() will continue to increment\n // watchStreamFailures even though we are already marked Offline,\n // but this is non-harmful.\n\n return Promise.resolve();\n }\n );\n }\n }\n\n /**\n * Updates our OnlineState as appropriate after the watch stream reports a\n * failure. The first failure moves us to the 'Unknown' state. We then may\n * allow multiple failures (based on MAX_WATCH_STREAM_FAILURES) before we\n * actually transition to the 'Offline' state.\n */\n handleWatchStreamFailure(error: FirestoreError): void {\n if (this.state === OnlineState.Online) {\n this.setAndBroadcast(OnlineState.Unknown);\n\n // To get to OnlineState.Online, set() must have been called which would\n // have reset our heuristics.\n debugAssert(\n this.watchStreamFailures === 0,\n 'watchStreamFailures must be 0'\n );\n debugAssert(\n this.onlineStateTimer === null,\n 'onlineStateTimer must be null'\n );\n } else {\n this.watchStreamFailures++;\n if (this.watchStreamFailures >= MAX_WATCH_STREAM_FAILURES) {\n this.clearOnlineStateTimer();\n\n this.logClientOfflineWarningIfNecessary(\n `Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +\n `times. Most recent error: ${error.toString()}`\n );\n\n this.setAndBroadcast(OnlineState.Offline);\n }\n }\n }\n\n /**\n * Explicitly sets the OnlineState to the specified state.\n *\n * Note that this resets our timers / failure counters, etc. used by our\n * Offline heuristics, so must not be used in place of\n * handleWatchStreamStart() and handleWatchStreamFailure().\n */\n set(newState: OnlineState): void {\n this.clearOnlineStateTimer();\n this.watchStreamFailures = 0;\n\n if (newState === OnlineState.Online) {\n // We've connected to watch at least once. Don't warn the developer\n // about being offline going forward.\n this.shouldWarnClientIsOffline = false;\n }\n\n this.setAndBroadcast(newState);\n }\n\n private setAndBroadcast(newState: OnlineState): void {\n if (newState !== this.state) {\n this.state = newState;\n this.onlineStateHandler(newState);\n }\n }\n\n private logClientOfflineWarningIfNecessary(details: string): void {\n const message =\n `Could not reach Cloud Firestore backend. ${details}\\n` +\n `This typically indicates that your device does not have a healthy ` +\n `Internet connection at the moment. The client will operate in offline ` +\n `mode until it is able to successfully connect to the backend.`;\n if (this.shouldWarnClientIsOffline) {\n logError(message);\n this.shouldWarnClientIsOffline = false;\n } else {\n logDebug(LOG_TAG, message);\n }\n }\n\n private clearOnlineStateTimer(): void {\n if (this.onlineStateTimer !== null) {\n this.onlineStateTimer.cancel();\n this.onlineStateTimer = null;\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { Transaction } from '../core/transaction';\nimport { OnlineState, TargetId } from '../core/types';\nimport { ignoreIfPrimaryLeaseLoss, LocalStore } from '../local/local_store';\nimport { TargetData, TargetPurpose } from '../local/target_data';\nimport { MutationResult } from '../model/mutation';\nimport {\n BATCHID_UNKNOWN,\n MutationBatch,\n MutationBatchResult\n} from '../model/mutation_batch';\nimport { debugAssert } from '../util/assert';\nimport { FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { DocumentKeySet } from '../model/collections';\nimport { AsyncQueue } from '../util/async_queue';\nimport { ConnectivityMonitor, NetworkStatus } from './connectivity_monitor';\nimport {\n Datastore,\n newPersistentWatchStream,\n newPersistentWriteStream\n} from './datastore';\nimport { OnlineStateTracker } from './online_state_tracker';\nimport {\n PersistentListenStream,\n PersistentWriteStream\n} from './persistent_stream';\nimport { RemoteSyncer } from './remote_syncer';\nimport { isPermanentError, isPermanentWriteError } from './rpc_error';\nimport {\n DocumentWatchChange,\n ExistenceFilterChange,\n TargetMetadataProvider,\n WatchChange,\n WatchChangeAggregator,\n WatchTargetChange,\n WatchTargetChangeState\n} from './watch_change';\nimport { ByteString } from '../util/byte_string';\nimport { isIndexedDbTransactionError } from '../local/simple_db';\n\nconst LOG_TAG = 'RemoteStore';\n\n// TODO(b/35853402): Negotiate this with the stream.\nconst MAX_PENDING_WRITES = 10;\n\n/**\n * RemoteStore - An interface to remotely stored data, basically providing a\n * wrapper around the Datastore that is more reliable for the rest of the\n * system.\n *\n * RemoteStore is responsible for maintaining the connection to the server.\n * - maintaining a list of active listens.\n * - reconnecting when the connection is dropped.\n * - resuming all the active listens on reconnect.\n *\n * RemoteStore handles all incoming events from the Datastore.\n * - listening to the watch stream and repackaging the events as RemoteEvents\n * - notifying SyncEngine of any changes to the active listens.\n *\n * RemoteStore takes writes from other components and handles them reliably.\n * - pulling pending mutations from LocalStore and sending them to Datastore.\n * - retrying mutations that failed because of network problems.\n * - acking mutations to the SyncEngine once they are accepted or rejected.\n */\nexport class RemoteStore implements TargetMetadataProvider {\n /**\n * A list of up to MAX_PENDING_WRITES writes that we have fetched from the\n * LocalStore via fillWritePipeline() and have or will send to the write\n * stream.\n *\n * Whenever writePipeline.length > 0 the RemoteStore will attempt to start or\n * restart the write stream. When the stream is established the writes in the\n * pipeline will be sent in order.\n *\n * Writes remain in writePipeline until they are acknowledged by the backend\n * and thus will automatically be re-sent if the stream is interrupted /\n * restarted before they're acknowledged.\n *\n * Write responses from the backend are linked to their originating request\n * purely based on order, and so we can just shift() writes from the front of\n * the writePipeline as we receive responses.\n */\n private writePipeline: MutationBatch[] = [];\n\n /**\n * A mapping of watched targets that the client cares about tracking and the\n * user has explicitly called a 'listen' for this target.\n *\n * These targets may or may not have been sent to or acknowledged by the\n * server. On re-establishing the listen stream, these targets should be sent\n * to the server. The targets removed with unlistens are removed eagerly\n * without waiting for confirmation from the listen stream.\n */\n private listenTargets = new Map<TargetId, TargetData>();\n\n private connectivityMonitor: ConnectivityMonitor;\n private watchStream: PersistentListenStream;\n private writeStream: PersistentWriteStream;\n private watchChangeAggregator: WatchChangeAggregator | null = null;\n\n /**\n * Set to true by enableNetwork() and false by disableNetwork() and indicates\n * the user-preferred network state.\n */\n private networkEnabled = false;\n\n private isPrimary = false;\n\n /**\n * When set to `true`, the network was taken offline due to an IndexedDB\n * failure. The state is flipped to `false` when access becomes available\n * again.\n */\n private indexedDbFailed = false;\n\n private onlineStateTracker: OnlineStateTracker;\n\n constructor(\n /**\n * The local store, used to fill the write pipeline with outbound mutations.\n */\n private localStore: LocalStore,\n /** The client-side proxy for interacting with the backend. */\n private datastore: Datastore,\n private asyncQueue: AsyncQueue,\n onlineStateHandler: (onlineState: OnlineState) => void,\n connectivityMonitor: ConnectivityMonitor\n ) {\n this.connectivityMonitor = connectivityMonitor;\n this.connectivityMonitor.addCallback((status: NetworkStatus) => {\n asyncQueue.enqueueAndForget(async () => {\n if (this.canUseNetwork()) {\n logDebug(\n LOG_TAG,\n 'Restarting streams for network reachability change.'\n );\n await this.restartNetwork();\n }\n });\n });\n\n this.onlineStateTracker = new OnlineStateTracker(\n asyncQueue,\n onlineStateHandler\n );\n\n // Create streams (but note they're not started yet).\n this.watchStream = newPersistentWatchStream(this.datastore, asyncQueue, {\n onOpen: this.onWatchStreamOpen.bind(this),\n onClose: this.onWatchStreamClose.bind(this),\n onWatchChange: this.onWatchStreamChange.bind(this)\n });\n\n this.writeStream = newPersistentWriteStream(this.datastore, asyncQueue, {\n onOpen: this.onWriteStreamOpen.bind(this),\n onClose: this.onWriteStreamClose.bind(this),\n onHandshakeComplete: this.onWriteHandshakeComplete.bind(this),\n onMutationResult: this.onMutationResult.bind(this)\n });\n }\n\n /**\n * SyncEngine to notify of watch and write events. This must be set\n * immediately after construction.\n */\n syncEngine!: RemoteSyncer;\n\n /**\n * Starts up the remote store, creating streams, restoring state from\n * LocalStore, etc.\n */\n start(): Promise<void> {\n return this.enableNetwork();\n }\n\n /** Re-enables the network. Idempotent. */\n enableNetwork(): Promise<void> {\n this.networkEnabled = true;\n return this.enableNetworkInternal();\n }\n\n private async enableNetworkInternal(): Promise<void> {\n if (this.canUseNetwork()) {\n this.writeStream.lastStreamToken = await this.localStore.getLastStreamToken();\n\n if (this.shouldStartWatchStream()) {\n this.startWatchStream();\n } else {\n this.onlineStateTracker.set(OnlineState.Unknown);\n }\n\n // This will start the write stream if necessary.\n await this.fillWritePipeline();\n }\n }\n\n /**\n * Temporarily disables the network. The network can be re-enabled using\n * enableNetwork().\n */\n async disableNetwork(): Promise<void> {\n this.networkEnabled = false;\n await this.disableNetworkInternal();\n\n // Set the OnlineState to Offline so get()s return from cache, etc.\n this.onlineStateTracker.set(OnlineState.Offline);\n }\n\n private async disableNetworkInternal(): Promise<void> {\n await this.writeStream.stop();\n await this.watchStream.stop();\n\n if (this.writePipeline.length > 0) {\n logDebug(\n LOG_TAG,\n `Stopping write stream with ${this.writePipeline.length} pending writes`\n );\n this.writePipeline = [];\n }\n\n this.cleanUpWatchStreamState();\n }\n\n async shutdown(): Promise<void> {\n logDebug(LOG_TAG, 'RemoteStore shutting down.');\n this.networkEnabled = false;\n await this.disableNetworkInternal();\n this.connectivityMonitor.shutdown();\n\n // Set the OnlineState to Unknown (rather than Offline) to avoid potentially\n // triggering spurious listener events with cached data, etc.\n this.onlineStateTracker.set(OnlineState.Unknown);\n }\n\n /**\n * Starts new listen for the given target. Uses resume token if provided. It\n * is a no-op if the target of given `TargetData` is already being listened to.\n */\n listen(targetData: TargetData): void {\n if (this.listenTargets.has(targetData.targetId)) {\n return;\n }\n\n // Mark this as something the client is currently listening for.\n this.listenTargets.set(targetData.targetId, targetData);\n\n if (this.shouldStartWatchStream()) {\n // The listen will be sent in onWatchStreamOpen\n this.startWatchStream();\n } else if (this.watchStream.isOpen()) {\n this.sendWatchRequest(targetData);\n }\n }\n\n /**\n * Removes the listen from server. It is a no-op if the given target id is\n * not being listened to.\n */\n unlisten(targetId: TargetId): void {\n debugAssert(\n this.listenTargets.has(targetId),\n `unlisten called on target no currently watched: ${targetId}`\n );\n\n this.listenTargets.delete(targetId);\n if (this.watchStream.isOpen()) {\n this.sendUnwatchRequest(targetId);\n }\n\n if (this.listenTargets.size === 0) {\n if (this.watchStream.isOpen()) {\n this.watchStream.markIdle();\n } else if (this.canUseNetwork()) {\n // Revert to OnlineState.Unknown if the watch stream is not open and we\n // have no listeners, since without any listens to send we cannot\n // confirm if the stream is healthy and upgrade to OnlineState.Online.\n this.onlineStateTracker.set(OnlineState.Unknown);\n }\n }\n }\n\n /** {@link TargetMetadataProvider.getTargetDataForTarget} */\n getTargetDataForTarget(targetId: TargetId): TargetData | null {\n return this.listenTargets.get(targetId) || null;\n }\n\n /** {@link TargetMetadataProvider.getRemoteKeysForTarget} */\n getRemoteKeysForTarget(targetId: TargetId): DocumentKeySet {\n return this.syncEngine.getRemoteKeysForTarget(targetId);\n }\n\n /**\n * We need to increment the the expected number of pending responses we're due\n * from watch so we wait for the ack to process any messages from this target.\n */\n private sendWatchRequest(targetData: TargetData): void {\n this.watchChangeAggregator!.recordPendingTargetRequest(targetData.targetId);\n this.watchStream.watch(targetData);\n }\n\n /**\n * We need to increment the expected number of pending responses we're due\n * from watch so we wait for the removal on the server before we process any\n * messages from this target.\n */\n private sendUnwatchRequest(targetId: TargetId): void {\n this.watchChangeAggregator!.recordPendingTargetRequest(targetId);\n this.watchStream.unwatch(targetId);\n }\n\n private startWatchStream(): void {\n debugAssert(\n this.shouldStartWatchStream(),\n 'startWatchStream() called when shouldStartWatchStream() is false.'\n );\n\n this.watchChangeAggregator = new WatchChangeAggregator(this);\n this.watchStream.start();\n this.onlineStateTracker.handleWatchStreamStart();\n }\n\n /**\n * Returns whether the watch stream should be started because it's necessary\n * and has not yet been started.\n */\n private shouldStartWatchStream(): boolean {\n return (\n this.canUseNetwork() &&\n !this.watchStream.isStarted() &&\n this.listenTargets.size > 0\n );\n }\n\n canUseNetwork(): boolean {\n return !this.indexedDbFailed && this.isPrimary && this.networkEnabled;\n }\n\n private cleanUpWatchStreamState(): void {\n this.watchChangeAggregator = null;\n }\n\n private async onWatchStreamOpen(): Promise<void> {\n this.listenTargets.forEach((targetData, targetId) => {\n this.sendWatchRequest(targetData);\n });\n }\n\n private async onWatchStreamClose(error?: FirestoreError): Promise<void> {\n if (error === undefined) {\n // Graceful stop (due to stop() or idle timeout). Make sure that's\n // desirable.\n debugAssert(\n !this.shouldStartWatchStream(),\n 'Watch stream was stopped gracefully while still needed.'\n );\n }\n\n this.cleanUpWatchStreamState();\n\n // If we still need the watch stream, retry the connection.\n if (this.shouldStartWatchStream()) {\n this.onlineStateTracker.handleWatchStreamFailure(error!);\n\n this.startWatchStream();\n } else {\n // No need to restart watch stream because there are no active targets.\n // The online state is set to unknown because there is no active attempt\n // at establishing a connection\n this.onlineStateTracker.set(OnlineState.Unknown);\n }\n }\n\n private async onWatchStreamChange(\n watchChange: WatchChange,\n snapshotVersion: SnapshotVersion\n ): Promise<void> {\n // Mark the client as online since we got a message from the server\n this.onlineStateTracker.set(OnlineState.Online);\n\n if (\n watchChange instanceof WatchTargetChange &&\n watchChange.state === WatchTargetChangeState.Removed &&\n watchChange.cause\n ) {\n // There was an error on a target, don't wait for a consistent snapshot\n // to raise events\n try {\n await this.handleTargetError(watchChange);\n } catch (e) {\n logDebug(\n LOG_TAG,\n 'Failed to remove targets %s: %s ',\n watchChange.targetIds.join(','),\n e\n );\n await this.disableNetworkUntilRecovery(e);\n }\n return;\n }\n\n if (watchChange instanceof DocumentWatchChange) {\n this.watchChangeAggregator!.handleDocumentChange(watchChange);\n } else if (watchChange instanceof ExistenceFilterChange) {\n this.watchChangeAggregator!.handleExistenceFilter(watchChange);\n } else {\n debugAssert(\n watchChange instanceof WatchTargetChange,\n 'Expected watchChange to be an instance of WatchTargetChange'\n );\n this.watchChangeAggregator!.handleTargetChange(watchChange);\n }\n\n if (!snapshotVersion.isEqual(SnapshotVersion.min())) {\n try {\n const lastRemoteSnapshotVersion = await this.localStore.getLastRemoteSnapshotVersion();\n if (snapshotVersion.compareTo(lastRemoteSnapshotVersion) >= 0) {\n // We have received a target change with a global snapshot if the snapshot\n // version is not equal to SnapshotVersion.min().\n await this.raiseWatchSnapshot(snapshotVersion);\n }\n } catch (e) {\n logDebug(LOG_TAG, 'Failed to raise snapshot:', e);\n await this.disableNetworkUntilRecovery(e);\n }\n }\n }\n\n /**\n * Recovery logic for IndexedDB errors that takes the network offline until\n * IndexedDb probing succeeds. Retries are scheduled with backoff using\n * `enqueueRetryable()`.\n */\n private async disableNetworkUntilRecovery(e: FirestoreError): Promise<void> {\n if (isIndexedDbTransactionError(e)) {\n debugAssert(\n !this.indexedDbFailed,\n 'Unexpected network event when IndexedDB was marked failed.'\n );\n this.indexedDbFailed = true;\n\n // Disable network and raise offline snapshots\n await this.disableNetworkInternal();\n this.onlineStateTracker.set(OnlineState.Offline);\n\n // Probe IndexedDB periodically and re-enable network\n this.asyncQueue.enqueueRetryable(async () => {\n logDebug(LOG_TAG, 'Retrying IndexedDB access');\n // Issue a simple read operation to determine if IndexedDB recovered.\n // Ideally, we would expose a health check directly on SimpleDb, but\n // RemoteStore only has access to persistence through LocalStore.\n await this.localStore.getLastRemoteSnapshotVersion();\n this.indexedDbFailed = false;\n await this.enableNetworkInternal();\n });\n } else {\n throw e;\n }\n }\n\n /**\n * Takes a batch of changes from the Datastore, repackages them as a\n * RemoteEvent, and passes that on to the listener, which is typically the\n * SyncEngine.\n */\n private raiseWatchSnapshot(snapshotVersion: SnapshotVersion): Promise<void> {\n debugAssert(\n !snapshotVersion.isEqual(SnapshotVersion.min()),\n \"Can't raise event for unknown SnapshotVersion\"\n );\n const remoteEvent = this.watchChangeAggregator!.createRemoteEvent(\n snapshotVersion\n );\n\n // Update in-memory resume tokens. LocalStore will update the\n // persistent view of these when applying the completed RemoteEvent.\n remoteEvent.targetChanges.forEach((change, targetId) => {\n if (change.resumeToken.approximateByteSize() > 0) {\n const targetData = this.listenTargets.get(targetId);\n // A watched target might have been removed already.\n if (targetData) {\n this.listenTargets.set(\n targetId,\n targetData.withResumeToken(change.resumeToken, snapshotVersion)\n );\n }\n }\n });\n\n // Re-establish listens for the targets that have been invalidated by\n // existence filter mismatches.\n remoteEvent.targetMismatches.forEach(targetId => {\n const targetData = this.listenTargets.get(targetId);\n if (!targetData) {\n // A watched target might have been removed already.\n return;\n }\n\n // Clear the resume token for the target, since we're in a known mismatch\n // state.\n this.listenTargets.set(\n targetId,\n targetData.withResumeToken(\n ByteString.EMPTY_BYTE_STRING,\n targetData.snapshotVersion\n )\n );\n\n // Cause a hard reset by unwatching and rewatching immediately, but\n // deliberately don't send a resume token so that we get a full update.\n this.sendUnwatchRequest(targetId);\n\n // Mark the target we send as being on behalf of an existence filter\n // mismatch, but don't actually retain that in listenTargets. This ensures\n // that we flag the first re-listen this way without impacting future\n // listens of this target (that might happen e.g. on reconnect).\n const requestTargetData = new TargetData(\n targetData.target,\n targetId,\n TargetPurpose.ExistenceFilterMismatch,\n targetData.sequenceNumber\n );\n this.sendWatchRequest(requestTargetData);\n });\n\n // Finally raise remote event\n return this.syncEngine.applyRemoteEvent(remoteEvent);\n }\n\n /** Handles an error on a target */\n private async handleTargetError(\n watchChange: WatchTargetChange\n ): Promise<void> {\n debugAssert(!!watchChange.cause, 'Handling target error without a cause');\n const error = watchChange.cause!;\n for (const targetId of watchChange.targetIds) {\n // A watched target might have been removed already.\n if (this.listenTargets.has(targetId)) {\n await this.syncEngine.rejectListen(targetId, error);\n this.listenTargets.delete(targetId);\n this.watchChangeAggregator!.removeTarget(targetId);\n }\n }\n }\n\n /**\n * Attempts to fill our write pipeline with writes from the LocalStore.\n *\n * Called internally to bootstrap or refill the write pipeline and by\n * SyncEngine whenever there are new mutations to process.\n *\n * Starts the write stream if necessary.\n */\n async fillWritePipeline(): Promise<void> {\n if (this.canAddToWritePipeline()) {\n const lastBatchIdRetrieved =\n this.writePipeline.length > 0\n ? this.writePipeline[this.writePipeline.length - 1].batchId\n : BATCHID_UNKNOWN;\n const batch = await this.localStore.nextMutationBatch(\n lastBatchIdRetrieved\n );\n\n if (batch === null) {\n if (this.writePipeline.length === 0) {\n this.writeStream.markIdle();\n }\n } else {\n this.addToWritePipeline(batch);\n await this.fillWritePipeline();\n }\n }\n\n if (this.shouldStartWriteStream()) {\n this.startWriteStream();\n }\n }\n\n /**\n * Returns true if we can add to the write pipeline (i.e. the network is\n * enabled and the write pipeline is not full).\n */\n private canAddToWritePipeline(): boolean {\n return (\n this.canUseNetwork() && this.writePipeline.length < MAX_PENDING_WRITES\n );\n }\n\n // For testing\n outstandingWrites(): number {\n return this.writePipeline.length;\n }\n\n /**\n * Queues additional writes to be sent to the write stream, sending them\n * immediately if the write stream is established.\n */\n private addToWritePipeline(batch: MutationBatch): void {\n debugAssert(\n this.canAddToWritePipeline(),\n 'addToWritePipeline called when pipeline is full'\n );\n this.writePipeline.push(batch);\n\n if (this.writeStream.isOpen() && this.writeStream.handshakeComplete) {\n this.writeStream.writeMutations(batch.mutations);\n }\n }\n\n private shouldStartWriteStream(): boolean {\n return (\n this.canUseNetwork() &&\n !this.writeStream.isStarted() &&\n this.writePipeline.length > 0\n );\n }\n\n private startWriteStream(): void {\n debugAssert(\n this.shouldStartWriteStream(),\n 'startWriteStream() called when shouldStartWriteStream() is false.'\n );\n this.writeStream.start();\n }\n\n private async onWriteStreamOpen(): Promise<void> {\n this.writeStream.writeHandshake();\n }\n\n private onWriteHandshakeComplete(): Promise<void> {\n // Record the stream token.\n return this.localStore\n .setLastStreamToken(this.writeStream.lastStreamToken)\n .then(() => {\n // Send the write pipeline now that the stream is established.\n for (const batch of this.writePipeline) {\n this.writeStream.writeMutations(batch.mutations);\n }\n })\n .catch(ignoreIfPrimaryLeaseLoss);\n }\n\n private onMutationResult(\n commitVersion: SnapshotVersion,\n results: MutationResult[]\n ): Promise<void> {\n // This is a response to a write containing mutations and should be\n // correlated to the first write in our write pipeline.\n debugAssert(\n this.writePipeline.length > 0,\n 'Got result for empty write pipeline'\n );\n const batch = this.writePipeline.shift()!;\n const success = MutationBatchResult.from(\n batch,\n commitVersion,\n results,\n this.writeStream.lastStreamToken\n );\n return this.syncEngine.applySuccessfulWrite(success).then(() => {\n // It's possible that with the completion of this mutation another\n // slot has freed up.\n return this.fillWritePipeline();\n });\n }\n\n private async onWriteStreamClose(error?: FirestoreError): Promise<void> {\n if (error === undefined) {\n // Graceful stop (due to stop() or idle timeout). Make sure that's\n // desirable.\n debugAssert(\n !this.shouldStartWriteStream(),\n 'Write stream was stopped gracefully while still needed.'\n );\n }\n\n // If the write stream closed due to an error, invoke the error callbacks if\n // there are pending writes.\n if (error && this.writePipeline.length > 0) {\n if (this.writeStream.handshakeComplete) {\n // This error affects the actual write.\n await this.handleWriteError(error!);\n } else {\n // If there was an error before the handshake has finished, it's\n // possible that the server is unable to process the stream token\n // we're sending. (Perhaps it's too old?)\n await this.handleHandshakeError(error!);\n }\n\n // The write stream might have been started by refilling the write\n // pipeline for failed writes\n if (this.shouldStartWriteStream()) {\n this.startWriteStream();\n }\n }\n // No pending writes, nothing to do\n }\n\n private async handleHandshakeError(error: FirestoreError): Promise<void> {\n // Reset the token if it's a permanent error, signaling the write stream is\n // no longer valid. Note that the handshake does not count as a write: see\n // comments on isPermanentWriteError for details.\n if (isPermanentError(error.code)) {\n logDebug(\n LOG_TAG,\n 'RemoteStore error before completed handshake; resetting stream token: ',\n this.writeStream.lastStreamToken\n );\n this.writeStream.lastStreamToken = ByteString.EMPTY_BYTE_STRING;\n\n return this.localStore\n .setLastStreamToken(ByteString.EMPTY_BYTE_STRING)\n .catch(ignoreIfPrimaryLeaseLoss);\n } else {\n // Some other error, don't reset stream token. Our stream logic will\n // just retry with exponential backoff.\n }\n }\n\n private async handleWriteError(error: FirestoreError): Promise<void> {\n // Only handle permanent errors here. If it's transient, just let the retry\n // logic kick in.\n if (isPermanentWriteError(error.code)) {\n // This was a permanent error, the request itself was the problem\n // so it's not going to succeed if we resend it.\n const batch = this.writePipeline.shift()!;\n\n // In this case it's also unlikely that the server itself is melting\n // down -- this was just a bad request so inhibit backoff on the next\n // restart.\n this.writeStream.inhibitBackoff();\n\n return this.syncEngine\n .rejectFailedWrite(batch.batchId, error)\n .then(() => {\n // It's possible that with the completion of this mutation\n // another slot has freed up.\n return this.fillWritePipeline();\n });\n } else {\n // Transient error, just let the retry logic kick in.\n }\n }\n\n createTransaction(): Transaction {\n return new Transaction(this.datastore);\n }\n\n private async restartNetwork(): Promise<void> {\n this.networkEnabled = false;\n await this.disableNetworkInternal();\n this.onlineStateTracker.set(OnlineState.Unknown);\n await this.enableNetwork();\n }\n\n async handleCredentialChange(): Promise<void> {\n if (this.canUseNetwork()) {\n // Tear down and re-create our network streams. This will ensure we get a fresh auth token\n // for the new user and re-fill the write pipeline with new mutations from the LocalStore\n // (since mutations are per-user).\n logDebug(LOG_TAG, 'RemoteStore restarting streams for new credential');\n await this.restartNetwork();\n }\n }\n\n /**\n * Toggles the network state when the client gains or loses its primary lease.\n */\n async applyPrimaryState(isPrimary: boolean): Promise<void> {\n this.isPrimary = isPrimary;\n\n if (isPrimary && this.networkEnabled) {\n await this.enableNetwork();\n } else if (!isPrimary) {\n await this.disableNetworkInternal();\n this.onlineStateTracker.set(OnlineState.Unknown);\n }\n }\n}\n","/**\n * @license\n * Copyright 2018 Google LLC\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\nimport { User } from '../auth/user';\nimport { ListenSequence } from '../core/listen_sequence';\nimport {\n BatchId,\n ListenSequenceNumber,\n MutationBatchState,\n OnlineState,\n TargetId\n} from '../core/types';\nimport { TargetIdSet, targetIdSet } from '../model/collections';\nimport { Platform } from '../platform/platform';\nimport { hardAssert, debugAssert } from '../util/assert';\nimport { AsyncQueue } from '../util/async_queue';\nimport { Code, FirestoreError } from '../util/error';\nimport { logError, logDebug } from '../util/log';\nimport { SortedSet } from '../util/sorted_set';\nimport { SortedMap } from '../util/sorted_map';\nimport { primitiveComparator } from '../util/misc';\nimport { isSafeInteger } from '../util/types';\nimport {\n QueryTargetState,\n SharedClientStateSyncer\n} from './shared_client_state_syncer';\nimport {\n CLIENT_STATE_KEY_PREFIX,\n ClientStateSchema,\n createWebStorageClientStateKey,\n createWebStorageMutationBatchKey,\n createWebStorageOnlineStateKey,\n createWebStorageQueryTargetMetadataKey,\n createWebStorageSequenceNumberKey,\n MUTATION_BATCH_KEY_PREFIX,\n MutationMetadataSchema,\n QUERY_TARGET_KEY_PREFIX,\n QueryTargetStateSchema,\n SharedOnlineStateSchema\n} from './shared_client_state_schema';\n\nconst LOG_TAG = 'SharedClientState';\n\n/**\n * A randomly-generated key assigned to each Firestore instance at startup.\n */\nexport type ClientId = string;\n\n/**\n * A `SharedClientState` keeps track of the global state of the mutations\n * and query targets for all active clients with the same persistence key (i.e.\n * project ID and FirebaseApp name). It relays local changes to other clients\n * and updates its local state as new state is observed.\n *\n * `SharedClientState` is primarily used for synchronization in Multi-Tab\n * environments. Each tab is responsible for registering its active query\n * targets and mutations. `SharedClientState` will then notify the listener\n * assigned to `.syncEngine` for updates to mutations and queries that\n * originated in other clients.\n *\n * To receive notifications, `.syncEngine` and `.onlineStateHandler` has to be\n * assigned before calling `start()`.\n */\nexport interface SharedClientState {\n syncEngine: SharedClientStateSyncer | null;\n onlineStateHandler: ((onlineState: OnlineState) => void) | null;\n sequenceNumberHandler:\n | ((sequenceNumber: ListenSequenceNumber) => void)\n | null;\n\n /** Registers the Mutation Batch ID of a newly pending mutation. */\n addPendingMutation(batchId: BatchId): void;\n\n /**\n * Records that a pending mutation has been acknowledged or rejected.\n * Called by the primary client to notify secondary clients of mutation\n * results as they come back from the backend.\n */\n updateMutationState(\n batchId: BatchId,\n state: 'acknowledged' | 'rejected',\n error?: FirestoreError\n ): void;\n\n /**\n * Associates a new Query Target ID with the local Firestore client. Returns\n * the new query state for the query (which can be 'current' if the query is\n * already associated with another tab).\n *\n * If the target id is already associated with local client, the method simply\n * returns its `QueryTargetState`.\n */\n addLocalQueryTarget(targetId: TargetId): QueryTargetState;\n\n /** Removes the Query Target ID association from the local client. */\n removeLocalQueryTarget(targetId: TargetId): void;\n\n /** Checks whether the target is associated with the local client. */\n isLocalQueryTarget(targetId: TargetId): boolean;\n\n /**\n * Processes an update to a query target.\n *\n * Called by the primary client to notify secondary clients of document\n * changes or state transitions that affect the provided query target.\n */\n updateQueryState(\n targetId: TargetId,\n state: QueryTargetState,\n error?: FirestoreError\n ): void;\n\n /**\n * Removes the target's metadata entry.\n *\n * Called by the primary client when all clients stopped listening to a query\n * target.\n */\n clearQueryState(targetId: TargetId): void;\n\n /**\n * Gets the active Query Targets IDs for all active clients.\n *\n * The implementation for this may require O(n) runtime, where 'n' is the size\n * of the result set.\n */\n // Visible for testing\n getAllActiveQueryTargets(): SortedSet<TargetId>;\n\n /**\n * Checks whether the provided target ID is currently being listened to by\n * any of the active clients.\n *\n * The implementation may require O(n*log m) runtime, where 'n' is the number\n * of clients and 'm' the number of targets.\n */\n isActiveQueryTarget(targetId: TargetId): boolean;\n\n /**\n * Starts the SharedClientState, reads existing client data and registers\n * listeners for updates to new and existing clients.\n */\n start(): Promise<void>;\n\n /** Shuts down the `SharedClientState` and its listeners. */\n shutdown(): void;\n\n /**\n * Changes the active user and removes all existing user-specific data. The\n * user change does not call back into SyncEngine (for example, no mutations\n * will be marked as removed).\n */\n handleUserChange(\n user: User,\n removedBatchIds: BatchId[],\n addedBatchIds: BatchId[]\n ): void;\n\n /** Changes the shared online state of all clients. */\n setOnlineState(onlineState: OnlineState): void;\n\n writeSequenceNumber(sequenceNumber: ListenSequenceNumber): void;\n}\n\n/**\n * Holds the state of a mutation batch, including its user ID, batch ID and\n * whether the batch is 'pending', 'acknowledged' or 'rejected'.\n */\n// Visible for testing\nexport class MutationMetadata {\n constructor(\n readonly user: User,\n readonly batchId: BatchId,\n readonly state: MutationBatchState,\n readonly error?: FirestoreError\n ) {\n debugAssert(\n (error !== undefined) === (state === 'rejected'),\n `MutationMetadata must contain an error iff state is 'rejected'`\n );\n }\n\n /**\n * Parses a MutationMetadata from its JSON representation in WebStorage.\n * Logs a warning and returns null if the format of the data is not valid.\n */\n static fromWebStorageEntry(\n user: User,\n batchId: BatchId,\n value: string\n ): MutationMetadata | null {\n const mutationBatch = JSON.parse(value) as MutationMetadataSchema;\n\n let validData =\n typeof mutationBatch === 'object' &&\n ['pending', 'acknowledged', 'rejected'].indexOf(mutationBatch.state) !==\n -1 &&\n (mutationBatch.error === undefined ||\n typeof mutationBatch.error === 'object');\n\n let firestoreError: FirestoreError | undefined = undefined;\n\n if (validData && mutationBatch.error) {\n validData =\n typeof mutationBatch.error.message === 'string' &&\n typeof mutationBatch.error.code === 'string';\n if (validData) {\n firestoreError = new FirestoreError(\n mutationBatch.error.code as Code,\n mutationBatch.error.message\n );\n }\n }\n\n if (validData) {\n return new MutationMetadata(\n user,\n batchId,\n mutationBatch.state,\n firestoreError\n );\n } else {\n logError(\n LOG_TAG,\n `Failed to parse mutation state for ID '${batchId}': ${value}`\n );\n return null;\n }\n }\n\n toWebStorageJSON(): string {\n const batchMetadata: MutationMetadataSchema = {\n state: this.state,\n updateTimeMs: Date.now() // Modify the existing value to trigger update.\n };\n\n if (this.error) {\n batchMetadata.error = {\n code: this.error.code,\n message: this.error.message\n };\n }\n\n return JSON.stringify(batchMetadata);\n }\n}\n\n/**\n * Holds the state of a query target, including its target ID and whether the\n * target is 'not-current', 'current' or 'rejected'.\n */\n// Visible for testing\nexport class QueryTargetMetadata {\n constructor(\n readonly targetId: TargetId,\n readonly state: QueryTargetState,\n readonly error?: FirestoreError\n ) {\n debugAssert(\n (error !== undefined) === (state === 'rejected'),\n `QueryTargetMetadata must contain an error iff state is 'rejected'`\n );\n }\n\n /**\n * Parses a QueryTargetMetadata from its JSON representation in WebStorage.\n * Logs a warning and returns null if the format of the data is not valid.\n */\n static fromWebStorageEntry(\n targetId: TargetId,\n value: string\n ): QueryTargetMetadata | null {\n const targetState = JSON.parse(value) as QueryTargetStateSchema;\n\n let validData =\n typeof targetState === 'object' &&\n ['not-current', 'current', 'rejected'].indexOf(targetState.state) !==\n -1 &&\n (targetState.error === undefined ||\n typeof targetState.error === 'object');\n\n let firestoreError: FirestoreError | undefined = undefined;\n\n if (validData && targetState.error) {\n validData =\n typeof targetState.error.message === 'string' &&\n typeof targetState.error.code === 'string';\n if (validData) {\n firestoreError = new FirestoreError(\n targetState.error.code as Code,\n targetState.error.message\n );\n }\n }\n\n if (validData) {\n return new QueryTargetMetadata(\n targetId,\n targetState.state,\n firestoreError\n );\n } else {\n logError(\n LOG_TAG,\n `Failed to parse target state for ID '${targetId}': ${value}`\n );\n return null;\n }\n }\n\n toWebStorageJSON(): string {\n const targetState: QueryTargetStateSchema = {\n state: this.state,\n updateTimeMs: Date.now() // Modify the existing value to trigger update.\n };\n\n if (this.error) {\n targetState.error = {\n code: this.error.code,\n message: this.error.message\n };\n }\n\n return JSON.stringify(targetState);\n }\n}\n\n/**\n * Metadata state of a single client denoting the query targets it is actively\n * listening to.\n */\n// Visible for testing.\nexport interface ClientState {\n readonly activeTargetIds: TargetIdSet;\n}\n\n/**\n * This class represents the immutable ClientState for a client read from\n * WebStorage, containing the list of active query targets.\n */\nclass RemoteClientState implements ClientState {\n private constructor(\n readonly clientId: ClientId,\n readonly activeTargetIds: TargetIdSet\n ) {}\n\n /**\n * Parses a RemoteClientState from the JSON representation in WebStorage.\n * Logs a warning and returns null if the format of the data is not valid.\n */\n static fromWebStorageEntry(\n clientId: ClientId,\n value: string\n ): RemoteClientState | null {\n const clientState = JSON.parse(value) as ClientStateSchema;\n\n let validData =\n typeof clientState === 'object' &&\n clientState.activeTargetIds instanceof Array;\n\n let activeTargetIdsSet = targetIdSet();\n\n for (let i = 0; validData && i < clientState.activeTargetIds.length; ++i) {\n validData = isSafeInteger(clientState.activeTargetIds[i]);\n activeTargetIdsSet = activeTargetIdsSet.add(\n clientState.activeTargetIds[i]\n );\n }\n\n if (validData) {\n return new RemoteClientState(clientId, activeTargetIdsSet);\n } else {\n logError(\n LOG_TAG,\n `Failed to parse client data for instance '${clientId}': ${value}`\n );\n return null;\n }\n }\n}\n\n/**\n * This class represents the online state for all clients participating in\n * multi-tab. The online state is only written to by the primary client, and\n * used in secondary clients to update their query views.\n */\nexport class SharedOnlineState {\n constructor(readonly clientId: string, readonly onlineState: OnlineState) {}\n\n /**\n * Parses a SharedOnlineState from its JSON representation in WebStorage.\n * Logs a warning and returns null if the format of the data is not valid.\n */\n static fromWebStorageEntry(value: string): SharedOnlineState | null {\n const onlineState = JSON.parse(value) as SharedOnlineStateSchema;\n\n const validData =\n typeof onlineState === 'object' &&\n ['Unknown', 'Online', 'Offline'].indexOf(onlineState.onlineState) !==\n -1 &&\n typeof onlineState.clientId === 'string';\n\n if (validData) {\n return new SharedOnlineState(\n onlineState.clientId,\n onlineState.onlineState as OnlineState\n );\n } else {\n logError(LOG_TAG, `Failed to parse online state: ${value}`);\n return null;\n }\n }\n}\n\n/**\n * Metadata state of the local client. Unlike `RemoteClientState`, this class is\n * mutable and keeps track of all pending mutations, which allows us to\n * update the range of pending mutation batch IDs as new mutations are added or\n * removed.\n *\n * The data in `LocalClientState` is not read from WebStorage and instead\n * updated via its instance methods. The updated state can be serialized via\n * `toWebStorageJSON()`.\n */\n// Visible for testing.\nexport class LocalClientState implements ClientState {\n activeTargetIds = targetIdSet();\n\n addQueryTarget(targetId: TargetId): void {\n this.activeTargetIds = this.activeTargetIds.add(targetId);\n }\n\n removeQueryTarget(targetId: TargetId): void {\n this.activeTargetIds = this.activeTargetIds.delete(targetId);\n }\n\n /**\n * Converts this entry into a JSON-encoded format we can use for WebStorage.\n * Does not encode `clientId` as it is part of the key in WebStorage.\n */\n toWebStorageJSON(): string {\n const data: ClientStateSchema = {\n activeTargetIds: this.activeTargetIds.toArray(),\n updateTimeMs: Date.now() // Modify the existing value to trigger update.\n };\n return JSON.stringify(data);\n }\n}\n\n/**\n * `WebStorageSharedClientState` uses WebStorage (window.localStorage) as the\n * backing store for the SharedClientState. It keeps track of all active\n * clients and supports modifications of the local client's data.\n */\nexport class WebStorageSharedClientState implements SharedClientState {\n syncEngine: SharedClientStateSyncer | null = null;\n onlineStateHandler: ((onlineState: OnlineState) => void) | null = null;\n sequenceNumberHandler:\n | ((sequenceNumber: ListenSequenceNumber) => void)\n | null = null;\n\n private readonly storage: Storage;\n private readonly localClientStorageKey: string;\n private readonly sequenceNumberKey: string;\n private readonly storageListener = this.handleWebStorageEvent.bind(this);\n private readonly onlineStateKey: string;\n private readonly clientStateKeyRe: RegExp;\n private readonly mutationBatchKeyRe: RegExp;\n private readonly queryTargetKeyRe: RegExp;\n private activeClients = new SortedMap<string, ClientState>(\n primitiveComparator\n );\n private started = false;\n private currentUser: User;\n\n /**\n * Captures WebStorage events that occur before `start()` is called. These\n * events are replayed once `WebStorageSharedClientState` is started.\n */\n private earlyEvents: StorageEvent[] = [];\n\n constructor(\n private readonly queue: AsyncQueue,\n private readonly platform: Platform,\n private readonly persistenceKey: string,\n private readonly localClientId: ClientId,\n initialUser: User\n ) {\n if (!WebStorageSharedClientState.isAvailable(this.platform)) {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'LocalStorage is not available on this platform.'\n );\n }\n // Escape the special characters mentioned here:\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\n const escapedPersistenceKey = persistenceKey.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&'\n );\n\n this.storage = this.platform.window!.localStorage;\n this.currentUser = initialUser;\n this.localClientStorageKey = createWebStorageClientStateKey(\n this.persistenceKey,\n this.localClientId\n );\n this.sequenceNumberKey = createWebStorageSequenceNumberKey(\n this.persistenceKey\n );\n this.activeClients = this.activeClients.insert(\n this.localClientId,\n new LocalClientState()\n );\n\n this.clientStateKeyRe = new RegExp(\n `^${CLIENT_STATE_KEY_PREFIX}_${escapedPersistenceKey}_([^_]*)$`\n );\n this.mutationBatchKeyRe = new RegExp(\n `^${MUTATION_BATCH_KEY_PREFIX}_${escapedPersistenceKey}_(\\\\d+)(?:_(.*))?$`\n );\n this.queryTargetKeyRe = new RegExp(\n `^${QUERY_TARGET_KEY_PREFIX}_${escapedPersistenceKey}_(\\\\d+)$`\n );\n\n this.onlineStateKey = createWebStorageOnlineStateKey(this.persistenceKey);\n\n // Rather than adding the storage observer during start(), we add the\n // storage observer during initialization. This ensures that we collect\n // events before other components populate their initial state (during their\n // respective start() calls). Otherwise, we might for example miss a\n // mutation that is added after LocalStore's start() processed the existing\n // mutations but before we observe WebStorage events.\n this.platform.window!.addEventListener('storage', this.storageListener);\n }\n\n /** Returns 'true' if WebStorage is available in the current environment. */\n static isAvailable(platform: Platform): boolean {\n return !!(platform.window && platform.window.localStorage != null);\n }\n\n async start(): Promise<void> {\n debugAssert(!this.started, 'WebStorageSharedClientState already started');\n debugAssert(\n this.syncEngine !== null,\n 'syncEngine property must be set before calling start()'\n );\n debugAssert(\n this.onlineStateHandler !== null,\n 'onlineStateHandler property must be set before calling start()'\n );\n\n // Retrieve the list of existing clients to backfill the data in\n // SharedClientState.\n const existingClients = await this.syncEngine!.getActiveClients();\n\n for (const clientId of existingClients) {\n if (clientId === this.localClientId) {\n continue;\n }\n\n const storageItem = this.getItem(\n createWebStorageClientStateKey(this.persistenceKey, clientId)\n );\n if (storageItem) {\n const clientState = RemoteClientState.fromWebStorageEntry(\n clientId,\n storageItem\n );\n if (clientState) {\n this.activeClients = this.activeClients.insert(\n clientState.clientId,\n clientState\n );\n }\n }\n }\n\n this.persistClientState();\n\n // Check if there is an existing online state and call the callback handler\n // if applicable.\n const onlineStateJSON = this.storage.getItem(this.onlineStateKey);\n if (onlineStateJSON) {\n const onlineState = this.fromWebStorageOnlineState(onlineStateJSON);\n if (onlineState) {\n this.handleOnlineStateEvent(onlineState);\n }\n }\n\n for (const event of this.earlyEvents) {\n this.handleWebStorageEvent(event);\n }\n\n this.earlyEvents = [];\n\n // Register a window unload hook to remove the client metadata entry from\n // WebStorage even if `shutdown()` was not called.\n this.platform.window!.addEventListener('unload', () => this.shutdown());\n\n this.started = true;\n }\n\n writeSequenceNumber(sequenceNumber: ListenSequenceNumber): void {\n this.setItem(this.sequenceNumberKey, JSON.stringify(sequenceNumber));\n }\n\n getAllActiveQueryTargets(): TargetIdSet {\n return this.extractActiveQueryTargets(this.activeClients);\n }\n\n isActiveQueryTarget(targetId: TargetId): boolean {\n let found = false;\n this.activeClients.forEach((key, value) => {\n if (value.activeTargetIds.has(targetId)) {\n found = true;\n }\n });\n return found;\n }\n\n addPendingMutation(batchId: BatchId): void {\n this.persistMutationState(batchId, 'pending');\n }\n\n updateMutationState(\n batchId: BatchId,\n state: 'acknowledged' | 'rejected',\n error?: FirestoreError\n ): void {\n this.persistMutationState(batchId, state, error);\n\n // Once a final mutation result is observed by other clients, they no longer\n // access the mutation's metadata entry. Since WebStorage replays events\n // in order, it is safe to delete the entry right after updating it.\n this.removeMutationState(batchId);\n }\n\n addLocalQueryTarget(targetId: TargetId): QueryTargetState {\n let queryState: QueryTargetState = 'not-current';\n\n // Lookup an existing query state if the target ID was already registered\n // by another tab\n if (this.isActiveQueryTarget(targetId)) {\n const storageItem = this.storage.getItem(\n createWebStorageQueryTargetMetadataKey(this.persistenceKey, targetId)\n );\n\n if (storageItem) {\n const metadata = QueryTargetMetadata.fromWebStorageEntry(\n targetId,\n storageItem\n );\n if (metadata) {\n queryState = metadata.state;\n }\n }\n }\n\n this.localClientState.addQueryTarget(targetId);\n this.persistClientState();\n\n return queryState;\n }\n\n removeLocalQueryTarget(targetId: TargetId): void {\n this.localClientState.removeQueryTarget(targetId);\n this.persistClientState();\n }\n\n isLocalQueryTarget(targetId: TargetId): boolean {\n return this.localClientState.activeTargetIds.has(targetId);\n }\n\n clearQueryState(targetId: TargetId): void {\n this.removeItem(\n createWebStorageQueryTargetMetadataKey(this.persistenceKey, targetId)\n );\n }\n\n updateQueryState(\n targetId: TargetId,\n state: QueryTargetState,\n error?: FirestoreError\n ): void {\n this.persistQueryTargetState(targetId, state, error);\n }\n\n handleUserChange(\n user: User,\n removedBatchIds: BatchId[],\n addedBatchIds: BatchId[]\n ): void {\n removedBatchIds.forEach(batchId => {\n this.removeMutationState(batchId);\n });\n this.currentUser = user;\n addedBatchIds.forEach(batchId => {\n this.addPendingMutation(batchId);\n });\n }\n\n setOnlineState(onlineState: OnlineState): void {\n this.persistOnlineState(onlineState);\n }\n\n shutdown(): void {\n if (this.started) {\n this.platform.window!.removeEventListener(\n 'storage',\n this.storageListener\n );\n this.removeItem(this.localClientStorageKey);\n this.started = false;\n }\n }\n\n private getItem(key: string): string | null {\n const value = this.storage.getItem(key);\n logDebug(LOG_TAG, 'READ', key, value);\n return value;\n }\n\n private setItem(key: string, value: string): void {\n logDebug(LOG_TAG, 'SET', key, value);\n this.storage.setItem(key, value);\n }\n\n private removeItem(key: string): void {\n logDebug(LOG_TAG, 'REMOVE', key);\n this.storage.removeItem(key);\n }\n\n private handleWebStorageEvent(event: StorageEvent): void {\n if (event.storageArea === this.storage) {\n logDebug(LOG_TAG, 'EVENT', event.key, event.newValue);\n\n if (event.key === this.localClientStorageKey) {\n logError(\n 'Received WebStorage notification for local change. Another client might have ' +\n 'garbage-collected our state'\n );\n return;\n }\n\n this.queue.enqueueRetryable(async () => {\n if (!this.started) {\n this.earlyEvents.push(event);\n return;\n }\n\n if (event.key === null) {\n return;\n }\n\n if (this.clientStateKeyRe.test(event.key)) {\n if (event.newValue != null) {\n const clientState = this.fromWebStorageClientState(\n event.key,\n event.newValue\n );\n if (clientState) {\n return this.handleClientStateEvent(\n clientState.clientId,\n clientState\n );\n }\n } else {\n const clientId = this.fromWebStorageClientStateKey(event.key)!;\n return this.handleClientStateEvent(clientId, null);\n }\n } else if (this.mutationBatchKeyRe.test(event.key)) {\n if (event.newValue !== null) {\n const mutationMetadata = this.fromWebStorageMutationMetadata(\n event.key,\n event.newValue\n );\n if (mutationMetadata) {\n return this.handleMutationBatchEvent(mutationMetadata);\n }\n }\n } else if (this.queryTargetKeyRe.test(event.key)) {\n if (event.newValue !== null) {\n const queryTargetMetadata = this.fromWebStorageQueryTargetMetadata(\n event.key,\n event.newValue\n );\n if (queryTargetMetadata) {\n return this.handleQueryTargetEvent(queryTargetMetadata);\n }\n }\n } else if (event.key === this.onlineStateKey) {\n if (event.newValue !== null) {\n const onlineState = this.fromWebStorageOnlineState(event.newValue);\n if (onlineState) {\n return this.handleOnlineStateEvent(onlineState);\n }\n }\n } else if (event.key === this.sequenceNumberKey) {\n debugAssert(\n !!this.sequenceNumberHandler,\n 'Missing sequenceNumberHandler'\n );\n const sequenceNumber = fromWebStorageSequenceNumber(event.newValue);\n if (sequenceNumber !== ListenSequence.INVALID) {\n this.sequenceNumberHandler!(sequenceNumber);\n }\n }\n });\n }\n }\n\n private get localClientState(): LocalClientState {\n return this.activeClients.get(this.localClientId) as LocalClientState;\n }\n\n private persistClientState(): void {\n this.setItem(\n this.localClientStorageKey,\n this.localClientState.toWebStorageJSON()\n );\n }\n\n private persistMutationState(\n batchId: BatchId,\n state: MutationBatchState,\n error?: FirestoreError\n ): void {\n const mutationState = new MutationMetadata(\n this.currentUser,\n batchId,\n state,\n error\n );\n const mutationKey = createWebStorageMutationBatchKey(\n this.persistenceKey,\n this.currentUser,\n batchId\n );\n this.setItem(mutationKey, mutationState.toWebStorageJSON());\n }\n\n private removeMutationState(batchId: BatchId): void {\n const mutationKey = createWebStorageMutationBatchKey(\n this.persistenceKey,\n this.currentUser,\n batchId\n );\n this.removeItem(mutationKey);\n }\n\n private persistOnlineState(onlineState: OnlineState): void {\n const entry: SharedOnlineStateSchema = {\n clientId: this.localClientId,\n onlineState\n };\n this.storage.setItem(this.onlineStateKey, JSON.stringify(entry));\n }\n\n private persistQueryTargetState(\n targetId: TargetId,\n state: QueryTargetState,\n error?: FirestoreError\n ): void {\n const targetKey = createWebStorageQueryTargetMetadataKey(\n this.persistenceKey,\n targetId\n );\n const targetMetadata = new QueryTargetMetadata(targetId, state, error);\n this.setItem(targetKey, targetMetadata.toWebStorageJSON());\n }\n\n /**\n * Parses a client state key in WebStorage. Returns null if the key does not\n * match the expected key format.\n */\n private fromWebStorageClientStateKey(key: string): ClientId | null {\n const match = this.clientStateKeyRe.exec(key);\n return match ? match[1] : null;\n }\n\n /**\n * Parses a client state in WebStorage. Returns 'null' if the value could not\n * be parsed.\n */\n private fromWebStorageClientState(\n key: string,\n value: string\n ): RemoteClientState | null {\n const clientId = this.fromWebStorageClientStateKey(key);\n debugAssert(clientId !== null, `Cannot parse client state key '${key}'`);\n return RemoteClientState.fromWebStorageEntry(clientId, value);\n }\n\n /**\n * Parses a mutation batch state in WebStorage. Returns 'null' if the value\n * could not be parsed.\n */\n private fromWebStorageMutationMetadata(\n key: string,\n value: string\n ): MutationMetadata | null {\n const match = this.mutationBatchKeyRe.exec(key);\n debugAssert(match !== null, `Cannot parse mutation batch key '${key}'`);\n\n const batchId = Number(match[1]);\n const userId = match[2] !== undefined ? match[2] : null;\n return MutationMetadata.fromWebStorageEntry(\n new User(userId),\n batchId,\n value\n );\n }\n\n /**\n * Parses a query target state from WebStorage. Returns 'null' if the value\n * could not be parsed.\n */\n private fromWebStorageQueryTargetMetadata(\n key: string,\n value: string\n ): QueryTargetMetadata | null {\n const match = this.queryTargetKeyRe.exec(key);\n debugAssert(match !== null, `Cannot parse query target key '${key}'`);\n\n const targetId = Number(match[1]);\n return QueryTargetMetadata.fromWebStorageEntry(targetId, value);\n }\n\n /**\n * Parses an online state from WebStorage. Returns 'null' if the value\n * could not be parsed.\n */\n private fromWebStorageOnlineState(value: string): SharedOnlineState | null {\n return SharedOnlineState.fromWebStorageEntry(value);\n }\n\n private async handleMutationBatchEvent(\n mutationBatch: MutationMetadata\n ): Promise<void> {\n if (mutationBatch.user.uid !== this.currentUser.uid) {\n logDebug(\n LOG_TAG,\n `Ignoring mutation for non-active user ${mutationBatch.user.uid}`\n );\n return;\n }\n\n return this.syncEngine!.applyBatchState(\n mutationBatch.batchId,\n mutationBatch.state,\n mutationBatch.error\n );\n }\n\n private handleQueryTargetEvent(\n targetMetadata: QueryTargetMetadata\n ): Promise<void> {\n return this.syncEngine!.applyTargetState(\n targetMetadata.targetId,\n targetMetadata.state,\n targetMetadata.error\n );\n }\n\n private handleClientStateEvent(\n clientId: ClientId,\n clientState: RemoteClientState | null\n ): Promise<void> {\n const updatedClients = clientState\n ? this.activeClients.insert(clientId, clientState)\n : this.activeClients.remove(clientId);\n\n const existingTargets = this.extractActiveQueryTargets(this.activeClients);\n const newTargets = this.extractActiveQueryTargets(updatedClients);\n\n const addedTargets: TargetId[] = [];\n const removedTargets: TargetId[] = [];\n\n newTargets.forEach(targetId => {\n if (!existingTargets.has(targetId)) {\n addedTargets.push(targetId);\n }\n });\n\n existingTargets.forEach(targetId => {\n if (!newTargets.has(targetId)) {\n removedTargets.push(targetId);\n }\n });\n\n return this.syncEngine!.applyActiveTargetsChange(\n addedTargets,\n removedTargets\n ).then(() => {\n this.activeClients = updatedClients;\n });\n }\n\n private handleOnlineStateEvent(onlineState: SharedOnlineState): void {\n // We check whether the client that wrote this online state is still active\n // by comparing its client ID to the list of clients kept active in\n // IndexedDb. If a client does not update their IndexedDb client state\n // within 5 seconds, it is considered inactive and we don't emit an online\n // state event.\n if (this.activeClients.get(onlineState.clientId)) {\n this.onlineStateHandler!(onlineState.onlineState);\n }\n }\n\n private extractActiveQueryTargets(\n clients: SortedMap<string, ClientState>\n ): SortedSet<TargetId> {\n let activeTargets = targetIdSet();\n clients.forEach((kev, value) => {\n activeTargets = activeTargets.unionWith(value.activeTargetIds);\n });\n return activeTargets;\n }\n}\n\nfunction fromWebStorageSequenceNumber(\n seqString: string | null\n): ListenSequenceNumber {\n let sequenceNumber = ListenSequence.INVALID;\n if (seqString != null) {\n try {\n const parsed = JSON.parse(seqString);\n hardAssert(\n typeof parsed === 'number',\n 'Found non-numeric sequence number'\n );\n sequenceNumber = parsed;\n } catch (e) {\n logError(LOG_TAG, 'Failed to read sequence number from WebStorage', e);\n }\n }\n return sequenceNumber;\n}\n\n/**\n * `MemorySharedClientState` is a simple implementation of SharedClientState for\n * clients using memory persistence. The state in this class remains fully\n * isolated and no synchronization is performed.\n */\nexport class MemorySharedClientState implements SharedClientState {\n private localState = new LocalClientState();\n private queryState: { [targetId: number]: QueryTargetState } = {};\n\n syncEngine: SharedClientStateSyncer | null = null;\n onlineStateHandler: ((onlineState: OnlineState) => void) | null = null;\n sequenceNumberHandler:\n | ((sequenceNumber: ListenSequenceNumber) => void)\n | null = null;\n\n addPendingMutation(batchId: BatchId): void {\n // No op.\n }\n\n updateMutationState(\n batchId: BatchId,\n state: 'acknowledged' | 'rejected',\n error?: FirestoreError\n ): void {\n // No op.\n }\n\n addLocalQueryTarget(targetId: TargetId): QueryTargetState {\n this.localState.addQueryTarget(targetId);\n return this.queryState[targetId] || 'not-current';\n }\n\n updateQueryState(\n targetId: TargetId,\n state: QueryTargetState,\n error?: FirestoreError\n ): void {\n this.queryState[targetId] = state;\n }\n\n removeLocalQueryTarget(targetId: TargetId): void {\n this.localState.removeQueryTarget(targetId);\n }\n\n isLocalQueryTarget(targetId: TargetId): boolean {\n return this.localState.activeTargetIds.has(targetId);\n }\n\n clearQueryState(targetId: TargetId): void {\n delete this.queryState[targetId];\n }\n\n getAllActiveQueryTargets(): TargetIdSet {\n return this.localState.activeTargetIds;\n }\n\n isActiveQueryTarget(targetId: TargetId): boolean {\n return this.localState.activeTargetIds.has(targetId);\n }\n\n start(): Promise<void> {\n this.localState = new LocalClientState();\n return Promise.resolve();\n }\n\n handleUserChange(\n user: User,\n removedBatchIds: BatchId[],\n addedBatchIds: BatchId[]\n ): void {\n // No op.\n }\n\n setOnlineState(onlineState: OnlineState): void {\n // No op.\n }\n\n shutdown(): void {}\n\n writeSequenceNumber(sequenceNumber: ListenSequenceNumber): void {}\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { Deferred } from '../util/promise';\nimport { TimerId, AsyncQueue } from '../util/async_queue';\nimport { ExponentialBackoff } from '../remote/backoff';\nimport { Transaction } from './transaction';\nimport { RemoteStore } from '../remote/remote_store';\nimport { isNullOrUndefined } from '../util/types';\nimport { isPermanentError } from '../remote/rpc_error';\nimport { FirestoreError } from '../util/error';\n\nconst RETRY_COUNT = 5;\n\n/**\n * TransactionRunner encapsulates the logic needed to run and retry transactions\n * with backoff.\n */\nexport class TransactionRunner<T> {\n private retries = RETRY_COUNT;\n private backoff: ExponentialBackoff;\n\n constructor(\n private readonly asyncQueue: AsyncQueue,\n private readonly remoteStore: RemoteStore,\n private readonly updateFunction: (transaction: Transaction) => Promise<T>,\n private readonly deferred: Deferred<T>\n ) {\n this.backoff = new ExponentialBackoff(\n this.asyncQueue,\n TimerId.TransactionRetry\n );\n }\n\n /** Runs the transaction and sets the result on deferred. */\n run(): void {\n this.runWithBackOff();\n }\n\n private runWithBackOff(): void {\n this.backoff.backoffAndRun(async () => {\n const transaction = this.remoteStore.createTransaction();\n const userPromise = this.tryRunUpdateFunction(transaction);\n if (userPromise) {\n userPromise\n .then(result => {\n this.asyncQueue.enqueueAndForget(() => {\n return transaction\n .commit()\n .then(() => {\n this.deferred.resolve(result);\n })\n .catch(commitError => {\n this.handleTransactionError(commitError);\n });\n });\n })\n .catch(userPromiseError => {\n this.handleTransactionError(userPromiseError);\n });\n }\n });\n }\n\n private tryRunUpdateFunction(transaction: Transaction): Promise<T> | null {\n try {\n const userPromise = this.updateFunction(transaction);\n if (\n isNullOrUndefined(userPromise) ||\n !userPromise.catch ||\n !userPromise.then\n ) {\n this.deferred.reject(\n Error('Transaction callback must return a Promise')\n );\n return null;\n }\n return userPromise;\n } catch (error) {\n // Do not retry errors thrown by user provided updateFunction.\n this.deferred.reject(error);\n return null;\n }\n }\n\n private handleTransactionError(error: Error): void {\n if (this.retries > 0 && this.isRetryableTransactionError(error)) {\n this.retries -= 1;\n this.asyncQueue.enqueueAndForget(() => {\n this.runWithBackOff();\n return Promise.resolve();\n });\n } else {\n this.deferred.reject(error);\n }\n }\n\n private isRetryableTransactionError(error: Error): boolean {\n if (error.name === 'FirebaseError') {\n // In transactions, the backend will fail outdated reads with FAILED_PRECONDITION and\n // non-matching document versions with ABORTED. These errors should be retried.\n const code = (error as FirestoreError).code;\n return (\n code === 'aborted' ||\n code === 'failed-precondition' ||\n !isPermanentError(code)\n );\n }\n return false;\n }\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { QueryEngine } from './query_engine';\nimport { LocalDocumentsView } from './local_documents_view';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { LimitType, Query } from '../core/query';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport {\n DocumentKeySet,\n DocumentMap,\n MaybeDocumentMap\n} from '../model/collections';\nimport { Document } from '../model/document';\nimport { debugAssert } from '../util/assert';\nimport { getLogLevel, LogLevel, logDebug } from '../util/log';\nimport { SortedSet } from '../util/sorted_set';\n\n// TOOD(b/140938512): Drop SimpleQueryEngine and rename IndexFreeQueryEngine.\n\n/**\n * A query engine that takes advantage of the target document mapping in the\n * QueryCache. The IndexFreeQueryEngine optimizes query execution by only\n * reading the documents that previously matched a query plus any documents that were\n * edited after the query was last listened to.\n *\n * There are some cases where Index-Free queries are not guaranteed to produce\n * the same results as full collection scans. In these cases, the\n * IndexFreeQueryEngine falls back to full query processing. These cases are:\n *\n * - Limit queries where a document that matched the query previously no longer\n * matches the query.\n *\n * - Limit queries where a document edit may cause the document to sort below\n * another document that is in the local cache.\n *\n * - Queries that have never been CURRENT or free of Limbo documents.\n */\nexport class IndexFreeQueryEngine implements QueryEngine {\n private localDocumentsView: LocalDocumentsView | undefined;\n\n setLocalDocumentsView(localDocuments: LocalDocumentsView): void {\n this.localDocumentsView = localDocuments;\n }\n\n getDocumentsMatchingQuery(\n transaction: PersistenceTransaction,\n query: Query,\n lastLimboFreeSnapshotVersion: SnapshotVersion,\n remoteKeys: DocumentKeySet\n ): PersistencePromise<DocumentMap> {\n debugAssert(\n this.localDocumentsView !== undefined,\n 'setLocalDocumentsView() not called'\n );\n\n // Queries that match all documents don't benefit from using\n // IndexFreeQueries. It is more efficient to scan all documents in a\n // collection, rather than to perform individual lookups.\n if (query.matchesAllDocuments()) {\n return this.executeFullCollectionScan(transaction, query);\n }\n\n // Queries that have never seen a snapshot without limbo free documents\n // should also be run as a full collection scan.\n if (lastLimboFreeSnapshotVersion.isEqual(SnapshotVersion.min())) {\n return this.executeFullCollectionScan(transaction, query);\n }\n\n return this.localDocumentsView!.getDocuments(transaction, remoteKeys).next(\n documents => {\n const previousResults = this.applyQuery(query, documents);\n\n if (\n (query.hasLimitToFirst() || query.hasLimitToLast()) &&\n this.needsRefill(\n query.limitType,\n previousResults,\n remoteKeys,\n lastLimboFreeSnapshotVersion\n )\n ) {\n return this.executeFullCollectionScan(transaction, query);\n }\n\n if (getLogLevel() <= LogLevel.DEBUG) {\n logDebug(\n 'IndexFreeQueryEngine',\n 'Re-using previous result from %s to execute query: %s',\n lastLimboFreeSnapshotVersion.toString(),\n query.toString()\n );\n }\n\n // Retrieve all results for documents that were updated since the last\n // limbo-document free remote snapshot.\n return this.localDocumentsView!.getDocumentsMatchingQuery(\n transaction,\n query,\n lastLimboFreeSnapshotVersion\n ).next(updatedResults => {\n // We merge `previousResults` into `updateResults`, since\n // `updateResults` is already a DocumentMap. If a document is\n // contained in both lists, then its contents are the same.\n previousResults.forEach(doc => {\n updatedResults = updatedResults.insert(doc.key, doc);\n });\n return updatedResults;\n });\n }\n );\n }\n\n /** Applies the query filter and sorting to the provided documents. */\n private applyQuery(\n query: Query,\n documents: MaybeDocumentMap\n ): SortedSet<Document> {\n // Sort the documents and re-apply the query filter since previously\n // matching documents do not necessarily still match the query.\n let queryResults = new SortedSet<Document>((d1, d2) =>\n query.docComparator(d1, d2)\n );\n documents.forEach((_, maybeDoc) => {\n if (maybeDoc instanceof Document && query.matches(maybeDoc)) {\n queryResults = queryResults.add(maybeDoc);\n }\n });\n return queryResults;\n }\n\n /**\n * Determines if a limit query needs to be refilled from cache, making it\n * ineligible for index-free execution.\n *\n * @param sortedPreviousResults The documents that matched the query when it\n * was last synchronized, sorted by the query's comparator.\n * @param remoteKeys The document keys that matched the query at the last\n * snapshot.\n * @param limboFreeSnapshotVersion The version of the snapshot when the query\n * was last synchronized.\n */\n private needsRefill(\n limitType: LimitType,\n sortedPreviousResults: SortedSet<Document>,\n remoteKeys: DocumentKeySet,\n limboFreeSnapshotVersion: SnapshotVersion\n ): boolean {\n // The query needs to be refilled if a previously matching document no\n // longer matches.\n if (remoteKeys.size !== sortedPreviousResults.size) {\n return true;\n }\n\n // Limit queries are not eligible for index-free query execution if there is\n // a potential that an older document from cache now sorts before a document\n // that was previously part of the limit. This, however, can only happen if\n // the document at the edge of the limit goes out of limit.\n // If a document that is not the limit boundary sorts differently,\n // the boundary of the limit itself did not change and documents from cache\n // will continue to be \"rejected\" by this boundary. Therefore, we can ignore\n // any modifications that don't affect the last document.\n const docAtLimitEdge =\n limitType === LimitType.First\n ? sortedPreviousResults.last()\n : sortedPreviousResults.first();\n if (!docAtLimitEdge) {\n // We don't need to refill the query if there were already no documents.\n return false;\n }\n return (\n docAtLimitEdge.hasPendingWrites ||\n docAtLimitEdge.version.compareTo(limboFreeSnapshotVersion) > 0\n );\n }\n\n private executeFullCollectionScan(\n transaction: PersistenceTransaction,\n query: Query\n ): PersistencePromise<DocumentMap> {\n if (getLogLevel() <= LogLevel.DEBUG) {\n logDebug(\n 'IndexFreeQueryEngine',\n 'Using full collection scan to execute query:',\n query.toString()\n );\n }\n\n return this.localDocumentsView!.getDocumentsMatchingQuery(\n transaction,\n query,\n SnapshotVersion.min()\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Timestamp } from '../api/timestamp';\nimport { Query } from '../core/query';\nimport { BatchId } from '../core/types';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation } from '../model/mutation';\nimport { MutationBatch, BATCHID_UNKNOWN } from '../model/mutation_batch';\nimport { debugAssert, hardAssert } from '../util/assert';\nimport { primitiveComparator } from '../util/misc';\nimport { ByteString } from '../util/byte_string';\nimport { SortedMap } from '../util/sorted_map';\nimport { SortedSet } from '../util/sorted_set';\n\nimport { IndexManager } from './index_manager';\nimport { MutationQueue } from './mutation_queue';\nimport { PersistenceTransaction, ReferenceDelegate } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { DocReference } from './reference_set';\n\nexport class MemoryMutationQueue implements MutationQueue {\n /**\n * The set of all mutations that have been sent but not yet been applied to\n * the backend.\n */\n private mutationQueue: MutationBatch[] = [];\n\n /** Next value to use when assigning sequential IDs to each mutation batch. */\n private nextBatchId: BatchId = 1;\n\n /** The last received stream token from the server, used to acknowledge which\n * responses the client has processed. Stream tokens are opaque checkpoint\n * markers whose only real value is their inclusion in the next request.\n */\n private lastStreamToken: ByteString = ByteString.EMPTY_BYTE_STRING;\n\n /** An ordered mapping between documents and the mutations batch IDs. */\n private batchesByDocumentKey = new SortedSet(DocReference.compareByKey);\n\n constructor(\n private readonly indexManager: IndexManager,\n private readonly referenceDelegate: ReferenceDelegate\n ) {}\n\n checkEmpty(transaction: PersistenceTransaction): PersistencePromise<boolean> {\n return PersistencePromise.resolve(this.mutationQueue.length === 0);\n }\n\n acknowledgeBatch(\n transaction: PersistenceTransaction,\n batch: MutationBatch,\n streamToken: ByteString\n ): PersistencePromise<void> {\n const batchId = batch.batchId;\n const batchIndex = this.indexOfExistingBatchId(batchId, 'acknowledged');\n hardAssert(\n batchIndex === 0,\n 'Can only acknowledge the first batch in the mutation queue'\n );\n\n // Verify that the batch in the queue is the one to be acknowledged.\n const check = this.mutationQueue[batchIndex];\n debugAssert(\n batchId === check.batchId,\n 'Queue ordering failure: expected batch ' +\n batchId +\n ', got batch ' +\n check.batchId\n );\n\n this.lastStreamToken = streamToken;\n return PersistencePromise.resolve();\n }\n\n getLastStreamToken(\n transaction: PersistenceTransaction\n ): PersistencePromise<ByteString> {\n return PersistencePromise.resolve(this.lastStreamToken);\n }\n\n setLastStreamToken(\n transaction: PersistenceTransaction,\n streamToken: ByteString\n ): PersistencePromise<void> {\n this.lastStreamToken = streamToken;\n return PersistencePromise.resolve();\n }\n\n addMutationBatch(\n transaction: PersistenceTransaction,\n localWriteTime: Timestamp,\n baseMutations: Mutation[],\n mutations: Mutation[]\n ): PersistencePromise<MutationBatch> {\n debugAssert(mutations.length !== 0, 'Mutation batches should not be empty');\n\n const batchId = this.nextBatchId;\n this.nextBatchId++;\n\n if (this.mutationQueue.length > 0) {\n const prior = this.mutationQueue[this.mutationQueue.length - 1];\n debugAssert(\n prior.batchId < batchId,\n 'Mutation batchIDs must be monotonically increasing order'\n );\n }\n\n const batch = new MutationBatch(\n batchId,\n localWriteTime,\n baseMutations,\n mutations\n );\n this.mutationQueue.push(batch);\n\n // Track references by document key and index collection parents.\n for (const mutation of mutations) {\n this.batchesByDocumentKey = this.batchesByDocumentKey.add(\n new DocReference(mutation.key, batchId)\n );\n\n this.indexManager.addToCollectionParentIndex(\n transaction,\n mutation.key.path.popLast()\n );\n }\n\n return PersistencePromise.resolve(batch);\n }\n\n lookupMutationBatch(\n transaction: PersistenceTransaction,\n batchId: BatchId\n ): PersistencePromise<MutationBatch | null> {\n return PersistencePromise.resolve(this.findMutationBatch(batchId));\n }\n\n getNextMutationBatchAfterBatchId(\n transaction: PersistenceTransaction,\n batchId: BatchId\n ): PersistencePromise<MutationBatch | null> {\n const nextBatchId = batchId + 1;\n\n // The requested batchId may still be out of range so normalize it to the\n // start of the queue.\n const rawIndex = this.indexOfBatchId(nextBatchId);\n const index = rawIndex < 0 ? 0 : rawIndex;\n return PersistencePromise.resolve(\n this.mutationQueue.length > index ? this.mutationQueue[index] : null\n );\n }\n\n getHighestUnacknowledgedBatchId(): PersistencePromise<BatchId> {\n return PersistencePromise.resolve(\n this.mutationQueue.length === 0 ? BATCHID_UNKNOWN : this.nextBatchId - 1\n );\n }\n\n getAllMutationBatches(\n transaction: PersistenceTransaction\n ): PersistencePromise<MutationBatch[]> {\n return PersistencePromise.resolve(this.mutationQueue.slice());\n }\n\n getAllMutationBatchesAffectingDocumentKey(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MutationBatch[]> {\n const start = new DocReference(documentKey, 0);\n const end = new DocReference(documentKey, Number.POSITIVE_INFINITY);\n const result: MutationBatch[] = [];\n this.batchesByDocumentKey.forEachInRange([start, end], ref => {\n debugAssert(\n documentKey.isEqual(ref.key),\n \"Should only iterate over a single key's batches\"\n );\n const batch = this.findMutationBatch(ref.targetOrBatchId);\n debugAssert(\n batch !== null,\n 'Batches in the index must exist in the main table'\n );\n result.push(batch!);\n });\n\n return PersistencePromise.resolve(result);\n }\n\n getAllMutationBatchesAffectingDocumentKeys(\n transaction: PersistenceTransaction,\n documentKeys: SortedMap<DocumentKey, unknown>\n ): PersistencePromise<MutationBatch[]> {\n let uniqueBatchIDs = new SortedSet<number>(primitiveComparator);\n\n documentKeys.forEach(documentKey => {\n const start = new DocReference(documentKey, 0);\n const end = new DocReference(documentKey, Number.POSITIVE_INFINITY);\n this.batchesByDocumentKey.forEachInRange([start, end], ref => {\n debugAssert(\n documentKey.isEqual(ref.key),\n \"For each key, should only iterate over a single key's batches\"\n );\n\n uniqueBatchIDs = uniqueBatchIDs.add(ref.targetOrBatchId);\n });\n });\n\n return PersistencePromise.resolve(this.findMutationBatches(uniqueBatchIDs));\n }\n\n getAllMutationBatchesAffectingQuery(\n transaction: PersistenceTransaction,\n query: Query\n ): PersistencePromise<MutationBatch[]> {\n debugAssert(\n !query.isCollectionGroupQuery(),\n 'CollectionGroup queries should be handled in LocalDocumentsView'\n );\n // Use the query path as a prefix for testing if a document matches the\n // query.\n const prefix = query.path;\n const immediateChildrenPathLength = prefix.length + 1;\n\n // Construct a document reference for actually scanning the index. Unlike\n // the prefix the document key in this reference must have an even number of\n // segments. The empty segment can be used a suffix of the query path\n // because it precedes all other segments in an ordered traversal.\n let startPath = prefix;\n if (!DocumentKey.isDocumentKey(startPath)) {\n startPath = startPath.child('');\n }\n\n const start = new DocReference(new DocumentKey(startPath), 0);\n\n // Find unique batchIDs referenced by all documents potentially matching the\n // query.\n let uniqueBatchIDs = new SortedSet<number>(primitiveComparator);\n\n this.batchesByDocumentKey.forEachWhile(ref => {\n const rowKeyPath = ref.key.path;\n if (!prefix.isPrefixOf(rowKeyPath)) {\n return false;\n } else {\n // Rows with document keys more than one segment longer than the query\n // path can't be matches. For example, a query on 'rooms' can't match\n // the document /rooms/abc/messages/xyx.\n // TODO(mcg): we'll need a different scanner when we implement\n // ancestor queries.\n if (rowKeyPath.length === immediateChildrenPathLength) {\n uniqueBatchIDs = uniqueBatchIDs.add(ref.targetOrBatchId);\n }\n return true;\n }\n }, start);\n\n return PersistencePromise.resolve(this.findMutationBatches(uniqueBatchIDs));\n }\n\n private findMutationBatches(batchIDs: SortedSet<number>): MutationBatch[] {\n // Construct an array of matching batches, sorted by batchID to ensure that\n // multiple mutations affecting the same document key are applied in order.\n const result: MutationBatch[] = [];\n batchIDs.forEach(batchId => {\n const batch = this.findMutationBatch(batchId);\n if (batch !== null) {\n result.push(batch);\n }\n });\n return result;\n }\n\n removeMutationBatch(\n transaction: PersistenceTransaction,\n batch: MutationBatch\n ): PersistencePromise<void> {\n // Find the position of the first batch for removal.\n const batchIndex = this.indexOfExistingBatchId(batch.batchId, 'removed');\n hardAssert(\n batchIndex === 0,\n 'Can only remove the first entry of the mutation queue'\n );\n this.mutationQueue.shift();\n\n let references = this.batchesByDocumentKey;\n return PersistencePromise.forEach(batch.mutations, (mutation: Mutation) => {\n const ref = new DocReference(mutation.key, batch.batchId);\n references = references.delete(ref);\n return this.referenceDelegate.markPotentiallyOrphaned(\n transaction,\n mutation.key\n );\n }).next(() => {\n this.batchesByDocumentKey = references;\n });\n }\n\n removeCachedMutationKeys(batchId: BatchId): void {\n // No-op since the memory mutation queue does not maintain a separate cache.\n }\n\n containsKey(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n const ref = new DocReference(key, 0);\n const firstRef = this.batchesByDocumentKey.firstAfterOrEqual(ref);\n return PersistencePromise.resolve(key.isEqual(firstRef && firstRef.key));\n }\n\n performConsistencyCheck(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n if (this.mutationQueue.length === 0) {\n debugAssert(\n this.batchesByDocumentKey.isEmpty(),\n 'Document leak -- detected dangling mutation references when queue is empty.'\n );\n }\n return PersistencePromise.resolve();\n }\n\n /**\n * Finds the index of the given batchId in the mutation queue and asserts that\n * the resulting index is within the bounds of the queue.\n *\n * @param batchId The batchId to search for\n * @param action A description of what the caller is doing, phrased in passive\n * form (e.g. \"acknowledged\" in a routine that acknowledges batches).\n */\n private indexOfExistingBatchId(batchId: BatchId, action: string): number {\n const index = this.indexOfBatchId(batchId);\n debugAssert(\n index >= 0 && index < this.mutationQueue.length,\n 'Batches must exist to be ' + action\n );\n return index;\n }\n\n /**\n * Finds the index of the given batchId in the mutation queue. This operation\n * is O(1).\n *\n * @return The computed index of the batch with the given batchId, based on\n * the state of the queue. Note this index can be negative if the requested\n * batchId has already been remvoed from the queue or past the end of the\n * queue if the batchId is larger than the last added batch.\n */\n private indexOfBatchId(batchId: BatchId): number {\n if (this.mutationQueue.length === 0) {\n // As an index this is past the end of the queue\n return 0;\n }\n\n // Examine the front of the queue to figure out the difference between the\n // batchId and indexes in the array. Note that since the queue is ordered\n // by batchId, if the first batch has a larger batchId then the requested\n // batchId doesn't exist in the queue.\n const firstBatchId = this.mutationQueue[0].batchId;\n return batchId - firstBatchId;\n }\n\n /**\n * A version of lookupMutationBatch that doesn't return a promise, this makes\n * other functions that uses this code easier to read and more efficent.\n */\n private findMutationBatch(batchId: BatchId): MutationBatch | null {\n const index = this.indexOfBatchId(batchId);\n if (index < 0 || index >= this.mutationQueue.length) {\n return null;\n }\n\n const batch = this.mutationQueue[index];\n debugAssert(batch.batchId === batchId, 'If found batch must match');\n return batch;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Query } from '../core/query';\nimport {\n DocumentKeySet,\n DocumentMap,\n documentMap,\n DocumentSizeEntry,\n NullableMaybeDocumentMap,\n nullableMaybeDocumentMap\n} from '../model/collections';\nimport { Document, MaybeDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\n\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { debugAssert } from '../util/assert';\nimport { SortedMap } from '../util/sorted_map';\nimport { IndexManager } from './index_manager';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { RemoteDocumentCache } from './remote_document_cache';\nimport { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';\n\nexport type DocumentSizer = (doc: MaybeDocument) => number;\n\n/** Miscellaneous collection types / constants. */\ninterface MemoryRemoteDocumentCacheEntry extends DocumentSizeEntry {\n readTime: SnapshotVersion;\n}\n\ntype DocumentEntryMap = SortedMap<DocumentKey, MemoryRemoteDocumentCacheEntry>;\nfunction documentEntryMap(): DocumentEntryMap {\n return new SortedMap<DocumentKey, MemoryRemoteDocumentCacheEntry>(\n DocumentKey.comparator\n );\n}\n\nexport class MemoryRemoteDocumentCache implements RemoteDocumentCache {\n /** Underlying cache of documents and their read times. */\n private docs = documentEntryMap();\n\n /** Size of all cached documents. */\n private size = 0;\n\n /**\n * @param sizer Used to assess the size of a document. For eager GC, this is expected to just\n * return 0 to avoid unnecessarily doing the work of calculating the size.\n */\n constructor(\n private readonly indexManager: IndexManager,\n private readonly sizer: DocumentSizer\n ) {}\n\n /**\n * Adds the supplied entry to the cache and updates the cache size as appropriate.\n *\n * All calls of `addEntry` are required to go through the RemoteDocumentChangeBuffer\n * returned by `newChangeBuffer()`.\n */\n private addEntry(\n transaction: PersistenceTransaction,\n doc: MaybeDocument,\n readTime: SnapshotVersion\n ): PersistencePromise<void> {\n debugAssert(\n !readTime.isEqual(SnapshotVersion.min()),\n 'Cannot add a document with a read time of zero'\n );\n\n const key = doc.key;\n const entry = this.docs.get(key);\n const previousSize = entry ? entry.size : 0;\n const currentSize = this.sizer(doc);\n\n this.docs = this.docs.insert(key, {\n maybeDocument: doc,\n size: currentSize,\n readTime\n });\n\n this.size += currentSize - previousSize;\n\n return this.indexManager.addToCollectionParentIndex(\n transaction,\n key.path.popLast()\n );\n }\n\n /**\n * Removes the specified entry from the cache and updates the cache size as appropriate.\n *\n * All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer\n * returned by `newChangeBuffer()`.\n */\n private removeEntry(documentKey: DocumentKey): void {\n const entry = this.docs.get(documentKey);\n if (entry) {\n this.docs = this.docs.remove(documentKey);\n this.size -= entry.size;\n }\n }\n\n getEntry(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n const entry = this.docs.get(documentKey);\n return PersistencePromise.resolve(entry ? entry.maybeDocument : null);\n }\n\n getEntries(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap> {\n let results = nullableMaybeDocumentMap();\n documentKeys.forEach(documentKey => {\n const entry = this.docs.get(documentKey);\n results = results.insert(documentKey, entry ? entry.maybeDocument : null);\n });\n return PersistencePromise.resolve(results);\n }\n\n getDocumentsMatchingQuery(\n transaction: PersistenceTransaction,\n query: Query,\n sinceReadTime: SnapshotVersion\n ): PersistencePromise<DocumentMap> {\n debugAssert(\n !query.isCollectionGroupQuery(),\n 'CollectionGroup queries should be handled in LocalDocumentsView'\n );\n let results = documentMap();\n\n // Documents are ordered by key, so we can use a prefix scan to narrow down\n // the documents we need to match the query against.\n const prefix = new DocumentKey(query.path.child(''));\n const iterator = this.docs.getIteratorFrom(prefix);\n while (iterator.hasNext()) {\n const {\n key,\n value: { maybeDocument, readTime }\n } = iterator.getNext();\n if (!query.path.isPrefixOf(key.path)) {\n break;\n }\n if (readTime.compareTo(sinceReadTime) <= 0) {\n continue;\n }\n if (maybeDocument instanceof Document && query.matches(maybeDocument)) {\n results = results.insert(maybeDocument.key, maybeDocument);\n }\n }\n return PersistencePromise.resolve(results);\n }\n\n forEachDocumentKey(\n transaction: PersistenceTransaction,\n f: (key: DocumentKey) => PersistencePromise<void>\n ): PersistencePromise<void> {\n return PersistencePromise.forEach(this.docs, (key: DocumentKey) => f(key));\n }\n\n newChangeBuffer(options?: {\n trackRemovals: boolean;\n }): RemoteDocumentChangeBuffer {\n // `trackRemovals` is ignores since the MemoryRemoteDocumentCache keeps\n // a separate changelog and does not need special handling for removals.\n return new MemoryRemoteDocumentCache.RemoteDocumentChangeBuffer(this);\n }\n\n getSize(txn: PersistenceTransaction): PersistencePromise<number> {\n return PersistencePromise.resolve(this.size);\n }\n\n /**\n * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.\n */\n private static RemoteDocumentChangeBuffer = class extends RemoteDocumentChangeBuffer {\n constructor(private readonly documentCache: MemoryRemoteDocumentCache) {\n super();\n }\n\n protected applyChanges(\n transaction: PersistenceTransaction\n ): PersistencePromise<void> {\n const promises: Array<PersistencePromise<void>> = [];\n this.changes.forEach((key, doc) => {\n if (doc) {\n promises.push(\n this.documentCache.addEntry(transaction, doc, this.readTime)\n );\n } else {\n this.documentCache.removeEntry(key);\n }\n });\n return PersistencePromise.waitFor(promises);\n }\n\n protected getFromCache(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n return this.documentCache.getEntry(transaction, documentKey);\n }\n\n protected getAllFromCache(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap> {\n return this.documentCache.getEntries(transaction, documentKeys);\n }\n };\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { DocumentKeySet, NullableMaybeDocumentMap } from '../model/collections';\nimport { MaybeDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { debugAssert } from '../util/assert';\nimport { ObjectMap } from '../util/obj_map';\n\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { SnapshotVersion } from '../core/snapshot_version';\n\n/**\n * An in-memory buffer of entries to be written to a RemoteDocumentCache.\n * It can be used to batch up a set of changes to be written to the cache, but\n * additionally supports reading entries back with the `getEntry()` method,\n * falling back to the underlying RemoteDocumentCache if no entry is\n * buffered.\n *\n * Entries added to the cache *must* be read first. This is to facilitate\n * calculating the size delta of the pending changes.\n *\n * PORTING NOTE: This class was implemented then removed from other platforms.\n * If byte-counting ends up being needed on the other platforms, consider\n * porting this class as part of that implementation work.\n */\nexport abstract class RemoteDocumentChangeBuffer {\n // A mapping of document key to the new cache entry that should be written (or null if any\n // existing cache entry should be removed).\n protected changes: ObjectMap<\n DocumentKey,\n MaybeDocument | null\n > = new ObjectMap(key => key.toString());\n\n // The read time to use for all added documents in this change buffer.\n private _readTime: SnapshotVersion | undefined;\n\n private changesApplied = false;\n\n protected abstract getFromCache(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null>;\n\n protected abstract getAllFromCache(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap>;\n\n protected abstract applyChanges(\n transaction: PersistenceTransaction\n ): PersistencePromise<void>;\n\n protected set readTime(value: SnapshotVersion) {\n // Right now (for simplicity) we just track a single readTime for all the\n // added entries since we expect them to all be the same, but we could\n // rework to store per-entry readTimes if necessary.\n debugAssert(\n this._readTime === undefined || this._readTime.isEqual(value),\n 'All changes in a RemoteDocumentChangeBuffer must have the same read time'\n );\n this._readTime = value;\n }\n\n protected get readTime(): SnapshotVersion {\n debugAssert(\n this._readTime !== undefined,\n 'Read time is not set. All removeEntry() calls must include a readTime if `trackRemovals` is used.'\n );\n return this._readTime;\n }\n\n /**\n * Buffers a `RemoteDocumentCache.addEntry()` call.\n *\n * You can only modify documents that have already been retrieved via\n * `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).\n */\n addEntry(maybeDocument: MaybeDocument, readTime: SnapshotVersion): void {\n this.assertNotApplied();\n this.readTime = readTime;\n this.changes.set(maybeDocument.key, maybeDocument);\n }\n\n /**\n * Buffers a `RemoteDocumentCache.removeEntry()` call.\n *\n * You can only remove documents that have already been retrieved via\n * `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).\n */\n removeEntry(key: DocumentKey, readTime?: SnapshotVersion): void {\n this.assertNotApplied();\n if (readTime) {\n this.readTime = readTime;\n }\n this.changes.set(key, null);\n }\n\n /**\n * Looks up an entry in the cache. The buffered changes will first be checked,\n * and if no buffered change applies, this will forward to\n * `RemoteDocumentCache.getEntry()`.\n *\n * @param transaction The transaction in which to perform any persistence\n * operations.\n * @param documentKey The key of the entry to look up.\n * @return The cached Document or NoDocument entry, or null if we have nothing\n * cached.\n */\n getEntry(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n this.assertNotApplied();\n const bufferedEntry = this.changes.get(documentKey);\n if (bufferedEntry !== undefined) {\n return PersistencePromise.resolve<MaybeDocument | null>(bufferedEntry);\n } else {\n return this.getFromCache(transaction, documentKey);\n }\n }\n\n /**\n * Looks up several entries in the cache, forwarding to\n * `RemoteDocumentCache.getEntry()`.\n *\n * @param transaction The transaction in which to perform any persistence\n * operations.\n * @param documentKeys The keys of the entries to look up.\n * @return A map of cached `Document`s or `NoDocument`s, indexed by key. If an\n * entry cannot be found, the corresponding key will be mapped to a null\n * value.\n */\n getEntries(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap> {\n return this.getAllFromCache(transaction, documentKeys);\n }\n\n /**\n * Applies buffered changes to the underlying RemoteDocumentCache, using\n * the provided transaction.\n */\n apply(transaction: PersistenceTransaction): PersistencePromise<void> {\n this.assertNotApplied();\n this.changesApplied = true;\n return this.applyChanges(transaction);\n }\n\n /** Helper to assert this.changes is not null */\n protected assertNotApplied(): void {\n debugAssert(!this.changesApplied, 'Changes have already been applied.');\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { TargetIdGenerator } from '../core/target_id_generator';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { DocumentKeySet } from '../model/collections';\nimport { DocumentKey } from '../model/document_key';\nimport { debugAssert } from '../util/assert';\nimport { ObjectMap } from '../util/obj_map';\n\nimport { ActiveTargets } from './lru_garbage_collector';\nimport { MemoryPersistence } from './memory_persistence';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { ReferenceSet } from './reference_set';\nimport { TargetCache } from './target_cache';\nimport { TargetData } from './target_data';\nimport { Target } from '../core/target';\n\nexport class MemoryTargetCache implements TargetCache {\n /**\n * Maps a target to the data about that target\n */\n private targets = new ObjectMap<Target, TargetData>(t => t.canonicalId());\n\n /** The last received snapshot version. */\n private lastRemoteSnapshotVersion = SnapshotVersion.min();\n /** The highest numbered target ID encountered. */\n private highestTargetId: TargetId = 0;\n /** The highest sequence number encountered. */\n private highestSequenceNumber: ListenSequenceNumber = 0;\n /**\n * A ordered bidirectional mapping between documents and the remote target\n * IDs.\n */\n private references = new ReferenceSet();\n\n private targetCount = 0;\n\n private targetIdGenerator = TargetIdGenerator.forTargetCache();\n\n constructor(private readonly persistence: MemoryPersistence) {}\n\n forEachTarget(\n txn: PersistenceTransaction,\n f: (q: TargetData) => void\n ): PersistencePromise<void> {\n this.targets.forEach((_, targetData) => f(targetData));\n return PersistencePromise.resolve();\n }\n\n getLastRemoteSnapshotVersion(\n transaction: PersistenceTransaction\n ): PersistencePromise<SnapshotVersion> {\n return PersistencePromise.resolve(this.lastRemoteSnapshotVersion);\n }\n\n getHighestSequenceNumber(\n transaction: PersistenceTransaction\n ): PersistencePromise<ListenSequenceNumber> {\n return PersistencePromise.resolve(this.highestSequenceNumber);\n }\n\n allocateTargetId(\n transaction: PersistenceTransaction\n ): PersistencePromise<TargetId> {\n this.highestTargetId = this.targetIdGenerator.next();\n return PersistencePromise.resolve(this.highestTargetId);\n }\n\n setTargetsMetadata(\n transaction: PersistenceTransaction,\n highestListenSequenceNumber: number,\n lastRemoteSnapshotVersion?: SnapshotVersion\n ): PersistencePromise<void> {\n if (lastRemoteSnapshotVersion) {\n this.lastRemoteSnapshotVersion = lastRemoteSnapshotVersion;\n }\n if (highestListenSequenceNumber > this.highestSequenceNumber) {\n this.highestSequenceNumber = highestListenSequenceNumber;\n }\n return PersistencePromise.resolve();\n }\n\n private saveTargetData(targetData: TargetData): void {\n this.targets.set(targetData.target, targetData);\n const targetId = targetData.targetId;\n if (targetId > this.highestTargetId) {\n this.targetIdGenerator = new TargetIdGenerator(targetId);\n this.highestTargetId = targetId;\n }\n if (targetData.sequenceNumber > this.highestSequenceNumber) {\n this.highestSequenceNumber = targetData.sequenceNumber;\n }\n }\n\n addTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n debugAssert(\n !this.targets.has(targetData.target),\n 'Adding a target that already exists'\n );\n this.saveTargetData(targetData);\n this.targetCount += 1;\n return PersistencePromise.resolve();\n }\n\n updateTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n debugAssert(\n this.targets.has(targetData.target),\n 'Updating a non-existent target'\n );\n this.saveTargetData(targetData);\n return PersistencePromise.resolve();\n }\n\n removeTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n debugAssert(this.targetCount > 0, 'Removing a target from an empty cache');\n debugAssert(\n this.targets.has(targetData.target),\n 'Removing a non-existent target from the cache'\n );\n this.targets.delete(targetData.target);\n this.references.removeReferencesForId(targetData.targetId);\n this.targetCount -= 1;\n return PersistencePromise.resolve();\n }\n\n removeTargets(\n transaction: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number> {\n let count = 0;\n const removals: Array<PersistencePromise<void>> = [];\n this.targets.forEach((key, targetData) => {\n if (\n targetData.sequenceNumber <= upperBound &&\n activeTargetIds.get(targetData.targetId) === null\n ) {\n this.targets.delete(key);\n removals.push(\n this.removeMatchingKeysForTargetId(transaction, targetData.targetId)\n );\n count++;\n }\n });\n return PersistencePromise.waitFor(removals).next(() => count);\n }\n\n getTargetCount(\n transaction: PersistenceTransaction\n ): PersistencePromise<number> {\n return PersistencePromise.resolve(this.targetCount);\n }\n\n getTargetData(\n transaction: PersistenceTransaction,\n target: Target\n ): PersistencePromise<TargetData | null> {\n const targetData = this.targets.get(target) || null;\n return PersistencePromise.resolve(targetData);\n }\n\n addMatchingKeys(\n txn: PersistenceTransaction,\n keys: DocumentKeySet,\n targetId: TargetId\n ): PersistencePromise<void> {\n this.references.addReferences(keys, targetId);\n return PersistencePromise.resolve();\n }\n\n removeMatchingKeys(\n txn: PersistenceTransaction,\n keys: DocumentKeySet,\n targetId: TargetId\n ): PersistencePromise<void> {\n this.references.removeReferences(keys, targetId);\n const referenceDelegate = this.persistence.referenceDelegate;\n const promises: Array<PersistencePromise<void>> = [];\n if (referenceDelegate) {\n keys.forEach(key => {\n promises.push(referenceDelegate.markPotentiallyOrphaned(txn, key));\n });\n }\n return PersistencePromise.waitFor(promises);\n }\n\n removeMatchingKeysForTargetId(\n txn: PersistenceTransaction,\n targetId: TargetId\n ): PersistencePromise<void> {\n this.references.removeReferencesForId(targetId);\n return PersistencePromise.resolve();\n }\n\n getMatchingKeysForTargetId(\n txn: PersistenceTransaction,\n targetId: TargetId\n ): PersistencePromise<DocumentKeySet> {\n const matchingKeys = this.references.referencesForId(targetId);\n return PersistencePromise.resolve(matchingKeys);\n }\n\n containsKey(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n return PersistencePromise.resolve(this.references.containsKey(key));\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { User } from '../auth/user';\nimport { Document, MaybeDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { fail } from '../util/assert';\nimport { logDebug } from '../util/log';\nimport { ObjectMap } from '../util/obj_map';\nimport { encodeResourcePath } from './encoded_resource_path';\nimport {\n ActiveTargets,\n LruDelegate,\n LruGarbageCollector,\n LruParams\n} from './lru_garbage_collector';\nimport { ListenSequence } from '../core/listen_sequence';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { estimateByteSize } from '../model/values';\nimport { MemoryIndexManager } from './memory_index_manager';\nimport { MemoryMutationQueue } from './memory_mutation_queue';\nimport { MemoryRemoteDocumentCache } from './memory_remote_document_cache';\nimport { MemoryTargetCache } from './memory_target_cache';\nimport { MutationQueue } from './mutation_queue';\nimport {\n Persistence,\n PersistenceTransaction,\n PersistenceTransactionMode,\n ReferenceDelegate\n} from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { ReferenceSet } from './reference_set';\nimport { TargetData } from './target_data';\n\nconst LOG_TAG = 'MemoryPersistence';\n/**\n * A memory-backed instance of Persistence. Data is stored only in RAM and\n * not persisted across sessions.\n */\nexport class MemoryPersistence implements Persistence {\n /**\n * Note that these are retained here to make it easier to write tests\n * affecting both the in-memory and IndexedDB-backed persistence layers. Tests\n * can create a new LocalStore wrapping this Persistence instance and this\n * will make the in-memory persistence layer behave as if it were actually\n * persisting values.\n */\n private readonly indexManager: MemoryIndexManager;\n private mutationQueues: { [user: string]: MemoryMutationQueue } = {};\n private readonly remoteDocumentCache: MemoryRemoteDocumentCache;\n private readonly targetCache: MemoryTargetCache;\n private readonly listenSequence = new ListenSequence(0);\n\n private _started = false;\n\n readonly referenceDelegate: MemoryReferenceDelegate;\n\n /**\n * The constructor accepts a factory for creating a reference delegate. This\n * allows both the delegate and this instance to have strong references to\n * each other without having nullable fields that would then need to be\n * checked or asserted on every access.\n */\n constructor(\n referenceDelegateFactory: (p: MemoryPersistence) => MemoryReferenceDelegate\n ) {\n this._started = true;\n this.referenceDelegate = referenceDelegateFactory(this);\n this.targetCache = new MemoryTargetCache(this);\n const sizer = (doc: MaybeDocument): number =>\n this.referenceDelegate.documentSize(doc);\n this.indexManager = new MemoryIndexManager();\n this.remoteDocumentCache = new MemoryRemoteDocumentCache(\n this.indexManager,\n sizer\n );\n }\n\n start(): Promise<void> {\n return Promise.resolve();\n }\n\n shutdown(): Promise<void> {\n // No durable state to ensure is closed on shutdown.\n this._started = false;\n return Promise.resolve();\n }\n\n get started(): boolean {\n return this._started;\n }\n\n setDatabaseDeletedListener(): void {\n // No op.\n }\n\n getIndexManager(): MemoryIndexManager {\n return this.indexManager;\n }\n\n getMutationQueue(user: User): MutationQueue {\n let queue = this.mutationQueues[user.toKey()];\n if (!queue) {\n queue = new MemoryMutationQueue(\n this.indexManager,\n this.referenceDelegate\n );\n this.mutationQueues[user.toKey()] = queue;\n }\n return queue;\n }\n\n getTargetCache(): MemoryTargetCache {\n return this.targetCache;\n }\n\n getRemoteDocumentCache(): MemoryRemoteDocumentCache {\n return this.remoteDocumentCache;\n }\n\n runTransaction<T>(\n action: string,\n mode: PersistenceTransactionMode,\n transactionOperation: (\n transaction: PersistenceTransaction\n ) => PersistencePromise<T>\n ): Promise<T> {\n logDebug(LOG_TAG, 'Starting transaction:', action);\n const txn = new MemoryTransaction(this.listenSequence.next());\n this.referenceDelegate.onTransactionStarted();\n return transactionOperation(txn)\n .next(result => {\n return this.referenceDelegate\n .onTransactionCommitted(txn)\n .next(() => result);\n })\n .toPromise()\n .then(result => {\n txn.raiseOnCommittedEvent();\n return result;\n });\n }\n\n mutationQueuesContainKey(\n transaction: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n return PersistencePromise.or(\n Object.values(this.mutationQueues).map(queue => () =>\n queue.containsKey(transaction, key)\n )\n );\n }\n}\n\n/**\n * Memory persistence is not actually transactional, but future implementations\n * may have transaction-scoped state.\n */\nexport class MemoryTransaction extends PersistenceTransaction {\n constructor(readonly currentSequenceNumber: ListenSequenceNumber) {\n super();\n }\n}\n\nexport interface MemoryReferenceDelegate extends ReferenceDelegate {\n documentSize(doc: MaybeDocument): number;\n onTransactionStarted(): void;\n onTransactionCommitted(txn: PersistenceTransaction): PersistencePromise<void>;\n}\n\nexport class MemoryEagerDelegate implements MemoryReferenceDelegate {\n /** Tracks all documents that are active in Query views. */\n private localViewReferences: ReferenceSet = new ReferenceSet();\n /** The list of documents that are potentially GCed after each transaction. */\n private _orphanedDocuments: Set<DocumentKey> | null = null;\n\n private constructor(private readonly persistence: MemoryPersistence) {}\n\n static factory(persistence: MemoryPersistence): MemoryEagerDelegate {\n return new MemoryEagerDelegate(persistence);\n }\n\n private get orphanedDocuments(): Set<DocumentKey> {\n if (!this._orphanedDocuments) {\n throw fail('orphanedDocuments is only valid during a transaction.');\n } else {\n return this._orphanedDocuments;\n }\n }\n\n addReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.localViewReferences.addReference(key, targetId);\n this.orphanedDocuments.delete(key);\n return PersistencePromise.resolve();\n }\n\n removeReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.localViewReferences.removeReference(key, targetId);\n this.orphanedDocuments.add(key);\n return PersistencePromise.resolve();\n }\n\n markPotentiallyOrphaned(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.orphanedDocuments.add(key);\n return PersistencePromise.resolve();\n }\n\n removeTarget(\n txn: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n const orphaned = this.localViewReferences.removeReferencesForId(\n targetData.targetId\n );\n orphaned.forEach(key => this.orphanedDocuments.add(key));\n const cache = this.persistence.getTargetCache();\n return cache\n .getMatchingKeysForTargetId(txn, targetData.targetId)\n .next(keys => {\n keys.forEach(key => this.orphanedDocuments.add(key));\n })\n .next(() => cache.removeTargetData(txn, targetData));\n }\n\n onTransactionStarted(): void {\n this._orphanedDocuments = new Set<DocumentKey>();\n }\n\n onTransactionCommitted(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n // Remove newly orphaned documents.\n const cache = this.persistence.getRemoteDocumentCache();\n const changeBuffer = cache.newChangeBuffer();\n return PersistencePromise.forEach(\n this.orphanedDocuments,\n (key: DocumentKey) => {\n return this.isReferenced(txn, key).next(isReferenced => {\n if (!isReferenced) {\n changeBuffer.removeEntry(key);\n }\n });\n }\n ).next(() => {\n this._orphanedDocuments = null;\n return changeBuffer.apply(txn);\n });\n }\n\n updateLimboDocument(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n return this.isReferenced(txn, key).next(isReferenced => {\n if (isReferenced) {\n this.orphanedDocuments.delete(key);\n } else {\n this.orphanedDocuments.add(key);\n }\n });\n }\n\n documentSize(doc: MaybeDocument): number {\n // For eager GC, we don't care about the document size, there are no size thresholds.\n return 0;\n }\n\n private isReferenced(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n return PersistencePromise.or([\n () =>\n PersistencePromise.resolve(this.localViewReferences.containsKey(key)),\n () => this.persistence.getTargetCache().containsKey(txn, key),\n () => this.persistence.mutationQueuesContainKey(txn, key)\n ]);\n }\n}\n\nexport class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {\n private orphanedSequenceNumbers: ObjectMap<\n DocumentKey,\n ListenSequenceNumber\n > = new ObjectMap(k => encodeResourcePath(k.path));\n\n readonly garbageCollector: LruGarbageCollector;\n\n constructor(\n private readonly persistence: MemoryPersistence,\n lruParams: LruParams\n ) {\n this.garbageCollector = new LruGarbageCollector(this, lruParams);\n }\n\n // No-ops, present so memory persistence doesn't have to care which delegate\n // it has.\n onTransactionStarted(): void {}\n\n onTransactionCommitted(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n return PersistencePromise.resolve();\n }\n\n forEachTarget(\n txn: PersistenceTransaction,\n f: (q: TargetData) => void\n ): PersistencePromise<void> {\n return this.persistence.getTargetCache().forEachTarget(txn, f);\n }\n\n getSequenceNumberCount(\n txn: PersistenceTransaction\n ): PersistencePromise<number> {\n const docCountPromise = this.orphanedDocumentCount(txn);\n const targetCountPromise = this.persistence\n .getTargetCache()\n .getTargetCount(txn);\n return targetCountPromise.next(targetCount =>\n docCountPromise.next(docCount => targetCount + docCount)\n );\n }\n\n private orphanedDocumentCount(\n txn: PersistenceTransaction\n ): PersistencePromise<number> {\n let orphanedCount = 0;\n return this.forEachOrphanedDocumentSequenceNumber(txn, _ => {\n orphanedCount++;\n }).next(() => orphanedCount);\n }\n\n forEachOrphanedDocumentSequenceNumber(\n txn: PersistenceTransaction,\n f: (sequenceNumber: ListenSequenceNumber) => void\n ): PersistencePromise<void> {\n return PersistencePromise.forEach(\n this.orphanedSequenceNumbers,\n (key, sequenceNumber) => {\n // Pass in the exact sequence number as the upper bound so we know it won't be pinned by\n // being too recent.\n return this.isPinned(txn, key, sequenceNumber).next(isPinned => {\n if (!isPinned) {\n return f(sequenceNumber);\n } else {\n return PersistencePromise.resolve();\n }\n });\n }\n );\n }\n\n removeTargets(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number> {\n return this.persistence\n .getTargetCache()\n .removeTargets(txn, upperBound, activeTargetIds);\n }\n\n removeOrphanedDocuments(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber\n ): PersistencePromise<number> {\n let count = 0;\n const cache = this.persistence.getRemoteDocumentCache();\n const changeBuffer = cache.newChangeBuffer();\n const p = cache.forEachDocumentKey(txn, key => {\n return this.isPinned(txn, key, upperBound).next(isPinned => {\n if (!isPinned) {\n count++;\n changeBuffer.removeEntry(key);\n }\n });\n });\n return p.next(() => changeBuffer.apply(txn)).next(() => count);\n }\n\n markPotentiallyOrphaned(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);\n return PersistencePromise.resolve();\n }\n\n removeTarget(\n txn: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n const updated = targetData.withSequenceNumber(txn.currentSequenceNumber);\n return this.persistence.getTargetCache().updateTargetData(txn, updated);\n }\n\n addReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);\n return PersistencePromise.resolve();\n }\n\n removeReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);\n return PersistencePromise.resolve();\n }\n\n updateLimboDocument(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n this.orphanedSequenceNumbers.set(key, txn.currentSequenceNumber);\n return PersistencePromise.resolve();\n }\n\n documentSize(maybeDoc: MaybeDocument): number {\n let documentSize = maybeDoc.key.toString().length;\n if (maybeDoc instanceof Document) {\n documentSize += estimateByteSize(maybeDoc.toProto());\n }\n return documentSize;\n }\n\n private isPinned(\n txn: PersistenceTransaction,\n key: DocumentKey,\n upperBound: ListenSequenceNumber\n ): PersistencePromise<boolean> {\n return PersistencePromise.or([\n () => this.persistence.mutationQueuesContainKey(txn, key),\n () => this.persistence.getTargetCache().containsKey(txn, key),\n () => {\n const orphanedAt = this.orphanedSequenceNumbers.get(key);\n return PersistencePromise.resolve(\n orphanedAt !== undefined && orphanedAt > upperBound\n );\n }\n ]);\n }\n\n getCacheSize(txn: PersistenceTransaction): PersistencePromise<number> {\n return this.persistence.getRemoteDocumentCache().getSize(txn);\n }\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport {\n ClientId,\n MemorySharedClientState,\n SharedClientState,\n WebStorageSharedClientState\n} from '../local/shared_client_state';\nimport { LocalStore, MultiTabLocalStore } from '../local/local_store';\nimport { MultiTabSyncEngine, SyncEngine } from './sync_engine';\nimport { RemoteStore } from '../remote/remote_store';\nimport { EventManager } from './event_manager';\nimport { AsyncQueue } from '../util/async_queue';\nimport { DatabaseInfo } from './database_info';\nimport { Platform } from '../platform/platform';\nimport { Datastore } from '../remote/datastore';\nimport { User } from '../auth/user';\nimport { PersistenceSettings } from './firestore_client';\nimport { debugAssert } from '../util/assert';\nimport { GarbageCollectionScheduler, Persistence } from '../local/persistence';\nimport { Code, FirestoreError } from '../util/error';\nimport { OnlineStateSource } from './types';\nimport { LruParams, LruScheduler } from '../local/lru_garbage_collector';\nimport { IndexFreeQueryEngine } from '../local/index_free_query_engine';\nimport { IndexedDbPersistence } from '../local/indexeddb_persistence';\nimport {\n MemoryEagerDelegate,\n MemoryPersistence\n} from '../local/memory_persistence';\n\nconst MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE =\n 'You are using the memory-only build of Firestore. Persistence support is ' +\n 'only available via the @firebase/firestore bundle or the ' +\n 'firebase-firestore.js build.';\n\nexport interface ComponentConfiguration {\n asyncQueue: AsyncQueue;\n databaseInfo: DatabaseInfo;\n platform: Platform;\n datastore: Datastore;\n clientId: ClientId;\n initialUser: User;\n maxConcurrentLimboResolutions: number;\n persistenceSettings: PersistenceSettings;\n}\n\n/**\n * Initializes and wires up all core components for Firestore. Implementations\n * override `initialize()` to provide all components.\n */\nexport interface ComponentProvider {\n persistence: Persistence;\n sharedClientState: SharedClientState;\n localStore: LocalStore;\n syncEngine: SyncEngine;\n gcScheduler: GarbageCollectionScheduler | null;\n remoteStore: RemoteStore;\n eventManager: EventManager;\n\n initialize(cfg: ComponentConfiguration): Promise<void>;\n\n clearPersistence(databaseId: DatabaseInfo): Promise<void>;\n}\n\n/**\n * Provides all components needed for Firestore with in-memory persistence.\n * Uses EagerGC garbage collection.\n */\nexport class MemoryComponentProvider implements ComponentProvider {\n persistence!: Persistence;\n sharedClientState!: SharedClientState;\n localStore!: LocalStore;\n syncEngine!: SyncEngine;\n gcScheduler!: GarbageCollectionScheduler | null;\n remoteStore!: RemoteStore;\n eventManager!: EventManager;\n\n async initialize(cfg: ComponentConfiguration): Promise<void> {\n this.sharedClientState = this.createSharedClientState(cfg);\n this.persistence = this.createPersistence(cfg);\n await this.persistence.start();\n this.gcScheduler = this.createGarbageCollectionScheduler(cfg);\n this.localStore = this.createLocalStore(cfg);\n this.remoteStore = this.createRemoteStore(cfg);\n this.syncEngine = this.createSyncEngine(cfg);\n this.eventManager = this.createEventManager(cfg);\n\n this.sharedClientState.onlineStateHandler = onlineState =>\n this.syncEngine.applyOnlineStateChange(\n onlineState,\n OnlineStateSource.SharedClientState\n );\n this.remoteStore.syncEngine = this.syncEngine;\n\n await this.localStore.start();\n await this.sharedClientState.start();\n await this.remoteStore.start();\n\n await this.remoteStore.applyPrimaryState(this.syncEngine.isPrimaryClient);\n }\n\n createEventManager(cfg: ComponentConfiguration): EventManager {\n return new EventManager(this.syncEngine);\n }\n\n createGarbageCollectionScheduler(\n cfg: ComponentConfiguration\n ): GarbageCollectionScheduler | null {\n return null;\n }\n\n createLocalStore(cfg: ComponentConfiguration): LocalStore {\n return new LocalStore(\n this.persistence,\n new IndexFreeQueryEngine(),\n cfg.initialUser\n );\n }\n\n createPersistence(cfg: ComponentConfiguration): Persistence {\n debugAssert(\n !cfg.persistenceSettings.durable,\n 'Can only start memory persistence'\n );\n return new MemoryPersistence(MemoryEagerDelegate.factory);\n }\n\n createRemoteStore(cfg: ComponentConfiguration): RemoteStore {\n return new RemoteStore(\n this.localStore,\n cfg.datastore,\n cfg.asyncQueue,\n onlineState =>\n this.syncEngine.applyOnlineStateChange(\n onlineState,\n OnlineStateSource.RemoteStore\n ),\n cfg.platform.newConnectivityMonitor()\n );\n }\n\n createSharedClientState(cfg: ComponentConfiguration): SharedClientState {\n return new MemorySharedClientState();\n }\n\n createSyncEngine(cfg: ComponentConfiguration): SyncEngine {\n return new SyncEngine(\n this.localStore,\n this.remoteStore,\n this.sharedClientState,\n cfg.initialUser,\n cfg.maxConcurrentLimboResolutions\n );\n }\n\n clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n MEMORY_ONLY_PERSISTENCE_ERROR_MESSAGE\n );\n }\n}\n\n/**\n * Provides all components needed for Firestore with IndexedDB persistence.\n */\nexport class IndexedDbComponentProvider extends MemoryComponentProvider {\n persistence!: IndexedDbPersistence;\n\n // TODO(tree-shaking): Create an IndexedDbComponentProvider and a\n // MultiTabComponentProvider. The IndexedDbComponentProvider should depend\n // on LocalStore and SyncEngine.\n localStore!: MultiTabLocalStore;\n syncEngine!: MultiTabSyncEngine;\n\n async initialize(cfg: ComponentConfiguration): Promise<void> {\n await super.initialize(cfg);\n\n // NOTE: This will immediately call the listener, so we make sure to\n // set it after localStore / remoteStore are started.\n await this.persistence.setPrimaryStateListener(async isPrimary => {\n await (this.syncEngine as MultiTabSyncEngine).applyPrimaryState(\n isPrimary\n );\n if (this.gcScheduler) {\n if (isPrimary && !this.gcScheduler.started) {\n this.gcScheduler.start(this.localStore);\n } else if (!isPrimary) {\n this.gcScheduler.stop();\n }\n }\n });\n }\n\n createLocalStore(cfg: ComponentConfiguration): LocalStore {\n return new MultiTabLocalStore(\n this.persistence,\n new IndexFreeQueryEngine(),\n cfg.initialUser\n );\n }\n\n createSyncEngine(cfg: ComponentConfiguration): SyncEngine {\n const syncEngine = new MultiTabSyncEngine(\n this.localStore,\n this.remoteStore,\n this.sharedClientState,\n cfg.initialUser,\n cfg.maxConcurrentLimboResolutions\n );\n if (this.sharedClientState instanceof WebStorageSharedClientState) {\n this.sharedClientState.syncEngine = syncEngine;\n }\n return syncEngine;\n }\n\n createGarbageCollectionScheduler(\n cfg: ComponentConfiguration\n ): GarbageCollectionScheduler | null {\n const garbageCollector = this.persistence.referenceDelegate\n .garbageCollector;\n return new LruScheduler(garbageCollector, cfg.asyncQueue);\n }\n\n createPersistence(cfg: ComponentConfiguration): Persistence {\n debugAssert(\n cfg.persistenceSettings.durable,\n 'Can only start durable persistence'\n );\n\n const persistenceKey = IndexedDbPersistence.buildStoragePrefix(\n cfg.databaseInfo\n );\n const serializer = cfg.platform.newSerializer(cfg.databaseInfo.databaseId);\n return new IndexedDbPersistence(\n cfg.persistenceSettings.synchronizeTabs,\n persistenceKey,\n cfg.clientId,\n cfg.platform,\n LruParams.withCacheSize(cfg.persistenceSettings.cacheSizeBytes),\n cfg.asyncQueue,\n serializer,\n this.sharedClientState\n );\n }\n\n createSharedClientState(cfg: ComponentConfiguration): SharedClientState {\n if (\n cfg.persistenceSettings.durable &&\n cfg.persistenceSettings.synchronizeTabs\n ) {\n if (!WebStorageSharedClientState.isAvailable(cfg.platform)) {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'IndexedDB persistence is only available on platforms that support LocalStorage.'\n );\n }\n const persistenceKey = IndexedDbPersistence.buildStoragePrefix(\n cfg.databaseInfo\n );\n return new WebStorageSharedClientState(\n cfg.asyncQueue,\n cfg.platform,\n persistenceKey,\n cfg.clientId,\n cfg.initialUser\n );\n }\n return new MemorySharedClientState();\n }\n\n clearPersistence(databaseInfo: DatabaseInfo): Promise<void> {\n const persistenceKey = IndexedDbPersistence.buildStoragePrefix(\n databaseInfo\n );\n return IndexedDbPersistence.clearPersistence(persistenceKey);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { CredentialsProvider } from '../api/credentials';\nimport { User } from '../auth/user';\nimport { LocalStore } from '../local/local_store';\nimport { GarbageCollectionScheduler, Persistence } from '../local/persistence';\nimport { Document, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation } from '../model/mutation';\nimport { Platform } from '../platform/platform';\nimport { newDatastore } from '../remote/datastore';\nimport { RemoteStore } from '../remote/remote_store';\nimport { AsyncQueue, wrapInUserErrorIfRecoverable } from '../util/async_queue';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { Deferred } from '../util/promise';\nimport {\n EventManager,\n ListenOptions,\n Observer,\n QueryListener\n} from './event_manager';\nimport { SyncEngine } from './sync_engine';\nimport { View } from './view';\n\nimport { SharedClientState } from '../local/shared_client_state';\nimport { AutoId } from '../util/misc';\nimport { DatabaseId, DatabaseInfo } from './database_info';\nimport { Query } from './query';\nimport { Transaction } from './transaction';\nimport { ViewSnapshot } from './view_snapshot';\nimport {\n ComponentProvider,\n MemoryComponentProvider\n} from './component_provider';\n\nconst LOG_TAG = 'FirestoreClient';\nconst MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100;\n\n/** DOMException error code constants. */\nconst DOM_EXCEPTION_INVALID_STATE = 11;\nconst DOM_EXCEPTION_ABORTED = 20;\nconst DOM_EXCEPTION_QUOTA_EXCEEDED = 22;\n\nexport type PersistenceSettings =\n | {\n readonly durable: false;\n }\n | {\n readonly durable: true;\n readonly cacheSizeBytes: number;\n readonly synchronizeTabs: boolean;\n };\n\n/**\n * FirestoreClient is a top-level class that constructs and owns all of the\n * pieces of the client SDK architecture. It is responsible for creating the\n * async queue that is shared by all of the other components in the system.\n */\nexport class FirestoreClient {\n // NOTE: These should technically have '|undefined' in the types, since\n // they're initialized asynchronously rather than in the constructor, but\n // given that all work is done on the async queue and we assert that\n // initialization completes before any other work is queued, we're cheating\n // with the types rather than littering the code with '!' or unnecessary\n // undefined checks.\n private eventMgr!: EventManager;\n private persistence!: Persistence;\n private localStore!: LocalStore;\n private remoteStore!: RemoteStore;\n private syncEngine!: SyncEngine;\n private gcScheduler!: GarbageCollectionScheduler | null;\n\n // PORTING NOTE: SharedClientState is only used for multi-tab web.\n private sharedClientState!: SharedClientState;\n\n private readonly clientId = AutoId.newId();\n\n constructor(\n private platform: Platform,\n private databaseInfo: DatabaseInfo,\n private credentials: CredentialsProvider,\n /**\n * Asynchronous queue responsible for all of our internal processing. When\n * we get incoming work from the user (via public API) or the network\n * (incoming GRPC messages), we should always schedule onto this queue.\n * This ensures all of our work is properly serialized (e.g. we don't\n * start processing a new operation while the previous one is waiting for\n * an async I/O to complete).\n */\n private asyncQueue: AsyncQueue\n ) {}\n\n /**\n * Starts up the FirestoreClient, returning only whether or not enabling\n * persistence succeeded.\n *\n * The intent here is to \"do the right thing\" as far as users are concerned.\n * Namely, in cases where offline persistence is requested and possible,\n * enable it, but otherwise fall back to persistence disabled. For the most\n * part we expect this to succeed one way or the other so we don't expect our\n * users to actually wait on the firestore.enablePersistence Promise since\n * they generally won't care.\n *\n * Of course some users actually do care about whether or not persistence\n * was successfully enabled, so the Promise returned from this method\n * indicates this outcome.\n *\n * This presents a problem though: even before enablePersistence resolves or\n * rejects, users may have made calls to e.g. firestore.collection() which\n * means that the FirestoreClient in there will be available and will be\n * enqueuing actions on the async queue.\n *\n * Meanwhile any failure of an operation on the async queue causes it to\n * panic and reject any further work, on the premise that unhandled errors\n * are fatal.\n *\n * Consequently the fallback is handled internally here in start, and if the\n * fallback succeeds we signal success to the async queue even though the\n * start() itself signals failure.\n *\n * @param componentProvider Provider that returns all core components.\n * @param persistenceSettings Settings object to configure offline\n * persistence.\n * @returns A deferred result indicating the user-visible result of enabling\n * offline persistence. This method will reject this if IndexedDB fails to\n * start for any reason. If usePersistence is false this is\n * unconditionally resolved.\n */\n start(\n componentProvider: ComponentProvider,\n persistenceSettings: PersistenceSettings\n ): Promise<void> {\n this.verifyNotTerminated();\n // We defer our initialization until we get the current user from\n // setChangeListener(). We block the async queue until we got the initial\n // user and the initialization is completed. This will prevent any scheduled\n // work from happening before initialization is completed.\n //\n // If initializationDone resolved then the FirestoreClient is in a usable\n // state.\n const initializationDone = new Deferred<void>();\n\n // If usePersistence is true, certain classes of errors while starting are\n // recoverable but only by falling back to persistence disabled.\n //\n // If there's an error in the first case but not in recovery we cannot\n // reject the promise blocking the async queue because this will cause the\n // async queue to panic.\n const persistenceResult = new Deferred<void>();\n\n let initialized = false;\n this.credentials.setChangeListener(user => {\n if (!initialized) {\n initialized = true;\n\n logDebug(LOG_TAG, 'Initializing. user=', user.uid);\n\n return this.initializeComponents(\n componentProvider,\n persistenceSettings,\n user,\n persistenceResult\n ).then(initializationDone.resolve, initializationDone.reject);\n } else {\n this.asyncQueue.enqueueRetryable(() => {\n return this.handleCredentialChange(user);\n });\n }\n });\n\n // Block the async queue until initialization is done\n this.asyncQueue.enqueueAndForget(() => {\n return initializationDone.promise;\n });\n\n // Return only the result of enabling persistence. Note that this does not\n // need to await the completion of initializationDone because the result of\n // this method should not reflect any other kind of failure to start.\n return persistenceResult.promise;\n }\n\n /** Enables the network connection and requeues all pending operations. */\n enableNetwork(): Promise<void> {\n this.verifyNotTerminated();\n return this.asyncQueue.enqueue(() => {\n return this.syncEngine.enableNetwork();\n });\n }\n\n /**\n * Initializes persistent storage, attempting to use IndexedDB if\n * usePersistence is true or memory-only if false.\n *\n * If IndexedDB fails because it's already open in another tab or because the\n * platform can't possibly support our implementation then this method rejects\n * the persistenceResult and falls back on memory-only persistence.\n *\n * @param componentProvider The provider that provides all core componennts\n * for IndexedDB or memory-backed persistence\n * @param persistenceSettings Settings object to configure offline persistence\n * @param user The initial user\n * @param persistenceResult A deferred result indicating the user-visible\n * result of enabling offline persistence. This method will reject this if\n * IndexedDB fails to start for any reason. If usePersistence is false\n * this is unconditionally resolved.\n * @returns a Promise indicating whether or not initialization should\n * continue, i.e. that one of the persistence implementations actually\n * succeeded.\n */\n private async initializeComponents(\n componentProvider: ComponentProvider,\n persistenceSettings: PersistenceSettings,\n user: User,\n persistenceResult: Deferred<void>\n ): Promise<void> {\n try {\n // TODO(mrschmidt): Ideally, ComponentProvider would also initialize\n // Datastore (without duplicating the initializing logic once per\n // provider).\n\n const connection = await this.platform.loadConnection(this.databaseInfo);\n const serializer = this.platform.newSerializer(\n this.databaseInfo.databaseId\n );\n const datastore = newDatastore(connection, this.credentials, serializer);\n\n await componentProvider.initialize({\n asyncQueue: this.asyncQueue,\n databaseInfo: this.databaseInfo,\n platform: this.platform,\n datastore,\n clientId: this.clientId,\n initialUser: user,\n maxConcurrentLimboResolutions: MAX_CONCURRENT_LIMBO_RESOLUTIONS,\n persistenceSettings\n });\n\n this.persistence = componentProvider.persistence;\n this.sharedClientState = componentProvider.sharedClientState;\n this.localStore = componentProvider.localStore;\n this.remoteStore = componentProvider.remoteStore;\n this.syncEngine = componentProvider.syncEngine;\n this.gcScheduler = componentProvider.gcScheduler;\n this.eventMgr = componentProvider.eventManager;\n\n // When a user calls clearPersistence() in one client, all other clients\n // need to be terminated to allow the delete to succeed.\n this.persistence.setDatabaseDeletedListener(async () => {\n await this.terminate();\n });\n\n persistenceResult.resolve();\n } catch (error) {\n // Regardless of whether or not the retry succeeds, from an user\n // perspective, offline persistence has failed.\n persistenceResult.reject(error);\n\n // An unknown failure on the first stage shuts everything down.\n if (!this.canFallback(error)) {\n throw error;\n }\n console.warn(\n 'Error enabling offline persistence. Falling back to' +\n ' persistence disabled: ' +\n error\n );\n return this.initializeComponents(\n new MemoryComponentProvider(),\n { durable: false },\n user,\n persistenceResult\n );\n }\n }\n\n /**\n * Decides whether the provided error allows us to gracefully disable\n * persistence (as opposed to crashing the client).\n */\n private canFallback(error: FirestoreError | DOMException): boolean {\n if (error.name === 'FirebaseError') {\n return (\n error.code === Code.FAILED_PRECONDITION ||\n error.code === Code.UNIMPLEMENTED\n );\n } else if (\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException\n ) {\n // There are a few known circumstances where we can open IndexedDb but\n // trying to read/write will fail (e.g. quota exceeded). For\n // well-understood cases, we attempt to detect these and then gracefully\n // fall back to memory persistence.\n // NOTE: Rather than continue to add to this list, we could decide to\n // always fall back, with the risk that we might accidentally hide errors\n // representing actual SDK bugs.\n return (\n // When the browser is out of quota we could get either quota exceeded\n // or an aborted error depending on whether the error happened during\n // schema migration.\n error.code === DOM_EXCEPTION_QUOTA_EXCEEDED ||\n error.code === DOM_EXCEPTION_ABORTED ||\n // Firefox Private Browsing mode disables IndexedDb and returns\n // INVALID_STATE for any usage.\n error.code === DOM_EXCEPTION_INVALID_STATE\n );\n }\n\n return true;\n }\n\n /**\n * Checks that the client has not been terminated. Ensures that other methods on\n * this class cannot be called after the client is terminated.\n */\n private verifyNotTerminated(): void {\n if (this.asyncQueue.isShuttingDown) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'The client has already been terminated.'\n );\n }\n }\n\n private handleCredentialChange(user: User): Promise<void> {\n this.asyncQueue.verifyOperationInProgress();\n\n logDebug(LOG_TAG, 'Credential Changed. Current user: ' + user.uid);\n return this.syncEngine.handleCredentialChange(user);\n }\n\n /** Disables the network connection. Pending operations will not complete. */\n disableNetwork(): Promise<void> {\n this.verifyNotTerminated();\n return this.asyncQueue.enqueue(() => {\n return this.syncEngine.disableNetwork();\n });\n }\n\n terminate(): Promise<void> {\n return this.asyncQueue.enqueueAndInitiateShutdown(async () => {\n // PORTING NOTE: LocalStore does not need an explicit shutdown on web.\n if (this.gcScheduler) {\n this.gcScheduler.stop();\n }\n\n await this.remoteStore.shutdown();\n await this.sharedClientState.shutdown();\n await this.persistence.shutdown();\n\n // `removeChangeListener` must be called after shutting down the\n // RemoteStore as it will prevent the RemoteStore from retrieving\n // auth tokens.\n this.credentials.removeChangeListener();\n });\n }\n\n /**\n * Returns a Promise that resolves when all writes that were pending at the time this\n * method was called received server acknowledgement. An acknowledgement can be either acceptance\n * or rejection.\n */\n waitForPendingWrites(): Promise<void> {\n this.verifyNotTerminated();\n\n const deferred = new Deferred<void>();\n this.asyncQueue.enqueueAndForget(() => {\n return this.syncEngine.registerPendingWritesCallback(deferred);\n });\n return deferred.promise;\n }\n\n listen(\n query: Query,\n observer: Observer<ViewSnapshot>,\n options: ListenOptions\n ): QueryListener {\n this.verifyNotTerminated();\n const listener = new QueryListener(query, observer, options);\n this.asyncQueue.enqueueAndForget(() => this.eventMgr.listen(listener));\n return listener;\n }\n\n unlisten(listener: QueryListener): void {\n // Checks for termination but does not raise error, allowing unlisten after\n // termination to be a no-op.\n if (this.clientTerminated) {\n return;\n }\n this.asyncQueue.enqueueAndForget(() => {\n return this.eventMgr.unlisten(listener);\n });\n }\n\n async getDocumentFromLocalCache(\n docKey: DocumentKey\n ): Promise<Document | null> {\n this.verifyNotTerminated();\n const deferred = new Deferred<Document | null>();\n await this.asyncQueue.enqueue(async () => {\n try {\n const maybeDoc = await this.localStore.readDocument(docKey);\n if (maybeDoc instanceof Document) {\n deferred.resolve(maybeDoc);\n } else if (maybeDoc instanceof NoDocument) {\n deferred.resolve(null);\n } else {\n deferred.reject(\n new FirestoreError(\n Code.UNAVAILABLE,\n 'Failed to get document from cache. (However, this document may ' +\n \"exist on the server. Run again without setting 'source' in \" +\n 'the GetOptions to attempt to retrieve the document from the ' +\n 'server.)'\n )\n );\n }\n } catch (e) {\n const firestoreError = wrapInUserErrorIfRecoverable(\n e,\n `Failed to get document '${docKey} from cache`\n );\n deferred.reject(firestoreError);\n }\n });\n\n return deferred.promise;\n }\n\n async getDocumentsFromLocalCache(query: Query): Promise<ViewSnapshot> {\n this.verifyNotTerminated();\n const deferred = new Deferred<ViewSnapshot>();\n await this.asyncQueue.enqueue(async () => {\n try {\n const queryResult = await this.localStore.executeQuery(\n query,\n /* usePreviousResults= */ true\n );\n const view = new View(query, queryResult.remoteKeys);\n const viewDocChanges = view.computeDocChanges(queryResult.documents);\n const viewChange = view.applyChanges(\n viewDocChanges,\n /* updateLimboDocuments= */ false\n );\n deferred.resolve(viewChange.snapshot!);\n } catch (e) {\n const firestoreError = wrapInUserErrorIfRecoverable(\n e,\n `Failed to execute query '${query} against cache`\n );\n deferred.reject(firestoreError);\n }\n });\n return deferred.promise;\n }\n\n write(mutations: Mutation[]): Promise<void> {\n this.verifyNotTerminated();\n const deferred = new Deferred<void>();\n this.asyncQueue.enqueueAndForget(() =>\n this.syncEngine.write(mutations, deferred)\n );\n return deferred.promise;\n }\n\n databaseId(): DatabaseId {\n return this.databaseInfo.databaseId;\n }\n\n addSnapshotsInSyncListener(observer: Observer<void>): void {\n this.verifyNotTerminated();\n this.asyncQueue.enqueueAndForget(() => {\n this.eventMgr.addSnapshotsInSyncListener(observer);\n return Promise.resolve();\n });\n }\n\n removeSnapshotsInSyncListener(observer: Observer<void>): void {\n // Checks for shutdown but does not raise error, allowing remove after\n // shutdown to be a no-op.\n if (this.clientTerminated) {\n return;\n }\n this.asyncQueue.enqueueAndForget(() => {\n this.eventMgr.removeSnapshotsInSyncListener(observer);\n return Promise.resolve();\n });\n }\n\n get clientTerminated(): boolean {\n // Technically, the asyncQueue is still running, but only accepting operations\n // related to termination or supposed to be run after termination. It is effectively\n // terminated to the eyes of users.\n return this.asyncQueue.isShuttingDown;\n }\n\n transaction<T>(\n updateFunction: (transaction: Transaction) => Promise<T>\n ): Promise<T> {\n this.verifyNotTerminated();\n const deferred = new Deferred<T>();\n this.asyncQueue.enqueueAndForget(() => {\n this.syncEngine.runTransaction(this.asyncQueue, updateFunction, deferred);\n return Promise.resolve();\n });\n return deferred.promise;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Observer } from '../core/event_manager';\nimport { EventHandler } from './misc';\n\n/*\n * A wrapper implementation of Observer<T> that will dispatch events\n * asynchronously. To allow immediate silencing, a mute call is added which\n * causes events scheduled to no longer be raised.\n */\nexport class AsyncObserver<T> implements Observer<T> {\n /**\n * When set to true, will not raise future events. Necessary to deal with\n * async detachment of listener.\n */\n private muted = false;\n\n constructor(private observer: Observer<T>) {}\n\n next(value: T): void {\n this.scheduleEvent(this.observer.next, value);\n }\n\n error(error: Error): void {\n this.scheduleEvent(this.observer.error, error);\n }\n\n mute(): void {\n this.muted = true;\n }\n\n private scheduleEvent<E>(eventHandler: EventHandler<E>, event: E): void {\n if (!this.muted) {\n setTimeout(() => {\n if (!this.muted) {\n eventHandler(event);\n }\n }, 0);\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { JsonObject } from '../model/object_value';\n\n/**\n * Observer/Subscribe interfaces.\n */\nexport type NextFn<T> = (value: T) => void;\nexport type ErrorFn = (error: Error) => void;\nexport type CompleteFn = () => void;\n\n// Allow for any of the Observer methods to be undefined.\nexport interface PartialObserver<T> {\n next?: NextFn<T>;\n error?: ErrorFn;\n complete?: CompleteFn;\n}\n\nexport interface Unsubscribe {\n (): void;\n}\n\nexport function isPartialObserver(obj: unknown): boolean {\n return implementsAnyMethods(obj, ['next', 'error', 'complete']);\n}\n\n/**\n * Returns true if obj is an object and contains at least one of the specified\n * methods.\n */\nfunction implementsAnyMethods(obj: unknown, methods: string[]): boolean {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n const object = obj as JsonObject<unknown>;\n for (const method of methods) {\n if (method in object && typeof object[method] === 'function') {\n return true;\n }\n }\n return false;\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\n\nimport * as api from '../protos/firestore_proto_api';\n\nimport { DocumentReference, Firestore } from './database';\nimport { Blob } from './blob';\nimport { GeoPoint } from './geo_point';\nimport { Timestamp } from './timestamp';\nimport { DatabaseId } from '../core/database_info';\nimport { DocumentKey } from '../model/document_key';\nimport {\n normalizeByteString,\n normalizeNumber,\n normalizeTimestamp,\n typeOrder\n} from '../model/values';\nimport {\n getLocalWriteTime,\n getPreviousValue\n} from '../model/server_timestamps';\nimport { fail, hardAssert } from '../util/assert';\nimport { forEach } from '../util/obj';\nimport { TypeOrder } from '../model/object_value';\nimport { ResourcePath } from '../model/path';\nimport { isValidResourceName } from '../remote/serializer';\nimport { logError } from '../util/log';\n\nexport type ServerTimestampBehavior = 'estimate' | 'previous' | 'none';\n\n/**\n * Converts Firestore's internal types to the JavaScript types that we expose\n * to the user.\n */\nexport class UserDataWriter<T = firestore.DocumentData> {\n constructor(\n private readonly firestore: Firestore,\n private readonly timestampsInSnapshots: boolean,\n private readonly serverTimestampBehavior?: ServerTimestampBehavior,\n private readonly converter?: firestore.FirestoreDataConverter<T>\n ) {}\n\n convertValue(value: api.Value): unknown {\n switch (typeOrder(value)) {\n case TypeOrder.NullValue:\n return null;\n case TypeOrder.BooleanValue:\n return value.booleanValue!;\n case TypeOrder.NumberValue:\n return normalizeNumber(value.integerValue || value.doubleValue);\n case TypeOrder.TimestampValue:\n return this.convertTimestamp(value.timestampValue!);\n case TypeOrder.ServerTimestampValue:\n return this.convertServerTimestamp(value);\n case TypeOrder.StringValue:\n return value.stringValue!;\n case TypeOrder.BlobValue:\n return new Blob(normalizeByteString(value.bytesValue!));\n case TypeOrder.RefValue:\n return this.convertReference(value.referenceValue!);\n case TypeOrder.GeoPointValue:\n return this.convertGeoPoint(value.geoPointValue!);\n case TypeOrder.ArrayValue:\n return this.convertArray(value.arrayValue!);\n case TypeOrder.ObjectValue:\n return this.convertObject(value.mapValue!);\n default:\n throw fail('Invalid value type: ' + JSON.stringify(value));\n }\n }\n\n private convertObject(mapValue: api.MapValue): firestore.DocumentData {\n const result: firestore.DocumentData = {};\n forEach(mapValue.fields || {}, (key, value) => {\n result[key] = this.convertValue(value);\n });\n return result;\n }\n\n private convertGeoPoint(value: api.LatLng): GeoPoint {\n return new GeoPoint(\n normalizeNumber(value.latitude),\n normalizeNumber(value.longitude)\n );\n }\n\n private convertArray(arrayValue: api.ArrayValue): unknown[] {\n return (arrayValue.values || []).map(value => this.convertValue(value));\n }\n\n private convertServerTimestamp(value: api.Value): unknown {\n switch (this.serverTimestampBehavior) {\n case 'previous':\n const previousValue = getPreviousValue(value);\n if (previousValue == null) {\n return null;\n }\n return this.convertValue(previousValue);\n case 'estimate':\n return this.convertTimestamp(getLocalWriteTime(value));\n default:\n return null;\n }\n }\n\n private convertTimestamp(value: api.Timestamp): Timestamp | Date {\n const normalizedValue = normalizeTimestamp(value);\n const timestamp = new Timestamp(\n normalizedValue.seconds,\n normalizedValue.nanos\n );\n if (this.timestampsInSnapshots) {\n return timestamp;\n } else {\n return timestamp.toDate();\n }\n }\n\n private convertReference(name: string): DocumentReference<T> {\n const resourcePath = ResourcePath.fromString(name);\n hardAssert(\n isValidResourceName(resourcePath),\n 'ReferenceValue is not valid ' + name\n );\n const databaseId = new DatabaseId(resourcePath.get(1), resourcePath.get(3));\n const key = new DocumentKey(resourcePath.popFirst(5));\n\n if (!databaseId.isEqual(this.firestore._databaseId)) {\n // TODO(b/64130202): Somehow support foreign references.\n logError(\n `Document ${key} contains a document ` +\n `reference within a different database (` +\n `${databaseId.projectId}/${databaseId.database}) which is not ` +\n `supported. It will be treated as a reference in the current ` +\n `database (${this.firestore._databaseId.projectId}/${this.firestore._databaseId.database}) ` +\n `instead.`\n );\n }\n\n return new DocumentReference(key, this.firestore, this.converter);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport * as firestore from '@firebase/firestore-types';\n\nimport * as api from '../protos/firestore_proto_api';\n\nimport { FirebaseApp } from '@firebase/app-types';\nimport { _FirebaseApp, FirebaseService } from '@firebase/app-types/private';\nimport { DatabaseId, DatabaseInfo } from '../core/database_info';\nimport { ListenOptions } from '../core/event_manager';\nimport {\n ComponentProvider,\n MemoryComponentProvider\n} from '../core/component_provider';\nimport { FirestoreClient, PersistenceSettings } from '../core/firestore_client';\nimport {\n Bound,\n Direction,\n FieldFilter,\n Filter,\n Operator,\n OrderBy,\n Query as InternalQuery\n} from '../core/query';\nimport { Transaction as InternalTransaction } from '../core/transaction';\nimport { ChangeType, ViewSnapshot } from '../core/view_snapshot';\nimport { LruParams } from '../local/lru_garbage_collector';\nimport { Document, MaybeDocument, NoDocument } from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { DeleteMutation, Mutation, Precondition } from '../model/mutation';\nimport { FieldPath, ResourcePath } from '../model/path';\nimport { isServerTimestamp } from '../model/server_timestamps';\nimport { refValue } from '../model/values';\nimport { PlatformSupport } from '../platform/platform';\nimport { debugAssert, fail } from '../util/assert';\nimport { AsyncObserver } from '../util/async_observer';\nimport { AsyncQueue } from '../util/async_queue';\nimport { Code, FirestoreError } from '../util/error';\nimport {\n invalidClassError,\n validateArgType,\n validateAtLeastNumberOfArgs,\n validateBetweenNumberOfArgs,\n validateDefined,\n validateExactNumberOfArgs,\n validateNamedOptionalPropertyEquals,\n validateNamedOptionalType,\n validateNamedType,\n validateOptionalArgType,\n validateOptionalArrayElements,\n validateOptionNames,\n validatePositiveNumber,\n validateStringEnum,\n valueDescription\n} from '../util/input_validation';\nimport { getLogLevel, logError, LogLevel, setLogLevel } from '../util/log';\nimport { AutoId } from '../util/misc';\nimport { Deferred, Rejecter, Resolver } from '../util/promise';\nimport { FieldPath as ExternalFieldPath } from './field_path';\n\nimport {\n CredentialsProvider,\n CredentialsSettings,\n EmptyCredentialsProvider,\n FirebaseCredentialsProvider,\n makeCredentialsProvider\n} from './credentials';\nimport {\n CompleteFn,\n ErrorFn,\n isPartialObserver,\n NextFn,\n PartialObserver,\n Unsubscribe\n} from './observer';\nimport { fieldPathFromArgument, UserDataReader } from './user_data_reader';\nimport { UserDataWriter } from './user_data_writer';\nimport { FirebaseAuthInternalName } from '@firebase/auth-interop-types';\nimport { Provider } from '@firebase/component';\n\n// settings() defaults:\nconst DEFAULT_HOST = 'firestore.googleapis.com';\nconst DEFAULT_SSL = true;\nconst DEFAULT_TIMESTAMPS_IN_SNAPSHOTS = true;\nconst DEFAULT_FORCE_LONG_POLLING = false;\nconst DEFAULT_IGNORE_UNDEFINED_PROPERTIES = false;\n\n/**\n * Constant used to indicate the LRU garbage collection should be disabled.\n * Set this value as the `cacheSizeBytes` on the settings passed to the\n * `Firestore` instance.\n */\nexport const CACHE_SIZE_UNLIMITED = LruParams.COLLECTION_DISABLED;\n\n// enablePersistence() defaults:\nconst DEFAULT_SYNCHRONIZE_TABS = false;\n\n/** Undocumented, private additional settings not exposed in our public API. */\ninterface PrivateSettings extends firestore.Settings {\n // Can be a google-auth-library or gapi client.\n credentials?: CredentialsSettings;\n}\n\n/**\n * Options that can be provided in the Firestore constructor when not using\n * Firebase (aka standalone mode).\n */\nexport interface FirestoreDatabase {\n projectId: string;\n database?: string;\n}\n\n/**\n * A concrete type describing all the values that can be applied via a\n * user-supplied firestore.Settings object. This is a separate type so that\n * defaults can be supplied and the value can be checked for equality.\n */\nclass FirestoreSettings {\n /** The hostname to connect to. */\n readonly host: string;\n\n /** Whether to use SSL when connecting. */\n readonly ssl: boolean;\n\n readonly timestampsInSnapshots: boolean;\n\n readonly cacheSizeBytes: number;\n\n readonly forceLongPolling: boolean;\n\n readonly ignoreUndefinedProperties: boolean;\n\n // Can be a google-auth-library or gapi client.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n credentials?: any;\n\n constructor(settings: PrivateSettings) {\n if (settings.host === undefined) {\n if (settings.ssl !== undefined) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n \"Can't provide ssl option if host option is not set\"\n );\n }\n this.host = DEFAULT_HOST;\n this.ssl = DEFAULT_SSL;\n } else {\n validateNamedType('settings', 'non-empty string', 'host', settings.host);\n this.host = settings.host;\n\n validateNamedOptionalType('settings', 'boolean', 'ssl', settings.ssl);\n this.ssl = settings.ssl ?? DEFAULT_SSL;\n }\n validateOptionNames('settings', settings, [\n 'host',\n 'ssl',\n 'credentials',\n 'timestampsInSnapshots',\n 'cacheSizeBytes',\n 'experimentalForceLongPolling',\n 'ignoreUndefinedProperties'\n ]);\n\n validateNamedOptionalType(\n 'settings',\n 'object',\n 'credentials',\n settings.credentials\n );\n this.credentials = settings.credentials;\n\n validateNamedOptionalType(\n 'settings',\n 'boolean',\n 'timestampsInSnapshots',\n settings.timestampsInSnapshots\n );\n\n validateNamedOptionalType(\n 'settings',\n 'boolean',\n 'ignoreUndefinedProperties',\n settings.ignoreUndefinedProperties\n );\n\n // Nobody should set timestampsInSnapshots anymore, but the error depends on\n // whether they set it to true or false...\n if (settings.timestampsInSnapshots === true) {\n logError(\n \"The setting 'timestampsInSnapshots: true' is no longer required \" +\n 'and should be removed.'\n );\n } else if (settings.timestampsInSnapshots === false) {\n logError(\n \"Support for 'timestampsInSnapshots: false' will be removed soon. \" +\n 'You must update your code to handle Timestamp objects.'\n );\n }\n this.timestampsInSnapshots =\n settings.timestampsInSnapshots ?? DEFAULT_TIMESTAMPS_IN_SNAPSHOTS;\n this.ignoreUndefinedProperties =\n settings.ignoreUndefinedProperties ?? DEFAULT_IGNORE_UNDEFINED_PROPERTIES;\n\n validateNamedOptionalType(\n 'settings',\n 'number',\n 'cacheSizeBytes',\n settings.cacheSizeBytes\n );\n if (settings.cacheSizeBytes === undefined) {\n this.cacheSizeBytes = LruParams.DEFAULT_CACHE_SIZE_BYTES;\n } else {\n if (\n settings.cacheSizeBytes !== CACHE_SIZE_UNLIMITED &&\n settings.cacheSizeBytes < LruParams.MINIMUM_CACHE_SIZE_BYTES\n ) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `cacheSizeBytes must be at least ${LruParams.MINIMUM_CACHE_SIZE_BYTES}`\n );\n } else {\n this.cacheSizeBytes = settings.cacheSizeBytes;\n }\n }\n\n validateNamedOptionalType(\n 'settings',\n 'boolean',\n 'experimentalForceLongPolling',\n settings.experimentalForceLongPolling\n );\n this.forceLongPolling =\n settings.experimentalForceLongPolling ?? DEFAULT_FORCE_LONG_POLLING;\n }\n\n isEqual(other: FirestoreSettings): boolean {\n return (\n this.host === other.host &&\n this.ssl === other.ssl &&\n this.timestampsInSnapshots === other.timestampsInSnapshots &&\n this.credentials === other.credentials &&\n this.cacheSizeBytes === other.cacheSizeBytes &&\n this.forceLongPolling === other.forceLongPolling &&\n this.ignoreUndefinedProperties === other.ignoreUndefinedProperties\n );\n }\n}\n\n/**\n * The root reference to the database.\n */\nexport class Firestore implements firestore.FirebaseFirestore, FirebaseService {\n // The objects that are a part of this API are exposed to third-parties as\n // compiled javascript so we want to flag our private members with a leading\n // underscore to discourage their use.\n readonly _databaseId: DatabaseId;\n private readonly _persistenceKey: string;\n private readonly _componentProvider: ComponentProvider;\n private _credentials: CredentialsProvider;\n private readonly _firebaseApp: FirebaseApp | null = null;\n private _settings: FirestoreSettings;\n\n // The firestore client instance. This will be available as soon as\n // configureClient is called, but any calls against it will block until\n // setup has completed.\n //\n // Operations on the _firestoreClient don't block on _firestoreReady. Those\n // are already set to synchronize on the async queue.\n private _firestoreClient: FirestoreClient | undefined;\n\n // Public for use in tests.\n // TODO(mikelehen): Use modularized initialization instead.\n readonly _queue = new AsyncQueue();\n\n _userDataReader: UserDataReader | undefined;\n\n // Note: We are using `MemoryComponentProvider` as a default\n // ComponentProvider to ensure backwards compatibility with the format\n // expected by the console build.\n constructor(\n databaseIdOrApp: FirestoreDatabase | FirebaseApp,\n authProvider: Provider<FirebaseAuthInternalName>,\n componentProvider: ComponentProvider = new MemoryComponentProvider()\n ) {\n if (typeof (databaseIdOrApp as FirebaseApp).options === 'object') {\n // This is very likely a Firebase app object\n // TODO(b/34177605): Can we somehow use instanceof?\n const app = databaseIdOrApp as FirebaseApp;\n this._firebaseApp = app;\n this._databaseId = Firestore.databaseIdFromApp(app);\n this._persistenceKey = app.name;\n this._credentials = new FirebaseCredentialsProvider(authProvider);\n } else {\n const external = databaseIdOrApp as FirestoreDatabase;\n if (!external.projectId) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Must provide projectId'\n );\n }\n\n this._databaseId = new DatabaseId(external.projectId, external.database);\n // Use a default persistenceKey that lines up with FirebaseApp.\n this._persistenceKey = '[DEFAULT]';\n this._credentials = new EmptyCredentialsProvider();\n }\n\n this._componentProvider = componentProvider;\n this._settings = new FirestoreSettings({});\n }\n\n get _dataReader(): UserDataReader {\n debugAssert(\n !!this._firestoreClient,\n 'Cannot obtain UserDataReader before instance is intitialized'\n );\n if (!this._userDataReader) {\n // Lazy initialize UserDataReader once the settings are frozen\n this._userDataReader = new UserDataReader(\n this._databaseId,\n this._settings.ignoreUndefinedProperties\n );\n }\n return this._userDataReader;\n }\n\n settings(settingsLiteral: firestore.Settings): void {\n validateExactNumberOfArgs('Firestore.settings', arguments, 1);\n validateArgType('Firestore.settings', 'object', 1, settingsLiteral);\n\n const newSettings = new FirestoreSettings(settingsLiteral);\n if (this._firestoreClient && !this._settings.isEqual(newSettings)) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'Firestore has already been started and its settings can no longer ' +\n 'be changed. You can only call settings() before calling any other ' +\n 'methods on a Firestore object.'\n );\n }\n\n this._settings = newSettings;\n if (newSettings.credentials !== undefined) {\n this._credentials = makeCredentialsProvider(newSettings.credentials);\n }\n }\n\n enableNetwork(): Promise<void> {\n this.ensureClientConfigured();\n return this._firestoreClient!.enableNetwork();\n }\n\n disableNetwork(): Promise<void> {\n this.ensureClientConfigured();\n return this._firestoreClient!.disableNetwork();\n }\n\n enablePersistence(settings?: firestore.PersistenceSettings): Promise<void> {\n if (this._firestoreClient) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'Firestore has already been started and persistence can no longer ' +\n 'be enabled. You can only call enablePersistence() before calling ' +\n 'any other methods on a Firestore object.'\n );\n }\n\n let synchronizeTabs = false;\n\n if (settings) {\n if (settings.experimentalTabSynchronization !== undefined) {\n logError(\n \"The 'experimentalTabSynchronization' setting will be removed. Use 'synchronizeTabs' instead.\"\n );\n }\n synchronizeTabs =\n settings.synchronizeTabs ??\n settings.experimentalTabSynchronization ??\n DEFAULT_SYNCHRONIZE_TABS;\n }\n\n return this.configureClient(this._componentProvider, {\n durable: true,\n cacheSizeBytes: this._settings.cacheSizeBytes,\n synchronizeTabs\n });\n }\n\n async clearPersistence(): Promise<void> {\n if (\n this._firestoreClient !== undefined &&\n !this._firestoreClient.clientTerminated\n ) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'Persistence cannot be cleared after this Firestore instance is initialized.'\n );\n }\n\n const deferred = new Deferred<void>();\n this._queue.enqueueAndForgetEvenAfterShutdown(async () => {\n try {\n const databaseInfo = this.makeDatabaseInfo();\n await this._componentProvider.clearPersistence(databaseInfo);\n deferred.resolve();\n } catch (e) {\n deferred.reject(e);\n }\n });\n return deferred.promise;\n }\n\n terminate(): Promise<void> {\n (this.app as _FirebaseApp)._removeServiceInstance('firestore');\n return this.INTERNAL.delete();\n }\n\n get _isTerminated(): boolean {\n this.ensureClientConfigured();\n return this._firestoreClient!.clientTerminated;\n }\n\n waitForPendingWrites(): Promise<void> {\n this.ensureClientConfigured();\n return this._firestoreClient!.waitForPendingWrites();\n }\n\n onSnapshotsInSync(observer: PartialObserver<void>): Unsubscribe;\n onSnapshotsInSync(onSync: () => void): Unsubscribe;\n onSnapshotsInSync(arg: unknown): Unsubscribe {\n this.ensureClientConfigured();\n\n if (isPartialObserver(arg)) {\n return this.onSnapshotsInSyncInternal(arg as PartialObserver<void>);\n } else {\n validateArgType('Firestore.onSnapshotsInSync', 'function', 1, arg);\n const observer: PartialObserver<void> = {\n next: arg as () => void\n };\n return this.onSnapshotsInSyncInternal(observer);\n }\n }\n\n private onSnapshotsInSyncInternal(\n observer: PartialObserver<void>\n ): Unsubscribe {\n const errHandler = (err: Error): void => {\n throw fail('Uncaught Error in onSnapshotsInSync');\n };\n const asyncObserver = new AsyncObserver<void>({\n next: () => {\n if (observer.next) {\n observer.next();\n }\n },\n error: errHandler\n });\n this._firestoreClient!.addSnapshotsInSyncListener(asyncObserver);\n return () => {\n asyncObserver.mute();\n this._firestoreClient!.removeSnapshotsInSyncListener(asyncObserver);\n };\n }\n\n ensureClientConfigured(): FirestoreClient {\n if (!this._firestoreClient) {\n // Kick off starting the client but don't actually wait for it.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.configureClient(new MemoryComponentProvider(), {\n durable: false\n });\n }\n return this._firestoreClient as FirestoreClient;\n }\n\n private makeDatabaseInfo(): DatabaseInfo {\n return new DatabaseInfo(\n this._databaseId,\n this._persistenceKey,\n this._settings.host,\n this._settings.ssl,\n this._settings.forceLongPolling\n );\n }\n\n private configureClient(\n componentProvider: ComponentProvider,\n persistenceSettings: PersistenceSettings\n ): Promise<void> {\n debugAssert(!!this._settings.host, 'FirestoreSettings.host is not set');\n\n debugAssert(\n !this._firestoreClient,\n 'configureClient() called multiple times'\n );\n\n const databaseInfo = this.makeDatabaseInfo();\n\n this._firestoreClient = new FirestoreClient(\n PlatformSupport.getPlatform(),\n databaseInfo,\n this._credentials,\n this._queue\n );\n\n return this._firestoreClient.start(componentProvider, persistenceSettings);\n }\n\n private static databaseIdFromApp(app: FirebaseApp): DatabaseId {\n if (!contains(app.options, 'projectId')) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n '\"projectId\" not provided in firebase.initializeApp.'\n );\n }\n\n const projectId = app.options.projectId;\n if (!projectId || typeof projectId !== 'string') {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'projectId must be a string in FirebaseApp.options'\n );\n }\n return new DatabaseId(projectId);\n }\n\n get app(): FirebaseApp {\n if (!this._firebaseApp) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n \"Firestore was not initialized using the Firebase SDK. 'app' is \" +\n 'not available'\n );\n }\n return this._firebaseApp;\n }\n\n INTERNAL = {\n delete: async (): Promise<void> => {\n // The client must be initalized to ensure that all subsequent API usage\n // throws an exception.\n this.ensureClientConfigured();\n await this._firestoreClient!.terminate();\n }\n };\n\n collection(pathString: string): firestore.CollectionReference {\n validateExactNumberOfArgs('Firestore.collection', arguments, 1);\n validateArgType('Firestore.collection', 'non-empty string', 1, pathString);\n this.ensureClientConfigured();\n return new CollectionReference(ResourcePath.fromString(pathString), this);\n }\n\n doc(pathString: string): firestore.DocumentReference {\n validateExactNumberOfArgs('Firestore.doc', arguments, 1);\n validateArgType('Firestore.doc', 'non-empty string', 1, pathString);\n this.ensureClientConfigured();\n return DocumentReference.forPath(ResourcePath.fromString(pathString), this);\n }\n\n collectionGroup(collectionId: string): firestore.Query {\n validateExactNumberOfArgs('Firestore.collectionGroup', arguments, 1);\n validateArgType(\n 'Firestore.collectionGroup',\n 'non-empty string',\n 1,\n collectionId\n );\n if (collectionId.indexOf('/') >= 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid collection ID '${collectionId}' passed to function ` +\n `Firestore.collectionGroup(). Collection IDs must not contain '/'.`\n );\n }\n this.ensureClientConfigured();\n return new Query(\n new InternalQuery(ResourcePath.EMPTY_PATH, collectionId),\n this\n );\n }\n\n runTransaction<T>(\n updateFunction: (transaction: firestore.Transaction) => Promise<T>\n ): Promise<T> {\n validateExactNumberOfArgs('Firestore.runTransaction', arguments, 1);\n validateArgType('Firestore.runTransaction', 'function', 1, updateFunction);\n return this.ensureClientConfigured().transaction(\n (transaction: InternalTransaction) => {\n return updateFunction(new Transaction(this, transaction));\n }\n );\n }\n\n batch(): firestore.WriteBatch {\n this.ensureClientConfigured();\n\n return new WriteBatch(this);\n }\n\n static get logLevel(): firestore.LogLevel {\n switch (getLogLevel()) {\n case LogLevel.DEBUG:\n return 'debug';\n case LogLevel.SILENT:\n return 'silent';\n default:\n // The default log level is error\n return 'error';\n }\n }\n\n static setLogLevel(level: firestore.LogLevel): void {\n validateExactNumberOfArgs('Firestore.setLogLevel', arguments, 1);\n validateArgType('Firestore.setLogLevel', 'non-empty string', 1, level);\n switch (level) {\n case 'debug':\n setLogLevel(LogLevel.DEBUG);\n break;\n case 'error':\n setLogLevel(LogLevel.ERROR);\n break;\n case 'silent':\n setLogLevel(LogLevel.SILENT);\n break;\n default:\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid log level: ' + level\n );\n }\n }\n\n // Note: this is not a property because the minifier can't work correctly with\n // the way TypeScript compiler outputs properties.\n _areTimestampsInSnapshotsEnabled(): boolean {\n return this._settings.timestampsInSnapshots;\n }\n}\n\n/**\n * A reference to a transaction.\n */\nexport class Transaction implements firestore.Transaction {\n constructor(\n private _firestore: Firestore,\n private _transaction: InternalTransaction\n ) {}\n\n get<T>(\n documentRef: firestore.DocumentReference<T>\n ): Promise<firestore.DocumentSnapshot<T>> {\n validateExactNumberOfArgs('Transaction.get', arguments, 1);\n const ref = validateReference(\n 'Transaction.get',\n documentRef,\n this._firestore\n );\n return this._transaction\n .lookup([ref._key])\n .then((docs: MaybeDocument[]) => {\n if (!docs || docs.length !== 1) {\n return fail('Mismatch in docs returned from document lookup.');\n }\n const doc = docs[0];\n if (doc instanceof NoDocument) {\n return new DocumentSnapshot<T>(\n this._firestore,\n ref._key,\n null,\n /* fromCache= */ false,\n /* hasPendingWrites= */ false,\n ref._converter\n );\n } else if (doc instanceof Document) {\n return new DocumentSnapshot<T>(\n this._firestore,\n ref._key,\n doc,\n /* fromCache= */ false,\n /* hasPendingWrites= */ false,\n ref._converter\n );\n } else {\n throw fail(\n `BatchGetDocumentsRequest returned unexpected document type: ${doc.constructor.name}`\n );\n }\n });\n }\n\n set<T>(\n documentRef: firestore.DocumentReference<T>,\n value: T,\n options?: firestore.SetOptions\n ): Transaction {\n validateBetweenNumberOfArgs('Transaction.set', arguments, 2, 3);\n const ref = validateReference(\n 'Transaction.set',\n documentRef,\n this._firestore\n );\n options = validateSetOptions('Transaction.set', options);\n const [convertedValue, functionName] = applyFirestoreDataConverter(\n ref._converter,\n value,\n 'Transaction.set'\n );\n const parsed =\n options.merge || options.mergeFields\n ? this._firestore._dataReader.parseMergeData(\n functionName,\n convertedValue,\n options.mergeFields\n )\n : this._firestore._dataReader.parseSetData(\n functionName,\n convertedValue\n );\n this._transaction.set(ref._key, parsed);\n return this;\n }\n\n update(\n documentRef: firestore.DocumentReference<unknown>,\n value: firestore.UpdateData\n ): Transaction;\n update(\n documentRef: firestore.DocumentReference<unknown>,\n field: string | ExternalFieldPath,\n value: unknown,\n ...moreFieldsAndValues: unknown[]\n ): Transaction;\n update(\n documentRef: firestore.DocumentReference<unknown>,\n fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,\n value?: unknown,\n ...moreFieldsAndValues: unknown[]\n ): Transaction {\n let ref;\n let parsed;\n\n if (\n typeof fieldOrUpdateData === 'string' ||\n fieldOrUpdateData instanceof ExternalFieldPath\n ) {\n validateAtLeastNumberOfArgs('Transaction.update', arguments, 3);\n ref = validateReference(\n 'Transaction.update',\n documentRef,\n this._firestore\n );\n parsed = this._firestore._dataReader.parseUpdateVarargs(\n 'Transaction.update',\n fieldOrUpdateData,\n value,\n moreFieldsAndValues\n );\n } else {\n validateExactNumberOfArgs('Transaction.update', arguments, 2);\n ref = validateReference(\n 'Transaction.update',\n documentRef,\n this._firestore\n );\n parsed = this._firestore._dataReader.parseUpdateData(\n 'Transaction.update',\n fieldOrUpdateData\n );\n }\n\n this._transaction.update(ref._key, parsed);\n return this;\n }\n\n delete(documentRef: firestore.DocumentReference<unknown>): Transaction {\n validateExactNumberOfArgs('Transaction.delete', arguments, 1);\n const ref = validateReference(\n 'Transaction.delete',\n documentRef,\n this._firestore\n );\n this._transaction.delete(ref._key);\n return this;\n }\n}\n\nexport class WriteBatch implements firestore.WriteBatch {\n private _mutations = [] as Mutation[];\n private _committed = false;\n\n constructor(private _firestore: Firestore) {}\n\n set<T>(\n documentRef: firestore.DocumentReference<T>,\n value: T,\n options?: firestore.SetOptions\n ): WriteBatch {\n validateBetweenNumberOfArgs('WriteBatch.set', arguments, 2, 3);\n this.verifyNotCommitted();\n const ref = validateReference(\n 'WriteBatch.set',\n documentRef,\n this._firestore\n );\n options = validateSetOptions('WriteBatch.set', options);\n const [convertedValue, functionName] = applyFirestoreDataConverter(\n ref._converter,\n value,\n 'WriteBatch.set'\n );\n const parsed =\n options.merge || options.mergeFields\n ? this._firestore._dataReader.parseMergeData(\n functionName,\n convertedValue,\n options.mergeFields\n )\n : this._firestore._dataReader.parseSetData(\n functionName,\n convertedValue\n );\n this._mutations = this._mutations.concat(\n parsed.toMutations(ref._key, Precondition.none())\n );\n return this;\n }\n\n update(\n documentRef: firestore.DocumentReference<unknown>,\n value: firestore.UpdateData\n ): WriteBatch;\n update(\n documentRef: firestore.DocumentReference<unknown>,\n field: string | ExternalFieldPath,\n value: unknown,\n ...moreFieldsAndValues: unknown[]\n ): WriteBatch;\n update(\n documentRef: firestore.DocumentReference<unknown>,\n fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,\n value?: unknown,\n ...moreFieldsAndValues: unknown[]\n ): WriteBatch {\n this.verifyNotCommitted();\n\n let ref;\n let parsed;\n\n if (\n typeof fieldOrUpdateData === 'string' ||\n fieldOrUpdateData instanceof ExternalFieldPath\n ) {\n validateAtLeastNumberOfArgs('WriteBatch.update', arguments, 3);\n ref = validateReference(\n 'WriteBatch.update',\n documentRef,\n this._firestore\n );\n parsed = this._firestore._dataReader.parseUpdateVarargs(\n 'WriteBatch.update',\n fieldOrUpdateData,\n value,\n moreFieldsAndValues\n );\n } else {\n validateExactNumberOfArgs('WriteBatch.update', arguments, 2);\n ref = validateReference(\n 'WriteBatch.update',\n documentRef,\n this._firestore\n );\n parsed = this._firestore._dataReader.parseUpdateData(\n 'WriteBatch.update',\n fieldOrUpdateData\n );\n }\n\n this._mutations = this._mutations.concat(\n parsed.toMutations(ref._key, Precondition.exists(true))\n );\n return this;\n }\n\n delete(documentRef: firestore.DocumentReference<unknown>): WriteBatch {\n validateExactNumberOfArgs('WriteBatch.delete', arguments, 1);\n this.verifyNotCommitted();\n const ref = validateReference(\n 'WriteBatch.delete',\n documentRef,\n this._firestore\n );\n this._mutations = this._mutations.concat(\n new DeleteMutation(ref._key, Precondition.none())\n );\n return this;\n }\n\n commit(): Promise<void> {\n this.verifyNotCommitted();\n this._committed = true;\n if (this._mutations.length > 0) {\n return this._firestore.ensureClientConfigured().write(this._mutations);\n }\n\n return Promise.resolve();\n }\n\n private verifyNotCommitted(): void {\n if (this._committed) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n 'A write batch can no longer be used after commit() ' +\n 'has been called.'\n );\n }\n }\n}\n\n/**\n * A reference to a particular document in a collection in the database.\n */\nexport class DocumentReference<T = firestore.DocumentData>\n implements firestore.DocumentReference<T> {\n private _firestoreClient: FirestoreClient;\n\n constructor(\n public _key: DocumentKey,\n readonly firestore: Firestore,\n readonly _converter?: firestore.FirestoreDataConverter<T>\n ) {\n this._firestoreClient = this.firestore.ensureClientConfigured();\n }\n\n static forPath<U>(\n path: ResourcePath,\n firestore: Firestore,\n converter?: firestore.FirestoreDataConverter<U>\n ): DocumentReference<U> {\n if (path.length % 2 !== 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid document reference. Document ' +\n 'references must have an even number of segments, but ' +\n `${path.canonicalString()} has ${path.length}`\n );\n }\n return new DocumentReference(new DocumentKey(path), firestore, converter);\n }\n\n get id(): string {\n return this._key.path.lastSegment();\n }\n\n get parent(): firestore.CollectionReference<T> {\n return new CollectionReference(\n this._key.path.popLast(),\n this.firestore,\n this._converter\n );\n }\n\n get path(): string {\n return this._key.path.canonicalString();\n }\n\n collection(\n pathString: string\n ): firestore.CollectionReference<firestore.DocumentData> {\n validateExactNumberOfArgs('DocumentReference.collection', arguments, 1);\n validateArgType(\n 'DocumentReference.collection',\n 'non-empty string',\n 1,\n pathString\n );\n if (!pathString) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Must provide a non-empty collection name to collection()'\n );\n }\n const path = ResourcePath.fromString(pathString);\n return new CollectionReference(this._key.path.child(path), this.firestore);\n }\n\n isEqual(other: firestore.DocumentReference<T>): boolean {\n if (!(other instanceof DocumentReference)) {\n throw invalidClassError('isEqual', 'DocumentReference', 1, other);\n }\n return (\n this.firestore === other.firestore &&\n this._key.isEqual(other._key) &&\n this._converter === other._converter\n );\n }\n\n set(\n value: firestore.DocumentData,\n options?: firestore.SetOptions\n ): Promise<void>;\n set(value: T, options?: firestore.SetOptions): Promise<void> {\n validateBetweenNumberOfArgs('DocumentReference.set', arguments, 1, 2);\n options = validateSetOptions('DocumentReference.set', options);\n const [convertedValue, functionName] = applyFirestoreDataConverter(\n this._converter,\n value,\n 'DocumentReference.set'\n );\n const parsed =\n options.merge || options.mergeFields\n ? this.firestore._dataReader.parseMergeData(\n functionName,\n convertedValue,\n options.mergeFields\n )\n : this.firestore._dataReader.parseSetData(functionName, convertedValue);\n return this._firestoreClient.write(\n parsed.toMutations(this._key, Precondition.none())\n );\n }\n\n update(value: firestore.UpdateData): Promise<void>;\n update(\n field: string | ExternalFieldPath,\n value: unknown,\n ...moreFieldsAndValues: unknown[]\n ): Promise<void>;\n update(\n fieldOrUpdateData: string | ExternalFieldPath | firestore.UpdateData,\n value?: unknown,\n ...moreFieldsAndValues: unknown[]\n ): Promise<void> {\n let parsed;\n\n if (\n typeof fieldOrUpdateData === 'string' ||\n fieldOrUpdateData instanceof ExternalFieldPath\n ) {\n validateAtLeastNumberOfArgs('DocumentReference.update', arguments, 2);\n parsed = this.firestore._dataReader.parseUpdateVarargs(\n 'DocumentReference.update',\n fieldOrUpdateData,\n value,\n moreFieldsAndValues\n );\n } else {\n validateExactNumberOfArgs('DocumentReference.update', arguments, 1);\n parsed = this.firestore._dataReader.parseUpdateData(\n 'DocumentReference.update',\n fieldOrUpdateData\n );\n }\n\n return this._firestoreClient.write(\n parsed.toMutations(this._key, Precondition.exists(true))\n );\n }\n\n delete(): Promise<void> {\n validateExactNumberOfArgs('DocumentReference.delete', arguments, 0);\n return this._firestoreClient.write([\n new DeleteMutation(this._key, Precondition.none())\n ]);\n }\n\n onSnapshot(\n observer: PartialObserver<firestore.DocumentSnapshot<T>>\n ): Unsubscribe;\n onSnapshot(\n options: firestore.SnapshotListenOptions,\n observer: PartialObserver<firestore.DocumentSnapshot<T>>\n ): Unsubscribe;\n onSnapshot(\n onNext: NextFn<firestore.DocumentSnapshot<T>>,\n onError?: ErrorFn,\n onCompletion?: CompleteFn\n ): Unsubscribe;\n onSnapshot(\n options: firestore.SnapshotListenOptions,\n onNext: NextFn<firestore.DocumentSnapshot<T>>,\n onError?: ErrorFn,\n onCompletion?: CompleteFn\n ): Unsubscribe;\n\n onSnapshot(...args: unknown[]): Unsubscribe {\n validateBetweenNumberOfArgs(\n 'DocumentReference.onSnapshot',\n arguments,\n 1,\n 4\n );\n let options: firestore.SnapshotListenOptions = {\n includeMetadataChanges: false\n };\n let observer: PartialObserver<firestore.DocumentSnapshot<T>>;\n let currArg = 0;\n if (\n typeof args[currArg] === 'object' &&\n !isPartialObserver(args[currArg])\n ) {\n options = args[currArg] as firestore.SnapshotListenOptions;\n validateOptionNames('DocumentReference.onSnapshot', options, [\n 'includeMetadataChanges'\n ]);\n validateNamedOptionalType(\n 'DocumentReference.onSnapshot',\n 'boolean',\n 'includeMetadataChanges',\n options.includeMetadataChanges\n );\n currArg++;\n }\n\n const internalOptions = {\n includeMetadataChanges: options.includeMetadataChanges\n };\n\n if (isPartialObserver(args[currArg])) {\n observer = args[currArg] as PartialObserver<\n firestore.DocumentSnapshot<T>\n >;\n } else {\n validateArgType(\n 'DocumentReference.onSnapshot',\n 'function',\n currArg,\n args[currArg]\n );\n validateOptionalArgType(\n 'DocumentReference.onSnapshot',\n 'function',\n currArg + 1,\n args[currArg + 1]\n );\n validateOptionalArgType(\n 'DocumentReference.onSnapshot',\n 'function',\n currArg + 2,\n args[currArg + 2]\n );\n observer = {\n next: args[currArg] as NextFn<firestore.DocumentSnapshot<T>>,\n error: args[currArg + 1] as ErrorFn,\n complete: args[currArg + 2] as CompleteFn\n };\n }\n return this.onSnapshotInternal(internalOptions, observer);\n }\n\n private onSnapshotInternal(\n options: ListenOptions,\n observer: PartialObserver<firestore.DocumentSnapshot<T>>\n ): Unsubscribe {\n let errHandler = (err: Error): void => {\n console.error('Uncaught Error in onSnapshot:', err);\n };\n if (observer.error) {\n errHandler = observer.error.bind(observer);\n }\n\n const asyncObserver = new AsyncObserver<ViewSnapshot>({\n next: snapshot => {\n if (observer.next) {\n debugAssert(\n snapshot.docs.size <= 1,\n 'Too many documents returned on a document query'\n );\n const doc = snapshot.docs.get(this._key);\n\n observer.next(\n new DocumentSnapshot(\n this.firestore,\n this._key,\n doc,\n snapshot.fromCache,\n snapshot.hasPendingWrites,\n this._converter\n )\n );\n }\n },\n error: errHandler\n });\n const internalListener = this._firestoreClient.listen(\n InternalQuery.atPath(this._key.path),\n asyncObserver,\n options\n );\n\n return () => {\n asyncObserver.mute();\n this._firestoreClient.unlisten(internalListener);\n };\n }\n\n get(options?: firestore.GetOptions): Promise<firestore.DocumentSnapshot<T>> {\n validateBetweenNumberOfArgs('DocumentReference.get', arguments, 0, 1);\n validateGetOptions('DocumentReference.get', options);\n return new Promise(\n (resolve: Resolver<firestore.DocumentSnapshot<T>>, reject: Rejecter) => {\n if (options && options.source === 'cache') {\n this.firestore\n .ensureClientConfigured()\n .getDocumentFromLocalCache(this._key)\n .then(doc => {\n resolve(\n new DocumentSnapshot(\n this.firestore,\n this._key,\n doc,\n /*fromCache=*/ true,\n doc instanceof Document ? doc.hasLocalMutations : false,\n this._converter\n )\n );\n }, reject);\n } else {\n this.getViaSnapshotListener(resolve, reject, options);\n }\n }\n );\n }\n\n private getViaSnapshotListener(\n resolve: Resolver<firestore.DocumentSnapshot<T>>,\n reject: Rejecter,\n options?: firestore.GetOptions\n ): void {\n const unlisten = this.onSnapshotInternal(\n {\n includeMetadataChanges: true,\n waitForSyncWhenOnline: true\n },\n {\n next: (snap: firestore.DocumentSnapshot<T>) => {\n // Remove query first before passing event to user to avoid\n // user actions affecting the now stale query.\n unlisten();\n\n if (!snap.exists && snap.metadata.fromCache) {\n // TODO(dimond): If we're online and the document doesn't\n // exist then we resolve with a doc.exists set to false. If\n // we're offline however, we reject the Promise in this\n // case. Two options: 1) Cache the negative response from\n // the server so we can deliver that even when you're\n // offline 2) Actually reject the Promise in the online case\n // if the document doesn't exist.\n reject(\n new FirestoreError(\n Code.UNAVAILABLE,\n 'Failed to get document because the client is ' + 'offline.'\n )\n );\n } else if (\n snap.exists &&\n snap.metadata.fromCache &&\n options &&\n options.source === 'server'\n ) {\n reject(\n new FirestoreError(\n Code.UNAVAILABLE,\n 'Failed to get document from server. (However, this ' +\n 'document does exist in the local cache. Run again ' +\n 'without setting source to \"server\" to ' +\n 'retrieve the cached document.)'\n )\n );\n } else {\n resolve(snap);\n }\n },\n error: reject\n }\n );\n }\n\n withConverter<U>(\n converter: firestore.FirestoreDataConverter<U>\n ): firestore.DocumentReference<U> {\n return new DocumentReference<U>(this._key, this.firestore, converter);\n }\n}\n\nclass SnapshotMetadata implements firestore.SnapshotMetadata {\n constructor(\n readonly hasPendingWrites: boolean,\n readonly fromCache: boolean\n ) {}\n\n isEqual(other: firestore.SnapshotMetadata): boolean {\n return (\n this.hasPendingWrites === other.hasPendingWrites &&\n this.fromCache === other.fromCache\n );\n }\n}\n\n/**\n * Options interface that can be provided to configure the deserialization of\n * DocumentSnapshots.\n */\nexport interface SnapshotOptions extends firestore.SnapshotOptions {}\n\nexport class DocumentSnapshot<T = firestore.DocumentData>\n implements firestore.DocumentSnapshot<T> {\n constructor(\n private _firestore: Firestore,\n private _key: DocumentKey,\n public _document: Document | null,\n private _fromCache: boolean,\n private _hasPendingWrites: boolean,\n private readonly _converter?: firestore.FirestoreDataConverter<T>\n ) {}\n\n data(options?: firestore.SnapshotOptions): T | undefined {\n validateBetweenNumberOfArgs('DocumentSnapshot.data', arguments, 0, 1);\n options = validateSnapshotOptions('DocumentSnapshot.data', options);\n if (!this._document) {\n return undefined;\n } else {\n // We only want to use the converter and create a new DocumentSnapshot\n // if a converter has been provided.\n if (this._converter) {\n const snapshot = new QueryDocumentSnapshot(\n this._firestore,\n this._key,\n this._document,\n this._fromCache,\n this._hasPendingWrites\n );\n return this._converter.fromFirestore(snapshot, options);\n } else {\n const userDataWriter = new UserDataWriter(\n this._firestore,\n this._firestore._areTimestampsInSnapshotsEnabled(),\n options.serverTimestamps,\n /* converter= */ undefined\n );\n return userDataWriter.convertValue(this._document.toProto()) as T;\n }\n }\n }\n\n get(\n fieldPath: string | ExternalFieldPath,\n options?: firestore.SnapshotOptions\n ): unknown {\n validateBetweenNumberOfArgs('DocumentSnapshot.get', arguments, 1, 2);\n options = validateSnapshotOptions('DocumentSnapshot.get', options);\n if (this._document) {\n const value = this._document\n .data()\n .field(fieldPathFromArgument('DocumentSnapshot.get', fieldPath));\n if (value !== null) {\n const userDataWriter = new UserDataWriter(\n this._firestore,\n this._firestore._areTimestampsInSnapshotsEnabled(),\n options.serverTimestamps,\n this._converter\n );\n return userDataWriter.convertValue(value);\n }\n }\n return undefined;\n }\n\n get id(): string {\n return this._key.path.lastSegment();\n }\n\n get ref(): firestore.DocumentReference<T> {\n return new DocumentReference<T>(\n this._key,\n this._firestore,\n this._converter\n );\n }\n\n get exists(): boolean {\n return this._document !== null;\n }\n\n get metadata(): firestore.SnapshotMetadata {\n return new SnapshotMetadata(this._hasPendingWrites, this._fromCache);\n }\n\n isEqual(other: firestore.DocumentSnapshot<T>): boolean {\n if (!(other instanceof DocumentSnapshot)) {\n throw invalidClassError('isEqual', 'DocumentSnapshot', 1, other);\n }\n return (\n this._firestore === other._firestore &&\n this._fromCache === other._fromCache &&\n this._key.isEqual(other._key) &&\n (this._document === null\n ? other._document === null\n : this._document.isEqual(other._document)) &&\n this._converter === other._converter\n );\n }\n}\n\nexport class QueryDocumentSnapshot<T = firestore.DocumentData>\n extends DocumentSnapshot<T>\n implements firestore.QueryDocumentSnapshot<T> {\n data(options?: SnapshotOptions): T {\n const data = super.data(options);\n debugAssert(\n data !== undefined,\n 'Document in a QueryDocumentSnapshot should exist'\n );\n return data;\n }\n}\n\nexport class Query<T = firestore.DocumentData> implements firestore.Query<T> {\n constructor(\n public _query: InternalQuery,\n readonly firestore: Firestore,\n protected readonly _converter?: firestore.FirestoreDataConverter<T>\n ) {}\n\n where(\n field: string | ExternalFieldPath,\n opStr: firestore.WhereFilterOp,\n value: unknown\n ): firestore.Query<T> {\n validateExactNumberOfArgs('Query.where', arguments, 3);\n validateDefined('Query.where', 3, value);\n\n // Enumerated from the WhereFilterOp type in index.d.ts.\n const whereFilterOpEnums = [\n Operator.LESS_THAN,\n Operator.LESS_THAN_OR_EQUAL,\n Operator.EQUAL,\n Operator.GREATER_THAN_OR_EQUAL,\n Operator.GREATER_THAN,\n Operator.ARRAY_CONTAINS,\n Operator.IN,\n Operator.ARRAY_CONTAINS_ANY\n ];\n const op = validateStringEnum('Query.where', whereFilterOpEnums, 2, opStr);\n\n let fieldValue: api.Value;\n const fieldPath = fieldPathFromArgument('Query.where', field);\n if (fieldPath.isKeyField()) {\n if (\n op === Operator.ARRAY_CONTAINS ||\n op === Operator.ARRAY_CONTAINS_ANY\n ) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid Query. You can't perform '${op}' ` +\n 'queries on FieldPath.documentId().'\n );\n } else if (op === Operator.IN) {\n this.validateDisjunctiveFilterElements(value, op);\n const referenceList: api.Value[] = [];\n for (const arrayValue of value as api.Value[]) {\n referenceList.push(this.parseDocumentIdValue(arrayValue));\n }\n fieldValue = { arrayValue: { values: referenceList } };\n } else {\n fieldValue = this.parseDocumentIdValue(value);\n }\n } else {\n if (op === Operator.IN || op === Operator.ARRAY_CONTAINS_ANY) {\n this.validateDisjunctiveFilterElements(value, op);\n }\n fieldValue = this.firestore._dataReader.parseQueryValue(\n 'Query.where',\n value,\n // We only allow nested arrays for IN queries.\n /** allowArrays = */ op === Operator.IN\n );\n }\n const filter = FieldFilter.create(fieldPath, op, fieldValue);\n this.validateNewFilter(filter);\n return new Query(\n this._query.addFilter(filter),\n this.firestore,\n this._converter\n );\n }\n\n orderBy(\n field: string | ExternalFieldPath,\n directionStr?: firestore.OrderByDirection\n ): firestore.Query<T> {\n validateBetweenNumberOfArgs('Query.orderBy', arguments, 1, 2);\n validateOptionalArgType(\n 'Query.orderBy',\n 'non-empty string',\n 2,\n directionStr\n );\n let direction: Direction;\n if (directionStr === undefined || directionStr === 'asc') {\n direction = Direction.ASCENDING;\n } else if (directionStr === 'desc') {\n direction = Direction.DESCENDING;\n } else {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Function Query.orderBy() has unknown direction '${directionStr}', ` +\n `expected 'asc' or 'desc'.`\n );\n }\n if (this._query.startAt !== null) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. You must not call Query.startAt() or ' +\n 'Query.startAfter() before calling Query.orderBy().'\n );\n }\n if (this._query.endAt !== null) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. You must not call Query.endAt() or ' +\n 'Query.endBefore() before calling Query.orderBy().'\n );\n }\n const fieldPath = fieldPathFromArgument('Query.orderBy', field);\n const orderBy = new OrderBy(fieldPath, direction);\n this.validateNewOrderBy(orderBy);\n return new Query(\n this._query.addOrderBy(orderBy),\n this.firestore,\n this._converter\n );\n }\n\n limit(n: number): firestore.Query<T> {\n validateExactNumberOfArgs('Query.limit', arguments, 1);\n validateArgType('Query.limit', 'number', 1, n);\n validatePositiveNumber('Query.limit', 1, n);\n return new Query(\n this._query.withLimitToFirst(n),\n this.firestore,\n this._converter\n );\n }\n\n limitToLast(n: number): firestore.Query<T> {\n validateExactNumberOfArgs('Query.limitToLast', arguments, 1);\n validateArgType('Query.limitToLast', 'number', 1, n);\n validatePositiveNumber('Query.limitToLast', 1, n);\n return new Query(\n this._query.withLimitToLast(n),\n this.firestore,\n this._converter\n );\n }\n\n startAt(\n docOrField: unknown | firestore.DocumentSnapshot<unknown>,\n ...fields: unknown[]\n ): firestore.Query<T> {\n validateAtLeastNumberOfArgs('Query.startAt', arguments, 1);\n const bound = this.boundFromDocOrFields(\n 'Query.startAt',\n docOrField,\n fields,\n /*before=*/ true\n );\n return new Query(\n this._query.withStartAt(bound),\n this.firestore,\n this._converter\n );\n }\n\n startAfter(\n docOrField: unknown | firestore.DocumentSnapshot<unknown>,\n ...fields: unknown[]\n ): firestore.Query<T> {\n validateAtLeastNumberOfArgs('Query.startAfter', arguments, 1);\n const bound = this.boundFromDocOrFields(\n 'Query.startAfter',\n docOrField,\n fields,\n /*before=*/ false\n );\n return new Query(\n this._query.withStartAt(bound),\n this.firestore,\n this._converter\n );\n }\n\n endBefore(\n docOrField: unknown | firestore.DocumentSnapshot<unknown>,\n ...fields: unknown[]\n ): firestore.Query<T> {\n validateAtLeastNumberOfArgs('Query.endBefore', arguments, 1);\n const bound = this.boundFromDocOrFields(\n 'Query.endBefore',\n docOrField,\n fields,\n /*before=*/ true\n );\n return new Query(\n this._query.withEndAt(bound),\n this.firestore,\n this._converter\n );\n }\n\n endAt(\n docOrField: unknown | firestore.DocumentSnapshot<unknown>,\n ...fields: unknown[]\n ): firestore.Query<T> {\n validateAtLeastNumberOfArgs('Query.endAt', arguments, 1);\n const bound = this.boundFromDocOrFields(\n 'Query.endAt',\n docOrField,\n fields,\n /*before=*/ false\n );\n return new Query(\n this._query.withEndAt(bound),\n this.firestore,\n this._converter\n );\n }\n\n isEqual(other: firestore.Query<T>): boolean {\n if (!(other instanceof Query)) {\n throw invalidClassError('isEqual', 'Query', 1, other);\n }\n return (\n this.firestore === other.firestore && this._query.isEqual(other._query)\n );\n }\n\n withConverter<U>(\n converter: firestore.FirestoreDataConverter<U>\n ): firestore.Query<U> {\n return new Query<U>(this._query, this.firestore, converter);\n }\n\n /** Helper function to create a bound from a document or fields */\n private boundFromDocOrFields(\n methodName: string,\n docOrField: unknown | firestore.DocumentSnapshot<T>,\n fields: unknown[],\n before: boolean\n ): Bound {\n validateDefined(methodName, 1, docOrField);\n if (docOrField instanceof DocumentSnapshot) {\n if (fields.length > 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Too many arguments provided to ${methodName}().`\n );\n }\n const snap = docOrField;\n if (!snap.exists) {\n throw new FirestoreError(\n Code.NOT_FOUND,\n `Can't use a DocumentSnapshot that doesn't exist for ` +\n `${methodName}().`\n );\n }\n return this.boundFromDocument(snap._document!, before);\n } else {\n const allFields = [docOrField].concat(fields);\n return this.boundFromFields(methodName, allFields, before);\n }\n }\n\n /**\n * Create a Bound from a query and a document.\n *\n * Note that the Bound will always include the key of the document\n * and so only the provided document will compare equal to the returned\n * position.\n *\n * Will throw if the document does not contain all fields of the order by\n * of the query or if any of the fields in the order by are an uncommitted\n * server timestamp.\n */\n private boundFromDocument(doc: Document, before: boolean): Bound {\n const components: api.Value[] = [];\n\n // Because people expect to continue/end a query at the exact document\n // provided, we need to use the implicit sort order rather than the explicit\n // sort order, because it's guaranteed to contain the document key. That way\n // the position becomes unambiguous and the query continues/ends exactly at\n // the provided document. Without the key (by using the explicit sort\n // orders), multiple documents could match the position, yielding duplicate\n // results.\n for (const orderBy of this._query.orderBy) {\n if (orderBy.field.isKeyField()) {\n components.push(refValue(this.firestore._databaseId, doc.key));\n } else {\n const value = doc.field(orderBy.field);\n if (isServerTimestamp(value)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. You are trying to start or end a query using a ' +\n 'document for which the field \"' +\n orderBy.field +\n '\" is an uncommitted server timestamp. (Since the value of ' +\n 'this field is unknown, you cannot start/end a query with it.)'\n );\n } else if (value !== null) {\n components.push(value);\n } else {\n const field = orderBy.field.canonicalString();\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. You are trying to start or end a query using a ` +\n `document for which the field '${field}' (used as the ` +\n `orderBy) does not exist.`\n );\n }\n }\n }\n return new Bound(components, before);\n }\n\n /**\n * Converts a list of field values to a Bound for the given query.\n */\n private boundFromFields(\n methodName: string,\n values: unknown[],\n before: boolean\n ): Bound {\n // Use explicit order by's because it has to match the query the user made\n const orderBy = this._query.explicitOrderBy;\n if (values.length > orderBy.length) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Too many arguments provided to ${methodName}(). ` +\n `The number of arguments must be less than or equal to the ` +\n `number of Query.orderBy() clauses`\n );\n }\n\n const components: api.Value[] = [];\n for (let i = 0; i < values.length; i++) {\n const rawValue = values[i];\n const orderByComponent = orderBy[i];\n if (orderByComponent.field.isKeyField()) {\n if (typeof rawValue !== 'string') {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. Expected a string for document ID in ` +\n `${methodName}(), but got a ${typeof rawValue}`\n );\n }\n if (\n !this._query.isCollectionGroupQuery() &&\n rawValue.indexOf('/') !== -1\n ) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. When querying a collection and ordering by FieldPath.documentId(), ` +\n `the value passed to ${methodName}() must be a plain document ID, but ` +\n `'${rawValue}' contains a slash.`\n );\n }\n const path = this._query.path.child(ResourcePath.fromString(rawValue));\n if (!DocumentKey.isDocumentKey(path)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. When querying a collection group and ordering by ` +\n `FieldPath.documentId(), the value passed to ${methodName}() must result in a ` +\n `valid document path, but '${path}' is not because it contains an odd number ` +\n `of segments.`\n );\n }\n const key = new DocumentKey(path);\n components.push(refValue(this.firestore._databaseId, key));\n } else {\n const wrapped = this.firestore._dataReader.parseQueryValue(\n methodName,\n rawValue\n );\n components.push(wrapped);\n }\n }\n\n return new Bound(components, before);\n }\n\n onSnapshot(\n observer: PartialObserver<firestore.QuerySnapshot<T>>\n ): Unsubscribe;\n onSnapshot(\n options: firestore.SnapshotListenOptions,\n observer: PartialObserver<firestore.QuerySnapshot<T>>\n ): Unsubscribe;\n onSnapshot(\n onNext: NextFn<firestore.QuerySnapshot<T>>,\n onError?: ErrorFn,\n onCompletion?: CompleteFn\n ): Unsubscribe;\n onSnapshot(\n options: firestore.SnapshotListenOptions,\n onNext: NextFn<firestore.QuerySnapshot<T>>,\n onError?: ErrorFn,\n onCompletion?: CompleteFn\n ): Unsubscribe;\n\n onSnapshot(...args: unknown[]): Unsubscribe {\n validateBetweenNumberOfArgs('Query.onSnapshot', arguments, 1, 4);\n let options: firestore.SnapshotListenOptions = {};\n let observer: PartialObserver<firestore.QuerySnapshot<T>>;\n let currArg = 0;\n if (\n typeof args[currArg] === 'object' &&\n !isPartialObserver(args[currArg])\n ) {\n options = args[currArg] as firestore.SnapshotListenOptions;\n validateOptionNames('Query.onSnapshot', options, [\n 'includeMetadataChanges'\n ]);\n validateNamedOptionalType(\n 'Query.onSnapshot',\n 'boolean',\n 'includeMetadataChanges',\n options.includeMetadataChanges\n );\n currArg++;\n }\n\n if (isPartialObserver(args[currArg])) {\n observer = args[currArg] as PartialObserver<firestore.QuerySnapshot<T>>;\n } else {\n validateArgType('Query.onSnapshot', 'function', currArg, args[currArg]);\n validateOptionalArgType(\n 'Query.onSnapshot',\n 'function',\n currArg + 1,\n args[currArg + 1]\n );\n validateOptionalArgType(\n 'Query.onSnapshot',\n 'function',\n currArg + 2,\n args[currArg + 2]\n );\n observer = {\n next: args[currArg] as NextFn<firestore.QuerySnapshot<T>>,\n error: args[currArg + 1] as ErrorFn,\n complete: args[currArg + 2] as CompleteFn\n };\n }\n this.validateHasExplicitOrderByForLimitToLast(this._query);\n return this.onSnapshotInternal(options, observer);\n }\n\n private onSnapshotInternal(\n options: ListenOptions,\n observer: PartialObserver<firestore.QuerySnapshot<T>>\n ): Unsubscribe {\n let errHandler = (err: Error): void => {\n console.error('Uncaught Error in onSnapshot:', err);\n };\n if (observer.error) {\n errHandler = observer.error.bind(observer);\n }\n\n const asyncObserver = new AsyncObserver<ViewSnapshot>({\n next: (result: ViewSnapshot): void => {\n if (observer.next) {\n observer.next(\n new QuerySnapshot(\n this.firestore,\n this._query,\n result,\n this._converter\n )\n );\n }\n },\n error: errHandler\n });\n\n const firestoreClient = this.firestore.ensureClientConfigured();\n const internalListener = firestoreClient.listen(\n this._query,\n asyncObserver,\n options\n );\n return (): void => {\n asyncObserver.mute();\n firestoreClient.unlisten(internalListener);\n };\n }\n\n private validateHasExplicitOrderByForLimitToLast(query: InternalQuery): void {\n if (query.hasLimitToLast() && query.explicitOrderBy.length === 0) {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'limitToLast() queries require specifying at least one orderBy() clause'\n );\n }\n }\n\n get(options?: firestore.GetOptions): Promise<firestore.QuerySnapshot<T>> {\n validateBetweenNumberOfArgs('Query.get', arguments, 0, 1);\n validateGetOptions('Query.get', options);\n this.validateHasExplicitOrderByForLimitToLast(this._query);\n return new Promise(\n (resolve: Resolver<firestore.QuerySnapshot<T>>, reject: Rejecter) => {\n if (options && options.source === 'cache') {\n this.firestore\n .ensureClientConfigured()\n .getDocumentsFromLocalCache(this._query)\n .then((viewSnap: ViewSnapshot) => {\n resolve(\n new QuerySnapshot(\n this.firestore,\n this._query,\n viewSnap,\n this._converter\n )\n );\n }, reject);\n } else {\n this.getViaSnapshotListener(resolve, reject, options);\n }\n }\n );\n }\n\n private getViaSnapshotListener(\n resolve: Resolver<firestore.QuerySnapshot<T>>,\n reject: Rejecter,\n options?: firestore.GetOptions\n ): void {\n const unlisten = this.onSnapshotInternal(\n {\n includeMetadataChanges: true,\n waitForSyncWhenOnline: true\n },\n {\n next: (result: firestore.QuerySnapshot<T>) => {\n // Remove query first before passing event to user to avoid\n // user actions affecting the now stale query.\n unlisten();\n\n if (\n result.metadata.fromCache &&\n options &&\n options.source === 'server'\n ) {\n reject(\n new FirestoreError(\n Code.UNAVAILABLE,\n 'Failed to get documents from server. (However, these ' +\n 'documents may exist in the local cache. Run again ' +\n 'without setting source to \"server\" to ' +\n 'retrieve the cached documents.)'\n )\n );\n } else {\n resolve(result);\n }\n },\n error: reject\n }\n );\n }\n\n /**\n * Parses the given documentIdValue into a ReferenceValue, throwing\n * appropriate errors if the value is anything other than a DocumentReference\n * or String, or if the string is malformed.\n */\n private parseDocumentIdValue(documentIdValue: unknown): api.Value {\n if (typeof documentIdValue === 'string') {\n if (documentIdValue === '') {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. When querying with FieldPath.documentId(), you ' +\n 'must provide a valid document ID, but it was an empty string.'\n );\n }\n if (\n !this._query.isCollectionGroupQuery() &&\n documentIdValue.indexOf('/') !== -1\n ) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. When querying a collection by ` +\n `FieldPath.documentId(), you must provide a plain document ID, but ` +\n `'${documentIdValue}' contains a '/' character.`\n );\n }\n const path = this._query.path.child(\n ResourcePath.fromString(documentIdValue)\n );\n if (!DocumentKey.isDocumentKey(path)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. When querying a collection group by ` +\n `FieldPath.documentId(), the value provided must result in a valid document path, ` +\n `but '${path}' is not because it has an odd number of segments (${path.length}).`\n );\n }\n return refValue(this.firestore._databaseId, new DocumentKey(path));\n } else if (documentIdValue instanceof DocumentReference) {\n const ref = documentIdValue as DocumentReference<T>;\n return refValue(this.firestore._databaseId, ref._key);\n } else {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. When querying with FieldPath.documentId(), you must provide a valid ` +\n `string or a DocumentReference, but it was: ` +\n `${valueDescription(documentIdValue)}.`\n );\n }\n }\n\n /**\n * Validates that the value passed into a disjunctrive filter satisfies all\n * array requirements.\n */\n private validateDisjunctiveFilterElements(\n value: unknown,\n operator: Operator\n ): void {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid Query. A non-empty array is required for ' +\n `'${operator.toString()}' filters.`\n );\n }\n if (value.length > 10) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid Query. '${operator.toString()}' filters support a ` +\n 'maximum of 10 elements in the value array.'\n );\n }\n if (value.indexOf(null) >= 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid Query. '${operator.toString()}' filters cannot contain 'null' ` +\n 'in the value array.'\n );\n }\n if (value.filter(element => Number.isNaN(element)).length > 0) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid Query. '${operator.toString()}' filters cannot contain 'NaN' ` +\n 'in the value array.'\n );\n }\n }\n\n private validateNewFilter(filter: Filter): void {\n if (filter instanceof FieldFilter) {\n const arrayOps = [Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY];\n const disjunctiveOps = [Operator.IN, Operator.ARRAY_CONTAINS_ANY];\n const isArrayOp = arrayOps.indexOf(filter.op) >= 0;\n const isDisjunctiveOp = disjunctiveOps.indexOf(filter.op) >= 0;\n\n if (filter.isInequality()) {\n const existingField = this._query.getInequalityFilterField();\n if (existingField !== null && !existingField.isEqual(filter.field)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. All where filters with an inequality' +\n ' (<, <=, >, or >=) must be on the same field. But you have' +\n ` inequality filters on '${existingField.toString()}'` +\n ` and '${filter.field.toString()}'`\n );\n }\n\n const firstOrderByField = this._query.getFirstOrderByField();\n if (firstOrderByField !== null) {\n this.validateOrderByAndInequalityMatch(\n filter.field,\n firstOrderByField\n );\n }\n } else if (isDisjunctiveOp || isArrayOp) {\n // You can have at most 1 disjunctive filter and 1 array filter. Check if\n // the new filter conflicts with an existing one.\n let conflictingOp: Operator | null = null;\n if (isDisjunctiveOp) {\n conflictingOp = this._query.findFilterOperator(disjunctiveOps);\n }\n if (conflictingOp === null && isArrayOp) {\n conflictingOp = this._query.findFilterOperator(arrayOps);\n }\n if (conflictingOp != null) {\n // We special case when it's a duplicate op to give a slightly clearer error message.\n if (conflictingOp === filter.op) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid query. You cannot use more than one ' +\n `'${filter.op.toString()}' filter.`\n );\n } else {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. You cannot use '${filter.op.toString()}' filters ` +\n `with '${conflictingOp.toString()}' filters.`\n );\n }\n }\n }\n }\n }\n\n private validateNewOrderBy(orderBy: OrderBy): void {\n if (this._query.getFirstOrderByField() === null) {\n // This is the first order by. It must match any inequality.\n const inequalityField = this._query.getInequalityFilterField();\n if (inequalityField !== null) {\n this.validateOrderByAndInequalityMatch(inequalityField, orderBy.field);\n }\n }\n }\n\n private validateOrderByAndInequalityMatch(\n inequality: FieldPath,\n orderBy: FieldPath\n ): void {\n if (!orderBy.isEqual(inequality)) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid query. You have a where filter with an inequality ` +\n `(<, <=, >, or >=) on field '${inequality.toString()}' ` +\n `and so you must also use '${inequality.toString()}' ` +\n `as your first Query.orderBy(), but your first Query.orderBy() ` +\n `is on field '${orderBy.toString()}' instead.`\n );\n }\n }\n}\n\nexport class QuerySnapshot<T = firestore.DocumentData>\n implements firestore.QuerySnapshot<T> {\n private _cachedChanges: Array<firestore.DocumentChange<T>> | null = null;\n private _cachedChangesIncludeMetadataChanges: boolean | null = null;\n\n readonly metadata: firestore.SnapshotMetadata;\n\n constructor(\n private readonly _firestore: Firestore,\n private readonly _originalQuery: InternalQuery,\n private readonly _snapshot: ViewSnapshot,\n private readonly _converter?: firestore.FirestoreDataConverter<T>\n ) {\n this.metadata = new SnapshotMetadata(\n _snapshot.hasPendingWrites,\n _snapshot.fromCache\n );\n }\n\n get docs(): Array<firestore.QueryDocumentSnapshot<T>> {\n const result: Array<firestore.QueryDocumentSnapshot<T>> = [];\n this.forEach(doc => result.push(doc));\n return result;\n }\n\n get empty(): boolean {\n return this._snapshot.docs.isEmpty();\n }\n\n get size(): number {\n return this._snapshot.docs.size;\n }\n\n forEach(\n callback: (result: firestore.QueryDocumentSnapshot<T>) => void,\n thisArg?: unknown\n ): void {\n validateBetweenNumberOfArgs('QuerySnapshot.forEach', arguments, 1, 2);\n validateArgType('QuerySnapshot.forEach', 'function', 1, callback);\n this._snapshot.docs.forEach(doc => {\n callback.call(thisArg, this.convertToDocumentImpl(doc));\n });\n }\n\n get query(): firestore.Query<T> {\n return new Query(this._originalQuery, this._firestore, this._converter);\n }\n\n docChanges(\n options?: firestore.SnapshotListenOptions\n ): Array<firestore.DocumentChange<T>> {\n if (options) {\n validateOptionNames('QuerySnapshot.docChanges', options, [\n 'includeMetadataChanges'\n ]);\n validateNamedOptionalType(\n 'QuerySnapshot.docChanges',\n 'boolean',\n 'includeMetadataChanges',\n options.includeMetadataChanges\n );\n }\n\n const includeMetadataChanges = !!(\n options && options.includeMetadataChanges\n );\n\n if (includeMetadataChanges && this._snapshot.excludesMetadataChanges) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'To include metadata changes with your document changes, you must ' +\n 'also pass { includeMetadataChanges:true } to onSnapshot().'\n );\n }\n\n if (\n !this._cachedChanges ||\n this._cachedChangesIncludeMetadataChanges !== includeMetadataChanges\n ) {\n this._cachedChanges = changesFromSnapshot<T>(\n this._firestore,\n includeMetadataChanges,\n this._snapshot,\n this._converter\n );\n this._cachedChangesIncludeMetadataChanges = includeMetadataChanges;\n }\n\n return this._cachedChanges;\n }\n\n /** Check the equality. The call can be very expensive. */\n isEqual(other: firestore.QuerySnapshot<T>): boolean {\n if (!(other instanceof QuerySnapshot)) {\n throw invalidClassError('isEqual', 'QuerySnapshot', 1, other);\n }\n\n return (\n this._firestore === other._firestore &&\n this._originalQuery.isEqual(other._originalQuery) &&\n this._snapshot.isEqual(other._snapshot) &&\n this._converter === other._converter\n );\n }\n\n private convertToDocumentImpl(doc: Document): QueryDocumentSnapshot<T> {\n return new QueryDocumentSnapshot(\n this._firestore,\n doc.key,\n doc,\n this.metadata.fromCache,\n this._snapshot.mutatedKeys.has(doc.key),\n this._converter\n );\n }\n}\n\nexport class CollectionReference<T = firestore.DocumentData> extends Query<T>\n implements firestore.CollectionReference<T> {\n constructor(\n readonly _path: ResourcePath,\n firestore: Firestore,\n _converter?: firestore.FirestoreDataConverter<T>\n ) {\n super(InternalQuery.atPath(_path), firestore, _converter);\n if (_path.length % 2 !== 1) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Invalid collection reference. Collection ' +\n 'references must have an odd number of segments, but ' +\n `${_path.canonicalString()} has ${_path.length}`\n );\n }\n }\n\n get id(): string {\n return this._query.path.lastSegment();\n }\n\n get parent(): firestore.DocumentReference<firestore.DocumentData> | null {\n const parentPath = this._query.path.popLast();\n if (parentPath.isEmpty()) {\n return null;\n } else {\n return new DocumentReference<firestore.DocumentData>(\n new DocumentKey(parentPath),\n this.firestore\n );\n }\n }\n\n get path(): string {\n return this._query.path.canonicalString();\n }\n\n doc(pathString?: string): firestore.DocumentReference<T> {\n validateBetweenNumberOfArgs('CollectionReference.doc', arguments, 0, 1);\n // We allow omission of 'pathString' but explicitly prohibit passing in both\n // 'undefined' and 'null'.\n if (arguments.length === 0) {\n pathString = AutoId.newId();\n }\n validateArgType(\n 'CollectionReference.doc',\n 'non-empty string',\n 1,\n pathString\n );\n if (pathString === '') {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Document path must be a non-empty string'\n );\n }\n const path = ResourcePath.fromString(pathString!);\n return DocumentReference.forPath<T>(\n this._query.path.child(path),\n this.firestore,\n this._converter\n );\n }\n\n add(value: T): Promise<firestore.DocumentReference<T>> {\n validateExactNumberOfArgs('CollectionReference.add', arguments, 1);\n const convertedValue = this._converter\n ? this._converter.toFirestore(value)\n : value;\n validateArgType('CollectionReference.add', 'object', 1, convertedValue);\n const docRef = this.doc();\n return docRef.set(value).then(() => docRef);\n }\n\n withConverter<U>(\n converter: firestore.FirestoreDataConverter<U>\n ): firestore.CollectionReference<U> {\n return new CollectionReference<U>(this._path, this.firestore, converter);\n }\n}\n\nfunction validateSetOptions(\n methodName: string,\n options: firestore.SetOptions | undefined\n): firestore.SetOptions {\n if (options === undefined) {\n return {\n merge: false\n };\n }\n\n validateOptionNames(methodName, options, ['merge', 'mergeFields']);\n validateNamedOptionalType(methodName, 'boolean', 'merge', options.merge);\n validateOptionalArrayElements(\n methodName,\n 'mergeFields',\n 'a string or a FieldPath',\n options.mergeFields,\n element =>\n typeof element === 'string' || element instanceof ExternalFieldPath\n );\n\n if (options.mergeFields !== undefined && options.merge !== undefined) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n `Invalid options passed to function ${methodName}(): You cannot specify both \"merge\" ` +\n `and \"mergeFields\".`\n );\n }\n\n return options;\n}\n\nfunction validateSnapshotOptions(\n methodName: string,\n options: firestore.SnapshotOptions | undefined\n): firestore.SnapshotOptions {\n if (options === undefined) {\n return {};\n }\n\n validateOptionNames(methodName, options, ['serverTimestamps']);\n validateNamedOptionalPropertyEquals(\n methodName,\n 'options',\n 'serverTimestamps',\n options.serverTimestamps,\n ['estimate', 'previous', 'none']\n );\n return options;\n}\n\nfunction validateGetOptions(\n methodName: string,\n options: firestore.GetOptions | undefined\n): void {\n validateOptionalArgType(methodName, 'object', 1, options);\n if (options) {\n validateOptionNames(methodName, options, ['source']);\n validateNamedOptionalPropertyEquals(\n methodName,\n 'options',\n 'source',\n options.source,\n ['default', 'server', 'cache']\n );\n }\n}\n\nfunction validateReference<T>(\n methodName: string,\n documentRef: firestore.DocumentReference<T>,\n firestore: Firestore\n): DocumentReference<T> {\n if (!(documentRef instanceof DocumentReference)) {\n throw invalidClassError(methodName, 'DocumentReference', 1, documentRef);\n } else if (documentRef.firestore !== firestore) {\n throw new FirestoreError(\n Code.INVALID_ARGUMENT,\n 'Provided document reference is from a different Firestore instance.'\n );\n } else {\n return documentRef;\n }\n}\n\n/**\n * Calculates the array of firestore.DocumentChange's for a given ViewSnapshot.\n *\n * Exported for testing.\n */\nexport function changesFromSnapshot<T>(\n firestore: Firestore,\n includeMetadataChanges: boolean,\n snapshot: ViewSnapshot,\n converter?: firestore.FirestoreDataConverter<T>\n): Array<firestore.DocumentChange<T>> {\n if (snapshot.oldDocs.isEmpty()) {\n // Special case the first snapshot because index calculation is easy and\n // fast\n let lastDoc: Document;\n let index = 0;\n return snapshot.docChanges.map(change => {\n const doc = new QueryDocumentSnapshot<T>(\n firestore,\n change.doc.key,\n change.doc,\n snapshot.fromCache,\n snapshot.mutatedKeys.has(change.doc.key),\n converter\n );\n debugAssert(\n change.type === ChangeType.Added,\n 'Invalid event type for first snapshot'\n );\n debugAssert(\n !lastDoc || snapshot.query.docComparator(lastDoc, change.doc) < 0,\n 'Got added events in wrong order'\n );\n lastDoc = change.doc;\n return {\n type: 'added' as firestore.DocumentChangeType,\n doc,\n oldIndex: -1,\n newIndex: index++\n };\n });\n } else {\n // A DocumentSet that is updated incrementally as changes are applied to use\n // to lookup the index of a document.\n let indexTracker = snapshot.oldDocs;\n return snapshot.docChanges\n .filter(\n change => includeMetadataChanges || change.type !== ChangeType.Metadata\n )\n .map(change => {\n const doc = new QueryDocumentSnapshot<T>(\n firestore,\n change.doc.key,\n change.doc,\n snapshot.fromCache,\n snapshot.mutatedKeys.has(change.doc.key),\n converter\n );\n let oldIndex = -1;\n let newIndex = -1;\n if (change.type !== ChangeType.Added) {\n oldIndex = indexTracker.indexOf(change.doc.key);\n debugAssert(oldIndex >= 0, 'Index for document not found');\n indexTracker = indexTracker.delete(change.doc.key);\n }\n if (change.type !== ChangeType.Removed) {\n indexTracker = indexTracker.add(change.doc);\n newIndex = indexTracker.indexOf(change.doc.key);\n }\n return { type: resultChangeType(change.type), doc, oldIndex, newIndex };\n });\n }\n}\n\nfunction resultChangeType(type: ChangeType): firestore.DocumentChangeType {\n switch (type) {\n case ChangeType.Added:\n return 'added';\n case ChangeType.Modified:\n case ChangeType.Metadata:\n return 'modified';\n case ChangeType.Removed:\n return 'removed';\n default:\n return fail('Unknown change type: ' + type);\n }\n}\n\n/**\n * Converts custom model object of type T into DocumentData by applying the\n * converter if it exists.\n *\n * This function is used when converting user objects to DocumentData\n * because we want to provide the user with a more specific error message if\n * their set() or fails due to invalid data originating from a toFirestore()\n * call.\n */\nfunction applyFirestoreDataConverter<T>(\n converter: firestore.FirestoreDataConverter<T> | undefined,\n value: T,\n functionName: string\n): [firestore.DocumentData, string] {\n let convertedValue;\n if (converter) {\n convertedValue = converter.toFirestore(value);\n functionName = 'toFirestore() in ' + functionName;\n } else {\n convertedValue = value as firestore.DocumentData;\n }\n return [convertedValue, functionName];\n}\n\nfunction contains(obj: object, key: string): obj is { key: unknown } {\n return Object.prototype.hasOwnProperty.call(obj, key);\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { Code, FirestoreError } from './error';\n\n/**\n * Helper function to prevent instantiation through the constructor.\n *\n * This method creates a new constructor that throws when it's invoked.\n * The prototype of that constructor is then set to the prototype of the hidden\n * \"class\" to expose all the prototype methods and allow for instanceof\n * checks.\n *\n * To also make all the static methods available, all properties of the\n * original constructor are copied to the new constructor.\n */\nexport function makeConstructorPrivate<T extends Function>(\n cls: T,\n optionalMessage?: string\n): T {\n function PublicConstructor(): never {\n let error = 'This constructor is private.';\n if (optionalMessage) {\n error += ' ';\n error += optionalMessage;\n }\n throw new FirestoreError(Code.INVALID_ARGUMENT, error);\n }\n\n // Make sure instanceof checks work and all methods are exposed on the public\n // constructor\n PublicConstructor.prototype = cls.prototype;\n\n // Copy any static methods/members\n Object.assign(PublicConstructor, cls);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return PublicConstructor as any;\n}\n","/**\n * @license\n * Copyright 2020 Google LLC\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\nimport firebase from '@firebase/app';\nimport { FirebaseNamespace } from '@firebase/app-types';\n\nimport { Firestore } from './src/api/database';\nimport { MemoryComponentProvider } from './src/core/component_provider';\nimport { configureForFirebase } from './src/platform/config';\n\nimport './register-module';\nimport './src/platform_browser/browser_init';\n\nimport { name, version } from './package.json';\n\n/**\n * Registers the memory-only Firestore build with the components framework.\n */\nexport function registerFirestore(instance: FirebaseNamespace): void {\n configureForFirebase(\n instance,\n (app, auth) => new Firestore(app, auth, new MemoryComponentProvider())\n );\n instance.registerVersion(name, version);\n}\n\nregisterFirestore(firebase);\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { FirebaseApp, FirebaseNamespace } from '@firebase/app-types';\nimport { FirebaseAuthInternalName } from '@firebase/auth-interop-types';\nimport { _FirebaseNamespace } from '@firebase/app-types/private';\nimport { Component, ComponentType, Provider } from '@firebase/component';\nimport {\n CACHE_SIZE_UNLIMITED,\n Firestore,\n DocumentReference,\n DocumentSnapshot,\n QueryDocumentSnapshot,\n Query,\n QuerySnapshot,\n CollectionReference,\n Transaction,\n WriteBatch\n} from '../api/database';\nimport { Blob } from '../api/blob';\nimport { FieldPath } from '../api/field_path';\nimport { GeoPoint } from '../api/geo_point';\nimport { Timestamp } from '../api/timestamp';\nimport { makeConstructorPrivate } from '../util/api';\nimport { FieldValue } from '../api/field_value';\n\n// Public instance that disallows construction at runtime. Note that this still\n// allows instanceof checks.\nexport const PublicFirestore = makeConstructorPrivate(\n Firestore,\n 'Use firebase.firestore() instead.'\n);\nexport const PublicTransaction = makeConstructorPrivate(\n Transaction,\n 'Use firebase.firestore().runTransaction() instead.'\n);\nexport const PublicWriteBatch = makeConstructorPrivate(\n WriteBatch,\n 'Use firebase.firestore().batch() instead.'\n);\nexport const PublicDocumentReference = makeConstructorPrivate(\n DocumentReference,\n 'Use firebase.firestore().doc() instead.'\n);\nexport const PublicDocumentSnapshot = makeConstructorPrivate(DocumentSnapshot);\nexport const PublicQueryDocumentSnapshot = makeConstructorPrivate(\n QueryDocumentSnapshot\n);\nexport const PublicQuery = makeConstructorPrivate(Query);\nexport const PublicQuerySnapshot = makeConstructorPrivate(QuerySnapshot);\nexport const PublicCollectionReference = makeConstructorPrivate(\n CollectionReference,\n 'Use firebase.firestore().collection() instead.'\n);\nexport const PublicFieldValue = makeConstructorPrivate(\n FieldValue,\n 'Use FieldValue.<field>() instead.'\n);\nexport const PublicBlob = makeConstructorPrivate(\n Blob,\n 'Use Blob.fromUint8Array() or Blob.fromBase64String() instead.'\n);\n\nconst firestoreNamespace = {\n Firestore: PublicFirestore,\n GeoPoint,\n Timestamp,\n Blob: PublicBlob,\n Transaction: PublicTransaction,\n WriteBatch: PublicWriteBatch,\n DocumentReference: PublicDocumentReference,\n DocumentSnapshot: PublicDocumentSnapshot,\n Query: PublicQuery,\n QueryDocumentSnapshot: PublicQueryDocumentSnapshot,\n QuerySnapshot: PublicQuerySnapshot,\n CollectionReference: PublicCollectionReference,\n FieldPath,\n FieldValue: PublicFieldValue,\n setLogLevel: Firestore.setLogLevel,\n CACHE_SIZE_UNLIMITED\n};\n\n/**\n * Configures Firestore as part of the Firebase SDK by calling registerService.\n *\n * @param firebase The FirebaseNamespace to register Firestore with\n * @param firestoreFactory A factory function that returns a new Firestore\n * instance.\n */\nexport function configureForFirebase(\n firebase: FirebaseNamespace,\n firestoreFactory: (\n app: FirebaseApp,\n auth: Provider<FirebaseAuthInternalName>\n ) => Firestore\n): void {\n (firebase as _FirebaseNamespace).INTERNAL.registerComponent(\n new Component(\n 'firestore',\n container => {\n const app = container.getProvider('app').getImmediate()!;\n return firestoreFactory(app, container.getProvider('auth-internal'));\n },\n ComponentType.PUBLIC\n ).setServiceProps({ ...firestoreNamespace })\n );\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { ConnectivityMonitor, NetworkStatus } from './connectivity_monitor';\n\nexport class NoopConnectivityMonitor implements ConnectivityMonitor {\n addCallback(callback: (status: NetworkStatus) => void): void {\n // No-op.\n }\n\n shutdown(): void {\n // No-op.\n }\n}\n","/**\n * @license\n * Copyright 2019 Google LLC\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\nimport { logDebug } from '../util/log';\nimport {\n ConnectivityMonitor,\n ConnectivityMonitorCallback,\n NetworkStatus\n} from './../remote/connectivity_monitor';\n\n// References to `window` are guarded by BrowserConnectivityMonitor.isAvailable()\n/* eslint-disable no-restricted-globals */\n\nconst LOG_TAG = 'ConnectivityMonitor';\n\n/**\n * Browser implementation of ConnectivityMonitor.\n */\nexport class BrowserConnectivityMonitor implements ConnectivityMonitor {\n private readonly networkAvailableListener = (): void =>\n this.onNetworkAvailable();\n private readonly networkUnavailableListener = (): void =>\n this.onNetworkUnavailable();\n private callbacks: ConnectivityMonitorCallback[] = [];\n\n constructor() {\n this.configureNetworkMonitoring();\n }\n\n addCallback(callback: (status: NetworkStatus) => void): void {\n this.callbacks.push(callback);\n }\n\n shutdown(): void {\n window.removeEventListener('online', this.networkAvailableListener);\n window.removeEventListener('offline', this.networkUnavailableListener);\n }\n\n private configureNetworkMonitoring(): void {\n window.addEventListener('online', this.networkAvailableListener);\n window.addEventListener('offline', this.networkUnavailableListener);\n }\n\n private onNetworkAvailable(): void {\n logDebug(LOG_TAG, 'Network connectivity changed: AVAILABLE');\n for (const callback of this.callbacks) {\n callback(NetworkStatus.AVAILABLE);\n }\n }\n\n private onNetworkUnavailable(): void {\n logDebug(LOG_TAG, 'Network connectivity changed: UNAVAILABLE');\n for (const callback of this.callbacks) {\n callback(NetworkStatus.UNAVAILABLE);\n }\n }\n\n // TODO(chenbrian): Consider passing in window either into this component or\n // here for testing via FakeWindow.\n /** Checks that all used attributes of window are available. */\n static isAvailable(): boolean {\n return (\n typeof window !== 'undefined' &&\n window.addEventListener !== undefined &&\n window.removeEventListener !== undefined\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { debugAssert } from '../util/assert';\nimport { FirestoreError } from '../util/error';\n\nimport { Stream } from './connection';\n\n/**\n * Provides a simple helper class that implements the Stream interface to\n * bridge to other implementations that are streams but do not implement the\n * interface. The stream callbacks are invoked with the callOn... methods.\n */\nexport class StreamBridge<I, O> implements Stream<I, O> {\n private wrappedOnOpen: (() => void) | undefined;\n private wrappedOnClose: ((err?: FirestoreError) => void) | undefined;\n private wrappedOnMessage: ((msg: O) => void) | undefined;\n\n private sendFn: (msg: I) => void;\n private closeFn: () => void;\n\n constructor(args: { sendFn: (msg: I) => void; closeFn: () => void }) {\n this.sendFn = args.sendFn;\n this.closeFn = args.closeFn;\n }\n\n onOpen(callback: () => void): void {\n debugAssert(!this.wrappedOnOpen, 'Called onOpen on stream twice!');\n this.wrappedOnOpen = callback;\n }\n\n onClose(callback: (err?: FirestoreError) => void): void {\n debugAssert(!this.wrappedOnClose, 'Called onClose on stream twice!');\n this.wrappedOnClose = callback;\n }\n\n onMessage(callback: (msg: O) => void): void {\n debugAssert(!this.wrappedOnMessage, 'Called onMessage on stream twice!');\n this.wrappedOnMessage = callback;\n }\n\n close(): void {\n this.closeFn();\n }\n\n send(msg: I): void {\n this.sendFn(msg);\n }\n\n callOnOpen(): void {\n debugAssert(\n this.wrappedOnOpen !== undefined,\n 'Cannot call onOpen because no callback was set'\n );\n this.wrappedOnOpen();\n }\n\n callOnClose(err?: FirestoreError): void {\n debugAssert(\n this.wrappedOnClose !== undefined,\n 'Cannot call onClose because no callback was set'\n );\n this.wrappedOnClose(err);\n }\n\n callOnMessage(msg: O): void {\n debugAssert(\n this.wrappedOnMessage !== undefined,\n 'Cannot call onMessage because no callback was set'\n );\n this.wrappedOnMessage(msg);\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport {\n createWebChannelTransport,\n ErrorCode,\n EventType,\n WebChannel,\n WebChannelError,\n WebChannelOptions,\n XhrIo\n} from '@firebase/webchannel-wrapper';\n\nimport {\n isBrowserExtension,\n isElectron,\n isIE,\n isMobileCordova,\n isReactNative,\n isUWP\n} from '@firebase/util';\n\nimport { Token } from '../api/credentials';\nimport { DatabaseId, DatabaseInfo } from '../core/database_info';\nimport { SDK_VERSION } from '../core/version';\nimport { Connection, Stream } from '../remote/connection';\nimport {\n mapCodeFromRpcStatus,\n mapCodeFromHttpResponseErrorStatus\n} from '../remote/rpc_error';\nimport { StreamBridge } from '../remote/stream_bridge';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug } from '../util/log';\nimport { Indexable } from '../util/misc';\nimport { Rejecter, Resolver } from '../util/promise';\nimport { StringMap } from '../util/types';\n\nconst LOG_TAG = 'Connection';\n\nconst RPC_STREAM_SERVICE = 'google.firestore.v1.Firestore';\nconst RPC_URL_VERSION = 'v1';\n\n/**\n * Maps RPC names to the corresponding REST endpoint name.\n * Uses Object Literal notation to avoid renaming.\n */\nconst RPC_NAME_REST_MAPPING: { [key: string]: string } = {};\nRPC_NAME_REST_MAPPING['BatchGetDocuments'] = 'batchGet';\nRPC_NAME_REST_MAPPING['Commit'] = 'commit';\n\n// TODO(b/38203344): The SDK_VERSION is set independently from Firebase because\n// we are doing out-of-band releases. Once we release as part of Firebase, we\n// should use the Firebase version instead.\nconst X_GOOG_API_CLIENT_VALUE = 'gl-js/ fire/' + SDK_VERSION;\n\nconst XHR_TIMEOUT_SECS = 15;\n\nexport class WebChannelConnection implements Connection {\n private readonly databaseId: DatabaseId;\n private readonly baseUrl: string;\n private readonly forceLongPolling: boolean;\n\n constructor(info: DatabaseInfo) {\n this.databaseId = info.databaseId;\n const proto = info.ssl ? 'https' : 'http';\n this.baseUrl = proto + '://' + info.host;\n this.forceLongPolling = info.forceLongPolling;\n }\n\n /**\n * Modifies the headers for a request, adding any authorization token if\n * present and any additional headers for the request.\n */\n private modifyHeadersForRequest(\n headers: StringMap,\n token: Token | null\n ): void {\n if (token) {\n for (const header in token.authHeaders) {\n if (token.authHeaders.hasOwnProperty(header)) {\n headers[header] = token.authHeaders[header];\n }\n }\n }\n headers['X-Goog-Api-Client'] = X_GOOG_API_CLIENT_VALUE;\n }\n\n invokeRPC<Req, Resp>(\n rpcName: string,\n request: Req,\n token: Token | null\n ): Promise<Resp> {\n const url = this.makeUrl(rpcName);\n\n return new Promise((resolve: Resolver<Resp>, reject: Rejecter) => {\n const xhr = new XhrIo();\n xhr.listenOnce(EventType.COMPLETE, () => {\n try {\n switch (xhr.getLastErrorCode()) {\n case ErrorCode.NO_ERROR:\n const json = xhr.getResponseJson() as Resp;\n logDebug(LOG_TAG, 'XHR received:', JSON.stringify(json));\n resolve(json);\n break;\n case ErrorCode.TIMEOUT:\n logDebug(LOG_TAG, 'RPC \"' + rpcName + '\" timed out');\n reject(\n new FirestoreError(Code.DEADLINE_EXCEEDED, 'Request time out')\n );\n break;\n case ErrorCode.HTTP_ERROR:\n const status = xhr.getStatus();\n logDebug(\n LOG_TAG,\n 'RPC \"' + rpcName + '\" failed with status:',\n status,\n 'response text:',\n xhr.getResponseText()\n );\n if (status > 0) {\n const responseError = (xhr.getResponseJson() as WebChannelError)\n .error;\n if (\n !!responseError &&\n !!responseError.status &&\n !!responseError.message\n ) {\n const firestoreErrorCode = mapCodeFromHttpResponseErrorStatus(\n responseError.status\n );\n reject(\n new FirestoreError(\n firestoreErrorCode,\n responseError.message\n )\n );\n } else {\n reject(\n new FirestoreError(\n Code.UNKNOWN,\n 'Server responded with status ' + xhr.getStatus()\n )\n );\n }\n } else {\n // If we received an HTTP_ERROR but there's no status code,\n // it's most probably a connection issue\n logDebug(LOG_TAG, 'RPC \"' + rpcName + '\" failed');\n reject(\n new FirestoreError(Code.UNAVAILABLE, 'Connection failed.')\n );\n }\n break;\n default:\n fail(\n 'RPC \"' +\n rpcName +\n '\" failed with unanticipated ' +\n 'webchannel error ' +\n xhr.getLastErrorCode() +\n ': ' +\n xhr.getLastError() +\n ', giving up.'\n );\n }\n } finally {\n logDebug(LOG_TAG, 'RPC \"' + rpcName + '\" completed.');\n }\n });\n\n // The database field is already encoded in URL. Specifying it again in\n // the body is not necessary in production, and will cause duplicate field\n // errors in the Firestore Emulator. Let's remove it.\n const jsonObj = ({ ...request } as unknown) as Indexable;\n delete jsonObj.database;\n\n const requestString = JSON.stringify(jsonObj);\n logDebug(LOG_TAG, 'XHR sending: ', url + ' ' + requestString);\n // Content-Type: text/plain will avoid preflight requests which might\n // mess with CORS and redirects by proxies. If we add custom headers\n // we will need to change this code to potentially use the\n // $httpOverwrite parameter supported by ESF to avoid\n // triggering preflight requests.\n const headers: StringMap = { 'Content-Type': 'text/plain' };\n\n this.modifyHeadersForRequest(headers, token);\n\n xhr.send(url, 'POST', requestString, headers, XHR_TIMEOUT_SECS);\n });\n }\n\n invokeStreamingRPC<Req, Resp>(\n rpcName: string,\n request: Req,\n token: Token | null\n ): Promise<Resp[]> {\n // The REST API automatically aggregates all of the streamed results, so we\n // can just use the normal invoke() method.\n return this.invokeRPC<Req, Resp[]>(rpcName, request, token);\n }\n\n openStream<Req, Resp>(\n rpcName: string,\n token: Token | null\n ): Stream<Req, Resp> {\n const urlParts = [\n this.baseUrl,\n '/',\n RPC_STREAM_SERVICE,\n '/',\n rpcName,\n '/channel'\n ];\n const webchannelTransport = createWebChannelTransport();\n const request: WebChannelOptions = {\n // Required for backend stickiness, routing behavior is based on this\n // parameter.\n httpSessionIdParam: 'gsessionid',\n initMessageHeaders: {},\n messageUrlParams: {\n // This param is used to improve routing and project isolation by the\n // backend and must be included in every request.\n database: `projects/${this.databaseId.projectId}/databases/${this.databaseId.database}`\n },\n sendRawJson: true,\n supportsCrossDomainXhr: true,\n internalChannelParams: {\n // Override the default timeout (randomized between 10-20 seconds) since\n // a large write batch on a slow internet connection may take a long\n // time to send to the backend. Rather than have WebChannel impose a\n // tight timeout which could lead to infinite timeouts and retries, we\n // set it very large (5-10 minutes) and rely on the browser's builtin\n // timeouts to kick in if the request isn't working.\n forwardChannelRequestTimeoutMs: 10 * 60 * 1000\n },\n forceLongPolling: this.forceLongPolling\n };\n\n this.modifyHeadersForRequest(request.initMessageHeaders!, token);\n\n // Sending the custom headers we just added to request.initMessageHeaders\n // (Authorization, etc.) will trigger the browser to make a CORS preflight\n // request because the XHR will no longer meet the criteria for a \"simple\"\n // CORS request:\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests\n //\n // Therefore to avoid the CORS preflight request (an extra network\n // roundtrip), we use the httpHeadersOverwriteParam option to specify that\n // the headers should instead be encoded into a special \"$httpHeaders\" query\n // parameter, which is recognized by the webchannel backend. This is\n // formally defined here:\n // https://github.com/google/closure-library/blob/b0e1815b13fb92a46d7c9b3c30de5d6a396a3245/closure/goog/net/rpc/httpcors.js#L32\n //\n // TODO(b/145624756): There is a backend bug where $httpHeaders isn't respected if the request\n // doesn't have an Origin header. So we have to exclude a few browser environments that are\n // known to (sometimes) not include an Origin. See\n // https://github.com/firebase/firebase-js-sdk/issues/1491.\n if (\n !isMobileCordova() &&\n !isReactNative() &&\n !isElectron() &&\n !isIE() &&\n !isUWP() &&\n !isBrowserExtension()\n ) {\n request.httpHeadersOverwriteParam = '$httpHeaders';\n }\n\n const url = urlParts.join('');\n logDebug(LOG_TAG, 'Creating WebChannel: ' + url + ' ' + request);\n const channel = webchannelTransport.createWebChannel(url, request);\n\n // WebChannel supports sending the first message with the handshake - saving\n // a network round trip. However, it will have to call send in the same\n // JS event loop as open. In order to enforce this, we delay actually\n // opening the WebChannel until send is called. Whether we have called\n // open is tracked with this variable.\n let opened = false;\n\n // A flag to determine whether the stream was closed (by us or through an\n // error/close event) to avoid delivering multiple close events or sending\n // on a closed stream\n let closed = false;\n\n const streamBridge = new StreamBridge<Req, Resp>({\n sendFn: (msg: Req) => {\n if (!closed) {\n if (!opened) {\n logDebug(LOG_TAG, 'Opening WebChannel transport.');\n channel.open();\n opened = true;\n }\n logDebug(LOG_TAG, 'WebChannel sending:', msg);\n channel.send(msg);\n } else {\n logDebug(LOG_TAG, 'Not sending because WebChannel is closed:', msg);\n }\n },\n closeFn: () => channel.close()\n });\n\n // Closure events are guarded and exceptions are swallowed, so catch any\n // exception and rethrow using a setTimeout so they become visible again.\n // Note that eventually this function could go away if we are confident\n // enough the code is exception free.\n const unguardedEventListen = <T>(\n type: string,\n fn: (param?: T) => void\n ): void => {\n // TODO(dimond): closure typing seems broken because WebChannel does\n // not implement goog.events.Listenable\n channel.listen(type, (param: unknown) => {\n try {\n fn(param as T);\n } catch (e) {\n setTimeout(() => {\n throw e;\n }, 0);\n }\n });\n };\n\n unguardedEventListen(WebChannel.EventType.OPEN, () => {\n if (!closed) {\n logDebug(LOG_TAG, 'WebChannel transport opened.');\n }\n });\n\n unguardedEventListen(WebChannel.EventType.CLOSE, () => {\n if (!closed) {\n closed = true;\n logDebug(LOG_TAG, 'WebChannel transport closed');\n streamBridge.callOnClose();\n }\n });\n\n unguardedEventListen<Error>(WebChannel.EventType.ERROR, err => {\n if (!closed) {\n closed = true;\n logDebug(LOG_TAG, 'WebChannel transport errored:', err);\n streamBridge.callOnClose(\n new FirestoreError(\n Code.UNAVAILABLE,\n 'The operation could not be completed'\n )\n );\n }\n });\n\n // WebChannel delivers message events as array. If batching is not enabled\n // (it's off by default) each message will be delivered alone, resulting in\n // a single element array.\n interface WebChannelResponse {\n data: Resp[];\n }\n\n unguardedEventListen<WebChannelResponse>(\n WebChannel.EventType.MESSAGE,\n msg => {\n if (!closed) {\n const msgData = msg!.data[0];\n hardAssert(!!msgData, 'Got a webchannel message without data.');\n // TODO(b/35143891): There is a bug in One Platform that caused errors\n // (and only errors) to be wrapped in an extra array. To be forward\n // compatible with the bug we need to check either condition. The latter\n // can be removed once the fix has been rolled out.\n // Use any because msgData.error is not typed.\n const msgDataOrError: WebChannelError | object = msgData;\n const error =\n msgDataOrError.error ||\n (msgDataOrError as WebChannelError[])[0]?.error;\n if (error) {\n logDebug(LOG_TAG, 'WebChannel received error:', error);\n // error.status will be a string like 'OK' or 'NOT_FOUND'.\n const status: string = error.status;\n let code = mapCodeFromRpcStatus(status);\n let message = error.message;\n if (code === undefined) {\n code = Code.INTERNAL;\n message =\n 'Unknown error status: ' +\n status +\n ' with message ' +\n error.message;\n }\n // Mark closed so no further events are propagated\n closed = true;\n streamBridge.callOnClose(new FirestoreError(code, message));\n channel.close();\n } else {\n logDebug(LOG_TAG, 'WebChannel received:', msgData);\n streamBridge.callOnMessage(msgData);\n }\n }\n }\n );\n\n setTimeout(() => {\n // Technically we could/should wait for the WebChannel opened event,\n // but because we want to send the first message with the WebChannel\n // handshake we pretend the channel opened here (asynchronously), and\n // then delay the actual open until the first message is sent.\n streamBridge.callOnOpen();\n }, 0);\n return streamBridge;\n }\n\n // visible for testing\n makeUrl(rpcName: string): string {\n const urlRpcName = RPC_NAME_REST_MAPPING[rpcName];\n debugAssert(\n urlRpcName !== undefined,\n 'Unknown REST mapping for: ' + rpcName\n );\n return (\n this.baseUrl +\n '/' +\n RPC_URL_VERSION +\n '/projects/' +\n this.databaseId.projectId +\n '/databases/' +\n this.databaseId.database +\n '/documents:' +\n urlRpcName\n );\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { DatabaseId, DatabaseInfo } from '../core/database_info';\nimport { Platform } from '../platform/platform';\nimport { Connection } from '../remote/connection';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport { ConnectivityMonitor } from './../remote/connectivity_monitor';\n\nimport { NoopConnectivityMonitor } from '../remote/connectivity_monitor_noop';\nimport { BrowserConnectivityMonitor } from './browser_connectivity_monitor';\nimport { WebChannelConnection } from './webchannel_connection';\nimport { debugAssert } from '../util/assert';\n\n// Implements the Platform API for browsers and some browser-like environments\n// (including ReactNative).\nexport class BrowserPlatform implements Platform {\n readonly base64Available: boolean;\n\n constructor() {\n this.base64Available = typeof atob !== 'undefined';\n }\n\n get document(): Document | null {\n // `document` is not always available, e.g. in ReactNative and WebWorkers.\n // eslint-disable-next-line no-restricted-globals\n return typeof document !== 'undefined' ? document : null;\n }\n\n get window(): Window | null {\n // `window` is not always available, e.g. in ReactNative and WebWorkers.\n // eslint-disable-next-line no-restricted-globals\n return typeof window !== 'undefined' ? window : null;\n }\n\n loadConnection(databaseInfo: DatabaseInfo): Promise<Connection> {\n return Promise.resolve(new WebChannelConnection(databaseInfo));\n }\n\n newConnectivityMonitor(): ConnectivityMonitor {\n if (BrowserConnectivityMonitor.isAvailable()) {\n return new BrowserConnectivityMonitor();\n } else {\n return new NoopConnectivityMonitor();\n }\n }\n\n newSerializer(databaseId: DatabaseId): JsonProtoSerializer {\n return new JsonProtoSerializer(databaseId, { useProto3Json: true });\n }\n\n formatJSON(value: unknown): string {\n return JSON.stringify(value);\n }\n\n atob(encoded: string): string {\n return atob(encoded);\n }\n\n btoa(raw: string): string {\n return btoa(raw);\n }\n\n randomBytes(nBytes: number): Uint8Array {\n debugAssert(nBytes >= 0, `Expecting non-negative nBytes, got: ${nBytes}`);\n\n // Polyfills for IE and WebWorker by using `self` and `msCrypto` when `crypto` is not available.\n const crypto =\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n typeof self !== 'undefined' && (self.crypto || (self as any)['msCrypto']);\n const bytes = new Uint8Array(nBytes);\n if (crypto) {\n crypto.getRandomValues(bytes);\n } else {\n // Falls back to Math.random\n for (let i = 0; i < nBytes; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return bytes;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google LLC\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\nimport { PlatformSupport } from '../platform/platform';\nimport { BrowserPlatform } from './browser_platform';\n\n/**\n * This code needs to run before Firestore is used. This can be achieved in\n * several ways:\n * 1) Through the JSCompiler compiling this code and then (automatically)\n * executing it before exporting the Firestore symbols.\n * 2) Through importing this module first in a Firestore main module\n */\nPlatformSupport.setPlatform(new BrowserPlatform());\n"],"names":["LogLevel","extendStatics","d","b","Object","setPrototypeOf","__proto__","Array","p","hasOwnProperty","__extends","__","this","constructor","prototype","create","__awaiter","thisArg","_arguments","P","generator","Promise","resolve","reject","fulfilled","value","step","next","e","rejected","result","done","then","apply","__generator","body","f","y","t","g","_","label","sent","trys","ops","verb","throw","return","Symbol","iterator","n","v","op","TypeError","call","pop","length","push","__spreadArrays","s","i","il","arguments","r","k","a","j","jl","DEBUG","VERBOSE","INFO","WARN","ERROR","SILENT","defaultLogHandler","instance","logType","_i","args","logLevel","now","Date","toISOString","method","ConsoleMethod","Error","console","name","defaultLogLevel","_a","Logger","_logLevel","val","_logHandler","_userLogHandler","getUA","navigator","code","message","_super","_this","FirebaseError","captureStackTrace","ErrorFactory","data","customData","fullCode","service","template","errors","replace","PATTERN","key","toString","fullMessage","serviceName","error","_b","keys","slice","warn","Component","mode","instantiationMode","multipleInstances","props","serviceProps","instanceFactory","type","goog","goog.global","self","goog.typeOf","className","goog.isArrayLike","goog.isObject","goog.UID_PROPERTY_","Math","goog.uidCounter_","fn","selfObj","var_args","goog.bindJs_","boundArgs","newArgs","goog.bind","Function","goog.bindNative_","goog.partial","goog.now","childCtor","parentCtor","tempCtor","goog.Disposable","goog.Disposable.prototype.dispose","goog.Disposable.MONITORING_MODE","obj","goog.getUid","goog.Disposable.prototype.disposeInternal","goog.array.indexOf","arr","opt_fromIndex","fromIndex","goog.array.forEach","opt_obj","l","arr2","goog.array.concat","goog.array.toArray","object","rv","goog.string.internal.isEmptyOrWhitespace","str","goog.labs.userAgent.util.userAgent_","goog.string.internal.trim","String","subString","goog.string.internal.compareElements_","left","right","goog.global.navigator","userAgent","goog.object.forEach","goog.object.clone","res","goog.object.PROTOTYPE_FIELDS_","target","source","goog.object.PROTOTYPE_FIELDS_.length","goog.reflect.sinkValue","x","goog.nullFunction","goog.userAgent.VERSION","goog.userAgent.OPERA","goog.string.internal.contains","goog.userAgent.IE","goog.userAgent.EDGE","goog.userAgent.EDGE_OR_IE","goog.userAgent.GECKO","goog.userAgent.WEBKIT","doc","undefined","version","docMode","goog.userAgent.getDocumentMode_","parseFloat","Oa","Ta","goog.userAgent.isVersionOrHigherCache_","valueFn","order","v1Subs","v2Subs","subCount","subIdx","v1Sub","v2Sub","v1Comp","v2Comp","parseInt","cacheObj","documentMode","goog.userAgent.DOCUMENT_MODE","HAS_W3C_EVENT_SUPPORT","Number","SET_KEY_CODE_TO_PREVENT_DEFAULT","goog.userAgent.isVersionOrHigher","PASSIVE_EVENTS","goog.global.addEventListener","passive","options","get","goog.global.removeEventListener","goog.events.Event","opt_target","goog.events.BrowserEvent","opt_e","opt_currentTarget","goog.events.Event.call","init","relevantTouch","relatedTarget","MOUSEOVER","MOUSEOUT","goog.events.BrowserEvent.IE_POINTER_TYPE_MAP","goog.events.Event.prototype.preventDefault","goog.inherits","arg","2","TOUCH","3","PEN","4","MOUSE","goog.events.BrowserEvent.prototype.preventDefault","goog.events.BrowserEvent.superClass_.preventDefault.call","be","goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT","VK_F1","VK_F12","ex","goog.events.Listenable.IMPLEMENTED_BY_PROP","goog.events.ListenableKey.counter_","goog.events.Listener","listener","src","capture","opt_handler","proxy","goog.events.Listener.prototype.markAsRemoved","goog.events.ListenerMap","goog.events.ListenerMap.prototype.removeByKey","markAsRemoved","goog.events.ListenerMap.findListenerIndex_","listenerArray","opt_useCapture","opt_listenerScope","listenerObj","goog.events.ListenerMap.prototype.add","callOnce","typeStr","index","goog.events.LISTENER_MAP_PROP_","goog.events.onStringMap_","opt_options","goog.events.listenOnce","goog.events.wrapListener","goog.events.listen_","goog.events.listen","goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT","proxyCallbackFunction","listenerMap","goog.events.getListenerMap_","goog.events.handleBrowserEvent_","eventObject","goog.events.BrowserFeature.PASSIVE_EVENTS","goog.events.getOnString_","goog.events.unlistenByKey","removeByKey","goog.events.onString_","goog.events.fireListener","listenerFn","listenerHandler","opt_evt","ieEvent","parts","cur","evt","goog.events.LISTENER_WRAPPER_PROP_","goog.events.EventTarget","goog.Disposable.call","goog.events.EventTarget.prototype.fireListeners","unlistenByKey","B","goog.events.EventTarget.prototype.addEventListener","handler","opt_capture","opt_handlerScope","goog.events.EventTarget.prototype.removeEventListener","goog.events.unlisten","eventTargetListeners_","c","goog.events.EventTarget.prototype.dispatchEvent","ancestorsTree","ancestor","getParentEventTarget","actualEventTarget_","oldEvent","opt_ancestorsTree","currentTarget","fireListeners","goog.events.EventTarget.prototype.disposeInternal","goog.events.EventTarget.superClass_.disposeInternal.call","removeAllListeners","goog.events.EventTarget.prototype.listen","goog.events.EventTarget.prototype.listenOnce","goog.json.serialize","goog.async.WorkQueue","Db","goog.async.WorkQueue.freelist_","class_1","item","goog.async.WorkItem","goog.async.WorkQueue.DEFAULT_MAX_UNUSED","reset","limit","goog.async.throwException","exception","goog.global.setTimeout","goog.async.run","callback","opt_context","promise","goog.async.run.schedule_","goog.global.Promise.resolve","goog.async.run.processWorkQueue","goog.async.run.workQueueScheduled_","goog.async.WorkQueue.prototype.add","scope","goog.async.WorkQueue.freelist_.get","goog.async.WorkItem.prototype.set","goog.async.WorkItem.prototype.reset","goog.async.run.workQueue_","goog.async.WorkQueue.prototype.remove","goog.async.run.workQueue_.remove","put","goog.async.WorkQueue.freelist_.put","goog.Timer","opt_interval","opt_timerObject","goog.events.EventTarget.call","goog.Timer.prototype.stop","opt_delay","goog.async.Throttle","interval","goog.async.Throttle.prototype.doAction_","goog.Timer.callOnce","goog.events.EventHandler","opt_scope","Hb","goog.Timer.prototype.tick_","elapsed","goog.Timer.intervalScale","dispatchTick","goog.Timer.TICK","stop","goog.Timer.prototype.start","goog.Timer.prototype","goog.Timer.prototype.disposeInternal","goog.Timer.superClass_.disposeInternal.call","Kb","goog.async.Throttle.prototype.fire","doAction_","goog.async.Throttle.prototype.disposeInternal","goog.async.Throttle.superClass_.disposeInternal.call","goog.global.clearTimeout","goog.async.Throttle.prototype.onTimer_","goog.events.EventHandler.typeArray_","goog.events.EventHandler.prototype.listen_","opt_fn","goog.events.EventHandler.prototype.removeAll","goog.labs.net.webChannel.WebChannelDebug","goog.events.EventHandler.prototype.disposeInternal","goog.events.EventHandler.superClass_.disposeInternal.call","removeAll","goog.events.EventHandler.prototype.handleEvent","goog.labs.net.webChannel.requestStats.eventTarget_","requestStats.ServerReachabilityEvent","goog.labs.net.webChannel.requestStats.Event.SERVER_REACHABILITY_EVENT","reachabilityType","goog.labs.net.webChannel.requestStats.getStatEventTarget_","goog.labs.net.webChannel.requestStats.ServerReachabilityEvent","requestStats.StatEvent","eventTarget","goog.labs.net.webChannel.requestStats.Event.STAT_EVENT","stat","goog.labs.net.webChannel.requestStats.StatEvent","requestStats.TimingEvent","goog.labs.net.webChannel.requestStats.Event.TIMING_EVENT","ms","goog.labs.net.webChannel.requestStats.TimingEvent","goog.net.ErrorCode","NO_ERROR","ACCESS_DENIED","FILE_NOT_FOUND","FF_SILENT_ERROR","CUSTOM_ERROR","EXCEPTION","HTTP_ERROR","ABORT","TIMEOUT","OFFLINE","goog.net.EventType","COMPLETE","SUCCESS","READY","READY_STATE_CHANGE","INCREMENTAL_DATA","PROGRESS","DOWNLOAD_PROGRESS","UPLOAD_PROGRESS","goog.net.XmlHttpFactory","goog.net.XmlHttpFactory.prototype.getOptions","cachedOptions_","goog.net.WebChannel","cc","goog.net.WebChannel.EventType","OPEN","CLOSE","MESSAGE","goog.labs.net.webChannel.ChannelRequest","channel","opt_requestId","opt_retryId","goog.labs.net.webChannel.ChannelRequest.TIMEOUT_MS_","EDGE_POLLING_INTERVAL_","goog.net.WebChannel.MessageEvent","goog.net.WebChannel.ErrorEvent","goog.net.DefaultXmlHttpFactory","factory","goog.labs.net.webChannel.ChannelRequest.INVALID_CHUNK_","goog.labs.net.webChannel.ChannelRequest.INCOMPLETE_CHUNK_","ChannelRequest.prototype.xmlHttpPost","uri","postData","XML_HTTP","makeUnique","clone","decodeChunks","sendXmlHttp_","ChannelRequest.prototype.sendXmlHttp_","hostPrefix","ensureWatchDogTimer_","requestUri_","retryId_","values","createXhrIo","headers","REQUEST_MADE","ChannelRequest.prototype.decodeNextChunks_","readyState","responseText","decodeNextChunksSuccessful","chunkText","getNextChunk_","BAD_DATA","goog.labs.net.webChannel.requestStats.notifyStatEvent","REQUEST_INCOMPLETE_DATA","REQUEST_BAD_DATA","safeOnRequestData_","NO_DATA","REQUEST_NO_DATA","cleanup_","dispatchFailure_","ChannelRequest.prototype.getNextChunk_","sizeStartIndex","sizeEndIndex","isNaN","size","chunkStartIndex","ChannelRequest.prototype.ensureWatchDogTimer_","startWatchDogTimer_","ChannelRequest.prototype.startWatchDogTimer_","time","goog.labs.net.webChannel.requestStats.setTimeout","ChannelRequest.prototype.cancelWatchDogTimer_","ChannelRequest.prototype.dispatchFailure_","CLOSED","onRequestComplete","ChannelRequest.prototype.cleanup_","cancelWatchDogTimer_","readyStateChangeThrottle_","xmlhttp","ChannelRequest.prototype.safeOnRequestData_","channel_","request","hasRequest","OPENED","response","responseValues","handlePostResponse_","goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE","clearDeadBackchannelTimer_","BACKCHANNEL_MISSING","goog.labs.net.webChannel.WebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF","getRequestCount","onForwardChannelFlushed_","signalError_","BAD_RESPONSE","respArray","nextArray","onInput_","OPENING","negotiatedVersion","serverKeepaliveMs","applyControlHeaders_","xhr","clientProtocol","goog.net.WebChannel.X_CLIENT_WIRE_PROTOCOL","forwardChannelRequestPool_","z","Set","addRequest","getHttpSessionIdParam","httpSessionIdHeader","goog.net.WebChannel.X_HTTP_SESSION_ID","setHttpSessionId","setParameterValue","createDataUri","getBackChannelUri","removeRequest","backChannelRequestTimeoutMs_","va","opt_timeout","ensureBackChannel_","ensureForwardChannel_","STOP","disconnect","BACK_CHANNEL_ACTIVITY","goog.structs.forEach","col","goog.structs.getValues","goog.structs.Map","opt_map","argLength","addAll","goog.structs.Map.prototype.cleanupKeysArray_","srcIndex","destIndex","seen","goog.structs.Map.hasKey_","H","ChannelRequest.prototype.setTimeout","timeout","goog.labs.net.webChannel.ChannelRequest.prototype","ChannelRequest.prototype.readyStateChangeHandler_","throttle","INTERACTIVE","getReadyState","ChannelRequest.prototype.xmlHttpHandler_","onXmlHttpReadyStateChanged_","errorCode","statusCode","goog.labs.net.webChannel.requestStats.notifyServerReachabilityEvent","REQUEST_FAILED","REQUEST_SUCCEEDED","status","xmlHttp_","h","goog.net.WebChannel.X_HTTP_INITIAL_RESPONSE","initialResponse","UNKNOWN_SESSION_ID","REQUEST_UNKNOWN_SESSION_ID","decodeNextChunks_","listen_","STATUS","REQUEST_BAD_STATUS","ChannelRequest.prototype.pollResponse_","ChannelRequest.prototype.cancel","ChannelRequest.prototype.onWatchDogTimeout_","CLOSE_REQUEST","handleTimeout_","REQUEST_TIMEOUT","O","goog.structs.Map.prototype.getValues","cleanupKeysArray_","goog.structs.Map.prototype.getKeys","goog.structs.Map.prototype","goog.structs.Map.prototype.get","opt_val","goog.structs.Map.prototype.set","goog.structs.Map.prototype.forEach","goog.uri.utils.splitRe_","goog.Uri","opt_uri","opt_ignoreCase","setScheme","setDomain","setPort","goog.Uri.QueryData","setQueryData","m","SCHEME","setUserInfo","goog.Uri.decodeOrEmpty_","USER_INFO","DOMAIN","PORT","setPath","PATH","QUERY_DATA","setFragment","FRAGMENT","goog.Uri.prototype.clone","goog.Uri.prototype.setScheme","newScheme","opt_decode","goog.Uri.prototype.setDomain","newDomain","goog.Uri.prototype.setPort","newPort","goog.Uri.prototype.setQueryData","queryData","goog.Uri.QueryData.prototype.setIgnoreCase","ignoreCase","ensureKeyMapInitialized_","invalidateCache_","lowerCase","remove","setValues","goog.Uri.encodeSpecialChars_","goog.Uri.reDisallowedInQuery_","goog.Uri.prototype.setParameterValue","goog.Uri.prototype.makeUnique","RANDOM","opt_preserveReserved","decodeURI","decodeURIComponent","unescapedPart","extra","opt_removeDoubleEncoding","encoded","encodeURI","goog.Uri.encodeChar_","ch","goog.Uri.prototype.toString","out","scheme","getScheme","goog.Uri.reDisallowedInSchemeOrUserInfo_","domain","getDomain","userInfo","getUserInfo","encodeURIComponent","port","getPort","path","getPath","hasDomain","goog.Uri.reDisallowedInAbsolutePath_","goog.Uri.reDisallowedInRelativePath_","query","getEncodedQuery","fragment","getFragment","goog.Uri.reDisallowedInFragment_","opt_query","goog.Uri.QueryData.prototype.ensureKeyMapInitialized_","encodedQuery","pairs","indexOfEquals","goog.uri.utils.parseQueryData","goog.Uri.QueryData.prototype.remove","getKeyName_","keyMap_","goog.Uri.QueryData.prototype.containsKey","goog.Uri.QueryData.prototype.setValues","goog.Uri.QueryData.prototype.getKeyName_","keyName","goog.labs.net.webChannel.Wire.QueuedMap","mapId","map","ForwardChannelRequestPool","opt_maxPoolSize","module$contents$goog$labs$net$webChannel$ForwardChannelRequestPool_ForwardChannelRequestPool.MAX_POOL_SIZE_","entrys","goog.global.performance.getEntriesByType","goog.global.chrome","goog.global.chrome.loadTimes","R","goog.Uri.QueryData.prototype.add","goog.Uri.QueryData.prototype","goog.Uri.QueryData.prototype.forEach","goog.Uri.QueryData.prototype.getKeys","vals","goog.Uri.QueryData.prototype.getValues","opt_key","containsKey","goog.Uri.QueryData.prototype.set","goog.Uri.QueryData.prototype.get","opt_default","goog.Uri.QueryData.prototype.toString","sb","encodedKey","param","ForwardChannelRequestPool.prototype.isFull","ForwardChannelRequestPool.prototype.getRequestCount","ForwardChannelRequestPool.prototype.hasRequest","req","ForwardChannelRequestPool.prototype.addRequest","ForwardChannelRequestPool.prototype.removeRequest","ForwardChannelRequestPool.prototype.getPendingMessages","goog.json.NativeJsonProcessor","goog.labs.net.webChannel.WireV8","WireV8.prototype.encodeMessage","buffer","opt_prefix","prefix","encodedValue","netUtils.imageCallback_","channelDebug","img","debugText","ForwardChannelRequestPool.prototype.cancel","getPendingMessages","goog.json.NativeJsonProcessor.prototype.stringify","opt_replacer","goog.json.NativeJsonProcessor.prototype.parse","opt_reviver","goog.json.hybrid.parse","goog.net.XhrIo","opt_xmlHttpFactory","goog.net.XhrIo.ResponseType.DEFAULT","DEFAULT","goog.net.XhrIo.HTTP_SCHEME_PATTERN","goog.net.XhrIo.METHODS_WITH_FORM_DATA","goog.net.XhrIo.isContentTypeHeader_","header","goog.net.XhrIo.prototype.error_","err","dispatchErrors_","cleanUpXhr_","goog.net.XhrIo.prototype.dispatchErrors_","goog.net.XhrIo.prototype.onReadyStateChangeHelper_","LOCAL_REQUEST_ERROR","isComplete","isSuccess","OK","CREATED","ACCEPTED","NO_CONTENT","PARTIAL_CONTENT","NOT_MODIFIED","QUIRK_IE_NO_CONTENT","goog.global.self","goog.global.self.location","protocol","goog.global.self.location.protocol","goog.net.XhrIo.HTTP_SCHEME_PATTERN.test","LOADED","getStatusText","goog.net.XhrIo.prototype.cleanUpXhr_","opt_fromDispose","cleanUpTimeoutTimer_","clearedOnReadyStateChange","USE_NULL_FUNCTION","goog.net.XhrIo.prototype.cleanUpTimeoutTimer_","goog.net.XhrIo.prototype.getReadyState","UNINITIALIZED","exports.setHttpHeadersWithOverwriteParam","url","urlParam","extraHeaders","exports.generateHttpHeadersOverwriteParam","httpHeaders","getInternalChannelParam","paramName","defaultValue","goog.labs.net.webChannel.WebChannelBase","$jscomp.scope.getInternalChannelParam","WebChannelBase.prototype.disconnect","cancelRequests_","rid","addAdditionalParams_","goog.global.navigator.sendBeacon","requestSent","goog.global.Image","Image","onClose_","WebChannelBase.prototype.cancelRequests_","clearForwardChannelTimer_","WebChannelBase.prototype.sendMap","WebChannelBase.prototype.ensureForwardChannel_","isFull","WebChannelBase.prototype.makeForwardChannelRequest_","opt_retryRequest","module$exports$goog$net$rpc$HttpCors.setHttpHeadersWithOverwriteParam","requeuePendingMaps_","dequeueOutgoingMaps_","goog.labs.net.webChannel.WebChannelBase.MAX_MAPS_PER_REQUEST_","requestText","WebChannelBase.prototype.addAdditionalParams_","params","WebChannelBase.prototype.dequeueOutgoingMaps_","maxNum","count","badMapHandler","outgoingMaps_","offset","messageQueue","encodeMessage","pendingMessages","WebChannelBase.prototype.ensureBackChannel_","WebChannelBase.prototype.maybeRetryBackChannel_","goog.labs.net.webChannel.WebChannelBase.BACK_CHANNEL_MAX_RETRIES","getRetryTime_","WebChannelBase.prototype.clearDeadBackchannelTimer_","WebChannelBase.prototype.onRequestComplete","BACK_CHANNEL","FORWARD_CHANNEL","forwardChannelRetryCount_","lastError","WebChannelBase.prototype.maybeRetryForwardChannel_","INIT","getForwardChannelMaxRetries","maybeRetryForwardChannel_","maybeRetryBackChannel_","WebChannelBase.prototype.getRetryTime_","retryCount","retryTime","goog.labs.net.webChannel.WebChannelBase.INACTIVE_CHANNEL_RETRY_FACTOR","WebChannelBase.prototype.signalError_","imageUri","goog.global.location","goog.global.location.protocol","netUtils.testLoadImage","goog.labs.net.webChannel.netUtils.imageCallback_","goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT","ERROR_OTHER","onError_","WebChannelBase.prototype.onClose_","WebChannelBase.prototype.createDataUri","opt_scheme","opt_domain","opt_port","opt_path","locationPage","getHttpSessionId","WebChannelBase.prototype.createXhrIo","WebChannelBase.Handler","goog.labs.net.webChannel.WebChannelBaseTransport","messageHeaders","goog.net.WebChannel.X_CLIENT_PROTOCOL","goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL","initHeaders","goog.net.WebChannel.X_WEBCHANNEL_CONTENT_TYPE","goog.net.WebChannel.X_WEBCHANNEL_CLIENT_PROFILE","httpHeadersOverwriteParam","httpSessionIdParam","messageUrlParams_","goog.labs.net.webChannel.WebChannelBaseTransport.Channel.Handler_","array","goog.net.WebChannel.MessageEvent.call","metadata","metadataKey","goog.net.WebChannel.ErrorEvent.call","NETWORK_ERROR","W","goog.net.XhrIo.prototype.send","opt_method","opt_content","opt_headers","XMLHttpRequest","getOptions","goog.net.XmlHttp.factory_.getOptions","error_","content","goog.array.find","contentTypeKey","contentIsFormData","goog.net.XhrIo.CONTENT_TYPE_HEADER","goog.net.XhrIo.FORM_CONTENT_TYPE","goog.net.XhrIo.prototype","goog.net.XhrIo.prototype.timeout_","goog.net.XhrIo.prototype.abort","opt_failureCode","goog.net.XhrIo.prototype.disposeInternal","goog.net.XhrIo.prototype.onReadyStateChange_","isDisposed","onReadyStateChangeHelper_","goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_","goog.net.XhrIo.prototype.getStatus","goog.net.XhrIo.prototype.getResponseText","goog.net.XhrIo.prototype.getResponseJson","opt_xssiPrefix","goog.net.XhrIo.prototype.getLastErrorCode","goog.net.XhrIo.prototype.getLastError","ud","goog.labs.net.webChannel.Wire.LATEST_CHANNEL_VERSION","goog.labs.net.webChannel.WebChannelBase.prototype","WebChannelBase.prototype.onStartForwardChannelTimer_","startForwardChannel_","goog.object.extend","total","goog.labs.net.webChannel.Wire.RAW_DATA_KEY","goog.labs.net.webChannel.WebChannelBase.MAX_CHARS_PER_GET_","goog.net.WebChannelTransport.CLIENT_VERSION","xmlHttpPost","makeForwardChannelRequest_","WebChannelBase.prototype.onStartBackChannelTimer_","startBackChannel_","readyStateChangeThrottleMs_","backChannelRequest_","hostPrefix_","WebChannelBase.prototype.onBackChannelDead_","BACKCHANNEL_DEAD","WebChannelBase.prototype.testNetworkCallback_","networkUp","ERROR_NETWORK","Dd","WebChannelBase.Handler.prototype.channelOpened","WebChannelBase.Handler.prototype.channelHandleArray","WebChannelBase.Handler.prototype.channelError","WebChannelBase.Handler.prototype.channelClosed","WebChannelBase.Handler.prototype.badMapError","WebChannelBaseTransport.prototype.createWebChannel","goog.labs.net.webChannel.WebChannelBaseTransport.Channel","Y","WebChannelBaseTransport.Channel.prototype.addEventListener","goog.labs.net.webChannel.WebChannelBaseTransport.Channel.superClass_.addEventListener.call","WebChannelBaseTransport.Channel.prototype.removeEventListener","goog.labs.net.webChannel.WebChannelBaseTransport.Channel.superClass_.removeEventListener.call","WebChannelBaseTransport.Channel.prototype.open","supportCrossDomain","url_","CONNECT_ATTEMPT","channelPath","opt_extraParams","connectChannel_","WebChannelBaseTransport.Channel.prototype.close","WebChannelBaseTransport.Channel.prototype.send","rawJson","sendMap","WebChannelBaseTransport.Channel.prototype.disposeInternal","goog.labs.net.webChannel.WebChannelBaseTransport.Channel.MessageEvent","goog.labs.net.webChannel.WebChannelBaseTransport.Channel.ErrorEvent","goog.labs.net.webChannel.WebChannelBase.Handler","WebChannelBaseTransport.Channel.Handler_.prototype.channelOpened","WebChannelBaseTransport.Channel.Handler_.prototype.channelHandleArray","WebChannelBaseTransport.Channel.Handler_.prototype.channelError","WebChannelBaseTransport.Channel.Handler_.prototype.channelClosed","createWebChannelTransport","goog.net.createWebChannelTransport","ErrorCode","EventType","WebChannel","XhrIo","SDK_VERSION","firebase","[object Object]","uid","__PRIVATE_isAuthenticated","__PRIVATE_otherUser","User","user","__PRIVATE_authHeaders","Authorization","Code","CANCELLED","UNKNOWN","INVALID_ARGUMENT","DEADLINE_EXCEEDED","NOT_FOUND","ALREADY_EXISTS","PERMISSION_DENIED","UNAUTHENTICATED","RESOURCE_EXHAUSTED","FAILED_PRECONDITION","ABORTED","OUT_OF_RANGE","UNIMPLEMENTED","INTERNAL","UNAVAILABLE","DATA_LOSS","__PRIVATE_changeListener","__PRIVATE_initialTokenCounter","__PRIVATE_tokenCounter","forceRefresh","auth","getToken","__PRIVATE_tokenData","FirestoreError","__PRIVATE_hardAssert","accessToken","__PRIVATE_OAuthToken","currentUser","__PRIVATE_receivedInitialUser","removeAuthTokenListener","__PRIVATE_tokenListener","__PRIVATE_currentUid","getUid","o","X-Goog-AuthUser","__PRIVATE_sessionIndex","__PRIVATE_authHeader","__PRIVATE_gapi","__PRIVATE_getAuthHeaderValueForFirstParty","__PRIVATE_FirstPartyToken","__PRIVATE_FIRST_PARTY","Timestamp","fromMillis","date","getTime","milliseconds","seconds","floor","toMillis","nanoseconds","other","__PRIVATE_primitiveComparator","__PRIVATE_adjustedSeconds","padStart","__PRIVATE_SnapshotVersion","timestamp","__PRIVATE__compareTo","isEqual","__PRIVATE_len","__PRIVATE_BasePath","__PRIVATE_comparator","__PRIVATE_nameOrPath","segments","forEach","__PRIVATE_segment","__PRIVATE_construct","__PRIVATE_i","__PRIVATE_potentialChild","end","p1","p2","min","ResourcePath","__PRIVATE_toArray","join","__PRIVATE_canonicalString","indexOf","split","filter","fail","__PRIVATE_authProvider","__PRIVATE_getUser","getImmediate","optional","addAuthTokenListener","super","__PRIVATE_identifierRegExp","FieldPath","test","__PRIVATE_isValidIdentifier","__PRIVATE_current","__PRIVATE_addCurrentSegment","__PRIVATE_inBackticks","__PRIVATE_DocumentKey","__PRIVATE_fromString","__PRIVATE_popFirst","collectionId","k1","k2","__PRIVATE_objectSize","__PRIVATE_isEmpty","base64","__PRIVATE_ByteString","__PRIVATE_PlatformSupport","__PRIVATE_getPlatform","atob","__PRIVATE_binaryString","fromCharCode","btoa","Uint8Array","charCodeAt","__PRIVATE_isNullOrUndefined","__PRIVATE_isNegativeZero","__PRIVATE_isServerTimestamp","mapValue","fields","__type__","stringValue","__PRIVATE_getLocalWriteTime","__PRIVATE_localWriteTime","__PRIVATE_normalizeTimestamp","__local_write_time__","timestampValue","nanos","__PRIVATE_ISO_TIMESTAMP_REG_EXP","RegExp","__PRIVATE_typeOrder","__PRIVATE_valueEquals","__PRIVATE_leftType","booleanValue","__PRIVATE_leftTimestamp","__PRIVATE_rightTimestamp","__PRIVATE_normalizeByteString","bytesValue","referenceValue","__PRIVATE_normalizeNumber","geoPointValue","latitude","longitude","integerValue","__PRIVATE_n1","doubleValue","__PRIVATE_n2","__PRIVATE_arrayEquals","arrayValue","__PRIVATE_leftMap","__PRIVATE_rightMap","__PRIVATE_arrayValueContains","__PRIVATE_haystack","__PRIVATE_needle","find","__PRIVATE_valueCompare","__PRIVATE_comparison","__PRIVATE_leftBytes","__PRIVATE_rightBytes","__PRIVATE_leftNumber","__PRIVATE_rightNumber","__PRIVATE_rightType","__PRIVATE_compareTimestamps","__PRIVATE_compareTo","__PRIVATE_leftPath","__PRIVATE_rightPath","__PRIVATE_leftSegments","__PRIVATE_rightSegments","__PRIVATE_leftArray","__PRIVATE_rightArray","compare","__PRIVATE_leftKeys","__PRIVATE_rightKeys","sort","__PRIVATE_keyCompare","canonicalId","__PRIVATE_canonifyValue","__PRIVATE_normalizedTimestamp","toBase64","__PRIVATE_fromName","__PRIVATE_geoPoint","first","__PRIVATE_sortedKeys","__PRIVATE_fraction","exec","__PRIVATE_nanoStr","substr","__PRIVATE_parsedDate","blob","fromBase64String","fromUint8Array","__PRIVATE_refValue","__PRIVATE_databaseId","projectId","database","isInteger","isArray","__PRIVATE_isNullValue","__PRIVATE_isNanValue","__PRIVATE_isMapValue","previousValue","__previous_value__","__PRIVATE_transformResult","__PRIVATE_ServerTimestampTransform","__PRIVATE_coercedFieldValuesArray","__PRIVATE_toUnion","some","element","elements","__PRIVATE_ArrayUnionTransformOperation","__PRIVATE_toRemove","__PRIVATE_ArrayRemoveTransformOperation","__PRIVATE_baseValue","__PRIVATE_computeBaseValue","__PRIVATE_sum","asNumber","__PRIVATE_operand","serializer","__PRIVATE_toInteger","__PRIVATE_toDouble","__PRIVATE_NumericIncrementTransformOperation","transformResults","fieldPath","__PRIVATE_isPrefixOf","__PRIVATE_l","field","transform","Precondition","exists","wt","updateTime","__PRIVATE_maybeDoc","Document","__PRIVATE_Mutation","__PRIVATE_mutationResult","__PRIVATE_verifyKeyMatches","hasCommittedMutations","__PRIVATE_baseDoc","__PRIVATE_precondition","__PRIVATE_isValidFor","__PRIVATE_getPostMutationVersion","At","__PRIVATE_SetMutation","__PRIVATE_UnknownDocument","__PRIVATE_newData","__PRIVATE_patchDocument","__PRIVATE_PatchMutation","__PRIVATE_fieldMask","__PRIVATE_ObjectValue","empty","__PRIVATE_patchObject","__PRIVATE_builder","__PRIVATE_ObjectValueBuilder","newValue","set","delete","__PRIVATE_build","__PRIVATE_requireDocument","__PRIVATE_serverTransformResults","__PRIVATE_transformObject","__PRIVATE_localTransformResults","__PRIVATE_baseObject","fieldTransforms","__PRIVATE_fieldTransform","__PRIVATE_existingValue","__PRIVATE_coercedValue","__PRIVATE_TransformMutation","__PRIVATE_applyToRemoteDocument","__PRIVATE_applyToLocalView","__PRIVATE_NoDocument","__PRIVATE_DeleteMutation","__PRIVATE_VerifyMutation","proto","__PRIVATE_lastSegment","__PRIVATE_setOverlay","__PRIVATE_currentLevel","__PRIVATE_overlayMap","__PRIVATE_currentSegment","currentValue","Map","entries","__PRIVATE_mergedResult","__PRIVATE_applyOverlay","__PRIVATE_EMPTY_PATH","__PRIVATE_currentPath","__PRIVATE_currentOverlays","__PRIVATE_modified","__PRIVATE_resultAtPath","__PRIVATE_pathSegment","__PRIVATE_nested","child","__PRIVATE_extractFieldMask","__PRIVATE_nestedFields","__PRIVATE_nestedPath","__PRIVATE_FieldMask","__PRIVATE_RpcCode","__PRIVATE_MaybeDocument","__PRIVATE_objectValue","__PRIVATE_hasLocalMutations","hasPendingWrites","__PRIVATE_memoizedCanonicalId","collectionGroup","filters","orderBy","startAt","endAt","__PRIVATE_isDocumentKey","Query","__PRIVATE_memoizedOrderBy","__PRIVATE_inequalityField","__PRIVATE_getInequalityFilterField","__PRIVATE_firstOrderByField","__PRIVATE_getFirstOrderByField","__PRIVATE_isKeyField","__PRIVATE_OrderBy","__PRIVATE_keyField","__PRIVATE_foundKeyOrdering","__PRIVATE_explicitOrderBy","__PRIVATE_lastDirection","dir","__PRIVATE_newFilters","concat","__PRIVATE_limitType","__PRIVATE_newOrderBy","bound","__PRIVATE_toTarget","__PRIVATE_d1","__PRIVATE_d2","__PRIVATE_comparedOnKeyField","__PRIVATE_comp","__PRIVATE_matchesPathAndCollectionGroup","__PRIVATE_matchesOrderBy","__PRIVATE_matchesFilters","__PRIVATE_matchesBounds","FieldFilter","__PRIVATE_isInequality","__PRIVATE_operators","__PRIVATE_isDocumentQuery","__PRIVATE_memoizedTarget","Target","__PRIVATE_orderBys","__PRIVATE_Bound","position","before","__PRIVATE_docPath","__PRIVATE_hasCollectionId","__PRIVATE_isImmediateParentOf","matches","__PRIVATE_sortsBeforeDocument","__PRIVATE_KeyFieldInFilter","__PRIVATE_KeyFieldFilter","__PRIVATE_ArrayContainsFilter","__PRIVATE_InFilter","__PRIVATE_ArrayContainsAnyFilter","__PRIVATE_matchesComparison","__PRIVATE_orderByComponent","component","v1","v2","__PRIVATE_isKeyOrderBy","sequenceNumber","__PRIVATE_TargetData","targetId","__PRIVATE_purpose","__PRIVATE_snapshotVersion","lastLimboFreeSnapshotVersion","resumeToken","__PRIVATE_EMPTY_BYTE_STRING","__PRIVATE_assertValidBound","__PRIVATE_isPermanentError","__PRIVATE_mapCodeFromRpcCode","__PRIVATE_logError","RpcCode","__PRIVATE_SortedMap","root","__PRIVATE_insert","__PRIVATE_copy","__PRIVATE_LLRBNode","__PRIVATE_BLACK","node","cmp","__PRIVATE_prunedNodes","__PRIVATE_minKey","__PRIVATE_maxKey","action","__PRIVATE_inorderTraversal","__PRIVATE_descriptions","__PRIVATE_reverseTraversal","__PRIVATE_SortedMapIterator","__PRIVATE_nodeStack","__PRIVATE_isReverse","color","__PRIVATE_fixUp","EMPTY","__PRIVATE_isRed","__PRIVATE_moveRedLeft","__PRIVATE_removeMin","__PRIVATE_smallest","__PRIVATE_rotateRight","__PRIVATE_moveRedRight","__PRIVATE_rotateLeft","__PRIVATE_colorFlip","__PRIVATE_nl","RED","__PRIVATE_nr","__PRIVATE_blackDepth","__PRIVATE_check","pow","__PRIVATE_startKey","__PRIVATE_elem","cb","range","__PRIVATE_iter","__PRIVATE_getIteratorFrom","__PRIVATE_hasNext","__PRIVATE_getNext","start","__PRIVATE_getIterator","__PRIVATE_SortedSetIterator","has","add","__PRIVATE_SortedSet","__PRIVATE_thisIt","__PRIVATE_otherIt","__PRIVATE_thisElem","__PRIVATE_otherElem","__PRIVATE_res","__PRIVATE_EMPTY_MAYBE_DOCUMENT_MAP","__PRIVATE_maybeDocumentMap","__PRIVATE_nullableMaybeDocumentMap","__PRIVATE_EMPTY_DOCUMENT_MAP","__PRIVATE_documentMap","__PRIVATE_EMPTY_DOCUMENT_VERSION_MAP","__PRIVATE_documentVersionMap","__PRIVATE_EMPTY_DOCUMENT_KEY_SET","__PRIVATE_documentKeySet","__PRIVATE_EMPTY_TARGET_ID_SET","__PRIVATE_targetIdSet","__PRIVATE_updatedTargetIds","removedTargetIds","__PRIVATE_newDoc","__PRIVATE_existenceFilter","state","targetIds","cause","__PRIVATE_oldSet","__PRIVATE_DocumentSet","__PRIVATE_keyedMap","__PRIVATE_sortedSet","__PRIVATE_thisDoc","__PRIVATE_otherDoc","__PRIVATE_docStrings","__PRIVATE_newSet","__PRIVATE_change","__PRIVATE_oldChange","__PRIVATE_changeMap","__PRIVATE_changes","documents","__PRIVATE_mutatedKeys","fromCache","__PRIVATE_ViewSnapshot","__PRIVATE_emptySet","__PRIVATE_syncStateChanged","docs","__PRIVATE_oldDocs","docChanges","__PRIVATE_otherChanges","__PRIVATE_targetChanges","TargetChange","__PRIVATE_createSynthesizedTargetChangeForCurrentChange","__PRIVATE_RemoteEvent","ds","__PRIVATE__current","__PRIVATE__resumeToken","gs","__PRIVATE_pendingResponses","bs","__PRIVATE__hasPendingChanges","__PRIVATE_approximateByteSize","__PRIVATE_addedDocuments","__PRIVATE_modifiedDocuments","__PRIVATE_removedDocuments","__PRIVATE_documentChanges","__PRIVATE_changeType","__PRIVATE_snapshotChangesMap","__PRIVATE_docChange","__PRIVATE_addDocumentToTarget","__PRIVATE_removeDocumentFromTarget","targetChange","__PRIVATE_forEachTarget","__PRIVATE_targetState","__PRIVATE_ensureTargetState","__PRIVATE_isActiveTarget","__PRIVATE_updateResumeToken","__PRIVATE_recordTargetResponse","__PRIVATE_isPending","__PRIVATE_clearPendingChanges","removeTarget","__PRIVATE_markCurrent","__PRIVATE_resetTarget","__PRIVATE_targetStates","__PRIVATE__","__PRIVATE_watchChange","__PRIVATE_expectedCount","__PRIVATE_targetData","__PRIVATE_targetDataForActiveTarget","__PRIVATE_getCurrentDocumentCountForTarget","__PRIVATE_pendingTargetResets","__PRIVATE_pendingDocumentUpdates","__PRIVATE_targetContainsDocument","__PRIVATE_hasPendingChanges","__PRIVATE_toTargetChange","__PRIVATE_resolvedLimboDocuments","__PRIVATE_pendingDocumentTargetMapping","__PRIVATE_targets","__PRIVATE_isOnlyLimboTarget","__PRIVATE_forEachWhile","__PRIVATE_remoteEvent","__PRIVATE_documentTargetMap","document","__PRIVATE_addDocumentChange","__PRIVATE_ensureDocumentTargetMapping","__PRIVATE_updatedDocument","__PRIVATE_removeDocumentChange","__PRIVATE_metadataProvider","__PRIVATE_getRemoteKeysForTarget","__PRIVATE_recordPendingTargetRequest","__PRIVATE_TargetState","__PRIVATE_targetMapping","__PRIVATE_targetActive","__PRIVATE_logDebug","__PRIVATE_getTargetDataForTarget","__PRIVATE_targetMismatches","__PRIVATE_documentUpdates","__PRIVATE_excludesMetadataChanges","__PRIVATE_DIRECTIONS","asc","desc","__PRIVATE_OPERATORS","<","<=",">",">=","==","array-contains","in","array-contains-any","__PRIVATE_useProto3Json","Infinity","MAX_SAFE_INTEGER","MIN_SAFE_INTEGER","bytes","toUint8Array","__PRIVATE_toTimestamp","__PRIVATE_fromTimestamp","__PRIVATE_fullyQualifiedPrefixPath","__PRIVATE_resource","__PRIVATE_isValidResourceName","__PRIVATE_toResourceName","__PRIVATE_fromResourceName","__PRIVATE_extractLocalPathFromResourceName","__PRIVATE_resourceName","mi","__PRIVATE_toName","__PRIVATE_toProto","fromVersion","found","missing","readTime","__PRIVATE_fromFound","__PRIVATE_fromMissing","__PRIVATE_fromWatchTargetChangeState","targetChangeType","__PRIVATE_fromBytes","__PRIVATE_causeProto","__PRIVATE_fromRpcStatus","__PRIVATE_WatchTargetChange","documentChange","__PRIVATE_entityChange","__PRIVATE_DocumentWatchChange","documentDelete","__PRIVATE_docDelete","documentRemove","__PRIVATE_docRemove","ExistenceFilter","__PRIVATE_ExistenceFilterChange","__PRIVATE_mutation","update","__PRIVATE_toMutationDocument","updateMask","__PRIVATE_toDocumentMask","__PRIVATE_toFieldTransform","verify","__PRIVATE_isNone","currentDocument","__PRIVATE_toPrecondition","__PRIVATE_fromPrecondition","__PRIVATE_none","__PRIVATE_fromDocumentMask","__PRIVATE_fromFieldTransform","toVersion","commitTime","__PRIVATE_MutationResult","__PRIVATE_protos","__PRIVATE_fromWriteResult","setToServerValue","appendMissingElements","removeAllFromArray","increment","__PRIVATE_fromServerFormat","FieldTransform","__PRIVATE_toQueryPath","__PRIVATE_documentsTarget","__PRIVATE_atPath","__PRIVATE_fromQueryPath","structuredQuery","parent","from","allDescendants","__PRIVATE_popLast","where","__PRIVATE_toFilter","__PRIVATE_toOrder","__PRIVATE_toInt32Proto","__PRIVATE_toCursor","__PRIVATE_fromCount","__PRIVATE_filterBy","__PRIVATE_fromFilter","__PRIVATE_fromOrder","__PRIVATE_fromInt32Proto","__PRIVATE_fromCursor","__PRIVATE_toLabel","goog-listen-tags","__PRIVATE_toDocumentsTarget","__PRIVATE_toQueryTarget","__PRIVATE_toBytes","__PRIVATE_toUnaryOrFieldFilter","compositeFilter","unaryFilter","__PRIVATE_fromUnaryFilter","fieldFilter","__PRIVATE_fromFieldFilter","reduce","__PRIVATE_accum","__PRIVATE_toPropertyOrder","__PRIVATE_fromPropertyOrder","cursor","__PRIVATE_fieldReference","__PRIVATE_toFieldPathReference","direction","__PRIVATE_toDirection","__PRIVATE_fromFieldPathReference","__PRIVATE_fromDirection","__PRIVATE_fromOperatorName","__PRIVATE_toOperatorName","__PRIVATE_nanField","NaN","__PRIVATE_nullField","nullValue","__PRIVATE_canonicalFields","fieldPaths","__PRIVATE_paths","platform","__PRIVATE_logClient","__PRIVATE_getLogLevel","setLogLevel","__PRIVATE_newLevel","msg","__PRIVATE_argToString","debug","__PRIVATE_formatJSON","__PRIVATE_failure","assertion","__PRIVATE_debugCast","__PRIVATE_chars","__PRIVATE_maxMultiple","__PRIVATE_autoId","__PRIVATE_randomBytes","charAt","every","persistenceKey","host","ssl","forceLongPolling","_n","__PRIVATE_DatabaseId","id","__PRIVATE_mapKeyFn","__PRIVATE_inner","__PRIVATE_otherKey","splice","__PRIVATE_docKey","__PRIVATE_batchResult","__PRIVATE_mutationResults","mutations","baseMutations","__PRIVATE_maybeDocs","__PRIVATE_mutatedDocuments","__PRIVATE_m","__PRIVATE_mutatedDocument","batchId","batch","__PRIVATE_commitVersion","results","streamToken","__PRIVATE_versionMap","__PRIVATE_MutationBatchResult","__PRIVATE_nextFn","__PRIVATE_catchFn","__PRIVATE_callbackAttached","__PRIVATE_isDone","__PRIVATE_wrapFailure","__PRIVATE_wrapSuccess","PersistencePromise","__PRIVATE_nextCallback","__PRIVATE_catchCallback","__PRIVATE_wrapUserFunction","all","__PRIVATE_resolvedCount","__PRIVATE_err","__PRIVATE_predicates","predicate","__PRIVATE_isTrue","collection","__PRIVATE_promises","__PRIVATE_waitFor","transaction","__PRIVATE_mutationQueue","__PRIVATE_getAllMutationBatchesAffectingDocumentKey","__PRIVATE_batches","__PRIVATE_getDocumentInternal","__PRIVATE_inBatches","__PRIVATE_remoteDocumentCache","__PRIVATE_getEntry","__PRIVATE_localView","getEntries","__PRIVATE_getLocalViewOfDocuments","__PRIVATE_baseDocs","__PRIVATE_getAllMutationBatchesAffectingDocumentKeys","__PRIVATE_applyLocalMutationsToDocuments","__PRIVATE_sinceReadTime","__PRIVATE_getDocumentsMatchingDocumentQuery","__PRIVATE_isCollectionGroupQuery","__PRIVATE_getDocumentsMatchingCollectionGroupQuery","__PRIVATE_getDocumentsMatchingCollectionQuery","__PRIVATE_getDocument","__PRIVATE_indexManager","__PRIVATE_getCollectionParents","__PRIVATE_parents","__PRIVATE_collectionQuery","__PRIVATE_asCollectionQueryAtPath","__PRIVATE_mutationBatches","__PRIVATE_getDocumentsMatchingQuery","__PRIVATE_queryResults","__PRIVATE_getAllMutationBatchesAffectingQuery","__PRIVATE_matchingMutationBatches","__PRIVATE_addMissingBaseDocuments","__PRIVATE_mergedDocuments","__PRIVATE_mutatedDoc","__PRIVATE_existingDocuments","__PRIVATE_missingBaseDocEntriesForPatching","__PRIVATE_missingBaseDocs","__PRIVATE_viewSnapshot","__PRIVATE_addedKeys","__PRIVATE_removedKeys","__PRIVATE_LocalViewChanges","__PRIVATE_externalPreviousValue","max","__PRIVATE_nextValue","__PRIVATE_writeNewSequenceNumber","__PRIVATE_sequenceNumberSyncer","__PRIVATE_sequenceNumberHandler","__PRIVATE_setPreviousValue","__PRIVATE_writeSequenceNumber","__PRIVATE_docVersions","__PRIVATE_currentBaseMs","__PRIVATE_maxDelayMs","cancel","__PRIVATE_desiredDelayWithJitterMs","__PRIVATE_jitterDelayMs","__PRIVATE_delaySoFarMs","__PRIVATE_lastAttemptTime","__PRIVATE_remainingDelayMs","__PRIVATE_timerPromise","__PRIVATE_queue","__PRIVATE_enqueueAfterDelay","__PRIVATE_timerId","__PRIVATE_backoffFactor","__PRIVATE_initialDelayMs","__PRIVATE_skipDelay","random","collectionPath","__PRIVATE_collectionParentIndex","parentPath","__PRIVATE_existingParents","__PRIVATE_added","__PRIVATE_lastId","__PRIVATE_TargetIdGenerator","__PRIVATE_MemoryCollectionParentIndex","__PRIVATE_isIndexedDbTransactionError","__PRIVATE_asyncQueue","__PRIVATE_delayMs","__PRIVATE_removalCallback","__PRIVATE_delayedOp","__PRIVATE_DelayedOperation","__PRIVATE_timerHandle","setTimeout","__PRIVATE_handleDelayElapsed","reason","clearTimeout","__PRIVATE_deferred","__PRIVATE_enqueueAndForget","Mr","__PRIVATE__isShuttingDown","enqueue","__PRIVATE_verifyNotFailed","__PRIVATE_enqueueInternal","window","removeEventListener","__PRIVATE_visibilityHandler","__PRIVATE_enqueueEvenAfterShutdown","__PRIVATE_retryableTail","__PRIVATE_Deferred","__PRIVATE_retryingOp","async","__PRIVATE_backoff","__PRIVATE_backoffAndRun","__PRIVATE_newTail","__PRIVATE_tail","__PRIVATE_operationInProgress","catch","stack","__PRIVATE_timerIdsToSkip","__PRIVATE_createAndSchedule","__PRIVATE_removedOp","__PRIVATE_removeDelayedOperation","__PRIVATE_delayedOperations","__PRIVATE_currentTail","__PRIVATE_lastTimerId","__PRIVATE_drain","__PRIVATE_targetTimeMs","__PRIVATE_ExponentialBackoff","__PRIVATE_skipBackoff","addEventListener","bind","__PRIVATE_wrapInUserErrorIfRecoverable","__PRIVATE_cacheSize","__PRIVATE_LruParams","__PRIVATE_DEFAULT_COLLECTION_PERCENTILE","__PRIVATE_DEFAULT_MAX_SEQUENCE_NUMBERS_TO_COLLECT","__PRIVATE_cacheSizeCollectionThreshold","__PRIVATE_percentileToCollect","__PRIVATE_maximumSequenceNumbersToCollect","__PRIVATE_DEFAULT_CACHE_SIZE_BYTES","__PRIVATE_COLLECTION_DISABLED","__PRIVATE_newMutationQueue","__PRIVATE_newLocalDocuments","__PRIVATE_localDocuments","persistence","runTransaction","txn","__PRIVATE_oldBatches","__PRIVATE_getAllMutationBatches","__PRIVATE_promisedOldBatches","__PRIVATE_getMutationQueue","__PRIVATE_LocalDocumentsView","__PRIVATE_remoteDocuments","__PRIVATE_getIndexManager","__PRIVATE_newBatches","__PRIVATE_removedBatchIds","__PRIVATE_addedBatchIds","__PRIVATE_changedKeys","__PRIVATE_getDocuments","__PRIVATE_affectedDocuments","Vh","ph","yh","__PRIVATE_queryEngine","__PRIVATE_setLocalDocumentsView","__PRIVATE_existingDocs","__PRIVATE_extractBaseValue","__PRIVATE_addMutationBatch","__PRIVATE_applyToLocalDocumentSet","vh","__PRIVATE_affected","__PRIVATE_documentBuffer","__PRIVATE_newChangeBuffer","Dh","__PRIVATE_acknowledgeBatch","__PRIVATE_applyWriteToRemoteDocuments","__PRIVATE_performConsistencyCheck","__PRIVATE_affectedKeys","__PRIVATE_lookupMutationBatch","__PRIVATE_removeMutationBatch","__PRIVATE_getHighestUnacknowledgedBatchId","__PRIVATE_getLastStreamToken","__PRIVATE_setLastStreamToken","__PRIVATE_targetCache","__PRIVATE_getLastRemoteSnapshotVersion","__PRIVATE_remoteVersion","__PRIVATE_newTargetDataByTargetMap","__PRIVATE_targetDataByTarget","__PRIVATE_oldTargetData","__PRIVATE_removeMatchingKeys","__PRIVATE_addMatchingKeys","__PRIVATE_newTargetData","__PRIVATE_withResumeToken","__PRIVATE_withSequenceNumber","__PRIVATE_currentSequenceNumber","__PRIVATE_LocalStore","__PRIVATE_shouldPersistTargetData","__PRIVATE_updateTargetData","__PRIVATE_changedDocs","__PRIVATE_updatedKeys","__PRIVATE_existingDoc","__PRIVATE_removeEntry","__PRIVATE_addEntry","__PRIVATE_referenceDelegate","__PRIVATE_updateLimboDocument","__PRIVATE_updateRemoteVersion","lastRemoteSnapshotVersion","__PRIVATE_setTargetsMetadata","__PRIVATE_toMicroseconds","__PRIVATE_RESUME_TOKEN_MAX_AGE_MICROS","__PRIVATE_viewChanges","__PRIVATE_viewChange","__PRIVATE_addReference","__PRIVATE_removeReference","e_42","__PRIVATE_updatedTargetData","__PRIVATE_withLastLimboFreeSnapshotVersion","__PRIVATE_afterBatchId","__PRIVATE_getNextMutationBatchAfterBatchId","__PRIVATE_getTargetData","__PRIVATE_cached","__PRIVATE_allocateTargetId","__PRIVATE_addTargetData","__PRIVATE_targetIdByTarget","__PRIVATE_keepPersistedTargetData","__PRIVATE_usePreviousResults","__PRIVATE_remoteKeys","__PRIVATE_getMatchingKeysForTargetId","_o","__PRIVATE_docKeys","__PRIVATE_promiseChain","__PRIVATE_remoteDoc","__PRIVATE_ackVersion","__PRIVATE_garbageCollector","__PRIVATE_collect","__PRIVATE_initialUser","__PRIVATE_ObjectMap","__PRIVATE_getRemoteDocumentCache","__PRIVATE_getTargetCache","__PRIVATE_ignoreIfPrimaryLeaseLoss","__PRIVATE_refsByKey","ref","__PRIVATE_DocReference","__PRIVATE_refsByTarget","__PRIVATE_removeRef","__PRIVATE_emptyKey","__PRIVATE_startRef","__PRIVATE_endRef","__PRIVATE_forEachInRange","__PRIVATE_firstRef","__PRIVATE_firstAfterOrEqual","__PRIVATE_targetOrBatchId","__PRIVATE_compareByKey","__PRIVATE_compareByTargetId","__PRIVATE_validateNoArgs","functionName","__PRIVATE_formatPlural","__PRIVATE_validateExactNumberOfArgs","__PRIVATE_numberOfArgs","__PRIVATE_validateAtLeastNumberOfArgs","__PRIVATE_minNumberOfArgs","__PRIVATE_validateBetweenNumberOfArgs","__PRIVATE_maxNumberOfArgs","__PRIVATE_validateArgType","__PRIVATE_argument","__PRIVATE_validateType","__PRIVATE_ordinal","__PRIVATE_validateOptionalArgType","__PRIVATE_validateNamedType","__PRIVATE_optionName","__PRIVATE_validateNamedOptionalType","__PRIVATE_validateNamedOptionalPropertyEquals","__PRIVATE_inputName","input","__PRIVATE_expected","__PRIVATE_expectedDescription","__PRIVATE_valueDescription","__PRIVATE_actualDescription","__PRIVATE_isPlainObject","description","getPrototypeOf","substring","JSON","stringify","__PRIVATE_customObjectName","__PRIVATE_validateDefined","__PRIVATE_validateOptionNames","__PRIVATE_optionNames","__PRIVATE_invalidClassError","__PRIVATE_validatePositiveNumber","num","__PRIVATE_assertUint8ArrayAvailable","__PRIVATE_assertBase64Available","__PRIVATE_base64Available","Blob","__PRIVATE__byteString","__PRIVATE__DOCUMENT_ID","__PRIVATE__internalPath","fieldNames","__PRIVATE_InternalFieldPath","__PRIVATE_byteString","__PRIVATE_RESERVED","__PRIVATE__methodName","__PRIVATE_FieldValueImpl","context","__PRIVATE_dataSource","__PRIVATE_createError","__PRIVATE_DeleteFieldValueImpl","__PRIVATE_ServerTimestampFieldValueImpl","__PRIVATE_parseContext","__PRIVATE_ParseContext","No","methodName","Lo","ignoreUndefinedProperties","__PRIVATE_parsedElements","__PRIVATE__elements","__PRIVATE_parseData","arrayUnion","__PRIVATE__operand","__PRIVATE_numericIncrement","__PRIVATE__lat","__PRIVATE__long","__PRIVATE_RESERVED_FIELD_REGEX","isFinite","__PRIVATE_isWrite","settings","configuration","__PRIVATE_childPath","__PRIVATE_contextWith","__PRIVATE_validatePathSegment","__PRIVATE_validatePath","__PRIVATE_fieldDescription","__PRIVATE_createContext","__PRIVATE_validatePlainObject","__PRIVATE_updateData","__PRIVATE_parseObject","__PRIVATE_ParsedSetData","__PRIVATE_validatedFieldPaths","__PRIVATE_stringOrFieldPath","__PRIVATE_ExternalFieldPath","__PRIVATE_fieldPathFromDotSeparatedString","contains","__PRIVATE_fieldMaskContains","__PRIVATE_covers","__PRIVATE_fieldMaskPaths","__PRIVATE_childContext","__PRIVATE_childContextForFieldPath","__PRIVATE_parsedValue","mask","__PRIVATE_ParsedUpdateData","moreFieldsAndValues","__PRIVATE_fieldPathFromArgument","__PRIVATE_allowArrays","__PRIVATE_newSerializer","__PRIVATE_looksLikeJsonObject","__PRIVATE_arrayElement","__PRIVATE_entryIndex","__PRIVATE_parsedEntry","__PRIVATE_childContextForArray","__PRIVATE_toNumber","fromDate","GeoPoint","DocumentReference","__PRIVATE_thisDb","__PRIVATE_otherDb","firestore","__PRIVATE__databaseId","__PRIVATE__key","__PRIVATE_childContextForField","search","view","__PRIVATE_performBackoff","__PRIVATE_isStarted","close","__PRIVATE_isOpen","__PRIVATE_idleTimer","__PRIVATE_idleTimerId","__PRIVATE_handleIdleCloseTimer","__PRIVATE_cancelIdleCheck","stream","send","__PRIVATE_finalState","__PRIVATE_closeCount","__PRIVATE_resetToMax","__PRIVATE_credentialsProvider","__PRIVATE_invalidateToken","__PRIVATE_tearDown","__PRIVATE_onClose","__PRIVATE_dispatchIfNotClosed","__PRIVATE_getCloseGuardedDispatcher","token","__PRIVATE_startStream","__PRIVATE_rpcError","__PRIVATE_handleStreamClose","__PRIVATE_startRpc","__PRIVATE_onOpen","onMessage","__PRIVATE_startCloseCount","__PRIVATE_PersistentStream","__PRIVATE_connection","__PRIVATE_openStream","__PRIVATE_watchChangeProto","__PRIVATE_fromWatchChange","snapshot","__PRIVATE_versionFromListenResponse","__PRIVATE_onWatchChange","__PRIVATE_encodedDatabaseId","addTarget","labels","__PRIVATE_toListenRequestLabels","__PRIVATE_sendRequest","ba","__PRIVATE_handshakeComplete_","__PRIVATE_writeMutations","__PRIVATE_responseProto","lastStreamToken","__PRIVATE_fromWriteResults","writeResults","__PRIVATE_onMutationResult","__PRIVATE_onHandshakeComplete","writes","__PRIVATE_toMutation","__PRIVATE_rpcName","credentials","__PRIVATE_invokeRPC","__PRIVATE_invokeStreamingRPC","__PRIVATE_ensureCommitNotCalled","__PRIVATE_datastore","__PRIVATE_datastoreImpl","__PRIVATE_params","__PRIVATE_fromMaybeDocument","__PRIVATE_recordVersion","write","__PRIVATE_toMutations","__PRIVATE_writtenDocs","__PRIVATE_preconditionForUpdate","__PRIVATE_lastWriteError","__PRIVATE_unwritten","__PRIVATE_readVersions","__PRIVATE__version","__PRIVATE_committed","__PRIVATE_docVersion","__PRIVATE_existingVersion","__PRIVATE_watchStreamFailures","__PRIVATE_setAndBroadcast","__PRIVATE_onlineStateTimer","__PRIVATE_logClientOfflineWarningIfNecessary","__PRIVATE_clearOnlineStateTimer","__PRIVATE_newState","__PRIVATE_shouldWarnClientIsOffline","__PRIVATE_onlineStateHandler","details","enableNetwork","networkEnabled","__PRIVATE_enableNetworkInternal","__PRIVATE_canUseNetwork","__PRIVATE_writeStream","__PRIVATE_localStore","__PRIVATE_shouldStartWatchStream","__PRIVATE_startWatchStream","__PRIVATE_onlineStateTracker","__PRIVATE_fillWritePipeline","__PRIVATE_disableNetworkInternal","__PRIVATE_watchStream","__PRIVATE_writePipeline","__PRIVATE_cleanUpWatchStreamState","__PRIVATE_connectivityMonitor","__PRIVATE_shutdown","__PRIVATE_listenTargets","__PRIVATE_sendWatchRequest","__PRIVATE_sendUnwatchRequest","__PRIVATE_markIdle","__PRIVATE_syncEngine","__PRIVATE_watchChangeAggregator","__PRIVATE_watch","__PRIVATE_unwatch","__PRIVATE_WatchChangeAggregator","__PRIVATE_handleWatchStreamStart","__PRIVATE_indexedDbFailed","isPrimary","__PRIVATE_handleWatchStreamFailure","__PRIVATE_handleTargetError","__PRIVATE_disableNetworkUntilRecovery","__PRIVATE_handleDocumentChange","__PRIVATE_handleExistenceFilter","__PRIVATE_handleTargetChange","__PRIVATE_raiseWatchSnapshot","__PRIVATE_enqueueRetryable","__PRIVATE_createRemoteEvent","__PRIVATE_requestTargetData","__PRIVATE_applyRemoteEvent","s_49","__PRIVATE_rejectListen","__PRIVATE_canAddToWritePipeline","__PRIVATE_lastBatchIdRetrieved","__PRIVATE_nextMutationBatch","__PRIVATE_addToWritePipeline","__PRIVATE_shouldStartWriteStream","__PRIVATE_startWriteStream","__PRIVATE_handshakeComplete","__PRIVATE_writeHandshake","shift","__PRIVATE_success","__PRIVATE_applySuccessfulWrite","__PRIVATE_handleWriteError","__PRIVATE_handleHandshakeError","__PRIVATE_inhibitBackoff","__PRIVATE_rejectFailedWrite","Transaction","__PRIVATE_restartNetwork","activeTargetIds","updateTimeMs","__PRIVATE_localState","__PRIVATE_addQueryTarget","__PRIVATE_queryState","__PRIVATE_removeQueryTarget","__PRIVATE_LocalClientState","onlineState","uc","__PRIVATE__syncedDocuments","__PRIVATE_previousChanges","__PRIVATE_changeSet","__PRIVATE_DocumentChangeSet","__PRIVATE_oldDocumentSet","__PRIVATE_documentSet","__PRIVATE_newMutatedKeys","__PRIVATE_newDocumentSet","__PRIVATE_needsRefill","__PRIVATE_lastDocInLimit","__PRIVATE_hasLimitToFirst","last","__PRIVATE_firstDocInLimit","__PRIVATE_hasLimitToLast","__PRIVATE_newMaybeDoc","__PRIVATE_oldDoc","__PRIVATE_oldDocHadPendingMutations","__PRIVATE_newDocHasPendingMutations","__PRIVATE_changeApplied","track","__PRIVATE_shouldWaitForSyncedDocument","__PRIVATE_docComparator","ac","lc","fc","ns","__PRIVATE_updateLimboDocuments","__PRIVATE_getChanges","__PRIVATE_c1","__PRIVATE_c2","__PRIVATE_applyTargetChange","__PRIVATE_limboChanges","__PRIVATE_newSyncState","__PRIVATE_limboDocuments","__PRIVATE_syncState","Ec","__PRIVATE_applyChanges","__PRIVATE_oldLimboDocuments","__PRIVATE_shouldBeInLimbo","__PRIVATE_RemovedLimboDocument","__PRIVATE_AddedLimboDocument","__PRIVATE_queryResult","__PRIVATE_computeDocChanges","__PRIVATE_fromInitialDocuments","__PRIVATE_runWithBackOff","__PRIVATE_remoteStore","__PRIVATE_createTransaction","__PRIVATE_userPromise","__PRIVATE_tryRunUpdateFunction","commit","__PRIVATE_commitError","__PRIVATE_handleTransactionError","__PRIVATE_userPromiseError","updateFunction","__PRIVATE_retries","__PRIVATE_isRetryableTransactionError","Uc","__PRIVATE_syncEngineListener","__PRIVATE_assertSubscribed","__PRIVATE_queryView","__PRIVATE_queryViewsByQuery","__PRIVATE_sharedClientState","__PRIVATE_addLocalQueryTarget","__PRIVATE_computeInitialSnapshot","__PRIVATE_allocateTarget","__PRIVATE_initializeViewAndComputeSnapshot","__PRIVATE_isPrimaryClient","listen","__PRIVATE_executeQuery","__PRIVATE_View","__PRIVATE_viewDocChanges","__PRIVATE_synthesizedTargetChange","__PRIVATE_updateTrackedLimbos","__PRIVATE_QueryView","__PRIVATE_queriesByTarget","__PRIVATE_queries","q","__PRIVATE_removeLocalQueryTarget","__PRIVATE_isActiveQueryTarget","__PRIVATE_releaseTarget","__PRIVATE_clearQueryState","__PRIVATE_unlisten","__PRIVATE_removeAndCleanupTarget","__PRIVATE_userCallback","__PRIVATE_localWrite","__PRIVATE_addPendingMutation","__PRIVATE_addMutationCallback","__PRIVATE_emitNewSnapsAndNotifyLocalStore","__PRIVATE_TransactionRunner","__PRIVATE_run","__PRIVATE_limboResolution","__PRIVATE_activeLimboResolutionsByTarget","__PRIVATE_receivedDocument","__PRIVATE_newViewSnapshots","__PRIVATE_applyOnlineStateChange","__PRIVATE_onOnlineStateChange","__PRIVATE_updateQueryState","__PRIVATE_limboKey","event","__PRIVATE_activeLimboTargetsByKey","__PRIVATE_pumpEnqueuedLimboResolutions","__PRIVATE_mutationBatchResult","__PRIVATE_processUserCallback","__PRIVATE_triggerPendingWritesCallbacks","__PRIVATE_updateMutationState","__PRIVATE_rejectBatch","__PRIVATE_highestBatchId","__PRIVATE_callbacks","__PRIVATE_pendingWritesCallbacks","__PRIVATE_firestoreError","__PRIVATE_errorMessage","clear","__PRIVATE_newCallbacks","__PRIVATE_mutationUserCallbacks","__PRIVATE_toKey","__PRIVATE_onWatchError","__PRIVATE_limboDocumentRefs","__PRIVATE_removeReferencesForId","__PRIVATE_containsKey","__PRIVATE_removeLimboTarget","__PRIVATE_limboTargetId","__PRIVATE_limboChange","__PRIVATE_trackLimboChange","__PRIVATE_enqueuedLimboResolutions","__PRIVATE_maxConcurrentLimboResolutions","__PRIVATE_limboTargetIdGenerator","__PRIVATE_LimboResolution","__PRIVATE_ListenSequence","__PRIVATE_INVALID","__PRIVATE_newSnaps","__PRIVATE_docChangesInAllViews","__PRIVATE_queriesProcessed","__PRIVATE_fromSnapshot","__PRIVATE_notifyLocalViewChanges","__PRIVATE_fnName","__PRIVATE_handleUserChange","__PRIVATE_rejectOutstandingPendingWritesCallbacks","__PRIVATE_handleCredentialChange","disableNetwork","__PRIVATE_keySet","__PRIVATE_unionWith","__PRIVATE_syncedDocuments","__PRIVATE_firstListen","__PRIVATE_queryInfo","__PRIVATE_QueryListenersInfo","__PRIVATE_viewSnap","onError","__PRIVATE_listeners","__PRIVATE_onViewSnapshot","__PRIVATE_raiseSnapshotsInSyncEvent","__PRIVATE_lastListen","__PRIVATE_viewSnaps","__PRIVATE_raisedEvent","observer","__PRIVATE_snapshotsInSyncListeners","__PRIVATE_snap","includeMetadataChanges","__PRIVATE_raisedInitialEvent","__PRIVATE_shouldRaiseEvent","__PRIVATE_queryObserver","__PRIVATE_shouldRaiseInitialEvent","__PRIVATE_raiseInitialEvent","__PRIVATE_maybeOnline","__PRIVATE_waitForSyncWhenOnline","__PRIVATE_hasPendingWritesChanged","__PRIVATE_localDocumentsView","__PRIVATE_matchesAllDocuments","__PRIVATE_executeFullCollectionScan","__PRIVATE_previousResults","__PRIVATE_applyQuery","__PRIVATE_updatedResults","__PRIVATE_sortedPreviousResults","__PRIVATE_limboFreeSnapshotVersion","__PRIVATE_docAtLimitEdge","__PRIVATE_batchIndex","__PRIVATE_indexOfExistingBatchId","__PRIVATE_nextBatchId","__PRIVATE_MutationBatch","__PRIVATE_batchesByDocumentKey","__PRIVATE_addToCollectionParentIndex","__PRIVATE_findMutationBatch","__PRIVATE_rawIndex","__PRIVATE_indexOfBatchId","__PRIVATE_documentKey","POSITIVE_INFINITY","__PRIVATE_documentKeys","__PRIVATE_uniqueBatchIDs","__PRIVATE_findMutationBatches","__PRIVATE_immediateChildrenPathLength","__PRIVATE_startPath","__PRIVATE_rowKeyPath","__PRIVATE_batchIDs","__PRIVATE_references","__PRIVATE_markPotentiallyOrphaned","__PRIVATE_entry","__PRIVATE_previousSize","__PRIVATE_currentSize","__PRIVATE_sizer","$l","__PRIVATE_maybeDocument","__PRIVATE_MemoryRemoteDocumentCache","__PRIVATE_RemoteDocumentChangeBuffer","subscribe","__PRIVATE_ReferenceSet","__PRIVATE_forSyncEngine","__PRIVATE_addCallback","__PRIVATE_OnlineStateTracker","Aa","__PRIVATE_onWatchStreamOpen","__PRIVATE_onWatchStreamClose","Va","__PRIVATE_onWatchStreamChange","__PRIVATE_PersistentListenStream","__PRIVATE_onWriteStreamOpen","__PRIVATE_onWriteStreamClose","Ca","__PRIVATE_onWriteHandshakeComplete","Sa","__PRIVATE_PersistentWriteStream","__PRIVATE_connectionTimerId","__PRIVATE_documentCache","__PRIVATE__readTime","__PRIVATE_assertNotApplied","__PRIVATE_bufferedEntry","__PRIVATE_getFromCache","__PRIVATE_getAllFromCache","__PRIVATE_changesApplied","__PRIVATE_highestSequenceNumber","highestTargetId","__PRIVATE_targetIdGenerator","highestListenSequenceNumber","__PRIVATE_saveTargetData","targetCount","upperBound","__PRIVATE_removals","__PRIVATE_removeMatchingKeysForTargetId","__PRIVATE_addReferences","__PRIVATE_removeReferences","__PRIVATE_matchingKeys","__PRIVATE_referencesForId","__PRIVATE__started","n_","__PRIVATE_mutationQueues","__PRIVATE_MemoryMutationQueue","__PRIVATE_transactionOperation","__PRIVATE_MemoryTransaction","__PRIVATE_listenSequence","__PRIVATE_onTransactionStarted","__PRIVATE_onTransactionCommitted","__PRIVATE_toPromise","__PRIVATE_raiseOnCommittedEvent","__PRIVATE_or","__PRIVATE_onCommittedListeners","__PRIVATE_MemoryEagerDelegate","w_","__PRIVATE__orphanedDocuments","__PRIVATE_localViewReferences","__PRIVATE_orphanedDocuments","cache","__PRIVATE_removeTargetData","__PRIVATE_changeBuffer","__PRIVATE_isReferenced","__PRIVATE_mutationQueuesContainKey","__PRIVATE_cfg","__PRIVATE_createSharedClientState","__PRIVATE_createPersistence","__PRIVATE_gcScheduler","__PRIVATE_createGarbageCollectionScheduler","__PRIVATE_createLocalStore","__PRIVATE_createRemoteStore","__PRIVATE_createSyncEngine","__PRIVATE_eventManager","__PRIVATE_createEventManager","__PRIVATE_applyPrimaryState","__PRIVATE_EventManager","__PRIVATE_IndexFreeQueryEngine","__PRIVATE_MemoryPersistence","__PRIVATE_factory","__PRIVATE_RemoteStore","__PRIVATE_newConnectivityMonitor","__PRIVATE_MemorySharedClientState","__PRIVATE_SyncEngine","__PRIVATE_databaseInfo","__PRIVATE_componentProvider","__PRIVATE_persistenceSettings","__PRIVATE_verifyNotTerminated","__PRIVATE_initializationDone","__PRIVATE_persistenceResult","__PRIVATE_initialized","__PRIVATE_setChangeListener","__PRIVATE_initializeComponents","__PRIVATE_loadConnection","__PRIVATE_DatastoreImpl","initialize","Pr","v_","ka","clientId","g_","Dc","F_","__PRIVATE_eventMgr","__PRIVATE_setDatabaseDeletedListener","terminate","__PRIVATE_canFallback","__PRIVATE_MemoryComponentProvider","L_","DOMException","__PRIVATE_isShuttingDown","__PRIVATE_verifyOperationInProgress","__PRIVATE_enqueueAndInitiateShutdown","__PRIVATE_removeChangeListener","__PRIVATE_registerPendingWritesCallback","__PRIVATE_QueryListener","__PRIVATE_clientTerminated","__PRIVATE_readDocument","__PRIVATE_addSnapshotsInSyncListener","__PRIVATE_removeSnapshotsInSyncListener","O_","__PRIVATE_scheduleEvent","muted","eventHandler","__PRIVATE_AutoId","__PRIVATE_newId","__PRIVATE_referenceDelegateFactory","__PRIVATE_MemoryTargetCache","__PRIVATE_MemoryIndexManager","__PRIVATE_documentSize","__PRIVATE_forTargetCache","__PRIVATE_isPartialObserver","__PRIVATE_methods","__PRIVATE_convertTimestamp","__PRIVATE_convertServerTimestamp","__PRIVATE_convertReference","__PRIVATE_convertGeoPoint","__PRIVATE_convertArray","__PRIVATE_convertObject","__PRIVATE_convertValue","__PRIVATE_serverTimestampBehavior","__PRIVATE_getPreviousValue","__PRIVATE_normalizedValue","timestampsInSnapshots","toDate","__PRIVATE_resourcePath","converter","CACHE_SIZE_UNLIMITED","cacheSizeBytes","rf","__PRIVATE__userDataReader","__PRIVATE_UserDataReader","__PRIVATE__settings","__PRIVATE_settingsLiteral","__PRIVATE_newSettings","__PRIVATE_FirestoreSettings","__PRIVATE__firestoreClient","__PRIVATE__credentials","__PRIVATE_EmptyCredentialsProvider","__PRIVATE_client","getAuthHeaderValueForFirstParty","__PRIVATE_FirstPartyCredentialsProvider","__PRIVATE_ensureClientConfigured","synchronizeTabs","experimentalTabSynchronization","__PRIVATE_configureClient","__PRIVATE__componentProvider","__PRIVATE__queue","__PRIVATE_enqueueAndForgetEvenAfterShutdown","__PRIVATE_makeDatabaseInfo","clearPersistence","app","_removeServiceInstance","lf","waitForPendingWrites","__PRIVATE_onSnapshotsInSyncInternal","__PRIVATE_asyncObserver","__PRIVATE_AsyncObserver","__PRIVATE_mute","__PRIVATE_DatabaseInfo","__PRIVATE__persistenceKey","__PRIVATE_FirestoreClient","__PRIVATE__firebaseApp","__PRIVATE_pathString","CollectionReference","__PRIVATE_forPath","__PRIVATE_InternalQuery","WriteBatch","level","documentRef","__PRIVATE_validateReference","__PRIVATE__firestore","__PRIVATE__transaction","__PRIVATE_lookup","DocumentSnapshot","__PRIVATE__converter","__PRIVATE_validateSetOptions","__PRIVATE_convertedValue","__PRIVATE_parsed","merge","mergeFields","__PRIVATE__dataReader","__PRIVATE_parseMergeData","__PRIVATE_parseSetData","__PRIVATE_fieldOrUpdateData","__PRIVATE_parseUpdateVarargs","__PRIVATE_parseUpdateData","__PRIVATE_verifyNotCommitted","__PRIVATE__mutations","__PRIVATE__committed","__PRIVATE_currArg","__PRIVATE_internalOptions","complete","__PRIVATE_onSnapshotInternal","__PRIVATE_errHandler","__PRIVATE_internalListener","__PRIVATE_validateGetOptions","__PRIVATE_getDocumentFromLocalCache","__PRIVATE_getViaSnapshotListener","Rl","__PRIVATE_validateSnapshotOptions","__PRIVATE__document","QueryDocumentSnapshot","__PRIVATE__fromCache","__PRIVATE__hasPendingWrites","fromFirestore","__PRIVATE_UserDataWriter","__PRIVATE__areTimestampsInSnapshotsEnabled","serverTimestamps","SnapshotMetadata","opStr","__PRIVATE_fieldValue","__PRIVATE_enums","__PRIVATE_validateDisjunctiveFilterElements","__PRIVATE_referenceList","__PRIVATE_parseDocumentIdValue","__PRIVATE_parseQueryValue","__PRIVATE_validateNewFilter","__PRIVATE__query","__PRIVATE_addFilter","directionStr","__PRIVATE_validateNewOrderBy","__PRIVATE_addOrderBy","__PRIVATE_withLimitToFirst","__PRIVATE_withLimitToLast","__PRIVATE_docOrField","__PRIVATE_boundFromDocOrFields","__PRIVATE_withStartAt","__PRIVATE_withEndAt","__PRIVATE_boundFromDocument","Vf","__PRIVATE_allFields","__PRIVATE_boundFromFields","components","__PRIVATE_rawValue","__PRIVATE_wrapped","__PRIVATE_validateHasExplicitOrderByForLimitToLast","QuerySnapshot","__PRIVATE_firestoreClient","__PRIVATE_getDocumentsFromLocalCache","__PRIVATE_documentIdValue","operator","__PRIVATE_arrayOps","__PRIVATE_disjunctiveOps","__PRIVATE_isArrayOp","__PRIVATE_isDisjunctiveOp","__PRIVATE_existingField","__PRIVATE_validateOrderByAndInequalityMatch","__PRIVATE_conflictingOp","__PRIVATE_findFilterOperator","__PRIVATE_inequality","__PRIVATE__snapshot","__PRIVATE_convertToDocumentImpl","__PRIVATE__originalQuery","__PRIVATE__cachedChanges","__PRIVATE__cachedChangesIncludeMetadataChanges","oldIndex","newIndex","__PRIVATE_indexTracker","__PRIVATE_resultChangeType","toFirestore","__PRIVATE_docRef","__PRIVATE__path","__PRIVATE_databaseIdOrApp","__PRIVATE_AsyncQueue","Firestore","__PRIVATE_databaseIdFromApp","__PRIVATE_FirebaseCredentialsProvider","external","__PRIVATE_MINIMUM_CACHE_SIZE_BYTES","experimentalForceLongPolling","__PRIVATE_validator","__PRIVATE_applyFirestoreDataConverter","__PRIVATE_makeConstructorPrivate","__PRIVATE_cls","__PRIVATE_optionalMessage","__PRIVATE_PublicConstructor","assign","__PRIVATE_PublicFirestore","__PRIVATE_PublicTransaction","__PRIVATE_PublicWriteBatch","__PRIVATE_PublicDocumentReference","__PRIVATE_PublicDocumentSnapshot","__PRIVATE_PublicQueryDocumentSnapshot","__PRIVATE_PublicQuery","__PRIVATE_PublicQuerySnapshot","__PRIVATE_PublicCollectionReference","__PRIVATE_PublicFieldValue","__PRIVATE_ArrayUnionFieldValueImpl","__PRIVATE_ArrayRemoveFieldValueImpl","__PRIVATE_NumericIncrementFieldValueImpl","__PRIVATE_PublicBlob","__PRIVATE_firestoreNamespace","FieldValue","__PRIVATE_networkAvailableListener","__PRIVATE_networkUnavailableListener","__PRIVATE_wrappedOnOpen","__PRIVATE_wrappedOnClose","__PRIVATE_wrappedOnMessage","__PRIVATE_closeFn","__PRIVATE_sendFn","__PRIVATE_RPC_NAME_REST_MAPPING","BatchGetDocuments","Commit","__PRIVATE_X_GOOG_API_CLIENT_VALUE","__PRIVATE_header","__PRIVATE_makeUrl","__PRIVATE_xhr","listenOnce","getLastErrorCode","json","getResponseJson","getStatus","getResponseText","__PRIVATE_responseError","__PRIVATE_firestoreErrorCode","__PRIVATE_serverError","toLowerCase","__PRIVATE_jsonObj","__PRIVATE_requestString","Content-Type","__PRIVATE_modifyHeadersForRequest","runtime","ua","__PRIVATE_urlParts","__PRIVATE_baseUrl","__PRIVATE_webchannelTransport","initMessageHeaders","messageUrlParams","sendRawJson","supportsCrossDomainXhr","internalChannelParams","forwardChannelRequestTimeoutMs","chrome","browser","__PRIVATE_unguardedEventListen","createWebChannel","__PRIVATE_opened","closed","__PRIVATE_streamBridge","__PRIVATE_StreamBridge","zf","open","Yf","__PRIVATE_callOnClose","__PRIVATE_msgData","__PRIVATE_callOnMessage","__PRIVATE_callOnOpen","__PRIVATE_urlRpcName","info","__PRIVATE_onNetworkAvailable","__PRIVATE_onNetworkUnavailable","__PRIVATE_configureNetworkMonitoring","__PRIVATE_setPlatform","__PRIVATE_WebChannelConnection","__PRIVATE_BrowserConnectivityMonitor","__PRIVATE_isAvailable","__PRIVATE_NoopConnectivityMonitor","__PRIVATE_JsonProtoSerializer","hi","__PRIVATE_encoded","raw","__PRIVATE_nBytes","crypto","msCrypto","getRandomValues","registerComponent","container","getProvider","setServiceProps","registerVersion"],"mappings":"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"} |