diff --git a/web-app/.meteor/local/build/README b/web-app/.meteor/local/build/README
new file mode 100644
index 0000000..5e02281
--- /dev/null
+++ b/web-app/.meteor/local/build/README
@@ -0,0 +1,14 @@
+This is a Meteor application bundle. It has only one external dependency:
+Node.js 0.10.36 or newer. To run the application:
+
+ $ (cd programs/server && npm install)
+ $ export MONGO_URL='mongodb://user:password@host:port/databasename'
+ $ export ROOT_URL='http://example.com'
+ $ export MAIL_URL='smtp://user:password@mailhost:port/'
+ $ node main.js
+
+Use the PORT environment variable to set the port where the
+application will listen. The default is 80, but that will require
+root on most systems.
+
+Find out more about Meteor at meteor.com.
diff --git a/web-app/.meteor/local/build/main.js b/web-app/.meteor/local/build/main.js
new file mode 100644
index 0000000..056d8a4
--- /dev/null
+++ b/web-app/.meteor/local/build/main.js
@@ -0,0 +1,9 @@
+
+// The debugger pauses here when you run `meteor debug`, because this is
+// the very first code to be executed by the server process. If you have
+// not already added any `debugger` statements to your code, feel free to
+// do so now, wait for the server to restart, then reload this page and
+// click the |▶ button to continue.
+process.argv.splice(2, 0, 'program.json');
+process.chdir(require('path').join(__dirname, 'programs', 'server'));
+require('./programs/server/boot.js');
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.browser.html b/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.browser.html
new file mode 100644
index 0000000..3871c81
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.browser.html
@@ -0,0 +1,28 @@
+
+
+{{#each css}} {{/each}}
+
+{{#if inlineScriptsAllowed}}
+
+{{else}}
+
+{{/if}}
+{{#each js}}
+{{/each}}
+{{#each additionalStaticJs}}
+ {{#if ../inlineScriptsAllowed}}
+
+ {{else}}
+
+ {{/if}}
+{{/each}}
+
+{{{head}}}
+
+
+{{{body}}}
+
+
diff --git a/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.cordova.html b/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.cordova.html
new file mode 100644
index 0000000..7f82521
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/assets/packages/boilerplate-generator/boilerplate_web.cordova.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+{{#each css}} {{/each}}
+
+
+
+
+{{#each js}}
+{{/each}}
+{{#each additionalStaticJs}}
+ {{#if ../inlineScriptsAllowed}}
+
+ {{else}}
+
+ {{/if}}
+{{/each}}
+ {{{head}}}
+
+
+
+ {{{body}}}
+
+
diff --git a/web-app/.meteor/local/build/programs/server/boot-utils.js b/web-app/.meteor/local/build/programs/server/boot-utils.js
new file mode 100644
index 0000000..58e4024
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/boot-utils.js
@@ -0,0 +1,7 @@
+// Separated from boot.js for testing.
+
+// Check that we have a pid that looks like an integer (non-decimal
+// integer is okay).
+exports.validPid = function (pid) {
+ return ! isNaN(+pid);
+};
diff --git a/web-app/.meteor/local/build/programs/server/boot.js b/web-app/.meteor/local/build/programs/server/boot.js
new file mode 100644
index 0000000..bad9b3e
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/boot.js
@@ -0,0 +1,263 @@
+var Fiber = require("fibers");
+var fs = require("fs");
+var path = require("path");
+var Future = require("fibers/future");
+var _ = require('underscore');
+var sourcemap_support = require('source-map-support');
+
+var bootUtils = require('./boot-utils.js');
+var files = require('./mini-files.js');
+
+// This code is duplicated in tools/main.js.
+var MIN_NODE_VERSION = 'v0.10.36';
+
+if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
+ process.stderr.write(
+ 'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n');
+ process.exit(1);
+}
+
+// read our control files
+var serverJsonPath = path.resolve(process.argv[2]);
+var serverDir = path.dirname(serverJsonPath);
+var serverJson = JSON.parse(fs.readFileSync(serverJsonPath, 'utf8'));
+var configJson =
+ JSON.parse(fs.readFileSync(path.resolve(serverDir, 'config.json'), 'utf8'));
+
+// Set up environment
+__meteor_bootstrap__ = {
+ startupHooks: [],
+ serverDir: serverDir,
+ configJson: configJson };
+__meteor_runtime_config__ = { meteorRelease: configJson.meteorRelease };
+
+
+// connect (and some other NPM modules) use $NODE_ENV to make some decisions;
+// eg, if $NODE_ENV is not production, they send stack traces on error. connect
+// considers 'development' to be the default mode, but that's less safe than
+// assuming 'production' to be the default. If you really want development mode,
+// set it in your wrapper script (eg, run-app.js).
+if (!process.env.NODE_ENV)
+ process.env.NODE_ENV = 'production';
+
+// Map from load path to its source map.
+var parsedSourceMaps = {};
+
+// Read all the source maps into memory once.
+_.each(serverJson.load, function (fileInfo) {
+ if (fileInfo.sourceMap) {
+ var rawSourceMap = fs.readFileSync(
+ path.resolve(serverDir, fileInfo.sourceMap), 'utf8');
+ // Parse the source map only once, not each time it's needed. Also remove
+ // the anti-XSSI header if it's there.
+ var parsedSourceMap = JSON.parse(rawSourceMap.replace(/^\)\]\}'/, ''));
+ // source-map-support doesn't ever look at the sourcesContent field, so
+ // there's no point in keeping it in memory.
+ delete parsedSourceMap.sourcesContent;
+ var url;
+ if (fileInfo.sourceMapRoot) {
+ // Add the specified root to any root that may be in the file.
+ parsedSourceMap.sourceRoot = path.join(
+ fileInfo.sourceMapRoot, parsedSourceMap.sourceRoot || '');
+ }
+ parsedSourceMaps[path.resolve(__dirname, fileInfo.path)] = parsedSourceMap;
+ }
+});
+
+var retrieveSourceMap = function (pathForSourceMap) {
+ if (_.has(parsedSourceMaps, pathForSourceMap))
+ return { map: parsedSourceMaps[pathForSourceMap] };
+ return null;
+};
+
+sourcemap_support.install({
+ // Use the source maps specified in program.json instead of parsing source
+ // code for them.
+ retrieveSourceMap: retrieveSourceMap,
+ // For now, don't fix the source line in uncaught exceptions, because we
+ // haven't fixed handleUncaughtExceptions in source-map-support to properly
+ // locate the source files.
+ handleUncaughtExceptions: false
+});
+
+// Only enabled by default in development.
+if (process.env.METEOR_SHELL_DIR) {
+ require('./shell-server.js').listen(process.env.METEOR_SHELL_DIR);
+}
+
+// As a replacement to the old keepalives mechanism, check for a running
+// parent every few seconds. Exit if the parent is not running.
+//
+// Two caveats to this strategy:
+// * Doesn't catch the case where the parent is CPU-hogging (but maybe we
+// don't want to catch that case anyway, since the bundler not yielding
+// is what caused #2536).
+// * Could be fooled by pid re-use, i.e. if another process comes up and
+// takes the parent process's place before the child process dies.
+var startCheckForLiveParent = function (parentPid) {
+ if (parentPid) {
+ if (! bootUtils.validPid(parentPid)) {
+ console.error("METEOR_PARENT_PID must be a valid process ID.");
+ process.exit(1);
+ }
+
+ setInterval(function () {
+ try {
+ process.kill(parentPid, 0);
+ } catch (err) {
+ console.error("Parent process is dead! Exiting.");
+ process.exit(1);
+ }
+ }, 3000);
+ }
+};
+
+
+Fiber(function () {
+ _.each(serverJson.load, function (fileInfo) {
+ var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path));
+
+ var Npm = {
+ /**
+ * @summary Require a package that was specified using
+ * `Npm.depends()`.
+ * @param {String} name The name of the package to require.
+ * @locus Server
+ * @memberOf Npm
+ */
+ require: function (name) {
+ if (! fileInfo.node_modules) {
+ return require(name);
+ }
+
+ var nodeModuleBase = path.resolve(serverDir,
+ files.convertToOSPath(fileInfo.node_modules));
+ var nodeModuleDir = path.resolve(nodeModuleBase, name);
+
+ // If the user does `Npm.require('foo/bar')`, then we should resolve to
+ // the package's node modules if `foo` was one of the modules we
+ // installed. (`foo/bar` might be implemented as `foo/bar.js` so we
+ // can't just naively see if all of nodeModuleDir exists.
+ if (fs.existsSync(path.resolve(nodeModuleBase, name.split("/")[0]))) {
+ return require(nodeModuleDir);
+ }
+
+ try {
+ return require(name);
+ } catch (e) {
+ // Try to guess the package name so we can print a nice
+ // error message
+ // fileInfo.path is a standard path, use files.pathSep
+ var filePathParts = fileInfo.path.split(files.pathSep);
+ var packageName = filePathParts[1].replace(/\.js$/, '');
+
+ // XXX better message
+ throw new Error(
+ "Can't find npm module '" + name +
+ "'. Did you forget to call 'Npm.depends' in package.js " +
+ "within the '" + packageName + "' package?");
+ }
+ }
+ };
+ var getAsset = function (assetPath, encoding, callback) {
+ var fut;
+ if (! callback) {
+ fut = new Future();
+ callback = fut.resolver();
+ }
+ // This assumes that we've already loaded the meteor package, so meteor
+ // itself (and weird special cases like js-analyze) can't call
+ // Assets.get*. (We could change this function so that it doesn't call
+ // bindEnvironment if you don't pass a callback if we need to.)
+ var _callback = Package.meteor.Meteor.bindEnvironment(function (err, result) {
+ if (result && ! encoding)
+ // Sadly, this copies in Node 0.10.
+ result = new Uint8Array(result);
+ callback(err, result);
+ }, function (e) {
+ console.log("Exception in callback of getAsset", e.stack);
+ });
+
+ // Convert a DOS-style path to Unix-style in case the application code was
+ // written on Windows.
+ assetPath = files.convertToStandardPath(assetPath);
+
+ if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
+ _callback(new Error("Unknown asset: " + assetPath));
+ } else {
+ var filePath = path.join(serverDir, fileInfo.assets[assetPath]);
+ fs.readFile(files.convertToOSPath(filePath), encoding, _callback);
+ }
+ if (fut)
+ return fut.wait();
+ };
+
+ var Assets = {
+ getText: function (assetPath, callback) {
+ return getAsset(assetPath, "utf8", callback);
+ },
+ getBinary: function (assetPath, callback) {
+ return getAsset(assetPath, undefined, callback);
+ }
+ };
+
+ // \n is necessary in case final line is a //-comment
+ var wrapped = "(function(Npm, Assets){" + code + "\n})";
+
+ // It is safer to use the absolute path when source map is present as
+ // different tooling, such as node-inspector, can get confused on relative
+ // urls.
+
+ // fileInfo.path is a standard path, convert it to OS path to join with
+ // __dirname
+ var fileInfoOSPath = files.convertToOSPath(fileInfo.path);
+ var absoluteFilePath = path.resolve(__dirname, fileInfoOSPath);
+
+ var scriptPath =
+ parsedSourceMaps[absoluteFilePath] ? absoluteFilePath : fileInfoOSPath;
+ // The final 'true' is an undocumented argument to runIn[Foo]Context that
+ // causes it to print out a descriptive error message on parse error. It's
+ // what require() uses to generate its errors.
+ var func = require('vm').runInThisContext(wrapped, scriptPath, true);
+ func.call(global, Npm, Assets); // Coffeescript
+ });
+
+ // run the user startup hooks. other calls to startup() during this can still
+ // add hooks to the end.
+ while (__meteor_bootstrap__.startupHooks.length) {
+ var hook = __meteor_bootstrap__.startupHooks.shift();
+ hook();
+ }
+ // Setting this to null tells Meteor.startup to call hooks immediately.
+ __meteor_bootstrap__.startupHooks = null;
+
+ // find and run main()
+ // XXX hack. we should know the package that contains main.
+ var mains = [];
+ var globalMain;
+ if ('main' in global) {
+ mains.push(main);
+ globalMain = main;
+ }
+ typeof Package !== 'undefined' && _.each(Package, function (p, n) {
+ if ('main' in p && p.main !== globalMain) {
+ mains.push(p.main);
+ }
+ });
+ if (! mains.length) {
+ process.stderr.write("Program has no main() function.\n");
+ process.exit(1);
+ }
+ if (mains.length > 1) {
+ process.stderr.write("Program has more than one main() function?\n");
+ process.exit(1);
+ }
+ var exitCode = mains[0].call({}, process.argv.slice(3));
+ // XXX hack, needs a better way to keep alive
+ if (exitCode !== 'DAEMON')
+ process.exit(exitCode);
+
+ if (process.env.METEOR_PARENT_PID) {
+ startCheckForLiveParent(process.env.METEOR_PARENT_PID);
+ }
+}).run();
diff --git a/web-app/.meteor/local/build/programs/server/config.json b/web-app/.meteor/local/build/programs/server/config.json
new file mode 100644
index 0000000..94d6023
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/config.json
@@ -0,0 +1,6 @@
+{
+ "meteorRelease": "METEOR@1.1.0.2",
+ "clientPaths": {
+ "web.browser": "../web.browser/program.json"
+ }
+}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/mini-files.js b/web-app/.meteor/local/build/programs/server/mini-files.js
new file mode 100644
index 0000000..46aa74b
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/mini-files.js
@@ -0,0 +1,108 @@
+var _ = require("underscore");
+var os = require("os");
+var path = require("path");
+
+// All of these functions are attached to files.js for the tool;
+// they live here because we need them in boot.js as well to avoid duplicating
+// a lot of the code.
+//
+// Note that this file does NOT contain any of the "perform I/O maybe
+// synchronously" functions from files.js; this is intentional, because we want
+// to make it very hard to accidentally use fs.*Sync functions in the app server
+// after bootup (since they block all concurrency!)
+var files = module.exports;
+
+var toPosixPath = function (p, partialPath) {
+ // Sometimes, you can have a path like \Users\IEUser on windows, and this
+ // actually means you want C:\Users\IEUser
+ if (p[0] === "\\" && (! partialPath)) {
+ p = process.env.SystemDrive + p;
+ }
+
+ p = p.replace(/\\/g, '/');
+ if (p[1] === ':' && ! partialPath) {
+ // transform "C:/bla/bla" to "/c/bla/bla"
+ p = '/' + p[0] + p.slice(2);
+ }
+
+ return p;
+};
+
+var toDosPath = function (p, partialPath) {
+ if (p[0] === '/' && ! partialPath) {
+ if (! /^\/[A-Za-z](\/|$)/.test(p))
+ throw new Error("Surprising path: " + p);
+ // transform a previously windows path back
+ // "/C/something" to "c:/something"
+ p = p[1] + ":" + p.slice(2);
+ }
+
+ p = p.replace(/\//g, '\\');
+ return p;
+};
+
+
+var convertToOSPath = function (standardPath, partialPath) {
+ if (process.platform === "win32") {
+ return toDosPath(standardPath, partialPath);
+ }
+
+ return standardPath;
+};
+
+var convertToStandardPath = function (osPath, partialPath) {
+ if (process.platform === "win32") {
+ return toPosixPath(osPath, partialPath);
+ }
+
+ return osPath;
+}
+
+var convertToOSLineEndings = function (fileContents) {
+ return fileContents.replace(/\n/g, os.EOL);
+};
+
+var convertToStandardLineEndings = function (fileContents) {
+ // Convert all kinds of end-of-line chars to linuxy "\n".
+ return fileContents.replace(new RegExp("\r\n", "g"), "\n")
+ .replace(new RegExp("\r", "g"), "\n");
+};
+
+
+// wrappings for path functions that always run as they were on unix (using
+// forward slashes)
+var wrapPathFunction = function (name, partialPaths) {
+ var f = path[name];
+ return function (/* args */) {
+ if (process.platform === 'win32') {
+ var args = _.toArray(arguments);
+ args = _.map(args, function (p, i) {
+ // if partialPaths is turned on (for path.join mostly)
+ // forget about conversion of absolute paths for Windows
+ return toDosPath(p, partialPaths);
+ });
+ return toPosixPath(f.apply(path, args), partialPaths);
+ } else {
+ return f.apply(path, arguments);
+ }
+ };
+};
+
+files.pathJoin = wrapPathFunction("join", true);
+files.pathNormalize = wrapPathFunction("normalize");
+files.pathRelative = wrapPathFunction("relative");
+files.pathResolve = wrapPathFunction("resolve");
+files.pathDirname = wrapPathFunction("dirname");
+files.pathBasename = wrapPathFunction("basename");
+files.pathExtname = wrapPathFunction("extname");
+files.pathSep = '/';
+files.pathDelimiter = ':';
+files.pathOsDelimiter = path.delimiter;
+
+files.convertToStandardPath = convertToStandardPath;
+files.convertToOSPath = convertToOSPath;
+files.convertToWindowsPath = toDosPath;
+files.convertToPosixPath = toPosixPath;
+
+files.convertToStandardLineEndings = convertToStandardLineEndings;
+files.convertToOSLineEndings = convertToOSLineEndings;
diff --git a/web-app/.meteor/local/build/programs/server/npm-shrinkwrap.json b/web-app/.meteor/local/build/programs/server/npm-shrinkwrap.json
new file mode 100644
index 0000000..9136b92
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/npm-shrinkwrap.json
@@ -0,0 +1,40 @@
+{
+ "name": "meteor-dev-bundle",
+ "version": "0.0.0",
+ "dependencies": {
+ "fibers": {
+ "version": "1.0.5",
+ "from": "fibers@1.0.5",
+ "resolved": "https://registry.npmjs.org/fibers/-/fibers-1.0.5.tgz"
+ },
+ "semver": {
+ "version": "4.1.0",
+ "from": "semver@4.1.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-4.1.0.tgz"
+ },
+ "source-map-support": {
+ "version": "0.2.8",
+ "from": "source-map-support@0.2.8",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.8.tgz",
+ "dependencies": {
+ "source-map": {
+ "version": "0.1.32",
+ "from": "source-map@0.1.32",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz",
+ "dependencies": {
+ "amdefine": {
+ "version": "0.1.0",
+ "from": "amdefine@>=0.0.4",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "underscore": {
+ "version": "1.5.2",
+ "from": "underscore@1.5.2",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz"
+ }
+ }
+}
diff --git a/web-app/.meteor/local/build/programs/server/package.json b/web-app/.meteor/local/build/programs/server/package.json
new file mode 100644
index 0000000..ec572d0
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "meteor-dev-bundle",
+ "version": "0.0.0",
+ "dependencies": {
+ "fibers": "1.0.5",
+ "underscore": "1.5.2",
+ "source-map-support": "0.2.8",
+ "semver": "4.1.0"
+ },
+ "devDependencies": {
+ "eachline": "https://github.com/meteor/node-eachline/tarball/ff89722ff94e6b6a08652bf5f44c8fffea8a21da",
+ "chalk": "0.5.1"
+ }
+}
diff --git a/web-app/.meteor/local/build/programs/server/packages/autopublish.js b/web-app/.meteor/local/build/programs/server/packages/autopublish.js
new file mode 100644
index 0000000..dce0285
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/autopublish.js
@@ -0,0 +1,14 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.autopublish = {};
+
+})();
+
+//# sourceMappingURL=autopublish.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/autopublish.js.map b/web-app/.meteor/local/build/programs/server/packages/autopublish.js.map
new file mode 100644
index 0000000..abd020f
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/autopublish.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":[],"names":[],"mappings":";;;;;","file":"/packages/autopublish.js"}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/autoupdate.js b/web-app/.meteor/local/build/programs/server/packages/autoupdate.js
new file mode 100644
index 0000000..565e37e
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/autoupdate.js
@@ -0,0 +1,237 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var WebApp = Package.webapp.WebApp;
+var main = Package.webapp.main;
+var WebAppInternals = Package.webapp.WebAppInternals;
+var DDP = Package.ddp.DDP;
+var DDPServer = Package.ddp.DDPServer;
+var MongoInternals = Package.mongo.MongoInternals;
+var Mongo = Package.mongo.Mongo;
+var _ = Package.underscore._;
+
+/* Package-scope variables */
+var Autoupdate, ClientVersions;
+
+(function () {
+
+//////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/autoupdate/autoupdate_server.js //
+// //
+//////////////////////////////////////////////////////////////////////////////////
+ //
+// Publish the current client versions to the client. When a client // 1
+// sees the subscription change and that there is a new version of the // 2
+// client available on the server, it can reload. // 3
+// // 4
+// By default there are two current client versions. The refreshable client // 5
+// version is identified by a hash of the client resources seen by the browser // 6
+// that are refreshable, such as CSS, while the non refreshable client version // 7
+// is identified by a hash of the rest of the client assets // 8
+// (the HTML, code, and static files in the `public` directory). // 9
+// // 10
+// If the environment variable `AUTOUPDATE_VERSION` is set it will be // 11
+// used as the client id instead. You can use this to control when // 12
+// the client reloads. For example, if you want to only force a // 13
+// reload on major changes, you can use a custom AUTOUPDATE_VERSION // 14
+// which you only change when something worth pushing to clients // 15
+// immediately happens. // 16
+// // 17
+// The server publishes a `meteor_autoupdate_clientVersions` // 18
+// collection. There are two documents in this collection, a document // 19
+// with _id 'version' which represnets the non refreshable client assets, // 20
+// and a document with _id 'version-refreshable' which represents the // 21
+// refreshable client assets. Each document has a 'version' field // 22
+// which is equivalent to the hash of the relevant assets. The refreshable // 23
+// document also contains a list of the refreshable assets, so that the client // 24
+// can swap in the new assets without forcing a page refresh. Clients can // 25
+// observe changes on these documents to detect when there is a new // 26
+// version available. // 27
+// // 28
+// In this implementation only two documents are present in the collection // 29
+// the current refreshable client version and the current nonRefreshable client // 30
+// version. Developers can easily experiment with different versioning and // 31
+// updating models by forking this package. // 32
+ // 33
+var Future = Npm.require("fibers/future"); // 34
+ // 35
+Autoupdate = {}; // 36
+ // 37
+// The collection of acceptable client versions. // 38
+ClientVersions = new Mongo.Collection("meteor_autoupdate_clientVersions", // 39
+ { connection: null }); // 40
+ // 41
+// The client hash includes __meteor_runtime_config__, so wait until // 42
+// all packages have loaded and have had a chance to populate the // 43
+// runtime config before using the client hash as our default auto // 44
+// update version id. // 45
+ // 46
+// Note: Tests allow people to override Autoupdate.autoupdateVersion before // 47
+// startup. // 48
+Autoupdate.autoupdateVersion = null; // 49
+Autoupdate.autoupdateVersionRefreshable = null; // 50
+Autoupdate.autoupdateVersionCordova = null; // 51
+Autoupdate.appId = __meteor_runtime_config__.appId = process.env.APP_ID; // 52
+ // 53
+var syncQueue = new Meteor._SynchronousQueue(); // 54
+ // 55
+// updateVersions can only be called after the server has fully loaded. // 56
+var updateVersions = function (shouldReloadClientProgram) { // 57
+ // Step 1: load the current client program on the server and update the // 58
+ // hash values in __meteor_runtime_config__. // 59
+ if (shouldReloadClientProgram) { // 60
+ WebAppInternals.reloadClientPrograms(); // 61
+ } // 62
+ // 63
+ // If we just re-read the client program, or if we don't have an autoupdate // 64
+ // version, calculate it. // 65
+ if (shouldReloadClientProgram || Autoupdate.autoupdateVersion === null) { // 66
+ Autoupdate.autoupdateVersion = // 67
+ process.env.AUTOUPDATE_VERSION || // 68
+ WebApp.calculateClientHashNonRefreshable(); // 69
+ } // 70
+ // If we just recalculated it OR if it was set by (eg) test-in-browser, // 71
+ // ensure it ends up in __meteor_runtime_config__. // 72
+ __meteor_runtime_config__.autoupdateVersion = // 73
+ Autoupdate.autoupdateVersion; // 74
+ // 75
+ Autoupdate.autoupdateVersionRefreshable = // 76
+ __meteor_runtime_config__.autoupdateVersionRefreshable = // 77
+ process.env.AUTOUPDATE_VERSION || // 78
+ WebApp.calculateClientHashRefreshable(); // 79
+ // 80
+ Autoupdate.autoupdateVersionCordova = // 81
+ __meteor_runtime_config__.autoupdateVersionCordova = // 82
+ process.env.AUTOUPDATE_VERSION || // 83
+ WebApp.calculateClientHashCordova(); // 84
+ // 85
+ // Step 2: form the new client boilerplate which contains the updated // 86
+ // assets and __meteor_runtime_config__. // 87
+ if (shouldReloadClientProgram) { // 88
+ WebAppInternals.generateBoilerplate(); // 89
+ } // 90
+ // 91
+ // XXX COMPAT WITH 0.8.3 // 92
+ if (! ClientVersions.findOne({current: true})) { // 93
+ // To ensure apps with version of Meteor prior to 0.9.0 (in // 94
+ // which the structure of documents in `ClientVersions` was // 95
+ // different) also reload. // 96
+ ClientVersions.insert({current: true}); // 97
+ } // 98
+ // 99
+ if (! ClientVersions.findOne({_id: "version"})) { // 100
+ ClientVersions.insert({ // 101
+ _id: "version", // 102
+ version: Autoupdate.autoupdateVersion // 103
+ }); // 104
+ } else { // 105
+ ClientVersions.update("version", { $set: { // 106
+ version: Autoupdate.autoupdateVersion // 107
+ }}); // 108
+ } // 109
+ // 110
+ if (! ClientVersions.findOne({_id: "version-cordova"})) { // 111
+ ClientVersions.insert({ // 112
+ _id: "version-cordova", // 113
+ version: Autoupdate.autoupdateVersionCordova, // 114
+ refreshable: false // 115
+ }); // 116
+ } else { // 117
+ ClientVersions.update("version-cordova", { $set: { // 118
+ version: Autoupdate.autoupdateVersionCordova // 119
+ }}); // 120
+ } // 121
+ // 122
+ // Use `onListening` here because we need to use // 123
+ // `WebAppInternals.refreshableAssets`, which is only set after // 124
+ // `WebApp.generateBoilerplate` is called by `main` in webapp. // 125
+ WebApp.onListening(function () { // 126
+ if (! ClientVersions.findOne({_id: "version-refreshable"})) { // 127
+ ClientVersions.insert({ // 128
+ _id: "version-refreshable", // 129
+ version: Autoupdate.autoupdateVersionRefreshable, // 130
+ assets: WebAppInternals.refreshableAssets // 131
+ }); // 132
+ } else { // 133
+ ClientVersions.update("version-refreshable", { $set: { // 134
+ version: Autoupdate.autoupdateVersionRefreshable, // 135
+ assets: WebAppInternals.refreshableAssets // 136
+ }}); // 137
+ } // 138
+ }); // 139
+}; // 140
+ // 141
+Meteor.publish( // 142
+ "meteor_autoupdate_clientVersions", // 143
+ function (appId) { // 144
+ // `null` happens when a client doesn't have an appId and passes // 145
+ // `undefined` to `Meteor.subscribe`. `undefined` is translated to // 146
+ // `null` as JSON doesn't have `undefined. // 147
+ check(appId, Match.OneOf(String, undefined, null)); // 148
+ // 149
+ // Don't notify clients using wrong appId such as mobile apps built with a // 150
+ // different server but pointing at the same local url // 151
+ if (Autoupdate.appId && appId && Autoupdate.appId !== appId) // 152
+ return []; // 153
+ // 154
+ return ClientVersions.find(); // 155
+ }, // 156
+ {is_auto: true} // 157
+); // 158
+ // 159
+Meteor.startup(function () { // 160
+ updateVersions(false); // 161
+}); // 162
+ // 163
+var fut = new Future(); // 164
+ // 165
+// We only want 'refresh' to trigger 'updateVersions' AFTER onListen, // 166
+// so we add a queued task that waits for onListen before 'refresh' can queue // 167
+// tasks. Note that the `onListening` callbacks do not fire until after // 168
+// Meteor.startup, so there is no concern that the 'updateVersions' calls from // 169
+// 'refresh' will overlap with the `updateVersions` call from Meteor.startup. // 170
+ // 171
+syncQueue.queueTask(function () { // 172
+ fut.wait(); // 173
+}); // 174
+ // 175
+WebApp.onListening(function () { // 176
+ fut.return(); // 177
+}); // 178
+ // 179
+var enqueueVersionsRefresh = function () { // 180
+ syncQueue.queueTask(function () { // 181
+ updateVersions(true); // 182
+ }); // 183
+}; // 184
+ // 185
+// Listen for the special {refresh: 'client'} message, which signals that a // 186
+// client asset has changed. // 187
+process.on('message', Meteor.bindEnvironment(function (m) { // 188
+ if (m && m.refresh === 'client') { // 189
+ enqueueVersionsRefresh(); // 190
+ } // 191
+})); // 192
+ // 193
+// Another way to tell the process to refresh: send SIGHUP signal // 194
+process.on('SIGHUP', Meteor.bindEnvironment(function () { // 195
+ enqueueVersionsRefresh(); // 196
+})); // 197
+ // 198
+ // 199
+//////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.autoupdate = {
+ Autoupdate: Autoupdate
+};
+
+})();
+
+//# sourceMappingURL=autoupdate.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/autoupdate.js.map b/web-app/.meteor/local/build/programs/server/packages/autoupdate.js.map
new file mode 100644
index 0000000..3f372c8
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/autoupdate.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["autoupdate/autoupdate_server.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,oE;AACA,sE;AACA,iD;AACA,E;AACA,2E;AACA,8E;AACA,8E;AACA,2D;AACA,gE;AACA,E;AACA,qE;AACA,mE;AACA,gE;AACA,mE;AACA,gE;AACA,uB;AACA,E;AACA,4D;AACA,qE;AACA,yE;AACA,qE;AACA,iE;AACA,0E;AACA,8E;AACA,yE;AACA,mE;AACA,qB;AACA,E;AACA,0E;AACA,+E;AACA,2E;AACA,2C;;AAEA,0C;;AAEA,gB;;AAEA,gD;AACA,yE;AACA,wB;;AAEA,oE;AACA,iE;AACA,kE;AACA,qB;;AAEA,2E;AACA,W;AACA,oC;AACA,+C;AACA,2C;AACA,wE;;AAEA,+C;;AAEA,uE;AACA,2D;AACA,yE;AACA,8C;AACA,kC;AACA,2C;AACA,G;;AAEA,6E;AACA,2B;AACA,2E;AACA,kC;AACA,uC;AACA,iD;AACA,G;AACA,yE;AACA,oD;AACA,+C;AACA,iC;;AAEA,2C;AACA,4D;AACA,uC;AACA,8C;;AAEA,uC;AACA,wD;AACA,uC;AACA,0C;;AAEA,uE;AACA,0C;AACA,kC;AACA,0C;AACA,G;;AAEA,0B;AACA,kD;AACA,+D;AACA,+D;AACA,8B;AACA,2C;AACA,G;;AAEA,mD;AACA,2B;AACA,qB;AACA,2C;AACA,O;AACA,U;AACA,8C;AACA,2C;AACA,Q;AACA,G;;AAEA,2D;AACA,2B;AACA,6B;AACA,mD;AACA,wB;AACA,O;AACA,U;AACA,sD;AACA,kD;AACA,Q;AACA,G;;AAEA,kD;AACA,iE;AACA,gE;AACA,kC;AACA,iE;AACA,6B;AACA,mC;AACA,yD;AACA,iD;AACA,S;AACA,Y;AACA,4D;AACA,yD;AACA,iD;AACA,U;AACA,K;AACA,K;AACA,E;;AAEA,e;AACA,qC;AACA,oB;AACA,oE;AACA,sE;AACA,8C;AACA,uD;;AAEA,8E;AACA,0D;AACA,gE;AACA,gB;;AAEA,iC;AACA,I;AACA,iB;AACA,E;;AAEA,4B;AACA,wB;AACA,G;;AAEA,uB;;AAEA,qE;AACA,6E;AACA,uE;AACA,8E;AACA,6E;;AAEA,iC;AACA,a;AACA,G;;AAEA,gC;AACA,e;AACA,G;;AAEA,0C;AACA,mC;AACA,yB;AACA,K;AACA,E;;AAEA,2E;AACA,4B;AACA,2D;AACA,oC;AACA,6B;AACA,G;AACA,I;;AAEA,iE;AACA,yD;AACA,2B;AACA,I","file":"/packages/autoupdate.js","sourcesContent":["// Publish the current client versions to the client. When a client\n// sees the subscription change and that there is a new version of the\n// client available on the server, it can reload.\n//\n// By default there are two current client versions. The refreshable client\n// version is identified by a hash of the client resources seen by the browser\n// that are refreshable, such as CSS, while the non refreshable client version\n// is identified by a hash of the rest of the client assets\n// (the HTML, code, and static files in the `public` directory).\n//\n// If the environment variable `AUTOUPDATE_VERSION` is set it will be\n// used as the client id instead. You can use this to control when\n// the client reloads. For example, if you want to only force a\n// reload on major changes, you can use a custom AUTOUPDATE_VERSION\n// which you only change when something worth pushing to clients\n// immediately happens.\n//\n// The server publishes a `meteor_autoupdate_clientVersions`\n// collection. There are two documents in this collection, a document\n// with _id 'version' which represnets the non refreshable client assets,\n// and a document with _id 'version-refreshable' which represents the\n// refreshable client assets. Each document has a 'version' field\n// which is equivalent to the hash of the relevant assets. The refreshable\n// document also contains a list of the refreshable assets, so that the client\n// can swap in the new assets without forcing a page refresh. Clients can\n// observe changes on these documents to detect when there is a new\n// version available.\n//\n// In this implementation only two documents are present in the collection\n// the current refreshable client version and the current nonRefreshable client\n// version. Developers can easily experiment with different versioning and\n// updating models by forking this package.\n\nvar Future = Npm.require(\"fibers/future\");\n\nAutoupdate = {};\n\n// The collection of acceptable client versions.\nClientVersions = new Mongo.Collection(\"meteor_autoupdate_clientVersions\",\n { connection: null });\n\n// The client hash includes __meteor_runtime_config__, so wait until\n// all packages have loaded and have had a chance to populate the\n// runtime config before using the client hash as our default auto\n// update version id.\n\n// Note: Tests allow people to override Autoupdate.autoupdateVersion before\n// startup.\nAutoupdate.autoupdateVersion = null;\nAutoupdate.autoupdateVersionRefreshable = null;\nAutoupdate.autoupdateVersionCordova = null;\nAutoupdate.appId = __meteor_runtime_config__.appId = process.env.APP_ID;\n\nvar syncQueue = new Meteor._SynchronousQueue();\n\n// updateVersions can only be called after the server has fully loaded.\nvar updateVersions = function (shouldReloadClientProgram) {\n // Step 1: load the current client program on the server and update the\n // hash values in __meteor_runtime_config__.\n if (shouldReloadClientProgram) {\n WebAppInternals.reloadClientPrograms();\n }\n\n // If we just re-read the client program, or if we don't have an autoupdate\n // version, calculate it.\n if (shouldReloadClientProgram || Autoupdate.autoupdateVersion === null) {\n Autoupdate.autoupdateVersion =\n process.env.AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashNonRefreshable();\n }\n // If we just recalculated it OR if it was set by (eg) test-in-browser,\n // ensure it ends up in __meteor_runtime_config__.\n __meteor_runtime_config__.autoupdateVersion =\n Autoupdate.autoupdateVersion;\n\n Autoupdate.autoupdateVersionRefreshable =\n __meteor_runtime_config__.autoupdateVersionRefreshable =\n process.env.AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashRefreshable();\n\n Autoupdate.autoupdateVersionCordova =\n __meteor_runtime_config__.autoupdateVersionCordova =\n process.env.AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashCordova();\n\n // Step 2: form the new client boilerplate which contains the updated\n // assets and __meteor_runtime_config__.\n if (shouldReloadClientProgram) {\n WebAppInternals.generateBoilerplate();\n }\n\n // XXX COMPAT WITH 0.8.3\n if (! ClientVersions.findOne({current: true})) {\n // To ensure apps with version of Meteor prior to 0.9.0 (in\n // which the structure of documents in `ClientVersions` was\n // different) also reload.\n ClientVersions.insert({current: true});\n }\n\n if (! ClientVersions.findOne({_id: \"version\"})) {\n ClientVersions.insert({\n _id: \"version\",\n version: Autoupdate.autoupdateVersion\n });\n } else {\n ClientVersions.update(\"version\", { $set: {\n version: Autoupdate.autoupdateVersion\n }});\n }\n\n if (! ClientVersions.findOne({_id: \"version-cordova\"})) {\n ClientVersions.insert({\n _id: \"version-cordova\",\n version: Autoupdate.autoupdateVersionCordova,\n refreshable: false\n });\n } else {\n ClientVersions.update(\"version-cordova\", { $set: {\n version: Autoupdate.autoupdateVersionCordova\n }});\n }\n\n // Use `onListening` here because we need to use\n // `WebAppInternals.refreshableAssets`, which is only set after\n // `WebApp.generateBoilerplate` is called by `main` in webapp.\n WebApp.onListening(function () {\n if (! ClientVersions.findOne({_id: \"version-refreshable\"})) {\n ClientVersions.insert({\n _id: \"version-refreshable\",\n version: Autoupdate.autoupdateVersionRefreshable,\n assets: WebAppInternals.refreshableAssets\n });\n } else {\n ClientVersions.update(\"version-refreshable\", { $set: {\n version: Autoupdate.autoupdateVersionRefreshable,\n assets: WebAppInternals.refreshableAssets\n }});\n }\n });\n};\n\nMeteor.publish(\n \"meteor_autoupdate_clientVersions\",\n function (appId) {\n // `null` happens when a client doesn't have an appId and passes\n // `undefined` to `Meteor.subscribe`. `undefined` is translated to\n // `null` as JSON doesn't have `undefined.\n check(appId, Match.OneOf(String, undefined, null));\n\n // Don't notify clients using wrong appId such as mobile apps built with a\n // different server but pointing at the same local url\n if (Autoupdate.appId && appId && Autoupdate.appId !== appId)\n return [];\n\n return ClientVersions.find();\n },\n {is_auto: true}\n);\n\nMeteor.startup(function () {\n updateVersions(false);\n});\n\nvar fut = new Future();\n\n// We only want 'refresh' to trigger 'updateVersions' AFTER onListen,\n// so we add a queued task that waits for onListen before 'refresh' can queue\n// tasks. Note that the `onListening` callbacks do not fire until after\n// Meteor.startup, so there is no concern that the 'updateVersions' calls from\n// 'refresh' will overlap with the `updateVersions` call from Meteor.startup.\n\nsyncQueue.queueTask(function () {\n fut.wait();\n});\n\nWebApp.onListening(function () {\n fut.return();\n});\n\nvar enqueueVersionsRefresh = function () {\n syncQueue.queueTask(function () {\n updateVersions(true);\n });\n};\n\n// Listen for the special {refresh: 'client'} message, which signals that a\n// client asset has changed.\nprocess.on('message', Meteor.bindEnvironment(function (m) {\n if (m && m.refresh === 'client') {\n enqueueVersionsRefresh();\n }\n}));\n\n// Another way to tell the process to refresh: send SIGHUP signal\nprocess.on('SIGHUP', Meteor.bindEnvironment(function () {\n enqueueVersionsRefresh();\n}));\n\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/base64.js b/web-app/.meteor/local/build/programs/server/packages/base64.js
new file mode 100644
index 0000000..8fd732c
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/base64.js
@@ -0,0 +1,175 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+
+/* Package-scope variables */
+var Base64;
+
+(function () {
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/base64/base64.js //
+// //
+//////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Base 64 encoding // 1
+ // 2
+var BASE_64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // 3
+ // 4
+var BASE_64_VALS = {}; // 5
+ // 6
+for (var i = 0; i < BASE_64_CHARS.length; i++) { // 7
+ BASE_64_VALS[BASE_64_CHARS.charAt(i)] = i; // 8
+}; // 9
+ // 10
+Base64 = {}; // 11
+ // 12
+Base64.encode = function (array) { // 13
+ // 14
+ if (typeof array === "string") { // 15
+ var str = array; // 16
+ array = Base64.newBinary(str.length); // 17
+ for (var i = 0; i < str.length; i++) { // 18
+ var ch = str.charCodeAt(i); // 19
+ if (ch > 0xFF) { // 20
+ throw new Error( // 21
+ "Not ascii. Base64.encode can only take ascii strings."); // 22
+ } // 23
+ array[i] = ch; // 24
+ } // 25
+ } // 26
+ // 27
+ var answer = []; // 28
+ var a = null; // 29
+ var b = null; // 30
+ var c = null; // 31
+ var d = null; // 32
+ for (var i = 0; i < array.length; i++) { // 33
+ switch (i % 3) { // 34
+ case 0: // 35
+ a = (array[i] >> 2) & 0x3F; // 36
+ b = (array[i] & 0x03) << 4; // 37
+ break; // 38
+ case 1: // 39
+ b = b | (array[i] >> 4) & 0xF; // 40
+ c = (array[i] & 0xF) << 2; // 41
+ break; // 42
+ case 2: // 43
+ c = c | (array[i] >> 6) & 0x03; // 44
+ d = array[i] & 0x3F; // 45
+ answer.push(getChar(a)); // 46
+ answer.push(getChar(b)); // 47
+ answer.push(getChar(c)); // 48
+ answer.push(getChar(d)); // 49
+ a = null; // 50
+ b = null; // 51
+ c = null; // 52
+ d = null; // 53
+ break; // 54
+ } // 55
+ } // 56
+ if (a != null) { // 57
+ answer.push(getChar(a)); // 58
+ answer.push(getChar(b)); // 59
+ if (c == null) // 60
+ answer.push('='); // 61
+ else // 62
+ answer.push(getChar(c)); // 63
+ if (d == null) // 64
+ answer.push('='); // 65
+ } // 66
+ return answer.join(""); // 67
+}; // 68
+ // 69
+var getChar = function (val) { // 70
+ return BASE_64_CHARS.charAt(val); // 71
+}; // 72
+ // 73
+var getVal = function (ch) { // 74
+ if (ch === '=') { // 75
+ return -1; // 76
+ } // 77
+ return BASE_64_VALS[ch]; // 78
+}; // 79
+ // 80
+// XXX This is a weird place for this to live, but it's used both by // 81
+// this package and 'ejson', and we can't put it in 'ejson' without // 82
+// introducing a circular dependency. It should probably be in its own // 83
+// package or as a helper in a package that both 'base64' and 'ejson' // 84
+// use. // 85
+Base64.newBinary = function (len) { // 86
+ if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') { // 87
+ var ret = []; // 88
+ for (var i = 0; i < len; i++) { // 89
+ ret.push(0); // 90
+ } // 91
+ ret.$Uint8ArrayPolyfill = true; // 92
+ return ret; // 93
+ } // 94
+ return new Uint8Array(new ArrayBuffer(len)); // 95
+}; // 96
+ // 97
+Base64.decode = function (str) { // 98
+ var len = Math.floor((str.length*3)/4); // 99
+ if (str.charAt(str.length - 1) == '=') { // 100
+ len--; // 101
+ if (str.charAt(str.length - 2) == '=') // 102
+ len--; // 103
+ } // 104
+ var arr = Base64.newBinary(len); // 105
+ // 106
+ var one = null; // 107
+ var two = null; // 108
+ var three = null; // 109
+ // 110
+ var j = 0; // 111
+ // 112
+ for (var i = 0; i < str.length; i++) { // 113
+ var c = str.charAt(i); // 114
+ var v = getVal(c); // 115
+ switch (i % 4) { // 116
+ case 0: // 117
+ if (v < 0) // 118
+ throw new Error('invalid base64 string'); // 119
+ one = v << 2; // 120
+ break; // 121
+ case 1: // 122
+ if (v < 0) // 123
+ throw new Error('invalid base64 string'); // 124
+ one = one | (v >> 4); // 125
+ arr[j++] = one; // 126
+ two = (v & 0x0F) << 4; // 127
+ break; // 128
+ case 2: // 129
+ if (v >= 0) { // 130
+ two = two | (v >> 2); // 131
+ arr[j++] = two; // 132
+ three = (v & 0x03) << 6; // 133
+ } // 134
+ break; // 135
+ case 3: // 136
+ if (v >= 0) { // 137
+ arr[j++] = three | v; // 138
+ } // 139
+ break; // 140
+ } // 141
+ } // 142
+ return arr; // 143
+}; // 144
+ // 145
+//////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.base64 = {
+ Base64: Base64
+};
+
+})();
+
+//# sourceMappingURL=base64.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/base64.js.map b/web-app/.meteor/local/build/programs/server/packages/base64.js.map
new file mode 100644
index 0000000..3dd2dd2
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/base64.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["base64/base64.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mB;;AAEA,uF;;AAEA,sB;;AAEA,gD;AACA,4C;AACA,E;;AAEA,Y;;AAEA,kC;;AAEA,kC;AACA,oB;AACA,yC;AACA,0C;AACA,iC;AACA,sB;AACA,wB;AACA,mE;AACA,O;AACA,oB;AACA,K;AACA,G;;AAEA,kB;AACA,e;AACA,e;AACA,e;AACA,e;AACA,0C;AACA,oB;AACA,W;AACA,iC;AACA,iC;AACA,Y;AACA,W;AACA,oC;AACA,gC;AACA,Y;AACA,W;AACA,qC;AACA,0B;AACA,8B;AACA,8B;AACA,8B;AACA,8B;AACA,e;AACA,e;AACA,e;AACA,e;AACA,Y;AACA,K;AACA,G;AACA,kB;AACA,4B;AACA,4B;AACA,kB;AACA,uB;AACA,Q;AACA,8B;AACA,kB;AACA,uB;AACA,G;AACA,yB;AACA,E;;AAEA,8B;AACA,mC;AACA,E;;AAEA,4B;AACA,mB;AACA,c;AACA,G;AACA,0B;AACA,E;;AAEA,oE;AACA,mE;AACA,sE;AACA,qE;AACA,O;AACA,mC;AACA,gF;AACA,iB;AACA,mC;AACA,kB;AACA,K;AACA,mC;AACA,e;AACA,G;AACA,8C;AACA,E;;AAEA,gC;AACA,yC;AACA,0C;AACA,U;AACA,0C;AACA,Y;AACA,G;AACA,kC;;AAEA,iB;AACA,iB;AACA,mB;;AAEA,Y;;AAEA,wC;AACA,0B;AACA,sB;AACA,oB;AACA,W;AACA,gB;AACA,iD;AACA,mB;AACA,Y;AACA,W;AACA,gB;AACA,iD;AACA,2B;AACA,qB;AACA,4B;AACA,Y;AACA,W;AACA,mB;AACA,6B;AACA,uB;AACA,gC;AACA,O;AACA,Y;AACA,W;AACA,mB;AACA,6B;AACA,O;AACA,Y;AACA,K;AACA,G;AACA,a;AACA,E","file":"/packages/base64.js","sourcesContent":["// Base 64 encoding\n\nvar BASE_64_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nvar BASE_64_VALS = {};\n\nfor (var i = 0; i < BASE_64_CHARS.length; i++) {\n BASE_64_VALS[BASE_64_CHARS.charAt(i)] = i;\n};\n\nBase64 = {};\n\nBase64.encode = function (array) {\n\n if (typeof array === \"string\") {\n var str = array;\n array = Base64.newBinary(str.length);\n for (var i = 0; i < str.length; i++) {\n var ch = str.charCodeAt(i);\n if (ch > 0xFF) {\n throw new Error(\n \"Not ascii. Base64.encode can only take ascii strings.\");\n }\n array[i] = ch;\n }\n }\n\n var answer = [];\n var a = null;\n var b = null;\n var c = null;\n var d = null;\n for (var i = 0; i < array.length; i++) {\n switch (i % 3) {\n case 0:\n a = (array[i] >> 2) & 0x3F;\n b = (array[i] & 0x03) << 4;\n break;\n case 1:\n b = b | (array[i] >> 4) & 0xF;\n c = (array[i] & 0xF) << 2;\n break;\n case 2:\n c = c | (array[i] >> 6) & 0x03;\n d = array[i] & 0x3F;\n answer.push(getChar(a));\n answer.push(getChar(b));\n answer.push(getChar(c));\n answer.push(getChar(d));\n a = null;\n b = null;\n c = null;\n d = null;\n break;\n }\n }\n if (a != null) {\n answer.push(getChar(a));\n answer.push(getChar(b));\n if (c == null)\n answer.push('=');\n else\n answer.push(getChar(c));\n if (d == null)\n answer.push('=');\n }\n return answer.join(\"\");\n};\n\nvar getChar = function (val) {\n return BASE_64_CHARS.charAt(val);\n};\n\nvar getVal = function (ch) {\n if (ch === '=') {\n return -1;\n }\n return BASE_64_VALS[ch];\n};\n\n// XXX This is a weird place for this to live, but it's used both by\n// this package and 'ejson', and we can't put it in 'ejson' without\n// introducing a circular dependency. It should probably be in its own\n// package or as a helper in a package that both 'base64' and 'ejson'\n// use.\nBase64.newBinary = function (len) {\n if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') {\n var ret = [];\n for (var i = 0; i < len; i++) {\n ret.push(0);\n }\n ret.$Uint8ArrayPolyfill = true;\n return ret;\n }\n return new Uint8Array(new ArrayBuffer(len));\n};\n\nBase64.decode = function (str) {\n var len = Math.floor((str.length*3)/4);\n if (str.charAt(str.length - 1) == '=') {\n len--;\n if (str.charAt(str.length - 2) == '=')\n len--;\n }\n var arr = Base64.newBinary(len);\n\n var one = null;\n var two = null;\n var three = null;\n\n var j = 0;\n\n for (var i = 0; i < str.length; i++) {\n var c = str.charAt(i);\n var v = getVal(c);\n switch (i % 4) {\n case 0:\n if (v < 0)\n throw new Error('invalid base64 string');\n one = v << 2;\n break;\n case 1:\n if (v < 0)\n throw new Error('invalid base64 string');\n one = one | (v >> 4);\n arr[j++] = one;\n two = (v & 0x0F) << 4;\n break;\n case 2:\n if (v >= 0) {\n two = two | (v >> 2);\n arr[j++] = two;\n three = (v & 0x03) << 6;\n }\n break;\n case 3:\n if (v >= 0) {\n arr[j++] = three | v;\n }\n break;\n }\n }\n return arr;\n};\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/binary-heap.js b/web-app/.meteor/local/build/programs/server/packages/binary-heap.js
new file mode 100644
index 0000000..0df1bdb
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/binary-heap.js
@@ -0,0 +1,369 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var _ = Package.underscore._;
+var IdMap = Package['id-map'].IdMap;
+
+/* Package-scope variables */
+var MaxHeap, MinHeap, MinMaxHeap;
+
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/binary-heap/max-heap.js //
+// //
+////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Constructor of Heap // 1
+// - comparator - Function - given two items returns a number // 2
+// - options: // 3
+// - initData - Array - Optional - the initial data in a format: // 4
+// Object: // 5
+// - id - String - unique id of the item // 6
+// - value - Any - the data value // 7
+// each value is retained // 8
+// - IdMap - Constructor - Optional - custom IdMap class to store id->index // 9
+// mappings internally. Standard IdMap is used by default. // 10
+MaxHeap = function (comparator, options) { // 11
+ if (! _.isFunction(comparator)) // 12
+ throw new Error('Passed comparator is invalid, should be a comparison function'); // 13
+ var self = this; // 14
+ // 15
+ // a C-style comparator that is given two values and returns a number, // 16
+ // negative if the first value is less than the second, positive if the second // 17
+ // value is greater than the first and zero if they are equal. // 18
+ self._comparator = comparator; // 19
+ // 20
+ options = _.defaults(options || {}, { IdMap: IdMap }); // 21
+ // 22
+ // _heapIdx maps an id to an index in the Heap array the corresponding value // 23
+ // is located on. // 24
+ self._heapIdx = new options.IdMap; // 25
+ // 26
+ // The Heap data-structure implemented as a 0-based contiguous array where // 27
+ // every item on index idx is a node in a complete binary tree. Every node can // 28
+ // have children on indexes idx*2+1 and idx*2+2, except for the leaves. Every // 29
+ // node has a parent on index (idx-1)/2; // 30
+ self._heap = []; // 31
+ // 32
+ // If the initial array is passed, we can build the heap in linear time // 33
+ // complexity (O(N)) compared to linearithmic time complexity (O(nlogn)) if // 34
+ // we push elements one by one. // 35
+ if (_.isArray(options.initData)) // 36
+ self._initFromData(options.initData); // 37
+}; // 38
+ // 39
+_.extend(MaxHeap.prototype, { // 40
+ // Builds a new heap in-place in linear time based on passed data // 41
+ _initFromData: function (data) { // 42
+ var self = this; // 43
+ // 44
+ self._heap = _.map(data, function (o) { // 45
+ return { id: o.id, value: o.value }; // 46
+ }); // 47
+ // 48
+ _.each(data, function (o, i) { // 49
+ self._heapIdx.set(o.id, i); // 50
+ }); // 51
+ // 52
+ if (! data.length) // 53
+ return; // 54
+ // 55
+ // start from the first non-leaf - the parent of the last leaf // 56
+ for (var i = parentIdx(data.length - 1); i >= 0; i--) // 57
+ self._downHeap(i); // 58
+ }, // 59
+ // 60
+ _downHeap: function (idx) { // 61
+ var self = this; // 62
+ // 63
+ while (leftChildIdx(idx) < self.size()) { // 64
+ var left = leftChildIdx(idx); // 65
+ var right = rightChildIdx(idx); // 66
+ var largest = idx; // 67
+ // 68
+ if (left < self.size()) { // 69
+ largest = self._maxIndex(largest, left); // 70
+ } // 71
+ if (right < self.size()) { // 72
+ largest = self._maxIndex(largest, right); // 73
+ } // 74
+ // 75
+ if (largest === idx) // 76
+ break; // 77
+ // 78
+ self._swap(largest, idx); // 79
+ idx = largest; // 80
+ } // 81
+ }, // 82
+ // 83
+ _upHeap: function (idx) { // 84
+ var self = this; // 85
+ // 86
+ while (idx > 0) { // 87
+ var parent = parentIdx(idx); // 88
+ if (self._maxIndex(parent, idx) === idx) { // 89
+ self._swap(parent, idx) // 90
+ idx = parent; // 91
+ } else { // 92
+ break; // 93
+ } // 94
+ } // 95
+ }, // 96
+ // 97
+ _maxIndex: function (idxA, idxB) { // 98
+ var self = this; // 99
+ var valueA = self._get(idxA); // 100
+ var valueB = self._get(idxB); // 101
+ return self._comparator(valueA, valueB) >= 0 ? idxA : idxB; // 102
+ }, // 103
+ // 104
+ // Internal: gets raw data object placed on idxth place in heap // 105
+ _get: function (idx) { // 106
+ var self = this; // 107
+ return self._heap[idx].value; // 108
+ }, // 109
+ // 110
+ _swap: function (idxA, idxB) { // 111
+ var self = this; // 112
+ var recA = self._heap[idxA]; // 113
+ var recB = self._heap[idxB]; // 114
+ // 115
+ self._heapIdx.set(recA.id, idxB); // 116
+ self._heapIdx.set(recB.id, idxA); // 117
+ // 118
+ self._heap[idxA] = recB; // 119
+ self._heap[idxB] = recA; // 120
+ }, // 121
+ // 122
+ get: function (id) { // 123
+ var self = this; // 124
+ if (! self.has(id)) // 125
+ return null; // 126
+ return self._get(self._heapIdx.get(id)); // 127
+ }, // 128
+ set: function (id, value) { // 129
+ var self = this; // 130
+ // 131
+ if (self.has(id)) { // 132
+ if (self.get(id) === value) // 133
+ return; // 134
+ // 135
+ var idx = self._heapIdx.get(id); // 136
+ self._heap[idx].value = value; // 137
+ // 138
+ // Fix the new value's position // 139
+ // Either bubble new value up if it is greater than its parent // 140
+ self._upHeap(idx); // 141
+ // or bubble it down if it is smaller than one of its children // 142
+ self._downHeap(idx); // 143
+ } else { // 144
+ self._heapIdx.set(id, self._heap.length); // 145
+ self._heap.push({ id: id, value: value }); // 146
+ self._upHeap(self._heap.length - 1); // 147
+ } // 148
+ }, // 149
+ remove: function (id) { // 150
+ var self = this; // 151
+ // 152
+ if (self.has(id)) { // 153
+ var last = self._heap.length - 1; // 154
+ var idx = self._heapIdx.get(id); // 155
+ // 156
+ if (idx !== last) { // 157
+ self._swap(idx, last); // 158
+ self._heap.pop(); // 159
+ self._heapIdx.remove(id); // 160
+ // 161
+ // Fix the swapped value's position // 162
+ self._upHeap(idx); // 163
+ self._downHeap(idx); // 164
+ } else { // 165
+ self._heap.pop(); // 166
+ self._heapIdx.remove(id); // 167
+ } // 168
+ } // 169
+ }, // 170
+ has: function (id) { // 171
+ var self = this; // 172
+ return self._heapIdx.has(id); // 173
+ }, // 174
+ empty: function () { // 175
+ var self = this; // 176
+ return !self.size(); // 177
+ }, // 178
+ clear: function () { // 179
+ var self = this; // 180
+ self._heap = []; // 181
+ self._heapIdx.clear(); // 182
+ }, // 183
+ // iterate over values in no particular order // 184
+ forEach: function (iterator) { // 185
+ var self = this; // 186
+ _.each(self._heap, function (obj) { // 187
+ return iterator(obj.value, obj.id); // 188
+ }); // 189
+ }, // 190
+ size: function () { // 191
+ var self = this; // 192
+ return self._heap.length; // 193
+ }, // 194
+ setDefault: function (id, def) { // 195
+ var self = this; // 196
+ if (self.has(id)) // 197
+ return self.get(id); // 198
+ self.set(id, def); // 199
+ return def; // 200
+ }, // 201
+ clone: function () { // 202
+ var self = this; // 203
+ var clone = new MaxHeap(self._comparator, self._heap); // 204
+ return clone; // 205
+ }, // 206
+ // 207
+ maxElementId: function () { // 208
+ var self = this; // 209
+ return self.size() ? self._heap[0].id : null; // 210
+ }, // 211
+ // 212
+ _selfCheck: function () { // 213
+ var self = this; // 214
+ for (var i = 1; i < self._heap.length; i++) // 215
+ if (self._maxIndex(parentIdx(i), i) !== parentIdx(i)) // 216
+ throw new Error("An item with id " + self._heap[i].id + // 217
+ " has a parent younger than it: " + // 218
+ self._heap[parentIdx(i)].id); // 219
+ } // 220
+}); // 221
+ // 222
+function leftChildIdx (i) { return i * 2 + 1; } // 223
+function rightChildIdx (i) { return i * 2 + 2; } // 224
+function parentIdx (i) { return (i - 1) >> 1; } // 225
+ // 226
+ // 227
+////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/binary-heap/min-heap.js //
+// //
+////////////////////////////////////////////////////////////////////////////////////////
+ //
+MinHeap = function (comparator, options) { // 1
+ var self = this; // 2
+ MaxHeap.call(self, function (a, b) { // 3
+ return -comparator(a, b); // 4
+ }, options); // 5
+}; // 6
+ // 7
+Meteor._inherits(MinHeap, MaxHeap); // 8
+ // 9
+_.extend(MinHeap.prototype, { // 10
+ maxElementId: function () { // 11
+ throw new Error("Cannot call maxElementId on MinHeap"); // 12
+ }, // 13
+ minElementId: function () { // 14
+ var self = this; // 15
+ return MaxHeap.prototype.maxElementId.call(self); // 16
+ } // 17
+}); // 18
+ // 19
+ // 20
+////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/binary-heap/min-max-heap.js //
+// //
+////////////////////////////////////////////////////////////////////////////////////////
+ //
+// This implementation of Min/Max-Heap is just a subclass of Max-Heap // 1
+// with a Min-Heap as an encapsulated property. // 2
+// // 3
+// Most of the operations are just proxy methods to call the same method on both // 4
+// heaps. // 5
+// // 6
+// This implementation takes 2*N memory but is fairly simple to write and // 7
+// understand. And the constant factor of a simple Heap is usually smaller // 8
+// compared to other two-way priority queues like Min/Max Heaps // 9
+// (http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf) // 10
+// and Interval Heaps // 11
+// (http://www.cise.ufl.edu/~sahni/dsaac/enrich/c13/double.htm) // 12
+MinMaxHeap = function (comparator, options) { // 13
+ var self = this; // 14
+ // 15
+ MaxHeap.call(self, comparator, options); // 16
+ self._minHeap = new MinHeap(comparator, options); // 17
+}; // 18
+ // 19
+Meteor._inherits(MinMaxHeap, MaxHeap); // 20
+ // 21
+_.extend(MinMaxHeap.prototype, { // 22
+ set: function (id, value) { // 23
+ var self = this; // 24
+ MaxHeap.prototype.set.apply(self, arguments); // 25
+ self._minHeap.set(id, value); // 26
+ }, // 27
+ remove: function (id) { // 28
+ var self = this; // 29
+ MaxHeap.prototype.remove.apply(self, arguments); // 30
+ self._minHeap.remove(id); // 31
+ }, // 32
+ clear: function () { // 33
+ var self = this; // 34
+ MaxHeap.prototype.clear.apply(self, arguments); // 35
+ self._minHeap.clear(); // 36
+ }, // 37
+ setDefault: function (id, def) { // 38
+ var self = this; // 39
+ MaxHeap.prototype.setDefault.apply(self, arguments); // 40
+ return self._minHeap.setDefault(id, def); // 41
+ }, // 42
+ clone: function () { // 43
+ var self = this; // 44
+ var clone = new MinMaxHeap(self._comparator, self._heap); // 45
+ return clone; // 46
+ }, // 47
+ minElementId: function () { // 48
+ var self = this; // 49
+ return self._minHeap.minElementId(); // 50
+ } // 51
+}); // 52
+ // 53
+ // 54
+////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['binary-heap'] = {
+ MaxHeap: MaxHeap,
+ MinHeap: MinHeap,
+ MinMaxHeap: MinMaxHeap
+};
+
+})();
+
+//# sourceMappingURL=binary-heap.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/binary-heap.js.map b/web-app/.meteor/local/build/programs/server/packages/binary-heap.js.map
new file mode 100644
index 0000000..d5b4a83
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/binary-heap.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["binary-heap/max-heap.js","binary-heap/min-heap.js","binary-heap/min-max-heap.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,sB;AACA,6D;AACA,a;AACA,kE;AACA,iB;AACA,iD;AACA,0C;AACA,8B;AACA,6E;AACA,gE;AACA,0C;AACA,iC;AACA,qF;AACA,kB;;AAEA,wE;AACA,gF;AACA,gE;AACA,gC;;AAEA,wD;;AAEA,8E;AACA,mB;AACA,oC;;AAEA,4E;AACA,gF;AACA,+E;AACA,0C;AACA,kB;;AAEA,yE;AACA,6E;AACA,iC;AACA,kC;AACA,yC;AACA,E;;AAEA,6B;AACA,mE;AACA,kC;AACA,oB;;AAEA,2C;AACA,0C;AACA,O;;AAEA,kC;AACA,iC;AACA,O;;AAEA,sB;AACA,a;;AAEA,kE;AACA,yD;AACA,wB;AACA,I;;AAEA,6B;AACA,oB;;AAEA,6C;AACA,mC;AACA,qC;AACA,wB;;AAEA,+B;AACA,gD;AACA,O;AACA,gC;AACA,iD;AACA,O;;AAEA,0B;AACA,c;;AAEA,+B;AACA,oB;AACA,K;AACA,I;;AAEA,2B;AACA,oB;;AAEA,qB;AACA,kC;AACA,gD;AACA,+B;AACA,qB;AACA,c;AACA,c;AACA,O;AACA,K;AACA,I;;AAEA,oC;AACA,oB;AACA,iC;AACA,iC;AACA,+D;AACA,I;;AAEA,iE;AACA,wB;AACA,oB;AACA,iC;AACA,I;;AAEA,gC;AACA,oB;AACA,gC;AACA,gC;;AAEA,qC;AACA,qC;;AAEA,4B;AACA,4B;AACA,I;;AAEA,sB;AACA,oB;AACA,uB;AACA,kB;AACA,4C;AACA,I;AACA,6B;AACA,oB;;AAEA,uB;AACA,iC;AACA,e;;AAEA,sC;AACA,oC;;AAEA,qC;AACA,oE;AACA,wB;AACA,oE;AACA,0B;AACA,Y;AACA,+C;AACA,gD;AACA,0C;AACA,K;AACA,I;AACA,yB;AACA,oB;;AAEA,uB;AACA,uC;AACA,sC;;AAEA,yB;AACA,8B;AACA,yB;AACA,iC;;AAEA,2C;AACA,0B;AACA,4B;AACA,c;AACA,yB;AACA,iC;AACA,O;AACA,K;AACA,I;AACA,sB;AACA,oB;AACA,iC;AACA,I;AACA,sB;AACA,oB;AACA,wB;AACA,I;AACA,sB;AACA,oB;AACA,oB;AACA,0B;AACA,I;AACA,+C;AACA,gC;AACA,oB;AACA,uC;AACA,yC;AACA,O;AACA,I;AACA,qB;AACA,oB;AACA,6B;AACA,I;AACA,kC;AACA,oB;AACA,qB;AACA,0B;AACA,sB;AACA,e;AACA,I;AACA,sB;AACA,oB;AACA,0D;AACA,iB;AACA,I;;AAEA,6B;AACA,oB;AACA,iD;AACA,I;;AAEA,2B;AACA,oB;AACA,+C;AACA,2D;AACA,iE;AACA,6D;AACA,uD;AACA,G;AACA,G;;AAEA,+C;AACA,gD;AACA,+C;;;;;;;;;;;;;;;;;;;;AChOA,0C;AACA,kB;AACA,sC;AACA,6B;AACA,c;AACA,E;;AAEA,mC;;AAEA,6B;AACA,6B;AACA,2D;AACA,I;AACA,6B;AACA,oB;AACA,qD;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;;ACjBA,qE;AACA,+C;AACA,E;AACA,gF;AACA,S;AACA,E;AACA,yE;AACA,0E;AACA,+D;AACA,gF;AACA,qB;AACA,+D;AACA,6C;AACA,kB;;AAEA,0C;AACA,mD;AACA,E;;AAEA,sC;;AAEA,gC;AACA,6B;AACA,oB;AACA,iD;AACA,iC;AACA,I;AACA,yB;AACA,oB;AACA,oD;AACA,6B;AACA,I;AACA,sB;AACA,oB;AACA,mD;AACA,0B;AACA,I;AACA,kC;AACA,oB;AACA,wD;AACA,6C;AACA,I;AACA,sB;AACA,oB;AACA,6D;AACA,iB;AACA,I;AACA,6B;AACA,oB;AACA,wC;AACA,G;AACA,G","file":"/packages/binary-heap.js","sourcesContent":["// Constructor of Heap\n// - comparator - Function - given two items returns a number\n// - options:\n// - initData - Array - Optional - the initial data in a format:\n// Object:\n// - id - String - unique id of the item\n// - value - Any - the data value\n// each value is retained\n// - IdMap - Constructor - Optional - custom IdMap class to store id->index\n// mappings internally. Standard IdMap is used by default.\nMaxHeap = function (comparator, options) {\n if (! _.isFunction(comparator))\n throw new Error('Passed comparator is invalid, should be a comparison function');\n var self = this;\n\n // a C-style comparator that is given two values and returns a number,\n // negative if the first value is less than the second, positive if the second\n // value is greater than the first and zero if they are equal.\n self._comparator = comparator;\n\n options = _.defaults(options || {}, { IdMap: IdMap });\n\n // _heapIdx maps an id to an index in the Heap array the corresponding value\n // is located on.\n self._heapIdx = new options.IdMap;\n\n // The Heap data-structure implemented as a 0-based contiguous array where\n // every item on index idx is a node in a complete binary tree. Every node can\n // have children on indexes idx*2+1 and idx*2+2, except for the leaves. Every\n // node has a parent on index (idx-1)/2;\n self._heap = [];\n\n // If the initial array is passed, we can build the heap in linear time\n // complexity (O(N)) compared to linearithmic time complexity (O(nlogn)) if\n // we push elements one by one.\n if (_.isArray(options.initData))\n self._initFromData(options.initData);\n};\n\n_.extend(MaxHeap.prototype, {\n // Builds a new heap in-place in linear time based on passed data\n _initFromData: function (data) {\n var self = this;\n\n self._heap = _.map(data, function (o) {\n return { id: o.id, value: o.value };\n });\n\n _.each(data, function (o, i) {\n self._heapIdx.set(o.id, i);\n });\n\n if (! data.length)\n return;\n\n // start from the first non-leaf - the parent of the last leaf\n for (var i = parentIdx(data.length - 1); i >= 0; i--)\n self._downHeap(i);\n },\n\n _downHeap: function (idx) {\n var self = this;\n\n while (leftChildIdx(idx) < self.size()) {\n var left = leftChildIdx(idx);\n var right = rightChildIdx(idx);\n var largest = idx;\n\n if (left < self.size()) {\n largest = self._maxIndex(largest, left);\n }\n if (right < self.size()) {\n largest = self._maxIndex(largest, right);\n }\n\n if (largest === idx)\n break;\n\n self._swap(largest, idx);\n idx = largest;\n }\n },\n\n _upHeap: function (idx) {\n var self = this;\n\n while (idx > 0) {\n var parent = parentIdx(idx);\n if (self._maxIndex(parent, idx) === idx) {\n self._swap(parent, idx)\n idx = parent;\n } else {\n break;\n }\n }\n },\n\n _maxIndex: function (idxA, idxB) {\n var self = this;\n var valueA = self._get(idxA);\n var valueB = self._get(idxB);\n return self._comparator(valueA, valueB) >= 0 ? idxA : idxB;\n },\n\n // Internal: gets raw data object placed on idxth place in heap\n _get: function (idx) {\n var self = this;\n return self._heap[idx].value;\n },\n\n _swap: function (idxA, idxB) {\n var self = this;\n var recA = self._heap[idxA];\n var recB = self._heap[idxB];\n\n self._heapIdx.set(recA.id, idxB);\n self._heapIdx.set(recB.id, idxA);\n\n self._heap[idxA] = recB;\n self._heap[idxB] = recA;\n },\n\n get: function (id) {\n var self = this;\n if (! self.has(id))\n return null;\n return self._get(self._heapIdx.get(id));\n },\n set: function (id, value) {\n var self = this;\n\n if (self.has(id)) {\n if (self.get(id) === value)\n return;\n\n var idx = self._heapIdx.get(id);\n self._heap[idx].value = value;\n\n // Fix the new value's position\n // Either bubble new value up if it is greater than its parent\n self._upHeap(idx);\n // or bubble it down if it is smaller than one of its children\n self._downHeap(idx);\n } else {\n self._heapIdx.set(id, self._heap.length);\n self._heap.push({ id: id, value: value });\n self._upHeap(self._heap.length - 1);\n }\n },\n remove: function (id) {\n var self = this;\n\n if (self.has(id)) {\n var last = self._heap.length - 1;\n var idx = self._heapIdx.get(id);\n\n if (idx !== last) {\n self._swap(idx, last);\n self._heap.pop();\n self._heapIdx.remove(id);\n\n // Fix the swapped value's position\n self._upHeap(idx);\n self._downHeap(idx);\n } else {\n self._heap.pop();\n self._heapIdx.remove(id);\n }\n }\n },\n has: function (id) {\n var self = this;\n return self._heapIdx.has(id);\n },\n empty: function () {\n var self = this;\n return !self.size();\n },\n clear: function () {\n var self = this;\n self._heap = [];\n self._heapIdx.clear();\n },\n // iterate over values in no particular order\n forEach: function (iterator) {\n var self = this;\n _.each(self._heap, function (obj) {\n return iterator(obj.value, obj.id);\n });\n },\n size: function () {\n var self = this;\n return self._heap.length;\n },\n setDefault: function (id, def) {\n var self = this;\n if (self.has(id))\n return self.get(id);\n self.set(id, def);\n return def;\n },\n clone: function () {\n var self = this;\n var clone = new MaxHeap(self._comparator, self._heap);\n return clone;\n },\n\n maxElementId: function () {\n var self = this;\n return self.size() ? self._heap[0].id : null;\n },\n\n _selfCheck: function () {\n var self = this;\n for (var i = 1; i < self._heap.length; i++)\n if (self._maxIndex(parentIdx(i), i) !== parentIdx(i))\n throw new Error(\"An item with id \" + self._heap[i].id +\n \" has a parent younger than it: \" +\n self._heap[parentIdx(i)].id);\n }\n});\n\nfunction leftChildIdx (i) { return i * 2 + 1; }\nfunction rightChildIdx (i) { return i * 2 + 2; }\nfunction parentIdx (i) { return (i - 1) >> 1; }\n\n","MinHeap = function (comparator, options) {\n var self = this;\n MaxHeap.call(self, function (a, b) {\n return -comparator(a, b);\n }, options);\n};\n\nMeteor._inherits(MinHeap, MaxHeap);\n\n_.extend(MinHeap.prototype, {\n maxElementId: function () {\n throw new Error(\"Cannot call maxElementId on MinHeap\");\n },\n minElementId: function () {\n var self = this;\n return MaxHeap.prototype.maxElementId.call(self);\n }\n});\n\n","// This implementation of Min/Max-Heap is just a subclass of Max-Heap\n// with a Min-Heap as an encapsulated property.\n//\n// Most of the operations are just proxy methods to call the same method on both\n// heaps.\n//\n// This implementation takes 2*N memory but is fairly simple to write and\n// understand. And the constant factor of a simple Heap is usually smaller\n// compared to other two-way priority queues like Min/Max Heaps\n// (http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf)\n// and Interval Heaps\n// (http://www.cise.ufl.edu/~sahni/dsaac/enrich/c13/double.htm)\nMinMaxHeap = function (comparator, options) {\n var self = this;\n\n MaxHeap.call(self, comparator, options);\n self._minHeap = new MinHeap(comparator, options);\n};\n\nMeteor._inherits(MinMaxHeap, MaxHeap);\n\n_.extend(MinMaxHeap.prototype, {\n set: function (id, value) {\n var self = this;\n MaxHeap.prototype.set.apply(self, arguments);\n self._minHeap.set(id, value);\n },\n remove: function (id) {\n var self = this;\n MaxHeap.prototype.remove.apply(self, arguments);\n self._minHeap.remove(id);\n },\n clear: function () {\n var self = this;\n MaxHeap.prototype.clear.apply(self, arguments);\n self._minHeap.clear();\n },\n setDefault: function (id, def) {\n var self = this;\n MaxHeap.prototype.setDefault.apply(self, arguments);\n return self._minHeap.setDefault(id, def);\n },\n clone: function () {\n var self = this;\n var clone = new MinMaxHeap(self._comparator, self._heap);\n return clone;\n },\n minElementId: function () {\n var self = this;\n return self._minHeap.minElementId();\n }\n});\n\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js b/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js
new file mode 100644
index 0000000..a5cf3be
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js
@@ -0,0 +1,403 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var HTML = Package.htmljs.HTML;
+var _ = Package.underscore._;
+
+/* Package-scope variables */
+var BlazeTools, toJSLiteral, toObjectLiteralKey, ToJSVisitor;
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze-tools/preamble.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////////
+ //
+BlazeTools = {}; // 1
+ // 2
+///////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze-tools/tokens.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////////
+ //
+ // 1
+// Adapted from source code of http://xregexp.com/plugins/#unicode // 2
+var unicodeCategories = { // 3
+ Ll: "0061-007A00B500DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F05210523052505270561-05871D00-1D2B1D6B-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7B2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2CF32D00-2D252D272D2DA641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA661A663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CA78EA791A793A7A1A7A3A7A5A7A7A7A9A7FAFB00-FB06FB13-FB17FF41-FF5A",
+ Lm: "02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D6A1D781D9B-1DBF2071207F2090-209C2C7C2C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A7F8A7F9A9CFAA70AADDAAF3AAF4FF70FF9EFF9F",
+ Lo: "00AA00BA01BB01C0-01C3029405D0-05EA05F0-05F20620-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150840-085808A008A2-08AC0904-0939093D09500958-09610972-09770979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10CF10CF20D05-0D0C0D0E-0D100D12-0D3A0D3D0D4E0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC-0EDF0F000F40-0F470F49-0F6C0F88-0F8C1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA10FD-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1BBA-1BE51C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF11CF51CF62135-21382D30-2D672D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31BA31F0-31FF3400-4DB54E00-9FCCA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCAAE0-AAEAAAF2AB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2EABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",
+ Lt: "01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC", // 7
+ Lu: "0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E05200522052405260531-055610A0-10C510C710CD1E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CED2CF2A640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA660A662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BA78DA790A792A7A0A7A2A7A4A7A6A7A8A7AAFF21-FF3A",
+ Mc: "0903093B093E-09400949-094C094E094F0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1BAC1BAD1BE71BEA-1BEC1BEE1BF21BF31C24-1C2B1C341C351CE11CF21CF3302E302FA823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BAAEBAAEEAAEFAAF5ABE3ABE4ABE6ABE7ABE9ABEAABEC",
+ Mn: "0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065F067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0859-085B08E4-08FE0900-0902093A093C0941-0948094D0951-095709620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F8D-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135D-135F1712-17141732-1734175217531772177317B417B517B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91BAB1BE61BE81BE91BED1BEF-1BF11C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF20D0-20DC20E120E5-20F02CEF-2CF12D7F2DE0-2DFF302A-302D3099309AA66FA674-A67DA69FA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1AAECAAEDAAF6ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",
+ Nd: "0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19D91A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",
+ Nl: "16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF", // 12
+ Pc: "005F203F20402054FE33FE34FE4D-FE4FFF3F" // 13
+}; // 14
+ // 15
+var unicodeClass = function (abbrev) { // 16
+ return '[' + // 17
+ unicodeCategories[abbrev].replace(/[0-9A-F]{4}/ig, "\\u$&") + ']'; // 18
+}; // 19
+ // 20
+// See ECMA-262 spec, 3rd edition, Section 7.6 // 21
+// Match one or more characters that can start an identifier. // 22
+// This is IdentifierStart+. // 23
+var rIdentifierPrefix = new RegExp( // 24
+ "^([a-zA-Z$_]+|\\\\u[0-9a-fA-F]{4}|" + // 25
+ [unicodeClass('Lu'), unicodeClass('Ll'), unicodeClass('Lt'), // 26
+ unicodeClass('Lm'), unicodeClass('Lo'), unicodeClass('Nl')].join('|') + // 27
+ ")+"); // 28
+// Match one or more characters that can continue an identifier. // 29
+// This is (IdentifierPart and not IdentifierStart)+. // 30
+// To match a full identifier, match rIdentifierPrefix, then // 31
+// match rIdentifierMiddle followed by rIdentifierPrefix until they both fail. // 32
+var rIdentifierMiddle = new RegExp( // 33
+ "^([0-9]|" + [unicodeClass('Mn'), unicodeClass('Mc'), unicodeClass('Nd'), // 34
+ unicodeClass('Pc')].join('|') + ")+"); // 35
+ // 36
+ // 37
+// See ECMA-262 spec, 3rd edition, Section 7.8.3 // 38
+var rHexLiteral = /^0[xX][0-9a-fA-F]+(?!\w)/; // 39
+var rDecLiteral = // 40
+ /^(((0|[1-9][0-9]*)(\.[0-9]*)?)|\.[0-9]+)([Ee][+-]?[0-9]+)?(?!\w)/; // 41
+ // 42
+// Section 7.8.4 // 43
+var rStringQuote = /^["']/; // 44
+// Match one or more characters besides quotes, backslashes, or line ends // 45
+var rStringMiddle = /^(?=.)[^"'\\]+?((?!.)|(?=["'\\]))/; // 46
+// Match one escape sequence, including the backslash. // 47
+var rEscapeSequence = // 48
+ /^\\(['"\\bfnrtv]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/; // 49
+// Match one ES5 line continuation // 50
+var rLineContinuation = // 51
+ /^\\(\r\n|[\u000A\u000D\u2028\u2029])/; // 52
+ // 53
+ // 54
+BlazeTools.parseNumber = function (scanner) { // 55
+ var startPos = scanner.pos; // 56
+ // 57
+ var isNegative = false; // 58
+ if (scanner.peek() === '-') { // 59
+ scanner.pos++; // 60
+ isNegative = true; // 61
+ } // 62
+ // Note that we allow `"-0xa"`, unlike `Number(...)`. // 63
+ // 64
+ var rest = scanner.rest(); // 65
+ var match = rDecLiteral.exec(rest) || rHexLiteral.exec(rest); // 66
+ if (! match) { // 67
+ scanner.pos = startPos; // 68
+ return null; // 69
+ } // 70
+ var matchText = match[0]; // 71
+ scanner.pos += matchText.length; // 72
+ // 73
+ var text = (isNegative ? '-' : '') + matchText; // 74
+ var value = Number(matchText); // 75
+ value = (isNegative ? -value : value); // 76
+ return { text: text, value: value }; // 77
+}; // 78
+ // 79
+BlazeTools.parseIdentifierName = function (scanner) { // 80
+ var startPos = scanner.pos; // 81
+ var rest = scanner.rest(); // 82
+ var match = rIdentifierPrefix.exec(rest); // 83
+ if (! match) // 84
+ return null; // 85
+ scanner.pos += match[0].length; // 86
+ rest = scanner.rest(); // 87
+ var foundMore = true; // 88
+ // 89
+ while (foundMore) { // 90
+ foundMore = false; // 91
+ // 92
+ match = rIdentifierMiddle.exec(rest); // 93
+ if (match) { // 94
+ foundMore = true; // 95
+ scanner.pos += match[0].length; // 96
+ rest = scanner.rest(); // 97
+ } // 98
+ // 99
+ match = rIdentifierPrefix.exec(rest); // 100
+ if (match) { // 101
+ foundMore = true; // 102
+ scanner.pos += match[0].length; // 103
+ rest = scanner.rest(); // 104
+ } // 105
+ } // 106
+ // 107
+ return scanner.input.substring(startPos, scanner.pos); // 108
+}; // 109
+ // 110
+BlazeTools.parseStringLiteral = function (scanner) { // 111
+ var startPos = scanner.pos; // 112
+ var rest = scanner.rest(); // 113
+ var match = rStringQuote.exec(rest); // 114
+ if (! match) // 115
+ return null; // 116
+ // 117
+ var quote = match[0]; // 118
+ scanner.pos++; // 119
+ rest = scanner.rest(); // 120
+ // 121
+ var jsonLiteral = '"'; // 122
+ // 123
+ while (match) { // 124
+ match = rStringMiddle.exec(rest); // 125
+ if (match) { // 126
+ jsonLiteral += match[0]; // 127
+ } else { // 128
+ match = rEscapeSequence.exec(rest); // 129
+ if (match) { // 130
+ var esc = match[0]; // 131
+ // Convert all string escapes to JSON-compatible string escapes, so we // 132
+ // can use JSON.parse for some of the work. JSON strings are not the // 133
+ // same as JS strings. They don't support `\0`, `\v`, `\'`, or hex // 134
+ // escapes. // 135
+ if (esc === '\\0') // 136
+ jsonLiteral += '\\u0000'; // 137
+ else if (esc === '\\v') // 138
+ // Note: IE 8 doesn't correctly parse '\v' in JavaScript. // 139
+ jsonLiteral += '\\u000b'; // 140
+ else if (esc.charAt(1) === 'x') // 141
+ jsonLiteral += '\\u00' + esc.slice(2); // 142
+ else if (esc === '\\\'') // 143
+ jsonLiteral += "'"; // 144
+ else // 145
+ jsonLiteral += esc; // 146
+ } else { // 147
+ match = rLineContinuation.exec(rest); // 148
+ if (! match) { // 149
+ match = rStringQuote.exec(rest); // 150
+ if (match) { // 151
+ var c = match[0]; // 152
+ if (c !== quote) { // 153
+ if (c === '"') // 154
+ jsonLiteral += '\\'; // 155
+ jsonLiteral += c; // 156
+ } // 157
+ } // 158
+ } // 159
+ } // 160
+ } // 161
+ if (match) { // 162
+ scanner.pos += match[0].length; // 163
+ rest = scanner.rest(); // 164
+ if (match[0] === quote) // 165
+ break; // 166
+ } // 167
+ } // 168
+ // 169
+ if (match[0] !== quote) // 170
+ scanner.fatal("Unterminated string literal"); // 171
+ // 172
+ jsonLiteral += '"'; // 173
+ var text = scanner.input.substring(startPos, scanner.pos); // 174
+ var value = JSON.parse(jsonLiteral); // 175
+ return { text: text, value: value }; // 176
+}; // 177
+ // 178
+///////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze-tools/tojs.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////////
+ //
+ // 1
+BlazeTools.EmitCode = function (value) { // 2
+ if (! (this instanceof BlazeTools.EmitCode)) // 3
+ // called without `new` // 4
+ return new BlazeTools.EmitCode(value); // 5
+ // 6
+ if (typeof value !== 'string') // 7
+ throw new Error('BlazeTools.EmitCode must be constructed with a string'); // 8
+ // 9
+ this.value = value; // 10
+}; // 11
+BlazeTools.EmitCode.prototype.toJS = function (visitor) { // 12
+ return this.value; // 13
+}; // 14
+ // 15
+// Turns any JSONable value into a JavaScript literal. // 16
+toJSLiteral = function (obj) { // 17
+ // See for `\u2028\u2029`. // 18
+ // Also escape Unicode surrogates. // 19
+ return (JSON.stringify(obj) // 20
+ .replace(/[\u2028\u2029\ud800-\udfff]/g, function (c) { // 21
+ return '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4); // 22
+ })); // 23
+}; // 24
+BlazeTools.toJSLiteral = toJSLiteral; // 25
+ // 26
+ // 27
+ // 28
+var jsReservedWordSet = (function (set) { // 29
+ _.each("abstract else instanceof super boolean enum int switch break export interface synchronized byte extends let this case false long throw catch final native throws char finally new transient class float null true const for package try continue function private typeof debugger goto protected var default if public void delete implements return volatile do import short while double in static with".split(' '), function (w) {
+ set[w] = 1; // 31
+ }); // 32
+ return set; // 33
+})({}); // 34
+ // 35
+toObjectLiteralKey = function (k) { // 36
+ if (/^[a-zA-Z$_][a-zA-Z$0-9_]*$/.test(k) && jsReservedWordSet[k] !== 1) // 37
+ return k; // 38
+ return toJSLiteral(k); // 39
+}; // 40
+BlazeTools.toObjectLiteralKey = toObjectLiteralKey; // 41
+ // 42
+var hasToJS = function (x) { // 43
+ return x.toJS && (typeof (x.toJS) === 'function'); // 44
+}; // 45
+ // 46
+ToJSVisitor = HTML.Visitor.extend(); // 47
+ToJSVisitor.def({ // 48
+ visitNull: function (nullOrUndefined) { // 49
+ return 'null'; // 50
+ }, // 51
+ visitPrimitive: function (stringBooleanOrNumber) { // 52
+ return toJSLiteral(stringBooleanOrNumber); // 53
+ }, // 54
+ visitArray: function (array) { // 55
+ var parts = []; // 56
+ for (var i = 0; i < array.length; i++) // 57
+ parts.push(this.visit(array[i])); // 58
+ return '[' + parts.join(', ') + ']'; // 59
+ }, // 60
+ visitTag: function (tag) { // 61
+ return this.generateCall(tag.tagName, tag.attrs, tag.children); // 62
+ }, // 63
+ visitComment: function (comment) { // 64
+ return this.generateCall('HTML.Comment', null, [comment.value]); // 65
+ }, // 66
+ visitCharRef: function (charRef) { // 67
+ return this.generateCall('HTML.CharRef', // 68
+ {html: charRef.html, str: charRef.str}); // 69
+ }, // 70
+ visitRaw: function (raw) { // 71
+ return this.generateCall('HTML.Raw', null, [raw.value]); // 72
+ }, // 73
+ visitObject: function (x) { // 74
+ if (hasToJS(x)) { // 75
+ return x.toJS(this); // 76
+ } // 77
+ // 78
+ throw new Error("Unexpected object in HTMLjs in toJS: " + x); // 79
+ }, // 80
+ generateCall: function (name, attrs, children) { // 81
+ var tagSymbol; // 82
+ if (name.indexOf('.') >= 0) { // 83
+ tagSymbol = name; // 84
+ } else if (HTML.isTagEnsured(name)) { // 85
+ tagSymbol = 'HTML.' + HTML.getSymbolName(name); // 86
+ } else { // 87
+ tagSymbol = 'HTML.getTag(' + toJSLiteral(name) + ')'; // 88
+ } // 89
+ // 90
+ var attrsArray = null; // 91
+ if (attrs) { // 92
+ attrsArray = []; // 93
+ var needsHTMLAttrs = false; // 94
+ if (HTML.isArray(attrs)) { // 95
+ var attrsArray = []; // 96
+ for (var i = 0; i < attrs.length; i++) { // 97
+ var a = attrs[i]; // 98
+ if (hasToJS(a)) { // 99
+ attrsArray.push(a.toJS(this)); // 100
+ needsHTMLAttrs = true; // 101
+ } else { // 102
+ var attrsObjStr = this.generateAttrsDictionary(attrs[i]); // 103
+ if (attrsObjStr !== null) // 104
+ attrsArray.push(attrsObjStr); // 105
+ } // 106
+ } // 107
+ } else if (hasToJS(attrs)) { // 108
+ attrsArray.push(attrs.toJS(this)); // 109
+ needsHTMLAttrs = true; // 110
+ } else { // 111
+ attrsArray.push(this.generateAttrsDictionary(attrs)); // 112
+ } // 113
+ } // 114
+ var attrsStr = null; // 115
+ if (attrsArray && attrsArray.length) { // 116
+ if (attrsArray.length === 1 && ! needsHTMLAttrs) { // 117
+ attrsStr = attrsArray[0]; // 118
+ } else { // 119
+ attrsStr = 'HTML.Attrs(' + attrsArray.join(', ') + ')'; // 120
+ } // 121
+ } // 122
+ // 123
+ var argStrs = []; // 124
+ if (attrsStr !== null) // 125
+ argStrs.push(attrsStr); // 126
+ // 127
+ if (children) { // 128
+ for (var i = 0; i < children.length; i++) // 129
+ argStrs.push(this.visit(children[i])); // 130
+ } // 131
+ // 132
+ return tagSymbol + '(' + argStrs.join(', ') + ')'; // 133
+ }, // 134
+ generateAttrsDictionary: function (attrsDict) { // 135
+ if (attrsDict.toJS && (typeof (attrsDict.toJS) === 'function')) { // 136
+ // not an attrs dictionary, but something else! Like a template tag. // 137
+ return attrsDict.toJS(this); // 138
+ } // 139
+ // 140
+ var kvStrs = []; // 141
+ for (var k in attrsDict) { // 142
+ if (! HTML.isNully(attrsDict[k])) // 143
+ kvStrs.push(toObjectLiteralKey(k) + ': ' + // 144
+ this.visit(attrsDict[k])); // 145
+ } // 146
+ if (kvStrs.length) // 147
+ return '{' + kvStrs.join(', ') + '}'; // 148
+ return null; // 149
+ } // 150
+}); // 151
+BlazeTools.ToJSVisitor = ToJSVisitor; // 152
+ // 153
+BlazeTools.toJS = function (content) { // 154
+ return (new ToJSVisitor).visit(content); // 155
+}; // 156
+ // 157
+///////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['blaze-tools'] = {
+ BlazeTools: BlazeTools
+};
+
+})();
+
+//# sourceMappingURL=blaze-tools.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js.map b/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js.map
new file mode 100644
index 0000000..45eac66
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/blaze-tools.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["blaze-tools/preamble.js","blaze-tools/tokens.js","blaze-tools/tojs.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,gB;;;;;;;;;;;;;;;;;;;;ACCA,kE;AACA,yB;AACA,6kF;AACA,wS;AACA,iuE;AACA,gE;AACA,kgF;AACA,gxB;AACA,2zC;AACA,oU;AACA,mE;AACA,6C;AACA,E;;AAEA,sC;AACA,c;AACA,sE;AACA,E;;AAEA,8C;AACA,6D;AACA,4B;AACA,mC;AACA,wC;AACA,gE;AACA,4E;AACA,U;AACA,gE;AACA,qD;AACA,4D;AACA,8E;AACA,mC;AACA,2E;AACA,sD;;;AAGA,gD;AACA,6C;AACA,iB;AACA,yE;;AAEA,gB;AACA,2B;AACA,yE;AACA,wD;AACA,sD;AACA,qB;AACA,mF;AACA,kC;AACA,uB;AACA,6C;;;AAGA,6C;AACA,6B;;AAEA,yB;AACA,+B;AACA,kB;AACA,sB;AACA,G;AACA,uD;;AAEA,4B;AACA,+D;AACA,gB;AACA,2B;AACA,gB;AACA,G;AACA,2B;AACA,kC;;AAEA,iD;AACA,gC;AACA,wC;AACA,sC;AACA,E;;AAEA,qD;AACA,6B;AACA,4B;AACA,2C;AACA,c;AACA,gB;AACA,iC;AACA,wB;AACA,uB;;AAEA,qB;AACA,sB;;AAEA,yC;AACA,gB;AACA,uB;AACA,qC;AACA,4B;AACA,K;;AAEA,yC;AACA,gB;AACA,uB;AACA,qC;AACA,4B;AACA,K;AACA,G;;AAEA,wD;AACA,E;;AAEA,oD;AACA,6B;AACA,4B;AACA,sC;AACA,c;AACA,gB;;AAEA,uB;AACA,gB;AACA,wB;;AAEA,wB;;AAEA,iB;AACA,qC;AACA,gB;AACA,8B;AACA,Y;AACA,yC;AACA,kB;AACA,2B;AACA,8E;AACA,6E;AACA,2E;AACA,mB;AACA,0B;AACA,mC;AACA,+B;AACA,mE;AACA,mC;AACA,uC;AACA,gD;AACA,gC;AACA,6B;AACA,Y;AACA,6B;AACA,c;AACA,6C;AACA,sB;AACA,0C;AACA,sB;AACA,6B;AACA,8B;AACA,4B;AACA,oC;AACA,+B;AACA,a;AACA,W;AACA,S;AACA,O;AACA,K;AACA,gB;AACA,qC;AACA,4B;AACA,6B;AACA,c;AACA,K;AACA,G;;AAEA,yB;AACA,iD;;AAEA,qB;AACA,4D;AACA,sC;AACA,sC;AACA,E;;;;;;;;;;;;;;;;;;;;AC/KA,wC;AACA,8C;AACA,2B;AACA,0C;;AAEA,gC;AACA,6E;;AAEA,qB;AACA,E;AACA,yD;AACA,oB;AACA,E;;AAEA,sD;AACA,8B;AACA,oF;AACA,oC;AACA,6B;AACA,iE;AACA,4E;AACA,c;AACA,E;AACA,qC;;;;AAIA,yC;AACA,+a;AACA,e;AACA,K;AACA,a;AACA,O;;AAEA,mC;AACA,yE;AACA,a;AACA,wB;AACA,E;AACA,mD;;AAEA,4B;AACA,oD;AACA,E;;AAEA,oC;AACA,iB;AACA,yC;AACA,kB;AACA,I;AACA,oD;AACA,8C;AACA,I;AACA,gC;AACA,mB;AACA,0C;AACA,uC;AACA,wC;AACA,I;AACA,4B;AACA,mE;AACA,I;AACA,oC;AACA,oE;AACA,I;AACA,oC;AACA,4C;AACA,qE;AACA,I;AACA,4B;AACA,4D;AACA,I;AACA,6B;AACA,qB;AACA,0B;AACA,K;;AAEA,iE;AACA,I;AACA,kD;AACA,kB;AACA,iC;AACA,uB;AACA,yC;AACA,qD;AACA,Y;AACA,2D;AACA,K;;AAEA,0B;AACA,gB;AACA,sB;AACA,iC;AACA,gC;AACA,4B;AACA,gD;AACA,2B;AACA,2B;AACA,0C;AACA,kC;AACA,kB;AACA,qE;AACA,qC;AACA,2C;AACA,W;AACA,S;AACA,kC;AACA,0C;AACA,8B;AACA,c;AACA,6D;AACA,O;AACA,K;AACA,wB;AACA,0C;AACA,wD;AACA,iC;AACA,c;AACA,+D;AACA,O;AACA,K;;AAEA,qB;AACA,0B;AACA,6B;;AAEA,mB;AACA,+C;AACA,8C;AACA,K;;AAEA,sD;AACA,I;AACA,iD;AACA,qE;AACA,2E;AACA,kC;AACA,K;;AAEA,oB;AACA,8B;AACA,uC;AACA,kD;AACA,8C;AACA,K;AACA,sB;AACA,2C;AACA,gB;AACA,G;AACA,G;AACA,qC;;AAEA,sC;AACA,0C;AACA,E","file":"/packages/blaze-tools.js","sourcesContent":["BlazeTools = {};\n","\n// Adapted from source code of http://xregexp.com/plugins/#unicode\nvar unicodeCategories = {\n Ll: \"0061-007A00B500DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F05210523052505270561-05871D00-1D2B1D6B-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7B2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2CF32D00-2D252D272D2DA641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA661A663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CA78EA791A793A7A1A7A3A7A5A7A7A7A9A7FAFB00-FB06FB13-FB17FF41-FF5A\",\n Lm: \"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D6A1D781D9B-1DBF2071207F2090-209C2C7C2C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A7F8A7F9A9CFAA70AADDAAF3AAF4FF70FF9EFF9F\",\n Lo: \"00AA00BA01BB01C0-01C3029405D0-05EA05F0-05F20620-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150840-085808A008A2-08AC0904-0939093D09500958-09610972-09770979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10CF10CF20D05-0D0C0D0E-0D100D12-0D3A0D3D0D4E0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC-0EDF0F000F40-0F470F49-0F6C0F88-0F8C1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA10FD-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1BBA-1BE51C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF11CF51CF62135-21382D30-2D672D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31BA31F0-31FF3400-4DB54E00-9FCCA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCAAE0-AAEAAAF2AB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2EABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC\",\n Lt: \"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC\",\n Lu: \"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E05200522052405260531-055610A0-10C510C710CD1E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CED2CF2A640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA660A662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BA78DA790A792A7A0A7A2A7A4A7A6A7A8A7AAFF21-FF3A\",\n Mc: \"0903093B093E-09400949-094C094E094F0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1BAC1BAD1BE71BEA-1BEC1BEE1BF21BF31C24-1C2B1C341C351CE11CF21CF3302E302FA823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BAAEBAAEEAAEFAAF5ABE3ABE4ABE6ABE7ABE9ABEAABEC\",\n Mn: \"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065F067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0859-085B08E4-08FE0900-0902093A093C0941-0948094D0951-095709620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F8D-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135D-135F1712-17141732-1734175217531772177317B417B517B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91BAB1BE61BE81BE91BED1BEF-1BF11C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF20D0-20DC20E120E5-20F02CEF-2CF12D7F2DE0-2DFF302A-302D3099309AA66FA674-A67DA69FA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1AAECAAEDAAF6ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26\",\n Nd: \"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19D91A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19\",\n Nl: \"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF\",\n Pc: \"005F203F20402054FE33FE34FE4D-FE4FFF3F\"\n};\n\nvar unicodeClass = function (abbrev) {\n return '[' +\n unicodeCategories[abbrev].replace(/[0-9A-F]{4}/ig, \"\\\\u$&\") + ']';\n};\n\n// See ECMA-262 spec, 3rd edition, Section 7.6\n// Match one or more characters that can start an identifier.\n// This is IdentifierStart+.\nvar rIdentifierPrefix = new RegExp(\n \"^([a-zA-Z$_]+|\\\\\\\\u[0-9a-fA-F]{4}|\" +\n [unicodeClass('Lu'), unicodeClass('Ll'), unicodeClass('Lt'),\n unicodeClass('Lm'), unicodeClass('Lo'), unicodeClass('Nl')].join('|') +\n \")+\");\n// Match one or more characters that can continue an identifier.\n// This is (IdentifierPart and not IdentifierStart)+.\n// To match a full identifier, match rIdentifierPrefix, then\n// match rIdentifierMiddle followed by rIdentifierPrefix until they both fail.\nvar rIdentifierMiddle = new RegExp(\n \"^([0-9]|\" + [unicodeClass('Mn'), unicodeClass('Mc'), unicodeClass('Nd'),\n unicodeClass('Pc')].join('|') + \")+\");\n\n\n// See ECMA-262 spec, 3rd edition, Section 7.8.3\nvar rHexLiteral = /^0[xX][0-9a-fA-F]+(?!\\w)/;\nvar rDecLiteral =\n /^(((0|[1-9][0-9]*)(\\.[0-9]*)?)|\\.[0-9]+)([Ee][+-]?[0-9]+)?(?!\\w)/;\n\n// Section 7.8.4\nvar rStringQuote = /^[\"']/;\n// Match one or more characters besides quotes, backslashes, or line ends\nvar rStringMiddle = /^(?=.)[^\"'\\\\]+?((?!.)|(?=[\"'\\\\]))/;\n// Match one escape sequence, including the backslash.\nvar rEscapeSequence =\n /^\\\\(['\"\\\\bfnrtv]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/;\n// Match one ES5 line continuation\nvar rLineContinuation =\n /^\\\\(\\r\\n|[\\u000A\\u000D\\u2028\\u2029])/;\n\n\nBlazeTools.parseNumber = function (scanner) {\n var startPos = scanner.pos;\n\n var isNegative = false;\n if (scanner.peek() === '-') {\n scanner.pos++;\n isNegative = true;\n }\n // Note that we allow `\"-0xa\"`, unlike `Number(...)`.\n\n var rest = scanner.rest();\n var match = rDecLiteral.exec(rest) || rHexLiteral.exec(rest);\n if (! match) {\n scanner.pos = startPos;\n return null;\n }\n var matchText = match[0];\n scanner.pos += matchText.length;\n\n var text = (isNegative ? '-' : '') + matchText;\n var value = Number(matchText);\n value = (isNegative ? -value : value);\n return { text: text, value: value };\n};\n\nBlazeTools.parseIdentifierName = function (scanner) {\n var startPos = scanner.pos;\n var rest = scanner.rest();\n var match = rIdentifierPrefix.exec(rest);\n if (! match)\n return null;\n scanner.pos += match[0].length;\n rest = scanner.rest();\n var foundMore = true;\n\n while (foundMore) {\n foundMore = false;\n\n match = rIdentifierMiddle.exec(rest);\n if (match) {\n foundMore = true;\n scanner.pos += match[0].length;\n rest = scanner.rest();\n }\n\n match = rIdentifierPrefix.exec(rest);\n if (match) {\n foundMore = true;\n scanner.pos += match[0].length;\n rest = scanner.rest();\n }\n }\n\n return scanner.input.substring(startPos, scanner.pos);\n};\n\nBlazeTools.parseStringLiteral = function (scanner) {\n var startPos = scanner.pos;\n var rest = scanner.rest();\n var match = rStringQuote.exec(rest);\n if (! match)\n return null;\n\n var quote = match[0];\n scanner.pos++;\n rest = scanner.rest();\n\n var jsonLiteral = '\"';\n\n while (match) {\n match = rStringMiddle.exec(rest);\n if (match) {\n jsonLiteral += match[0];\n } else {\n match = rEscapeSequence.exec(rest);\n if (match) {\n var esc = match[0];\n // Convert all string escapes to JSON-compatible string escapes, so we\n // can use JSON.parse for some of the work. JSON strings are not the\n // same as JS strings. They don't support `\\0`, `\\v`, `\\'`, or hex\n // escapes.\n if (esc === '\\\\0')\n jsonLiteral += '\\\\u0000';\n else if (esc === '\\\\v')\n // Note: IE 8 doesn't correctly parse '\\v' in JavaScript.\n jsonLiteral += '\\\\u000b';\n else if (esc.charAt(1) === 'x')\n jsonLiteral += '\\\\u00' + esc.slice(2);\n else if (esc === '\\\\\\'')\n jsonLiteral += \"'\";\n else\n jsonLiteral += esc;\n } else {\n match = rLineContinuation.exec(rest);\n if (! match) {\n match = rStringQuote.exec(rest);\n if (match) {\n var c = match[0];\n if (c !== quote) {\n if (c === '\"')\n jsonLiteral += '\\\\';\n jsonLiteral += c;\n }\n }\n }\n }\n }\n if (match) {\n scanner.pos += match[0].length;\n rest = scanner.rest();\n if (match[0] === quote)\n break;\n }\n }\n\n if (match[0] !== quote)\n scanner.fatal(\"Unterminated string literal\");\n\n jsonLiteral += '\"';\n var text = scanner.input.substring(startPos, scanner.pos);\n var value = JSON.parse(jsonLiteral);\n return { text: text, value: value };\n};\n","\nBlazeTools.EmitCode = function (value) {\n if (! (this instanceof BlazeTools.EmitCode))\n // called without `new`\n return new BlazeTools.EmitCode(value);\n\n if (typeof value !== 'string')\n throw new Error('BlazeTools.EmitCode must be constructed with a string');\n\n this.value = value;\n};\nBlazeTools.EmitCode.prototype.toJS = function (visitor) {\n return this.value;\n};\n\n// Turns any JSONable value into a JavaScript literal.\ntoJSLiteral = function (obj) {\n // See for `\\u2028\\u2029`.\n // Also escape Unicode surrogates.\n return (JSON.stringify(obj)\n .replace(/[\\u2028\\u2029\\ud800-\\udfff]/g, function (c) {\n return '\\\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4);\n }));\n};\nBlazeTools.toJSLiteral = toJSLiteral;\n\n\n\nvar jsReservedWordSet = (function (set) {\n _.each(\"abstract else instanceof super boolean enum int switch break export interface synchronized byte extends let this case false long throw catch final native throws char finally new transient class float null true const for package try continue function private typeof debugger goto protected var default if public void delete implements return volatile do import short while double in static with\".split(' '), function (w) {\n set[w] = 1;\n });\n return set;\n})({});\n\ntoObjectLiteralKey = function (k) {\n if (/^[a-zA-Z$_][a-zA-Z$0-9_]*$/.test(k) && jsReservedWordSet[k] !== 1)\n return k;\n return toJSLiteral(k);\n};\nBlazeTools.toObjectLiteralKey = toObjectLiteralKey;\n\nvar hasToJS = function (x) {\n return x.toJS && (typeof (x.toJS) === 'function');\n};\n\nToJSVisitor = HTML.Visitor.extend();\nToJSVisitor.def({\n visitNull: function (nullOrUndefined) {\n return 'null';\n },\n visitPrimitive: function (stringBooleanOrNumber) {\n return toJSLiteral(stringBooleanOrNumber);\n },\n visitArray: function (array) {\n var parts = [];\n for (var i = 0; i < array.length; i++)\n parts.push(this.visit(array[i]));\n return '[' + parts.join(', ') + ']';\n },\n visitTag: function (tag) {\n return this.generateCall(tag.tagName, tag.attrs, tag.children);\n },\n visitComment: function (comment) {\n return this.generateCall('HTML.Comment', null, [comment.value]);\n },\n visitCharRef: function (charRef) {\n return this.generateCall('HTML.CharRef',\n {html: charRef.html, str: charRef.str});\n },\n visitRaw: function (raw) {\n return this.generateCall('HTML.Raw', null, [raw.value]);\n },\n visitObject: function (x) {\n if (hasToJS(x)) {\n return x.toJS(this);\n }\n\n throw new Error(\"Unexpected object in HTMLjs in toJS: \" + x);\n },\n generateCall: function (name, attrs, children) {\n var tagSymbol;\n if (name.indexOf('.') >= 0) {\n tagSymbol = name;\n } else if (HTML.isTagEnsured(name)) {\n tagSymbol = 'HTML.' + HTML.getSymbolName(name);\n } else {\n tagSymbol = 'HTML.getTag(' + toJSLiteral(name) + ')';\n }\n\n var attrsArray = null;\n if (attrs) {\n attrsArray = [];\n var needsHTMLAttrs = false;\n if (HTML.isArray(attrs)) {\n var attrsArray = [];\n for (var i = 0; i < attrs.length; i++) {\n var a = attrs[i];\n if (hasToJS(a)) {\n attrsArray.push(a.toJS(this));\n needsHTMLAttrs = true;\n } else {\n var attrsObjStr = this.generateAttrsDictionary(attrs[i]);\n if (attrsObjStr !== null)\n attrsArray.push(attrsObjStr);\n }\n }\n } else if (hasToJS(attrs)) {\n attrsArray.push(attrs.toJS(this));\n needsHTMLAttrs = true;\n } else {\n attrsArray.push(this.generateAttrsDictionary(attrs));\n }\n }\n var attrsStr = null;\n if (attrsArray && attrsArray.length) {\n if (attrsArray.length === 1 && ! needsHTMLAttrs) {\n attrsStr = attrsArray[0];\n } else {\n attrsStr = 'HTML.Attrs(' + attrsArray.join(', ') + ')';\n }\n }\n\n var argStrs = [];\n if (attrsStr !== null)\n argStrs.push(attrsStr);\n\n if (children) {\n for (var i = 0; i < children.length; i++)\n argStrs.push(this.visit(children[i]));\n }\n\n return tagSymbol + '(' + argStrs.join(', ') + ')';\n },\n generateAttrsDictionary: function (attrsDict) {\n if (attrsDict.toJS && (typeof (attrsDict.toJS) === 'function')) {\n // not an attrs dictionary, but something else! Like a template tag.\n return attrsDict.toJS(this);\n }\n\n var kvStrs = [];\n for (var k in attrsDict) {\n if (! HTML.isNully(attrsDict[k]))\n kvStrs.push(toObjectLiteralKey(k) + ': ' +\n this.visit(attrsDict[k]));\n }\n if (kvStrs.length)\n return '{' + kvStrs.join(', ') + '}';\n return null;\n }\n});\nBlazeTools.ToJSVisitor = ToJSVisitor;\n\nBlazeTools.toJS = function (content) {\n return (new ToJSVisitor).visit(content);\n};\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/blaze.js b/web-app/.meteor/local/build/programs/server/packages/blaze.js
new file mode 100644
index 0000000..f76533d
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/blaze.js
@@ -0,0 +1,2084 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var Tracker = Package.tracker.Tracker;
+var Deps = Package.tracker.Deps;
+var _ = Package.underscore._;
+var HTML = Package.htmljs.HTML;
+var ObserveSequence = Package['observe-sequence'].ObserveSequence;
+var ReactiveVar = Package['reactive-var'].ReactiveVar;
+
+/* Package-scope variables */
+var Blaze, UI, Handlebars;
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/preamble.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+/** // 1
+ * @namespace Blaze // 2
+ * @summary The namespace for all Blaze-related methods and classes. // 3
+ */ // 4
+Blaze = {}; // 5
+ // 6
+// Utility to HTML-escape a string. Included for legacy reasons. // 7
+Blaze._escape = (function() { // 8
+ var escape_map = { // 9
+ "<": "<", // 10
+ ">": ">", // 11
+ '"': """, // 12
+ "'": "'", // 13
+ "`": "`", /* IE allows backtick-delimited attributes?? */ // 14
+ "&": "&" // 15
+ }; // 16
+ var escape_one = function(c) { // 17
+ return escape_map[c]; // 18
+ }; // 19
+ // 20
+ return function (x) { // 21
+ return x.replace(/[&<>"'`]/g, escape_one); // 22
+ }; // 23
+})(); // 24
+ // 25
+Blaze._warn = function (msg) { // 26
+ msg = 'Warning: ' + msg; // 27
+ // 28
+ if ((typeof Log !== 'undefined') && Log && Log.warn) // 29
+ Log.warn(msg); // use Meteor's "logging" package // 30
+ else if ((typeof console !== 'undefined') && console.log) // 31
+ console.log(msg); // 32
+}; // 33
+ // 34
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/exceptions.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+var debugFunc; // 1
+ // 2
+// We call into user code in many places, and it's nice to catch exceptions // 3
+// propagated from user code immediately so that the whole system doesn't just // 4
+// break. Catching exceptions is easy; reporting them is hard. This helper // 5
+// reports exceptions. // 6
+// // 7
+// Usage: // 8
+// // 9
+// ``` // 10
+// try { // 11
+// // ... someStuff ... // 12
+// } catch (e) { // 13
+// reportUIException(e); // 14
+// } // 15
+// ``` // 16
+// // 17
+// An optional second argument overrides the default message. // 18
+ // 19
+// Set this to `true` to cause `reportException` to throw // 20
+// the next exception rather than reporting it. This is // 21
+// useful in unit tests that test error messages. // 22
+Blaze._throwNextException = false; // 23
+ // 24
+Blaze._reportException = function (e, msg) { // 25
+ if (Blaze._throwNextException) { // 26
+ Blaze._throwNextException = false; // 27
+ throw e; // 28
+ } // 29
+ // 30
+ if (! debugFunc) // 31
+ // adapted from Tracker // 32
+ debugFunc = function () { // 33
+ return (typeof Meteor !== "undefined" ? Meteor._debug : // 34
+ ((typeof console !== "undefined") && console.log ? console.log : // 35
+ function () {})); // 36
+ }; // 37
+ // 38
+ // In Chrome, `e.stack` is a multiline string that starts with the message // 39
+ // and contains a stack trace. Furthermore, `console.log` makes it clickable. // 40
+ // `console.log` supplies the space between the two arguments. // 41
+ debugFunc()(msg || 'Exception caught in template:', e.stack || e.message); // 42
+}; // 43
+ // 44
+Blaze._wrapCatchingExceptions = function (f, where) { // 45
+ if (typeof f !== 'function') // 46
+ return f; // 47
+ // 48
+ return function () { // 49
+ try { // 50
+ return f.apply(this, arguments); // 51
+ } catch (e) { // 52
+ Blaze._reportException(e, 'Exception in ' + where + ':'); // 53
+ } // 54
+ }; // 55
+}; // 56
+ // 57
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/view.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+/// [new] Blaze.View([name], renderMethod) // 1
+/// // 2
+/// Blaze.View is the building block of reactive DOM. Views have // 3
+/// the following features: // 4
+/// // 5
+/// * lifecycle callbacks - Views are created, rendered, and destroyed, // 6
+/// and callbacks can be registered to fire when these things happen. // 7
+/// // 8
+/// * parent pointer - A View points to its parentView, which is the // 9
+/// View that caused it to be rendered. These pointers form a // 10
+/// hierarchy or tree of Views. // 11
+/// // 12
+/// * render() method - A View's render() method specifies the DOM // 13
+/// (or HTML) content of the View. If the method establishes // 14
+/// reactive dependencies, it may be re-run. // 15
+/// // 16
+/// * a DOMRange - If a View is rendered to DOM, its position and // 17
+/// extent in the DOM are tracked using a DOMRange object. // 18
+/// // 19
+/// When a View is constructed by calling Blaze.View, the View is // 20
+/// not yet considered "created." It doesn't have a parentView yet, // 21
+/// and no logic has been run to initialize the View. All real // 22
+/// work is deferred until at least creation time, when the onViewCreated // 23
+/// callbacks are fired, which happens when the View is "used" in // 24
+/// some way that requires it to be rendered. // 25
+/// // 26
+/// ...more lifecycle stuff // 27
+/// // 28
+/// `name` is an optional string tag identifying the View. The only // 29
+/// time it's used is when looking in the View tree for a View of a // 30
+/// particular name; for example, data contexts are stored on Views // 31
+/// of name "with". Names are also useful when debugging, so in // 32
+/// general it's good for functions that create Views to set the name. // 33
+/// Views associated with templates have names of the form "Template.foo". // 34
+ // 35
+/** // 36
+ * @class // 37
+ * @summary Constructor for a View, which represents a reactive region of DOM. // 38
+ * @locus Client // 39
+ * @param {String} [name] Optional. A name for this type of View. See [`view.name`](#view_name). // 40
+ * @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). In this function, `this` is bound to the View.
+ */ // 42
+Blaze.View = function (name, render) { // 43
+ if (! (this instanceof Blaze.View)) // 44
+ // called without `new` // 45
+ return new Blaze.View(name, render); // 46
+ // 47
+ if (typeof name === 'function') { // 48
+ // omitted "name" argument // 49
+ render = name; // 50
+ name = ''; // 51
+ } // 52
+ this.name = name; // 53
+ this._render = render; // 54
+ // 55
+ this._callbacks = { // 56
+ created: null, // 57
+ rendered: null, // 58
+ destroyed: null // 59
+ }; // 60
+ // 61
+ // Setting all properties here is good for readability, // 62
+ // and also may help Chrome optimize the code by keeping // 63
+ // the View object from changing shape too much. // 64
+ this.isCreated = false; // 65
+ this._isCreatedForExpansion = false; // 66
+ this.isRendered = false; // 67
+ this._isAttached = false; // 68
+ this.isDestroyed = false; // 69
+ this._isInRender = false; // 70
+ this.parentView = null; // 71
+ this._domrange = null; // 72
+ // This flag is normally set to false except for the cases when view's parent // 73
+ // was generated as part of expanding some syntactic sugar expressions or // 74
+ // methods. // 75
+ // Ex.: Blaze.renderWithData is an equivalent to creating a view with regular // 76
+ // Blaze.render and wrapping it into {{#with data}}{{/with}} view. Since the // 77
+ // users don't know anything about these generated parent views, Blaze needs // 78
+ // this information to be available on views to make smarter decisions. For // 79
+ // example: removing the generated parent view with the view on Blaze.remove. // 80
+ this._hasGeneratedParent = false; // 81
+ // 82
+ this.renderCount = 0; // 83
+}; // 84
+ // 85
+Blaze.View.prototype._render = function () { return null; }; // 86
+ // 87
+Blaze.View.prototype.onViewCreated = function (cb) { // 88
+ this._callbacks.created = this._callbacks.created || []; // 89
+ this._callbacks.created.push(cb); // 90
+}; // 91
+ // 92
+Blaze.View.prototype._onViewRendered = function (cb) { // 93
+ this._callbacks.rendered = this._callbacks.rendered || []; // 94
+ this._callbacks.rendered.push(cb); // 95
+}; // 96
+ // 97
+Blaze.View.prototype.onViewReady = function (cb) { // 98
+ var self = this; // 99
+ var fire = function () { // 100
+ Tracker.afterFlush(function () { // 101
+ if (! self.isDestroyed) { // 102
+ Blaze._withCurrentView(self, function () { // 103
+ cb.call(self); // 104
+ }); // 105
+ } // 106
+ }); // 107
+ }; // 108
+ self._onViewRendered(function onViewRendered() { // 109
+ if (self.isDestroyed) // 110
+ return; // 111
+ if (! self._domrange.attached) // 112
+ self._domrange.onAttached(fire); // 113
+ else // 114
+ fire(); // 115
+ }); // 116
+}; // 117
+ // 118
+Blaze.View.prototype.onViewDestroyed = function (cb) { // 119
+ this._callbacks.destroyed = this._callbacks.destroyed || []; // 120
+ this._callbacks.destroyed.push(cb); // 121
+}; // 122
+ // 123
+/// View#autorun(func) // 124
+/// // 125
+/// Sets up a Tracker autorun that is "scoped" to this View in two // 126
+/// important ways: 1) Blaze.currentView is automatically set // 127
+/// on every re-run, and 2) the autorun is stopped when the // 128
+/// View is destroyed. As with Tracker.autorun, the first run of // 129
+/// the function is immediate, and a Computation object that can // 130
+/// be used to stop the autorun is returned. // 131
+/// // 132
+/// View#autorun is meant to be called from View callbacks like // 133
+/// onViewCreated, or from outside the rendering process. It may not // 134
+/// be called before the onViewCreated callbacks are fired (too early), // 135
+/// or from a render() method (too confusing). // 136
+/// // 137
+/// Typically, autoruns that update the state // 138
+/// of the View (as in Blaze.With) should be started from an onViewCreated // 139
+/// callback. Autoruns that update the DOM should be started // 140
+/// from either onViewCreated (guarded against the absence of // 141
+/// view._domrange), or onViewReady. // 142
+Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { // 143
+ var self = this; // 144
+ // 145
+ // The restrictions on when View#autorun can be called are in order // 146
+ // to avoid bad patterns, like creating a Blaze.View and immediately // 147
+ // calling autorun on it. A freshly created View is not ready to // 148
+ // have logic run on it; it doesn't have a parentView, for example. // 149
+ // It's when the View is materialized or expanded that the onViewCreated // 150
+ // handlers are fired and the View starts up. // 151
+ // // 152
+ // Letting the render() method call `this.autorun()` is problematic // 153
+ // because of re-render. The best we can do is to stop the old // 154
+ // autorun and start a new one for each render, but that's a pattern // 155
+ // we try to avoid internally because it leads to helpers being // 156
+ // called extra times, in the case where the autorun causes the // 157
+ // view to re-render (and thus the autorun to be torn down and a // 158
+ // new one established). // 159
+ // // 160
+ // We could lift these restrictions in various ways. One interesting // 161
+ // idea is to allow you to call `view.autorun` after instantiating // 162
+ // `view`, and automatically wrap it in `view.onViewCreated`, deferring // 163
+ // the autorun so that it starts at an appropriate time. However, // 164
+ // then we can't return the Computation object to the caller, because // 165
+ // it doesn't exist yet. // 166
+ if (! self.isCreated) { // 167
+ throw new Error("View#autorun must be called from the created callback at the earliest"); // 168
+ } // 169
+ if (this._isInRender) { // 170
+ throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback");
+ } // 172
+ if (Tracker.active) { // 173
+ throw new Error("Can't call View#autorun from a Tracker Computation; try calling it from the created or rendered callback");
+ } // 175
+ // 176
+ // Each local variable allocate additional space on each frame of the // 177
+ // execution stack. When too many variables are allocated on stack, you can // 178
+ // run out of memory on stack running a deep recursion (which is typical for // 179
+ // Blaze functions) and get stackoverlow error. (The size of the stack varies // 180
+ // between browsers). // 181
+ // The trick we use here is to allocate only one variable on stack `locals` // 182
+ // that keeps references to all the rest. Since locals is allocated on heap, // 183
+ // we don't take up any space on the stack. // 184
+ var locals = {}; // 185
+ locals.templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; // 186
+ // 187
+ locals.f = function viewAutorun(c) { // 188
+ return Blaze._withCurrentView(_inViewScope || self, function () { // 189
+ return Blaze.Template._withTemplateInstanceFunc(locals.templateInstanceFunc, function () { // 190
+ return f.call(self, c); // 191
+ }); // 192
+ }); // 193
+ }; // 194
+ // 195
+ // Give the autorun function a better name for debugging and profiling. // 196
+ // The `displayName` property is not part of the spec but browsers like Chrome // 197
+ // and Firefox prefer it in debuggers over the name function was declared by. // 198
+ locals.f.displayName = // 199
+ (self.name || 'anonymous') + ':' + (displayName || 'anonymous'); // 200
+ locals.c = Tracker.autorun(locals.f); // 201
+ // 202
+ self.onViewDestroyed(function () { locals.c.stop(); }); // 203
+ // 204
+ return locals.c; // 205
+}; // 206
+ // 207
+Blaze.View.prototype._errorIfShouldntCallSubscribe = function () { // 208
+ var self = this; // 209
+ // 210
+ if (! self.isCreated) { // 211
+ throw new Error("View#subscribe must be called from the created callback at the earliest"); // 212
+ } // 213
+ if (self._isInRender) { // 214
+ throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback");
+ } // 216
+ if (self.isDestroyed) { // 217
+ throw new Error("Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered.");
+ } // 219
+}; // 220
+ // 221
+/** // 222
+ * Just like Blaze.View#autorun, but with Meteor.subscribe instead of // 223
+ * Tracker.autorun. Stop the subscription when the view is destroyed. // 224
+ * @return {SubscriptionHandle} A handle to the subscription so that you can // 225
+ * see if it is ready, or stop it manually // 226
+ */ // 227
+Blaze.View.prototype.subscribe = function (args, options) { // 228
+ var self = this; // 229
+ options = {} || options; // 230
+ // 231
+ self._errorIfShouldntCallSubscribe(); // 232
+ // 233
+ var subHandle; // 234
+ if (options.connection) { // 235
+ subHandle = options.connection.subscribe.apply(options.connection, args); // 236
+ } else { // 237
+ subHandle = Meteor.subscribe.apply(Meteor, args); // 238
+ } // 239
+ // 240
+ self.onViewDestroyed(function () { // 241
+ subHandle.stop(); // 242
+ }); // 243
+ // 244
+ return subHandle; // 245
+}; // 246
+ // 247
+Blaze.View.prototype.firstNode = function () { // 248
+ if (! this._isAttached) // 249
+ throw new Error("View must be attached before accessing its DOM"); // 250
+ // 251
+ return this._domrange.firstNode(); // 252
+}; // 253
+ // 254
+Blaze.View.prototype.lastNode = function () { // 255
+ if (! this._isAttached) // 256
+ throw new Error("View must be attached before accessing its DOM"); // 257
+ // 258
+ return this._domrange.lastNode(); // 259
+}; // 260
+ // 261
+Blaze._fireCallbacks = function (view, which) { // 262
+ Blaze._withCurrentView(view, function () { // 263
+ Tracker.nonreactive(function fireCallbacks() { // 264
+ var cbs = view._callbacks[which]; // 265
+ for (var i = 0, N = (cbs && cbs.length); i < N; i++) // 266
+ cbs[i].call(view); // 267
+ }); // 268
+ }); // 269
+}; // 270
+ // 271
+Blaze._createView = function (view, parentView, forExpansion) { // 272
+ if (view.isCreated) // 273
+ throw new Error("Can't render the same View twice"); // 274
+ // 275
+ view.parentView = (parentView || null); // 276
+ view.isCreated = true; // 277
+ if (forExpansion) // 278
+ view._isCreatedForExpansion = true; // 279
+ // 280
+ Blaze._fireCallbacks(view, 'created'); // 281
+}; // 282
+ // 283
+var doFirstRender = function (view, initialContent) { // 284
+ var domrange = new Blaze._DOMRange(initialContent); // 285
+ view._domrange = domrange; // 286
+ domrange.view = view; // 287
+ view.isRendered = true; // 288
+ Blaze._fireCallbacks(view, 'rendered'); // 289
+ // 290
+ var teardownHook = null; // 291
+ // 292
+ domrange.onAttached(function attached(range, element) { // 293
+ view._isAttached = true; // 294
+ // 295
+ teardownHook = Blaze._DOMBackend.Teardown.onElementTeardown( // 296
+ element, function teardown() { // 297
+ Blaze._destroyView(view, true /* _skipNodes */); // 298
+ }); // 299
+ }); // 300
+ // 301
+ // tear down the teardown hook // 302
+ view.onViewDestroyed(function () { // 303
+ teardownHook && teardownHook.stop(); // 304
+ teardownHook = null; // 305
+ }); // 306
+ // 307
+ return domrange; // 308
+}; // 309
+ // 310
+// Take an uncreated View `view` and create and render it to DOM, // 311
+// setting up the autorun that updates the View. Returns a new // 312
+// DOMRange, which has been associated with the View. // 313
+// // 314
+// The private arguments `_workStack` and `_intoArray` are passed in // 315
+// by Blaze._materializeDOM. If provided, then we avoid the mutual // 316
+// recursion of calling back into Blaze._materializeDOM so that deep // 317
+// View hierarchies don't blow the stack. Instead, we push tasks onto // 318
+// workStack for the initial rendering and subsequent setup of the // 319
+// View, and they are done after we return. When there is a // 320
+// _workStack, we do not return the new DOMRange, but instead push it // 321
+// into _intoArray from a _workStack task. // 322
+Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { // 323
+ Blaze._createView(view, parentView); // 324
+ // 325
+ var domrange; // 326
+ var lastHtmljs; // 327
+ // We don't expect to be called in a Computation, but just in case, // 328
+ // wrap in Tracker.nonreactive. // 329
+ Tracker.nonreactive(function () { // 330
+ view.autorun(function doRender(c) { // 331
+ // `view.autorun` sets the current view. // 332
+ view.renderCount++; // 333
+ view._isInRender = true; // 334
+ // Any dependencies that should invalidate this Computation come // 335
+ // from this line: // 336
+ var htmljs = view._render(); // 337
+ view._isInRender = false; // 338
+ // 339
+ if (! c.firstRun) { // 340
+ Tracker.nonreactive(function doMaterialize() { // 341
+ // re-render // 342
+ var rangesAndNodes = Blaze._materializeDOM(htmljs, [], view); // 343
+ if (! Blaze._isContentEqual(lastHtmljs, htmljs)) { // 344
+ domrange.setMembers(rangesAndNodes); // 345
+ Blaze._fireCallbacks(view, 'rendered'); // 346
+ } // 347
+ }); // 348
+ } // 349
+ lastHtmljs = htmljs; // 350
+ // 351
+ // Causes any nested views to stop immediately, not when we call // 352
+ // `setMembers` the next time around the autorun. Otherwise, // 353
+ // helpers in the DOM tree to be replaced might be scheduled // 354
+ // to re-run before we have a chance to stop them. // 355
+ Tracker.onInvalidate(function () { // 356
+ if (domrange) { // 357
+ domrange.destroyMembers(); // 358
+ } // 359
+ }); // 360
+ }, undefined, 'materialize'); // 361
+ // 362
+ // first render. lastHtmljs is the first htmljs. // 363
+ var initialContents; // 364
+ if (! _workStack) { // 365
+ initialContents = Blaze._materializeDOM(lastHtmljs, [], view); // 366
+ domrange = doFirstRender(view, initialContents); // 367
+ initialContents = null; // help GC because we close over this scope a lot // 368
+ } else { // 369
+ // We're being called from Blaze._materializeDOM, so to avoid // 370
+ // recursion and save stack space, provide a description of the // 371
+ // work to be done instead of doing it. Tasks pushed onto // 372
+ // _workStack will be done in LIFO order after we return. // 373
+ // The work will still be done within a Tracker.nonreactive, // 374
+ // because it will be done by some call to Blaze._materializeDOM // 375
+ // (which is always called in a Tracker.nonreactive). // 376
+ initialContents = []; // 377
+ // push this function first so that it happens last // 378
+ _workStack.push(function () { // 379
+ domrange = doFirstRender(view, initialContents); // 380
+ initialContents = null; // help GC because of all the closures here // 381
+ _intoArray.push(domrange); // 382
+ }); // 383
+ // now push the task that calculates initialContents // 384
+ _workStack.push([lastHtmljs, initialContents, view]); // 385
+ } // 386
+ }); // 387
+ // 388
+ if (! _workStack) { // 389
+ return domrange; // 390
+ } else { // 391
+ return null; // 392
+ } // 393
+}; // 394
+ // 395
+// Expands a View to HTMLjs, calling `render` recursively on all // 396
+// Views and evaluating any dynamic attributes. Calls the `created` // 397
+// callback, but not the `materialized` or `rendered` callbacks. // 398
+// Destroys the view immediately, unless called in a Tracker Computation, // 399
+// in which case the view will be destroyed when the Computation is // 400
+// invalidated. If called in a Tracker Computation, the result is a // 401
+// reactive string; that is, the Computation will be invalidated // 402
+// if any changes are made to the view or subviews that might affect // 403
+// the HTML. // 404
+Blaze._expandView = function (view, parentView) { // 405
+ Blaze._createView(view, parentView, true /*forExpansion*/); // 406
+ // 407
+ view._isInRender = true; // 408
+ var htmljs = Blaze._withCurrentView(view, function () { // 409
+ return view._render(); // 410
+ }); // 411
+ view._isInRender = false; // 412
+ // 413
+ var result = Blaze._expand(htmljs, view); // 414
+ // 415
+ if (Tracker.active) { // 416
+ Tracker.onInvalidate(function () { // 417
+ Blaze._destroyView(view); // 418
+ }); // 419
+ } else { // 420
+ Blaze._destroyView(view); // 421
+ } // 422
+ // 423
+ return result; // 424
+}; // 425
+ // 426
+// Options: `parentView` // 427
+Blaze._HTMLJSExpander = HTML.TransformingVisitor.extend(); // 428
+Blaze._HTMLJSExpander.def({ // 429
+ visitObject: function (x) { // 430
+ if (x instanceof Blaze.Template) // 431
+ x = x.constructView(); // 432
+ if (x instanceof Blaze.View) // 433
+ return Blaze._expandView(x, this.parentView); // 434
+ // 435
+ // this will throw an error; other objects are not allowed! // 436
+ return HTML.TransformingVisitor.prototype.visitObject.call(this, x); // 437
+ }, // 438
+ visitAttributes: function (attrs) { // 439
+ // expand dynamic attributes // 440
+ if (typeof attrs === 'function') // 441
+ attrs = Blaze._withCurrentView(this.parentView, attrs); // 442
+ // 443
+ // call super (e.g. for case where `attrs` is an array) // 444
+ return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs); // 445
+ }, // 446
+ visitAttribute: function (name, value, tag) { // 447
+ // expand attribute values that are functions. Any attribute value // 448
+ // that contains Views must be wrapped in a function. // 449
+ if (typeof value === 'function') // 450
+ value = Blaze._withCurrentView(this.parentView, value); // 451
+ // 452
+ return HTML.TransformingVisitor.prototype.visitAttribute.call( // 453
+ this, name, value, tag); // 454
+ } // 455
+}); // 456
+ // 457
+// Return Blaze.currentView, but only if it is being rendered // 458
+// (i.e. we are in its render() method). // 459
+var currentViewIfRendering = function () { // 460
+ var view = Blaze.currentView; // 461
+ return (view && view._isInRender) ? view : null; // 462
+}; // 463
+ // 464
+Blaze._expand = function (htmljs, parentView) { // 465
+ parentView = parentView || currentViewIfRendering(); // 466
+ return (new Blaze._HTMLJSExpander( // 467
+ {parentView: parentView})).visit(htmljs); // 468
+}; // 469
+ // 470
+Blaze._expandAttributes = function (attrs, parentView) { // 471
+ parentView = parentView || currentViewIfRendering(); // 472
+ return (new Blaze._HTMLJSExpander( // 473
+ {parentView: parentView})).visitAttributes(attrs); // 474
+}; // 475
+ // 476
+Blaze._destroyView = function (view, _skipNodes) { // 477
+ if (view.isDestroyed) // 478
+ return; // 479
+ view.isDestroyed = true; // 480
+ // 481
+ Blaze._fireCallbacks(view, 'destroyed'); // 482
+ // 483
+ // Destroy views and elements recursively. If _skipNodes, // 484
+ // only recurse up to views, not elements, for the case where // 485
+ // the backend (jQuery) is recursing over the elements already. // 486
+ // 487
+ if (view._domrange) // 488
+ view._domrange.destroyMembers(_skipNodes); // 489
+}; // 490
+ // 491
+Blaze._destroyNode = function (node) { // 492
+ if (node.nodeType === 1) // 493
+ Blaze._DOMBackend.Teardown.tearDownElement(node); // 494
+}; // 495
+ // 496
+// Are the HTMLjs entities `a` and `b` the same? We could be // 497
+// more elaborate here but the point is to catch the most basic // 498
+// cases. // 499
+Blaze._isContentEqual = function (a, b) { // 500
+ if (a instanceof HTML.Raw) { // 501
+ return (b instanceof HTML.Raw) && (a.value === b.value); // 502
+ } else if (a == null) { // 503
+ return (b == null); // 504
+ } else { // 505
+ return (a === b) && // 506
+ ((typeof a === 'number') || (typeof a === 'boolean') || // 507
+ (typeof a === 'string')); // 508
+ } // 509
+}; // 510
+ // 511
+/** // 512
+ * @summary The View corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.
+ * @locus Client // 514
+ * @type {Blaze.View} // 515
+ */ // 516
+Blaze.currentView = null; // 517
+ // 518
+Blaze._withCurrentView = function (view, func) { // 519
+ var oldView = Blaze.currentView; // 520
+ try { // 521
+ Blaze.currentView = view; // 522
+ return func(); // 523
+ } finally { // 524
+ Blaze.currentView = oldView; // 525
+ } // 526
+}; // 527
+ // 528
+// Blaze.render publicly takes a View or a Template. // 529
+// Privately, it takes any HTMLJS (extended with Views and Templates) // 530
+// except null or undefined, or a function that returns any extended // 531
+// HTMLJS. // 532
+var checkRenderContent = function (content) { // 533
+ if (content === null) // 534
+ throw new Error("Can't render null"); // 535
+ if (typeof content === 'undefined') // 536
+ throw new Error("Can't render undefined"); // 537
+ // 538
+ if ((content instanceof Blaze.View) || // 539
+ (content instanceof Blaze.Template) || // 540
+ (typeof content === 'function')) // 541
+ return; // 542
+ // 543
+ try { // 544
+ // Throw if content doesn't look like HTMLJS at the top level // 545
+ // (i.e. verify that this is an HTML.Tag, or an array, // 546
+ // or a primitive, etc.) // 547
+ (new HTML.Visitor).visit(content); // 548
+ } catch (e) { // 549
+ // Make error message suitable for public API // 550
+ throw new Error("Expected Template or View"); // 551
+ } // 552
+}; // 553
+ // 554
+// For Blaze.render and Blaze.toHTML, take content and // 555
+// wrap it in a View, unless it's a single View or // 556
+// Template already. // 557
+var contentAsView = function (content) { // 558
+ checkRenderContent(content); // 559
+ // 560
+ if (content instanceof Blaze.Template) { // 561
+ return content.constructView(); // 562
+ } else if (content instanceof Blaze.View) { // 563
+ return content; // 564
+ } else { // 565
+ var func = content; // 566
+ if (typeof func !== 'function') { // 567
+ func = function () { // 568
+ return content; // 569
+ }; // 570
+ } // 571
+ return Blaze.View('render', func); // 572
+ } // 573
+}; // 574
+ // 575
+// For Blaze.renderWithData and Blaze.toHTMLWithData, wrap content // 576
+// in a function, if necessary, so it can be a content arg to // 577
+// a Blaze.With. // 578
+var contentAsFunc = function (content) { // 579
+ checkRenderContent(content); // 580
+ // 581
+ if (typeof content !== 'function') { // 582
+ return function () { // 583
+ return content; // 584
+ }; // 585
+ } else { // 586
+ return content; // 587
+ } // 588
+}; // 589
+ // 590
+/** // 591
+ * @summary Renders a template or View to DOM nodes and inserts it into the DOM, returning a rendered [View](#blaze_view) which can be passed to [`Blaze.remove`](#blaze_remove).
+ * @locus Client // 593
+ * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render. If a template, a View object is [constructed](#template_constructview). If a View, it must be an unrendered View, which becomes a rendered View and is returned.
+ * @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node. // 595
+ * @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.
+ * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).
+ */ // 598
+Blaze.render = function (content, parentElement, nextNode, parentView) { // 599
+ if (! parentElement) { // 600
+ Blaze._warn("Blaze.render without a parent element is deprecated. " + // 601
+ "You must specify where to insert the rendered content."); // 602
+ } // 603
+ // 604
+ if (nextNode instanceof Blaze.View) { // 605
+ // handle omitted nextNode // 606
+ parentView = nextNode; // 607
+ nextNode = null; // 608
+ } // 609
+ // 610
+ // parentElement must be a DOM node. in particular, can't be the // 611
+ // result of a call to `$`. Can't check if `parentElement instanceof // 612
+ // Node` since 'Node' is undefined in IE8. // 613
+ if (parentElement && typeof parentElement.nodeType !== 'number') // 614
+ throw new Error("'parentElement' must be a DOM node"); // 615
+ if (nextNode && typeof nextNode.nodeType !== 'number') // 'nextNode' is optional // 616
+ throw new Error("'nextNode' must be a DOM node"); // 617
+ // 618
+ parentView = parentView || currentViewIfRendering(); // 619
+ // 620
+ var view = contentAsView(content); // 621
+ Blaze._materializeView(view, parentView); // 622
+ // 623
+ if (parentElement) { // 624
+ view._domrange.attach(parentElement, nextNode); // 625
+ } // 626
+ // 627
+ return view; // 628
+}; // 629
+ // 630
+Blaze.insert = function (view, parentElement, nextNode) { // 631
+ Blaze._warn("Blaze.insert has been deprecated. Specify where to insert the " + // 632
+ "rendered content in the call to Blaze.render."); // 633
+ // 634
+ if (! (view && (view._domrange instanceof Blaze._DOMRange))) // 635
+ throw new Error("Expected template rendered with Blaze.render"); // 636
+ // 637
+ view._domrange.attach(parentElement, nextNode); // 638
+}; // 639
+ // 640
+/** // 641
+ * @summary Renders a template or View to DOM nodes with a data context. Otherwise identical to `Blaze.render`. // 642
+ * @locus Client // 643
+ * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render. // 644
+ * @param {Object|Function} data The data context to use, or a function returning a data context. If a function is provided, it will be reactively re-run.
+ * @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node. // 646
+ * @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.
+ * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).
+ */ // 649
+Blaze.renderWithData = function (content, data, parentElement, nextNode, parentView) { // 650
+ // We defer the handling of optional arguments to Blaze.render. At this point, // 651
+ // `nextNode` may actually be `parentView`. // 652
+ return Blaze.render(Blaze._TemplateWith(data, contentAsFunc(content)), // 653
+ parentElement, nextNode, parentView); // 654
+}; // 655
+ // 656
+/** // 657
+ * @summary Removes a rendered View from the DOM, stopping all reactive updates and event listeners on it. // 658
+ * @locus Client // 659
+ * @param {Blaze.View} renderedView The return value from `Blaze.render` or `Blaze.renderWithData`. // 660
+ */ // 661
+Blaze.remove = function (view) { // 662
+ if (! (view && (view._domrange instanceof Blaze._DOMRange))) // 663
+ throw new Error("Expected template rendered with Blaze.render"); // 664
+ // 665
+ while (view) { // 666
+ if (! view.isDestroyed) { // 667
+ var range = view._domrange; // 668
+ if (range.attached && ! range.parentRange) // 669
+ range.detach(); // 670
+ range.destroy(); // 671
+ } // 672
+ // 673
+ view = view._hasGeneratedParent && view.parentView; // 674
+ } // 675
+}; // 676
+ // 677
+/** // 678
+ * @summary Renders a template or View to a string of HTML. // 679
+ * @locus Client // 680
+ * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.
+ */ // 682
+Blaze.toHTML = function (content, parentView) { // 683
+ parentView = parentView || currentViewIfRendering(); // 684
+ // 685
+ return HTML.toHTML(Blaze._expandView(contentAsView(content), parentView)); // 686
+}; // 687
+ // 688
+/** // 689
+ * @summary Renders a template or View to HTML with a data context. Otherwise identical to `Blaze.toHTML`. // 690
+ * @locus Client // 691
+ * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.
+ * @param {Object|Function} data The data context to use, or a function returning a data context. // 693
+ */ // 694
+Blaze.toHTMLWithData = function (content, data, parentView) { // 695
+ parentView = parentView || currentViewIfRendering(); // 696
+ // 697
+ return HTML.toHTML(Blaze._expandView(Blaze._TemplateWith( // 698
+ data, contentAsFunc(content)), parentView)); // 699
+}; // 700
+ // 701
+Blaze._toText = function (htmljs, parentView, textMode) { // 702
+ if (typeof htmljs === 'function') // 703
+ throw new Error("Blaze._toText doesn't take a function, just HTMLjs"); // 704
+ // 705
+ if ((parentView != null) && ! (parentView instanceof Blaze.View)) { // 706
+ // omitted parentView argument // 707
+ textMode = parentView; // 708
+ parentView = null; // 709
+ } // 710
+ parentView = parentView || currentViewIfRendering(); // 711
+ // 712
+ if (! textMode) // 713
+ throw new Error("textMode required"); // 714
+ if (! (textMode === HTML.TEXTMODE.STRING || // 715
+ textMode === HTML.TEXTMODE.RCDATA || // 716
+ textMode === HTML.TEXTMODE.ATTRIBUTE)) // 717
+ throw new Error("Unknown textMode: " + textMode); // 718
+ // 719
+ return HTML.toText(Blaze._expand(htmljs, parentView), textMode); // 720
+}; // 721
+ // 722
+/** // 723
+ * @summary Returns the current data context, or the data context that was used when rendering a particular DOM element or View from a Meteor template.
+ * @locus Client // 725
+ * @param {DOMElement|Blaze.View} [elementOrView] Optional. An element that was rendered by a Meteor, or a View. // 726
+ */ // 727
+Blaze.getData = function (elementOrView) { // 728
+ var theWith; // 729
+ // 730
+ if (! elementOrView) { // 731
+ theWith = Blaze.getView('with'); // 732
+ } else if (elementOrView instanceof Blaze.View) { // 733
+ var view = elementOrView; // 734
+ theWith = (view.name === 'with' ? view : // 735
+ Blaze.getView(view, 'with')); // 736
+ } else if (typeof elementOrView.nodeType === 'number') { // 737
+ if (elementOrView.nodeType !== 1) // 738
+ throw new Error("Expected DOM element"); // 739
+ theWith = Blaze.getView(elementOrView, 'with'); // 740
+ } else { // 741
+ throw new Error("Expected DOM element or View"); // 742
+ } // 743
+ // 744
+ return theWith ? theWith.dataVar.get() : null; // 745
+}; // 746
+ // 747
+// For back-compat // 748
+Blaze.getElementData = function (element) { // 749
+ Blaze._warn("Blaze.getElementData has been deprecated. Use " + // 750
+ "Blaze.getData(element) instead."); // 751
+ // 752
+ if (element.nodeType !== 1) // 753
+ throw new Error("Expected DOM element"); // 754
+ // 755
+ return Blaze.getData(element); // 756
+}; // 757
+ // 758
+// Both arguments are optional. // 759
+ // 760
+/** // 761
+ * @summary Gets either the current View, or the View enclosing the given DOM element. // 762
+ * @locus Client // 763
+ * @param {DOMElement} [element] Optional. If specified, the View enclosing `element` is returned. // 764
+ */ // 765
+Blaze.getView = function (elementOrView, _viewName) { // 766
+ var viewName = _viewName; // 767
+ // 768
+ if ((typeof elementOrView) === 'string') { // 769
+ // omitted elementOrView; viewName present // 770
+ viewName = elementOrView; // 771
+ elementOrView = null; // 772
+ } // 773
+ // 774
+ // We could eventually shorten the code by folding the logic // 775
+ // from the other methods into this method. // 776
+ if (! elementOrView) { // 777
+ return Blaze._getCurrentView(viewName); // 778
+ } else if (elementOrView instanceof Blaze.View) { // 779
+ return Blaze._getParentView(elementOrView, viewName); // 780
+ } else if (typeof elementOrView.nodeType === 'number') { // 781
+ return Blaze._getElementView(elementOrView, viewName); // 782
+ } else { // 783
+ throw new Error("Expected DOM element or View"); // 784
+ } // 785
+}; // 786
+ // 787
+// Gets the current view or its nearest ancestor of name // 788
+// `name`. // 789
+Blaze._getCurrentView = function (name) { // 790
+ var view = Blaze.currentView; // 791
+ // Better to fail in cases where it doesn't make sense // 792
+ // to use Blaze._getCurrentView(). There will be a current // 793
+ // view anywhere it does. You can check Blaze.currentView // 794
+ // if you want to know whether there is one or not. // 795
+ if (! view) // 796
+ throw new Error("There is no current view"); // 797
+ // 798
+ if (name) { // 799
+ while (view && view.name !== name) // 800
+ view = view.parentView; // 801
+ return view || null; // 802
+ } else { // 803
+ // Blaze._getCurrentView() with no arguments just returns // 804
+ // Blaze.currentView. // 805
+ return view; // 806
+ } // 807
+}; // 808
+ // 809
+Blaze._getParentView = function (view, name) { // 810
+ var v = view.parentView; // 811
+ // 812
+ if (name) { // 813
+ while (v && v.name !== name) // 814
+ v = v.parentView; // 815
+ } // 816
+ // 817
+ return v || null; // 818
+}; // 819
+ // 820
+Blaze._getElementView = function (elem, name) { // 821
+ var range = Blaze._DOMRange.forElement(elem); // 822
+ var view = null; // 823
+ while (range && ! view) { // 824
+ view = (range.view || null); // 825
+ if (! view) { // 826
+ if (range.parentRange) // 827
+ range = range.parentRange; // 828
+ else // 829
+ range = Blaze._DOMRange.forElement(range.parentElement); // 830
+ } // 831
+ } // 832
+ // 833
+ if (name) { // 834
+ while (view && view.name !== name) // 835
+ view = view.parentView; // 836
+ return view || null; // 837
+ } else { // 838
+ return view; // 839
+ } // 840
+}; // 841
+ // 842
+Blaze._addEventMap = function (view, eventMap, thisInHandler) { // 843
+ thisInHandler = (thisInHandler || null); // 844
+ var handles = []; // 845
+ // 846
+ if (! view._domrange) // 847
+ throw new Error("View must have a DOMRange"); // 848
+ // 849
+ view._domrange.onAttached(function attached_eventMaps(range, element) { // 850
+ _.each(eventMap, function (handler, spec) { // 851
+ var clauses = spec.split(/,\s+/); // 852
+ // iterate over clauses of spec, e.g. ['click .foo', 'click .bar'] // 853
+ _.each(clauses, function (clause) { // 854
+ var parts = clause.split(/\s+/); // 855
+ if (parts.length === 0) // 856
+ return; // 857
+ // 858
+ var newEvents = parts.shift(); // 859
+ var selector = parts.join(' '); // 860
+ handles.push(Blaze._EventSupport.listen( // 861
+ element, newEvents, selector, // 862
+ function (evt) { // 863
+ if (! range.containsElement(evt.currentTarget)) // 864
+ return null; // 865
+ var handlerThis = thisInHandler || this; // 866
+ var handlerArgs = arguments; // 867
+ return Blaze._withCurrentView(view, function () { // 868
+ return handler.apply(handlerThis, handlerArgs); // 869
+ }); // 870
+ }, // 871
+ range, function (r) { // 872
+ return r.parentRange; // 873
+ })); // 874
+ }); // 875
+ }); // 876
+ }); // 877
+ // 878
+ view.onViewDestroyed(function () { // 879
+ _.each(handles, function (h) { // 880
+ h.stop(); // 881
+ }); // 882
+ handles.length = 0; // 883
+ }); // 884
+}; // 885
+ // 886
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/builtins.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+Blaze._calculateCondition = function (cond) { // 1
+ if (cond instanceof Array && cond.length === 0) // 2
+ cond = false; // 3
+ return !! cond; // 4
+}; // 5
+ // 6
+/** // 7
+ * @summary Constructs a View that renders content with a data context. // 8
+ * @locus Client // 9
+ * @param {Object|Function} data An object to use as the data context, or a function returning such an object. If a function is provided, it will be reactively re-run.
+ * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). // 11
+ */ // 12
+Blaze.With = function (data, contentFunc) { // 13
+ var view = Blaze.View('with', contentFunc); // 14
+ // 15
+ view.dataVar = new ReactiveVar; // 16
+ // 17
+ view.onViewCreated(function () { // 18
+ if (typeof data === 'function') { // 19
+ // `data` is a reactive function // 20
+ view.autorun(function () { // 21
+ view.dataVar.set(data()); // 22
+ }, view.parentView, 'setData'); // 23
+ } else { // 24
+ view.dataVar.set(data); // 25
+ } // 26
+ }); // 27
+ // 28
+ return view; // 29
+}; // 30
+ // 31
+/** // 32
+ * @summary Constructs a View that renders content conditionally. // 33
+ * @locus Client // 34
+ * @param {Function} conditionFunc A function to reactively re-run. Whether the result is truthy or falsy determines whether `contentFunc` or `elseFunc` is shown. An empty array is considered falsy.
+ * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). // 36
+ * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the "else" case.
+ */ // 38
+Blaze.If = function (conditionFunc, contentFunc, elseFunc, _not) { // 39
+ var conditionVar = new ReactiveVar; // 40
+ // 41
+ var view = Blaze.View(_not ? 'unless' : 'if', function () { // 42
+ return conditionVar.get() ? contentFunc() : // 43
+ (elseFunc ? elseFunc() : null); // 44
+ }); // 45
+ view.__conditionVar = conditionVar; // 46
+ view.onViewCreated(function () { // 47
+ this.autorun(function () { // 48
+ var cond = Blaze._calculateCondition(conditionFunc()); // 49
+ conditionVar.set(_not ? (! cond) : cond); // 50
+ }, this.parentView, 'condition'); // 51
+ }); // 52
+ // 53
+ return view; // 54
+}; // 55
+ // 56
+/** // 57
+ * @summary An inverted [`Blaze.If`](#blaze_if). // 58
+ * @locus Client // 59
+ * @param {Function} conditionFunc A function to reactively re-run. If the result is falsy, `contentFunc` is shown, otherwise `elseFunc` is shown. An empty array is considered falsy.
+ * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). // 61
+ * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the "else" case.
+ */ // 63
+Blaze.Unless = function (conditionFunc, contentFunc, elseFunc) { // 64
+ return Blaze.If(conditionFunc, contentFunc, elseFunc, true /*_not*/); // 65
+}; // 66
+ // 67
+/** // 68
+ * @summary Constructs a View that renders `contentFunc` for each item in a sequence. // 69
+ * @locus Client // 70
+ * @param {Function} argFunc A function to reactively re-run. The function may return a Cursor, an array, null, or undefined.
+ * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). // 72
+ * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content) to display in the case when there are no items to display.
+ */ // 74
+Blaze.Each = function (argFunc, contentFunc, elseFunc) { // 75
+ var eachView = Blaze.View('each', function () { // 76
+ var subviews = this.initialSubviews; // 77
+ this.initialSubviews = null; // 78
+ if (this._isCreatedForExpansion) { // 79
+ this.expandedValueDep = new Tracker.Dependency; // 80
+ this.expandedValueDep.depend(); // 81
+ } // 82
+ return subviews; // 83
+ }); // 84
+ eachView.initialSubviews = []; // 85
+ eachView.numItems = 0; // 86
+ eachView.inElseMode = false; // 87
+ eachView.stopHandle = null; // 88
+ eachView.contentFunc = contentFunc; // 89
+ eachView.elseFunc = elseFunc; // 90
+ eachView.argVar = new ReactiveVar; // 91
+ // 92
+ eachView.onViewCreated(function () { // 93
+ // We evaluate argFunc in an autorun to make sure // 94
+ // Blaze.currentView is always set when it runs (rather than // 95
+ // passing argFunc straight to ObserveSequence). // 96
+ eachView.autorun(function () { // 97
+ eachView.argVar.set(argFunc()); // 98
+ }, eachView.parentView, 'collection'); // 99
+ // 100
+ eachView.stopHandle = ObserveSequence.observe(function () { // 101
+ return eachView.argVar.get(); // 102
+ }, { // 103
+ addedAt: function (id, item, index) { // 104
+ Tracker.nonreactive(function () { // 105
+ var newItemView = Blaze.With(item, eachView.contentFunc); // 106
+ eachView.numItems++; // 107
+ // 108
+ if (eachView.expandedValueDep) { // 109
+ eachView.expandedValueDep.changed(); // 110
+ } else if (eachView._domrange) { // 111
+ if (eachView.inElseMode) { // 112
+ eachView._domrange.removeMember(0); // 113
+ eachView.inElseMode = false; // 114
+ } // 115
+ // 116
+ var range = Blaze._materializeView(newItemView, eachView); // 117
+ eachView._domrange.addMember(range, index); // 118
+ } else { // 119
+ eachView.initialSubviews.splice(index, 0, newItemView); // 120
+ } // 121
+ }); // 122
+ }, // 123
+ removedAt: function (id, item, index) { // 124
+ Tracker.nonreactive(function () { // 125
+ eachView.numItems--; // 126
+ if (eachView.expandedValueDep) { // 127
+ eachView.expandedValueDep.changed(); // 128
+ } else if (eachView._domrange) { // 129
+ eachView._domrange.removeMember(index); // 130
+ if (eachView.elseFunc && eachView.numItems === 0) { // 131
+ eachView.inElseMode = true; // 132
+ eachView._domrange.addMember( // 133
+ Blaze._materializeView( // 134
+ Blaze.View('each_else',eachView.elseFunc), // 135
+ eachView), 0); // 136
+ } // 137
+ } else { // 138
+ eachView.initialSubviews.splice(index, 1); // 139
+ } // 140
+ }); // 141
+ }, // 142
+ changedAt: function (id, newItem, oldItem, index) { // 143
+ Tracker.nonreactive(function () { // 144
+ if (eachView.expandedValueDep) { // 145
+ eachView.expandedValueDep.changed(); // 146
+ } else { // 147
+ var itemView; // 148
+ if (eachView._domrange) { // 149
+ itemView = eachView._domrange.getMember(index).view; // 150
+ } else { // 151
+ itemView = eachView.initialSubviews[index]; // 152
+ } // 153
+ itemView.dataVar.set(newItem); // 154
+ } // 155
+ }); // 156
+ }, // 157
+ movedTo: function (id, item, fromIndex, toIndex) { // 158
+ Tracker.nonreactive(function () { // 159
+ if (eachView.expandedValueDep) { // 160
+ eachView.expandedValueDep.changed(); // 161
+ } else if (eachView._domrange) { // 162
+ eachView._domrange.moveMember(fromIndex, toIndex); // 163
+ } else { // 164
+ var subviews = eachView.initialSubviews; // 165
+ var itemView = subviews[fromIndex]; // 166
+ subviews.splice(fromIndex, 1); // 167
+ subviews.splice(toIndex, 0, itemView); // 168
+ } // 169
+ }); // 170
+ } // 171
+ }); // 172
+ // 173
+ if (eachView.elseFunc && eachView.numItems === 0) { // 174
+ eachView.inElseMode = true; // 175
+ eachView.initialSubviews[0] = // 176
+ Blaze.View('each_else', eachView.elseFunc); // 177
+ } // 178
+ }); // 179
+ // 180
+ eachView.onViewDestroyed(function () { // 181
+ if (eachView.stopHandle) // 182
+ eachView.stopHandle.stop(); // 183
+ }); // 184
+ // 185
+ return eachView; // 186
+}; // 187
+ // 188
+Blaze._TemplateWith = function (arg, contentFunc) { // 189
+ var w; // 190
+ // 191
+ var argFunc = arg; // 192
+ if (typeof arg !== 'function') { // 193
+ argFunc = function () { // 194
+ return arg; // 195
+ }; // 196
+ } // 197
+ // 198
+ // This is a little messy. When we compile `{{> Template.contentBlock}}`, we // 199
+ // wrap it in Blaze._InOuterTemplateScope in order to skip the intermediate // 200
+ // parent Views in the current template. However, when there's an argument // 201
+ // (`{{> Template.contentBlock arg}}`), the argument needs to be evaluated // 202
+ // in the original scope. There's no good order to nest // 203
+ // Blaze._InOuterTemplateScope and Spacebars.TemplateWith to achieve this, // 204
+ // so we wrap argFunc to run it in the "original parentView" of the // 205
+ // Blaze._InOuterTemplateScope. // 206
+ // // 207
+ // To make this better, reconsider _InOuterTemplateScope as a primitive. // 208
+ // Longer term, evaluate expressions in the proper lexical scope. // 209
+ var wrappedArgFunc = function () { // 210
+ var viewToEvaluateArg = null; // 211
+ if (w.parentView && w.parentView.name === 'InOuterTemplateScope') { // 212
+ viewToEvaluateArg = w.parentView.originalParentView; // 213
+ } // 214
+ if (viewToEvaluateArg) { // 215
+ return Blaze._withCurrentView(viewToEvaluateArg, argFunc); // 216
+ } else { // 217
+ return argFunc(); // 218
+ } // 219
+ }; // 220
+ // 221
+ var wrappedContentFunc = function () { // 222
+ var content = contentFunc.call(this); // 223
+ // 224
+ // Since we are generating the Blaze._TemplateWith view for the // 225
+ // user, set the flag on the child view. If `content` is a template, // 226
+ // construct the View so that we can set the flag. // 227
+ if (content instanceof Blaze.Template) { // 228
+ content = content.constructView(); // 229
+ } // 230
+ if (content instanceof Blaze.View) { // 231
+ content._hasGeneratedParent = true; // 232
+ } // 233
+ // 234
+ return content; // 235
+ }; // 236
+ // 237
+ w = Blaze.With(wrappedArgFunc, wrappedContentFunc); // 238
+ w.__isTemplateWith = true; // 239
+ return w; // 240
+}; // 241
+ // 242
+Blaze._InOuterTemplateScope = function (templateView, contentFunc) { // 243
+ var view = Blaze.View('InOuterTemplateScope', contentFunc); // 244
+ var parentView = templateView.parentView; // 245
+ // 246
+ // Hack so that if you call `{{> foo bar}}` and it expands into // 247
+ // `{{#with bar}}{{> foo}}{{/with}}`, and then `foo` is a template // 248
+ // that inserts `{{> Template.contentBlock}}`, the data context for // 249
+ // `Template.contentBlock` is not `bar` but the one enclosing that. // 250
+ if (parentView.__isTemplateWith) // 251
+ parentView = parentView.parentView; // 252
+ // 253
+ view.onViewCreated(function () { // 254
+ this.originalParentView = this.parentView; // 255
+ this.parentView = parentView; // 256
+ }); // 257
+ return view; // 258
+}; // 259
+ // 260
+// XXX COMPAT WITH 0.9.0 // 261
+Blaze.InOuterTemplateScope = Blaze._InOuterTemplateScope; // 262
+ // 263
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/lookup.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+Blaze._globalHelpers = {}; // 1
+ // 2
+// Documented as Template.registerHelper. // 3
+// This definition also provides back-compat for `UI.registerHelper`. // 4
+Blaze.registerHelper = function (name, func) { // 5
+ Blaze._globalHelpers[name] = func; // 6
+}; // 7
+ // 8
+var bindIfIsFunction = function (x, target) { // 9
+ if (typeof x !== 'function') // 10
+ return x; // 11
+ return _.bind(x, target); // 12
+}; // 13
+ // 14
+// If `x` is a function, binds the value of `this` for that function // 15
+// to the current data context. // 16
+var bindDataContext = function (x) { // 17
+ if (typeof x === 'function') { // 18
+ return function () { // 19
+ var data = Blaze.getData(); // 20
+ if (data == null) // 21
+ data = {}; // 22
+ return x.apply(data, arguments); // 23
+ }; // 24
+ } // 25
+ return x; // 26
+}; // 27
+ // 28
+Blaze._OLDSTYLE_HELPER = {}; // 29
+ // 30
+var getTemplateHelper = Blaze._getTemplateHelper = function (template, name) { // 31
+ // XXX COMPAT WITH 0.9.3 // 32
+ var isKnownOldStyleHelper = false; // 33
+ // 34
+ if (template.__helpers.has(name)) { // 35
+ var helper = template.__helpers.get(name); // 36
+ if (helper === Blaze._OLDSTYLE_HELPER) { // 37
+ isKnownOldStyleHelper = true; // 38
+ } else { // 39
+ return helper; // 40
+ } // 41
+ } // 42
+ // 43
+ // old-style helper // 44
+ if (name in template) { // 45
+ // Only warn once per helper // 46
+ if (! isKnownOldStyleHelper) { // 47
+ template.__helpers.set(name, Blaze._OLDSTYLE_HELPER); // 48
+ if (! template._NOWARN_OLDSTYLE_HELPERS) { // 49
+ Blaze._warn('Assigning helper with `' + template.viewName + '.' + // 50
+ name + ' = ...` is deprecated. Use `' + template.viewName + // 51
+ '.helpers(...)` instead.'); // 52
+ } // 53
+ } // 54
+ return template[name]; // 55
+ } // 56
+ // 57
+ return null; // 58
+}; // 59
+ // 60
+var wrapHelper = function (f, templateFunc) { // 61
+ if (typeof f !== "function") { // 62
+ return f; // 63
+ } // 64
+ // 65
+ return function () { // 66
+ var self = this; // 67
+ var args = arguments; // 68
+ // 69
+ return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () { // 70
+ return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args); // 71
+ }); // 72
+ }; // 73
+}; // 74
+ // 75
+// Looks up a name, like "foo" or "..", as a helper of the // 76
+// current template; a global helper; the name of a template; // 77
+// or a property of the data context. Called on the View of // 78
+// a template (i.e. a View with a `.template` property, // 79
+// where the helpers are). Used for the first name in a // 80
+// "path" in a template tag, like "foo" in `{{foo.bar}}` or // 81
+// ".." in `{{frobulate ../blah}}`. // 82
+// // 83
+// Returns a function, a non-function value, or null. If // 84
+// a function is found, it is bound appropriately. // 85
+// // 86
+// NOTE: This function must not establish any reactive // 87
+// dependencies itself. If there is any reactivity in the // 88
+// value, lookup should return a function. // 89
+Blaze.View.prototype.lookup = function (name, _options) { // 90
+ var template = this.template; // 91
+ var lookupTemplate = _options && _options.template; // 92
+ var helper; // 93
+ var boundTmplInstance; // 94
+ // 95
+ if (this.templateInstance) { // 96
+ boundTmplInstance = _.bind(this.templateInstance, this); // 97
+ } // 98
+ // 99
+ if (/^\./.test(name)) { // 100
+ // starts with a dot. must be a series of dots which maps to an // 101
+ // ancestor of the appropriate height. // 102
+ if (!/^(\.)+$/.test(name)) // 103
+ throw new Error("id starting with dot must be a series of dots"); // 104
+ // 105
+ return Blaze._parentData(name.length - 1, true /*_functionWrapped*/); // 106
+ // 107
+ } else if (template && // 108
+ ((helper = getTemplateHelper(template, name)) != null)) { // 109
+ return wrapHelper(bindDataContext(helper), boundTmplInstance); // 110
+ } else if (lookupTemplate && (name in Blaze.Template) && // 111
+ (Blaze.Template[name] instanceof Blaze.Template)) { // 112
+ return Blaze.Template[name]; // 113
+ } else if (Blaze._globalHelpers[name] != null) { // 114
+ return wrapHelper(bindDataContext(Blaze._globalHelpers[name]), // 115
+ boundTmplInstance); // 116
+ } else { // 117
+ return function () { // 118
+ var isCalledAsFunction = (arguments.length > 0); // 119
+ var data = Blaze.getData(); // 120
+ if (lookupTemplate && ! (data && data[name])) { // 121
+ throw new Error("No such template: " + name); // 122
+ } // 123
+ if (isCalledAsFunction && ! (data && data[name])) { // 124
+ throw new Error("No such function: " + name); // 125
+ } // 126
+ if (! data) // 127
+ return null; // 128
+ var x = data[name]; // 129
+ if (typeof x !== 'function') { // 130
+ if (isCalledAsFunction) { // 131
+ throw new Error("Can't call non-function: " + x); // 132
+ } // 133
+ return x; // 134
+ } // 135
+ return x.apply(data, arguments); // 136
+ }; // 137
+ } // 138
+ return null; // 139
+}; // 140
+ // 141
+// Implement Spacebars' {{../..}}. // 142
+// @param height {Number} The number of '..'s // 143
+Blaze._parentData = function (height, _functionWrapped) { // 144
+ // If height is null or undefined, we default to 1, the first parent. // 145
+ if (height == null) { // 146
+ height = 1; // 147
+ } // 148
+ var theWith = Blaze.getView('with'); // 149
+ for (var i = 0; (i < height) && theWith; i++) { // 150
+ theWith = Blaze.getView(theWith, 'with'); // 151
+ } // 152
+ // 153
+ if (! theWith) // 154
+ return null; // 155
+ if (_functionWrapped) // 156
+ return function () { return theWith.dataVar.get(); }; // 157
+ return theWith.dataVar.get(); // 158
+}; // 159
+ // 160
+ // 161
+Blaze.View.prototype.lookupTemplate = function (name) { // 162
+ return this.lookup(name, {template:true}); // 163
+}; // 164
+ // 165
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/template.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// [new] Blaze.Template([viewName], renderFunction) // 1
+// // 2
+// `Blaze.Template` is the class of templates, like `Template.foo` in // 3
+// Meteor, which is `instanceof Template`. // 4
+// // 5
+// `viewKind` is a string that looks like "Template.foo" for templates // 6
+// defined by the compiler. // 7
+ // 8
+/** // 9
+ * @class // 10
+ * @summary Constructor for a Template, which is used to construct Views with particular name and content. // 11
+ * @locus Client // 12
+ * @param {String} [viewName] Optional. A name for Views constructed by this Template. See [`view.name`](#view_name).
+ * @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). This function is used as the `renderFunction` for Views constructed by this Template.
+ */ // 15
+Blaze.Template = function (viewName, renderFunction) { // 16
+ if (! (this instanceof Blaze.Template)) // 17
+ // called without `new` // 18
+ return new Blaze.Template(viewName, renderFunction); // 19
+ // 20
+ if (typeof viewName === 'function') { // 21
+ // omitted "viewName" argument // 22
+ renderFunction = viewName; // 23
+ viewName = ''; // 24
+ } // 25
+ if (typeof viewName !== 'string') // 26
+ throw new Error("viewName must be a String (or omitted)"); // 27
+ if (typeof renderFunction !== 'function') // 28
+ throw new Error("renderFunction must be a function"); // 29
+ // 30
+ this.viewName = viewName; // 31
+ this.renderFunction = renderFunction; // 32
+ // 33
+ this.__helpers = new HelperMap; // 34
+ this.__eventMaps = []; // 35
+ // 36
+ this._callbacks = { // 37
+ created: [], // 38
+ rendered: [], // 39
+ destroyed: [] // 40
+ }; // 41
+}; // 42
+var Template = Blaze.Template; // 43
+ // 44
+var HelperMap = function () {}; // 45
+HelperMap.prototype.get = function (name) { // 46
+ return this[' '+name]; // 47
+}; // 48
+HelperMap.prototype.set = function (name, helper) { // 49
+ this[' '+name] = helper; // 50
+}; // 51
+HelperMap.prototype.has = function (name) { // 52
+ return (' '+name) in this; // 53
+}; // 54
+ // 55
+/** // 56
+ * @summary Returns true if `value` is a template object like `Template.myTemplate`. // 57
+ * @locus Client // 58
+ * @param {Any} value The value to test. // 59
+ */ // 60
+Blaze.isTemplate = function (t) { // 61
+ return (t instanceof Blaze.Template); // 62
+}; // 63
+ // 64
+/** // 65
+ * @name onCreated // 66
+ * @instance // 67
+ * @memberOf Template // 68
+ * @summary Register a function to be called when an instance of this template is created. // 69
+ * @param {Function} callback A function to be added as a callback. // 70
+ * @locus Client // 71
+ */ // 72
+Template.prototype.onCreated = function (cb) { // 73
+ this._callbacks.created.push(cb); // 74
+}; // 75
+ // 76
+/** // 77
+ * @name onRendered // 78
+ * @instance // 79
+ * @memberOf Template // 80
+ * @summary Register a function to be called when an instance of this template is inserted into the DOM. // 81
+ * @param {Function} callback A function to be added as a callback. // 82
+ * @locus Client // 83
+ */ // 84
+Template.prototype.onRendered = function (cb) { // 85
+ this._callbacks.rendered.push(cb); // 86
+}; // 87
+ // 88
+/** // 89
+ * @name onDestroyed // 90
+ * @instance // 91
+ * @memberOf Template // 92
+ * @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed. // 93
+ * @param {Function} callback A function to be added as a callback. // 94
+ * @locus Client // 95
+ */ // 96
+Template.prototype.onDestroyed = function (cb) { // 97
+ this._callbacks.destroyed.push(cb); // 98
+}; // 99
+ // 100
+Template.prototype._getCallbacks = function (which) { // 101
+ var self = this; // 102
+ var callbacks = self[which] ? [self[which]] : []; // 103
+ // Fire all callbacks added with the new API (Template.onRendered()) // 104
+ // as well as the old-style callback (e.g. Template.rendered) for // 105
+ // backwards-compatibility. // 106
+ callbacks = callbacks.concat(self._callbacks[which]); // 107
+ return callbacks; // 108
+}; // 109
+ // 110
+var fireCallbacks = function (callbacks, template) { // 111
+ Template._withTemplateInstanceFunc( // 112
+ function () { return template; }, // 113
+ function () { // 114
+ for (var i = 0, N = callbacks.length; i < N; i++) { // 115
+ callbacks[i].call(template); // 116
+ } // 117
+ }); // 118
+}; // 119
+ // 120
+Template.prototype.constructView = function (contentFunc, elseFunc) { // 121
+ var self = this; // 122
+ var view = Blaze.View(self.viewName, self.renderFunction); // 123
+ view.template = self; // 124
+ // 125
+ view.templateContentBlock = ( // 126
+ contentFunc ? new Template('(contentBlock)', contentFunc) : null); // 127
+ view.templateElseBlock = ( // 128
+ elseFunc ? new Template('(elseBlock)', elseFunc) : null); // 129
+ // 130
+ if (self.__eventMaps || typeof self.events === 'object') { // 131
+ view._onViewRendered(function () { // 132
+ if (view.renderCount !== 1) // 133
+ return; // 134
+ // 135
+ if (! self.__eventMaps.length && typeof self.events === "object") { // 136
+ // Provide limited back-compat support for `.events = {...}` // 137
+ // syntax. Pass `template.events` to the original `.events(...)` // 138
+ // function. This code must run only once per template, in // 139
+ // order to not bind the handlers more than once, which is // 140
+ // ensured by the fact that we only do this when `__eventMaps` // 141
+ // is falsy, and we cause it to be set now. // 142
+ Template.prototype.events.call(self, self.events); // 143
+ } // 144
+ // 145
+ _.each(self.__eventMaps, function (m) { // 146
+ Blaze._addEventMap(view, m, view); // 147
+ }); // 148
+ }); // 149
+ } // 150
+ // 151
+ view._templateInstance = new Blaze.TemplateInstance(view); // 152
+ view.templateInstance = function () { // 153
+ // Update data, firstNode, and lastNode, and return the TemplateInstance // 154
+ // object. // 155
+ var inst = view._templateInstance; // 156
+ // 157
+ /** // 158
+ * @instance // 159
+ * @memberOf Blaze.TemplateInstance // 160
+ * @name data // 161
+ * @summary The data context of this instance's latest invocation. // 162
+ * @locus Client // 163
+ */ // 164
+ inst.data = Blaze.getData(view); // 165
+ // 166
+ if (view._domrange && !view.isDestroyed) { // 167
+ inst.firstNode = view._domrange.firstNode(); // 168
+ inst.lastNode = view._domrange.lastNode(); // 169
+ } else { // 170
+ // on 'created' or 'destroyed' callbacks we don't have a DomRange // 171
+ inst.firstNode = null; // 172
+ inst.lastNode = null; // 173
+ } // 174
+ // 175
+ return inst; // 176
+ }; // 177
+ // 178
+ /** // 179
+ * @name created // 180
+ * @instance // 181
+ * @memberOf Template // 182
+ * @summary Provide a callback when an instance of a template is created. // 183
+ * @locus Client // 184
+ * @deprecated in 1.1 // 185
+ */ // 186
+ // To avoid situations when new callbacks are added in between view // 187
+ // instantiation and event being fired, decide on all callbacks to fire // 188
+ // immediately and then fire them on the event. // 189
+ var createdCallbacks = self._getCallbacks('created'); // 190
+ view.onViewCreated(function () { // 191
+ fireCallbacks(createdCallbacks, view.templateInstance()); // 192
+ }); // 193
+ // 194
+ /** // 195
+ * @name rendered // 196
+ * @instance // 197
+ * @memberOf Template // 198
+ * @summary Provide a callback when an instance of a template is rendered. // 199
+ * @locus Client // 200
+ * @deprecated in 1.1 // 201
+ */ // 202
+ var renderedCallbacks = self._getCallbacks('rendered'); // 203
+ view.onViewReady(function () { // 204
+ fireCallbacks(renderedCallbacks, view.templateInstance()); // 205
+ }); // 206
+ // 207
+ /** // 208
+ * @name destroyed // 209
+ * @instance // 210
+ * @memberOf Template // 211
+ * @summary Provide a callback when an instance of a template is destroyed. // 212
+ * @locus Client // 213
+ * @deprecated in 1.1 // 214
+ */ // 215
+ var destroyedCallbacks = self._getCallbacks('destroyed'); // 216
+ view.onViewDestroyed(function () { // 217
+ fireCallbacks(destroyedCallbacks, view.templateInstance()); // 218
+ }); // 219
+ // 220
+ return view; // 221
+}; // 222
+ // 223
+/** // 224
+ * @class // 225
+ * @summary The class for template instances // 226
+ * @param {Blaze.View} view // 227
+ * @instanceName template // 228
+ */ // 229
+Blaze.TemplateInstance = function (view) { // 230
+ if (! (this instanceof Blaze.TemplateInstance)) // 231
+ // called without `new` // 232
+ return new Blaze.TemplateInstance(view); // 233
+ // 234
+ if (! (view instanceof Blaze.View)) // 235
+ throw new Error("View required"); // 236
+ // 237
+ view._templateInstance = this; // 238
+ // 239
+ /** // 240
+ * @name view // 241
+ * @memberOf Blaze.TemplateInstance // 242
+ * @instance // 243
+ * @summary The [View](#blaze_view) object for this invocation of the template. // 244
+ * @locus Client // 245
+ * @type {Blaze.View} // 246
+ */ // 247
+ this.view = view; // 248
+ this.data = null; // 249
+ // 250
+ /** // 251
+ * @name firstNode // 252
+ * @memberOf Blaze.TemplateInstance // 253
+ * @instance // 254
+ * @summary The first top-level DOM node in this template instance. // 255
+ * @locus Client // 256
+ * @type {DOMNode} // 257
+ */ // 258
+ this.firstNode = null; // 259
+ // 260
+ /** // 261
+ * @name lastNode // 262
+ * @memberOf Blaze.TemplateInstance // 263
+ * @instance // 264
+ * @summary The last top-level DOM node in this template instance. // 265
+ * @locus Client // 266
+ * @type {DOMNode} // 267
+ */ // 268
+ this.lastNode = null; // 269
+ // 270
+ // This dependency is used to identify state transitions in // 271
+ // _subscriptionHandles which could cause the result of // 272
+ // TemplateInstance#subscriptionsReady to change. Basically this is triggered // 273
+ // whenever a new subscription handle is added or when a subscription handle // 274
+ // is removed and they are not ready. // 275
+ this._allSubsReadyDep = new Tracker.Dependency(); // 276
+ this._allSubsReady = false; // 277
+ // 278
+ this._subscriptionHandles = {}; // 279
+}; // 280
+ // 281
+/** // 282
+ * @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object. // 283
+ * @locus Client // 284
+ * @param {String} selector The CSS selector to match, scoped to the template contents. // 285
+ * @returns {DOMNode[]} // 286
+ */ // 287
+Blaze.TemplateInstance.prototype.$ = function (selector) { // 288
+ var view = this.view; // 289
+ if (! view._domrange) // 290
+ throw new Error("Can't use $ on template instance with no DOM"); // 291
+ return view._domrange.$(selector); // 292
+}; // 293
+ // 294
+/** // 295
+ * @summary Find all elements matching `selector` in this template instance. // 296
+ * @locus Client // 297
+ * @param {String} selector The CSS selector to match, scoped to the template contents. // 298
+ * @returns {DOMElement[]} // 299
+ */ // 300
+Blaze.TemplateInstance.prototype.findAll = function (selector) { // 301
+ return Array.prototype.slice.call(this.$(selector)); // 302
+}; // 303
+ // 304
+/** // 305
+ * @summary Find one element matching `selector` in this template instance. // 306
+ * @locus Client // 307
+ * @param {String} selector The CSS selector to match, scoped to the template contents. // 308
+ * @returns {DOMElement} // 309
+ */ // 310
+Blaze.TemplateInstance.prototype.find = function (selector) { // 311
+ var result = this.$(selector); // 312
+ return result[0] || null; // 313
+}; // 314
+ // 315
+/** // 316
+ * @summary A version of [Tracker.autorun](#tracker_autorun) that is stopped when the template is destroyed. // 317
+ * @locus Client // 318
+ * @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object. // 319
+ */ // 320
+Blaze.TemplateInstance.prototype.autorun = function (f) { // 321
+ return this.view.autorun(f); // 322
+}; // 323
+ // 324
+/** // 325
+ * @summary A version of [Meteor.subscribe](#meteor_subscribe) that is stopped // 326
+ * when the template is destroyed. // 327
+ * @return {SubscriptionHandle} The subscription handle to the newly made // 328
+ * subscription. Call `handle.stop()` to manually stop the subscription, or // 329
+ * `handle.ready()` to find out if this particular subscription has loaded all // 330
+ * of its inital data. // 331
+ * @locus Client // 332
+ * @param {String} name Name of the subscription. Matches the name of the // 333
+ * server's `publish()` call. // 334
+ * @param {Any} [arg1,arg2...] Optional arguments passed to publisher function // 335
+ * on server. // 336
+ * @param {Function|Object} [callbacks] Optional. May include `onStop` and // 337
+ * `onReady` callbacks. If a function is passed instead of an object, it is // 338
+ * interpreted as an `onReady` callback. // 339
+ */ // 340
+Blaze.TemplateInstance.prototype.subscribe = function (/* arguments */) { // 341
+ var self = this; // 342
+ // 343
+ var subHandles = self._subscriptionHandles; // 344
+ var args = _.toArray(arguments); // 345
+ // 346
+ // Duplicate logic from Meteor.subscribe // 347
+ var callbacks = {}; // 348
+ if (args.length) { // 349
+ var lastParam = _.last(args); // 350
+ if (_.isFunction(lastParam)) { // 351
+ callbacks.onReady = args.pop(); // 352
+ } else if (lastParam && // 353
+ // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use // 354
+ // onStop with an error callback instead. // 355
+ _.any([lastParam.onReady, lastParam.onError, lastParam.onStop], // 356
+ _.isFunction)) { // 357
+ callbacks = args.pop(); // 358
+ } // 359
+ } // 360
+ // 361
+ var subHandle; // 362
+ var oldStopped = callbacks.onStop; // 363
+ callbacks.onStop = function (error) { // 364
+ // When the subscription is stopped, remove it from the set of tracked // 365
+ // subscriptions to avoid this list growing without bound // 366
+ delete subHandles[subHandle.subscriptionId]; // 367
+ // 368
+ // Removing a subscription can only change the result of subscriptionsReady // 369
+ // if we are not ready (that subscription could be the one blocking us being // 370
+ // ready). // 371
+ if (! self._allSubsReady) { // 372
+ self._allSubsReadyDep.changed(); // 373
+ } // 374
+ // 375
+ if (oldStopped) { // 376
+ oldStopped(error); // 377
+ } // 378
+ }; // 379
+ args.push(callbacks); // 380
+ // 381
+ subHandle = self.view.subscribe.call(self.view, args); // 382
+ // 383
+ if (! _.has(subHandles, subHandle.subscriptionId)) { // 384
+ subHandles[subHandle.subscriptionId] = subHandle; // 385
+ // 386
+ // Adding a new subscription will always cause us to transition from ready // 387
+ // to not ready, but if we are already not ready then this can't make us // 388
+ // ready. // 389
+ if (self._allSubsReady) { // 390
+ self._allSubsReadyDep.changed(); // 391
+ } // 392
+ } // 393
+ // 394
+ return subHandle; // 395
+}; // 396
+ // 397
+/** // 398
+ * @summary A reactive function that returns true when all of the subscriptions // 399
+ * called with [this.subscribe](#TemplateInstance-subscribe) are ready. // 400
+ * @return {Boolean} True if all subscriptions on this template instance are // 401
+ * ready. // 402
+ */ // 403
+Blaze.TemplateInstance.prototype.subscriptionsReady = function () { // 404
+ this._allSubsReadyDep.depend(); // 405
+ // 406
+ this._allSubsReady = _.all(this._subscriptionHandles, function (handle) { // 407
+ return handle.ready(); // 408
+ }); // 409
+ // 410
+ return this._allSubsReady; // 411
+}; // 412
+ // 413
+/** // 414
+ * @summary Specify template helpers available to this template. // 415
+ * @locus Client // 416
+ * @param {Object} helpers Dictionary of helper functions by name. // 417
+ */ // 418
+Template.prototype.helpers = function (dict) { // 419
+ for (var k in dict) // 420
+ this.__helpers.set(k, dict[k]); // 421
+}; // 422
+ // 423
+// Kind of like Blaze.currentView but for the template instance. // 424
+// This is a function, not a value -- so that not all helpers // 425
+// are implicitly dependent on the current template instance's `data` property, // 426
+// which would make them dependenct on the data context of the template // 427
+// inclusion. // 428
+Template._currentTemplateInstanceFunc = null; // 429
+ // 430
+Template._withTemplateInstanceFunc = function (templateInstanceFunc, func) { // 431
+ if (typeof func !== 'function') // 432
+ throw new Error("Expected function, got: " + func); // 433
+ var oldTmplInstanceFunc = Template._currentTemplateInstanceFunc; // 434
+ try { // 435
+ Template._currentTemplateInstanceFunc = templateInstanceFunc; // 436
+ return func(); // 437
+ } finally { // 438
+ Template._currentTemplateInstanceFunc = oldTmplInstanceFunc; // 439
+ } // 440
+}; // 441
+ // 442
+/** // 443
+ * @summary Specify event handlers for this template. // 444
+ * @locus Client // 445
+ * @param {EventMap} eventMap Event handlers to associate with this template. // 446
+ */ // 447
+Template.prototype.events = function (eventMap) { // 448
+ var template = this; // 449
+ var eventMap2 = {}; // 450
+ for (var k in eventMap) { // 451
+ eventMap2[k] = (function (k, v) { // 452
+ return function (event/*, ...*/) { // 453
+ var view = this; // passed by EventAugmenter // 454
+ var data = Blaze.getData(event.currentTarget); // 455
+ if (data == null) // 456
+ data = {}; // 457
+ var args = Array.prototype.slice.call(arguments); // 458
+ var tmplInstanceFunc = _.bind(view.templateInstance, view); // 459
+ args.splice(1, 0, tmplInstanceFunc()); // 460
+ // 461
+ return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () { // 462
+ return v.apply(data, args); // 463
+ }); // 464
+ }; // 465
+ })(k, eventMap[k]); // 466
+ } // 467
+ // 468
+ template.__eventMaps.push(eventMap2); // 469
+}; // 470
+ // 471
+/** // 472
+ * @function // 473
+ * @name instance // 474
+ * @memberOf Template // 475
+ * @summary The [template instance](#template_inst) corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.
+ * @locus Client // 477
+ * @returns {Blaze.TemplateInstance} // 478
+ */ // 479
+Template.instance = function () { // 480
+ return Template._currentTemplateInstanceFunc // 481
+ && Template._currentTemplateInstanceFunc(); // 482
+}; // 483
+ // 484
+// Note: Template.currentData() is documented to take zero arguments, // 485
+// while Blaze.getData takes up to one. // 486
+ // 487
+/** // 488
+ * @summary // 489
+ * // 490
+ * - Inside an `onCreated`, `onRendered`, or `onDestroyed` callback, returns // 491
+ * the data context of the template. // 492
+ * - Inside an event handler, returns the data context of the template on which // 493
+ * this event handler was defined. // 494
+ * - Inside a helper, returns the data context of the DOM node where the helper // 495
+ * was used. // 496
+ * // 497
+ * Establishes a reactive dependency on the result. // 498
+ * @locus Client // 499
+ * @function // 500
+ */ // 501
+Template.currentData = Blaze.getData; // 502
+ // 503
+/** // 504
+ * @summary Accesses other data contexts that enclose the current data context. // 505
+ * @locus Client // 506
+ * @function // 507
+ * @param {Integer} [numLevels] The number of levels beyond the current data context to look. Defaults to 1. // 508
+ */ // 509
+Template.parentData = Blaze._parentData; // 510
+ // 511
+/** // 512
+ * @summary Defines a [helper function](#template_helpers) which can be used from all templates. // 513
+ * @locus Client // 514
+ * @function // 515
+ * @param {String} name The name of the helper function you are defining. // 516
+ * @param {Function} function The helper function itself. // 517
+ */ // 518
+Template.registerHelper = Blaze.registerHelper; // 519
+ // 520
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/blaze/backcompat.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+UI = Blaze; // 1
+ // 2
+Blaze.ReactiveVar = ReactiveVar; // 3
+UI._templateInstance = Blaze.Template.instance; // 4
+ // 5
+Handlebars = {}; // 6
+Handlebars.registerHelper = Blaze.registerHelper; // 7
+ // 8
+Handlebars._escape = Blaze._escape; // 9
+ // 10
+// Return these from {{...}} helpers to achieve the same as returning // 11
+// strings from {{{...}}} helpers // 12
+Handlebars.SafeString = function(string) { // 13
+ this.string = string; // 14
+}; // 15
+Handlebars.SafeString.prototype.toString = function() { // 16
+ return this.string.toString(); // 17
+}; // 18
+ // 19
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.blaze = {
+ Blaze: Blaze,
+ UI: UI,
+ Handlebars: Handlebars
+};
+
+})();
+
+//# sourceMappingURL=blaze.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/blaze.js.map b/web-app/.meteor/local/build/programs/server/packages/blaze.js.map
new file mode 100644
index 0000000..d9f776c
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/blaze.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["blaze/preamble.js","blaze/exceptions.js","blaze/view.js","blaze/builtins.js","blaze/lookup.js","blaze/template.js","blaze/backcompat.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,G;AACA,mB;AACA,oE;AACA,G;AACA,W;;AAEA,iE;AACA,6B;AACA,oB;AACA,gB;AACA,gB;AACA,kB;AACA,kB;AACA,kE;AACA,gB;AACA,I;AACA,gC;AACA,yB;AACA,I;;AAEA,uB;AACA,8C;AACA,I;AACA,K;;AAEA,8B;AACA,0B;;AAEA,sD;AACA,oD;AACA,2D;AACA,qB;AACA,E;;;;;;;;;;;;;;;;;;;AChCA,c;;AAEA,2E;AACA,8E;AACA,4E;AACA,sB;AACA,E;AACA,S;AACA,E;AACA,M;AACA,Q;AACA,yB;AACA,gB;AACA,0B;AACA,I;AACA,M;AACA,E;AACA,6D;;AAEA,yD;AACA,wD;AACA,iD;AACA,kC;;AAEA,4C;AACA,kC;AACA,sC;AACA,Y;AACA,G;;AAEA,kB;AACA,2B;AACA,6B;AACA,6D;AACA,8E;AACA,gC;AACA,M;;AAEA,4E;AACA,gF;AACA,gE;AACA,4E;AACA,E;;AAEA,qD;AACA,8B;AACA,a;;AAEA,sB;AACA,S;AACA,sC;AACA,iB;AACA,+D;AACA,K;AACA,I;AACA,E;;;;;;;;;;;;;;;;;;;ACvDA,0C;AACA,G;AACA,iE;AACA,2B;AACA,G;AACA,uE;AACA,uE;AACA,G;AACA,oE;AACA,gE;AACA,iC;AACA,G;AACA,kE;AACA,+D;AACA,8C;AACA,G;AACA,iE;AACA,4D;AACA,G;AACA,iE;AACA,oE;AACA,+D;AACA,yE;AACA,iE;AACA,6C;AACA,G;AACA,2B;AACA,G;AACA,oE;AACA,mE;AACA,mE;AACA,gE;AACA,sE;AACA,0E;;AAEA,G;AACA,S;AACA,8E;AACA,gB;AACA,kG;AACA,wJ;AACA,G;AACA,sC;AACA,qC;AACA,2B;AACA,wC;;AAEA,mC;AACA,8B;AACA,kB;AACA,c;AACA,G;AACA,mB;AACA,wB;;AAEA,qB;AACA,kB;AACA,mB;AACA,mB;AACA,I;;AAEA,yD;AACA,0D;AACA,kD;AACA,yB;AACA,sC;AACA,0B;AACA,2B;AACA,2B;AACA,2B;AACA,yB;AACA,wB;AACA,+E;AACA,2E;AACA,a;AACA,+E;AACA,8E;AACA,8E;AACA,6E;AACA,+E;AACA,mC;;AAEA,uB;AACA,E;;AAEA,4D;;AAEA,oD;AACA,0D;AACA,mC;AACA,E;;AAEA,sD;AACA,4D;AACA,oC;AACA,E;;AAEA,kD;AACA,kB;AACA,0B;AACA,oC;AACA,+B;AACA,kD;AACA,wB;AACA,W;AACA,O;AACA,O;AACA,I;AACA,kD;AACA,yB;AACA,a;AACA,kC;AACA,sC;AACA,Q;AACA,a;AACA,K;AACA,E;;AAEA,sD;AACA,8D;AACA,qC;AACA,E;;AAEA,sB;AACA,G;AACA,kE;AACA,6D;AACA,2D;AACA,iE;AACA,gE;AACA,4C;AACA,G;AACA,+D;AACA,qE;AACA,uE;AACA,8C;AACA,G;AACA,6C;AACA,0E;AACA,6D;AACA,6D;AACA,oC;AACA,wE;AACA,kB;;AAEA,qE;AACA,sE;AACA,mE;AACA,qE;AACA,0E;AACA,+C;AACA,I;AACA,qE;AACA,iE;AACA,sE;AACA,iE;AACA,iE;AACA,kE;AACA,0B;AACA,I;AACA,uE;AACA,oE;AACA,yE;AACA,oE;AACA,uE;AACA,0B;AACA,yB;AACA,6F;AACA,G;AACA,yB;AACA,0H;AACA,G;AACA,uB;AACA,gI;AACA,G;;AAEA,uE;AACA,6E;AACA,8E;AACA,+E;AACA,uB;AACA,6E;AACA,8E;AACA,6C;AACA,kB;AACA,4E;;AAEA,sC;AACA,qE;AACA,gG;AACA,+B;AACA,S;AACA,O;AACA,I;;AAEA,yE;AACA,gF;AACA,+E;AACA,wB;AACA,oE;AACA,uC;;AAEA,yD;;AAEA,kB;AACA,E;;AAEA,kE;AACA,kB;;AAEA,yB;AACA,+F;AACA,G;AACA,yB;AACA,4H;AACA,G;AACA,yB;AACA,gI;AACA,G;AACA,E;;AAEA,G;AACA,qE;AACA,qE;AACA,4E;AACA,0C;AACA,G;AACA,2D;AACA,kB;AACA,0B;;AAEA,uC;;AAEA,gB;AACA,2B;AACA,6E;AACA,U;AACA,qD;AACA,G;;AAEA,oC;AACA,qB;AACA,K;;AAEA,mB;AACA,E;;AAEA,8C;AACA,yB;AACA,sE;;AAEA,oC;AACA,E;;AAEA,6C;AACA,yB;AACA,sE;;AAEA,mC;AACA,E;;AAEA,+C;AACA,4C;AACA,kD;AACA,uC;AACA,0D;AACA,0B;AACA,O;AACA,K;AACA,E;;AAEA,+D;AACA,qB;AACA,wD;;AAEA,yC;AACA,wB;AACA,mB;AACA,uC;;AAEA,wC;AACA,E;;AAEA,qD;AACA,qD;AACA,4B;AACA,uB;AACA,yB;AACA,yC;;AAEA,0B;;AAEA,yD;AACA,4B;;AAEA,gE;AACA,oC;AACA,wD;AACA,S;AACA,K;;AAEA,gC;AACA,oC;AACA,wC;AACA,wB;AACA,K;;AAEA,kB;AACA,E;;AAEA,iE;AACA,+D;AACA,qD;AACA,E;AACA,oE;AACA,mE;AACA,oE;AACA,sE;AACA,kE;AACA,4D;AACA,qE;AACA,0C;AACA,8E;AACA,sC;;AAEA,e;AACA,iB;AACA,qE;AACA,iC;AACA,mC;AACA,uC;AACA,8C;AACA,yB;AACA,8B;AACA,sE;AACA,wB;AACA,kC;AACA,+B;;AAEA,yB;AACA,sD;AACA,sB;AACA,uE;AACA,4D;AACA,gD;AACA,mD;AACA,W;AACA,W;AACA,O;AACA,0B;;AAEA,sE;AACA,mE;AACA,kE;AACA,wD;AACA,wC;AACA,uB;AACA,oC;AACA,S;AACA,S;AACA,iC;;AAEA,qD;AACA,wB;AACA,uB;AACA,oE;AACA,sD;AACA,+E;AACA,Y;AACA,mE;AACA,qE;AACA,gE;AACA,+D;AACA,kE;AACA,sE;AACA,2D;AACA,2B;AACA,yD;AACA,mC;AACA,wD;AACA,2E;AACA,kC;AACA,S;AACA,0D;AACA,2D;AACA,K;AACA,K;;AAEA,qB;AACA,oB;AACA,U;AACA,gB;AACA,G;AACA,E;;AAEA,gE;AACA,oE;AACA,gE;AACA,yE;AACA,mE;AACA,oE;AACA,gE;AACA,oE;AACA,Y;AACA,iD;AACA,6D;;AAEA,0B;AACA,yD;AACA,0B;AACA,K;AACA,2B;;AAEA,2C;;AAEA,uB;AACA,sC;AACA,+B;AACA,O;AACA,U;AACA,6B;AACA,G;;AAEA,gB;AACA,E;;AAEA,wB;AACA,0D;AACA,2B;AACA,6B;AACA,oC;AACA,4B;AACA,gC;AACA,mD;;AAEA,+D;AACA,wE;AACA,I;AACA,qC;AACA,gC;AACA,oC;AACA,6D;;AAEA,2D;AACA,gF;AACA,I;AACA,+C;AACA,uE;AACA,yD;AACA,oC;AACA,6D;;AAEA,kE;AACA,8B;AACA,G;AACA,G;;AAEA,6D;AACA,wC;AACA,0C;AACA,+B;AACA,kD;AACA,E;;AAEA,+C;AACA,sD;AACA,oC;AACA,6C;AACA,E;;AAEA,wD;AACA,sD;AACA,oC;AACA,sD;AACA,E;;AAEA,kD;AACA,uB;AACA,W;AACA,0B;;AAEA,0C;;AAEA,4D;AACA,+D;AACA,iE;;AAEA,qB;AACA,8C;AACA,E;;AAEA,sC;AACA,0B;AACA,qD;AACA,E;;AAEA,6D;AACA,+D;AACA,S;AACA,yC;AACA,8B;AACA,4D;AACA,yB;AACA,uB;AACA,U;AACA,uB;AACA,6D;AACA,gC;AACA,G;AACA,E;;AAEA,G;AACA,oI;AACA,gB;AACA,qB;AACA,G;AACA,yB;;AAEA,gD;AACA,kC;AACA,O;AACA,6B;AACA,kB;AACA,a;AACA,gC;AACA,G;AACA,E;;AAEA,oD;AACA,qE;AACA,oE;AACA,U;AACA,6C;AACA,uB;AACA,yC;AACA,qC;AACA,8C;;AAEA,wC;AACA,4C;AACA,sC;AACA,W;;AAEA,O;AACA,iE;AACA,0D;AACA,4B;AACA,sC;AACA,e;AACA,iD;AACA,iD;AACA,G;AACA,E;;AAEA,sD;AACA,kD;AACA,oB;AACA,wC;AACA,8B;;AAEA,0C;AACA,mC;AACA,6C;AACA,mB;AACA,U;AACA,uB;AACA,qC;AACA,0B;AACA,uB;AACA,Q;AACA,K;AACA,sC;AACA,G;AACA,E;;AAEA,kE;AACA,6D;AACA,gB;AACA,wC;AACA,8B;;AAEA,sC;AACA,wB;AACA,qB;AACA,M;AACA,U;AACA,mB;AACA,G;AACA,E;;AAEA,G;AACA,iL;AACA,gB;AACA,qR;AACA,sH;AACA,6N;AACA,kI;AACA,G;AACA,wE;AACA,wB;AACA,yE;AACA,0E;AACA,G;;AAEA,uC;AACA,8B;AACA,0B;AACA,oB;AACA,G;;AAEA,kE;AACA,sE;AACA,4C;AACA,kE;AACA,0D;AACA,kF;AACA,qD;;AAEA,sD;;AAEA,oC;AACA,2C;;AAEA,sB;AACA,mD;AACA,G;;AAEA,c;AACA,E;;AAEA,yD;AACA,iF;AACA,+D;;AAEA,8D;AACA,oE;;AAEA,iD;AACA,E;;AAEA,G;AACA,gH;AACA,gB;AACA,kH;AACA,2J;AACA,sH;AACA,6N;AACA,kI;AACA,G;AACA,sF;AACA,iF;AACA,6C;AACA,wE;AACA,+D;AACA,E;;AAEA,G;AACA,0G;AACA,gB;AACA,mG;AACA,G;AACA,gC;AACA,8D;AACA,oE;;AAEA,gB;AACA,6B;AACA,iC;AACA,gD;AACA,uB;AACA,sB;AACA,K;;AAEA,uD;AACA,G;AACA,E;;AAEA,G;AACA,2D;AACA,gB;AACA,oI;AACA,G;AACA,+C;AACA,sD;;AAEA,4E;AACA,E;;AAEA,G;AACA,2G;AACA,gB;AACA,oI;AACA,iG;AACA,G;AACA,6D;AACA,sD;;AAEA,2D;AACA,gD;AACA,E;;AAEA,yD;AACA,mC;AACA,0E;;AAEA,qE;AACA,kC;AACA,0B;AACA,sB;AACA,G;AACA,sD;;AAEA,iB;AACA,yC;AACA,6C;AACA,6C;AACA,+C;AACA,qD;;AAEA,kE;AACA,E;;AAEA,G;AACA,uJ;AACA,gB;AACA,iH;AACA,G;AACA,0C;AACA,c;;AAEA,wB;AACA,oC;AACA,mD;AACA,6B;AACA,4C;AACA,4C;AACA,0D;AACA,qC;AACA,8C;AACA,mD;AACA,U;AACA,oD;AACA,G;;AAEA,gD;AACA,E;;AAEA,kB;AACA,2C;AACA,iE;AACA,iD;;AAEA,6B;AACA,4C;;AAEA,gC;AACA,E;;AAEA,+B;;AAEA,G;AACA,sF;AACA,gB;AACA,mG;AACA,G;AACA,qD;AACA,2B;;AAEA,4C;AACA,8C;AACA,6B;AACA,yB;AACA,G;;AAEA,8D;AACA,6C;AACA,wB;AACA,2C;AACA,mD;AACA,yD;AACA,0D;AACA,0D;AACA,U;AACA,oD;AACA,G;AACA,E;;AAEA,wD;AACA,U;AACA,yC;AACA,+B;AACA,wD;AACA,6D;AACA,4D;AACA,qD;AACA,a;AACA,gD;;AAEA,a;AACA,sC;AACA,6B;AACA,wB;AACA,U;AACA,6D;AACA,yB;AACA,gB;AACA,G;AACA,E;;AAEA,8C;AACA,0B;;AAEA,a;AACA,gC;AACA,uB;AACA,G;;AAEA,mB;AACA,E;;AAEA,+C;AACA,+C;AACA,kB;AACA,2B;AACA,gC;AACA,iB;AACA,4B;AACA,kC;AACA,U;AACA,gE;AACA,K;AACA,G;;AAEA,a;AACA,sC;AACA,6B;AACA,wB;AACA,U;AACA,gB;AACA,G;AACA,E;;AAEA,+D;AACA,0C;AACA,mB;;AAEA,uB;AACA,iD;;AAEA,yE;AACA,+C;AACA,uC;AACA,wE;AACA,yC;AACA,wC;AACA,+B;AACA,iB;;AAEA,sC;AACA,uC;AACA,gD;AACA,uC;AACA,0B;AACA,2D;AACA,0B;AACA,oD;AACA,wC;AACA,6D;AACA,6D;AACA,e;AACA,Y;AACA,+B;AACA,iC;AACA,c;AACA,S;AACA,O;AACA,K;;AAEA,oC;AACA,kC;AACA,e;AACA,O;AACA,uB;AACA,K;AACA,E;;;;;;;;;;;;;;;;;;;ACp3BA,6C;AACA,iD;AACA,iB;AACA,iB;AACA,E;;AAEA,G;AACA,uE;AACA,gB;AACA,wK;AACA,qG;AACA,G;AACA,2C;AACA,6C;;AAEA,iC;;AAEA,kC;AACA,qC;AACA,sC;AACA,gC;AACA,iC;AACA,qC;AACA,Y;AACA,6B;AACA,K;AACA,K;;AAEA,c;AACA,E;;AAEA,G;AACA,iE;AACA,gB;AACA,wM;AACA,qG;AACA,sL;AACA,G;AACA,kE;AACA,qC;;AAEA,6D;AACA,+C;AACA,qC;AACA,K;AACA,qC;AACA,kC;AACA,8B;AACA,4D;AACA,+C;AACA,qC;AACA,K;;AAEA,c;AACA,E;;AAEA,G;AACA,gD;AACA,gB;AACA,wL;AACA,qG;AACA,sL;AACA,G;AACA,gE;AACA,uE;AACA,E;;AAEA,G;AACA,qF;AACA,gB;AACA,8H;AACA,qG;AACA,yK;AACA,G;AACA,wD;AACA,iD;AACA,wC;AACA,gC;AACA,sC;AACA,qD;AACA,qC;AACA,K;AACA,oB;AACA,K;AACA,gC;AACA,wB;AACA,8B;AACA,6B;AACA,qC;AACA,+B;AACA,oC;;AAEA,sC;AACA,qD;AACA,gE;AACA,oD;AACA,kC;AACA,qC;AACA,0C;;AAEA,+D;AACA,mC;AACA,Q;AACA,2C;AACA,yC;AACA,mE;AACA,8B;;AAEA,0C;AACA,gD;AACA,0C;AACA,sC;AACA,iD;AACA,0C;AACA,a;;AAEA,sE;AACA,uD;AACA,kB;AACA,mE;AACA,W;AACA,W;AACA,Q;AACA,6C;AACA,yC;AACA,8B;AACA,0C;AACA,gD;AACA,0C;AACA,mD;AACA,+D;AACA,yC;AACA,2C;AACA,uC;AACA,4D;AACA,gC;AACA,a;AACA,kB;AACA,sD;AACA,W;AACA,W;AACA,Q;AACA,yD;AACA,yC;AACA,0C;AACA,gD;AACA,kB;AACA,yB;AACA,qC;AACA,kE;AACA,oB;AACA,yD;AACA,a;AACA,0C;AACA,W;AACA,W;AACA,Q;AACA,wD;AACA,yC;AACA,0C;AACA,gD;AACA,0C;AACA,8D;AACA,kB;AACA,oD;AACA,+C;AACA,0C;AACA,kD;AACA,W;AACA,W;AACA,O;AACA,O;;AAEA,uD;AACA,iC;AACA,mC;AACA,mD;AACA,K;AACA,K;;AAEA,wC;AACA,4B;AACA,iC;AACA,K;;AAEA,kB;AACA,E;;AAEA,mD;AACA,Q;;AAEA,oB;AACA,kC;AACA,2B;AACA,iB;AACA,M;AACA,G;;AAEA,+E;AACA,6E;AACA,6E;AACA,4E;AACA,0D;AACA,4E;AACA,qE;AACA,iC;AACA,I;AACA,0E;AACA,mE;AACA,oC;AACA,iC;AACA,uE;AACA,0D;AACA,K;AACA,4B;AACA,gE;AACA,Y;AACA,uB;AACA,K;AACA,I;;AAEA,wC;AACA,yC;;AAEA,mE;AACA,yE;AACA,sD;AACA,4C;AACA,wC;AACA,K;AACA,wC;AACA,yC;AACA,K;;AAEA,mB;AACA,I;;AAEA,qD;AACA,4B;AACA,W;AACA,E;;AAEA,oE;AACA,6D;AACA,2C;;AAEA,iE;AACA,oE;AACA,qE;AACA,qE;AACA,kC;AACA,uC;;AAEA,kC;AACA,8C;AACA,iC;AACA,K;AACA,c;AACA,E;;AAEA,wB;AACA,yD;;;;;;;;;;;;;;;;;;;ACrQA,0B;;AAEA,yC;AACA,qE;AACA,8C;AACA,oC;AACA,E;;AAEA,6C;AACA,8B;AACA,a;AACA,2B;AACA,E;;AAEA,oE;AACA,+B;AACA,oC;AACA,gC;AACA,wB;AACA,iC;AACA,uB;AACA,kB;AACA,sC;AACA,M;AACA,G;AACA,W;AACA,E;;AAEA,4B;;AAEA,8E;AACA,0B;AACA,oC;;AAEA,qC;AACA,8C;AACA,4C;AACA,mC;AACA,Y;AACA,oB;AACA,K;AACA,G;;AAEA,qB;AACA,yB;AACA,gC;AACA,kC;AACA,2D;AACA,gD;AACA,yE;AACA,gF;AACA,+C;AACA,O;AACA,K;AACA,0B;AACA,G;;AAEA,c;AACA,E;;AAEA,6C;AACA,gC;AACA,a;AACA,G;;AAEA,sB;AACA,oB;AACA,yB;;AAEA,+E;AACA,mF;AACA,O;AACA,I;AACA,E;;AAEA,0D;AACA,6D;AACA,4D;AACA,uD;AACA,wD;AACA,2D;AACA,mC;AACA,E;AACA,yD;AACA,kD;AACA,E;AACA,sD;AACA,0D;AACA,0C;AACA,yD;AACA,+B;AACA,qD;AACA,a;AACA,wB;;AAEA,8B;AACA,4D;AACA,G;;AAEA,yB;AACA,mE;AACA,0C;AACA,8B;AACA,uE;;AAEA,yE;;AAEA,wB;AACA,sE;AACA,kE;AACA,0D;AACA,gE;AACA,gC;AACA,kD;AACA,kE;AACA,yB;AACA,U;AACA,wB;AACA,sD;AACA,iC;AACA,qD;AACA,qD;AACA,O;AACA,yD;AACA,qD;AACA,O;AACA,iB;AACA,oB;AACA,yB;AACA,oC;AACA,iC;AACA,2D;AACA,S;AACA,iB;AACA,O;AACA,sC;AACA,M;AACA,G;AACA,c;AACA,E;;AAEA,kC;AACA,6C;AACA,yD;AACA,uE;AACA,uB;AACA,e;AACA,G;AACA,sC;AACA,iD;AACA,6C;AACA,G;;AAEA,gB;AACA,gB;AACA,uB;AACA,yD;AACA,+B;AACA,E;;;AAGA,uD;AACA,4C;AACA,E;;;;;;;;;;;;;;;;;;;ACnKA,mD;AACA,E;AACA,qE;AACA,0C;AACA,E;AACA,sE;AACA,2B;;AAEA,G;AACA,S;AACA,0G;AACA,gB;AACA,uH;AACA,+L;AACA,G;AACA,sD;AACA,yC;AACA,2B;AACA,wD;;AAEA,uC;AACA,kC;AACA,8B;AACA,kB;AACA,G;AACA,mC;AACA,8D;AACA,2C;AACA,yD;;AAEA,2B;AACA,uC;;AAEA,iC;AACA,wB;;AAEA,qB;AACA,gB;AACA,iB;AACA,iB;AACA,I;AACA,E;AACA,8B;;AAEA,+B;AACA,2C;AACA,wB;AACA,E;AACA,mD;AACA,0B;AACA,E;AACA,2C;AACA,4B;AACA,E;;AAEA,G;AACA,oF;AACA,gB;AACA,wC;AACA,G;AACA,iC;AACA,uC;AACA,E;;AAEA,G;AACA,mB;AACA,Y;AACA,qB;AACA,0F;AACA,mE;AACA,gB;AACA,G;AACA,8C;AACA,mC;AACA,E;;AAEA,G;AACA,oB;AACA,Y;AACA,qB;AACA,wG;AACA,mE;AACA,gB;AACA,G;AACA,+C;AACA,oC;AACA,E;;AAEA,G;AACA,qB;AACA,Y;AACA,qB;AACA,qH;AACA,mE;AACA,gB;AACA,G;AACA,gD;AACA,qC;AACA,E;;AAEA,qD;AACA,kB;AACA,mD;AACA,sE;AACA,mE;AACA,6B;AACA,uD;AACA,mB;AACA,E;;AAEA,oD;AACA,qC;AACA,qC;AACA,iB;AACA,yD;AACA,oC;AACA,O;AACA,O;AACA,E;;AAEA,qE;AACA,kB;AACA,4D;AACA,uB;;AAEA,+B;AACA,sE;AACA,4B;AACA,6D;;AAEA,4D;AACA,sC;AACA,iC;AACA,e;;AAEA,yE;AACA,oE;AACA,yE;AACA,mE;AACA,kE;AACA,sE;AACA,mD;AACA,0D;AACA,O;;AAEA,6C;AACA,0C;AACA,S;AACA,O;AACA,G;;AAEA,4D;AACA,uC;AACA,4E;AACA,c;AACA,sC;;AAEA,O;AACA,gB;AACA,uC;AACA,kB;AACA,sE;AACA,oB;AACA,O;AACA,oC;;AAEA,8C;AACA,kD;AACA,gD;AACA,Y;AACA,uE;AACA,4B;AACA,2B;AACA,K;;AAEA,gB;AACA,I;;AAEA,K;AACA,mB;AACA,c;AACA,uB;AACA,2E;AACA,kB;AACA,uB;AACA,K;AACA,qE;AACA,yE;AACA,iD;AACA,uD;AACA,kC;AACA,6D;AACA,K;;AAEA,K;AACA,oB;AACA,c;AACA,uB;AACA,4E;AACA,kB;AACA,uB;AACA,K;AACA,yD;AACA,gC;AACA,8D;AACA,K;;AAEA,K;AACA,qB;AACA,c;AACA,uB;AACA,6E;AACA,kB;AACA,uB;AACA,K;AACA,2D;AACA,oC;AACA,+D;AACA,K;;AAEA,c;AACA,E;;AAEA,G;AACA,S;AACA,4C;AACA,2B;AACA,yB;AACA,G;AACA,0C;AACA,iD;AACA,2B;AACA,4C;;AAEA,qC;AACA,qC;;AAEA,gC;;AAEA,K;AACA,e;AACA,qC;AACA,c;AACA,iF;AACA,kB;AACA,uB;AACA,K;AACA,mB;AACA,mB;;AAEA,K;AACA,oB;AACA,qC;AACA,c;AACA,qE;AACA,kB;AACA,oB;AACA,K;AACA,wB;;AAEA,K;AACA,mB;AACA,qC;AACA,c;AACA,oE;AACA,kB;AACA,oB;AACA,K;AACA,uB;;AAEA,6D;AACA,yD;AACA,+E;AACA,8E;AACA,uC;AACA,mD;AACA,6B;;AAEA,iC;AACA,E;;AAEA,G;AACA,gH;AACA,gB;AACA,uF;AACA,uB;AACA,G;AACA,0D;AACA,uB;AACA,uB;AACA,oE;AACA,oC;AACA,E;;AAEA,G;AACA,4E;AACA,gB;AACA,uF;AACA,0B;AACA,G;AACA,gE;AACA,sD;AACA,E;;AAEA,G;AACA,2E;AACA,gB;AACA,uF;AACA,wB;AACA,G;AACA,6D;AACA,gC;AACA,2B;AACA,E;;AAEA,G;AACA,4G;AACA,gB;AACA,yG;AACA,G;AACA,yD;AACA,8B;AACA,E;;AAEA,G;AACA,8E;AACA,kC;AACA,yE;AACA,2E;AACA,8E;AACA,sB;AACA,gB;AACA,0E;AACA,6B;AACA,8E;AACA,a;AACA,0E;AACA,2E;AACA,wC;AACA,G;AACA,yE;AACA,kB;;AAEA,6C;AACA,kC;;AAEA,0C;AACA,qB;AACA,oB;AACA,iC;AACA,kC;AACA,qC;AACA,2B;AACA,sE;AACA,+C;AACA,qE;AACA,wB;AACA,6B;AACA,K;AACA,G;;AAEA,gB;AACA,oC;AACA,uC;AACA,0E;AACA,6D;AACA,gD;;AAEA,+E;AACA,gF;AACA,c;AACA,+B;AACA,sC;AACA,K;;AAEA,qB;AACA,wB;AACA,K;AACA,I;AACA,uB;;AAEA,wD;;AAEA,sD;AACA,qD;;AAEA,8E;AACA,4E;AACA,a;AACA,6B;AACA,sC;AACA,K;AACA,G;;AAEA,mB;AACA,E;;AAEA,G;AACA,+E;AACA,uE;AACA,4E;AACA,S;AACA,G;AACA,mE;AACA,iC;;AAEA,2E;AACA,0B;AACA,K;;AAEA,4B;AACA,E;;AAEA,G;AACA,gE;AACA,gB;AACA,kE;AACA,G;AACA,8C;AACA,qB;AACA,mC;AACA,E;;AAEA,gE;AACA,6D;AACA,+E;AACA,uE;AACA,a;AACA,6C;;AAEA,4E;AACA,iC;AACA,uD;AACA,kE;AACA,O;AACA,iE;AACA,kB;AACA,a;AACA,gE;AACA,G;AACA,E;;AAEA,G;AACA,qD;AACA,gB;AACA,6E;AACA,G;AACA,iD;AACA,sB;AACA,qB;AACA,2B;AACA,qC;AACA,wC;AACA,oD;AACA,sD;AACA,yB;AACA,oB;AACA,yD;AACA,mE;AACA,8C;;AAEA,iF;AACA,qC;AACA,W;AACA,Q;AACA,uB;AACA,G;;AAEA,uC;AACA,E;;AAEA,G;AACA,Y;AACA,iB;AACA,qB;AACA,mK;AACA,gB;AACA,oC;AACA,G;AACA,iC;AACA,8C;AACA,+C;AACA,E;;AAEA,qE;AACA,uC;;AAEA,G;AACA,W;AACA,E;AACA,4E;AACA,oC;AACA,+E;AACA,kC;AACA,+E;AACA,Y;AACA,E;AACA,mD;AACA,gB;AACA,Y;AACA,G;AACA,qC;;AAEA,G;AACA,+E;AACA,gB;AACA,Y;AACA,4G;AACA,G;AACA,wC;;AAEA,G;AACA,gG;AACA,gB;AACA,Y;AACA,yE;AACA,yD;AACA,G;AACA,+C;;;;;;;;;;;;;;;;;;;ACtgBA,W;;AAEA,gC;AACA,+C;;AAEA,gB;AACA,iD;;AAEA,mC;;AAEA,qE;AACA,iC;AACA,0C;AACA,uB;AACA,E;AACA,uD;AACA,gC;AACA,E","file":"/packages/blaze.js","sourcesContent":["/**\n * @namespace Blaze\n * @summary The namespace for all Blaze-related methods and classes.\n */\nBlaze = {};\n\n// Utility to HTML-escape a string. Included for legacy reasons.\nBlaze._escape = (function() {\n var escape_map = {\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n \"`\": \"`\", /* IE allows backtick-delimited attributes?? */\n \"&\": \"&\"\n };\n var escape_one = function(c) {\n return escape_map[c];\n };\n\n return function (x) {\n return x.replace(/[&<>\"'`]/g, escape_one);\n };\n})();\n\nBlaze._warn = function (msg) {\n msg = 'Warning: ' + msg;\n\n if ((typeof Log !== 'undefined') && Log && Log.warn)\n Log.warn(msg); // use Meteor's \"logging\" package\n else if ((typeof console !== 'undefined') && console.log)\n console.log(msg);\n};\n","var debugFunc;\n\n// We call into user code in many places, and it's nice to catch exceptions\n// propagated from user code immediately so that the whole system doesn't just\n// break. Catching exceptions is easy; reporting them is hard. This helper\n// reports exceptions.\n//\n// Usage:\n//\n// ```\n// try {\n// // ... someStuff ...\n// } catch (e) {\n// reportUIException(e);\n// }\n// ```\n//\n// An optional second argument overrides the default message.\n\n// Set this to `true` to cause `reportException` to throw\n// the next exception rather than reporting it. This is\n// useful in unit tests that test error messages.\nBlaze._throwNextException = false;\n\nBlaze._reportException = function (e, msg) {\n if (Blaze._throwNextException) {\n Blaze._throwNextException = false;\n throw e;\n }\n\n if (! debugFunc)\n // adapted from Tracker\n debugFunc = function () {\n return (typeof Meteor !== \"undefined\" ? Meteor._debug :\n ((typeof console !== \"undefined\") && console.log ? console.log :\n function () {}));\n };\n\n // In Chrome, `e.stack` is a multiline string that starts with the message\n // and contains a stack trace. Furthermore, `console.log` makes it clickable.\n // `console.log` supplies the space between the two arguments.\n debugFunc()(msg || 'Exception caught in template:', e.stack || e.message);\n};\n\nBlaze._wrapCatchingExceptions = function (f, where) {\n if (typeof f !== 'function')\n return f;\n\n return function () {\n try {\n return f.apply(this, arguments);\n } catch (e) {\n Blaze._reportException(e, 'Exception in ' + where + ':');\n }\n };\n};\n","/// [new] Blaze.View([name], renderMethod)\n///\n/// Blaze.View is the building block of reactive DOM. Views have\n/// the following features:\n///\n/// * lifecycle callbacks - Views are created, rendered, and destroyed,\n/// and callbacks can be registered to fire when these things happen.\n///\n/// * parent pointer - A View points to its parentView, which is the\n/// View that caused it to be rendered. These pointers form a\n/// hierarchy or tree of Views.\n///\n/// * render() method - A View's render() method specifies the DOM\n/// (or HTML) content of the View. If the method establishes\n/// reactive dependencies, it may be re-run.\n///\n/// * a DOMRange - If a View is rendered to DOM, its position and\n/// extent in the DOM are tracked using a DOMRange object.\n///\n/// When a View is constructed by calling Blaze.View, the View is\n/// not yet considered \"created.\" It doesn't have a parentView yet,\n/// and no logic has been run to initialize the View. All real\n/// work is deferred until at least creation time, when the onViewCreated\n/// callbacks are fired, which happens when the View is \"used\" in\n/// some way that requires it to be rendered.\n///\n/// ...more lifecycle stuff\n///\n/// `name` is an optional string tag identifying the View. The only\n/// time it's used is when looking in the View tree for a View of a\n/// particular name; for example, data contexts are stored on Views\n/// of name \"with\". Names are also useful when debugging, so in\n/// general it's good for functions that create Views to set the name.\n/// Views associated with templates have names of the form \"Template.foo\".\n\n/**\n * @class\n * @summary Constructor for a View, which represents a reactive region of DOM.\n * @locus Client\n * @param {String} [name] Optional. A name for this type of View. See [`view.name`](#view_name).\n * @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). In this function, `this` is bound to the View.\n */\nBlaze.View = function (name, render) {\n if (! (this instanceof Blaze.View))\n // called without `new`\n return new Blaze.View(name, render);\n\n if (typeof name === 'function') {\n // omitted \"name\" argument\n render = name;\n name = '';\n }\n this.name = name;\n this._render = render;\n\n this._callbacks = {\n created: null,\n rendered: null,\n destroyed: null\n };\n\n // Setting all properties here is good for readability,\n // and also may help Chrome optimize the code by keeping\n // the View object from changing shape too much.\n this.isCreated = false;\n this._isCreatedForExpansion = false;\n this.isRendered = false;\n this._isAttached = false;\n this.isDestroyed = false;\n this._isInRender = false;\n this.parentView = null;\n this._domrange = null;\n // This flag is normally set to false except for the cases when view's parent\n // was generated as part of expanding some syntactic sugar expressions or\n // methods.\n // Ex.: Blaze.renderWithData is an equivalent to creating a view with regular\n // Blaze.render and wrapping it into {{#with data}}{{/with}} view. Since the\n // users don't know anything about these generated parent views, Blaze needs\n // this information to be available on views to make smarter decisions. For\n // example: removing the generated parent view with the view on Blaze.remove.\n this._hasGeneratedParent = false;\n\n this.renderCount = 0;\n};\n\nBlaze.View.prototype._render = function () { return null; };\n\nBlaze.View.prototype.onViewCreated = function (cb) {\n this._callbacks.created = this._callbacks.created || [];\n this._callbacks.created.push(cb);\n};\n\nBlaze.View.prototype._onViewRendered = function (cb) {\n this._callbacks.rendered = this._callbacks.rendered || [];\n this._callbacks.rendered.push(cb);\n};\n\nBlaze.View.prototype.onViewReady = function (cb) {\n var self = this;\n var fire = function () {\n Tracker.afterFlush(function () {\n if (! self.isDestroyed) {\n Blaze._withCurrentView(self, function () {\n cb.call(self);\n });\n }\n });\n };\n self._onViewRendered(function onViewRendered() {\n if (self.isDestroyed)\n return;\n if (! self._domrange.attached)\n self._domrange.onAttached(fire);\n else\n fire();\n });\n};\n\nBlaze.View.prototype.onViewDestroyed = function (cb) {\n this._callbacks.destroyed = this._callbacks.destroyed || [];\n this._callbacks.destroyed.push(cb);\n};\n\n/// View#autorun(func)\n///\n/// Sets up a Tracker autorun that is \"scoped\" to this View in two\n/// important ways: 1) Blaze.currentView is automatically set\n/// on every re-run, and 2) the autorun is stopped when the\n/// View is destroyed. As with Tracker.autorun, the first run of\n/// the function is immediate, and a Computation object that can\n/// be used to stop the autorun is returned.\n///\n/// View#autorun is meant to be called from View callbacks like\n/// onViewCreated, or from outside the rendering process. It may not\n/// be called before the onViewCreated callbacks are fired (too early),\n/// or from a render() method (too confusing).\n///\n/// Typically, autoruns that update the state\n/// of the View (as in Blaze.With) should be started from an onViewCreated\n/// callback. Autoruns that update the DOM should be started\n/// from either onViewCreated (guarded against the absence of\n/// view._domrange), or onViewReady.\nBlaze.View.prototype.autorun = function (f, _inViewScope, displayName) {\n var self = this;\n\n // The restrictions on when View#autorun can be called are in order\n // to avoid bad patterns, like creating a Blaze.View and immediately\n // calling autorun on it. A freshly created View is not ready to\n // have logic run on it; it doesn't have a parentView, for example.\n // It's when the View is materialized or expanded that the onViewCreated\n // handlers are fired and the View starts up.\n //\n // Letting the render() method call `this.autorun()` is problematic\n // because of re-render. The best we can do is to stop the old\n // autorun and start a new one for each render, but that's a pattern\n // we try to avoid internally because it leads to helpers being\n // called extra times, in the case where the autorun causes the\n // view to re-render (and thus the autorun to be torn down and a\n // new one established).\n //\n // We could lift these restrictions in various ways. One interesting\n // idea is to allow you to call `view.autorun` after instantiating\n // `view`, and automatically wrap it in `view.onViewCreated`, deferring\n // the autorun so that it starts at an appropriate time. However,\n // then we can't return the Computation object to the caller, because\n // it doesn't exist yet.\n if (! self.isCreated) {\n throw new Error(\"View#autorun must be called from the created callback at the earliest\");\n }\n if (this._isInRender) {\n throw new Error(\"Can't call View#autorun from inside render(); try calling it from the created or rendered callback\");\n }\n if (Tracker.active) {\n throw new Error(\"Can't call View#autorun from a Tracker Computation; try calling it from the created or rendered callback\");\n }\n\n // Each local variable allocate additional space on each frame of the\n // execution stack. When too many variables are allocated on stack, you can\n // run out of memory on stack running a deep recursion (which is typical for\n // Blaze functions) and get stackoverlow error. (The size of the stack varies\n // between browsers).\n // The trick we use here is to allocate only one variable on stack `locals`\n // that keeps references to all the rest. Since locals is allocated on heap,\n // we don't take up any space on the stack.\n var locals = {};\n locals.templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc;\n\n locals.f = function viewAutorun(c) {\n return Blaze._withCurrentView(_inViewScope || self, function () {\n return Blaze.Template._withTemplateInstanceFunc(locals.templateInstanceFunc, function () {\n return f.call(self, c);\n });\n });\n };\n\n // Give the autorun function a better name for debugging and profiling.\n // The `displayName` property is not part of the spec but browsers like Chrome\n // and Firefox prefer it in debuggers over the name function was declared by.\n locals.f.displayName =\n (self.name || 'anonymous') + ':' + (displayName || 'anonymous');\n locals.c = Tracker.autorun(locals.f);\n\n self.onViewDestroyed(function () { locals.c.stop(); });\n\n return locals.c;\n};\n\nBlaze.View.prototype._errorIfShouldntCallSubscribe = function () {\n var self = this;\n\n if (! self.isCreated) {\n throw new Error(\"View#subscribe must be called from the created callback at the earliest\");\n }\n if (self._isInRender) {\n throw new Error(\"Can't call View#subscribe from inside render(); try calling it from the created or rendered callback\");\n }\n if (self.isDestroyed) {\n throw new Error(\"Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered.\");\n }\n};\n\n/**\n * Just like Blaze.View#autorun, but with Meteor.subscribe instead of\n * Tracker.autorun. Stop the subscription when the view is destroyed.\n * @return {SubscriptionHandle} A handle to the subscription so that you can\n * see if it is ready, or stop it manually\n */\nBlaze.View.prototype.subscribe = function (args, options) {\n var self = this;\n options = {} || options;\n\n self._errorIfShouldntCallSubscribe();\n\n var subHandle;\n if (options.connection) {\n subHandle = options.connection.subscribe.apply(options.connection, args);\n } else {\n subHandle = Meteor.subscribe.apply(Meteor, args);\n }\n\n self.onViewDestroyed(function () {\n subHandle.stop();\n });\n\n return subHandle;\n};\n\nBlaze.View.prototype.firstNode = function () {\n if (! this._isAttached)\n throw new Error(\"View must be attached before accessing its DOM\");\n\n return this._domrange.firstNode();\n};\n\nBlaze.View.prototype.lastNode = function () {\n if (! this._isAttached)\n throw new Error(\"View must be attached before accessing its DOM\");\n\n return this._domrange.lastNode();\n};\n\nBlaze._fireCallbacks = function (view, which) {\n Blaze._withCurrentView(view, function () {\n Tracker.nonreactive(function fireCallbacks() {\n var cbs = view._callbacks[which];\n for (var i = 0, N = (cbs && cbs.length); i < N; i++)\n cbs[i].call(view);\n });\n });\n};\n\nBlaze._createView = function (view, parentView, forExpansion) {\n if (view.isCreated)\n throw new Error(\"Can't render the same View twice\");\n\n view.parentView = (parentView || null);\n view.isCreated = true;\n if (forExpansion)\n view._isCreatedForExpansion = true;\n\n Blaze._fireCallbacks(view, 'created');\n};\n\nvar doFirstRender = function (view, initialContent) {\n var domrange = new Blaze._DOMRange(initialContent);\n view._domrange = domrange;\n domrange.view = view;\n view.isRendered = true;\n Blaze._fireCallbacks(view, 'rendered');\n\n var teardownHook = null;\n\n domrange.onAttached(function attached(range, element) {\n view._isAttached = true;\n\n teardownHook = Blaze._DOMBackend.Teardown.onElementTeardown(\n element, function teardown() {\n Blaze._destroyView(view, true /* _skipNodes */);\n });\n });\n\n // tear down the teardown hook\n view.onViewDestroyed(function () {\n teardownHook && teardownHook.stop();\n teardownHook = null;\n });\n\n return domrange;\n};\n\n// Take an uncreated View `view` and create and render it to DOM,\n// setting up the autorun that updates the View. Returns a new\n// DOMRange, which has been associated with the View.\n//\n// The private arguments `_workStack` and `_intoArray` are passed in\n// by Blaze._materializeDOM. If provided, then we avoid the mutual\n// recursion of calling back into Blaze._materializeDOM so that deep\n// View hierarchies don't blow the stack. Instead, we push tasks onto\n// workStack for the initial rendering and subsequent setup of the\n// View, and they are done after we return. When there is a\n// _workStack, we do not return the new DOMRange, but instead push it\n// into _intoArray from a _workStack task.\nBlaze._materializeView = function (view, parentView, _workStack, _intoArray) {\n Blaze._createView(view, parentView);\n\n var domrange;\n var lastHtmljs;\n // We don't expect to be called in a Computation, but just in case,\n // wrap in Tracker.nonreactive.\n Tracker.nonreactive(function () {\n view.autorun(function doRender(c) {\n // `view.autorun` sets the current view.\n view.renderCount++;\n view._isInRender = true;\n // Any dependencies that should invalidate this Computation come\n // from this line:\n var htmljs = view._render();\n view._isInRender = false;\n\n if (! c.firstRun) {\n Tracker.nonreactive(function doMaterialize() {\n // re-render\n var rangesAndNodes = Blaze._materializeDOM(htmljs, [], view);\n if (! Blaze._isContentEqual(lastHtmljs, htmljs)) {\n domrange.setMembers(rangesAndNodes);\n Blaze._fireCallbacks(view, 'rendered');\n }\n });\n }\n lastHtmljs = htmljs;\n\n // Causes any nested views to stop immediately, not when we call\n // `setMembers` the next time around the autorun. Otherwise,\n // helpers in the DOM tree to be replaced might be scheduled\n // to re-run before we have a chance to stop them.\n Tracker.onInvalidate(function () {\n if (domrange) {\n domrange.destroyMembers();\n }\n });\n }, undefined, 'materialize');\n\n // first render. lastHtmljs is the first htmljs.\n var initialContents;\n if (! _workStack) {\n initialContents = Blaze._materializeDOM(lastHtmljs, [], view);\n domrange = doFirstRender(view, initialContents);\n initialContents = null; // help GC because we close over this scope a lot\n } else {\n // We're being called from Blaze._materializeDOM, so to avoid\n // recursion and save stack space, provide a description of the\n // work to be done instead of doing it. Tasks pushed onto\n // _workStack will be done in LIFO order after we return.\n // The work will still be done within a Tracker.nonreactive,\n // because it will be done by some call to Blaze._materializeDOM\n // (which is always called in a Tracker.nonreactive).\n initialContents = [];\n // push this function first so that it happens last\n _workStack.push(function () {\n domrange = doFirstRender(view, initialContents);\n initialContents = null; // help GC because of all the closures here\n _intoArray.push(domrange);\n });\n // now push the task that calculates initialContents\n _workStack.push([lastHtmljs, initialContents, view]);\n }\n });\n\n if (! _workStack) {\n return domrange;\n } else {\n return null;\n }\n};\n\n// Expands a View to HTMLjs, calling `render` recursively on all\n// Views and evaluating any dynamic attributes. Calls the `created`\n// callback, but not the `materialized` or `rendered` callbacks.\n// Destroys the view immediately, unless called in a Tracker Computation,\n// in which case the view will be destroyed when the Computation is\n// invalidated. If called in a Tracker Computation, the result is a\n// reactive string; that is, the Computation will be invalidated\n// if any changes are made to the view or subviews that might affect\n// the HTML.\nBlaze._expandView = function (view, parentView) {\n Blaze._createView(view, parentView, true /*forExpansion*/);\n\n view._isInRender = true;\n var htmljs = Blaze._withCurrentView(view, function () {\n return view._render();\n });\n view._isInRender = false;\n\n var result = Blaze._expand(htmljs, view);\n\n if (Tracker.active) {\n Tracker.onInvalidate(function () {\n Blaze._destroyView(view);\n });\n } else {\n Blaze._destroyView(view);\n }\n\n return result;\n};\n\n// Options: `parentView`\nBlaze._HTMLJSExpander = HTML.TransformingVisitor.extend();\nBlaze._HTMLJSExpander.def({\n visitObject: function (x) {\n if (x instanceof Blaze.Template)\n x = x.constructView();\n if (x instanceof Blaze.View)\n return Blaze._expandView(x, this.parentView);\n\n // this will throw an error; other objects are not allowed!\n return HTML.TransformingVisitor.prototype.visitObject.call(this, x);\n },\n visitAttributes: function (attrs) {\n // expand dynamic attributes\n if (typeof attrs === 'function')\n attrs = Blaze._withCurrentView(this.parentView, attrs);\n\n // call super (e.g. for case where `attrs` is an array)\n return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs);\n },\n visitAttribute: function (name, value, tag) {\n // expand attribute values that are functions. Any attribute value\n // that contains Views must be wrapped in a function.\n if (typeof value === 'function')\n value = Blaze._withCurrentView(this.parentView, value);\n\n return HTML.TransformingVisitor.prototype.visitAttribute.call(\n this, name, value, tag);\n }\n});\n\n// Return Blaze.currentView, but only if it is being rendered\n// (i.e. we are in its render() method).\nvar currentViewIfRendering = function () {\n var view = Blaze.currentView;\n return (view && view._isInRender) ? view : null;\n};\n\nBlaze._expand = function (htmljs, parentView) {\n parentView = parentView || currentViewIfRendering();\n return (new Blaze._HTMLJSExpander(\n {parentView: parentView})).visit(htmljs);\n};\n\nBlaze._expandAttributes = function (attrs, parentView) {\n parentView = parentView || currentViewIfRendering();\n return (new Blaze._HTMLJSExpander(\n {parentView: parentView})).visitAttributes(attrs);\n};\n\nBlaze._destroyView = function (view, _skipNodes) {\n if (view.isDestroyed)\n return;\n view.isDestroyed = true;\n\n Blaze._fireCallbacks(view, 'destroyed');\n\n // Destroy views and elements recursively. If _skipNodes,\n // only recurse up to views, not elements, for the case where\n // the backend (jQuery) is recursing over the elements already.\n\n if (view._domrange)\n view._domrange.destroyMembers(_skipNodes);\n};\n\nBlaze._destroyNode = function (node) {\n if (node.nodeType === 1)\n Blaze._DOMBackend.Teardown.tearDownElement(node);\n};\n\n// Are the HTMLjs entities `a` and `b` the same? We could be\n// more elaborate here but the point is to catch the most basic\n// cases.\nBlaze._isContentEqual = function (a, b) {\n if (a instanceof HTML.Raw) {\n return (b instanceof HTML.Raw) && (a.value === b.value);\n } else if (a == null) {\n return (b == null);\n } else {\n return (a === b) &&\n ((typeof a === 'number') || (typeof a === 'boolean') ||\n (typeof a === 'string'));\n }\n};\n\n/**\n * @summary The View corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.\n * @locus Client\n * @type {Blaze.View}\n */\nBlaze.currentView = null;\n\nBlaze._withCurrentView = function (view, func) {\n var oldView = Blaze.currentView;\n try {\n Blaze.currentView = view;\n return func();\n } finally {\n Blaze.currentView = oldView;\n }\n};\n\n// Blaze.render publicly takes a View or a Template.\n// Privately, it takes any HTMLJS (extended with Views and Templates)\n// except null or undefined, or a function that returns any extended\n// HTMLJS.\nvar checkRenderContent = function (content) {\n if (content === null)\n throw new Error(\"Can't render null\");\n if (typeof content === 'undefined')\n throw new Error(\"Can't render undefined\");\n\n if ((content instanceof Blaze.View) ||\n (content instanceof Blaze.Template) ||\n (typeof content === 'function'))\n return;\n\n try {\n // Throw if content doesn't look like HTMLJS at the top level\n // (i.e. verify that this is an HTML.Tag, or an array,\n // or a primitive, etc.)\n (new HTML.Visitor).visit(content);\n } catch (e) {\n // Make error message suitable for public API\n throw new Error(\"Expected Template or View\");\n }\n};\n\n// For Blaze.render and Blaze.toHTML, take content and\n// wrap it in a View, unless it's a single View or\n// Template already.\nvar contentAsView = function (content) {\n checkRenderContent(content);\n\n if (content instanceof Blaze.Template) {\n return content.constructView();\n } else if (content instanceof Blaze.View) {\n return content;\n } else {\n var func = content;\n if (typeof func !== 'function') {\n func = function () {\n return content;\n };\n }\n return Blaze.View('render', func);\n }\n};\n\n// For Blaze.renderWithData and Blaze.toHTMLWithData, wrap content\n// in a function, if necessary, so it can be a content arg to\n// a Blaze.With.\nvar contentAsFunc = function (content) {\n checkRenderContent(content);\n\n if (typeof content !== 'function') {\n return function () {\n return content;\n };\n } else {\n return content;\n }\n};\n\n/**\n * @summary Renders a template or View to DOM nodes and inserts it into the DOM, returning a rendered [View](#blaze_view) which can be passed to [`Blaze.remove`](#blaze_remove).\n * @locus Client\n * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render. If a template, a View object is [constructed](#template_constructview). If a View, it must be an unrendered View, which becomes a rendered View and is returned.\n * @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node.\n * @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.\n * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).\n */\nBlaze.render = function (content, parentElement, nextNode, parentView) {\n if (! parentElement) {\n Blaze._warn(\"Blaze.render without a parent element is deprecated. \" +\n \"You must specify where to insert the rendered content.\");\n }\n\n if (nextNode instanceof Blaze.View) {\n // handle omitted nextNode\n parentView = nextNode;\n nextNode = null;\n }\n\n // parentElement must be a DOM node. in particular, can't be the\n // result of a call to `$`. Can't check if `parentElement instanceof\n // Node` since 'Node' is undefined in IE8.\n if (parentElement && typeof parentElement.nodeType !== 'number')\n throw new Error(\"'parentElement' must be a DOM node\");\n if (nextNode && typeof nextNode.nodeType !== 'number') // 'nextNode' is optional\n throw new Error(\"'nextNode' must be a DOM node\");\n\n parentView = parentView || currentViewIfRendering();\n\n var view = contentAsView(content);\n Blaze._materializeView(view, parentView);\n\n if (parentElement) {\n view._domrange.attach(parentElement, nextNode);\n }\n\n return view;\n};\n\nBlaze.insert = function (view, parentElement, nextNode) {\n Blaze._warn(\"Blaze.insert has been deprecated. Specify where to insert the \" +\n \"rendered content in the call to Blaze.render.\");\n\n if (! (view && (view._domrange instanceof Blaze._DOMRange)))\n throw new Error(\"Expected template rendered with Blaze.render\");\n\n view._domrange.attach(parentElement, nextNode);\n};\n\n/**\n * @summary Renders a template or View to DOM nodes with a data context. Otherwise identical to `Blaze.render`.\n * @locus Client\n * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render.\n * @param {Object|Function} data The data context to use, or a function returning a data context. If a function is provided, it will be reactively re-run.\n * @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node.\n * @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.\n * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).\n */\nBlaze.renderWithData = function (content, data, parentElement, nextNode, parentView) {\n // We defer the handling of optional arguments to Blaze.render. At this point,\n // `nextNode` may actually be `parentView`.\n return Blaze.render(Blaze._TemplateWith(data, contentAsFunc(content)),\n parentElement, nextNode, parentView);\n};\n\n/**\n * @summary Removes a rendered View from the DOM, stopping all reactive updates and event listeners on it.\n * @locus Client\n * @param {Blaze.View} renderedView The return value from `Blaze.render` or `Blaze.renderWithData`.\n */\nBlaze.remove = function (view) {\n if (! (view && (view._domrange instanceof Blaze._DOMRange)))\n throw new Error(\"Expected template rendered with Blaze.render\");\n\n while (view) {\n if (! view.isDestroyed) {\n var range = view._domrange;\n if (range.attached && ! range.parentRange)\n range.detach();\n range.destroy();\n }\n\n view = view._hasGeneratedParent && view.parentView;\n }\n};\n\n/**\n * @summary Renders a template or View to a string of HTML.\n * @locus Client\n * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.\n */\nBlaze.toHTML = function (content, parentView) {\n parentView = parentView || currentViewIfRendering();\n\n return HTML.toHTML(Blaze._expandView(contentAsView(content), parentView));\n};\n\n/**\n * @summary Renders a template or View to HTML with a data context. Otherwise identical to `Blaze.toHTML`.\n * @locus Client\n * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.\n * @param {Object|Function} data The data context to use, or a function returning a data context.\n */\nBlaze.toHTMLWithData = function (content, data, parentView) {\n parentView = parentView || currentViewIfRendering();\n\n return HTML.toHTML(Blaze._expandView(Blaze._TemplateWith(\n data, contentAsFunc(content)), parentView));\n};\n\nBlaze._toText = function (htmljs, parentView, textMode) {\n if (typeof htmljs === 'function')\n throw new Error(\"Blaze._toText doesn't take a function, just HTMLjs\");\n\n if ((parentView != null) && ! (parentView instanceof Blaze.View)) {\n // omitted parentView argument\n textMode = parentView;\n parentView = null;\n }\n parentView = parentView || currentViewIfRendering();\n\n if (! textMode)\n throw new Error(\"textMode required\");\n if (! (textMode === HTML.TEXTMODE.STRING ||\n textMode === HTML.TEXTMODE.RCDATA ||\n textMode === HTML.TEXTMODE.ATTRIBUTE))\n throw new Error(\"Unknown textMode: \" + textMode);\n\n return HTML.toText(Blaze._expand(htmljs, parentView), textMode);\n};\n\n/**\n * @summary Returns the current data context, or the data context that was used when rendering a particular DOM element or View from a Meteor template.\n * @locus Client\n * @param {DOMElement|Blaze.View} [elementOrView] Optional. An element that was rendered by a Meteor, or a View.\n */\nBlaze.getData = function (elementOrView) {\n var theWith;\n\n if (! elementOrView) {\n theWith = Blaze.getView('with');\n } else if (elementOrView instanceof Blaze.View) {\n var view = elementOrView;\n theWith = (view.name === 'with' ? view :\n Blaze.getView(view, 'with'));\n } else if (typeof elementOrView.nodeType === 'number') {\n if (elementOrView.nodeType !== 1)\n throw new Error(\"Expected DOM element\");\n theWith = Blaze.getView(elementOrView, 'with');\n } else {\n throw new Error(\"Expected DOM element or View\");\n }\n\n return theWith ? theWith.dataVar.get() : null;\n};\n\n// For back-compat\nBlaze.getElementData = function (element) {\n Blaze._warn(\"Blaze.getElementData has been deprecated. Use \" +\n \"Blaze.getData(element) instead.\");\n\n if (element.nodeType !== 1)\n throw new Error(\"Expected DOM element\");\n\n return Blaze.getData(element);\n};\n\n// Both arguments are optional.\n\n/**\n * @summary Gets either the current View, or the View enclosing the given DOM element.\n * @locus Client\n * @param {DOMElement} [element] Optional. If specified, the View enclosing `element` is returned.\n */\nBlaze.getView = function (elementOrView, _viewName) {\n var viewName = _viewName;\n\n if ((typeof elementOrView) === 'string') {\n // omitted elementOrView; viewName present\n viewName = elementOrView;\n elementOrView = null;\n }\n\n // We could eventually shorten the code by folding the logic\n // from the other methods into this method.\n if (! elementOrView) {\n return Blaze._getCurrentView(viewName);\n } else if (elementOrView instanceof Blaze.View) {\n return Blaze._getParentView(elementOrView, viewName);\n } else if (typeof elementOrView.nodeType === 'number') {\n return Blaze._getElementView(elementOrView, viewName);\n } else {\n throw new Error(\"Expected DOM element or View\");\n }\n};\n\n// Gets the current view or its nearest ancestor of name\n// `name`.\nBlaze._getCurrentView = function (name) {\n var view = Blaze.currentView;\n // Better to fail in cases where it doesn't make sense\n // to use Blaze._getCurrentView(). There will be a current\n // view anywhere it does. You can check Blaze.currentView\n // if you want to know whether there is one or not.\n if (! view)\n throw new Error(\"There is no current view\");\n\n if (name) {\n while (view && view.name !== name)\n view = view.parentView;\n return view || null;\n } else {\n // Blaze._getCurrentView() with no arguments just returns\n // Blaze.currentView.\n return view;\n }\n};\n\nBlaze._getParentView = function (view, name) {\n var v = view.parentView;\n\n if (name) {\n while (v && v.name !== name)\n v = v.parentView;\n }\n\n return v || null;\n};\n\nBlaze._getElementView = function (elem, name) {\n var range = Blaze._DOMRange.forElement(elem);\n var view = null;\n while (range && ! view) {\n view = (range.view || null);\n if (! view) {\n if (range.parentRange)\n range = range.parentRange;\n else\n range = Blaze._DOMRange.forElement(range.parentElement);\n }\n }\n\n if (name) {\n while (view && view.name !== name)\n view = view.parentView;\n return view || null;\n } else {\n return view;\n }\n};\n\nBlaze._addEventMap = function (view, eventMap, thisInHandler) {\n thisInHandler = (thisInHandler || null);\n var handles = [];\n\n if (! view._domrange)\n throw new Error(\"View must have a DOMRange\");\n\n view._domrange.onAttached(function attached_eventMaps(range, element) {\n _.each(eventMap, function (handler, spec) {\n var clauses = spec.split(/,\\s+/);\n // iterate over clauses of spec, e.g. ['click .foo', 'click .bar']\n _.each(clauses, function (clause) {\n var parts = clause.split(/\\s+/);\n if (parts.length === 0)\n return;\n\n var newEvents = parts.shift();\n var selector = parts.join(' ');\n handles.push(Blaze._EventSupport.listen(\n element, newEvents, selector,\n function (evt) {\n if (! range.containsElement(evt.currentTarget))\n return null;\n var handlerThis = thisInHandler || this;\n var handlerArgs = arguments;\n return Blaze._withCurrentView(view, function () {\n return handler.apply(handlerThis, handlerArgs);\n });\n },\n range, function (r) {\n return r.parentRange;\n }));\n });\n });\n });\n\n view.onViewDestroyed(function () {\n _.each(handles, function (h) {\n h.stop();\n });\n handles.length = 0;\n });\n};\n","Blaze._calculateCondition = function (cond) {\n if (cond instanceof Array && cond.length === 0)\n cond = false;\n return !! cond;\n};\n\n/**\n * @summary Constructs a View that renders content with a data context.\n * @locus Client\n * @param {Object|Function} data An object to use as the data context, or a function returning such an object. If a function is provided, it will be reactively re-run.\n * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content).\n */\nBlaze.With = function (data, contentFunc) {\n var view = Blaze.View('with', contentFunc);\n\n view.dataVar = new ReactiveVar;\n\n view.onViewCreated(function () {\n if (typeof data === 'function') {\n // `data` is a reactive function\n view.autorun(function () {\n view.dataVar.set(data());\n }, view.parentView, 'setData');\n } else {\n view.dataVar.set(data);\n }\n });\n\n return view;\n};\n\n/**\n * @summary Constructs a View that renders content conditionally.\n * @locus Client\n * @param {Function} conditionFunc A function to reactively re-run. Whether the result is truthy or falsy determines whether `contentFunc` or `elseFunc` is shown. An empty array is considered falsy.\n * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content).\n * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the \"else\" case.\n */\nBlaze.If = function (conditionFunc, contentFunc, elseFunc, _not) {\n var conditionVar = new ReactiveVar;\n\n var view = Blaze.View(_not ? 'unless' : 'if', function () {\n return conditionVar.get() ? contentFunc() :\n (elseFunc ? elseFunc() : null);\n });\n view.__conditionVar = conditionVar;\n view.onViewCreated(function () {\n this.autorun(function () {\n var cond = Blaze._calculateCondition(conditionFunc());\n conditionVar.set(_not ? (! cond) : cond);\n }, this.parentView, 'condition');\n });\n\n return view;\n};\n\n/**\n * @summary An inverted [`Blaze.If`](#blaze_if).\n * @locus Client\n * @param {Function} conditionFunc A function to reactively re-run. If the result is falsy, `contentFunc` is shown, otherwise `elseFunc` is shown. An empty array is considered falsy.\n * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content).\n * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the \"else\" case.\n */\nBlaze.Unless = function (conditionFunc, contentFunc, elseFunc) {\n return Blaze.If(conditionFunc, contentFunc, elseFunc, true /*_not*/);\n};\n\n/**\n * @summary Constructs a View that renders `contentFunc` for each item in a sequence.\n * @locus Client\n * @param {Function} argFunc A function to reactively re-run. The function may return a Cursor, an array, null, or undefined.\n * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content).\n * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content) to display in the case when there are no items to display.\n */\nBlaze.Each = function (argFunc, contentFunc, elseFunc) {\n var eachView = Blaze.View('each', function () {\n var subviews = this.initialSubviews;\n this.initialSubviews = null;\n if (this._isCreatedForExpansion) {\n this.expandedValueDep = new Tracker.Dependency;\n this.expandedValueDep.depend();\n }\n return subviews;\n });\n eachView.initialSubviews = [];\n eachView.numItems = 0;\n eachView.inElseMode = false;\n eachView.stopHandle = null;\n eachView.contentFunc = contentFunc;\n eachView.elseFunc = elseFunc;\n eachView.argVar = new ReactiveVar;\n\n eachView.onViewCreated(function () {\n // We evaluate argFunc in an autorun to make sure\n // Blaze.currentView is always set when it runs (rather than\n // passing argFunc straight to ObserveSequence).\n eachView.autorun(function () {\n eachView.argVar.set(argFunc());\n }, eachView.parentView, 'collection');\n\n eachView.stopHandle = ObserveSequence.observe(function () {\n return eachView.argVar.get();\n }, {\n addedAt: function (id, item, index) {\n Tracker.nonreactive(function () {\n var newItemView = Blaze.With(item, eachView.contentFunc);\n eachView.numItems++;\n\n if (eachView.expandedValueDep) {\n eachView.expandedValueDep.changed();\n } else if (eachView._domrange) {\n if (eachView.inElseMode) {\n eachView._domrange.removeMember(0);\n eachView.inElseMode = false;\n }\n\n var range = Blaze._materializeView(newItemView, eachView);\n eachView._domrange.addMember(range, index);\n } else {\n eachView.initialSubviews.splice(index, 0, newItemView);\n }\n });\n },\n removedAt: function (id, item, index) {\n Tracker.nonreactive(function () {\n eachView.numItems--;\n if (eachView.expandedValueDep) {\n eachView.expandedValueDep.changed();\n } else if (eachView._domrange) {\n eachView._domrange.removeMember(index);\n if (eachView.elseFunc && eachView.numItems === 0) {\n eachView.inElseMode = true;\n eachView._domrange.addMember(\n Blaze._materializeView(\n Blaze.View('each_else',eachView.elseFunc),\n eachView), 0);\n }\n } else {\n eachView.initialSubviews.splice(index, 1);\n }\n });\n },\n changedAt: function (id, newItem, oldItem, index) {\n Tracker.nonreactive(function () {\n if (eachView.expandedValueDep) {\n eachView.expandedValueDep.changed();\n } else {\n var itemView;\n if (eachView._domrange) {\n itemView = eachView._domrange.getMember(index).view;\n } else {\n itemView = eachView.initialSubviews[index];\n }\n itemView.dataVar.set(newItem);\n }\n });\n },\n movedTo: function (id, item, fromIndex, toIndex) {\n Tracker.nonreactive(function () {\n if (eachView.expandedValueDep) {\n eachView.expandedValueDep.changed();\n } else if (eachView._domrange) {\n eachView._domrange.moveMember(fromIndex, toIndex);\n } else {\n var subviews = eachView.initialSubviews;\n var itemView = subviews[fromIndex];\n subviews.splice(fromIndex, 1);\n subviews.splice(toIndex, 0, itemView);\n }\n });\n }\n });\n\n if (eachView.elseFunc && eachView.numItems === 0) {\n eachView.inElseMode = true;\n eachView.initialSubviews[0] =\n Blaze.View('each_else', eachView.elseFunc);\n }\n });\n\n eachView.onViewDestroyed(function () {\n if (eachView.stopHandle)\n eachView.stopHandle.stop();\n });\n\n return eachView;\n};\n\nBlaze._TemplateWith = function (arg, contentFunc) {\n var w;\n\n var argFunc = arg;\n if (typeof arg !== 'function') {\n argFunc = function () {\n return arg;\n };\n }\n\n // This is a little messy. When we compile `{{> Template.contentBlock}}`, we\n // wrap it in Blaze._InOuterTemplateScope in order to skip the intermediate\n // parent Views in the current template. However, when there's an argument\n // (`{{> Template.contentBlock arg}}`), the argument needs to be evaluated\n // in the original scope. There's no good order to nest\n // Blaze._InOuterTemplateScope and Spacebars.TemplateWith to achieve this,\n // so we wrap argFunc to run it in the \"original parentView\" of the\n // Blaze._InOuterTemplateScope.\n //\n // To make this better, reconsider _InOuterTemplateScope as a primitive.\n // Longer term, evaluate expressions in the proper lexical scope.\n var wrappedArgFunc = function () {\n var viewToEvaluateArg = null;\n if (w.parentView && w.parentView.name === 'InOuterTemplateScope') {\n viewToEvaluateArg = w.parentView.originalParentView;\n }\n if (viewToEvaluateArg) {\n return Blaze._withCurrentView(viewToEvaluateArg, argFunc);\n } else {\n return argFunc();\n }\n };\n\n var wrappedContentFunc = function () {\n var content = contentFunc.call(this);\n\n // Since we are generating the Blaze._TemplateWith view for the\n // user, set the flag on the child view. If `content` is a template,\n // construct the View so that we can set the flag.\n if (content instanceof Blaze.Template) {\n content = content.constructView();\n }\n if (content instanceof Blaze.View) {\n content._hasGeneratedParent = true;\n }\n\n return content;\n };\n\n w = Blaze.With(wrappedArgFunc, wrappedContentFunc);\n w.__isTemplateWith = true;\n return w;\n};\n\nBlaze._InOuterTemplateScope = function (templateView, contentFunc) {\n var view = Blaze.View('InOuterTemplateScope', contentFunc);\n var parentView = templateView.parentView;\n\n // Hack so that if you call `{{> foo bar}}` and it expands into\n // `{{#with bar}}{{> foo}}{{/with}}`, and then `foo` is a template\n // that inserts `{{> Template.contentBlock}}`, the data context for\n // `Template.contentBlock` is not `bar` but the one enclosing that.\n if (parentView.__isTemplateWith)\n parentView = parentView.parentView;\n\n view.onViewCreated(function () {\n this.originalParentView = this.parentView;\n this.parentView = parentView;\n });\n return view;\n};\n\n// XXX COMPAT WITH 0.9.0\nBlaze.InOuterTemplateScope = Blaze._InOuterTemplateScope;\n","Blaze._globalHelpers = {};\n\n// Documented as Template.registerHelper.\n// This definition also provides back-compat for `UI.registerHelper`.\nBlaze.registerHelper = function (name, func) {\n Blaze._globalHelpers[name] = func;\n};\n\nvar bindIfIsFunction = function (x, target) {\n if (typeof x !== 'function')\n return x;\n return _.bind(x, target);\n};\n\n// If `x` is a function, binds the value of `this` for that function\n// to the current data context.\nvar bindDataContext = function (x) {\n if (typeof x === 'function') {\n return function () {\n var data = Blaze.getData();\n if (data == null)\n data = {};\n return x.apply(data, arguments);\n };\n }\n return x;\n};\n\nBlaze._OLDSTYLE_HELPER = {};\n\nvar getTemplateHelper = Blaze._getTemplateHelper = function (template, name) {\n // XXX COMPAT WITH 0.9.3\n var isKnownOldStyleHelper = false;\n\n if (template.__helpers.has(name)) {\n var helper = template.__helpers.get(name);\n if (helper === Blaze._OLDSTYLE_HELPER) {\n isKnownOldStyleHelper = true;\n } else {\n return helper;\n }\n }\n\n // old-style helper\n if (name in template) {\n // Only warn once per helper\n if (! isKnownOldStyleHelper) {\n template.__helpers.set(name, Blaze._OLDSTYLE_HELPER);\n if (! template._NOWARN_OLDSTYLE_HELPERS) {\n Blaze._warn('Assigning helper with `' + template.viewName + '.' +\n name + ' = ...` is deprecated. Use `' + template.viewName +\n '.helpers(...)` instead.');\n }\n }\n return template[name];\n }\n\n return null;\n};\n\nvar wrapHelper = function (f, templateFunc) {\n if (typeof f !== \"function\") {\n return f;\n }\n\n return function () {\n var self = this;\n var args = arguments;\n\n return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () {\n return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args);\n });\n };\n};\n\n// Looks up a name, like \"foo\" or \"..\", as a helper of the\n// current template; a global helper; the name of a template;\n// or a property of the data context. Called on the View of\n// a template (i.e. a View with a `.template` property,\n// where the helpers are). Used for the first name in a\n// \"path\" in a template tag, like \"foo\" in `{{foo.bar}}` or\n// \"..\" in `{{frobulate ../blah}}`.\n//\n// Returns a function, a non-function value, or null. If\n// a function is found, it is bound appropriately.\n//\n// NOTE: This function must not establish any reactive\n// dependencies itself. If there is any reactivity in the\n// value, lookup should return a function.\nBlaze.View.prototype.lookup = function (name, _options) {\n var template = this.template;\n var lookupTemplate = _options && _options.template;\n var helper;\n var boundTmplInstance;\n\n if (this.templateInstance) {\n boundTmplInstance = _.bind(this.templateInstance, this);\n }\n\n if (/^\\./.test(name)) {\n // starts with a dot. must be a series of dots which maps to an\n // ancestor of the appropriate height.\n if (!/^(\\.)+$/.test(name))\n throw new Error(\"id starting with dot must be a series of dots\");\n\n return Blaze._parentData(name.length - 1, true /*_functionWrapped*/);\n\n } else if (template &&\n ((helper = getTemplateHelper(template, name)) != null)) {\n return wrapHelper(bindDataContext(helper), boundTmplInstance);\n } else if (lookupTemplate && (name in Blaze.Template) &&\n (Blaze.Template[name] instanceof Blaze.Template)) {\n return Blaze.Template[name];\n } else if (Blaze._globalHelpers[name] != null) {\n return wrapHelper(bindDataContext(Blaze._globalHelpers[name]),\n boundTmplInstance);\n } else {\n return function () {\n var isCalledAsFunction = (arguments.length > 0);\n var data = Blaze.getData();\n if (lookupTemplate && ! (data && data[name])) {\n throw new Error(\"No such template: \" + name);\n }\n if (isCalledAsFunction && ! (data && data[name])) {\n throw new Error(\"No such function: \" + name);\n }\n if (! data)\n return null;\n var x = data[name];\n if (typeof x !== 'function') {\n if (isCalledAsFunction) {\n throw new Error(\"Can't call non-function: \" + x);\n }\n return x;\n }\n return x.apply(data, arguments);\n };\n }\n return null;\n};\n\n// Implement Spacebars' {{../..}}.\n// @param height {Number} The number of '..'s\nBlaze._parentData = function (height, _functionWrapped) {\n // If height is null or undefined, we default to 1, the first parent.\n if (height == null) {\n height = 1;\n }\n var theWith = Blaze.getView('with');\n for (var i = 0; (i < height) && theWith; i++) {\n theWith = Blaze.getView(theWith, 'with');\n }\n\n if (! theWith)\n return null;\n if (_functionWrapped)\n return function () { return theWith.dataVar.get(); };\n return theWith.dataVar.get();\n};\n\n\nBlaze.View.prototype.lookupTemplate = function (name) {\n return this.lookup(name, {template:true});\n};\n","// [new] Blaze.Template([viewName], renderFunction)\n//\n// `Blaze.Template` is the class of templates, like `Template.foo` in\n// Meteor, which is `instanceof Template`.\n//\n// `viewKind` is a string that looks like \"Template.foo\" for templates\n// defined by the compiler.\n\n/**\n * @class\n * @summary Constructor for a Template, which is used to construct Views with particular name and content.\n * @locus Client\n * @param {String} [viewName] Optional. A name for Views constructed by this Template. See [`view.name`](#view_name).\n * @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). This function is used as the `renderFunction` for Views constructed by this Template.\n */\nBlaze.Template = function (viewName, renderFunction) {\n if (! (this instanceof Blaze.Template))\n // called without `new`\n return new Blaze.Template(viewName, renderFunction);\n\n if (typeof viewName === 'function') {\n // omitted \"viewName\" argument\n renderFunction = viewName;\n viewName = '';\n }\n if (typeof viewName !== 'string')\n throw new Error(\"viewName must be a String (or omitted)\");\n if (typeof renderFunction !== 'function')\n throw new Error(\"renderFunction must be a function\");\n\n this.viewName = viewName;\n this.renderFunction = renderFunction;\n\n this.__helpers = new HelperMap;\n this.__eventMaps = [];\n\n this._callbacks = {\n created: [],\n rendered: [],\n destroyed: []\n };\n};\nvar Template = Blaze.Template;\n\nvar HelperMap = function () {};\nHelperMap.prototype.get = function (name) {\n return this[' '+name];\n};\nHelperMap.prototype.set = function (name, helper) {\n this[' '+name] = helper;\n};\nHelperMap.prototype.has = function (name) {\n return (' '+name) in this;\n};\n\n/**\n * @summary Returns true if `value` is a template object like `Template.myTemplate`.\n * @locus Client\n * @param {Any} value The value to test.\n */\nBlaze.isTemplate = function (t) {\n return (t instanceof Blaze.Template);\n};\n\n/**\n * @name onCreated\n * @instance\n * @memberOf Template\n * @summary Register a function to be called when an instance of this template is created.\n * @param {Function} callback A function to be added as a callback.\n * @locus Client\n */\nTemplate.prototype.onCreated = function (cb) {\n this._callbacks.created.push(cb);\n};\n\n/**\n * @name onRendered\n * @instance\n * @memberOf Template\n * @summary Register a function to be called when an instance of this template is inserted into the DOM.\n * @param {Function} callback A function to be added as a callback.\n * @locus Client\n */\nTemplate.prototype.onRendered = function (cb) {\n this._callbacks.rendered.push(cb);\n};\n\n/**\n * @name onDestroyed\n * @instance\n * @memberOf Template\n * @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed.\n * @param {Function} callback A function to be added as a callback.\n * @locus Client\n */\nTemplate.prototype.onDestroyed = function (cb) {\n this._callbacks.destroyed.push(cb);\n};\n\nTemplate.prototype._getCallbacks = function (which) {\n var self = this;\n var callbacks = self[which] ? [self[which]] : [];\n // Fire all callbacks added with the new API (Template.onRendered())\n // as well as the old-style callback (e.g. Template.rendered) for\n // backwards-compatibility.\n callbacks = callbacks.concat(self._callbacks[which]);\n return callbacks;\n};\n\nvar fireCallbacks = function (callbacks, template) {\n Template._withTemplateInstanceFunc(\n function () { return template; },\n function () {\n for (var i = 0, N = callbacks.length; i < N; i++) {\n callbacks[i].call(template);\n }\n });\n};\n\nTemplate.prototype.constructView = function (contentFunc, elseFunc) {\n var self = this;\n var view = Blaze.View(self.viewName, self.renderFunction);\n view.template = self;\n\n view.templateContentBlock = (\n contentFunc ? new Template('(contentBlock)', contentFunc) : null);\n view.templateElseBlock = (\n elseFunc ? new Template('(elseBlock)', elseFunc) : null);\n\n if (self.__eventMaps || typeof self.events === 'object') {\n view._onViewRendered(function () {\n if (view.renderCount !== 1)\n return;\n\n if (! self.__eventMaps.length && typeof self.events === \"object\") {\n // Provide limited back-compat support for `.events = {...}`\n // syntax. Pass `template.events` to the original `.events(...)`\n // function. This code must run only once per template, in\n // order to not bind the handlers more than once, which is\n // ensured by the fact that we only do this when `__eventMaps`\n // is falsy, and we cause it to be set now.\n Template.prototype.events.call(self, self.events);\n }\n\n _.each(self.__eventMaps, function (m) {\n Blaze._addEventMap(view, m, view);\n });\n });\n }\n\n view._templateInstance = new Blaze.TemplateInstance(view);\n view.templateInstance = function () {\n // Update data, firstNode, and lastNode, and return the TemplateInstance\n // object.\n var inst = view._templateInstance;\n\n /**\n * @instance\n * @memberOf Blaze.TemplateInstance\n * @name data\n * @summary The data context of this instance's latest invocation.\n * @locus Client\n */\n inst.data = Blaze.getData(view);\n\n if (view._domrange && !view.isDestroyed) {\n inst.firstNode = view._domrange.firstNode();\n inst.lastNode = view._domrange.lastNode();\n } else {\n // on 'created' or 'destroyed' callbacks we don't have a DomRange\n inst.firstNode = null;\n inst.lastNode = null;\n }\n\n return inst;\n };\n\n /**\n * @name created\n * @instance\n * @memberOf Template\n * @summary Provide a callback when an instance of a template is created.\n * @locus Client\n * @deprecated in 1.1\n */\n // To avoid situations when new callbacks are added in between view\n // instantiation and event being fired, decide on all callbacks to fire\n // immediately and then fire them on the event.\n var createdCallbacks = self._getCallbacks('created');\n view.onViewCreated(function () {\n fireCallbacks(createdCallbacks, view.templateInstance());\n });\n\n /**\n * @name rendered\n * @instance\n * @memberOf Template\n * @summary Provide a callback when an instance of a template is rendered.\n * @locus Client\n * @deprecated in 1.1\n */\n var renderedCallbacks = self._getCallbacks('rendered');\n view.onViewReady(function () {\n fireCallbacks(renderedCallbacks, view.templateInstance());\n });\n\n /**\n * @name destroyed\n * @instance\n * @memberOf Template\n * @summary Provide a callback when an instance of a template is destroyed.\n * @locus Client\n * @deprecated in 1.1\n */\n var destroyedCallbacks = self._getCallbacks('destroyed');\n view.onViewDestroyed(function () {\n fireCallbacks(destroyedCallbacks, view.templateInstance());\n });\n\n return view;\n};\n\n/**\n * @class\n * @summary The class for template instances\n * @param {Blaze.View} view\n * @instanceName template\n */\nBlaze.TemplateInstance = function (view) {\n if (! (this instanceof Blaze.TemplateInstance))\n // called without `new`\n return new Blaze.TemplateInstance(view);\n\n if (! (view instanceof Blaze.View))\n throw new Error(\"View required\");\n\n view._templateInstance = this;\n\n /**\n * @name view\n * @memberOf Blaze.TemplateInstance\n * @instance\n * @summary The [View](#blaze_view) object for this invocation of the template.\n * @locus Client\n * @type {Blaze.View}\n */\n this.view = view;\n this.data = null;\n\n /**\n * @name firstNode\n * @memberOf Blaze.TemplateInstance\n * @instance\n * @summary The first top-level DOM node in this template instance.\n * @locus Client\n * @type {DOMNode}\n */\n this.firstNode = null;\n\n /**\n * @name lastNode\n * @memberOf Blaze.TemplateInstance\n * @instance\n * @summary The last top-level DOM node in this template instance.\n * @locus Client\n * @type {DOMNode}\n */\n this.lastNode = null;\n\n // This dependency is used to identify state transitions in\n // _subscriptionHandles which could cause the result of\n // TemplateInstance#subscriptionsReady to change. Basically this is triggered\n // whenever a new subscription handle is added or when a subscription handle\n // is removed and they are not ready.\n this._allSubsReadyDep = new Tracker.Dependency();\n this._allSubsReady = false;\n\n this._subscriptionHandles = {};\n};\n\n/**\n * @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object.\n * @locus Client\n * @param {String} selector The CSS selector to match, scoped to the template contents.\n * @returns {DOMNode[]}\n */\nBlaze.TemplateInstance.prototype.$ = function (selector) {\n var view = this.view;\n if (! view._domrange)\n throw new Error(\"Can't use $ on template instance with no DOM\");\n return view._domrange.$(selector);\n};\n\n/**\n * @summary Find all elements matching `selector` in this template instance.\n * @locus Client\n * @param {String} selector The CSS selector to match, scoped to the template contents.\n * @returns {DOMElement[]}\n */\nBlaze.TemplateInstance.prototype.findAll = function (selector) {\n return Array.prototype.slice.call(this.$(selector));\n};\n\n/**\n * @summary Find one element matching `selector` in this template instance.\n * @locus Client\n * @param {String} selector The CSS selector to match, scoped to the template contents.\n * @returns {DOMElement}\n */\nBlaze.TemplateInstance.prototype.find = function (selector) {\n var result = this.$(selector);\n return result[0] || null;\n};\n\n/**\n * @summary A version of [Tracker.autorun](#tracker_autorun) that is stopped when the template is destroyed.\n * @locus Client\n * @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object.\n */\nBlaze.TemplateInstance.prototype.autorun = function (f) {\n return this.view.autorun(f);\n};\n\n/**\n * @summary A version of [Meteor.subscribe](#meteor_subscribe) that is stopped\n * when the template is destroyed.\n * @return {SubscriptionHandle} The subscription handle to the newly made\n * subscription. Call `handle.stop()` to manually stop the subscription, or\n * `handle.ready()` to find out if this particular subscription has loaded all\n * of its inital data.\n * @locus Client\n * @param {String} name Name of the subscription. Matches the name of the\n * server's `publish()` call.\n * @param {Any} [arg1,arg2...] Optional arguments passed to publisher function\n * on server.\n * @param {Function|Object} [callbacks] Optional. May include `onStop` and\n * `onReady` callbacks. If a function is passed instead of an object, it is\n * interpreted as an `onReady` callback.\n */\nBlaze.TemplateInstance.prototype.subscribe = function (/* arguments */) {\n var self = this;\n\n var subHandles = self._subscriptionHandles;\n var args = _.toArray(arguments);\n\n // Duplicate logic from Meteor.subscribe\n var callbacks = {};\n if (args.length) {\n var lastParam = _.last(args);\n if (_.isFunction(lastParam)) {\n callbacks.onReady = args.pop();\n } else if (lastParam &&\n // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use\n // onStop with an error callback instead.\n _.any([lastParam.onReady, lastParam.onError, lastParam.onStop],\n _.isFunction)) {\n callbacks = args.pop();\n }\n }\n\n var subHandle;\n var oldStopped = callbacks.onStop;\n callbacks.onStop = function (error) {\n // When the subscription is stopped, remove it from the set of tracked\n // subscriptions to avoid this list growing without bound\n delete subHandles[subHandle.subscriptionId];\n\n // Removing a subscription can only change the result of subscriptionsReady\n // if we are not ready (that subscription could be the one blocking us being\n // ready).\n if (! self._allSubsReady) {\n self._allSubsReadyDep.changed();\n }\n\n if (oldStopped) {\n oldStopped(error);\n }\n };\n args.push(callbacks);\n\n subHandle = self.view.subscribe.call(self.view, args);\n\n if (! _.has(subHandles, subHandle.subscriptionId)) {\n subHandles[subHandle.subscriptionId] = subHandle;\n\n // Adding a new subscription will always cause us to transition from ready\n // to not ready, but if we are already not ready then this can't make us\n // ready.\n if (self._allSubsReady) {\n self._allSubsReadyDep.changed();\n }\n }\n\n return subHandle;\n};\n\n/**\n * @summary A reactive function that returns true when all of the subscriptions\n * called with [this.subscribe](#TemplateInstance-subscribe) are ready.\n * @return {Boolean} True if all subscriptions on this template instance are\n * ready.\n */\nBlaze.TemplateInstance.prototype.subscriptionsReady = function () {\n this._allSubsReadyDep.depend();\n\n this._allSubsReady = _.all(this._subscriptionHandles, function (handle) {\n return handle.ready();\n });\n\n return this._allSubsReady;\n};\n\n/**\n * @summary Specify template helpers available to this template.\n * @locus Client\n * @param {Object} helpers Dictionary of helper functions by name.\n */\nTemplate.prototype.helpers = function (dict) {\n for (var k in dict)\n this.__helpers.set(k, dict[k]);\n};\n\n// Kind of like Blaze.currentView but for the template instance.\n// This is a function, not a value -- so that not all helpers\n// are implicitly dependent on the current template instance's `data` property,\n// which would make them dependenct on the data context of the template\n// inclusion.\nTemplate._currentTemplateInstanceFunc = null;\n\nTemplate._withTemplateInstanceFunc = function (templateInstanceFunc, func) {\n if (typeof func !== 'function')\n throw new Error(\"Expected function, got: \" + func);\n var oldTmplInstanceFunc = Template._currentTemplateInstanceFunc;\n try {\n Template._currentTemplateInstanceFunc = templateInstanceFunc;\n return func();\n } finally {\n Template._currentTemplateInstanceFunc = oldTmplInstanceFunc;\n }\n};\n\n/**\n * @summary Specify event handlers for this template.\n * @locus Client\n * @param {EventMap} eventMap Event handlers to associate with this template.\n */\nTemplate.prototype.events = function (eventMap) {\n var template = this;\n var eventMap2 = {};\n for (var k in eventMap) {\n eventMap2[k] = (function (k, v) {\n return function (event/*, ...*/) {\n var view = this; // passed by EventAugmenter\n var data = Blaze.getData(event.currentTarget);\n if (data == null)\n data = {};\n var args = Array.prototype.slice.call(arguments);\n var tmplInstanceFunc = _.bind(view.templateInstance, view);\n args.splice(1, 0, tmplInstanceFunc());\n\n return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () {\n return v.apply(data, args);\n });\n };\n })(k, eventMap[k]);\n }\n\n template.__eventMaps.push(eventMap2);\n};\n\n/**\n * @function\n * @name instance\n * @memberOf Template\n * @summary The [template instance](#template_inst) corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.\n * @locus Client\n * @returns {Blaze.TemplateInstance}\n */\nTemplate.instance = function () {\n return Template._currentTemplateInstanceFunc\n && Template._currentTemplateInstanceFunc();\n};\n\n// Note: Template.currentData() is documented to take zero arguments,\n// while Blaze.getData takes up to one.\n\n/**\n * @summary\n *\n * - Inside an `onCreated`, `onRendered`, or `onDestroyed` callback, returns\n * the data context of the template.\n * - Inside an event handler, returns the data context of the template on which\n * this event handler was defined.\n * - Inside a helper, returns the data context of the DOM node where the helper\n * was used.\n *\n * Establishes a reactive dependency on the result.\n * @locus Client\n * @function\n */\nTemplate.currentData = Blaze.getData;\n\n/**\n * @summary Accesses other data contexts that enclose the current data context.\n * @locus Client\n * @function\n * @param {Integer} [numLevels] The number of levels beyond the current data context to look. Defaults to 1.\n */\nTemplate.parentData = Blaze._parentData;\n\n/**\n * @summary Defines a [helper function](#template_helpers) which can be used from all templates.\n * @locus Client\n * @function\n * @param {String} name The name of the helper function you are defining.\n * @param {Function} function The helper function itself.\n */\nTemplate.registerHelper = Blaze.registerHelper;\n","UI = Blaze;\n\nBlaze.ReactiveVar = ReactiveVar;\nUI._templateInstance = Blaze.Template.instance;\n\nHandlebars = {};\nHandlebars.registerHelper = Blaze.registerHelper;\n\nHandlebars._escape = Blaze._escape;\n\n// Return these from {{...}} helpers to achieve the same as returning\n// strings from {{{...}}} helpers\nHandlebars.SafeString = function(string) {\n this.string = string;\n};\nHandlebars.SafeString.prototype.toString = function() {\n return this.string.toString();\n};\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js b/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js
new file mode 100644
index 0000000..9e1b347
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js
@@ -0,0 +1,141 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var _ = Package.underscore._;
+var SpacebarsCompiler = Package['spacebars-compiler'].SpacebarsCompiler;
+var Spacebars = Package.spacebars.Spacebars;
+var HTML = Package.htmljs.HTML;
+var Blaze = Package.blaze.Blaze;
+var UI = Package.blaze.UI;
+var Handlebars = Package.blaze.Handlebars;
+
+/* Package-scope variables */
+var Boilerplate;
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/boilerplate-generator/boilerplate-generator.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////
+ //
+var fs = Npm.require('fs'); // 1
+var path = Npm.require('path'); // 2
+ // 3
+// Copied from webapp_server // 4
+var readUtf8FileSync = function (filename) { // 5
+ return Meteor.wrapAsync(fs.readFile)(filename, 'utf8'); // 6
+}; // 7
+ // 8
+Boilerplate = function (arch, manifest, options) { // 9
+ var self = this; // 10
+ options = options || {}; // 11
+ self.template = _getTemplate(arch); // 12
+ self.baseData = null; // 13
+ self.func = null; // 14
+ // 15
+ self._generateBoilerplateFromManifestAndSource( // 16
+ manifest, // 17
+ self.template, // 18
+ options // 19
+ ); // 20
+}; // 21
+ // 22
+// The 'extraData' argument can be used to extend 'self.baseData'. Its // 23
+// purpose is to allow you to specify data that you might not know at // 24
+// the time that you construct the Boilerplate object. (e.g. it is used // 25
+// by 'webapp' to specify data that is only known at request-time). // 26
+Boilerplate.prototype.toHTML = function (extraData) { // 27
+ var self = this; // 28
+ // 29
+ if (! self.baseData || ! self.func) // 30
+ throw new Error('Boilerplate did not instantiate correctly.'); // 31
+ // 32
+ return "\n" + // 33
+ Blaze.toHTML(Blaze.With(_.extend(self.baseData, extraData), // 34
+ self.func)); // 35
+}; // 36
+ // 37
+// XXX Exported to allow client-side only changes to rebuild the boilerplate // 38
+// without requiring a full server restart. // 39
+// Produces an HTML string with given manifest and boilerplateSource. // 40
+// Optionally takes urlMapper in case urls from manifest need to be prefixed // 41
+// or rewritten. // 42
+// Optionally takes pathMapper for resolving relative file system paths. // 43
+// Optionally allows to override fields of the data context. // 44
+Boilerplate.prototype._generateBoilerplateFromManifestAndSource = // 45
+ function (manifest, boilerplateSource, options) { // 46
+ var self = this; // 47
+ // map to the identity by default // 48
+ var urlMapper = options.urlMapper || _.identity; // 49
+ var pathMapper = options.pathMapper || _.identity; // 50
+ // 51
+ var boilerplateBaseData = { // 52
+ css: [], // 53
+ js: [], // 54
+ head: '', // 55
+ body: '', // 56
+ meteorManifest: JSON.stringify(manifest) // 57
+ }; // 58
+ // 59
+ // allow the caller to extend the default base data // 60
+ _.extend(boilerplateBaseData, options.baseDataExtension); // 61
+ // 62
+ _.each(manifest, function (item) { // 63
+ var urlPath = urlMapper(item.url); // 64
+ var itemObj = { url: urlPath }; // 65
+ // 66
+ if (options.inline) { // 67
+ itemObj.scriptContent = readUtf8FileSync( // 68
+ pathMapper(item.path)); // 69
+ itemObj.inline = true; // 70
+ } // 71
+ // 72
+ if (item.type === 'css' && item.where === 'client') { // 73
+ boilerplateBaseData.css.push(itemObj); // 74
+ } // 75
+ if (item.type === 'js' && item.where === 'client') { // 76
+ boilerplateBaseData.js.push(itemObj); // 77
+ } // 78
+ if (item.type === 'head') { // 79
+ boilerplateBaseData.head = // 80
+ readUtf8FileSync(pathMapper(item.path)); // 81
+ } // 82
+ if (item.type === 'body') { // 83
+ boilerplateBaseData.body = // 84
+ readUtf8FileSync(pathMapper(item.path)); // 85
+ } // 86
+ }); // 87
+ var boilerplateRenderCode = SpacebarsCompiler.compile( // 88
+ boilerplateSource, { isBody: true }); // 89
+ // 90
+ // Note that we are actually depending on eval's local environment capture // 91
+ // so that UI and HTML are visible to the eval'd code. // 92
+ // XXX the template we are evaluating relies on the fact that UI is globally // 93
+ // available. // 94
+ global.UI = UI; // 95
+ self.func = eval(boilerplateRenderCode); // 96
+ self.baseData = boilerplateBaseData; // 97
+}; // 98
+ // 99
+var _getTemplate = _.memoize(function (arch) { // 100
+ var filename = 'boilerplate_' + arch + '.html'; // 101
+ return Assets.getText(filename); // 102
+}); // 103
+ // 104
+///////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['boilerplate-generator'] = {
+ Boilerplate: Boilerplate
+};
+
+})();
+
+//# sourceMappingURL=boilerplate-generator.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js.map b/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js.map
new file mode 100644
index 0000000..9456a33
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/boilerplate-generator.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["boilerplate-generator/boilerplate-generator.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,2B;AACA,+B;;AAEA,4B;AACA,4C;AACA,yD;AACA,E;;AAEA,kD;AACA,kB;AACA,0B;AACA,qC;AACA,uB;AACA,mB;;AAEA,iD;AACA,a;AACA,kB;AACA,W;AACA,I;AACA,E;;AAEA,sE;AACA,qE;AACA,uE;AACA,mE;AACA,qD;AACA,kB;;AAEA,qC;AACA,kE;;AAEA,+B;AACA,+D;AACA,wC;AACA,E;;AAEA,4E;AACA,2C;AACA,qE;AACA,4E;AACA,gB;AACA,wE;AACA,4D;AACA,iE;AACA,mD;AACA,oB;AACA,qC;AACA,oD;AACA,sD;;AAEA,+B;AACA,c;AACA,a;AACA,e;AACA,e;AACA,8C;AACA,M;;AAEA,uD;AACA,6D;;AAEA,sC;AACA,wC;AACA,qC;;AAEA,2B;AACA,iD;AACA,iC;AACA,8B;AACA,O;;AAEA,2D;AACA,8C;AACA,O;AACA,0D;AACA,6C;AACA,O;AACA,iC;AACA,kC;AACA,kD;AACA,O;AACA,iC;AACA,kC;AACA,kD;AACA,O;AACA,O;AACA,0D;AACA,2C;;AAEA,8E;AACA,0D;AACA,gF;AACA,mB;AACA,mB;AACA,4C;AACA,wC;AACA,E;;AAEA,8C;AACA,iD;AACA,kC;AACA,G","file":"/packages/boilerplate-generator.js","sourcesContent":["var fs = Npm.require('fs');\nvar path = Npm.require('path');\n\n// Copied from webapp_server\nvar readUtf8FileSync = function (filename) {\n return Meteor.wrapAsync(fs.readFile)(filename, 'utf8');\n};\n\nBoilerplate = function (arch, manifest, options) {\n var self = this;\n options = options || {};\n self.template = _getTemplate(arch);\n self.baseData = null;\n self.func = null;\n\n self._generateBoilerplateFromManifestAndSource(\n manifest,\n self.template,\n options\n );\n};\n\n// The 'extraData' argument can be used to extend 'self.baseData'. Its\n// purpose is to allow you to specify data that you might not know at\n// the time that you construct the Boilerplate object. (e.g. it is used\n// by 'webapp' to specify data that is only known at request-time).\nBoilerplate.prototype.toHTML = function (extraData) {\n var self = this;\n\n if (! self.baseData || ! self.func)\n throw new Error('Boilerplate did not instantiate correctly.');\n\n return \"\\n\" +\n Blaze.toHTML(Blaze.With(_.extend(self.baseData, extraData),\n self.func));\n};\n\n// XXX Exported to allow client-side only changes to rebuild the boilerplate\n// without requiring a full server restart.\n// Produces an HTML string with given manifest and boilerplateSource.\n// Optionally takes urlMapper in case urls from manifest need to be prefixed\n// or rewritten.\n// Optionally takes pathMapper for resolving relative file system paths.\n// Optionally allows to override fields of the data context.\nBoilerplate.prototype._generateBoilerplateFromManifestAndSource =\n function (manifest, boilerplateSource, options) {\n var self = this;\n // map to the identity by default\n var urlMapper = options.urlMapper || _.identity;\n var pathMapper = options.pathMapper || _.identity;\n\n var boilerplateBaseData = {\n css: [],\n js: [],\n head: '',\n body: '',\n meteorManifest: JSON.stringify(manifest)\n };\n\n // allow the caller to extend the default base data\n _.extend(boilerplateBaseData, options.baseDataExtension);\n\n _.each(manifest, function (item) {\n var urlPath = urlMapper(item.url);\n var itemObj = { url: urlPath };\n\n if (options.inline) {\n itemObj.scriptContent = readUtf8FileSync(\n pathMapper(item.path));\n itemObj.inline = true;\n }\n\n if (item.type === 'css' && item.where === 'client') {\n boilerplateBaseData.css.push(itemObj);\n }\n if (item.type === 'js' && item.where === 'client') {\n boilerplateBaseData.js.push(itemObj);\n }\n if (item.type === 'head') {\n boilerplateBaseData.head =\n readUtf8FileSync(pathMapper(item.path));\n }\n if (item.type === 'body') {\n boilerplateBaseData.body =\n readUtf8FileSync(pathMapper(item.path));\n }\n });\n var boilerplateRenderCode = SpacebarsCompiler.compile(\n boilerplateSource, { isBody: true });\n\n // Note that we are actually depending on eval's local environment capture\n // so that UI and HTML are visible to the eval'd code.\n // XXX the template we are evaluating relies on the fact that UI is globally\n // available.\n global.UI = UI;\n self.func = eval(boilerplateRenderCode);\n self.baseData = boilerplateBaseData;\n};\n\nvar _getTemplate = _.memoize(function (arch) {\n var filename = 'boilerplate_' + arch + '.html';\n return Assets.getText(filename);\n});\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/callback-hook.js b/web-app/.meteor/local/build/programs/server/packages/callback-hook.js
new file mode 100644
index 0000000..cbc466f
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/callback-hook.js
@@ -0,0 +1,139 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var _ = Package.underscore._;
+
+/* Package-scope variables */
+var Hook;
+
+(function () {
+
+//////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/callback-hook/hook.js //
+// //
+//////////////////////////////////////////////////////////////////////////////////
+ //
+// XXX This pattern is under development. Do not add more callsites // 1
+// using this package for now. See: // 2
+// https://meteor.hackpad.com/Design-proposal-Hooks-YxvgEW06q6f // 3
+// // 4
+// Encapsulates the pattern of registering callbacks on a hook. // 5
+// // 6
+// The `each` method of the hook calls its iterator function argument // 7
+// with each registered callback. This allows the hook to // 8
+// conditionally decide not to call the callback (if, for example, the // 9
+// observed object has been closed or terminated). // 10
+// // 11
+// Callbacks are bound with `Meteor.bindEnvironment`, so they will be // 12
+// called with the Meteor environment of the calling code that // 13
+// registered the callback. // 14
+// // 15
+// Registering a callback returns an object with a single `stop` // 16
+// method which unregisters the callback. // 17
+// // 18
+// The code is careful to allow a callback to be safely unregistered // 19
+// while the callbacks are being iterated over. // 20
+// // 21
+// If the hook is configured with the `exceptionHandler` option, the // 22
+// handler will be called if a called callback throws an exception. // 23
+// By default (if the exception handler doesn't itself throw an // 24
+// exception, or if the iterator function doesn't return a falsy value // 25
+// to terminate the calling of callbacks), the remaining callbacks // 26
+// will still be called. // 27
+// // 28
+// Alternatively, the `debugPrintExceptions` option can be specified // 29
+// as string describing the callback. On an exception the string and // 30
+// the exception will be printed to the console log with // 31
+// `Meteor._debug`, and the exception otherwise ignored. // 32
+// // 33
+// If an exception handler isn't specified, exceptions thrown in the // 34
+// callback will propagate up to the iterator function, and will // 35
+// terminate calling the remaining callbacks if not caught. // 36
+ // 37
+Hook = function (options) { // 38
+ var self = this; // 39
+ options = options || {}; // 40
+ self.nextCallbackId = 0; // 41
+ self.callbacks = {}; // 42
+ // 43
+ if (options.exceptionHandler) // 44
+ self.exceptionHandler = options.exceptionHandler; // 45
+ else if (options.debugPrintExceptions) { // 46
+ if (! _.isString(options.debugPrintExceptions)) // 47
+ throw new Error("Hook option debugPrintExceptions should be a string"); // 48
+ self.exceptionHandler = options.debugPrintExceptions; // 49
+ } // 50
+}; // 51
+ // 52
+_.extend(Hook.prototype, { // 53
+ register: function (callback) { // 54
+ var self = this; // 55
+ // 56
+ callback = Meteor.bindEnvironment( // 57
+ callback, // 58
+ self.exceptionHandler || function (exception) { // 59
+ // Note: this relies on the undocumented fact that if bindEnvironment's // 60
+ // onException throws, and you are invoking the callback either in the // 61
+ // browser or from within a Fiber in Node, the exception is propagated. // 62
+ throw exception; // 63
+ } // 64
+ ); // 65
+ // 66
+ var id = self.nextCallbackId++; // 67
+ self.callbacks[id] = callback; // 68
+ // 69
+ return { // 70
+ stop: function () { // 71
+ delete self.callbacks[id]; // 72
+ } // 73
+ }; // 74
+ }, // 75
+ // 76
+ // For each registered callback, call the passed iterator function // 77
+ // with the callback. // 78
+ // // 79
+ // The iterator function can choose whether or not to call the // 80
+ // callback. (For example, it might not call the callback if the // 81
+ // observed object has been closed or terminated). // 82
+ // // 83
+ // The iteration is stopped if the iterator function returns a falsy // 84
+ // value or throws an exception. // 85
+ // // 86
+ each: function (iterator) { // 87
+ var self = this; // 88
+ // 89
+ // Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't // 90
+ // run them to completion (and exceptions thrown from onException are not // 91
+ // propagated), so we need to be in a Fiber. // 92
+ Meteor._nodeCodeMustBeInFiber(); // 93
+ // 94
+ var ids = _.keys(self.callbacks); // 95
+ for (var i = 0; i < ids.length; ++i) { // 96
+ var id = ids[i]; // 97
+ // check to see if the callback was removed during iteration // 98
+ if (_.has(self.callbacks, id)) { // 99
+ var callback = self.callbacks[id]; // 100
+ // 101
+ if (! iterator(callback)) // 102
+ break; // 103
+ } // 104
+ } // 105
+ } // 106
+}); // 107
+ // 108
+//////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['callback-hook'] = {
+ Hook: Hook
+};
+
+})();
+
+//# sourceMappingURL=callback-hook.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/callback-hook.js.map b/web-app/.meteor/local/build/programs/server/packages/callback-hook.js.map
new file mode 100644
index 0000000..ecc48f9
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/callback-hook.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["callback-hook/hook.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mE;AACA,mC;AACA,+D;AACA,E;AACA,+D;AACA,E;AACA,qE;AACA,0D;AACA,sE;AACA,kD;AACA,E;AACA,qE;AACA,8D;AACA,2B;AACA,E;AACA,gE;AACA,yC;AACA,E;AACA,oE;AACA,+C;AACA,E;AACA,oE;AACA,mE;AACA,+D;AACA,sE;AACA,kE;AACA,wB;AACA,E;AACA,oE;AACA,qE;AACA,wD;AACA,wD;AACA,E;AACA,oE;AACA,gE;AACA,2D;;AAEA,2B;AACA,kB;AACA,0B;AACA,0B;AACA,sB;;AAEA,+B;AACA,qD;AACA,0C;AACA,mD;AACA,6E;AACA,yD;AACA,G;AACA,E;;AAEA,0B;AACA,iC;AACA,oB;;AAEA,sC;AACA,e;AACA,qD;AACA,+E;AACA,8E;AACA,+E;AACA,wB;AACA,O;AACA,M;;AAEA,mC;AACA,kC;;AAEA,Y;AACA,yB;AACA,kC;AACA,O;AACA,M;AACA,I;;AAEA,oE;AACA,uB;AACA,I;AACA,gE;AACA,mE;AACA,oD;AACA,I;AACA,sE;AACA,kC;AACA,I;AACA,6B;AACA,oB;;AAEA,8E;AACA,6E;AACA,gD;AACA,oC;;AAEA,qC;AACA,4C;AACA,sB;AACA,kE;AACA,sC;AACA,0C;;AAEA,iC;AACA,gB;AACA,O;AACA,K;AACA,G;AACA,G","file":"/packages/callback-hook.js","sourcesContent":["// XXX This pattern is under development. Do not add more callsites\n// using this package for now. See:\n// https://meteor.hackpad.com/Design-proposal-Hooks-YxvgEW06q6f\n//\n// Encapsulates the pattern of registering callbacks on a hook.\n//\n// The `each` method of the hook calls its iterator function argument\n// with each registered callback. This allows the hook to\n// conditionally decide not to call the callback (if, for example, the\n// observed object has been closed or terminated).\n//\n// Callbacks are bound with `Meteor.bindEnvironment`, so they will be\n// called with the Meteor environment of the calling code that\n// registered the callback.\n//\n// Registering a callback returns an object with a single `stop`\n// method which unregisters the callback.\n//\n// The code is careful to allow a callback to be safely unregistered\n// while the callbacks are being iterated over.\n//\n// If the hook is configured with the `exceptionHandler` option, the\n// handler will be called if a called callback throws an exception.\n// By default (if the exception handler doesn't itself throw an\n// exception, or if the iterator function doesn't return a falsy value\n// to terminate the calling of callbacks), the remaining callbacks\n// will still be called.\n//\n// Alternatively, the `debugPrintExceptions` option can be specified\n// as string describing the callback. On an exception the string and\n// the exception will be printed to the console log with\n// `Meteor._debug`, and the exception otherwise ignored.\n//\n// If an exception handler isn't specified, exceptions thrown in the\n// callback will propagate up to the iterator function, and will\n// terminate calling the remaining callbacks if not caught.\n\nHook = function (options) {\n var self = this;\n options = options || {};\n self.nextCallbackId = 0;\n self.callbacks = {};\n\n if (options.exceptionHandler)\n self.exceptionHandler = options.exceptionHandler;\n else if (options.debugPrintExceptions) {\n if (! _.isString(options.debugPrintExceptions))\n throw new Error(\"Hook option debugPrintExceptions should be a string\");\n self.exceptionHandler = options.debugPrintExceptions;\n }\n};\n\n_.extend(Hook.prototype, {\n register: function (callback) {\n var self = this;\n\n callback = Meteor.bindEnvironment(\n callback,\n self.exceptionHandler || function (exception) {\n // Note: this relies on the undocumented fact that if bindEnvironment's\n // onException throws, and you are invoking the callback either in the\n // browser or from within a Fiber in Node, the exception is propagated.\n throw exception;\n }\n );\n\n var id = self.nextCallbackId++;\n self.callbacks[id] = callback;\n\n return {\n stop: function () {\n delete self.callbacks[id];\n }\n };\n },\n\n // For each registered callback, call the passed iterator function\n // with the callback.\n //\n // The iterator function can choose whether or not to call the\n // callback. (For example, it might not call the callback if the\n // observed object has been closed or terminated).\n //\n // The iteration is stopped if the iterator function returns a falsy\n // value or throws an exception.\n //\n each: function (iterator) {\n var self = this;\n\n // Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't\n // run them to completion (and exceptions thrown from onException are not\n // propagated), so we need to be in a Fiber.\n Meteor._nodeCodeMustBeInFiber();\n\n var ids = _.keys(self.callbacks);\n for (var i = 0; i < ids.length; ++i) {\n var id = ids[i];\n // check to see if the callback was removed during iteration\n if (_.has(self.callbacks, id)) {\n var callback = self.callbacks[id];\n\n if (! iterator(callback))\n break;\n }\n }\n }\n});\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/check.js b/web-app/.meteor/local/build/programs/server/packages/check.js
new file mode 100644
index 0000000..97b6c53
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/check.js
@@ -0,0 +1,416 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var _ = Package.underscore._;
+var EJSON = Package.ejson.EJSON;
+
+/* Package-scope variables */
+var check, Match;
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/check/match.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////
+ //
+// XXX docs // 1
+ // 2
+// Things we explicitly do NOT support: // 3
+// - heterogenous arrays // 4
+ // 5
+var currentArgumentChecker = new Meteor.EnvironmentVariable; // 6
+ // 7
+/** // 8
+ * @summary Check that a value matches a [pattern](#matchpatterns). // 9
+ * If the value does not match the pattern, throw a `Match.Error`. // 10
+ * // 11
+ * Particularly useful to assert that arguments to a function have the right // 12
+ * types and structure. // 13
+ * @locus Anywhere // 14
+ * @param {Any} value The value to check // 15
+ * @param {MatchPattern} pattern The pattern to match // 16
+ * `value` against // 17
+ */ // 18
+check = function (value, pattern) { // 19
+ // Record that check got called, if somebody cared. // 20
+ // // 21
+ // We use getOrNullIfOutsideFiber so that it's OK to call check() // 22
+ // from non-Fiber server contexts; the downside is that if you forget to // 23
+ // bindEnvironment on some random callback in your method/publisher, // 24
+ // it might not find the argumentChecker and you'll get an error about // 25
+ // not checking an argument that it looks like you're checking (instead // 26
+ // of just getting a "Node code must run in a Fiber" error). // 27
+ var argChecker = currentArgumentChecker.getOrNullIfOutsideFiber(); // 28
+ if (argChecker) // 29
+ argChecker.checking(value); // 30
+ try { // 31
+ checkSubtree(value, pattern); // 32
+ } catch (err) { // 33
+ if ((err instanceof Match.Error) && err.path) // 34
+ err.message += " in field " + err.path; // 35
+ throw err; // 36
+ } // 37
+}; // 38
+ // 39
+/** // 40
+ * @namespace Match // 41
+ * @summary The namespace for all Match types and methods. // 42
+ */ // 43
+Match = { // 44
+ Optional: function (pattern) { // 45
+ return new Optional(pattern); // 46
+ }, // 47
+ OneOf: function (/*arguments*/) { // 48
+ return new OneOf(_.toArray(arguments)); // 49
+ }, // 50
+ Any: ['__any__'], // 51
+ Where: function (condition) { // 52
+ return new Where(condition); // 53
+ }, // 54
+ ObjectIncluding: function (pattern) { // 55
+ return new ObjectIncluding(pattern); // 56
+ }, // 57
+ ObjectWithValues: function (pattern) { // 58
+ return new ObjectWithValues(pattern); // 59
+ }, // 60
+ // Matches only signed 32-bit integers // 61
+ Integer: ['__integer__'], // 62
+ // 63
+ // XXX matchers should know how to describe themselves for errors // 64
+ Error: Meteor.makeErrorType("Match.Error", function (msg) { // 65
+ this.message = "Match error: " + msg; // 66
+ // The path of the value that failed to match. Initially empty, this gets // 67
+ // populated by catching and rethrowing the exception as it goes back up the // 68
+ // stack. // 69
+ // E.g.: "vals[3].entity.created" // 70
+ this.path = ""; // 71
+ // If this gets sent over DDP, don't give full internal details but at least // 72
+ // provide something better than 500 Internal server error. // 73
+ this.sanitizedError = new Meteor.Error(400, "Match failed"); // 74
+ }), // 75
+ // 76
+ // Tests to see if value matches pattern. Unlike check, it merely returns true // 77
+ // or false (unless an error other than Match.Error was thrown). It does not // 78
+ // interact with _failIfArgumentsAreNotAllChecked. // 79
+ // XXX maybe also implement a Match.match which returns more information about // 80
+ // failures but without using exception handling or doing what check() // 81
+ // does with _failIfArgumentsAreNotAllChecked and Meteor.Error conversion // 82
+ // 83
+ /** // 84
+ * @summary Returns true if the value matches the pattern. // 85
+ * @locus Anywhere // 86
+ * @param {Any} value The value to check // 87
+ * @param {MatchPattern} pattern The pattern to match `value` against // 88
+ */ // 89
+ test: function (value, pattern) { // 90
+ try { // 91
+ checkSubtree(value, pattern); // 92
+ return true; // 93
+ } catch (e) { // 94
+ if (e instanceof Match.Error) // 95
+ return false; // 96
+ // Rethrow other errors. // 97
+ throw e; // 98
+ } // 99
+ }, // 100
+ // 101
+ // Runs `f.apply(context, args)`. If check() is not called on every element of // 102
+ // `args` (either directly or in the first level of an array), throws an error // 103
+ // (using `description` in the message). // 104
+ // // 105
+ _failIfArgumentsAreNotAllChecked: function (f, context, args, description) { // 106
+ var argChecker = new ArgumentChecker(args, description); // 107
+ var result = currentArgumentChecker.withValue(argChecker, function () { // 108
+ return f.apply(context, args); // 109
+ }); // 110
+ // If f didn't itself throw, make sure it checked all of its arguments. // 111
+ argChecker.throwUnlessAllArgumentsHaveBeenChecked(); // 112
+ return result; // 113
+ } // 114
+}; // 115
+ // 116
+var Optional = function (pattern) { // 117
+ this.pattern = pattern; // 118
+}; // 119
+ // 120
+var OneOf = function (choices) { // 121
+ if (_.isEmpty(choices)) // 122
+ throw new Error("Must provide at least one choice to Match.OneOf"); // 123
+ this.choices = choices; // 124
+}; // 125
+ // 126
+var Where = function (condition) { // 127
+ this.condition = condition; // 128
+}; // 129
+ // 130
+var ObjectIncluding = function (pattern) { // 131
+ this.pattern = pattern; // 132
+}; // 133
+ // 134
+var ObjectWithValues = function (pattern) { // 135
+ this.pattern = pattern; // 136
+}; // 137
+ // 138
+var typeofChecks = [ // 139
+ [String, "string"], // 140
+ [Number, "number"], // 141
+ [Boolean, "boolean"], // 142
+ // While we don't allow undefined in EJSON, this is good for optional // 143
+ // arguments with OneOf. // 144
+ [undefined, "undefined"] // 145
+]; // 146
+ // 147
+var checkSubtree = function (value, pattern) { // 148
+ // Match anything! // 149
+ if (pattern === Match.Any) // 150
+ return; // 151
+ // 152
+ // Basic atomic types. // 153
+ // Do not match boxed objects (e.g. String, Boolean) // 154
+ for (var i = 0; i < typeofChecks.length; ++i) { // 155
+ if (pattern === typeofChecks[i][0]) { // 156
+ if (typeof value === typeofChecks[i][1]) // 157
+ return; // 158
+ throw new Match.Error("Expected " + typeofChecks[i][1] + ", got " + // 159
+ typeof value); // 160
+ } // 161
+ } // 162
+ if (pattern === null) { // 163
+ if (value === null) // 164
+ return; // 165
+ throw new Match.Error("Expected null, got " + EJSON.stringify(value)); // 166
+ } // 167
+ // 168
+ // Strings and numbers match literally. Goes well with Match.OneOf. // 169
+ if (typeof pattern === "string" || typeof pattern === "number") { // 170
+ if (value === pattern) // 171
+ return; // 172
+ throw new Match.Error("Expected " + pattern + ", got " + // 173
+ EJSON.stringify(value)); // 174
+ } // 175
+ // 176
+ // Match.Integer is special type encoded with array // 177
+ if (pattern === Match.Integer) { // 178
+ // There is no consistent and reliable way to check if variable is a 64-bit // 179
+ // integer. One of the popular solutions is to get reminder of division by 1 // 180
+ // but this method fails on really large floats with big precision. // 181
+ // E.g.: 1.348192308491824e+23 % 1 === 0 in V8 // 182
+ // Bitwise operators work consistantly but always cast variable to 32-bit // 183
+ // signed integer according to JavaScript specs. // 184
+ if (typeof value === "number" && (value | 0) === value) // 185
+ return // 186
+ throw new Match.Error("Expected Integer, got " // 187
+ + (value instanceof Object ? EJSON.stringify(value) : value)); // 188
+ } // 189
+ // 190
+ // "Object" is shorthand for Match.ObjectIncluding({}); // 191
+ if (pattern === Object) // 192
+ pattern = Match.ObjectIncluding({}); // 193
+ // 194
+ // Array (checked AFTER Any, which is implemented as an Array). // 195
+ if (pattern instanceof Array) { // 196
+ if (pattern.length !== 1) // 197
+ throw Error("Bad pattern: arrays must have one type element" + // 198
+ EJSON.stringify(pattern)); // 199
+ if (!_.isArray(value) && !_.isArguments(value)) { // 200
+ throw new Match.Error("Expected array, got " + EJSON.stringify(value)); // 201
+ } // 202
+ // 203
+ _.each(value, function (valueElement, index) { // 204
+ try { // 205
+ checkSubtree(valueElement, pattern[0]); // 206
+ } catch (err) { // 207
+ if (err instanceof Match.Error) { // 208
+ err.path = _prependPath(index, err.path); // 209
+ } // 210
+ throw err; // 211
+ } // 212
+ }); // 213
+ return; // 214
+ } // 215
+ // 216
+ // Arbitrary validation checks. The condition can return false or throw a // 217
+ // Match.Error (ie, it can internally use check()) to fail. // 218
+ if (pattern instanceof Where) { // 219
+ if (pattern.condition(value)) // 220
+ return; // 221
+ // XXX this error is terrible // 222
+ throw new Match.Error("Failed Match.Where validation"); // 223
+ } // 224
+ // 225
+ // 226
+ if (pattern instanceof Optional) // 227
+ pattern = Match.OneOf(undefined, pattern.pattern); // 228
+ // 229
+ if (pattern instanceof OneOf) { // 230
+ for (var i = 0; i < pattern.choices.length; ++i) { // 231
+ try { // 232
+ checkSubtree(value, pattern.choices[i]); // 233
+ // No error? Yay, return. // 234
+ return; // 235
+ } catch (err) { // 236
+ // Other errors should be thrown. Match errors just mean try another // 237
+ // choice. // 238
+ if (!(err instanceof Match.Error)) // 239
+ throw err; // 240
+ } // 241
+ } // 242
+ // XXX this error is terrible // 243
+ throw new Match.Error("Failed Match.OneOf or Match.Optional validation"); // 244
+ } // 245
+ // 246
+ // A function that isn't something we special-case is assumed to be a // 247
+ // constructor. // 248
+ if (pattern instanceof Function) { // 249
+ if (value instanceof pattern) // 250
+ return; // 251
+ throw new Match.Error("Expected " + (pattern.name || // 252
+ "particular constructor")); // 253
+ } // 254
+ // 255
+ var unknownKeysAllowed = false; // 256
+ var unknownKeyPattern; // 257
+ if (pattern instanceof ObjectIncluding) { // 258
+ unknownKeysAllowed = true; // 259
+ pattern = pattern.pattern; // 260
+ } // 261
+ if (pattern instanceof ObjectWithValues) { // 262
+ unknownKeysAllowed = true; // 263
+ unknownKeyPattern = [pattern.pattern]; // 264
+ pattern = {}; // no required keys // 265
+ } // 266
+ // 267
+ if (typeof pattern !== "object") // 268
+ throw Error("Bad pattern: unknown pattern type"); // 269
+ // 270
+ // An object, with required and optional keys. Note that this does NOT do // 271
+ // structural matches against objects of special types that happen to match // 272
+ // the pattern: this really needs to be a plain old {Object}! // 273
+ if (typeof value !== 'object') // 274
+ throw new Match.Error("Expected object, got " + typeof value); // 275
+ if (value === null) // 276
+ throw new Match.Error("Expected object, got null"); // 277
+ if (value.constructor !== Object) // 278
+ throw new Match.Error("Expected plain object"); // 279
+ // 280
+ var requiredPatterns = {}; // 281
+ var optionalPatterns = {}; // 282
+ _.each(pattern, function (subPattern, key) { // 283
+ if (subPattern instanceof Optional) // 284
+ optionalPatterns[key] = subPattern.pattern; // 285
+ else // 286
+ requiredPatterns[key] = subPattern; // 287
+ }); // 288
+ // 289
+ _.each(value, function (subValue, key) { // 290
+ try { // 291
+ if (_.has(requiredPatterns, key)) { // 292
+ checkSubtree(subValue, requiredPatterns[key]); // 293
+ delete requiredPatterns[key]; // 294
+ } else if (_.has(optionalPatterns, key)) { // 295
+ checkSubtree(subValue, optionalPatterns[key]); // 296
+ } else { // 297
+ if (!unknownKeysAllowed) // 298
+ throw new Match.Error("Unknown key"); // 299
+ if (unknownKeyPattern) { // 300
+ checkSubtree(subValue, unknownKeyPattern[0]); // 301
+ } // 302
+ } // 303
+ } catch (err) { // 304
+ if (err instanceof Match.Error) // 305
+ err.path = _prependPath(key, err.path); // 306
+ throw err; // 307
+ } // 308
+ }); // 309
+ // 310
+ _.each(requiredPatterns, function (subPattern, key) { // 311
+ throw new Match.Error("Missing key '" + key + "'"); // 312
+ }); // 313
+}; // 314
+ // 315
+var ArgumentChecker = function (args, description) { // 316
+ var self = this; // 317
+ // Make a SHALLOW copy of the arguments. (We'll be doing identity checks // 318
+ // against its contents.) // 319
+ self.args = _.clone(args); // 320
+ // Since the common case will be to check arguments in order, and we splice // 321
+ // out arguments when we check them, make it so we splice out from the end // 322
+ // rather than the beginning. // 323
+ self.args.reverse(); // 324
+ self.description = description; // 325
+}; // 326
+ // 327
+_.extend(ArgumentChecker.prototype, { // 328
+ checking: function (value) { // 329
+ var self = this; // 330
+ if (self._checkingOneValue(value)) // 331
+ return; // 332
+ // Allow check(arguments, [String]) or check(arguments.slice(1), [String]) // 333
+ // or check([foo, bar], [String]) to count... but only if value wasn't // 334
+ // itself an argument. // 335
+ if (_.isArray(value) || _.isArguments(value)) { // 336
+ _.each(value, _.bind(self._checkingOneValue, self)); // 337
+ } // 338
+ }, // 339
+ _checkingOneValue: function (value) { // 340
+ var self = this; // 341
+ for (var i = 0; i < self.args.length; ++i) { // 342
+ // Is this value one of the arguments? (This can have a false positive if // 343
+ // the argument is an interned primitive, but it's still a good enough // 344
+ // check.) // 345
+ // (NaN is not === to itself, so we have to check specially.) // 346
+ if (value === self.args[i] || (_.isNaN(value) && _.isNaN(self.args[i]))) { // 347
+ self.args.splice(i, 1); // 348
+ return true; // 349
+ } // 350
+ } // 351
+ return false; // 352
+ }, // 353
+ throwUnlessAllArgumentsHaveBeenChecked: function () { // 354
+ var self = this; // 355
+ if (!_.isEmpty(self.args)) // 356
+ throw new Error("Did not check() all arguments during " + // 357
+ self.description); // 358
+ } // 359
+}); // 360
+ // 361
+var _jsKeywords = ["do", "if", "in", "for", "let", "new", "try", "var", "case", // 362
+ "else", "enum", "eval", "false", "null", "this", "true", "void", "with", // 363
+ "break", "catch", "class", "const", "super", "throw", "while", "yield", // 364
+ "delete", "export", "import", "public", "return", "static", "switch", // 365
+ "typeof", "default", "extends", "finally", "package", "private", "continue", // 366
+ "debugger", "function", "arguments", "interface", "protected", "implements", // 367
+ "instanceof"]; // 368
+ // 369
+// Assumes the base of path is already escaped properly // 370
+// returns key + base // 371
+var _prependPath = function (key, base) { // 372
+ if ((typeof key) === "number" || key.match(/^[0-9]+$/)) // 373
+ key = "[" + key + "]"; // 374
+ else if (!key.match(/^[a-z_$][0-9a-z_$]*$/i) || _.contains(_jsKeywords, key)) // 375
+ key = JSON.stringify([key]); // 376
+ // 377
+ if (base && base[0] !== "[") // 378
+ return key + '.' + base; // 379
+ return key + base; // 380
+}; // 381
+ // 382
+ // 383
+///////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.check = {
+ check: check,
+ Match: Match
+};
+
+})();
+
+//# sourceMappingURL=check.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/check.js.map b/web-app/.meteor/local/build/programs/server/packages/check.js.map
new file mode 100644
index 0000000..ff2996d
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/check.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["check/match.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,W;;AAEA,uC;AACA,2B;;AAEA,4D;;AAEA,G;AACA,mE;AACA,kE;AACA,E;AACA,4E;AACA,uB;AACA,kB;AACA,wC;AACA,qD;AACA,kB;AACA,G;AACA,mC;AACA,qD;AACA,I;AACA,mE;AACA,0E;AACA,sE;AACA,wE;AACA,yE;AACA,8D;AACA,oE;AACA,iB;AACA,+B;AACA,O;AACA,iC;AACA,iB;AACA,iD;AACA,6C;AACA,c;AACA,G;AACA,E;;AAEA,G;AACA,mB;AACA,0D;AACA,G;AACA,S;AACA,gC;AACA,iC;AACA,I;AACA,mC;AACA,2C;AACA,I;AACA,mB;AACA,+B;AACA,gC;AACA,I;AACA,uC;AACA,wC;AACA,I;AACA,wC;AACA,yC;AACA,I;AACA,wC;AACA,2B;;AAEA,mE;AACA,6D;AACA,yC;AACA,6E;AACA,gF;AACA,a;AACA,qC;AACA,mB;AACA,gF;AACA,+D;AACA,gE;AACA,K;;AAEA,gF;AACA,8E;AACA,oD;AACA,gF;AACA,4E;AACA,+E;;AAEA,K;AACA,4D;AACA,oB;AACA,0C;AACA,uE;AACA,K;AACA,mC;AACA,S;AACA,mC;AACA,kB;AACA,iB;AACA,mC;AACA,qB;AACA,8B;AACA,c;AACA,K;AACA,I;;AAEA,gF;AACA,gF;AACA,0C;AACA,I;AACA,8E;AACA,4D;AACA,2E;AACA,oC;AACA,O;AACA,2E;AACA,wD;AACA,kB;AACA,G;AACA,E;;AAEA,mC;AACA,yB;AACA,E;;AAEA,gC;AACA,yB;AACA,uE;AACA,yB;AACA,E;;AAEA,kC;AACA,6B;AACA,E;;AAEA,0C;AACA,yB;AACA,E;;AAEA,2C;AACA,yB;AACA,E;;AAEA,oB;AACA,qB;AACA,qB;AACA,uB;AACA,uE;AACA,0B;AACA,0B;AACA,E;;AAEA,8C;AACA,oB;AACA,4B;AACA,W;;AAEA,wB;AACA,sD;AACA,iD;AACA,yC;AACA,8C;AACA,e;AACA,yE;AACA,0C;AACA,K;AACA,G;AACA,yB;AACA,uB;AACA,a;AACA,0E;AACA,G;;AAEA,sE;AACA,mE;AACA,0B;AACA,a;AACA,4D;AACA,kD;AACA,G;;AAEA,qD;AACA,kC;AACA,+E;AACA,gF;AACA,uE;AACA,kD;AACA,6E;AACA,oD;AACA,2D;AACA,Y;AACA,kD;AACA,8E;AACA,G;;AAEA,yD;AACA,yB;AACA,wC;;AAEA,iE;AACA,iC;AACA,6B;AACA,oE;AACA,4C;AACA,qD;AACA,6E;AACA,K;;AAEA,kD;AACA,W;AACA,+C;AACA,qB;AACA,yC;AACA,mD;AACA,S;AACA,kB;AACA,O;AACA,O;AACA,W;AACA,G;;AAEA,2E;AACA,6D;AACA,iC;AACA,iC;AACA,a;AACA,iC;AACA,2D;AACA,G;;;AAGA,kC;AACA,sD;;AAEA,iC;AACA,sD;AACA,W;AACA,gD;AACA,iC;AACA,e;AACA,qB;AACA,4E;AACA,kB;AACA,0C;AACA,oB;AACA,O;AACA,K;AACA,iC;AACA,6E;AACA,G;;AAEA,uE;AACA,iB;AACA,oC;AACA,iC;AACA,a;AACA,wD;AACA,oE;AACA,G;;AAEA,iC;AACA,wB;AACA,2C;AACA,8B;AACA,8B;AACA,G;AACA,4C;AACA,8B;AACA,0C;AACA,sC;AACA,G;;AAEA,kC;AACA,qD;;AAEA,2E;AACA,6E;AACA,+D;AACA,gC;AACA,kE;AACA,qB;AACA,uD;AACA,mC;AACA,mD;;AAEA,4B;AACA,4B;AACA,8C;AACA,uC;AACA,iD;AACA,Q;AACA,yC;AACA,K;;AAEA,0C;AACA,S;AACA,yC;AACA,sD;AACA,qC;AACA,gD;AACA,sD;AACA,c;AACA,gC;AACA,+C;AACA,gC;AACA,uD;AACA,S;AACA,O;AACA,mB;AACA,qC;AACA,+C;AACA,gB;AACA,K;AACA,K;;AAEA,uD;AACA,uD;AACA,K;AACA,E;;AAEA,oD;AACA,kB;AACA,0E;AACA,2B;AACA,4B;AACA,6E;AACA,4E;AACA,+B;AACA,sB;AACA,iC;AACA,E;;AAEA,qC;AACA,8B;AACA,oB;AACA,sC;AACA,a;AACA,8E;AACA,0E;AACA,0B;AACA,mD;AACA,0D;AACA,K;AACA,I;AACA,uC;AACA,oB;AACA,gD;AACA,+E;AACA,4E;AACA,gB;AACA,mE;AACA,gF;AACA,+B;AACA,oB;AACA,O;AACA,K;AACA,iB;AACA,I;AACA,uD;AACA,oB;AACA,8B;AACA,+D;AACA,wC;AACA,G;AACA,G;;AAEA,+E;AACA,0E;AACA,yE;AACA,uE;AACA,8E;AACA,8E;AACA,gB;;AAEA,uD;AACA,qB;AACA,yC;AACA,yD;AACA,0B;AACA,+E;AACA,gC;;AAEA,8B;AACA,4B;AACA,oB;AACA,E","file":"/packages/check.js","sourcesContent":["// XXX docs\n\n// Things we explicitly do NOT support:\n// - heterogenous arrays\n\nvar currentArgumentChecker = new Meteor.EnvironmentVariable;\n\n/**\n * @summary Check that a value matches a [pattern](#matchpatterns).\n * If the value does not match the pattern, throw a `Match.Error`.\n *\n * Particularly useful to assert that arguments to a function have the right\n * types and structure.\n * @locus Anywhere\n * @param {Any} value The value to check\n * @param {MatchPattern} pattern The pattern to match\n * `value` against\n */\ncheck = function (value, pattern) {\n // Record that check got called, if somebody cared.\n //\n // We use getOrNullIfOutsideFiber so that it's OK to call check()\n // from non-Fiber server contexts; the downside is that if you forget to\n // bindEnvironment on some random callback in your method/publisher,\n // it might not find the argumentChecker and you'll get an error about\n // not checking an argument that it looks like you're checking (instead\n // of just getting a \"Node code must run in a Fiber\" error).\n var argChecker = currentArgumentChecker.getOrNullIfOutsideFiber();\n if (argChecker)\n argChecker.checking(value);\n try {\n checkSubtree(value, pattern);\n } catch (err) {\n if ((err instanceof Match.Error) && err.path)\n err.message += \" in field \" + err.path;\n throw err;\n }\n};\n\n/**\n * @namespace Match\n * @summary The namespace for all Match types and methods.\n */\nMatch = {\n Optional: function (pattern) {\n return new Optional(pattern);\n },\n OneOf: function (/*arguments*/) {\n return new OneOf(_.toArray(arguments));\n },\n Any: ['__any__'],\n Where: function (condition) {\n return new Where(condition);\n },\n ObjectIncluding: function (pattern) {\n return new ObjectIncluding(pattern);\n },\n ObjectWithValues: function (pattern) {\n return new ObjectWithValues(pattern);\n },\n // Matches only signed 32-bit integers\n Integer: ['__integer__'],\n\n // XXX matchers should know how to describe themselves for errors\n Error: Meteor.makeErrorType(\"Match.Error\", function (msg) {\n this.message = \"Match error: \" + msg;\n // The path of the value that failed to match. Initially empty, this gets\n // populated by catching and rethrowing the exception as it goes back up the\n // stack.\n // E.g.: \"vals[3].entity.created\"\n this.path = \"\";\n // If this gets sent over DDP, don't give full internal details but at least\n // provide something better than 500 Internal server error.\n this.sanitizedError = new Meteor.Error(400, \"Match failed\");\n }),\n\n // Tests to see if value matches pattern. Unlike check, it merely returns true\n // or false (unless an error other than Match.Error was thrown). It does not\n // interact with _failIfArgumentsAreNotAllChecked.\n // XXX maybe also implement a Match.match which returns more information about\n // failures but without using exception handling or doing what check()\n // does with _failIfArgumentsAreNotAllChecked and Meteor.Error conversion\n\n /**\n * @summary Returns true if the value matches the pattern.\n * @locus Anywhere\n * @param {Any} value The value to check\n * @param {MatchPattern} pattern The pattern to match `value` against\n */\n test: function (value, pattern) {\n try {\n checkSubtree(value, pattern);\n return true;\n } catch (e) {\n if (e instanceof Match.Error)\n return false;\n // Rethrow other errors.\n throw e;\n }\n },\n\n // Runs `f.apply(context, args)`. If check() is not called on every element of\n // `args` (either directly or in the first level of an array), throws an error\n // (using `description` in the message).\n //\n _failIfArgumentsAreNotAllChecked: function (f, context, args, description) {\n var argChecker = new ArgumentChecker(args, description);\n var result = currentArgumentChecker.withValue(argChecker, function () {\n return f.apply(context, args);\n });\n // If f didn't itself throw, make sure it checked all of its arguments.\n argChecker.throwUnlessAllArgumentsHaveBeenChecked();\n return result;\n }\n};\n\nvar Optional = function (pattern) {\n this.pattern = pattern;\n};\n\nvar OneOf = function (choices) {\n if (_.isEmpty(choices))\n throw new Error(\"Must provide at least one choice to Match.OneOf\");\n this.choices = choices;\n};\n\nvar Where = function (condition) {\n this.condition = condition;\n};\n\nvar ObjectIncluding = function (pattern) {\n this.pattern = pattern;\n};\n\nvar ObjectWithValues = function (pattern) {\n this.pattern = pattern;\n};\n\nvar typeofChecks = [\n [String, \"string\"],\n [Number, \"number\"],\n [Boolean, \"boolean\"],\n // While we don't allow undefined in EJSON, this is good for optional\n // arguments with OneOf.\n [undefined, \"undefined\"]\n];\n\nvar checkSubtree = function (value, pattern) {\n // Match anything!\n if (pattern === Match.Any)\n return;\n\n // Basic atomic types.\n // Do not match boxed objects (e.g. String, Boolean)\n for (var i = 0; i < typeofChecks.length; ++i) {\n if (pattern === typeofChecks[i][0]) {\n if (typeof value === typeofChecks[i][1])\n return;\n throw new Match.Error(\"Expected \" + typeofChecks[i][1] + \", got \" +\n typeof value);\n }\n }\n if (pattern === null) {\n if (value === null)\n return;\n throw new Match.Error(\"Expected null, got \" + EJSON.stringify(value));\n }\n\n // Strings and numbers match literally. Goes well with Match.OneOf.\n if (typeof pattern === \"string\" || typeof pattern === \"number\") {\n if (value === pattern)\n return;\n throw new Match.Error(\"Expected \" + pattern + \", got \" +\n EJSON.stringify(value));\n }\n\n // Match.Integer is special type encoded with array\n if (pattern === Match.Integer) {\n // There is no consistent and reliable way to check if variable is a 64-bit\n // integer. One of the popular solutions is to get reminder of division by 1\n // but this method fails on really large floats with big precision.\n // E.g.: 1.348192308491824e+23 % 1 === 0 in V8\n // Bitwise operators work consistantly but always cast variable to 32-bit\n // signed integer according to JavaScript specs.\n if (typeof value === \"number\" && (value | 0) === value)\n return\n throw new Match.Error(\"Expected Integer, got \"\n + (value instanceof Object ? EJSON.stringify(value) : value));\n }\n\n // \"Object\" is shorthand for Match.ObjectIncluding({});\n if (pattern === Object)\n pattern = Match.ObjectIncluding({});\n\n // Array (checked AFTER Any, which is implemented as an Array).\n if (pattern instanceof Array) {\n if (pattern.length !== 1)\n throw Error(\"Bad pattern: arrays must have one type element\" +\n EJSON.stringify(pattern));\n if (!_.isArray(value) && !_.isArguments(value)) {\n throw new Match.Error(\"Expected array, got \" + EJSON.stringify(value));\n }\n\n _.each(value, function (valueElement, index) {\n try {\n checkSubtree(valueElement, pattern[0]);\n } catch (err) {\n if (err instanceof Match.Error) {\n err.path = _prependPath(index, err.path);\n }\n throw err;\n }\n });\n return;\n }\n\n // Arbitrary validation checks. The condition can return false or throw a\n // Match.Error (ie, it can internally use check()) to fail.\n if (pattern instanceof Where) {\n if (pattern.condition(value))\n return;\n // XXX this error is terrible\n throw new Match.Error(\"Failed Match.Where validation\");\n }\n\n\n if (pattern instanceof Optional)\n pattern = Match.OneOf(undefined, pattern.pattern);\n\n if (pattern instanceof OneOf) {\n for (var i = 0; i < pattern.choices.length; ++i) {\n try {\n checkSubtree(value, pattern.choices[i]);\n // No error? Yay, return.\n return;\n } catch (err) {\n // Other errors should be thrown. Match errors just mean try another\n // choice.\n if (!(err instanceof Match.Error))\n throw err;\n }\n }\n // XXX this error is terrible\n throw new Match.Error(\"Failed Match.OneOf or Match.Optional validation\");\n }\n\n // A function that isn't something we special-case is assumed to be a\n // constructor.\n if (pattern instanceof Function) {\n if (value instanceof pattern)\n return;\n throw new Match.Error(\"Expected \" + (pattern.name ||\n \"particular constructor\"));\n }\n\n var unknownKeysAllowed = false;\n var unknownKeyPattern;\n if (pattern instanceof ObjectIncluding) {\n unknownKeysAllowed = true;\n pattern = pattern.pattern;\n }\n if (pattern instanceof ObjectWithValues) {\n unknownKeysAllowed = true;\n unknownKeyPattern = [pattern.pattern];\n pattern = {}; // no required keys\n }\n\n if (typeof pattern !== \"object\")\n throw Error(\"Bad pattern: unknown pattern type\");\n\n // An object, with required and optional keys. Note that this does NOT do\n // structural matches against objects of special types that happen to match\n // the pattern: this really needs to be a plain old {Object}!\n if (typeof value !== 'object')\n throw new Match.Error(\"Expected object, got \" + typeof value);\n if (value === null)\n throw new Match.Error(\"Expected object, got null\");\n if (value.constructor !== Object)\n throw new Match.Error(\"Expected plain object\");\n\n var requiredPatterns = {};\n var optionalPatterns = {};\n _.each(pattern, function (subPattern, key) {\n if (subPattern instanceof Optional)\n optionalPatterns[key] = subPattern.pattern;\n else\n requiredPatterns[key] = subPattern;\n });\n\n _.each(value, function (subValue, key) {\n try {\n if (_.has(requiredPatterns, key)) {\n checkSubtree(subValue, requiredPatterns[key]);\n delete requiredPatterns[key];\n } else if (_.has(optionalPatterns, key)) {\n checkSubtree(subValue, optionalPatterns[key]);\n } else {\n if (!unknownKeysAllowed)\n throw new Match.Error(\"Unknown key\");\n if (unknownKeyPattern) {\n checkSubtree(subValue, unknownKeyPattern[0]);\n }\n }\n } catch (err) {\n if (err instanceof Match.Error)\n err.path = _prependPath(key, err.path);\n throw err;\n }\n });\n\n _.each(requiredPatterns, function (subPattern, key) {\n throw new Match.Error(\"Missing key '\" + key + \"'\");\n });\n};\n\nvar ArgumentChecker = function (args, description) {\n var self = this;\n // Make a SHALLOW copy of the arguments. (We'll be doing identity checks\n // against its contents.)\n self.args = _.clone(args);\n // Since the common case will be to check arguments in order, and we splice\n // out arguments when we check them, make it so we splice out from the end\n // rather than the beginning.\n self.args.reverse();\n self.description = description;\n};\n\n_.extend(ArgumentChecker.prototype, {\n checking: function (value) {\n var self = this;\n if (self._checkingOneValue(value))\n return;\n // Allow check(arguments, [String]) or check(arguments.slice(1), [String])\n // or check([foo, bar], [String]) to count... but only if value wasn't\n // itself an argument.\n if (_.isArray(value) || _.isArguments(value)) {\n _.each(value, _.bind(self._checkingOneValue, self));\n }\n },\n _checkingOneValue: function (value) {\n var self = this;\n for (var i = 0; i < self.args.length; ++i) {\n // Is this value one of the arguments? (This can have a false positive if\n // the argument is an interned primitive, but it's still a good enough\n // check.)\n // (NaN is not === to itself, so we have to check specially.)\n if (value === self.args[i] || (_.isNaN(value) && _.isNaN(self.args[i]))) {\n self.args.splice(i, 1);\n return true;\n }\n }\n return false;\n },\n throwUnlessAllArgumentsHaveBeenChecked: function () {\n var self = this;\n if (!_.isEmpty(self.args))\n throw new Error(\"Did not check() all arguments during \" +\n self.description);\n }\n});\n\nvar _jsKeywords = [\"do\", \"if\", \"in\", \"for\", \"let\", \"new\", \"try\", \"var\", \"case\",\n \"else\", \"enum\", \"eval\", \"false\", \"null\", \"this\", \"true\", \"void\", \"with\",\n \"break\", \"catch\", \"class\", \"const\", \"super\", \"throw\", \"while\", \"yield\",\n \"delete\", \"export\", \"import\", \"public\", \"return\", \"static\", \"switch\",\n \"typeof\", \"default\", \"extends\", \"finally\", \"package\", \"private\", \"continue\",\n \"debugger\", \"function\", \"arguments\", \"interface\", \"protected\", \"implements\",\n \"instanceof\"];\n\n// Assumes the base of path is already escaped properly\n// returns key + base\nvar _prependPath = function (key, base) {\n if ((typeof key) === \"number\" || key.match(/^[0-9]+$/))\n key = \"[\" + key + \"]\";\n else if (!key.match(/^[a-z_$][0-9a-z_$]*$/i) || _.contains(_jsKeywords, key))\n key = JSON.stringify([key]);\n\n if (base && base[0] !== \"[\")\n return key + '.' + base;\n return key + base;\n};\n\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js b/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js
new file mode 100644
index 0000000..727a99c
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js
@@ -0,0 +1,14 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['d3js:d3'] = {};
+
+})();
+
+//# sourceMappingURL=d3js_d3.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js.map b/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js.map
new file mode 100644
index 0000000..11ecca8
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/d3js_d3.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":[],"names":[],"mappings":";;;;;","file":"/packages/d3js:d3.js"}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/ddp.js b/web-app/.meteor/local/build/programs/server/packages/ddp.js
new file mode 100644
index 0000000..9ed6004
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/ddp.js
@@ -0,0 +1,4781 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var check = Package.check.check;
+var Match = Package.check.Match;
+var Random = Package.random.Random;
+var EJSON = Package.ejson.EJSON;
+var _ = Package.underscore._;
+var Tracker = Package.tracker.Tracker;
+var Deps = Package.tracker.Deps;
+var Log = Package.logging.Log;
+var Retry = Package.retry.Retry;
+var Hook = Package['callback-hook'].Hook;
+var LocalCollection = Package.minimongo.LocalCollection;
+var Minimongo = Package.minimongo.Minimongo;
+
+/* Package-scope variables */
+var DDP, DDPServer, LivedataTest, toSockjsUrl, toWebsocketUrl, StreamServer, Heartbeat, Server, SUPPORTED_DDP_VERSIONS, MethodInvocation, parseDDP, stringifyDDP, RandomStream, makeRpcSeed, allConnections;
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/common.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+/** // 1
+ * @namespace DDP // 2
+ * @summary The namespace for DDP-related methods. // 3
+ */ // 4
+DDP = {}; // 5
+LivedataTest = {}; // 6
+ // 7
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/stream_client_nodejs.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// @param endpoint {String} URL to Meteor app // 1
+// "http://subdomain.meteor.com/" or "/" or // 2
+// "ddp+sockjs://foo-**.meteor.com/sockjs" // 3
+// // 4
+// We do some rewriting of the URL to eventually make it "ws://" or "wss://", // 5
+// whatever was passed in. At the very least, what Meteor.absoluteUrl() returns // 6
+// us should work. // 7
+// // 8
+// We don't do any heartbeating. (The logic that did this in sockjs was removed, // 9
+// because it used a built-in sockjs mechanism. We could do it with WebSocket // 10
+// ping frames or with DDP-level messages.) // 11
+LivedataTest.ClientStream = function (endpoint, options) { // 12
+ var self = this; // 13
+ options = options || {}; // 14
+ // 15
+ self.options = _.extend({ // 16
+ retry: true // 17
+ }, options); // 18
+ // 19
+ self.client = null; // created in _launchConnection // 20
+ self.endpoint = endpoint; // 21
+ // 22
+ self.headers = self.options.headers || {}; // 23
+ // 24
+ self._initCommon(self.options); // 25
+ // 26
+ //// Kickoff! // 27
+ self._launchConnection(); // 28
+}; // 29
+ // 30
+_.extend(LivedataTest.ClientStream.prototype, { // 31
+ // 32
+ // data is a utf8 string. Data sent while not connected is dropped on // 33
+ // the floor, and it is up the user of this API to retransmit lost // 34
+ // messages on 'reset' // 35
+ send: function (data) { // 36
+ var self = this; // 37
+ if (self.currentStatus.connected) { // 38
+ self.client.send(data); // 39
+ } // 40
+ }, // 41
+ // 42
+ // Changes where this connection points // 43
+ _changeUrl: function (url) { // 44
+ var self = this; // 45
+ self.endpoint = url; // 46
+ }, // 47
+ // 48
+ _onConnect: function (client) { // 49
+ var self = this; // 50
+ // 51
+ if (client !== self.client) { // 52
+ // This connection is not from the last call to _launchConnection. // 53
+ // But _launchConnection calls _cleanup which closes previous connections. // 54
+ // It's our belief that this stifles future 'open' events, but maybe // 55
+ // we are wrong? // 56
+ throw new Error("Got open from inactive client " + !!self.client); // 57
+ } // 58
+ // 59
+ if (self._forcedToDisconnect) { // 60
+ // We were asked to disconnect between trying to open the connection and // 61
+ // actually opening it. Let's just pretend this never happened. // 62
+ self.client.close(); // 63
+ self.client = null; // 64
+ return; // 65
+ } // 66
+ // 67
+ if (self.currentStatus.connected) { // 68
+ // We already have a connection. It must have been the case that we // 69
+ // started two parallel connection attempts (because we wanted to // 70
+ // 'reconnect now' on a hanging connection and we had no way to cancel the // 71
+ // connection attempt.) But this shouldn't happen (similarly to the client // 72
+ // !== self.client check above). // 73
+ throw new Error("Two parallel connections?"); // 74
+ } // 75
+ // 76
+ self._clearConnectionTimer(); // 77
+ // 78
+ // update status // 79
+ self.currentStatus.status = "connected"; // 80
+ self.currentStatus.connected = true; // 81
+ self.currentStatus.retryCount = 0; // 82
+ self.statusChanged(); // 83
+ // 84
+ // fire resets. This must come after status change so that clients // 85
+ // can call send from within a reset callback. // 86
+ _.each(self.eventCallbacks.reset, function (callback) { callback(); }); // 87
+ }, // 88
+ // 89
+ _cleanup: function (maybeError) { // 90
+ var self = this; // 91
+ // 92
+ self._clearConnectionTimer(); // 93
+ if (self.client) { // 94
+ var client = self.client; // 95
+ self.client = null; // 96
+ client.close(); // 97
+ // 98
+ _.each(self.eventCallbacks.disconnect, function (callback) { // 99
+ callback(maybeError); // 100
+ }); // 101
+ } // 102
+ }, // 103
+ // 104
+ _clearConnectionTimer: function () { // 105
+ var self = this; // 106
+ // 107
+ if (self.connectionTimer) { // 108
+ clearTimeout(self.connectionTimer); // 109
+ self.connectionTimer = null; // 110
+ } // 111
+ }, // 112
+ // 113
+ _getProxyUrl: function (targetUrl) { // 114
+ var self = this; // 115
+ // Similar to code in tools/http-helpers.js. // 116
+ var proxy = process.env.HTTP_PROXY || process.env.http_proxy || null; // 117
+ // if we're going to a secure url, try the https_proxy env variable first. // 118
+ if (targetUrl.match(/^wss:/)) { // 119
+ proxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy; // 120
+ } // 121
+ return proxy; // 122
+ }, // 123
+ // 124
+ _launchConnection: function () { // 125
+ var self = this; // 126
+ self._cleanup(); // cleanup the old socket, if there was one. // 127
+ // 128
+ // Since server-to-server DDP is still an experimental feature, we only // 129
+ // require the module if we actually create a server-to-server // 130
+ // connection. // 131
+ var FayeWebSocket = Npm.require('faye-websocket'); // 132
+ // 133
+ var targetUrl = toWebsocketUrl(self.endpoint); // 134
+ var fayeOptions = { headers: self.headers }; // 135
+ var proxyUrl = self._getProxyUrl(targetUrl); // 136
+ if (proxyUrl) { // 137
+ fayeOptions.proxy = { origin: proxyUrl }; // 138
+ }; // 139
+ // 140
+ // We would like to specify 'ddp' as the subprotocol here. The npm module we // 141
+ // used to use as a client would fail the handshake if we ask for a // 142
+ // subprotocol and the server doesn't send one back (and sockjs doesn't). // 143
+ // Faye doesn't have that behavior; it's unclear from reading RFC 6455 if // 144
+ // Faye is erroneous or not. So for now, we don't specify protocols. // 145
+ var subprotocols = []; // 146
+ // 147
+ var client = self.client = new FayeWebSocket.Client( // 148
+ targetUrl, subprotocols, fayeOptions); // 149
+ // 150
+ self._clearConnectionTimer(); // 151
+ self.connectionTimer = Meteor.setTimeout( // 152
+ function () { // 153
+ self._lostConnection( // 154
+ new DDP.ConnectionError("DDP connection timed out")); // 155
+ }, // 156
+ self.CONNECT_TIMEOUT); // 157
+ // 158
+ self.client.on('open', Meteor.bindEnvironment(function () { // 159
+ return self._onConnect(client); // 160
+ }, "stream connect callback")); // 161
+ // 162
+ var clientOnIfCurrent = function (event, description, f) { // 163
+ self.client.on(event, Meteor.bindEnvironment(function () { // 164
+ // Ignore events from any connection we've already cleaned up. // 165
+ if (client !== self.client) // 166
+ return; // 167
+ f.apply(this, arguments); // 168
+ }, description)); // 169
+ }; // 170
+ // 171
+ clientOnIfCurrent('error', 'stream error callback', function (error) { // 172
+ if (!self.options._dontPrintErrors) // 173
+ Meteor._debug("stream error", error.message); // 174
+ // 175
+ // Faye's 'error' object is not a JS error (and among other things, // 176
+ // doesn't stringify well). Convert it to one. // 177
+ self._lostConnection(new DDP.ConnectionError(error.message)); // 178
+ }); // 179
+ // 180
+ // 181
+ clientOnIfCurrent('close', 'stream close callback', function () { // 182
+ self._lostConnection(); // 183
+ }); // 184
+ // 185
+ // 186
+ clientOnIfCurrent('message', 'stream message callback', function (message) { // 187
+ // Ignore binary frames, where message.data is a Buffer // 188
+ if (typeof message.data !== "string") // 189
+ return; // 190
+ // 191
+ _.each(self.eventCallbacks.message, function (callback) { // 192
+ callback(message.data); // 193
+ }); // 194
+ }); // 195
+ } // 196
+}); // 197
+ // 198
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/stream_client_common.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// XXX from Underscore.String (http://epeli.github.com/underscore.string/) // 1
+var startsWith = function(str, starts) { // 2
+ return str.length >= starts.length && // 3
+ str.substring(0, starts.length) === starts; // 4
+}; // 5
+var endsWith = function(str, ends) { // 6
+ return str.length >= ends.length && // 7
+ str.substring(str.length - ends.length) === ends; // 8
+}; // 9
+ // 10
+// @param url {String} URL to Meteor app, eg: // 11
+// "/" or "madewith.meteor.com" or "https://foo.meteor.com" // 12
+// or "ddp+sockjs://ddp--****-foo.meteor.com/sockjs" // 13
+// @returns {String} URL to the endpoint with the specific scheme and subPath, e.g. // 14
+// for scheme "http" and subPath "sockjs" // 15
+// "http://subdomain.meteor.com/sockjs" or "/sockjs" // 16
+// or "https://ddp--1234-foo.meteor.com/sockjs" // 17
+var translateUrl = function(url, newSchemeBase, subPath) { // 18
+ if (! newSchemeBase) { // 19
+ newSchemeBase = "http"; // 20
+ } // 21
+ // 22
+ var ddpUrlMatch = url.match(/^ddp(i?)\+sockjs:\/\//); // 23
+ var httpUrlMatch = url.match(/^http(s?):\/\//); // 24
+ var newScheme; // 25
+ if (ddpUrlMatch) { // 26
+ // Remove scheme and split off the host. // 27
+ var urlAfterDDP = url.substr(ddpUrlMatch[0].length); // 28
+ newScheme = ddpUrlMatch[1] === "i" ? newSchemeBase : newSchemeBase + "s"; // 29
+ var slashPos = urlAfterDDP.indexOf('/'); // 30
+ var host = // 31
+ slashPos === -1 ? urlAfterDDP : urlAfterDDP.substr(0, slashPos); // 32
+ var rest = slashPos === -1 ? '' : urlAfterDDP.substr(slashPos); // 33
+ // 34
+ // In the host (ONLY!), change '*' characters into random digits. This // 35
+ // allows different stream connections to connect to different hostnames // 36
+ // and avoid browser per-hostname connection limits. // 37
+ host = host.replace(/\*/g, function () { // 38
+ return Math.floor(Random.fraction()*10); // 39
+ }); // 40
+ // 41
+ return newScheme + '://' + host + rest; // 42
+ } else if (httpUrlMatch) { // 43
+ newScheme = !httpUrlMatch[1] ? newSchemeBase : newSchemeBase + "s"; // 44
+ var urlAfterHttp = url.substr(httpUrlMatch[0].length); // 45
+ url = newScheme + "://" + urlAfterHttp; // 46
+ } // 47
+ // 48
+ // Prefix FQDNs but not relative URLs // 49
+ if (url.indexOf("://") === -1 && !startsWith(url, "/")) { // 50
+ url = newSchemeBase + "://" + url; // 51
+ } // 52
+ // 53
+ // XXX This is not what we should be doing: if I have a site // 54
+ // deployed at "/foo", then DDP.connect("/") should actually connect // 55
+ // to "/", not to "/foo". "/" is an absolute path. (Contrast: if // 56
+ // deployed at "/foo", it would be reasonable for DDP.connect("bar") // 57
+ // to connect to "/foo/bar"). // 58
+ // // 59
+ // We should make this properly honor absolute paths rather than // 60
+ // forcing the path to be relative to the site root. Simultaneously, // 61
+ // we should set DDP_DEFAULT_CONNECTION_URL to include the site // 62
+ // root. See also client_convenience.js #RationalizingRelativeDDPURLs // 63
+ url = Meteor._relativeToSiteRootUrl(url); // 64
+ // 65
+ if (endsWith(url, "/")) // 66
+ return url + subPath; // 67
+ else // 68
+ return url + "/" + subPath; // 69
+}; // 70
+ // 71
+toSockjsUrl = function (url) { // 72
+ return translateUrl(url, "http", "sockjs"); // 73
+}; // 74
+ // 75
+toWebsocketUrl = function (url) { // 76
+ var ret = translateUrl(url, "ws", "websocket"); // 77
+ return ret; // 78
+}; // 79
+ // 80
+LivedataTest.toSockjsUrl = toSockjsUrl; // 81
+ // 82
+ // 83
+_.extend(LivedataTest.ClientStream.prototype, { // 84
+ // 85
+ // Register for callbacks. // 86
+ on: function (name, callback) { // 87
+ var self = this; // 88
+ // 89
+ if (name !== 'message' && name !== 'reset' && name !== 'disconnect') // 90
+ throw new Error("unknown event type: " + name); // 91
+ // 92
+ if (!self.eventCallbacks[name]) // 93
+ self.eventCallbacks[name] = []; // 94
+ self.eventCallbacks[name].push(callback); // 95
+ }, // 96
+ // 97
+ // 98
+ _initCommon: function (options) { // 99
+ var self = this; // 100
+ options = options || {}; // 101
+ // 102
+ //// Constants // 103
+ // 104
+ // how long to wait until we declare the connection attempt // 105
+ // failed. // 106
+ self.CONNECT_TIMEOUT = options.connectTimeoutMs || 10000; // 107
+ // 108
+ self.eventCallbacks = {}; // name -> [callback] // 109
+ // 110
+ self._forcedToDisconnect = false; // 111
+ // 112
+ //// Reactive status // 113
+ self.currentStatus = { // 114
+ status: "connecting", // 115
+ connected: false, // 116
+ retryCount: 0 // 117
+ }; // 118
+ // 119
+ // 120
+ self.statusListeners = typeof Tracker !== 'undefined' && new Tracker.Dependency; // 121
+ self.statusChanged = function () { // 122
+ if (self.statusListeners) // 123
+ self.statusListeners.changed(); // 124
+ }; // 125
+ // 126
+ //// Retry logic // 127
+ self._retry = new Retry; // 128
+ self.connectionTimer = null; // 129
+ // 130
+ }, // 131
+ // 132
+ // Trigger a reconnect. // 133
+ reconnect: function (options) { // 134
+ var self = this; // 135
+ options = options || {}; // 136
+ // 137
+ if (options.url) { // 138
+ self._changeUrl(options.url); // 139
+ } // 140
+ // 141
+ if (options._sockjsOptions) { // 142
+ self.options._sockjsOptions = options._sockjsOptions; // 143
+ } // 144
+ // 145
+ if (self.currentStatus.connected) { // 146
+ if (options._force || options.url) { // 147
+ // force reconnect. // 148
+ self._lostConnection(new DDP.ForcedReconnectError); // 149
+ } // else, noop. // 150
+ return; // 151
+ } // 152
+ // 153
+ // if we're mid-connection, stop it. // 154
+ if (self.currentStatus.status === "connecting") { // 155
+ // Pretend it's a clean close. // 156
+ self._lostConnection(); // 157
+ } // 158
+ // 159
+ self._retry.clear(); // 160
+ self.currentStatus.retryCount -= 1; // don't count manual retries // 161
+ self._retryNow(); // 162
+ }, // 163
+ // 164
+ disconnect: function (options) { // 165
+ var self = this; // 166
+ options = options || {}; // 167
+ // 168
+ // Failed is permanent. If we're failed, don't let people go back // 169
+ // online by calling 'disconnect' then 'reconnect'. // 170
+ if (self._forcedToDisconnect) // 171
+ return; // 172
+ // 173
+ // If _permanent is set, permanently disconnect a stream. Once a stream // 174
+ // is forced to disconnect, it can never reconnect. This is for // 175
+ // error cases such as ddp version mismatch, where trying again // 176
+ // won't fix the problem. // 177
+ if (options._permanent) { // 178
+ self._forcedToDisconnect = true; // 179
+ } // 180
+ // 181
+ self._cleanup(); // 182
+ self._retry.clear(); // 183
+ // 184
+ self.currentStatus = { // 185
+ status: (options._permanent ? "failed" : "offline"), // 186
+ connected: false, // 187
+ retryCount: 0 // 188
+ }; // 189
+ // 190
+ if (options._permanent && options._error) // 191
+ self.currentStatus.reason = options._error; // 192
+ // 193
+ self.statusChanged(); // 194
+ }, // 195
+ // 196
+ // maybeError is set unless it's a clean protocol-level close. // 197
+ _lostConnection: function (maybeError) { // 198
+ var self = this; // 199
+ // 200
+ self._cleanup(maybeError); // 201
+ self._retryLater(maybeError); // sets status. no need to do it here. // 202
+ }, // 203
+ // 204
+ // fired when we detect that we've gone online. try to reconnect // 205
+ // immediately. // 206
+ _online: function () { // 207
+ // if we've requested to be offline by disconnecting, don't reconnect. // 208
+ if (this.currentStatus.status != "offline") // 209
+ this.reconnect(); // 210
+ }, // 211
+ // 212
+ _retryLater: function (maybeError) { // 213
+ var self = this; // 214
+ // 215
+ var timeout = 0; // 216
+ if (self.options.retry || // 217
+ (maybeError && maybeError.errorType === "DDP.ForcedReconnectError")) { // 218
+ timeout = self._retry.retryLater( // 219
+ self.currentStatus.retryCount, // 220
+ _.bind(self._retryNow, self) // 221
+ ); // 222
+ self.currentStatus.status = "waiting"; // 223
+ self.currentStatus.retryTime = (new Date()).getTime() + timeout; // 224
+ } else { // 225
+ self.currentStatus.status = "failed"; // 226
+ delete self.currentStatus.retryTime; // 227
+ } // 228
+ // 229
+ self.currentStatus.connected = false; // 230
+ self.statusChanged(); // 231
+ }, // 232
+ // 233
+ _retryNow: function () { // 234
+ var self = this; // 235
+ // 236
+ if (self._forcedToDisconnect) // 237
+ return; // 238
+ // 239
+ self.currentStatus.retryCount += 1; // 240
+ self.currentStatus.status = "connecting"; // 241
+ self.currentStatus.connected = false; // 242
+ delete self.currentStatus.retryTime; // 243
+ self.statusChanged(); // 244
+ // 245
+ self._launchConnection(); // 246
+ }, // 247
+ // 248
+ // 249
+ // Get current status. Reactive. // 250
+ status: function () { // 251
+ var self = this; // 252
+ if (self.statusListeners) // 253
+ self.statusListeners.depend(); // 254
+ return self.currentStatus; // 255
+ } // 256
+}); // 257
+ // 258
+DDP.ConnectionError = Meteor.makeErrorType( // 259
+ "DDP.ConnectionError", function (message) { // 260
+ var self = this; // 261
+ self.message = message; // 262
+}); // 263
+ // 264
+DDP.ForcedReconnectError = Meteor.makeErrorType( // 265
+ "DDP.ForcedReconnectError", function () {}); // 266
+ // 267
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/stream_server.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+var url = Npm.require('url'); // 1
+ // 2
+var pathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || ""; // 3
+ // 4
+StreamServer = function () { // 5
+ var self = this; // 6
+ self.registration_callbacks = []; // 7
+ self.open_sockets = []; // 8
+ // 9
+ // Because we are installing directly onto WebApp.httpServer instead of using // 10
+ // WebApp.app, we have to process the path prefix ourselves. // 11
+ self.prefix = pathPrefix + '/sockjs'; // 12
+ // routepolicy is only a weak dependency, because we don't need it if we're // 13
+ // just doing server-to-server DDP as a client. // 14
+ if (Package.routepolicy) { // 15
+ Package.routepolicy.RoutePolicy.declare(self.prefix + '/', 'network'); // 16
+ } // 17
+ // 18
+ // set up sockjs // 19
+ var sockjs = Npm.require('sockjs'); // 20
+ var serverOptions = { // 21
+ prefix: self.prefix, // 22
+ log: function() {}, // 23
+ // this is the default, but we code it explicitly because we depend // 24
+ // on it in stream_client:HEARTBEAT_TIMEOUT // 25
+ heartbeat_delay: 45000, // 26
+ // The default disconnect_delay is 5 seconds, but if the server ends up CPU // 27
+ // bound for that much time, SockJS might not notice that the user has // 28
+ // reconnected because the timer (of disconnect_delay ms) can fire before // 29
+ // SockJS processes the new connection. Eventually we'll fix this by not // 30
+ // combining CPU-heavy processing with SockJS termination (eg a proxy which // 31
+ // converts to Unix sockets) but for now, raise the delay. // 32
+ disconnect_delay: 60 * 1000, // 33
+ // Set the USE_JSESSIONID environment variable to enable setting the // 34
+ // JSESSIONID cookie. This is useful for setting up proxies with // 35
+ // session affinity. // 36
+ jsessionid: !!process.env.USE_JSESSIONID // 37
+ }; // 38
+ // 39
+ // If you know your server environment (eg, proxies) will prevent websockets // 40
+ // from ever working, set $DISABLE_WEBSOCKETS and SockJS clients (ie, // 41
+ // browsers) will not waste time attempting to use them. // 42
+ // (Your server will still have a /websocket endpoint.) // 43
+ if (process.env.DISABLE_WEBSOCKETS) // 44
+ serverOptions.websocket = false; // 45
+ // 46
+ self.server = sockjs.createServer(serverOptions); // 47
+ if (!Package.webapp) { // 48
+ throw new Error("Cannot create a DDP server without the webapp package"); // 49
+ } // 50
+ // Install the sockjs handlers, but we want to keep around our own particular // 51
+ // request handler that adjusts idle timeouts while we have an outstanding // 52
+ // request. This compensates for the fact that sockjs removes all listeners // 53
+ // for "request" to add its own. // 54
+ Package.webapp.WebApp.httpServer.removeListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback); // 55
+ self.server.installHandlers(Package.webapp.WebApp.httpServer); // 56
+ Package.webapp.WebApp.httpServer.addListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback); // 57
+ // 58
+ // Support the /websocket endpoint // 59
+ self._redirectWebsocketEndpoint(); // 60
+ // 61
+ self.server.on('connection', function (socket) { // 62
+ socket.send = function (data) { // 63
+ socket.write(data); // 64
+ }; // 65
+ socket.on('close', function () { // 66
+ self.open_sockets = _.without(self.open_sockets, socket); // 67
+ }); // 68
+ self.open_sockets.push(socket); // 69
+ // 70
+ // XXX COMPAT WITH 0.6.6. Send the old style welcome message, which // 71
+ // will force old clients to reload. Remove this once we're not // 72
+ // concerned about people upgrading from a pre-0.7.0 release. Also, // 73
+ // remove the clause in the client that ignores the welcome message // 74
+ // (livedata_connection.js) // 75
+ socket.send(JSON.stringify({server_id: "0"})); // 76
+ // 77
+ // call all our callbacks when we get a new socket. they will do the // 78
+ // work of setting up handlers and such for specific messages. // 79
+ _.each(self.registration_callbacks, function (callback) { // 80
+ callback(socket); // 81
+ }); // 82
+ }); // 83
+ // 84
+}; // 85
+ // 86
+_.extend(StreamServer.prototype, { // 87
+ // call my callback when a new socket connects. // 88
+ // also call it for all current connections. // 89
+ register: function (callback) { // 90
+ var self = this; // 91
+ self.registration_callbacks.push(callback); // 92
+ _.each(self.all_sockets(), function (socket) { // 93
+ callback(socket); // 94
+ }); // 95
+ }, // 96
+ // 97
+ // get a list of all sockets // 98
+ all_sockets: function () { // 99
+ var self = this; // 100
+ return _.values(self.open_sockets); // 101
+ }, // 102
+ // 103
+ // Redirect /websocket to /sockjs/websocket in order to not expose // 104
+ // sockjs to clients that want to use raw websockets // 105
+ _redirectWebsocketEndpoint: function() { // 106
+ var self = this; // 107
+ // Unfortunately we can't use a connect middleware here since // 108
+ // sockjs installs itself prior to all existing listeners // 109
+ // (meaning prior to any connect middlewares) so we need to take // 110
+ // an approach similar to overshadowListeners in // 111
+ // https://github.com/sockjs/sockjs-node/blob/cf820c55af6a9953e16558555a31decea554f70e/src/utils.coffee // 112
+ _.each(['request', 'upgrade'], function(event) { // 113
+ var httpServer = Package.webapp.WebApp.httpServer; // 114
+ var oldHttpServerListeners = httpServer.listeners(event).slice(0); // 115
+ httpServer.removeAllListeners(event); // 116
+ // 117
+ // request and upgrade have different arguments passed but // 118
+ // we only care about the first one which is always request // 119
+ var newListener = function(request /*, moreArguments */) { // 120
+ // Store arguments for use within the closure below // 121
+ var args = arguments; // 122
+ // 123
+ // Rewrite /websocket and /websocket/ urls to /sockjs/websocket while // 124
+ // preserving query string. // 125
+ var parsedUrl = url.parse(request.url); // 126
+ if (parsedUrl.pathname === pathPrefix + '/websocket' || // 127
+ parsedUrl.pathname === pathPrefix + '/websocket/') { // 128
+ parsedUrl.pathname = self.prefix + '/websocket'; // 129
+ request.url = url.format(parsedUrl); // 130
+ } // 131
+ _.each(oldHttpServerListeners, function(oldListener) { // 132
+ oldListener.apply(httpServer, args); // 133
+ }); // 134
+ }; // 135
+ httpServer.addListener(event, newListener); // 136
+ }); // 137
+ } // 138
+}); // 139
+ // 140
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/heartbeat.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Heartbeat options: // 1
+// heartbeatInterval: interval to send pings, in milliseconds. // 2
+// heartbeatTimeout: timeout to close the connection if a reply isn't // 3
+// received, in milliseconds. // 4
+// sendPing: function to call to send a ping on the connection. // 5
+// onTimeout: function to call to close the connection. // 6
+ // 7
+Heartbeat = function (options) { // 8
+ var self = this; // 9
+ // 10
+ self.heartbeatInterval = options.heartbeatInterval; // 11
+ self.heartbeatTimeout = options.heartbeatTimeout; // 12
+ self._sendPing = options.sendPing; // 13
+ self._onTimeout = options.onTimeout; // 14
+ // 15
+ self._heartbeatIntervalHandle = null; // 16
+ self._heartbeatTimeoutHandle = null; // 17
+}; // 18
+ // 19
+_.extend(Heartbeat.prototype, { // 20
+ stop: function () { // 21
+ var self = this; // 22
+ self._clearHeartbeatIntervalTimer(); // 23
+ self._clearHeartbeatTimeoutTimer(); // 24
+ }, // 25
+ // 26
+ start: function () { // 27
+ var self = this; // 28
+ self.stop(); // 29
+ self._startHeartbeatIntervalTimer(); // 30
+ }, // 31
+ // 32
+ _startHeartbeatIntervalTimer: function () { // 33
+ var self = this; // 34
+ self._heartbeatIntervalHandle = Meteor.setTimeout( // 35
+ _.bind(self._heartbeatIntervalFired, self), // 36
+ self.heartbeatInterval // 37
+ ); // 38
+ }, // 39
+ // 40
+ _startHeartbeatTimeoutTimer: function () { // 41
+ var self = this; // 42
+ self._heartbeatTimeoutHandle = Meteor.setTimeout( // 43
+ _.bind(self._heartbeatTimeoutFired, self), // 44
+ self.heartbeatTimeout // 45
+ ); // 46
+ }, // 47
+ // 48
+ _clearHeartbeatIntervalTimer: function () { // 49
+ var self = this; // 50
+ if (self._heartbeatIntervalHandle) { // 51
+ Meteor.clearTimeout(self._heartbeatIntervalHandle); // 52
+ self._heartbeatIntervalHandle = null; // 53
+ } // 54
+ }, // 55
+ // 56
+ _clearHeartbeatTimeoutTimer: function () { // 57
+ var self = this; // 58
+ if (self._heartbeatTimeoutHandle) { // 59
+ Meteor.clearTimeout(self._heartbeatTimeoutHandle); // 60
+ self._heartbeatTimeoutHandle = null; // 61
+ } // 62
+ }, // 63
+ // 64
+ // The heartbeat interval timer is fired when we should send a ping. // 65
+ _heartbeatIntervalFired: function () { // 66
+ var self = this; // 67
+ self._heartbeatIntervalHandle = null; // 68
+ self._sendPing(); // 69
+ // Wait for a pong. // 70
+ self._startHeartbeatTimeoutTimer(); // 71
+ }, // 72
+ // 73
+ // The heartbeat timeout timer is fired when we sent a ping, but we // 74
+ // timed out waiting for the pong. // 75
+ _heartbeatTimeoutFired: function () { // 76
+ var self = this; // 77
+ self._heartbeatTimeoutHandle = null; // 78
+ self._onTimeout(); // 79
+ }, // 80
+ // 81
+ pingReceived: function () { // 82
+ var self = this; // 83
+ // We know the connection is alive if we receive a ping, so we // 84
+ // don't need to send a ping ourselves. Reset the interval timer. // 85
+ if (self._heartbeatIntervalHandle) { // 86
+ self._clearHeartbeatIntervalTimer(); // 87
+ self._startHeartbeatIntervalTimer(); // 88
+ } // 89
+ }, // 90
+ // 91
+ pongReceived: function () { // 92
+ var self = this; // 93
+ // 94
+ // Receiving a pong means we won't timeout, so clear the timeout // 95
+ // timer and start the interval again. // 96
+ if (self._heartbeatTimeoutHandle) { // 97
+ self._clearHeartbeatTimeoutTimer(); // 98
+ self._startHeartbeatIntervalTimer(); // 99
+ } // 100
+ } // 101
+}); // 102
+ // 103
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/livedata_server.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+DDPServer = {}; // 1
+ // 2
+var Fiber = Npm.require('fibers'); // 3
+ // 4
+// This file contains classes: // 5
+// * Session - The server's connection to a single DDP client // 6
+// * Subscription - A single subscription for a single client // 7
+// * Server - An entire server that may talk to > 1 client. A DDP endpoint. // 8
+// // 9
+// Session and Subscription are file scope. For now, until we freeze // 10
+// the interface, Server is package scope (in the future it should be // 11
+// exported.) // 12
+ // 13
+// Represents a single document in a SessionCollectionView // 14
+var SessionDocumentView = function () { // 15
+ var self = this; // 16
+ self.existsIn = {}; // set of subscriptionHandle // 17
+ self.dataByKey = {}; // key-> [ {subscriptionHandle, value} by precedence] // 18
+}; // 19
+ // 20
+_.extend(SessionDocumentView.prototype, { // 21
+ // 22
+ getFields: function () { // 23
+ var self = this; // 24
+ var ret = {}; // 25
+ _.each(self.dataByKey, function (precedenceList, key) { // 26
+ ret[key] = precedenceList[0].value; // 27
+ }); // 28
+ return ret; // 29
+ }, // 30
+ // 31
+ clearField: function (subscriptionHandle, key, changeCollector) { // 32
+ var self = this; // 33
+ // Publish API ignores _id if present in fields // 34
+ if (key === "_id") // 35
+ return; // 36
+ var precedenceList = self.dataByKey[key]; // 37
+ // 38
+ // It's okay to clear fields that didn't exist. No need to throw // 39
+ // an error. // 40
+ if (!precedenceList) // 41
+ return; // 42
+ // 43
+ var removedValue = undefined; // 44
+ for (var i = 0; i < precedenceList.length; i++) { // 45
+ var precedence = precedenceList[i]; // 46
+ if (precedence.subscriptionHandle === subscriptionHandle) { // 47
+ // The view's value can only change if this subscription is the one that // 48
+ // used to have precedence. // 49
+ if (i === 0) // 50
+ removedValue = precedence.value; // 51
+ precedenceList.splice(i, 1); // 52
+ break; // 53
+ } // 54
+ } // 55
+ if (_.isEmpty(precedenceList)) { // 56
+ delete self.dataByKey[key]; // 57
+ changeCollector[key] = undefined; // 58
+ } else if (removedValue !== undefined && // 59
+ !EJSON.equals(removedValue, precedenceList[0].value)) { // 60
+ changeCollector[key] = precedenceList[0].value; // 61
+ } // 62
+ }, // 63
+ // 64
+ changeField: function (subscriptionHandle, key, value, // 65
+ changeCollector, isAdd) { // 66
+ var self = this; // 67
+ // Publish API ignores _id if present in fields // 68
+ if (key === "_id") // 69
+ return; // 70
+ // 71
+ // Don't share state with the data passed in by the user. // 72
+ value = EJSON.clone(value); // 73
+ // 74
+ if (!_.has(self.dataByKey, key)) { // 75
+ self.dataByKey[key] = [{subscriptionHandle: subscriptionHandle, // 76
+ value: value}]; // 77
+ changeCollector[key] = value; // 78
+ return; // 79
+ } // 80
+ var precedenceList = self.dataByKey[key]; // 81
+ var elt; // 82
+ if (!isAdd) { // 83
+ elt = _.find(precedenceList, function (precedence) { // 84
+ return precedence.subscriptionHandle === subscriptionHandle; // 85
+ }); // 86
+ } // 87
+ // 88
+ if (elt) { // 89
+ if (elt === precedenceList[0] && !EJSON.equals(value, elt.value)) { // 90
+ // this subscription is changing the value of this field. // 91
+ changeCollector[key] = value; // 92
+ } // 93
+ elt.value = value; // 94
+ } else { // 95
+ // this subscription is newly caring about this field // 96
+ precedenceList.push({subscriptionHandle: subscriptionHandle, value: value}); // 97
+ } // 98
+ // 99
+ } // 100
+}); // 101
+ // 102
+/** // 103
+ * Represents a client's view of a single collection // 104
+ * @param {String} collectionName Name of the collection it represents // 105
+ * @param {Object.} sessionCallbacks The callbacks for added, changed, removed // 106
+ * @class SessionCollectionView // 107
+ */ // 108
+var SessionCollectionView = function (collectionName, sessionCallbacks) { // 109
+ var self = this; // 110
+ self.collectionName = collectionName; // 111
+ self.documents = {}; // 112
+ self.callbacks = sessionCallbacks; // 113
+}; // 114
+ // 115
+LivedataTest.SessionCollectionView = SessionCollectionView; // 116
+ // 117
+ // 118
+_.extend(SessionCollectionView.prototype, { // 119
+ // 120
+ isEmpty: function () { // 121
+ var self = this; // 122
+ return _.isEmpty(self.documents); // 123
+ }, // 124
+ // 125
+ diff: function (previous) { // 126
+ var self = this; // 127
+ LocalCollection._diffObjects(previous.documents, self.documents, { // 128
+ both: _.bind(self.diffDocument, self), // 129
+ // 130
+ rightOnly: function (id, nowDV) { // 131
+ self.callbacks.added(self.collectionName, id, nowDV.getFields()); // 132
+ }, // 133
+ // 134
+ leftOnly: function (id, prevDV) { // 135
+ self.callbacks.removed(self.collectionName, id); // 136
+ } // 137
+ }); // 138
+ }, // 139
+ // 140
+ diffDocument: function (id, prevDV, nowDV) { // 141
+ var self = this; // 142
+ var fields = {}; // 143
+ LocalCollection._diffObjects(prevDV.getFields(), nowDV.getFields(), { // 144
+ both: function (key, prev, now) { // 145
+ if (!EJSON.equals(prev, now)) // 146
+ fields[key] = now; // 147
+ }, // 148
+ rightOnly: function (key, now) { // 149
+ fields[key] = now; // 150
+ }, // 151
+ leftOnly: function(key, prev) { // 152
+ fields[key] = undefined; // 153
+ } // 154
+ }); // 155
+ self.callbacks.changed(self.collectionName, id, fields); // 156
+ }, // 157
+ // 158
+ added: function (subscriptionHandle, id, fields) { // 159
+ var self = this; // 160
+ var docView = self.documents[id]; // 161
+ var added = false; // 162
+ if (!docView) { // 163
+ added = true; // 164
+ docView = new SessionDocumentView(); // 165
+ self.documents[id] = docView; // 166
+ } // 167
+ docView.existsIn[subscriptionHandle] = true; // 168
+ var changeCollector = {}; // 169
+ _.each(fields, function (value, key) { // 170
+ docView.changeField( // 171
+ subscriptionHandle, key, value, changeCollector, true); // 172
+ }); // 173
+ if (added) // 174
+ self.callbacks.added(self.collectionName, id, changeCollector); // 175
+ else // 176
+ self.callbacks.changed(self.collectionName, id, changeCollector); // 177
+ }, // 178
+ // 179
+ changed: function (subscriptionHandle, id, changed) { // 180
+ var self = this; // 181
+ var changedResult = {}; // 182
+ var docView = self.documents[id]; // 183
+ if (!docView) // 184
+ throw new Error("Could not find element with id " + id + " to change"); // 185
+ _.each(changed, function (value, key) { // 186
+ if (value === undefined) // 187
+ docView.clearField(subscriptionHandle, key, changedResult); // 188
+ else // 189
+ docView.changeField(subscriptionHandle, key, value, changedResult); // 190
+ }); // 191
+ self.callbacks.changed(self.collectionName, id, changedResult); // 192
+ }, // 193
+ // 194
+ removed: function (subscriptionHandle, id) { // 195
+ var self = this; // 196
+ var docView = self.documents[id]; // 197
+ if (!docView) { // 198
+ var err = new Error("Removed nonexistent document " + id); // 199
+ throw err; // 200
+ } // 201
+ delete docView.existsIn[subscriptionHandle]; // 202
+ if (_.isEmpty(docView.existsIn)) { // 203
+ // it is gone from everyone // 204
+ self.callbacks.removed(self.collectionName, id); // 205
+ delete self.documents[id]; // 206
+ } else { // 207
+ var changed = {}; // 208
+ // remove this subscription from every precedence list // 209
+ // and record the changes // 210
+ _.each(docView.dataByKey, function (precedenceList, key) { // 211
+ docView.clearField(subscriptionHandle, key, changed); // 212
+ }); // 213
+ // 214
+ self.callbacks.changed(self.collectionName, id, changed); // 215
+ } // 216
+ } // 217
+}); // 218
+ // 219
+/******************************************************************************/ // 220
+/* Session */ // 221
+/******************************************************************************/ // 222
+ // 223
+var Session = function (server, version, socket, options) { // 224
+ var self = this; // 225
+ self.id = Random.id(); // 226
+ // 227
+ self.server = server; // 228
+ self.version = version; // 229
+ // 230
+ self.initialized = false; // 231
+ self.socket = socket; // 232
+ // 233
+ // set to null when the session is destroyed. multiple places below // 234
+ // use this to determine if the session is alive or not. // 235
+ self.inQueue = new Meteor._DoubleEndedQueue(); // 236
+ // 237
+ self.blocked = false; // 238
+ self.workerRunning = false; // 239
+ // 240
+ // Sub objects for active subscriptions // 241
+ self._namedSubs = {}; // 242
+ self._universalSubs = []; // 243
+ // 244
+ self.userId = null; // 245
+ // 246
+ self.collectionViews = {}; // 247
+ // 248
+ // Set this to false to not send messages when collectionViews are // 249
+ // modified. This is done when rerunning subs in _setUserId and those messages // 250
+ // are calculated via a diff instead. // 251
+ self._isSending = true; // 252
+ // 253
+ // If this is true, don't start a newly-created universal publisher on this // 254
+ // session. The session will take care of starting it when appropriate. // 255
+ self._dontStartNewUniversalSubs = false; // 256
+ // 257
+ // when we are rerunning subscriptions, any ready messages // 258
+ // we want to buffer up for when we are done rerunning subscriptions // 259
+ self._pendingReady = []; // 260
+ // 261
+ // List of callbacks to call when this connection is closed. // 262
+ self._closeCallbacks = []; // 263
+ // 264
+ // 265
+ // XXX HACK: If a sockjs connection, save off the URL. This is // 266
+ // temporary and will go away in the near future. // 267
+ self._socketUrl = socket.url; // 268
+ // 269
+ // Allow tests to disable responding to pings. // 270
+ self._respondToPings = options.respondToPings; // 271
+ // 272
+ // This object is the public interface to the session. In the public // 273
+ // API, it is called the `connection` object. Internally we call it // 274
+ // a `connectionHandle` to avoid ambiguity. // 275
+ self.connectionHandle = { // 276
+ id: self.id, // 277
+ close: function () { // 278
+ self.close(); // 279
+ }, // 280
+ onClose: function (fn) { // 281
+ var cb = Meteor.bindEnvironment(fn, "connection onClose callback"); // 282
+ if (self.inQueue) { // 283
+ self._closeCallbacks.push(cb); // 284
+ } else { // 285
+ // if we're already closed, call the callback. // 286
+ Meteor.defer(cb); // 287
+ } // 288
+ }, // 289
+ clientAddress: self._clientAddress(), // 290
+ httpHeaders: self.socket.headers // 291
+ }; // 292
+ // 293
+ socket.send(stringifyDDP({msg: 'connected', // 294
+ session: self.id})); // 295
+ // On initial connect, spin up all the universal publishers. // 296
+ Fiber(function () { // 297
+ self.startUniversalSubs(); // 298
+ }).run(); // 299
+ // 300
+ if (version !== 'pre1' && options.heartbeatInterval !== 0) { // 301
+ self.heartbeat = new Heartbeat({ // 302
+ heartbeatInterval: options.heartbeatInterval, // 303
+ heartbeatTimeout: options.heartbeatTimeout, // 304
+ onTimeout: function () { // 305
+ self.close(); // 306
+ }, // 307
+ sendPing: function () { // 308
+ self.send({msg: 'ping'}); // 309
+ } // 310
+ }); // 311
+ self.heartbeat.start(); // 312
+ } // 313
+ // 314
+ Package.facts && Package.facts.Facts.incrementServerFact( // 315
+ "livedata", "sessions", 1); // 316
+}; // 317
+ // 318
+_.extend(Session.prototype, { // 319
+ // 320
+ sendReady: function (subscriptionIds) { // 321
+ var self = this; // 322
+ if (self._isSending) // 323
+ self.send({msg: "ready", subs: subscriptionIds}); // 324
+ else { // 325
+ _.each(subscriptionIds, function (subscriptionId) { // 326
+ self._pendingReady.push(subscriptionId); // 327
+ }); // 328
+ } // 329
+ }, // 330
+ // 331
+ sendAdded: function (collectionName, id, fields) { // 332
+ var self = this; // 333
+ if (self._isSending) // 334
+ self.send({msg: "added", collection: collectionName, id: id, fields: fields}); // 335
+ }, // 336
+ // 337
+ sendChanged: function (collectionName, id, fields) { // 338
+ var self = this; // 339
+ if (_.isEmpty(fields)) // 340
+ return; // 341
+ // 342
+ if (self._isSending) { // 343
+ self.send({ // 344
+ msg: "changed", // 345
+ collection: collectionName, // 346
+ id: id, // 347
+ fields: fields // 348
+ }); // 349
+ } // 350
+ }, // 351
+ // 352
+ sendRemoved: function (collectionName, id) { // 353
+ var self = this; // 354
+ if (self._isSending) // 355
+ self.send({msg: "removed", collection: collectionName, id: id}); // 356
+ }, // 357
+ // 358
+ getSendCallbacks: function () { // 359
+ var self = this; // 360
+ return { // 361
+ added: _.bind(self.sendAdded, self), // 362
+ changed: _.bind(self.sendChanged, self), // 363
+ removed: _.bind(self.sendRemoved, self) // 364
+ }; // 365
+ }, // 366
+ // 367
+ getCollectionView: function (collectionName) { // 368
+ var self = this; // 369
+ if (_.has(self.collectionViews, collectionName)) { // 370
+ return self.collectionViews[collectionName]; // 371
+ } // 372
+ var ret = new SessionCollectionView(collectionName, // 373
+ self.getSendCallbacks()); // 374
+ self.collectionViews[collectionName] = ret; // 375
+ return ret; // 376
+ }, // 377
+ // 378
+ added: function (subscriptionHandle, collectionName, id, fields) { // 379
+ var self = this; // 380
+ var view = self.getCollectionView(collectionName); // 381
+ view.added(subscriptionHandle, id, fields); // 382
+ }, // 383
+ // 384
+ removed: function (subscriptionHandle, collectionName, id) { // 385
+ var self = this; // 386
+ var view = self.getCollectionView(collectionName); // 387
+ view.removed(subscriptionHandle, id); // 388
+ if (view.isEmpty()) { // 389
+ delete self.collectionViews[collectionName]; // 390
+ } // 391
+ }, // 392
+ // 393
+ changed: function (subscriptionHandle, collectionName, id, fields) { // 394
+ var self = this; // 395
+ var view = self.getCollectionView(collectionName); // 396
+ view.changed(subscriptionHandle, id, fields); // 397
+ }, // 398
+ // 399
+ startUniversalSubs: function () { // 400
+ var self = this; // 401
+ // Make a shallow copy of the set of universal handlers and start them. If // 402
+ // additional universal publishers start while we're running them (due to // 403
+ // yielding), they will run separately as part of Server.publish. // 404
+ var handlers = _.clone(self.server.universal_publish_handlers); // 405
+ _.each(handlers, function (handler) { // 406
+ self._startSubscription(handler); // 407
+ }); // 408
+ }, // 409
+ // 410
+ // Destroy this session and unregister it at the server. // 411
+ close: function () { // 412
+ var self = this; // 413
+ // 414
+ // Destroy this session, even if it's not registered at the // 415
+ // server. Stop all processing and tear everything down. If a socket // 416
+ // was attached, close it. // 417
+ // 418
+ // Already destroyed. // 419
+ if (! self.inQueue) // 420
+ return; // 421
+ // 422
+ // Drop the merge box data immediately. // 423
+ self.inQueue = null; // 424
+ self.collectionViews = {}; // 425
+ // 426
+ if (self.heartbeat) { // 427
+ self.heartbeat.stop(); // 428
+ self.heartbeat = null; // 429
+ } // 430
+ // 431
+ if (self.socket) { // 432
+ self.socket.close(); // 433
+ self.socket._meteorSession = null; // 434
+ } // 435
+ // 436
+ Package.facts && Package.facts.Facts.incrementServerFact( // 437
+ "livedata", "sessions", -1); // 438
+ // 439
+ Meteor.defer(function () { // 440
+ // stop callbacks can yield, so we defer this on close. // 441
+ // sub._isDeactivated() detects that we set inQueue to null and // 442
+ // treats it as semi-deactivated (it will ignore incoming callbacks, etc). // 443
+ self._deactivateAllSubscriptions(); // 444
+ // 445
+ // Defer calling the close callbacks, so that the caller closing // 446
+ // the session isn't waiting for all the callbacks to complete. // 447
+ _.each(self._closeCallbacks, function (callback) { // 448
+ callback(); // 449
+ }); // 450
+ }); // 451
+ // 452
+ // Unregister the session. // 453
+ self.server._removeSession(self); // 454
+ }, // 455
+ // 456
+ // Send a message (doing nothing if no socket is connected right now.) // 457
+ // It should be a JSON object (it will be stringified.) // 458
+ send: function (msg) { // 459
+ var self = this; // 460
+ if (self.socket) { // 461
+ if (Meteor._printSentDDP) // 462
+ Meteor._debug("Sent DDP", stringifyDDP(msg)); // 463
+ self.socket.send(stringifyDDP(msg)); // 464
+ } // 465
+ }, // 466
+ // 467
+ // Send a connection error. // 468
+ sendError: function (reason, offendingMessage) { // 469
+ var self = this; // 470
+ var msg = {msg: 'error', reason: reason}; // 471
+ if (offendingMessage) // 472
+ msg.offendingMessage = offendingMessage; // 473
+ self.send(msg); // 474
+ }, // 475
+ // 476
+ // Process 'msg' as an incoming message. (But as a guard against // 477
+ // race conditions during reconnection, ignore the message if // 478
+ // 'socket' is not the currently connected socket.) // 479
+ // // 480
+ // We run the messages from the client one at a time, in the order // 481
+ // given by the client. The message handler is passed an idempotent // 482
+ // function 'unblock' which it may call to allow other messages to // 483
+ // begin running in parallel in another fiber (for example, a method // 484
+ // that wants to yield.) Otherwise, it is automatically unblocked // 485
+ // when it returns. // 486
+ // // 487
+ // Actually, we don't have to 'totally order' the messages in this // 488
+ // way, but it's the easiest thing that's correct. (unsub needs to // 489
+ // be ordered against sub, methods need to be ordered against each // 490
+ // other.) // 491
+ processMessage: function (msg_in) { // 492
+ var self = this; // 493
+ if (!self.inQueue) // we have been destroyed. // 494
+ return; // 495
+ // 496
+ // Respond to ping and pong messages immediately without queuing. // 497
+ // If the negotiated DDP version is "pre1" which didn't support // 498
+ // pings, preserve the "pre1" behavior of responding with a "bad // 499
+ // request" for the unknown messages. // 500
+ // // 501
+ // Fibers are needed because heartbeat uses Meteor.setTimeout, which // 502
+ // needs a Fiber. We could actually use regular setTimeout and avoid // 503
+ // these new fibers, but it is easier to just make everything use // 504
+ // Meteor.setTimeout and not think too hard. // 505
+ if (self.version !== 'pre1' && msg_in.msg === 'ping') { // 506
+ if (self._respondToPings) // 507
+ self.send({msg: "pong", id: msg_in.id}); // 508
+ if (self.heartbeat) // 509
+ Fiber(function () { // 510
+ self.heartbeat.pingReceived(); // 511
+ }).run(); // 512
+ return; // 513
+ } // 514
+ if (self.version !== 'pre1' && msg_in.msg === 'pong') { // 515
+ if (self.heartbeat) // 516
+ Fiber(function () { // 517
+ self.heartbeat.pongReceived(); // 518
+ }).run(); // 519
+ return; // 520
+ } // 521
+ // 522
+ self.inQueue.push(msg_in); // 523
+ if (self.workerRunning) // 524
+ return; // 525
+ self.workerRunning = true; // 526
+ // 527
+ var processNext = function () { // 528
+ var msg = self.inQueue && self.inQueue.shift(); // 529
+ if (!msg) { // 530
+ self.workerRunning = false; // 531
+ return; // 532
+ } // 533
+ // 534
+ Fiber(function () { // 535
+ var blocked = true; // 536
+ // 537
+ var unblock = function () { // 538
+ if (!blocked) // 539
+ return; // idempotent // 540
+ blocked = false; // 541
+ processNext(); // 542
+ }; // 543
+ // 544
+ if (_.has(self.protocol_handlers, msg.msg)) // 545
+ self.protocol_handlers[msg.msg].call(self, msg, unblock); // 546
+ else // 547
+ self.sendError('Bad request', msg); // 548
+ unblock(); // in case the handler didn't already do it // 549
+ }).run(); // 550
+ }; // 551
+ // 552
+ processNext(); // 553
+ }, // 554
+ // 555
+ protocol_handlers: { // 556
+ sub: function (msg) { // 557
+ var self = this; // 558
+ // 559
+ // reject malformed messages // 560
+ if (typeof (msg.id) !== "string" || // 561
+ typeof (msg.name) !== "string" || // 562
+ (('params' in msg) && !(msg.params instanceof Array))) { // 563
+ self.sendError("Malformed subscription", msg); // 564
+ return; // 565
+ } // 566
+ // 567
+ if (!self.server.publish_handlers[msg.name]) { // 568
+ self.send({ // 569
+ msg: 'nosub', id: msg.id, // 570
+ error: new Meteor.Error(404, "Subscription not found")}); // 571
+ return; // 572
+ } // 573
+ // 574
+ if (_.has(self._namedSubs, msg.id)) // 575
+ // subs are idempotent, or rather, they are ignored if a sub // 576
+ // with that id already exists. this is important during // 577
+ // reconnect. // 578
+ return; // 579
+ // 580
+ var handler = self.server.publish_handlers[msg.name]; // 581
+ self._startSubscription(handler, msg.id, msg.params, msg.name); // 582
+ // 583
+ }, // 584
+ // 585
+ unsub: function (msg) { // 586
+ var self = this; // 587
+ // 588
+ self._stopSubscription(msg.id); // 589
+ }, // 590
+ // 591
+ method: function (msg, unblock) { // 592
+ var self = this; // 593
+ // 594
+ // reject malformed messages // 595
+ // For now, we silently ignore unknown attributes, // 596
+ // for forwards compatibility. // 597
+ if (typeof (msg.id) !== "string" || // 598
+ typeof (msg.method) !== "string" || // 599
+ (('params' in msg) && !(msg.params instanceof Array)) || // 600
+ (('randomSeed' in msg) && (typeof msg.randomSeed !== "string"))) { // 601
+ self.sendError("Malformed method invocation", msg); // 602
+ return; // 603
+ } // 604
+ // 605
+ var randomSeed = msg.randomSeed || null; // 606
+ // 607
+ // set up to mark the method as satisfied once all observers // 608
+ // (and subscriptions) have reacted to any writes that were // 609
+ // done. // 610
+ var fence = new DDPServer._WriteFence; // 611
+ fence.onAllCommitted(function () { // 612
+ // Retire the fence so that future writes are allowed. // 613
+ // This means that callbacks like timers are free to use // 614
+ // the fence, and if they fire before it's armed (for // 615
+ // example, because the method waits for them) their // 616
+ // writes will be included in the fence. // 617
+ fence.retire(); // 618
+ self.send({ // 619
+ msg: 'updated', methods: [msg.id]}); // 620
+ }); // 621
+ // 622
+ // find the handler // 623
+ var handler = self.server.method_handlers[msg.method]; // 624
+ if (!handler) { // 625
+ self.send({ // 626
+ msg: 'result', id: msg.id, // 627
+ error: new Meteor.Error(404, "Method not found")}); // 628
+ fence.arm(); // 629
+ return; // 630
+ } // 631
+ // 632
+ var setUserId = function(userId) { // 633
+ self._setUserId(userId); // 634
+ }; // 635
+ // 636
+ var invocation = new MethodInvocation({ // 637
+ isSimulation: false, // 638
+ userId: self.userId, // 639
+ setUserId: setUserId, // 640
+ unblock: unblock, // 641
+ connection: self.connectionHandle, // 642
+ randomSeed: randomSeed // 643
+ }); // 644
+ try { // 645
+ var result = DDPServer._CurrentWriteFence.withValue(fence, function () { // 646
+ return DDP._CurrentInvocation.withValue(invocation, function () { // 647
+ return maybeAuditArgumentChecks( // 648
+ handler, invocation, msg.params, "call to '" + msg.method + "'"); // 649
+ }); // 650
+ }); // 651
+ } catch (e) { // 652
+ var exception = e; // 653
+ } // 654
+ // 655
+ fence.arm(); // we're done adding writes to the fence // 656
+ unblock(); // unblock, if the method hasn't done it already // 657
+ // 658
+ exception = wrapInternalException( // 659
+ exception, "while invoking method '" + msg.method + "'"); // 660
+ // 661
+ // send response and add to cache // 662
+ var payload = // 663
+ exception ? {error: exception} : (result !== undefined ? // 664
+ {result: result} : {}); // 665
+ self.send(_.extend({msg: 'result', id: msg.id}, payload)); // 666
+ } // 667
+ }, // 668
+ // 669
+ _eachSub: function (f) { // 670
+ var self = this; // 671
+ _.each(self._namedSubs, f); // 672
+ _.each(self._universalSubs, f); // 673
+ }, // 674
+ // 675
+ _diffCollectionViews: function (beforeCVs) { // 676
+ var self = this; // 677
+ LocalCollection._diffObjects(beforeCVs, self.collectionViews, { // 678
+ both: function (collectionName, leftValue, rightValue) { // 679
+ rightValue.diff(leftValue); // 680
+ }, // 681
+ rightOnly: function (collectionName, rightValue) { // 682
+ _.each(rightValue.documents, function (docView, id) { // 683
+ self.sendAdded(collectionName, id, docView.getFields()); // 684
+ }); // 685
+ }, // 686
+ leftOnly: function (collectionName, leftValue) { // 687
+ _.each(leftValue.documents, function (doc, id) { // 688
+ self.sendRemoved(collectionName, id); // 689
+ }); // 690
+ } // 691
+ }); // 692
+ }, // 693
+ // 694
+ // Sets the current user id in all appropriate contexts and reruns // 695
+ // all subscriptions // 696
+ _setUserId: function(userId) { // 697
+ var self = this; // 698
+ // 699
+ if (userId !== null && typeof userId !== "string") // 700
+ throw new Error("setUserId must be called on string or null, not " + // 701
+ typeof userId); // 702
+ // 703
+ // Prevent newly-created universal subscriptions from being added to our // 704
+ // session; they will be found below when we call startUniversalSubs. // 705
+ // // 706
+ // (We don't have to worry about named subscriptions, because we only add // 707
+ // them when we process a 'sub' message. We are currently processing a // 708
+ // 'method' message, and the method did not unblock, because it is illegal // 709
+ // to call setUserId after unblock. Thus we cannot be concurrently adding a // 710
+ // new named subscription.) // 711
+ self._dontStartNewUniversalSubs = true; // 712
+ // 713
+ // Prevent current subs from updating our collectionViews and call their // 714
+ // stop callbacks. This may yield. // 715
+ self._eachSub(function (sub) { // 716
+ sub._deactivate(); // 717
+ }); // 718
+ // 719
+ // All subs should now be deactivated. Stop sending messages to the client, // 720
+ // save the state of the published collections, reset to an empty view, and // 721
+ // update the userId. // 722
+ self._isSending = false; // 723
+ var beforeCVs = self.collectionViews; // 724
+ self.collectionViews = {}; // 725
+ self.userId = userId; // 726
+ // 727
+ // Save the old named subs, and reset to having no subscriptions. // 728
+ var oldNamedSubs = self._namedSubs; // 729
+ self._namedSubs = {}; // 730
+ self._universalSubs = []; // 731
+ // 732
+ _.each(oldNamedSubs, function (sub, subscriptionId) { // 733
+ self._namedSubs[subscriptionId] = sub._recreate(); // 734
+ // nb: if the handler throws or calls this.error(), it will in fact // 735
+ // immediately send its 'nosub'. This is OK, though. // 736
+ self._namedSubs[subscriptionId]._runHandler(); // 737
+ }); // 738
+ // 739
+ // Allow newly-created universal subs to be started on our connection in // 740
+ // parallel with the ones we're spinning up here, and spin up universal // 741
+ // subs. // 742
+ self._dontStartNewUniversalSubs = false; // 743
+ self.startUniversalSubs(); // 744
+ // 745
+ // Start sending messages again, beginning with the diff from the previous // 746
+ // state of the world to the current state. No yields are allowed during // 747
+ // this diff, so that other changes cannot interleave. // 748
+ Meteor._noYieldsAllowed(function () { // 749
+ self._isSending = true; // 750
+ self._diffCollectionViews(beforeCVs); // 751
+ if (!_.isEmpty(self._pendingReady)) { // 752
+ self.sendReady(self._pendingReady); // 753
+ self._pendingReady = []; // 754
+ } // 755
+ }); // 756
+ }, // 757
+ // 758
+ _startSubscription: function (handler, subId, params, name) { // 759
+ var self = this; // 760
+ // 761
+ var sub = new Subscription( // 762
+ self, handler, subId, params, name); // 763
+ if (subId) // 764
+ self._namedSubs[subId] = sub; // 765
+ else // 766
+ self._universalSubs.push(sub); // 767
+ // 768
+ sub._runHandler(); // 769
+ }, // 770
+ // 771
+ // tear down specified subscription // 772
+ _stopSubscription: function (subId, error) { // 773
+ var self = this; // 774
+ // 775
+ var subName = null; // 776
+ // 777
+ if (subId && self._namedSubs[subId]) { // 778
+ subName = self._namedSubs[subId]._name; // 779
+ self._namedSubs[subId]._removeAllDocuments(); // 780
+ self._namedSubs[subId]._deactivate(); // 781
+ delete self._namedSubs[subId]; // 782
+ } // 783
+ // 784
+ var response = {msg: 'nosub', id: subId}; // 785
+ // 786
+ if (error) { // 787
+ response.error = wrapInternalException( // 788
+ error, // 789
+ subName ? ("from sub " + subName + " id " + subId) // 790
+ : ("from sub id " + subId)); // 791
+ } // 792
+ // 793
+ self.send(response); // 794
+ }, // 795
+ // 796
+ // tear down all subscriptions. Note that this does NOT send removed or nosub // 797
+ // messages, since we assume the client is gone. // 798
+ _deactivateAllSubscriptions: function () { // 799
+ var self = this; // 800
+ // 801
+ _.each(self._namedSubs, function (sub, id) { // 802
+ sub._deactivate(); // 803
+ }); // 804
+ self._namedSubs = {}; // 805
+ // 806
+ _.each(self._universalSubs, function (sub) { // 807
+ sub._deactivate(); // 808
+ }); // 809
+ self._universalSubs = []; // 810
+ }, // 811
+ // 812
+ // Determine the remote client's IP address, based on the // 813
+ // HTTP_FORWARDED_COUNT environment variable representing how many // 814
+ // proxies the server is behind. // 815
+ _clientAddress: function () { // 816
+ var self = this; // 817
+ // 818
+ // For the reported client address for a connection to be correct, // 819
+ // the developer must set the HTTP_FORWARDED_COUNT environment // 820
+ // variable to an integer representing the number of hops they // 821
+ // expect in the `x-forwarded-for` header. E.g., set to "1" if the // 822
+ // server is behind one proxy. // 823
+ // // 824
+ // This could be computed once at startup instead of every time. // 825
+ var httpForwardedCount = parseInt(process.env['HTTP_FORWARDED_COUNT']) || 0; // 826
+ // 827
+ if (httpForwardedCount === 0) // 828
+ return self.socket.remoteAddress; // 829
+ // 830
+ var forwardedFor = self.socket.headers["x-forwarded-for"]; // 831
+ if (! _.isString(forwardedFor)) // 832
+ return null; // 833
+ forwardedFor = forwardedFor.trim().split(/\s*,\s*/); // 834
+ // 835
+ // Typically the first value in the `x-forwarded-for` header is // 836
+ // the original IP address of the client connecting to the first // 837
+ // proxy. However, the end user can easily spoof the header, in // 838
+ // which case the first value(s) will be the fake IP address from // 839
+ // the user pretending to be a proxy reporting the original IP // 840
+ // address value. By counting HTTP_FORWARDED_COUNT back from the // 841
+ // end of the list, we ensure that we get the IP address being // 842
+ // reported by *our* first proxy. // 843
+ // 844
+ if (httpForwardedCount < 0 || httpForwardedCount > forwardedFor.length) // 845
+ return null; // 846
+ // 847
+ return forwardedFor[forwardedFor.length - httpForwardedCount]; // 848
+ } // 849
+}); // 850
+ // 851
+/******************************************************************************/ // 852
+/* Subscription */ // 853
+/******************************************************************************/ // 854
+ // 855
+// ctor for a sub handle: the input to each publish function // 856
+ // 857
+// Instance name is this because it's usually referred to as this inside a // 858
+// publish // 859
+/** // 860
+ * @summary The server's side of a subscription // 861
+ * @class Subscription // 862
+ * @instanceName this // 863
+ */ // 864
+var Subscription = function ( // 865
+ session, handler, subscriptionId, params, name) { // 866
+ var self = this; // 867
+ self._session = session; // type is Session // 868
+ // 869
+ /** // 870
+ * @summary Access inside the publish function. The incoming [connection](#meteor_onconnection) for this subscription.
+ * @locus Server // 872
+ * @name connection // 873
+ * @memberOf Subscription // 874
+ * @instance // 875
+ */ // 876
+ self.connection = session.connectionHandle; // public API object // 877
+ // 878
+ self._handler = handler; // 879
+ // 880
+ // my subscription ID (generated by client, undefined for universal subs). // 881
+ self._subscriptionId = subscriptionId; // 882
+ // undefined for universal subs // 883
+ self._name = name; // 884
+ // 885
+ self._params = params || []; // 886
+ // 887
+ // Only named subscriptions have IDs, but we need some sort of string // 888
+ // internally to keep track of all subscriptions inside // 889
+ // SessionDocumentViews. We use this subscriptionHandle for that. // 890
+ if (self._subscriptionId) { // 891
+ self._subscriptionHandle = 'N' + self._subscriptionId; // 892
+ } else { // 893
+ self._subscriptionHandle = 'U' + Random.id(); // 894
+ } // 895
+ // 896
+ // has _deactivate been called? // 897
+ self._deactivated = false; // 898
+ // 899
+ // stop callbacks to g/c this sub. called w/ zero arguments. // 900
+ self._stopCallbacks = []; // 901
+ // 902
+ // the set of (collection, documentid) that this subscription has // 903
+ // an opinion about // 904
+ self._documents = {}; // 905
+ // 906
+ // remember if we are ready. // 907
+ self._ready = false; // 908
+ // 909
+ // Part of the public API: the user of this sub. // 910
+ // 911
+ /** // 912
+ * @summary Access inside the publish function. The id of the logged-in user, or `null` if no user is logged in. // 913
+ * @locus Server // 914
+ * @memberOf Subscription // 915
+ * @name userId // 916
+ * @instance // 917
+ */ // 918
+ self.userId = session.userId; // 919
+ // 920
+ // For now, the id filter is going to default to // 921
+ // the to/from DDP methods on LocalCollection, to // 922
+ // specifically deal with mongo/minimongo ObjectIds. // 923
+ // 924
+ // Later, you will be able to make this be "raw" // 925
+ // if you want to publish a collection that you know // 926
+ // just has strings for keys and no funny business, to // 927
+ // a ddp consumer that isn't minimongo // 928
+ // 929
+ self._idFilter = { // 930
+ idStringify: LocalCollection._idStringify, // 931
+ idParse: LocalCollection._idParse // 932
+ }; // 933
+ // 934
+ Package.facts && Package.facts.Facts.incrementServerFact( // 935
+ "livedata", "subscriptions", 1); // 936
+}; // 937
+ // 938
+_.extend(Subscription.prototype, { // 939
+ _runHandler: function () { // 940
+ // XXX should we unblock() here? Either before running the publish // 941
+ // function, or before running _publishCursor. // 942
+ // // 943
+ // Right now, each publish function blocks all future publishes and // 944
+ // methods waiting on data from Mongo (or whatever else the function // 945
+ // blocks on). This probably slows page load in common cases. // 946
+ // 947
+ var self = this; // 948
+ try { // 949
+ var res = maybeAuditArgumentChecks( // 950
+ self._handler, self, EJSON.clone(self._params), // 951
+ // It's OK that this would look weird for universal subscriptions, // 952
+ // because they have no arguments so there can never be an // 953
+ // audit-argument-checks failure. // 954
+ "publisher '" + self._name + "'"); // 955
+ } catch (e) { // 956
+ self.error(e); // 957
+ return; // 958
+ } // 959
+ // 960
+ // Did the handler call this.error or this.stop? // 961
+ if (self._isDeactivated()) // 962
+ return; // 963
+ // 964
+ // SPECIAL CASE: Instead of writing their own callbacks that invoke // 965
+ // this.added/changed/ready/etc, the user can just return a collection // 966
+ // cursor or array of cursors from the publish function; we call their // 967
+ // _publishCursor method which starts observing the cursor and publishes the // 968
+ // results. Note that _publishCursor does NOT call ready(). // 969
+ // // 970
+ // XXX This uses an undocumented interface which only the Mongo cursor // 971
+ // interface publishes. Should we make this interface public and encourage // 972
+ // users to implement it themselves? Arguably, it's unnecessary; users can // 973
+ // already write their own functions like // 974
+ // var publishMyReactiveThingy = function (name, handler) { // 975
+ // Meteor.publish(name, function () { // 976
+ // var reactiveThingy = handler(); // 977
+ // reactiveThingy.publishMe(); // 978
+ // }); // 979
+ // }; // 980
+ var isCursor = function (c) { // 981
+ return c && c._publishCursor; // 982
+ }; // 983
+ if (isCursor(res)) { // 984
+ try { // 985
+ res._publishCursor(self); // 986
+ } catch (e) { // 987
+ self.error(e); // 988
+ return; // 989
+ } // 990
+ // _publishCursor only returns after the initial added callbacks have run. // 991
+ // mark subscription as ready. // 992
+ self.ready(); // 993
+ } else if (_.isArray(res)) { // 994
+ // check all the elements are cursors // 995
+ if (! _.all(res, isCursor)) { // 996
+ self.error(new Error("Publish function returned an array of non-Cursors")); // 997
+ return; // 998
+ } // 999
+ // find duplicate collection names // 1000
+ // XXX we should support overlapping cursors, but that would require the // 1001
+ // merge box to allow overlap within a subscription // 1002
+ var collectionNames = {}; // 1003
+ for (var i = 0; i < res.length; ++i) { // 1004
+ var collectionName = res[i]._getCollectionName(); // 1005
+ if (_.has(collectionNames, collectionName)) { // 1006
+ self.error(new Error( // 1007
+ "Publish function returned multiple cursors for collection " + // 1008
+ collectionName)); // 1009
+ return; // 1010
+ } // 1011
+ collectionNames[collectionName] = true; // 1012
+ }; // 1013
+ // 1014
+ try { // 1015
+ _.each(res, function (cur) { // 1016
+ cur._publishCursor(self); // 1017
+ }); // 1018
+ } catch (e) { // 1019
+ self.error(e); // 1020
+ return; // 1021
+ } // 1022
+ self.ready(); // 1023
+ } else if (res) { // 1024
+ // truthy values other than cursors or arrays are probably a // 1025
+ // user mistake (possible returning a Mongo document via, say, // 1026
+ // `coll.findOne()`). // 1027
+ self.error(new Error("Publish function can only return a Cursor or " // 1028
+ + "an array of Cursors")); // 1029
+ } // 1030
+ }, // 1031
+ // 1032
+ // This calls all stop callbacks and prevents the handler from updating any // 1033
+ // SessionCollectionViews further. It's used when the user unsubscribes or // 1034
+ // disconnects, as well as during setUserId re-runs. It does *NOT* send // 1035
+ // removed messages for the published objects; if that is necessary, call // 1036
+ // _removeAllDocuments first. // 1037
+ _deactivate: function() { // 1038
+ var self = this; // 1039
+ if (self._deactivated) // 1040
+ return; // 1041
+ self._deactivated = true; // 1042
+ self._callStopCallbacks(); // 1043
+ Package.facts && Package.facts.Facts.incrementServerFact( // 1044
+ "livedata", "subscriptions", -1); // 1045
+ }, // 1046
+ // 1047
+ _callStopCallbacks: function () { // 1048
+ var self = this; // 1049
+ // tell listeners, so they can clean up // 1050
+ var callbacks = self._stopCallbacks; // 1051
+ self._stopCallbacks = []; // 1052
+ _.each(callbacks, function (callback) { // 1053
+ callback(); // 1054
+ }); // 1055
+ }, // 1056
+ // 1057
+ // Send remove messages for every document. // 1058
+ _removeAllDocuments: function () { // 1059
+ var self = this; // 1060
+ Meteor._noYieldsAllowed(function () { // 1061
+ _.each(self._documents, function(collectionDocs, collectionName) { // 1062
+ // Iterate over _.keys instead of the dictionary itself, since we'll be // 1063
+ // mutating it. // 1064
+ _.each(_.keys(collectionDocs), function (strId) { // 1065
+ self.removed(collectionName, self._idFilter.idParse(strId)); // 1066
+ }); // 1067
+ }); // 1068
+ }); // 1069
+ }, // 1070
+ // 1071
+ // Returns a new Subscription for the same session with the same // 1072
+ // initial creation parameters. This isn't a clone: it doesn't have // 1073
+ // the same _documents cache, stopped state or callbacks; may have a // 1074
+ // different _subscriptionHandle, and gets its userId from the // 1075
+ // session, not from this object. // 1076
+ _recreate: function () { // 1077
+ var self = this; // 1078
+ return new Subscription( // 1079
+ self._session, self._handler, self._subscriptionId, self._params, // 1080
+ self._name); // 1081
+ }, // 1082
+ // 1083
+ /** // 1084
+ * @summary Call inside the publish function. Stops this client's subscription, triggering a call on the client to the `onStop` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any. If `error` is not a [`Meteor.Error`](#meteor_error), it will be [sanitized](#meteor_error).
+ * @locus Server // 1086
+ * @param {Error} error The error to pass to the client. // 1087
+ * @instance // 1088
+ * @memberOf Subscription // 1089
+ */ // 1090
+ error: function (error) { // 1091
+ var self = this; // 1092
+ if (self._isDeactivated()) // 1093
+ return; // 1094
+ self._session._stopSubscription(self._subscriptionId, error); // 1095
+ }, // 1096
+ // 1097
+ // Note that while our DDP client will notice that you've called stop() on the // 1098
+ // server (and clean up its _subscriptions table) we don't actually provide a // 1099
+ // mechanism for an app to notice this (the subscribe onError callback only // 1100
+ // triggers if there is an error). // 1101
+ // 1102
+ /** // 1103
+ * @summary Call inside the publish function. Stops this client's subscription and invokes the client's `onStop` callback with no error.
+ * @locus Server // 1105
+ * @instance // 1106
+ * @memberOf Subscription // 1107
+ */ // 1108
+ stop: function () { // 1109
+ var self = this; // 1110
+ if (self._isDeactivated()) // 1111
+ return; // 1112
+ self._session._stopSubscription(self._subscriptionId); // 1113
+ }, // 1114
+ // 1115
+ /** // 1116
+ * @summary Call inside the publish function. Registers a callback function to run when the subscription is stopped.
+ * @locus Server // 1118
+ * @memberOf Subscription // 1119
+ * @instance // 1120
+ * @param {Function} func The callback function // 1121
+ */ // 1122
+ onStop: function (callback) { // 1123
+ var self = this; // 1124
+ if (self._isDeactivated()) // 1125
+ callback(); // 1126
+ else // 1127
+ self._stopCallbacks.push(callback); // 1128
+ }, // 1129
+ // 1130
+ // This returns true if the sub has been deactivated, *OR* if the session was // 1131
+ // destroyed but the deferred call to _deactivateAllSubscriptions hasn't // 1132
+ // happened yet. // 1133
+ _isDeactivated: function () { // 1134
+ var self = this; // 1135
+ return self._deactivated || self._session.inQueue === null; // 1136
+ }, // 1137
+ // 1138
+ /** // 1139
+ * @summary Call inside the publish function. Informs the subscriber that a document has been added to the record set.
+ * @locus Server // 1141
+ * @memberOf Subscription // 1142
+ * @instance // 1143
+ * @param {String} collection The name of the collection that contains the new document. // 1144
+ * @param {String} id The new document's ID. // 1145
+ * @param {Object} fields The fields in the new document. If `_id` is present it is ignored. // 1146
+ */ // 1147
+ added: function (collectionName, id, fields) { // 1148
+ var self = this; // 1149
+ if (self._isDeactivated()) // 1150
+ return; // 1151
+ id = self._idFilter.idStringify(id); // 1152
+ Meteor._ensure(self._documents, collectionName)[id] = true; // 1153
+ self._session.added(self._subscriptionHandle, collectionName, id, fields); // 1154
+ }, // 1155
+ // 1156
+ /** // 1157
+ * @summary Call inside the publish function. Informs the subscriber that a document in the record set has been modified.
+ * @locus Server // 1159
+ * @memberOf Subscription // 1160
+ * @instance // 1161
+ * @param {String} collection The name of the collection that contains the changed document. // 1162
+ * @param {String} id The changed document's ID. // 1163
+ * @param {Object} fields The fields in the document that have changed, together with their new values. If a field is not present in `fields` it was left unchanged; if it is present in `fields` and has a value of `undefined` it was removed from the document. If `_id` is present it is ignored.
+ */ // 1165
+ changed: function (collectionName, id, fields) { // 1166
+ var self = this; // 1167
+ if (self._isDeactivated()) // 1168
+ return; // 1169
+ id = self._idFilter.idStringify(id); // 1170
+ self._session.changed(self._subscriptionHandle, collectionName, id, fields); // 1171
+ }, // 1172
+ // 1173
+ /** // 1174
+ * @summary Call inside the publish function. Informs the subscriber that a document has been removed from the record set.
+ * @locus Server // 1176
+ * @memberOf Subscription // 1177
+ * @instance // 1178
+ * @param {String} collection The name of the collection that the document has been removed from. // 1179
+ * @param {String} id The ID of the document that has been removed. // 1180
+ */ // 1181
+ removed: function (collectionName, id) { // 1182
+ var self = this; // 1183
+ if (self._isDeactivated()) // 1184
+ return; // 1185
+ id = self._idFilter.idStringify(id); // 1186
+ // We don't bother to delete sets of things in a collection if the // 1187
+ // collection is empty. It could break _removeAllDocuments. // 1188
+ delete self._documents[collectionName][id]; // 1189
+ self._session.removed(self._subscriptionHandle, collectionName, id); // 1190
+ }, // 1191
+ // 1192
+ /** // 1193
+ * @summary Call inside the publish function. Informs the subscriber that an initial, complete snapshot of the record set has been sent. This will trigger a call on the client to the `onReady` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any.
+ * @locus Server // 1195
+ * @memberOf Subscription // 1196
+ * @instance // 1197
+ */ // 1198
+ ready: function () { // 1199
+ var self = this; // 1200
+ if (self._isDeactivated()) // 1201
+ return; // 1202
+ if (!self._subscriptionId) // 1203
+ return; // unnecessary but ignored for universal sub // 1204
+ if (!self._ready) { // 1205
+ self._session.sendReady([self._subscriptionId]); // 1206
+ self._ready = true; // 1207
+ } // 1208
+ } // 1209
+}); // 1210
+ // 1211
+/******************************************************************************/ // 1212
+/* Server */ // 1213
+/******************************************************************************/ // 1214
+ // 1215
+Server = function (options) { // 1216
+ var self = this; // 1217
+ // 1218
+ // The default heartbeat interval is 30 seconds on the server and 35 // 1219
+ // seconds on the client. Since the client doesn't need to send a // 1220
+ // ping as long as it is receiving pings, this means that pings // 1221
+ // normally go from the server to the client. // 1222
+ // // 1223
+ // Note: Troposphere depends on the ability to mutate // 1224
+ // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life. // 1225
+ self.options = _.defaults(options || {}, { // 1226
+ heartbeatInterval: 30000, // 1227
+ heartbeatTimeout: 15000, // 1228
+ // For testing, allow responding to pings to be disabled. // 1229
+ respondToPings: true // 1230
+ }); // 1231
+ // 1232
+ // Map of callbacks to call when a new connection comes in to the // 1233
+ // server and completes DDP version negotiation. Use an object instead // 1234
+ // of an array so we can safely remove one from the list while // 1235
+ // iterating over it. // 1236
+ self.onConnectionHook = new Hook({ // 1237
+ debugPrintExceptions: "onConnection callback" // 1238
+ }); // 1239
+ // 1240
+ self.publish_handlers = {}; // 1241
+ self.universal_publish_handlers = []; // 1242
+ // 1243
+ self.method_handlers = {}; // 1244
+ // 1245
+ self.sessions = {}; // map from id to session // 1246
+ // 1247
+ self.stream_server = new StreamServer; // 1248
+ // 1249
+ self.stream_server.register(function (socket) { // 1250
+ // socket implements the SockJSConnection interface // 1251
+ socket._meteorSession = null; // 1252
+ // 1253
+ var sendError = function (reason, offendingMessage) { // 1254
+ var msg = {msg: 'error', reason: reason}; // 1255
+ if (offendingMessage) // 1256
+ msg.offendingMessage = offendingMessage; // 1257
+ socket.send(stringifyDDP(msg)); // 1258
+ }; // 1259
+ // 1260
+ socket.on('data', function (raw_msg) { // 1261
+ if (Meteor._printReceivedDDP) { // 1262
+ Meteor._debug("Received DDP", raw_msg); // 1263
+ } // 1264
+ try { // 1265
+ try { // 1266
+ var msg = parseDDP(raw_msg); // 1267
+ } catch (err) { // 1268
+ sendError('Parse error'); // 1269
+ return; // 1270
+ } // 1271
+ if (msg === null || !msg.msg) { // 1272
+ sendError('Bad request', msg); // 1273
+ return; // 1274
+ } // 1275
+ // 1276
+ if (msg.msg === 'connect') { // 1277
+ if (socket._meteorSession) { // 1278
+ sendError("Already connected", msg); // 1279
+ return; // 1280
+ } // 1281
+ Fiber(function () { // 1282
+ self._handleConnect(socket, msg); // 1283
+ }).run(); // 1284
+ return; // 1285
+ } // 1286
+ // 1287
+ if (!socket._meteorSession) { // 1288
+ sendError('Must connect first', msg); // 1289
+ return; // 1290
+ } // 1291
+ socket._meteorSession.processMessage(msg); // 1292
+ } catch (e) { // 1293
+ // XXX print stack nicely // 1294
+ Meteor._debug("Internal exception while processing message", msg, // 1295
+ e.message, e.stack); // 1296
+ } // 1297
+ }); // 1298
+ // 1299
+ socket.on('close', function () { // 1300
+ if (socket._meteorSession) { // 1301
+ Fiber(function () { // 1302
+ socket._meteorSession.close(); // 1303
+ }).run(); // 1304
+ } // 1305
+ }); // 1306
+ }); // 1307
+}; // 1308
+ // 1309
+_.extend(Server.prototype, { // 1310
+ // 1311
+ /** // 1312
+ * @summary Register a callback to be called when a new DDP connection is made to the server. // 1313
+ * @locus Server // 1314
+ * @param {function} callback The function to call when a new DDP connection is established. // 1315
+ * @memberOf Meteor // 1316
+ */ // 1317
+ onConnection: function (fn) { // 1318
+ var self = this; // 1319
+ return self.onConnectionHook.register(fn); // 1320
+ }, // 1321
+ // 1322
+ _handleConnect: function (socket, msg) { // 1323
+ var self = this; // 1324
+ // 1325
+ // The connect message must specify a version and an array of supported // 1326
+ // versions, and it must claim to support what it is proposing. // 1327
+ if (!(typeof (msg.version) === 'string' && // 1328
+ _.isArray(msg.support) && // 1329
+ _.all(msg.support, _.isString) && // 1330
+ _.contains(msg.support, msg.version))) { // 1331
+ socket.send(stringifyDDP({msg: 'failed', // 1332
+ version: SUPPORTED_DDP_VERSIONS[0]})); // 1333
+ socket.close(); // 1334
+ return; // 1335
+ } // 1336
+ // 1337
+ // In the future, handle session resumption: something like: // 1338
+ // socket._meteorSession = self.sessions[msg.session] // 1339
+ var version = calculateVersion(msg.support, SUPPORTED_DDP_VERSIONS); // 1340
+ // 1341
+ if (msg.version !== version) { // 1342
+ // The best version to use (according to the client's stated preferences) // 1343
+ // is not the one the client is trying to use. Inform them about the best // 1344
+ // version to use. // 1345
+ socket.send(stringifyDDP({msg: 'failed', version: version})); // 1346
+ socket.close(); // 1347
+ return; // 1348
+ } // 1349
+ // 1350
+ // Yay, version matches! Create a new session. // 1351
+ // Note: Troposphere depends on the ability to mutate // 1352
+ // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life. // 1353
+ socket._meteorSession = new Session(self, version, socket, self.options); // 1354
+ self.sessions[socket._meteorSession.id] = socket._meteorSession; // 1355
+ self.onConnectionHook.each(function (callback) { // 1356
+ if (socket._meteorSession) // 1357
+ callback(socket._meteorSession.connectionHandle); // 1358
+ return true; // 1359
+ }); // 1360
+ }, // 1361
+ /** // 1362
+ * Register a publish handler function. // 1363
+ * // 1364
+ * @param name {String} identifier for query // 1365
+ * @param handler {Function} publish handler // 1366
+ * @param options {Object} // 1367
+ * // 1368
+ * Server will call handler function on each new subscription, // 1369
+ * either when receiving DDP sub message for a named subscription, or on // 1370
+ * DDP connect for a universal subscription. // 1371
+ * // 1372
+ * If name is null, this will be a subscription that is // 1373
+ * automatically established and permanently on for all connected // 1374
+ * client, instead of a subscription that can be turned on and off // 1375
+ * with subscribe(). // 1376
+ * // 1377
+ * options to contain: // 1378
+ * - (mostly internal) is_auto: true if generated automatically // 1379
+ * from an autopublish hook. this is for cosmetic purposes only // 1380
+ * (it lets us determine whether to print a warning suggesting // 1381
+ * that you turn off autopublish.) // 1382
+ */ // 1383
+ // 1384
+ /** // 1385
+ * @summary Publish a record set. // 1386
+ * @memberOf Meteor // 1387
+ * @locus Server // 1388
+ * @param {String} name Name of the record set. If `null`, the set has no name, and the record set is automatically sent to all connected clients.
+ * @param {Function} func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to `subscribe`, the function is called with the same arguments.
+ */ // 1391
+ publish: function (name, handler, options) { // 1392
+ var self = this; // 1393
+ // 1394
+ options = options || {}; // 1395
+ // 1396
+ if (name && name in self.publish_handlers) { // 1397
+ Meteor._debug("Ignoring duplicate publish named '" + name + "'"); // 1398
+ return; // 1399
+ } // 1400
+ // 1401
+ if (Package.autopublish && !options.is_auto) { // 1402
+ // They have autopublish on, yet they're trying to manually // 1403
+ // picking stuff to publish. They probably should turn off // 1404
+ // autopublish. (This check isn't perfect -- if you create a // 1405
+ // publish before you turn on autopublish, it won't catch // 1406
+ // it. But this will definitely handle the simple case where // 1407
+ // you've added the autopublish package to your app, and are // 1408
+ // calling publish from your app code.) // 1409
+ if (!self.warned_about_autopublish) { // 1410
+ self.warned_about_autopublish = true; // 1411
+ Meteor._debug( // 1412
+"** You've set up some data subscriptions with Meteor.publish(), but\n" + // 1413
+"** you still have autopublish turned on. Because autopublish is still\n" + // 1414
+"** on, your Meteor.publish() calls won't have much effect. All data\n" + // 1415
+"** will still be sent to all clients.\n" + // 1416
+"**\n" + // 1417
+"** Turn off autopublish by removing the autopublish package:\n" + // 1418
+"**\n" + // 1419
+"** $ meteor remove autopublish\n" + // 1420
+"**\n" + // 1421
+"** .. and make sure you have Meteor.publish() and Meteor.subscribe() calls\n" + // 1422
+"** for each collection that you want clients to see.\n"); // 1423
+ } // 1424
+ } // 1425
+ // 1426
+ if (name) // 1427
+ self.publish_handlers[name] = handler; // 1428
+ else { // 1429
+ self.universal_publish_handlers.push(handler); // 1430
+ // Spin up the new publisher on any existing session too. Run each // 1431
+ // session's subscription in a new Fiber, so that there's no change for // 1432
+ // self.sessions to change while we're running this loop. // 1433
+ _.each(self.sessions, function (session) { // 1434
+ if (!session._dontStartNewUniversalSubs) { // 1435
+ Fiber(function() { // 1436
+ session._startSubscription(handler); // 1437
+ }).run(); // 1438
+ } // 1439
+ }); // 1440
+ } // 1441
+ }, // 1442
+ // 1443
+ _removeSession: function (session) { // 1444
+ var self = this; // 1445
+ if (self.sessions[session.id]) { // 1446
+ delete self.sessions[session.id]; // 1447
+ } // 1448
+ }, // 1449
+ // 1450
+ /** // 1451
+ * @summary Defines functions that can be invoked over the network by clients. // 1452
+ * @locus Anywhere // 1453
+ * @param {Object} methods Dictionary whose keys are method names and values are functions. // 1454
+ * @memberOf Meteor // 1455
+ */ // 1456
+ methods: function (methods) { // 1457
+ var self = this; // 1458
+ _.each(methods, function (func, name) { // 1459
+ if (self.method_handlers[name]) // 1460
+ throw new Error("A method named '" + name + "' is already defined"); // 1461
+ self.method_handlers[name] = func; // 1462
+ }); // 1463
+ }, // 1464
+ // 1465
+ call: function (name /*, arguments */) { // 1466
+ // if it's a function, the last argument is the result callback, // 1467
+ // not a parameter to the remote method. // 1468
+ var args = Array.prototype.slice.call(arguments, 1); // 1469
+ if (args.length && typeof args[args.length - 1] === "function") // 1470
+ var callback = args.pop(); // 1471
+ return this.apply(name, args, callback); // 1472
+ }, // 1473
+ // 1474
+ // @param options {Optional Object} // 1475
+ // @param callback {Optional Function} // 1476
+ apply: function (name, args, options, callback) { // 1477
+ var self = this; // 1478
+ // 1479
+ // We were passed 3 arguments. They may be either (name, args, options) // 1480
+ // or (name, args, callback) // 1481
+ if (!callback && typeof options === 'function') { // 1482
+ callback = options; // 1483
+ options = {}; // 1484
+ } // 1485
+ options = options || {}; // 1486
+ // 1487
+ if (callback) // 1488
+ // It's not really necessary to do this, since we immediately // 1489
+ // run the callback in this fiber before returning, but we do it // 1490
+ // anyway for regularity. // 1491
+ // XXX improve error message (and how we report it) // 1492
+ callback = Meteor.bindEnvironment( // 1493
+ callback, // 1494
+ "delivering result of invoking '" + name + "'" // 1495
+ ); // 1496
+ // 1497
+ // Run the handler // 1498
+ var handler = self.method_handlers[name]; // 1499
+ var exception; // 1500
+ if (!handler) { // 1501
+ exception = new Meteor.Error(404, "Method not found"); // 1502
+ } else { // 1503
+ // If this is a method call from within another method, get the // 1504
+ // user state from the outer method, otherwise don't allow // 1505
+ // setUserId to be called // 1506
+ var userId = null; // 1507
+ var setUserId = function() { // 1508
+ throw new Error("Can't call setUserId on a server initiated method call"); // 1509
+ }; // 1510
+ var connection = null; // 1511
+ var currentInvocation = DDP._CurrentInvocation.get(); // 1512
+ if (currentInvocation) { // 1513
+ userId = currentInvocation.userId; // 1514
+ setUserId = function(userId) { // 1515
+ currentInvocation.setUserId(userId); // 1516
+ }; // 1517
+ connection = currentInvocation.connection; // 1518
+ } // 1519
+ // 1520
+ var invocation = new MethodInvocation({ // 1521
+ isSimulation: false, // 1522
+ userId: userId, // 1523
+ setUserId: setUserId, // 1524
+ connection: connection, // 1525
+ randomSeed: makeRpcSeed(currentInvocation, name) // 1526
+ }); // 1527
+ try { // 1528
+ var result = DDP._CurrentInvocation.withValue(invocation, function () { // 1529
+ return maybeAuditArgumentChecks( // 1530
+ handler, invocation, EJSON.clone(args), "internal call to '" + // 1531
+ name + "'"); // 1532
+ }); // 1533
+ result = EJSON.clone(result); // 1534
+ } catch (e) { // 1535
+ exception = e; // 1536
+ } // 1537
+ } // 1538
+ // 1539
+ // Return the result in whichever way the caller asked for it. Note that we // 1540
+ // do NOT block on the write fence in an analogous way to how the client // 1541
+ // blocks on the relevant data being visible, so you are NOT guaranteed that // 1542
+ // cursor observe callbacks have fired when your callback is invoked. (We // 1543
+ // can change this if there's a real use case.) // 1544
+ if (callback) { // 1545
+ callback(exception, result); // 1546
+ return undefined; // 1547
+ } // 1548
+ if (exception) // 1549
+ throw exception; // 1550
+ return result; // 1551
+ }, // 1552
+ // 1553
+ _urlForSession: function (sessionId) { // 1554
+ var self = this; // 1555
+ var session = self.sessions[sessionId]; // 1556
+ if (session) // 1557
+ return session._socketUrl; // 1558
+ else // 1559
+ return null; // 1560
+ } // 1561
+}); // 1562
+ // 1563
+var calculateVersion = function (clientSupportedVersions, // 1564
+ serverSupportedVersions) { // 1565
+ var correctVersion = _.find(clientSupportedVersions, function (version) { // 1566
+ return _.contains(serverSupportedVersions, version); // 1567
+ }); // 1568
+ if (!correctVersion) { // 1569
+ correctVersion = serverSupportedVersions[0]; // 1570
+ } // 1571
+ return correctVersion; // 1572
+}; // 1573
+ // 1574
+LivedataTest.calculateVersion = calculateVersion; // 1575
+ // 1576
+ // 1577
+// "blind" exceptions other than those that were deliberately thrown to signal // 1578
+// errors to the client // 1579
+var wrapInternalException = function (exception, context) { // 1580
+ if (!exception || exception instanceof Meteor.Error) // 1581
+ return exception; // 1582
+ // 1583
+ // tests can set the 'expected' flag on an exception so it won't go to the // 1584
+ // server log // 1585
+ if (!exception.expected) { // 1586
+ Meteor._debug("Exception " + context, exception.stack); // 1587
+ if (exception.sanitizedError) { // 1588
+ Meteor._debug("Sanitized and reported to the client as:", exception.sanitizedError.message); // 1589
+ Meteor._debug(); // 1590
+ } // 1591
+ } // 1592
+ // 1593
+ // Did the error contain more details that could have been useful if caught in // 1594
+ // server code (or if thrown from non-client-originated code), but also // 1595
+ // provided a "sanitized" version with more context than 500 Internal server // 1596
+ // error? Use that. // 1597
+ if (exception.sanitizedError) { // 1598
+ if (exception.sanitizedError instanceof Meteor.Error) // 1599
+ return exception.sanitizedError; // 1600
+ Meteor._debug("Exception " + context + " provides a sanitizedError that " + // 1601
+ "is not a Meteor.Error; ignoring"); // 1602
+ } // 1603
+ // 1604
+ return new Meteor.Error(500, "Internal server error"); // 1605
+}; // 1606
+ // 1607
+ // 1608
+// Audit argument checks, if the audit-argument-checks package exists (it is a // 1609
+// weak dependency of this package). // 1610
+var maybeAuditArgumentChecks = function (f, context, args, description) { // 1611
+ args = args || []; // 1612
+ if (Package['audit-argument-checks']) { // 1613
+ return Match._failIfArgumentsAreNotAllChecked( // 1614
+ f, context, args, description); // 1615
+ } // 1616
+ return f.apply(context, args); // 1617
+}; // 1618
+ // 1619
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/writefence.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+var path = Npm.require('path'); // 1
+var Future = Npm.require(path.join('fibers', 'future')); // 2
+ // 3
+// A write fence collects a group of writes, and provides a callback // 4
+// when all of the writes are fully committed and propagated (all // 5
+// observers have been notified of the write and acknowledged it.) // 6
+// // 7
+DDPServer._WriteFence = function () { // 8
+ var self = this; // 9
+ // 10
+ self.armed = false; // 11
+ self.fired = false; // 12
+ self.retired = false; // 13
+ self.outstanding_writes = 0; // 14
+ self.completion_callbacks = []; // 15
+}; // 16
+ // 17
+// The current write fence. When there is a current write fence, code // 18
+// that writes to databases should register their writes with it using // 19
+// beginWrite(). // 20
+// // 21
+DDPServer._CurrentWriteFence = new Meteor.EnvironmentVariable; // 22
+ // 23
+_.extend(DDPServer._WriteFence.prototype, { // 24
+ // Start tracking a write, and return an object to represent it. The // 25
+ // object has a single method, committed(). This method should be // 26
+ // called when the write is fully committed and propagated. You can // 27
+ // continue to add writes to the WriteFence up until it is triggered // 28
+ // (calls its callbacks because all writes have committed.) // 29
+ beginWrite: function () { // 30
+ var self = this; // 31
+ // 32
+ if (self.retired) // 33
+ return { committed: function () {} }; // 34
+ // 35
+ if (self.fired) // 36
+ throw new Error("fence has already activated -- too late to add writes"); // 37
+ // 38
+ self.outstanding_writes++; // 39
+ var committed = false; // 40
+ return { // 41
+ committed: function () { // 42
+ if (committed) // 43
+ throw new Error("committed called twice on the same write"); // 44
+ committed = true; // 45
+ self.outstanding_writes--; // 46
+ self._maybeFire(); // 47
+ } // 48
+ }; // 49
+ }, // 50
+ // 51
+ // Arm the fence. Once the fence is armed, and there are no more // 52
+ // uncommitted writes, it will activate. // 53
+ arm: function () { // 54
+ var self = this; // 55
+ if (self === DDPServer._CurrentWriteFence.get()) // 56
+ throw Error("Can't arm the current fence"); // 57
+ self.armed = true; // 58
+ self._maybeFire(); // 59
+ }, // 60
+ // 61
+ // Register a function to be called when the fence fires. // 62
+ onAllCommitted: function (func) { // 63
+ var self = this; // 64
+ if (self.fired) // 65
+ throw new Error("fence has already activated -- too late to " + // 66
+ "add a callback"); // 67
+ self.completion_callbacks.push(func); // 68
+ }, // 69
+ // 70
+ // Convenience function. Arms the fence, then blocks until it fires. // 71
+ armAndWait: function () { // 72
+ var self = this; // 73
+ var future = new Future; // 74
+ self.onAllCommitted(function () { // 75
+ future['return'](); // 76
+ }); // 77
+ self.arm(); // 78
+ future.wait(); // 79
+ }, // 80
+ // 81
+ _maybeFire: function () { // 82
+ var self = this; // 83
+ if (self.fired) // 84
+ throw new Error("write fence already activated?"); // 85
+ if (self.armed && !self.outstanding_writes) { // 86
+ self.fired = true; // 87
+ _.each(self.completion_callbacks, function (f) {f(self);}); // 88
+ self.completion_callbacks = []; // 89
+ } // 90
+ }, // 91
+ // 92
+ // Deactivate this fence so that adding more writes has no effect. // 93
+ // The fence must have already fired. // 94
+ retire: function () { // 95
+ var self = this; // 96
+ if (! self.fired) // 97
+ throw new Error("Can't retire a fence that hasn't fired."); // 98
+ self.retired = true; // 99
+ } // 100
+}); // 101
+ // 102
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/crossbar.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// A "crossbar" is a class that provides structured notification registration. // 1
+// See _match for the definition of how a notification matches a trigger. // 2
+// All notifications and triggers must have a string key named 'collection'. // 3
+ // 4
+DDPServer._Crossbar = function (options) { // 5
+ var self = this; // 6
+ options = options || {}; // 7
+ // 8
+ self.nextId = 1; // 9
+ // map from collection name (string) -> listener id -> object. each object has // 10
+ // keys 'trigger', 'callback'. // 11
+ self.listenersByCollection = {}; // 12
+ self.factPackage = options.factPackage || "livedata"; // 13
+ self.factName = options.factName || null; // 14
+}; // 15
+ // 16
+_.extend(DDPServer._Crossbar.prototype, { // 17
+ // Listen for notification that match 'trigger'. A notification // 18
+ // matches if it has the key-value pairs in trigger as a // 19
+ // subset. When a notification matches, call 'callback', passing // 20
+ // the actual notification. // 21
+ // // 22
+ // Returns a listen handle, which is an object with a method // 23
+ // stop(). Call stop() to stop listening. // 24
+ // // 25
+ // XXX It should be legal to call fire() from inside a listen() // 26
+ // callback? // 27
+ listen: function (trigger, callback) { // 28
+ var self = this; // 29
+ var id = self.nextId++; // 30
+ // 31
+ if (typeof(trigger.collection) !== 'string') { // 32
+ throw Error("Trigger lacks collection!"); // 33
+ } // 34
+ // 35
+ var collection = trigger.collection; // save in case trigger is mutated // 36
+ var record = {trigger: EJSON.clone(trigger), callback: callback}; // 37
+ if (! _.has(self.listenersByCollection, collection)) { // 38
+ self.listenersByCollection[collection] = {}; // 39
+ } // 40
+ self.listenersByCollection[collection][id] = record; // 41
+ // 42
+ if (self.factName && Package.facts) { // 43
+ Package.facts.Facts.incrementServerFact( // 44
+ self.factPackage, self.factName, 1); // 45
+ } // 46
+ // 47
+ return { // 48
+ stop: function () { // 49
+ if (self.factName && Package.facts) { // 50
+ Package.facts.Facts.incrementServerFact( // 51
+ self.factPackage, self.factName, -1); // 52
+ } // 53
+ delete self.listenersByCollection[collection][id]; // 54
+ if (_.isEmpty(self.listenersByCollection[collection])) { // 55
+ delete self.listenersByCollection[collection]; // 56
+ } // 57
+ } // 58
+ }; // 59
+ }, // 60
+ // 61
+ // Fire the provided 'notification' (an object whose attribute // 62
+ // values are all JSON-compatibile) -- inform all matching listeners // 63
+ // (registered with listen()). // 64
+ // // 65
+ // If fire() is called inside a write fence, then each of the // 66
+ // listener callbacks will be called inside the write fence as well. // 67
+ // // 68
+ // The listeners may be invoked in parallel, rather than serially. // 69
+ fire: function (notification) { // 70
+ var self = this; // 71
+ // 72
+ if (typeof(notification.collection) !== 'string') { // 73
+ throw Error("Notification lacks collection!"); // 74
+ } // 75
+ // 76
+ if (! _.has(self.listenersByCollection, notification.collection)) // 77
+ return; // 78
+ // 79
+ var listenersForCollection = // 80
+ self.listenersByCollection[notification.collection]; // 81
+ var callbackIds = []; // 82
+ _.each(listenersForCollection, function (l, id) { // 83
+ if (self._matches(notification, l.trigger)) { // 84
+ callbackIds.push(id); // 85
+ } // 86
+ }); // 87
+ // 88
+ // Listener callbacks can yield, so we need to first find all the ones that // 89
+ // match in a single iteration over self.listenersByCollection (which can't // 90
+ // be mutated during this iteration), and then invoke the matching // 91
+ // callbacks, checking before each call to ensure they haven't stopped. // 92
+ // Note that we don't have to check that // 93
+ // self.listenersByCollection[notification.collection] still === // 94
+ // listenersForCollection, because the only way that stops being true is if // 95
+ // listenersForCollection first gets reduced down to the empty object (and // 96
+ // then never gets increased again). // 97
+ _.each(callbackIds, function (id) { // 98
+ if (_.has(listenersForCollection, id)) { // 99
+ listenersForCollection[id].callback(notification); // 100
+ } // 101
+ }); // 102
+ }, // 103
+ // 104
+ // A notification matches a trigger if all keys that exist in both are equal. // 105
+ // // 106
+ // Examples: // 107
+ // N:{collection: "C"} matches T:{collection: "C"} // 108
+ // (a non-targeted write to a collection matches a // 109
+ // non-targeted query) // 110
+ // N:{collection: "C", id: "X"} matches T:{collection: "C"} // 111
+ // (a targeted write to a collection matches a non-targeted query) // 112
+ // N:{collection: "C"} matches T:{collection: "C", id: "X"} // 113
+ // (a non-targeted write to a collection matches a // 114
+ // targeted query) // 115
+ // N:{collection: "C", id: "X"} matches T:{collection: "C", id: "X"} // 116
+ // (a targeted write to a collection matches a targeted query targeted // 117
+ // at the same document) // 118
+ // N:{collection: "C", id: "X"} does not match T:{collection: "C", id: "Y"} // 119
+ // (a targeted write to a collection does not match a targeted query // 120
+ // targeted at a different document) // 121
+ _matches: function (notification, trigger) { // 122
+ // Most notifications that use the crossbar have a string `collection` and // 123
+ // maybe an `id` that is a string or ObjectID. We're already dividing up // 124
+ // triggers by collection, but let's fast-track "nope, different ID" (and // 125
+ // avoid the overly generic EJSON.equals). This makes a noticeable // 126
+ // performance difference; see https://github.com/meteor/meteor/pull/3697 // 127
+ if (typeof(notification.id) === 'string' && // 128
+ typeof(trigger.id) === 'string' && // 129
+ notification.id !== trigger.id) { // 130
+ return false; // 131
+ } // 132
+ if (notification.id instanceof LocalCollection._ObjectID && // 133
+ trigger.id instanceof LocalCollection._ObjectID && // 134
+ ! notification.id.equals(trigger.id)) { // 135
+ return false; // 136
+ } // 137
+ // 138
+ return _.all(trigger, function (triggerValue, key) { // 139
+ return !_.has(notification, key) || // 140
+ EJSON.equals(triggerValue, notification[key]); // 141
+ }); // 142
+ } // 143
+}); // 144
+ // 145
+// The "invalidation crossbar" is a specific instance used by the DDP server to // 146
+// implement write fence notifications. Listener callbacks on this crossbar // 147
+// should call beginWrite on the current write fence before they return, if they // 148
+// want to delay the write fence from firing (ie, the DDP method-data-updated // 149
+// message from being sent). // 150
+DDPServer._InvalidationCrossbar = new DDPServer._Crossbar({ // 151
+ factName: "invalidation-crossbar-listeners" // 152
+}); // 153
+ // 154
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/livedata_common.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// All the supported versions (for both the client and server) // 1
+// These must be in order of preference; most favored-first // 2
+SUPPORTED_DDP_VERSIONS = [ '1', 'pre2', 'pre1' ]; // 3
+ // 4
+LivedataTest.SUPPORTED_DDP_VERSIONS = SUPPORTED_DDP_VERSIONS; // 5
+ // 6
+// Instance name is this because it is usually referred to as this inside a // 7
+// method definition // 8
+/** // 9
+ * @summary The state for a single invocation of a method, referenced by this // 10
+ * inside a method definition. // 11
+ * @param {Object} options // 12
+ * @instanceName this // 13
+ */ // 14
+MethodInvocation = function (options) { // 15
+ var self = this; // 16
+ // 17
+ // true if we're running not the actual method, but a stub (that is, // 18
+ // if we're on a client (which may be a browser, or in the future a // 19
+ // server connecting to another server) and presently running a // 20
+ // simulation of a server-side method for latency compensation // 21
+ // purposes). not currently true except in a client such as a browser, // 22
+ // since there's usually no point in running stubs unless you have a // 23
+ // zero-latency connection to the user. // 24
+ // 25
+ /** // 26
+ * @summary Access inside a method invocation. Boolean value, true if this invocation is a stub. // 27
+ * @locus Anywhere // 28
+ * @name isSimulation // 29
+ * @memberOf MethodInvocation // 30
+ * @instance // 31
+ * @type {Boolean} // 32
+ */ // 33
+ this.isSimulation = options.isSimulation; // 34
+ // 35
+ // call this function to allow other method invocations (from the // 36
+ // same client) to continue running without waiting for this one to // 37
+ // complete. // 38
+ this._unblock = options.unblock || function () {}; // 39
+ this._calledUnblock = false; // 40
+ // 41
+ // current user id // 42
+ // 43
+ /** // 44
+ * @summary The id of the user that made this method call, or `null` if no user was logged in. // 45
+ * @locus Anywhere // 46
+ * @name userId // 47
+ * @memberOf MethodInvocation // 48
+ * @instance // 49
+ */ // 50
+ this.userId = options.userId; // 51
+ // 52
+ // sets current user id in all appropriate server contexts and // 53
+ // reruns subscriptions // 54
+ this._setUserId = options.setUserId || function () {}; // 55
+ // 56
+ // On the server, the connection this method call came in on. // 57
+ // 58
+ /** // 59
+ * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call.
+ * @locus Server // 61
+ * @name connection // 62
+ * @memberOf MethodInvocation // 63
+ * @instance // 64
+ */ // 65
+ this.connection = options.connection; // 66
+ // 67
+ // The seed for randomStream value generation // 68
+ this.randomSeed = options.randomSeed; // 69
+ // 70
+ // This is set by RandomStream.get; and holds the random stream state // 71
+ this.randomStream = null; // 72
+}; // 73
+ // 74
+_.extend(MethodInvocation.prototype, { // 75
+ /** // 76
+ * @summary Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber.
+ * @locus Server // 78
+ * @memberOf MethodInvocation // 79
+ * @instance // 80
+ */ // 81
+ unblock: function () { // 82
+ var self = this; // 83
+ self._calledUnblock = true; // 84
+ self._unblock(); // 85
+ }, // 86
+ // 87
+ /** // 88
+ * @summary Set the logged in user. // 89
+ * @locus Server // 90
+ * @memberOf MethodInvocation // 91
+ * @instance // 92
+ * @param {String | null} userId The value that should be returned by `userId` on this connection. // 93
+ */ // 94
+ setUserId: function(userId) { // 95
+ var self = this; // 96
+ if (self._calledUnblock) // 97
+ throw new Error("Can't call setUserId in a method after calling unblock"); // 98
+ self.userId = userId; // 99
+ self._setUserId(userId); // 100
+ } // 101
+}); // 102
+ // 103
+parseDDP = function (stringMessage) { // 104
+ try { // 105
+ var msg = JSON.parse(stringMessage); // 106
+ } catch (e) { // 107
+ Meteor._debug("Discarding message with invalid JSON", stringMessage); // 108
+ return null; // 109
+ } // 110
+ // DDP messages must be objects. // 111
+ if (msg === null || typeof msg !== 'object') { // 112
+ Meteor._debug("Discarding non-object DDP message", stringMessage); // 113
+ return null; // 114
+ } // 115
+ // 116
+ // massage msg to get it into "abstract ddp" rather than "wire ddp" format. // 117
+ // 118
+ // switch between "cleared" rep of unsetting fields and "undefined" // 119
+ // rep of same // 120
+ if (_.has(msg, 'cleared')) { // 121
+ if (!_.has(msg, 'fields')) // 122
+ msg.fields = {}; // 123
+ _.each(msg.cleared, function (clearKey) { // 124
+ msg.fields[clearKey] = undefined; // 125
+ }); // 126
+ delete msg.cleared; // 127
+ } // 128
+ // 129
+ _.each(['fields', 'params', 'result'], function (field) { // 130
+ if (_.has(msg, field)) // 131
+ msg[field] = EJSON._adjustTypesFromJSONValue(msg[field]); // 132
+ }); // 133
+ // 134
+ return msg; // 135
+}; // 136
+ // 137
+stringifyDDP = function (msg) { // 138
+ var copy = EJSON.clone(msg); // 139
+ // swizzle 'changed' messages from 'fields undefined' rep to 'fields // 140
+ // and cleared' rep // 141
+ if (_.has(msg, 'fields')) { // 142
+ var cleared = []; // 143
+ _.each(msg.fields, function (value, key) { // 144
+ if (value === undefined) { // 145
+ cleared.push(key); // 146
+ delete copy.fields[key]; // 147
+ } // 148
+ }); // 149
+ if (!_.isEmpty(cleared)) // 150
+ copy.cleared = cleared; // 151
+ if (_.isEmpty(copy.fields)) // 152
+ delete copy.fields; // 153
+ } // 154
+ // adjust types to basic // 155
+ _.each(['fields', 'params', 'result'], function (field) { // 156
+ if (_.has(copy, field)) // 157
+ copy[field] = EJSON._adjustTypesToJSONValue(copy[field]); // 158
+ }); // 159
+ if (msg.id && typeof msg.id !== 'string') { // 160
+ throw new Error("Message id is not a string"); // 161
+ } // 162
+ return JSON.stringify(copy); // 163
+}; // 164
+ // 165
+// This is private but it's used in a few places. accounts-base uses // 166
+// it to get the current user. accounts-password uses it to stash SRP // 167
+// state in the DDP session. Meteor.setTimeout and friends clear // 168
+// it. We can probably find a better way to factor this. // 169
+DDP._CurrentInvocation = new Meteor.EnvironmentVariable; // 170
+ // 171
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/random_stream.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// RandomStream allows for generation of pseudo-random values, from a seed. // 1
+// // 2
+// We use this for consistent 'random' numbers across the client and server. // 3
+// We want to generate probably-unique IDs on the client, and we ideally want // 4
+// the server to generate the same IDs when it executes the method. // 5
+// // 6
+// For generated values to be the same, we must seed ourselves the same way, // 7
+// and we must keep track of the current state of our pseudo-random generators. // 8
+// We call this state the scope. By default, we use the current DDP method // 9
+// invocation as our scope. DDP now allows the client to specify a randomSeed. // 10
+// If a randomSeed is provided it will be used to seed our random sequences. // 11
+// In this way, client and server method calls will generate the same values. // 12
+// // 13
+// We expose multiple named streams; each stream is independent // 14
+// and is seeded differently (but predictably from the name). // 15
+// By using multiple streams, we support reordering of requests, // 16
+// as long as they occur on different streams. // 17
+// // 18
+// @param options {Optional Object} // 19
+// seed: Array or value - Seed value(s) for the generator. // 20
+// If an array, will be used as-is // 21
+// If a value, will be converted to a single-value array // 22
+// If omitted, a random array will be used as the seed. // 23
+RandomStream = function (options) { // 24
+ var self = this; // 25
+ // 26
+ this.seed = [].concat(options.seed || randomToken()); // 27
+ // 28
+ this.sequences = {}; // 29
+}; // 30
+ // 31
+// Returns a random string of sufficient length for a random seed. // 32
+// This is a placeholder function; a similar function is planned // 33
+// for Random itself; when that is added we should remove this function, // 34
+// and call Random's randomToken instead. // 35
+function randomToken() { // 36
+ return Random.hexString(20); // 37
+}; // 38
+ // 39
+// Returns the random stream with the specified name, in the specified scope. // 40
+// If scope is null (or otherwise falsey) then we will use Random, which will // 41
+// give us as random numbers as possible, but won't produce the same // 42
+// values across client and server. // 43
+// However, scope will normally be the current DDP method invocation, so // 44
+// we'll use the stream with the specified name, and we should get consistent // 45
+// values on the client and server sides of a method call. // 46
+RandomStream.get = function (scope, name) { // 47
+ if (!name) { // 48
+ name = "default"; // 49
+ } // 50
+ if (!scope) { // 51
+ // There was no scope passed in; // 52
+ // the sequence won't actually be reproducible. // 53
+ return Random; // 54
+ } // 55
+ var randomStream = scope.randomStream; // 56
+ if (!randomStream) { // 57
+ scope.randomStream = randomStream = new RandomStream({ // 58
+ seed: scope.randomSeed // 59
+ }); // 60
+ } // 61
+ return randomStream._sequence(name); // 62
+}; // 63
+ // 64
+// Returns the named sequence of pseudo-random values. // 65
+// The scope will be DDP._CurrentInvocation.get(), so the stream will produce // 66
+// consistent values for method calls on the client and server. // 67
+DDP.randomStream = function (name) { // 68
+ var scope = DDP._CurrentInvocation.get(); // 69
+ return RandomStream.get(scope, name); // 70
+}; // 71
+ // 72
+// Creates a randomSeed for passing to a method call. // 73
+// Note that we take enclosing as an argument, // 74
+// though we expect it to be DDP._CurrentInvocation.get() // 75
+// However, we often evaluate makeRpcSeed lazily, and thus the relevant // 76
+// invocation may not be the one currently in scope. // 77
+// If enclosing is null, we'll use Random and values won't be repeatable. // 78
+makeRpcSeed = function (enclosing, methodName) { // 79
+ var stream = RandomStream.get(enclosing, '/rpc/' + methodName); // 80
+ return stream.hexString(20); // 81
+}; // 82
+ // 83
+_.extend(RandomStream.prototype, { // 84
+ // Get a random sequence with the specified name, creating it if does not exist. // 85
+ // New sequences are seeded with the seed concatenated with the name. // 86
+ // By passing a seed into Random.create, we use the Alea generator. // 87
+ _sequence: function (name) { // 88
+ var self = this; // 89
+ // 90
+ var sequence = self.sequences[name] || null; // 91
+ if (sequence === null) { // 92
+ var sequenceSeed = self.seed.concat(name); // 93
+ for (var i = 0; i < sequenceSeed.length; i++) { // 94
+ if (_.isFunction(sequenceSeed[i])) { // 95
+ sequenceSeed[i] = sequenceSeed[i](); // 96
+ } // 97
+ } // 98
+ self.sequences[name] = sequence = Random.createWithSeeds.apply(null, sequenceSeed); // 99
+ } // 100
+ return sequence; // 101
+ } // 102
+}); // 103
+ // 104
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/livedata_connection.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+if (Meteor.isServer) { // 1
+ var path = Npm.require('path'); // 2
+ var Fiber = Npm.require('fibers'); // 3
+ var Future = Npm.require(path.join('fibers', 'future')); // 4
+} // 5
+ // 6
+// @param url {String|Object} URL to Meteor app, // 7
+// or an object as a test hook (see code) // 8
+// Options: // 9
+// reloadWithOutstanding: is it OK to reload if there are outstanding methods? // 10
+// headers: extra headers to send on the websockets connection, for // 11
+// server-to-server DDP only // 12
+// _sockjsOptions: Specifies options to pass through to the sockjs client // 13
+// onDDPNegotiationVersionFailure: callback when version negotiation fails. // 14
+// // 15
+// XXX There should be a way to destroy a DDP connection, causing all // 16
+// outstanding method calls to fail. // 17
+// // 18
+// XXX Our current way of handling failure and reconnection is great // 19
+// for an app (where we want to tolerate being disconnected as an // 20
+// expect state, and keep trying forever to reconnect) but cumbersome // 21
+// for something like a command line tool that wants to make a // 22
+// connection, call a method, and print an error if connection // 23
+// fails. We should have better usability in the latter case (while // 24
+// still transparently reconnecting if it's just a transient failure // 25
+// or the server migrating us). // 26
+var Connection = function (url, options) { // 27
+ var self = this; // 28
+ options = _.extend({ // 29
+ onConnected: function () {}, // 30
+ onDDPVersionNegotiationFailure: function (description) { // 31
+ Meteor._debug(description); // 32
+ }, // 33
+ heartbeatInterval: 35000, // 34
+ heartbeatTimeout: 15000, // 35
+ // These options are only for testing. // 36
+ reloadWithOutstanding: false, // 37
+ supportedDDPVersions: SUPPORTED_DDP_VERSIONS, // 38
+ retry: true, // 39
+ respondToPings: true // 40
+ }, options); // 41
+ // 42
+ // If set, called when we reconnect, queuing method calls _before_ the // 43
+ // existing outstanding ones. This is the only data member that is part of the // 44
+ // public API! // 45
+ self.onReconnect = null; // 46
+ // 47
+ // as a test hook, allow passing a stream instead of a url. // 48
+ if (typeof url === "object") { // 49
+ self._stream = url; // 50
+ } else { // 51
+ self._stream = new LivedataTest.ClientStream(url, { // 52
+ retry: options.retry, // 53
+ headers: options.headers, // 54
+ _sockjsOptions: options._sockjsOptions, // 55
+ // Used to keep some tests quiet, or for other cases in which // 56
+ // the right thing to do with connection errors is to silently // 57
+ // fail (e.g. sending package usage stats). At some point we // 58
+ // should have a real API for handling client-stream-level // 59
+ // errors. // 60
+ _dontPrintErrors: options._dontPrintErrors, // 61
+ connectTimeoutMs: options.connectTimeoutMs // 62
+ }); // 63
+ } // 64
+ // 65
+ self._lastSessionId = null; // 66
+ self._versionSuggestion = null; // The last proposed DDP version. // 67
+ self._version = null; // The DDP version agreed on by client and server. // 68
+ self._stores = {}; // name -> object with methods // 69
+ self._methodHandlers = {}; // name -> func // 70
+ self._nextMethodId = 1; // 71
+ self._supportedDDPVersions = options.supportedDDPVersions; // 72
+ // 73
+ self._heartbeatInterval = options.heartbeatInterval; // 74
+ self._heartbeatTimeout = options.heartbeatTimeout; // 75
+ // 76
+ // Tracks methods which the user has tried to call but which have not yet // 77
+ // called their user callback (ie, they are waiting on their result or for all // 78
+ // of their writes to be written to the local cache). Map from method ID to // 79
+ // MethodInvoker object. // 80
+ self._methodInvokers = {}; // 81
+ // 82
+ // Tracks methods which the user has called but whose result messages have not // 83
+ // arrived yet. // 84
+ // // 85
+ // _outstandingMethodBlocks is an array of blocks of methods. Each block // 86
+ // represents a set of methods that can run at the same time. The first block // 87
+ // represents the methods which are currently in flight; subsequent blocks // 88
+ // must wait for previous blocks to be fully finished before they can be sent // 89
+ // to the server. // 90
+ // // 91
+ // Each block is an object with the following fields: // 92
+ // - methods: a list of MethodInvoker objects // 93
+ // - wait: a boolean; if true, this block had a single method invoked with // 94
+ // the "wait" option // 95
+ // // 96
+ // There will never be adjacent blocks with wait=false, because the only thing // 97
+ // that makes methods need to be serialized is a wait method. // 98
+ // // 99
+ // Methods are removed from the first block when their "result" is // 100
+ // received. The entire first block is only removed when all of the in-flight // 101
+ // methods have received their results (so the "methods" list is empty) *AND* // 102
+ // all of the data written by those methods are visible in the local cache. So // 103
+ // it is possible for the first block's methods list to be empty, if we are // 104
+ // still waiting for some objects to quiesce. // 105
+ // // 106
+ // Example: // 107
+ // _outstandingMethodBlocks = [ // 108
+ // {wait: false, methods: []}, // 109
+ // {wait: true, methods: []}, // 110
+ // {wait: false, methods: [, // 111
+ // ]}] // 112
+ // This means that there were some methods which were sent to the server and // 113
+ // which have returned their results, but some of the data written by // 114
+ // the methods may not be visible in the local cache. Once all that data is // 115
+ // visible, we will send a 'login' method. Once the login method has returned // 116
+ // and all the data is visible (including re-running subs if userId changes), // 117
+ // we will send the 'foo' and 'bar' methods in parallel. // 118
+ self._outstandingMethodBlocks = []; // 119
+ // 120
+ // method ID -> array of objects with keys 'collection' and 'id', listing // 121
+ // documents written by a given method's stub. keys are associated with // 122
+ // methods whose stub wrote at least one document, and whose data-done message // 123
+ // has not yet been received. // 124
+ self._documentsWrittenByStub = {}; // 125
+ // collection -> IdMap of "server document" object. A "server document" has: // 126
+ // - "document": the version of the document according the // 127
+ // server (ie, the snapshot before a stub wrote it, amended by any changes // 128
+ // received from the server) // 129
+ // It is undefined if we think the document does not exist // 130
+ // - "writtenByStubs": a set of method IDs whose stubs wrote to the document // 131
+ // whose "data done" messages have not yet been processed // 132
+ self._serverDocuments = {}; // 133
+ // 134
+ // Array of callbacks to be called after the next update of the local // 135
+ // cache. Used for: // 136
+ // - Calling methodInvoker.dataVisible and sub ready callbacks after // 137
+ // the relevant data is flushed. // 138
+ // - Invoking the callbacks of "half-finished" methods after reconnect // 139
+ // quiescence. Specifically, methods whose result was received over the old // 140
+ // connection (so we don't re-send it) but whose data had not been made // 141
+ // visible. // 142
+ self._afterUpdateCallbacks = []; // 143
+ // 144
+ // In two contexts, we buffer all incoming data messages and then process them // 145
+ // all at once in a single update: // 146
+ // - During reconnect, we buffer all data messages until all subs that had // 147
+ // been ready before reconnect are ready again, and all methods that are // 148
+ // active have returned their "data done message"; then // 149
+ // - During the execution of a "wait" method, we buffer all data messages // 150
+ // until the wait method gets its "data done" message. (If the wait method // 151
+ // occurs during reconnect, it doesn't get any special handling.) // 152
+ // all data messages are processed in one update. // 153
+ // // 154
+ // The following fields are used for this "quiescence" process. // 155
+ // 156
+ // This buffers the messages that aren't being processed yet. // 157
+ self._messagesBufferedUntilQuiescence = []; // 158
+ // Map from method ID -> true. Methods are removed from this when their // 159
+ // "data done" message is received, and we will not quiesce until it is // 160
+ // empty. // 161
+ self._methodsBlockingQuiescence = {}; // 162
+ // map from sub ID -> true for subs that were ready (ie, called the sub // 163
+ // ready callback) before reconnect but haven't become ready again yet // 164
+ self._subsBeingRevived = {}; // map from sub._id -> true // 165
+ // if true, the next data update should reset all stores. (set during // 166
+ // reconnect.) // 167
+ self._resetStores = false; // 168
+ // 169
+ // name -> array of updates for (yet to be created) collections // 170
+ self._updatesForUnknownStores = {}; // 171
+ // if we're blocking a migration, the retry func // 172
+ self._retryMigrate = null; // 173
+ // 174
+ // metadata for subscriptions. Map from sub ID to object with keys: // 175
+ // - id // 176
+ // - name // 177
+ // - params // 178
+ // - inactive (if true, will be cleaned up if not reused in re-run) // 179
+ // - ready (has the 'ready' message been received?) // 180
+ // - readyCallback (an optional callback to call when ready) // 181
+ // - errorCallback (an optional callback to call if the sub terminates with // 182
+ // an error, XXX COMPAT WITH 1.0.3.1) // 183
+ // - stopCallback (an optional callback to call when the sub terminates // 184
+ // for any reason, with an error argument if an error triggered the stop) // 185
+ self._subscriptions = {}; // 186
+ // 187
+ // Reactive userId. // 188
+ self._userId = null; // 189
+ self._userIdDeps = new Tracker.Dependency; // 190
+ // 191
+ // Block auto-reload while we're waiting for method responses. // 192
+ if (Meteor.isClient && Package.reload && !options.reloadWithOutstanding) { // 193
+ Package.reload.Reload._onMigrate(function (retry) { // 194
+ if (!self._readyToMigrate()) { // 195
+ if (self._retryMigrate) // 196
+ throw new Error("Two migrations in progress?"); // 197
+ self._retryMigrate = retry; // 198
+ return false; // 199
+ } else { // 200
+ return [true]; // 201
+ } // 202
+ }); // 203
+ } // 204
+ // 205
+ var onMessage = function (raw_msg) { // 206
+ try { // 207
+ var msg = parseDDP(raw_msg); // 208
+ } catch (e) { // 209
+ Meteor._debug("Exception while parsing DDP", e); // 210
+ return; // 211
+ } // 212
+ // 213
+ if (msg === null || !msg.msg) { // 214
+ // XXX COMPAT WITH 0.6.6. ignore the old welcome message for back // 215
+ // compat. Remove this 'if' once the server stops sending welcome // 216
+ // messages (stream_server.js). // 217
+ if (! (msg && msg.server_id)) // 218
+ Meteor._debug("discarding invalid livedata message", msg); // 219
+ return; // 220
+ } // 221
+ // 222
+ if (msg.msg === 'connected') { // 223
+ self._version = self._versionSuggestion; // 224
+ self._livedata_connected(msg); // 225
+ options.onConnected(); // 226
+ } // 227
+ else if (msg.msg == 'failed') { // 228
+ if (_.contains(self._supportedDDPVersions, msg.version)) { // 229
+ self._versionSuggestion = msg.version; // 230
+ self._stream.reconnect({_force: true}); // 231
+ } else { // 232
+ var description = // 233
+ "DDP version negotiation failed; server requested version " + msg.version; // 234
+ self._stream.disconnect({_permanent: true, _error: description}); // 235
+ options.onDDPVersionNegotiationFailure(description); // 236
+ } // 237
+ } // 238
+ else if (msg.msg === 'ping') { // 239
+ if (options.respondToPings) // 240
+ self._send({msg: "pong", id: msg.id}); // 241
+ if (self._heartbeat) // 242
+ self._heartbeat.pingReceived(); // 243
+ } // 244
+ else if (msg.msg === 'pong') { // 245
+ if (self._heartbeat) { // 246
+ self._heartbeat.pongReceived(); // 247
+ } // 248
+ } // 249
+ else if (_.include(['added', 'changed', 'removed', 'ready', 'updated'], msg.msg)) // 250
+ self._livedata_data(msg); // 251
+ else if (msg.msg === 'nosub') // 252
+ self._livedata_nosub(msg); // 253
+ else if (msg.msg === 'result') // 254
+ self._livedata_result(msg); // 255
+ else if (msg.msg === 'error') // 256
+ self._livedata_error(msg); // 257
+ else // 258
+ Meteor._debug("discarding unknown livedata message type", msg); // 259
+ }; // 260
+ // 261
+ var onReset = function () { // 262
+ // Send a connect message at the beginning of the stream. // 263
+ // NOTE: reset is called even on the first connection, so this is // 264
+ // the only place we send this message. // 265
+ var msg = {msg: 'connect'}; // 266
+ if (self._lastSessionId) // 267
+ msg.session = self._lastSessionId; // 268
+ msg.version = self._versionSuggestion || self._supportedDDPVersions[0]; // 269
+ self._versionSuggestion = msg.version; // 270
+ msg.support = self._supportedDDPVersions; // 271
+ self._send(msg); // 272
+ // 273
+ // Now, to minimize setup latency, go ahead and blast out all of // 274
+ // our pending methods ands subscriptions before we've even taken // 275
+ // the necessary RTT to know if we successfully reconnected. (1) // 276
+ // They're supposed to be idempotent; (2) even if we did // 277
+ // reconnect, we're not sure what messages might have gotten lost // 278
+ // (in either direction) since we were disconnected (TCP being // 279
+ // sloppy about that.) // 280
+ // 281
+ // If the current block of methods all got their results (but didn't all get // 282
+ // their data visible), discard the empty block now. // 283
+ if (! _.isEmpty(self._outstandingMethodBlocks) && // 284
+ _.isEmpty(self._outstandingMethodBlocks[0].methods)) { // 285
+ self._outstandingMethodBlocks.shift(); // 286
+ } // 287
+ // 288
+ // Mark all messages as unsent, they have not yet been sent on this // 289
+ // connection. // 290
+ _.each(self._methodInvokers, function (m) { // 291
+ m.sentMessage = false; // 292
+ }); // 293
+ // 294
+ // If an `onReconnect` handler is set, call it first. Go through // 295
+ // some hoops to ensure that methods that are called from within // 296
+ // `onReconnect` get executed _before_ ones that were originally // 297
+ // outstanding (since `onReconnect` is used to re-establish auth // 298
+ // certificates) // 299
+ if (self.onReconnect) // 300
+ self._callOnReconnectAndSendAppropriateOutstandingMethods(); // 301
+ else // 302
+ self._sendOutstandingMethods(); // 303
+ // 304
+ // add new subscriptions at the end. this way they take effect after // 305
+ // the handlers and we don't see flicker. // 306
+ _.each(self._subscriptions, function (sub, id) { // 307
+ self._send({ // 308
+ msg: 'sub', // 309
+ id: id, // 310
+ name: sub.name, // 311
+ params: sub.params // 312
+ }); // 313
+ }); // 314
+ }; // 315
+ // 316
+ var onDisconnect = function () { // 317
+ if (self._heartbeat) { // 318
+ self._heartbeat.stop(); // 319
+ self._heartbeat = null; // 320
+ } // 321
+ }; // 322
+ // 323
+ if (Meteor.isServer) { // 324
+ self._stream.on('message', Meteor.bindEnvironment(onMessage, Meteor._debug)); // 325
+ self._stream.on('reset', Meteor.bindEnvironment(onReset, Meteor._debug)); // 326
+ self._stream.on('disconnect', Meteor.bindEnvironment(onDisconnect, Meteor._debug)); // 327
+ } else { // 328
+ self._stream.on('message', onMessage); // 329
+ self._stream.on('reset', onReset); // 330
+ self._stream.on('disconnect', onDisconnect); // 331
+ } // 332
+}; // 333
+ // 334
+// A MethodInvoker manages sending a method to the server and calling the user's // 335
+// callbacks. On construction, it registers itself in the connection's // 336
+// _methodInvokers map; it removes itself once the method is fully finished and // 337
+// the callback is invoked. This occurs when it has both received a result, // 338
+// and the data written by it is fully visible. // 339
+var MethodInvoker = function (options) { // 340
+ var self = this; // 341
+ // 342
+ // Public (within this file) fields. // 343
+ self.methodId = options.methodId; // 344
+ self.sentMessage = false; // 345
+ // 346
+ self._callback = options.callback; // 347
+ self._connection = options.connection; // 348
+ self._message = options.message; // 349
+ self._onResultReceived = options.onResultReceived || function () {}; // 350
+ self._wait = options.wait; // 351
+ self._methodResult = null; // 352
+ self._dataVisible = false; // 353
+ // 354
+ // Register with the connection. // 355
+ self._connection._methodInvokers[self.methodId] = self; // 356
+}; // 357
+_.extend(MethodInvoker.prototype, { // 358
+ // Sends the method message to the server. May be called additional times if // 359
+ // we lose the connection and reconnect before receiving a result. // 360
+ sendMessage: function () { // 361
+ var self = this; // 362
+ // This function is called before sending a method (including resending on // 363
+ // reconnect). We should only (re)send methods where we don't already have a // 364
+ // result! // 365
+ if (self.gotResult()) // 366
+ throw new Error("sendingMethod is called on method with result"); // 367
+ // 368
+ // If we're re-sending it, it doesn't matter if data was written the first // 369
+ // time. // 370
+ self._dataVisible = false; // 371
+ // 372
+ self.sentMessage = true; // 373
+ // 374
+ // If this is a wait method, make all data messages be buffered until it is // 375
+ // done. // 376
+ if (self._wait) // 377
+ self._connection._methodsBlockingQuiescence[self.methodId] = true; // 378
+ // 379
+ // Actually send the message. // 380
+ self._connection._send(self._message); // 381
+ }, // 382
+ // Invoke the callback, if we have both a result and know that all data has // 383
+ // been written to the local cache. // 384
+ _maybeInvokeCallback: function () { // 385
+ var self = this; // 386
+ if (self._methodResult && self._dataVisible) { // 387
+ // Call the callback. (This won't throw: the callback was wrapped with // 388
+ // bindEnvironment.) // 389
+ self._callback(self._methodResult[0], self._methodResult[1]); // 390
+ // 391
+ // Forget about this method. // 392
+ delete self._connection._methodInvokers[self.methodId]; // 393
+ // 394
+ // Let the connection know that this method is finished, so it can try to // 395
+ // move on to the next block of methods. // 396
+ self._connection._outstandingMethodFinished(); // 397
+ } // 398
+ }, // 399
+ // Call with the result of the method from the server. Only may be called // 400
+ // once; once it is called, you should not call sendMessage again. // 401
+ // If the user provided an onResultReceived callback, call it immediately. // 402
+ // Then invoke the main callback if data is also visible. // 403
+ receiveResult: function (err, result) { // 404
+ var self = this; // 405
+ if (self.gotResult()) // 406
+ throw new Error("Methods should only receive results once"); // 407
+ self._methodResult = [err, result]; // 408
+ self._onResultReceived(err, result); // 409
+ self._maybeInvokeCallback(); // 410
+ }, // 411
+ // Call this when all data written by the method is visible. This means that // 412
+ // the method has returns its "data is done" message *AND* all server // 413
+ // documents that are buffered at that time have been written to the local // 414
+ // cache. Invokes the main callback if the result has been received. // 415
+ dataVisible: function () { // 416
+ var self = this; // 417
+ self._dataVisible = true; // 418
+ self._maybeInvokeCallback(); // 419
+ }, // 420
+ // True if receiveResult has been called. // 421
+ gotResult: function () { // 422
+ var self = this; // 423
+ return !!self._methodResult; // 424
+ } // 425
+}); // 426
+ // 427
+_.extend(Connection.prototype, { // 428
+ // 'name' is the name of the data on the wire that should go in the // 429
+ // store. 'wrappedStore' should be an object with methods beginUpdate, update, // 430
+ // endUpdate, saveOriginals, retrieveOriginals. see Collection for an example. // 431
+ registerStore: function (name, wrappedStore) { // 432
+ var self = this; // 433
+ // 434
+ if (name in self._stores) // 435
+ return false; // 436
+ // 437
+ // Wrap the input object in an object which makes any store method not // 438
+ // implemented by 'store' into a no-op. // 439
+ var store = {}; // 440
+ _.each(['update', 'beginUpdate', 'endUpdate', 'saveOriginals', // 441
+ 'retrieveOriginals'], function (method) { // 442
+ store[method] = function () { // 443
+ return (wrappedStore[method] // 444
+ ? wrappedStore[method].apply(wrappedStore, arguments) // 445
+ : undefined); // 446
+ }; // 447
+ }); // 448
+ // 449
+ self._stores[name] = store; // 450
+ // 451
+ var queued = self._updatesForUnknownStores[name]; // 452
+ if (queued) { // 453
+ store.beginUpdate(queued.length, false); // 454
+ _.each(queued, function (msg) { // 455
+ store.update(msg); // 456
+ }); // 457
+ store.endUpdate(); // 458
+ delete self._updatesForUnknownStores[name]; // 459
+ } // 460
+ // 461
+ return true; // 462
+ }, // 463
+ // 464
+ /** // 465
+ * @memberOf Meteor // 466
+ * @summary Subscribe to a record set. Returns a handle that provides // 467
+ * `stop()` and `ready()` methods. // 468
+ * @locus Client // 469
+ * @param {String} name Name of the subscription. Matches the name of the // 470
+ * server's `publish()` call. // 471
+ * @param {Any} [arg1,arg2...] Optional arguments passed to publisher // 472
+ * function on server. // 473
+ * @param {Function|Object} [callbacks] Optional. May include `onStop` // 474
+ * and `onReady` callbacks. If there is an error, it is passed as an // 475
+ * argument to `onStop`. If a function is passed instead of an object, it // 476
+ * is interpreted as an `onReady` callback. // 477
+ */ // 478
+ subscribe: function (name /* .. [arguments] .. (callback|callbacks) */) { // 479
+ var self = this; // 480
+ // 481
+ var params = Array.prototype.slice.call(arguments, 1); // 482
+ var callbacks = {}; // 483
+ if (params.length) { // 484
+ var lastParam = params[params.length - 1]; // 485
+ if (_.isFunction(lastParam)) { // 486
+ callbacks.onReady = params.pop(); // 487
+ } else if (lastParam && // 488
+ // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use // 489
+ // onStop with an error callback instead. // 490
+ _.any([lastParam.onReady, lastParam.onError, lastParam.onStop], // 491
+ _.isFunction)) { // 492
+ callbacks = params.pop(); // 493
+ } // 494
+ } // 495
+ // 496
+ // Is there an existing sub with the same name and param, run in an // 497
+ // invalidated Computation? This will happen if we are rerunning an // 498
+ // existing computation. // 499
+ // // 500
+ // For example, consider a rerun of: // 501
+ // // 502
+ // Tracker.autorun(function () { // 503
+ // Meteor.subscribe("foo", Session.get("foo")); // 504
+ // Meteor.subscribe("bar", Session.get("bar")); // 505
+ // }); // 506
+ // // 507
+ // If "foo" has changed but "bar" has not, we will match the "bar" // 508
+ // subcribe to an existing inactive subscription in order to not // 509
+ // unsub and resub the subscription unnecessarily. // 510
+ // // 511
+ // We only look for one such sub; if there are N apparently-identical subs // 512
+ // being invalidated, we will require N matching subscribe calls to keep // 513
+ // them all active. // 514
+ var existing = _.find(self._subscriptions, function (sub) { // 515
+ return sub.inactive && sub.name === name && // 516
+ EJSON.equals(sub.params, params); // 517
+ }); // 518
+ // 519
+ var id; // 520
+ if (existing) { // 521
+ id = existing.id; // 522
+ existing.inactive = false; // reactivate // 523
+ // 524
+ if (callbacks.onReady) { // 525
+ // If the sub is not already ready, replace any ready callback with the // 526
+ // one provided now. (It's not really clear what users would expect for // 527
+ // an onReady callback inside an autorun; the semantics we provide is // 528
+ // that at the time the sub first becomes ready, we call the last // 529
+ // onReady callback provided, if any.) // 530
+ if (!existing.ready) // 531
+ existing.readyCallback = callbacks.onReady; // 532
+ } // 533
+ // 534
+ // XXX COMPAT WITH 1.0.3.1 we used to have onError but now we call // 535
+ // onStop with an optional error argument // 536
+ if (callbacks.onError) { // 537
+ // Replace existing callback if any, so that errors aren't // 538
+ // double-reported. // 539
+ existing.errorCallback = callbacks.onError; // 540
+ } // 541
+ // 542
+ if (callbacks.onStop) { // 543
+ existing.stopCallback = callbacks.onStop; // 544
+ } // 545
+ } else { // 546
+ // New sub! Generate an id, save it locally, and send message. // 547
+ id = Random.id(); // 548
+ self._subscriptions[id] = { // 549
+ id: id, // 550
+ name: name, // 551
+ params: EJSON.clone(params), // 552
+ inactive: false, // 553
+ ready: false, // 554
+ readyDeps: new Tracker.Dependency, // 555
+ readyCallback: callbacks.onReady, // 556
+ // XXX COMPAT WITH 1.0.3.1 #errorCallback // 557
+ errorCallback: callbacks.onError, // 558
+ stopCallback: callbacks.onStop, // 559
+ connection: self, // 560
+ remove: function() { // 561
+ delete this.connection._subscriptions[this.id]; // 562
+ this.ready && this.readyDeps.changed(); // 563
+ }, // 564
+ stop: function() { // 565
+ this.connection._send({msg: 'unsub', id: id}); // 566
+ this.remove(); // 567
+ // 568
+ if (callbacks.onStop) { // 569
+ callbacks.onStop(); // 570
+ } // 571
+ } // 572
+ }; // 573
+ self._send({msg: 'sub', id: id, name: name, params: params}); // 574
+ } // 575
+ // 576
+ // return a handle to the application. // 577
+ var handle = { // 578
+ stop: function () { // 579
+ if (!_.has(self._subscriptions, id)) // 580
+ return; // 581
+ // 582
+ self._subscriptions[id].stop(); // 583
+ }, // 584
+ ready: function () { // 585
+ // return false if we've unsubscribed. // 586
+ if (!_.has(self._subscriptions, id)) // 587
+ return false; // 588
+ var record = self._subscriptions[id]; // 589
+ record.readyDeps.depend(); // 590
+ return record.ready; // 591
+ }, // 592
+ subscriptionId: id // 593
+ }; // 594
+ // 595
+ if (Tracker.active) { // 596
+ // We're in a reactive computation, so we'd like to unsubscribe when the // 597
+ // computation is invalidated... but not if the rerun just re-subscribes // 598
+ // to the same subscription! When a rerun happens, we use onInvalidate // 599
+ // as a change to mark the subscription "inactive" so that it can // 600
+ // be reused from the rerun. If it isn't reused, it's killed from // 601
+ // an afterFlush. // 602
+ Tracker.onInvalidate(function (c) { // 603
+ if (_.has(self._subscriptions, id)) // 604
+ self._subscriptions[id].inactive = true; // 605
+ // 606
+ Tracker.afterFlush(function () { // 607
+ if (_.has(self._subscriptions, id) && // 608
+ self._subscriptions[id].inactive) // 609
+ handle.stop(); // 610
+ }); // 611
+ }); // 612
+ } // 613
+ // 614
+ return handle; // 615
+ }, // 616
+ // 617
+ // options: // 618
+ // - onLateError {Function(error)} called if an error was received after the ready event. // 619
+ // (errors received before ready cause an error to be thrown) // 620
+ _subscribeAndWait: function (name, args, options) { // 621
+ var self = this; // 622
+ var f = new Future(); // 623
+ var ready = false; // 624
+ var handle; // 625
+ args = args || []; // 626
+ args.push({ // 627
+ onReady: function () { // 628
+ ready = true; // 629
+ f['return'](); // 630
+ }, // 631
+ onError: function (e) { // 632
+ if (!ready) // 633
+ f['throw'](e); // 634
+ else // 635
+ options && options.onLateError && options.onLateError(e); // 636
+ } // 637
+ }); // 638
+ // 639
+ handle = self.subscribe.apply(self, [name].concat(args)); // 640
+ f.wait(); // 641
+ return handle; // 642
+ }, // 643
+ // 644
+ methods: function (methods) { // 645
+ var self = this; // 646
+ _.each(methods, function (func, name) { // 647
+ if (self._methodHandlers[name]) // 648
+ throw new Error("A method named '" + name + "' is already defined"); // 649
+ self._methodHandlers[name] = func; // 650
+ }); // 651
+ }, // 652
+ // 653
+ /** // 654
+ * @memberOf Meteor // 655
+ * @summary Invokes a method passing any number of arguments. // 656
+ * @locus Anywhere // 657
+ * @param {String} name Name of method to invoke // 658
+ * @param {EJSONable} [arg1,arg2...] Optional method arguments // 659
+ * @param {Function} [asyncCallback] Optional callback, which is called asynchronously with the error or result after the method is complete. If not provided, the method runs synchronously if possible (see below).
+ */ // 661
+ call: function (name /* .. [arguments] .. callback */) { // 662
+ // if it's a function, the last argument is the result callback, // 663
+ // not a parameter to the remote method. // 664
+ var args = Array.prototype.slice.call(arguments, 1); // 665
+ if (args.length && typeof args[args.length - 1] === "function") // 666
+ var callback = args.pop(); // 667
+ return this.apply(name, args, callback); // 668
+ }, // 669
+ // 670
+ // @param options {Optional Object} // 671
+ // wait: Boolean - Should we wait to call this until all current methods // 672
+ // are fully finished, and block subsequent method calls // 673
+ // until this method is fully finished? // 674
+ // (does not affect methods called from within this method) // 675
+ // onResultReceived: Function - a callback to call as soon as the method // 676
+ // result is received. the data written by // 677
+ // the method may not yet be in the cache! // 678
+ // returnStubValue: Boolean - If true then in cases where we would have // 679
+ // otherwise discarded the stub's return value // 680
+ // and returned undefined, instead we go ahead // 681
+ // and return it. Specifically, this is any // 682
+ // time other than when (a) we are already // 683
+ // inside a stub or (b) we are in Node and no // 684
+ // callback was provided. Currently we require // 685
+ // this flag to be explicitly passed to reduce // 686
+ // the likelihood that stub return values will // 687
+ // be confused with server return values; we // 688
+ // may improve this in future. // 689
+ // @param callback {Optional Function} // 690
+ // 691
+ /** // 692
+ * @memberOf Meteor // 693
+ * @summary Invoke a method passing an array of arguments. // 694
+ * @locus Anywhere // 695
+ * @param {String} name Name of method to invoke // 696
+ * @param {EJSONable[]} args Method arguments // 697
+ * @param {Object} [options] // 698
+ * @param {Boolean} options.wait (Client only) If true, don't send this method until all previous method calls have completed, and don't send any subsequent method calls until this one is completed.
+ * @param {Function} options.onResultReceived (Client only) This callback is invoked with the error or result of the method (just like `asyncCallback`) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.
+ * @param {Function} [asyncCallback] Optional callback; same semantics as in [`Meteor.call`](#meteor_call). // 701
+ */ // 702
+ apply: function (name, args, options, callback) { // 703
+ var self = this; // 704
+ // 705
+ // We were passed 3 arguments. They may be either (name, args, options) // 706
+ // or (name, args, callback) // 707
+ if (!callback && typeof options === 'function') { // 708
+ callback = options; // 709
+ options = {}; // 710
+ } // 711
+ options = options || {}; // 712
+ // 713
+ if (callback) { // 714
+ // XXX would it be better form to do the binding in stream.on, // 715
+ // or caller, instead of here? // 716
+ // XXX improve error message (and how we report it) // 717
+ callback = Meteor.bindEnvironment( // 718
+ callback, // 719
+ "delivering result of invoking '" + name + "'" // 720
+ ); // 721
+ } // 722
+ // 723
+ // Keep our args safe from mutation (eg if we don't send the message for a // 724
+ // while because of a wait method). // 725
+ args = EJSON.clone(args); // 726
+ // 727
+ // Lazily allocate method ID once we know that it'll be needed. // 728
+ var methodId = (function () { // 729
+ var id; // 730
+ return function () { // 731
+ if (id === undefined) // 732
+ id = '' + (self._nextMethodId++); // 733
+ return id; // 734
+ }; // 735
+ })(); // 736
+ // 737
+ var enclosing = DDP._CurrentInvocation.get(); // 738
+ var alreadyInSimulation = enclosing && enclosing.isSimulation; // 739
+ // 740
+ // Lazily generate a randomSeed, only if it is requested by the stub. // 741
+ // The random streams only have utility if they're used on both the client // 742
+ // and the server; if the client doesn't generate any 'random' values // 743
+ // then we don't expect the server to generate any either. // 744
+ // Less commonly, the server may perform different actions from the client, // 745
+ // and may in fact generate values where the client did not, but we don't // 746
+ // have any client-side values to match, so even here we may as well just // 747
+ // use a random seed on the server. In that case, we don't pass the // 748
+ // randomSeed to save bandwidth, and we don't even generate it to save a // 749
+ // bit of CPU and to avoid consuming entropy. // 750
+ var randomSeed = null; // 751
+ var randomSeedGenerator = function () { // 752
+ if (randomSeed === null) { // 753
+ randomSeed = makeRpcSeed(enclosing, name); // 754
+ } // 755
+ return randomSeed; // 756
+ }; // 757
+ // 758
+ // Run the stub, if we have one. The stub is supposed to make some // 759
+ // temporary writes to the database to give the user a smooth experience // 760
+ // until the actual result of executing the method comes back from the // 761
+ // server (whereupon the temporary writes to the database will be reversed // 762
+ // during the beginUpdate/endUpdate process.) // 763
+ // // 764
+ // Normally, we ignore the return value of the stub (even if it is an // 765
+ // exception), in favor of the real return value from the server. The // 766
+ // exception is if the *caller* is a stub. In that case, we're not going // 767
+ // to do a RPC, so we use the return value of the stub as our return // 768
+ // value. // 769
+ // 770
+ var stub = self._methodHandlers[name]; // 771
+ if (stub) { // 772
+ var setUserId = function(userId) { // 773
+ self.setUserId(userId); // 774
+ }; // 775
+ // 776
+ var invocation = new MethodInvocation({ // 777
+ isSimulation: true, // 778
+ userId: self.userId(), // 779
+ setUserId: setUserId, // 780
+ randomSeed: function () { return randomSeedGenerator(); } // 781
+ }); // 782
+ // 783
+ if (!alreadyInSimulation) // 784
+ self._saveOriginals(); // 785
+ // 786
+ try { // 787
+ // Note that unlike in the corresponding server code, we never audit // 788
+ // that stubs check() their arguments. // 789
+ var stubReturnValue = DDP._CurrentInvocation.withValue(invocation, function () { // 790
+ if (Meteor.isServer) { // 791
+ // Because saveOriginals and retrieveOriginals aren't reentrant, // 792
+ // don't allow stubs to yield. // 793
+ return Meteor._noYieldsAllowed(function () { // 794
+ // re-clone, so that the stub can't affect our caller's values // 795
+ return stub.apply(invocation, EJSON.clone(args)); // 796
+ }); // 797
+ } else { // 798
+ return stub.apply(invocation, EJSON.clone(args)); // 799
+ } // 800
+ }); // 801
+ } // 802
+ catch (e) { // 803
+ var exception = e; // 804
+ } // 805
+ // 806
+ if (!alreadyInSimulation) // 807
+ self._retrieveAndStoreOriginals(methodId()); // 808
+ } // 809
+ // 810
+ // If we're in a simulation, stop and return the result we have, // 811
+ // rather than going on to do an RPC. If there was no stub, // 812
+ // we'll end up returning undefined. // 813
+ if (alreadyInSimulation) { // 814
+ if (callback) { // 815
+ callback(exception, stubReturnValue); // 816
+ return undefined; // 817
+ } // 818
+ if (exception) // 819
+ throw exception; // 820
+ return stubReturnValue; // 821
+ } // 822
+ // 823
+ // If an exception occurred in a stub, and we're ignoring it // 824
+ // because we're doing an RPC and want to use what the server // 825
+ // returns instead, log it so the developer knows. // 826
+ // // 827
+ // Tests can set the 'expected' flag on an exception so it won't // 828
+ // go to log. // 829
+ if (exception && !exception.expected) { // 830
+ Meteor._debug("Exception while simulating the effect of invoking '" + // 831
+ name + "'", exception, exception.stack); // 832
+ } // 833
+ // 834
+ // 835
+ // At this point we're definitely doing an RPC, and we're going to // 836
+ // return the value of the RPC to the caller. // 837
+ // 838
+ // If the caller didn't give a callback, decide what to do. // 839
+ if (!callback) { // 840
+ if (Meteor.isClient) { // 841
+ // On the client, we don't have fibers, so we can't block. The // 842
+ // only thing we can do is to return undefined and discard the // 843
+ // result of the RPC. If an error occurred then print the error // 844
+ // to the console. // 845
+ callback = function (err) { // 846
+ err && Meteor._debug("Error invoking Method '" + name + "':", // 847
+ err.message); // 848
+ }; // 849
+ } else { // 850
+ // On the server, make the function synchronous. Throw on // 851
+ // errors, return on success. // 852
+ var future = new Future; // 853
+ callback = future.resolver(); // 854
+ } // 855
+ } // 856
+ // Send the RPC. Note that on the client, it is important that the // 857
+ // stub have finished before we send the RPC, so that we know we have // 858
+ // a complete list of which local documents the stub wrote. // 859
+ var message = { // 860
+ msg: 'method', // 861
+ method: name, // 862
+ params: args, // 863
+ id: methodId() // 864
+ }; // 865
+ // 866
+ // Send the randomSeed only if we used it // 867
+ if (randomSeed !== null) { // 868
+ message.randomSeed = randomSeed; // 869
+ } // 870
+ // 871
+ var methodInvoker = new MethodInvoker({ // 872
+ methodId: methodId(), // 873
+ callback: callback, // 874
+ connection: self, // 875
+ onResultReceived: options.onResultReceived, // 876
+ wait: !!options.wait, // 877
+ message: message // 878
+ }); // 879
+ // 880
+ if (options.wait) { // 881
+ // It's a wait method! Wait methods go in their own block. // 882
+ self._outstandingMethodBlocks.push( // 883
+ {wait: true, methods: [methodInvoker]}); // 884
+ } else { // 885
+ // Not a wait method. Start a new block if the previous block was a wait // 886
+ // block, and add it to the last block of methods. // 887
+ if (_.isEmpty(self._outstandingMethodBlocks) || // 888
+ _.last(self._outstandingMethodBlocks).wait) // 889
+ self._outstandingMethodBlocks.push({wait: false, methods: []}); // 890
+ _.last(self._outstandingMethodBlocks).methods.push(methodInvoker); // 891
+ } // 892
+ // 893
+ // If we added it to the first block, send it out now. // 894
+ if (self._outstandingMethodBlocks.length === 1) // 895
+ methodInvoker.sendMessage(); // 896
+ // 897
+ // If we're using the default callback on the server, // 898
+ // block waiting for the result. // 899
+ if (future) { // 900
+ return future.wait(); // 901
+ } // 902
+ return options.returnStubValue ? stubReturnValue : undefined; // 903
+ }, // 904
+ // 905
+ // Before calling a method stub, prepare all stores to track changes and allow // 906
+ // _retrieveAndStoreOriginals to get the original versions of changed // 907
+ // documents. // 908
+ _saveOriginals: function () { // 909
+ var self = this; // 910
+ _.each(self._stores, function (s) { // 911
+ s.saveOriginals(); // 912
+ }); // 913
+ }, // 914
+ // Retrieves the original versions of all documents modified by the stub for // 915
+ // method 'methodId' from all stores and saves them to _serverDocuments (keyed // 916
+ // by document) and _documentsWrittenByStub (keyed by method ID). // 917
+ _retrieveAndStoreOriginals: function (methodId) { // 918
+ var self = this; // 919
+ if (self._documentsWrittenByStub[methodId]) // 920
+ throw new Error("Duplicate methodId in _retrieveAndStoreOriginals"); // 921
+ // 922
+ var docsWritten = []; // 923
+ _.each(self._stores, function (s, collection) { // 924
+ var originals = s.retrieveOriginals(); // 925
+ // not all stores define retrieveOriginals // 926
+ if (!originals) // 927
+ return; // 928
+ originals.forEach(function (doc, id) { // 929
+ docsWritten.push({collection: collection, id: id}); // 930
+ if (!_.has(self._serverDocuments, collection)) // 931
+ self._serverDocuments[collection] = new LocalCollection._IdMap; // 932
+ var serverDoc = self._serverDocuments[collection].setDefault(id, {}); // 933
+ if (serverDoc.writtenByStubs) { // 934
+ // We're not the first stub to write this doc. Just add our method ID // 935
+ // to the record. // 936
+ serverDoc.writtenByStubs[methodId] = true; // 937
+ } else { // 938
+ // First stub! Save the original value and our method ID. // 939
+ serverDoc.document = doc; // 940
+ serverDoc.flushCallbacks = []; // 941
+ serverDoc.writtenByStubs = {}; // 942
+ serverDoc.writtenByStubs[methodId] = true; // 943
+ } // 944
+ }); // 945
+ }); // 946
+ if (!_.isEmpty(docsWritten)) { // 947
+ self._documentsWrittenByStub[methodId] = docsWritten; // 948
+ } // 949
+ }, // 950
+ // 951
+ // This is very much a private function we use to make the tests // 952
+ // take up fewer server resources after they complete. // 953
+ _unsubscribeAll: function () { // 954
+ var self = this; // 955
+ _.each(_.clone(self._subscriptions), function (sub, id) { // 956
+ // Avoid killing the autoupdate subscription so that developers // 957
+ // still get hot code pushes when writing tests. // 958
+ // // 959
+ // XXX it's a hack to encode knowledge about autoupdate here, // 960
+ // but it doesn't seem worth it yet to have a special API for // 961
+ // subscriptions to preserve after unit tests. // 962
+ if (sub.name !== 'meteor_autoupdate_clientVersions') { // 963
+ self._subscriptions[id].stop(); // 964
+ } // 965
+ }); // 966
+ }, // 967
+ // 968
+ // Sends the DDP stringification of the given message object // 969
+ _send: function (obj) { // 970
+ var self = this; // 971
+ self._stream.send(stringifyDDP(obj)); // 972
+ }, // 973
+ // 974
+ // We detected via DDP-level heartbeats that we've lost the // 975
+ // connection. Unlike `disconnect` or `close`, a lost connection // 976
+ // will be automatically retried. // 977
+ _lostConnection: function (error) { // 978
+ var self = this; // 979
+ self._stream._lostConnection(error); // 980
+ }, // 981
+ // 982
+ /** // 983
+ * @summary Get the current connection status. A reactive data source. // 984
+ * @locus Client // 985
+ * @memberOf Meteor // 986
+ */ // 987
+ status: function (/*passthrough args*/) { // 988
+ var self = this; // 989
+ return self._stream.status.apply(self._stream, arguments); // 990
+ }, // 991
+ // 992
+ /** // 993
+ * @summary Force an immediate reconnection attempt if the client is not connected to the server. // 994
+ // 995
+ This method does nothing if the client is already connected. // 996
+ * @locus Client // 997
+ * @memberOf Meteor // 998
+ */ // 999
+ reconnect: function (/*passthrough args*/) { // 1000
+ var self = this; // 1001
+ return self._stream.reconnect.apply(self._stream, arguments); // 1002
+ }, // 1003
+ // 1004
+ /** // 1005
+ * @summary Disconnect the client from the server. // 1006
+ * @locus Client // 1007
+ * @memberOf Meteor // 1008
+ */ // 1009
+ disconnect: function (/*passthrough args*/) { // 1010
+ var self = this; // 1011
+ return self._stream.disconnect.apply(self._stream, arguments); // 1012
+ }, // 1013
+ // 1014
+ close: function () { // 1015
+ var self = this; // 1016
+ return self._stream.disconnect({_permanent: true}); // 1017
+ }, // 1018
+ // 1019
+ /// // 1020
+ /// Reactive user system // 1021
+ /// // 1022
+ userId: function () { // 1023
+ var self = this; // 1024
+ if (self._userIdDeps) // 1025
+ self._userIdDeps.depend(); // 1026
+ return self._userId; // 1027
+ }, // 1028
+ // 1029
+ setUserId: function (userId) { // 1030
+ var self = this; // 1031
+ // Avoid invalidating dependents if setUserId is called with current value. // 1032
+ if (self._userId === userId) // 1033
+ return; // 1034
+ self._userId = userId; // 1035
+ if (self._userIdDeps) // 1036
+ self._userIdDeps.changed(); // 1037
+ }, // 1038
+ // 1039
+ // Returns true if we are in a state after reconnect of waiting for subs to be // 1040
+ // revived or early methods to finish their data, or we are waiting for a // 1041
+ // "wait" method to finish. // 1042
+ _waitingForQuiescence: function () { // 1043
+ var self = this; // 1044
+ return (! _.isEmpty(self._subsBeingRevived) || // 1045
+ ! _.isEmpty(self._methodsBlockingQuiescence)); // 1046
+ }, // 1047
+ // 1048
+ // Returns true if any method whose message has been sent to the server has // 1049
+ // not yet invoked its user callback. // 1050
+ _anyMethodsAreOutstanding: function () { // 1051
+ var self = this; // 1052
+ return _.any(_.pluck(self._methodInvokers, 'sentMessage')); // 1053
+ }, // 1054
+ // 1055
+ _livedata_connected: function (msg) { // 1056
+ var self = this; // 1057
+ // 1058
+ if (self._version !== 'pre1' && self._heartbeatInterval !== 0) { // 1059
+ self._heartbeat = new Heartbeat({ // 1060
+ heartbeatInterval: self._heartbeatInterval, // 1061
+ heartbeatTimeout: self._heartbeatTimeout, // 1062
+ onTimeout: function () { // 1063
+ self._lostConnection( // 1064
+ new DDP.ConnectionError("DDP heartbeat timed out")); // 1065
+ }, // 1066
+ sendPing: function () { // 1067
+ self._send({msg: 'ping'}); // 1068
+ } // 1069
+ }); // 1070
+ self._heartbeat.start(); // 1071
+ } // 1072
+ // 1073
+ // If this is a reconnect, we'll have to reset all stores. // 1074
+ if (self._lastSessionId) // 1075
+ self._resetStores = true; // 1076
+ // 1077
+ if (typeof (msg.session) === "string") { // 1078
+ var reconnectedToPreviousSession = (self._lastSessionId === msg.session); // 1079
+ self._lastSessionId = msg.session; // 1080
+ } // 1081
+ // 1082
+ if (reconnectedToPreviousSession) { // 1083
+ // Successful reconnection -- pick up where we left off. Note that right // 1084
+ // now, this never happens: the server never connects us to a previous // 1085
+ // session, because DDP doesn't provide enough data for the server to know // 1086
+ // what messages the client has processed. We need to improve DDP to make // 1087
+ // this possible, at which point we'll probably need more code here. // 1088
+ return; // 1089
+ } // 1090
+ // 1091
+ // Server doesn't have our data any more. Re-sync a new session. // 1092
+ // 1093
+ // Forget about messages we were buffering for unknown collections. They'll // 1094
+ // be resent if still relevant. // 1095
+ self._updatesForUnknownStores = {}; // 1096
+ // 1097
+ if (self._resetStores) { // 1098
+ // Forget about the effects of stubs. We'll be resetting all collections // 1099
+ // anyway. // 1100
+ self._documentsWrittenByStub = {}; // 1101
+ self._serverDocuments = {}; // 1102
+ } // 1103
+ // 1104
+ // Clear _afterUpdateCallbacks. // 1105
+ self._afterUpdateCallbacks = []; // 1106
+ // 1107
+ // Mark all named subscriptions which are ready (ie, we already called the // 1108
+ // ready callback) as needing to be revived. // 1109
+ // XXX We should also block reconnect quiescence until unnamed subscriptions // 1110
+ // (eg, autopublish) are done re-publishing to avoid flicker! // 1111
+ self._subsBeingRevived = {}; // 1112
+ _.each(self._subscriptions, function (sub, id) { // 1113
+ if (sub.ready) // 1114
+ self._subsBeingRevived[id] = true; // 1115
+ }); // 1116
+ // 1117
+ // Arrange for "half-finished" methods to have their callbacks run, and // 1118
+ // track methods that were sent on this connection so that we don't // 1119
+ // quiesce until they are all done. // 1120
+ // // 1121
+ // Start by clearing _methodsBlockingQuiescence: methods sent before // 1122
+ // reconnect don't matter, and any "wait" methods sent on the new connection // 1123
+ // that we drop here will be restored by the loop below. // 1124
+ self._methodsBlockingQuiescence = {}; // 1125
+ if (self._resetStores) { // 1126
+ _.each(self._methodInvokers, function (invoker) { // 1127
+ if (invoker.gotResult()) { // 1128
+ // This method already got its result, but it didn't call its callback // 1129
+ // because its data didn't become visible. We did not resend the // 1130
+ // method RPC. We'll call its callback when we get a full quiesce, // 1131
+ // since that's as close as we'll get to "data must be visible". // 1132
+ self._afterUpdateCallbacks.push(_.bind(invoker.dataVisible, invoker)); // 1133
+ } else if (invoker.sentMessage) { // 1134
+ // This method has been sent on this connection (maybe as a resend // 1135
+ // from the last connection, maybe from onReconnect, maybe just very // 1136
+ // quickly before processing the connected message). // 1137
+ // // 1138
+ // We don't need to do anything special to ensure its callbacks get // 1139
+ // called, but we'll count it as a method which is preventing // 1140
+ // reconnect quiescence. (eg, it might be a login method that was run // 1141
+ // from onReconnect, and we don't want to see flicker by seeing a // 1142
+ // logged-out state.) // 1143
+ self._methodsBlockingQuiescence[invoker.methodId] = true; // 1144
+ } // 1145
+ }); // 1146
+ } // 1147
+ // 1148
+ self._messagesBufferedUntilQuiescence = []; // 1149
+ // 1150
+ // If we're not waiting on any methods or subs, we can reset the stores and // 1151
+ // call the callbacks immediately. // 1152
+ if (!self._waitingForQuiescence()) { // 1153
+ if (self._resetStores) { // 1154
+ _.each(self._stores, function (s) { // 1155
+ s.beginUpdate(0, true); // 1156
+ s.endUpdate(); // 1157
+ }); // 1158
+ self._resetStores = false; // 1159
+ } // 1160
+ self._runAfterUpdateCallbacks(); // 1161
+ } // 1162
+ }, // 1163
+ // 1164
+ // 1165
+ _processOneDataMessage: function (msg, updates) { // 1166
+ var self = this; // 1167
+ // Using underscore here so as not to need to capitalize. // 1168
+ self['_process_' + msg.msg](msg, updates); // 1169
+ }, // 1170
+ // 1171
+ // 1172
+ _livedata_data: function (msg) { // 1173
+ var self = this; // 1174
+ // 1175
+ // collection name -> array of messages // 1176
+ var updates = {}; // 1177
+ // 1178
+ if (self._waitingForQuiescence()) { // 1179
+ self._messagesBufferedUntilQuiescence.push(msg); // 1180
+ // 1181
+ if (msg.msg === "nosub") // 1182
+ delete self._subsBeingRevived[msg.id]; // 1183
+ // 1184
+ _.each(msg.subs || [], function (subId) { // 1185
+ delete self._subsBeingRevived[subId]; // 1186
+ }); // 1187
+ _.each(msg.methods || [], function (methodId) { // 1188
+ delete self._methodsBlockingQuiescence[methodId]; // 1189
+ }); // 1190
+ // 1191
+ if (self._waitingForQuiescence()) // 1192
+ return; // 1193
+ // 1194
+ // No methods or subs are blocking quiescence! // 1195
+ // We'll now process and all of our buffered messages, reset all stores, // 1196
+ // and apply them all at once. // 1197
+ _.each(self._messagesBufferedUntilQuiescence, function (bufferedMsg) { // 1198
+ self._processOneDataMessage(bufferedMsg, updates); // 1199
+ }); // 1200
+ self._messagesBufferedUntilQuiescence = []; // 1201
+ } else { // 1202
+ self._processOneDataMessage(msg, updates); // 1203
+ } // 1204
+ // 1205
+ if (self._resetStores || !_.isEmpty(updates)) { // 1206
+ // Begin a transactional update of each store. // 1207
+ _.each(self._stores, function (s, storeName) { // 1208
+ s.beginUpdate(_.has(updates, storeName) ? updates[storeName].length : 0, // 1209
+ self._resetStores); // 1210
+ }); // 1211
+ self._resetStores = false; // 1212
+ // 1213
+ _.each(updates, function (updateMessages, storeName) { // 1214
+ var store = self._stores[storeName]; // 1215
+ if (store) { // 1216
+ _.each(updateMessages, function (updateMessage) { // 1217
+ store.update(updateMessage); // 1218
+ }); // 1219
+ } else { // 1220
+ // Nobody's listening for this data. Queue it up until // 1221
+ // someone wants it. // 1222
+ // XXX memory use will grow without bound if you forget to // 1223
+ // create a collection or just don't care about it... going // 1224
+ // to have to do something about that. // 1225
+ if (!_.has(self._updatesForUnknownStores, storeName)) // 1226
+ self._updatesForUnknownStores[storeName] = []; // 1227
+ Array.prototype.push.apply(self._updatesForUnknownStores[storeName], // 1228
+ updateMessages); // 1229
+ } // 1230
+ }); // 1231
+ // 1232
+ // End update transaction. // 1233
+ _.each(self._stores, function (s) { s.endUpdate(); }); // 1234
+ } // 1235
+ // 1236
+ self._runAfterUpdateCallbacks(); // 1237
+ }, // 1238
+ // 1239
+ // Call any callbacks deferred with _runWhenAllServerDocsAreFlushed whose // 1240
+ // relevant docs have been flushed, as well as dataVisible callbacks at // 1241
+ // reconnect-quiescence time. // 1242
+ _runAfterUpdateCallbacks: function () { // 1243
+ var self = this; // 1244
+ var callbacks = self._afterUpdateCallbacks; // 1245
+ self._afterUpdateCallbacks = []; // 1246
+ _.each(callbacks, function (c) { // 1247
+ c(); // 1248
+ }); // 1249
+ }, // 1250
+ // 1251
+ _pushUpdate: function (updates, collection, msg) { // 1252
+ var self = this; // 1253
+ if (!_.has(updates, collection)) { // 1254
+ updates[collection] = []; // 1255
+ } // 1256
+ updates[collection].push(msg); // 1257
+ }, // 1258
+ // 1259
+ _getServerDoc: function (collection, id) { // 1260
+ var self = this; // 1261
+ if (!_.has(self._serverDocuments, collection)) // 1262
+ return null; // 1263
+ var serverDocsForCollection = self._serverDocuments[collection]; // 1264
+ return serverDocsForCollection.get(id) || null; // 1265
+ }, // 1266
+ // 1267
+ _process_added: function (msg, updates) { // 1268
+ var self = this; // 1269
+ var id = LocalCollection._idParse(msg.id); // 1270
+ var serverDoc = self._getServerDoc(msg.collection, id); // 1271
+ if (serverDoc) { // 1272
+ // Some outstanding stub wrote here. // 1273
+ if (serverDoc.document !== undefined) // 1274
+ throw new Error("Server sent add for existing id: " + msg.id); // 1275
+ serverDoc.document = msg.fields || {}; // 1276
+ serverDoc.document._id = id; // 1277
+ } else { // 1278
+ self._pushUpdate(updates, msg.collection, msg); // 1279
+ } // 1280
+ }, // 1281
+ // 1282
+ _process_changed: function (msg, updates) { // 1283
+ var self = this; // 1284
+ var serverDoc = self._getServerDoc( // 1285
+ msg.collection, LocalCollection._idParse(msg.id)); // 1286
+ if (serverDoc) { // 1287
+ if (serverDoc.document === undefined) // 1288
+ throw new Error("Server sent changed for nonexisting id: " + msg.id); // 1289
+ LocalCollection._applyChanges(serverDoc.document, msg.fields); // 1290
+ } else { // 1291
+ self._pushUpdate(updates, msg.collection, msg); // 1292
+ } // 1293
+ }, // 1294
+ // 1295
+ _process_removed: function (msg, updates) { // 1296
+ var self = this; // 1297
+ var serverDoc = self._getServerDoc( // 1298
+ msg.collection, LocalCollection._idParse(msg.id)); // 1299
+ if (serverDoc) { // 1300
+ // Some outstanding stub wrote here. // 1301
+ if (serverDoc.document === undefined) // 1302
+ throw new Error("Server sent removed for nonexisting id:" + msg.id); // 1303
+ serverDoc.document = undefined; // 1304
+ } else { // 1305
+ self._pushUpdate(updates, msg.collection, { // 1306
+ msg: 'removed', // 1307
+ collection: msg.collection, // 1308
+ id: msg.id // 1309
+ }); // 1310
+ } // 1311
+ }, // 1312
+ // 1313
+ _process_updated: function (msg, updates) { // 1314
+ var self = this; // 1315
+ // Process "method done" messages. // 1316
+ _.each(msg.methods, function (methodId) { // 1317
+ _.each(self._documentsWrittenByStub[methodId], function (written) { // 1318
+ var serverDoc = self._getServerDoc(written.collection, written.id); // 1319
+ if (!serverDoc) // 1320
+ throw new Error("Lost serverDoc for " + JSON.stringify(written)); // 1321
+ if (!serverDoc.writtenByStubs[methodId]) // 1322
+ throw new Error("Doc " + JSON.stringify(written) + // 1323
+ " not written by method " + methodId); // 1324
+ delete serverDoc.writtenByStubs[methodId]; // 1325
+ if (_.isEmpty(serverDoc.writtenByStubs)) { // 1326
+ // All methods whose stubs wrote this method have completed! We can // 1327
+ // now copy the saved document to the database (reverting the stub's // 1328
+ // change if the server did not write to this object, or applying the // 1329
+ // server's writes if it did). // 1330
+ // 1331
+ // This is a fake ddp 'replace' message. It's just for talking // 1332
+ // between livedata connections and minimongo. (We have to stringify // 1333
+ // the ID because it's supposed to look like a wire message.) // 1334
+ self._pushUpdate(updates, written.collection, { // 1335
+ msg: 'replace', // 1336
+ id: LocalCollection._idStringify(written.id), // 1337
+ replace: serverDoc.document // 1338
+ }); // 1339
+ // Call all flush callbacks. // 1340
+ _.each(serverDoc.flushCallbacks, function (c) { // 1341
+ c(); // 1342
+ }); // 1343
+ // 1344
+ // Delete this completed serverDocument. Don't bother to GC empty // 1345
+ // IdMaps inside self._serverDocuments, since there probably aren't // 1346
+ // many collections and they'll be written repeatedly. // 1347
+ self._serverDocuments[written.collection].remove(written.id); // 1348
+ } // 1349
+ }); // 1350
+ delete self._documentsWrittenByStub[methodId]; // 1351
+ // 1352
+ // We want to call the data-written callback, but we can't do so until all // 1353
+ // currently buffered messages are flushed. // 1354
+ var callbackInvoker = self._methodInvokers[methodId]; // 1355
+ if (!callbackInvoker) // 1356
+ throw new Error("No callback invoker for method " + methodId); // 1357
+ self._runWhenAllServerDocsAreFlushed( // 1358
+ _.bind(callbackInvoker.dataVisible, callbackInvoker)); // 1359
+ }); // 1360
+ }, // 1361
+ // 1362
+ _process_ready: function (msg, updates) { // 1363
+ var self = this; // 1364
+ // Process "sub ready" messages. "sub ready" messages don't take effect // 1365
+ // until all current server documents have been flushed to the local // 1366
+ // database. We can use a write fence to implement this. // 1367
+ _.each(msg.subs, function (subId) { // 1368
+ self._runWhenAllServerDocsAreFlushed(function () { // 1369
+ var subRecord = self._subscriptions[subId]; // 1370
+ // Did we already unsubscribe? // 1371
+ if (!subRecord) // 1372
+ return; // 1373
+ // Did we already receive a ready message? (Oops!) // 1374
+ if (subRecord.ready) // 1375
+ return; // 1376
+ subRecord.readyCallback && subRecord.readyCallback(); // 1377
+ subRecord.ready = true; // 1378
+ subRecord.readyDeps.changed(); // 1379
+ }); // 1380
+ }); // 1381
+ }, // 1382
+ // 1383
+ // Ensures that "f" will be called after all documents currently in // 1384
+ // _serverDocuments have been written to the local cache. f will not be called // 1385
+ // if the connection is lost before then! // 1386
+ _runWhenAllServerDocsAreFlushed: function (f) { // 1387
+ var self = this; // 1388
+ var runFAfterUpdates = function () { // 1389
+ self._afterUpdateCallbacks.push(f); // 1390
+ }; // 1391
+ var unflushedServerDocCount = 0; // 1392
+ var onServerDocFlush = function () { // 1393
+ --unflushedServerDocCount; // 1394
+ if (unflushedServerDocCount === 0) { // 1395
+ // This was the last doc to flush! Arrange to run f after the updates // 1396
+ // have been applied. // 1397
+ runFAfterUpdates(); // 1398
+ } // 1399
+ }; // 1400
+ _.each(self._serverDocuments, function (collectionDocs) { // 1401
+ collectionDocs.forEach(function (serverDoc) { // 1402
+ var writtenByStubForAMethodWithSentMessage = _.any( // 1403
+ serverDoc.writtenByStubs, function (dummy, methodId) { // 1404
+ var invoker = self._methodInvokers[methodId]; // 1405
+ return invoker && invoker.sentMessage; // 1406
+ }); // 1407
+ if (writtenByStubForAMethodWithSentMessage) { // 1408
+ ++unflushedServerDocCount; // 1409
+ serverDoc.flushCallbacks.push(onServerDocFlush); // 1410
+ } // 1411
+ }); // 1412
+ }); // 1413
+ if (unflushedServerDocCount === 0) { // 1414
+ // There aren't any buffered docs --- we can call f as soon as the current // 1415
+ // round of updates is applied! // 1416
+ runFAfterUpdates(); // 1417
+ } // 1418
+ }, // 1419
+ // 1420
+ _livedata_nosub: function (msg) { // 1421
+ var self = this; // 1422
+ // 1423
+ // First pass it through _livedata_data, which only uses it to help get // 1424
+ // towards quiescence. // 1425
+ self._livedata_data(msg); // 1426
+ // 1427
+ // Do the rest of our processing immediately, with no // 1428
+ // buffering-until-quiescence. // 1429
+ // 1430
+ // we weren't subbed anyway, or we initiated the unsub. // 1431
+ if (!_.has(self._subscriptions, msg.id)) // 1432
+ return; // 1433
+ // 1434
+ // XXX COMPAT WITH 1.0.3.1 #errorCallback // 1435
+ var errorCallback = self._subscriptions[msg.id].errorCallback; // 1436
+ var stopCallback = self._subscriptions[msg.id].stopCallback; // 1437
+ // 1438
+ self._subscriptions[msg.id].remove(); // 1439
+ // 1440
+ var meteorErrorFromMsg = function (msgArg) { // 1441
+ return msgArg && msgArg.error && new Meteor.Error( // 1442
+ msgArg.error.error, msgArg.error.reason, msgArg.error.details); // 1443
+ } // 1444
+ // 1445
+ // XXX COMPAT WITH 1.0.3.1 #errorCallback // 1446
+ if (errorCallback && msg.error) { // 1447
+ errorCallback(meteorErrorFromMsg(msg)); // 1448
+ } // 1449
+ // 1450
+ if (stopCallback) { // 1451
+ stopCallback(meteorErrorFromMsg(msg)); // 1452
+ } // 1453
+ }, // 1454
+ // 1455
+ _process_nosub: function () { // 1456
+ // This is called as part of the "buffer until quiescence" process, but // 1457
+ // nosub's effect is always immediate. It only goes in the buffer at all // 1458
+ // because it's possible for a nosub to be the thing that triggers // 1459
+ // quiescence, if we were waiting for a sub to be revived and it dies // 1460
+ // instead. // 1461
+ }, // 1462
+ // 1463
+ _livedata_result: function (msg) { // 1464
+ // id, result or error. error has error (code), reason, details // 1465
+ // 1466
+ var self = this; // 1467
+ // 1468
+ // find the outstanding request // 1469
+ // should be O(1) in nearly all realistic use cases // 1470
+ if (_.isEmpty(self._outstandingMethodBlocks)) { // 1471
+ Meteor._debug("Received method result but no methods outstanding"); // 1472
+ return; // 1473
+ } // 1474
+ var currentMethodBlock = self._outstandingMethodBlocks[0].methods; // 1475
+ var m; // 1476
+ for (var i = 0; i < currentMethodBlock.length; i++) { // 1477
+ m = currentMethodBlock[i]; // 1478
+ if (m.methodId === msg.id) // 1479
+ break; // 1480
+ } // 1481
+ // 1482
+ if (!m) { // 1483
+ Meteor._debug("Can't match method response to original method call", msg); // 1484
+ return; // 1485
+ } // 1486
+ // 1487
+ // Remove from current method block. This may leave the block empty, but we // 1488
+ // don't move on to the next block until the callback has been delivered, in // 1489
+ // _outstandingMethodFinished. // 1490
+ currentMethodBlock.splice(i, 1); // 1491
+ // 1492
+ if (_.has(msg, 'error')) { // 1493
+ m.receiveResult(new Meteor.Error( // 1494
+ msg.error.error, msg.error.reason, // 1495
+ msg.error.details)); // 1496
+ } else { // 1497
+ // msg.result may be undefined if the method didn't return a // 1498
+ // value // 1499
+ m.receiveResult(undefined, msg.result); // 1500
+ } // 1501
+ }, // 1502
+ // 1503
+ // Called by MethodInvoker after a method's callback is invoked. If this was // 1504
+ // the last outstanding method in the current block, runs the next block. If // 1505
+ // there are no more methods, consider accepting a hot code push. // 1506
+ _outstandingMethodFinished: function () { // 1507
+ var self = this; // 1508
+ if (self._anyMethodsAreOutstanding()) // 1509
+ return; // 1510
+ // 1511
+ // No methods are outstanding. This should mean that the first block of // 1512
+ // methods is empty. (Or it might not exist, if this was a method that // 1513
+ // half-finished before disconnect/reconnect.) // 1514
+ if (! _.isEmpty(self._outstandingMethodBlocks)) { // 1515
+ var firstBlock = self._outstandingMethodBlocks.shift(); // 1516
+ if (! _.isEmpty(firstBlock.methods)) // 1517
+ throw new Error("No methods outstanding but nonempty block: " + // 1518
+ JSON.stringify(firstBlock)); // 1519
+ // 1520
+ // Send the outstanding methods now in the first block. // 1521
+ if (!_.isEmpty(self._outstandingMethodBlocks)) // 1522
+ self._sendOutstandingMethods(); // 1523
+ } // 1524
+ // 1525
+ // Maybe accept a hot code push. // 1526
+ self._maybeMigrate(); // 1527
+ }, // 1528
+ // 1529
+ // Sends messages for all the methods in the first block in // 1530
+ // _outstandingMethodBlocks. // 1531
+ _sendOutstandingMethods: function() { // 1532
+ var self = this; // 1533
+ if (_.isEmpty(self._outstandingMethodBlocks)) // 1534
+ return; // 1535
+ _.each(self._outstandingMethodBlocks[0].methods, function (m) { // 1536
+ m.sendMessage(); // 1537
+ }); // 1538
+ }, // 1539
+ // 1540
+ _livedata_error: function (msg) { // 1541
+ Meteor._debug("Received error from server: ", msg.reason); // 1542
+ if (msg.offendingMessage) // 1543
+ Meteor._debug("For: ", msg.offendingMessage); // 1544
+ }, // 1545
+ // 1546
+ _callOnReconnectAndSendAppropriateOutstandingMethods: function() { // 1547
+ var self = this; // 1548
+ var oldOutstandingMethodBlocks = self._outstandingMethodBlocks; // 1549
+ self._outstandingMethodBlocks = []; // 1550
+ // 1551
+ self.onReconnect(); // 1552
+ // 1553
+ if (_.isEmpty(oldOutstandingMethodBlocks)) // 1554
+ return; // 1555
+ // 1556
+ // We have at least one block worth of old outstanding methods to try // 1557
+ // again. First: did onReconnect actually send anything? If not, we just // 1558
+ // restore all outstanding methods and run the first block. // 1559
+ if (_.isEmpty(self._outstandingMethodBlocks)) { // 1560
+ self._outstandingMethodBlocks = oldOutstandingMethodBlocks; // 1561
+ self._sendOutstandingMethods(); // 1562
+ return; // 1563
+ } // 1564
+ // 1565
+ // OK, there are blocks on both sides. Special case: merge the last block of // 1566
+ // the reconnect methods with the first block of the original methods, if // 1567
+ // neither of them are "wait" blocks. // 1568
+ if (!_.last(self._outstandingMethodBlocks).wait && // 1569
+ !oldOutstandingMethodBlocks[0].wait) { // 1570
+ _.each(oldOutstandingMethodBlocks[0].methods, function (m) { // 1571
+ _.last(self._outstandingMethodBlocks).methods.push(m); // 1572
+ // 1573
+ // If this "last block" is also the first block, send the message. // 1574
+ if (self._outstandingMethodBlocks.length === 1) // 1575
+ m.sendMessage(); // 1576
+ }); // 1577
+ // 1578
+ oldOutstandingMethodBlocks.shift(); // 1579
+ } // 1580
+ // 1581
+ // Now add the rest of the original blocks on. // 1582
+ _.each(oldOutstandingMethodBlocks, function (block) { // 1583
+ self._outstandingMethodBlocks.push(block); // 1584
+ }); // 1585
+ }, // 1586
+ // 1587
+ // We can accept a hot code push if there are no methods in flight. // 1588
+ _readyToMigrate: function() { // 1589
+ var self = this; // 1590
+ return _.isEmpty(self._methodInvokers); // 1591
+ }, // 1592
+ // 1593
+ // If we were blocking a migration, see if it's now possible to continue. // 1594
+ // Call whenever the set of outstanding/blocked methods shrinks. // 1595
+ _maybeMigrate: function () { // 1596
+ var self = this; // 1597
+ if (self._retryMigrate && self._readyToMigrate()) { // 1598
+ self._retryMigrate(); // 1599
+ self._retryMigrate = null; // 1600
+ } // 1601
+ } // 1602
+}); // 1603
+ // 1604
+LivedataTest.Connection = Connection; // 1605
+ // 1606
+// @param url {String} URL to Meteor app, // 1607
+// e.g.: // 1608
+// "subdomain.meteor.com", // 1609
+// "http://subdomain.meteor.com", // 1610
+// "/", // 1611
+// "ddp+sockjs://ddp--****-foo.meteor.com/sockjs" // 1612
+ // 1613
+/** // 1614
+ * @summary Connect to the server of a different Meteor application to subscribe to its document sets and invoke its remote methods.
+ * @locus Anywhere // 1616
+ * @param {String} url The URL of another Meteor application. // 1617
+ */ // 1618
+DDP.connect = function (url, options) { // 1619
+ var ret = new Connection(url, options); // 1620
+ allConnections.push(ret); // hack. see below. // 1621
+ return ret; // 1622
+}; // 1623
+ // 1624
+// Hack for `spiderable` package: a way to see if the page is done // 1625
+// loading all the data it needs. // 1626
+// // 1627
+allConnections = []; // 1628
+DDP._allSubscriptionsReady = function () { // 1629
+ return _.all(allConnections, function (conn) { // 1630
+ return _.all(conn._subscriptions, function (sub) { // 1631
+ return sub.ready; // 1632
+ }); // 1633
+ }); // 1634
+}; // 1635
+ // 1636
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ddp/server_convenience.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Only create a server if we are in an environment with a HTTP server // 1
+// (as opposed to, eg, a command-line tool). // 2
+// // 3
+// Note: this whole conditional is a total hack to get around the fact that this // 4
+// package logically should be split into a ddp-client and ddp-server package; // 5
+// see https://github.com/meteor/meteor/issues/3452 // 6
+// // 7
+// Until we do that, this conditional (and the weak dependency on webapp that // 8
+// should really be a strong dependency of the ddp-server package) allows you to // 9
+// build projects which use `ddp` in Node without wanting to run a DDP server // 10
+// (ie, allows you to act as if you were using the nonexistent `ddp-client` // 11
+// server package). // 12
+if (Package.webapp) { // 13
+ if (process.env.DDP_DEFAULT_CONNECTION_URL) { // 14
+ __meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL = // 15
+ process.env.DDP_DEFAULT_CONNECTION_URL; // 16
+ } // 17
+ // 18
+ Meteor.server = new Server; // 19
+ // 20
+ Meteor.refresh = function (notification) { // 21
+ DDPServer._InvalidationCrossbar.fire(notification); // 22
+ }; // 23
+ // 24
+ // Proxy the public methods of Meteor.server so they can // 25
+ // be called directly on Meteor. // 26
+ _.each(['publish', 'methods', 'call', 'apply', 'onConnection'], // 27
+ function (name) { // 28
+ Meteor[name] = _.bind(Meteor.server[name], Meteor.server); // 29
+ }); // 30
+} else { // 31
+ // No server? Make these empty/no-ops. // 32
+ Meteor.server = null; // 33
+ Meteor.refresh = function (notification) { // 34
+ }; // 35
+ // 36
+ // Make these empty/no-ops too, so that non-webapp apps can still // 37
+ // depend on/use packages that use those functions. // 38
+ _.each(['publish', 'methods', 'onConnection'], // 39
+ function (name) { // 40
+ Meteor[name] = function () { }; // 41
+ }); // 42
+} // 43
+ // 44
+// Meteor.server used to be called Meteor.default_server. Provide // 45
+// backcompat as a courtesy even though it was never documented. // 46
+// XXX COMPAT WITH 0.6.4 // 47
+Meteor.default_server = Meteor.server; // 48
+ // 49
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.ddp = {
+ DDP: DDP,
+ DDPServer: DDPServer,
+ LivedataTest: LivedataTest
+};
+
+})();
+
+//# sourceMappingURL=ddp.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/ddp.js.map b/web-app/.meteor/local/build/programs/server/packages/ddp.js.map
new file mode 100644
index 0000000..fd1aa1e
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/ddp.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["ddp/common.js","ddp/stream_client_nodejs.js","ddp/stream_client_common.js","ddp/stream_server.js","ddp/heartbeat.js","ddp/livedata_server.js","ddp/writefence.js","ddp/crossbar.js","ddp/livedata_common.js","ddp/random_stream.js","ddp/livedata_connection.js","ddp/server_convenience.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,G;AACA,iB;AACA,kD;AACA,G;AACA,S;AACA,kB;;;;;;;;;;;;;;;;;;;ACLA,6C;AACA,6C;AACA,4C;AACA,E;AACA,6E;AACA,gF;AACA,kB;AACA,E;AACA,gF;AACA,6E;AACA,2C;AACA,0D;AACA,kB;AACA,0B;;AAEA,2B;AACA,e;AACA,c;;AAEA,sD;AACA,2B;;AAEA,4C;;AAEA,iC;;AAEA,e;AACA,2B;AACA,E;;AAEA,+C;;AAEA,uE;AACA,oE;AACA,wB;AACA,yB;AACA,oB;AACA,uC;AACA,6B;AACA,K;AACA,I;;AAEA,yC;AACA,8B;AACA,oB;AACA,wB;AACA,I;;AAEA,iC;AACA,oB;;AAEA,iC;AACA,wE;AACA,gF;AACA,0E;AACA,sB;AACA,wE;AACA,K;;AAEA,mC;AACA,8E;AACA,qE;AACA,0B;AACA,yB;AACA,a;AACA,K;;AAEA,uC;AACA,yE;AACA,uE;AACA,gF;AACA,gF;AACA,sC;AACA,mD;AACA,K;;AAEA,iC;;AAEA,oB;AACA,4C;AACA,wC;AACA,sC;AACA,yB;;AAEA,sE;AACA,kD;AACA,2E;AACA,I;;AAEA,mC;AACA,oB;;AAEA,iC;AACA,sB;AACA,+B;AACA,yB;AACA,qB;;AAEA,kE;AACA,6B;AACA,S;AACA,K;AACA,I;;AAEA,sC;AACA,oB;;AAEA,+B;AACA,yC;AACA,kC;AACA,K;AACA,I;;AAEA,sC;AACA,oB;AACA,gD;AACA,yE;AACA,8E;AACA,mC;AACA,0E;AACA,K;AACA,iB;AACA,I;;AAEA,kC;AACA,oB;AACA,iE;;AAEA,2E;AACA,kE;AACA,kB;AACA,sD;;AAEA,kD;AACA,gD;AACA,gD;AACA,mB;AACA,+C;AACA,M;;AAEA,gF;AACA,uE;AACA,6E;AACA,6E;AACA,yE;AACA,0B;;AAEA,wD;AACA,4C;;AAEA,iC;AACA,6C;AACA,mB;AACA,6B;AACA,+D;AACA,Q;AACA,4B;;AAEA,+D;AACA,qC;AACA,mC;;AAEA,8D;AACA,gE;AACA,sE;AACA,mC;AACA,iB;AACA,iC;AACA,uB;AACA,M;;AAEA,0E;AACA,yC;AACA,qD;;AAEA,yE;AACA,oD;AACA,mE;AACA,O;;;AAGA,qE;AACA,6B;AACA,O;;;AAGA,gF;AACA,6D;AACA,2C;AACA,e;;AAEA,+D;AACA,+B;AACA,S;AACA,O;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACpMA,0E;AACA,wC;AACA,uC;AACA,+C;AACA,E;AACA,oC;AACA,qC;AACA,qD;AACA,E;;AAEA,6C;AACA,6D;AACA,sD;AACA,mF;AACA,yC;AACA,sD;AACA,iD;AACA,2D;AACA,wB;AACA,2B;AACA,G;;AAEA,uD;AACA,iD;AACA,gB;AACA,oB;AACA,4C;AACA,wD;AACA,6E;AACA,4C;AACA,c;AACA,0E;AACA,mE;;AAEA,0E;AACA,4E;AACA,wD;AACA,4C;AACA,8C;AACA,O;;AAEA,2C;AACA,4B;AACA,uE;AACA,0D;AACA,2C;AACA,G;;AAEA,uC;AACA,2D;AACA,sC;AACA,G;;AAEA,8D;AACA,sE;AACA,kE;AACA,sE;AACA,+B;AACA,I;AACA,kE;AACA,sE;AACA,iE;AACA,uE;AACA,2C;;AAEA,yB;AACA,yB;AACA,M;AACA,+B;AACA,E;;AAEA,8B;AACA,6C;AACA,E;;AAEA,iC;AACA,iD;AACA,a;AACA,E;;AAEA,uC;;;AAGA,+C;;AAEA,4B;AACA,iC;AACA,oB;;AAEA,wE;AACA,qD;;AAEA,mC;AACA,qC;AACA,6C;AACA,I;;;AAGA,mC;AACA,oB;AACA,4B;;AAEA,kB;;AAEA,+D;AACA,c;AACA,6D;;AAEA,mD;;AAEA,qC;;AAEA,wB;AACA,0B;AACA,2B;AACA,uB;AACA,mB;AACA,M;;;AAGA,oF;AACA,sC;AACA,+B;AACA,uC;AACA,M;;AAEA,oB;AACA,4B;AACA,gC;;AAEA,I;;AAEA,yB;AACA,iC;AACA,oB;AACA,4B;;AAEA,sB;AACA,mC;AACA,K;;AAEA,iC;AACA,2D;AACA,K;;AAEA,uC;AACA,0C;AACA,2B;AACA,2D;AACA,sB;AACA,a;AACA,K;;AAEA,wC;AACA,qD;AACA,oC;AACA,6B;AACA,K;;AAEA,wB;AACA,qE;AACA,qB;AACA,I;;AAEA,kC;AACA,oB;AACA,4B;;AAEA,qE;AACA,uD;AACA,iC;AACA,a;;AAEA,2E;AACA,mE;AACA,mE;AACA,6B;AACA,6B;AACA,sC;AACA,K;;AAEA,oB;AACA,wB;;AAEA,0B;AACA,0D;AACA,uB;AACA,mB;AACA,M;;AAEA,6C;AACA,iD;;AAEA,yB;AACA,I;;AAEA,gE;AACA,0C;AACA,oB;;AAEA,8B;AACA,wE;AACA,I;;AAEA,kE;AACA,iB;AACA,wB;AACA,0E;AACA,+C;AACA,uB;AACA,I;;AAEA,sC;AACA,oB;;AAEA,oB;AACA,6B;AACA,8E;AACA,uC;AACA,sC;AACA,oC;AACA,Q;AACA,4C;AACA,sE;AACA,Y;AACA,2C;AACA,0C;AACA,K;;AAEA,yC;AACA,yB;AACA,I;;AAEA,0B;AACA,oB;;AAEA,iC;AACA,a;;AAEA,uC;AACA,6C;AACA,yC;AACA,wC;AACA,yB;;AAEA,6B;AACA,I;;;AAGA,kC;AACA,uB;AACA,oB;AACA,6B;AACA,oC;AACA,8B;AACA,G;AACA,G;;AAEA,2C;AACA,6C;AACA,oB;AACA,2B;AACA,G;;AAEA,gD;AACA,8C;;;;;;;;;;;;;;;;;;;ACzQA,6B;;AAEA,uE;;AAEA,4B;AACA,kB;AACA,mC;AACA,yB;;AAEA,+E;AACA,8D;AACA,uC;AACA,6E;AACA,iD;AACA,4B;AACA,0E;AACA,G;;AAEA,kB;AACA,qC;AACA,uB;AACA,wB;AACA,uB;AACA,uE;AACA,+C;AACA,2B;AACA,+E;AACA,0E;AACA,6E;AACA,4E;AACA,+E;AACA,8D;AACA,gC;AACA,wE;AACA,oE;AACA,wB;AACA,4C;AACA,I;;AAEA,8E;AACA,uE;AACA,0D;AACA,yD;AACA,qC;AACA,oC;;AAEA,mD;AACA,wB;AACA,6E;AACA,G;AACA,+E;AACA,4E;AACA,8E;AACA,kC;AACA,sH;AACA,gE;AACA,mH;;AAEA,oC;AACA,oC;;AAEA,kD;AACA,mC;AACA,yB;AACA,M;AACA,oC;AACA,+D;AACA,O;AACA,mC;;AAEA,uE;AACA,mE;AACA,uE;AACA,uE;AACA,+B;AACA,kD;;AAEA,wE;AACA,kE;AACA,6D;AACA,uB;AACA,O;AACA,K;;AAEA,E;;AAEA,kC;AACA,iD;AACA,8C;AACA,iC;AACA,oB;AACA,+C;AACA,kD;AACA,uB;AACA,O;AACA,I;;AAEA,8B;AACA,4B;AACA,oB;AACA,uC;AACA,I;;AAEA,oE;AACA,sD;AACA,0C;AACA,oB;AACA,iE;AACA,6D;AACA,oE;AACA,oD;AACA,2G;AACA,oD;AACA,wD;AACA,wE;AACA,2C;;AAEA,gE;AACA,iE;AACA,gE;AACA,2D;AACA,6B;;AAEA,6E;AACA,mC;AACA,+C;AACA,+D;AACA,gE;AACA,0D;AACA,8C;AACA,S;AACA,8D;AACA,8C;AACA,W;AACA,Q;AACA,iD;AACA,O;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;AC1IA,qB;AACA,gE;AACA,uE;AACA,iC;AACA,iE;AACA,yD;;AAEA,gC;AACA,kB;;AAEA,qD;AACA,mD;AACA,oC;AACA,sC;;AAEA,uC;AACA,sC;AACA,E;;AAEA,+B;AACA,qB;AACA,oB;AACA,wC;AACA,uC;AACA,I;;AAEA,sB;AACA,oB;AACA,gB;AACA,wC;AACA,I;;AAEA,6C;AACA,oB;AACA,sD;AACA,iD;AACA,4B;AACA,M;AACA,I;;AAEA,4C;AACA,oB;AACA,qD;AACA,gD;AACA,2B;AACA,M;AACA,I;;AAEA,6C;AACA,oB;AACA,wC;AACA,yD;AACA,2C;AACA,K;AACA,I;;AAEA,4C;AACA,oB;AACA,uC;AACA,wD;AACA,0C;AACA,K;AACA,I;;AAEA,sE;AACA,wC;AACA,oB;AACA,yC;AACA,qB;AACA,uB;AACA,uC;AACA,I;;AAEA,qE;AACA,oC;AACA,uC;AACA,oB;AACA,wC;AACA,sB;AACA,I;;AAEA,6B;AACA,oB;AACA,kE;AACA,sE;AACA,wC;AACA,0C;AACA,0C;AACA,K;AACA,I;;AAEA,6B;AACA,oB;;AAEA,oE;AACA,0C;AACA,uC;AACA,yC;AACA,0C;AACA,K;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACrGA,e;;AAEA,kC;;AAEA,8B;AACA,6D;AACA,6D;AACA,2E;AACA,E;AACA,oE;AACA,qE;AACA,a;;AAEA,0D;AACA,uC;AACA,kB;AACA,kD;AACA,4E;AACA,E;;AAEA,yC;;AAEA,0B;AACA,oB;AACA,iB;AACA,2D;AACA,yC;AACA,O;AACA,e;AACA,I;;AAEA,mE;AACA,oB;AACA,mD;AACA,sB;AACA,a;AACA,6C;;AAEA,oE;AACA,gB;AACA,wB;AACA,a;;AAEA,iC;AACA,qD;AACA,yC;AACA,iE;AACA,gF;AACA,mC;AACA,oB;AACA,0C;AACA,oC;AACA,c;AACA,O;AACA,K;AACA,oC;AACA,iC;AACA,uC;AACA,4C;AACA,sE;AACA,qD;AACA,K;AACA,I;;AAEA,wD;AACA,kD;AACA,oB;AACA,mD;AACA,sB;AACA,a;;AAEA,6D;AACA,+B;;AAEA,sC;AACA,qE;AACA,6C;AACA,mC;AACA,a;AACA,K;AACA,6C;AACA,Y;AACA,iB;AACA,0D;AACA,oE;AACA,S;AACA,K;;AAEA,c;AACA,yE;AACA,iE;AACA,qC;AACA,O;AACA,wB;AACA,Y;AACA,2D;AACA,kF;AACA,K;;AAEA,G;AACA,G;;AAEA,G;AACA,oD;AACA,sE;AACA,gG;AACA,+B;AACA,G;AACA,yE;AACA,kB;AACA,uC;AACA,sB;AACA,oC;AACA,E;;AAEA,2D;;;AAGA,2C;;AAEA,wB;AACA,oB;AACA,qC;AACA,I;;AAEA,6B;AACA,oB;AACA,sE;AACA,4C;;AAEA,uC;AACA,yE;AACA,Q;;AAEA,uC;AACA,wD;AACA,O;AACA,O;AACA,I;;AAEA,8C;AACA,oB;AACA,oB;AACA,yE;AACA,uC;AACA,qC;AACA,4B;AACA,Q;AACA,sC;AACA,0B;AACA,Q;AACA,qC;AACA,gC;AACA,O;AACA,O;AACA,4D;AACA,I;;AAEA,oD;AACA,oB;AACA,qC;AACA,sB;AACA,mB;AACA,mB;AACA,0C;AACA,mC;AACA,K;AACA,gD;AACA,6B;AACA,0C;AACA,0B;AACA,+D;AACA,O;AACA,c;AACA,qE;AACA,Q;AACA,uE;AACA,I;;AAEA,uD;AACA,oB;AACA,2B;AACA,qC;AACA,iB;AACA,6E;AACA,2C;AACA,8B;AACA,mE;AACA,U;AACA,2E;AACA,O;AACA,mE;AACA,I;;AAEA,8C;AACA,oB;AACA,qC;AACA,mB;AACA,gE;AACA,gB;AACA,K;AACA,gD;AACA,sC;AACA,iC;AACA,sD;AACA,gC;AACA,Y;AACA,uB;AACA,4D;AACA,+B;AACA,gE;AACA,6D;AACA,S;;AAEA,+D;AACA,K;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,2D;AACA,kB;AACA,wB;;AAEA,uB;AACA,yB;;AAEA,2B;AACA,uB;;AAEA,qE;AACA,0D;AACA,gD;;AAEA,uB;AACA,6B;;AAEA,yC;AACA,uB;AACA,2B;;AAEA,qB;;AAEA,4B;;AAEA,oE;AACA,gF;AACA,uC;AACA,yB;;AAEA,6E;AACA,yE;AACA,0C;;AAEA,4D;AACA,sE;AACA,0B;;AAEA,8D;AACA,4B;;;AAGA,gE;AACA,mD;AACA,+B;;AAEA,gD;AACA,gD;;AAEA,sE;AACA,sE;AACA,6C;AACA,2B;AACA,gB;AACA,wB;AACA,mB;AACA,M;AACA,4B;AACA,yE;AACA,yB;AACA,sC;AACA,c;AACA,sD;AACA,yB;AACA,O;AACA,M;AACA,yC;AACA,oC;AACA,I;;AAEA,6C;AACA,gD;AACA,8D;AACA,qB;AACA,8B;AACA,W;;AAEA,8D;AACA,oC;AACA,mD;AACA,iD;AACA,8B;AACA,qB;AACA,Q;AACA,6B;AACA,iC;AACA,O;AACA,O;AACA,2B;AACA,G;;AAEA,2D;AACA,+B;AACA,E;;AAEA,6B;;AAEA,yC;AACA,oB;AACA,wB;AACA,uD;AACA,U;AACA,yD;AACA,gD;AACA,S;AACA,K;AACA,I;;AAEA,oD;AACA,oB;AACA,wB;AACA,oF;AACA,I;;AAEA,sD;AACA,oB;AACA,0B;AACA,a;;AAEA,0B;AACA,iB;AACA,uB;AACA,mC;AACA,e;AACA,sB;AACA,S;AACA,K;AACA,I;;AAEA,8C;AACA,oB;AACA,wB;AACA,sE;AACA,I;;AAEA,iC;AACA,oB;AACA,Y;AACA,0C;AACA,8C;AACA,6C;AACA,M;AACA,I;;AAEA,gD;AACA,oB;AACA,sD;AACA,kD;AACA,K;AACA,uD;AACA,iE;AACA,+C;AACA,e;AACA,I;;AAEA,oE;AACA,oB;AACA,sD;AACA,+C;AACA,I;;AAEA,8D;AACA,oB;AACA,sD;AACA,yC;AACA,yB;AACA,kD;AACA,K;AACA,I;;AAEA,sE;AACA,oB;AACA,sD;AACA,iD;AACA,I;;AAEA,mC;AACA,oB;AACA,8E;AACA,6E;AACA,qE;AACA,mE;AACA,yC;AACA,uC;AACA,O;AACA,I;;AAEA,0D;AACA,sB;AACA,oB;;AAEA,+D;AACA,wE;AACA,8B;;AAEA,yB;AACA,uB;AACA,a;;AAEA,2C;AACA,wB;AACA,8B;;AAEA,yB;AACA,4B;AACA,4B;AACA,K;;AAEA,sB;AACA,0B;AACA,wC;AACA,K;;AAEA,6D;AACA,kC;;AAEA,8B;AACA,6D;AACA,qE;AACA,gF;AACA,yC;;AAEA,sE;AACA,qE;AACA,wD;AACA,mB;AACA,S;AACA,O;;AAEA,8B;AACA,qC;AACA,I;;AAEA,wE;AACA,yD;AACA,wB;AACA,oB;AACA,sB;AACA,+B;AACA,qD;AACA,0C;AACA,K;AACA,I;;AAEA,6B;AACA,kD;AACA,oB;AACA,6C;AACA,yB;AACA,8C;AACA,mB;AACA,I;;AAEA,kE;AACA,+D;AACA,qD;AACA,I;AACA,oE;AACA,qE;AACA,oE;AACA,sE;AACA,mE;AACA,qB;AACA,I;AACA,oE;AACA,oE;AACA,oE;AACA,Y;AACA,qC;AACA,oB;AACA,iD;AACA,a;;AAEA,qE;AACA,mE;AACA,oE;AACA,yC;AACA,M;AACA,wE;AACA,wE;AACA,qE;AACA,gD;AACA,2D;AACA,+B;AACA,gD;AACA,yB;AACA,2B;AACA,wC;AACA,iB;AACA,a;AACA,K;AACA,2D;AACA,yB;AACA,2B;AACA,wC;AACA,iB;AACA,a;AACA,K;;AAEA,8B;AACA,2B;AACA,a;AACA,8B;;AAEA,mC;AACA,qD;AACA,iB;AACA,mC;AACA,e;AACA,O;;AAEA,yB;AACA,2B;;AAEA,mC;AACA,uB;AACA,iC;AACA,0B;AACA,wB;AACA,U;;AAEA,mD;AACA,mE;AACA,Y;AACA,6C;AACA,8D;AACA,e;AACA,M;;AAEA,kB;AACA,I;;AAEA,sB;AACA,yB;AACA,sB;;AAEA,kC;AACA,yC;AACA,2C;AACA,kE;AACA,sD;AACA,e;AACA,O;;AAEA,oD;AACA,mB;AACA,mC;AACA,mE;AACA,e;AACA,O;;AAEA,yC;AACA,oE;AACA,gE;AACA,qB;AACA,e;;AAEA,2D;AACA,qE;;AAEA,M;;AAEA,2B;AACA,sB;;AAEA,qC;AACA,M;;AAEA,qC;AACA,sB;;AAEA,kC;AACA,wD;AACA,oC;AACA,yC;AACA,6C;AACA,kE;AACA,4E;AACA,2D;AACA,e;AACA,O;;AAEA,8C;;AAEA,kE;AACA,iE;AACA,c;AACA,4C;AACA,wC;AACA,8D;AACA,gE;AACA,6D;AACA,4D;AACA,gD;AACA,uB;AACA,mB;AACA,8C;AACA,S;;AAEA,yB;AACA,4D;AACA,qB;AACA,mB;AACA,oC;AACA,6D;AACA,oB;AACA,e;AACA,O;;AAEA,wC;AACA,gC;AACA,Q;;AAEA,6C;AACA,4B;AACA,4B;AACA,6B;AACA,yB;AACA,0C;AACA,8B;AACA,S;AACA,W;AACA,gF;AACA,2E;AACA,4C;AACA,+E;AACA,a;AACA,W;AACA,mB;AACA,0B;AACA,O;;AAEA,2D;AACA,iE;;AAEA,wC;AACA,iE;;AAEA,uC;AACA,mB;AACA,gE;AACA,iE;AACA,gE;AACA,K;AACA,I;;AAEA,0B;AACA,oB;AACA,+B;AACA,mC;AACA,I;;AAEA,8C;AACA,oB;AACA,mE;AACA,8D;AACA,mC;AACA,Q;AACA,wD;AACA,6D;AACA,kE;AACA,W;AACA,Q;AACA,sD;AACA,wD;AACA,+C;AACA,W;AACA,O;AACA,O;AACA,I;;AAEA,oE;AACA,sB;AACA,gC;AACA,oB;;AAEA,sD;AACA,0E;AACA,qC;;AAEA,4E;AACA,yE;AACA,M;AACA,6E;AACA,0E;AACA,8E;AACA,+E;AACA,+B;AACA,2C;;AAEA,4E;AACA,sC;AACA,kC;AACA,wB;AACA,O;;AAEA,+E;AACA,+E;AACA,yB;AACA,4B;AACA,yC;AACA,8B;AACA,yB;;AAEA,qE;AACA,uC;AACA,yB;AACA,6B;;AAEA,yD;AACA,wD;AACA,yE;AACA,0D;AACA,oD;AACA,O;;AAEA,4E;AACA,2E;AACA,Y;AACA,4C;AACA,8B;;AAEA,8E;AACA,4E;AACA,0D;AACA,yC;AACA,6B;AACA,2C;AACA,2C;AACA,2C;AACA,gC;AACA,O;AACA,O;AACA,I;;AAEA,+D;AACA,oB;;AAEA,+B;AACA,0C;AACA,c;AACA,mC;AACA,Q;AACA,oC;;AAEA,sB;AACA,I;;AAEA,qC;AACA,8C;AACA,oB;;AAEA,uB;;AAEA,0C;AACA,6C;AACA,mD;AACA,2C;AACA,oC;AACA,K;;AAEA,6C;;AAEA,gB;AACA,6C;AACA,c;AACA,0D;AACA,sC;AACA,K;;AAEA,wB;AACA,I;;AAEA,+E;AACA,kD;AACA,4C;AACA,oB;;AAEA,gD;AACA,wB;AACA,O;AACA,yB;;AAEA,gD;AACA,wB;AACA,O;AACA,6B;AACA,I;;AAEA,2D;AACA,oE;AACA,kC;AACA,+B;AACA,oB;;AAEA,sE;AACA,kE;AACA,kE;AACA,sE;AACA,kC;AACA,M;AACA,oE;AACA,gF;;AAEA,iC;AACA,uC;;AAEA,8D;AACA,mC;AACA,kB;AACA,wD;;AAEA,mE;AACA,oE;AACA,oE;AACA,qE;AACA,kE;AACA,qE;AACA,kE;AACA,qC;;AAEA,2E;AACA,kB;;AAEA,kE;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,4D;;AAEA,0E;AACA,U;AACA,G;AACA,+C;AACA,sB;AACA,qB;AACA,G;AACA,6B;AACA,qD;AACA,kB;AACA,6C;;AAEA,K;AACA,wH;AACA,kB;AACA,sB;AACA,2B;AACA,c;AACA,K;AACA,kE;;AAEA,0B;;AAEA,4E;AACA,wC;AACA,iC;AACA,oB;;AAEA,8B;;AAEA,uE;AACA,yD;AACA,mE;AACA,6B;AACA,0D;AACA,U;AACA,iD;AACA,G;;AAEA,iC;AACA,4B;;AAEA,+D;AACA,2B;;AAEA,mE;AACA,qB;AACA,uB;;AAEA,8B;AACA,sB;;AAEA,kD;;AAEA,K;AACA,kH;AACA,kB;AACA,2B;AACA,kB;AACA,c;AACA,K;AACA,+B;;AAEA,kD;AACA,mD;AACA,sD;;AAEA,kD;AACA,sD;AACA,wD;AACA,wC;;AAEA,oB;AACA,8C;AACA,qC;AACA,I;;AAEA,2D;AACA,oC;AACA,E;;AAEA,kC;AACA,4B;AACA,sE;AACA,kD;AACA,M;AACA,uE;AACA,wE;AACA,iE;;AAEA,oB;AACA,S;AACA,yC;AACA,uD;AACA,0E;AACA,kE;AACA,yC;AACA,0C;AACA,iB;AACA,oB;AACA,a;AACA,K;;AAEA,oD;AACA,8B;AACA,a;;AAEA,uE;AACA,0E;AACA,0E;AACA,gF;AACA,+D;AACA,M;AACA,0E;AACA,8E;AACA,8E;AACA,6C;AACA,iE;AACA,6C;AACA,4C;AACA,wC;AACA,c;AACA,W;AACA,iC;AACA,mC;AACA,M;AACA,wB;AACA,W;AACA,iC;AACA,mB;AACA,sB;AACA,e;AACA,O;AACA,gF;AACA,oC;AACA,mB;AACA,gC;AACA,2C;AACA,mC;AACA,mF;AACA,e;AACA,O;AACA,wC;AACA,8E;AACA,yD;AACA,+B;AACA,4C;AACA,yD;AACA,qD;AACA,+B;AACA,0E;AACA,+B;AACA,iB;AACA,S;AACA,+C;AACA,Q;;AAEA,W;AACA,oC;AACA,mC;AACA,W;AACA,mB;AACA,sB;AACA,e;AACA,O;AACA,mB;AACA,qB;AACA,kE;AACA,oE;AACA,2B;AACA,0E;AACA,qD;AACA,K;AACA,I;;AAEA,6E;AACA,4E;AACA,yE;AACA,2E;AACA,+B;AACA,2B;AACA,oB;AACA,0B;AACA,a;AACA,6B;AACA,8B;AACA,6D;AACA,uC;AACA,I;;AAEA,mC;AACA,oB;AACA,2C;AACA,wC;AACA,6B;AACA,2C;AACA,iB;AACA,O;AACA,I;;AAEA,6C;AACA,oC;AACA,oB;AACA,yC;AACA,wE;AACA,+E;AACA,uB;AACA,yD;AACA,sE;AACA,W;AACA,S;AACA,O;AACA,I;;AAEA,kE;AACA,qE;AACA,sE;AACA,gE;AACA,mC;AACA,0B;AACA,oB;AACA,4B;AACA,uE;AACA,kB;AACA,I;;AAEA,K;AACA,kS;AACA,kB;AACA,0D;AACA,c;AACA,2B;AACA,K;AACA,2B;AACA,oB;AACA,8B;AACA,a;AACA,iE;AACA,I;;AAEA,gF;AACA,+E;AACA,6E;AACA,oC;;AAEA,K;AACA,2I;AACA,kB;AACA,c;AACA,2B;AACA,K;AACA,qB;AACA,oB;AACA,8B;AACA,a;AACA,0D;AACA,I;;AAEA,K;AACA,uH;AACA,kB;AACA,2B;AACA,c;AACA,iD;AACA,K;AACA,+B;AACA,oB;AACA,8B;AACA,iB;AACA,Q;AACA,yC;AACA,I;;AAEA,+E;AACA,0E;AACA,kB;AACA,+B;AACA,oB;AACA,+D;AACA,I;;AAEA,K;AACA,yH;AACA,kB;AACA,2B;AACA,c;AACA,0F;AACA,8C;AACA,+F;AACA,K;AACA,gD;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,+D;AACA,8E;AACA,I;;AAEA,K;AACA,4H;AACA,kB;AACA,2B;AACA,c;AACA,8F;AACA,kD;AACA,yS;AACA,K;AACA,kD;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,gF;AACA,I;;AAEA,K;AACA,6H;AACA,kB;AACA,2B;AACA,c;AACA,mG;AACA,qE;AACA,K;AACA,0C;AACA,oB;AACA,8B;AACA,a;AACA,wC;AACA,sE;AACA,gE;AACA,+C;AACA,wE;AACA,I;;AAEA,K;AACA,yQ;AACA,kB;AACA,2B;AACA,c;AACA,K;AACA,sB;AACA,oB;AACA,8B;AACA,a;AACA,8B;AACA,2D;AACA,uB;AACA,sD;AACA,yB;AACA,K;AACA,G;AACA,G;;AAEA,gF;AACA,gF;AACA,gF;;AAEA,6B;AACA,kB;;AAEA,sE;AACA,oE;AACA,iE;AACA,+C;AACA,I;AACA,uD;AACA,2E;AACA,4C;AACA,6B;AACA,4B;AACA,6D;AACA,wB;AACA,K;;AAEA,mE;AACA,wE;AACA,gE;AACA,uB;AACA,oC;AACA,iD;AACA,K;;AAEA,6B;AACA,uC;;AAEA,4B;;AAEA,+C;;AAEA,wC;;AAEA,iD;AACA,uD;AACA,iC;;AAEA,yD;AACA,+C;AACA,2B;AACA,gD;AACA,qC;AACA,M;;AAEA,0C;AACA,qC;AACA,+C;AACA,O;AACA,W;AACA,a;AACA,sC;AACA,uB;AACA,mC;AACA,iB;AACA,S;AACA,uC;AACA,wC;AACA,iB;AACA,S;;AAEA,oC;AACA,sC;AACA,gD;AACA,mB;AACA,W;AACA,6B;AACA,6C;AACA,mB;AACA,iB;AACA,S;;AAEA,qC;AACA,+C;AACA,iB;AACA,S;AACA,kD;AACA,mB;AACA,iC;AACA,yE;AACA,0C;AACA,O;AACA,O;;AAEA,oC;AACA,kC;AACA,2B;AACA,wC;AACA,iB;AACA,O;AACA,O;AACA,K;AACA,E;;AAEA,4B;;AAEA,K;AACA,+F;AACA,kB;AACA,8F;AACA,qB;AACA,K;AACA,+B;AACA,oB;AACA,8C;AACA,I;;AAEA,0C;AACA,oB;;AAEA,2E;AACA,mE;AACA,8C;AACA,mC;AACA,2C;AACA,kD;AACA,8C;AACA,sE;AACA,qB;AACA,a;AACA,K;;AAEA,gE;AACA,0D;AACA,wE;;AAEA,kC;AACA,+E;AACA,+E;AACA,wB;AACA,mE;AACA,qB;AACA,a;AACA,K;;AAEA,kD;AACA,yD;AACA,6E;AACA,6E;AACA,oE;AACA,oD;AACA,gC;AACA,yD;AACA,kB;AACA,O;AACA,I;AACA,K;AACA,yC;AACA,I;AACA,8C;AACA,8C;AACA,4B;AACA,I;AACA,gE;AACA,0E;AACA,8C;AACA,I;AACA,yD;AACA,mE;AACA,oE;AACA,sB;AACA,I;AACA,wB;AACA,kE;AACA,oE;AACA,mE;AACA,uC;AACA,K;;AAEA,K;AACA,mC;AACA,qB;AACA,kB;AACA,qJ;AACA,qQ;AACA,K;AACA,8C;AACA,oB;;AAEA,4B;;AAEA,gD;AACA,uE;AACA,a;AACA,K;;AAEA,kD;AACA,iE;AACA,gE;AACA,kE;AACA,+D;AACA,kE;AACA,kE;AACA,6C;AACA,2C;AACA,6C;AACA,sB;AACA,yE;AACA,2E;AACA,yE;AACA,2C;AACA,Q;AACA,kE;AACA,Q;AACA,sC;AACA,Q;AACA,gF;AACA,0D;AACA,O;AACA,K;;AAEA,a;AACA,4C;AACA,U;AACA,oD;AACA,wE;AACA,6E;AACA,+D;AACA,gD;AACA,kD;AACA,4B;AACA,gD;AACA,mB;AACA,S;AACA,S;AACA,K;AACA,I;;AAEA,sC;AACA,oB;AACA,oC;AACA,uC;AACA,K;AACA,I;;AAEA,K;AACA,gF;AACA,oB;AACA,6F;AACA,qB;AACA,K;AACA,+B;AACA,oB;AACA,2C;AACA,qC;AACA,4E;AACA,wC;AACA,O;AACA,I;;AAEA,0C;AACA,oE;AACA,4C;AACA,wD;AACA,mE;AACA,gC;AACA,4C;AACA,I;;AAEA,qC;AACA,wC;AACA,mD;AACA,oB;;AAEA,2E;AACA,gC;AACA,qD;AACA,yB;AACA,mB;AACA,K;AACA,4B;;AAEA,iB;AACA,mE;AACA,sE;AACA,+B;AACA,yD;AACA,wC;AACA,iB;AACA,sD;AACA,Q;;AAEA,sB;AACA,6C;AACA,kB;AACA,mB;AACA,4D;AACA,Y;AACA,qE;AACA,gE;AACA,+B;AACA,wB;AACA,kC;AACA,kF;AACA,Q;AACA,4B;AACA,2D;AACA,8B;AACA,0C;AACA,sC;AACA,8C;AACA,U;AACA,kD;AACA,O;;AAEA,6C;AACA,4B;AACA,uB;AACA,6B;AACA,+B;AACA,wD;AACA,S;AACA,W;AACA,+E;AACA,0C;AACA,0E;AACA,0B;AACA,W;AACA,qC;AACA,mB;AACA,sB;AACA,O;AACA,K;;AAEA,+E;AACA,4E;AACA,gF;AACA,6E;AACA,mD;AACA,mB;AACA,kC;AACA,uB;AACA,K;AACA,kB;AACA,sB;AACA,kB;AACA,I;;AAEA,wC;AACA,oB;AACA,2C;AACA,gB;AACA,gC;AACA,Q;AACA,kB;AACA,G;AACA,G;;AAEA,yD;AACA,2D;AACA,2E;AACA,wD;AACA,K;AACA,wB;AACA,gD;AACA,G;AACA,wB;AACA,E;;AAEA,iD;;;AAGA,8E;AACA,uB;AACA,2D;AACA,sD;AACA,qB;;AAEA,4E;AACA,e;AACA,4B;AACA,2D;AACA,mC;AACA,kG;AACA,sB;AACA,K;AACA,G;;AAEA,gF;AACA,yE;AACA,8E;AACA,qB;AACA,iC;AACA,yD;AACA,sC;AACA,+E;AACA,qD;AACA,G;;AAEA,wD;AACA,E;;;AAGA,8E;AACA,oC;AACA,yE;AACA,oB;AACA,yC;AACA,kD;AACA,qC;AACA,G;AACA,gC;AACA,E;;;;;;;;;;;;;;;;;;;ACjlDA,+B;AACA,wD;;AAEA,oE;AACA,iE;AACA,kE;AACA,E;AACA,qC;AACA,kB;;AAEA,qB;AACA,qB;AACA,uB;AACA,8B;AACA,iC;AACA,E;;AAEA,qE;AACA,sE;AACA,gB;AACA,E;AACA,8D;;AAEA,2C;AACA,sE;AACA,mE;AACA,qE;AACA,sE;AACA,6D;AACA,2B;AACA,oB;;AAEA,qB;AACA,2C;;AAEA,mB;AACA,+E;;AAEA,8B;AACA,0B;AACA,Y;AACA,8B;AACA,sB;AACA,sE;AACA,yB;AACA,kC;AACA,0B;AACA,O;AACA,M;AACA,I;;AAEA,kE;AACA,0C;AACA,oB;AACA,oB;AACA,oD;AACA,iD;AACA,sB;AACA,sB;AACA,I;;AAEA,2D;AACA,mC;AACA,oB;AACA,mB;AACA,qE;AACA,wC;AACA,yC;AACA,I;;AAEA,sE;AACA,2B;AACA,oB;AACA,4B;AACA,qC;AACA,yB;AACA,O;AACA,e;AACA,kB;AACA,I;;AAEA,2B;AACA,oB;AACA,mB;AACA,wD;AACA,iD;AACA,wB;AACA,iE;AACA,qC;AACA,K;AACA,I;;AAEA,oE;AACA,uC;AACA,uB;AACA,oB;AACA,qB;AACA,iE;AACA,wB;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACpGA,8E;AACA,yE;AACA,4E;;AAEA,0C;AACA,kB;AACA,0B;;AAEA,kB;AACA,gF;AACA,gC;AACA,kC;AACA,uD;AACA,2C;AACA,E;;AAEA,yC;AACA,iE;AACA,0D;AACA,kE;AACA,6B;AACA,I;AACA,8D;AACA,2C;AACA,I;AACA,iE;AACA,c;AACA,wC;AACA,oB;AACA,2B;;AAEA,kD;AACA,+C;AACA,K;;AAEA,4E;AACA,qE;AACA,0D;AACA,kD;AACA,K;AACA,wD;;AAEA,yC;AACA,8C;AACA,4C;AACA,K;;AAEA,Y;AACA,yB;AACA,6C;AACA,kD;AACA,iD;AACA,S;AACA,0D;AACA,gE;AACA,wD;AACA,S;AACA,O;AACA,M;AACA,I;;AAEA,gE;AACA,sE;AACA,gC;AACA,I;AACA,+D;AACA,sE;AACA,I;AACA,oE;AACA,iC;AACA,oB;;AAEA,uD;AACA,oD;AACA,K;;AAEA,qE;AACA,a;;AAEA,gC;AACA,8D;AACA,yB;AACA,qD;AACA,mD;AACA,6B;AACA,O;AACA,O;;AAEA,+E;AACA,+E;AACA,sE;AACA,2E;AACA,4C;AACA,oE;AACA,+E;AACA,8E;AACA,wC;AACA,uC;AACA,8C;AACA,0D;AACA,O;AACA,O;AACA,I;;AAEA,+E;AACA,I;AACA,c;AACA,qD;AACA,uD;AACA,4B;AACA,8D;AACA,uE;AACA,8D;AACA,uD;AACA,wB;AACA,uE;AACA,2E;AACA,8B;AACA,8E;AACA,yE;AACA,0C;AACA,8C;AACA,8E;AACA,4E;AACA,6E;AACA,sE;AACA,6E;AACA,+C;AACA,0C;AACA,yC;AACA,mB;AACA,K;AACA,+D;AACA,0D;AACA,+C;AACA,mB;AACA,K;;AAEA,wD;AACA,yC;AACA,sD;AACA,O;AACA,G;AACA,G;;AAEA,+E;AACA,2E;AACA,gF;AACA,6E;AACA,4B;AACA,2D;AACA,6C;AACA,G;;;;;;;;;;;;;;;;;;;ACxJA,8D;AACA,2D;AACA,iD;;AAEA,6D;;AAEA,2E;AACA,oB;AACA,G;AACA,6E;AACA,8B;AACA,0B;AACA,qB;AACA,G;AACA,uC;AACA,kB;;AAEA,sE;AACA,qE;AACA,iE;AACA,gE;AACA,wE;AACA,sE;AACA,yC;;AAEA,K;AACA,mG;AACA,oB;AACA,wB;AACA,+B;AACA,c;AACA,oB;AACA,K;AACA,2C;;AAEA,mE;AACA,qE;AACA,c;AACA,oD;AACA,8B;;AAEA,oB;;AAEA,K;AACA,gG;AACA,oB;AACA,kB;AACA,+B;AACA,c;AACA,K;AACA,+B;;AAEA,gE;AACA,yB;AACA,wD;;AAEA,+D;;AAEA,K;AACA,uN;AACA,kB;AACA,sB;AACA,+B;AACA,c;AACA,K;AACA,uC;;AAEA,+C;AACA,uC;;AAEA,uE;AACA,2B;AACA,E;;AAEA,sC;AACA,K;AACA,yH;AACA,kB;AACA,+B;AACA,c;AACA,K;AACA,wB;AACA,oB;AACA,+B;AACA,oB;AACA,I;;AAEA,K;AACA,qC;AACA,kB;AACA,+B;AACA,c;AACA,oG;AACA,K;AACA,+B;AACA,oB;AACA,4B;AACA,gF;AACA,yB;AACA,4B;AACA,G;AACA,G;;AAEA,qC;AACA,O;AACA,wC;AACA,e;AACA,yE;AACA,gB;AACA,G;AACA,kC;AACA,gD;AACA,sE;AACA,gB;AACA,G;;AAEA,6E;;AAEA,qE;AACA,gB;AACA,8B;AACA,8B;AACA,sB;AACA,6C;AACA,uC;AACA,O;AACA,uB;AACA,G;;AAEA,2D;AACA,0B;AACA,+D;AACA,K;;AAEA,a;AACA,E;;AAEA,+B;AACA,8B;AACA,sE;AACA,qB;AACA,6B;AACA,qB;AACA,8C;AACA,gC;AACA,0B;AACA,gC;AACA,O;AACA,O;AACA,4B;AACA,6B;AACA,+B;AACA,yB;AACA,G;AACA,0B;AACA,2D;AACA,2B;AACA,+D;AACA,K;AACA,6C;AACA,kD;AACA,G;AACA,8B;AACA,E;;AAEA,oE;AACA,qE;AACA,gE;AACA,wD;AACA,wD;;;;;;;;;;;;;;;;;;;ACzKA,2E;AACA,E;AACA,4E;AACA,6E;AACA,mE;AACA,E;AACA,4E;AACA,+E;AACA,0E;AACA,+E;AACA,4E;AACA,6E;AACA,E;AACA,+D;AACA,6D;AACA,gE;AACA,8C;AACA,E;AACA,mC;AACA,4D;AACA,2D;AACA,iF;AACA,gF;AACA,mC;AACA,kB;;AAEA,uD;;AAEA,sB;AACA,E;;AAEA,kE;AACA,gE;AACA,wE;AACA,yC;AACA,wB;AACA,8B;AACA,E;;AAEA,6E;AACA,6E;AACA,oE;AACA,mC;AACA,wE;AACA,6E;AACA,0D;AACA,2C;AACA,c;AACA,qB;AACA,G;AACA,e;AACA,oC;AACA,mD;AACA,kB;AACA,G;AACA,wC;AACA,sB;AACA,0D;AACA,4B;AACA,O;AACA,G;AACA,sC;AACA,E;;AAEA,sD;AACA,6E;AACA,+D;AACA,oC;AACA,2C;AACA,uC;AACA,E;;AAEA,qD;AACA,8C;AACA,yD;AACA,uE;AACA,oD;AACA,yE;AACA,gD;AACA,iE;AACA,8B;AACA,E;;AAEA,kC;AACA,kF;AACA,uE;AACA,qE;AACA,8B;AACA,oB;;AAEA,gD;AACA,4B;AACA,gD;AACA,qD;AACA,4C;AACA,8C;AACA,S;AACA,O;AACA,yF;AACA,K;AACA,oB;AACA,G;AACA,G;;;;;;;;;;;;;;;;;;;ACtGA,sB;AACA,iC;AACA,oC;AACA,0D;AACA,C;;AAEA,gD;AACA,2C;AACA,W;AACA,gF;AACA,qE;AACA,gC;AACA,2E;AACA,6E;AACA,E;AACA,qE;AACA,oC;AACA,E;AACA,oE;AACA,iE;AACA,qE;AACA,8D;AACA,8D;AACA,mE;AACA,oE;AACA,+B;AACA,0C;AACA,kB;AACA,sB;AACA,gC;AACA,4D;AACA,iC;AACA,M;AACA,6B;AACA,4B;AACA,0C;AACA,iC;AACA,iD;AACA,gB;AACA,wB;AACA,c;;AAEA,wE;AACA,gF;AACA,gB;AACA,0B;;AAEA,6D;AACA,gC;AACA,uB;AACA,U;AACA,uD;AACA,2B;AACA,+B;AACA,6C;AACA,mE;AACA,oE;AACA,kE;AACA,gE;AACA,gB;AACA,iD;AACA,gD;AACA,O;AACA,G;;AAEA,6B;AACA,oE;AACA,4E;AACA,mD;AACA,4C;AACA,yB;AACA,4D;;AAEA,sD;AACA,oD;;AAEA,2E;AACA,gF;AACA,6E;AACA,0B;AACA,4B;;AAEA,gF;AACA,iB;AACA,I;AACA,0E;AACA,+E;AACA,4E;AACA,+E;AACA,mB;AACA,I;AACA,uD;AACA,+C;AACA,4E;AACA,8B;AACA,I;AACA,gF;AACA,+D;AACA,I;AACA,oE;AACA,+E;AACA,+E;AACA,gF;AACA,6E;AACA,+C;AACA,I;AACA,a;AACA,kC;AACA,mC;AACA,6D;AACA,0D;AACA,4D;AACA,8E;AACA,uE;AACA,6E;AACA,+E;AACA,+E;AACA,0D;AACA,qC;;AAEA,2E;AACA,yE;AACA,gF;AACA,+B;AACA,oC;AACA,8E;AACA,4D;AACA,8E;AACA,gC;AACA,8D;AACA,8E;AACA,6D;AACA,6B;;AAEA,uE;AACA,qB;AACA,uE;AACA,qC;AACA,yE;AACA,gF;AACA,4E;AACA,gB;AACA,kC;;AAEA,gF;AACA,oC;AACA,8E;AACA,8E;AACA,6D;AACA,6E;AACA,gF;AACA,uE;AACA,mD;AACA,I;AACA,iE;;AAEA,+D;AACA,6C;AACA,yE;AACA,yE;AACA,W;AACA,uC;AACA,yE;AACA,wE;AACA,0D;AACA,uE;AACA,gB;AACA,4B;;AAEA,iE;AACA,qC;AACA,kD;AACA,4B;;AAEA,sE;AACA,W;AACA,a;AACA,e;AACA,uE;AACA,uD;AACA,gE;AACA,+E;AACA,0D;AACA,2E;AACA,+E;AACA,2B;;AAEA,qB;AACA,sB;AACA,4C;;AAEA,gE;AACA,4E;AACA,uD;AACA,oC;AACA,+B;AACA,yD;AACA,mC;AACA,qB;AACA,c;AACA,sB;AACA,O;AACA,O;AACA,G;;AAEA,sC;AACA,S;AACA,kC;AACA,iB;AACA,sD;AACA,a;AACA,K;;AAEA,mC;AACA,uE;AACA,wE;AACA,qC;AACA,mC;AACA,kE;AACA,a;AACA,K;;AAEA,kC;AACA,8C;AACA,oC;AACA,4B;AACA,K;AACA,mC;AACA,gE;AACA,8C;AACA,+C;AACA,c;AACA,yB;AACA,wF;AACA,yE;AACA,4D;AACA,O;AACA,K;AACA,kC;AACA,iC;AACA,8C;AACA,0B;AACA,uC;AACA,K;AACA,kC;AACA,4B;AACA,uC;AACA,O;AACA,K;AACA,qF;AACA,+B;AACA,iC;AACA,gC;AACA,kC;AACA,iC;AACA,iC;AACA,gC;AACA,Q;AACA,qE;AACA,I;;AAEA,6B;AACA,6D;AACA,qE;AACA,2C;AACA,+B;AACA,4B;AACA,wC;AACA,2E;AACA,0C;AACA,6C;AACA,oB;;AAEA,oE;AACA,qE;AACA,oE;AACA,4D;AACA,qE;AACA,kE;AACA,0B;;AAEA,gF;AACA,wD;AACA,qD;AACA,8D;AACA,4C;AACA,K;;AAEA,uE;AACA,kB;AACA,+C;AACA,4B;AACA,O;;AAEA,oE;AACA,oE;AACA,oE;AACA,oE;AACA,oB;AACA,yB;AACA,kE;AACA,Q;AACA,qC;;AAEA,wE;AACA,6C;AACA,oD;AACA,kB;AACA,mB;AACA,e;AACA,uB;AACA,0B;AACA,S;AACA,O;AACA,I;;AAEA,kC;AACA,0B;AACA,6B;AACA,6B;AACA,K;AACA,I;;AAEA,wB;AACA,iF;AACA,6E;AACA,uF;AACA,U;AACA,0C;AACA,sC;AACA,gD;AACA,G;AACA,E;;AAEA,gF;AACA,sE;AACA,+E;AACA,2E;AACA,+C;AACA,wC;AACA,kB;;AAEA,sC;AACA,mC;AACA,2B;;AAEA,oC;AACA,wC;AACA,kC;AACA,sE;AACA,4B;AACA,4B;AACA,4B;;AAEA,kC;AACA,yD;AACA,E;AACA,mC;AACA,8E;AACA,oE;AACA,4B;AACA,oB;AACA,8E;AACA,gF;AACA,c;AACA,yB;AACA,uE;;AAEA,8E;AACA,Y;AACA,8B;;AAEA,4B;;AAEA,+E;AACA,Y;AACA,mB;AACA,wE;;AAEA,iC;AACA,0C;AACA,I;AACA,6E;AACA,qC;AACA,qC;AACA,oB;AACA,kD;AACA,4E;AACA,0B;AACA,mE;;AAEA,kC;AACA,6D;;AAEA,+E;AACA,8C;AACA,oD;AACA,K;AACA,I;AACA,2E;AACA,oE;AACA,4E;AACA,2D;AACA,yC;AACA,oB;AACA,yB;AACA,kE;AACA,uC;AACA,wC;AACA,gC;AACA,I;AACA,8E;AACA,uE;AACA,4E;AACA,sE;AACA,4B;AACA,oB;AACA,6B;AACA,gC;AACA,I;AACA,2C;AACA,0B;AACA,oB;AACA,gC;AACA,G;AACA,G;;AAEA,gC;AACA,qE;AACA,gF;AACA,gF;AACA,gD;AACA,oB;;AAEA,6B;AACA,mB;;AAEA,0E;AACA,2C;AACA,mB;AACA,kE;AACA,qD;AACA,2C;AACA,4C;AACA,6E;AACA,qC;AACA,gB;AACA,e;;AAEA,+B;;AAEA,qD;AACA,iB;AACA,8C;AACA,qC;AACA,0B;AACA,S;AACA,wB;AACA,iD;AACA,K;;AAEA,gB;AACA,I;;AAEA,K;AACA,qB;AACA,wE;AACA,oC;AACA,kB;AACA,4E;AACA,+B;AACA,uE;AACA,wB;AACA,wE;AACA,sE;AACA,2E;AACA,6C;AACA,K;AACA,2E;AACA,oB;;AAEA,0D;AACA,uB;AACA,wB;AACA,gD;AACA,oC;AACA,yC;AACA,6B;AACA,wE;AACA,iD;AACA,uE;AACA,0B;AACA,iC;AACA,O;AACA,K;;AAEA,uE;AACA,uE;AACA,4B;AACA,M;AACA,wC;AACA,M;AACA,wC;AACA,yD;AACA,yD;AACA,c;AACA,M;AACA,sE;AACA,oE;AACA,sD;AACA,M;AACA,8E;AACA,4E;AACA,uB;AACA,+D;AACA,iD;AACA,yC;AACA,O;;AAEA,W;AACA,mB;AACA,uB;AACA,8C;;AAEA,8B;AACA,+E;AACA,+E;AACA,6E;AACA,yE;AACA,8C;AACA,4B;AACA,qD;AACA,O;;AAEA,wE;AACA,+C;AACA,8B;AACA,kE;AACA,2B;AACA,mD;AACA,O;;AAEA,6B;AACA,iD;AACA,O;AACA,Y;AACA,oE;AACA,uB;AACA,iC;AACA,e;AACA,mB;AACA,oC;AACA,wB;AACA,qB;AACA,0C;AACA,yC;AACA,iD;AACA,yC;AACA,uC;AACA,yB;AACA,4B;AACA,yD;AACA,iD;AACA,U;AACA,0B;AACA,wD;AACA,wB;;AAEA,iC;AACA,+B;AACA,W;AACA,S;AACA,Q;AACA,mE;AACA,K;;AAEA,0C;AACA,kB;AACA,yB;AACA,4C;AACA,iB;;AAEA,uC;AACA,Q;AACA,0B;AACA,8C;AACA,4C;AACA,uB;AACA,6C;AACA,kC;AACA,4B;AACA,Q;AACA,wB;AACA,M;;AAEA,yB;AACA,8E;AACA,8E;AACA,6E;AACA,uE;AACA,wE;AACA,uB;AACA,yC;AACA,2C;AACA,kD;;AAEA,wC;AACA,+C;AACA,+C;AACA,0B;AACA,W;AACA,S;AACA,K;;AAEA,kB;AACA,I;;AAEA,a;AACA,2F;AACA,mE;AACA,qD;AACA,oB;AACA,yB;AACA,sB;AACA,e;AACA,sB;AACA,e;AACA,4B;AACA,qB;AACA,sB;AACA,Q;AACA,6B;AACA,mB;AACA,wB;AACA,Y;AACA,mE;AACA,O;AACA,O;;AAEA,6D;AACA,a;AACA,kB;AACA,I;;AAEA,+B;AACA,oB;AACA,2C;AACA,qC;AACA,4E;AACA,wC;AACA,O;AACA,I;;AAEA,K;AACA,qB;AACA,+D;AACA,oB;AACA,kD;AACA,gE;AACA,uN;AACA,K;AACA,0D;AACA,oE;AACA,4C;AACA,wD;AACA,mE;AACA,gC;AACA,4C;AACA,I;;AAEA,qC;AACA,4E;AACA,4E;AACA,2D;AACA,+E;AACA,4E;AACA,2E;AACA,2E;AACA,2E;AACA,6E;AACA,6E;AACA,2E;AACA,yE;AACA,4E;AACA,8E;AACA,6E;AACA,6E;AACA,2E;AACA,6D;AACA,wC;;AAEA,K;AACA,qB;AACA,4D;AACA,oB;AACA,kD;AACA,+C;AACA,8B;AACA,wM;AACA,8Q;AACA,6G;AACA,K;AACA,mD;AACA,oB;;AAEA,2E;AACA,gC;AACA,qD;AACA,yB;AACA,mB;AACA,K;AACA,4B;;AAEA,mB;AACA,oE;AACA,oC;AACA,yD;AACA,wC;AACA,iB;AACA,sD;AACA,Q;AACA,K;;AAEA,8E;AACA,uC;AACA,6B;;AAEA,mE;AACA,iC;AACA,a;AACA,0B;AACA,6B;AACA,2C;AACA,kB;AACA,Q;AACA,S;;AAEA,iD;AACA,kE;;AAEA,yE;AACA,8E;AACA,yE;AACA,8D;AACA,+E;AACA,6E;AACA,6E;AACA,wE;AACA,4E;AACA,iD;AACA,0B;AACA,2C;AACA,gC;AACA,kD;AACA,O;AACA,wB;AACA,M;;AAEA,sE;AACA,4E;AACA,0E;AACA,8E;AACA,iD;AACA,M;AACA,yE;AACA,yE;AACA,4E;AACA,wE;AACA,a;;AAEA,0C;AACA,e;AACA,wC;AACA,+B;AACA,Q;;AAEA,6C;AACA,2B;AACA,8B;AACA,6B;AACA,iE;AACA,S;;AAEA,+B;AACA,8B;;AAEA,W;AACA,4E;AACA,8C;AACA,wF;AACA,gC;AACA,4E;AACA,0C;AACA,wD;AACA,4E;AACA,+D;AACA,e;AACA,kB;AACA,6D;AACA,W;AACA,W;AACA,O;AACA,iB;AACA,0B;AACA,O;;AAEA,+B;AACA,oD;AACA,K;;AAEA,oE;AACA,+D;AACA,wC;AACA,8B;AACA,qB;AACA,6C;AACA,yB;AACA,O;AACA,oB;AACA,wB;AACA,6B;AACA,K;;AAEA,gE;AACA,iE;AACA,sD;AACA,M;AACA,oE;AACA,iB;AACA,2C;AACA,2E;AACA,4D;AACA,K;;;AAGA,sE;AACA,iD;;AAEA,+D;AACA,oB;AACA,4B;AACA,sE;AACA,sE;AACA,uE;AACA,0B;AACA,mC;AACA,uE;AACA,4C;AACA,U;AACA,c;AACA,iE;AACA,qC;AACA,gC;AACA,qC;AACA,O;AACA,K;AACA,sE;AACA,yE;AACA,+D;AACA,mB;AACA,oB;AACA,mB;AACA,mB;AACA,oB;AACA,M;;AAEA,6C;AACA,8B;AACA,sC;AACA,K;;AAEA,2C;AACA,2B;AACA,yB;AACA,uB;AACA,iD;AACA,2B;AACA,sB;AACA,O;;AAEA,uB;AACA,gE;AACA,yC;AACA,gD;AACA,Y;AACA,8E;AACA,wD;AACA,qD;AACA,qD;AACA,uE;AACA,wE;AACA,K;;AAEA,0D;AACA,mD;AACA,kC;;AAEA,yD;AACA,oC;AACA,iB;AACA,2B;AACA,K;AACA,iE;AACA,I;;AAEA,gF;AACA,uE;AACA,e;AACA,+B;AACA,oB;AACA,uC;AACA,wB;AACA,O;AACA,I;AACA,8E;AACA,gF;AACA,mE;AACA,mD;AACA,oB;AACA,+C;AACA,0E;;AAEA,yB;AACA,mD;AACA,4C;AACA,gD;AACA,qB;AACA,e;AACA,4C;AACA,2D;AACA,sD;AACA,yE;AACA,6E;AACA,uC;AACA,+E;AACA,2B;AACA,oD;AACA,gB;AACA,mE;AACA,mC;AACA,wC;AACA,wC;AACA,oD;AACA,S;AACA,S;AACA,O;AACA,kC;AACA,2D;AACA,K;AACA,I;;AAEA,kE;AACA,wD;AACA,gC;AACA,oB;AACA,6D;AACA,qE;AACA,sD;AACA,Q;AACA,mE;AACA,mE;AACA,oD;AACA,4D;AACA,uC;AACA,O;AACA,O;AACA,I;;AAEA,8D;AACA,yB;AACA,oB;AACA,yC;AACA,I;;AAEA,6D;AACA,mE;AACA,mC;AACA,qC;AACA,oB;AACA,wC;AACA,I;;AAEA,K;AACA,wE;AACA,kB;AACA,qB;AACA,K;AACA,2C;AACA,oB;AACA,8D;AACA,I;;AAEA,K;AACA,mG;;AAEA,8D;AACA,kB;AACA,qB;AACA,K;AACA,8C;AACA,oB;AACA,iE;AACA,I;;AAEA,K;AACA,oD;AACA,kB;AACA,qB;AACA,K;AACA,+C;AACA,oB;AACA,kE;AACA,I;;AAEA,sB;AACA,oB;AACA,uD;AACA,I;;AAEA,K;AACA,0B;AACA,K;AACA,uB;AACA,oB;AACA,yB;AACA,gC;AACA,wB;AACA,I;;AAEA,gC;AACA,oB;AACA,+E;AACA,gC;AACA,a;AACA,0B;AACA,yB;AACA,iC;AACA,I;;AAEA,gF;AACA,2E;AACA,6B;AACA,sC;AACA,oB;AACA,kD;AACA,0D;AACA,I;;AAEA,6E;AACA,uC;AACA,0C;AACA,oB;AACA,+D;AACA,I;;AAEA,uC;AACA,oB;;AAEA,oE;AACA,uC;AACA,mD;AACA,iD;AACA,gC;AACA,+B;AACA,gE;AACA,U;AACA,+B;AACA,oC;AACA,S;AACA,S;AACA,8B;AACA,K;;AAEA,8D;AACA,4B;AACA,+B;;AAEA,4C;AACA,+E;AACA,wC;AACA,K;;AAEA,uC;AACA,+E;AACA,4E;AACA,gF;AACA,+E;AACA,0E;AACA,a;AACA,K;;AAEA,oE;;AAEA,+E;AACA,mC;AACA,uC;;AAEA,4B;AACA,8E;AACA,gB;AACA,wC;AACA,iC;AACA,K;;AAEA,mC;AACA,oC;;AAEA,8E;AACA,gD;AACA,gF;AACA,qE;AACA,gC;AACA,oD;AACA,oB;AACA,0C;AACA,O;;AAEA,2E;AACA,uE;AACA,uC;AACA,M;AACA,wE;AACA,gF;AACA,4D;AACA,yC;AACA,4B;AACA,uD;AACA,kC;AACA,gF;AACA,0E;AACA,4E;AACA,0E;AACA,gF;AACA,yC;AACA,4E;AACA,8E;AACA,8D;AACA,Y;AACA,6E;AACA,uE;AACA,+E;AACA,2E;AACA,+B;AACA,mE;AACA,S;AACA,S;AACA,K;;AAEA,+C;;AAEA,+E;AACA,sC;AACA,wC;AACA,8B;AACA,2C;AACA,iC;AACA,wB;AACA,W;AACA,kC;AACA,O;AACA,sC;AACA,K;AACA,I;;;AAGA,mD;AACA,oB;AACA,6D;AACA,8C;AACA,I;;;AAGA,kC;AACA,oB;;AAEA,2C;AACA,qB;;AAEA,uC;AACA,sD;;AAEA,8B;AACA,8C;;AAEA,+C;AACA,6C;AACA,S;AACA,qD;AACA,yD;AACA,S;;AAEA,uC;AACA,e;;AAEA,oD;AACA,8E;AACA,oC;AACA,4E;AACA,0D;AACA,S;AACA,iD;AACA,Y;AACA,gD;AACA,K;;AAEA,mD;AACA,oD;AACA,oD;AACA,gF;AACA,yC;AACA,S;AACA,gC;;AAEA,4D;AACA,4C;AACA,oB;AACA,2D;AACA,wC;AACA,a;AACA,gB;AACA,gE;AACA,8B;AACA,oE;AACA,qE;AACA,gD;AACA,+D;AACA,0D;AACA,8E;AACA,qD;AACA,S;AACA,S;;AAEA,gC;AACA,4D;AACA,K;;AAEA,oC;AACA,I;;AAEA,2E;AACA,yE;AACA,+B;AACA,yC;AACA,oB;AACA,+C;AACA,oC;AACA,oC;AACA,U;AACA,O;AACA,I;;AAEA,oD;AACA,oB;AACA,sC;AACA,+B;AACA,K;AACA,kC;AACA,I;;AAEA,4C;AACA,oB;AACA,kD;AACA,kB;AACA,oE;AACA,mD;AACA,I;;AAEA,2C;AACA,oB;AACA,8C;AACA,2D;AACA,oB;AACA,0C;AACA,2C;AACA,sE;AACA,4C;AACA,kC;AACA,Y;AACA,qD;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,uC;AACA,wD;AACA,oB;AACA,2C;AACA,6E;AACA,oE;AACA,Y;AACA,qD;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,uC;AACA,wD;AACA,oB;AACA,0C;AACA,2C;AACA,4E;AACA,qC;AACA,Y;AACA,iD;AACA,uB;AACA,mC;AACA,kB;AACA,S;AACA,K;AACA,I;;AAEA,6C;AACA,oB;AACA,sC;AACA,6C;AACA,yE;AACA,2E;AACA,uB;AACA,2E;AACA,gD;AACA,4D;AACA,iE;AACA,kD;AACA,kD;AACA,6E;AACA,8E;AACA,+E;AACA,wC;;AAEA,yE;AACA,+E;AACA,uE;AACA,yD;AACA,2B;AACA,yD;AACA,uC;AACA,a;AACA,sC;AACA,yD;AACA,gB;AACA,a;;AAEA,2E;AACA,6E;AACA,gE;AACA,uE;AACA,S;AACA,S;AACA,oD;;AAEA,gF;AACA,iD;AACA,2D;AACA,2B;AACA,sE;AACA,2C;AACA,8D;AACA,O;AACA,I;;AAEA,2C;AACA,oB;AACA,2E;AACA,wE;AACA,4D;AACA,uC;AACA,wD;AACA,mD;AACA,sC;AACA,uB;AACA,iB;AACA,0D;AACA,4B;AACA,iB;AACA,6D;AACA,+B;AACA,sC;AACA,S;AACA,O;AACA,I;;AAEA,qE;AACA,gF;AACA,2C;AACA,iD;AACA,oB;AACA,wC;AACA,yC;AACA,M;AACA,oC;AACA,wC;AACA,gC;AACA,0C;AACA,6E;AACA,6B;AACA,2B;AACA,O;AACA,M;AACA,6D;AACA,mD;AACA,2D;AACA,gE;AACA,yD;AACA,kD;AACA,a;AACA,qD;AACA,oC;AACA,0D;AACA,S;AACA,S;AACA,O;AACA,wC;AACA,gF;AACA,qC;AACA,yB;AACA,K;AACA,I;;AAEA,mC;AACA,oB;;AAEA,2E;AACA,0B;AACA,6B;;AAEA,yD;AACA,kC;;AAEA,2D;AACA,4C;AACA,a;;AAEA,6C;AACA,kE;AACA,gE;;AAEA,yC;;AAEA,gD;AACA,wD;AACA,uE;AACA,K;;AAEA,6C;AACA,qC;AACA,6C;AACA,K;;AAEA,uB;AACA,4C;AACA,K;AACA,I;;AAEA,+B;AACA,2E;AACA,4E;AACA,sE;AACA,yE;AACA,e;AACA,I;;AAEA,oC;AACA,mE;;AAEA,oB;;AAEA,mC;AACA,uD;AACA,mD;AACA,yE;AACA,a;AACA,K;AACA,sE;AACA,U;AACA,yD;AACA,gC;AACA,gC;AACA,c;AACA,K;;AAEA,a;AACA,gF;AACA,a;AACA,K;;AAEA,+E;AACA,gF;AACA,kC;AACA,oC;;AAEA,8B;AACA,uC;AACA,0C;AACA,4B;AACA,Y;AACA,kE;AACA,c;AACA,6C;AACA,K;AACA,I;;AAEA,+E;AACA,8E;AACA,mE;AACA,2C;AACA,oB;AACA,yC;AACA,a;;AAEA,2E;AACA,0E;AACA,kD;AACA,qD;AACA,6D;AACA,0C;AACA,uE;AACA,oD;;AAEA,6D;AACA,oD;AACA,uC;AACA,K;;AAEA,oC;AACA,yB;AACA,I;;AAEA,6D;AACA,8B;AACA,uC;AACA,oB;AACA,iD;AACA,a;AACA,mE;AACA,sB;AACA,O;AACA,I;;AAEA,mC;AACA,8D;AACA,6B;AACA,mD;AACA,I;;AAEA,oE;AACA,oB;AACA,mE;AACA,uC;;AAEA,uB;;AAEA,8C;AACA,a;;AAEA,yE;AACA,4E;AACA,+D;AACA,mD;AACA,iE;AACA,qC;AACA,a;AACA,K;;AAEA,gF;AACA,6E;AACA,yC;AACA,sD;AACA,8C;AACA,kE;AACA,8D;;AAEA,0E;AACA,uD;AACA,0B;AACA,S;;AAEA,yC;AACA,K;;AAEA,kD;AACA,yD;AACA,gD;AACA,O;AACA,I;;AAEA,qE;AACA,+B;AACA,oB;AACA,2C;AACA,I;;AAEA,2E;AACA,kE;AACA,8B;AACA,oB;AACA,uD;AACA,2B;AACA,gC;AACA,K;AACA,G;AACA,G;;AAEA,qC;;AAEA,yC;AACA,Y;AACA,8B;AACA,qC;AACA,W;AACA,qD;;AAEA,G;AACA,oI;AACA,kB;AACA,6D;AACA,G;AACA,uC;AACA,yC;AACA,+C;AACA,a;AACA,E;;AAEA,kE;AACA,iC;AACA,E;AACA,oB;AACA,0C;AACA,gD;AACA,sD;AACA,uB;AACA,O;AACA,K;AACA,E;;;;;;;;;;;;;;;;;;;AClmDA,sE;AACA,4C;AACA,E;AACA,gF;AACA,8E;AACA,mD;AACA,E;AACA,6E;AACA,gF;AACA,6E;AACA,2E;AACA,mB;AACA,qB;AACA,+C;AACA,0D;AACA,6C;AACA,G;;AAEA,6B;;AAEA,4C;AACA,uD;AACA,I;;AAEA,0D;AACA,kC;AACA,iE;AACA,0B;AACA,qE;AACA,Y;AACA,Q;AACA,wC;AACA,uB;AACA,4C;AACA,I;;AAEA,mE;AACA,qD;AACA,gD;AACA,uB;AACA,uC;AACA,S;AACA,C;;AAEA,iE;AACA,gE;AACA,wB;AACA,sC","file":"/packages/ddp.js","sourcesContent":["/**\n * @namespace DDP\n * @summary The namespace for DDP-related methods.\n */\nDDP = {};\nLivedataTest = {};\n","// @param endpoint {String} URL to Meteor app\n// \"http://subdomain.meteor.com/\" or \"/\" or\n// \"ddp+sockjs://foo-**.meteor.com/sockjs\"\n//\n// We do some rewriting of the URL to eventually make it \"ws://\" or \"wss://\",\n// whatever was passed in. At the very least, what Meteor.absoluteUrl() returns\n// us should work.\n//\n// We don't do any heartbeating. (The logic that did this in sockjs was removed,\n// because it used a built-in sockjs mechanism. We could do it with WebSocket\n// ping frames or with DDP-level messages.)\nLivedataTest.ClientStream = function (endpoint, options) {\n var self = this;\n options = options || {};\n\n self.options = _.extend({\n retry: true\n }, options);\n\n self.client = null; // created in _launchConnection\n self.endpoint = endpoint;\n\n self.headers = self.options.headers || {};\n\n self._initCommon(self.options);\n\n //// Kickoff!\n self._launchConnection();\n};\n\n_.extend(LivedataTest.ClientStream.prototype, {\n\n // data is a utf8 string. Data sent while not connected is dropped on\n // the floor, and it is up the user of this API to retransmit lost\n // messages on 'reset'\n send: function (data) {\n var self = this;\n if (self.currentStatus.connected) {\n self.client.send(data);\n }\n },\n\n // Changes where this connection points\n _changeUrl: function (url) {\n var self = this;\n self.endpoint = url;\n },\n\n _onConnect: function (client) {\n var self = this;\n\n if (client !== self.client) {\n // This connection is not from the last call to _launchConnection.\n // But _launchConnection calls _cleanup which closes previous connections.\n // It's our belief that this stifles future 'open' events, but maybe\n // we are wrong?\n throw new Error(\"Got open from inactive client \" + !!self.client);\n }\n\n if (self._forcedToDisconnect) {\n // We were asked to disconnect between trying to open the connection and\n // actually opening it. Let's just pretend this never happened.\n self.client.close();\n self.client = null;\n return;\n }\n\n if (self.currentStatus.connected) {\n // We already have a connection. It must have been the case that we\n // started two parallel connection attempts (because we wanted to\n // 'reconnect now' on a hanging connection and we had no way to cancel the\n // connection attempt.) But this shouldn't happen (similarly to the client\n // !== self.client check above).\n throw new Error(\"Two parallel connections?\");\n }\n\n self._clearConnectionTimer();\n\n // update status\n self.currentStatus.status = \"connected\";\n self.currentStatus.connected = true;\n self.currentStatus.retryCount = 0;\n self.statusChanged();\n\n // fire resets. This must come after status change so that clients\n // can call send from within a reset callback.\n _.each(self.eventCallbacks.reset, function (callback) { callback(); });\n },\n\n _cleanup: function (maybeError) {\n var self = this;\n\n self._clearConnectionTimer();\n if (self.client) {\n var client = self.client;\n self.client = null;\n client.close();\n\n _.each(self.eventCallbacks.disconnect, function (callback) {\n callback(maybeError);\n });\n }\n },\n\n _clearConnectionTimer: function () {\n var self = this;\n\n if (self.connectionTimer) {\n clearTimeout(self.connectionTimer);\n self.connectionTimer = null;\n }\n },\n\n _getProxyUrl: function (targetUrl) {\n var self = this;\n // Similar to code in tools/http-helpers.js.\n var proxy = process.env.HTTP_PROXY || process.env.http_proxy || null;\n // if we're going to a secure url, try the https_proxy env variable first.\n if (targetUrl.match(/^wss:/)) {\n proxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy;\n }\n return proxy;\n },\n\n _launchConnection: function () {\n var self = this;\n self._cleanup(); // cleanup the old socket, if there was one.\n\n // Since server-to-server DDP is still an experimental feature, we only\n // require the module if we actually create a server-to-server\n // connection.\n var FayeWebSocket = Npm.require('faye-websocket');\n\n var targetUrl = toWebsocketUrl(self.endpoint);\n var fayeOptions = { headers: self.headers };\n var proxyUrl = self._getProxyUrl(targetUrl);\n if (proxyUrl) {\n fayeOptions.proxy = { origin: proxyUrl };\n };\n\n // We would like to specify 'ddp' as the subprotocol here. The npm module we\n // used to use as a client would fail the handshake if we ask for a\n // subprotocol and the server doesn't send one back (and sockjs doesn't).\n // Faye doesn't have that behavior; it's unclear from reading RFC 6455 if\n // Faye is erroneous or not. So for now, we don't specify protocols.\n var subprotocols = [];\n\n var client = self.client = new FayeWebSocket.Client(\n targetUrl, subprotocols, fayeOptions);\n\n self._clearConnectionTimer();\n self.connectionTimer = Meteor.setTimeout(\n function () {\n self._lostConnection(\n new DDP.ConnectionError(\"DDP connection timed out\"));\n },\n self.CONNECT_TIMEOUT);\n\n self.client.on('open', Meteor.bindEnvironment(function () {\n return self._onConnect(client);\n }, \"stream connect callback\"));\n\n var clientOnIfCurrent = function (event, description, f) {\n self.client.on(event, Meteor.bindEnvironment(function () {\n // Ignore events from any connection we've already cleaned up.\n if (client !== self.client)\n return;\n f.apply(this, arguments);\n }, description));\n };\n\n clientOnIfCurrent('error', 'stream error callback', function (error) {\n if (!self.options._dontPrintErrors)\n Meteor._debug(\"stream error\", error.message);\n\n // Faye's 'error' object is not a JS error (and among other things,\n // doesn't stringify well). Convert it to one.\n self._lostConnection(new DDP.ConnectionError(error.message));\n });\n\n\n clientOnIfCurrent('close', 'stream close callback', function () {\n self._lostConnection();\n });\n\n\n clientOnIfCurrent('message', 'stream message callback', function (message) {\n // Ignore binary frames, where message.data is a Buffer\n if (typeof message.data !== \"string\")\n return;\n\n _.each(self.eventCallbacks.message, function (callback) {\n callback(message.data);\n });\n });\n }\n});\n","// XXX from Underscore.String (http://epeli.github.com/underscore.string/)\nvar startsWith = function(str, starts) {\n return str.length >= starts.length &&\n str.substring(0, starts.length) === starts;\n};\nvar endsWith = function(str, ends) {\n return str.length >= ends.length &&\n str.substring(str.length - ends.length) === ends;\n};\n\n// @param url {String} URL to Meteor app, eg:\n// \"/\" or \"madewith.meteor.com\" or \"https://foo.meteor.com\"\n// or \"ddp+sockjs://ddp--****-foo.meteor.com/sockjs\"\n// @returns {String} URL to the endpoint with the specific scheme and subPath, e.g.\n// for scheme \"http\" and subPath \"sockjs\"\n// \"http://subdomain.meteor.com/sockjs\" or \"/sockjs\"\n// or \"https://ddp--1234-foo.meteor.com/sockjs\"\nvar translateUrl = function(url, newSchemeBase, subPath) {\n if (! newSchemeBase) {\n newSchemeBase = \"http\";\n }\n\n var ddpUrlMatch = url.match(/^ddp(i?)\\+sockjs:\\/\\//);\n var httpUrlMatch = url.match(/^http(s?):\\/\\//);\n var newScheme;\n if (ddpUrlMatch) {\n // Remove scheme and split off the host.\n var urlAfterDDP = url.substr(ddpUrlMatch[0].length);\n newScheme = ddpUrlMatch[1] === \"i\" ? newSchemeBase : newSchemeBase + \"s\";\n var slashPos = urlAfterDDP.indexOf('/');\n var host =\n slashPos === -1 ? urlAfterDDP : urlAfterDDP.substr(0, slashPos);\n var rest = slashPos === -1 ? '' : urlAfterDDP.substr(slashPos);\n\n // In the host (ONLY!), change '*' characters into random digits. This\n // allows different stream connections to connect to different hostnames\n // and avoid browser per-hostname connection limits.\n host = host.replace(/\\*/g, function () {\n return Math.floor(Random.fraction()*10);\n });\n\n return newScheme + '://' + host + rest;\n } else if (httpUrlMatch) {\n newScheme = !httpUrlMatch[1] ? newSchemeBase : newSchemeBase + \"s\";\n var urlAfterHttp = url.substr(httpUrlMatch[0].length);\n url = newScheme + \"://\" + urlAfterHttp;\n }\n\n // Prefix FQDNs but not relative URLs\n if (url.indexOf(\"://\") === -1 && !startsWith(url, \"/\")) {\n url = newSchemeBase + \"://\" + url;\n }\n\n // XXX This is not what we should be doing: if I have a site\n // deployed at \"/foo\", then DDP.connect(\"/\") should actually connect\n // to \"/\", not to \"/foo\". \"/\" is an absolute path. (Contrast: if\n // deployed at \"/foo\", it would be reasonable for DDP.connect(\"bar\")\n // to connect to \"/foo/bar\").\n //\n // We should make this properly honor absolute paths rather than\n // forcing the path to be relative to the site root. Simultaneously,\n // we should set DDP_DEFAULT_CONNECTION_URL to include the site\n // root. See also client_convenience.js #RationalizingRelativeDDPURLs\n url = Meteor._relativeToSiteRootUrl(url);\n\n if (endsWith(url, \"/\"))\n return url + subPath;\n else\n return url + \"/\" + subPath;\n};\n\ntoSockjsUrl = function (url) {\n return translateUrl(url, \"http\", \"sockjs\");\n};\n\ntoWebsocketUrl = function (url) {\n var ret = translateUrl(url, \"ws\", \"websocket\");\n return ret;\n};\n\nLivedataTest.toSockjsUrl = toSockjsUrl;\n\n\n_.extend(LivedataTest.ClientStream.prototype, {\n\n // Register for callbacks.\n on: function (name, callback) {\n var self = this;\n\n if (name !== 'message' && name !== 'reset' && name !== 'disconnect')\n throw new Error(\"unknown event type: \" + name);\n\n if (!self.eventCallbacks[name])\n self.eventCallbacks[name] = [];\n self.eventCallbacks[name].push(callback);\n },\n\n\n _initCommon: function (options) {\n var self = this;\n options = options || {};\n\n //// Constants\n\n // how long to wait until we declare the connection attempt\n // failed.\n self.CONNECT_TIMEOUT = options.connectTimeoutMs || 10000;\n\n self.eventCallbacks = {}; // name -> [callback]\n\n self._forcedToDisconnect = false;\n\n //// Reactive status\n self.currentStatus = {\n status: \"connecting\",\n connected: false,\n retryCount: 0\n };\n\n\n self.statusListeners = typeof Tracker !== 'undefined' && new Tracker.Dependency;\n self.statusChanged = function () {\n if (self.statusListeners)\n self.statusListeners.changed();\n };\n\n //// Retry logic\n self._retry = new Retry;\n self.connectionTimer = null;\n\n },\n\n // Trigger a reconnect.\n reconnect: function (options) {\n var self = this;\n options = options || {};\n\n if (options.url) {\n self._changeUrl(options.url);\n }\n\n if (options._sockjsOptions) {\n self.options._sockjsOptions = options._sockjsOptions;\n }\n\n if (self.currentStatus.connected) {\n if (options._force || options.url) {\n // force reconnect.\n self._lostConnection(new DDP.ForcedReconnectError);\n } // else, noop.\n return;\n }\n\n // if we're mid-connection, stop it.\n if (self.currentStatus.status === \"connecting\") {\n // Pretend it's a clean close.\n self._lostConnection();\n }\n\n self._retry.clear();\n self.currentStatus.retryCount -= 1; // don't count manual retries\n self._retryNow();\n },\n\n disconnect: function (options) {\n var self = this;\n options = options || {};\n\n // Failed is permanent. If we're failed, don't let people go back\n // online by calling 'disconnect' then 'reconnect'.\n if (self._forcedToDisconnect)\n return;\n\n // If _permanent is set, permanently disconnect a stream. Once a stream\n // is forced to disconnect, it can never reconnect. This is for\n // error cases such as ddp version mismatch, where trying again\n // won't fix the problem.\n if (options._permanent) {\n self._forcedToDisconnect = true;\n }\n\n self._cleanup();\n self._retry.clear();\n\n self.currentStatus = {\n status: (options._permanent ? \"failed\" : \"offline\"),\n connected: false,\n retryCount: 0\n };\n\n if (options._permanent && options._error)\n self.currentStatus.reason = options._error;\n\n self.statusChanged();\n },\n\n // maybeError is set unless it's a clean protocol-level close.\n _lostConnection: function (maybeError) {\n var self = this;\n\n self._cleanup(maybeError);\n self._retryLater(maybeError); // sets status. no need to do it here.\n },\n\n // fired when we detect that we've gone online. try to reconnect\n // immediately.\n _online: function () {\n // if we've requested to be offline by disconnecting, don't reconnect.\n if (this.currentStatus.status != \"offline\")\n this.reconnect();\n },\n\n _retryLater: function (maybeError) {\n var self = this;\n\n var timeout = 0;\n if (self.options.retry ||\n (maybeError && maybeError.errorType === \"DDP.ForcedReconnectError\")) {\n timeout = self._retry.retryLater(\n self.currentStatus.retryCount,\n _.bind(self._retryNow, self)\n );\n self.currentStatus.status = \"waiting\";\n self.currentStatus.retryTime = (new Date()).getTime() + timeout;\n } else {\n self.currentStatus.status = \"failed\";\n delete self.currentStatus.retryTime;\n }\n\n self.currentStatus.connected = false;\n self.statusChanged();\n },\n\n _retryNow: function () {\n var self = this;\n\n if (self._forcedToDisconnect)\n return;\n\n self.currentStatus.retryCount += 1;\n self.currentStatus.status = \"connecting\";\n self.currentStatus.connected = false;\n delete self.currentStatus.retryTime;\n self.statusChanged();\n\n self._launchConnection();\n },\n\n\n // Get current status. Reactive.\n status: function () {\n var self = this;\n if (self.statusListeners)\n self.statusListeners.depend();\n return self.currentStatus;\n }\n});\n\nDDP.ConnectionError = Meteor.makeErrorType(\n \"DDP.ConnectionError\", function (message) {\n var self = this;\n self.message = message;\n});\n\nDDP.ForcedReconnectError = Meteor.makeErrorType(\n \"DDP.ForcedReconnectError\", function () {});\n","var url = Npm.require('url');\n\nvar pathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || \"\";\n\nStreamServer = function () {\n var self = this;\n self.registration_callbacks = [];\n self.open_sockets = [];\n\n // Because we are installing directly onto WebApp.httpServer instead of using\n // WebApp.app, we have to process the path prefix ourselves.\n self.prefix = pathPrefix + '/sockjs';\n // routepolicy is only a weak dependency, because we don't need it if we're\n // just doing server-to-server DDP as a client.\n if (Package.routepolicy) {\n Package.routepolicy.RoutePolicy.declare(self.prefix + '/', 'network');\n }\n\n // set up sockjs\n var sockjs = Npm.require('sockjs');\n var serverOptions = {\n prefix: self.prefix,\n log: function() {},\n // this is the default, but we code it explicitly because we depend\n // on it in stream_client:HEARTBEAT_TIMEOUT\n heartbeat_delay: 45000,\n // The default disconnect_delay is 5 seconds, but if the server ends up CPU\n // bound for that much time, SockJS might not notice that the user has\n // reconnected because the timer (of disconnect_delay ms) can fire before\n // SockJS processes the new connection. Eventually we'll fix this by not\n // combining CPU-heavy processing with SockJS termination (eg a proxy which\n // converts to Unix sockets) but for now, raise the delay.\n disconnect_delay: 60 * 1000,\n // Set the USE_JSESSIONID environment variable to enable setting the\n // JSESSIONID cookie. This is useful for setting up proxies with\n // session affinity.\n jsessionid: !!process.env.USE_JSESSIONID\n };\n\n // If you know your server environment (eg, proxies) will prevent websockets\n // from ever working, set $DISABLE_WEBSOCKETS and SockJS clients (ie,\n // browsers) will not waste time attempting to use them.\n // (Your server will still have a /websocket endpoint.)\n if (process.env.DISABLE_WEBSOCKETS)\n serverOptions.websocket = false;\n\n self.server = sockjs.createServer(serverOptions);\n if (!Package.webapp) {\n throw new Error(\"Cannot create a DDP server without the webapp package\");\n }\n // Install the sockjs handlers, but we want to keep around our own particular\n // request handler that adjusts idle timeouts while we have an outstanding\n // request. This compensates for the fact that sockjs removes all listeners\n // for \"request\" to add its own.\n Package.webapp.WebApp.httpServer.removeListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);\n self.server.installHandlers(Package.webapp.WebApp.httpServer);\n Package.webapp.WebApp.httpServer.addListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);\n\n // Support the /websocket endpoint\n self._redirectWebsocketEndpoint();\n\n self.server.on('connection', function (socket) {\n socket.send = function (data) {\n socket.write(data);\n };\n socket.on('close', function () {\n self.open_sockets = _.without(self.open_sockets, socket);\n });\n self.open_sockets.push(socket);\n\n // XXX COMPAT WITH 0.6.6. Send the old style welcome message, which\n // will force old clients to reload. Remove this once we're not\n // concerned about people upgrading from a pre-0.7.0 release. Also,\n // remove the clause in the client that ignores the welcome message\n // (livedata_connection.js)\n socket.send(JSON.stringify({server_id: \"0\"}));\n\n // call all our callbacks when we get a new socket. they will do the\n // work of setting up handlers and such for specific messages.\n _.each(self.registration_callbacks, function (callback) {\n callback(socket);\n });\n });\n\n};\n\n_.extend(StreamServer.prototype, {\n // call my callback when a new socket connects.\n // also call it for all current connections.\n register: function (callback) {\n var self = this;\n self.registration_callbacks.push(callback);\n _.each(self.all_sockets(), function (socket) {\n callback(socket);\n });\n },\n\n // get a list of all sockets\n all_sockets: function () {\n var self = this;\n return _.values(self.open_sockets);\n },\n\n // Redirect /websocket to /sockjs/websocket in order to not expose\n // sockjs to clients that want to use raw websockets\n _redirectWebsocketEndpoint: function() {\n var self = this;\n // Unfortunately we can't use a connect middleware here since\n // sockjs installs itself prior to all existing listeners\n // (meaning prior to any connect middlewares) so we need to take\n // an approach similar to overshadowListeners in\n // https://github.com/sockjs/sockjs-node/blob/cf820c55af6a9953e16558555a31decea554f70e/src/utils.coffee\n _.each(['request', 'upgrade'], function(event) {\n var httpServer = Package.webapp.WebApp.httpServer;\n var oldHttpServerListeners = httpServer.listeners(event).slice(0);\n httpServer.removeAllListeners(event);\n\n // request and upgrade have different arguments passed but\n // we only care about the first one which is always request\n var newListener = function(request /*, moreArguments */) {\n // Store arguments for use within the closure below\n var args = arguments;\n\n // Rewrite /websocket and /websocket/ urls to /sockjs/websocket while\n // preserving query string.\n var parsedUrl = url.parse(request.url);\n if (parsedUrl.pathname === pathPrefix + '/websocket' ||\n parsedUrl.pathname === pathPrefix + '/websocket/') {\n parsedUrl.pathname = self.prefix + '/websocket';\n request.url = url.format(parsedUrl);\n }\n _.each(oldHttpServerListeners, function(oldListener) {\n oldListener.apply(httpServer, args);\n });\n };\n httpServer.addListener(event, newListener);\n });\n }\n});\n","// Heartbeat options:\n// heartbeatInterval: interval to send pings, in milliseconds.\n// heartbeatTimeout: timeout to close the connection if a reply isn't\n// received, in milliseconds.\n// sendPing: function to call to send a ping on the connection.\n// onTimeout: function to call to close the connection.\n\nHeartbeat = function (options) {\n var self = this;\n\n self.heartbeatInterval = options.heartbeatInterval;\n self.heartbeatTimeout = options.heartbeatTimeout;\n self._sendPing = options.sendPing;\n self._onTimeout = options.onTimeout;\n\n self._heartbeatIntervalHandle = null;\n self._heartbeatTimeoutHandle = null;\n};\n\n_.extend(Heartbeat.prototype, {\n stop: function () {\n var self = this;\n self._clearHeartbeatIntervalTimer();\n self._clearHeartbeatTimeoutTimer();\n },\n\n start: function () {\n var self = this;\n self.stop();\n self._startHeartbeatIntervalTimer();\n },\n\n _startHeartbeatIntervalTimer: function () {\n var self = this;\n self._heartbeatIntervalHandle = Meteor.setTimeout(\n _.bind(self._heartbeatIntervalFired, self),\n self.heartbeatInterval\n );\n },\n\n _startHeartbeatTimeoutTimer: function () {\n var self = this;\n self._heartbeatTimeoutHandle = Meteor.setTimeout(\n _.bind(self._heartbeatTimeoutFired, self),\n self.heartbeatTimeout\n );\n },\n\n _clearHeartbeatIntervalTimer: function () {\n var self = this;\n if (self._heartbeatIntervalHandle) {\n Meteor.clearTimeout(self._heartbeatIntervalHandle);\n self._heartbeatIntervalHandle = null;\n }\n },\n\n _clearHeartbeatTimeoutTimer: function () {\n var self = this;\n if (self._heartbeatTimeoutHandle) {\n Meteor.clearTimeout(self._heartbeatTimeoutHandle);\n self._heartbeatTimeoutHandle = null;\n }\n },\n\n // The heartbeat interval timer is fired when we should send a ping.\n _heartbeatIntervalFired: function () {\n var self = this;\n self._heartbeatIntervalHandle = null;\n self._sendPing();\n // Wait for a pong.\n self._startHeartbeatTimeoutTimer();\n },\n\n // The heartbeat timeout timer is fired when we sent a ping, but we\n // timed out waiting for the pong.\n _heartbeatTimeoutFired: function () {\n var self = this;\n self._heartbeatTimeoutHandle = null;\n self._onTimeout();\n },\n\n pingReceived: function () {\n var self = this;\n // We know the connection is alive if we receive a ping, so we\n // don't need to send a ping ourselves. Reset the interval timer.\n if (self._heartbeatIntervalHandle) {\n self._clearHeartbeatIntervalTimer();\n self._startHeartbeatIntervalTimer();\n }\n },\n\n pongReceived: function () {\n var self = this;\n\n // Receiving a pong means we won't timeout, so clear the timeout\n // timer and start the interval again.\n if (self._heartbeatTimeoutHandle) {\n self._clearHeartbeatTimeoutTimer();\n self._startHeartbeatIntervalTimer();\n }\n }\n});\n","DDPServer = {};\n\nvar Fiber = Npm.require('fibers');\n\n// This file contains classes:\n// * Session - The server's connection to a single DDP client\n// * Subscription - A single subscription for a single client\n// * Server - An entire server that may talk to > 1 client. A DDP endpoint.\n//\n// Session and Subscription are file scope. For now, until we freeze\n// the interface, Server is package scope (in the future it should be\n// exported.)\n\n// Represents a single document in a SessionCollectionView\nvar SessionDocumentView = function () {\n var self = this;\n self.existsIn = {}; // set of subscriptionHandle\n self.dataByKey = {}; // key-> [ {subscriptionHandle, value} by precedence]\n};\n\n_.extend(SessionDocumentView.prototype, {\n\n getFields: function () {\n var self = this;\n var ret = {};\n _.each(self.dataByKey, function (precedenceList, key) {\n ret[key] = precedenceList[0].value;\n });\n return ret;\n },\n\n clearField: function (subscriptionHandle, key, changeCollector) {\n var self = this;\n // Publish API ignores _id if present in fields\n if (key === \"_id\")\n return;\n var precedenceList = self.dataByKey[key];\n\n // It's okay to clear fields that didn't exist. No need to throw\n // an error.\n if (!precedenceList)\n return;\n\n var removedValue = undefined;\n for (var i = 0; i < precedenceList.length; i++) {\n var precedence = precedenceList[i];\n if (precedence.subscriptionHandle === subscriptionHandle) {\n // The view's value can only change if this subscription is the one that\n // used to have precedence.\n if (i === 0)\n removedValue = precedence.value;\n precedenceList.splice(i, 1);\n break;\n }\n }\n if (_.isEmpty(precedenceList)) {\n delete self.dataByKey[key];\n changeCollector[key] = undefined;\n } else if (removedValue !== undefined &&\n !EJSON.equals(removedValue, precedenceList[0].value)) {\n changeCollector[key] = precedenceList[0].value;\n }\n },\n\n changeField: function (subscriptionHandle, key, value,\n changeCollector, isAdd) {\n var self = this;\n // Publish API ignores _id if present in fields\n if (key === \"_id\")\n return;\n\n // Don't share state with the data passed in by the user.\n value = EJSON.clone(value);\n\n if (!_.has(self.dataByKey, key)) {\n self.dataByKey[key] = [{subscriptionHandle: subscriptionHandle,\n value: value}];\n changeCollector[key] = value;\n return;\n }\n var precedenceList = self.dataByKey[key];\n var elt;\n if (!isAdd) {\n elt = _.find(precedenceList, function (precedence) {\n return precedence.subscriptionHandle === subscriptionHandle;\n });\n }\n\n if (elt) {\n if (elt === precedenceList[0] && !EJSON.equals(value, elt.value)) {\n // this subscription is changing the value of this field.\n changeCollector[key] = value;\n }\n elt.value = value;\n } else {\n // this subscription is newly caring about this field\n precedenceList.push({subscriptionHandle: subscriptionHandle, value: value});\n }\n\n }\n});\n\n/**\n * Represents a client's view of a single collection\n * @param {String} collectionName Name of the collection it represents\n * @param {Object.} sessionCallbacks The callbacks for added, changed, removed\n * @class SessionCollectionView\n */\nvar SessionCollectionView = function (collectionName, sessionCallbacks) {\n var self = this;\n self.collectionName = collectionName;\n self.documents = {};\n self.callbacks = sessionCallbacks;\n};\n\nLivedataTest.SessionCollectionView = SessionCollectionView;\n\n\n_.extend(SessionCollectionView.prototype, {\n\n isEmpty: function () {\n var self = this;\n return _.isEmpty(self.documents);\n },\n\n diff: function (previous) {\n var self = this;\n LocalCollection._diffObjects(previous.documents, self.documents, {\n both: _.bind(self.diffDocument, self),\n\n rightOnly: function (id, nowDV) {\n self.callbacks.added(self.collectionName, id, nowDV.getFields());\n },\n\n leftOnly: function (id, prevDV) {\n self.callbacks.removed(self.collectionName, id);\n }\n });\n },\n\n diffDocument: function (id, prevDV, nowDV) {\n var self = this;\n var fields = {};\n LocalCollection._diffObjects(prevDV.getFields(), nowDV.getFields(), {\n both: function (key, prev, now) {\n if (!EJSON.equals(prev, now))\n fields[key] = now;\n },\n rightOnly: function (key, now) {\n fields[key] = now;\n },\n leftOnly: function(key, prev) {\n fields[key] = undefined;\n }\n });\n self.callbacks.changed(self.collectionName, id, fields);\n },\n\n added: function (subscriptionHandle, id, fields) {\n var self = this;\n var docView = self.documents[id];\n var added = false;\n if (!docView) {\n added = true;\n docView = new SessionDocumentView();\n self.documents[id] = docView;\n }\n docView.existsIn[subscriptionHandle] = true;\n var changeCollector = {};\n _.each(fields, function (value, key) {\n docView.changeField(\n subscriptionHandle, key, value, changeCollector, true);\n });\n if (added)\n self.callbacks.added(self.collectionName, id, changeCollector);\n else\n self.callbacks.changed(self.collectionName, id, changeCollector);\n },\n\n changed: function (subscriptionHandle, id, changed) {\n var self = this;\n var changedResult = {};\n var docView = self.documents[id];\n if (!docView)\n throw new Error(\"Could not find element with id \" + id + \" to change\");\n _.each(changed, function (value, key) {\n if (value === undefined)\n docView.clearField(subscriptionHandle, key, changedResult);\n else\n docView.changeField(subscriptionHandle, key, value, changedResult);\n });\n self.callbacks.changed(self.collectionName, id, changedResult);\n },\n\n removed: function (subscriptionHandle, id) {\n var self = this;\n var docView = self.documents[id];\n if (!docView) {\n var err = new Error(\"Removed nonexistent document \" + id);\n throw err;\n }\n delete docView.existsIn[subscriptionHandle];\n if (_.isEmpty(docView.existsIn)) {\n // it is gone from everyone\n self.callbacks.removed(self.collectionName, id);\n delete self.documents[id];\n } else {\n var changed = {};\n // remove this subscription from every precedence list\n // and record the changes\n _.each(docView.dataByKey, function (precedenceList, key) {\n docView.clearField(subscriptionHandle, key, changed);\n });\n\n self.callbacks.changed(self.collectionName, id, changed);\n }\n }\n});\n\n/******************************************************************************/\n/* Session */\n/******************************************************************************/\n\nvar Session = function (server, version, socket, options) {\n var self = this;\n self.id = Random.id();\n\n self.server = server;\n self.version = version;\n\n self.initialized = false;\n self.socket = socket;\n\n // set to null when the session is destroyed. multiple places below\n // use this to determine if the session is alive or not.\n self.inQueue = new Meteor._DoubleEndedQueue();\n\n self.blocked = false;\n self.workerRunning = false;\n\n // Sub objects for active subscriptions\n self._namedSubs = {};\n self._universalSubs = [];\n\n self.userId = null;\n\n self.collectionViews = {};\n\n // Set this to false to not send messages when collectionViews are\n // modified. This is done when rerunning subs in _setUserId and those messages\n // are calculated via a diff instead.\n self._isSending = true;\n\n // If this is true, don't start a newly-created universal publisher on this\n // session. The session will take care of starting it when appropriate.\n self._dontStartNewUniversalSubs = false;\n\n // when we are rerunning subscriptions, any ready messages\n // we want to buffer up for when we are done rerunning subscriptions\n self._pendingReady = [];\n\n // List of callbacks to call when this connection is closed.\n self._closeCallbacks = [];\n\n\n // XXX HACK: If a sockjs connection, save off the URL. This is\n // temporary and will go away in the near future.\n self._socketUrl = socket.url;\n\n // Allow tests to disable responding to pings.\n self._respondToPings = options.respondToPings;\n\n // This object is the public interface to the session. In the public\n // API, it is called the `connection` object. Internally we call it\n // a `connectionHandle` to avoid ambiguity.\n self.connectionHandle = {\n id: self.id,\n close: function () {\n self.close();\n },\n onClose: function (fn) {\n var cb = Meteor.bindEnvironment(fn, \"connection onClose callback\");\n if (self.inQueue) {\n self._closeCallbacks.push(cb);\n } else {\n // if we're already closed, call the callback.\n Meteor.defer(cb);\n }\n },\n clientAddress: self._clientAddress(),\n httpHeaders: self.socket.headers\n };\n\n socket.send(stringifyDDP({msg: 'connected',\n session: self.id}));\n // On initial connect, spin up all the universal publishers.\n Fiber(function () {\n self.startUniversalSubs();\n }).run();\n\n if (version !== 'pre1' && options.heartbeatInterval !== 0) {\n self.heartbeat = new Heartbeat({\n heartbeatInterval: options.heartbeatInterval,\n heartbeatTimeout: options.heartbeatTimeout,\n onTimeout: function () {\n self.close();\n },\n sendPing: function () {\n self.send({msg: 'ping'});\n }\n });\n self.heartbeat.start();\n }\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"sessions\", 1);\n};\n\n_.extend(Session.prototype, {\n\n sendReady: function (subscriptionIds) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"ready\", subs: subscriptionIds});\n else {\n _.each(subscriptionIds, function (subscriptionId) {\n self._pendingReady.push(subscriptionId);\n });\n }\n },\n\n sendAdded: function (collectionName, id, fields) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"added\", collection: collectionName, id: id, fields: fields});\n },\n\n sendChanged: function (collectionName, id, fields) {\n var self = this;\n if (_.isEmpty(fields))\n return;\n\n if (self._isSending) {\n self.send({\n msg: \"changed\",\n collection: collectionName,\n id: id,\n fields: fields\n });\n }\n },\n\n sendRemoved: function (collectionName, id) {\n var self = this;\n if (self._isSending)\n self.send({msg: \"removed\", collection: collectionName, id: id});\n },\n\n getSendCallbacks: function () {\n var self = this;\n return {\n added: _.bind(self.sendAdded, self),\n changed: _.bind(self.sendChanged, self),\n removed: _.bind(self.sendRemoved, self)\n };\n },\n\n getCollectionView: function (collectionName) {\n var self = this;\n if (_.has(self.collectionViews, collectionName)) {\n return self.collectionViews[collectionName];\n }\n var ret = new SessionCollectionView(collectionName,\n self.getSendCallbacks());\n self.collectionViews[collectionName] = ret;\n return ret;\n },\n\n added: function (subscriptionHandle, collectionName, id, fields) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.added(subscriptionHandle, id, fields);\n },\n\n removed: function (subscriptionHandle, collectionName, id) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.removed(subscriptionHandle, id);\n if (view.isEmpty()) {\n delete self.collectionViews[collectionName];\n }\n },\n\n changed: function (subscriptionHandle, collectionName, id, fields) {\n var self = this;\n var view = self.getCollectionView(collectionName);\n view.changed(subscriptionHandle, id, fields);\n },\n\n startUniversalSubs: function () {\n var self = this;\n // Make a shallow copy of the set of universal handlers and start them. If\n // additional universal publishers start while we're running them (due to\n // yielding), they will run separately as part of Server.publish.\n var handlers = _.clone(self.server.universal_publish_handlers);\n _.each(handlers, function (handler) {\n self._startSubscription(handler);\n });\n },\n\n // Destroy this session and unregister it at the server.\n close: function () {\n var self = this;\n\n // Destroy this session, even if it's not registered at the\n // server. Stop all processing and tear everything down. If a socket\n // was attached, close it.\n\n // Already destroyed.\n if (! self.inQueue)\n return;\n\n // Drop the merge box data immediately.\n self.inQueue = null;\n self.collectionViews = {};\n\n if (self.heartbeat) {\n self.heartbeat.stop();\n self.heartbeat = null;\n }\n\n if (self.socket) {\n self.socket.close();\n self.socket._meteorSession = null;\n }\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"sessions\", -1);\n\n Meteor.defer(function () {\n // stop callbacks can yield, so we defer this on close.\n // sub._isDeactivated() detects that we set inQueue to null and\n // treats it as semi-deactivated (it will ignore incoming callbacks, etc).\n self._deactivateAllSubscriptions();\n\n // Defer calling the close callbacks, so that the caller closing\n // the session isn't waiting for all the callbacks to complete.\n _.each(self._closeCallbacks, function (callback) {\n callback();\n });\n });\n\n // Unregister the session.\n self.server._removeSession(self);\n },\n\n // Send a message (doing nothing if no socket is connected right now.)\n // It should be a JSON object (it will be stringified.)\n send: function (msg) {\n var self = this;\n if (self.socket) {\n if (Meteor._printSentDDP)\n Meteor._debug(\"Sent DDP\", stringifyDDP(msg));\n self.socket.send(stringifyDDP(msg));\n }\n },\n\n // Send a connection error.\n sendError: function (reason, offendingMessage) {\n var self = this;\n var msg = {msg: 'error', reason: reason};\n if (offendingMessage)\n msg.offendingMessage = offendingMessage;\n self.send(msg);\n },\n\n // Process 'msg' as an incoming message. (But as a guard against\n // race conditions during reconnection, ignore the message if\n // 'socket' is not the currently connected socket.)\n //\n // We run the messages from the client one at a time, in the order\n // given by the client. The message handler is passed an idempotent\n // function 'unblock' which it may call to allow other messages to\n // begin running in parallel in another fiber (for example, a method\n // that wants to yield.) Otherwise, it is automatically unblocked\n // when it returns.\n //\n // Actually, we don't have to 'totally order' the messages in this\n // way, but it's the easiest thing that's correct. (unsub needs to\n // be ordered against sub, methods need to be ordered against each\n // other.)\n processMessage: function (msg_in) {\n var self = this;\n if (!self.inQueue) // we have been destroyed.\n return;\n\n // Respond to ping and pong messages immediately without queuing.\n // If the negotiated DDP version is \"pre1\" which didn't support\n // pings, preserve the \"pre1\" behavior of responding with a \"bad\n // request\" for the unknown messages.\n //\n // Fibers are needed because heartbeat uses Meteor.setTimeout, which\n // needs a Fiber. We could actually use regular setTimeout and avoid\n // these new fibers, but it is easier to just make everything use\n // Meteor.setTimeout and not think too hard.\n if (self.version !== 'pre1' && msg_in.msg === 'ping') {\n if (self._respondToPings)\n self.send({msg: \"pong\", id: msg_in.id});\n if (self.heartbeat)\n Fiber(function () {\n self.heartbeat.pingReceived();\n }).run();\n return;\n }\n if (self.version !== 'pre1' && msg_in.msg === 'pong') {\n if (self.heartbeat)\n Fiber(function () {\n self.heartbeat.pongReceived();\n }).run();\n return;\n }\n\n self.inQueue.push(msg_in);\n if (self.workerRunning)\n return;\n self.workerRunning = true;\n\n var processNext = function () {\n var msg = self.inQueue && self.inQueue.shift();\n if (!msg) {\n self.workerRunning = false;\n return;\n }\n\n Fiber(function () {\n var blocked = true;\n\n var unblock = function () {\n if (!blocked)\n return; // idempotent\n blocked = false;\n processNext();\n };\n\n if (_.has(self.protocol_handlers, msg.msg))\n self.protocol_handlers[msg.msg].call(self, msg, unblock);\n else\n self.sendError('Bad request', msg);\n unblock(); // in case the handler didn't already do it\n }).run();\n };\n\n processNext();\n },\n\n protocol_handlers: {\n sub: function (msg) {\n var self = this;\n\n // reject malformed messages\n if (typeof (msg.id) !== \"string\" ||\n typeof (msg.name) !== \"string\" ||\n (('params' in msg) && !(msg.params instanceof Array))) {\n self.sendError(\"Malformed subscription\", msg);\n return;\n }\n\n if (!self.server.publish_handlers[msg.name]) {\n self.send({\n msg: 'nosub', id: msg.id,\n error: new Meteor.Error(404, \"Subscription not found\")});\n return;\n }\n\n if (_.has(self._namedSubs, msg.id))\n // subs are idempotent, or rather, they are ignored if a sub\n // with that id already exists. this is important during\n // reconnect.\n return;\n\n var handler = self.server.publish_handlers[msg.name];\n self._startSubscription(handler, msg.id, msg.params, msg.name);\n\n },\n\n unsub: function (msg) {\n var self = this;\n\n self._stopSubscription(msg.id);\n },\n\n method: function (msg, unblock) {\n var self = this;\n\n // reject malformed messages\n // For now, we silently ignore unknown attributes,\n // for forwards compatibility.\n if (typeof (msg.id) !== \"string\" ||\n typeof (msg.method) !== \"string\" ||\n (('params' in msg) && !(msg.params instanceof Array)) ||\n (('randomSeed' in msg) && (typeof msg.randomSeed !== \"string\"))) {\n self.sendError(\"Malformed method invocation\", msg);\n return;\n }\n\n var randomSeed = msg.randomSeed || null;\n\n // set up to mark the method as satisfied once all observers\n // (and subscriptions) have reacted to any writes that were\n // done.\n var fence = new DDPServer._WriteFence;\n fence.onAllCommitted(function () {\n // Retire the fence so that future writes are allowed.\n // This means that callbacks like timers are free to use\n // the fence, and if they fire before it's armed (for\n // example, because the method waits for them) their\n // writes will be included in the fence.\n fence.retire();\n self.send({\n msg: 'updated', methods: [msg.id]});\n });\n\n // find the handler\n var handler = self.server.method_handlers[msg.method];\n if (!handler) {\n self.send({\n msg: 'result', id: msg.id,\n error: new Meteor.Error(404, \"Method not found\")});\n fence.arm();\n return;\n }\n\n var setUserId = function(userId) {\n self._setUserId(userId);\n };\n\n var invocation = new MethodInvocation({\n isSimulation: false,\n userId: self.userId,\n setUserId: setUserId,\n unblock: unblock,\n connection: self.connectionHandle,\n randomSeed: randomSeed\n });\n try {\n var result = DDPServer._CurrentWriteFence.withValue(fence, function () {\n return DDP._CurrentInvocation.withValue(invocation, function () {\n return maybeAuditArgumentChecks(\n handler, invocation, msg.params, \"call to '\" + msg.method + \"'\");\n });\n });\n } catch (e) {\n var exception = e;\n }\n\n fence.arm(); // we're done adding writes to the fence\n unblock(); // unblock, if the method hasn't done it already\n\n exception = wrapInternalException(\n exception, \"while invoking method '\" + msg.method + \"'\");\n\n // send response and add to cache\n var payload =\n exception ? {error: exception} : (result !== undefined ?\n {result: result} : {});\n self.send(_.extend({msg: 'result', id: msg.id}, payload));\n }\n },\n\n _eachSub: function (f) {\n var self = this;\n _.each(self._namedSubs, f);\n _.each(self._universalSubs, f);\n },\n\n _diffCollectionViews: function (beforeCVs) {\n var self = this;\n LocalCollection._diffObjects(beforeCVs, self.collectionViews, {\n both: function (collectionName, leftValue, rightValue) {\n rightValue.diff(leftValue);\n },\n rightOnly: function (collectionName, rightValue) {\n _.each(rightValue.documents, function (docView, id) {\n self.sendAdded(collectionName, id, docView.getFields());\n });\n },\n leftOnly: function (collectionName, leftValue) {\n _.each(leftValue.documents, function (doc, id) {\n self.sendRemoved(collectionName, id);\n });\n }\n });\n },\n\n // Sets the current user id in all appropriate contexts and reruns\n // all subscriptions\n _setUserId: function(userId) {\n var self = this;\n\n if (userId !== null && typeof userId !== \"string\")\n throw new Error(\"setUserId must be called on string or null, not \" +\n typeof userId);\n\n // Prevent newly-created universal subscriptions from being added to our\n // session; they will be found below when we call startUniversalSubs.\n //\n // (We don't have to worry about named subscriptions, because we only add\n // them when we process a 'sub' message. We are currently processing a\n // 'method' message, and the method did not unblock, because it is illegal\n // to call setUserId after unblock. Thus we cannot be concurrently adding a\n // new named subscription.)\n self._dontStartNewUniversalSubs = true;\n\n // Prevent current subs from updating our collectionViews and call their\n // stop callbacks. This may yield.\n self._eachSub(function (sub) {\n sub._deactivate();\n });\n\n // All subs should now be deactivated. Stop sending messages to the client,\n // save the state of the published collections, reset to an empty view, and\n // update the userId.\n self._isSending = false;\n var beforeCVs = self.collectionViews;\n self.collectionViews = {};\n self.userId = userId;\n\n // Save the old named subs, and reset to having no subscriptions.\n var oldNamedSubs = self._namedSubs;\n self._namedSubs = {};\n self._universalSubs = [];\n\n _.each(oldNamedSubs, function (sub, subscriptionId) {\n self._namedSubs[subscriptionId] = sub._recreate();\n // nb: if the handler throws or calls this.error(), it will in fact\n // immediately send its 'nosub'. This is OK, though.\n self._namedSubs[subscriptionId]._runHandler();\n });\n\n // Allow newly-created universal subs to be started on our connection in\n // parallel with the ones we're spinning up here, and spin up universal\n // subs.\n self._dontStartNewUniversalSubs = false;\n self.startUniversalSubs();\n\n // Start sending messages again, beginning with the diff from the previous\n // state of the world to the current state. No yields are allowed during\n // this diff, so that other changes cannot interleave.\n Meteor._noYieldsAllowed(function () {\n self._isSending = true;\n self._diffCollectionViews(beforeCVs);\n if (!_.isEmpty(self._pendingReady)) {\n self.sendReady(self._pendingReady);\n self._pendingReady = [];\n }\n });\n },\n\n _startSubscription: function (handler, subId, params, name) {\n var self = this;\n\n var sub = new Subscription(\n self, handler, subId, params, name);\n if (subId)\n self._namedSubs[subId] = sub;\n else\n self._universalSubs.push(sub);\n\n sub._runHandler();\n },\n\n // tear down specified subscription\n _stopSubscription: function (subId, error) {\n var self = this;\n\n var subName = null;\n\n if (subId && self._namedSubs[subId]) {\n subName = self._namedSubs[subId]._name;\n self._namedSubs[subId]._removeAllDocuments();\n self._namedSubs[subId]._deactivate();\n delete self._namedSubs[subId];\n }\n\n var response = {msg: 'nosub', id: subId};\n\n if (error) {\n response.error = wrapInternalException(\n error,\n subName ? (\"from sub \" + subName + \" id \" + subId)\n : (\"from sub id \" + subId));\n }\n\n self.send(response);\n },\n\n // tear down all subscriptions. Note that this does NOT send removed or nosub\n // messages, since we assume the client is gone.\n _deactivateAllSubscriptions: function () {\n var self = this;\n\n _.each(self._namedSubs, function (sub, id) {\n sub._deactivate();\n });\n self._namedSubs = {};\n\n _.each(self._universalSubs, function (sub) {\n sub._deactivate();\n });\n self._universalSubs = [];\n },\n\n // Determine the remote client's IP address, based on the\n // HTTP_FORWARDED_COUNT environment variable representing how many\n // proxies the server is behind.\n _clientAddress: function () {\n var self = this;\n\n // For the reported client address for a connection to be correct,\n // the developer must set the HTTP_FORWARDED_COUNT environment\n // variable to an integer representing the number of hops they\n // expect in the `x-forwarded-for` header. E.g., set to \"1\" if the\n // server is behind one proxy.\n //\n // This could be computed once at startup instead of every time.\n var httpForwardedCount = parseInt(process.env['HTTP_FORWARDED_COUNT']) || 0;\n\n if (httpForwardedCount === 0)\n return self.socket.remoteAddress;\n\n var forwardedFor = self.socket.headers[\"x-forwarded-for\"];\n if (! _.isString(forwardedFor))\n return null;\n forwardedFor = forwardedFor.trim().split(/\\s*,\\s*/);\n\n // Typically the first value in the `x-forwarded-for` header is\n // the original IP address of the client connecting to the first\n // proxy. However, the end user can easily spoof the header, in\n // which case the first value(s) will be the fake IP address from\n // the user pretending to be a proxy reporting the original IP\n // address value. By counting HTTP_FORWARDED_COUNT back from the\n // end of the list, we ensure that we get the IP address being\n // reported by *our* first proxy.\n\n if (httpForwardedCount < 0 || httpForwardedCount > forwardedFor.length)\n return null;\n\n return forwardedFor[forwardedFor.length - httpForwardedCount];\n }\n});\n\n/******************************************************************************/\n/* Subscription */\n/******************************************************************************/\n\n// ctor for a sub handle: the input to each publish function\n\n// Instance name is this because it's usually referred to as this inside a\n// publish\n/**\n * @summary The server's side of a subscription\n * @class Subscription\n * @instanceName this\n */\nvar Subscription = function (\n session, handler, subscriptionId, params, name) {\n var self = this;\n self._session = session; // type is Session\n\n /**\n * @summary Access inside the publish function. The incoming [connection](#meteor_onconnection) for this subscription.\n * @locus Server\n * @name connection\n * @memberOf Subscription\n * @instance\n */\n self.connection = session.connectionHandle; // public API object\n\n self._handler = handler;\n\n // my subscription ID (generated by client, undefined for universal subs).\n self._subscriptionId = subscriptionId;\n // undefined for universal subs\n self._name = name;\n\n self._params = params || [];\n\n // Only named subscriptions have IDs, but we need some sort of string\n // internally to keep track of all subscriptions inside\n // SessionDocumentViews. We use this subscriptionHandle for that.\n if (self._subscriptionId) {\n self._subscriptionHandle = 'N' + self._subscriptionId;\n } else {\n self._subscriptionHandle = 'U' + Random.id();\n }\n\n // has _deactivate been called?\n self._deactivated = false;\n\n // stop callbacks to g/c this sub. called w/ zero arguments.\n self._stopCallbacks = [];\n\n // the set of (collection, documentid) that this subscription has\n // an opinion about\n self._documents = {};\n\n // remember if we are ready.\n self._ready = false;\n\n // Part of the public API: the user of this sub.\n\n /**\n * @summary Access inside the publish function. The id of the logged-in user, or `null` if no user is logged in.\n * @locus Server\n * @memberOf Subscription\n * @name userId\n * @instance\n */\n self.userId = session.userId;\n\n // For now, the id filter is going to default to\n // the to/from DDP methods on LocalCollection, to\n // specifically deal with mongo/minimongo ObjectIds.\n\n // Later, you will be able to make this be \"raw\"\n // if you want to publish a collection that you know\n // just has strings for keys and no funny business, to\n // a ddp consumer that isn't minimongo\n\n self._idFilter = {\n idStringify: LocalCollection._idStringify,\n idParse: LocalCollection._idParse\n };\n\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"subscriptions\", 1);\n};\n\n_.extend(Subscription.prototype, {\n _runHandler: function () {\n // XXX should we unblock() here? Either before running the publish\n // function, or before running _publishCursor.\n //\n // Right now, each publish function blocks all future publishes and\n // methods waiting on data from Mongo (or whatever else the function\n // blocks on). This probably slows page load in common cases.\n\n var self = this;\n try {\n var res = maybeAuditArgumentChecks(\n self._handler, self, EJSON.clone(self._params),\n // It's OK that this would look weird for universal subscriptions,\n // because they have no arguments so there can never be an\n // audit-argument-checks failure.\n \"publisher '\" + self._name + \"'\");\n } catch (e) {\n self.error(e);\n return;\n }\n\n // Did the handler call this.error or this.stop?\n if (self._isDeactivated())\n return;\n\n // SPECIAL CASE: Instead of writing their own callbacks that invoke\n // this.added/changed/ready/etc, the user can just return a collection\n // cursor or array of cursors from the publish function; we call their\n // _publishCursor method which starts observing the cursor and publishes the\n // results. Note that _publishCursor does NOT call ready().\n //\n // XXX This uses an undocumented interface which only the Mongo cursor\n // interface publishes. Should we make this interface public and encourage\n // users to implement it themselves? Arguably, it's unnecessary; users can\n // already write their own functions like\n // var publishMyReactiveThingy = function (name, handler) {\n // Meteor.publish(name, function () {\n // var reactiveThingy = handler();\n // reactiveThingy.publishMe();\n // });\n // };\n var isCursor = function (c) {\n return c && c._publishCursor;\n };\n if (isCursor(res)) {\n try {\n res._publishCursor(self);\n } catch (e) {\n self.error(e);\n return;\n }\n // _publishCursor only returns after the initial added callbacks have run.\n // mark subscription as ready.\n self.ready();\n } else if (_.isArray(res)) {\n // check all the elements are cursors\n if (! _.all(res, isCursor)) {\n self.error(new Error(\"Publish function returned an array of non-Cursors\"));\n return;\n }\n // find duplicate collection names\n // XXX we should support overlapping cursors, but that would require the\n // merge box to allow overlap within a subscription\n var collectionNames = {};\n for (var i = 0; i < res.length; ++i) {\n var collectionName = res[i]._getCollectionName();\n if (_.has(collectionNames, collectionName)) {\n self.error(new Error(\n \"Publish function returned multiple cursors for collection \" +\n collectionName));\n return;\n }\n collectionNames[collectionName] = true;\n };\n\n try {\n _.each(res, function (cur) {\n cur._publishCursor(self);\n });\n } catch (e) {\n self.error(e);\n return;\n }\n self.ready();\n } else if (res) {\n // truthy values other than cursors or arrays are probably a\n // user mistake (possible returning a Mongo document via, say,\n // `coll.findOne()`).\n self.error(new Error(\"Publish function can only return a Cursor or \"\n + \"an array of Cursors\"));\n }\n },\n\n // This calls all stop callbacks and prevents the handler from updating any\n // SessionCollectionViews further. It's used when the user unsubscribes or\n // disconnects, as well as during setUserId re-runs. It does *NOT* send\n // removed messages for the published objects; if that is necessary, call\n // _removeAllDocuments first.\n _deactivate: function() {\n var self = this;\n if (self._deactivated)\n return;\n self._deactivated = true;\n self._callStopCallbacks();\n Package.facts && Package.facts.Facts.incrementServerFact(\n \"livedata\", \"subscriptions\", -1);\n },\n\n _callStopCallbacks: function () {\n var self = this;\n // tell listeners, so they can clean up\n var callbacks = self._stopCallbacks;\n self._stopCallbacks = [];\n _.each(callbacks, function (callback) {\n callback();\n });\n },\n\n // Send remove messages for every document.\n _removeAllDocuments: function () {\n var self = this;\n Meteor._noYieldsAllowed(function () {\n _.each(self._documents, function(collectionDocs, collectionName) {\n // Iterate over _.keys instead of the dictionary itself, since we'll be\n // mutating it.\n _.each(_.keys(collectionDocs), function (strId) {\n self.removed(collectionName, self._idFilter.idParse(strId));\n });\n });\n });\n },\n\n // Returns a new Subscription for the same session with the same\n // initial creation parameters. This isn't a clone: it doesn't have\n // the same _documents cache, stopped state or callbacks; may have a\n // different _subscriptionHandle, and gets its userId from the\n // session, not from this object.\n _recreate: function () {\n var self = this;\n return new Subscription(\n self._session, self._handler, self._subscriptionId, self._params,\n self._name);\n },\n\n /**\n * @summary Call inside the publish function. Stops this client's subscription, triggering a call on the client to the `onStop` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any. If `error` is not a [`Meteor.Error`](#meteor_error), it will be [sanitized](#meteor_error).\n * @locus Server\n * @param {Error} error The error to pass to the client.\n * @instance\n * @memberOf Subscription\n */\n error: function (error) {\n var self = this;\n if (self._isDeactivated())\n return;\n self._session._stopSubscription(self._subscriptionId, error);\n },\n\n // Note that while our DDP client will notice that you've called stop() on the\n // server (and clean up its _subscriptions table) we don't actually provide a\n // mechanism for an app to notice this (the subscribe onError callback only\n // triggers if there is an error).\n\n /**\n * @summary Call inside the publish function. Stops this client's subscription and invokes the client's `onStop` callback with no error.\n * @locus Server\n * @instance\n * @memberOf Subscription\n */\n stop: function () {\n var self = this;\n if (self._isDeactivated())\n return;\n self._session._stopSubscription(self._subscriptionId);\n },\n\n /**\n * @summary Call inside the publish function. Registers a callback function to run when the subscription is stopped.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {Function} func The callback function\n */\n onStop: function (callback) {\n var self = this;\n if (self._isDeactivated())\n callback();\n else\n self._stopCallbacks.push(callback);\n },\n\n // This returns true if the sub has been deactivated, *OR* if the session was\n // destroyed but the deferred call to _deactivateAllSubscriptions hasn't\n // happened yet.\n _isDeactivated: function () {\n var self = this;\n return self._deactivated || self._session.inQueue === null;\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document has been added to the record set.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that contains the new document.\n * @param {String} id The new document's ID.\n * @param {Object} fields The fields in the new document. If `_id` is present it is ignored.\n */\n added: function (collectionName, id, fields) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n Meteor._ensure(self._documents, collectionName)[id] = true;\n self._session.added(self._subscriptionHandle, collectionName, id, fields);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document in the record set has been modified.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that contains the changed document.\n * @param {String} id The changed document's ID.\n * @param {Object} fields The fields in the document that have changed, together with their new values. If a field is not present in `fields` it was left unchanged; if it is present in `fields` and has a value of `undefined` it was removed from the document. If `_id` is present it is ignored.\n */\n changed: function (collectionName, id, fields) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n self._session.changed(self._subscriptionHandle, collectionName, id, fields);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that a document has been removed from the record set.\n * @locus Server\n * @memberOf Subscription\n * @instance\n * @param {String} collection The name of the collection that the document has been removed from.\n * @param {String} id The ID of the document that has been removed.\n */\n removed: function (collectionName, id) {\n var self = this;\n if (self._isDeactivated())\n return;\n id = self._idFilter.idStringify(id);\n // We don't bother to delete sets of things in a collection if the\n // collection is empty. It could break _removeAllDocuments.\n delete self._documents[collectionName][id];\n self._session.removed(self._subscriptionHandle, collectionName, id);\n },\n\n /**\n * @summary Call inside the publish function. Informs the subscriber that an initial, complete snapshot of the record set has been sent. This will trigger a call on the client to the `onReady` callback passed to [`Meteor.subscribe`](#meteor_subscribe), if any.\n * @locus Server\n * @memberOf Subscription\n * @instance\n */\n ready: function () {\n var self = this;\n if (self._isDeactivated())\n return;\n if (!self._subscriptionId)\n return; // unnecessary but ignored for universal sub\n if (!self._ready) {\n self._session.sendReady([self._subscriptionId]);\n self._ready = true;\n }\n }\n});\n\n/******************************************************************************/\n/* Server */\n/******************************************************************************/\n\nServer = function (options) {\n var self = this;\n\n // The default heartbeat interval is 30 seconds on the server and 35\n // seconds on the client. Since the client doesn't need to send a\n // ping as long as it is receiving pings, this means that pings\n // normally go from the server to the client.\n //\n // Note: Troposphere depends on the ability to mutate\n // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.\n self.options = _.defaults(options || {}, {\n heartbeatInterval: 30000,\n heartbeatTimeout: 15000,\n // For testing, allow responding to pings to be disabled.\n respondToPings: true\n });\n\n // Map of callbacks to call when a new connection comes in to the\n // server and completes DDP version negotiation. Use an object instead\n // of an array so we can safely remove one from the list while\n // iterating over it.\n self.onConnectionHook = new Hook({\n debugPrintExceptions: \"onConnection callback\"\n });\n\n self.publish_handlers = {};\n self.universal_publish_handlers = [];\n\n self.method_handlers = {};\n\n self.sessions = {}; // map from id to session\n\n self.stream_server = new StreamServer;\n\n self.stream_server.register(function (socket) {\n // socket implements the SockJSConnection interface\n socket._meteorSession = null;\n\n var sendError = function (reason, offendingMessage) {\n var msg = {msg: 'error', reason: reason};\n if (offendingMessage)\n msg.offendingMessage = offendingMessage;\n socket.send(stringifyDDP(msg));\n };\n\n socket.on('data', function (raw_msg) {\n if (Meteor._printReceivedDDP) {\n Meteor._debug(\"Received DDP\", raw_msg);\n }\n try {\n try {\n var msg = parseDDP(raw_msg);\n } catch (err) {\n sendError('Parse error');\n return;\n }\n if (msg === null || !msg.msg) {\n sendError('Bad request', msg);\n return;\n }\n\n if (msg.msg === 'connect') {\n if (socket._meteorSession) {\n sendError(\"Already connected\", msg);\n return;\n }\n Fiber(function () {\n self._handleConnect(socket, msg);\n }).run();\n return;\n }\n\n if (!socket._meteorSession) {\n sendError('Must connect first', msg);\n return;\n }\n socket._meteorSession.processMessage(msg);\n } catch (e) {\n // XXX print stack nicely\n Meteor._debug(\"Internal exception while processing message\", msg,\n e.message, e.stack);\n }\n });\n\n socket.on('close', function () {\n if (socket._meteorSession) {\n Fiber(function () {\n socket._meteorSession.close();\n }).run();\n }\n });\n });\n};\n\n_.extend(Server.prototype, {\n\n /**\n * @summary Register a callback to be called when a new DDP connection is made to the server.\n * @locus Server\n * @param {function} callback The function to call when a new DDP connection is established.\n * @memberOf Meteor\n */\n onConnection: function (fn) {\n var self = this;\n return self.onConnectionHook.register(fn);\n },\n\n _handleConnect: function (socket, msg) {\n var self = this;\n\n // The connect message must specify a version and an array of supported\n // versions, and it must claim to support what it is proposing.\n if (!(typeof (msg.version) === 'string' &&\n _.isArray(msg.support) &&\n _.all(msg.support, _.isString) &&\n _.contains(msg.support, msg.version))) {\n socket.send(stringifyDDP({msg: 'failed',\n version: SUPPORTED_DDP_VERSIONS[0]}));\n socket.close();\n return;\n }\n\n // In the future, handle session resumption: something like:\n // socket._meteorSession = self.sessions[msg.session]\n var version = calculateVersion(msg.support, SUPPORTED_DDP_VERSIONS);\n\n if (msg.version !== version) {\n // The best version to use (according to the client's stated preferences)\n // is not the one the client is trying to use. Inform them about the best\n // version to use.\n socket.send(stringifyDDP({msg: 'failed', version: version}));\n socket.close();\n return;\n }\n\n // Yay, version matches! Create a new session.\n // Note: Troposphere depends on the ability to mutate\n // Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.\n socket._meteorSession = new Session(self, version, socket, self.options);\n self.sessions[socket._meteorSession.id] = socket._meteorSession;\n self.onConnectionHook.each(function (callback) {\n if (socket._meteorSession)\n callback(socket._meteorSession.connectionHandle);\n return true;\n });\n },\n /**\n * Register a publish handler function.\n *\n * @param name {String} identifier for query\n * @param handler {Function} publish handler\n * @param options {Object}\n *\n * Server will call handler function on each new subscription,\n * either when receiving DDP sub message for a named subscription, or on\n * DDP connect for a universal subscription.\n *\n * If name is null, this will be a subscription that is\n * automatically established and permanently on for all connected\n * client, instead of a subscription that can be turned on and off\n * with subscribe().\n *\n * options to contain:\n * - (mostly internal) is_auto: true if generated automatically\n * from an autopublish hook. this is for cosmetic purposes only\n * (it lets us determine whether to print a warning suggesting\n * that you turn off autopublish.)\n */\n\n /**\n * @summary Publish a record set.\n * @memberOf Meteor\n * @locus Server\n * @param {String} name Name of the record set. If `null`, the set has no name, and the record set is automatically sent to all connected clients.\n * @param {Function} func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to `subscribe`, the function is called with the same arguments.\n */\n publish: function (name, handler, options) {\n var self = this;\n\n options = options || {};\n\n if (name && name in self.publish_handlers) {\n Meteor._debug(\"Ignoring duplicate publish named '\" + name + \"'\");\n return;\n }\n\n if (Package.autopublish && !options.is_auto) {\n // They have autopublish on, yet they're trying to manually\n // picking stuff to publish. They probably should turn off\n // autopublish. (This check isn't perfect -- if you create a\n // publish before you turn on autopublish, it won't catch\n // it. But this will definitely handle the simple case where\n // you've added the autopublish package to your app, and are\n // calling publish from your app code.)\n if (!self.warned_about_autopublish) {\n self.warned_about_autopublish = true;\n Meteor._debug(\n\"** You've set up some data subscriptions with Meteor.publish(), but\\n\" +\n\"** you still have autopublish turned on. Because autopublish is still\\n\" +\n\"** on, your Meteor.publish() calls won't have much effect. All data\\n\" +\n\"** will still be sent to all clients.\\n\" +\n\"**\\n\" +\n\"** Turn off autopublish by removing the autopublish package:\\n\" +\n\"**\\n\" +\n\"** $ meteor remove autopublish\\n\" +\n\"**\\n\" +\n\"** .. and make sure you have Meteor.publish() and Meteor.subscribe() calls\\n\" +\n\"** for each collection that you want clients to see.\\n\");\n }\n }\n\n if (name)\n self.publish_handlers[name] = handler;\n else {\n self.universal_publish_handlers.push(handler);\n // Spin up the new publisher on any existing session too. Run each\n // session's subscription in a new Fiber, so that there's no change for\n // self.sessions to change while we're running this loop.\n _.each(self.sessions, function (session) {\n if (!session._dontStartNewUniversalSubs) {\n Fiber(function() {\n session._startSubscription(handler);\n }).run();\n }\n });\n }\n },\n\n _removeSession: function (session) {\n var self = this;\n if (self.sessions[session.id]) {\n delete self.sessions[session.id];\n }\n },\n\n /**\n * @summary Defines functions that can be invoked over the network by clients.\n * @locus Anywhere\n * @param {Object} methods Dictionary whose keys are method names and values are functions.\n * @memberOf Meteor\n */\n methods: function (methods) {\n var self = this;\n _.each(methods, function (func, name) {\n if (self.method_handlers[name])\n throw new Error(\"A method named '\" + name + \"' is already defined\");\n self.method_handlers[name] = func;\n });\n },\n\n call: function (name /*, arguments */) {\n // if it's a function, the last argument is the result callback,\n // not a parameter to the remote method.\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length && typeof args[args.length - 1] === \"function\")\n var callback = args.pop();\n return this.apply(name, args, callback);\n },\n\n // @param options {Optional Object}\n // @param callback {Optional Function}\n apply: function (name, args, options, callback) {\n var self = this;\n\n // We were passed 3 arguments. They may be either (name, args, options)\n // or (name, args, callback)\n if (!callback && typeof options === 'function') {\n callback = options;\n options = {};\n }\n options = options || {};\n\n if (callback)\n // It's not really necessary to do this, since we immediately\n // run the callback in this fiber before returning, but we do it\n // anyway for regularity.\n // XXX improve error message (and how we report it)\n callback = Meteor.bindEnvironment(\n callback,\n \"delivering result of invoking '\" + name + \"'\"\n );\n\n // Run the handler\n var handler = self.method_handlers[name];\n var exception;\n if (!handler) {\n exception = new Meteor.Error(404, \"Method not found\");\n } else {\n // If this is a method call from within another method, get the\n // user state from the outer method, otherwise don't allow\n // setUserId to be called\n var userId = null;\n var setUserId = function() {\n throw new Error(\"Can't call setUserId on a server initiated method call\");\n };\n var connection = null;\n var currentInvocation = DDP._CurrentInvocation.get();\n if (currentInvocation) {\n userId = currentInvocation.userId;\n setUserId = function(userId) {\n currentInvocation.setUserId(userId);\n };\n connection = currentInvocation.connection;\n }\n\n var invocation = new MethodInvocation({\n isSimulation: false,\n userId: userId,\n setUserId: setUserId,\n connection: connection,\n randomSeed: makeRpcSeed(currentInvocation, name)\n });\n try {\n var result = DDP._CurrentInvocation.withValue(invocation, function () {\n return maybeAuditArgumentChecks(\n handler, invocation, EJSON.clone(args), \"internal call to '\" +\n name + \"'\");\n });\n result = EJSON.clone(result);\n } catch (e) {\n exception = e;\n }\n }\n\n // Return the result in whichever way the caller asked for it. Note that we\n // do NOT block on the write fence in an analogous way to how the client\n // blocks on the relevant data being visible, so you are NOT guaranteed that\n // cursor observe callbacks have fired when your callback is invoked. (We\n // can change this if there's a real use case.)\n if (callback) {\n callback(exception, result);\n return undefined;\n }\n if (exception)\n throw exception;\n return result;\n },\n\n _urlForSession: function (sessionId) {\n var self = this;\n var session = self.sessions[sessionId];\n if (session)\n return session._socketUrl;\n else\n return null;\n }\n});\n\nvar calculateVersion = function (clientSupportedVersions,\n serverSupportedVersions) {\n var correctVersion = _.find(clientSupportedVersions, function (version) {\n return _.contains(serverSupportedVersions, version);\n });\n if (!correctVersion) {\n correctVersion = serverSupportedVersions[0];\n }\n return correctVersion;\n};\n\nLivedataTest.calculateVersion = calculateVersion;\n\n\n// \"blind\" exceptions other than those that were deliberately thrown to signal\n// errors to the client\nvar wrapInternalException = function (exception, context) {\n if (!exception || exception instanceof Meteor.Error)\n return exception;\n\n // tests can set the 'expected' flag on an exception so it won't go to the\n // server log\n if (!exception.expected) {\n Meteor._debug(\"Exception \" + context, exception.stack);\n if (exception.sanitizedError) {\n Meteor._debug(\"Sanitized and reported to the client as:\", exception.sanitizedError.message);\n Meteor._debug();\n }\n }\n\n // Did the error contain more details that could have been useful if caught in\n // server code (or if thrown from non-client-originated code), but also\n // provided a \"sanitized\" version with more context than 500 Internal server\n // error? Use that.\n if (exception.sanitizedError) {\n if (exception.sanitizedError instanceof Meteor.Error)\n return exception.sanitizedError;\n Meteor._debug(\"Exception \" + context + \" provides a sanitizedError that \" +\n \"is not a Meteor.Error; ignoring\");\n }\n\n return new Meteor.Error(500, \"Internal server error\");\n};\n\n\n// Audit argument checks, if the audit-argument-checks package exists (it is a\n// weak dependency of this package).\nvar maybeAuditArgumentChecks = function (f, context, args, description) {\n args = args || [];\n if (Package['audit-argument-checks']) {\n return Match._failIfArgumentsAreNotAllChecked(\n f, context, args, description);\n }\n return f.apply(context, args);\n};\n","var path = Npm.require('path');\nvar Future = Npm.require(path.join('fibers', 'future'));\n\n// A write fence collects a group of writes, and provides a callback\n// when all of the writes are fully committed and propagated (all\n// observers have been notified of the write and acknowledged it.)\n//\nDDPServer._WriteFence = function () {\n var self = this;\n\n self.armed = false;\n self.fired = false;\n self.retired = false;\n self.outstanding_writes = 0;\n self.completion_callbacks = [];\n};\n\n// The current write fence. When there is a current write fence, code\n// that writes to databases should register their writes with it using\n// beginWrite().\n//\nDDPServer._CurrentWriteFence = new Meteor.EnvironmentVariable;\n\n_.extend(DDPServer._WriteFence.prototype, {\n // Start tracking a write, and return an object to represent it. The\n // object has a single method, committed(). This method should be\n // called when the write is fully committed and propagated. You can\n // continue to add writes to the WriteFence up until it is triggered\n // (calls its callbacks because all writes have committed.)\n beginWrite: function () {\n var self = this;\n\n if (self.retired)\n return { committed: function () {} };\n\n if (self.fired)\n throw new Error(\"fence has already activated -- too late to add writes\");\n\n self.outstanding_writes++;\n var committed = false;\n return {\n committed: function () {\n if (committed)\n throw new Error(\"committed called twice on the same write\");\n committed = true;\n self.outstanding_writes--;\n self._maybeFire();\n }\n };\n },\n\n // Arm the fence. Once the fence is armed, and there are no more\n // uncommitted writes, it will activate.\n arm: function () {\n var self = this;\n if (self === DDPServer._CurrentWriteFence.get())\n throw Error(\"Can't arm the current fence\");\n self.armed = true;\n self._maybeFire();\n },\n\n // Register a function to be called when the fence fires.\n onAllCommitted: function (func) {\n var self = this;\n if (self.fired)\n throw new Error(\"fence has already activated -- too late to \" +\n \"add a callback\");\n self.completion_callbacks.push(func);\n },\n\n // Convenience function. Arms the fence, then blocks until it fires.\n armAndWait: function () {\n var self = this;\n var future = new Future;\n self.onAllCommitted(function () {\n future['return']();\n });\n self.arm();\n future.wait();\n },\n\n _maybeFire: function () {\n var self = this;\n if (self.fired)\n throw new Error(\"write fence already activated?\");\n if (self.armed && !self.outstanding_writes) {\n self.fired = true;\n _.each(self.completion_callbacks, function (f) {f(self);});\n self.completion_callbacks = [];\n }\n },\n\n // Deactivate this fence so that adding more writes has no effect.\n // The fence must have already fired.\n retire: function () {\n var self = this;\n if (! self.fired)\n throw new Error(\"Can't retire a fence that hasn't fired.\");\n self.retired = true;\n }\n});\n","// A \"crossbar\" is a class that provides structured notification registration.\n// See _match for the definition of how a notification matches a trigger.\n// All notifications and triggers must have a string key named 'collection'.\n\nDDPServer._Crossbar = function (options) {\n var self = this;\n options = options || {};\n\n self.nextId = 1;\n // map from collection name (string) -> listener id -> object. each object has\n // keys 'trigger', 'callback'.\n self.listenersByCollection = {};\n self.factPackage = options.factPackage || \"livedata\";\n self.factName = options.factName || null;\n};\n\n_.extend(DDPServer._Crossbar.prototype, {\n // Listen for notification that match 'trigger'. A notification\n // matches if it has the key-value pairs in trigger as a\n // subset. When a notification matches, call 'callback', passing\n // the actual notification.\n //\n // Returns a listen handle, which is an object with a method\n // stop(). Call stop() to stop listening.\n //\n // XXX It should be legal to call fire() from inside a listen()\n // callback?\n listen: function (trigger, callback) {\n var self = this;\n var id = self.nextId++;\n\n if (typeof(trigger.collection) !== 'string') {\n throw Error(\"Trigger lacks collection!\");\n }\n\n var collection = trigger.collection; // save in case trigger is mutated\n var record = {trigger: EJSON.clone(trigger), callback: callback};\n if (! _.has(self.listenersByCollection, collection)) {\n self.listenersByCollection[collection] = {};\n }\n self.listenersByCollection[collection][id] = record;\n\n if (self.factName && Package.facts) {\n Package.facts.Facts.incrementServerFact(\n self.factPackage, self.factName, 1);\n }\n\n return {\n stop: function () {\n if (self.factName && Package.facts) {\n Package.facts.Facts.incrementServerFact(\n self.factPackage, self.factName, -1);\n }\n delete self.listenersByCollection[collection][id];\n if (_.isEmpty(self.listenersByCollection[collection])) {\n delete self.listenersByCollection[collection];\n }\n }\n };\n },\n\n // Fire the provided 'notification' (an object whose attribute\n // values are all JSON-compatibile) -- inform all matching listeners\n // (registered with listen()).\n //\n // If fire() is called inside a write fence, then each of the\n // listener callbacks will be called inside the write fence as well.\n //\n // The listeners may be invoked in parallel, rather than serially.\n fire: function (notification) {\n var self = this;\n\n if (typeof(notification.collection) !== 'string') {\n throw Error(\"Notification lacks collection!\");\n }\n\n if (! _.has(self.listenersByCollection, notification.collection))\n return;\n\n var listenersForCollection =\n self.listenersByCollection[notification.collection];\n var callbackIds = [];\n _.each(listenersForCollection, function (l, id) {\n if (self._matches(notification, l.trigger)) {\n callbackIds.push(id);\n }\n });\n\n // Listener callbacks can yield, so we need to first find all the ones that\n // match in a single iteration over self.listenersByCollection (which can't\n // be mutated during this iteration), and then invoke the matching\n // callbacks, checking before each call to ensure they haven't stopped.\n // Note that we don't have to check that\n // self.listenersByCollection[notification.collection] still ===\n // listenersForCollection, because the only way that stops being true is if\n // listenersForCollection first gets reduced down to the empty object (and\n // then never gets increased again).\n _.each(callbackIds, function (id) {\n if (_.has(listenersForCollection, id)) {\n listenersForCollection[id].callback(notification);\n }\n });\n },\n\n // A notification matches a trigger if all keys that exist in both are equal.\n //\n // Examples:\n // N:{collection: \"C\"} matches T:{collection: \"C\"}\n // (a non-targeted write to a collection matches a\n // non-targeted query)\n // N:{collection: \"C\", id: \"X\"} matches T:{collection: \"C\"}\n // (a targeted write to a collection matches a non-targeted query)\n // N:{collection: \"C\"} matches T:{collection: \"C\", id: \"X\"}\n // (a non-targeted write to a collection matches a\n // targeted query)\n // N:{collection: \"C\", id: \"X\"} matches T:{collection: \"C\", id: \"X\"}\n // (a targeted write to a collection matches a targeted query targeted\n // at the same document)\n // N:{collection: \"C\", id: \"X\"} does not match T:{collection: \"C\", id: \"Y\"}\n // (a targeted write to a collection does not match a targeted query\n // targeted at a different document)\n _matches: function (notification, trigger) {\n // Most notifications that use the crossbar have a string `collection` and\n // maybe an `id` that is a string or ObjectID. We're already dividing up\n // triggers by collection, but let's fast-track \"nope, different ID\" (and\n // avoid the overly generic EJSON.equals). This makes a noticeable\n // performance difference; see https://github.com/meteor/meteor/pull/3697\n if (typeof(notification.id) === 'string' &&\n typeof(trigger.id) === 'string' &&\n notification.id !== trigger.id) {\n return false;\n }\n if (notification.id instanceof LocalCollection._ObjectID &&\n trigger.id instanceof LocalCollection._ObjectID &&\n ! notification.id.equals(trigger.id)) {\n return false;\n }\n\n return _.all(trigger, function (triggerValue, key) {\n return !_.has(notification, key) ||\n EJSON.equals(triggerValue, notification[key]);\n });\n }\n});\n\n// The \"invalidation crossbar\" is a specific instance used by the DDP server to\n// implement write fence notifications. Listener callbacks on this crossbar\n// should call beginWrite on the current write fence before they return, if they\n// want to delay the write fence from firing (ie, the DDP method-data-updated\n// message from being sent).\nDDPServer._InvalidationCrossbar = new DDPServer._Crossbar({\n factName: \"invalidation-crossbar-listeners\"\n});\n","// All the supported versions (for both the client and server)\n// These must be in order of preference; most favored-first\nSUPPORTED_DDP_VERSIONS = [ '1', 'pre2', 'pre1' ];\n\nLivedataTest.SUPPORTED_DDP_VERSIONS = SUPPORTED_DDP_VERSIONS;\n\n// Instance name is this because it is usually referred to as this inside a\n// method definition\n/**\n * @summary The state for a single invocation of a method, referenced by this\n * inside a method definition.\n * @param {Object} options\n * @instanceName this\n */\nMethodInvocation = function (options) {\n var self = this;\n\n // true if we're running not the actual method, but a stub (that is,\n // if we're on a client (which may be a browser, or in the future a\n // server connecting to another server) and presently running a\n // simulation of a server-side method for latency compensation\n // purposes). not currently true except in a client such as a browser,\n // since there's usually no point in running stubs unless you have a\n // zero-latency connection to the user.\n\n /**\n * @summary Access inside a method invocation. Boolean value, true if this invocation is a stub.\n * @locus Anywhere\n * @name isSimulation\n * @memberOf MethodInvocation\n * @instance\n * @type {Boolean}\n */\n this.isSimulation = options.isSimulation;\n\n // call this function to allow other method invocations (from the\n // same client) to continue running without waiting for this one to\n // complete.\n this._unblock = options.unblock || function () {};\n this._calledUnblock = false;\n\n // current user id\n\n /**\n * @summary The id of the user that made this method call, or `null` if no user was logged in.\n * @locus Anywhere\n * @name userId\n * @memberOf MethodInvocation\n * @instance\n */\n this.userId = options.userId;\n\n // sets current user id in all appropriate server contexts and\n // reruns subscriptions\n this._setUserId = options.setUserId || function () {};\n\n // On the server, the connection this method call came in on.\n\n /**\n * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call.\n * @locus Server\n * @name connection\n * @memberOf MethodInvocation\n * @instance\n */\n this.connection = options.connection;\n\n // The seed for randomStream value generation\n this.randomSeed = options.randomSeed;\n\n // This is set by RandomStream.get; and holds the random stream state\n this.randomStream = null;\n};\n\n_.extend(MethodInvocation.prototype, {\n /**\n * @summary Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber.\n * @locus Server\n * @memberOf MethodInvocation\n * @instance\n */\n unblock: function () {\n var self = this;\n self._calledUnblock = true;\n self._unblock();\n },\n\n /**\n * @summary Set the logged in user.\n * @locus Server\n * @memberOf MethodInvocation\n * @instance\n * @param {String | null} userId The value that should be returned by `userId` on this connection.\n */\n setUserId: function(userId) {\n var self = this;\n if (self._calledUnblock)\n throw new Error(\"Can't call setUserId in a method after calling unblock\");\n self.userId = userId;\n self._setUserId(userId);\n }\n});\n\nparseDDP = function (stringMessage) {\n try {\n var msg = JSON.parse(stringMessage);\n } catch (e) {\n Meteor._debug(\"Discarding message with invalid JSON\", stringMessage);\n return null;\n }\n // DDP messages must be objects.\n if (msg === null || typeof msg !== 'object') {\n Meteor._debug(\"Discarding non-object DDP message\", stringMessage);\n return null;\n }\n\n // massage msg to get it into \"abstract ddp\" rather than \"wire ddp\" format.\n\n // switch between \"cleared\" rep of unsetting fields and \"undefined\"\n // rep of same\n if (_.has(msg, 'cleared')) {\n if (!_.has(msg, 'fields'))\n msg.fields = {};\n _.each(msg.cleared, function (clearKey) {\n msg.fields[clearKey] = undefined;\n });\n delete msg.cleared;\n }\n\n _.each(['fields', 'params', 'result'], function (field) {\n if (_.has(msg, field))\n msg[field] = EJSON._adjustTypesFromJSONValue(msg[field]);\n });\n\n return msg;\n};\n\nstringifyDDP = function (msg) {\n var copy = EJSON.clone(msg);\n // swizzle 'changed' messages from 'fields undefined' rep to 'fields\n // and cleared' rep\n if (_.has(msg, 'fields')) {\n var cleared = [];\n _.each(msg.fields, function (value, key) {\n if (value === undefined) {\n cleared.push(key);\n delete copy.fields[key];\n }\n });\n if (!_.isEmpty(cleared))\n copy.cleared = cleared;\n if (_.isEmpty(copy.fields))\n delete copy.fields;\n }\n // adjust types to basic\n _.each(['fields', 'params', 'result'], function (field) {\n if (_.has(copy, field))\n copy[field] = EJSON._adjustTypesToJSONValue(copy[field]);\n });\n if (msg.id && typeof msg.id !== 'string') {\n throw new Error(\"Message id is not a string\");\n }\n return JSON.stringify(copy);\n};\n\n// This is private but it's used in a few places. accounts-base uses\n// it to get the current user. accounts-password uses it to stash SRP\n// state in the DDP session. Meteor.setTimeout and friends clear\n// it. We can probably find a better way to factor this.\nDDP._CurrentInvocation = new Meteor.EnvironmentVariable;\n","// RandomStream allows for generation of pseudo-random values, from a seed.\n//\n// We use this for consistent 'random' numbers across the client and server.\n// We want to generate probably-unique IDs on the client, and we ideally want\n// the server to generate the same IDs when it executes the method.\n//\n// For generated values to be the same, we must seed ourselves the same way,\n// and we must keep track of the current state of our pseudo-random generators.\n// We call this state the scope. By default, we use the current DDP method\n// invocation as our scope. DDP now allows the client to specify a randomSeed.\n// If a randomSeed is provided it will be used to seed our random sequences.\n// In this way, client and server method calls will generate the same values.\n//\n// We expose multiple named streams; each stream is independent\n// and is seeded differently (but predictably from the name).\n// By using multiple streams, we support reordering of requests,\n// as long as they occur on different streams.\n//\n// @param options {Optional Object}\n// seed: Array or value - Seed value(s) for the generator.\n// If an array, will be used as-is\n// If a value, will be converted to a single-value array\n// If omitted, a random array will be used as the seed.\nRandomStream = function (options) {\n var self = this;\n\n this.seed = [].concat(options.seed || randomToken());\n\n this.sequences = {};\n};\n\n// Returns a random string of sufficient length for a random seed.\n// This is a placeholder function; a similar function is planned\n// for Random itself; when that is added we should remove this function,\n// and call Random's randomToken instead.\nfunction randomToken() {\n return Random.hexString(20);\n};\n\n// Returns the random stream with the specified name, in the specified scope.\n// If scope is null (or otherwise falsey) then we will use Random, which will\n// give us as random numbers as possible, but won't produce the same\n// values across client and server.\n// However, scope will normally be the current DDP method invocation, so\n// we'll use the stream with the specified name, and we should get consistent\n// values on the client and server sides of a method call.\nRandomStream.get = function (scope, name) {\n if (!name) {\n name = \"default\";\n }\n if (!scope) {\n // There was no scope passed in;\n // the sequence won't actually be reproducible.\n return Random;\n }\n var randomStream = scope.randomStream;\n if (!randomStream) {\n scope.randomStream = randomStream = new RandomStream({\n seed: scope.randomSeed\n });\n }\n return randomStream._sequence(name);\n};\n\n// Returns the named sequence of pseudo-random values.\n// The scope will be DDP._CurrentInvocation.get(), so the stream will produce\n// consistent values for method calls on the client and server.\nDDP.randomStream = function (name) {\n var scope = DDP._CurrentInvocation.get();\n return RandomStream.get(scope, name);\n};\n\n// Creates a randomSeed for passing to a method call.\n// Note that we take enclosing as an argument,\n// though we expect it to be DDP._CurrentInvocation.get()\n// However, we often evaluate makeRpcSeed lazily, and thus the relevant\n// invocation may not be the one currently in scope.\n// If enclosing is null, we'll use Random and values won't be repeatable.\nmakeRpcSeed = function (enclosing, methodName) {\n var stream = RandomStream.get(enclosing, '/rpc/' + methodName);\n return stream.hexString(20);\n};\n\n_.extend(RandomStream.prototype, {\n // Get a random sequence with the specified name, creating it if does not exist.\n // New sequences are seeded with the seed concatenated with the name.\n // By passing a seed into Random.create, we use the Alea generator.\n _sequence: function (name) {\n var self = this;\n\n var sequence = self.sequences[name] || null;\n if (sequence === null) {\n var sequenceSeed = self.seed.concat(name);\n for (var i = 0; i < sequenceSeed.length; i++) {\n if (_.isFunction(sequenceSeed[i])) {\n sequenceSeed[i] = sequenceSeed[i]();\n }\n }\n self.sequences[name] = sequence = Random.createWithSeeds.apply(null, sequenceSeed);\n }\n return sequence;\n }\n});\n","if (Meteor.isServer) {\n var path = Npm.require('path');\n var Fiber = Npm.require('fibers');\n var Future = Npm.require(path.join('fibers', 'future'));\n}\n\n// @param url {String|Object} URL to Meteor app,\n// or an object as a test hook (see code)\n// Options:\n// reloadWithOutstanding: is it OK to reload if there are outstanding methods?\n// headers: extra headers to send on the websockets connection, for\n// server-to-server DDP only\n// _sockjsOptions: Specifies options to pass through to the sockjs client\n// onDDPNegotiationVersionFailure: callback when version negotiation fails.\n//\n// XXX There should be a way to destroy a DDP connection, causing all\n// outstanding method calls to fail.\n//\n// XXX Our current way of handling failure and reconnection is great\n// for an app (where we want to tolerate being disconnected as an\n// expect state, and keep trying forever to reconnect) but cumbersome\n// for something like a command line tool that wants to make a\n// connection, call a method, and print an error if connection\n// fails. We should have better usability in the latter case (while\n// still transparently reconnecting if it's just a transient failure\n// or the server migrating us).\nvar Connection = function (url, options) {\n var self = this;\n options = _.extend({\n onConnected: function () {},\n onDDPVersionNegotiationFailure: function (description) {\n Meteor._debug(description);\n },\n heartbeatInterval: 35000,\n heartbeatTimeout: 15000,\n // These options are only for testing.\n reloadWithOutstanding: false,\n supportedDDPVersions: SUPPORTED_DDP_VERSIONS,\n retry: true,\n respondToPings: true\n }, options);\n\n // If set, called when we reconnect, queuing method calls _before_ the\n // existing outstanding ones. This is the only data member that is part of the\n // public API!\n self.onReconnect = null;\n\n // as a test hook, allow passing a stream instead of a url.\n if (typeof url === \"object\") {\n self._stream = url;\n } else {\n self._stream = new LivedataTest.ClientStream(url, {\n retry: options.retry,\n headers: options.headers,\n _sockjsOptions: options._sockjsOptions,\n // Used to keep some tests quiet, or for other cases in which\n // the right thing to do with connection errors is to silently\n // fail (e.g. sending package usage stats). At some point we\n // should have a real API for handling client-stream-level\n // errors.\n _dontPrintErrors: options._dontPrintErrors,\n connectTimeoutMs: options.connectTimeoutMs\n });\n }\n\n self._lastSessionId = null;\n self._versionSuggestion = null; // The last proposed DDP version.\n self._version = null; // The DDP version agreed on by client and server.\n self._stores = {}; // name -> object with methods\n self._methodHandlers = {}; // name -> func\n self._nextMethodId = 1;\n self._supportedDDPVersions = options.supportedDDPVersions;\n\n self._heartbeatInterval = options.heartbeatInterval;\n self._heartbeatTimeout = options.heartbeatTimeout;\n\n // Tracks methods which the user has tried to call but which have not yet\n // called their user callback (ie, they are waiting on their result or for all\n // of their writes to be written to the local cache). Map from method ID to\n // MethodInvoker object.\n self._methodInvokers = {};\n\n // Tracks methods which the user has called but whose result messages have not\n // arrived yet.\n //\n // _outstandingMethodBlocks is an array of blocks of methods. Each block\n // represents a set of methods that can run at the same time. The first block\n // represents the methods which are currently in flight; subsequent blocks\n // must wait for previous blocks to be fully finished before they can be sent\n // to the server.\n //\n // Each block is an object with the following fields:\n // - methods: a list of MethodInvoker objects\n // - wait: a boolean; if true, this block had a single method invoked with\n // the \"wait\" option\n //\n // There will never be adjacent blocks with wait=false, because the only thing\n // that makes methods need to be serialized is a wait method.\n //\n // Methods are removed from the first block when their \"result\" is\n // received. The entire first block is only removed when all of the in-flight\n // methods have received their results (so the \"methods\" list is empty) *AND*\n // all of the data written by those methods are visible in the local cache. So\n // it is possible for the first block's methods list to be empty, if we are\n // still waiting for some objects to quiesce.\n //\n // Example:\n // _outstandingMethodBlocks = [\n // {wait: false, methods: []},\n // {wait: true, methods: []},\n // {wait: false, methods: [,\n // ]}]\n // This means that there were some methods which were sent to the server and\n // which have returned their results, but some of the data written by\n // the methods may not be visible in the local cache. Once all that data is\n // visible, we will send a 'login' method. Once the login method has returned\n // and all the data is visible (including re-running subs if userId changes),\n // we will send the 'foo' and 'bar' methods in parallel.\n self._outstandingMethodBlocks = [];\n\n // method ID -> array of objects with keys 'collection' and 'id', listing\n // documents written by a given method's stub. keys are associated with\n // methods whose stub wrote at least one document, and whose data-done message\n // has not yet been received.\n self._documentsWrittenByStub = {};\n // collection -> IdMap of \"server document\" object. A \"server document\" has:\n // - \"document\": the version of the document according the\n // server (ie, the snapshot before a stub wrote it, amended by any changes\n // received from the server)\n // It is undefined if we think the document does not exist\n // - \"writtenByStubs\": a set of method IDs whose stubs wrote to the document\n // whose \"data done\" messages have not yet been processed\n self._serverDocuments = {};\n\n // Array of callbacks to be called after the next update of the local\n // cache. Used for:\n // - Calling methodInvoker.dataVisible and sub ready callbacks after\n // the relevant data is flushed.\n // - Invoking the callbacks of \"half-finished\" methods after reconnect\n // quiescence. Specifically, methods whose result was received over the old\n // connection (so we don't re-send it) but whose data had not been made\n // visible.\n self._afterUpdateCallbacks = [];\n\n // In two contexts, we buffer all incoming data messages and then process them\n // all at once in a single update:\n // - During reconnect, we buffer all data messages until all subs that had\n // been ready before reconnect are ready again, and all methods that are\n // active have returned their \"data done message\"; then\n // - During the execution of a \"wait\" method, we buffer all data messages\n // until the wait method gets its \"data done\" message. (If the wait method\n // occurs during reconnect, it doesn't get any special handling.)\n // all data messages are processed in one update.\n //\n // The following fields are used for this \"quiescence\" process.\n\n // This buffers the messages that aren't being processed yet.\n self._messagesBufferedUntilQuiescence = [];\n // Map from method ID -> true. Methods are removed from this when their\n // \"data done\" message is received, and we will not quiesce until it is\n // empty.\n self._methodsBlockingQuiescence = {};\n // map from sub ID -> true for subs that were ready (ie, called the sub\n // ready callback) before reconnect but haven't become ready again yet\n self._subsBeingRevived = {}; // map from sub._id -> true\n // if true, the next data update should reset all stores. (set during\n // reconnect.)\n self._resetStores = false;\n\n // name -> array of updates for (yet to be created) collections\n self._updatesForUnknownStores = {};\n // if we're blocking a migration, the retry func\n self._retryMigrate = null;\n\n // metadata for subscriptions. Map from sub ID to object with keys:\n // - id\n // - name\n // - params\n // - inactive (if true, will be cleaned up if not reused in re-run)\n // - ready (has the 'ready' message been received?)\n // - readyCallback (an optional callback to call when ready)\n // - errorCallback (an optional callback to call if the sub terminates with\n // an error, XXX COMPAT WITH 1.0.3.1)\n // - stopCallback (an optional callback to call when the sub terminates\n // for any reason, with an error argument if an error triggered the stop)\n self._subscriptions = {};\n\n // Reactive userId.\n self._userId = null;\n self._userIdDeps = new Tracker.Dependency;\n\n // Block auto-reload while we're waiting for method responses.\n if (Meteor.isClient && Package.reload && !options.reloadWithOutstanding) {\n Package.reload.Reload._onMigrate(function (retry) {\n if (!self._readyToMigrate()) {\n if (self._retryMigrate)\n throw new Error(\"Two migrations in progress?\");\n self._retryMigrate = retry;\n return false;\n } else {\n return [true];\n }\n });\n }\n\n var onMessage = function (raw_msg) {\n try {\n var msg = parseDDP(raw_msg);\n } catch (e) {\n Meteor._debug(\"Exception while parsing DDP\", e);\n return;\n }\n\n if (msg === null || !msg.msg) {\n // XXX COMPAT WITH 0.6.6. ignore the old welcome message for back\n // compat. Remove this 'if' once the server stops sending welcome\n // messages (stream_server.js).\n if (! (msg && msg.server_id))\n Meteor._debug(\"discarding invalid livedata message\", msg);\n return;\n }\n\n if (msg.msg === 'connected') {\n self._version = self._versionSuggestion;\n self._livedata_connected(msg);\n options.onConnected();\n }\n else if (msg.msg == 'failed') {\n if (_.contains(self._supportedDDPVersions, msg.version)) {\n self._versionSuggestion = msg.version;\n self._stream.reconnect({_force: true});\n } else {\n var description =\n \"DDP version negotiation failed; server requested version \" + msg.version;\n self._stream.disconnect({_permanent: true, _error: description});\n options.onDDPVersionNegotiationFailure(description);\n }\n }\n else if (msg.msg === 'ping') {\n if (options.respondToPings)\n self._send({msg: \"pong\", id: msg.id});\n if (self._heartbeat)\n self._heartbeat.pingReceived();\n }\n else if (msg.msg === 'pong') {\n if (self._heartbeat) {\n self._heartbeat.pongReceived();\n }\n }\n else if (_.include(['added', 'changed', 'removed', 'ready', 'updated'], msg.msg))\n self._livedata_data(msg);\n else if (msg.msg === 'nosub')\n self._livedata_nosub(msg);\n else if (msg.msg === 'result')\n self._livedata_result(msg);\n else if (msg.msg === 'error')\n self._livedata_error(msg);\n else\n Meteor._debug(\"discarding unknown livedata message type\", msg);\n };\n\n var onReset = function () {\n // Send a connect message at the beginning of the stream.\n // NOTE: reset is called even on the first connection, so this is\n // the only place we send this message.\n var msg = {msg: 'connect'};\n if (self._lastSessionId)\n msg.session = self._lastSessionId;\n msg.version = self._versionSuggestion || self._supportedDDPVersions[0];\n self._versionSuggestion = msg.version;\n msg.support = self._supportedDDPVersions;\n self._send(msg);\n\n // Now, to minimize setup latency, go ahead and blast out all of\n // our pending methods ands subscriptions before we've even taken\n // the necessary RTT to know if we successfully reconnected. (1)\n // They're supposed to be idempotent; (2) even if we did\n // reconnect, we're not sure what messages might have gotten lost\n // (in either direction) since we were disconnected (TCP being\n // sloppy about that.)\n\n // If the current block of methods all got their results (but didn't all get\n // their data visible), discard the empty block now.\n if (! _.isEmpty(self._outstandingMethodBlocks) &&\n _.isEmpty(self._outstandingMethodBlocks[0].methods)) {\n self._outstandingMethodBlocks.shift();\n }\n\n // Mark all messages as unsent, they have not yet been sent on this\n // connection.\n _.each(self._methodInvokers, function (m) {\n m.sentMessage = false;\n });\n\n // If an `onReconnect` handler is set, call it first. Go through\n // some hoops to ensure that methods that are called from within\n // `onReconnect` get executed _before_ ones that were originally\n // outstanding (since `onReconnect` is used to re-establish auth\n // certificates)\n if (self.onReconnect)\n self._callOnReconnectAndSendAppropriateOutstandingMethods();\n else\n self._sendOutstandingMethods();\n\n // add new subscriptions at the end. this way they take effect after\n // the handlers and we don't see flicker.\n _.each(self._subscriptions, function (sub, id) {\n self._send({\n msg: 'sub',\n id: id,\n name: sub.name,\n params: sub.params\n });\n });\n };\n\n var onDisconnect = function () {\n if (self._heartbeat) {\n self._heartbeat.stop();\n self._heartbeat = null;\n }\n };\n\n if (Meteor.isServer) {\n self._stream.on('message', Meteor.bindEnvironment(onMessage, Meteor._debug));\n self._stream.on('reset', Meteor.bindEnvironment(onReset, Meteor._debug));\n self._stream.on('disconnect', Meteor.bindEnvironment(onDisconnect, Meteor._debug));\n } else {\n self._stream.on('message', onMessage);\n self._stream.on('reset', onReset);\n self._stream.on('disconnect', onDisconnect);\n }\n};\n\n// A MethodInvoker manages sending a method to the server and calling the user's\n// callbacks. On construction, it registers itself in the connection's\n// _methodInvokers map; it removes itself once the method is fully finished and\n// the callback is invoked. This occurs when it has both received a result,\n// and the data written by it is fully visible.\nvar MethodInvoker = function (options) {\n var self = this;\n\n // Public (within this file) fields.\n self.methodId = options.methodId;\n self.sentMessage = false;\n\n self._callback = options.callback;\n self._connection = options.connection;\n self._message = options.message;\n self._onResultReceived = options.onResultReceived || function () {};\n self._wait = options.wait;\n self._methodResult = null;\n self._dataVisible = false;\n\n // Register with the connection.\n self._connection._methodInvokers[self.methodId] = self;\n};\n_.extend(MethodInvoker.prototype, {\n // Sends the method message to the server. May be called additional times if\n // we lose the connection and reconnect before receiving a result.\n sendMessage: function () {\n var self = this;\n // This function is called before sending a method (including resending on\n // reconnect). We should only (re)send methods where we don't already have a\n // result!\n if (self.gotResult())\n throw new Error(\"sendingMethod is called on method with result\");\n\n // If we're re-sending it, it doesn't matter if data was written the first\n // time.\n self._dataVisible = false;\n\n self.sentMessage = true;\n\n // If this is a wait method, make all data messages be buffered until it is\n // done.\n if (self._wait)\n self._connection._methodsBlockingQuiescence[self.methodId] = true;\n\n // Actually send the message.\n self._connection._send(self._message);\n },\n // Invoke the callback, if we have both a result and know that all data has\n // been written to the local cache.\n _maybeInvokeCallback: function () {\n var self = this;\n if (self._methodResult && self._dataVisible) {\n // Call the callback. (This won't throw: the callback was wrapped with\n // bindEnvironment.)\n self._callback(self._methodResult[0], self._methodResult[1]);\n\n // Forget about this method.\n delete self._connection._methodInvokers[self.methodId];\n\n // Let the connection know that this method is finished, so it can try to\n // move on to the next block of methods.\n self._connection._outstandingMethodFinished();\n }\n },\n // Call with the result of the method from the server. Only may be called\n // once; once it is called, you should not call sendMessage again.\n // If the user provided an onResultReceived callback, call it immediately.\n // Then invoke the main callback if data is also visible.\n receiveResult: function (err, result) {\n var self = this;\n if (self.gotResult())\n throw new Error(\"Methods should only receive results once\");\n self._methodResult = [err, result];\n self._onResultReceived(err, result);\n self._maybeInvokeCallback();\n },\n // Call this when all data written by the method is visible. This means that\n // the method has returns its \"data is done\" message *AND* all server\n // documents that are buffered at that time have been written to the local\n // cache. Invokes the main callback if the result has been received.\n dataVisible: function () {\n var self = this;\n self._dataVisible = true;\n self._maybeInvokeCallback();\n },\n // True if receiveResult has been called.\n gotResult: function () {\n var self = this;\n return !!self._methodResult;\n }\n});\n\n_.extend(Connection.prototype, {\n // 'name' is the name of the data on the wire that should go in the\n // store. 'wrappedStore' should be an object with methods beginUpdate, update,\n // endUpdate, saveOriginals, retrieveOriginals. see Collection for an example.\n registerStore: function (name, wrappedStore) {\n var self = this;\n\n if (name in self._stores)\n return false;\n\n // Wrap the input object in an object which makes any store method not\n // implemented by 'store' into a no-op.\n var store = {};\n _.each(['update', 'beginUpdate', 'endUpdate', 'saveOriginals',\n 'retrieveOriginals'], function (method) {\n store[method] = function () {\n return (wrappedStore[method]\n ? wrappedStore[method].apply(wrappedStore, arguments)\n : undefined);\n };\n });\n\n self._stores[name] = store;\n\n var queued = self._updatesForUnknownStores[name];\n if (queued) {\n store.beginUpdate(queued.length, false);\n _.each(queued, function (msg) {\n store.update(msg);\n });\n store.endUpdate();\n delete self._updatesForUnknownStores[name];\n }\n\n return true;\n },\n\n /**\n * @memberOf Meteor\n * @summary Subscribe to a record set. Returns a handle that provides\n * `stop()` and `ready()` methods.\n * @locus Client\n * @param {String} name Name of the subscription. Matches the name of the\n * server's `publish()` call.\n * @param {Any} [arg1,arg2...] Optional arguments passed to publisher\n * function on server.\n * @param {Function|Object} [callbacks] Optional. May include `onStop`\n * and `onReady` callbacks. If there is an error, it is passed as an\n * argument to `onStop`. If a function is passed instead of an object, it\n * is interpreted as an `onReady` callback.\n */\n subscribe: function (name /* .. [arguments] .. (callback|callbacks) */) {\n var self = this;\n\n var params = Array.prototype.slice.call(arguments, 1);\n var callbacks = {};\n if (params.length) {\n var lastParam = params[params.length - 1];\n if (_.isFunction(lastParam)) {\n callbacks.onReady = params.pop();\n } else if (lastParam &&\n // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use\n // onStop with an error callback instead.\n _.any([lastParam.onReady, lastParam.onError, lastParam.onStop],\n _.isFunction)) {\n callbacks = params.pop();\n }\n }\n\n // Is there an existing sub with the same name and param, run in an\n // invalidated Computation? This will happen if we are rerunning an\n // existing computation.\n //\n // For example, consider a rerun of:\n //\n // Tracker.autorun(function () {\n // Meteor.subscribe(\"foo\", Session.get(\"foo\"));\n // Meteor.subscribe(\"bar\", Session.get(\"bar\"));\n // });\n //\n // If \"foo\" has changed but \"bar\" has not, we will match the \"bar\"\n // subcribe to an existing inactive subscription in order to not\n // unsub and resub the subscription unnecessarily.\n //\n // We only look for one such sub; if there are N apparently-identical subs\n // being invalidated, we will require N matching subscribe calls to keep\n // them all active.\n var existing = _.find(self._subscriptions, function (sub) {\n return sub.inactive && sub.name === name &&\n EJSON.equals(sub.params, params);\n });\n\n var id;\n if (existing) {\n id = existing.id;\n existing.inactive = false; // reactivate\n\n if (callbacks.onReady) {\n // If the sub is not already ready, replace any ready callback with the\n // one provided now. (It's not really clear what users would expect for\n // an onReady callback inside an autorun; the semantics we provide is\n // that at the time the sub first becomes ready, we call the last\n // onReady callback provided, if any.)\n if (!existing.ready)\n existing.readyCallback = callbacks.onReady;\n }\n\n // XXX COMPAT WITH 1.0.3.1 we used to have onError but now we call\n // onStop with an optional error argument\n if (callbacks.onError) {\n // Replace existing callback if any, so that errors aren't\n // double-reported.\n existing.errorCallback = callbacks.onError;\n }\n\n if (callbacks.onStop) {\n existing.stopCallback = callbacks.onStop;\n }\n } else {\n // New sub! Generate an id, save it locally, and send message.\n id = Random.id();\n self._subscriptions[id] = {\n id: id,\n name: name,\n params: EJSON.clone(params),\n inactive: false,\n ready: false,\n readyDeps: new Tracker.Dependency,\n readyCallback: callbacks.onReady,\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n errorCallback: callbacks.onError,\n stopCallback: callbacks.onStop,\n connection: self,\n remove: function() {\n delete this.connection._subscriptions[this.id];\n this.ready && this.readyDeps.changed();\n },\n stop: function() {\n this.connection._send({msg: 'unsub', id: id});\n this.remove();\n\n if (callbacks.onStop) {\n callbacks.onStop();\n }\n }\n };\n self._send({msg: 'sub', id: id, name: name, params: params});\n }\n\n // return a handle to the application.\n var handle = {\n stop: function () {\n if (!_.has(self._subscriptions, id))\n return;\n\n self._subscriptions[id].stop();\n },\n ready: function () {\n // return false if we've unsubscribed.\n if (!_.has(self._subscriptions, id))\n return false;\n var record = self._subscriptions[id];\n record.readyDeps.depend();\n return record.ready;\n },\n subscriptionId: id\n };\n\n if (Tracker.active) {\n // We're in a reactive computation, so we'd like to unsubscribe when the\n // computation is invalidated... but not if the rerun just re-subscribes\n // to the same subscription! When a rerun happens, we use onInvalidate\n // as a change to mark the subscription \"inactive\" so that it can\n // be reused from the rerun. If it isn't reused, it's killed from\n // an afterFlush.\n Tracker.onInvalidate(function (c) {\n if (_.has(self._subscriptions, id))\n self._subscriptions[id].inactive = true;\n\n Tracker.afterFlush(function () {\n if (_.has(self._subscriptions, id) &&\n self._subscriptions[id].inactive)\n handle.stop();\n });\n });\n }\n\n return handle;\n },\n\n // options:\n // - onLateError {Function(error)} called if an error was received after the ready event.\n // (errors received before ready cause an error to be thrown)\n _subscribeAndWait: function (name, args, options) {\n var self = this;\n var f = new Future();\n var ready = false;\n var handle;\n args = args || [];\n args.push({\n onReady: function () {\n ready = true;\n f['return']();\n },\n onError: function (e) {\n if (!ready)\n f['throw'](e);\n else\n options && options.onLateError && options.onLateError(e);\n }\n });\n\n handle = self.subscribe.apply(self, [name].concat(args));\n f.wait();\n return handle;\n },\n\n methods: function (methods) {\n var self = this;\n _.each(methods, function (func, name) {\n if (self._methodHandlers[name])\n throw new Error(\"A method named '\" + name + \"' is already defined\");\n self._methodHandlers[name] = func;\n });\n },\n\n /**\n * @memberOf Meteor\n * @summary Invokes a method passing any number of arguments.\n * @locus Anywhere\n * @param {String} name Name of method to invoke\n * @param {EJSONable} [arg1,arg2...] Optional method arguments\n * @param {Function} [asyncCallback] Optional callback, which is called asynchronously with the error or result after the method is complete. If not provided, the method runs synchronously if possible (see below).\n */\n call: function (name /* .. [arguments] .. callback */) {\n // if it's a function, the last argument is the result callback,\n // not a parameter to the remote method.\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length && typeof args[args.length - 1] === \"function\")\n var callback = args.pop();\n return this.apply(name, args, callback);\n },\n\n // @param options {Optional Object}\n // wait: Boolean - Should we wait to call this until all current methods\n // are fully finished, and block subsequent method calls\n // until this method is fully finished?\n // (does not affect methods called from within this method)\n // onResultReceived: Function - a callback to call as soon as the method\n // result is received. the data written by\n // the method may not yet be in the cache!\n // returnStubValue: Boolean - If true then in cases where we would have\n // otherwise discarded the stub's return value\n // and returned undefined, instead we go ahead\n // and return it. Specifically, this is any\n // time other than when (a) we are already\n // inside a stub or (b) we are in Node and no\n // callback was provided. Currently we require\n // this flag to be explicitly passed to reduce\n // the likelihood that stub return values will\n // be confused with server return values; we\n // may improve this in future.\n // @param callback {Optional Function}\n\n /**\n * @memberOf Meteor\n * @summary Invoke a method passing an array of arguments.\n * @locus Anywhere\n * @param {String} name Name of method to invoke\n * @param {EJSONable[]} args Method arguments\n * @param {Object} [options]\n * @param {Boolean} options.wait (Client only) If true, don't send this method until all previous method calls have completed, and don't send any subsequent method calls until this one is completed.\n * @param {Function} options.onResultReceived (Client only) This callback is invoked with the error or result of the method (just like `asyncCallback`) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.\n * @param {Function} [asyncCallback] Optional callback; same semantics as in [`Meteor.call`](#meteor_call).\n */\n apply: function (name, args, options, callback) {\n var self = this;\n\n // We were passed 3 arguments. They may be either (name, args, options)\n // or (name, args, callback)\n if (!callback && typeof options === 'function') {\n callback = options;\n options = {};\n }\n options = options || {};\n\n if (callback) {\n // XXX would it be better form to do the binding in stream.on,\n // or caller, instead of here?\n // XXX improve error message (and how we report it)\n callback = Meteor.bindEnvironment(\n callback,\n \"delivering result of invoking '\" + name + \"'\"\n );\n }\n\n // Keep our args safe from mutation (eg if we don't send the message for a\n // while because of a wait method).\n args = EJSON.clone(args);\n\n // Lazily allocate method ID once we know that it'll be needed.\n var methodId = (function () {\n var id;\n return function () {\n if (id === undefined)\n id = '' + (self._nextMethodId++);\n return id;\n };\n })();\n\n var enclosing = DDP._CurrentInvocation.get();\n var alreadyInSimulation = enclosing && enclosing.isSimulation;\n\n // Lazily generate a randomSeed, only if it is requested by the stub.\n // The random streams only have utility if they're used on both the client\n // and the server; if the client doesn't generate any 'random' values\n // then we don't expect the server to generate any either.\n // Less commonly, the server may perform different actions from the client,\n // and may in fact generate values where the client did not, but we don't\n // have any client-side values to match, so even here we may as well just\n // use a random seed on the server. In that case, we don't pass the\n // randomSeed to save bandwidth, and we don't even generate it to save a\n // bit of CPU and to avoid consuming entropy.\n var randomSeed = null;\n var randomSeedGenerator = function () {\n if (randomSeed === null) {\n randomSeed = makeRpcSeed(enclosing, name);\n }\n return randomSeed;\n };\n\n // Run the stub, if we have one. The stub is supposed to make some\n // temporary writes to the database to give the user a smooth experience\n // until the actual result of executing the method comes back from the\n // server (whereupon the temporary writes to the database will be reversed\n // during the beginUpdate/endUpdate process.)\n //\n // Normally, we ignore the return value of the stub (even if it is an\n // exception), in favor of the real return value from the server. The\n // exception is if the *caller* is a stub. In that case, we're not going\n // to do a RPC, so we use the return value of the stub as our return\n // value.\n\n var stub = self._methodHandlers[name];\n if (stub) {\n var setUserId = function(userId) {\n self.setUserId(userId);\n };\n\n var invocation = new MethodInvocation({\n isSimulation: true,\n userId: self.userId(),\n setUserId: setUserId,\n randomSeed: function () { return randomSeedGenerator(); }\n });\n\n if (!alreadyInSimulation)\n self._saveOriginals();\n\n try {\n // Note that unlike in the corresponding server code, we never audit\n // that stubs check() their arguments.\n var stubReturnValue = DDP._CurrentInvocation.withValue(invocation, function () {\n if (Meteor.isServer) {\n // Because saveOriginals and retrieveOriginals aren't reentrant,\n // don't allow stubs to yield.\n return Meteor._noYieldsAllowed(function () {\n // re-clone, so that the stub can't affect our caller's values\n return stub.apply(invocation, EJSON.clone(args));\n });\n } else {\n return stub.apply(invocation, EJSON.clone(args));\n }\n });\n }\n catch (e) {\n var exception = e;\n }\n\n if (!alreadyInSimulation)\n self._retrieveAndStoreOriginals(methodId());\n }\n\n // If we're in a simulation, stop and return the result we have,\n // rather than going on to do an RPC. If there was no stub,\n // we'll end up returning undefined.\n if (alreadyInSimulation) {\n if (callback) {\n callback(exception, stubReturnValue);\n return undefined;\n }\n if (exception)\n throw exception;\n return stubReturnValue;\n }\n\n // If an exception occurred in a stub, and we're ignoring it\n // because we're doing an RPC and want to use what the server\n // returns instead, log it so the developer knows.\n //\n // Tests can set the 'expected' flag on an exception so it won't\n // go to log.\n if (exception && !exception.expected) {\n Meteor._debug(\"Exception while simulating the effect of invoking '\" +\n name + \"'\", exception, exception.stack);\n }\n\n\n // At this point we're definitely doing an RPC, and we're going to\n // return the value of the RPC to the caller.\n\n // If the caller didn't give a callback, decide what to do.\n if (!callback) {\n if (Meteor.isClient) {\n // On the client, we don't have fibers, so we can't block. The\n // only thing we can do is to return undefined and discard the\n // result of the RPC. If an error occurred then print the error\n // to the console.\n callback = function (err) {\n err && Meteor._debug(\"Error invoking Method '\" + name + \"':\",\n err.message);\n };\n } else {\n // On the server, make the function synchronous. Throw on\n // errors, return on success.\n var future = new Future;\n callback = future.resolver();\n }\n }\n // Send the RPC. Note that on the client, it is important that the\n // stub have finished before we send the RPC, so that we know we have\n // a complete list of which local documents the stub wrote.\n var message = {\n msg: 'method',\n method: name,\n params: args,\n id: methodId()\n };\n\n // Send the randomSeed only if we used it\n if (randomSeed !== null) {\n message.randomSeed = randomSeed;\n }\n\n var methodInvoker = new MethodInvoker({\n methodId: methodId(),\n callback: callback,\n connection: self,\n onResultReceived: options.onResultReceived,\n wait: !!options.wait,\n message: message\n });\n\n if (options.wait) {\n // It's a wait method! Wait methods go in their own block.\n self._outstandingMethodBlocks.push(\n {wait: true, methods: [methodInvoker]});\n } else {\n // Not a wait method. Start a new block if the previous block was a wait\n // block, and add it to the last block of methods.\n if (_.isEmpty(self._outstandingMethodBlocks) ||\n _.last(self._outstandingMethodBlocks).wait)\n self._outstandingMethodBlocks.push({wait: false, methods: []});\n _.last(self._outstandingMethodBlocks).methods.push(methodInvoker);\n }\n\n // If we added it to the first block, send it out now.\n if (self._outstandingMethodBlocks.length === 1)\n methodInvoker.sendMessage();\n\n // If we're using the default callback on the server,\n // block waiting for the result.\n if (future) {\n return future.wait();\n }\n return options.returnStubValue ? stubReturnValue : undefined;\n },\n\n // Before calling a method stub, prepare all stores to track changes and allow\n // _retrieveAndStoreOriginals to get the original versions of changed\n // documents.\n _saveOriginals: function () {\n var self = this;\n _.each(self._stores, function (s) {\n s.saveOriginals();\n });\n },\n // Retrieves the original versions of all documents modified by the stub for\n // method 'methodId' from all stores and saves them to _serverDocuments (keyed\n // by document) and _documentsWrittenByStub (keyed by method ID).\n _retrieveAndStoreOriginals: function (methodId) {\n var self = this;\n if (self._documentsWrittenByStub[methodId])\n throw new Error(\"Duplicate methodId in _retrieveAndStoreOriginals\");\n\n var docsWritten = [];\n _.each(self._stores, function (s, collection) {\n var originals = s.retrieveOriginals();\n // not all stores define retrieveOriginals\n if (!originals)\n return;\n originals.forEach(function (doc, id) {\n docsWritten.push({collection: collection, id: id});\n if (!_.has(self._serverDocuments, collection))\n self._serverDocuments[collection] = new LocalCollection._IdMap;\n var serverDoc = self._serverDocuments[collection].setDefault(id, {});\n if (serverDoc.writtenByStubs) {\n // We're not the first stub to write this doc. Just add our method ID\n // to the record.\n serverDoc.writtenByStubs[methodId] = true;\n } else {\n // First stub! Save the original value and our method ID.\n serverDoc.document = doc;\n serverDoc.flushCallbacks = [];\n serverDoc.writtenByStubs = {};\n serverDoc.writtenByStubs[methodId] = true;\n }\n });\n });\n if (!_.isEmpty(docsWritten)) {\n self._documentsWrittenByStub[methodId] = docsWritten;\n }\n },\n\n // This is very much a private function we use to make the tests\n // take up fewer server resources after they complete.\n _unsubscribeAll: function () {\n var self = this;\n _.each(_.clone(self._subscriptions), function (sub, id) {\n // Avoid killing the autoupdate subscription so that developers\n // still get hot code pushes when writing tests.\n //\n // XXX it's a hack to encode knowledge about autoupdate here,\n // but it doesn't seem worth it yet to have a special API for\n // subscriptions to preserve after unit tests.\n if (sub.name !== 'meteor_autoupdate_clientVersions') {\n self._subscriptions[id].stop();\n }\n });\n },\n\n // Sends the DDP stringification of the given message object\n _send: function (obj) {\n var self = this;\n self._stream.send(stringifyDDP(obj));\n },\n\n // We detected via DDP-level heartbeats that we've lost the\n // connection. Unlike `disconnect` or `close`, a lost connection\n // will be automatically retried.\n _lostConnection: function (error) {\n var self = this;\n self._stream._lostConnection(error);\n },\n\n /**\n * @summary Get the current connection status. A reactive data source.\n * @locus Client\n * @memberOf Meteor\n */\n status: function (/*passthrough args*/) {\n var self = this;\n return self._stream.status.apply(self._stream, arguments);\n },\n\n /**\n * @summary Force an immediate reconnection attempt if the client is not connected to the server.\n\n This method does nothing if the client is already connected.\n * @locus Client\n * @memberOf Meteor\n */\n reconnect: function (/*passthrough args*/) {\n var self = this;\n return self._stream.reconnect.apply(self._stream, arguments);\n },\n\n /**\n * @summary Disconnect the client from the server.\n * @locus Client\n * @memberOf Meteor\n */\n disconnect: function (/*passthrough args*/) {\n var self = this;\n return self._stream.disconnect.apply(self._stream, arguments);\n },\n\n close: function () {\n var self = this;\n return self._stream.disconnect({_permanent: true});\n },\n\n ///\n /// Reactive user system\n ///\n userId: function () {\n var self = this;\n if (self._userIdDeps)\n self._userIdDeps.depend();\n return self._userId;\n },\n\n setUserId: function (userId) {\n var self = this;\n // Avoid invalidating dependents if setUserId is called with current value.\n if (self._userId === userId)\n return;\n self._userId = userId;\n if (self._userIdDeps)\n self._userIdDeps.changed();\n },\n\n // Returns true if we are in a state after reconnect of waiting for subs to be\n // revived or early methods to finish their data, or we are waiting for a\n // \"wait\" method to finish.\n _waitingForQuiescence: function () {\n var self = this;\n return (! _.isEmpty(self._subsBeingRevived) ||\n ! _.isEmpty(self._methodsBlockingQuiescence));\n },\n\n // Returns true if any method whose message has been sent to the server has\n // not yet invoked its user callback.\n _anyMethodsAreOutstanding: function () {\n var self = this;\n return _.any(_.pluck(self._methodInvokers, 'sentMessage'));\n },\n\n _livedata_connected: function (msg) {\n var self = this;\n\n if (self._version !== 'pre1' && self._heartbeatInterval !== 0) {\n self._heartbeat = new Heartbeat({\n heartbeatInterval: self._heartbeatInterval,\n heartbeatTimeout: self._heartbeatTimeout,\n onTimeout: function () {\n self._lostConnection(\n new DDP.ConnectionError(\"DDP heartbeat timed out\"));\n },\n sendPing: function () {\n self._send({msg: 'ping'});\n }\n });\n self._heartbeat.start();\n }\n\n // If this is a reconnect, we'll have to reset all stores.\n if (self._lastSessionId)\n self._resetStores = true;\n\n if (typeof (msg.session) === \"string\") {\n var reconnectedToPreviousSession = (self._lastSessionId === msg.session);\n self._lastSessionId = msg.session;\n }\n\n if (reconnectedToPreviousSession) {\n // Successful reconnection -- pick up where we left off. Note that right\n // now, this never happens: the server never connects us to a previous\n // session, because DDP doesn't provide enough data for the server to know\n // what messages the client has processed. We need to improve DDP to make\n // this possible, at which point we'll probably need more code here.\n return;\n }\n\n // Server doesn't have our data any more. Re-sync a new session.\n\n // Forget about messages we were buffering for unknown collections. They'll\n // be resent if still relevant.\n self._updatesForUnknownStores = {};\n\n if (self._resetStores) {\n // Forget about the effects of stubs. We'll be resetting all collections\n // anyway.\n self._documentsWrittenByStub = {};\n self._serverDocuments = {};\n }\n\n // Clear _afterUpdateCallbacks.\n self._afterUpdateCallbacks = [];\n\n // Mark all named subscriptions which are ready (ie, we already called the\n // ready callback) as needing to be revived.\n // XXX We should also block reconnect quiescence until unnamed subscriptions\n // (eg, autopublish) are done re-publishing to avoid flicker!\n self._subsBeingRevived = {};\n _.each(self._subscriptions, function (sub, id) {\n if (sub.ready)\n self._subsBeingRevived[id] = true;\n });\n\n // Arrange for \"half-finished\" methods to have their callbacks run, and\n // track methods that were sent on this connection so that we don't\n // quiesce until they are all done.\n //\n // Start by clearing _methodsBlockingQuiescence: methods sent before\n // reconnect don't matter, and any \"wait\" methods sent on the new connection\n // that we drop here will be restored by the loop below.\n self._methodsBlockingQuiescence = {};\n if (self._resetStores) {\n _.each(self._methodInvokers, function (invoker) {\n if (invoker.gotResult()) {\n // This method already got its result, but it didn't call its callback\n // because its data didn't become visible. We did not resend the\n // method RPC. We'll call its callback when we get a full quiesce,\n // since that's as close as we'll get to \"data must be visible\".\n self._afterUpdateCallbacks.push(_.bind(invoker.dataVisible, invoker));\n } else if (invoker.sentMessage) {\n // This method has been sent on this connection (maybe as a resend\n // from the last connection, maybe from onReconnect, maybe just very\n // quickly before processing the connected message).\n //\n // We don't need to do anything special to ensure its callbacks get\n // called, but we'll count it as a method which is preventing\n // reconnect quiescence. (eg, it might be a login method that was run\n // from onReconnect, and we don't want to see flicker by seeing a\n // logged-out state.)\n self._methodsBlockingQuiescence[invoker.methodId] = true;\n }\n });\n }\n\n self._messagesBufferedUntilQuiescence = [];\n\n // If we're not waiting on any methods or subs, we can reset the stores and\n // call the callbacks immediately.\n if (!self._waitingForQuiescence()) {\n if (self._resetStores) {\n _.each(self._stores, function (s) {\n s.beginUpdate(0, true);\n s.endUpdate();\n });\n self._resetStores = false;\n }\n self._runAfterUpdateCallbacks();\n }\n },\n\n\n _processOneDataMessage: function (msg, updates) {\n var self = this;\n // Using underscore here so as not to need to capitalize.\n self['_process_' + msg.msg](msg, updates);\n },\n\n\n _livedata_data: function (msg) {\n var self = this;\n\n // collection name -> array of messages\n var updates = {};\n\n if (self._waitingForQuiescence()) {\n self._messagesBufferedUntilQuiescence.push(msg);\n\n if (msg.msg === \"nosub\")\n delete self._subsBeingRevived[msg.id];\n\n _.each(msg.subs || [], function (subId) {\n delete self._subsBeingRevived[subId];\n });\n _.each(msg.methods || [], function (methodId) {\n delete self._methodsBlockingQuiescence[methodId];\n });\n\n if (self._waitingForQuiescence())\n return;\n\n // No methods or subs are blocking quiescence!\n // We'll now process and all of our buffered messages, reset all stores,\n // and apply them all at once.\n _.each(self._messagesBufferedUntilQuiescence, function (bufferedMsg) {\n self._processOneDataMessage(bufferedMsg, updates);\n });\n self._messagesBufferedUntilQuiescence = [];\n } else {\n self._processOneDataMessage(msg, updates);\n }\n\n if (self._resetStores || !_.isEmpty(updates)) {\n // Begin a transactional update of each store.\n _.each(self._stores, function (s, storeName) {\n s.beginUpdate(_.has(updates, storeName) ? updates[storeName].length : 0,\n self._resetStores);\n });\n self._resetStores = false;\n\n _.each(updates, function (updateMessages, storeName) {\n var store = self._stores[storeName];\n if (store) {\n _.each(updateMessages, function (updateMessage) {\n store.update(updateMessage);\n });\n } else {\n // Nobody's listening for this data. Queue it up until\n // someone wants it.\n // XXX memory use will grow without bound if you forget to\n // create a collection or just don't care about it... going\n // to have to do something about that.\n if (!_.has(self._updatesForUnknownStores, storeName))\n self._updatesForUnknownStores[storeName] = [];\n Array.prototype.push.apply(self._updatesForUnknownStores[storeName],\n updateMessages);\n }\n });\n\n // End update transaction.\n _.each(self._stores, function (s) { s.endUpdate(); });\n }\n\n self._runAfterUpdateCallbacks();\n },\n\n // Call any callbacks deferred with _runWhenAllServerDocsAreFlushed whose\n // relevant docs have been flushed, as well as dataVisible callbacks at\n // reconnect-quiescence time.\n _runAfterUpdateCallbacks: function () {\n var self = this;\n var callbacks = self._afterUpdateCallbacks;\n self._afterUpdateCallbacks = [];\n _.each(callbacks, function (c) {\n c();\n });\n },\n\n _pushUpdate: function (updates, collection, msg) {\n var self = this;\n if (!_.has(updates, collection)) {\n updates[collection] = [];\n }\n updates[collection].push(msg);\n },\n\n _getServerDoc: function (collection, id) {\n var self = this;\n if (!_.has(self._serverDocuments, collection))\n return null;\n var serverDocsForCollection = self._serverDocuments[collection];\n return serverDocsForCollection.get(id) || null;\n },\n\n _process_added: function (msg, updates) {\n var self = this;\n var id = LocalCollection._idParse(msg.id);\n var serverDoc = self._getServerDoc(msg.collection, id);\n if (serverDoc) {\n // Some outstanding stub wrote here.\n if (serverDoc.document !== undefined)\n throw new Error(\"Server sent add for existing id: \" + msg.id);\n serverDoc.document = msg.fields || {};\n serverDoc.document._id = id;\n } else {\n self._pushUpdate(updates, msg.collection, msg);\n }\n },\n\n _process_changed: function (msg, updates) {\n var self = this;\n var serverDoc = self._getServerDoc(\n msg.collection, LocalCollection._idParse(msg.id));\n if (serverDoc) {\n if (serverDoc.document === undefined)\n throw new Error(\"Server sent changed for nonexisting id: \" + msg.id);\n LocalCollection._applyChanges(serverDoc.document, msg.fields);\n } else {\n self._pushUpdate(updates, msg.collection, msg);\n }\n },\n\n _process_removed: function (msg, updates) {\n var self = this;\n var serverDoc = self._getServerDoc(\n msg.collection, LocalCollection._idParse(msg.id));\n if (serverDoc) {\n // Some outstanding stub wrote here.\n if (serverDoc.document === undefined)\n throw new Error(\"Server sent removed for nonexisting id:\" + msg.id);\n serverDoc.document = undefined;\n } else {\n self._pushUpdate(updates, msg.collection, {\n msg: 'removed',\n collection: msg.collection,\n id: msg.id\n });\n }\n },\n\n _process_updated: function (msg, updates) {\n var self = this;\n // Process \"method done\" messages.\n _.each(msg.methods, function (methodId) {\n _.each(self._documentsWrittenByStub[methodId], function (written) {\n var serverDoc = self._getServerDoc(written.collection, written.id);\n if (!serverDoc)\n throw new Error(\"Lost serverDoc for \" + JSON.stringify(written));\n if (!serverDoc.writtenByStubs[methodId])\n throw new Error(\"Doc \" + JSON.stringify(written) +\n \" not written by method \" + methodId);\n delete serverDoc.writtenByStubs[methodId];\n if (_.isEmpty(serverDoc.writtenByStubs)) {\n // All methods whose stubs wrote this method have completed! We can\n // now copy the saved document to the database (reverting the stub's\n // change if the server did not write to this object, or applying the\n // server's writes if it did).\n\n // This is a fake ddp 'replace' message. It's just for talking\n // between livedata connections and minimongo. (We have to stringify\n // the ID because it's supposed to look like a wire message.)\n self._pushUpdate(updates, written.collection, {\n msg: 'replace',\n id: LocalCollection._idStringify(written.id),\n replace: serverDoc.document\n });\n // Call all flush callbacks.\n _.each(serverDoc.flushCallbacks, function (c) {\n c();\n });\n\n // Delete this completed serverDocument. Don't bother to GC empty\n // IdMaps inside self._serverDocuments, since there probably aren't\n // many collections and they'll be written repeatedly.\n self._serverDocuments[written.collection].remove(written.id);\n }\n });\n delete self._documentsWrittenByStub[methodId];\n\n // We want to call the data-written callback, but we can't do so until all\n // currently buffered messages are flushed.\n var callbackInvoker = self._methodInvokers[methodId];\n if (!callbackInvoker)\n throw new Error(\"No callback invoker for method \" + methodId);\n self._runWhenAllServerDocsAreFlushed(\n _.bind(callbackInvoker.dataVisible, callbackInvoker));\n });\n },\n\n _process_ready: function (msg, updates) {\n var self = this;\n // Process \"sub ready\" messages. \"sub ready\" messages don't take effect\n // until all current server documents have been flushed to the local\n // database. We can use a write fence to implement this.\n _.each(msg.subs, function (subId) {\n self._runWhenAllServerDocsAreFlushed(function () {\n var subRecord = self._subscriptions[subId];\n // Did we already unsubscribe?\n if (!subRecord)\n return;\n // Did we already receive a ready message? (Oops!)\n if (subRecord.ready)\n return;\n subRecord.readyCallback && subRecord.readyCallback();\n subRecord.ready = true;\n subRecord.readyDeps.changed();\n });\n });\n },\n\n // Ensures that \"f\" will be called after all documents currently in\n // _serverDocuments have been written to the local cache. f will not be called\n // if the connection is lost before then!\n _runWhenAllServerDocsAreFlushed: function (f) {\n var self = this;\n var runFAfterUpdates = function () {\n self._afterUpdateCallbacks.push(f);\n };\n var unflushedServerDocCount = 0;\n var onServerDocFlush = function () {\n --unflushedServerDocCount;\n if (unflushedServerDocCount === 0) {\n // This was the last doc to flush! Arrange to run f after the updates\n // have been applied.\n runFAfterUpdates();\n }\n };\n _.each(self._serverDocuments, function (collectionDocs) {\n collectionDocs.forEach(function (serverDoc) {\n var writtenByStubForAMethodWithSentMessage = _.any(\n serverDoc.writtenByStubs, function (dummy, methodId) {\n var invoker = self._methodInvokers[methodId];\n return invoker && invoker.sentMessage;\n });\n if (writtenByStubForAMethodWithSentMessage) {\n ++unflushedServerDocCount;\n serverDoc.flushCallbacks.push(onServerDocFlush);\n }\n });\n });\n if (unflushedServerDocCount === 0) {\n // There aren't any buffered docs --- we can call f as soon as the current\n // round of updates is applied!\n runFAfterUpdates();\n }\n },\n\n _livedata_nosub: function (msg) {\n var self = this;\n\n // First pass it through _livedata_data, which only uses it to help get\n // towards quiescence.\n self._livedata_data(msg);\n\n // Do the rest of our processing immediately, with no\n // buffering-until-quiescence.\n\n // we weren't subbed anyway, or we initiated the unsub.\n if (!_.has(self._subscriptions, msg.id))\n return;\n\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n var errorCallback = self._subscriptions[msg.id].errorCallback;\n var stopCallback = self._subscriptions[msg.id].stopCallback;\n\n self._subscriptions[msg.id].remove();\n\n var meteorErrorFromMsg = function (msgArg) {\n return msgArg && msgArg.error && new Meteor.Error(\n msgArg.error.error, msgArg.error.reason, msgArg.error.details);\n }\n\n // XXX COMPAT WITH 1.0.3.1 #errorCallback\n if (errorCallback && msg.error) {\n errorCallback(meteorErrorFromMsg(msg));\n }\n\n if (stopCallback) {\n stopCallback(meteorErrorFromMsg(msg));\n }\n },\n\n _process_nosub: function () {\n // This is called as part of the \"buffer until quiescence\" process, but\n // nosub's effect is always immediate. It only goes in the buffer at all\n // because it's possible for a nosub to be the thing that triggers\n // quiescence, if we were waiting for a sub to be revived and it dies\n // instead.\n },\n\n _livedata_result: function (msg) {\n // id, result or error. error has error (code), reason, details\n\n var self = this;\n\n // find the outstanding request\n // should be O(1) in nearly all realistic use cases\n if (_.isEmpty(self._outstandingMethodBlocks)) {\n Meteor._debug(\"Received method result but no methods outstanding\");\n return;\n }\n var currentMethodBlock = self._outstandingMethodBlocks[0].methods;\n var m;\n for (var i = 0; i < currentMethodBlock.length; i++) {\n m = currentMethodBlock[i];\n if (m.methodId === msg.id)\n break;\n }\n\n if (!m) {\n Meteor._debug(\"Can't match method response to original method call\", msg);\n return;\n }\n\n // Remove from current method block. This may leave the block empty, but we\n // don't move on to the next block until the callback has been delivered, in\n // _outstandingMethodFinished.\n currentMethodBlock.splice(i, 1);\n\n if (_.has(msg, 'error')) {\n m.receiveResult(new Meteor.Error(\n msg.error.error, msg.error.reason,\n msg.error.details));\n } else {\n // msg.result may be undefined if the method didn't return a\n // value\n m.receiveResult(undefined, msg.result);\n }\n },\n\n // Called by MethodInvoker after a method's callback is invoked. If this was\n // the last outstanding method in the current block, runs the next block. If\n // there are no more methods, consider accepting a hot code push.\n _outstandingMethodFinished: function () {\n var self = this;\n if (self._anyMethodsAreOutstanding())\n return;\n\n // No methods are outstanding. This should mean that the first block of\n // methods is empty. (Or it might not exist, if this was a method that\n // half-finished before disconnect/reconnect.)\n if (! _.isEmpty(self._outstandingMethodBlocks)) {\n var firstBlock = self._outstandingMethodBlocks.shift();\n if (! _.isEmpty(firstBlock.methods))\n throw new Error(\"No methods outstanding but nonempty block: \" +\n JSON.stringify(firstBlock));\n\n // Send the outstanding methods now in the first block.\n if (!_.isEmpty(self._outstandingMethodBlocks))\n self._sendOutstandingMethods();\n }\n\n // Maybe accept a hot code push.\n self._maybeMigrate();\n },\n\n // Sends messages for all the methods in the first block in\n // _outstandingMethodBlocks.\n _sendOutstandingMethods: function() {\n var self = this;\n if (_.isEmpty(self._outstandingMethodBlocks))\n return;\n _.each(self._outstandingMethodBlocks[0].methods, function (m) {\n m.sendMessage();\n });\n },\n\n _livedata_error: function (msg) {\n Meteor._debug(\"Received error from server: \", msg.reason);\n if (msg.offendingMessage)\n Meteor._debug(\"For: \", msg.offendingMessage);\n },\n\n _callOnReconnectAndSendAppropriateOutstandingMethods: function() {\n var self = this;\n var oldOutstandingMethodBlocks = self._outstandingMethodBlocks;\n self._outstandingMethodBlocks = [];\n\n self.onReconnect();\n\n if (_.isEmpty(oldOutstandingMethodBlocks))\n return;\n\n // We have at least one block worth of old outstanding methods to try\n // again. First: did onReconnect actually send anything? If not, we just\n // restore all outstanding methods and run the first block.\n if (_.isEmpty(self._outstandingMethodBlocks)) {\n self._outstandingMethodBlocks = oldOutstandingMethodBlocks;\n self._sendOutstandingMethods();\n return;\n }\n\n // OK, there are blocks on both sides. Special case: merge the last block of\n // the reconnect methods with the first block of the original methods, if\n // neither of them are \"wait\" blocks.\n if (!_.last(self._outstandingMethodBlocks).wait &&\n !oldOutstandingMethodBlocks[0].wait) {\n _.each(oldOutstandingMethodBlocks[0].methods, function (m) {\n _.last(self._outstandingMethodBlocks).methods.push(m);\n\n // If this \"last block\" is also the first block, send the message.\n if (self._outstandingMethodBlocks.length === 1)\n m.sendMessage();\n });\n\n oldOutstandingMethodBlocks.shift();\n }\n\n // Now add the rest of the original blocks on.\n _.each(oldOutstandingMethodBlocks, function (block) {\n self._outstandingMethodBlocks.push(block);\n });\n },\n\n // We can accept a hot code push if there are no methods in flight.\n _readyToMigrate: function() {\n var self = this;\n return _.isEmpty(self._methodInvokers);\n },\n\n // If we were blocking a migration, see if it's now possible to continue.\n // Call whenever the set of outstanding/blocked methods shrinks.\n _maybeMigrate: function () {\n var self = this;\n if (self._retryMigrate && self._readyToMigrate()) {\n self._retryMigrate();\n self._retryMigrate = null;\n }\n }\n});\n\nLivedataTest.Connection = Connection;\n\n// @param url {String} URL to Meteor app,\n// e.g.:\n// \"subdomain.meteor.com\",\n// \"http://subdomain.meteor.com\",\n// \"/\",\n// \"ddp+sockjs://ddp--****-foo.meteor.com/sockjs\"\n\n/**\n * @summary Connect to the server of a different Meteor application to subscribe to its document sets and invoke its remote methods.\n * @locus Anywhere\n * @param {String} url The URL of another Meteor application.\n */\nDDP.connect = function (url, options) {\n var ret = new Connection(url, options);\n allConnections.push(ret); // hack. see below.\n return ret;\n};\n\n// Hack for `spiderable` package: a way to see if the page is done\n// loading all the data it needs.\n//\nallConnections = [];\nDDP._allSubscriptionsReady = function () {\n return _.all(allConnections, function (conn) {\n return _.all(conn._subscriptions, function (sub) {\n return sub.ready;\n });\n });\n};\n","// Only create a server if we are in an environment with a HTTP server\n// (as opposed to, eg, a command-line tool).\n//\n// Note: this whole conditional is a total hack to get around the fact that this\n// package logically should be split into a ddp-client and ddp-server package;\n// see https://github.com/meteor/meteor/issues/3452\n//\n// Until we do that, this conditional (and the weak dependency on webapp that\n// should really be a strong dependency of the ddp-server package) allows you to\n// build projects which use `ddp` in Node without wanting to run a DDP server\n// (ie, allows you to act as if you were using the nonexistent `ddp-client`\n// server package).\nif (Package.webapp) {\n if (process.env.DDP_DEFAULT_CONNECTION_URL) {\n __meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL =\n process.env.DDP_DEFAULT_CONNECTION_URL;\n }\n\n Meteor.server = new Server;\n\n Meteor.refresh = function (notification) {\n DDPServer._InvalidationCrossbar.fire(notification);\n };\n\n // Proxy the public methods of Meteor.server so they can\n // be called directly on Meteor.\n _.each(['publish', 'methods', 'call', 'apply', 'onConnection'],\n function (name) {\n Meteor[name] = _.bind(Meteor.server[name], Meteor.server);\n });\n} else {\n // No server? Make these empty/no-ops.\n Meteor.server = null;\n Meteor.refresh = function (notification) {\n };\n\n // Make these empty/no-ops too, so that non-webapp apps can still\n // depend on/use packages that use those functions.\n _.each(['publish', 'methods', 'onConnection'],\n function (name) {\n Meteor[name] = function () { };\n });\n}\n\n// Meteor.server used to be called Meteor.default_server. Provide\n// backcompat as a courtesy even though it was never documented.\n// XXX COMPAT WITH 0.6.4\nMeteor.default_server = Meteor.server;\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/deps.js b/web-app/.meteor/local/build/programs/server/packages/deps.js
new file mode 100644
index 0000000..443ab79
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/deps.js
@@ -0,0 +1,22 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var Tracker = Package.tracker.Tracker;
+var Deps = Package.tracker.Deps;
+
+/* Package-scope variables */
+var Tracker, Deps;
+
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.deps = {
+ Tracker: Tracker,
+ Deps: Deps
+};
+
+})();
+
+//# sourceMappingURL=deps.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/deps.js.map b/web-app/.meteor/local/build/programs/server/packages/deps.js.map
new file mode 100644
index 0000000..2e8a49c
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/deps.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":[],"names":[],"mappings":";;;;;;;;;;","file":"/packages/deps.js"}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/ejson.js b/web-app/.meteor/local/build/programs/server/packages/ejson.js
new file mode 100644
index 0000000..3326785
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/ejson.js
@@ -0,0 +1,679 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var _ = Package.underscore._;
+var Base64 = Package.base64.Base64;
+
+/* Package-scope variables */
+var EJSON, EJSONTest;
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ejson/ejson.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+/** // 1
+ * @namespace // 2
+ * @summary Namespace for EJSON functions // 3
+ */ // 4
+EJSON = {}; // 5
+EJSONTest = {}; // 6
+ // 7
+ // 8
+ // 9
+// Custom type interface definition // 10
+/** // 11
+ * @class CustomType // 12
+ * @instanceName customType // 13
+ * @memberOf EJSON // 14
+ * @summary The interface that a class must satisfy to be able to become an // 15
+ * EJSON custom type via EJSON.addType. // 16
+ */ // 17
+ // 18
+/** // 19
+ * @function typeName // 20
+ * @memberOf EJSON.CustomType // 21
+ * @summary Return the tag used to identify this type. This must match the tag used to register this type with [`EJSON.addType`](#ejson_add_type).
+ * @locus Anywhere // 23
+ * @instance // 24
+ */ // 25
+ // 26
+/** // 27
+ * @function toJSONValue // 28
+ * @memberOf EJSON.CustomType // 29
+ * @summary Serialize this instance into a JSON-compatible value. // 30
+ * @locus Anywhere // 31
+ * @instance // 32
+ */ // 33
+ // 34
+/** // 35
+ * @function clone // 36
+ * @memberOf EJSON.CustomType // 37
+ * @summary Return a value `r` such that `this.equals(r)` is true, and modifications to `r` do not affect `this` and vice versa.
+ * @locus Anywhere // 39
+ * @instance // 40
+ */ // 41
+ // 42
+/** // 43
+ * @function equals // 44
+ * @memberOf EJSON.CustomType // 45
+ * @summary Return `true` if `other` has a value equal to `this`; `false` otherwise. // 46
+ * @locus Anywhere // 47
+ * @param {Object} other Another object to compare this to. // 48
+ * @instance // 49
+ */ // 50
+ // 51
+ // 52
+var customTypes = {}; // 53
+// Add a custom type, using a method of your choice to get to and // 54
+// from a basic JSON-able representation. The factory argument // 55
+// is a function of JSON-able --> your object // 56
+// The type you add must have: // 57
+// - A toJSONValue() method, so that Meteor can serialize it // 58
+// - a typeName() method, to show how to look it up in our type table. // 59
+// It is okay if these methods are monkey-patched on. // 60
+// EJSON.clone will use toJSONValue and the given factory to produce // 61
+// a clone, but you may specify a method clone() that will be // 62
+// used instead. // 63
+// Similarly, EJSON.equals will use toJSONValue to make comparisons, // 64
+// but you may provide a method equals() instead. // 65
+/** // 66
+ * @summary Add a custom datatype to EJSON. // 67
+ * @locus Anywhere // 68
+ * @param {String} name A tag for your custom type; must be unique among custom data types defined in your project, and must match the result of your type's `typeName` method.
+ * @param {Function} factory A function that deserializes a JSON-compatible value into an instance of your type. This should match the serialization performed by your type's `toJSONValue` method.
+ */ // 71
+EJSON.addType = function (name, factory) { // 72
+ if (_.has(customTypes, name)) // 73
+ throw new Error("Type " + name + " already present"); // 74
+ customTypes[name] = factory; // 75
+}; // 76
+ // 77
+var isInfOrNan = function (obj) { // 78
+ return _.isNaN(obj) || obj === Infinity || obj === -Infinity; // 79
+}; // 80
+ // 81
+var builtinConverters = [ // 82
+ { // Date // 83
+ matchJSONValue: function (obj) { // 84
+ return _.has(obj, '$date') && _.size(obj) === 1; // 85
+ }, // 86
+ matchObject: function (obj) { // 87
+ return obj instanceof Date; // 88
+ }, // 89
+ toJSONValue: function (obj) { // 90
+ return {$date: obj.getTime()}; // 91
+ }, // 92
+ fromJSONValue: function (obj) { // 93
+ return new Date(obj.$date); // 94
+ } // 95
+ }, // 96
+ { // NaN, Inf, -Inf. (These are the only objects with typeof !== 'object' // 97
+ // which we match.) // 98
+ matchJSONValue: function (obj) { // 99
+ return _.has(obj, '$InfNaN') && _.size(obj) === 1; // 100
+ }, // 101
+ matchObject: isInfOrNan, // 102
+ toJSONValue: function (obj) { // 103
+ var sign; // 104
+ if (_.isNaN(obj)) // 105
+ sign = 0; // 106
+ else if (obj === Infinity) // 107
+ sign = 1; // 108
+ else // 109
+ sign = -1; // 110
+ return {$InfNaN: sign}; // 111
+ }, // 112
+ fromJSONValue: function (obj) { // 113
+ return obj.$InfNaN/0; // 114
+ } // 115
+ }, // 116
+ { // Binary // 117
+ matchJSONValue: function (obj) { // 118
+ return _.has(obj, '$binary') && _.size(obj) === 1; // 119
+ }, // 120
+ matchObject: function (obj) { // 121
+ return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array // 122
+ || (obj && _.has(obj, '$Uint8ArrayPolyfill')); // 123
+ }, // 124
+ toJSONValue: function (obj) { // 125
+ return {$binary: Base64.encode(obj)}; // 126
+ }, // 127
+ fromJSONValue: function (obj) { // 128
+ return Base64.decode(obj.$binary); // 129
+ } // 130
+ }, // 131
+ { // Escaping one level // 132
+ matchJSONValue: function (obj) { // 133
+ return _.has(obj, '$escape') && _.size(obj) === 1; // 134
+ }, // 135
+ matchObject: function (obj) { // 136
+ if (_.isEmpty(obj) || _.size(obj) > 2) { // 137
+ return false; // 138
+ } // 139
+ return _.any(builtinConverters, function (converter) { // 140
+ return converter.matchJSONValue(obj); // 141
+ }); // 142
+ }, // 143
+ toJSONValue: function (obj) { // 144
+ var newObj = {}; // 145
+ _.each(obj, function (value, key) { // 146
+ newObj[key] = EJSON.toJSONValue(value); // 147
+ }); // 148
+ return {$escape: newObj}; // 149
+ }, // 150
+ fromJSONValue: function (obj) { // 151
+ var newObj = {}; // 152
+ _.each(obj.$escape, function (value, key) { // 153
+ newObj[key] = EJSON.fromJSONValue(value); // 154
+ }); // 155
+ return newObj; // 156
+ } // 157
+ }, // 158
+ { // Custom // 159
+ matchJSONValue: function (obj) { // 160
+ return _.has(obj, '$type') && _.has(obj, '$value') && _.size(obj) === 2; // 161
+ }, // 162
+ matchObject: function (obj) { // 163
+ return EJSON._isCustomType(obj); // 164
+ }, // 165
+ toJSONValue: function (obj) { // 166
+ var jsonValue = Meteor._noYieldsAllowed(function () { // 167
+ return obj.toJSONValue(); // 168
+ }); // 169
+ return {$type: obj.typeName(), $value: jsonValue}; // 170
+ }, // 171
+ fromJSONValue: function (obj) { // 172
+ var typeName = obj.$type; // 173
+ if (!_.has(customTypes, typeName)) // 174
+ throw new Error("Custom EJSON type " + typeName + " is not defined"); // 175
+ var converter = customTypes[typeName]; // 176
+ return Meteor._noYieldsAllowed(function () { // 177
+ return converter(obj.$value); // 178
+ }); // 179
+ } // 180
+ } // 181
+]; // 182
+ // 183
+EJSON._isCustomType = function (obj) { // 184
+ return obj && // 185
+ typeof obj.toJSONValue === 'function' && // 186
+ typeof obj.typeName === 'function' && // 187
+ _.has(customTypes, obj.typeName()); // 188
+}; // 189
+ // 190
+ // 191
+// for both arrays and objects, in-place modification. // 192
+var adjustTypesToJSONValue = // 193
+EJSON._adjustTypesToJSONValue = function (obj) { // 194
+ // Is it an atom that we need to adjust? // 195
+ if (obj === null) // 196
+ return null; // 197
+ var maybeChanged = toJSONValueHelper(obj); // 198
+ if (maybeChanged !== undefined) // 199
+ return maybeChanged; // 200
+ // 201
+ // Other atoms are unchanged. // 202
+ if (typeof obj !== 'object') // 203
+ return obj; // 204
+ // 205
+ // Iterate over array or object structure. // 206
+ _.each(obj, function (value, key) { // 207
+ if (typeof value !== 'object' && value !== undefined && // 208
+ !isInfOrNan(value)) // 209
+ return; // continue // 210
+ // 211
+ var changed = toJSONValueHelper(value); // 212
+ if (changed) { // 213
+ obj[key] = changed; // 214
+ return; // on to the next key // 215
+ } // 216
+ // if we get here, value is an object but not adjustable // 217
+ // at this level. recurse. // 218
+ adjustTypesToJSONValue(value); // 219
+ }); // 220
+ return obj; // 221
+}; // 222
+ // 223
+// Either return the JSON-compatible version of the argument, or undefined (if // 224
+// the item isn't itself replaceable, but maybe some fields in it are) // 225
+var toJSONValueHelper = function (item) { // 226
+ for (var i = 0; i < builtinConverters.length; i++) { // 227
+ var converter = builtinConverters[i]; // 228
+ if (converter.matchObject(item)) { // 229
+ return converter.toJSONValue(item); // 230
+ } // 231
+ } // 232
+ return undefined; // 233
+}; // 234
+ // 235
+/** // 236
+ * @summary Serialize an EJSON-compatible value into its plain JSON representation. // 237
+ * @locus Anywhere // 238
+ * @param {EJSON} val A value to serialize to plain JSON. // 239
+ */ // 240
+EJSON.toJSONValue = function (item) { // 241
+ var changed = toJSONValueHelper(item); // 242
+ if (changed !== undefined) // 243
+ return changed; // 244
+ if (typeof item === 'object') { // 245
+ item = EJSON.clone(item); // 246
+ adjustTypesToJSONValue(item); // 247
+ } // 248
+ return item; // 249
+}; // 250
+ // 251
+// for both arrays and objects. Tries its best to just // 252
+// use the object you hand it, but may return something // 253
+// different if the object you hand it itself needs changing. // 254
+// // 255
+var adjustTypesFromJSONValue = // 256
+EJSON._adjustTypesFromJSONValue = function (obj) { // 257
+ if (obj === null) // 258
+ return null; // 259
+ var maybeChanged = fromJSONValueHelper(obj); // 260
+ if (maybeChanged !== obj) // 261
+ return maybeChanged; // 262
+ // 263
+ // Other atoms are unchanged. // 264
+ if (typeof obj !== 'object') // 265
+ return obj; // 266
+ // 267
+ _.each(obj, function (value, key) { // 268
+ if (typeof value === 'object') { // 269
+ var changed = fromJSONValueHelper(value); // 270
+ if (value !== changed) { // 271
+ obj[key] = changed; // 272
+ return; // 273
+ } // 274
+ // if we get here, value is an object but not adjustable // 275
+ // at this level. recurse. // 276
+ adjustTypesFromJSONValue(value); // 277
+ } // 278
+ }); // 279
+ return obj; // 280
+}; // 281
+ // 282
+// Either return the argument changed to have the non-json // 283
+// rep of itself (the Object version) or the argument itself. // 284
+ // 285
+// DOES NOT RECURSE. For actually getting the fully-changed value, use // 286
+// EJSON.fromJSONValue // 287
+var fromJSONValueHelper = function (value) { // 288
+ if (typeof value === 'object' && value !== null) { // 289
+ if (_.size(value) <= 2 // 290
+ && _.all(value, function (v, k) { // 291
+ return typeof k === 'string' && k.substr(0, 1) === '$'; // 292
+ })) { // 293
+ for (var i = 0; i < builtinConverters.length; i++) { // 294
+ var converter = builtinConverters[i]; // 295
+ if (converter.matchJSONValue(value)) { // 296
+ return converter.fromJSONValue(value); // 297
+ } // 298
+ } // 299
+ } // 300
+ } // 301
+ return value; // 302
+}; // 303
+ // 304
+/** // 305
+ * @summary Deserialize an EJSON value from its plain JSON representation. // 306
+ * @locus Anywhere // 307
+ * @param {JSONCompatible} val A value to deserialize into EJSON. // 308
+ */ // 309
+EJSON.fromJSONValue = function (item) { // 310
+ var changed = fromJSONValueHelper(item); // 311
+ if (changed === item && typeof item === 'object') { // 312
+ item = EJSON.clone(item); // 313
+ adjustTypesFromJSONValue(item); // 314
+ return item; // 315
+ } else { // 316
+ return changed; // 317
+ } // 318
+}; // 319
+ // 320
+/** // 321
+ * @summary Serialize a value to a string. // 322
+ // 323
+For EJSON values, the serialization fully represents the value. For non-EJSON values, serializes the same way as `JSON.stringify`.
+ * @locus Anywhere // 325
+ * @param {EJSON} val A value to stringify. // 326
+ * @param {Object} [options] // 327
+ * @param {Boolean | Integer | String} options.indent Indents objects and arrays for easy readability. When `true`, indents by 2 spaces; when an integer, indents by that number of spaces; and when a string, uses the string as the indentation pattern.
+ * @param {Boolean} options.canonical When `true`, stringifies keys in an object in sorted order. // 329
+ */ // 330
+EJSON.stringify = function (item, options) { // 331
+ var json = EJSON.toJSONValue(item); // 332
+ if (options && (options.canonical || options.indent)) { // 333
+ return EJSON._canonicalStringify(json, options); // 334
+ } else { // 335
+ return JSON.stringify(json); // 336
+ } // 337
+}; // 338
+ // 339
+/** // 340
+ * @summary Parse a string into an EJSON value. Throws an error if the string is not valid EJSON. // 341
+ * @locus Anywhere // 342
+ * @param {String} str A string to parse into an EJSON value. // 343
+ */ // 344
+EJSON.parse = function (item) { // 345
+ if (typeof item !== 'string') // 346
+ throw new Error("EJSON.parse argument should be a string"); // 347
+ return EJSON.fromJSONValue(JSON.parse(item)); // 348
+}; // 349
+ // 350
+/** // 351
+ * @summary Returns true if `x` is a buffer of binary data, as returned from [`EJSON.newBinary`](#ejson_new_binary). // 352
+ * @param {Object} x The variable to check. // 353
+ * @locus Anywhere // 354
+ */ // 355
+EJSON.isBinary = function (obj) { // 356
+ return !!((typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array) || // 357
+ (obj && obj.$Uint8ArrayPolyfill)); // 358
+}; // 359
+ // 360
+/** // 361
+ * @summary Return true if `a` and `b` are equal to each other. Return false otherwise. Uses the `equals` method on `a` if present, otherwise performs a deep comparison.
+ * @locus Anywhere // 363
+ * @param {EJSON} a // 364
+ * @param {EJSON} b // 365
+ * @param {Object} [options] // 366
+ * @param {Boolean} options.keyOrderSensitive Compare in key sensitive order, if supported by the JavaScript implementation. For example, `{a: 1, b: 2}` is equal to `{b: 2, a: 1}` only when `keyOrderSensitive` is `false`. The default is `false`.
+ */ // 368
+EJSON.equals = function (a, b, options) { // 369
+ var i; // 370
+ var keyOrderSensitive = !!(options && options.keyOrderSensitive); // 371
+ if (a === b) // 372
+ return true; // 373
+ if (_.isNaN(a) && _.isNaN(b)) // 374
+ return true; // This differs from the IEEE spec for NaN equality, b/c we don't want // 375
+ // anything ever with a NaN to be poisoned from becoming equal to anything. // 376
+ if (!a || !b) // if either one is falsy, they'd have to be === to be equal // 377
+ return false; // 378
+ if (!(typeof a === 'object' && typeof b === 'object')) // 379
+ return false; // 380
+ if (a instanceof Date && b instanceof Date) // 381
+ return a.valueOf() === b.valueOf(); // 382
+ if (EJSON.isBinary(a) && EJSON.isBinary(b)) { // 383
+ if (a.length !== b.length) // 384
+ return false; // 385
+ for (i = 0; i < a.length; i++) { // 386
+ if (a[i] !== b[i]) // 387
+ return false; // 388
+ } // 389
+ return true; // 390
+ } // 391
+ if (typeof (a.equals) === 'function') // 392
+ return a.equals(b, options); // 393
+ if (typeof (b.equals) === 'function') // 394
+ return b.equals(a, options); // 395
+ if (a instanceof Array) { // 396
+ if (!(b instanceof Array)) // 397
+ return false; // 398
+ if (a.length !== b.length) // 399
+ return false; // 400
+ for (i = 0; i < a.length; i++) { // 401
+ if (!EJSON.equals(a[i], b[i], options)) // 402
+ return false; // 403
+ } // 404
+ return true; // 405
+ } // 406
+ // fallback for custom types that don't implement their own equals // 407
+ switch (EJSON._isCustomType(a) + EJSON._isCustomType(b)) { // 408
+ case 1: return false; // 409
+ case 2: return EJSON.equals(EJSON.toJSONValue(a), EJSON.toJSONValue(b)); // 410
+ } // 411
+ // fall back to structural equality of objects // 412
+ var ret; // 413
+ if (keyOrderSensitive) { // 414
+ var bKeys = []; // 415
+ _.each(b, function (val, x) { // 416
+ bKeys.push(x); // 417
+ }); // 418
+ i = 0; // 419
+ ret = _.all(a, function (val, x) { // 420
+ if (i >= bKeys.length) { // 421
+ return false; // 422
+ } // 423
+ if (x !== bKeys[i]) { // 424
+ return false; // 425
+ } // 426
+ if (!EJSON.equals(val, b[bKeys[i]], options)) { // 427
+ return false; // 428
+ } // 429
+ i++; // 430
+ return true; // 431
+ }); // 432
+ return ret && i === bKeys.length; // 433
+ } else { // 434
+ i = 0; // 435
+ ret = _.all(a, function (val, key) { // 436
+ if (!_.has(b, key)) { // 437
+ return false; // 438
+ } // 439
+ if (!EJSON.equals(val, b[key], options)) { // 440
+ return false; // 441
+ } // 442
+ i++; // 443
+ return true; // 444
+ }); // 445
+ return ret && _.size(b) === i; // 446
+ } // 447
+}; // 448
+ // 449
+/** // 450
+ * @summary Return a deep copy of `val`. // 451
+ * @locus Anywhere // 452
+ * @param {EJSON} val A value to copy. // 453
+ */ // 454
+EJSON.clone = function (v) { // 455
+ var ret; // 456
+ if (typeof v !== "object") // 457
+ return v; // 458
+ if (v === null) // 459
+ return null; // null has typeof "object" // 460
+ if (v instanceof Date) // 461
+ return new Date(v.getTime()); // 462
+ // RegExps are not really EJSON elements (eg we don't define a serialization // 463
+ // for them), but they're immutable anyway, so we can support them in clone. // 464
+ if (v instanceof RegExp) // 465
+ return v; // 466
+ if (EJSON.isBinary(v)) { // 467
+ ret = EJSON.newBinary(v.length); // 468
+ for (var i = 0; i < v.length; i++) { // 469
+ ret[i] = v[i]; // 470
+ } // 471
+ return ret; // 472
+ } // 473
+ // XXX: Use something better than underscore's isArray // 474
+ if (_.isArray(v) || _.isArguments(v)) { // 475
+ // For some reason, _.map doesn't work in this context on Opera (weird test // 476
+ // failures). // 477
+ ret = []; // 478
+ for (i = 0; i < v.length; i++) // 479
+ ret[i] = EJSON.clone(v[i]); // 480
+ return ret; // 481
+ } // 482
+ // handle general user-defined typed Objects if they have a clone method // 483
+ if (typeof v.clone === 'function') { // 484
+ return v.clone(); // 485
+ } // 486
+ // handle other custom types // 487
+ if (EJSON._isCustomType(v)) { // 488
+ return EJSON.fromJSONValue(EJSON.clone(EJSON.toJSONValue(v)), true); // 489
+ } // 490
+ // handle other objects // 491
+ ret = {}; // 492
+ _.each(v, function (value, key) { // 493
+ ret[key] = EJSON.clone(value); // 494
+ }); // 495
+ return ret; // 496
+}; // 497
+ // 498
+/** // 499
+ * @summary Allocate a new buffer of binary data that EJSON can serialize. // 500
+ * @locus Anywhere // 501
+ * @param {Number} size The number of bytes of binary data to allocate. // 502
+ */ // 503
+// EJSON.newBinary is the public documented API for this functionality, // 504
+// but the implementation is in the 'base64' package to avoid // 505
+// introducing a circular dependency. (If the implementation were here, // 506
+// then 'base64' would have to use EJSON.newBinary, and 'ejson' would // 507
+// also have to use 'base64'.) // 508
+EJSON.newBinary = Base64.newBinary; // 509
+ // 510
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/ejson/stringify.js //
+// //
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Based on json2.js from https://github.com/douglascrockford/JSON-js // 1
+// // 2
+// json2.js // 3
+// 2012-10-08 // 4
+// // 5
+// Public Domain. // 6
+// // 7
+// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. // 8
+ // 9
+function quote(string) { // 10
+ return JSON.stringify(string); // 11
+} // 12
+ // 13
+var str = function (key, holder, singleIndent, outerIndent, canonical) { // 14
+ // 15
+ // Produce a string from holder[key]. // 16
+ // 17
+ var i; // The loop counter. // 18
+ var k; // The member key. // 19
+ var v; // The member value. // 20
+ var length; // 21
+ var innerIndent = outerIndent; // 22
+ var partial; // 23
+ var value = holder[key]; // 24
+ // 25
+ // What happens next depends on the value's type. // 26
+ // 27
+ switch (typeof value) { // 28
+ case 'string': // 29
+ return quote(value); // 30
+ case 'number': // 31
+ // JSON numbers must be finite. Encode non-finite numbers as null. // 32
+ return isFinite(value) ? String(value) : 'null'; // 33
+ case 'boolean': // 34
+ return String(value); // 35
+ // If the type is 'object', we might be dealing with an object or an array or // 36
+ // null. // 37
+ case 'object': // 38
+ // Due to a specification blunder in ECMAScript, typeof null is 'object', // 39
+ // so watch out for that case. // 40
+ if (!value) { // 41
+ return 'null'; // 42
+ } // 43
+ // Make an array to hold the partial results of stringifying this object value. // 44
+ innerIndent = outerIndent + singleIndent; // 45
+ partial = []; // 46
+ // 47
+ // Is the value an array? // 48
+ if (_.isArray(value) || _.isArguments(value)) { // 49
+ // 50
+ // The value is an array. Stringify every element. Use null as a placeholder // 51
+ // for non-JSON values. // 52
+ // 53
+ length = value.length; // 54
+ for (i = 0; i < length; i += 1) { // 55
+ partial[i] = str(i, value, singleIndent, innerIndent, canonical) || 'null'; // 56
+ } // 57
+ // 58
+ // Join all of the elements together, separated with commas, and wrap them in // 59
+ // brackets. // 60
+ // 61
+ if (partial.length === 0) { // 62
+ v = '[]'; // 63
+ } else if (innerIndent) { // 64
+ v = '[\n' + innerIndent + partial.join(',\n' + innerIndent) + '\n' + outerIndent + ']'; // 65
+ } else { // 66
+ v = '[' + partial.join(',') + ']'; // 67
+ } // 68
+ return v; // 69
+ } // 70
+ // 71
+ // 72
+ // Iterate through all of the keys in the object. // 73
+ var keys = _.keys(value); // 74
+ if (canonical) // 75
+ keys = keys.sort(); // 76
+ _.each(keys, function (k) { // 77
+ v = str(k, value, singleIndent, innerIndent, canonical); // 78
+ if (v) { // 79
+ partial.push(quote(k) + (innerIndent ? ': ' : ':') + v); // 80
+ } // 81
+ }); // 82
+ // 83
+ // 84
+ // Join all of the member texts together, separated with commas, // 85
+ // and wrap them in braces. // 86
+ // 87
+ if (partial.length === 0) { // 88
+ v = '{}'; // 89
+ } else if (innerIndent) { // 90
+ v = '{\n' + innerIndent + partial.join(',\n' + innerIndent) + '\n' + outerIndent + '}'; // 91
+ } else { // 92
+ v = '{' + partial.join(',') + '}'; // 93
+ } // 94
+ return v; // 95
+ } // 96
+} // 97
+ // 98
+// If the JSON object does not yet have a stringify method, give it one. // 99
+ // 100
+EJSON._canonicalStringify = function (value, options) { // 101
+ // Make a fake root object containing our value under the key of ''. // 102
+ // Return the result of stringifying the value. // 103
+ options = _.extend({ // 104
+ indent: "", // 105
+ canonical: false // 106
+ }, options); // 107
+ if (options.indent === true) { // 108
+ options.indent = " "; // 109
+ } else if (typeof options.indent === 'number') { // 110
+ var newIndent = ""; // 111
+ for (var i = 0; i < options.indent; i++) { // 112
+ newIndent += ' '; // 113
+ } // 114
+ options.indent = newIndent; // 115
+ } // 116
+ return str('', {'': value}, options.indent, "", options.canonical); // 117
+}; // 118
+ // 119
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package.ejson = {
+ EJSON: EJSON,
+ EJSONTest: EJSONTest
+};
+
+})();
+
+//# sourceMappingURL=ejson.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/ejson.js.map b/web-app/.meteor/local/build/programs/server/packages/ejson.js.map
new file mode 100644
index 0000000..21243a0
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/ejson.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["ejson/ejson.js","ejson/stringify.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,G;AACA,a;AACA,yC;AACA,G;AACA,W;AACA,e;;;;AAIA,mC;AACA,G;AACA,oB;AACA,2B;AACA,kB;AACA,2E;AACA,uC;AACA,G;;AAEA,G;AACA,qB;AACA,6B;AACA,mJ;AACA,kB;AACA,Y;AACA,G;;AAEA,G;AACA,wB;AACA,6B;AACA,iE;AACA,kB;AACA,Y;AACA,G;;AAEA,G;AACA,kB;AACA,6B;AACA,gI;AACA,kB;AACA,Y;AACA,G;;AAEA,G;AACA,mB;AACA,6B;AACA,oF;AACA,kB;AACA,2D;AACA,Y;AACA,G;;;AAGA,qB;AACA,iE;AACA,+D;AACA,6C;AACA,8B;AACA,4D;AACA,sE;AACA,qD;AACA,oE;AACA,6D;AACA,gB;AACA,oE;AACA,iD;AACA,G;AACA,2C;AACA,kB;AACA,+K;AACA,oM;AACA,G;AACA,0C;AACA,+B;AACA,yD;AACA,8B;AACA,E;;AAEA,iC;AACA,+D;AACA,E;;AAEA,yB;AACA,W;AACA,oC;AACA,sD;AACA,M;AACA,iC;AACA,iC;AACA,M;AACA,iC;AACA,oC;AACA,M;AACA,mC;AACA,iC;AACA,K;AACA,I;AACA,2E;AACA,uB;AACA,oC;AACA,wD;AACA,M;AACA,4B;AACA,iC;AACA,e;AACA,uB;AACA,iB;AACA,gC;AACA,iB;AACA,U;AACA,kB;AACA,6B;AACA,M;AACA,mC;AACA,2B;AACA,K;AACA,I;AACA,a;AACA,oC;AACA,wD;AACA,M;AACA,iC;AACA,2E;AACA,sD;AACA,M;AACA,iC;AACA,2C;AACA,M;AACA,mC;AACA,wC;AACA,K;AACA,I;AACA,yB;AACA,oC;AACA,wD;AACA,M;AACA,iC;AACA,8C;AACA,qB;AACA,O;AACA,4D;AACA,6C;AACA,S;AACA,M;AACA,iC;AACA,sB;AACA,yC;AACA,+C;AACA,S;AACA,+B;AACA,M;AACA,mC;AACA,sB;AACA,iD;AACA,iD;AACA,S;AACA,oB;AACA,K;AACA,I;AACA,a;AACA,oC;AACA,8E;AACA,M;AACA,iC;AACA,sC;AACA,M;AACA,iC;AACA,2D;AACA,iC;AACA,S;AACA,wD;AACA,M;AACA,mC;AACA,+B;AACA,wC;AACA,6E;AACA,4C;AACA,kD;AACA,qC;AACA,S;AACA,K;AACA,G;AACA,E;;AAEA,sC;AACA,e;AACA,4C;AACA,yC;AACA,uC;AACA,E;;;AAGA,sD;AACA,4B;AACA,gD;AACA,0C;AACA,mB;AACA,gB;AACA,4C;AACA,iC;AACA,wB;;AAEA,+B;AACA,8B;AACA,e;;AAEA,4C;AACA,qC;AACA,2D;AACA,2B;AACA,yB;;AAEA,2C;AACA,kB;AACA,yB;AACA,mC;AACA,K;AACA,4D;AACA,+B;AACA,kC;AACA,K;AACA,a;AACA,E;;AAEA,8E;AACA,sE;AACA,yC;AACA,sD;AACA,yC;AACA,sC;AACA,yC;AACA,K;AACA,G;AACA,mB;AACA,E;;AAEA,G;AACA,mF;AACA,kB;AACA,yD;AACA,G;AACA,qC;AACA,wC;AACA,4B;AACA,mB;AACA,iC;AACA,6B;AACA,iC;AACA,G;AACA,c;AACA,E;;AAEA,sD;AACA,uD;AACA,6D;AACA,E;AACA,8B;AACA,kD;AACA,mB;AACA,gB;AACA,8C;AACA,2B;AACA,wB;;AAEA,+B;AACA,8B;AACA,e;;AAEA,qC;AACA,oC;AACA,+C;AACA,8B;AACA,2B;AACA,e;AACA,O;AACA,8D;AACA,iC;AACA,sC;AACA,K;AACA,K;AACA,a;AACA,E;;AAEA,0D;AACA,6D;;AAEA,uE;AACA,sB;AACA,4C;AACA,oD;AACA,0B;AACA,yC;AACA,iE;AACA,a;AACA,0D;AACA,6C;AACA,8C;AACA,gD;AACA,S;AACA,O;AACA,K;AACA,G;AACA,e;AACA,E;;AAEA,G;AACA,0E;AACA,kB;AACA,iE;AACA,G;AACA,uC;AACA,0C;AACA,qD;AACA,6B;AACA,mC;AACA,gB;AACA,U;AACA,mB;AACA,G;AACA,E;;AAEA,G;AACA,0C;;AAEA,kI;AACA,kB;AACA,2C;AACA,4B;AACA,2P;AACA,iG;AACA,G;AACA,4C;AACA,qC;AACA,yD;AACA,oD;AACA,U;AACA,gC;AACA,G;AACA,E;;AAEA,G;AACA,iG;AACA,kB;AACA,6D;AACA,G;AACA,+B;AACA,+B;AACA,+D;AACA,+C;AACA,E;;AAEA,G;AACA,oH;AACA,2C;AACA,kB;AACA,G;AACA,iC;AACA,+E;AACA,sC;AACA,E;;AAEA,G;AACA,2K;AACA,kB;AACA,mB;AACA,mB;AACA,4B;AACA,uP;AACA,G;AACA,yC;AACA,Q;AACA,mE;AACA,c;AACA,gB;AACA,+B;AACA,uF;AACA,4F;AACA,4E;AACA,iB;AACA,wD;AACA,iB;AACA,6C;AACA,uC;AACA,+C;AACA,8B;AACA,mB;AACA,oC;AACA,wB;AACA,qB;AACA,K;AACA,gB;AACA,G;AACA,uC;AACA,gC;AACA,uC;AACA,gC;AACA,2B;AACA,8B;AACA,mB;AACA,8B;AACA,mB;AACA,oC;AACA,6C;AACA,qB;AACA,K;AACA,gB;AACA,G;AACA,oE;AACA,4D;AACA,yB;AACA,4E;AACA,G;AACA,gD;AACA,U;AACA,0B;AACA,mB;AACA,iC;AACA,sB;AACA,O;AACA,U;AACA,sC;AACA,8B;AACA,qB;AACA,O;AACA,2B;AACA,qB;AACA,O;AACA,qD;AACA,qB;AACA,O;AACA,U;AACA,kB;AACA,O;AACA,qC;AACA,U;AACA,U;AACA,wC;AACA,2B;AACA,qB;AACA,O;AACA,gD;AACA,qB;AACA,O;AACA,U;AACA,kB;AACA,O;AACA,kC;AACA,G;AACA,E;;AAEA,G;AACA,wC;AACA,kB;AACA,sC;AACA,G;AACA,4B;AACA,U;AACA,4B;AACA,a;AACA,iB;AACA,4C;AACA,wB;AACA,iC;AACA,8E;AACA,8E;AACA,0B;AACA,a;AACA,0B;AACA,oC;AACA,wC;AACA,oB;AACA,K;AACA,e;AACA,G;AACA,wD;AACA,yC;AACA,+E;AACA,iB;AACA,a;AACA,kC;AACA,iC;AACA,e;AACA,G;AACA,0E;AACA,sC;AACA,qB;AACA,G;AACA,8B;AACA,+B;AACA,wE;AACA,G;AACA,yB;AACA,W;AACA,mC;AACA,kC;AACA,K;AACA,a;AACA,E;;AAEA,G;AACA,0E;AACA,kB;AACA,uE;AACA,G;AACA,uE;AACA,6D;AACA,uE;AACA,qE;AACA,8B;AACA,mC;;;;;;;;;;;;;;;;;;;AC5fA,qE;AACA,E;AACA,c;AACA,gB;AACA,E;AACA,oB;AACA,E;AACA,6D;;AAEA,wB;AACA,gC;AACA,C;;AAEA,wE;;AAEA,uC;;AAEA,sC;AACA,oC;AACA,sC;AACA,a;AACA,gC;AACA,c;AACA,0B;;AAEA,mD;;AAEA,yB;AACA,gB;AACA,wB;AACA,gB;AACA,sE;AACA,oD;AACA,iB;AACA,yB;AACA,+E;AACA,U;AACA,gB;AACA,6E;AACA,kC;AACA,iB;AACA,oB;AACA,K;AACA,mF;AACA,6C;AACA,iB;;AAEA,6B;AACA,mD;;AAEA,kF;AACA,6B;;AAEA,4B;AACA,uC;AACA,mF;AACA,O;;AAEA,mF;AACA,kB;;AAEA,iC;AACA,iB;AACA,+B;AACA,+F;AACA,c;AACA,0C;AACA,O;AACA,e;AACA,K;;;AAGA,qD;AACA,6B;AACA,kB;AACA,yB;AACA,+B;AACA,8D;AACA,c;AACA,gE;AACA,O;AACA,O;;;AAGA,oE;AACA,+B;;AAEA,+B;AACA,e;AACA,6B;AACA,6F;AACA,Y;AACA,wC;AACA,K;AACA,a;AACA,G;AACA,C;;AAEA,wE;;AAEA,uD;AACA,sE;AACA,iD;AACA,sB;AACA,e;AACA,oB;AACA,c;AACA,gC;AACA,0B;AACA,kD;AACA,uB;AACA,8C;AACA,uB;AACA,K;AACA,+B;AACA,G;AACA,qE;AACA,E","file":"/packages/ejson.js","sourcesContent":["/**\n * @namespace\n * @summary Namespace for EJSON functions\n */\nEJSON = {};\nEJSONTest = {};\n\n\n\n// Custom type interface definition\n/**\n * @class CustomType\n * @instanceName customType\n * @memberOf EJSON\n * @summary The interface that a class must satisfy to be able to become an\n * EJSON custom type via EJSON.addType.\n */\n\n/**\n * @function typeName\n * @memberOf EJSON.CustomType\n * @summary Return the tag used to identify this type. This must match the tag used to register this type with [`EJSON.addType`](#ejson_add_type).\n * @locus Anywhere\n * @instance\n */\n\n/**\n * @function toJSONValue\n * @memberOf EJSON.CustomType\n * @summary Serialize this instance into a JSON-compatible value.\n * @locus Anywhere\n * @instance\n */\n\n/**\n * @function clone\n * @memberOf EJSON.CustomType\n * @summary Return a value `r` such that `this.equals(r)` is true, and modifications to `r` do not affect `this` and vice versa.\n * @locus Anywhere\n * @instance\n */\n\n/**\n * @function equals\n * @memberOf EJSON.CustomType\n * @summary Return `true` if `other` has a value equal to `this`; `false` otherwise.\n * @locus Anywhere\n * @param {Object} other Another object to compare this to.\n * @instance\n */\n\n\nvar customTypes = {};\n// Add a custom type, using a method of your choice to get to and\n// from a basic JSON-able representation. The factory argument\n// is a function of JSON-able --> your object\n// The type you add must have:\n// - A toJSONValue() method, so that Meteor can serialize it\n// - a typeName() method, to show how to look it up in our type table.\n// It is okay if these methods are monkey-patched on.\n// EJSON.clone will use toJSONValue and the given factory to produce\n// a clone, but you may specify a method clone() that will be\n// used instead.\n// Similarly, EJSON.equals will use toJSONValue to make comparisons,\n// but you may provide a method equals() instead.\n/**\n * @summary Add a custom datatype to EJSON.\n * @locus Anywhere\n * @param {String} name A tag for your custom type; must be unique among custom data types defined in your project, and must match the result of your type's `typeName` method.\n * @param {Function} factory A function that deserializes a JSON-compatible value into an instance of your type. This should match the serialization performed by your type's `toJSONValue` method.\n */\nEJSON.addType = function (name, factory) {\n if (_.has(customTypes, name))\n throw new Error(\"Type \" + name + \" already present\");\n customTypes[name] = factory;\n};\n\nvar isInfOrNan = function (obj) {\n return _.isNaN(obj) || obj === Infinity || obj === -Infinity;\n};\n\nvar builtinConverters = [\n { // Date\n matchJSONValue: function (obj) {\n return _.has(obj, '$date') && _.size(obj) === 1;\n },\n matchObject: function (obj) {\n return obj instanceof Date;\n },\n toJSONValue: function (obj) {\n return {$date: obj.getTime()};\n },\n fromJSONValue: function (obj) {\n return new Date(obj.$date);\n }\n },\n { // NaN, Inf, -Inf. (These are the only objects with typeof !== 'object'\n // which we match.)\n matchJSONValue: function (obj) {\n return _.has(obj, '$InfNaN') && _.size(obj) === 1;\n },\n matchObject: isInfOrNan,\n toJSONValue: function (obj) {\n var sign;\n if (_.isNaN(obj))\n sign = 0;\n else if (obj === Infinity)\n sign = 1;\n else\n sign = -1;\n return {$InfNaN: sign};\n },\n fromJSONValue: function (obj) {\n return obj.$InfNaN/0;\n }\n },\n { // Binary\n matchJSONValue: function (obj) {\n return _.has(obj, '$binary') && _.size(obj) === 1;\n },\n matchObject: function (obj) {\n return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array\n || (obj && _.has(obj, '$Uint8ArrayPolyfill'));\n },\n toJSONValue: function (obj) {\n return {$binary: Base64.encode(obj)};\n },\n fromJSONValue: function (obj) {\n return Base64.decode(obj.$binary);\n }\n },\n { // Escaping one level\n matchJSONValue: function (obj) {\n return _.has(obj, '$escape') && _.size(obj) === 1;\n },\n matchObject: function (obj) {\n if (_.isEmpty(obj) || _.size(obj) > 2) {\n return false;\n }\n return _.any(builtinConverters, function (converter) {\n return converter.matchJSONValue(obj);\n });\n },\n toJSONValue: function (obj) {\n var newObj = {};\n _.each(obj, function (value, key) {\n newObj[key] = EJSON.toJSONValue(value);\n });\n return {$escape: newObj};\n },\n fromJSONValue: function (obj) {\n var newObj = {};\n _.each(obj.$escape, function (value, key) {\n newObj[key] = EJSON.fromJSONValue(value);\n });\n return newObj;\n }\n },\n { // Custom\n matchJSONValue: function (obj) {\n return _.has(obj, '$type') && _.has(obj, '$value') && _.size(obj) === 2;\n },\n matchObject: function (obj) {\n return EJSON._isCustomType(obj);\n },\n toJSONValue: function (obj) {\n var jsonValue = Meteor._noYieldsAllowed(function () {\n return obj.toJSONValue();\n });\n return {$type: obj.typeName(), $value: jsonValue};\n },\n fromJSONValue: function (obj) {\n var typeName = obj.$type;\n if (!_.has(customTypes, typeName))\n throw new Error(\"Custom EJSON type \" + typeName + \" is not defined\");\n var converter = customTypes[typeName];\n return Meteor._noYieldsAllowed(function () {\n return converter(obj.$value);\n });\n }\n }\n];\n\nEJSON._isCustomType = function (obj) {\n return obj &&\n typeof obj.toJSONValue === 'function' &&\n typeof obj.typeName === 'function' &&\n _.has(customTypes, obj.typeName());\n};\n\n\n// for both arrays and objects, in-place modification.\nvar adjustTypesToJSONValue =\nEJSON._adjustTypesToJSONValue = function (obj) {\n // Is it an atom that we need to adjust?\n if (obj === null)\n return null;\n var maybeChanged = toJSONValueHelper(obj);\n if (maybeChanged !== undefined)\n return maybeChanged;\n\n // Other atoms are unchanged.\n if (typeof obj !== 'object')\n return obj;\n\n // Iterate over array or object structure.\n _.each(obj, function (value, key) {\n if (typeof value !== 'object' && value !== undefined &&\n !isInfOrNan(value))\n return; // continue\n\n var changed = toJSONValueHelper(value);\n if (changed) {\n obj[key] = changed;\n return; // on to the next key\n }\n // if we get here, value is an object but not adjustable\n // at this level. recurse.\n adjustTypesToJSONValue(value);\n });\n return obj;\n};\n\n// Either return the JSON-compatible version of the argument, or undefined (if\n// the item isn't itself replaceable, but maybe some fields in it are)\nvar toJSONValueHelper = function (item) {\n for (var i = 0; i < builtinConverters.length; i++) {\n var converter = builtinConverters[i];\n if (converter.matchObject(item)) {\n return converter.toJSONValue(item);\n }\n }\n return undefined;\n};\n\n/**\n * @summary Serialize an EJSON-compatible value into its plain JSON representation.\n * @locus Anywhere\n * @param {EJSON} val A value to serialize to plain JSON.\n */\nEJSON.toJSONValue = function (item) {\n var changed = toJSONValueHelper(item);\n if (changed !== undefined)\n return changed;\n if (typeof item === 'object') {\n item = EJSON.clone(item);\n adjustTypesToJSONValue(item);\n }\n return item;\n};\n\n// for both arrays and objects. Tries its best to just\n// use the object you hand it, but may return something\n// different if the object you hand it itself needs changing.\n//\nvar adjustTypesFromJSONValue =\nEJSON._adjustTypesFromJSONValue = function (obj) {\n if (obj === null)\n return null;\n var maybeChanged = fromJSONValueHelper(obj);\n if (maybeChanged !== obj)\n return maybeChanged;\n\n // Other atoms are unchanged.\n if (typeof obj !== 'object')\n return obj;\n\n _.each(obj, function (value, key) {\n if (typeof value === 'object') {\n var changed = fromJSONValueHelper(value);\n if (value !== changed) {\n obj[key] = changed;\n return;\n }\n // if we get here, value is an object but not adjustable\n // at this level. recurse.\n adjustTypesFromJSONValue(value);\n }\n });\n return obj;\n};\n\n// Either return the argument changed to have the non-json\n// rep of itself (the Object version) or the argument itself.\n\n// DOES NOT RECURSE. For actually getting the fully-changed value, use\n// EJSON.fromJSONValue\nvar fromJSONValueHelper = function (value) {\n if (typeof value === 'object' && value !== null) {\n if (_.size(value) <= 2\n && _.all(value, function (v, k) {\n return typeof k === 'string' && k.substr(0, 1) === '$';\n })) {\n for (var i = 0; i < builtinConverters.length; i++) {\n var converter = builtinConverters[i];\n if (converter.matchJSONValue(value)) {\n return converter.fromJSONValue(value);\n }\n }\n }\n }\n return value;\n};\n\n/**\n * @summary Deserialize an EJSON value from its plain JSON representation.\n * @locus Anywhere\n * @param {JSONCompatible} val A value to deserialize into EJSON.\n */\nEJSON.fromJSONValue = function (item) {\n var changed = fromJSONValueHelper(item);\n if (changed === item && typeof item === 'object') {\n item = EJSON.clone(item);\n adjustTypesFromJSONValue(item);\n return item;\n } else {\n return changed;\n }\n};\n\n/**\n * @summary Serialize a value to a string.\n\nFor EJSON values, the serialization fully represents the value. For non-EJSON values, serializes the same way as `JSON.stringify`.\n * @locus Anywhere\n * @param {EJSON} val A value to stringify.\n * @param {Object} [options]\n * @param {Boolean | Integer | String} options.indent Indents objects and arrays for easy readability. When `true`, indents by 2 spaces; when an integer, indents by that number of spaces; and when a string, uses the string as the indentation pattern.\n * @param {Boolean} options.canonical When `true`, stringifies keys in an object in sorted order.\n */\nEJSON.stringify = function (item, options) {\n var json = EJSON.toJSONValue(item);\n if (options && (options.canonical || options.indent)) {\n return EJSON._canonicalStringify(json, options);\n } else {\n return JSON.stringify(json);\n }\n};\n\n/**\n * @summary Parse a string into an EJSON value. Throws an error if the string is not valid EJSON.\n * @locus Anywhere\n * @param {String} str A string to parse into an EJSON value.\n */\nEJSON.parse = function (item) {\n if (typeof item !== 'string')\n throw new Error(\"EJSON.parse argument should be a string\");\n return EJSON.fromJSONValue(JSON.parse(item));\n};\n\n/**\n * @summary Returns true if `x` is a buffer of binary data, as returned from [`EJSON.newBinary`](#ejson_new_binary).\n * @param {Object} x The variable to check.\n * @locus Anywhere\n */\nEJSON.isBinary = function (obj) {\n return !!((typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array) ||\n (obj && obj.$Uint8ArrayPolyfill));\n};\n\n/**\n * @summary Return true if `a` and `b` are equal to each other. Return false otherwise. Uses the `equals` method on `a` if present, otherwise performs a deep comparison.\n * @locus Anywhere\n * @param {EJSON} a\n * @param {EJSON} b\n * @param {Object} [options]\n * @param {Boolean} options.keyOrderSensitive Compare in key sensitive order, if supported by the JavaScript implementation. For example, `{a: 1, b: 2}` is equal to `{b: 2, a: 1}` only when `keyOrderSensitive` is `false`. The default is `false`.\n */\nEJSON.equals = function (a, b, options) {\n var i;\n var keyOrderSensitive = !!(options && options.keyOrderSensitive);\n if (a === b)\n return true;\n if (_.isNaN(a) && _.isNaN(b))\n return true; // This differs from the IEEE spec for NaN equality, b/c we don't want\n // anything ever with a NaN to be poisoned from becoming equal to anything.\n if (!a || !b) // if either one is falsy, they'd have to be === to be equal\n return false;\n if (!(typeof a === 'object' && typeof b === 'object'))\n return false;\n if (a instanceof Date && b instanceof Date)\n return a.valueOf() === b.valueOf();\n if (EJSON.isBinary(a) && EJSON.isBinary(b)) {\n if (a.length !== b.length)\n return false;\n for (i = 0; i < a.length; i++) {\n if (a[i] !== b[i])\n return false;\n }\n return true;\n }\n if (typeof (a.equals) === 'function')\n return a.equals(b, options);\n if (typeof (b.equals) === 'function')\n return b.equals(a, options);\n if (a instanceof Array) {\n if (!(b instanceof Array))\n return false;\n if (a.length !== b.length)\n return false;\n for (i = 0; i < a.length; i++) {\n if (!EJSON.equals(a[i], b[i], options))\n return false;\n }\n return true;\n }\n // fallback for custom types that don't implement their own equals\n switch (EJSON._isCustomType(a) + EJSON._isCustomType(b)) {\n case 1: return false;\n case 2: return EJSON.equals(EJSON.toJSONValue(a), EJSON.toJSONValue(b));\n }\n // fall back to structural equality of objects\n var ret;\n if (keyOrderSensitive) {\n var bKeys = [];\n _.each(b, function (val, x) {\n bKeys.push(x);\n });\n i = 0;\n ret = _.all(a, function (val, x) {\n if (i >= bKeys.length) {\n return false;\n }\n if (x !== bKeys[i]) {\n return false;\n }\n if (!EJSON.equals(val, b[bKeys[i]], options)) {\n return false;\n }\n i++;\n return true;\n });\n return ret && i === bKeys.length;\n } else {\n i = 0;\n ret = _.all(a, function (val, key) {\n if (!_.has(b, key)) {\n return false;\n }\n if (!EJSON.equals(val, b[key], options)) {\n return false;\n }\n i++;\n return true;\n });\n return ret && _.size(b) === i;\n }\n};\n\n/**\n * @summary Return a deep copy of `val`.\n * @locus Anywhere\n * @param {EJSON} val A value to copy.\n */\nEJSON.clone = function (v) {\n var ret;\n if (typeof v !== \"object\")\n return v;\n if (v === null)\n return null; // null has typeof \"object\"\n if (v instanceof Date)\n return new Date(v.getTime());\n // RegExps are not really EJSON elements (eg we don't define a serialization\n // for them), but they're immutable anyway, so we can support them in clone.\n if (v instanceof RegExp)\n return v;\n if (EJSON.isBinary(v)) {\n ret = EJSON.newBinary(v.length);\n for (var i = 0; i < v.length; i++) {\n ret[i] = v[i];\n }\n return ret;\n }\n // XXX: Use something better than underscore's isArray\n if (_.isArray(v) || _.isArguments(v)) {\n // For some reason, _.map doesn't work in this context on Opera (weird test\n // failures).\n ret = [];\n for (i = 0; i < v.length; i++)\n ret[i] = EJSON.clone(v[i]);\n return ret;\n }\n // handle general user-defined typed Objects if they have a clone method\n if (typeof v.clone === 'function') {\n return v.clone();\n }\n // handle other custom types\n if (EJSON._isCustomType(v)) {\n return EJSON.fromJSONValue(EJSON.clone(EJSON.toJSONValue(v)), true);\n }\n // handle other objects\n ret = {};\n _.each(v, function (value, key) {\n ret[key] = EJSON.clone(value);\n });\n return ret;\n};\n\n/**\n * @summary Allocate a new buffer of binary data that EJSON can serialize.\n * @locus Anywhere\n * @param {Number} size The number of bytes of binary data to allocate.\n */\n// EJSON.newBinary is the public documented API for this functionality,\n// but the implementation is in the 'base64' package to avoid\n// introducing a circular dependency. (If the implementation were here,\n// then 'base64' would have to use EJSON.newBinary, and 'ejson' would\n// also have to use 'base64'.)\nEJSON.newBinary = Base64.newBinary;\n","// Based on json2.js from https://github.com/douglascrockford/JSON-js\n//\n// json2.js\n// 2012-10-08\n//\n// Public Domain.\n//\n// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n\nfunction quote(string) {\n return JSON.stringify(string);\n}\n\nvar str = function (key, holder, singleIndent, outerIndent, canonical) {\n\n // Produce a string from holder[key].\n\n var i; // The loop counter.\n var k; // The member key.\n var v; // The member value.\n var length;\n var innerIndent = outerIndent;\n var partial;\n var value = holder[key];\n\n // What happens next depends on the value's type.\n\n switch (typeof value) {\n case 'string':\n return quote(value);\n case 'number':\n // JSON numbers must be finite. Encode non-finite numbers as null.\n return isFinite(value) ? String(value) : 'null';\n case 'boolean':\n return String(value);\n // If the type is 'object', we might be dealing with an object or an array or\n // null.\n case 'object':\n // Due to a specification blunder in ECMAScript, typeof null is 'object',\n // so watch out for that case.\n if (!value) {\n return 'null';\n }\n // Make an array to hold the partial results of stringifying this object value.\n innerIndent = outerIndent + singleIndent;\n partial = [];\n\n // Is the value an array?\n if (_.isArray(value) || _.isArguments(value)) {\n\n // The value is an array. Stringify every element. Use null as a placeholder\n // for non-JSON values.\n\n length = value.length;\n for (i = 0; i < length; i += 1) {\n partial[i] = str(i, value, singleIndent, innerIndent, canonical) || 'null';\n }\n\n // Join all of the elements together, separated with commas, and wrap them in\n // brackets.\n\n if (partial.length === 0) {\n v = '[]';\n } else if (innerIndent) {\n v = '[\\n' + innerIndent + partial.join(',\\n' + innerIndent) + '\\n' + outerIndent + ']';\n } else {\n v = '[' + partial.join(',') + ']';\n }\n return v;\n }\n\n\n // Iterate through all of the keys in the object.\n var keys = _.keys(value);\n if (canonical)\n keys = keys.sort();\n _.each(keys, function (k) {\n v = str(k, value, singleIndent, innerIndent, canonical);\n if (v) {\n partial.push(quote(k) + (innerIndent ? ': ' : ':') + v);\n }\n });\n\n\n // Join all of the member texts together, separated with commas,\n // and wrap them in braces.\n\n if (partial.length === 0) {\n v = '{}';\n } else if (innerIndent) {\n v = '{\\n' + innerIndent + partial.join(',\\n' + innerIndent) + '\\n' + outerIndent + '}';\n } else {\n v = '{' + partial.join(',') + '}';\n }\n return v;\n }\n}\n\n// If the JSON object does not yet have a stringify method, give it one.\n\nEJSON._canonicalStringify = function (value, options) {\n // Make a fake root object containing our value under the key of ''.\n // Return the result of stringifying the value.\n options = _.extend({\n indent: \"\",\n canonical: false\n }, options);\n if (options.indent === true) {\n options.indent = \" \";\n } else if (typeof options.indent === 'number') {\n var newIndent = \"\";\n for (var i = 0; i < options.indent; i++) {\n newIndent += ' ';\n }\n options.indent = newIndent;\n }\n return str('', {'': value}, options.indent, \"\", options.canonical);\n};\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js b/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js
new file mode 100644
index 0000000..ddcec2c
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js
@@ -0,0 +1,455 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+
+/* Package-scope variables */
+var GeoJSON, module;
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/geojson-utils/pre.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// Define an object named exports. This will cause geojson-utils.js to put `gju` // 1
+// as a field on it, instead of in the global namespace. See also post.js. // 2
+module = {exports:{}}; // 3
+ // 4
+ // 5
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/geojson-utils/geojson-utils.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+(function () { // 1
+ var gju = {}; // 2
+ // 3
+ // Export the geojson object for **CommonJS** // 4
+ if (typeof module !== 'undefined' && module.exports) { // 5
+ module.exports = gju; // 6
+ } // 7
+ // 8
+ // adapted from http://www.kevlindev.com/gui/math/intersection/Intersection.js // 9
+ gju.lineStringsIntersect = function (l1, l2) { // 10
+ var intersects = []; // 11
+ for (var i = 0; i <= l1.coordinates.length - 2; ++i) { // 12
+ for (var j = 0; j <= l2.coordinates.length - 2; ++j) { // 13
+ var a1 = { // 14
+ x: l1.coordinates[i][1], // 15
+ y: l1.coordinates[i][0] // 16
+ }, // 17
+ a2 = { // 18
+ x: l1.coordinates[i + 1][1], // 19
+ y: l1.coordinates[i + 1][0] // 20
+ }, // 21
+ b1 = { // 22
+ x: l2.coordinates[j][1], // 23
+ y: l2.coordinates[j][0] // 24
+ }, // 25
+ b2 = { // 26
+ x: l2.coordinates[j + 1][1], // 27
+ y: l2.coordinates[j + 1][0] // 28
+ }, // 29
+ ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), // 30
+ ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x), // 31
+ u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); // 32
+ if (u_b != 0) { // 33
+ var ua = ua_t / u_b, // 34
+ ub = ub_t / u_b; // 35
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) { // 36
+ intersects.push({ // 37
+ 'type': 'Point', // 38
+ 'coordinates': [a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)] // 39
+ }); // 40
+ } // 41
+ } // 42
+ } // 43
+ } // 44
+ if (intersects.length == 0) intersects = false; // 45
+ return intersects; // 46
+ } // 47
+ // 48
+ // Bounding Box // 49
+ // 50
+ function boundingBoxAroundPolyCoords (coords) { // 51
+ var xAll = [], yAll = [] // 52
+ // 53
+ for (var i = 0; i < coords[0].length; i++) { // 54
+ xAll.push(coords[0][i][1]) // 55
+ yAll.push(coords[0][i][0]) // 56
+ } // 57
+ // 58
+ xAll = xAll.sort(function (a,b) { return a - b }) // 59
+ yAll = yAll.sort(function (a,b) { return a - b }) // 60
+ // 61
+ return [ [xAll[0], yAll[0]], [xAll[xAll.length - 1], yAll[yAll.length - 1]] ] // 62
+ } // 63
+ // 64
+ gju.pointInBoundingBox = function (point, bounds) { // 65
+ return !(point.coordinates[1] < bounds[0][0] || point.coordinates[1] > bounds[1][0] || point.coordinates[0] < bounds[0][1] || point.coordinates[0] > bounds[1][1])
+ } // 67
+ // 68
+ // Point in Polygon // 69
+ // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#Listing the Vertices // 70
+ // 71
+ function pnpoly (x,y,coords) { // 72
+ var vert = [ [0,0] ] // 73
+ // 74
+ for (var i = 0; i < coords.length; i++) { // 75
+ for (var j = 0; j < coords[i].length; j++) { // 76
+ vert.push(coords[i][j]) // 77
+ } // 78
+ vert.push([0,0]) // 79
+ } // 80
+ // 81
+ var inside = false // 82
+ for (var i = 0, j = vert.length - 1; i < vert.length; j = i++) { // 83
+ if (((vert[i][0] > y) != (vert[j][0] > y)) && (x < (vert[j][1] - vert[i][1]) * (y - vert[i][0]) / (vert[j][0] - vert[i][0]) + vert[i][1])) inside = !inside
+ } // 85
+ // 86
+ return inside // 87
+ } // 88
+ // 89
+ gju.pointInPolygon = function (p, poly) { // 90
+ var coords = (poly.type == "Polygon") ? [ poly.coordinates ] : poly.coordinates // 91
+ // 92
+ var insideBox = false // 93
+ for (var i = 0; i < coords.length; i++) { // 94
+ if (gju.pointInBoundingBox(p, boundingBoxAroundPolyCoords(coords[i]))) insideBox = true // 95
+ } // 96
+ if (!insideBox) return false // 97
+ // 98
+ var insidePoly = false // 99
+ for (var i = 0; i < coords.length; i++) { // 100
+ if (pnpoly(p.coordinates[1], p.coordinates[0], coords[i])) insidePoly = true // 101
+ } // 102
+ // 103
+ return insidePoly // 104
+ } // 105
+ // 106
+ gju.numberToRadius = function (number) { // 107
+ return number * Math.PI / 180; // 108
+ } // 109
+ // 110
+ gju.numberToDegree = function (number) { // 111
+ return number * 180 / Math.PI; // 112
+ } // 113
+ // 114
+ // written with help from @tautologe // 115
+ gju.drawCircle = function (radiusInMeters, centerPoint, steps) { // 116
+ var center = [centerPoint.coordinates[1], centerPoint.coordinates[0]], // 117
+ dist = (radiusInMeters / 1000) / 6371, // 118
+ // convert meters to radiant // 119
+ radCenter = [gju.numberToRadius(center[0]), gju.numberToRadius(center[1])], // 120
+ steps = steps || 15, // 121
+ // 15 sided circle // 122
+ poly = [[center[0], center[1]]]; // 123
+ for (var i = 0; i < steps; i++) { // 124
+ var brng = 2 * Math.PI * i / steps; // 125
+ var lat = Math.asin(Math.sin(radCenter[0]) * Math.cos(dist) // 126
+ + Math.cos(radCenter[0]) * Math.sin(dist) * Math.cos(brng)); // 127
+ var lng = radCenter[1] + Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(radCenter[0]), // 128
+ Math.cos(dist) - Math.sin(radCenter[0]) * Math.sin(lat)); // 129
+ poly[i] = []; // 130
+ poly[i][1] = gju.numberToDegree(lat); // 131
+ poly[i][0] = gju.numberToDegree(lng); // 132
+ } // 133
+ return { // 134
+ "type": "Polygon", // 135
+ "coordinates": [poly] // 136
+ }; // 137
+ } // 138
+ // 139
+ // assumes rectangle starts at lower left point // 140
+ gju.rectangleCentroid = function (rectangle) { // 141
+ var bbox = rectangle.coordinates[0]; // 142
+ var xmin = bbox[0][0], // 143
+ ymin = bbox[0][1], // 144
+ xmax = bbox[2][0], // 145
+ ymax = bbox[2][1]; // 146
+ var xwidth = xmax - xmin; // 147
+ var ywidth = ymax - ymin; // 148
+ return { // 149
+ 'type': 'Point', // 150
+ 'coordinates': [xmin + xwidth / 2, ymin + ywidth / 2] // 151
+ }; // 152
+ } // 153
+ // 154
+ // from http://www.movable-type.co.uk/scripts/latlong.html // 155
+ gju.pointDistance = function (pt1, pt2) { // 156
+ var lon1 = pt1.coordinates[0], // 157
+ lat1 = pt1.coordinates[1], // 158
+ lon2 = pt2.coordinates[0], // 159
+ lat2 = pt2.coordinates[1], // 160
+ dLat = gju.numberToRadius(lat2 - lat1), // 161
+ dLon = gju.numberToRadius(lon2 - lon1), // 162
+ a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(gju.numberToRadius(lat1)) // 163
+ * Math.cos(gju.numberToRadius(lat2)) * Math.pow(Math.sin(dLon / 2), 2), // 164
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // 165
+ // Earth radius is 6371 km // 166
+ return (6371 * c) * 1000; // returns meters // 167
+ }, // 168
+ // 169
+ // checks if geometry lies entirely within a circle // 170
+ // works with Point, LineString, Polygon // 171
+ gju.geometryWithinRadius = function (geometry, center, radius) { // 172
+ if (geometry.type == 'Point') { // 173
+ return gju.pointDistance(geometry, center) <= radius; // 174
+ } else if (geometry.type == 'LineString' || geometry.type == 'Polygon') { // 175
+ var point = {}; // 176
+ var coordinates; // 177
+ if (geometry.type == 'Polygon') { // 178
+ // it's enough to check the exterior ring of the Polygon // 179
+ coordinates = geometry.coordinates[0]; // 180
+ } else { // 181
+ coordinates = geometry.coordinates; // 182
+ } // 183
+ for (var i in coordinates) { // 184
+ point.coordinates = coordinates[i]; // 185
+ if (gju.pointDistance(point, center) > radius) { // 186
+ return false; // 187
+ } // 188
+ } // 189
+ } // 190
+ return true; // 191
+ } // 192
+ // 193
+ // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt // 194
+ gju.area = function (polygon) { // 195
+ var area = 0; // 196
+ // TODO: polygon holes at coordinates[1] // 197
+ var points = polygon.coordinates[0]; // 198
+ var j = points.length - 1; // 199
+ var p1, p2; // 200
+ // 201
+ for (var i = 0; i < points.length; j = i++) { // 202
+ var p1 = { // 203
+ x: points[i][1], // 204
+ y: points[i][0] // 205
+ }; // 206
+ var p2 = { // 207
+ x: points[j][1], // 208
+ y: points[j][0] // 209
+ }; // 210
+ area += p1.x * p2.y; // 211
+ area -= p1.y * p2.x; // 212
+ } // 213
+ // 214
+ area /= 2; // 215
+ return area; // 216
+ }, // 217
+ // 218
+ // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt // 219
+ gju.centroid = function (polygon) { // 220
+ var f, x = 0, // 221
+ y = 0; // 222
+ // TODO: polygon holes at coordinates[1] // 223
+ var points = polygon.coordinates[0]; // 224
+ var j = points.length - 1; // 225
+ var p1, p2; // 226
+ // 227
+ for (var i = 0; i < points.length; j = i++) { // 228
+ var p1 = { // 229
+ x: points[i][1], // 230
+ y: points[i][0] // 231
+ }; // 232
+ var p2 = { // 233
+ x: points[j][1], // 234
+ y: points[j][0] // 235
+ }; // 236
+ f = p1.x * p2.y - p2.x * p1.y; // 237
+ x += (p1.x + p2.x) * f; // 238
+ y += (p1.y + p2.y) * f; // 239
+ } // 240
+ // 241
+ f = gju.area(polygon) * 6; // 242
+ return { // 243
+ 'type': 'Point', // 244
+ 'coordinates': [y / f, x / f] // 245
+ }; // 246
+ }, // 247
+ // 248
+ gju.simplify = function (source, kink) { /* source[] array of geojson points */ // 249
+ /* kink in metres, kinks above this depth kept */ // 250
+ /* kink depth is the height of the triangle abc where a-b and b-c are two consecutive line segments */ // 251
+ kink = kink || 20; // 252
+ source = source.map(function (o) { // 253
+ return { // 254
+ lng: o.coordinates[0], // 255
+ lat: o.coordinates[1] // 256
+ } // 257
+ }); // 258
+ // 259
+ var n_source, n_stack, n_dest, start, end, i, sig; // 260
+ var dev_sqr, max_dev_sqr, band_sqr; // 261
+ var x12, y12, d12, x13, y13, d13, x23, y23, d23; // 262
+ var F = (Math.PI / 180.0) * 0.5; // 263
+ var index = new Array(); /* aray of indexes of source points to include in the reduced line */ // 264
+ var sig_start = new Array(); /* indices of start & end of working section */ // 265
+ var sig_end = new Array(); // 266
+ // 267
+ /* check for simple cases */ // 268
+ // 269
+ if (source.length < 3) return (source); /* one or two points */ // 270
+ // 271
+ /* more complex case. initialize stack */ // 272
+ // 273
+ n_source = source.length; // 274
+ band_sqr = kink * 360.0 / (2.0 * Math.PI * 6378137.0); /* Now in degrees */ // 275
+ band_sqr *= band_sqr; // 276
+ n_dest = 0; // 277
+ sig_start[0] = 0; // 278
+ sig_end[0] = n_source - 1; // 279
+ n_stack = 1; // 280
+ // 281
+ /* while the stack is not empty ... */ // 282
+ while (n_stack > 0) { // 283
+ // 284
+ /* ... pop the top-most entries off the stacks */ // 285
+ // 286
+ start = sig_start[n_stack - 1]; // 287
+ end = sig_end[n_stack - 1]; // 288
+ n_stack--; // 289
+ // 290
+ if ((end - start) > 1) { /* any intermediate points ? */ // 291
+ // 292
+ /* ... yes, so find most deviant intermediate point to // 293
+ either side of line joining start & end points */ // 294
+ // 295
+ x12 = (source[end].lng() - source[start].lng()); // 296
+ y12 = (source[end].lat() - source[start].lat()); // 297
+ if (Math.abs(x12) > 180.0) x12 = 360.0 - Math.abs(x12); // 298
+ x12 *= Math.cos(F * (source[end].lat() + source[start].lat())); /* use avg lat to reduce lng */ // 299
+ d12 = (x12 * x12) + (y12 * y12); // 300
+ // 301
+ for (i = start + 1, sig = start, max_dev_sqr = -1.0; i < end; i++) { // 302
+ // 303
+ x13 = source[i].lng() - source[start].lng(); // 304
+ y13 = source[i].lat() - source[start].lat(); // 305
+ if (Math.abs(x13) > 180.0) x13 = 360.0 - Math.abs(x13); // 306
+ x13 *= Math.cos(F * (source[i].lat() + source[start].lat())); // 307
+ d13 = (x13 * x13) + (y13 * y13); // 308
+ // 309
+ x23 = source[i].lng() - source[end].lng(); // 310
+ y23 = source[i].lat() - source[end].lat(); // 311
+ if (Math.abs(x23) > 180.0) x23 = 360.0 - Math.abs(x23); // 312
+ x23 *= Math.cos(F * (source[i].lat() + source[end].lat())); // 313
+ d23 = (x23 * x23) + (y23 * y23); // 314
+ // 315
+ if (d13 >= (d12 + d23)) dev_sqr = d23; // 316
+ else if (d23 >= (d12 + d13)) dev_sqr = d13; // 317
+ else dev_sqr = (x13 * y12 - y13 * x12) * (x13 * y12 - y13 * x12) / d12; // solve triangle // 318
+ if (dev_sqr > max_dev_sqr) { // 319
+ sig = i; // 320
+ max_dev_sqr = dev_sqr; // 321
+ } // 322
+ } // 323
+ // 324
+ if (max_dev_sqr < band_sqr) { /* is there a sig. intermediate point ? */ // 325
+ /* ... no, so transfer current start point */ // 326
+ index[n_dest] = start; // 327
+ n_dest++; // 328
+ } else { /* ... yes, so push two sub-sections on stack for further processing */ // 329
+ n_stack++; // 330
+ sig_start[n_stack - 1] = sig; // 331
+ sig_end[n_stack - 1] = end; // 332
+ n_stack++; // 333
+ sig_start[n_stack - 1] = start; // 334
+ sig_end[n_stack - 1] = sig; // 335
+ } // 336
+ } else { /* ... no intermediate points, so transfer current start point */ // 337
+ index[n_dest] = start; // 338
+ n_dest++; // 339
+ } // 340
+ } // 341
+ // 342
+ /* transfer last point */ // 343
+ index[n_dest] = n_source - 1; // 344
+ n_dest++; // 345
+ // 346
+ /* make return array */ // 347
+ var r = new Array(); // 348
+ for (var i = 0; i < n_dest; i++) // 349
+ r.push(source[index[i]]); // 350
+ // 351
+ return r.map(function (o) { // 352
+ return { // 353
+ type: "Point", // 354
+ coordinates: [o.lng, o.lat] // 355
+ } // 356
+ }); // 357
+ } // 358
+ // 359
+ // http://www.movable-type.co.uk/scripts/latlong.html#destPoint // 360
+ gju.destinationPoint = function (pt, brng, dist) { // 361
+ dist = dist/6371; // convert dist to angular distance in radians // 362
+ brng = gju.numberToRadius(brng); // 363
+ // 364
+ var lat1 = gju.numberToRadius(pt.coordinates[0]); // 365
+ var lon1 = gju.numberToRadius(pt.coordinates[1]); // 366
+ // 367
+ var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + // 368
+ Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) ); // 369
+ var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1), // 370
+ Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); // 371
+ lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º // 372
+ // 373
+ return { // 374
+ 'type': 'Point', // 375
+ 'coordinates': [gju.numberToDegree(lat2), gju.numberToDegree(lon2)] // 376
+ }; // 377
+ }; // 378
+ // 379
+})(); // 380
+ // 381
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+
+
+
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/geojson-utils/post.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+// This exports object was created in pre.js. Now copy the `exports` object // 1
+// from it into the package-scope variable `GeoJSON`, which will get exported. // 2
+GeoJSON = module.exports; // 3
+ // 4
+ // 5
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}).call(this);
+
+
+/* Exports */
+if (typeof Package === 'undefined') Package = {};
+Package['geojson-utils'] = {
+ GeoJSON: GeoJSON
+};
+
+})();
+
+//# sourceMappingURL=geojson-utils.js.map
diff --git a/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js.map b/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js.map
new file mode 100644
index 0000000..7e9d955
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/geojson-utils.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["geojson-utils/pre.js","geojson-utils/geojson-utils.js","geojson-utils/post.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gF;AACA,2E;AACA,sB;;;;;;;;;;;;;;;;;;;;ACFA,c;AACA,e;;AAEA,+C;AACA,wD;AACA,yB;AACA,G;;AAEA,gF;AACA,gD;AACA,wB;AACA,0D;AACA,4D;AACA,kB;AACA,kC;AACA,iC;AACA,U;AACA,gB;AACA,wC;AACA,uC;AACA,Y;AACA,gB;AACA,oC;AACA,mC;AACA,Y;AACA,gB;AACA,wC;AACA,uC;AACA,Y;AACA,+E;AACA,+E;AACA,8E;AACA,uB;AACA,8B;AACA,4B;AACA,yD;AACA,6B;AACA,8B;AACA,mF;AACA,e;AACA,W;AACA,S;AACA,O;AACA,K;AACA,mD;AACA,sB;AACA,G;;AAEA,iB;;AAEA,iD;AACA,4B;;AAEA,gD;AACA,gC;AACA,gC;AACA,K;;AAEA,qD;AACA,qD;;AAEA,iF;AACA,G;;AAEA,qD;AACA,uK;AACA,G;;AAEA,qB;AACA,gG;;AAEA,gC;AACA,wB;;AAEA,6C;AACA,kD;AACA,+B;AACA,O;AACA,sB;AACA,K;;AAEA,sB;AACA,oE;AACA,iK;AACA,K;;AAEA,iB;AACA,G;;AAEA,2C;AACA,mF;;AAEA,yB;AACA,6C;AACA,6F;AACA,K;AACA,gC;;AAEA,0B;AACA,6C;AACA,kF;AACA,K;;AAEA,qB;AACA,G;;AAEA,0C;AACA,kC;AACA,G;;AAEA,0C;AACA,kC;AACA,G;;AAEA,sC;AACA,kE;AACA,0E;AACA,4C;AACA,kC;AACA,iF;AACA,0B;AACA,wB;AACA,sC;AACA,qC;AACA,yC;AACA,iE;AACA,0E;AACA,mG;AACA,mG;AACA,mB;AACA,2C;AACA,2C;AACA,K;AACA,Y;AACA,wB;AACA,2B;AACA,M;AACA,G;;AAEA,iD;AACA,gD;AACA,wC;AACA,0B;AACA,wB;AACA,wB;AACA,wB;AACA,6B;AACA,6B;AACA,Y;AACA,sB;AACA,2D;AACA,M;AACA,G;;AAEA,4D;AACA,2C;AACA,kC;AACA,gC;AACA,gC;AACA,gC;AACA,6C;AACA,6C;AACA,8E;AACA,+E;AACA,yD;AACA,8B;AACA,+C;AACA,I;;AAEA,qD;AACA,0C;AACA,kE;AACA,mC;AACA,2D;AACA,6E;AACA,qB;AACA,sB;AACA,uC;AACA,gE;AACA,8C;AACA,c;AACA,2C;AACA,O;AACA,kC;AACA,2C;AACA,wD;AACA,uB;AACA,S;AACA,O;AACA,K;AACA,gB;AACA,G;;AAEA,wE;AACA,iC;AACA,iB;AACA,4C;AACA,wC;AACA,8B;AACA,e;;AAEA,iD;AACA,gB;AACA,wB;AACA,uB;AACA,Q;AACA,gB;AACA,wB;AACA,uB;AACA,Q;AACA,0B;AACA,0B;AACA,K;;AAEA,c;AACA,gB;AACA,I;;AAEA,wE;AACA,qC;AACA,iB;AACA,Y;AACA,4C;AACA,wC;AACA,8B;AACA,e;;AAEA,iD;AACA,gB;AACA,wB;AACA,uB;AACA,Q;AACA,gB;AACA,wB;AACA,uB;AACA,Q;AACA,oC;AACA,6B;AACA,6B;AACA,K;;AAEA,8B;AACA,Y;AACA,sB;AACA,mC;AACA,M;AACA,I;;AAEA,iF;AACA,sD;AACA,0G;AACA,sB;AACA,sC;AACA,c;AACA,8B;AACA,6B;AACA,O;AACA,O;;AAEA,sD;AACA,uC;AACA,oD;AACA,oC;AACA,kG;AACA,gF;AACA,8B;;AAEA,gC;;AAEA,mE;;AAEA,6C;;AAEA,6B;AACA,+E;AACA,yB;AACA,e;AACA,qB;AACA,8B;AACA,gB;;AAEA,2C;AACA,yB;;AAEA,uD;;AAEA,qC;AACA,iC;AACA,gB;;AAEA,8D;;AAEA,8D;AACA,yD;;AAEA,wD;AACA,wD;AACA,+D;AACA,uG;AACA,wC;;AAEA,4E;;AAEA,sD;AACA,sD;AACA,iE;AACA,uE;AACA,0C;;AAEA,oD;AACA,oD;AACA,iE;AACA,qE;AACA,0C;;AAEA,gD;AACA,qD;AACA,mG;AACA,sC;AACA,oB;AACA,kC;AACA,W;AACA,S;;AAEA,gF;AACA,uD;AACA,gC;AACA,mB;AACA,wF;AACA,oB;AACA,uC;AACA,qC;AACA,oB;AACA,yC;AACA,qC;AACA,S;AACA,gF;AACA,8B;AACA,iB;AACA,O;AACA,K;;AAEA,6B;AACA,iC;AACA,a;;AAEA,2B;AACA,wB;AACA,oC;AACA,+B;;AAEA,+B;AACA,c;AACA,sB;AACA,mC;AACA,O;AACA,O;AACA,G;;AAEA,iE;AACA,oD;AACA,qE;AACA,oC;;AAEA,qD;AACA,qD;;AAEA,yD;AACA,yE;AACA,8E;AACA,+E;AACA,iF;;AAEA,Y;AACA,sB;AACA,yE;AACA,M;AACA,I;;AAEA,K;;;;;;;;;;;;;;;;;;;AC3XA,4E;AACA,8E;AACA,yB","file":"/packages/geojson-utils.js","sourcesContent":["// Define an object named exports. This will cause geojson-utils.js to put `gju`\n// as a field on it, instead of in the global namespace. See also post.js.\nmodule = {exports:{}};\n\n","(function () {\n var gju = {};\n\n // Export the geojson object for **CommonJS**\n if (typeof module !== 'undefined' && module.exports) {\n module.exports = gju;\n }\n\n // adapted from http://www.kevlindev.com/gui/math/intersection/Intersection.js\n gju.lineStringsIntersect = function (l1, l2) {\n var intersects = [];\n for (var i = 0; i <= l1.coordinates.length - 2; ++i) {\n for (var j = 0; j <= l2.coordinates.length - 2; ++j) {\n var a1 = {\n x: l1.coordinates[i][1],\n y: l1.coordinates[i][0]\n },\n a2 = {\n x: l1.coordinates[i + 1][1],\n y: l1.coordinates[i + 1][0]\n },\n b1 = {\n x: l2.coordinates[j][1],\n y: l2.coordinates[j][0]\n },\n b2 = {\n x: l2.coordinates[j + 1][1],\n y: l2.coordinates[j + 1][0]\n },\n ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),\n ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),\n u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);\n if (u_b != 0) {\n var ua = ua_t / u_b,\n ub = ub_t / u_b;\n if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {\n intersects.push({\n 'type': 'Point',\n 'coordinates': [a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)]\n });\n }\n }\n }\n }\n if (intersects.length == 0) intersects = false;\n return intersects;\n }\n\n // Bounding Box\n\n function boundingBoxAroundPolyCoords (coords) {\n var xAll = [], yAll = []\n\n for (var i = 0; i < coords[0].length; i++) {\n xAll.push(coords[0][i][1])\n yAll.push(coords[0][i][0])\n }\n\n xAll = xAll.sort(function (a,b) { return a - b })\n yAll = yAll.sort(function (a,b) { return a - b })\n\n return [ [xAll[0], yAll[0]], [xAll[xAll.length - 1], yAll[yAll.length - 1]] ]\n }\n\n gju.pointInBoundingBox = function (point, bounds) {\n return !(point.coordinates[1] < bounds[0][0] || point.coordinates[1] > bounds[1][0] || point.coordinates[0] < bounds[0][1] || point.coordinates[0] > bounds[1][1]) \n }\n\n // Point in Polygon\n // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#Listing the Vertices\n\n function pnpoly (x,y,coords) {\n var vert = [ [0,0] ]\n\n for (var i = 0; i < coords.length; i++) {\n for (var j = 0; j < coords[i].length; j++) {\n vert.push(coords[i][j])\n }\n vert.push([0,0])\n }\n\n var inside = false\n for (var i = 0, j = vert.length - 1; i < vert.length; j = i++) {\n if (((vert[i][0] > y) != (vert[j][0] > y)) && (x < (vert[j][1] - vert[i][1]) * (y - vert[i][0]) / (vert[j][0] - vert[i][0]) + vert[i][1])) inside = !inside\n }\n\n return inside\n }\n\n gju.pointInPolygon = function (p, poly) {\n var coords = (poly.type == \"Polygon\") ? [ poly.coordinates ] : poly.coordinates\n\n var insideBox = false\n for (var i = 0; i < coords.length; i++) {\n if (gju.pointInBoundingBox(p, boundingBoxAroundPolyCoords(coords[i]))) insideBox = true\n }\n if (!insideBox) return false\n\n var insidePoly = false\n for (var i = 0; i < coords.length; i++) {\n if (pnpoly(p.coordinates[1], p.coordinates[0], coords[i])) insidePoly = true\n }\n\n return insidePoly\n }\n\n gju.numberToRadius = function (number) {\n return number * Math.PI / 180;\n }\n\n gju.numberToDegree = function (number) {\n return number * 180 / Math.PI;\n }\n\n // written with help from @tautologe\n gju.drawCircle = function (radiusInMeters, centerPoint, steps) {\n var center = [centerPoint.coordinates[1], centerPoint.coordinates[0]],\n dist = (radiusInMeters / 1000) / 6371,\n // convert meters to radiant\n radCenter = [gju.numberToRadius(center[0]), gju.numberToRadius(center[1])],\n steps = steps || 15,\n // 15 sided circle\n poly = [[center[0], center[1]]];\n for (var i = 0; i < steps; i++) {\n var brng = 2 * Math.PI * i / steps;\n var lat = Math.asin(Math.sin(radCenter[0]) * Math.cos(dist)\n + Math.cos(radCenter[0]) * Math.sin(dist) * Math.cos(brng));\n var lng = radCenter[1] + Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(radCenter[0]),\n Math.cos(dist) - Math.sin(radCenter[0]) * Math.sin(lat));\n poly[i] = [];\n poly[i][1] = gju.numberToDegree(lat);\n poly[i][0] = gju.numberToDegree(lng);\n }\n return {\n \"type\": \"Polygon\",\n \"coordinates\": [poly]\n };\n }\n\n // assumes rectangle starts at lower left point\n gju.rectangleCentroid = function (rectangle) {\n var bbox = rectangle.coordinates[0];\n var xmin = bbox[0][0],\n ymin = bbox[0][1],\n xmax = bbox[2][0],\n ymax = bbox[2][1];\n var xwidth = xmax - xmin;\n var ywidth = ymax - ymin;\n return {\n 'type': 'Point',\n 'coordinates': [xmin + xwidth / 2, ymin + ywidth / 2]\n };\n }\n\n // from http://www.movable-type.co.uk/scripts/latlong.html\n gju.pointDistance = function (pt1, pt2) {\n var lon1 = pt1.coordinates[0],\n lat1 = pt1.coordinates[1],\n lon2 = pt2.coordinates[0],\n lat2 = pt2.coordinates[1],\n dLat = gju.numberToRadius(lat2 - lat1),\n dLon = gju.numberToRadius(lon2 - lon1),\n a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(gju.numberToRadius(lat1))\n * Math.cos(gju.numberToRadius(lat2)) * Math.pow(Math.sin(dLon / 2), 2),\n c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n // Earth radius is 6371 km\n return (6371 * c) * 1000; // returns meters\n },\n\n // checks if geometry lies entirely within a circle\n // works with Point, LineString, Polygon\n gju.geometryWithinRadius = function (geometry, center, radius) {\n if (geometry.type == 'Point') {\n return gju.pointDistance(geometry, center) <= radius;\n } else if (geometry.type == 'LineString' || geometry.type == 'Polygon') {\n var point = {};\n var coordinates;\n if (geometry.type == 'Polygon') {\n // it's enough to check the exterior ring of the Polygon\n coordinates = geometry.coordinates[0];\n } else {\n coordinates = geometry.coordinates;\n }\n for (var i in coordinates) {\n point.coordinates = coordinates[i];\n if (gju.pointDistance(point, center) > radius) {\n return false;\n }\n }\n }\n return true;\n }\n\n // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt\n gju.area = function (polygon) {\n var area = 0;\n // TODO: polygon holes at coordinates[1]\n var points = polygon.coordinates[0];\n var j = points.length - 1;\n var p1, p2;\n\n for (var i = 0; i < points.length; j = i++) {\n var p1 = {\n x: points[i][1],\n y: points[i][0]\n };\n var p2 = {\n x: points[j][1],\n y: points[j][0]\n };\n area += p1.x * p2.y;\n area -= p1.y * p2.x;\n }\n\n area /= 2;\n return area;\n },\n\n // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt\n gju.centroid = function (polygon) {\n var f, x = 0,\n y = 0;\n // TODO: polygon holes at coordinates[1]\n var points = polygon.coordinates[0];\n var j = points.length - 1;\n var p1, p2;\n\n for (var i = 0; i < points.length; j = i++) {\n var p1 = {\n x: points[i][1],\n y: points[i][0]\n };\n var p2 = {\n x: points[j][1],\n y: points[j][0]\n };\n f = p1.x * p2.y - p2.x * p1.y;\n x += (p1.x + p2.x) * f;\n y += (p1.y + p2.y) * f;\n }\n\n f = gju.area(polygon) * 6;\n return {\n 'type': 'Point',\n 'coordinates': [y / f, x / f]\n };\n },\n\n gju.simplify = function (source, kink) { /* source[] array of geojson points */\n /* kink\tin metres, kinks above this depth kept */\n /* kink depth is the height of the triangle abc where a-b and b-c are two consecutive line segments */\n kink = kink || 20;\n source = source.map(function (o) {\n return {\n lng: o.coordinates[0],\n lat: o.coordinates[1]\n }\n });\n\n var n_source, n_stack, n_dest, start, end, i, sig;\n var dev_sqr, max_dev_sqr, band_sqr;\n var x12, y12, d12, x13, y13, d13, x23, y23, d23;\n var F = (Math.PI / 180.0) * 0.5;\n var index = new Array(); /* aray of indexes of source points to include in the reduced line */\n var sig_start = new Array(); /* indices of start & end of working section */\n var sig_end = new Array();\n\n /* check for simple cases */\n\n if (source.length < 3) return (source); /* one or two points */\n\n /* more complex case. initialize stack */\n\n n_source = source.length;\n band_sqr = kink * 360.0 / (2.0 * Math.PI * 6378137.0); /* Now in degrees */\n band_sqr *= band_sqr;\n n_dest = 0;\n sig_start[0] = 0;\n sig_end[0] = n_source - 1;\n n_stack = 1;\n\n /* while the stack is not empty ... */\n while (n_stack > 0) {\n\n /* ... pop the top-most entries off the stacks */\n\n start = sig_start[n_stack - 1];\n end = sig_end[n_stack - 1];\n n_stack--;\n\n if ((end - start) > 1) { /* any intermediate points ? */\n\n /* ... yes, so find most deviant intermediate point to\n either side of line joining start & end points */\n\n x12 = (source[end].lng() - source[start].lng());\n y12 = (source[end].lat() - source[start].lat());\n if (Math.abs(x12) > 180.0) x12 = 360.0 - Math.abs(x12);\n x12 *= Math.cos(F * (source[end].lat() + source[start].lat())); /* use avg lat to reduce lng */\n d12 = (x12 * x12) + (y12 * y12);\n\n for (i = start + 1, sig = start, max_dev_sqr = -1.0; i < end; i++) {\n\n x13 = source[i].lng() - source[start].lng();\n y13 = source[i].lat() - source[start].lat();\n if (Math.abs(x13) > 180.0) x13 = 360.0 - Math.abs(x13);\n x13 *= Math.cos(F * (source[i].lat() + source[start].lat()));\n d13 = (x13 * x13) + (y13 * y13);\n\n x23 = source[i].lng() - source[end].lng();\n y23 = source[i].lat() - source[end].lat();\n if (Math.abs(x23) > 180.0) x23 = 360.0 - Math.abs(x23);\n x23 *= Math.cos(F * (source[i].lat() + source[end].lat()));\n d23 = (x23 * x23) + (y23 * y23);\n\n if (d13 >= (d12 + d23)) dev_sqr = d23;\n else if (d23 >= (d12 + d13)) dev_sqr = d13;\n else dev_sqr = (x13 * y12 - y13 * x12) * (x13 * y12 - y13 * x12) / d12; // solve triangle\n if (dev_sqr > max_dev_sqr) {\n sig = i;\n max_dev_sqr = dev_sqr;\n }\n }\n\n if (max_dev_sqr < band_sqr) { /* is there a sig. intermediate point ? */\n /* ... no, so transfer current start point */\n index[n_dest] = start;\n n_dest++;\n } else { /* ... yes, so push two sub-sections on stack for further processing */\n n_stack++;\n sig_start[n_stack - 1] = sig;\n sig_end[n_stack - 1] = end;\n n_stack++;\n sig_start[n_stack - 1] = start;\n sig_end[n_stack - 1] = sig;\n }\n } else { /* ... no intermediate points, so transfer current start point */\n index[n_dest] = start;\n n_dest++;\n }\n }\n\n /* transfer last point */\n index[n_dest] = n_source - 1;\n n_dest++;\n\n /* make return array */\n var r = new Array();\n for (var i = 0; i < n_dest; i++)\n r.push(source[index[i]]);\n\n return r.map(function (o) {\n return {\n type: \"Point\",\n coordinates: [o.lng, o.lat]\n }\n });\n }\n\n // http://www.movable-type.co.uk/scripts/latlong.html#destPoint\n gju.destinationPoint = function (pt, brng, dist) {\n dist = dist/6371; // convert dist to angular distance in radians\n brng = gju.numberToRadius(brng);\n\n var lat1 = gju.numberToRadius(pt.coordinates[0]);\n var lon1 = gju.numberToRadius(pt.coordinates[1]);\n\n var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) +\n Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );\n var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),\n Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));\n lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º\n\n return {\n 'type': 'Point',\n 'coordinates': [gju.numberToDegree(lat2), gju.numberToDegree(lon2)]\n };\n };\n\n})();\n","// This exports object was created in pre.js. Now copy the `exports` object\n// from it into the package-scope variable `GeoJSON`, which will get exported.\nGeoJSON = module.exports;\n\n"]}
\ No newline at end of file
diff --git a/web-app/.meteor/local/build/programs/server/packages/global-imports.js b/web-app/.meteor/local/build/programs/server/packages/global-imports.js
new file mode 100644
index 0000000..5297b26
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/global-imports.js
@@ -0,0 +1,28 @@
+/* Imports for global scope */
+
+moment = Package['momentjs:moment'].moment;
+lodash = Package['stevezhu:lodash'].lodash;
+_ = Package.underscore._;
+numeral = Package['numeral:numeral'].numeral;
+topojson = Package['mrt:topojson'].topojson;
+Meteor = Package.meteor.Meteor;
+WebApp = Package.webapp.WebApp;
+main = Package.webapp.main;
+WebAppInternals = Package.webapp.WebAppInternals;
+Log = Package.logging.Log;
+Tracker = Package.deps.Tracker;
+Deps = Package.deps.Deps;
+DDP = Package.livedata.DDP;
+DDPServer = Package.livedata.DDPServer;
+MongoInternals = Package.mongo.MongoInternals;
+Mongo = Package.mongo.Mongo;
+Blaze = Package.ui.Blaze;
+UI = Package.ui.UI;
+Handlebars = Package.ui.Handlebars;
+Spacebars = Package.spacebars.Spacebars;
+check = Package.check.check;
+Match = Package.check.Match;
+Random = Package.random.Random;
+EJSON = Package.ejson.EJSON;
+HTML = Package.htmljs.HTML;
+
diff --git a/web-app/.meteor/local/build/programs/server/packages/html-tools.js b/web-app/.meteor/local/build/programs/server/packages/html-tools.js
new file mode 100644
index 0000000..b50a396
--- /dev/null
+++ b/web-app/.meteor/local/build/programs/server/packages/html-tools.js
@@ -0,0 +1,3566 @@
+(function () {
+
+/* Imports */
+var Meteor = Package.meteor.Meteor;
+var HTML = Package.htmljs.HTML;
+
+/* Package-scope variables */
+var HTMLTools, Scanner, makeRegexMatcher, getCharacterReference, getComment, getDoctype, getHTMLToken, getTagToken, TEMPLATE_TAG_POSITION, isLookingAtEndTag, codePointToString, getContent, getRCData;
+
+(function () {
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// //
+// packages/html-tools/utils.js //
+// //
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // 1
+HTMLTools = {}; // 2
+HTMLTools.Parse = {}; // 3
+ // 4
+var asciiLowerCase = HTMLTools.asciiLowerCase = function (str) { // 5
+ return str.replace(/[A-Z]/g, function (c) { // 6
+ return String.fromCharCode(c.charCodeAt(0) + 32); // 7
+ }); // 8
+}; // 9
+ // 10
+var svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' ');
+ // 12
+var properAttributeCaseMap = (function (map) { // 13
+ for (var i = 0; i < svgCamelCaseAttributes.length; i++) { // 14
+ var a = svgCamelCaseAttributes[i]; // 15
+ map[asciiLowerCase(a)] = a; // 16
+ } // 17
+ return map; // 18
+})({}); // 19
+ // 20
+var properTagCaseMap = (function (map) { // 21
+ var knownElements = HTML.knownElementNames; // 22
+ for (var i = 0; i < knownElements.length; i++) { // 23
+ var a = knownElements[i]; // 24
+ map[asciiLowerCase(a)] = a; // 25
+ } // 26
+ return map; // 27
+})({}); // 28
+ // 29
+// Take a tag name in any case and make it the proper case for HTML. // 30
+// // 31
+// Modern browsers let you embed SVG in HTML, but SVG elements are special // 32
+// in that they have a case-sensitive DOM API (nodeName, getAttribute, // 33
+// setAttribute). For example, it has to be `setAttribute("viewBox")`, // 34
+// not `"viewbox"`. However, the browser's HTML parser is NOT case sensitive // 35
+// and will fix the case for you, so if you write `