1 line
2.3 MiB
1 line
2.3 MiB
{"version":3,"file":"firebase-firestore.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/remote_document_change_buffer.ts","../firestore/src/local/persistence.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/encoded_resource_path.ts","../firestore/src/local/memory_index_manager.ts","../firestore/src/local/indexeddb_index_manager.ts","../firestore/src/local/indexeddb_remote_document_cache.ts","../firestore/src/core/target_id_generator.ts","../firestore/src/local/indexeddb_target_cache.ts","../firestore/src/local/local_serializer.ts","../firestore/src/local/indexeddb_persistence.ts","../firestore/src/local/indexeddb_mutation_queue.ts","../firestore/src/local/indexeddb_schema.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/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/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_schema.ts","../firestore/src/core/view.ts","../firestore/src/core/sync_engine.ts","../firestore/src/core/event_manager.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/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.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 { 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 { 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 { 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 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 { ResourcePath } from '../model/path';\nimport { fail, hardAssert } from '../util/assert';\n\n/**\n * Helpers for dealing with resource paths stored in IndexedDB.\n *\n * Resource paths in their canonical string form do not sort as the server\n * sorts them. Specifically the server splits paths into segments first and then\n * sorts, putting end-of-segment before any character. In a UTF-8 string\n * encoding the slash ('/') that denotes the end-of-segment naturally comes\n * after other characters so the intent here is to encode the path delimiters in\n * such a way that the resulting strings sort naturally.\n *\n * Resource paths are also used for prefix scans so it's important to\n * distinguish whole segments from any longer segments of which they might be a\n * prefix. For example, it's important to make it possible to scan documents in\n * a collection \"foo\" without encountering documents in a collection \"foobar\".\n *\n * Separate from the concerns about resource path ordering and separation,\n * On Android, SQLite imposes additional restrictions since it does not handle\n * keys with embedded NUL bytes particularly well. Rather than change the\n * implementation we keep the encoding identical to keep the ports similar.\n *\n * Taken together this means resource paths when encoded for storage in\n * IndexedDB have the following characteristics:\n *\n * * Segment separators (\"/\") sort before everything else.\n * * All paths have a trailing separator.\n * * NUL bytes do not exist in the output, since IndexedDB doesn't treat them\n * well.\n *\n * Therefore resource paths are encoded into string form using the following\n * rules:\n *\n * * '\\x01' is used as an escape character.\n * * Path separators are encoded as \"\\x01\\x01\"\n * * NUL bytes are encoded as \"\\x01\\x10\"\n * * '\\x01' is encoded as \"\\x01\\x11\"\n *\n * This encoding leaves some room between path separators and the NUL byte\n * just in case we decide to support integer document ids after all.\n *\n * Note that characters treated specially by the backend ('.', '/', and '~')\n * are not treated specially here. This class assumes that any unescaping of\n * resource path strings into actual ResourcePath objects will handle these\n * characters there.\n */\nexport type EncodedResourcePath = string;\n\nconst escapeChar = '\\u0001';\nconst encodedSeparatorChar = '\\u0001';\nconst encodedNul = '\\u0010';\nconst encodedEscape = '\\u0011';\n\n/**\n * Encodes a resource path into a IndexedDb-compatible string form.\n */\nexport function encodeResourcePath(path: ResourcePath): EncodedResourcePath {\n let result = '';\n for (let i = 0; i < path.length; i++) {\n if (result.length > 0) {\n result = encodeSeparator(result);\n }\n result = encodeSegment(path.get(i), result);\n }\n return encodeSeparator(result);\n}\n\n/** Encodes a single segment of a resource path into the given result */\nfunction encodeSegment(segment: string, resultBuf: string): string {\n let result = resultBuf;\n const length = segment.length;\n for (let i = 0; i < length; i++) {\n const c = segment.charAt(i);\n switch (c) {\n case '\\0':\n result += escapeChar + encodedNul;\n break;\n case escapeChar:\n result += escapeChar + encodedEscape;\n break;\n default:\n result += c;\n }\n }\n return result;\n}\n\n/** Encodes a path separator into the given result */\nfunction encodeSeparator(result: string): string {\n return result + escapeChar + encodedSeparatorChar;\n}\n\n/**\n * Decodes the given IndexedDb-compatible string form of a resource path into\n * a ResourcePath instance. Note that this method is not suitable for use with\n * decoding resource names from the server; those are One Platform format\n * strings.\n */\nexport function decodeResourcePath(path: EncodedResourcePath): ResourcePath {\n // Event the empty path must encode as a path of at least length 2. A path\n // with exactly 2 must be the empty path.\n const length = path.length;\n hardAssert(length >= 2, 'Invalid path ' + path);\n if (length === 2) {\n hardAssert(\n path.charAt(0) === escapeChar && path.charAt(1) === encodedSeparatorChar,\n 'Non-empty path ' + path + ' had length 2'\n );\n return ResourcePath.EMPTY_PATH;\n }\n\n // Escape characters cannot exist past the second-to-last position in the\n // source value.\n const lastReasonableEscapeIndex = length - 2;\n\n const segments: string[] = [];\n let segmentBuilder = '';\n\n for (let start = 0; start < length; ) {\n // The last two characters of a valid encoded path must be a separator, so\n // there must be an end to this segment.\n const end = path.indexOf(escapeChar, start);\n if (end < 0 || end > lastReasonableEscapeIndex) {\n fail('Invalid encoded resource path: \"' + path + '\"');\n }\n\n const next = path.charAt(end + 1);\n switch (next) {\n case encodedSeparatorChar:\n const currentPiece = path.substring(start, end);\n let segment;\n if (segmentBuilder.length === 0) {\n // Avoid copying for the common case of a segment that excludes \\0\n // and \\001\n segment = currentPiece;\n } else {\n segmentBuilder += currentPiece;\n segment = segmentBuilder;\n segmentBuilder = '';\n }\n segments.push(segment);\n break;\n case encodedNul:\n segmentBuilder += path.substring(start, end);\n segmentBuilder += '\\0';\n break;\n case encodedEscape:\n // The escape character can be used in the output to encode itself.\n segmentBuilder += path.substring(start, end + 1);\n break;\n default:\n fail('Invalid encoded resource path: \"' + path + '\"');\n }\n\n start = end + 2;\n }\n\n return new ResourcePath(segments);\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 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 { immediateSuccessor } from '../util/misc';\nimport {\n decodeResourcePath,\n encodeResourcePath\n} from './encoded_resource_path';\nimport { IndexManager } from './index_manager';\nimport { IndexedDbPersistence } from './indexeddb_persistence';\nimport { DbCollectionParent, DbCollectionParentKey } from './indexeddb_schema';\nimport { MemoryCollectionParentIndex } from './memory_index_manager';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { SimpleDbStore } from './simple_db';\n\n/**\n * A persisted implementation of IndexManager.\n */\nexport class IndexedDbIndexManager implements IndexManager {\n /**\n * An in-memory copy of the index entries we've already written since the SDK\n * launched. Used to avoid re-writing the same entry repeatedly.\n *\n * This is *NOT* a complete cache of what's in persistence and so can never be used to\n * satisfy reads.\n */\n private collectionParentsCache = new MemoryCollectionParentIndex();\n\n /**\n * Adds a new entry to the collection parent index.\n *\n * Repeated calls for the same collectionPath should be avoided within a\n * transaction as IndexedDbIndexManager only caches writes once a transaction\n * has been committed.\n */\n addToCollectionParentIndex(\n transaction: PersistenceTransaction,\n collectionPath: ResourcePath\n ): PersistencePromise<void> {\n debugAssert(collectionPath.length % 2 === 1, 'Expected a collection path.');\n if (!this.collectionParentsCache.has(collectionPath)) {\n const collectionId = collectionPath.lastSegment();\n const parentPath = collectionPath.popLast();\n\n transaction.addOnCommittedListener(() => {\n // Add the collection to the in memory cache only if the transaction was\n // successfully committed.\n this.collectionParentsCache.add(collectionPath);\n });\n\n const collectionParent: DbCollectionParent = {\n collectionId,\n parent: encodeResourcePath(parentPath)\n };\n return collectionParentsStore(transaction).put(collectionParent);\n }\n return PersistencePromise.resolve();\n }\n\n getCollectionParents(\n transaction: PersistenceTransaction,\n collectionId: string\n ): PersistencePromise<ResourcePath[]> {\n const parentPaths = [] as ResourcePath[];\n const range = IDBKeyRange.bound(\n [collectionId, ''],\n [immediateSuccessor(collectionId), ''],\n /*lowerOpen=*/ false,\n /*upperOpen=*/ true\n );\n return collectionParentsStore(transaction)\n .loadAll(range)\n .next(entries => {\n for (const entry of entries) {\n // This collectionId guard shouldn't be necessary (and isn't as long\n // as we're running in a real browser), but there's a bug in\n // indexeddbshim that breaks our range in our tests running in node:\n // https://github.com/axemclion/IndexedDBShim/issues/334\n if (entry.collectionId !== collectionId) {\n break;\n }\n parentPaths.push(decodeResourcePath(entry.parent));\n }\n return parentPaths;\n });\n }\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the collectionParents\n * document store.\n */\nfunction collectionParentsStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbCollectionParentKey, DbCollectionParent> {\n return IndexedDbPersistence.getStore<\n DbCollectionParentKey,\n DbCollectionParent\n >(txn, DbCollectionParent.store);\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 DocumentSizeEntries,\n DocumentSizeEntry,\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 { ResourcePath } from '../model/path';\nimport { primitiveComparator } from '../util/misc';\nimport { SortedMap } from '../util/sorted_map';\nimport { SortedSet } from '../util/sorted_set';\n\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { debugAssert, fail, hardAssert } from '../util/assert';\nimport { IndexManager } from './index_manager';\nimport { IndexedDbPersistence } from './indexeddb_persistence';\nimport {\n DbRemoteDocument,\n DbRemoteDocumentGlobal,\n DbRemoteDocumentGlobalKey,\n DbRemoteDocumentKey\n} from './indexeddb_schema';\nimport { LocalSerializer } from './local_serializer';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { RemoteDocumentCache } from './remote_document_cache';\nimport { RemoteDocumentChangeBuffer } from './remote_document_change_buffer';\nimport { IterateOptions, SimpleDbStore } from './simple_db';\nimport { ObjectMap } from '../util/obj_map';\n\nexport class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {\n /**\n * @param {LocalSerializer} serializer The document serializer.\n * @param {IndexManager} indexManager The query indexes that need to be maintained.\n */\n constructor(\n readonly serializer: LocalSerializer,\n private readonly indexManager: IndexManager\n ) {}\n\n /**\n * Adds the supplied entries to the cache.\n *\n * All calls of `addEntry` are required to go through the RemoteDocumentChangeBuffer\n * returned by `newChangeBuffer()` to ensure proper accounting of metadata.\n */\n private addEntry(\n transaction: PersistenceTransaction,\n key: DocumentKey,\n doc: DbRemoteDocument\n ): PersistencePromise<void> {\n const documentStore = remoteDocumentsStore(transaction);\n return documentStore.put(dbKey(key), doc);\n }\n\n /**\n * Removes a document from the cache.\n *\n * All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer\n * returned by `newChangeBuffer()` to ensure proper accounting of metadata.\n */\n private removeEntry(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<void> {\n const store = remoteDocumentsStore(transaction);\n const key = dbKey(documentKey);\n return store.delete(key);\n }\n\n /**\n * Updates the current cache size.\n *\n * Callers to `addEntry()` and `removeEntry()` *must* call this afterwards to update the\n * cache's metadata.\n */\n private updateMetadata(\n transaction: PersistenceTransaction,\n sizeDelta: number\n ): PersistencePromise<void> {\n return this.getMetadata(transaction).next(metadata => {\n metadata.byteSize += sizeDelta;\n return this.setMetadata(transaction, metadata);\n });\n }\n\n getEntry(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n return remoteDocumentsStore(transaction)\n .get(dbKey(documentKey))\n .next(dbRemoteDoc => {\n return this.maybeDecodeDocument(dbRemoteDoc);\n });\n }\n\n /**\n * Looks up an entry in the cache.\n *\n * @param documentKey The key of the entry to look up.\n * @return The cached MaybeDocument entry and its size, or null if we have nothing cached.\n */\n getSizedEntry(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<DocumentSizeEntry | null> {\n return remoteDocumentsStore(transaction)\n .get(dbKey(documentKey))\n .next(dbRemoteDoc => {\n const doc = this.maybeDecodeDocument(dbRemoteDoc);\n return doc\n ? {\n maybeDocument: doc,\n size: dbDocumentSize(dbRemoteDoc!)\n }\n : null;\n });\n }\n\n getEntries(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap> {\n let results = nullableMaybeDocumentMap();\n return this.forEachDbEntry(\n transaction,\n documentKeys,\n (key, dbRemoteDoc) => {\n const doc = this.maybeDecodeDocument(dbRemoteDoc);\n results = results.insert(key, doc);\n }\n ).next(() => results);\n }\n\n /**\n * Looks up several entries in the cache.\n *\n * @param documentKeys The set of keys entries to look up.\n * @return A map of MaybeDocuments indexed by key (if a document cannot be\n * found, the key will be mapped to null) and a map of sizes indexed by\n * key (zero if the key cannot be found).\n */\n getSizedEntries(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<DocumentSizeEntries> {\n let results = nullableMaybeDocumentMap();\n let sizeMap = new SortedMap<DocumentKey, number>(DocumentKey.comparator);\n return this.forEachDbEntry(\n transaction,\n documentKeys,\n (key, dbRemoteDoc) => {\n const doc = this.maybeDecodeDocument(dbRemoteDoc);\n if (doc) {\n results = results.insert(key, doc);\n sizeMap = sizeMap.insert(key, dbDocumentSize(dbRemoteDoc!));\n } else {\n results = results.insert(key, null);\n sizeMap = sizeMap.insert(key, 0);\n }\n }\n ).next(() => {\n return { maybeDocuments: results, sizeMap };\n });\n }\n\n private forEachDbEntry(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet,\n callback: (key: DocumentKey, doc: DbRemoteDocument | null) => void\n ): PersistencePromise<void> {\n if (documentKeys.isEmpty()) {\n return PersistencePromise.resolve();\n }\n\n const range = IDBKeyRange.bound(\n documentKeys.first()!.path.toArray(),\n documentKeys.last()!.path.toArray()\n );\n const keyIter = documentKeys.getIterator();\n let nextKey: DocumentKey | null = keyIter.getNext();\n\n return remoteDocumentsStore(transaction)\n .iterate({ range }, (potentialKeyRaw, dbRemoteDoc, control) => {\n const potentialKey = DocumentKey.fromSegments(potentialKeyRaw);\n\n // Go through keys not found in cache.\n while (nextKey && DocumentKey.comparator(nextKey!, potentialKey) < 0) {\n callback(nextKey!, null);\n nextKey = keyIter.getNext();\n }\n\n if (nextKey && nextKey!.isEqual(potentialKey)) {\n // Key found in cache.\n callback(nextKey!, dbRemoteDoc);\n nextKey = keyIter.hasNext() ? keyIter.getNext() : null;\n }\n\n // Skip to the next key (if there is one).\n if (nextKey) {\n control.skip(nextKey!.path.toArray());\n } else {\n control.done();\n }\n })\n .next(() => {\n // The rest of the keys are not in the cache. One case where `iterate`\n // above won't go through them is when the cache is empty.\n while (nextKey) {\n callback(nextKey!, null);\n nextKey = keyIter.hasNext() ? keyIter.getNext() : null;\n }\n });\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 const immediateChildrenPathLength = query.path.length + 1;\n\n const iterationOptions: IterateOptions = {};\n if (sinceReadTime.isEqual(SnapshotVersion.min())) {\n // Documents are ordered by key, so we can use a prefix scan to narrow\n // down the documents we need to match the query against.\n const startKey = query.path.toArray();\n iterationOptions.range = IDBKeyRange.lowerBound(startKey);\n } else {\n // Execute an index-free query and filter by read time. This is safe\n // since all document changes to queries that have a\n // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.\n const collectionKey = query.path.toArray();\n const readTimeKey = this.serializer.toDbTimestampKey(sinceReadTime);\n iterationOptions.range = IDBKeyRange.lowerBound(\n [collectionKey, readTimeKey],\n /* open= */ true\n );\n iterationOptions.index = DbRemoteDocument.collectionReadTimeIndex;\n }\n\n return remoteDocumentsStore(transaction)\n .iterate(iterationOptions, (key, dbRemoteDoc, control) => {\n // The query is actually returning any path that starts with the query\n // path prefix which may include documents in subcollections. For\n // example, a query on 'rooms' will return rooms/abc/messages/xyx but we\n // shouldn't match it. Fix this by discarding rows with document keys\n // more than one segment longer than the query path.\n if (key.length !== immediateChildrenPathLength) {\n return;\n }\n\n const maybeDoc = this.serializer.fromDbRemoteDocument(dbRemoteDoc);\n if (!query.path.isPrefixOf(maybeDoc.key.path)) {\n control.done();\n } else if (maybeDoc instanceof Document && query.matches(maybeDoc)) {\n results = results.insert(maybeDoc.key, maybeDoc);\n }\n })\n .next(() => results);\n }\n\n /**\n * Returns the set of documents that have changed since the specified read\n * time.\n */\n // PORTING NOTE: This is only used for multi-tab synchronization.\n getNewDocumentChanges(\n transaction: PersistenceTransaction,\n sinceReadTime: SnapshotVersion\n ): PersistencePromise<{\n changedDocs: MaybeDocumentMap;\n readTime: SnapshotVersion;\n }> {\n let changedDocs = maybeDocumentMap();\n\n let lastReadTime = this.serializer.toDbTimestampKey(sinceReadTime);\n\n const documentsStore = remoteDocumentsStore(transaction);\n const range = IDBKeyRange.lowerBound(lastReadTime, true);\n return documentsStore\n .iterate(\n { index: DbRemoteDocument.readTimeIndex, range },\n (_, dbRemoteDoc) => {\n // Unlike `getEntry()` and others, `getNewDocumentChanges()` parses\n // the documents directly since we want to keep sentinel deletes.\n const doc = this.serializer.fromDbRemoteDocument(dbRemoteDoc);\n changedDocs = changedDocs.insert(doc.key, doc);\n lastReadTime = dbRemoteDoc.readTime!;\n }\n )\n .next(() => {\n return {\n changedDocs,\n readTime: this.serializer.fromDbTimestampKey(lastReadTime)\n };\n });\n }\n\n /**\n * Returns the read time of the most recently read document in the cache, or\n * SnapshotVersion.min() if not available.\n */\n // PORTING NOTE: This is only used for multi-tab synchronization.\n getLastReadTime(\n transaction: PersistenceTransaction\n ): PersistencePromise<SnapshotVersion> {\n const documentsStore = remoteDocumentsStore(transaction);\n\n // If there are no existing entries, we return SnapshotVersion.min().\n let readTime = SnapshotVersion.min();\n\n return documentsStore\n .iterate(\n { index: DbRemoteDocument.readTimeIndex, reverse: true },\n (key, dbRemoteDoc, control) => {\n if (dbRemoteDoc.readTime) {\n readTime = this.serializer.fromDbTimestampKey(dbRemoteDoc.readTime);\n }\n control.done();\n }\n )\n .next(() => readTime);\n }\n\n newChangeBuffer(options?: {\n trackRemovals: boolean;\n }): RemoteDocumentChangeBuffer {\n return new IndexedDbRemoteDocumentCache.RemoteDocumentChangeBuffer(\n this,\n !!options && options.trackRemovals\n );\n }\n\n getSize(txn: PersistenceTransaction): PersistencePromise<number> {\n return this.getMetadata(txn).next(metadata => metadata.byteSize);\n }\n\n private getMetadata(\n txn: PersistenceTransaction\n ): PersistencePromise<DbRemoteDocumentGlobal> {\n return documentGlobalStore(txn)\n .get(DbRemoteDocumentGlobal.key)\n .next(metadata => {\n hardAssert(!!metadata, 'Missing document cache metadata');\n return metadata!;\n });\n }\n\n private setMetadata(\n txn: PersistenceTransaction,\n metadata: DbRemoteDocumentGlobal\n ): PersistencePromise<void> {\n return documentGlobalStore(txn).put(DbRemoteDocumentGlobal.key, metadata);\n }\n\n /**\n * Decodes `remoteDoc` and returns the document (or null, if the document\n * corresponds to the format used for sentinel deletes).\n */\n private maybeDecodeDocument(\n dbRemoteDoc: DbRemoteDocument | null\n ): MaybeDocument | null {\n if (dbRemoteDoc) {\n const doc = this.serializer.fromDbRemoteDocument(dbRemoteDoc);\n if (\n doc instanceof NoDocument &&\n doc.version.isEqual(SnapshotVersion.min())\n ) {\n // The document is a sentinel removal and should only be used in the\n // `getNewDocumentChanges()`.\n return null;\n }\n\n return doc;\n }\n return null;\n }\n\n /**\n * Handles the details of adding and updating documents in the IndexedDbRemoteDocumentCache.\n *\n * Unlike the MemoryRemoteDocumentChangeBuffer, the IndexedDb implementation computes the size\n * delta for all submitted changes. This avoids having to re-read all documents from IndexedDb\n * when we apply the changes.\n */\n private static RemoteDocumentChangeBuffer = class extends RemoteDocumentChangeBuffer {\n // A map of document sizes prior to applying the changes in this buffer.\n protected documentSizes: ObjectMap<\n DocumentKey,\n number\n > = new ObjectMap(key => key.toString());\n\n /**\n * @param documentCache The IndexedDbRemoteDocumentCache to apply the changes to.\n * @param trackRemovals Whether to create sentinel deletes that can be tracked by\n * `getNewDocumentChanges()`.\n */\n constructor(\n private readonly documentCache: IndexedDbRemoteDocumentCache,\n private readonly trackRemovals: boolean\n ) {\n super();\n }\n\n protected applyChanges(\n transaction: PersistenceTransaction\n ): PersistencePromise<void> {\n const promises: Array<PersistencePromise<void>> = [];\n\n let sizeDelta = 0;\n\n let collectionParents = new SortedSet<ResourcePath>((l, r) =>\n primitiveComparator(l.canonicalString(), r.canonicalString())\n );\n\n this.changes.forEach((key, maybeDocument) => {\n const previousSize = this.documentSizes.get(key);\n debugAssert(\n previousSize !== undefined,\n `Cannot modify a document that wasn't read (for ${key})`\n );\n if (maybeDocument) {\n debugAssert(\n !this.readTime.isEqual(SnapshotVersion.min()),\n 'Cannot add a document with a read time of zero'\n );\n const doc = this.documentCache.serializer.toDbRemoteDocument(\n maybeDocument,\n this.readTime\n );\n collectionParents = collectionParents.add(key.path.popLast());\n\n const size = dbDocumentSize(doc);\n sizeDelta += size - previousSize!;\n promises.push(this.documentCache.addEntry(transaction, key, doc));\n } else {\n sizeDelta -= previousSize!;\n if (this.trackRemovals) {\n // In order to track removals, we store a \"sentinel delete\" in the\n // RemoteDocumentCache. This entry is represented by a NoDocument\n // with a version of 0 and ignored by `maybeDecodeDocument()` but\n // preserved in `getNewDocumentChanges()`.\n const deletedDoc = this.documentCache.serializer.toDbRemoteDocument(\n new NoDocument(key, SnapshotVersion.min()),\n this.readTime\n );\n promises.push(\n this.documentCache.addEntry(transaction, key, deletedDoc)\n );\n } else {\n promises.push(this.documentCache.removeEntry(transaction, key));\n }\n }\n });\n\n collectionParents.forEach(parent => {\n promises.push(\n this.documentCache.indexManager.addToCollectionParentIndex(\n transaction,\n parent\n )\n );\n });\n\n promises.push(this.documentCache.updateMetadata(transaction, sizeDelta));\n\n return PersistencePromise.waitFor(promises);\n }\n\n protected getFromCache(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MaybeDocument | null> {\n // Record the size of everything we load from the cache so we can compute a delta later.\n return this.documentCache\n .getSizedEntry(transaction, documentKey)\n .next(getResult => {\n if (getResult === null) {\n this.documentSizes.set(documentKey, 0);\n return null;\n } else {\n this.documentSizes.set(documentKey, getResult.size);\n return getResult.maybeDocument;\n }\n });\n }\n\n protected getAllFromCache(\n transaction: PersistenceTransaction,\n documentKeys: DocumentKeySet\n ): PersistencePromise<NullableMaybeDocumentMap> {\n // Record the size of everything we load from the cache so we can compute\n // a delta later.\n return this.documentCache\n .getSizedEntries(transaction, documentKeys)\n .next(({ maybeDocuments, sizeMap }) => {\n // Note: `getAllFromCache` returns two maps instead of a single map from\n // keys to `DocumentSizeEntry`s. This is to allow returning the\n // `NullableMaybeDocumentMap` directly, without a conversion.\n sizeMap.forEach((documentKey, size) => {\n this.documentSizes.set(documentKey, size);\n });\n return maybeDocuments;\n });\n }\n };\n}\n\nfunction documentGlobalStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbRemoteDocumentGlobalKey, DbRemoteDocumentGlobal> {\n return IndexedDbPersistence.getStore<\n DbRemoteDocumentGlobalKey,\n DbRemoteDocumentGlobal\n >(txn, DbRemoteDocumentGlobal.store);\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the remoteDocuments object store.\n */\nfunction remoteDocumentsStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbRemoteDocumentKey, DbRemoteDocument> {\n return IndexedDbPersistence.getStore<DbRemoteDocumentKey, DbRemoteDocument>(\n txn,\n DbRemoteDocument.store\n );\n}\n\nfunction dbKey(docKey: DocumentKey): DbRemoteDocumentKey {\n return docKey.path.toArray();\n}\n\n/**\n * Retrusn an approximate size for the given document.\n */\nexport function dbDocumentSize(doc: DbRemoteDocument): number {\n let value: unknown;\n if (doc.document) {\n value = doc.document;\n } else if (doc.unknownDocument) {\n value = doc.unknownDocument;\n } else if (doc.noDocument) {\n value = doc.noDocument;\n } else {\n throw fail('Unknown remote document type');\n }\n return JSON.stringify(value).length;\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 { Timestamp } from '../api/timestamp';\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { DocumentKeySet, documentKeySet } from '../model/collections';\nimport { DocumentKey } from '../model/document_key';\nimport { hardAssert } from '../util/assert';\nimport { immediateSuccessor } from '../util/misc';\nimport { TargetIdGenerator } from '../core/target_id_generator';\nimport {\n decodeResourcePath,\n encodeResourcePath\n} from './encoded_resource_path';\nimport {\n IndexedDbLruDelegate,\n IndexedDbPersistence\n} from './indexeddb_persistence';\nimport {\n DbTarget,\n DbTargetDocument,\n DbTargetDocumentKey,\n DbTargetGlobal,\n DbTargetGlobalKey,\n DbTargetKey\n} from './indexeddb_schema';\nimport { LocalSerializer } from './local_serializer';\nimport { ActiveTargets } from './lru_garbage_collector';\nimport { PersistenceTransaction } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { TargetCache } from './target_cache';\nimport { TargetData } from './target_data';\nimport { SimpleDbStore } from './simple_db';\nimport { Target } from '../core/target';\n\nexport class IndexedDbTargetCache implements TargetCache {\n constructor(\n private readonly referenceDelegate: IndexedDbLruDelegate,\n private serializer: LocalSerializer\n ) {}\n\n // PORTING NOTE: We don't cache global metadata for the target cache, since\n // some of it (in particular `highestTargetId`) can be modified by secondary\n // tabs. We could perhaps be more granular (and e.g. still cache\n // `lastRemoteSnapshotVersion` in memory) but for simplicity we currently go\n // to IndexedDb whenever we need to read metadata. We can revisit if it turns\n // out to have a meaningful performance impact.\n\n allocateTargetId(\n transaction: PersistenceTransaction\n ): PersistencePromise<TargetId> {\n return this.retrieveMetadata(transaction).next(metadata => {\n const targetIdGenerator = new TargetIdGenerator(metadata.highestTargetId);\n metadata.highestTargetId = targetIdGenerator.next();\n return this.saveMetadata(transaction, metadata).next(\n () => metadata.highestTargetId\n );\n });\n }\n\n getLastRemoteSnapshotVersion(\n transaction: PersistenceTransaction\n ): PersistencePromise<SnapshotVersion> {\n return this.retrieveMetadata(transaction).next(metadata => {\n return SnapshotVersion.fromTimestamp(\n new Timestamp(\n metadata.lastRemoteSnapshotVersion.seconds,\n metadata.lastRemoteSnapshotVersion.nanoseconds\n )\n );\n });\n }\n\n getHighestSequenceNumber(\n transaction: PersistenceTransaction\n ): PersistencePromise<ListenSequenceNumber> {\n return this.retrieveMetadata(transaction).next(\n targetGlobal => targetGlobal.highestListenSequenceNumber\n );\n }\n\n setTargetsMetadata(\n transaction: PersistenceTransaction,\n highestListenSequenceNumber: number,\n lastRemoteSnapshotVersion?: SnapshotVersion\n ): PersistencePromise<void> {\n return this.retrieveMetadata(transaction).next(metadata => {\n metadata.highestListenSequenceNumber = highestListenSequenceNumber;\n if (lastRemoteSnapshotVersion) {\n metadata.lastRemoteSnapshotVersion = lastRemoteSnapshotVersion.toTimestamp();\n }\n if (highestListenSequenceNumber > metadata.highestListenSequenceNumber) {\n metadata.highestListenSequenceNumber = highestListenSequenceNumber;\n }\n return this.saveMetadata(transaction, metadata);\n });\n }\n\n addTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n return this.saveTargetData(transaction, targetData).next(() => {\n return this.retrieveMetadata(transaction).next(metadata => {\n metadata.targetCount += 1;\n this.updateMetadataFromTargetData(targetData, metadata);\n return this.saveMetadata(transaction, metadata);\n });\n });\n }\n\n updateTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n return this.saveTargetData(transaction, targetData);\n }\n\n removeTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n return this.removeMatchingKeysForTargetId(transaction, targetData.targetId)\n .next(() => targetsStore(transaction).delete(targetData.targetId))\n .next(() => this.retrieveMetadata(transaction))\n .next(metadata => {\n hardAssert(\n metadata.targetCount > 0,\n 'Removing from an empty target cache'\n );\n metadata.targetCount -= 1;\n return this.saveMetadata(transaction, metadata);\n });\n }\n\n /**\n * Drops any targets with sequence number less than or equal to the upper bound, excepting those\n * present in `activeTargetIds`. Document associations for the removed targets are also removed.\n * Returns the number of targets removed.\n */\n removeTargets(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number> {\n let count = 0;\n const promises: Array<PersistencePromise<void>> = [];\n return targetsStore(txn)\n .iterate((key, value) => {\n const targetData = this.serializer.fromDbTarget(value);\n if (\n targetData.sequenceNumber <= upperBound &&\n activeTargetIds.get(targetData.targetId) === null\n ) {\n count++;\n promises.push(this.removeTargetData(txn, targetData));\n }\n })\n .next(() => PersistencePromise.waitFor(promises))\n .next(() => count);\n }\n\n /**\n * Call provided function with each `TargetData` that we have cached.\n */\n forEachTarget(\n txn: PersistenceTransaction,\n f: (q: TargetData) => void\n ): PersistencePromise<void> {\n return targetsStore(txn).iterate((key, value) => {\n const targetData = this.serializer.fromDbTarget(value);\n f(targetData);\n });\n }\n\n private retrieveMetadata(\n transaction: PersistenceTransaction\n ): PersistencePromise<DbTargetGlobal> {\n return globalTargetStore(transaction)\n .get(DbTargetGlobal.key)\n .next(metadata => {\n hardAssert(metadata !== null, 'Missing metadata row.');\n return metadata;\n });\n }\n\n private saveMetadata(\n transaction: PersistenceTransaction,\n metadata: DbTargetGlobal\n ): PersistencePromise<void> {\n return globalTargetStore(transaction).put(DbTargetGlobal.key, metadata);\n }\n\n private saveTargetData(\n transaction: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n return targetsStore(transaction).put(\n this.serializer.toDbTarget(targetData)\n );\n }\n\n /**\n * In-place updates the provided metadata to account for values in the given\n * TargetData. Saving is done separately. Returns true if there were any\n * changes to the metadata.\n */\n private updateMetadataFromTargetData(\n targetData: TargetData,\n metadata: DbTargetGlobal\n ): boolean {\n let updated = false;\n if (targetData.targetId > metadata.highestTargetId) {\n metadata.highestTargetId = targetData.targetId;\n updated = true;\n }\n\n if (targetData.sequenceNumber > metadata.highestListenSequenceNumber) {\n metadata.highestListenSequenceNumber = targetData.sequenceNumber;\n updated = true;\n }\n return updated;\n }\n\n getTargetCount(\n transaction: PersistenceTransaction\n ): PersistencePromise<number> {\n return this.retrieveMetadata(transaction).next(\n metadata => metadata.targetCount\n );\n }\n\n getTargetData(\n transaction: PersistenceTransaction,\n target: Target\n ): PersistencePromise<TargetData | null> {\n // Iterating by the canonicalId may yield more than one result because\n // canonicalId values are not required to be unique per target. This query\n // depends on the queryTargets index to be efficient.\n const canonicalId = target.canonicalId();\n const range = IDBKeyRange.bound(\n [canonicalId, Number.NEGATIVE_INFINITY],\n [canonicalId, Number.POSITIVE_INFINITY]\n );\n let result: TargetData | null = null;\n return targetsStore(transaction)\n .iterate(\n { range, index: DbTarget.queryTargetsIndexName },\n (key, value, control) => {\n const found = this.serializer.fromDbTarget(value);\n // After finding a potential match, check that the target is\n // actually equal to the requested target.\n if (target.isEqual(found.target)) {\n result = found;\n control.done();\n }\n }\n )\n .next(() => result);\n }\n\n addMatchingKeys(\n txn: PersistenceTransaction,\n keys: DocumentKeySet,\n targetId: TargetId\n ): PersistencePromise<void> {\n // PORTING NOTE: The reverse index (documentsTargets) is maintained by\n // IndexedDb.\n const promises: Array<PersistencePromise<void>> = [];\n const store = documentTargetStore(txn);\n keys.forEach(key => {\n const path = encodeResourcePath(key.path);\n promises.push(store.put(new DbTargetDocument(targetId, path)));\n promises.push(this.referenceDelegate.addReference(txn, targetId, key));\n });\n return PersistencePromise.waitFor(promises);\n }\n\n removeMatchingKeys(\n txn: PersistenceTransaction,\n keys: DocumentKeySet,\n targetId: TargetId\n ): PersistencePromise<void> {\n // PORTING NOTE: The reverse index (documentsTargets) is maintained by\n // IndexedDb.\n const store = documentTargetStore(txn);\n return PersistencePromise.forEach(keys, (key: DocumentKey) => {\n const path = encodeResourcePath(key.path);\n return PersistencePromise.waitFor([\n store.delete([targetId, path]),\n this.referenceDelegate.removeReference(txn, targetId, key)\n ]);\n });\n }\n\n removeMatchingKeysForTargetId(\n txn: PersistenceTransaction,\n targetId: TargetId\n ): PersistencePromise<void> {\n const store = documentTargetStore(txn);\n const range = IDBKeyRange.bound(\n [targetId],\n [targetId + 1],\n /*lowerOpen=*/ false,\n /*upperOpen=*/ true\n );\n return store.delete(range);\n }\n\n getMatchingKeysForTargetId(\n txn: PersistenceTransaction,\n targetId: TargetId\n ): PersistencePromise<DocumentKeySet> {\n const range = IDBKeyRange.bound(\n [targetId],\n [targetId + 1],\n /*lowerOpen=*/ false,\n /*upperOpen=*/ true\n );\n const store = documentTargetStore(txn);\n let result = documentKeySet();\n\n return store\n .iterate({ range, keysOnly: true }, (key, _, control) => {\n const path = decodeResourcePath(key[1]);\n const docKey = new DocumentKey(path);\n result = result.add(docKey);\n })\n .next(() => result);\n }\n\n containsKey(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n const path = encodeResourcePath(key.path);\n const range = IDBKeyRange.bound(\n [path],\n [immediateSuccessor(path)],\n /*lowerOpen=*/ false,\n /*upperOpen=*/ true\n );\n let count = 0;\n return documentTargetStore(txn!)\n .iterate(\n {\n index: DbTargetDocument.documentTargetsIndex,\n keysOnly: true,\n range\n },\n ([targetId, path], _, control) => {\n // Having a sentinel row for a document does not count as containing that document;\n // For the target cache, containing the document means the document is part of some\n // target.\n if (targetId !== 0) {\n count++;\n control.done();\n }\n }\n )\n .next(() => count > 0);\n }\n\n /**\n * Looks up a TargetData entry by target ID.\n *\n * @param targetId The target ID of the TargetData entry to look up.\n * @return The cached TargetData entry, or null if the cache has no entry for\n * the target.\n */\n // PORTING NOTE: Multi-tab only.\n getTargetDataForTarget(\n transaction: PersistenceTransaction,\n targetId: TargetId\n ): PersistencePromise<TargetData | null> {\n return targetsStore(transaction)\n .get(targetId)\n .next(found => {\n if (found) {\n return this.serializer.fromDbTarget(found);\n } else {\n return null;\n }\n });\n }\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the queries object store.\n */\nfunction targetsStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbTargetKey, DbTarget> {\n return IndexedDbPersistence.getStore<DbTargetKey, DbTarget>(\n txn,\n DbTarget.store\n );\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the target globals object store.\n */\nfunction globalTargetStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbTargetGlobalKey, DbTargetGlobal> {\n return IndexedDbPersistence.getStore<DbTargetGlobalKey, DbTargetGlobal>(\n txn,\n DbTargetGlobal.store\n );\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the document target object store.\n */\nexport function documentTargetStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbTargetDocumentKey, DbTargetDocument> {\n return IndexedDbPersistence.getStore<DbTargetDocumentKey, DbTargetDocument>(\n txn,\n DbTargetDocument.store\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 {\n Document,\n MaybeDocument,\n NoDocument,\n UnknownDocument\n} from '../model/document';\nimport { DocumentKey } from '../model/document_key';\nimport { MutationBatch } from '../model/mutation_batch';\nimport * as api from '../protos/firestore_proto_api';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport { debugAssert, fail } from '../util/assert';\nimport { ByteString } from '../util/byte_string';\nimport { Target } from '../core/target';\nimport {\n DbMutationBatch,\n DbNoDocument,\n DbQuery,\n DbRemoteDocument,\n DbTarget,\n DbTimestamp,\n DbTimestampKey,\n DbUnknownDocument\n} from './indexeddb_schema';\nimport { TargetData, TargetPurpose } from './target_data';\n\n/** Serializer for values stored in the LocalStore. */\nexport class LocalSerializer {\n constructor(private remoteSerializer: JsonProtoSerializer) {}\n\n /** Decodes a remote document from storage locally to a Document. */\n fromDbRemoteDocument(remoteDoc: DbRemoteDocument): MaybeDocument {\n if (remoteDoc.document) {\n return this.remoteSerializer.fromDocument(\n remoteDoc.document,\n !!remoteDoc.hasCommittedMutations\n );\n } else if (remoteDoc.noDocument) {\n const key = DocumentKey.fromSegments(remoteDoc.noDocument.path);\n const version = this.fromDbTimestamp(remoteDoc.noDocument.readTime);\n return new NoDocument(key, version, {\n hasCommittedMutations: !!remoteDoc.hasCommittedMutations\n });\n } else if (remoteDoc.unknownDocument) {\n const key = DocumentKey.fromSegments(remoteDoc.unknownDocument.path);\n const version = this.fromDbTimestamp(remoteDoc.unknownDocument.version);\n return new UnknownDocument(key, version);\n } else {\n return fail('Unexpected DbRemoteDocument');\n }\n }\n\n /** Encodes a document for storage locally. */\n toDbRemoteDocument(\n maybeDoc: MaybeDocument,\n readTime: SnapshotVersion\n ): DbRemoteDocument {\n const dbReadTime = this.toDbTimestampKey(readTime);\n const parentPath = maybeDoc.key.path.popLast().toArray();\n if (maybeDoc instanceof Document) {\n const doc = this.remoteSerializer.toDocument(maybeDoc);\n const hasCommittedMutations = maybeDoc.hasCommittedMutations;\n return new DbRemoteDocument(\n /* unknownDocument= */ null,\n /* noDocument= */ null,\n doc,\n hasCommittedMutations,\n dbReadTime,\n parentPath\n );\n } else if (maybeDoc instanceof NoDocument) {\n const path = maybeDoc.key.path.toArray();\n const readTime = this.toDbTimestamp(maybeDoc.version);\n const hasCommittedMutations = maybeDoc.hasCommittedMutations;\n return new DbRemoteDocument(\n /* unknownDocument= */ null,\n new DbNoDocument(path, readTime),\n /* document= */ null,\n hasCommittedMutations,\n dbReadTime,\n parentPath\n );\n } else if (maybeDoc instanceof UnknownDocument) {\n const path = maybeDoc.key.path.toArray();\n const readTime = this.toDbTimestamp(maybeDoc.version);\n return new DbRemoteDocument(\n new DbUnknownDocument(path, readTime),\n /* noDocument= */ null,\n /* document= */ null,\n /* hasCommittedMutations= */ true,\n dbReadTime,\n parentPath\n );\n } else {\n return fail('Unexpected MaybeDocument');\n }\n }\n\n toDbTimestampKey(snapshotVersion: SnapshotVersion): DbTimestampKey {\n const timestamp = snapshotVersion.toTimestamp();\n return [timestamp.seconds, timestamp.nanoseconds];\n }\n\n fromDbTimestampKey(dbTimestampKey: DbTimestampKey): SnapshotVersion {\n const timestamp = new Timestamp(dbTimestampKey[0], dbTimestampKey[1]);\n return SnapshotVersion.fromTimestamp(timestamp);\n }\n\n private toDbTimestamp(snapshotVersion: SnapshotVersion): DbTimestamp {\n const timestamp = snapshotVersion.toTimestamp();\n return new DbTimestamp(timestamp.seconds, timestamp.nanoseconds);\n }\n\n private fromDbTimestamp(dbTimestamp: DbTimestamp): SnapshotVersion {\n const timestamp = new Timestamp(\n dbTimestamp.seconds,\n dbTimestamp.nanoseconds\n );\n return SnapshotVersion.fromTimestamp(timestamp);\n }\n\n /** Encodes a batch of mutations into a DbMutationBatch for local storage. */\n toDbMutationBatch(userId: string, batch: MutationBatch): DbMutationBatch {\n const serializedBaseMutations = batch.baseMutations.map(m =>\n this.remoteSerializer.toMutation(m)\n );\n const serializedMutations = batch.mutations.map(m =>\n this.remoteSerializer.toMutation(m)\n );\n return new DbMutationBatch(\n userId,\n batch.batchId,\n batch.localWriteTime.toMillis(),\n serializedBaseMutations,\n serializedMutations\n );\n }\n\n /** Decodes a DbMutationBatch into a MutationBatch */\n fromDbMutationBatch(dbBatch: DbMutationBatch): MutationBatch {\n const baseMutations = (dbBatch.baseMutations || []).map(m =>\n this.remoteSerializer.fromMutation(m)\n );\n const mutations = dbBatch.mutations.map(m =>\n this.remoteSerializer.fromMutation(m)\n );\n const timestamp = Timestamp.fromMillis(dbBatch.localWriteTimeMs);\n return new MutationBatch(\n dbBatch.batchId,\n timestamp,\n baseMutations,\n mutations\n );\n }\n\n /** Decodes a DbTarget into TargetData */\n fromDbTarget(dbTarget: DbTarget): TargetData {\n const version = this.fromDbTimestamp(dbTarget.readTime);\n const lastLimboFreeSnapshotVersion =\n dbTarget.lastLimboFreeSnapshotVersion !== undefined\n ? this.fromDbTimestamp(dbTarget.lastLimboFreeSnapshotVersion)\n : SnapshotVersion.min();\n\n let target: Target;\n if (isDocumentQuery(dbTarget.query)) {\n target = this.remoteSerializer.fromDocumentsTarget(dbTarget.query);\n } else {\n target = this.remoteSerializer.fromQueryTarget(dbTarget.query);\n }\n return new TargetData(\n target,\n dbTarget.targetId,\n TargetPurpose.Listen,\n dbTarget.lastListenSequenceNumber,\n version,\n lastLimboFreeSnapshotVersion,\n ByteString.fromBase64String(dbTarget.resumeToken)\n );\n }\n\n /** Encodes TargetData into a DbTarget for storage locally. */\n toDbTarget(targetData: TargetData): DbTarget {\n debugAssert(\n TargetPurpose.Listen === targetData.purpose,\n 'Only queries with purpose ' +\n TargetPurpose.Listen +\n ' may be stored, got ' +\n targetData.purpose\n );\n const dbTimestamp = this.toDbTimestamp(targetData.snapshotVersion);\n const dbLastLimboFreeTimestamp = this.toDbTimestamp(\n targetData.lastLimboFreeSnapshotVersion\n );\n let queryProto: DbQuery;\n if (targetData.target.isDocumentQuery()) {\n queryProto = this.remoteSerializer.toDocumentsTarget(targetData.target);\n } else {\n queryProto = this.remoteSerializer.toQueryTarget(targetData.target);\n }\n\n // We can't store the resumeToken as a ByteString in IndexedDb, so we\n // convert it to a base64 string for storage.\n const resumeToken = targetData.resumeToken.toBase64();\n\n // lastListenSequenceNumber is always 0 until we do real GC.\n return new DbTarget(\n targetData.targetId,\n targetData.target.canonicalId(),\n dbTimestamp,\n resumeToken,\n targetData.sequenceNumber,\n dbLastLimboFreeTimestamp,\n queryProto\n );\n }\n}\n\n/**\n * A helper function for figuring out what kind of query has been stored.\n */\nfunction isDocumentQuery(dbQuery: DbQuery): dbQuery is api.DocumentsTarget {\n return (dbQuery as api.DocumentsTarget).documents !== undefined;\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 { DatabaseInfo } from '../core/database_info';\nimport { ListenSequence, SequenceNumberSyncer } from '../core/listen_sequence';\nimport { ListenSequenceNumber, TargetId } from '../core/types';\nimport { DocumentKey } from '../model/document_key';\nimport { Platform } from '../platform/platform';\nimport { JsonProtoSerializer } from '../remote/serializer';\nimport { debugAssert, fail } from '../util/assert';\nimport { AsyncQueue, DelayedOperation, TimerId } from '../util/async_queue';\nimport { Code, FirestoreError } from '../util/error';\nimport { logDebug, logError } from '../util/log';\nimport {\n decodeResourcePath,\n EncodedResourcePath,\n encodeResourcePath\n} from './encoded_resource_path';\nimport { IndexedDbIndexManager } from './indexeddb_index_manager';\nimport {\n IndexedDbMutationQueue,\n mutationQueuesContainKey\n} from './indexeddb_mutation_queue';\nimport { IndexedDbRemoteDocumentCache } from './indexeddb_remote_document_cache';\nimport {\n ALL_STORES,\n DbClientMetadata,\n DbClientMetadataKey,\n DbPrimaryClient,\n DbPrimaryClientKey,\n DbTargetDocument,\n SCHEMA_VERSION,\n SchemaConverter\n} from './indexeddb_schema';\nimport {\n documentTargetStore,\n IndexedDbTargetCache\n} from './indexeddb_target_cache';\nimport { LocalSerializer } from './local_serializer';\nimport {\n ActiveTargets,\n LruDelegate,\n LruGarbageCollector,\n LruParams\n} from './lru_garbage_collector';\nimport {\n Persistence,\n PersistenceTransaction,\n PersistenceTransactionMode,\n PRIMARY_LEASE_LOST_ERROR_MSG,\n PrimaryStateListener,\n ReferenceDelegate\n} from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { ClientId } from './shared_client_state';\nimport { TargetData } from './target_data';\nimport {\n isIndexedDbTransactionError,\n SimpleDb,\n SimpleDbStore,\n SimpleDbTransaction\n} from './simple_db';\n\nconst LOG_TAG = 'IndexedDbPersistence';\n\n/**\n * Oldest acceptable age in milliseconds for client metadata before the client\n * is considered inactive and its associated data is garbage collected.\n */\nconst MAX_CLIENT_AGE_MS = 30 * 60 * 1000; // 30 minutes\n\n/**\n * Oldest acceptable metadata age for clients that may participate in the\n * primary lease election. Clients that have not updated their client metadata\n * within 5 seconds are not eligible to receive a primary lease.\n */\nconst MAX_PRIMARY_ELIGIBLE_AGE_MS = 5000;\n\n/**\n * The interval at which clients will update their metadata, including\n * refreshing their primary lease if held or potentially trying to acquire it if\n * not held.\n *\n * Primary clients may opportunistically refresh their metadata earlier\n * if they're already performing an IndexedDB operation.\n */\nconst CLIENT_METADATA_REFRESH_INTERVAL_MS = 4000;\n/** User-facing error when the primary lease is required but not available. */\nconst PRIMARY_LEASE_EXCLUSIVE_ERROR_MSG =\n 'Failed to obtain exclusive access to the persistence layer. ' +\n 'To allow shared access, make sure to invoke ' +\n '`enablePersistence()` with `synchronizeTabs:true` in all tabs.';\nconst UNSUPPORTED_PLATFORM_ERROR_MSG =\n 'This platform is either missing' +\n ' IndexedDB or is known to have an incomplete implementation. Offline' +\n ' persistence has been disabled.';\n\n// The format of the LocalStorage key that stores zombied client is:\n// firestore_zombie_<persistence_prefix>_<instance_key>\nconst ZOMBIED_CLIENTS_KEY_PREFIX = 'firestore_zombie';\n\nexport class IndexedDbTransaction extends PersistenceTransaction {\n constructor(\n readonly simpleDbTransaction: SimpleDbTransaction,\n readonly currentSequenceNumber: ListenSequenceNumber\n ) {\n super();\n }\n}\n\n/**\n * An IndexedDB-backed instance of Persistence. Data is stored persistently\n * across sessions.\n *\n * On Web only, the Firestore SDKs support shared access to its persistence\n * layer. This allows multiple browser tabs to read and write to IndexedDb and\n * to synchronize state even without network connectivity. Shared access is\n * currently optional and not enabled unless all clients invoke\n * `enablePersistence()` with `{synchronizeTabs:true}`.\n *\n * In multi-tab mode, if multiple clients are active at the same time, the SDK\n * will designate one client as the “primary client”. An effort is made to pick\n * a visible, network-connected and active client, and this client is\n * responsible for letting other clients know about its presence. The primary\n * client writes a unique client-generated identifier (the client ID) to\n * IndexedDb’s “owner” store every 4 seconds. If the primary client fails to\n * update this entry, another client can acquire the lease and take over as\n * primary.\n *\n * Some persistence operations in the SDK are designated as primary-client only\n * operations. This includes the acknowledgment of mutations and all updates of\n * remote documents. The effects of these operations are written to persistence\n * and then broadcast to other tabs via LocalStorage (see\n * `WebStorageSharedClientState`), which then refresh their state from\n * persistence.\n *\n * Similarly, the primary client listens to notifications sent by secondary\n * clients to discover persistence changes written by secondary clients, such as\n * the addition of new mutations and query targets.\n *\n * If multi-tab is not enabled and another tab already obtained the primary\n * lease, IndexedDbPersistence enters a failed state and all subsequent\n * operations will automatically fail.\n *\n * Additionally, there is an optimization so that when a tab is closed, the\n * primary lease is released immediately (this is especially important to make\n * sure that a refreshed tab is able to immediately re-acquire the primary\n * lease). Unfortunately, IndexedDB cannot be reliably used in window.unload\n * since it is an asynchronous API. So in addition to attempting to give up the\n * lease, the leaseholder writes its client ID to a \"zombiedClient\" entry in\n * LocalStorage which acts as an indicator that another tab should go ahead and\n * take the primary lease immediately regardless of the current lease timestamp.\n *\n * TODO(b/114226234): Remove `synchronizeTabs` section when multi-tab is no\n * longer optional.\n */\nexport class IndexedDbPersistence implements Persistence {\n static getStore<Key extends IDBValidKey, Value>(\n txn: PersistenceTransaction,\n store: string\n ): SimpleDbStore<Key, Value> {\n if (txn instanceof IndexedDbTransaction) {\n return SimpleDb.getStore<Key, Value>(txn.simpleDbTransaction, store);\n } else {\n throw fail(\n 'IndexedDbPersistence must use instances of IndexedDbTransaction'\n );\n }\n }\n\n /**\n * The name of the main (and currently only) IndexedDB database. this name is\n * appended to the prefix provided to the IndexedDbPersistence constructor.\n */\n static MAIN_DATABASE = 'main';\n\n private readonly document: Document | null;\n private readonly window: Window;\n\n // Technically `simpleDb` should be `| undefined` because it is\n // initialized asynchronously by start(), but that would be more misleading\n // than useful.\n private simpleDb!: SimpleDb;\n\n private listenSequence: ListenSequence | null = null;\n\n private _started = false;\n private isPrimary = false;\n private networkEnabled = true;\n private dbName: string;\n\n /** Our window.unload handler, if registered. */\n private windowUnloadHandler: (() => void) | null = null;\n private inForeground = false;\n\n private serializer: LocalSerializer;\n\n /** Our 'visibilitychange' listener if registered. */\n private documentVisibilityHandler: ((e?: Event) => void) | null = null;\n\n /** The client metadata refresh task. */\n private clientMetadataRefresher: DelayedOperation<void> | null = null;\n\n /** The last time we garbage collected the client metadata object store. */\n private lastGarbageCollectionTime = Number.NEGATIVE_INFINITY;\n\n /** A listener to notify on primary state changes. */\n private primaryStateListener: PrimaryStateListener = _ => Promise.resolve();\n\n private readonly targetCache: IndexedDbTargetCache;\n private readonly indexManager: IndexedDbIndexManager;\n private readonly remoteDocumentCache: IndexedDbRemoteDocumentCache;\n private readonly webStorage: Storage;\n readonly referenceDelegate: IndexedDbLruDelegate;\n\n constructor(\n private readonly allowTabSynchronization: boolean,\n private readonly persistenceKey: string,\n private readonly clientId: ClientId,\n platform: Platform,\n lruParams: LruParams,\n private readonly queue: AsyncQueue,\n serializer: JsonProtoSerializer,\n private readonly sequenceNumberSyncer: SequenceNumberSyncer\n ) {\n if (!IndexedDbPersistence.isAvailable()) {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n UNSUPPORTED_PLATFORM_ERROR_MSG\n );\n }\n\n this.referenceDelegate = new IndexedDbLruDelegate(this, lruParams);\n this.dbName = persistenceKey + IndexedDbPersistence.MAIN_DATABASE;\n this.serializer = new LocalSerializer(serializer);\n this.document = platform.document;\n this.targetCache = new IndexedDbTargetCache(\n this.referenceDelegate,\n this.serializer\n );\n this.indexManager = new IndexedDbIndexManager();\n this.remoteDocumentCache = new IndexedDbRemoteDocumentCache(\n this.serializer,\n this.indexManager\n );\n if (platform.window && platform.window.localStorage) {\n this.window = platform.window;\n this.webStorage = this.window.localStorage;\n } else {\n throw new FirestoreError(\n Code.UNIMPLEMENTED,\n 'IndexedDB persistence is only available on platforms that support LocalStorage.'\n );\n }\n }\n\n /**\n * Attempt to start IndexedDb persistence.\n *\n * @return {Promise<void>} Whether persistence was enabled.\n */\n start(): Promise<void> {\n debugAssert(!this.started, 'IndexedDbPersistence double-started!');\n debugAssert(this.window !== null, \"Expected 'window' to be defined\");\n\n return SimpleDb.openOrCreate(\n this.dbName,\n SCHEMA_VERSION,\n new SchemaConverter(this.serializer)\n )\n .then(db => {\n this.simpleDb = db;\n // NOTE: This is expected to fail sometimes (in the case of another tab already\n // having the persistence lock), so it's the first thing we should do.\n return this.updateClientMetadataAndTryBecomePrimary();\n })\n .then(() => {\n if (!this.isPrimary && !this.allowTabSynchronization) {\n // Fail `start()` if `synchronizeTabs` is disabled and we cannot\n // obtain the primary lease.\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n PRIMARY_LEASE_EXCLUSIVE_ERROR_MSG\n );\n }\n this.attachVisibilityHandler();\n this.attachWindowUnloadHook();\n\n this.scheduleClientMetadataAndPrimaryLeaseRefreshes();\n\n return this.runTransaction(\n 'getHighestListenSequenceNumber',\n 'readonly',\n txn => this.targetCache.getHighestSequenceNumber(txn)\n );\n })\n .then(highestListenSequenceNumber => {\n this.listenSequence = new ListenSequence(\n highestListenSequenceNumber,\n this.sequenceNumberSyncer\n );\n })\n .then(() => {\n this._started = true;\n })\n .catch(reason => {\n this.simpleDb && this.simpleDb.close();\n return Promise.reject(reason);\n });\n }\n\n /**\n * Registers a listener that gets called when the primary state of the\n * instance changes. Upon registering, this listener is invoked immediately\n * with the current primary state.\n *\n * PORTING NOTE: This is only used for Web multi-tab.\n */\n setPrimaryStateListener(\n primaryStateListener: PrimaryStateListener\n ): Promise<void> {\n this.primaryStateListener = async primaryState => {\n if (this.started) {\n return primaryStateListener(primaryState);\n }\n };\n return primaryStateListener(this.isPrimary);\n }\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 this.simpleDb.setVersionChangeListener(async event => {\n // Check if an attempt is made to delete IndexedDB.\n if (event.newVersion === null) {\n await databaseDeletedListener();\n }\n });\n }\n\n /**\n * Adjusts the current network state in the client's metadata, potentially\n * affecting the primary lease.\n *\n * PORTING NOTE: This is only used for Web multi-tab.\n */\n setNetworkEnabled(networkEnabled: boolean): void {\n if (this.networkEnabled !== networkEnabled) {\n this.networkEnabled = networkEnabled;\n // Schedule a primary lease refresh for immediate execution. The eventual\n // lease update will be propagated via `primaryStateListener`.\n this.queue.enqueueAndForget(async () => {\n if (this.started) {\n await this.updateClientMetadataAndTryBecomePrimary();\n }\n });\n }\n }\n\n /**\n * Updates the client metadata in IndexedDb and attempts to either obtain or\n * extend the primary lease for the local client. Asynchronously notifies the\n * primary state listener if the client either newly obtained or released its\n * primary lease.\n */\n private updateClientMetadataAndTryBecomePrimary(): Promise<void> {\n return this.runTransaction(\n 'updateClientMetadataAndTryBecomePrimary',\n 'readwrite',\n txn => {\n const metadataStore = clientMetadataStore(txn);\n return metadataStore\n .put(\n new DbClientMetadata(\n this.clientId,\n Date.now(),\n this.networkEnabled,\n this.inForeground\n )\n )\n .next(() => {\n if (this.isPrimary) {\n return this.verifyPrimaryLease(txn).next(success => {\n if (!success) {\n this.isPrimary = false;\n this.queue.enqueueAndForget(() =>\n this.primaryStateListener(false)\n );\n }\n });\n }\n })\n .next(() => this.canActAsPrimary(txn))\n .next(canActAsPrimary => {\n if (this.isPrimary && !canActAsPrimary) {\n return this.releasePrimaryLeaseIfHeld(txn).next(() => false);\n } else if (canActAsPrimary) {\n return this.acquireOrExtendPrimaryLease(txn).next(() => true);\n } else {\n return /* canActAsPrimary= */ false;\n }\n });\n }\n )\n .catch(e => {\n if (!this.allowTabSynchronization) {\n if (isIndexedDbTransactionError(e)) {\n logDebug(LOG_TAG, 'Failed to extend owner lease: ', e);\n // Proceed with the existing state. Any subsequent access to\n // IndexedDB will verify the lease.\n return this.isPrimary;\n } else {\n throw e;\n }\n }\n\n logDebug(\n LOG_TAG,\n 'Releasing owner lease after error during lease refresh',\n e\n );\n return /* isPrimary= */ false;\n })\n .then(isPrimary => {\n if (this.isPrimary !== isPrimary) {\n this.queue.enqueueAndForget(() =>\n this.primaryStateListener(isPrimary)\n );\n }\n this.isPrimary = isPrimary;\n });\n }\n\n private verifyPrimaryLease(\n txn: PersistenceTransaction\n ): PersistencePromise<boolean> {\n const store = primaryClientStore(txn);\n return store.get(DbPrimaryClient.key).next(primaryClient => {\n return PersistencePromise.resolve(this.isLocalClient(primaryClient));\n });\n }\n\n private removeClientMetadata(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n const metadataStore = clientMetadataStore(txn);\n return metadataStore.delete(this.clientId);\n }\n\n /**\n * If the garbage collection threshold has passed, prunes the\n * RemoteDocumentChanges and the ClientMetadata store based on the last update\n * time of all clients.\n */\n private async maybeGarbageCollectMultiClientState(): Promise<void> {\n if (\n this.isPrimary &&\n !this.isWithinAge(this.lastGarbageCollectionTime, MAX_CLIENT_AGE_MS)\n ) {\n this.lastGarbageCollectionTime = Date.now();\n\n const inactiveClients = await this.runTransaction(\n 'maybeGarbageCollectMultiClientState',\n 'readwrite-primary',\n txn => {\n const metadataStore = IndexedDbPersistence.getStore<\n DbClientMetadataKey,\n DbClientMetadata\n >(txn, DbClientMetadata.store);\n\n return metadataStore.loadAll().next(existingClients => {\n const active = this.filterActiveClients(\n existingClients,\n MAX_CLIENT_AGE_MS\n );\n const inactive = existingClients.filter(\n client => active.indexOf(client) === -1\n );\n\n // Delete metadata for clients that are no longer considered active.\n return PersistencePromise.forEach(\n inactive,\n (inactiveClient: DbClientMetadata) =>\n metadataStore.delete(inactiveClient.clientId)\n ).next(() => inactive);\n });\n }\n ).catch(() => {\n // Ignore primary lease violations or any other type of error. The next\n // primary will run `maybeGarbageCollectMultiClientState()` again.\n // We don't use `ignoreIfPrimaryLeaseLoss()` since we don't want to depend\n // on LocalStore.\n return [];\n });\n\n // Delete potential leftover entries that may continue to mark the\n // inactive clients as zombied in LocalStorage.\n // Ideally we'd delete the IndexedDb and LocalStorage zombie entries for\n // the client atomically, but we can't. So we opt to delete the IndexedDb\n // entries first to avoid potentially reviving a zombied client.\n inactiveClients.forEach(inactiveClient => {\n this.window.localStorage.removeItem(\n this.zombiedClientLocalStorageKey(inactiveClient.clientId)\n );\n });\n }\n }\n\n /**\n * Schedules a recurring timer to update the client metadata and to either\n * extend or acquire the primary lease if the client is eligible.\n */\n private scheduleClientMetadataAndPrimaryLeaseRefreshes(): void {\n this.clientMetadataRefresher = this.queue.enqueueAfterDelay(\n TimerId.ClientMetadataRefresh,\n CLIENT_METADATA_REFRESH_INTERVAL_MS,\n () => {\n return this.updateClientMetadataAndTryBecomePrimary()\n .then(() => this.maybeGarbageCollectMultiClientState())\n .then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());\n }\n );\n }\n\n /** Checks whether `client` is the local client. */\n private isLocalClient(client: DbPrimaryClient | null): boolean {\n return client ? client.ownerId === this.clientId : false;\n }\n\n /**\n * Evaluate the state of all active clients and determine whether the local\n * client is or can act as the holder of the primary lease. Returns whether\n * the client is eligible for the lease, but does not actually acquire it.\n * May return 'false' even if there is no active leaseholder and another\n * (foreground) client should become leaseholder instead.\n */\n private canActAsPrimary(\n txn: PersistenceTransaction\n ): PersistencePromise<boolean> {\n const store = primaryClientStore(txn);\n return store\n .get(DbPrimaryClient.key)\n .next(currentPrimary => {\n const currentLeaseIsValid =\n currentPrimary !== null &&\n this.isWithinAge(\n currentPrimary.leaseTimestampMs,\n MAX_PRIMARY_ELIGIBLE_AGE_MS\n ) &&\n !this.isClientZombied(currentPrimary.ownerId);\n\n // A client is eligible for the primary lease if:\n // - its network is enabled and the client's tab is in the foreground.\n // - its network is enabled and no other client's tab is in the\n // foreground.\n // - every clients network is disabled and the client's tab is in the\n // foreground.\n // - every clients network is disabled and no other client's tab is in\n // the foreground.\n if (currentLeaseIsValid) {\n if (this.isLocalClient(currentPrimary) && this.networkEnabled) {\n return true;\n }\n\n if (!this.isLocalClient(currentPrimary)) {\n if (!currentPrimary!.allowTabSynchronization) {\n // Fail the `canActAsPrimary` check if the current leaseholder has\n // not opted into multi-tab synchronization. If this happens at\n // client startup, we reject the Promise returned by\n // `enablePersistence()` and the user can continue to use Firestore\n // with in-memory persistence.\n // If this fails during a lease refresh, we will instead block the\n // AsyncQueue from executing further operations. Note that this is\n // acceptable since mixing & matching different `synchronizeTabs`\n // settings is not supported.\n //\n // TODO(b/114226234): Remove this check when `synchronizeTabs` can\n // no longer be turned off.\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n PRIMARY_LEASE_EXCLUSIVE_ERROR_MSG\n );\n }\n\n return false;\n }\n }\n\n if (this.networkEnabled && this.inForeground) {\n return true;\n }\n\n return clientMetadataStore(txn)\n .loadAll()\n .next(existingClients => {\n // Process all existing clients and determine whether at least one of\n // them is better suited to obtain the primary lease.\n const preferredCandidate = this.filterActiveClients(\n existingClients,\n MAX_PRIMARY_ELIGIBLE_AGE_MS\n ).find(otherClient => {\n if (this.clientId !== otherClient.clientId) {\n const otherClientHasBetterNetworkState =\n !this.networkEnabled && otherClient.networkEnabled;\n const otherClientHasBetterVisibility =\n !this.inForeground && otherClient.inForeground;\n const otherClientHasSameNetworkState =\n this.networkEnabled === otherClient.networkEnabled;\n if (\n otherClientHasBetterNetworkState ||\n (otherClientHasBetterVisibility &&\n otherClientHasSameNetworkState)\n ) {\n return true;\n }\n }\n return false;\n });\n return preferredCandidate === undefined;\n });\n })\n .next(canActAsPrimary => {\n if (this.isPrimary !== canActAsPrimary) {\n logDebug(\n LOG_TAG,\n `Client ${\n canActAsPrimary ? 'is' : 'is not'\n } eligible for a primary lease.`\n );\n }\n return canActAsPrimary;\n });\n }\n\n async shutdown(): Promise<void> {\n // The shutdown() operations are idempotent and can be called even when\n // start() aborted (e.g. because it couldn't acquire the persistence lease).\n this._started = false;\n\n this.markClientZombied();\n if (this.clientMetadataRefresher) {\n this.clientMetadataRefresher.cancel();\n this.clientMetadataRefresher = null;\n }\n this.detachVisibilityHandler();\n this.detachWindowUnloadHook();\n await this.runTransaction('shutdown', 'readwrite', txn => {\n return this.releasePrimaryLeaseIfHeld(txn).next(() =>\n this.removeClientMetadata(txn)\n );\n }).catch(e => {\n logDebug(LOG_TAG, 'Proceeding with shutdown despite failure: ', e);\n });\n this.simpleDb.close();\n\n // Remove the entry marking the client as zombied from LocalStorage since\n // we successfully deleted its metadata from IndexedDb.\n this.removeClientZombiedEntry();\n }\n\n /**\n * Returns clients that are not zombied and have an updateTime within the\n * provided threshold.\n */\n private filterActiveClients(\n clients: DbClientMetadata[],\n activityThresholdMs: number\n ): DbClientMetadata[] {\n return clients.filter(\n client =>\n this.isWithinAge(client.updateTimeMs, activityThresholdMs) &&\n !this.isClientZombied(client.clientId)\n );\n }\n\n /**\n * Returns the IDs of the clients that are currently active. If multi-tab\n * is not supported, returns an array that only contains the local client's\n * ID.\n *\n * PORTING NOTE: This is only used for Web multi-tab.\n */\n getActiveClients(): Promise<ClientId[]> {\n return this.runTransaction('getActiveClients', 'readonly', txn => {\n return clientMetadataStore(txn)\n .loadAll()\n .next(clients =>\n this.filterActiveClients(clients, MAX_CLIENT_AGE_MS).map(\n clientMetadata => clientMetadata.clientId\n )\n );\n });\n }\n\n static async clearPersistence(persistenceKey: string): Promise<void> {\n if (!IndexedDbPersistence.isAvailable()) {\n return Promise.resolve();\n }\n const dbName = persistenceKey + IndexedDbPersistence.MAIN_DATABASE;\n await SimpleDb.delete(dbName);\n }\n\n get started(): boolean {\n return this._started;\n }\n\n getMutationQueue(user: User): IndexedDbMutationQueue {\n debugAssert(\n this.started,\n 'Cannot initialize MutationQueue before persistence is started.'\n );\n return IndexedDbMutationQueue.forUser(\n user,\n this.serializer,\n this.indexManager,\n this.referenceDelegate\n );\n }\n\n getTargetCache(): IndexedDbTargetCache {\n debugAssert(\n this.started,\n 'Cannot initialize TargetCache before persistence is started.'\n );\n return this.targetCache;\n }\n\n getRemoteDocumentCache(): IndexedDbRemoteDocumentCache {\n debugAssert(\n this.started,\n 'Cannot initialize RemoteDocumentCache before persistence is started.'\n );\n return this.remoteDocumentCache;\n }\n\n getIndexManager(): IndexedDbIndexManager {\n debugAssert(\n this.started,\n 'Cannot initialize IndexManager before persistence is started.'\n );\n return this.indexManager;\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\n const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';\n\n let persistenceTransaction: PersistenceTransaction;\n\n // Do all transactions as readwrite against all object stores, since we\n // are the only reader/writer.\n return this.simpleDb\n .runTransaction(simpleDbMode, ALL_STORES, simpleDbTxn => {\n persistenceTransaction = new IndexedDbTransaction(\n simpleDbTxn,\n this.listenSequence\n ? this.listenSequence.next()\n : ListenSequence.INVALID\n );\n\n if (mode === 'readwrite-primary') {\n // While we merely verify that we have (or can acquire) the lease\n // immediately, we wait to extend the primary lease until after\n // executing transactionOperation(). This ensures that even if the\n // transactionOperation takes a long time, we'll use a recent\n // leaseTimestampMs in the extended (or newly acquired) lease.\n return this.verifyPrimaryLease(persistenceTransaction)\n .next(holdsPrimaryLease => {\n if (holdsPrimaryLease) {\n return /* holdsPrimaryLease= */ true;\n }\n return this.canActAsPrimary(persistenceTransaction);\n })\n .next(holdsPrimaryLease => {\n if (!holdsPrimaryLease) {\n logError(\n `Failed to obtain primary lease for action '${action}'.`\n );\n this.isPrimary = false;\n this.queue.enqueueAndForget(() =>\n this.primaryStateListener(false)\n );\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n PRIMARY_LEASE_LOST_ERROR_MSG\n );\n }\n return transactionOperation(persistenceTransaction);\n })\n .next(result => {\n return this.acquireOrExtendPrimaryLease(\n persistenceTransaction\n ).next(() => result);\n });\n } else {\n return this.verifyAllowTabSynchronization(\n persistenceTransaction\n ).next(() => transactionOperation(persistenceTransaction));\n }\n })\n .then(result => {\n persistenceTransaction.raiseOnCommittedEvent();\n return result;\n });\n }\n\n /**\n * Verifies that the current tab is the primary leaseholder or alternatively\n * that the leaseholder has opted into multi-tab synchronization.\n */\n // TODO(b/114226234): Remove this check when `synchronizeTabs` can no longer\n // be turned off.\n private verifyAllowTabSynchronization(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n const store = primaryClientStore(txn);\n return store.get(DbPrimaryClient.key).next(currentPrimary => {\n const currentLeaseIsValid =\n currentPrimary !== null &&\n this.isWithinAge(\n currentPrimary.leaseTimestampMs,\n MAX_PRIMARY_ELIGIBLE_AGE_MS\n ) &&\n !this.isClientZombied(currentPrimary.ownerId);\n\n if (currentLeaseIsValid && !this.isLocalClient(currentPrimary)) {\n if (\n !this.allowTabSynchronization ||\n !currentPrimary!.allowTabSynchronization\n ) {\n throw new FirestoreError(\n Code.FAILED_PRECONDITION,\n PRIMARY_LEASE_EXCLUSIVE_ERROR_MSG\n );\n }\n }\n });\n }\n\n /**\n * Obtains or extends the new primary lease for the local client. This\n * method does not verify that the client is eligible for this lease.\n */\n private acquireOrExtendPrimaryLease(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n const newPrimary = new DbPrimaryClient(\n this.clientId,\n this.allowTabSynchronization,\n Date.now()\n );\n return primaryClientStore(txn).put(DbPrimaryClient.key, newPrimary);\n }\n\n static isAvailable(): boolean {\n return SimpleDb.isAvailable();\n }\n\n /**\n * Generates a string used as a prefix when storing data in IndexedDB and\n * LocalStorage.\n */\n static buildStoragePrefix(databaseInfo: DatabaseInfo): string {\n // Use two different prefix formats:\n //\n // * firestore / persistenceKey / projectID . databaseID / ...\n // * firestore / persistenceKey / projectID / ...\n //\n // projectIDs are DNS-compatible names and cannot contain dots\n // so there's no danger of collisions.\n let database = databaseInfo.databaseId.projectId;\n if (!databaseInfo.databaseId.isDefaultDatabase) {\n database += '.' + databaseInfo.databaseId.database;\n }\n\n return 'firestore/' + databaseInfo.persistenceKey + '/' + database + '/';\n }\n\n /** Checks the primary lease and removes it if we are the current primary. */\n private releasePrimaryLeaseIfHeld(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n const store = primaryClientStore(txn);\n return store.get(DbPrimaryClient.key).next(primaryClient => {\n if (this.isLocalClient(primaryClient)) {\n logDebug(LOG_TAG, 'Releasing primary lease.');\n return store.delete(DbPrimaryClient.key);\n } else {\n return PersistencePromise.resolve();\n }\n });\n }\n\n /** Verifies that `updateTimeMs` is within `maxAgeMs`. */\n private isWithinAge(updateTimeMs: number, maxAgeMs: number): boolean {\n const now = Date.now();\n const minAcceptable = now - maxAgeMs;\n const maxAcceptable = now;\n if (updateTimeMs < minAcceptable) {\n return false;\n } else if (updateTimeMs > maxAcceptable) {\n logError(\n `Detected an update time that is in the future: ${updateTimeMs} > ${maxAcceptable}`\n );\n return false;\n }\n\n return true;\n }\n\n private attachVisibilityHandler(): void {\n if (\n this.document !== null &&\n typeof this.document.addEventListener === 'function'\n ) {\n this.documentVisibilityHandler = () => {\n this.queue.enqueueAndForget(() => {\n this.inForeground = this.document!.visibilityState === 'visible';\n return this.updateClientMetadataAndTryBecomePrimary();\n });\n };\n\n this.document.addEventListener(\n 'visibilitychange',\n this.documentVisibilityHandler\n );\n\n this.inForeground = this.document.visibilityState === 'visible';\n }\n }\n\n private detachVisibilityHandler(): void {\n if (this.documentVisibilityHandler) {\n debugAssert(\n this.document !== null &&\n typeof this.document.addEventListener === 'function',\n \"Expected 'document.addEventListener' to be a function\"\n );\n this.document.removeEventListener(\n 'visibilitychange',\n this.documentVisibilityHandler\n );\n this.documentVisibilityHandler = null;\n }\n }\n\n /**\n * Attaches a window.unload handler that will synchronously write our\n * clientId to a \"zombie client id\" location in LocalStorage. This can be used\n * by tabs trying to acquire the primary lease to determine that the lease\n * is no longer valid even if the timestamp is recent. This is particularly\n * important for the refresh case (so the tab correctly re-acquires the\n * primary lease). LocalStorage is used for this rather than IndexedDb because\n * it is a synchronous API and so can be used reliably from an unload\n * handler.\n */\n private attachWindowUnloadHook(): void {\n if (typeof this.window.addEventListener === 'function') {\n this.windowUnloadHandler = () => {\n // Note: In theory, this should be scheduled on the AsyncQueue since it\n // accesses internal state. We execute this code directly during shutdown\n // to make sure it gets a chance to run.\n this.markClientZombied();\n\n this.queue.enqueueAndForget(() => {\n // Attempt graceful shutdown (including releasing our primary lease),\n // but there's no guarantee it will complete.\n return this.shutdown();\n });\n };\n this.window.addEventListener('unload', this.windowUnloadHandler);\n }\n }\n\n private detachWindowUnloadHook(): void {\n if (this.windowUnloadHandler) {\n debugAssert(\n typeof this.window.removeEventListener === 'function',\n \"Expected 'window.removeEventListener' to be a function\"\n );\n this.window.removeEventListener('unload', this.windowUnloadHandler);\n this.windowUnloadHandler = null;\n }\n }\n\n /**\n * Returns whether a client is \"zombied\" based on its LocalStorage entry.\n * Clients become zombied when their tab closes without running all of the\n * cleanup logic in `shutdown()`.\n */\n private isClientZombied(clientId: ClientId): boolean {\n try {\n const isZombied =\n this.webStorage.getItem(this.zombiedClientLocalStorageKey(clientId)) !==\n null;\n logDebug(\n LOG_TAG,\n `Client '${clientId}' ${\n isZombied ? 'is' : 'is not'\n } zombied in LocalStorage`\n );\n return isZombied;\n } catch (e) {\n // Gracefully handle if LocalStorage isn't working.\n logError(LOG_TAG, 'Failed to get zombied client id.', e);\n return false;\n }\n }\n\n /**\n * Record client as zombied (a client that had its tab closed). Zombied\n * clients are ignored during primary tab selection.\n */\n private markClientZombied(): void {\n try {\n this.webStorage.setItem(\n this.zombiedClientLocalStorageKey(this.clientId),\n String(Date.now())\n );\n } catch (e) {\n // Gracefully handle if LocalStorage isn't available / working.\n logError('Failed to set zombie client id.', e);\n }\n }\n\n /** Removes the zombied client entry if it exists. */\n private removeClientZombiedEntry(): void {\n try {\n this.webStorage.removeItem(\n this.zombiedClientLocalStorageKey(this.clientId)\n );\n } catch (e) {\n // Ignore\n }\n }\n\n private zombiedClientLocalStorageKey(clientId: ClientId): string {\n return `${ZOMBIED_CLIENTS_KEY_PREFIX}_${this.persistenceKey}_${clientId}`;\n }\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the primary client object store.\n */\nfunction primaryClientStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbPrimaryClientKey, DbPrimaryClient> {\n return IndexedDbPersistence.getStore<DbPrimaryClientKey, DbPrimaryClient>(\n txn,\n DbPrimaryClient.store\n );\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the client metadata object store.\n */\nfunction clientMetadataStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbClientMetadataKey, DbClientMetadata> {\n return IndexedDbPersistence.getStore<DbClientMetadataKey, DbClientMetadata>(\n txn,\n DbClientMetadata.store\n );\n}\n\n/** Provides LRU functionality for IndexedDB persistence. */\nexport class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {\n readonly garbageCollector: LruGarbageCollector;\n\n constructor(private readonly db: IndexedDbPersistence, params: LruParams) {\n this.garbageCollector = new LruGarbageCollector(this, params);\n }\n\n getSequenceNumberCount(\n txn: PersistenceTransaction\n ): PersistencePromise<number> {\n const docCountPromise = this.orphanedDocumentCount(txn);\n const targetCountPromise = this.db.getTargetCache().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 forEachTarget(\n txn: PersistenceTransaction,\n f: (q: TargetData) => void\n ): PersistencePromise<void> {\n return this.db.getTargetCache().forEachTarget(txn, f);\n }\n\n forEachOrphanedDocumentSequenceNumber(\n txn: PersistenceTransaction,\n f: (sequenceNumber: ListenSequenceNumber) => void\n ): PersistencePromise<void> {\n return this.forEachOrphanedDocument(txn, (docKey, sequenceNumber) =>\n f(sequenceNumber)\n );\n }\n\n addReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n return writeSentinelKey(txn, key);\n }\n\n removeReference(\n txn: PersistenceTransaction,\n targetId: TargetId,\n key: DocumentKey\n ): PersistencePromise<void> {\n return writeSentinelKey(txn, key);\n }\n\n removeTargets(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber,\n activeTargetIds: ActiveTargets\n ): PersistencePromise<number> {\n return this.db\n .getTargetCache()\n .removeTargets(txn, upperBound, activeTargetIds);\n }\n\n markPotentiallyOrphaned(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n return writeSentinelKey(txn, key);\n }\n\n /**\n * Returns true if anything would prevent this document from being garbage\n * collected, given that the document in question is not present in any\n * targets and has a sequence number less than or equal to the upper bound for\n * the collection run.\n */\n private isPinned(\n txn: PersistenceTransaction,\n docKey: DocumentKey\n ): PersistencePromise<boolean> {\n return mutationQueuesContainKey(txn, docKey);\n }\n\n removeOrphanedDocuments(\n txn: PersistenceTransaction,\n upperBound: ListenSequenceNumber\n ): PersistencePromise<number> {\n const documentCache = this.db.getRemoteDocumentCache();\n const changeBuffer = documentCache.newChangeBuffer();\n\n const promises: Array<PersistencePromise<void>> = [];\n let documentCount = 0;\n\n const iteration = this.forEachOrphanedDocument(\n txn,\n (docKey, sequenceNumber) => {\n if (sequenceNumber <= upperBound) {\n const p = this.isPinned(txn, docKey).next(isPinned => {\n if (!isPinned) {\n documentCount++;\n // Our size accounting requires us to read all documents before\n // removing them.\n return changeBuffer.getEntry(txn, docKey).next(() => {\n changeBuffer.removeEntry(docKey);\n return documentTargetStore(txn).delete(sentinelKey(docKey));\n });\n }\n });\n promises.push(p);\n }\n }\n );\n\n return iteration\n .next(() => PersistencePromise.waitFor(promises))\n .next(() => changeBuffer.apply(txn))\n .next(() => documentCount);\n }\n\n removeTarget(\n txn: PersistenceTransaction,\n targetData: TargetData\n ): PersistencePromise<void> {\n const updated = targetData.withSequenceNumber(txn.currentSequenceNumber);\n return this.db.getTargetCache().updateTargetData(txn, updated);\n }\n\n updateLimboDocument(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<void> {\n return writeSentinelKey(txn, key);\n }\n\n /**\n * Call provided function for each document in the cache that is 'orphaned'. Orphaned\n * means not a part of any target, so the only entry in the target-document index for\n * that document will be the sentinel row (targetId 0), which will also have the sequence\n * number for the last time the document was accessed.\n */\n private forEachOrphanedDocument(\n txn: PersistenceTransaction,\n f: (docKey: DocumentKey, sequenceNumber: ListenSequenceNumber) => void\n ): PersistencePromise<void> {\n const store = documentTargetStore(txn);\n let nextToReport: ListenSequenceNumber = ListenSequence.INVALID;\n let nextPath: EncodedResourcePath;\n return store\n .iterate(\n {\n index: DbTargetDocument.documentTargetsIndex\n },\n ([targetId, docKey], { path, sequenceNumber }) => {\n if (targetId === 0) {\n // if nextToReport is valid, report it, this is a new key so the\n // last one must not be a member of any targets.\n if (nextToReport !== ListenSequence.INVALID) {\n f(new DocumentKey(decodeResourcePath(nextPath)), nextToReport);\n }\n // set nextToReport to be this sequence number. It's the next one we\n // might report, if we don't find any targets for this document.\n // Note that the sequence number must be defined when the targetId\n // is 0.\n nextToReport = sequenceNumber!;\n nextPath = path;\n } else {\n // set nextToReport to be invalid, we know we don't need to report\n // this one since we found a target for it.\n nextToReport = ListenSequence.INVALID;\n }\n }\n )\n .next(() => {\n // Since we report sequence numbers after getting to the next key, we\n // need to check if the last key we iterated over was an orphaned\n // document and report it.\n if (nextToReport !== ListenSequence.INVALID) {\n f(new DocumentKey(decodeResourcePath(nextPath)), nextToReport);\n }\n });\n }\n\n getCacheSize(txn: PersistenceTransaction): PersistencePromise<number> {\n return this.db.getRemoteDocumentCache().getSize(txn);\n }\n}\n\nfunction sentinelKey(key: DocumentKey): [TargetId, EncodedResourcePath] {\n return [0, encodeResourcePath(key.path)];\n}\n\n/**\n * @return A value suitable for writing a sentinel row in the target-document\n * store.\n */\nfunction sentinelRow(\n key: DocumentKey,\n sequenceNumber: ListenSequenceNumber\n): DbTargetDocument {\n return new DbTargetDocument(0, encodeResourcePath(key.path), sequenceNumber);\n}\n\nfunction writeSentinelKey(\n txn: PersistenceTransaction,\n key: DocumentKey\n): PersistencePromise<void> {\n return documentTargetStore(txn).put(\n sentinelRow(key, txn.currentSequenceNumber)\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 { BatchId } from '../core/types';\nimport { DocumentKeySet } from '../model/collections';\nimport { DocumentKey } from '../model/document_key';\nimport { Mutation } from '../model/mutation';\nimport { BATCHID_UNKNOWN, MutationBatch } from '../model/mutation_batch';\nimport { ResourcePath } from '../model/path';\nimport { debugAssert, fail, 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';\nimport { decodeResourcePath } from './encoded_resource_path';\nimport { IndexManager } from './index_manager';\nimport {\n IndexedDbPersistence,\n IndexedDbTransaction\n} from './indexeddb_persistence';\nimport {\n DbDocumentMutation,\n DbDocumentMutationKey,\n DbMutationBatch,\n DbMutationBatchKey,\n DbMutationQueue,\n DbMutationQueueKey\n} from './indexeddb_schema';\nimport { LocalSerializer } from './local_serializer';\nimport { MutationQueue } from './mutation_queue';\nimport { PersistenceTransaction, ReferenceDelegate } from './persistence';\nimport { PersistencePromise } from './persistence_promise';\nimport { SimpleDbStore, SimpleDbTransaction } from './simple_db';\n\n/** A mutation queue for a specific user, backed by IndexedDB. */\nexport class IndexedDbMutationQueue implements MutationQueue {\n /**\n * Caches the document keys for pending mutation batches. If the mutation\n * has been removed from IndexedDb, the cached value may continue to\n * be used to retrieve the batch's document keys. To remove a cached value\n * locally, `removeCachedMutationKeys()` should be invoked either directly\n * or through `removeMutationBatches()`.\n *\n * With multi-tab, when the primary client acknowledges or rejects a mutation,\n * this cache is used by secondary clients to invalidate the local\n * view of the documents that were previously affected by the mutation.\n */\n // PORTING NOTE: Multi-tab only.\n private documentKeysByBatchId = {} as { [batchId: number]: DocumentKeySet };\n\n constructor(\n /**\n * The normalized userId (e.g. null UID => \"\" userId) used to store /\n * retrieve mutations.\n */\n private userId: string,\n private readonly serializer: LocalSerializer,\n private readonly indexManager: IndexManager,\n private readonly referenceDelegate: ReferenceDelegate\n ) {}\n\n /**\n * Creates a new mutation queue for the given user.\n * @param user The user for which to create a mutation queue.\n * @param serializer The serializer to use when persisting to IndexedDb.\n */\n static forUser(\n user: User,\n serializer: LocalSerializer,\n indexManager: IndexManager,\n referenceDelegate: ReferenceDelegate\n ): IndexedDbMutationQueue {\n // TODO(mcg): Figure out what constraints there are on userIDs\n // In particular, are there any reserved characters? are empty ids allowed?\n // For the moment store these together in the same mutations table assuming\n // that empty userIDs aren't allowed.\n hardAssert(user.uid !== '', 'UserID must not be an empty string.');\n const userId = user.isAuthenticated() ? user.uid! : '';\n return new IndexedDbMutationQueue(\n userId,\n serializer,\n indexManager,\n referenceDelegate\n );\n }\n\n checkEmpty(transaction: PersistenceTransaction): PersistencePromise<boolean> {\n let empty = true;\n const range = IDBKeyRange.bound(\n [this.userId, Number.NEGATIVE_INFINITY],\n [this.userId, Number.POSITIVE_INFINITY]\n );\n return mutationsStore(transaction)\n .iterate(\n { index: DbMutationBatch.userMutationsIndex, range },\n (key, value, control) => {\n empty = false;\n control.done();\n }\n )\n .next(() => empty);\n }\n\n acknowledgeBatch(\n transaction: PersistenceTransaction,\n batch: MutationBatch,\n streamToken: ByteString\n ): PersistencePromise<void> {\n return this.getMutationQueueMetadata(transaction).next(metadata => {\n // We can't store the resumeToken as a ByteString in IndexedDB, so we\n // convert it to a Base64 string for storage.\n metadata.lastStreamToken = streamToken.toBase64();\n\n return mutationQueuesStore(transaction).put(metadata);\n });\n }\n\n getLastStreamToken(\n transaction: PersistenceTransaction\n ): PersistencePromise<ByteString> {\n return this.getMutationQueueMetadata(transaction).next<ByteString>(\n metadata => ByteString.fromBase64String(metadata.lastStreamToken)\n );\n }\n\n setLastStreamToken(\n transaction: PersistenceTransaction,\n streamToken: ByteString\n ): PersistencePromise<void> {\n return this.getMutationQueueMetadata(transaction).next(metadata => {\n // We can't store the resumeToken as a ByteString in IndexedDB, so we\n // convert it to a Base64 string for storage.\n metadata.lastStreamToken = streamToken.toBase64();\n return mutationQueuesStore(transaction).put(metadata);\n });\n }\n\n addMutationBatch(\n transaction: PersistenceTransaction,\n localWriteTime: Timestamp,\n baseMutations: Mutation[],\n mutations: Mutation[]\n ): PersistencePromise<MutationBatch> {\n const documentStore = documentMutationsStore(transaction);\n const mutationStore = mutationsStore(transaction);\n\n // The IndexedDb implementation in Chrome (and Firefox) does not handle\n // compound indices that include auto-generated keys correctly. To ensure\n // that the index entry is added correctly in all browsers, we perform two\n // writes: The first write is used to retrieve the next auto-generated Batch\n // ID, and the second write populates the index and stores the actual\n // mutation batch.\n // See: https://bugs.chromium.org/p/chromium/issues/detail?id=701972\n\n // We write an empty object to obtain key\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return mutationStore.add({} as any).next(batchId => {\n hardAssert(\n typeof batchId === 'number',\n 'Auto-generated key is not a number'\n );\n\n const batch = new MutationBatch(\n batchId,\n localWriteTime,\n baseMutations,\n mutations\n );\n const dbBatch = this.serializer.toDbMutationBatch(this.userId, batch);\n\n const promises: Array<PersistencePromise<void>> = [];\n let collectionParents = new SortedSet<ResourcePath>((l, r) =>\n primitiveComparator(l.canonicalString(), r.canonicalString())\n );\n for (const mutation of mutations) {\n const indexKey = DbDocumentMutation.key(\n this.userId,\n mutation.key.path,\n batchId\n );\n collectionParents = collectionParents.add(mutation.key.path.popLast());\n promises.push(mutationStore.put(dbBatch));\n promises.push(\n documentStore.put(indexKey, DbDocumentMutation.PLACEHOLDER)\n );\n }\n\n collectionParents.forEach(parent => {\n promises.push(\n this.indexManager.addToCollectionParentIndex(transaction, parent)\n );\n });\n\n transaction.addOnCommittedListener(() => {\n this.documentKeysByBatchId[batchId] = batch.keys();\n });\n\n return PersistencePromise.waitFor(promises).next(() => batch);\n });\n }\n\n lookupMutationBatch(\n transaction: PersistenceTransaction,\n batchId: BatchId\n ): PersistencePromise<MutationBatch | null> {\n return mutationsStore(transaction)\n .get(batchId)\n .next(dbBatch => {\n if (dbBatch) {\n hardAssert(\n dbBatch.userId === this.userId,\n `Unexpected user '${dbBatch.userId}' for mutation batch ${batchId}`\n );\n return this.serializer.fromDbMutationBatch(dbBatch);\n }\n return null;\n });\n }\n\n /**\n * Returns the document keys for the mutation batch with the given batchId.\n * For primary clients, this method returns `null` after\n * `removeMutationBatches()` has been called. Secondary clients return a\n * cached result until `removeCachedMutationKeys()` is invoked.\n */\n // PORTING NOTE: Multi-tab only.\n lookupMutationKeys(\n transaction: PersistenceTransaction,\n batchId: BatchId\n ): PersistencePromise<DocumentKeySet | null> {\n if (this.documentKeysByBatchId[batchId]) {\n return PersistencePromise.resolve<DocumentKeySet | null>(\n this.documentKeysByBatchId[batchId]\n );\n } else {\n return this.lookupMutationBatch(transaction, batchId).next(batch => {\n if (batch) {\n const keys = batch.keys();\n this.documentKeysByBatchId[batchId] = keys;\n return keys;\n } else {\n return null;\n }\n });\n }\n }\n\n getNextMutationBatchAfterBatchId(\n transaction: PersistenceTransaction,\n batchId: BatchId\n ): PersistencePromise<MutationBatch | null> {\n const nextBatchId = batchId + 1;\n\n const range = IDBKeyRange.lowerBound([this.userId, nextBatchId]);\n let foundBatch: MutationBatch | null = null;\n return mutationsStore(transaction)\n .iterate(\n { index: DbMutationBatch.userMutationsIndex, range },\n (key, dbBatch, control) => {\n if (dbBatch.userId === this.userId) {\n hardAssert(\n dbBatch.batchId >= nextBatchId,\n 'Should have found mutation after ' + nextBatchId\n );\n foundBatch = this.serializer.fromDbMutationBatch(dbBatch);\n }\n control.done();\n }\n )\n .next(() => foundBatch);\n }\n\n getHighestUnacknowledgedBatchId(\n transaction: PersistenceTransaction\n ): PersistencePromise<BatchId> {\n const range = IDBKeyRange.upperBound([\n this.userId,\n Number.POSITIVE_INFINITY\n ]);\n\n let batchId = BATCHID_UNKNOWN;\n return mutationsStore(transaction)\n .iterate(\n { index: DbMutationBatch.userMutationsIndex, range, reverse: true },\n (key, dbBatch, control) => {\n batchId = dbBatch.batchId;\n control.done();\n }\n )\n .next(() => batchId);\n }\n\n getAllMutationBatches(\n transaction: PersistenceTransaction\n ): PersistencePromise<MutationBatch[]> {\n const range = IDBKeyRange.bound(\n [this.userId, BATCHID_UNKNOWN],\n [this.userId, Number.POSITIVE_INFINITY]\n );\n return mutationsStore(transaction)\n .loadAll(DbMutationBatch.userMutationsIndex, range)\n .next(dbBatches =>\n dbBatches.map(dbBatch => this.serializer.fromDbMutationBatch(dbBatch))\n );\n }\n\n getAllMutationBatchesAffectingDocumentKey(\n transaction: PersistenceTransaction,\n documentKey: DocumentKey\n ): PersistencePromise<MutationBatch[]> {\n // Scan the document-mutation index starting with a prefix starting with\n // the given documentKey.\n const indexPrefix = DbDocumentMutation.prefixForPath(\n this.userId,\n documentKey.path\n );\n const indexStart = IDBKeyRange.lowerBound(indexPrefix);\n\n const results: MutationBatch[] = [];\n return documentMutationsStore(transaction)\n .iterate({ range: indexStart }, (indexKey, _, control) => {\n const [userID, encodedPath, batchId] = indexKey;\n\n // Only consider rows matching exactly the specific key of\n // interest. Note that because we order by path first, and we\n // order terminators before path separators, we'll encounter all\n // the index rows for documentKey contiguously. In particular, all\n // the rows for documentKey will occur before any rows for\n // documents nested in a subcollection beneath documentKey so we\n // can stop as soon as we hit any such row.\n const path = decodeResourcePath(encodedPath);\n if (userID !== this.userId || !documentKey.path.isEqual(path)) {\n control.done();\n return;\n }\n // Look up the mutation batch in the store.\n return mutationsStore(transaction)\n .get(batchId)\n .next(mutation => {\n if (!mutation) {\n throw fail(\n 'Dangling document-mutation reference found: ' +\n indexKey +\n ' which points to ' +\n batchId\n );\n }\n hardAssert(\n mutation.userId === this.userId,\n `Unexpected user '${mutation.userId}' for mutation batch ${batchId}`\n );\n results.push(this.serializer.fromDbMutationBatch(mutation));\n });\n })\n .next(() => results);\n }\n\n getAllMutationBatchesAffectingDocumentKeys(\n transaction: PersistenceTransaction,\n documentKeys: SortedMap<DocumentKey, unknown>\n ): PersistencePromise<MutationBatch[]> {\n let uniqueBatchIDs = new SortedSet<BatchId>(primitiveComparator);\n\n const promises: Array<PersistencePromise<void>> = [];\n documentKeys.forEach(documentKey => {\n const indexStart = DbDocumentMutation.prefixForPath(\n this.userId,\n documentKey.path\n );\n const range = IDBKeyRange.lowerBound(indexStart);\n\n const promise = documentMutationsStore(transaction).iterate(\n { range },\n (indexKey, _, control) => {\n const [userID, encodedPath, batchID] = indexKey;\n\n // Only consider rows matching exactly the specific key of\n // interest. Note that because we order by path first, and we\n // order terminators before path separators, we'll encounter all\n // the index rows for documentKey contiguously. In particular, all\n // the rows for documentKey will occur before any rows for\n // documents nested in a subcollection beneath documentKey so we\n // can stop as soon as we hit any such row.\n const path = decodeResourcePath(encodedPath);\n if (userID !== this.userId || !documentKey.path.isEqual(path)) {\n control.done();\n return;\n }\n\n uniqueBatchIDs = uniqueBatchIDs.add(batchID);\n }\n );\n\n promises.push(promise);\n });\n\n return PersistencePromise.waitFor(promises).next(() =>\n this.lookupMutationBatches(transaction, uniqueBatchIDs)\n );\n }\n\n getAllMutationBatchesAffectingQuery(\n transaction: PersistenceTransaction,\n query: Query\n ): PersistencePromise<MutationBatch[]> {\n debugAssert(\n !query.isDocumentQuery(),\n \"Document queries shouldn't go down this path\"\n );\n debugAssert(\n !query.isCollectionGroupQuery(),\n 'CollectionGroup queries should be handled in LocalDocumentsView'\n );\n\n const queryPath = query.path;\n const immediateChildrenLength = queryPath.length + 1;\n\n // TODO(mcg): Actually implement a single-collection query\n //\n // This is actually executing an ancestor query, traversing the whole\n // subtree below the collection which can be horrifically inefficient for\n // some structures. The right way to solve this is to implement the full\n // value index, but that's not in the cards in the near future so this is\n // the best we can do for the moment.\n //\n // Since we don't yet index the actual properties in the mutations, our\n // current approach is to just return all mutation batches that affect\n // documents in the collection being queried.\n const indexPrefix = DbDocumentMutation.prefixForPath(\n this.userId,\n queryPath\n );\n const indexStart = IDBKeyRange.lowerBound(indexPrefix);\n\n // Collect up unique batchIDs encountered during a scan of the index. Use a\n // SortedSet to accumulate batch IDs so they can be traversed in order in a\n // scan of the main table.\n let uniqueBatchIDs = new SortedSet<BatchId>(primitiveComparator);\n return documentMutationsStore(transaction)\n .iterate({ range: indexStart }, (indexKey, _, control) => {\n const [userID, encodedPath, batchID] = indexKey;\n const path = decodeResourcePath(encodedPath);\n if (userID !== this.userId || !queryPath.isPrefixOf(path)) {\n control.done();\n return;\n }\n // Rows with document keys more than one segment longer than the\n // query path can't be matches. For example, a query on 'rooms'\n // can't match the document /rooms/abc/messages/xyx.\n // TODO(mcg): we'll need a different scanner when we implement\n // ancestor queries.\n if (path.length !== immediateChildrenLength) {\n return;\n }\n uniqueBatchIDs = uniqueBatchIDs.add(batchID);\n })\n .next(() => this.lookupMutationBatches(transaction, uniqueBatchIDs));\n }\n\n private lookupMutationBatches(\n transaction: PersistenceTransaction,\n batchIDs: SortedSet<BatchId>\n ): PersistencePromise<MutationBatch[]> {\n const results: MutationBatch[] = [];\n const promises: Array<PersistencePromise<void>> = [];\n // TODO(rockwood): Implement this using iterate.\n batchIDs.forEach(batchId => {\n promises.push(\n mutationsStore(transaction)\n .get(batchId)\n .next(mutation => {\n if (mutation === null) {\n throw fail(\n 'Dangling document-mutation reference found, ' +\n 'which points to ' +\n batchId\n );\n }\n hardAssert(\n mutation.userId === this.userId,\n `Unexpected user '${mutation.userId}' for mutation batch ${batchId}`\n );\n results.push(this.serializer.fromDbMutationBatch(mutation));\n })\n );\n });\n return PersistencePromise.waitFor(promises).next(() => results);\n }\n\n removeMutationBatch(\n transaction: PersistenceTransaction,\n batch: MutationBatch\n ): PersistencePromise<void> {\n return removeMutationBatch(\n (transaction as IndexedDbTransaction).simpleDbTransaction,\n this.userId,\n batch\n ).next(removedDocuments => {\n transaction.addOnCommittedListener(() => {\n this.removeCachedMutationKeys(batch.batchId);\n });\n return PersistencePromise.forEach(\n removedDocuments,\n (key: DocumentKey) => {\n return this.referenceDelegate.markPotentiallyOrphaned(\n transaction,\n key\n );\n }\n );\n });\n }\n\n /**\n * Clears the cached keys for a mutation batch. This method should be\n * called by secondary clients after they process mutation updates.\n *\n * Note that this method does not have to be called from primary clients as\n * the corresponding cache entries are cleared when an acknowledged or\n * rejected batch is removed from the mutation queue.\n */\n // PORTING NOTE: Multi-tab only\n removeCachedMutationKeys(batchId: BatchId): void {\n delete this.documentKeysByBatchId[batchId];\n }\n\n performConsistencyCheck(\n txn: PersistenceTransaction\n ): PersistencePromise<void> {\n return this.checkEmpty(txn).next(empty => {\n if (!empty) {\n return PersistencePromise.resolve();\n }\n\n // Verify that there are no entries in the documentMutations index if\n // the queue is empty.\n const startRange = IDBKeyRange.lowerBound(\n DbDocumentMutation.prefixForUser(this.userId)\n );\n const danglingMutationReferences: ResourcePath[] = [];\n return documentMutationsStore(txn)\n .iterate({ range: startRange }, (key, _, control) => {\n const userID = key[0];\n if (userID !== this.userId) {\n control.done();\n return;\n } else {\n const path = decodeResourcePath(key[1]);\n danglingMutationReferences.push(path);\n }\n })\n .next(() => {\n hardAssert(\n danglingMutationReferences.length === 0,\n 'Document leak -- detected dangling mutation references when queue is empty. ' +\n 'Dangling keys: ' +\n danglingMutationReferences.map(p => p.canonicalString())\n );\n });\n });\n }\n\n containsKey(\n txn: PersistenceTransaction,\n key: DocumentKey\n ): PersistencePromise<boolean> {\n return mutationQueueContainsKey(txn, this.userId, key);\n }\n\n // PORTING NOTE: Multi-tab only (state is held in memory in other clients).\n /** Returns the mutation queue's metadata from IndexedDb. */\n private getMutationQueueMetadata(\n transaction: PersistenceTransaction\n ): PersistencePromise<DbMutationQueue> {\n return mutationQueuesStore(transaction)\n .get(this.userId)\n .next((metadata: DbMutationQueue | null) => {\n return (\n metadata ||\n new DbMutationQueue(\n this.userId,\n BATCHID_UNKNOWN,\n /*lastStreamToken=*/ ''\n )\n );\n });\n }\n}\n\n/**\n * @return true if the mutation queue for the given user contains a pending\n * mutation for the given key.\n */\nfunction mutationQueueContainsKey(\n txn: PersistenceTransaction,\n userId: string,\n key: DocumentKey\n): PersistencePromise<boolean> {\n const indexKey = DbDocumentMutation.prefixForPath(userId, key.path);\n const encodedPath = indexKey[1];\n const startRange = IDBKeyRange.lowerBound(indexKey);\n let containsKey = false;\n return documentMutationsStore(txn)\n .iterate({ range: startRange, keysOnly: true }, (key, value, control) => {\n const [userID, keyPath, /*batchID*/ _] = key;\n if (userID === userId && keyPath === encodedPath) {\n containsKey = true;\n }\n control.done();\n })\n .next(() => containsKey);\n}\n\n/** Returns true if any mutation queue contains the given document. */\nexport function mutationQueuesContainKey(\n txn: PersistenceTransaction,\n docKey: DocumentKey\n): PersistencePromise<boolean> {\n let found = false;\n return mutationQueuesStore(txn)\n .iterateSerial(userId => {\n return mutationQueueContainsKey(txn, userId, docKey).next(containsKey => {\n if (containsKey) {\n found = true;\n }\n return PersistencePromise.resolve(!containsKey);\n });\n })\n .next(() => found);\n}\n\n/**\n * Delete a mutation batch and the associated document mutations.\n * @return A PersistencePromise of the document mutations that were removed.\n */\nexport function removeMutationBatch(\n txn: SimpleDbTransaction,\n userId: string,\n batch: MutationBatch\n): PersistencePromise<DocumentKey[]> {\n const mutationStore = txn.store<DbMutationBatchKey, DbMutationBatch>(\n DbMutationBatch.store\n );\n const indexTxn = txn.store<DbDocumentMutationKey, DbDocumentMutation>(\n DbDocumentMutation.store\n );\n const promises: Array<PersistencePromise<void>> = [];\n\n const range = IDBKeyRange.only(batch.batchId);\n let numDeleted = 0;\n const removePromise = mutationStore.iterate(\n { range },\n (key, value, control) => {\n numDeleted++;\n return control.delete();\n }\n );\n promises.push(\n removePromise.next(() => {\n hardAssert(\n numDeleted === 1,\n 'Dangling document-mutation reference found: Missing batch ' +\n batch.batchId\n );\n })\n );\n const removedDocuments: DocumentKey[] = [];\n for (const mutation of batch.mutations) {\n const indexKey = DbDocumentMutation.key(\n userId,\n mutation.key.path,\n batch.batchId\n );\n promises.push(indexTxn.delete(indexKey));\n removedDocuments.push(mutation.key);\n }\n return PersistencePromise.waitFor(promises).next(() => removedDocuments);\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the mutations object store.\n */\nfunction mutationsStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbMutationBatchKey, DbMutationBatch> {\n return IndexedDbPersistence.getStore<DbMutationBatchKey, DbMutationBatch>(\n txn,\n DbMutationBatch.store\n );\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the mutationQueues object store.\n */\nfunction documentMutationsStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbDocumentMutationKey, DbDocumentMutation> {\n return IndexedDbPersistence.getStore<\n DbDocumentMutationKey,\n DbDocumentMutation\n >(txn, DbDocumentMutation.store);\n}\n\n/**\n * Helper to get a typed SimpleDbStore for the mutationQueues object store.\n */\nfunction mutationQueuesStore(\n txn: PersistenceTransaction\n): SimpleDbStore<DbMutationQueueKey, DbMutationQueue> {\n return IndexedDbPersistence.getStore<DbMutationQueueKey, DbMutationQueue>(\n txn,\n DbMutationQueue.store\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 { BatchId, ListenSequenceNumber, TargetId } from '../core/types';\nimport { ResourcePath } from '../model/path';\nimport * as api from '../protos/firestore_proto_api';\nimport { hardAssert, debugAssert } from '../util/assert';\n\nimport { SnapshotVersion } from '../core/snapshot_version';\nimport { BATCHID_UNKNOWN } from '../model/mutation_batch';\nimport {\n decodeResourcePath,\n encodeResourcePath,\n EncodedResourcePath\n} from './encoded_resource_path';\nimport { removeMutationBatch } from './indexeddb_mutation_queue';\nimport { dbDocumentSize } from './indexeddb_remote_document_cache';\nimport { LocalSerializer } from './local_serializer';\nimport { MemoryCollectionParentIndex } from './memory_index_manager';\nimport { PersistencePromise } from './persistence_promise';\nimport { SimpleDbSchemaConverter, SimpleDbTransaction } from './simple_db';\n\n/**\n * Schema Version for the Web client:\n * 1. Initial version including Mutation Queue, Query Cache, and Remote\n * Document Cache\n * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No\n * longer required because migration 3 unconditionally clears it.\n * 3. Dropped and re-created Query Cache to deal with cache corruption related\n * to limbo resolution. Addresses\n * https://github.com/firebase/firebase-ios-sdk/issues/1548\n * 4. Multi-Tab Support.\n * 5. Removal of held write acks.\n * 6. Create document global for tracking document cache size.\n * 7. Ensure every cached document has a sentinel row with a sequence number.\n * 8. Add collection-parent index for Collection Group queries.\n * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than\n * an auto-incrementing ID. This is required for Index-Free queries.\n * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.\n */\nexport const SCHEMA_VERSION = 10;\n\n/** Performs database creation and schema upgrades. */\nexport class SchemaConverter implements SimpleDbSchemaConverter {\n constructor(private readonly serializer: LocalSerializer) {}\n\n /**\n * Performs database creation and schema upgrades.\n *\n * Note that in production, this method is only ever used to upgrade the schema\n * to SCHEMA_VERSION. Different values of toVersion are only used for testing\n * and local feature development.\n */\n createOrUpgrade(\n db: IDBDatabase,\n txn: IDBTransaction,\n fromVersion: number,\n toVersion: number\n ): PersistencePromise<void> {\n hardAssert(\n fromVersion < toVersion &&\n fromVersion >= 0 &&\n toVersion <= SCHEMA_VERSION,\n `Unexpected schema upgrade from v${fromVersion} to v${toVersion}.`\n );\n\n const simpleDbTransaction = new SimpleDbTransaction(txn);\n\n if (fromVersion < 1 && toVersion >= 1) {\n createPrimaryClientStore(db);\n createMutationQueue(db);\n createQueryCache(db);\n createRemoteDocumentCache(db);\n }\n\n // Migration 2 to populate the targetGlobal object no longer needed since\n // migration 3 unconditionally clears it.\n\n let p = PersistencePromise.resolve();\n if (fromVersion < 3 && toVersion >= 3) {\n // Brand new clients don't need to drop and recreate--only clients that\n // potentially have corrupt data.\n if (fromVersion !== 0) {\n dropQueryCache(db);\n createQueryCache(db);\n }\n p = p.next(() => writeEmptyTargetGlobalEntry(simpleDbTransaction));\n }\n\n if (fromVersion < 4 && toVersion >= 4) {\n if (fromVersion !== 0) {\n // Schema version 3 uses auto-generated keys to generate globally unique\n // mutation batch IDs (this was previously ensured internally by the\n // client). To migrate to the new schema, we have to read all mutations\n // and write them back out. We preserve the existing batch IDs to guarantee\n // consistency with other object stores. Any further mutation batch IDs will\n // be auto-generated.\n p = p.next(() =>\n upgradeMutationBatchSchemaAndMigrateData(db, simpleDbTransaction)\n );\n }\n\n p = p.next(() => {\n createClientMetadataStore(db);\n });\n }\n\n if (fromVersion < 5 && toVersion >= 5) {\n p = p.next(() => this.removeAcknowledgedMutations(simpleDbTransaction));\n }\n\n if (fromVersion < 6 && toVersion >= 6) {\n p = p.next(() => {\n createDocumentGlobalStore(db);\n return this.addDocumentGlobal(simpleDbTransaction);\n });\n }\n\n if (fromVersion < 7 && toVersion >= 7) {\n p = p.next(() => this.ensureSequenceNumbers(simpleDbTransaction));\n }\n\n if (fromVersion < 8 && toVersion >= 8) {\n p = p.next(() =>\n this.createCollectionParentIndex(db, simpleDbTransaction)\n );\n }\n\n if (fromVersion < 9 && toVersion >= 9) {\n p = p.next(() => {\n // Multi-Tab used to manage its own changelog, but this has been moved\n // to the DbRemoteDocument object store itself. Since the previous change\n // log only contained transient data, we can drop its object store.\n dropRemoteDocumentChangesStore(db);\n createRemoteDocumentReadTimeIndex(txn);\n });\n }\n\n if (fromVersion < 10 && toVersion >= 10) {\n p = p.next(() => this.rewriteCanonicalIds(simpleDbTransaction));\n }\n return p;\n }\n\n private addDocumentGlobal(\n txn: SimpleDbTransaction\n ): PersistencePromise<void> {\n let byteCount = 0;\n return txn\n .store<DbRemoteDocumentKey, DbRemoteDocument>(DbRemoteDocument.store)\n .iterate((_, doc) => {\n byteCount += dbDocumentSize(doc);\n })\n .next(() => {\n const metadata = new DbRemoteDocumentGlobal(byteCount);\n return txn\n .store<DbRemoteDocumentGlobalKey, DbRemoteDocumentGlobal>(\n DbRemoteDocumentGlobal.store\n )\n .put(DbRemoteDocumentGlobal.key, metadata);\n });\n }\n\n private removeAcknowledgedMutations(\n txn: SimpleDbTransaction\n ): PersistencePromise<void> {\n const queuesStore = txn.store<DbMutationQueueKey, DbMutationQueue>(\n DbMutationQueue.store\n );\n const mutationsStore = txn.store<DbMutationBatchKey, DbMutationBatch>(\n DbMutationBatch.store\n );\n\n return queuesStore.loadAll().next(queues => {\n return PersistencePromise.forEach(queues, (queue: DbMutationQueue) => {\n const range = IDBKeyRange.bound(\n [queue.userId, BATCHID_UNKNOWN],\n [queue.userId, queue.lastAcknowledgedBatchId]\n );\n\n return mutationsStore\n .loadAll(DbMutationBatch.userMutationsIndex, range)\n .next(dbBatches => {\n return PersistencePromise.forEach(\n dbBatches,\n (dbBatch: DbMutationBatch) => {\n hardAssert(\n dbBatch.userId === queue.userId,\n `Cannot process batch ${dbBatch.batchId} from unexpected user`\n );\n const batch = this.serializer.fromDbMutationBatch(dbBatch);\n\n return removeMutationBatch(\n txn,\n queue.userId,\n batch\n ).next(() => {});\n }\n );\n });\n });\n });\n }\n\n /**\n * Ensures that every document in the remote document cache has a corresponding sentinel row\n * with a sequence number. Missing rows are given the most recently used sequence number.\n */\n private ensureSequenceNumbers(\n txn: SimpleDbTransaction\n ): PersistencePromise<void> {\n const documentTargetStore = txn.store<\n DbTargetDocumentKey,\n DbTargetDocument\n >(DbTargetDocument.store);\n const documentsStore = txn.store<DbRemoteDocumentKey, DbRemoteDocument>(\n DbRemoteDocument.store\n );\n const globalTargetStore = txn.store<DbTargetGlobalKey, DbTargetGlobal>(\n DbTargetGlobal.store\n );\n\n return globalTargetStore.get(DbTargetGlobal.key).next(metadata => {\n debugAssert(\n !!metadata,\n 'Metadata should have been written during the version 3 migration'\n );\n const writeSentinelKey = (\n path: ResourcePath\n ): PersistencePromise<void> => {\n return documentTargetStore.put(\n new DbTargetDocument(\n 0,\n encodeResourcePath(path),\n metadata!.highestListenSequenceNumber!\n )\n );\n };\n\n const promises: Array<PersistencePromise<void>> = [];\n return documentsStore\n .iterate((key, doc) => {\n const path = new ResourcePath(key);\n const docSentinelKey = sentinelKey(path);\n promises.push(\n documentTargetStore.get(docSentinelKey).next(maybeSentinel => {\n if (!maybeSentinel) {\n return writeSentinelKey(path);\n } else {\n return PersistencePromise.resolve();\n }\n })\n );\n })\n .next(() => PersistencePromise.waitFor(promises));\n });\n }\n\n private createCollectionParentIndex(\n db: IDBDatabase,\n txn: SimpleDbTransaction\n ): PersistencePromise<void> {\n // Create the index.\n db.createObjectStore(DbCollectionParent.store, {\n keyPath: DbCollectionParent.keyPath\n });\n\n const collectionParentsStore = txn.store<\n DbCollectionParentKey,\n DbCollectionParent\n >(DbCollectionParent.store);\n\n // Helper to add an index entry iff we haven't already written it.\n const cache = new MemoryCollectionParentIndex();\n const addEntry = (\n collectionPath: ResourcePath\n ): PersistencePromise<void> | undefined => {\n if (cache.add(collectionPath)) {\n const collectionId = collectionPath.lastSegment();\n const parentPath = collectionPath.popLast();\n return collectionParentsStore.put({\n collectionId,\n parent: encodeResourcePath(parentPath)\n });\n }\n };\n\n // Index existing remote documents.\n return txn\n .store<DbRemoteDocumentKey, DbRemoteDocument>(DbRemoteDocument.store)\n .iterate({ keysOnly: true }, (pathSegments, _) => {\n const path = new ResourcePath(pathSegments);\n return addEntry(path.popLast());\n })\n .next(() => {\n // Index existing mutations.\n return txn\n .store<DbDocumentMutationKey, DbDocumentMutation>(\n DbDocumentMutation.store\n )\n .iterate({ keysOnly: true }, ([userID, encodedPath, batchId], _) => {\n const path = decodeResourcePath(encodedPath);\n return addEntry(path.popLast());\n });\n });\n }\n\n private rewriteCanonicalIds(\n txn: SimpleDbTransaction\n ): PersistencePromise<void> {\n const targetStore = txn.store<DbTargetKey, DbTarget>(DbTarget.store);\n return targetStore.iterate((key, originalDbTarget) => {\n const originalTargetData = this.serializer.fromDbTarget(originalDbTarget);\n const updatedDbTarget = this.serializer.toDbTarget(originalTargetData);\n return targetStore.put(updatedDbTarget);\n });\n }\n}\n\nfunction sentinelKey(path: ResourcePath): DbTargetDocumentKey {\n return [0, encodeResourcePath(path)];\n}\n\n/**\n * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.\n */\nexport class DbTimestamp {\n constructor(public seconds: number, public nanoseconds: number) {}\n}\n\n/** A timestamp type that can be used in IndexedDb keys. */\nexport type DbTimestampKey = [/* seconds */ number, /* nanos */ number];\n\n// The key for the singleton object in the DbPrimaryClient is a single string.\nexport type DbPrimaryClientKey = typeof DbPrimaryClient.key;\n\n/**\n * A singleton object to be stored in the 'owner' store in IndexedDb.\n *\n * A given database can have a single primary tab assigned at a given time. That\n * tab must validate that it is still holding the primary lease before every\n * operation that requires locked access. The primary tab should regularly\n * write an updated timestamp to this lease to prevent other tabs from\n * \"stealing\" the primary lease\n */\nexport class DbPrimaryClient {\n /**\n * Name of the IndexedDb object store.\n *\n * Note that the name 'owner' is chosen to ensure backwards compatibility with\n * older clients that only supported single locked access to the persistence\n * layer.\n */\n static store = 'owner';\n\n /**\n * The key string used for the single object that exists in the\n * DbPrimaryClient store.\n */\n static key = 'owner';\n\n constructor(\n public ownerId: string,\n /** Whether to allow shared access from multiple tabs. */\n public allowTabSynchronization: boolean,\n public leaseTimestampMs: number\n ) {}\n}\n\nfunction createPrimaryClientStore(db: IDBDatabase): void {\n db.createObjectStore(DbPrimaryClient.store);\n}\n\n/** Object keys in the 'mutationQueues' store are userId strings. */\nexport type DbMutationQueueKey = string;\n\n/**\n * An object to be stored in the 'mutationQueues' store in IndexedDb.\n *\n * Each user gets a single queue of MutationBatches to apply to the server.\n * DbMutationQueue tracks the metadata about the queue.\n */\nexport class DbMutationQueue {\n /** Name of the IndexedDb object store. */\n static store = 'mutationQueues';\n\n /** Keys are automatically assigned via the userId property. */\n static keyPath = 'userId';\n\n constructor(\n /**\n * The normalized user ID to which this queue belongs.\n */\n public userId: string,\n /**\n * An identifier for the highest numbered batch that has been acknowledged\n * by the server. All MutationBatches in this queue with batchIds less\n * than or equal to this value are considered to have been acknowledged by\n * the server.\n *\n * NOTE: this is deprecated and no longer used by the code.\n */\n public lastAcknowledgedBatchId: number,\n /**\n * A stream token that was previously sent by the server.\n *\n * See StreamingWriteRequest in datastore.proto for more details about\n * usage.\n *\n * After sending this token, earlier tokens may not be used anymore so\n * only a single stream token is retained.\n */\n public lastStreamToken: string\n ) {}\n}\n\n/** The 'mutations' store is keyed by batch ID. */\nexport type DbMutationBatchKey = BatchId;\n\n/**\n * An object to be stored in the 'mutations' store in IndexedDb.\n *\n * Represents a batch of user-level mutations intended to be sent to the server\n * in a single write. Each user-level batch gets a separate DbMutationBatch\n * with a new batchId.\n */\nexport class DbMutationBatch {\n /** Name of the IndexedDb object store. */\n static store = 'mutations';\n\n /** Keys are automatically assigned via the userId, batchId properties. */\n static keyPath = 'batchId';\n\n /** The index name for lookup of mutations by user. */\n static userMutationsIndex = 'userMutationsIndex';\n\n /** The user mutations index is keyed by [userId, batchId] pairs. */\n static userMutationsKeyPath = ['userId', 'batchId'];\n\n constructor(\n /**\n * The normalized user ID to which this batch belongs.\n */\n public userId: string,\n /**\n * An identifier for this batch, allocated using an auto-generated key.\n */\n public batchId: BatchId,\n /**\n * The local write time of the batch, stored as milliseconds since the\n * epoch.\n */\n public localWriteTimeMs: number,\n /**\n * A list of \"mutations\" that represent a partial base state from when this\n * write batch was initially created. During local application of the write\n * batch, these baseMutations are applied prior to the real writes in order\n * to override certain document fields from the remote document cache. This\n * is necessary in the case of non-idempotent writes (e.g. `increment()`\n * transforms) to make sure that the local view of the modified documents\n * doesn't flicker if the remote document cache receives the result of the\n * non-idempotent write before the write is removed from the queue.\n *\n * These mutations are never sent to the backend.\n */\n public baseMutations: api.Write[] | undefined,\n /**\n * A list of mutations to apply. All mutations will be applied atomically.\n *\n * Mutations are serialized via JsonProtoSerializer.toMutation().\n */\n public mutations: api.Write[]\n ) {}\n}\n\n/**\n * The key for a db document mutation, which is made up of a userID, path, and\n * batchId. Note that the path must be serialized into a form that indexedDB can\n * sort.\n */\nexport type DbDocumentMutationKey = [string, EncodedResourcePath, BatchId];\n\nfunction createMutationQueue(db: IDBDatabase): void {\n db.createObjectStore(DbMutationQueue.store, {\n keyPath: DbMutationQueue.keyPath\n });\n\n const mutationBatchesStore = db.createObjectStore(DbMutationBatch.store, {\n keyPath: DbMutationBatch.keyPath,\n autoIncrement: true\n });\n mutationBatchesStore.createIndex(\n DbMutationBatch.userMutationsIndex,\n DbMutationBatch.userMutationsKeyPath,\n { unique: true }\n );\n\n db.createObjectStore(DbDocumentMutation.store);\n}\n\n/**\n * Upgrade function to migrate the 'mutations' store from V1 to V3. Loads\n * and rewrites all data.\n */\nfunction upgradeMutationBatchSchemaAndMigrateData(\n db: IDBDatabase,\n txn: SimpleDbTransaction\n): PersistencePromise<void> {\n const v1MutationsStore = txn.store<[string, number], DbMutationBatch>(\n DbMutationBatch.store\n );\n return v1MutationsStore.loadAll().next(existingMutations => {\n db.deleteObjectStore(DbMutationBatch.store);\n\n const mutationsStore = db.createObjectStore(DbMutationBatch.store, {\n keyPath: DbMutationBatch.keyPath,\n autoIncrement: true\n });\n mutationsStore.createIndex(\n DbMutationBatch.userMutationsIndex,\n DbMutationBatch.userMutationsKeyPath,\n { unique: true }\n );\n\n const v3MutationsStore = txn.store<DbMutationBatchKey, DbMutationBatch>(\n DbMutationBatch.store\n );\n const writeAll = existingMutations.map(mutation =>\n v3MutationsStore.put(mutation)\n );\n\n return PersistencePromise.waitFor(writeAll);\n });\n}\n\n/**\n * An object to be stored in the 'documentMutations' store in IndexedDb.\n *\n * A manually maintained index of all the mutation batches that affect a given\n * document key. The rows in this table are references based on the contents of\n * DbMutationBatch.mutations.\n */\nexport class DbDocumentMutation {\n static store = 'documentMutations';\n\n /**\n * Creates a [userId] key for use in the DbDocumentMutations index to iterate\n * over all of a user's document mutations.\n */\n static prefixForUser(userId: string): [string] {\n return [userId];\n }\n\n /**\n * Creates a [userId, encodedPath] key for use in the DbDocumentMutations\n * index to iterate over all at document mutations for a given path or lower.\n */\n static prefixForPath(\n userId: string,\n path: ResourcePath\n ): [string, EncodedResourcePath] {\n return [userId, encodeResourcePath(path)];\n }\n\n /**\n * Creates a full index key of [userId, encodedPath, batchId] for inserting\n * and deleting into the DbDocumentMutations index.\n */\n static key(\n userId: string,\n path: ResourcePath,\n batchId: BatchId\n ): DbDocumentMutationKey {\n return [userId, encodeResourcePath(path), batchId];\n }\n\n /**\n * Because we store all the useful information for this store in the key,\n * there is no useful information to store as the value. The raw (unencoded)\n * path cannot be stored because IndexedDb doesn't store prototype\n * information.\n */\n static PLACEHOLDER = new DbDocumentMutation();\n\n private constructor() {}\n}\n\n/**\n * A key in the 'remoteDocuments' object store is a string array containing the\n * segments that make up the path.\n */\nexport type DbRemoteDocumentKey = string[];\n\nfunction createRemoteDocumentCache(db: IDBDatabase): void {\n db.createObjectStore(DbRemoteDocument.store);\n}\n\n/**\n * Represents the known absence of a document at a particular version.\n * Stored in IndexedDb as part of a DbRemoteDocument object.\n */\nexport class DbNoDocument {\n constructor(public path: string[], public readTime: DbTimestamp) {}\n}\n\n/**\n * Represents a document that is known to exist but whose data is unknown.\n * Stored in IndexedDb as part of a DbRemoteDocument object.\n */\nexport class DbUnknownDocument {\n constructor(public path: string[], public version: DbTimestamp) {}\n}\n\n/**\n * An object to be stored in the 'remoteDocuments' store in IndexedDb.\n * It represents either:\n *\n * - A complete document.\n * - A \"no document\" representing a document that is known not to exist (at\n * some version).\n * - An \"unknown document\" representing a document that is known to exist (at\n * some version) but whose contents are unknown.\n *\n * Note: This is the persisted equivalent of a MaybeDocument and could perhaps\n * be made more general if necessary.\n */\nexport class DbRemoteDocument {\n static store = 'remoteDocuments';\n\n /**\n * An index that provides access to all entries sorted by read time (which\n * corresponds to the last modification time of each row).\n *\n * This index is used to provide a changelog for Multi-Tab.\n */\n static readTimeIndex = 'readTimeIndex';\n\n static readTimeIndexPath = 'readTime';\n\n /**\n * An index that provides access to documents in a collection sorted by read\n * time.\n *\n * This index is used to allow the RemoteDocumentCache to fetch newly changed\n * documents in a collection.\n */\n static collectionReadTimeIndex = 'collectionReadTimeIndex';\n\n static collectionReadTimeIndexPath = ['parentPath', 'readTime'];\n\n // TODO: We are currently storing full document keys almost three times\n // (once as part of the primary key, once - partly - as `parentPath` and once\n // inside the encoded documents). During our next migration, we should\n // rewrite the primary key as parentPath + document ID which would allow us\n // to drop one value.\n\n constructor(\n /**\n * Set to an instance of DbUnknownDocument if the data for a document is\n * not known, but it is known that a document exists at the specified\n * version (e.g. it had a successful update applied to it)\n */\n public unknownDocument: DbUnknownDocument | null | undefined,\n /**\n * Set to an instance of a DbNoDocument if it is known that no document\n * exists.\n */\n public noDocument: DbNoDocument | null,\n /**\n * Set to an instance of a Document if there's a cached version of the\n * document.\n */\n public document: api.Document | null,\n /**\n * Documents that were written to the remote document store based on\n * a write acknowledgment are marked with `hasCommittedMutations`. These\n * documents are potentially inconsistent with the backend's copy and use\n * the write's commit version as their document version.\n */\n public hasCommittedMutations: boolean | undefined,\n\n /**\n * When the document was read from the backend. Undefined for data written\n * prior to schema version 9.\n */\n public readTime: DbTimestampKey | undefined,\n\n /**\n * The path of the collection this document is part of. Undefined for data\n * written prior to schema version 9.\n */\n public parentPath: string[] | undefined\n ) {}\n}\n\n/**\n * Contains a single entry that has metadata about the remote document cache.\n */\nexport class DbRemoteDocumentGlobal {\n static store = 'remoteDocumentGlobal';\n\n static key = 'remoteDocumentGlobalKey';\n\n /**\n * @param byteSize Approximately the total size in bytes of all the documents in the document\n * cache.\n */\n constructor(public byteSize: number) {}\n}\n\nexport type DbRemoteDocumentGlobalKey = typeof DbRemoteDocumentGlobal.key;\n\nfunction createDocumentGlobalStore(db: IDBDatabase): void {\n db.createObjectStore(DbRemoteDocumentGlobal.store);\n}\n\n/**\n * A key in the 'targets' object store is a targetId of the query.\n */\nexport type DbTargetKey = TargetId;\n\n/**\n * The persisted type for a query nested with in the 'targets' store in\n * IndexedDb. We use the proto definitions for these two kinds of queries in\n * order to avoid writing extra serialization logic.\n */\nexport type DbQuery = api.QueryTarget | api.DocumentsTarget;\n\n/**\n * An object to be stored in the 'targets' store in IndexedDb.\n *\n * This is based on and should be kept in sync with the proto used in the iOS\n * client.\n *\n * Each query the client listens to against the server is tracked on disk so\n * that the query can be efficiently resumed on restart.\n */\nexport class DbTarget {\n static store = 'targets';\n\n /** Keys are automatically assigned via the targetId property. */\n static keyPath = 'targetId';\n\n /** The name of the queryTargets index. */\n static queryTargetsIndexName = 'queryTargetsIndex';\n\n /**\n * The index of all canonicalIds to the targets that they match. This is not\n * a unique mapping because canonicalId does not promise a unique name for all\n * possible queries, so we append the targetId to make the mapping unique.\n */\n static queryTargetsKeyPath = ['canonicalId', 'targetId'];\n\n constructor(\n /**\n * An auto-generated sequential numeric identifier for the query.\n *\n * Queries are stored using their canonicalId as the key, but these\n * canonicalIds can be quite long so we additionally assign a unique\n * queryId which can be used by referenced data structures (e.g.\n * indexes) to minimize the on-disk cost.\n */\n public targetId: TargetId,\n /**\n * The canonical string representing this query. This is not unique.\n */\n public canonicalId: string,\n /**\n * The last readTime received from the Watch Service for this query.\n *\n * This is the same value as TargetChange.read_time in the protos.\n */\n public readTime: DbTimestamp,\n /**\n * An opaque, server-assigned token that allows watching a query to be\n * resumed after disconnecting without retransmitting all the data\n * that matches the query. The resume token essentially identifies a\n * point in time from which the server should resume sending results.\n *\n * This is related to the snapshotVersion in that the resumeToken\n * effectively also encodes that value, but the resumeToken is opaque\n * and sometimes encodes additional information.\n *\n * A consequence of this is that the resumeToken should be used when\n * asking the server to reason about where this client is in the watch\n * stream, but the client should use the snapshotVersion for its own\n * purposes.\n *\n * This is the same value as TargetChange.resume_token in the protos.\n */\n public resumeToken: string,\n /**\n * A sequence number representing the last time this query was\n * listened to, used for garbage collection purposes.\n *\n * Conventionally this would be a timestamp value, but device-local\n * clocks are unreliable and they must be able to create new listens\n * even while disconnected. Instead this should be a monotonically\n * increasing number that's incremented on each listen call.\n *\n * This is different from the queryId since the queryId is an\n * immutable identifier assigned to the Query on first use while\n * lastListenSequenceNumber is updated every time the query is\n * listened to.\n */\n public lastListenSequenceNumber: number,\n /**\n * Denotes the maximum snapshot version at which the associated query view\n * contained no limbo documents. Undefined for data written prior to\n * schema version 9.\n */\n public lastLimboFreeSnapshotVersion: DbTimestamp | undefined,\n /**\n * The query for this target.\n *\n * Because canonical ids are not unique we must store the actual query. We\n * use the proto to have an object we can persist without having to\n * duplicate translation logic to and from a `Query` object.\n */\n public query: DbQuery\n ) {}\n}\n\n/**\n * The key for a DbTargetDocument, containing a targetId and an encoded resource\n * path.\n */\nexport type DbTargetDocumentKey = [TargetId, EncodedResourcePath];\n\n/**\n * An object representing an association between a target and a document, or a\n * sentinel row marking the last sequence number at which a document was used.\n * Each document cached must have a corresponding sentinel row before lru\n * garbage collection is enabled.\n *\n * The target associations and sentinel rows are co-located so that orphaned\n * documents and their sequence numbers can be identified efficiently via a scan\n * of this store.\n */\nexport class DbTargetDocument {\n /** Name of the IndexedDb object store. */\n static store = 'targetDocuments';\n\n /** Keys are automatically assigned via the targetId, path properties. */\n static keyPath = ['targetId', 'path'];\n\n /** The index name for the reverse index. */\n static documentTargetsIndex = 'documentTargetsIndex';\n\n /** We also need to create the reverse index for these properties. */\n static documentTargetsKeyPath = ['path', 'targetId'];\n\n constructor(\n /**\n * The targetId identifying a target or 0 for a sentinel row.\n */\n public targetId: TargetId,\n /**\n * The path to the document, as encoded in the key.\n */\n public path: EncodedResourcePath,\n /**\n * If this is a sentinel row, this should be the sequence number of the last\n * time the document specified by `path` was used. Otherwise, it should be\n * `undefined`.\n */\n public sequenceNumber?: ListenSequenceNumber\n ) {\n debugAssert(\n (targetId === 0) === (sequenceNumber !== undefined),\n 'A target-document row must either have targetId == 0 and a defined sequence number, or a non-zero targetId and no sequence number'\n );\n }\n}\n\n/**\n * The type to represent the single allowed key for the DbTargetGlobal store.\n */\nexport type DbTargetGlobalKey = typeof DbTargetGlobal.key;\n\n/**\n * A record of global state tracked across all Targets, tracked separately\n * to avoid the need for extra indexes.\n *\n * This should be kept in-sync with the proto used in the iOS client.\n */\nexport class DbTargetGlobal {\n /**\n * The key string used for the single object that exists in the\n * DbTargetGlobal store.\n */\n static key = 'targetGlobalKey';\n static store = 'targetGlobal';\n\n constructor(\n /**\n * The highest numbered target id across all targets.\n *\n * See DbTarget.targetId.\n */\n public highestTargetId: TargetId,\n /**\n * The highest numbered lastListenSequenceNumber across all targets.\n *\n * See DbTarget.lastListenSequenceNumber.\n */\n public highestListenSequenceNumber: number,\n /**\n * A global snapshot version representing the last consistent snapshot we\n * received from the backend. This is monotonically increasing and any\n * snapshots received from the backend prior to this version (e.g. for\n * targets resumed with a resumeToken) should be suppressed (buffered)\n * until the backend has caught up to this snapshot version again. This\n * prevents our cache from ever going backwards in time.\n */\n public lastRemoteSnapshotVersion: DbTimestamp,\n /**\n * The number of targets persisted.\n */\n public targetCount: number\n ) {}\n}\n\n/**\n * The key for a DbCollectionParent entry, containing the collection ID\n * and the parent path that contains it. Note that the parent path will be an\n * empty path in the case of root-level collections.\n */\nexport type DbCollectionParentKey = [string, EncodedResourcePath];\n\n/**\n * An object representing an association between a Collection id (e.g. 'messages')\n * to a parent path (e.g. '/chats/123') that contains it as a (sub)collection.\n * This is used to efficiently find all collections to query when performing\n * a Collection Group query.\n */\nexport class DbCollectionParent {\n /** Name of the IndexedDb object store. */\n static store = 'collectionParents';\n\n /** Keys are automatically assigned via the collectionId, parent properties. */\n static keyPath = ['collectionId', 'parent'];\n\n constructor(\n /**\n * The collectionId (e.g. 'messages')\n */\n public collectionId: string,\n /**\n * The path to the parent (either a document location or an empty path for\n * a root-level collection).\n */\n public parent: EncodedResourcePath\n ) {}\n}\n\nfunction createQueryCache(db: IDBDatabase): void {\n const targetDocumentsStore = db.createObjectStore(DbTargetDocument.store, {\n keyPath: DbTargetDocument.keyPath\n });\n targetDocumentsStore.createIndex(\n DbTargetDocument.documentTargetsIndex,\n DbTargetDocument.documentTargetsKeyPath,\n { unique: true }\n );\n\n const targetStore = db.createObjectStore(DbTarget.store, {\n keyPath: DbTarget.keyPath\n });\n\n // NOTE: This is unique only because the TargetId is the suffix.\n targetStore.createIndex(\n DbTarget.queryTargetsIndexName,\n DbTarget.queryTargetsKeyPath,\n { unique: true }\n );\n db.createObjectStore(DbTargetGlobal.store);\n}\n\nfunction dropQueryCache(db: IDBDatabase): void {\n db.deleteObjectStore(DbTargetDocument.store);\n db.deleteObjectStore(DbTarget.store);\n db.deleteObjectStore(DbTargetGlobal.store);\n}\n\nfunction dropRemoteDocumentChangesStore(db: IDBDatabase): void {\n if (db.objectStoreNames.contains('remoteDocumentChanges')) {\n db.deleteObjectStore('remoteDocumentChanges');\n }\n}\n\n/**\n * Creates the target global singleton row.\n *\n * @param {IDBTransaction} txn The version upgrade transaction for indexeddb\n */\nfunction writeEmptyTargetGlobalEntry(\n txn: SimpleDbTransaction\n): PersistencePromise<void> {\n const globalStore = txn.store<DbTargetGlobalKey, DbTargetGlobal>(\n DbTargetGlobal.store\n );\n const metadata = new DbTargetGlobal(\n /*highestTargetId=*/ 0,\n /*lastListenSequenceNumber=*/ 0,\n SnapshotVersion.min().toTimestamp(),\n /*targetCount=*/ 0\n );\n return globalStore.put(DbTargetGlobal.key, metadata);\n}\n\n/**\n * Creates indices on the RemoteDocuments store used for both multi-tab\n * and Index-Free queries.\n */\nfunction createRemoteDocumentReadTimeIndex(txn: IDBTransaction): void {\n const remoteDocumentStore = txn.objectStore(DbRemoteDocument.store);\n remoteDocumentStore.createIndex(\n DbRemoteDocument.readTimeIndex,\n DbRemoteDocument.readTimeIndexPath,\n { unique: false }\n );\n remoteDocumentStore.createIndex(\n DbRemoteDocument.collectionReadTimeIndex,\n DbRemoteDocument.collectionReadTimeIndexPath,\n { unique: false }\n );\n}\n\n/**\n * A record of the metadata state of each client.\n *\n * PORTING NOTE: This is used to synchronize multi-tab state and does not need\n * to be ported to iOS or Android.\n */\nexport class DbClientMetadata {\n /** Name of the IndexedDb object store. */\n static store = 'clientMetadata';\n\n /** Keys are automatically assigned via the clientId properties. */\n static keyPath = 'clientId';\n\n constructor(\n // Note: Previous schema versions included a field\n // \"lastProcessedDocumentChangeId\". Don't use anymore.\n\n /** The auto-generated client id assigned at client startup. */\n public clientId: string,\n /** The last time this state was updated. */\n public updateTimeMs: number,\n /** Whether the client's network connection is enabled. */\n public networkEnabled: boolean,\n /** Whether this client is running in a foreground tab. */\n public inForeground: boolean\n ) {}\n}\n\n/** Object keys in the 'clientMetadata' store are clientId strings. */\nexport type DbClientMetadataKey = string;\n\nfunction createClientMetadataStore(db: IDBDatabase): void {\n db.createObjectStore(DbClientMetadata.store, {\n keyPath: DbClientMetadata.keyPath\n });\n}\n\n// Visible for testing\nexport const V1_STORES = [\n DbMutationQueue.store,\n DbMutationBatch.store,\n DbDocumentMutation.store,\n DbRemoteDocument.store,\n DbTarget.store,\n DbPrimaryClient.store,\n DbTargetGlobal.store,\n DbTargetDocument.store\n];\n\n// V2 is no longer usable (see comment at top of file)\n\n// Visible for testing\nexport const V3_STORES = V1_STORES;\n\n// Visible for testing\n// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.\nexport const V4_STORES = [...V3_STORES, DbClientMetadata.store];\n\n// V5 does not change the set of stores.\n\nexport const V6_STORES = [...V4_STORES, DbRemoteDocumentGlobal.store];\n\n// V7 does not change the set of stores.\n\nexport const V8_STORES = [...V6_STORES, DbCollectionParent.store];\n\n// V9 does not change the set of stores.\n\n// V10 does not change the set of stores.\n\n/**\n * The list of all default IndexedDB stores used throughout the SDK. This is\n * used when creating transactions so that access across all stores is done\n * atomically.\n */\nexport const ALL_STORES = V8_STORES;\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 { 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 { 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 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 { BatchId, MutationBatchState, TargetId } from '../core/types';\nimport { QueryTargetState } from './shared_client_state_syncer';\nimport { debugAssert } from '../util/assert';\nimport { ClientId } from './shared_client_state';\nimport { User } from '../auth/user';\n\n// The format of the LocalStorage key that stores the client state is:\n// firestore_clients_<persistence_prefix>_<instance_key>\nexport const CLIENT_STATE_KEY_PREFIX = 'firestore_clients';\n\n/** Assembles the key for a client state in WebStorage */\nexport function createWebStorageClientStateKey(\n persistenceKey: string,\n clientId: ClientId\n): string {\n debugAssert(\n clientId.indexOf('_') === -1,\n `Client key cannot contain '_', but was '${clientId}'`\n );\n\n return `${CLIENT_STATE_KEY_PREFIX}_${persistenceKey}_${clientId}`;\n}\n\n/**\n * The JSON representation of a clients's metadata as used during WebStorage\n * serialization. The ClientId is omitted here as it is encoded as part of the\n * key.\n */\nexport interface ClientStateSchema {\n activeTargetIds: number[];\n updateTimeMs: number;\n}\n\n// The format of the WebStorage key that stores the mutation state is:\n// firestore_mutations_<persistence_prefix>_<batch_id>\n// (for unauthenticated users)\n// or: firestore_mutations_<persistence_prefix>_<batch_id>_<user_uid>\n//\n// 'user_uid' is last to avoid needing to escape '_' characters that it might\n// contain.\nexport const MUTATION_BATCH_KEY_PREFIX = 'firestore_mutations';\n\n/** Assembles the key for a mutation batch in WebStorage */\nexport function createWebStorageMutationBatchKey(\n persistenceKey: string,\n user: User,\n batchId: BatchId\n): string {\n let mutationKey = `${MUTATION_BATCH_KEY_PREFIX}_${persistenceKey}_${batchId}`;\n\n if (user.isAuthenticated()) {\n mutationKey += `_${user.uid}`;\n }\n\n return mutationKey;\n}\n\n/**\n * The JSON representation of a mutation batch's metadata as used during\n * WebStorage serialization. The UserId and BatchId is omitted as it is\n * encoded as part of the key.\n */\nexport interface MutationMetadataSchema {\n state: MutationBatchState;\n error?: { code: string; message: string }; // Only set when state === 'rejected'\n updateTimeMs: number;\n}\n\n// The format of the WebStorage key that stores a query target's metadata is:\n// firestore_targets_<persistence_prefix>_<target_id>\nexport const QUERY_TARGET_KEY_PREFIX = 'firestore_targets';\n\n/** Assembles the key for a query state in WebStorage */\nexport function createWebStorageQueryTargetMetadataKey(\n persistenceKey: string,\n targetId: TargetId\n): string {\n return `${QUERY_TARGET_KEY_PREFIX}_${persistenceKey}_${targetId}`;\n}\n\n/**\n * The JSON representation of a query target's state as used during WebStorage\n * serialization. The TargetId is omitted as it is encoded as part of the key.\n */\nexport interface QueryTargetStateSchema {\n state: QueryTargetState;\n error?: { code: string; message: string }; // Only set when state === 'rejected'\n updateTimeMs: number;\n}\n\n// The WebStorage prefix that stores the primary tab's online state. The\n// format of the key is:\n// firestore_online_state_<persistence_prefix>\nexport const ONLINE_STATE_KEY_PREFIX = 'firestore_online_state';\n\n/** Assembles the key for the online state of the primary tab. */\nexport function createWebStorageOnlineStateKey(persistenceKey: string): string {\n return `${ONLINE_STATE_KEY_PREFIX}_${persistenceKey}`;\n}\n\n/**\n * The JSON representation of the system's online state, as written by the\n * primary client.\n */\nexport interface SharedOnlineStateSchema {\n /**\n * The clientId of the client that wrote this onlineState value. Tracked so\n * that on startup, clients can check if this client is still active when\n * determining whether to apply this value or not.\n */\n readonly clientId: string;\n readonly onlineState: string;\n}\n\n// The WebStorage key prefix for the key that stores the last sequence number allocated. The key\n// looks like 'firestore_sequence_number_<persistence_prefix>'.\nexport const SEQUENCE_NUMBER_KEY_PREFIX = 'firestore_sequence_number';\n\n/** Assembles the key for the current sequence number. */\nexport function createWebStorageSequenceNumberKey(\n persistenceKey: string\n): string {\n return `${SEQUENCE_NUMBER_KEY_PREFIX}_${persistenceKey}`;\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 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 { 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 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';\nimport { FirebaseNamespace } from '@firebase/app-types';\n\nimport { Firestore } from './src/api/database';\nimport { IndexedDbComponentProvider } from './src/core/component_provider';\nimport { configureForFirebase } from './src/platform/config';\nimport { name, version } from './package.json';\n\nimport './register-module';\nimport './src/platform_browser/browser_init';\n\n/**\n * Registers the main Firestore build with the components framework.\n * Persistence can be enabled via `firebase.firestore().enablePersistence()`.\n */\nexport function registerFirestore(instance: FirebaseNamespace): void {\n configureForFirebase(\n instance,\n (app, auth) => new Firestore(app, auth, new IndexedDbComponentProvider())\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","I","__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","isSafeInteger","isInteger","MAX_SAFE_INTEGER","MIN_SAFE_INTEGER","__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","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","Tt","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","fs","__PRIVATE__current","__PRIVATE__resumeToken","ys","__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","bytes","toUint8Array","__PRIVATE_toTimestamp","__PRIVATE_fromTimestamp","__PRIVATE_fullyQualifiedPrefixPath","__PRIVATE_resource","__PRIVATE_isValidResourceName","__PRIVATE_toResourceName","__PRIVATE_fromResourceName","__PRIVATE_extractLocalPathFromResourceName","__PRIVATE_resourceName","wi","__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","__PRIVATE_immediateSuccessor","persistenceKey","host","ssl","forceLongPolling","ln","__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","__PRIVATE__readTime","__PRIVATE_maybeDocument","__PRIVATE_assertNotApplied","transaction","__PRIVATE_documentKey","__PRIVATE_bufferedEntry","__PRIVATE_getFromCache","__PRIVATE_documentKeys","__PRIVATE_getAllFromCache","__PRIVATE_changesApplied","__PRIVATE_applyChanges","__PRIVATE_PRIMARY_LEASE_LOST_ERROR_MSG","__PRIVATE_onCommittedListeners","__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_ObjectMap","__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","__PRIVATE_encodeResourcePath","__PRIVATE_encodeSeparator","__PRIVATE_encodeSegment","__PRIVATE_resultBuf","__PRIVATE_escapeChar","__PRIVATE_decodeResourcePath","__PRIVATE_lastReasonableEscapeIndex","__PRIVATE_segmentBuilder","__PRIVATE_currentPiece","substring","collectionPath","__PRIVATE_collectionParentIndex","parentPath","__PRIVATE_existingParents","__PRIVATE_added","__PRIVATE_collectionParentsCache","__PRIVATE_addOnCommittedListener","__PRIVATE_collectionParent","__PRIVATE_collectionParentsStore","__PRIVATE_parentPaths","IDBKeyRange","__PRIVATE_loadAll","__PRIVATE_entry","__PRIVATE_MemoryCollectionParentIndex","txn","__PRIVATE_IndexedDbPersistence","__PRIVATE_getStore","DbCollectionParent","store","__PRIVATE_remoteDocumentsStore","__PRIVATE_dbKey","__PRIVATE_sizeDelta","getMetadata","byteSize","__PRIVATE_setMetadata","__PRIVATE_dbRemoteDoc","__PRIVATE_maybeDecodeDocument","Mr","__PRIVATE_dbDocumentSize","__PRIVATE_forEachDbEntry","__PRIVATE_sizeMap","Br","qr","last","__PRIVATE_keyIter","__PRIVATE_nextKey","__PRIVATE_iterate","__PRIVATE_potentialKeyRaw","control","__PRIVATE_potentialKey","__PRIVATE_fromSegments","__PRIVATE_skip","__PRIVATE_immediateChildrenPathLength","__PRIVATE_iterationOptions","lowerBound","__PRIVATE_collectionKey","__PRIVATE_readTimeKey","__PRIVATE_toDbTimestampKey","DbRemoteDocument","collectionReadTimeIndex","__PRIVATE_fromDbRemoteDocument","__PRIVATE_changedDocs","__PRIVATE_lastReadTime","__PRIVATE_documentsStore","readTimeIndex","Gr","__PRIVATE_fromDbTimestampKey","reverse","__PRIVATE_IndexedDbRemoteDocumentCache","__PRIVATE_RemoteDocumentChangeBuffer","__PRIVATE_trackRemovals","__PRIVATE_documentGlobalStore","DbRemoteDocumentGlobal","unknownDocument","noDocument","JSON","stringify","__PRIVATE_documentCache","__PRIVATE_collectionParents","__PRIVATE_previousSize","__PRIVATE_documentSizes","__PRIVATE_toDbRemoteDocument","__PRIVATE_addEntry","__PRIVATE_deletedDoc","__PRIVATE_removeEntry","__PRIVATE_addToCollectionParentIndex","updateMetadata","__PRIVATE_getSizedEntry","__PRIVATE_getResult","__PRIVATE_getSizedEntries","__PRIVATE_maybeDocuments","__PRIVATE_lastId","__PRIVATE_TargetIdGenerator","__PRIVATE_retrieveMetadata","__PRIVATE_targetIdGenerator","highestTargetId","__PRIVATE_saveMetadata","lastRemoteSnapshotVersion","__PRIVATE_targetGlobal","highestListenSequenceNumber","__PRIVATE_saveTargetData","targetCount","__PRIVATE_updateMetadataFromTargetData","__PRIVATE_removeMatchingKeysForTargetId","__PRIVATE_targetsStore","upperBound","activeTargetIds","__PRIVATE_fromDbTarget","__PRIVATE_removeTargetData","__PRIVATE_globalTargetStore","DbTargetGlobal","__PRIVATE_toDbTarget","updated","NEGATIVE_INFINITY","POSITIVE_INFINITY","DbTarget","queryTargetsIndexName","__PRIVATE_documentTargetStore","DbTargetDocument","__PRIVATE_referenceDelegate","__PRIVATE_addReference","__PRIVATE_removeReference","Dh","documentTargetsIndex","__PRIVATE_remoteDoc","__PRIVATE_remoteSerializer","__PRIVATE_fromDocument","__PRIVATE_fromDbTimestamp","__PRIVATE_dbReadTime","__PRIVATE_toDocument","__PRIVATE_toDbTimestamp","DbNoDocument","DbUnknownDocument","__PRIVATE_dbTimestampKey","DbTimestamp","__PRIVATE_dbTimestamp","userId","__PRIVATE_serializedBaseMutations","__PRIVATE_toMutation","__PRIVATE_serializedMutations","DbMutationBatch","__PRIVATE_dbBatch","__PRIVATE_fromMutation","localWriteTimeMs","__PRIVATE_MutationBatch","__PRIVATE_dbTarget","__PRIVATE_fromDocumentsTarget","__PRIVATE_fromQueryTarget","lastListenSequenceNumber","__PRIVATE_queryProto","__PRIVATE_dbLastLimboFreeTimestamp","__PRIVATE_PRIMARY_LEASE_EXCLUSIVE_ERROR_MSG","__PRIVATE_PersistenceTransaction","__PRIVATE_IndexedDbTransaction","__PRIVATE_SimpleDb","__PRIVATE_simpleDbTransaction","__PRIVATE_openOrCreate","__PRIVATE_dbName","SCHEMA_VERSION","SchemaConverter","db","__PRIVATE_simpleDb","__PRIVATE_updateClientMetadataAndTryBecomePrimary","isPrimary","allowTabSynchronization","__PRIVATE_attachVisibilityHandler","__PRIVATE_attachWindowUnloadHook","__PRIVATE_scheduleClientMetadataAndPrimaryLeaseRefreshes","runTransaction","__PRIVATE_targetCache","__PRIVATE_getHighestSequenceNumber","__PRIVATE_listenSequence","__PRIVATE_ListenSequence","__PRIVATE__started","catch","reason","close","__PRIVATE_primaryStateListener","async","__PRIVATE_primaryState","__PRIVATE_started","__PRIVATE_databaseDeletedListener","__PRIVATE_setVersionChangeListener","event","newVersion","networkEnabled","__PRIVATE_enqueueAndForget","__PRIVATE_clientMetadataStore","DbClientMetadata","clientId","inForeground","__PRIVATE_verifyPrimaryLease","__PRIVATE_success","__PRIVATE_canActAsPrimary","__PRIVATE_releasePrimaryLeaseIfHeld","__PRIVATE_acquireOrExtendPrimaryLease","__PRIVATE_isIndexedDbTransactionError","__PRIVATE_primaryClientStore","DbPrimaryClient","__PRIVATE_primaryClient","__PRIVATE_isLocalClient","__PRIVATE_isWithinAge","__PRIVATE_lastGarbageCollectionTime","__PRIVATE_metadataStore","__PRIVATE_existingClients","active","__PRIVATE_filterActiveClients","__PRIVATE_inactive","__PRIVATE_client","__PRIVATE_inactiveClient","window","localStorage","removeItem","__PRIVATE_zombiedClientLocalStorageKey","__PRIVATE_clientMetadataRefresher","__PRIVATE_maybeGarbageCollectMultiClientState","ownerId","__PRIVATE_currentPrimary","leaseTimestampMs","__PRIVATE_isClientZombied","__PRIVATE_otherClient","__PRIVATE_otherClientHasBetterNetworkState","__PRIVATE_otherClientHasBetterVisibility","__PRIVATE_otherClientHasSameNetworkState","__PRIVATE_markClientZombied","__PRIVATE_detachVisibilityHandler","__PRIVATE_detachWindowUnloadHook","__PRIVATE_removeClientMetadata","__PRIVATE_removeClientZombiedEntry","__PRIVATE_clients","__PRIVATE_activityThresholdMs","updateTimeMs","__PRIVATE_clientMetadata","__PRIVATE_isAvailable","__PRIVATE_MAIN_DATABASE","ro","__PRIVATE_IndexedDbMutationQueue","__PRIVATE_forUser","__PRIVATE_transactionOperation","__PRIVATE_persistenceTransaction","__PRIVATE_simpleDbMode","ALL_STORES","__PRIVATE_simpleDbTxn","__PRIVATE_INVALID","__PRIVATE_holdsPrimaryLease","__PRIVATE_verifyAllowTabSynchronization","__PRIVATE_raiseOnCommittedEvent","__PRIVATE_newPrimary","__PRIVATE_databaseInfo","__PRIVATE_isDefaultDatabase","__PRIVATE_maxAgeMs","addEventListener","__PRIVATE_documentVisibilityHandler","visibilityState","removeEventListener","__PRIVATE_windowUnloadHandler","__PRIVATE_shutdown","__PRIVATE_isZombied","__PRIVATE_webStorage","getItem","setItem","__PRIVATE_lruParams","__PRIVATE_IndexedDbLruDelegate","LocalSerializer","__PRIVATE_IndexedDbTargetCache","__PRIVATE_IndexedDbIndexManager","__PRIVATE_currentSequenceNumber","__PRIVATE_docCountPromise","__PRIVATE_orphanedDocumentCount","__PRIVATE_getTargetCache","__PRIVATE_getTargetCount","__PRIVATE_docCount","__PRIVATE_orphanedCount","__PRIVATE_forEachOrphanedDocumentSequenceNumber","__PRIVATE_forEachOrphanedDocument","__PRIVATE_writeSentinelKey","__PRIVATE_removeTargets","__PRIVATE_mutationQueuesStore","__PRIVATE_iterateSerial","__PRIVATE_mutationQueueContainsKey","__PRIVATE_containsKey","__PRIVATE_changeBuffer","__PRIVATE_getRemoteDocumentCache","__PRIVATE_newChangeBuffer","__PRIVATE_documentCount","__PRIVATE_isPinned","__PRIVATE_withSequenceNumber","__PRIVATE_updateTargetData","__PRIVATE_nextPath","__PRIVATE_nextToReport","__PRIVATE_getSize","__PRIVATE_params","__PRIVATE_garbageCollector","__PRIVATE_LruGarbageCollector","__PRIVATE_mutationsStore","userMutationsIndex","__PRIVATE_getMutationQueueMetadata","lastStreamToken","__PRIVATE_documentStore","__PRIVATE_documentMutationsStore","__PRIVATE_mutationStore","__PRIVATE_toDbMutationBatch","__PRIVATE_indexKey","DbDocumentMutation","PLACEHOLDER","__PRIVATE_documentKeysByBatchId","__PRIVATE_fromDbMutationBatch","__PRIVATE_lookupMutationBatch","__PRIVATE_nextBatchId","__PRIVATE_foundBatch","__PRIVATE_dbBatches","__PRIVATE_indexPrefix","prefixForPath","__PRIVATE_indexStart","__PRIVATE_userID","__PRIVATE_encodedPath","__PRIVATE_uniqueBatchIDs","__PRIVATE_batchID","__PRIVATE_lookupMutationBatches","__PRIVATE_queryPath","__PRIVATE_immediateChildrenLength","__PRIVATE_batchIDs","__PRIVATE_removeMutationBatch","__PRIVATE_removeCachedMutationKeys","__PRIVATE_markPotentiallyOrphaned","__PRIVATE_checkEmpty","__PRIVATE_startRange","prefixForUser","__PRIVATE_danglingMutationReferences","DbMutationQueue","keyPath","__PRIVATE_indexTxn","only","__PRIVATE_numDeleted","__PRIVATE_removePromise","__PRIVATE_SimpleDbTransaction","createObjectStore","autoIncrement","createIndex","userMutationsKeyPath","unique","__PRIVATE_createQueryCache","deleteObjectStore","__PRIVATE_globalStore","__PRIVATE_existingMutations","__PRIVATE_v3MutationsStore","__PRIVATE_writeAll","removeAcknowledgedMutations","addDocumentGlobal","ensureSequenceNumbers","createCollectionParentIndex","__PRIVATE_remoteDocumentStore","objectStoreNames","contains","objectStore","readTimeIndexPath","collectionReadTimeIndexPath","rewriteCanonicalIds","__PRIVATE_byteCount","__PRIVATE_queuesStore","__PRIVATE_queues","lastAcknowledgedBatchId","__PRIVATE_docSentinelKey","__PRIVATE_maybeSentinel","cache","__PRIVATE_pathSegments","__PRIVATE_targetStore","__PRIVATE_originalDbTarget","__PRIVATE_originalTargetData","__PRIVATE_updatedDbTarget","documentTargetsKeyPath","queryTargetsKeyPath","__PRIVATE_schemaConverter","indexedDB","open","onsuccess","onblocked","onerror","onupgradeneeded","oldVersion","createOrUpgrade","__PRIVATE_toPromise","__PRIVATE_wrapRequest","deleteDatabase","__PRIVATE_isMockPersistence","__PRIVATE_ua","__PRIVATE_iOSVersion","__PRIVATE_getIOSVersion","__PRIVATE_isUnsupportedIOS","__PRIVATE_androidVersion","__PRIVATE_getAndroidVersion","__PRIVATE_isUnsupportedAndroid","__PRIVATE_process","__PRIVATE_env","__PRIVATE_USE_MOCK_PERSISTENCE","__PRIVATE_iOSVersionRegex","match","__PRIVATE_androidVersionRegex","__PRIVATE_versionChangeListener","onversionchange","__PRIVATE_objectStores","__PRIVATE_transactionFn","__PRIVATE_readonly","__PRIVATE_attemptNumber","__PRIVATE_transactionFnResult","abort","__PRIVATE_completionPromise","__PRIVATE_retryable","Pn","__PRIVATE_shouldStop","Ea","__PRIVATE_dbCursor","la","__PRIVATE_completionDeferred","aborted","__PRIVATE_storeName","__PRIVATE_SimpleDbStore","__PRIVATE_keyOrValue","__PRIVATE_indexOrRange","__PRIVATE_iterateCursor","__PRIVATE_keysOnly","__PRIVATE_optionsOrCallback","__PRIVATE_cursorRequest","__PRIVATE_checkForAndReportiOSError","primaryKey","__PRIVATE_shouldContinue","continue","controller","__PRIVATE_IterationController","__PRIVATE_userResult","__PRIVATE_userPromise","__PRIVATE_skipToKey","__PRIVATE_indexName","openKeyCursor","openCursor","__PRIVATE_Deferred","oncomplete","onabort","__PRIVATE_IndexedDbTransactionError","__PRIVATE_reportedIOSError","__PRIVATE_IOS_ERROR","__PRIVATE_newError","setTimeout","__PRIVATE_asyncQueue","__PRIVATE_delayMs","__PRIVATE_removalCallback","__PRIVATE_delayedOp","__PRIVATE_DelayedOperation","__PRIVATE_timerHandle","__PRIVATE_handleDelayElapsed","clearTimeout","__PRIVATE_deferred","La","__PRIVATE__isShuttingDown","enqueue","__PRIVATE_verifyNotFailed","__PRIVATE_enqueueInternal","__PRIVATE_visibilityHandler","__PRIVATE_enqueueEvenAfterShutdown","__PRIVATE_retryableTail","__PRIVATE_retryingOp","__PRIVATE_backoff","__PRIVATE_backoffAndRun","__PRIVATE_newTail","__PRIVATE_tail","__PRIVATE_operationInProgress","stack","__PRIVATE_timerIdsToSkip","__PRIVATE_createAndSchedule","__PRIVATE_removedOp","__PRIVATE_removeDelayedOperation","__PRIVATE_delayedOperations","__PRIVATE_currentTail","__PRIVATE_lastTimerId","__PRIVATE_drain","__PRIVATE_targetTimeMs","__PRIVATE_ExponentialBackoff","__PRIVATE_skipBackoff","bind","__PRIVATE_wrapInUserErrorIfRecoverable","__PRIVATE_bufferEntryComparator","__PRIVATE_aSequence","__PRIVATE_aIndex","__PRIVATE_bSequence","__PRIVATE_bIndex","__PRIVATE_seqCmp","__PRIVATE_previousIndex","__PRIVATE_nextIndex","__PRIVATE_maxElements","__PRIVATE_highestValue","maxValue","__PRIVATE_GC_DID_NOT_RUN","Za","tu","eu","su","__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_localStore","__PRIVATE_scheduleGC","__PRIVATE_gcTask","delay","__PRIVATE_hasRun","__PRIVATE_collectGarbage","__PRIVATE_ignoreIfPrimaryLeaseLoss","__PRIVATE_percentile","__PRIVATE_delegate","__PRIVATE_getSequenceNumberCount","__PRIVATE_RollingSequenceNumberBuffer","__PRIVATE_addElement","__PRIVATE_removeOrphanedDocuments","__PRIVATE_getCacheSize","__PRIVATE_runGarbageCollection","__PRIVATE_upperBoundSequenceNumber","__PRIVATE_sequenceNumbersToCollect","__PRIVATE_targetsRemoved","__PRIVATE_countedTargetsTs","__PRIVATE_foundUpperBoundTs","__PRIVATE_removedTargetsTs","__PRIVATE_removedDocumentsTs","__PRIVATE_startTs","__PRIVATE_calculateTargetCount","__PRIVATE_sequenceNumbers","__PRIVATE_nthSequenceNumber","__PRIVATE_numTargetsRemoved","__PRIVATE_documentsRemoved","__PRIVATE_newMutationQueue","__PRIVATE_newLocalDocuments","__PRIVATE_localDocuments","persistence","__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","Cu","Fu","Nu","__PRIVATE_queryEngine","__PRIVATE_setLocalDocumentsView","__PRIVATE_existingDocs","__PRIVATE_extractBaseValue","__PRIVATE_addMutationBatch","__PRIVATE_applyToLocalDocumentSet","Dn","__PRIVATE_affected","__PRIVATE_documentBuffer","Xr","__PRIVATE_acknowledgeBatch","__PRIVATE_applyWriteToRemoteDocuments","__PRIVATE_performConsistencyCheck","__PRIVATE_affectedKeys","__PRIVATE_getHighestUnacknowledgedBatchId","__PRIVATE_getLastStreamToken","__PRIVATE_setLastStreamToken","__PRIVATE_getLastRemoteSnapshotVersion","__PRIVATE_remoteVersion","__PRIVATE_newTargetDataByTargetMap","__PRIVATE_targetDataByTarget","__PRIVATE_oldTargetData","__PRIVATE_removeMatchingKeys","__PRIVATE_addMatchingKeys","__PRIVATE_newTargetData","__PRIVATE_withResumeToken","__PRIVATE_LocalStore","__PRIVATE_shouldPersistTargetData","__PRIVATE_updatedKeys","__PRIVATE_existingDoc","__PRIVATE_updateLimboDocument","__PRIVATE_updateRemoteVersion","__PRIVATE_setTargetsMetadata","__PRIVATE_toMicroseconds","__PRIVATE_RESUME_TOKEN_MAX_AGE_MICROS","__PRIVATE_viewChanges","__PRIVATE_viewChange","e_59","__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","Ku","__PRIVATE_docKeys","__PRIVATE_promiseChain","__PRIVATE_ackVersion","__PRIVATE_collect","__PRIVATE_initialUser","__PRIVATE_synchronizeLastDocumentChangeReadTime","__PRIVATE_lookupMutationKeys","__PRIVATE_setNetworkEnabled","__PRIVATE_getActiveClients","__PRIVATE_cachedTargetData","__PRIVATE_getNewDocumentChanges","__PRIVATE_lastDocumentChangeReadTime","__PRIVATE_getLastReadTime","__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","__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","dc","methodName","Ec","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","__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","__PRIVATE_performBackoff","__PRIVATE_isStarted","__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","o_","__PRIVATE_handshakeComplete_","__PRIVATE_writeMutations","__PRIVATE_responseProto","__PRIVATE_fromWriteResults","writeResults","__PRIVATE_onMutationResult","__PRIVATE_onHandshakeComplete","writes","__PRIVATE_rpcName","credentials","__PRIVATE_invokeRPC","__PRIVATE_invokeStreamingRPC","__PRIVATE_ensureCommitNotCalled","__PRIVATE_datastore","__PRIVATE_datastoreImpl","__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","__PRIVATE_enableNetworkInternal","__PRIVATE_canUseNetwork","__PRIVATE_writeStream","__PRIVATE_shouldStartWatchStream","__PRIVATE_startWatchStream","__PRIVATE_onlineStateTracker","__PRIVATE_fillWritePipeline","__PRIVATE_disableNetworkInternal","__PRIVATE_watchStream","__PRIVATE_writePipeline","__PRIVATE_cleanUpWatchStreamState","__PRIVATE_connectivityMonitor","__PRIVATE_listenTargets","__PRIVATE_sendWatchRequest","__PRIVATE_sendUnwatchRequest","__PRIVATE_markIdle","__PRIVATE_syncEngine","__PRIVATE_watchChangeAggregator","__PRIVATE_watch","__PRIVATE_unwatch","__PRIVATE_WatchChangeAggregator","__PRIVATE_handleWatchStreamStart","__PRIVATE_indexedDbFailed","__PRIVATE_handleWatchStreamFailure","__PRIVATE_handleTargetError","__PRIVATE_disableNetworkUntilRecovery","__PRIVATE_handleDocumentChange","__PRIVATE_handleExistenceFilter","__PRIVATE_handleTargetChange","__PRIVATE_raiseWatchSnapshot","__PRIVATE_enqueueRetryable","__PRIVATE_createRemoteEvent","__PRIVATE_requestTargetData","__PRIVATE_applyRemoteEvent","s_58","__PRIVATE_rejectListen","__PRIVATE_canAddToWritePipeline","__PRIVATE_lastBatchIdRetrieved","__PRIVATE_nextMutationBatch","__PRIVATE_addToWritePipeline","__PRIVATE_shouldStartWriteStream","__PRIVATE_startWriteStream","__PRIVATE_handshakeComplete","__PRIVATE_writeHandshake","shift","__PRIVATE_applySuccessfulWrite","__PRIVATE_handleWriteError","__PRIVATE_handleHandshakeError","__PRIVATE_inhibitBackoff","__PRIVATE_rejectFailedWrite","Transaction","__PRIVATE_restartNetwork","__PRIVATE_addCallback","__PRIVATE_OnlineStateTracker","e_","__PRIVATE_onWatchStreamOpen","Yc","__PRIVATE_onWatchStreamClose","i_","__PRIVATE_onWatchStreamChange","__PRIVATE_PersistentListenStream","__PRIVATE_onWriteStreamOpen","__PRIVATE_onWriteStreamClose","c_","__PRIVATE_onWriteHandshakeComplete","u_","__PRIVATE_PersistentWriteStream","__PRIVATE_connectionTimerId","createWebStorageClientStateKey","createWebStorageMutationBatchKey","__PRIVATE_mutationKey","createWebStorageQueryTargetMetadataKey","view","__PRIVATE_mutationBatch","parse","__PRIVATE_validData","__PRIVATE_firestoreError","__PRIVATE_MutationMetadata","__PRIVATE_batchMetadata","__PRIVATE_QueryTargetMetadata","__PRIVATE_clientState","__PRIVATE_activeTargetIdsSet","__PRIVATE_RemoteClientState","onlineState","__PRIVATE_SharedOnlineState","e_71","__PRIVATE_localClientId","__PRIVATE_storageItem","__PRIVATE_fromWebStorageEntry","__PRIVATE_activeClients","__PRIVATE_persistClientState","__PRIVATE_onlineStateJSON","storage","__PRIVATE_onlineStateKey","__PRIVATE_fromWebStorageOnlineState","__PRIVATE_handleOnlineStateEvent","__PRIVATE_earlyEvents","t_65","__PRIVATE_handleWebStorageEvent","__PRIVATE_sequenceNumberKey","__PRIVATE_extractActiveQueryTargets","__PRIVATE_persistMutationState","__PRIVATE_removeMutationState","__PRIVATE_queryState","__PRIVATE_isActiveQueryTarget","__PRIVATE_localClientState","__PRIVATE_addQueryTarget","__PRIVATE_removeQueryTarget","__PRIVATE_persistQueryTargetState","__PRIVATE_addPendingMutation","__PRIVATE_persistOnlineState","__PRIVATE_storageListener","__PRIVATE_localClientStorageKey","storageArea","__PRIVATE_clientStateKeyRe","__PRIVATE_fromWebStorageClientStateKey","__PRIVATE_handleClientStateEvent","__PRIVATE_fromWebStorageClientState","__PRIVATE_mutationBatchKeyRe","__PRIVATE_mutationMetadata","__PRIVATE_fromWebStorageMutationMetadata","__PRIVATE_handleMutationBatchEvent","__PRIVATE_queryTargetKeyRe","__PRIVATE_queryTargetMetadata","__PRIVATE_fromWebStorageQueryTargetMetadata","__PRIVATE_handleQueryTargetEvent","__PRIVATE_seqString","__PRIVATE_parsed","Yl","__PRIVATE_toWebStorageJSON","__PRIVATE_mutationState","__PRIVATE_targetKey","__PRIVATE_targetMetadata","__PRIVATE_applyBatchState","__PRIVATE_applyTargetState","__PRIVATE_updatedClients","__PRIVATE_existingTargets","__PRIVATE_newTargets","__PRIVATE_addedTargets","__PRIVATE_removedTargets","__PRIVATE_applyActiveTargetsChange","__PRIVATE_activeTargets","__PRIVATE_kev","__PRIVATE_unionWith","__PRIVATE_localState","__PRIVATE_LocalClientState","Pd","__PRIVATE__syncedDocuments","__PRIVATE_previousChanges","__PRIVATE_changeSet","__PRIVATE_DocumentChangeSet","__PRIVATE_oldDocumentSet","__PRIVATE_documentSet","__PRIVATE_newMutatedKeys","__PRIVATE_newDocumentSet","__PRIVATE_needsRefill","__PRIVATE_lastDocInLimit","__PRIVATE_hasLimitToFirst","__PRIVATE_firstDocInLimit","__PRIVATE_hasLimitToLast","__PRIVATE_newMaybeDoc","__PRIVATE_oldDoc","__PRIVATE_oldDocHadPendingMutations","__PRIVATE_newDocHasPendingMutations","__PRIVATE_changeApplied","track","__PRIVATE_shouldWaitForSyncedDocument","__PRIVATE_docComparator","md","gd","yd","ns","__PRIVATE_updateLimboDocuments","__PRIVATE_getChanges","__PRIVATE_c1","__PRIVATE_c2","__PRIVATE_applyTargetChange","__PRIVATE_limboChanges","__PRIVATE_newSyncState","__PRIVATE_limboDocuments","__PRIVATE_syncState","Sd","__PRIVATE_oldLimboDocuments","__PRIVATE_shouldBeInLimbo","__PRIVATE_RemovedLimboDocument","__PRIVATE_AddedLimboDocument","__PRIVATE_queryResult","__PRIVATE_computeDocChanges","__PRIVATE_fromInitialDocuments","__PRIVATE_runWithBackOff","__PRIVATE_remoteStore","__PRIVATE_createTransaction","__PRIVATE_tryRunUpdateFunction","commit","__PRIVATE_commitError","__PRIVATE_handleTransactionError","__PRIVATE_userPromiseError","updateFunction","__PRIVATE_retries","__PRIVATE_isRetryableTransactionError","tf","__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_releaseTarget","__PRIVATE_clearQueryState","__PRIVATE_unlisten","__PRIVATE_removeAndCleanupTarget","__PRIVATE_userCallback","__PRIVATE_localWrite","__PRIVATE_addMutationCallback","__PRIVATE_emitNewSnapsAndNotifyLocalStore","__PRIVATE_TransactionRunner","__PRIVATE_run","__PRIVATE_limboResolution","__PRIVATE_activeLimboResolutionsByTarget","__PRIVATE_receivedDocument","__PRIVATE_newViewSnapshots","__PRIVATE_applyOnlineStateChange","__PRIVATE_onOnlineStateChange","__PRIVATE_updateQueryState","__PRIVATE_limboKey","__PRIVATE_activeLimboTargetsByKey","__PRIVATE_pumpEnqueuedLimboResolutions","__PRIVATE_mutationBatchResult","__PRIVATE_processUserCallback","__PRIVATE_triggerPendingWritesCallbacks","__PRIVATE_updateMutationState","__PRIVATE_rejectBatch","__PRIVATE_highestBatchId","__PRIVATE_callbacks","__PRIVATE_pendingWritesCallbacks","__PRIVATE_errorMessage","clear","__PRIVATE_newCallbacks","__PRIVATE_mutationUserCallbacks","__PRIVATE_toKey","__PRIVATE_onWatchError","__PRIVATE_limboDocumentRefs","__PRIVATE_removeReferencesForId","__PRIVATE_removeLimboTarget","__PRIVATE_limboTargetId","__PRIVATE_limboChange","__PRIVATE_trackLimboChange","__PRIVATE_enqueuedLimboResolutions","__PRIVATE_maxConcurrentLimboResolutions","__PRIVATE_limboTargetIdGenerator","__PRIVATE_LimboResolution","__PRIVATE_newSnaps","__PRIVATE_docChangesInAllViews","__PRIVATE_queriesProcessed","__PRIVATE_fromSnapshot","__PRIVATE_notifyLocalViewChanges","__PRIVATE_fnName","__PRIVATE_handleUserChange","__PRIVATE_rejectOutstandingPendingWritesCallbacks","__PRIVATE_handleCredentialChange","disableNetwork","__PRIVATE_keySet","__PRIVATE_syncedDocuments","__PRIVATE_SyncEngine","__PRIVATE__isPrimaryClient","__PRIVATE_synchronizeWithPersistedState","__PRIVATE_setOnlineState","__PRIVATE_batchState","__PRIVATE_lookupMutationDocuments","__PRIVATE_removeCachedMutationBatchMetadata","__PRIVATE_getAllActiveQueryTargets","__PRIVATE_synchronizeQueryViewsAndRaiseSnapshots","__PRIVATE_activeQueries","__PRIVATE_applyPrimaryState","t_74","__PRIVATE_isLocalQueryTarget","__PRIVATE_resetLimboDocuments","__PRIVATE_removeAllReferences","__PRIVATE_transitionToPrimary","e_92","t_78","__PRIVATE_synchronizeViewAndComputeSnapshot","__PRIVATE_getTarget","__PRIVATE_synthesizeTargetToQuery","__PRIVATE_synthesizedRemoteEvent","__PRIVATE_createSynthesizedRemoteEventForCurrentChange","__PRIVATE_removed","e_95","t_81","__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_batchesByDocumentKey","__PRIVATE_findMutationBatch","__PRIVATE_rawIndex","__PRIVATE_indexOfBatchId","__PRIVATE_findMutationBatches","__PRIVATE_startPath","__PRIVATE_rowKeyPath","__PRIVATE_references","__PRIVATE_currentSize","__PRIVATE_sizer","__PRIVATE_MemoryRemoteDocumentCache","subscribe","__PRIVATE_ReferenceSet","__PRIVATE_forSyncEngine","__PRIVATE_WebStorageSharedClientState","__PRIVATE_escapedPersistenceKey","__PRIVATE_highestSequenceNumber","__PRIVATE_removals","__PRIVATE_addReferences","__PRIVATE_removeReferences","__PRIVATE_matchingKeys","__PRIVATE_referencesForId","__PRIVATE_mutationQueues","__PRIVATE_MemoryMutationQueue","__PRIVATE_MemoryTransaction","__PRIVATE_onTransactionStarted","__PRIVATE_onTransactionCommitted","__PRIVATE_or","__PRIVATE_MemoryEagerDelegate","aT","__PRIVATE__orphanedDocuments","__PRIVATE_localViewReferences","__PRIVATE_orphanedDocuments","__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_EventManager","__PRIVATE_IndexFreeQueryEngine","__PRIVATE_MemoryPersistence","__PRIVATE_factory","__PRIVATE_RemoteStore","__PRIVATE_newConnectivityMonitor","__PRIVATE_MemorySharedClientState","__PRIVATE_MemoryComponentProvider","initialize","__PRIVATE_setPrimaryStateListener","__PRIVATE_MultiTabLocalStore","__PRIVATE_MultiTabSyncEngine","__PRIVATE_LruScheduler","__PRIVATE_buildStoragePrefix","__PRIVATE_persistenceSettings","synchronizeTabs","__PRIVATE_withCacheSize","cacheSizeBytes","__PRIVATE_durable","clearPersistence","__PRIVATE_componentProvider","__PRIVATE_verifyNotTerminated","__PRIVATE_initializationDone","__PRIVATE_persistenceResult","__PRIVATE_initialized","__PRIVATE_setChangeListener","__PRIVATE_initializeComponents","__PRIVATE_loadConnection","__PRIVATE_DatastoreImpl","Aa","mT","T_","RT","Qd","PT","__PRIVATE_eventMgr","__PRIVATE_setDatabaseDeletedListener","terminate","__PRIVATE_canFallback","VT","DOMException","__PRIVATE_isShuttingDown","__PRIVATE_verifyOperationInProgress","__PRIVATE_enqueueAndInitiateShutdown","__PRIVATE_removeChangeListener","__PRIVATE_registerPendingWritesCallback","__PRIVATE_QueryListener","__PRIVATE_clientTerminated","__PRIVATE_readDocument","__PRIVATE_addSnapshotsInSyncListener","__PRIVATE_removeSnapshotsInSyncListener","ST","__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","JT","__PRIVATE__userDataReader","__PRIVATE_UserDataReader","__PRIVATE__settings","__PRIVATE_settingsLiteral","__PRIVATE_newSettings","__PRIVATE_FirestoreSettings","__PRIVATE__firestoreClient","__PRIVATE__credentials","__PRIVATE_EmptyCredentialsProvider","getAuthHeaderValueForFirstParty","__PRIVATE_FirstPartyCredentialsProvider","__PRIVATE_ensureClientConfigured","experimentalTabSynchronization","__PRIVATE_configureClient","__PRIVATE__componentProvider","__PRIVATE__queue","__PRIVATE_enqueueAndForgetEvenAfterShutdown","__PRIVATE_makeDatabaseInfo","app","_removeServiceInstance","sE","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","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","Of","__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","fE","__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","xE","BE","__PRIVATE_callOnClose","__PRIVATE_msgData","__PRIVATE_callOnMessage","__PRIVATE_callOnOpen","__PRIVATE_urlRpcName","info","__PRIVATE_onNetworkAvailable","__PRIVATE_onNetworkUnavailable","__PRIVATE_configureNetworkMonitoring","__PRIVATE_setPlatform","__PRIVATE_WebChannelConnection","__PRIVATE_BrowserConnectivityMonitor","__PRIVATE_NoopConnectivityMonitor","__PRIVATE_JsonProtoSerializer","hi","__PRIVATE_encoded","raw","__PRIVATE_nBytes","crypto","msCrypto","getRandomValues","registerComponent","container","getProvider","__PRIVATE_IndexedDbComponentProvider","setServiceProps","registerVersion"],"mappings":"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"} |