<commit_before>/*
 * This script introduces a class representing a Gearman job server
 * and knowledge about protocol to communicate with it.
 *
 * (C) 2013 Vaclav Sykora
 * Apache License, Version 2.0, http://www.apache.org/licenses/
 *
 */

var net      = require('net'),
    winston  = require('winston'),
    Job      = require('./job').Job,
    protocol = require('./protocol'),
    common   = require('./common');

var JobServer = exports.JobServer = function (options) {
    options = options || {};

    // VALIDATION
    var pattern = { host: 'mandatory', port: 'mandatory' };
    var returned = common.verifyOptions(options, pattern);
    if (returned instanceof Error) { return returned; }

    this.host = options.host;
    this.port = options.port;

    this.connected = false;
    this.jobsWaiting4Created = []; // submited jobs waiting for JOB_CREATED response
};

// static logger
JobServer.logger = winston.loggers.get('JobServer');

/**
 * Method to establish a socket connection to a job server.
 * It uses a callback parameter as a synchronisation hook for 'after connect' actions.
 *
 * *callback(err)* called as
 *     * error callback when connection failed
 *     * success callback if connection opened, err=undefined
 *
 * Returns net.Socket if success.
 */
JobServer.prototype.connect = function (callback) {
    var self = this;
    var eventNames;

    if (!(callback instanceof Function)) { return new Error('invalid callback (not a function)'); }

    if (this.connected) {
        callback();
    } else {
        this.socket = net.createConnection(this.port, this.host);

        // fallback event registration, only for debugging purposes
        eventNames = [ 'end', 'close', 'timeout', 'drain' ];
        eventNames.forEach(function (name) {
            self.socket.on(name, function() {
                JobServer.logger.log('warn', 'unhandled event, name=%s', name);
            });
        });

        // emitted when a socket connection is successfully established
        this.socket.on('connect', function () {
            self.socket.setKeepAlive(true);
            self.connected = true;
            self.clientOrWorker.emit('js_connect', self.getUid()); // trigger event
            JobServer.logger.log('debug', 'connection established, uid=%s', self.getUid());
            callback();
        });

        // emitted when an error occurs
        this.socket.on('error', function (err) {
            JobServer.logger.log('error', 'socket error:', err);
            // ensures that no more I/O activity happens on this socket
            self.socket.destroy();

            self.disconnect();
            callback(err);
        });

        this.socket.on('data', function (chunk) {
            JobServer.logger.log('verbose', 'received packet, len=%d', chunk.length);
            JobServer.logger.log('verbose', 'received packet: %s', common.bufferAsHex(chunk));

            self._processData(chunk);
        });
    }

    return this.socket;
};

/**
 * Ends connection with job server and releases associated resources,
 * e.g. underlaying socket connection.
 * Sets property 'connected' to 'false'.
 */
JobServer.prototype.disconnect = function () {
    var i, eventNames;

    if (this.clientOrWorker && this.connected) {
        this.clientOrWorker.emit('disconnect', this.getUid());
    }
    this.connected = false;

    // close jobs waiting for packet JOB_CREATED
    for (i = 0; i < this.jobsWaiting4Created.length; i ++) {
        this.jobsWaiting4Created[i].close();
    }
    this.jobsWaiting4Created.length = 0;

    if (this.socket) {
        // remove listeners from socket
        eventNames = [ 'connect', 'data', 'error', 'end', 'close', 'timeout', 'drain' ];
        for (i = 0; i < eventNames.length; i ++) {
            this.socket.removeAllListeners(eventNames[i]);
        }

        this.socket.end();
        delete this.socket;
    }
    JobServer.logger.log('debug', 'connection closed, uid=%s', this.getUid());
};

/**
 * Sends the job server request that will be echoed back in response.
 *
 * *data* opaque data that is echoed back in response
 * *callback(err,echoedData)* called as
 *     * error callback when somethings went wrong
 *     * success callback response of the job server, will be invoked with echoed data as parameter
 */
JobServer.prototype.echo = function (data, callback) {
    return this._requestReponse(protocol.PACKET_TYPES.ECHO_REQ, data, callback);
}

/**
 * Sends the job server request to set an option for the connection in the job server.
 *
 * *optionName* name of the option to set
 * *callback(err,data)* called as
 *     * error callback when somethings went wrong
 *     * success callback response of the job server, will be invoked with echoed data as parameter
 */
JobServer.prototype.setOption = function (optionName, callback) {
    return this._requestReponse(protocol.PACKET_TYPES.OPTION_REQ, optionName, callback);
}

/**
 * Sends given packet to job servers and wait for response with callback.
 * Re-usable helper method.
 */
JobServer.prototype._requestReponse = function (packetType, data, callback) {
    var self = this;
    var packet, cb;
    if (!common.isString(data)) { return new Error('data for job server not a text'); }
    if (!(callback instanceof Function)) { return new Error('invalid callback (not a function)'); }
    this.expectedJsResponseCallback = callback;

    cb = function (err) {
        if (err instanceof Error) {
            JobServer.logger.log('error', 'failed to send %s, uid=%s', protocol.PACKET_CODES[packetType], this.getUid());
            delete self.expectedJsResponseCallback;
            callback(err);
        }
    };

    packet = protocol.encodePacket(packetType, 'ascii', [data]);
    this.send(packet, cb);
}

/**
 * Sends given data as Buffer through socket connection.
 * Underlaying socket connection with job server will be created when does not exist.
 *
 * Error event will be emitted on associated client/worker when something goes wrong.
 */
JobServer.prototype.send = function (data) {
    var self = this;
    var connectCb, sendCb;

    // VALIDATION
    if (!(data instanceof Buffer)) { return this._error(JobServer.logger, 'data has to be object of Buffer'); }
    if (data.length < protocol.CONSTANTS.HEADER_LEN) { return this._error(JobServer.logger, 'short data'); }
    if (protocol.CONSTANTS.HEADER_REQ != data.readUInt32BE(0)) { return this._error(JobServer.logger, 'no gearman packet'); }

    // invoked after connection established
    connectCb = function (connectErr) {
        if (connectErr instanceof Error) {
            self._error(connectErr); // connection failed
        } else {
            sendCb();
        }
    };

    sendCb = function () {
        self.socket.write(data);
        JobServer.logger.log('verbose', 'packet sent, type=%s, len=%d', protocol.PACKET_CODES[data.readUInt32BE(4)], data.length);
    }

    if (this.connected) {
        sendCb();
    } else {
        JobServer.logger.log('debug', 'unconnected job server, uid=%s', this.getUid());
        this.connect(connectCb);
    }
}

JobServer.prototype._processData = function (chunk) {
    if (!this.connected) {
        JobServer.logger.log('warn', 'trying to process data from disconnected job server (disconnect before all packet received?)');
        return;
    }

    if (chunk.readUInt32BE(0) !== protocol.CONSTANTS.HEADER_RESP) {
        // OUT OF SYNC!
        this.clientOrWorker.unrecoverableError('out of sync with server');
        return;
    }

    var handle, job, parsedPacket, processed, status;
    var packetType = chunk.readUInt32BE(4);
    var packetCode = protocol.PACKET_CODES[packetType];

    // parse packet if it is a response
    if (protocol.DEFINITION[packetCode] !== undefined
        && (protocol.CONSTANTS.TYPE_RESP & protocol.DEFINITION[packetCode][1])) {
        parsedPacket = protocol.parsePacket(chunk, protocol.DEFINITION[packetCode][2]);
    }

    switch (packetType) {

    // CLIENT/WORKER ==========================================================

    case protocol.PACKET_TYPES.ERROR:
        JobServer.logger.log('warn', 'job server error, uid=%s, code=%s, msg=%s',
            this.getUid(), parsedPacket[1], parsedPacket[2]);
        if (parsedPacket[1] == protocol.CONSTANTS.UNKNOWN_OPTION && this.expectedJsResponseCallback) {
            var cb = this.expectedJsResponseCallback;
            delete this.expectedJsResponseCallback;
            cb(new Error(parsedPacket[1], parsedPacket[2]));
        } else {
            this.clientOrWorker.emit('error', 'code=' + parsedPacket[1] + ', msg=' + parsedPacket[2]); // trigger event
        }
        break;

    case protocol.PACKET_TYPES.ECHO_RES:
    case protocol.PACKET_TYPES.OPTION_RES:
        if (this.expectedJsResponseCallback) {
            var cb = this.expectedJsResponseCallback;
            delete this.expectedJsResponseCallback;
            cb(null, parsedPacket[1]);
        } else {
            JobServer.logger.log('warn', 'received %s without registered callback', protocol.PACKET_CODES[packetType]);
        }
        break;

    // CLIENT =================================================================

    case protocol.PACKET_TYPES.JOB_CREATED:
        handle = parsedPacket[1];

        // remove it from queue of jobs waiting for JOB_CREATED
        if (this.jobsWaiting4Created.length === 0) {
            this.clientOrWorker.unrecoverableError('empty stack of job waiting 4 packet JOB_CREATED');
            return;
        }
        job = this.jobsWaiting4Created.pop();
        job.handle = handle;

        // and put it into hash of created jobs on Client
        this.clientOrWorker.jobs[handle] = job;

        if (this.jobsWaiting4Created.length === 0) {
            JobServer.logger.log('verbose', 'no more a job waiting 4 state JOB_CREATED');
            this.clientOrWorker.emit('done'); // trigger event
        }

        this.clientOrWorker.response(null, packetType, parsedPacket);
        break;

    case protocol.PACKET_TYPES.WORK_COMPLETE:
    case protocol.PACKET_TYPES.STATUS_RES: // for background jobs in response to a GET_STATUS request (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_STATUS: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_FAIL: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_EXCEPTION: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)

        this.clientOrWorker.response(null, packetType, parsedPacket);
        break;

    // WORKER =================================================================

    case protocol.PACKET_TYPES.NOOP:
    case protocol.PACKET_TYPES.JOB_ASSIGN:
    case protocol.PACKET_TYPES.NO_JOB:

        this.clientOrWorker.response(this, packetType, parsedPacket);
        break;

    // ========================================================================

    default:
        JobServer.logger.log('warn', 'unknown packet, type=%d', packetType);
        processed = chunk.length; // stop processing of rest of the chunk
    }

    if (parsedPacket) {
        processed = parsedPacket[0];
    }

    // recursive approach when more packets in buffer
    if (processed < chunk.length) {
        this._processData(chunk.slice(processed));
    }
}

/**
 * Processes an error that cannot be covered by client/worker.
 */
JobServer.prototype._error = function (err) { // #unit: not needed
    JobServer.logger.log('error', err);
    if (!this.clientOrWorker) {
        throw new Error('there is no client/worker associated with this job server (never should be state)');
    } else {
        this.clientOrWorker.emit('error', err instanceof Error ? err : new Error(err)); // trigger event
    }
}

/**
 * Gets an unique identifier of job server represented by the URL address.
 */
JobServer.prototype.getUid = function () { // #unit: not needed
    return this.host + ':' + this.port;
}

/**
 * Returns a human readable string representation of the object.
 */
JobServer.prototype.toString = function () { // #unit: not needed
    return 'JobServer(' + this.getUid() + ')';
}
<commit_msg>added TODO<commit_after>/*
 * This script introduces a class representing a Gearman job server
 * and knowledge about protocol to communicate with it.
 *
 * (C) 2013 Vaclav Sykora
 * Apache License, Version 2.0, http://www.apache.org/licenses/
 *
 */

var net      = require('net'),
    winston  = require('winston'),
    Job      = require('./job').Job,
    protocol = require('./protocol'),
    common   = require('./common');

var JobServer = exports.JobServer = function (options) {
    options = options || {};

    // VALIDATION
    var pattern = { host: 'mandatory', port: 'mandatory' };
    var returned = common.verifyOptions(options, pattern);
    if (returned instanceof Error) { return returned; }

    this.host = options.host;
    this.port = options.port;

    this.connected = false;
    this.jobsWaiting4Created = []; // submited jobs waiting for JOB_CREATED response
};

// static logger
JobServer.logger = winston.loggers.get('JobServer');

/**
 * Method to establish a socket connection to a job server.
 * It uses a callback parameter as a synchronisation hook for 'after connect' actions.
 *
 * *callback(err)* called as
 *     * error callback when connection failed
 *     * success callback if connection opened, err=undefined
 *
 * Returns net.Socket if success.
 */
JobServer.prototype.connect = function (callback) {
    var self = this;
    var eventNames;

    if (!(callback instanceof Function)) { return new Error('invalid callback (not a function)'); }

    if (this.connected) {
        callback();
    } else {
        this.socket = net.createConnection(this.port, this.host);

        // fallback event registration, only for debugging purposes
        eventNames = [ 'end', 'close', 'timeout', 'drain' ];
        eventNames.forEach(function (name) {
            self.socket.on(name, function() {
                JobServer.logger.log('warn', 'unhandled event, name=%s', name);
            });
        });

        // emitted when a socket connection is successfully established
        this.socket.on('connect', function () {
            self.socket.setKeepAlive(true);
            self.connected = true;
            self.clientOrWorker.emit('js_connect', self.getUid()); // trigger event
            JobServer.logger.log('debug', 'connection established, uid=%s', self.getUid());
            callback();
        });

        // emitted when an error occurs
        this.socket.on('error', function (err) {
            JobServer.logger.log('error', 'socket error:', err);
            // ensures that no more I/O activity happens on this socket
            self.socket.destroy();

            self.disconnect();
            callback(err);
        });

        this.socket.on('data', function (chunk) {
            JobServer.logger.log('verbose', 'received packet, len=%d', chunk.length);
            JobServer.logger.log('verbose', 'received packet: %s', common.bufferAsHex(chunk));

            self._processData(chunk);
        });
    }

    return this.socket;
};

/**
 * Ends connection with job server and releases associated resources,
 * e.g. underlaying socket connection.
 * Sets property 'connected' to 'false'.
 */
JobServer.prototype.disconnect = function () {
    var i, eventNames;

    if (this.clientOrWorker && this.connected) {
        this.clientOrWorker.emit('disconnect', this.getUid());
    }
    this.connected = false;

    // close jobs waiting for packet JOB_CREATED
    for (i = 0; i < this.jobsWaiting4Created.length; i ++) {
        this.jobsWaiting4Created[i].close();
    }
    this.jobsWaiting4Created.length = 0;

    if (this.socket) {
        // remove listeners from socket
        eventNames = [ 'connect', 'data', 'error', 'end', 'close', 'timeout', 'drain' ];
        for (i = 0; i < eventNames.length; i ++) {
            this.socket.removeAllListeners(eventNames[i]);
        }

        this.socket.end();
        delete this.socket;
    }

    // TODO: remove event listeners from itself (after JobServer becomes EventEmitter)

    JobServer.logger.log('debug', 'connection closed, uid=%s', this.getUid());
};

/**
 * Sends the job server request that will be echoed back in response.
 *
 * *data* opaque data that is echoed back in response
 * *callback(err,echoedData)* called as
 *     * error callback when somethings went wrong
 *     * success callback response of the job server, will be invoked with echoed data as parameter
 */
JobServer.prototype.echo = function (data, callback) {
    return this._requestReponse(protocol.PACKET_TYPES.ECHO_REQ, data, callback);
}

/**
 * Sends the job server request to set an option for the connection in the job server.
 *
 * *optionName* name of the option to set
 * *callback(err,data)* called as
 *     * error callback when somethings went wrong
 *     * success callback response of the job server, will be invoked with echoed data as parameter
 */
JobServer.prototype.setOption = function (optionName, callback) {
    return this._requestReponse(protocol.PACKET_TYPES.OPTION_REQ, optionName, callback);
}

/**
 * Sends given packet to job servers and wait for response with callback.
 * Re-usable helper method.
 */
JobServer.prototype._requestReponse = function (packetType, data, callback) {
    var self = this;
    var packet, cb;
    if (!common.isString(data)) { return new Error('data for job server not a text'); }
    if (!(callback instanceof Function)) { return new Error('invalid callback (not a function)'); }
    this.expectedJsResponseCallback = callback;

    cb = function (err) {
        if (err instanceof Error) {
            JobServer.logger.log('error', 'failed to send %s, uid=%s', protocol.PACKET_CODES[packetType], this.getUid());
            delete self.expectedJsResponseCallback;
            callback(err);
        }
    };

    packet = protocol.encodePacket(packetType, 'ascii', [data]);
    this.send(packet, cb);
}

/**
 * Sends given data as Buffer through socket connection.
 * Underlaying socket connection with job server will be created when does not exist.
 *
 * Error event will be emitted on associated client/worker when something goes wrong.
 */
JobServer.prototype.send = function (data) {
    var self = this;
    var connectCb, sendCb;

    // VALIDATION
    if (!(data instanceof Buffer)) { return this._error(JobServer.logger, 'data has to be object of Buffer'); }
    if (data.length < protocol.CONSTANTS.HEADER_LEN) { return this._error(JobServer.logger, 'short data'); }
    if (protocol.CONSTANTS.HEADER_REQ != data.readUInt32BE(0)) { return this._error(JobServer.logger, 'no gearman packet'); }

    // invoked after connection established
    connectCb = function (connectErr) {
        if (connectErr instanceof Error) {
            self._error(connectErr); // connection failed
        } else {
            sendCb();
        }
    };

    sendCb = function () {
        self.socket.write(data);
        JobServer.logger.log('verbose', 'packet sent, type=%s, len=%d', protocol.PACKET_CODES[data.readUInt32BE(4)], data.length);
    }

    if (this.connected) {
        sendCb();
    } else {
        JobServer.logger.log('debug', 'unconnected job server, uid=%s', this.getUid());
        this.connect(connectCb);
    }
}

JobServer.prototype._processData = function (chunk) {
    if (!this.connected) {
        JobServer.logger.log('warn', 'trying to process data from disconnected job server (disconnect before all packet received?)');
        return;
    }

    if (chunk.readUInt32BE(0) !== protocol.CONSTANTS.HEADER_RESP) {
        // OUT OF SYNC!
        this.clientOrWorker.unrecoverableError('out of sync with server');
        return;
    }

    var handle, job, parsedPacket, processed, status;
    var packetType = chunk.readUInt32BE(4);
    var packetCode = protocol.PACKET_CODES[packetType];

    // parse packet if it is a response
    if (protocol.DEFINITION[packetCode] !== undefined
        && (protocol.CONSTANTS.TYPE_RESP & protocol.DEFINITION[packetCode][1])) {
        parsedPacket = protocol.parsePacket(chunk, protocol.DEFINITION[packetCode][2]);
    }

    switch (packetType) {

    // CLIENT/WORKER ==========================================================

    case protocol.PACKET_TYPES.ERROR:
        JobServer.logger.log('warn', 'job server error, uid=%s, code=%s, msg=%s',
            this.getUid(), parsedPacket[1], parsedPacket[2]);
        if (parsedPacket[1] == protocol.CONSTANTS.UNKNOWN_OPTION && this.expectedJsResponseCallback) {
            var cb = this.expectedJsResponseCallback;
            delete this.expectedJsResponseCallback;
            cb(new Error(parsedPacket[1], parsedPacket[2]));
        } else {
            this.clientOrWorker.emit('error', 'code=' + parsedPacket[1] + ', msg=' + parsedPacket[2]); // trigger event
        }
        break;

    case protocol.PACKET_TYPES.ECHO_RES:
    case protocol.PACKET_TYPES.OPTION_RES:
        if (this.expectedJsResponseCallback) {
            var cb = this.expectedJsResponseCallback;
            delete this.expectedJsResponseCallback;
            cb(null, parsedPacket[1]);
        } else {
            JobServer.logger.log('warn', 'received %s without registered callback', protocol.PACKET_CODES[packetType]);
        }
        break;

    // CLIENT =================================================================

    case protocol.PACKET_TYPES.JOB_CREATED:
        handle = parsedPacket[1];

        // remove it from queue of jobs waiting for JOB_CREATED
        if (this.jobsWaiting4Created.length === 0) {
            this.clientOrWorker.unrecoverableError('empty stack of job waiting 4 packet JOB_CREATED');
            return;
        }
        job = this.jobsWaiting4Created.pop();
        job.handle = handle;

        // and put it into hash of created jobs on Client
        this.clientOrWorker.jobs[handle] = job;

        if (this.jobsWaiting4Created.length === 0) {
            JobServer.logger.log('verbose', 'no more a job waiting 4 state JOB_CREATED');
            this.clientOrWorker.emit('done'); // trigger event
        }

        this.clientOrWorker.response(null, packetType, parsedPacket);
        break;

    case protocol.PACKET_TYPES.WORK_COMPLETE:
    case protocol.PACKET_TYPES.STATUS_RES: // for background jobs in response to a GET_STATUS request (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_STATUS: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_FAIL: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)
    case protocol.PACKET_TYPES.WORK_EXCEPTION: // for non-background jobs, the server forwards this packet from the worker to clients (Gearman Docu)

        this.clientOrWorker.response(null, packetType, parsedPacket);
        break;

    // WORKER =================================================================

    case protocol.PACKET_TYPES.NOOP:
    case protocol.PACKET_TYPES.JOB_ASSIGN:
    case protocol.PACKET_TYPES.NO_JOB:

        this.clientOrWorker.response(this, packetType, parsedPacket);
        break;

    // ========================================================================

    default:
        JobServer.logger.log('warn', 'unknown packet, type=%d', packetType);
        processed = chunk.length; // stop processing of rest of the chunk
    }

    if (parsedPacket) {
        processed = parsedPacket[0];
    }

    // recursive approach when more packets in buffer
    if (processed < chunk.length) {
        this._processData(chunk.slice(processed));
    }
}

/**
 * Processes an error that cannot be covered by client/worker.
 */
JobServer.prototype._error = function (err) { // #unit: not needed
    JobServer.logger.log('error', err);
    if (!this.clientOrWorker) {
        throw new Error('there is no client/worker associated with this job server (never should be state)');
    } else {
        this.clientOrWorker.emit('error', err instanceof Error ? err : new Error(err)); // trigger event
    }
}

/**
 * Gets an unique identifier of job server represented by the URL address.
 */
JobServer.prototype.getUid = function () { // #unit: not needed
    return this.host + ':' + this.port;
}

/**
 * Returns a human readable string representation of the object.
 */
JobServer.prototype.toString = function () { // #unit: not needed
    return 'JobServer(' + this.getUid() + ')';
}
<|endoftext|>


<commit_before>'use strict'

let LoginView = Backbone.View.extend({
  tagName: "div",
  className: "panel panel-info",
  events: {
    "click #login-button": "tryLogin"
  },

  tryLogin: function() {
//    $('#login-form').submit(function(event) {
//      event.preventDefault();
      let $form = $('#login-form');
      let u = $form.find("input[name='username']").val();
      let p = $form.find("input[name='password']").val();
      let url = $form.attr("action");
      let csrftoken = Cookies.get("csrftoken");
      $.ajaxSetup({
        beforeSend: function(xhr, settings) {
          if (!csrfSafeMethod(settings.type)
            && sameOrigin(settings.url)) {
            xhr.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);
            //xhr.setRequestHeader("X-CSRFToken", csrftoken);
          }
        },
        type: "POST",
        contentType: "application/json"
      });
      let posting = $.ajax(url,
         {
           beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type)
              && sameOrigin(settings.url)) {
              xhr.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);
              //xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
          },
        },
        {
          username: u,
          password: p
        });
      posting.done(function(data) {
        console.log(data);
      });
//    });
  },

  render: function() {
    let template = $("#login-template").html();
    let compiled = _.template(template, { name: "Template Name" });
    $(this.el).html(compiled);
//    return this;
    $("#container").html(this.el);
  }
});
<commit_msg>WIP Login we already can negotiate an JWT Auth Token<commit_after>'use strict'

let LoginView = Backbone.View.extend({
  tagName: "div",
  className: "panel panel-info",
  events: {
    "click #login-button": "tryLogin"
  },

  tryLogin: function() {
//    $('#login-form').submit(function(event) {
//      event.preventDefault();
      let $form = $('#login-form');
      let u = $form.find("input[name='username']").val();
      let p = $form.find("input[name='password']").val();
      let url = $form.attr("action");
      let csrftoken = Cookies.get("csrftoken");
      let posting = $.ajax(url,
         {
           beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type)
              && sameOrigin(settings.url)) {
              xhr.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken);
            }
          },
          data: {
            username: u,
            password: p
          },
          method: "POST"
        });
      posting.done(function(data) {
        console.log(data);
      });
//    });
  },

  render: function() {
    let template = $("#login-template").html();
    let compiled = _.template(template, { name: "Template Name" });
    $(this.el).html(compiled);
//    return this;
    $("#container").html(this.el);
  }
});
<|endoftext|>


<commit_before>(function() {
    var dependencies = ['ngRoute', 'ngAnimate', 'firebase', 'jcs-autoValidate', 'chart.js'];

    var app = angular.module('weighIn', dependencies);

    app.constant('FirebaseUrl', 'https://watch-yo-weight.firebaseio.com');

    app.run(['$rootScope', '$location', function($rootScope, $location) {
        $rootScope.$on('$routeChangeError', function(event, next, previous, error) {
            if (error === 'AUTH_REQUIRED') {
                $location.path('/login');
            }
        });
    }]);

    app.config(function($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/home.html',
                controller: 'HomeController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/list', {
                templateUrl: 'views/list.html',
                controller: 'ListController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/add', {
                templateUrl: 'views/add.html',
                controller: 'AddWeightController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/login', {
                templateUrl: 'views/login.html',
                controller: 'LoginController'
            })
            .when('/logout', {
                templateUrl: 'views/logout.html',
                controller: 'LogoutController'
            });
    });

    app.factory('Auth', ['$firebaseAuth', 'rootRef',
        function($firebaseAuth, rootRef) {
            return $firebaseAuth(rootRef);
        }
    ]);

    app.service('rootRef', ['FirebaseUrl', Firebase]);

    app.service('weights', function(rootRef, $firebaseObject, $firebaseArray) {
        var weightsRef = rootRef.child('weights');

        this.get = function(date) {
            return $firebaseObject(weightsRef.child(date));
        }

        this.all = function() {
            return $firebaseArray(weightsRef);
        }

        this.add = function(weighInDate, weight) {
            var allWeights = $firebaseArray(weightsRef);

            var newWeight = {
                'date': weighInDate,
                'weight': weight
            };

            allWeights.$add(newWeight).then(function(ref) {
                return ref.key();
            });
        }
    });

    app.controller('HomeController', function($scope, $filter, $location, weights, Auth) {
        $scope.weights = weights.all();

        var chartData = {
            dates: [],
            weights: []
        };

        $scope.weights.$loaded().then(function(snapshot) {
            snapshot.forEach(function(childSnapshot) {
                var weighInDate = $filter('date')(new Date(childSnapshot.date), 'M/d/yy');
                chartData.dates.push(weighInDate);
                chartData.weights.push(childSnapshot.weight);
            });

            $scope.labels = chartData.dates
            $scope.data = [chartData.weights];
        });
    });

    app.controller('ListController', function($scope, weights) {
        $scope.weights = weights.all();
    });

    app.controller('AddWeightController', function($scope, $filter, $location, weights) {
        $scope.weighInDate = $filter('date')(new Date(), 'M/dd/yyyy');

        $scope.addWeight = function() {
            var weighInDateParts = $scope.weighInDate.split('/')
            var weighInDate = weighInDateParts[2] + '-' + weighInDateParts[0] + '-' + weighInDateParts[1];
            weights.add(weighInDate, $scope.weight);

            $location.path('/list');
        };
    });

    app.controller('LoginController', function($scope, rootRef) {
        $scope.login = function() {
            rootRef.authWithPassword({
                email: $scope.email,
                password: $scope.password
            }, function(error, authData) {
                if (error) {
                    console.log('Login Failed...redirecting.', error);
                } else {
                    window.location.href= '#/';
                }
            });
        }
    });

    app.controller('LogoutController', function($scope, $location, Auth) {
        $scope.init = function() {
            Auth.$unauth();
            $location.path('/login');
        }

        $scope.init();
    });

})();
<commit_msg>Fixed issue with date filter<commit_after>(function() {
    var dependencies = ['ngRoute', 'ngAnimate', 'firebase', 'jcs-autoValidate', 'chart.js'];

    var app = angular.module('weighIn', dependencies);

    app.constant('FirebaseUrl', 'https://watch-yo-weight.firebaseio.com');

    app.run(['$rootScope', '$location', function($rootScope, $location) {
        $rootScope.$on('$routeChangeError', function(event, next, previous, error) {
            if (error === 'AUTH_REQUIRED') {
                $location.path('/login');
            }
        });
    }]);

    app.config(function($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/home.html',
                controller: 'HomeController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/list', {
                templateUrl: 'views/list.html',
                controller: 'ListController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/add', {
                templateUrl: 'views/add.html',
                controller: 'AddWeightController',
                resolve: {
                    'currentAuth': ['Auth', function(Auth) {
                        return Auth.$requireAuth();
                    }]
                }
            })
            .when('/login', {
                templateUrl: 'views/login.html',
                controller: 'LoginController'
            })
            .when('/logout', {
                templateUrl: 'views/logout.html',
                controller: 'LogoutController'
            });
    });

    app.factory('Auth', ['$firebaseAuth', 'rootRef',
        function($firebaseAuth, rootRef) {
            return $firebaseAuth(rootRef);
        }
    ]);

    app.service('rootRef', ['FirebaseUrl', Firebase]);

    app.service('weights', function(rootRef, $firebaseObject, $firebaseArray) {
        var weightsRef = rootRef.child('weights');

        this.get = function(date) {
            return $firebaseObject(weightsRef.child(date));
        }

        this.all = function() {
            return $firebaseArray(weightsRef);
        }

        this.add = function(weighInDate, weight) {
            var allWeights = $firebaseArray(weightsRef);

            var newWeight = {
                'date': weighInDate,
                'weight': weight
            };

            allWeights.$add(newWeight).then(function(ref) {
                return ref.key();
            });
        }
    });

    app.controller('HomeController', function($scope, $filter, $location, weights, Auth) {
        $scope.weights = weights.all();

        var chartData = {
            dates: [],
            weights: []
        };

        $scope.weights.$loaded().then(function(snapshot) {
            snapshot.forEach(function(childSnapshot) {
                var weighInDate = $filter('date')(new Date(childSnapshot.date), 'M/d/yy');
                chartData.dates.push(weighInDate);
                chartData.weights.push(childSnapshot.weight);
            });

            $scope.labels = chartData.dates
            $scope.data = [chartData.weights];
        });
    });

    app.controller('ListController', function($scope, weights) {
        $scope.weights = weights.all();
    });

    app.controller('AddWeightController', function($scope, $filter, $location, weights) {
        $scope.weighInDate = $filter('date')(new Date(), 'MM/dd/yyyy');

        $scope.addWeight = function() {
            var weighInDateParts = $scope.weighInDate.split('/')
            var weighInDate = weighInDateParts[2] + '-' + weighInDateParts[0] + '-' + weighInDateParts[1];
            weights.add(weighInDate, $scope.weight);

            $location.path('/list');
        };
    });

    app.controller('LoginController', function($scope, rootRef) {
        $scope.login = function() {
            rootRef.authWithPassword({
                email: $scope.email,
                password: $scope.password
            }, function(error, authData) {
                if (error) {
                    console.log('Login Failed...redirecting.', error);
                } else {
                    window.location.href= '#/';
                }
            });
        }
    });

    app.controller('LogoutController', function($scope, $location, Auth) {
        $scope.init = function() {
            Auth.$unauth();
            $location.path('/login');
        }

        $scope.init();
    });

})();
<|endoftext|>


<commit_before>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { Switch } from 'react-native';
import { BRAND_COLOR } from '../styles';

type Props = {|
  value: boolean,
  disabled: boolean,
  defaultValue: boolean,
  onValueChange: (arg: boolean) => void,
|};

type State = {|
  valueControlled: boolean,
|};

/**
 * An on/off component, provides consistent styling of the
 * built-in Switch component.
 *
 * @prop [value] - Value of the switch component.
 *   Setting this turns the component into a controlled one.
 * @prop [disabled] - If set the component is not switchable and visually looks disabled.
 * @prop [defaultValue] - Initial value of the switch.
 * @prop onValueChange - Event called on switch.
 */
export default class ZulipSwitch extends PureComponent<Props, State> {
  state = {
    valueControlled: this.props.defaultValue,
  };

  static defaultProps = {
    value: false,
    disabled: false,
    defaultValue: false,
  };

  handleValueChange = (newValue: boolean) => {
    const { onValueChange } = this.props;

    if (onValueChange) {
      onValueChange(newValue);
    }

    this.setState({
      valueControlled: newValue,
    });
  };

  render() {
    const { value, disabled } = this.props;
    const { valueControlled } = this.state;
    const switchValue = typeof valueControlled !== 'undefined' ? valueControlled : value;

    return (
      <Switch
        value={switchValue}
        trackColor={{
          false: 'hsl(0, 0%, 86%)',
          true: BRAND_COLOR,
        }}
        onValueChange={this.handleValueChange}
        disabled={disabled}
      />
    );
  }
}
<commit_msg>ZulipSwitch: Remove redundant null check on onValueChange prop.<commit_after>/* @flow strict-local */
import React, { PureComponent } from 'react';
import { Switch } from 'react-native';
import { BRAND_COLOR } from '../styles';

type Props = {|
  value: boolean,
  disabled: boolean,
  defaultValue: boolean,
  onValueChange: (arg: boolean) => void,
|};

type State = {|
  valueControlled: boolean,
|};

/**
 * An on/off component, provides consistent styling of the
 * built-in Switch component.
 *
 * @prop [value] - Value of the switch component.
 *   Setting this turns the component into a controlled one.
 * @prop [disabled] - If set the component is not switchable and visually looks disabled.
 * @prop [defaultValue] - Initial value of the switch.
 * @prop onValueChange - Event called on switch.
 */
export default class ZulipSwitch extends PureComponent<Props, State> {
  state = {
    valueControlled: this.props.defaultValue,
  };

  static defaultProps = {
    value: false,
    disabled: false,
    defaultValue: false,
  };

  handleValueChange = (newValue: boolean) => {
    const { onValueChange } = this.props;

    onValueChange(newValue);

    this.setState({
      valueControlled: newValue,
    });
  };

  render() {
    const { value, disabled } = this.props;
    const { valueControlled } = this.state;
    const switchValue = typeof valueControlled !== 'undefined' ? valueControlled : value;

    return (
      <Switch
        value={switchValue}
        trackColor={{
          false: 'hsl(0, 0%, 86%)',
          true: BRAND_COLOR,
        }}
        onValueChange={this.handleValueChange}
        disabled={disabled}
      />
    );
  }
}
<|endoftext|>


<commit_before>import baseVw from '../baseVw';
import loadTemplate from '../../utils/loadTemplate';
import app from '../../app';
import $ from 'jquery';
import Dialog from '../modals/Dialog';
import Results from './Results';
import ResultsCol from '../../collections/Results';
import SettingsModal from '../modals/Settings/Settings';

export default class extends baseVw {
  constructor(options = {}) {
    super(options);
    this.options = options;

    const term = options.term;
    const testForURL = /^((http|https|ob):\/\/)/;

    if (term && testForURL.test(term)) {
      // if a search URL was passed in, reconstruct the url and parse the data
      const searchURL = new URL(`${term}${options.query ? `?${options.query}` : ''}`);
      const params = searchURL.searchParams;
      this.sProvider = `${searchURL.origin}${searchURL.pathname}`;
      this.serverPage = params.get('p') || 0;
      this.pageSize = params.get('ps') || 12;
      this.term = params.get('q') || '';
      this.callSearchProvider(searchURL);
    } else {
      this.sProvider = app.localSettings.get('searchProvider');
      this.serverPage = options.serverPage || 0;
      this.pageSize = options.pageSize || 12;
      // if the term was not a url, process the term before calling the search provider
      this.processTerm(term);
    }

    this.usingDefault = this.sProvider === app.localSettings.get('searchProvider');

    // if not using a passed in URL, update the default provider if it changes
    this.listenTo(app.localSettings, 'change:searchProvider', (_, provider) => {
      if (this.usingDefault) {
        this.sProvider = provider;
        this.processTerm(this.term);
      }
    });
  }

  className() {
    return 'search';
  }

  events() {
    return {
      'click .js-searchBtn': 'clickSearchBtn',
      'change .js-sortBy': 'changeSortBy',
      'change .js-filterWrapper select': 'changeFilter',
      'change .js-filterWrapper input': 'changeFilter',
      'keyup .js-searchInput': 'onKeyupSearchInput',
    };
  }

  get sortByQuery() {
    // return current sortBy state in the form of a query string
    return this.$sortBy ? `&sortBy=${this.$sortBy.val()}` : '';
  }

  get filterQuery() {
    // return all currently active filters in the form of a query string
    return this.$filters ? `&${this.$filters.serialize()}` : '';
  }

  processTerm(term) {
    this.term = term || '';
    // if term has spaces, replace them with +
    // if term is false, search for *
    const query = `q=${term ? term.replace(/\s+/g, '+') : '*'}`;
    const page = `&p=${this.serverPage}&ps=${this.pageSize}`;
    const searchURL = `${this.sProvider}?${query}${this.sortByQuery}${this.filterQuery}${page}`;

    this.callSearchProvider(searchURL);
  }

  callSearchProvider(searchURL) {
    // remove a pending search if it exists
    if (this.callSearch) this.callSearch.abort();

    // initial render to show the loading spinner
    this.render();

    // query the search provider
    this.callSearch = $.get({
      url: searchURL,
    })
        .done((data) => {
          this.render(data, searchURL);
        })
        .fail((xhr) => {
          if (xhr.statusText !== 'abort') {
            this.showSearchError(xhr);
            this.render({}, searchURL);
          }
        });
  }

  showSearchError(xhr = {}) {
    const title = app.polyglot.t('search.errors.searchFailTitle', { provider: this.sProvider });
    const failReason = xhr.responseJSON ? xhr.responseJSON.reason : '';
    const msg = failReason ?
        app.polyglot.t('search.errors.searchFailReason', { error: failReason }) : '';
    const buttons = [];
    if (this.usingDefault) {
      buttons.push({
        text: app.polyglot.t('search.changeProvider'),
        fragment: 'changeProvider',
      });
    } else {
      buttons.push({
        text: app.polyglot.t('search.useDefault',
            { term: this.term, defaultProvider: app.localSettings.get('searchProvider') }),
        fragment: 'useDefault',
      });
    }

    const errorDialog = new Dialog({
      title,
      msg,
      buttons,
      showCloseButton: false,
    }).render().open();
    this.listenTo(errorDialog, 'click-changeProvider', () => {
      this.changeProvider();
      errorDialog.close();
    });
    this.listenTo(errorDialog, 'click-useDefault', () => {
      this.useDefault();
      errorDialog.close();
    });
  }

  createResults(data, searchURL) {
    this.resultsCol = new ResultsCol();
    this.resultsCol.add(this.resultsCol.parse(data));

    const resultsView = this.createChild(Results, {
      searchURL,
      total: data.results ? data.results.total : 0,
      morePages: data.results ? data.results.morePages : false,
      serverPage: this.serverPage,
      pageSize: this.pageSize,
      initCol: this.resultsCol,
    });

    this.$resultsWrapper.html(resultsView.render().el);

    this.listenTo(resultsView, 'searchError', (xhr) => this.showSearchError(xhr));
    this.listenTo(resultsView, 'pageLoaded', () => this.scrollToTop());
  }

  clickSearchBtn() {
    this.processTerm(this.$searchInput.val());
  }

  onKeyupSearchInput(e) {
    if (e.which === 13) {
      this.processTerm(this.$searchInput.val());
    }
  }

  changeSortBy() {
    this.processTerm(this.term);
  }

  changeFilter() {
    this.processTerm(this.term);
  }

  changeProvider() {
    if (!this.settingsModal || !this.settingsModal.isOpen()) {
      this.settingsModal = new SettingsModal().render().open();
    }
  }

  useDefault() {
    this.usingDefault = true;
    this.sProvider = app.localSettings.get('searchProvider');
    this.processTerm(this.term);
  }

  scrollToTop() {
    this.$el[0].scrollIntoView();
  }

  render(data, searchURL) {
    if (data && !searchURL) {
      throw new Error('Please provide the search URL along with the data.');
    }

    // the first render has no data, and only shows the loading state
    const loading = !data;

    // check to see if the call to the provider failed, or returned an empty result
    const emptyData = $.isEmptyObject(data);

    loadTemplate('search/Search.html', (t) => {
      this.$el.html(t({
        term: this.term,
        provider: this.sProvider,
        defaultProvider: app.localSettings.get('searchProvider'),
        emptyData,
        loading,
        ...data,
      }));
    });
    this.$sortBy = this.$('#sortBy');
    this.$sortBy.select2();
    this.$('.js-filterWrapper').find('select').select2();
    this.$filters = this.$('.js-filterWrapper').find('select, input');
    this.$resultsWrapper = this.$('.js-resultsWrapper');
    this.$searchInput = this.$('.js-searchInput');
    this.$searchLogo = this.$('.js-searchLogo');

    this.$searchLogo.find('img').on('error', () => {
      this.$searchLogo.addClass('loadError');
    });

    // use the initial set of results data to create the results view
    if (data) this.createResults(data, searchURL);

    return this;
  }
}
<commit_msg>No search on sort by select list.<commit_after>import baseVw from '../baseVw';
import loadTemplate from '../../utils/loadTemplate';
import app from '../../app';
import $ from 'jquery';
import Dialog from '../modals/Dialog';
import Results from './Results';
import ResultsCol from '../../collections/Results';
import SettingsModal from '../modals/Settings/Settings';

export default class extends baseVw {
  constructor(options = {}) {
    super(options);
    this.options = options;

    const term = options.term;
    const testForURL = /^((http|https|ob):\/\/)/;

    if (term && testForURL.test(term)) {
      // if a search URL was passed in, reconstruct the url and parse the data
      const searchURL = new URL(`${term}${options.query ? `?${options.query}` : ''}`);
      const params = searchURL.searchParams;
      this.sProvider = `${searchURL.origin}${searchURL.pathname}`;
      this.serverPage = params.get('p') || 0;
      this.pageSize = params.get('ps') || 12;
      this.term = params.get('q') || '';
      this.callSearchProvider(searchURL);
    } else {
      this.sProvider = app.localSettings.get('searchProvider');
      this.serverPage = options.serverPage || 0;
      this.pageSize = options.pageSize || 12;
      // if the term was not a url, process the term before calling the search provider
      this.processTerm(term);
    }

    this.usingDefault = this.sProvider === app.localSettings.get('searchProvider');

    // if not using a passed in URL, update the default provider if it changes
    this.listenTo(app.localSettings, 'change:searchProvider', (_, provider) => {
      if (this.usingDefault) {
        this.sProvider = provider;
        this.processTerm(this.term);
      }
    });
  }

  className() {
    return 'search';
  }

  events() {
    return {
      'click .js-searchBtn': 'clickSearchBtn',
      'change .js-sortBy': 'changeSortBy',
      'change .js-filterWrapper select': 'changeFilter',
      'change .js-filterWrapper input': 'changeFilter',
      'keyup .js-searchInput': 'onKeyupSearchInput',
    };
  }

  get sortByQuery() {
    // return current sortBy state in the form of a query string
    return this.$sortBy ? `&sortBy=${this.$sortBy.val()}` : '';
  }

  get filterQuery() {
    // return all currently active filters in the form of a query string
    return this.$filters ? `&${this.$filters.serialize()}` : '';
  }

  processTerm(term) {
    this.term = term || '';
    // if term has spaces, replace them with +
    // if term is false, search for *
    const query = `q=${term ? term.replace(/\s+/g, '+') : '*'}`;
    const page = `&p=${this.serverPage}&ps=${this.pageSize}`;
    const searchURL = `${this.sProvider}?${query}${this.sortByQuery}${this.filterQuery}${page}`;

    this.callSearchProvider(searchURL);
  }

  callSearchProvider(searchURL) {
    // remove a pending search if it exists
    if (this.callSearch) this.callSearch.abort();

    // initial render to show the loading spinner
    this.render();

    // query the search provider
    this.callSearch = $.get({
      url: searchURL,
    })
        .done((data) => {
          this.render(data, searchURL);
        })
        .fail((xhr) => {
          if (xhr.statusText !== 'abort') {
            this.showSearchError(xhr);
            this.render({}, searchURL);
          }
        });
  }

  showSearchError(xhr = {}) {
    const title = app.polyglot.t('search.errors.searchFailTitle', { provider: this.sProvider });
    const failReason = xhr.responseJSON ? xhr.responseJSON.reason : '';
    const msg = failReason ?
        app.polyglot.t('search.errors.searchFailReason', { error: failReason }) : '';
    const buttons = [];
    if (this.usingDefault) {
      buttons.push({
        text: app.polyglot.t('search.changeProvider'),
        fragment: 'changeProvider',
      });
    } else {
      buttons.push({
        text: app.polyglot.t('search.useDefault',
            { term: this.term, defaultProvider: app.localSettings.get('searchProvider') }),
        fragment: 'useDefault',
      });
    }

    const errorDialog = new Dialog({
      title,
      msg,
      buttons,
      showCloseButton: false,
    }).render().open();
    this.listenTo(errorDialog, 'click-changeProvider', () => {
      this.changeProvider();
      errorDialog.close();
    });
    this.listenTo(errorDialog, 'click-useDefault', () => {
      this.useDefault();
      errorDialog.close();
    });
  }

  createResults(data, searchURL) {
    this.resultsCol = new ResultsCol();
    this.resultsCol.add(this.resultsCol.parse(data));

    const resultsView = this.createChild(Results, {
      searchURL,
      total: data.results ? data.results.total : 0,
      morePages: data.results ? data.results.morePages : false,
      serverPage: this.serverPage,
      pageSize: this.pageSize,
      initCol: this.resultsCol,
    });

    this.$resultsWrapper.html(resultsView.render().el);

    this.listenTo(resultsView, 'searchError', (xhr) => this.showSearchError(xhr));
    this.listenTo(resultsView, 'pageLoaded', () => this.scrollToTop());
  }

  clickSearchBtn() {
    this.processTerm(this.$searchInput.val());
  }

  onKeyupSearchInput(e) {
    if (e.which === 13) {
      this.processTerm(this.$searchInput.val());
    }
  }

  changeSortBy() {
    this.processTerm(this.term);
  }

  changeFilter() {
    this.processTerm(this.term);
  }

  changeProvider() {
    if (!this.settingsModal || !this.settingsModal.isOpen()) {
      this.settingsModal = new SettingsModal().render().open();
    }
  }

  useDefault() {
    this.usingDefault = true;
    this.sProvider = app.localSettings.get('searchProvider');
    this.processTerm(this.term);
  }

  scrollToTop() {
    this.$el[0].scrollIntoView();
  }

  render(data, searchURL) {
    if (data && !searchURL) {
      throw new Error('Please provide the search URL along with the data.');
    }

    // the first render has no data, and only shows the loading state
    const loading = !data;

    // check to see if the call to the provider failed, or returned an empty result
    const emptyData = $.isEmptyObject(data);

    loadTemplate('search/Search.html', (t) => {
      this.$el.html(t({
        term: this.term,
        provider: this.sProvider,
        defaultProvider: app.localSettings.get('searchProvider'),
        emptyData,
        loading,
        ...data,
      }));
    });
    this.$sortBy = this.$('#sortBy');
    this.$sortBy.select2({
      // disables the search box
      minimumResultsForSearch: Infinity,
    });
    this.$('.js-filterWrapper').find('select').select2();
    this.$filters = this.$('.js-filterWrapper').find('select, input');
    this.$resultsWrapper = this.$('.js-resultsWrapper');
    this.$searchInput = this.$('.js-searchInput');
    this.$searchLogo = this.$('.js-searchLogo');

    this.$searchLogo.find('img').on('error', () => {
      this.$searchLogo.addClass('loadError');
    });

    // use the initial set of results data to create the results view
    if (data) this.createResults(data, searchURL);

    return this;
  }
}
<|endoftext|>


<commit_before>self.addEventListener('notificationclick', function (event) {
    // console.log("notification click", event)
    // event.notification.close();

    event.waitUntil(
        clients
            .matchAll({type: "window"})
            .then(function (clientList) {
                for (var i = 0; i < clientList.length; i++) {
                    const client = clientList[i]
                    postMessage(client, event)
                    if (client.focus) {
                        return client.focus();
                    }
                }
                if (clients.openWindow) {
                    return clients
                        .openWindow(url)
                        .then(function(client) {
                            setTimeout(function () {
                                postMessage(client, event)
                            },2000)
                        })
                }
            })
    );

}, false);

function postMessage(client, event) {
    client.postMessage({
        type: "notification-clicked",
        action: event.action,
        data: event.notification.data
    })
}

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
    'messagingSenderId': '49437522774'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
// const messaging = firebase.messaging();

// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a sevice worker
//   `messaging.setBackgroundMessageHandler` handler.
// messaging.onMessage(function(payload) {
//     console.log("Message received. ", payload);
//     ...
// });

messaging.setBackgroundMessageHandler(function(payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Background Message Title';
    const notificationOptions = {
        body: 'Background Message body.',
        icon: '/firebase-logo.png'
    };

    return self.registration.showNotification(notificationTitle,
        notificationOptions);
});
<commit_msg>add fcm push<commit_after>self.addEventListener('notificationclick', function (event) {
    // console.log("notification click", event)
    // event.notification.close();

    event.waitUntil(
        clients
            .matchAll({type: "window"})
            .then(function (clientList) {
                for (var i = 0; i < clientList.length; i++) {
                    const client = clientList[i]
                    postMessage(client, event)
                    if (client.focus) {
                        return client.focus();
                    }
                }
                if (clients.openWindow) {
                    return clients
                        .openWindow(url)
                        .then(function(client) {
                            setTimeout(function () {
                                postMessage(client, event)
                            },2000)
                        })
                }
            })
    );

}, false);

function postMessage(client, event) {
    client.postMessage({
        type: "notification-clicked",
        action: event.action,
        data: event.notification.data
    })
}

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
    'messagingSenderId': '49437522774'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a sevice worker
//   `messaging.setBackgroundMessageHandler` handler.
// messaging.onMessage(function(payload) {
//     console.log("Message received. ", payload);
//     // ...
// });

messaging.setBackgroundMessageHandler(function(payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Background Message Title';
    const notificationOptions = {
        body: 'Background Message body.',
        icon: '/firebase-logo.png'
    };

    return self.registration.showNotification(notificationTitle,
        notificationOptions);
});
<|endoftext|>


<commit_before>var $ = require('jquery')
var _ = require('lodash')

export default class AugurAPI {
  constructor (hostURL, version, autobatch) {
    this.__downloadedGitRepos = []

    this._version = version || '/api/unstable'
    this._host = hostURL || 'http://' + window.location.host
    this.__cache = {}
    this.__timeout = null
    this.__pending = {}

    this.getDownloadedGitRepos = this.__EndpointFactory('repos')
    this.openRequests = 0
    this.getMetricsStatus = this.__EndpointFactory('metrics/status/filter')
    this.getMetricsStatusMetadata = this.__EndpointFactory('metrics/status/metadata')
  }

  // __autobatcher (url, params, fireTimeout) {
  //   if (this.__timeout !== null && !fireTimeout) {
  //     this.__timeout = setTimeout(() => {
  //       __autobatch(undefined, undefined, true)
  //     })
  //   }
  //   return new Promise((resolve, reject) => {
  //     if (fireTimeout) {
  //       let batchURL = this._host + this._version + '/batch'
  //       let requestArray = []
  //       Object.keys(this.__pending).forEach((key) => {
  //         requestArray.push({})
  //       })
  //       $.post(batchURL)
  //     }
  //   })
  // }

  __endpointURL (endpoint) {
    return '' + this._host + this._version + '/' + endpoint
  }

  __URLFunctionFactory (url) {
    var self = this
    return function (params, callback) {
      var cacheKey = window.btoa(url + JSON.stringify(params))
      this.openRequests++
      if (self.__cache[cacheKey]) {
        if (self.__cache[cacheKey].created_at > Date.now() - 1000 * 60) {
          return new Promise((resolve, reject) => {
            resolve(self.__cache[cacheKey].data)
          })
        }
      }
      return $.get(url, params).then((data) => {
        this.openRequests--
        self.__cache[cacheKey] = {
          created_at: Date.now(),
          data: data
        }
        return data
      })
    }
  }

  __EndpointFactory (endpoint) {
    return this.__URLFunctionFactory(this.__endpointURL(endpoint))
  }

  batch (endpoints) {
    let str = '[{"method": "GET", "path": "' + endpoints.join('"},{"method": "GET", "path": "') + '"}]'
    this.openRequests++
    let url = this.__endpointURL('batch')
    // Check cached
    if (this.__cache[window.btoa(url + endpoints.join(','))]) {
      if (this.__cache[window.btoa(url + endpoints.join(','))].created_at > Date.now() - 1000 * 60) {
        return new Promise((resolve, reject) => {
          resolve(this.__cache[window.btoa(url + endpoints.join(','))].data)
        })
      }
    }
    return $.ajax(url, {
      type: 'post',
      data: str,
      dataType: 'json',
      contentType: 'application/json'
    }).then((data) => {
      this.openRequests--
      // Save to cache
      this.__cache[window.btoa(url + endpoints.join(','))] = {
        created_at: Date.now(),
        data: data
      }
      return data
    })
  }

  batchMapped (repos, fields) {
    let endpoints = []
    let reverseMap = {}
    let processedData = {}
    repos.forEach((repo) => {
      Array.prototype.push.apply(endpoints, repo.batch(fields, true))
      _.assign(reverseMap, repo.__reverseEndpointMap)
      processedData[repo.toString()] = {}
    })
    return this.batch(endpoints).then((data) => {

      return new Promise((resolve, reject) => {
        if (Array.isArray(data)) {
          data.forEach(response => {
            if (response.status === 200 && reverseMap[response.path]) {
              processedData[reverseMap[response.path].owner][reverseMap[response.path].name] = JSON.parse(response.response)
            } else if (reverseMap[response.path]){
              processedData[reverseMap[response.path].owner][reverseMap[response.path].name] = null
            }
          })
          resolve(processedData)
        } else {
          reject(new Error('data-not-array'))
        }
      })
    })
  }

  Repo (repo) {
    if (repo.githubURL) {
      let splitURL = repo.githubURL.split('/')
      if (splitURL.length < 3) {
        repo.owner = splitURL[0]
        repo.name = splitURL[1]
      } else {
        repo.owner = splitURL[3]
        repo.name = splitURL[4]
      }
    }

    if (repo.gitURL) {
      if (repo.gitURL.includes('github.com')) {
        let splitURL = repo.gitURL.split('/')
        repo.owner = splitURL[1]
        repo.name = splitURL[2].split('.')[0]
      } else {
        let splitURL = repo.gitURL.split('/')
        repo.owner = splitURL[0]
        repo.name = splitURL[1]
      }
    }

    repo.toString = () => {
      if (repo.owner && repo.name) {
        return repo.owner + '/' + repo.name
      } else {
        return JSON.stringify(repo)
      }
    }
    repo.__endpointMap = {}
    repo.__reverseEndpointMap = {}

    repo.getDownloadedStatus = () => {
      this.getDownloadedGitRepos().then((data) => {
        let rs = false
        data.forEach((gitURL) => {
          if (gitURL.includes('github.com')) {
            let splitURL = gitURL.split('/')
            let owner = splitURL[3]
            let name = splitURL[4].split('.')[0]
            if (repo.toString() === (owner + '/' + name)) {
              rs = true
            }
          }
        })
        return rs
      })
    }

    var __Endpoint = (r, name, url) => {
      r[name] = this.__URLFunctionFactory(url)
      return r[name]
    }

    var Endpoint = (r, name, endpoint) => {
      var fullEndpoint = this._version + '/' + repo.owner + '/' + repo.name + '/' + endpoint
      var url = this._host + fullEndpoint
      r.__endpointMap[name] = fullEndpoint
      r.__reverseEndpointMap[fullEndpoint] = { name: name, owner: repo.toString() }
      return __Endpoint(r, name, url)
    }

    var Timeseries = (r, jsName, endpoint) => {
      let func = Endpoint(r, jsName, 'timeseries/' + endpoint)
      func.relativeTo = (baselineRepo, params, callback) => {
        var url = 'timeseries/' + endpoint + '/relative_to/' + baselineRepo.owner + '/' + baselineRepo.name
        return Endpoint(url)()
      }
      return func
    }

    var GitEndpoint = (r, jsName, endpoint) => {
      var url = this.__endpointURL('git/' + endpoint + '/?repo_url_base=' + window.btoa(r.gitURL))
      return __Endpoint(r, jsName, url)
    }

    var addRepoMetric = (r, jsName, endpoint) => {
      var url = this.__endpointURL('repo-groups/'+ repo.repoGroupID + '/repos'+ repo.repoID + '/' + endpoint)
      return __Endpoint(r, jsName, url)
    }

    repo.batch = (jsNameArray, noExecute) => {
      var routes = jsNameArray.map((e) => { return repo.__endpointMap[e] })
      if (noExecute) {
        return routes
      }
      return this.batch(routes).then((data) => {
        return new Promise((resolve, reject) => {
          if (Array.isArray(data)) {
            let mapped = {}
            data.forEach(response => {
              if (response.status === 200) {
                mapped[repo.__reverseEndpointMap[response.path].name] = JSON.parse(response.response)
              } else {
                mapped[repo.__reverseEndpointMap[response.path].name] = null
              }
            })
            resolve(mapped)
          } else {
            reject(new Error('data-not-array'))
          }
        })
      })
    }

    if (repo.owner && repo.name) {
      // DIVERSITY AND INCLUSION

      // GROWTH, MATURITY, AND DECLINE
      Timeseries(repo, 'closedIssues', 'issues/closed')
      Timeseries(repo, 'closedIssueResolutionDuration', 'issues/time_to_close')
      Timeseries(repo, 'codeCommits', 'commits')
      // Timeseries(repo, 'codeReviews', 'code_reviews')
      Timeseries(repo, 'codeReviewIteration', 'code_review_iteration')
      Timeseries(repo, 'contributionAcceptance', 'contribution_acceptance')
      Endpoint(repo, 'contributingGithubOrganizations', 'contributing_github_organizations')
      Timeseries(repo, 'firstResponseToIssueDuration', 'issues/response_time')
      Timeseries(repo, 'forks', 'forks')
      Timeseries(repo, 'linesOfCodeChanged', 'lines_changed')
      Timeseries(repo, 'maintainerResponseToMergeRequestDuration', 'pulls/maintainer_response_time')
      Timeseries(repo, 'newContributingGithubOrganizations', 'new_contributing_github_organizations')
      Timeseries(repo, 'openIssues', 'issues')
      Timeseries(repo, 'pullRequestComments', 'pulls/comments')
      Timeseries(repo, 'pullRequestsOpen', 'pulls')

      // RISK

      // VALUE

      // ACTIVITY
      Timeseries(repo, 'issueComments', 'issue_comments')
      Timeseries(repo, 'pullRequestsMadeClosed', 'pulls/made_closed')
      Timeseries(repo, 'watchers', 'watchers')

      // EXPERIMENTAL

      // Commit Related
      Timeseries(repo, 'commits100', 'commits100')
      Timeseries(repo, 'commitComments', 'commits/comments')
      Endpoint(repo, 'committerLocations', 'committer_locations')
      Timeseries(repo, 'totalCommitters', 'total_committers')

      // Issue Related
      Timeseries(repo, 'issueActivity', 'issues/activity')

      // Community / Contributions
      Endpoint(repo, 'communityAge', 'community_age')
      Timeseries(repo, 'communityEngagement', 'community_engagement')
      Endpoint(repo, 'contributors', 'contributors')
      Endpoint(repo, 'contributions', 'contributions')
      Endpoint(repo, 'projectAge', 'project_age')

      // Dependency Related
      Endpoint(repo, 'dependencies', 'dependencies')
      Endpoint(repo, 'dependencyStats', 'dependency_stats')
      Endpoint(repo, 'dependents', 'dependents')

      // Other
      Endpoint(repo, 'busFactor', 'bus_factor')
      Timeseries(repo, 'downloads', 'downloads')
      Timeseries(repo, 'fakes', 'fakes')
      Endpoint(repo, 'linkingWebsites', 'linking_websites')
      Timeseries(repo, 'majorTags', 'tags/major')
      Timeseries(repo, 'newWatchers', 'new_watchers')
      Timeseries(repo, 'tags', 'tags')
    }

    if (repo.gitURL) {
      // Other
      GitEndpoint(repo, 'changesByAuthor', 'changes_by_author'),
      GitEndpoint(repo, 'linesOfCodeCommitCountsByCalendarYearGrouped', 'lines_of_code_commit_counts_by_calendar_year_grouped'),
      GitEndpoint(repo, 'annualLinesOfCodeCountRankedByRepoInRepoGroup', 'annual_lines_of_code_count_ranked_by_repo_in_repo_group'),
      GitEndpoint(repo, 'annualCommitCountRankedByRepoInRepoGroup', 'annual_commit_count_ranked_by_repo_in_repo_group'),
      GitEndpoint(repo, 'annualLinesOfCodeCountRankedByNewRepoInRepoGroup', 'annual_lines_of_code_count_ranked_by_new_repo_in_repo_group'),
      GitEndpoint(repo, 'annualCommitCountRankedByNewRepoInRepoGroup', 'annual_commit_count_ranked_by_new_repo_in_repo_group')
      GitEndpoint(repo, 'facadeProject', 'facade_project')
    }

    if (repo.repoGroupID && repo.repoID) {
      addRepoMetric(repo, 'codeChanges', 'code-changes')
      addRepoMetric(repo, 'codeChangesLines', 'code-changes-lines')
      addRepoMetric(repo, 'issueNew', 'issues-new')
      addRepoMetric(repo, 'issuesClosed', 'issues-closed')
      addRepoMetric(repo, 'issueBacklog', 'issue-backlog')
      addRepoMetric(repo, 'pullRequestsMergeContributorNew', 'pull-requests-merge-contributor-new')
      addRepoMetric(repo, 'issuesFirstTimeOpened', 'issues-first-time-opened')
      addRepoMetric(repo, 'issuesFirstTimeClosed', 'issues-first-time-closed')
      addRepoMetric(repo, 'subProject', 'sub-projects')
      addRepoMetric(repo, 'contributors', 'contributors')
      addRepoMetric(repo, 'contributorsNew', 'contributors-new')
    }

    return repo
  }
}
<commit_msg>add helper function addRepoGroupMetric<commit_after>var $ = require('jquery')
var _ = require('lodash')

export default class AugurAPI {
  constructor (hostURL, version, autobatch) {
    this.__downloadedGitRepos = []

    this._version = version || '/api/unstable'
    this._host = hostURL || 'http://' + window.location.host
    this.__cache = {}
    this.__timeout = null
    this.__pending = {}

    this.getDownloadedGitRepos = this.__EndpointFactory('repos')
    this.getRepoGroups = this.__EndpointFactory('repo-groups')
    this.openRequests = 0
    this.getMetricsStatus = this.__EndpointFactory('metrics/status/filter')
    this.getMetricsStatusMetadata = this.__EndpointFactory('metrics/status/metadata')
  }

  // __autobatcher (url, params, fireTimeout) {
  //   if (this.__timeout !== null && !fireTimeout) {
  //     this.__timeout = setTimeout(() => {
  //       __autobatch(undefined, undefined, true)
  //     })
  //   }
  //   return new Promise((resolve, reject) => {
  //     if (fireTimeout) {
  //       let batchURL = this._host + this._version + '/batch'
  //       let requestArray = []
  //       Object.keys(this.__pending).forEach((key) => {
  //         requestArray.push({})
  //       })
  //       $.post(batchURL)
  //     }
  //   })
  // }

  __endpointURL (endpoint) {
    return '' + this._host + this._version + '/' + endpoint
  }

  __URLFunctionFactory (url) {
    var self = this
    return function (params, callback) {
      var cacheKey = window.btoa(url + JSON.stringify(params))
      this.openRequests++
      if (self.__cache[cacheKey]) {
        if (self.__cache[cacheKey].created_at > Date.now() - 1000 * 60) {
          return new Promise((resolve, reject) => {
            resolve(self.__cache[cacheKey].data)
          })
        }
      }
      return $.get(url, params).then((data) => {
        this.openRequests--
        self.__cache[cacheKey] = {
          created_at: Date.now(),
          data: data
        }
        return data
      })
    }
  }

  __EndpointFactory (endpoint) {
    return this.__URLFunctionFactory(this.__endpointURL(endpoint))
  }

  batch (endpoints) {
    let str = '[{"method": "GET", "path": "' + endpoints.join('"},{"method": "GET", "path": "') + '"}]'
    this.openRequests++
    let url = this.__endpointURL('batch')
    // Check cached
    if (this.__cache[window.btoa(url + endpoints.join(','))]) {
      if (this.__cache[window.btoa(url + endpoints.join(','))].created_at > Date.now() - 1000 * 60) {
        return new Promise((resolve, reject) => {
          resolve(this.__cache[window.btoa(url + endpoints.join(','))].data)
        })
      }
    }
    return $.ajax(url, {
      type: 'post',
      data: str,
      dataType: 'json',
      contentType: 'application/json'
    }).then((data) => {
      this.openRequests--
      // Save to cache
      this.__cache[window.btoa(url + endpoints.join(','))] = {
        created_at: Date.now(),
        data: data
      }
      return data
    })
  }

  batchMapped (repos, fields) {
    let endpoints = []
    let reverseMap = {}
    let processedData = {}
    repos.forEach((repo) => {
      Array.prototype.push.apply(endpoints, repo.batch(fields, true))
      _.assign(reverseMap, repo.__reverseEndpointMap)
      processedData[repo.toString()] = {}
    })
    return this.batch(endpoints).then((data) => {

      return new Promise((resolve, reject) => {
        if (Array.isArray(data)) {
          data.forEach(response => {
            if (response.status === 200 && reverseMap[response.path]) {
              processedData[reverseMap[response.path].owner][reverseMap[response.path].name] = JSON.parse(response.response)
            } else if (reverseMap[response.path]){
              processedData[reverseMap[response.path].owner][reverseMap[response.path].name] = null
            }
          })
          resolve(processedData)
        } else {
          reject(new Error('data-not-array'))
        }
      })
    })
  }

  Repo (repo) {
    if (repo.githubURL) {
      let splitURL = repo.githubURL.split('/')
      if (splitURL.length < 3) {
        repo.owner = splitURL[0]
        repo.name = splitURL[1]
      } else {
        repo.owner = splitURL[3]
        repo.name = splitURL[4]
      }
    }

    if (repo.gitURL) {
      if (repo.gitURL.includes('github.com')) {
        let splitURL = repo.gitURL.split('/')
        repo.owner = splitURL[1]
        repo.name = splitURL[2].split('.')[0]
      } else {
        let splitURL = repo.gitURL.split('/')
        repo.owner = splitURL[0]
        repo.name = splitURL[1]
      }
    }

    repo.toString = () => {
      if (repo.owner && repo.name) {
        return repo.owner + '/' + repo.name
      } else {
        return JSON.stringify(repo)
      }
    }
    repo.__endpointMap = {}
    repo.__reverseEndpointMap = {}

    repo.getDownloadedStatus = () => {
      this.getDownloadedGitRepos().then((data) => {
        let rs = false
        data.forEach((gitURL) => {
          if (gitURL.includes('github.com')) {
            let splitURL = gitURL.split('/')
            let owner = splitURL[3]
            let name = splitURL[4].split('.')[0]
            if (repo.toString() === (owner + '/' + name)) {
              rs = true
            }
          }
        })
        return rs
      })
    }

    var __Endpoint = (r, name, url) => {
      r[name] = this.__URLFunctionFactory(url)
      return r[name]
    }

    var Endpoint = (r, name, endpoint) => {
      var fullEndpoint = this._version + '/' + repo.owner + '/' + repo.name + '/' + endpoint
      var url = this._host + fullEndpoint
      r.__endpointMap[name] = fullEndpoint
      r.__reverseEndpointMap[fullEndpoint] = { name: name, owner: repo.toString() }
      return __Endpoint(r, name, url)
    }

    var Timeseries = (r, jsName, endpoint) => {
      let func = Endpoint(r, jsName, 'timeseries/' + endpoint)
      func.relativeTo = (baselineRepo, params, callback) => {
        var url = 'timeseries/' + endpoint + '/relative_to/' + baselineRepo.owner + '/' + baselineRepo.name
        return Endpoint(url)()
      }
      return func
    }

    var GitEndpoint = (r, jsName, endpoint) => {
      var url = this.__endpointURL('git/' + endpoint + '/?repo_url_base=' + window.btoa(r.gitURL))
      return __Endpoint(r, jsName, url)
    }

    var addRepoMetric = (r, jsName, endpoint) => {
      var url = this.__endpointURL('repo-groups/'+ repo.repoGroupID + '/repos'+ repo.repoID + '/' + endpoint)
      return __Endpoint(r, jsName, url)
    }

    var addRepoGroupMetric = (r, jsName, endpoint) => {
      var url = this.__endpointURL('repo-groups/'+ repo.repoGroupID + '/' + endpoint)
      return __Endpoint(r, jsName, url)
    }

    repo.batch = (jsNameArray, noExecute) => {
      var routes = jsNameArray.map((e) => { return repo.__endpointMap[e] })
      if (noExecute) {
        return routes
      }
      return this.batch(routes).then((data) => {
        return new Promise((resolve, reject) => {
          if (Array.isArray(data)) {
            let mapped = {}
            data.forEach(response => {
              if (response.status === 200) {
                mapped[repo.__reverseEndpointMap[response.path].name] = JSON.parse(response.response)
              } else {
                mapped[repo.__reverseEndpointMap[response.path].name] = null
              }
            })
            resolve(mapped)
          } else {
            reject(new Error('data-not-array'))
          }
        })
      })
    }

    if (repo.owner && repo.name) {
      // DIVERSITY AND INCLUSION

      // GROWTH, MATURITY, AND DECLINE
      Timeseries(repo, 'closedIssues', 'issues/closed')
      Timeseries(repo, 'closedIssueResolutionDuration', 'issues/time_to_close')
      Timeseries(repo, 'codeCommits', 'commits')
      // Timeseries(repo, 'codeReviews', 'code_reviews')
      Timeseries(repo, 'codeReviewIteration', 'code_review_iteration')
      Timeseries(repo, 'contributionAcceptance', 'contribution_acceptance')
      Endpoint(repo, 'contributingGithubOrganizations', 'contributing_github_organizations')
      Timeseries(repo, 'firstResponseToIssueDuration', 'issues/response_time')
      Timeseries(repo, 'forks', 'forks')
      Timeseries(repo, 'linesOfCodeChanged', 'lines_changed')
      Timeseries(repo, 'maintainerResponseToMergeRequestDuration', 'pulls/maintainer_response_time')
      Timeseries(repo, 'newContributingGithubOrganizations', 'new_contributing_github_organizations')
      Timeseries(repo, 'openIssues', 'issues')
      Timeseries(repo, 'pullRequestComments', 'pulls/comments')
      Timeseries(repo, 'pullRequestsOpen', 'pulls')

      // RISK

      // VALUE

      // ACTIVITY
      Timeseries(repo, 'issueComments', 'issue_comments')
      Timeseries(repo, 'pullRequestsMadeClosed', 'pulls/made_closed')
      Timeseries(repo, 'watchers', 'watchers')

      // EXPERIMENTAL

      // Commit Related
      Timeseries(repo, 'commits100', 'commits100')
      Timeseries(repo, 'commitComments', 'commits/comments')
      Endpoint(repo, 'committerLocations', 'committer_locations')
      Timeseries(repo, 'totalCommitters', 'total_committers')

      // Issue Related
      Timeseries(repo, 'issueActivity', 'issues/activity')

      // Community / Contributions
      Endpoint(repo, 'communityAge', 'community_age')
      Timeseries(repo, 'communityEngagement', 'community_engagement')
      Endpoint(repo, 'contributors', 'contributors')
      Endpoint(repo, 'contributions', 'contributions')
      Endpoint(repo, 'projectAge', 'project_age')

      // Dependency Related
      Endpoint(repo, 'dependencies', 'dependencies')
      Endpoint(repo, 'dependencyStats', 'dependency_stats')
      Endpoint(repo, 'dependents', 'dependents')

      // Other
      Endpoint(repo, 'busFactor', 'bus_factor')
      Timeseries(repo, 'downloads', 'downloads')
      Timeseries(repo, 'fakes', 'fakes')
      Endpoint(repo, 'linkingWebsites', 'linking_websites')
      Timeseries(repo, 'majorTags', 'tags/major')
      Timeseries(repo, 'newWatchers', 'new_watchers')
      Timeseries(repo, 'tags', 'tags')
    }

    if (repo.gitURL) {
      // Other
      GitEndpoint(repo, 'changesByAuthor', 'changes_by_author'),
      GitEndpoint(repo, 'linesOfCodeCommitCountsByCalendarYearGrouped', 'lines_of_code_commit_counts_by_calendar_year_grouped'),
      GitEndpoint(repo, 'annualLinesOfCodeCountRankedByRepoInRepoGroup', 'annual_lines_of_code_count_ranked_by_repo_in_repo_group'),
      GitEndpoint(repo, 'annualCommitCountRankedByRepoInRepoGroup', 'annual_commit_count_ranked_by_repo_in_repo_group'),
      GitEndpoint(repo, 'annualLinesOfCodeCountRankedByNewRepoInRepoGroup', 'annual_lines_of_code_count_ranked_by_new_repo_in_repo_group'),
      GitEndpoint(repo, 'annualCommitCountRankedByNewRepoInRepoGroup', 'annual_commit_count_ranked_by_new_repo_in_repo_group')
      GitEndpoint(repo, 'facadeProject', 'facade_project')
    }

    if (repo.repoGroupID && repo.repoID) {
      addRepoMetric(repo, 'codeChanges', 'code-changes')
      addRepoMetric(repo, 'codeChangesLines', 'code-changes-lines')
      addRepoMetric(repo, 'issueNew', 'issues-new')
      addRepoMetric(repo, 'issuesClosed', 'issues-closed')
      addRepoMetric(repo, 'issueBacklog', 'issue-backlog')
      addRepoMetric(repo, 'pullRequestsMergeContributorNew', 'pull-requests-merge-contributor-new')
      addRepoMetric(repo, 'issuesFirstTimeOpened', 'issues-first-time-opened')
      addRepoMetric(repo, 'issuesFirstTimeClosed', 'issues-first-time-closed')
      addRepoMetric(repo, 'subProject', 'sub-projects')
      addRepoMetric(repo, 'contributors', 'contributors')
      addRepoMetric(repo, 'contributorsNew', 'contributors-new')
    }

    if (repo.repoGroupID && repo.repoID == null) {
      addRepoGroupMetric(repo, 'codeChanges', 'code-changes')
      addRepoGroupMetric(repo, 'codeChangesLines', 'code-changes-lines')
      addRepoGroupMetric(repo, 'issueNew', 'issues-new')
      addRepoGroupMetric(repo, 'issuesClosed', 'issues-closed')
      addRepoGroupMetric(repo, 'issueBacklog', 'issue-backlog')
      addRepoGroupMetric(repo, 'pullRequestsMergeContributorNew', 'pull-requests-merge-contributor-new')
      addRepoGroupMetric(repo, 'issuesFirstTimeOpened', 'issues-first-time-opened')
      addRepoGroupMetric(repo, 'issuesFirstTimeClosed', 'issues-first-time-closed')
      addRepoGroupMetric(repo, 'subProject', 'sub-projects')
      addRepoGroupMetric(repo, 'contributors', 'contributors')
      addRepoGroupMetric(repo, 'contributorsNew', 'contributors-new')
    }

    return repo
  }
}
<|endoftext|>


<commit_before>/**
 * @flow
 * @private
 */

import Node from './node'
import Query from './query'
import { getKeyString, getKeyPaths, type KeyPath } from '../key-path'
import { type Callback } from '../emitter'

// The root key is an empty string. This can be a little
// counter-intuitive, so we keep track of them as a named constant.
const ROOT_PATH = ''

class CompareTree {
  snapshot: Object
  nodes: { [key: string]: Node }
  queries: { [key: string]: Query }

  constructor(initial: Object) {
    this.snapshot = initial
    this.nodes = {}
    this.queries = {}
  }

  /**
   * Create a subscription to a particular set of key paths.
   */
  on(subscription: *, callback: Callback, scope?: Object) {
    let keyPaths = getKeyPaths(subscription)
    let query = this.addQuery(keyPaths)

    for (var i = 0, len = keyPaths.length; i < len; i++) {
      this.addBranch(keyPaths[i], query)
    }

    query.on('change', callback, scope)

    return query
  }

  /**
   * Remove a subscription created by .on()
   */
  off(subscription: *, callback: Callback, scope?: Object) {
    let keyPaths = getKeyPaths(subscription)
    let id = Query.getId(keyPaths)

    let query = this.queries[id]

    if (query) {
      query.off('change', callback, scope)

      if (query.isAlone()) {
        this.prune(query)
      }
    }
  }

  /**
   * Compare a new snapshot to the last snapshot, triggering event
   * subscriptions along the way.
   */
  update(snapshot: Object) {
    let last = this.snapshot
    this.snapshot = snapshot

    let root = this.nodes[ROOT_PATH]

    if (root) {
      var queries = this.scan(this.nodes[ROOT_PATH], last, snapshot, [])

      for (var i = 0; i < queries.length; i++) {
        queries[i].trigger(snapshot)
      }
    }
  }

  /**
   * Add a node to the tree if it has not otherwise been added.
   */
  addNode(key: string, parent: ?Node) {
    let id = Node.getId(key, parent)

    if (!this.nodes[id]) {
      this.nodes[id] = new Node(id, key, parent)
    }

    return this.nodes[id]
  }

  /**
   * Add a query to the tree if it has not otherwise been
   * added. Queries are leaf nodes responsible for managing
   * subscriptions.
   */
  addQuery(dependencies: KeyPath[]): Query {
    let id = Query.getId(dependencies)

    if (!this.queries[id]) {
      this.queries[id] = new Query(id, dependencies)
    }

    return this.queries[id]
  }

  /**
   * Remove a query, then traverse that queries key paths to remove
   * unused parents.
   */
  prune(query: Query) {
    let ids = query.keyPaths.map(getKeyString)

    for (var i = 0, len = ids.length; i < len; i++) {
      let node = this.nodes[ids[i]]

      node.disconnect(query)

      do {
        if (node.isAlone()) {
          node.orphan()
          delete this.nodes[node.id]
        } else {
          break
        }

        node = node.parent
      } while (node)
    }

    delete this.queries[query.id]
  }

  /**
   * Build up a branch of nodes given a path of keys, appending a query
   * to the end.
   */
  addBranch(path: KeyPath, query: Query) {
    let last = this.addNode(ROOT_PATH, null)

    for (var i = 0, len = path.length; i < len; i++) {
      last = this.addNode(path[i], last)
    }

    last.connect(query)
  }

  /**
   * Traverse the tree of subscriptions, triggering queries along the way
   */
  scan(node: Node, last: *, next: *, queries: Query[]) {
    if (last !== next) {
      var edges = node.edges

      for (var i = 0, len = edges.length; i < len; i++) {
        var edge = edges[i]

        if (edge instanceof Query && queries.indexOf(edge) < 0) {
          queries.push(edge)
        } else if (edge instanceof Node) {
          var edgeLast = last == null ? last : last[edge.key]
          var edgeNext = next == null ? next : next[edge.key]

          this.scan(edge, edgeLast, edgeNext, queries)
        }
      }
    }

    return queries
  }
}

export default CompareTree
<commit_msg>Use hasOwnProperty instead of null check to prevent deopt.<commit_after>/**
 * @flow
 * @private
 */

import Node from './node'
import Query from './query'
import { getKeyString, getKeyPaths, type KeyPath } from '../key-path'
import { type Callback } from '../emitter'

// The root key is an empty string. This can be a little
// counter-intuitive, so we keep track of them as a named constant.
const ROOT_PATH = ''

class CompareTree {
  snapshot: Object
  nodes: { [key: string]: Node }
  queries: { [key: string]: Query }

  constructor(initial: Object) {
    this.snapshot = initial
    this.nodes = {}
    this.queries = {}
  }

  /**
   * Create a subscription to a particular set of key paths.
   */
  on(subscription: *, callback: Callback, scope?: Object) {
    let keyPaths = getKeyPaths(subscription)
    let query = this.addQuery(keyPaths)

    for (var i = keyPaths.length - 1; i >= 0; i--) {
      this.addBranch(keyPaths[i], query)
    }

    query.on('change', callback, scope)

    return query
  }

  /**
   * Remove a subscription created by .on()
   */
  off(subscription: *, callback: Callback, scope?: Object) {
    let keyPaths = getKeyPaths(subscription)
    let id = Query.getId(keyPaths)

    let query = this.queries[id]

    if (query) {
      query.off('change', callback, scope)

      if (query.isAlone()) {
        this.prune(query)
      }
    }
  }

  /**
   * Compare a new snapshot to the last snapshot, triggering event
   * subscriptions along the way.
   */
  update(snapshot: Object) {
    let last = this.snapshot
    this.snapshot = snapshot

    let root = this.nodes[ROOT_PATH]

    if (root) {
      var queries = this.scan(this.nodes[ROOT_PATH], last, snapshot, [])

      for (var i = 0; i < queries.length; i++) {
        queries[i].trigger(snapshot)
      }
    }
  }

  /**
   * Add a node to the tree if it has not otherwise been added.
   */
  addNode(key: string, parent: ?Node) {
    let id = Node.getId(key, parent)

    if (this.nodes.hasOwnProperty(id) === false) {
      this.nodes[id] = new Node(id, key, parent)
    }

    return this.nodes[id]
  }

  /**
   * Add a query to the tree if it has not otherwise been
   * added. Queries are leaf nodes responsible for managing
   * subscriptions.
   */
  addQuery(dependencies: KeyPath[]): Query {
    let id = Query.getId(dependencies)

    if (this.queries.hasOwnProperty(id) === false) {
      this.queries[id] = new Query(id, dependencies)
    }

    return this.queries[id]
  }

  /**
   * Remove a query, then traverse that queries key paths to remove
   * unused parents.
   */
  prune(query: Query) {
    let ids = query.keyPaths.map(getKeyString)

    for (var i = 0, len = ids.length; i < len; i++) {
      let node = this.nodes[ids[i]]

      node.disconnect(query)

      do {
        if (node.isAlone()) {
          node.orphan()
          delete this.nodes[node.id]
        } else {
          break
        }

        node = node.parent
      } while (node)
    }

    delete this.queries[query.id]
  }

  /**
   * Build up a branch of nodes given a path of keys, appending a query
   * to the end.
   */
  addBranch(path: KeyPath, query: Query) {
    let last = this.addNode(ROOT_PATH, null)

    for (var i = 0, len = path.length; i < len; i++) {
      last = this.addNode(path[i], last)
    }

    last.connect(query)
  }

  /**
   * Traverse the tree of subscriptions, triggering queries along the way
   */
  scan(node: Node, last: *, next: *, queries: Query[]) {
    if (last !== next) {
      var edges = node.edges

      for (var i = 0, len = edges.length; i < len; i++) {
        var edge = edges[i]

        if (edge instanceof Query && queries.indexOf(edge) < 0) {
          queries.push(edge)
        } else if (edge instanceof Node) {
          var edgeLast = last == null ? last : last[edge.key]
          var edgeNext = next == null ? next : next[edge.key]

          this.scan(edge, edgeLast, edgeNext, queries)
        }
      }
    }

    return queries
  }
}

export default CompareTree
<|endoftext|>