Source: ARCore/central.js

/**
 * Lens.Central handles communication with this devices Lens Central server.
 *
 * @namespace
 * @name Lens.Central
 */
define(["lib/underscore",               "ARCore/db",         "common/util/check",
        "lib/meteor",                   "cgi-bin/configuration"],
function(_,                              DB,                  check,
         meteor,                         Configuration) {
    "use strict";

    var States, Events;

    var now = function() {
        return (new Date()).getTime();
    };

    var DEFAULT_GROUP = "Default";

    var Meteor = meteor.meteor.Meteor;
    var DDP = meteor.livedata.DDP;
    var Accounts = meteor["accounts-base"].Accounts;

    window.Meteor = Meteor;

    // connects Meteor to a backend, using that as the default backend
    var connectAsDefault = function(url) {
        if(Meteor.connection) {
            Meteor.connection.disconnect();
        }

        Meteor.connection = DDP.connect(url);

        // Accounts keeps its own pointer to the current connection.
        Accounts.connection = Meteor.connection;

        // We need to re-connect Meteor.users using the new connection
        Meteor.users = new Meteor.Collection("users");

        // Proxy the public methods of Meteor.connection so they can
        // be called directly on Meteor, which Meteor's accounts system does.
        _.each(["subscribe", "methods", "call", "apply", "status", "reconnect", "disconnect"], function (name) {
            Meteor[name] = _.bind(Meteor.connection[name], Meteor.connection);
        });
    };


    /**
     * @privconstructor
     * @class
     * Tracks the state of a user and logs it to the Lens Central server.
     *
     * @name StateTracker
     */
    var StateTracker = function(group) {
        /** @alias StateTracker.prototype */
        var self = {};

        var stateId = null;

        /**
         * Sets the user's state. This ends the state previously set by this
         * StateTracker, but not states set by other StateTrackers, so users
         * can be in multiple states at the same time, but a StateTracker can
         * only track on state at a time.
         *
         * @param {String} newState The name of the new state.
         * @param {Any}    [data]   Arbitrary data to associate with the state.
         *                          Must be JSON-serializable.
         */
        self.setState = function(newState, data) {
            check(newState, String);
            check(data, check.Match.Optional(check.Match.Serializable));

            // end the previous state
            self.endState();

            // start a new state
            stateId = States.insert({
                user: Meteor.userId(),
                name: newState,
                prevState: stateId,
                data: data,
                startTime: now(),
                group: group
            });
        };

        /**
         * Ends the states previously set by this StateTracker without starting
         * a new state.
         */
        self.endState = function() {
            if(stateId) {
                States.update({_id: stateId}, {$set: {
                    endTime: now()
                }});
            }

            stateId = null;
        };

        Object.freeze(self);
        return self;
    };


    var Central = /** @lends Lens.Central */ {
        /**
         * Connects to a new Lens Central server (disconnecting from the
         * previous one, if there is one). You don't typically need to
         * call this; Lens will automatically connect to the Lens Central
         * server set in the configuration variable `lens.central_url`
         *
         * @param {String} url The URL of the server, e.g. "localhost:3434" or
         *                     "central.mycompany.com"
         */
        connect: function(url) {
            connectAsDefault(url);

            Central.login();
            Meteor.subscribe("my_events");

            States = new Meteor.Collection("States");
            Events = new Meteor.Collection("Events");
        },

        /**
         * Logs the user into the Lens Central server.
         *
         * @param  {Object}   [opts]
         * @param  {String}   [opts.username] The user's username. Defaults to
         *                                    "guest".
         * @param  {String}   [opts.password] The user's password. Defaults to
         *                                    the empty string.
         * @param  {Function} [callback]      Function to call when the log in
         *                                    is done.
         */
        login: function(opts, callback) {
            check(opts, check.Match.Optional({
                username: check.Match.Optional(String),
                password: check.Match.Optional(String)
            }));
            check(callback, check.Match.Optional(Function));

            opts = opts || {};

            Meteor.loginWithPassword(opts.username || "guest", opts.password || "guest", callback);
            Meteor.subscribe("this_user");
        },

        /**
         * Logs the user out of the Lens Central server.
         *
         * @param {Function} [callback] Function to call when the log out is
         *                              done.
         */
        logout: function(callback) {
            check(callback, check.Match.Optional(Function));
            Meteor.loginWithPassword("guest", "", callback);
        },

        /**
         * Returns the current user record. A reactive data source.
         * @return {Object} The user record.
         */
        user: Meteor.user,

        /**
         * Returns the current user ID. A reactive data source.
         * @return {String} The ID of the current user.
         */
        userId: Meteor.userId,

        /**
         * Returns a {@link StateTracker}. Users can have multiple states at
         * the same time by creating multiple StateTrackers.
         *
         * @param {String} group Which group the states will belong to.
         * @memberOf Lens.Central
         */
        StateTracker: function(group) {
            return StateTracker(group || DEFAULT_GROUP);
        },

        /**
         * Logs an event to the Lens Central server.
         * @param {String}  event             Name of the event.
         * @param {Object}  [opts]
         * @param {Any}     [opts.data]  Arbitrary data to associate with the
         *                               event. Must be JSON-serializable.
         * @param {String}  [opts.group] Which group this event belongs to.
         */
        logEvent: function(event, opts) {
            check(event, String);

            check(opts, check.Match.Optional({
                data: check.Match.Optional(check.Match.Serializable),
                group: check.Match.Optional(String)
            }));

            if(!opts) {
                opts = {};
            }

            Events.insert({
                user: Meteor.userId(),
                name: event,
                data: opts.data,
                group: (opts.group || DEFAULT_GROUP),
                time: now()
            });
        },

        /**
         * Returns a database connected to the Central server.
         * @return {Lens.DB}
         */
        db: function() {
            return DB("", null, function() {
                return Meteor.connection;
            }, false);
        },

        /**
         * Returns the connection to the Lens Central server. See Meteor's
         * documentation on [DDP.connect](http://docs.meteor.com/#ddp_connect)
         * for documentation on connection options.
         */
        connection: function() {
            return Meteor.connection;
        }
    };

    Central.connect(Configuration["lens.central_url"]);

    Object.freeze(Central);
    Lens._addMember(Central, "Central");

    return Central;
});