<commit_before>/**
 * Created by elgs on 6/23/14.
 */

(function () {
    "use strict";

    var fs = require('fs');
    var path = require('path');
    var mkdirp = require('mkdirp');
    var rimraf = require('rimraf');
    var _ = require('lodash');

    var Yotta = function (dbPath, config) {
        config = config || {
            syncInterval: 1,
            syncBufferSize: 1000,
            maxDataBufferSize: 1000000
        };
        this.dbPath = path.resolve(dbPath);
        this.dataFile = dbPath + '/data';
        this.indexFile = dbPath + '/index';
        this.lockFile = dbPath + '/.lock';
        this.closed = true;
        this.syncInterval = config.syncInterval || 1;
        this.syncBufferSize = config.syncBufferSize || 1000;
        this.maxDataBufferSize = config.maxDataBufferSize || 1000000;
        mkdirp.sync(this.dbPath);
    };

    Yotta.prototype.open = function () {
        if (fs.existsSync(this.lockFile)) {
            throw new Error('Database is locked.');
        }
        if (this.closed === false) {
            throw new Error('Database already opened.');
        }
        this.dataBuffer = {};
        this.indexBuffer = {};
        this.keySyncBuffer = [];
        fs.openSync(this.lockFile, 'w');
        var fd = fs.openSync(this.indexFile, 'a');
        var indexString = fs.readFileSync(this.indexFile, 'utf8');
        fs.close(fd);
        fd = fs.openSync(this.dataFile, 'a');
        fs.close(fd);
        indexString.split('\n').map(function (val) {
            if (val && val.trim()) {
                var tokens = val.split(',');
                var tokenLength = tokens.length;
                var key = tokens.slice(0, tokenLength - 2).join(',');
                var start = tokens[tokenLength - 2] >> 0;
                var length = tokens[tokenLength - 1] >> 0;
                if (start >= 0 && length >= 0) {
                    this.indexBuffer[key] = {
                        start: start,
                        length: length
                    };
                } else {
                    delete this.dataBuffer[key];
                    delete this.indexBuffer[key];
                }
            }
        }, this);
        var self = this;
        this.syncId = setInterval(function () {
            //console.log('sync...');
            self._syncFull();
        }, this.syncInterval * 1000);
        this.closed = false;
    };

    Yotta.prototype.close = function () {
        clearInterval(this.syncId);
        this._syncFull();
        this._rebuildIndex(true);
        fs.unlinkSync(this.lockFile);
        this.dataBuffer = null;
        this.indexBuffer = null;
        this.keySyncBuffer = null;
        this.closed = true;
    };

    Yotta.prototype._put = function (key, value) {
        var self = this;
        self.dataBuffer[key] = value;
        self.keySyncBuffer.push(key);
        if (self.keySyncBuffer.length >= self.syncBufferSize) {
            self._syncFull();
        }
    };

    Yotta.prototype.put = function (key, value, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._put(key, value);
                cb(null);
            });
        } else {
            // sync
            self._put(key, value);
        }
//        precise mode
//        var index = this._syncData(key, value);
//        this.indexBuffer[key] = index;
//        this._syncIndex(key, index);
    };

    Yotta.prototype._get = function (key) {
        var self = this;
        var value = self.dataBuffer[key];
        if (value === undefined) {
            var index = self.indexBuffer[key];
            if (index && index.start >= 0 && index.length >= 0) {
                var length = index.length;
                var buffer = new Buffer(length);
                var fd = fs.openSync(this.dataFile, 'r');
                fs.readSync(fd, buffer, 0, length, index.start);
                fs.closeSync(fd);
                value = buffer.toString();
                self.dataBuffer[key] = value;
            } else {
                value = undefined;
            }
        }
        return value;
    };

    Yotta.prototype.get = function (key, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                var ret = self._get(key);
                cb(null, ret);
            });
        } else {
            // sync
            return self._get(key);
        }
    };

    Yotta.prototype._remove = function (key) {
        var self = this;
        delete self.dataBuffer[key];
        delete self.indexBuffer[key];
        var i = self.keySyncBuffer.indexOf(key);
        if (i > -1) {
            self.keySyncBuffer.splice(i, 1);
        }
        fs.appendFileSync(self.indexFile, key + ',-1,-1\n');
    };

    Yotta.prototype.remove = function (key, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._remove(key);
                cb(null);
            });
        } else {
            // sync
            self._remove(key);
        }
    };

    //test(key, index, array of keys)
    Yotta.prototype.findKeys = function (test, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                var retIndex = Object.keys(self.indexBuffer).filter(test);
                var retData = Object.keys(self.dataBuffer).filter(test);
                cb(null, _.unique(retIndex.concat(retData)));
            });
        } else {
            // sync
            var retIndex = Object.keys(self.indexBuffer).filter(test);
            var retData = Object.keys(self.dataBuffer).filter(test);
            return _.unique(retIndex.concat(retData));
        }
    };

    //test(key, index, array of keys)
    Yotta.prototype.find = function (test, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self.findKeys(test, function (err, keys) {
                    var ret = {};
                    keys.forEach(function (key) {
                        ret[key] = self.get(key);
                    });
                    cb(null, ret);
                });
            });
        } else {
            // sync
            var keys = self.findKeys(test);
            var ret = {};
            keys.forEach(function (key) {
                ret[key] = self.get(key);
            });
            return ret;
        }
    };

    Yotta.prototype._vacuum = function () {
        var self = this;
        self._syncFull();
        var tmpVacuumDb = new Yotta(self.dbPath + '/.tmpVacuumDb');
        tmpVacuumDb.open();
        for (var key in self.indexBuffer) {
            var value = self.get(key);
            tmpVacuumDb.put(key, value);
        }
        tmpVacuumDb.close();
        fs.writeFileSync(self.dbPath + '/data', fs.readFileSync(self.dbPath + '/.tmpVacuumDb/data'));
        fs.writeFileSync(self.dbPath + '/index', fs.readFileSync(self.dbPath + '/.tmpVacuumDb/index'));
        rimraf.sync(self.dbPath + '/.tmpVacuumDb');
        self._rebuildIndex(false);
    };

    Yotta.prototype.vacuum = function (cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._vacuum();
                cb(null);
            });
        } else {
            // sync
            self._vacuum();
        }
    };

    Yotta.prototype._syncFull = function () {
        if (!this.keySyncBuffer || this.keySyncBuffer.length === 0) {
            return;
        }
        var fd = fs.openSync(this.dataFile, 'a');
        var size = fs.statSync(this.dataFile).size;
        var self = this;
        var newData = this.keySyncBuffer.reduce(function (previousValue, currentValue) {
            var valueBuffer = new Buffer(self.dataBuffer[currentValue]);
            self.indexBuffer[currentValue] = {
                start: size,
                length: valueBuffer.length
            };
            size += valueBuffer.length;
            return previousValue + valueBuffer;
        }, '');
        fs.appendFileSync(this.dataFile, newData);

        var newIndex = this.keySyncBuffer.reduce(function (previousValue, currentValue) {
            var index = self.indexBuffer[currentValue];
            return previousValue + currentValue + ',' + index.start + ',' + index.length + '\n';
        }, '');
        fs.appendFileSync(this.indexFile, newIndex);
        fs.close(fd);
        this.keySyncBuffer = [];
    };

    Yotta.prototype._syncData = function (key, value) {
        var valueBuffer = new Buffer(value);
        var size = fs.statSync(this.dataFile).size;
        fs.appendFileSync(this.dataFile, valueBuffer);
        return {
            start: size,
            length: valueBuffer.length
        };
    };

    Yotta.prototype._syncIndex = function (key, index) {
        fs.appendFileSync(this.indexFile, key + ',' + index.start + ',' + index.length + '\n');
    };

    Yotta.prototype._rebuildIndex = function (writeBack) {
        var indexString = fs.readFileSync(this.indexFile, 'utf8');
        this.indexBuffer = {};
        var tmpIndexBufferString = '';
        indexString.split('\n').map(function (val) {
            if (val && val.trim()) {
                var tokens = val.split(',');
                var tokenLength = tokens.length;
                var key = tokens.slice(0, tokenLength - 2).join(',');
                var start = tokens[tokenLength - 2] >> 0;
                var length = tokens[tokenLength - 1] >> 0;
                if (start >= 0 && length >= 0) {
                    this.indexBuffer[key] = val + '\n';
                } else {
                    delete this.indexBuffer[key];
                }
            }
        }, this);
        if (writeBack) {
            for (var i in this.indexBuffer) {
                tmpIndexBufferString += this.indexBuffer[i];
            }
            fs.writeFileSync(this.indexFile, tmpIndexBufferString);
        }
    };

    exports.Yotta = Yotta;
})();<commit_msg>Correctly close the files<commit_after>/**
 * Created by elgs on 6/23/14.
 */

(function () {
    "use strict";

    var fs = require('fs');
    var path = require('path');
    var mkdirp = require('mkdirp');
    var rimraf = require('rimraf');
    var _ = require('lodash');

    var Yotta = function (dbPath, config) {
        config = config || {
            syncInterval: 1,
            syncBufferSize: 1000,
            maxDataBufferSize: 1000000
        };
        this.dbPath = path.resolve(dbPath);
        this.dataFile = dbPath + '/data';
        this.indexFile = dbPath + '/index';
        this.lockFile = dbPath + '/.lock';
        this.closed = true;
        this.syncInterval = config.syncInterval || 1;
        this.syncBufferSize = config.syncBufferSize || 1000;
        this.maxDataBufferSize = config.maxDataBufferSize || 1000000;
        mkdirp.sync(this.dbPath);
    };

    Yotta.prototype.open = function () {
        if (fs.existsSync(this.lockFile)) {
            throw new Error('Database is locked.');
        }
        if (this.closed === false) {
            throw new Error('Database already opened.');
        }
        this.dataBuffer = {};
        this.indexBuffer = {};
        this.keySyncBuffer = [];
        fs.openSync(this.lockFile, 'w');
        var fd = fs.openSync(this.indexFile, 'a');
        var indexString = fs.readFileSync(this.indexFile, 'utf8');
        fs.closeSync(fd);
        fd = fs.openSync(this.dataFile, 'a');
        fs.closeSync(fd);
        indexString.split('\n').map(function (val) {
            if (val && val.trim()) {
                var tokens = val.split(',');
                var tokenLength = tokens.length;
                var key = tokens.slice(0, tokenLength - 2).join(',');
                var start = tokens[tokenLength - 2] >> 0;
                var length = tokens[tokenLength - 1] >> 0;
                if (start >= 0 && length >= 0) {
                    this.indexBuffer[key] = {
                        start: start,
                        length: length
                    };
                } else {
                    delete this.dataBuffer[key];
                    delete this.indexBuffer[key];
                }
            }
        }, this);
        var self = this;
        this.syncId = setInterval(function () {
            //console.log('sync...');
            self._syncFull();
        }, this.syncInterval * 1000);
        this.closed = false;
    };

    Yotta.prototype.close = function () {
        clearInterval(this.syncId);
        this._syncFull();
        this._rebuildIndex(true);
        fs.unlinkSync(this.lockFile);
        this.dataBuffer = null;
        this.indexBuffer = null;
        this.keySyncBuffer = null;
        this.closed = true;
    };

    Yotta.prototype._put = function (key, value) {
        var self = this;
        self.dataBuffer[key] = value;
        self.keySyncBuffer.push(key);
        if (self.keySyncBuffer.length >= self.syncBufferSize) {
            self._syncFull();
        }
    };

    Yotta.prototype.put = function (key, value, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._put(key, value);
                cb(null);
            });
        } else {
            // sync
            self._put(key, value);
        }
//        precise mode
//        var index = this._syncData(key, value);
//        this.indexBuffer[key] = index;
//        this._syncIndex(key, index);
    };

    Yotta.prototype._get = function (key) {
        var self = this;
        var value = self.dataBuffer[key];
        if (value === undefined) {
            var index = self.indexBuffer[key];
            if (index && index.start >= 0 && index.length >= 0) {
                var length = index.length;
                var buffer = new Buffer(length);
                var fd = fs.openSync(this.dataFile, 'r');
                fs.readSync(fd, buffer, 0, length, index.start);
                fs.closeSync(fd);
                value = buffer.toString();
                self.dataBuffer[key] = value;
            } else {
                value = undefined;
            }
        }
        return value;
    };

    Yotta.prototype.get = function (key, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                var ret = self._get(key);
                cb(null, ret);
            });
        } else {
            // sync
            return self._get(key);
        }
    };

    Yotta.prototype._remove = function (key) {
        var self = this;
        delete self.dataBuffer[key];
        delete self.indexBuffer[key];
        var i = self.keySyncBuffer.indexOf(key);
        if (i > -1) {
            self.keySyncBuffer.splice(i, 1);
        }
        fs.appendFileSync(self.indexFile, key + ',-1,-1\n');
    };

    Yotta.prototype.remove = function (key, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._remove(key);
                cb(null);
            });
        } else {
            // sync
            self._remove(key);
        }
    };

    //test(key, index, array of keys)
    Yotta.prototype.findKeys = function (test, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                var retIndex = Object.keys(self.indexBuffer).filter(test);
                var retData = Object.keys(self.dataBuffer).filter(test);
                cb(null, _.unique(retIndex.concat(retData)));
            });
        } else {
            // sync
            var retIndex = Object.keys(self.indexBuffer).filter(test);
            var retData = Object.keys(self.dataBuffer).filter(test);
            return _.unique(retIndex.concat(retData));
        }
    };

    //test(key, index, array of keys)
    Yotta.prototype.find = function (test, cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self.findKeys(test, function (err, keys) {
                    var ret = {};
                    keys.forEach(function (key) {
                        ret[key] = self.get(key);
                    });
                    cb(null, ret);
                });
            });
        } else {
            // sync
            var keys = self.findKeys(test);
            var ret = {};
            keys.forEach(function (key) {
                ret[key] = self.get(key);
            });
            return ret;
        }
    };

    Yotta.prototype._vacuum = function () {
        var self = this;
        self._syncFull();
        var tmpVacuumDb = new Yotta(self.dbPath + '/.tmpVacuumDb');
        tmpVacuumDb.open();
        for (var key in self.indexBuffer) {
            var value = self.get(key);
            tmpVacuumDb.put(key, value);
        }
        tmpVacuumDb.close();
        fs.writeFileSync(self.dbPath + '/data', fs.readFileSync(self.dbPath + '/.tmpVacuumDb/data'));
        fs.writeFileSync(self.dbPath + '/index', fs.readFileSync(self.dbPath + '/.tmpVacuumDb/index'));
        rimraf.sync(self.dbPath + '/.tmpVacuumDb');
        self._rebuildIndex(false);
    };

    Yotta.prototype.vacuum = function (cb) {
        var self = this;
        if (typeof cb === 'function') {
            // async
            setImmediate(function () {
                self._vacuum();
                cb(null);
            });
        } else {
            // sync
            self._vacuum();
        }
    };

    Yotta.prototype._syncFull = function () {
        if (!this.keySyncBuffer || this.keySyncBuffer.length === 0) {
            return;
        }
        var fd = fs.openSync(this.dataFile, 'a');
        var size = fs.statSync(this.dataFile).size;
        var self = this;
        var newData = this.keySyncBuffer.reduce(function (previousValue, currentValue) {
            var valueBuffer = new Buffer(self.dataBuffer[currentValue]);
            self.indexBuffer[currentValue] = {
                start: size,
                length: valueBuffer.length
            };
            size += valueBuffer.length;
            return previousValue + valueBuffer;
        }, '');
        fs.appendFileSync(this.dataFile, newData);

        var newIndex = this.keySyncBuffer.reduce(function (previousValue, currentValue) {
            var index = self.indexBuffer[currentValue];
            return previousValue + currentValue + ',' + index.start + ',' + index.length + '\n';
        }, '');
        fs.appendFileSync(this.indexFile, newIndex);
        fs.fsyncSync(fd);
        fs.closeSync(fd);
        this.keySyncBuffer = [];
    };

    Yotta.prototype._syncData = function (key, value) {
        var valueBuffer = new Buffer(value);
        var size = fs.statSync(this.dataFile).size;
        fs.appendFileSync(this.dataFile, valueBuffer);
        return {
            start: size,
            length: valueBuffer.length
        };
    };

    Yotta.prototype._syncIndex = function (key, index) {
        fs.appendFileSync(this.indexFile, key + ',' + index.start + ',' + index.length + '\n');
    };

    Yotta.prototype._rebuildIndex = function (writeBack) {
        var indexString = fs.readFileSync(this.indexFile, 'utf8');
        this.indexBuffer = {};
        var tmpIndexBufferString = '';
        indexString.split('\n').map(function (val) {
            if (val && val.trim()) {
                var tokens = val.split(',');
                var tokenLength = tokens.length;
                var key = tokens.slice(0, tokenLength - 2).join(',');
                var start = tokens[tokenLength - 2] >> 0;
                var length = tokens[tokenLength - 1] >> 0;
                if (start >= 0 && length >= 0) {
                    this.indexBuffer[key] = val + '\n';
                } else {
                    delete this.indexBuffer[key];
                }
            }
        }, this);
        if (writeBack) {
            for (var i in this.indexBuffer) {
                tmpIndexBufferString += this.indexBuffer[i];
            }
            fs.writeFileSync(this.indexFile, tmpIndexBufferString);
        }
    };

    exports.Yotta = Yotta;
})();<|endoftext|>


<commit_before>var logger = (require('../logging/vip-winston')).Logger;
var authService = require("../authentication/services");
var config = require('../config');
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var dataTestingMessageContent = require('./data-testing/content');
var feedProcessingMessageContent = require('./feed-processing/content');
var pg = require('pg');
var slack = require('./slack');

var transporter = nodemailer.createTransport(sesTransport({
  accessKeyId: config.aws.accessKey,
  secretAccessKey: config.aws.secretKey,
  rateLimit: config.email.rateLimit,
  region: config.aws.region
}));

var dataCentralizationOnly = function(user) {
  return user.app_metadata.roles &&
         user.app_metadata.roles.length == 1 &&
         user.app_metadata.roles[0] == 'data-centralization';
};

var messageOptions = {
  // data-testing message options
  testingComplete: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has completed",
      html: dataTestingMessageContent.testingComplete(message)
    }
  },
  errorDuringTesting: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has failed",
      html: dataTestingMessageContent.errorDuringTesting(message)
    }
  },

  // feed-processing message options
  approveFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: "A Feed has been Approved",
        html: feedProcessingMessageContent.approveFeed(message, recipient, fipsCode)
      }
    }
  },
  processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.processedFeed(message, recipient, fipsCode)
      };
    }
  },
  v5processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)){
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.v5processedFeed(message, recipient, fipsCode)
      };
    }
  },
  errorDuringProcessing: function(message, recipient) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Something Went Wrong with a Feed',
        text: feedProcessingMessageContent.errorDuringProcessing(message)
      };
    }
  }
};

var sendMessage = function(messageContent) {
  transporter.sendMail(messageContent, function(error, info) {
    if (error) {
      logger.info('Sending error: ' + error);
      logger.info('Message: ' + JSON.stringify(messageContent));
    }
  });
};

var badFips = function(fips) {
  if ((typeof fips != "string") ||
       (fips.length < 2) ||
       (fips.length > 5)) {
    return true;
  } else {
    return false;
  }
}

var sendEmail = function(message, fips, contentFn) {
  authService.getUsersByFips(fips, function (users) {
    for (var i = 0; i < users.length; i++) {
      var recipient = users[i];
      var messageContent = contentFn(message, recipient, fips);

      if (messageContent != null) {
        sendMessage(messageContent);
        logger.info("Sending a message to: " + messageContent.to + " with this subject: " + messageContent.subject);
      }
    };
  });
};

var loadFeed = function(publicId, callback) {
  var vip_id_query = "SELECT vip_id, spec_version, election_date, extract(epoch from (end_time - start_time)) as duration \
                      FROM results \
                      WHERE public_id = $1";

  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if (err) return logger.error('Could not connect to PostgreSQL. Error fetching client from pool: ', err);

    client.query(vip_id_query, [publicId], function(err, result) {
      done();

      if (err || result.rows.length == 0) {
        logger.error('No feed found or connection issue.');
        slack.message("ERROR: No feed found matching public_id in database (public_id follows):\n" + publicId);
      } else {
        callback(result);
      }
    });
  });
}

module.exports = {
  sendFeedProcessingSuccessNotifications: function(message, messageType) {
    loadFeed(message.publicId, (result) => {
      var fips = result.rows[0]['vip_id'];
      var spec_version = new String(result.rows[0]['spec_version']);
      if (badFips(fips)) {
        slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
      } else {
        var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
        var electionDate = result.rows[0]['election_date'] || "Not available";
        var duration = result.rows[0]['duration'] || "Not available";

        var reportSuffix = "/xml/errors/report";
        if (spec_version == "3.0") {
          reportSuffix = "/errors/report";
        }

        var checksum = "";
        if (message.checksum) {
          checksum = "\nChecksum: " + message.checksum
        }

        slack.message("SUCCESS: Feed processed for FIPS " + fips +
                      "\nState Name " + stateName +
                      "\nElection Date " + electionDate +
                      "\nVIP Spec Version " + spec_version +
                      checksum +
                      "\nFull Error Report (right-click, Save As) https://" +
                        process.env.BASE_URI + "/db/feeds/" + message.publicId + reportSuffix +
                      "\nProcessing-Time (sec) " + duration);
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }}});
  },
  sendFeedProcessingFailureNotifications: function(message, messageType) {
    var publicId = message.publicId;

    if (!publicId) {
      logger.error('No Public ID listed.');
      slack.message("ERROR: Feed processed but no public id found (message follows):\n" +
                    JSON.stringify(message));
    } else {
      loadFeed(publicId, (result) => {
        var fips = result.rows[0]['vip_id'];
        var spec_version = new String(result.rows[0]['spec_version']);
        if (badFips(fips)) {
          slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
        } else {
          var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
          var electionDate = result.rows[0]['election_date'] || "Not available";
          slack.message("EXCEPTION: Feed processed with errors for FIPS " + fips +
                        "\nState Name " + stateName +
                        "\nElection Date " + electionDate +
                        "\nVIP Spec Version " + spec_version +
                        "\nProcessed Message " + JSON.stringify(message));
        }
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }
      });
    }
  },
  sendAddressTestSuccessNotifications: function(message) {
    var fipsCode = message.fipsCode;
    var slackMessage = "";
    if(fipsCode == "undefined") {
      slackMessage += "SUCCESS: Batch Address file processed for an admin, available at: " + message["url"];
    } else {
      slackMessage += "SUCCESS: Batch Address file processed for fips " + fipsCode + ", available at: " + message["url"];
    }
    if(message.stats) {
      slackMessage += "\nMatch Breakdown:";
      var statuses = ["Match", "Possible Mismatch", "Mismatch", "No Result"];
      statuses.map( (key) => { if (message.stats.hasOwnProperty(key)) {
                                 slackMessage += "\n" + key + ": " + message.stats[key];
                               } else {
                                 slackMessage += "\n" + key + ": 0";
                               } });
    }
    slack.message(slackMessage);
    sendEmail(message, fipsCode, messageOptions['testingComplete']);
  },
  sendAddressTestFailureNotifications: function(message) {
    var fipsCode = message.fipsCode;
    if (fipsCode === undefined) {
      logger.warning("No fips code in batch-address.file.complete message.  Can't send batch address testing finished email notification.");
      slack.message("ERROR: Batch Address sent a message we don't understand: " +
                    JSON.stringify(message));
    } else if (fipsCode === "undefined") {
      slack.message("ERROR: Admin submitted Batch Address file FAILED (message follows):\n" +
                    JSON.stringify(message));
    } else if (badFips(fipsCode)) {
      slack.message("ERROR: Batch Address file processed but fips was bad, no emails sent (message follows)\n" +
                    JSON.stringify(message));
    } else {
      slack.message("ERROR: Batch Address file failed (message follows)\n" +
                    JSON.stringify(message));
      sendEmail(message, fipsCode, messageOptions['errorDuringTesting']);
    }
  }
};
<commit_msg>remove save as instructions<commit_after>var logger = (require('../logging/vip-winston')).Logger;
var authService = require("../authentication/services");
var config = require('../config');
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var dataTestingMessageContent = require('./data-testing/content');
var feedProcessingMessageContent = require('./feed-processing/content');
var pg = require('pg');
var slack = require('./slack');

var transporter = nodemailer.createTransport(sesTransport({
  accessKeyId: config.aws.accessKey,
  secretAccessKey: config.aws.secretKey,
  rateLimit: config.email.rateLimit,
  region: config.aws.region
}));

var dataCentralizationOnly = function(user) {
  return user.app_metadata.roles &&
         user.app_metadata.roles.length == 1 &&
         user.app_metadata.roles[0] == 'data-centralization';
};

var messageOptions = {
  // data-testing message options
  testingComplete: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has completed",
      html: dataTestingMessageContent.testingComplete(message)
    }
  },
  errorDuringTesting: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has failed",
      html: dataTestingMessageContent.errorDuringTesting(message)
    }
  },

  // feed-processing message options
  approveFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: "A Feed has been Approved",
        html: feedProcessingMessageContent.approveFeed(message, recipient, fipsCode)
      }
    }
  },
  processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.processedFeed(message, recipient, fipsCode)
      };
    }
  },
  v5processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)){
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.v5processedFeed(message, recipient, fipsCode)
      };
    }
  },
  errorDuringProcessing: function(message, recipient) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Something Went Wrong with a Feed',
        text: feedProcessingMessageContent.errorDuringProcessing(message)
      };
    }
  }
};

var sendMessage = function(messageContent) {
  transporter.sendMail(messageContent, function(error, info) {
    if (error) {
      logger.info('Sending error: ' + error);
      logger.info('Message: ' + JSON.stringify(messageContent));
    }
  });
};

var badFips = function(fips) {
  if ((typeof fips != "string") ||
       (fips.length < 2) ||
       (fips.length > 5)) {
    return true;
  } else {
    return false;
  }
}

var sendEmail = function(message, fips, contentFn) {
  authService.getUsersByFips(fips, function (users) {
    for (var i = 0; i < users.length; i++) {
      var recipient = users[i];
      var messageContent = contentFn(message, recipient, fips);

      if (messageContent != null) {
        sendMessage(messageContent);
        logger.info("Sending a message to: " + messageContent.to + " with this subject: " + messageContent.subject);
      }
    };
  });
};

var loadFeed = function(publicId, callback) {
  var vip_id_query = "SELECT vip_id, spec_version, election_date, extract(epoch from (end_time - start_time)) as duration \
                      FROM results \
                      WHERE public_id = $1";

  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if (err) return logger.error('Could not connect to PostgreSQL. Error fetching client from pool: ', err);

    client.query(vip_id_query, [publicId], function(err, result) {
      done();

      if (err || result.rows.length == 0) {
        logger.error('No feed found or connection issue.');
        slack.message("ERROR: No feed found matching public_id in database (public_id follows):\n" + publicId);
      } else {
        callback(result);
      }
    });
  });
}

module.exports = {
  sendFeedProcessingSuccessNotifications: function(message, messageType) {
    loadFeed(message.publicId, (result) => {
      var fips = result.rows[0]['vip_id'];
      var spec_version = new String(result.rows[0]['spec_version']);
      if (badFips(fips)) {
        slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
      } else {
        var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
        var electionDate = result.rows[0]['election_date'] || "Not available";
        var duration = result.rows[0]['duration'] || "Not available";

        var reportSuffix = "/xml/errors/report";
        if (spec_version == "3.0") {
          reportSuffix = "/errors/report";
        }

        var checksum = "";
        if (message.checksum) {
          checksum = "\nChecksum: " + message.checksum
        }

        slack.message("SUCCESS: Feed processed for FIPS " + fips +
                      "\nState Name " + stateName +
                      "\nElection Date " + electionDate +
                      "\nVIP Spec Version " + spec_version +
                      checksum +
                      "\nFull Error Report https://" +
                        process.env.BASE_URI + "/db/feeds/" + message.publicId + reportSuffix +
                      "\nProcessing-Time (sec) " + duration);
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }}});
  },
  sendFeedProcessingFailureNotifications: function(message, messageType) {
    var publicId = message.publicId;

    if (!publicId) {
      logger.error('No Public ID listed.');
      slack.message("ERROR: Feed processed but no public id found (message follows):\n" +
                    JSON.stringify(message));
    } else {
      loadFeed(publicId, (result) => {
        var fips = result.rows[0]['vip_id'];
        var spec_version = new String(result.rows[0]['spec_version']);
        if (badFips(fips)) {
          slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
        } else {
          var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
          var electionDate = result.rows[0]['election_date'] || "Not available";
          slack.message("EXCEPTION: Feed processed with errors for FIPS " + fips +
                        "\nState Name " + stateName +
                        "\nElection Date " + electionDate +
                        "\nVIP Spec Version " + spec_version +
                        "\nProcessed Message " + JSON.stringify(message));
        }
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }
      });
    }
  },
  sendAddressTestSuccessNotifications: function(message) {
    var fipsCode = message.fipsCode;
    var slackMessage = "";
    if(fipsCode == "undefined") {
      slackMessage += "SUCCESS: Batch Address file processed for an admin, available at: " + message["url"];
    } else {
      slackMessage += "SUCCESS: Batch Address file processed for fips " + fipsCode + ", available at: " + message["url"];
    }
    if(message.stats) {
      slackMessage += "\nMatch Breakdown:";
      var statuses = ["Match", "Possible Mismatch", "Mismatch", "No Result"];
      statuses.map( (key) => { if (message.stats.hasOwnProperty(key)) {
                                 slackMessage += "\n" + key + ": " + message.stats[key];
                               } else {
                                 slackMessage += "\n" + key + ": 0";
                               } });
    }
    slack.message(slackMessage);
    sendEmail(message, fipsCode, messageOptions['testingComplete']);
  },
  sendAddressTestFailureNotifications: function(message) {
    var fipsCode = message.fipsCode;
    if (fipsCode === undefined) {
      logger.warning("No fips code in batch-address.file.complete message.  Can't send batch address testing finished email notification.");
      slack.message("ERROR: Batch Address sent a message we don't understand: " +
                    JSON.stringify(message));
    } else if (fipsCode === "undefined") {
      slack.message("ERROR: Admin submitted Batch Address file FAILED (message follows):\n" +
                    JSON.stringify(message));
    } else if (badFips(fipsCode)) {
      slack.message("ERROR: Batch Address file processed but fips was bad, no emails sent (message follows)\n" +
                    JSON.stringify(message));
    } else {
      slack.message("ERROR: Batch Address file failed (message follows)\n" +
                    JSON.stringify(message));
      sendEmail(message, fipsCode, messageOptions['errorDuringTesting']);
    }
  }
};
<|endoftext|>


<commit_before>/* global Module */

/* Magic Mirror
 * Module: Calendar
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("calendar", {

	// Define module defaults
	defaults: {
		maximumEntries: 10, // Total Maximum Entries
		maximumNumberOfDays: 365,
		displaySymbol: true,
		defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
		displayRepeatingCountTitle: false,
		defaultRepeatingCountTitle: "",
		maxTitleLength: 25,
		fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
		animationSpeed: 2000,
		fade: true,
		urgency: 7,
		timeFormat: "relative",
		dateFormat: "MMM Do",
		getRelative: 6,
		fadePoint: 0.25, // Start on 1/4th of the list.
		hidePrivate: false,
		calendars: [
			{
				symbol: "calendar",
				url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
			},
		],
		titleReplace: {
			"De verjaardag van ": "",
			"'s birthday": ""
		},
		broadcastEvents: true
	},

	// Define required scripts.
	getStyles: function () {
		return ["calendar.css", "font-awesome.css"];
	},

	// Define required scripts.
	getScripts: function () {
		return ["moment.js"];
	},

	// Define required translations.
	getTranslations: function () {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build your own module including translations, check out the documentation.
		return false;
	},

	// Override start method.
	start: function () {
		Log.log("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			calendar.url = calendar.url.replace("webcal://", "http://");
			this.addCalendar(calendar.url, calendar.user, calendar.pass);
		}

		this.calendarData = {};
		this.loaded = false;
	},

	// Override socket notification handler.
	socketNotificationReceived: function (notification, payload) {
		if (notification === "CALENDAR_EVENTS") {
			if (this.hasCalendarURL(payload.url)) {
				this.calendarData[payload.url] = payload.events;
				this.loaded = true;

				if (this.config.broadcastEvents) {
					this.broadcastEvents();
				}
			}
		} else if (notification === "FETCH_ERROR") {
			Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
		} else if (notification === "INCORRECT_URL") {
			Log.error("Calendar Error. Incorrect url: " + payload.url);
		} else {
			Log.log("Calendar received an unknown socket notification: " + notification);
		}

		this.updateDom(this.config.animationSpeed);
	},

	// Override dom generator.
	getDom: function () {

		var events = this.createEventList();
		var wrapper = document.createElement("table");
		wrapper.className = "small";

		if (events.length === 0) {
			wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
			wrapper.className = "small dimmed";
			return wrapper;
		}

		for (var e in events) {
			var event = events[e];

			var eventWrapper = document.createElement("tr");
			eventWrapper.className = "normal";

			if (this.config.displaySymbol) {
				var symbolWrapper = document.createElement("td");
				symbolWrapper.className = "symbol";
				var symbol = document.createElement("span");
				symbol.className = "fa fa-" + this.symbolForUrl(event.url);
				symbolWrapper.appendChild(symbol);
				eventWrapper.appendChild(symbolWrapper);
			}

			var titleWrapper = document.createElement("td"),
				repeatingCountTitle = "";

			if (this.config.displayRepeatingCountTitle) {

				repeatingCountTitle = this.countTitleForUrl(event.url);

				if (repeatingCountTitle !== "") {
					var thisYear = new Date(parseInt(event.startDate)).getFullYear(),
						yearDiff = thisYear - event.firstYear;

					repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle;
				}
			}

			titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
			titleWrapper.className = "title bright";
			eventWrapper.appendChild(titleWrapper);

			var timeWrapper = document.createElement("td");
			//console.log(event.today);
			var now = new Date();
			// Define second, minute, hour, and day variables
			var oneSecond = 1000; // 1,000 milliseconds
			var oneMinute = oneSecond * 60;
			var oneHour = oneMinute * 60;
			var oneDay = oneHour * 24;
			if (event.fullDayEvent) {
				if (event.today) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
				} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
				} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
					if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
						timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
					} else {
						timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				} else {
					/* Check to see if the user displays absolute or relative dates with their events
					 * Also check to see if an event is happening within an 'urgency' time frameElement
					 * For example, if the user set an .urgency of 7 days, those events that fall within that
					 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
					 *
					 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
					 */
					if (this.config.timeFormat === "absolute") {
						if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
							// This event falls within the config.urgency period that the user has set
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
						}
					} else {
						timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				}
			} else {
				if (event.startDate >= new Date()) {
					if (event.startDate - now < 2 * oneDay) {
						// This event is within the next 48 hours (2 days)
						if (event.startDate - now < this.config.getRelative * oneHour) {
							// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							// Otherwise just say 'Today/Tomorrow at such-n-such time'
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
						}
					} else {
						/* Check to see if the user displays absolute or relative dates with their events
						 * Also check to see if an event is happening within an 'urgency' time frameElement
						 * For example, if the user set an .urgency of 7 days, those events that fall within that
						 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
						 *
						 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
						 */
						if (this.config.timeFormat === "absolute") {
							if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
								// This event falls within the config.urgency period that the user has set
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
							} else {
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
							}
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						}
					}
				} else {
					timeWrapper.innerHTML = this.capFirst(this.translate("RUNNING")) + " " + moment(event.endDate, "x").fromNow(true);
				}
			}
			//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
			//console.log(event);
			timeWrapper.className = "time light";
			eventWrapper.appendChild(timeWrapper);

			wrapper.appendChild(eventWrapper);

			// Create fade effect.
			if (this.config.fade && this.config.fadePoint < 1) {
				if (this.config.fadePoint < 0) {
					this.config.fadePoint = 0;
				}
				var startingPoint = events.length * this.config.fadePoint;
				var steps = events.length - startingPoint;
				if (e >= startingPoint) {
					var currentStep = e - startingPoint;
					eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
				}
			}
		}

		return wrapper;
	},

	/* hasCalendarURL(url)
	 * Check if this config contains the calendar url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return bool - Has calendar url
	 */
	hasCalendarURL: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url) {
				return true;
			}
		}

		return false;
	},

	/* createEventList()
	 * Creates the sorted list of all events.
	 *
	 * return array - Array with events.
	 */
	createEventList: function () {
		var events = [];
		var today = moment().startOf("day");
		for (var c in this.calendarData) {
			var calendar = this.calendarData[c];
			for (var e in calendar) {
				var event = calendar[e];
				if(this.config.hidePrivate) {
					if(event.class === "PRIVATE") {
					      // do not add the current event, skip it
					      continue;
					}
				}
				event.url = c;
				event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
				events.push(event);
			}
		}

		events.sort(function (a, b) {
			return a.startDate - b.startDate;
		});

		return events.slice(0, this.config.maximumEntries);
	},

	/* createEventList(url)
	 * Requests node helper to add calendar url.
	 *
	 * argument url sting - Url to add.
	 */
	addCalendar: function (url, user, pass) {
		this.sendSocketNotification("ADD_CALENDAR", {
			url: url,
			maximumEntries: this.config.maximumEntries,
			maximumNumberOfDays: this.config.maximumNumberOfDays,
			fetchInterval: this.config.fetchInterval,
			user: user,
			pass: pass
		});
	},

	/* symbolForUrl(url)
	 * Retrieves the symbol for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	symbolForUrl: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.symbol === "string") {
				return calendar.symbol;
			}
		}

		return this.config.defaultSymbol;
	},
	/* countTitleForUrl(url)
	 * Retrieves the name for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	countTitleForUrl: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
				return calendar.repeatingCountTitle;
			}
		}

		return this.config.defaultRepeatingCountTitle;
	},

	/* shorten(string, maxLength)
	 * Shortens a sting if it's longer than maxLenthg.
	 * Adds an ellipsis to the end.
	 *
	 * argument string string - The string to shorten.
	 * argument maxLength number - The max lenth of the string.
	 *
	 * return string - The shortened string.
	 */
	shorten: function (string, maxLength) {
		if (string.length > maxLength) {
			return string.slice(0, maxLength) + "…";
		}

		return string;
	},

	/* capFirst(string)
	 * Capitalize the first letter of a string
	 * Eeturn capitalized string
	 */

	capFirst: function (string) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	},

	/* titleTransform(title)
	 * Transforms the title of an event for usage.
	 * Replaces parts of the text as defined in config.titleReplace.
	 * Shortens title based on config.maxTitleLength
	 *
	 * argument title string - The title to transform.
	 *
	 * return string - The transformed title.
	 */
	titleTransform: function (title) {
		for (var needle in this.config.titleReplace) {
			var replacement = this.config.titleReplace[needle];
			title = title.replace(needle, replacement);
		}

		title = this.shorten(title, this.config.maxTitleLength);
		return title;
	},

	/* broadcastEvents()
	 * Broadcasts the events to all other modules for reuse.
	 * The all events available in one array, sorted on startdate.
	 */
	broadcastEvents: function () {
		var eventList = [];
		for (url in this.calendarData) {
			var calendar = this.calendarData[url];
			for (e in calendar) {
				var event = cloneObject(calendar[e]);
				delete event.url;
				eventList.push(event);
			}
		}

		eventList.sort(function(a,b) {
			return a.startDate - b.startDate;
		});

		this.sendNotification("CALENDAR_EVENTS", eventList);

	}
});
<commit_msg>added possibility to give each calendar another color<commit_after>/* global Module */

/* Magic Mirror
 * Module: Calendar
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("calendar", {

	// Define module defaults
	defaults: {
		maximumEntries: 10, // Total Maximum Entries
		maximumNumberOfDays: 365,
		displaySymbol: true,
		defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
		displayRepeatingCountTitle: false,
		defaultRepeatingCountTitle: "",
		maxTitleLength: 25,
		fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
		animationSpeed: 2000,
		fade: true,
		urgency: 7,
		timeFormat: "relative",
		dateFormat: "MMM Do",
		getRelative: 6,
		fadePoint: 0.25, // Start on 1/4th of the list.
		hidePrivate: false,
        colored: false,
		calendars: [
			{
				symbol: "calendar",
				url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
			},
		],
		titleReplace: {
			"De verjaardag van ": "",
			"'s birthday": ""
		},
		broadcastEvents: true
	},

	// Define required scripts.
	getStyles: function () {
		return ["calendar.css", "font-awesome.css"];
	},

	// Define required scripts.
	getScripts: function () {
		return ["moment.js"];
	},

	// Define required translations.
	getTranslations: function () {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build your own module including translations, check out the documentation.
		return false;
	},

	// Override start method.
	start: function () {
		Log.log("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			calendar.url = calendar.url.replace("webcal://", "http://");
			this.addCalendar(calendar.url, calendar.user, calendar.pass);
		}

		this.calendarData = {};
		this.loaded = false;
	},

	// Override socket notification handler.
	socketNotificationReceived: function (notification, payload) {
		if (notification === "CALENDAR_EVENTS") {
			if (this.hasCalendarURL(payload.url)) {
				this.calendarData[payload.url] = payload.events;
				this.loaded = true;

				if (this.config.broadcastEvents) {
					this.broadcastEvents();
				}
			}
		} else if (notification === "FETCH_ERROR") {
			Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
		} else if (notification === "INCORRECT_URL") {
			Log.error("Calendar Error. Incorrect url: " + payload.url);
		} else {
			Log.log("Calendar received an unknown socket notification: " + notification);
		}

		this.updateDom(this.config.animationSpeed);
	},

	// Override dom generator.
	getDom: function () {

		var events = this.createEventList();
		var wrapper = document.createElement("table");
		wrapper.className = "small";

		if (events.length === 0) {
			wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
			wrapper.className = "small dimmed";
			return wrapper;
		}

		for (var e in events) {
			var event = events[e];

			var eventWrapper = document.createElement("tr");

            if (this.config.colored) {
                eventWrapper.style.cssText = 'color:' + this.colorForUrl(event.url);
            }

			eventWrapper.className = "normal";

			if (this.config.displaySymbol) {
				var symbolWrapper = document.createElement("td");
				symbolWrapper.className = "symbol";
				var symbol = document.createElement("span");
				symbol.className = "fa fa-" + this.symbolForUrl(event.url);
				symbolWrapper.appendChild(symbol);
				eventWrapper.appendChild(symbolWrapper);
			}

			var titleWrapper = document.createElement("td"),
				repeatingCountTitle = "";

			if (this.config.displayRepeatingCountTitle) {

				repeatingCountTitle = this.countTitleForUrl(event.url);

				if (repeatingCountTitle !== "") {
					var thisYear = new Date(parseInt(event.startDate)).getFullYear(),
						yearDiff = thisYear - event.firstYear;

					repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle;
				}
			}

			titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;

            if (!this.config.colored) {
                titleWrapper.className = "title bright";
            } else {
                titleWrapper.className = "title";
            }

			eventWrapper.appendChild(titleWrapper);

			var timeWrapper = document.createElement("td");
			//console.log(event.today);
			var now = new Date();
			// Define second, minute, hour, and day variables
			var oneSecond = 1000; // 1,000 milliseconds
			var oneMinute = oneSecond * 60;
			var oneHour = oneMinute * 60;
			var oneDay = oneHour * 24;
			if (event.fullDayEvent) {
				if (event.today) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
				} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
				} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
					if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
						timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
					} else {
						timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				} else {
					/* Check to see if the user displays absolute or relative dates with their events
					 * Also check to see if an event is happening within an 'urgency' time frameElement
					 * For example, if the user set an .urgency of 7 days, those events that fall within that
					 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
					 *
					 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
					 */
					if (this.config.timeFormat === "absolute") {
						if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
							// This event falls within the config.urgency period that the user has set
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
						}
					} else {
						timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				}
			} else {
				if (event.startDate >= new Date()) {
					if (event.startDate - now < 2 * oneDay) {
						// This event is within the next 48 hours (2 days)
						if (event.startDate - now < this.config.getRelative * oneHour) {
							// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							// Otherwise just say 'Today/Tomorrow at such-n-such time'
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
						}
					} else {
						/* Check to see if the user displays absolute or relative dates with their events
						 * Also check to see if an event is happening within an 'urgency' time frameElement
						 * For example, if the user set an .urgency of 7 days, those events that fall within that
						 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
						 *
						 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
						 */
						if (this.config.timeFormat === "absolute") {
							if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
								// This event falls within the config.urgency period that the user has set
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
							} else {
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
							}
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						}
					}
				} else {
					timeWrapper.innerHTML = this.capFirst(this.translate("RUNNING")) + " " + moment(event.endDate, "x").fromNow(true);
				}
			}
			//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
			//console.log(event);
			timeWrapper.className = "time light";
			eventWrapper.appendChild(timeWrapper);

			wrapper.appendChild(eventWrapper);

			// Create fade effect.
			if (this.config.fade && this.config.fadePoint < 1) {
				if (this.config.fadePoint < 0) {
					this.config.fadePoint = 0;
				}
				var startingPoint = events.length * this.config.fadePoint;
				var steps = events.length - startingPoint;
				if (e >= startingPoint) {
					var currentStep = e - startingPoint;
					eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
				}
			}
		}

		return wrapper;
	},

	/* hasCalendarURL(url)
	 * Check if this config contains the calendar url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return bool - Has calendar url
	 */
	hasCalendarURL: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url) {
				return true;
			}
		}

		return false;
	},

	/* createEventList()
	 * Creates the sorted list of all events.
	 *
	 * return array - Array with events.
	 */
	createEventList: function () {
		var events = [];
		var today = moment().startOf("day");
		for (var c in this.calendarData) {
			var calendar = this.calendarData[c];
			for (var e in calendar) {
				var event = calendar[e];
				if(this.config.hidePrivate) {
					if(event.class === "PRIVATE") {
					      // do not add the current event, skip it
					      continue;
					}
				}
				event.url = c;
				event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
				events.push(event);
			}
		}

		events.sort(function (a, b) {
			return a.startDate - b.startDate;
		});

		return events.slice(0, this.config.maximumEntries);
	},

	/* createEventList(url)
	 * Requests node helper to add calendar url.
	 *
	 * argument url sting - Url to add.
	 */
	addCalendar: function (url, user, pass) {
		this.sendSocketNotification("ADD_CALENDAR", {
			url: url,
			maximumEntries: this.config.maximumEntries,
			maximumNumberOfDays: this.config.maximumNumberOfDays,
			fetchInterval: this.config.fetchInterval,
			user: user,
			pass: pass
		});
	},

	/* symbolForUrl(url)
	 * Retrieves the symbol for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	symbolForUrl: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.symbol === "string") {
				return calendar.symbol;
			}
		}

		return this.config.defaultSymbol;
	},

    /* colorForUrl(url)
	 * Retrieves the color for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Color
	 */
	colorForUrl: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.color === "string") {
				return calendar.color;
			}
		}

		return '#fff';
	},
	/* countTitleForUrl(url)
	 * Retrieves the name for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	countTitleForUrl: function (url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
				return calendar.repeatingCountTitle;
			}
		}

		return this.config.defaultRepeatingCountTitle;
	},

	/* shorten(string, maxLength)
	 * Shortens a sting if it's longer than maxLenthg.
	 * Adds an ellipsis to the end.
	 *
	 * argument string string - The string to shorten.
	 * argument maxLength number - The max lenth of the string.
	 *
	 * return string - The shortened string.
	 */
	shorten: function (string, maxLength) {
		if (string.length > maxLength) {
			return string.slice(0, maxLength) + "…";
		}

		return string;
	},

	/* capFirst(string)
	 * Capitalize the first letter of a string
	 * Eeturn capitalized string
	 */

	capFirst: function (string) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	},

	/* titleTransform(title)
	 * Transforms the title of an event for usage.
	 * Replaces parts of the text as defined in config.titleReplace.
	 * Shortens title based on config.maxTitleLength
	 *
	 * argument title string - The title to transform.
	 *
	 * return string - The transformed title.
	 */
	titleTransform: function (title) {
		for (var needle in this.config.titleReplace) {
			var replacement = this.config.titleReplace[needle];
			title = title.replace(needle, replacement);
		}

		title = this.shorten(title, this.config.maxTitleLength);
		return title;
	},

	/* broadcastEvents()
	 * Broadcasts the events to all other modules for reuse.
	 * The all events available in one array, sorted on startdate.
	 */
	broadcastEvents: function () {
		var eventList = [];
		for (url in this.calendarData) {
			var calendar = this.calendarData[url];
			for (e in calendar) {
				var event = cloneObject(calendar[e]);
				delete event.url;
				eventList.push(event);
			}
		}

		eventList.sort(function(a,b) {
			return a.startDate - b.startDate;
		});

		this.sendNotification("CALENDAR_EVENTS", eventList);

	}
});
<|endoftext|>


<commit_before>// 'use strict';

var c = require('../lib/channels.js');
var date = require('../lib/date.js');

var events = [];

events.push({
  when: date('W6D6', 09, 55),
  who: c.juniors,
  message: '<!channel> 5 Minutes until Summary Assessment Kickoff. https://zoom.us/j/685772608'
});

events.push({
  when: date('W6D6', 09, 59),
  who: c.juniors,
  message: '<!channel> 1 Minute until Summary Assessment Kickoff. https://zoom.us/j/685772608'
});

events.push({
  when: date('W6D6', 15, 00),
  who: c.juniors,
  message: '<!channel> 2 Hours Remaining.'
});

events.push({
  when: date('W6D6', 17, 00),
  who: c.juniors,
  message: '<!channel> 30 Minutes Left! Start wrapping up and getting the PRs submitted.'
});

events.push({
  when: date('W6D6', 17, 15),
  who: c.juniors,
  message: '<!channel> 15 Minutes Left! Get your pull requests in!'
});

events.push({
  when: date('W6D6', 17, 30),
  who: c.juniors,
  message: '<!channel> You made it! The summary assessment is over. If you haven\'t submitted a PR yet, ping HD now and get \'er done!'
});

module.exports = events;
<commit_msg>broken<commit_after>// 'use strict';

var c = require('../lib/channels.js');
var date = require('../lib/date.js');

var events = [];

events.push({
  when: date('W6D6', 08, 55),
  who: c.juniors,
  message: '<!channel> 5 Minutes until Summary Assessment Kickoff. https://zoom.us/j/685772608'
});

events.push({
  when: date('W6D6', 08, 59),
  who: c.juniors,
  message: '<!channel> 1 Minute until Summary Assessment Kickoff. https://zoom.us/j/685772608'
});

events.push({
  when: date('W6D6', 15, 00),
  who: c.juniors,
  message: '<!channel> 2 Hours Remaining.'
});

events.push({
  when: date('W6D6', 17, 00),
  who: c.juniors,
  message: '<!channel> 30 Minutes Left! Start wrapping up and getting the PRs submitted.'
});

events.push({
  when: date('W6D6', 17, 15),
  who: c.juniors,
  message: '<!channel> 15 Minutes Left! Get your pull requests in!'
});

events.push({
  when: date('W6D6', 17, 30),
  who: c.juniors,
  message: '<!channel> You made it! The summary assessment is over. If you haven\'t submitted a PR yet, ping HD now and get \'er done!'
});

module.exports = events;
<|endoftext|>


<commit_before><commit_msg>Updated Hyper to Iosevka font.<commit_after><|endoftext|>


<commit_before><commit_msg>docs: use the jsdoc-fresh theme (#47)<commit_after><|endoftext|>


<commit_before>'use strict';

var Backbone = require('../shim/backbone'),
    $ = require('jquery'),
    _ = require('underscore');

var AppRouter = Backbone.Marionette.AppRouter.extend({
    // Key of last route context.
    _previousRouteName: null,

    // Map of { routeName: { controller: [Object], methodName: [String] }, ... }
    _routeContext: null,

    // Promise chain to ensure everything triggers in order even if there
    // are animations in the setUp/tearDown methods.
    _sequence: null,

    initialize: function() {
        this._routeContext = {};
        this._sequence = $.Deferred().resolve().promise();
    },

    addRoute: function(route, controller, methodName) {
        var routeName = route.toString(),
            cb = controller[methodName];

        this._routeContext[routeName] = {
            controller: controller,
            methodName: methodName
        };

        this.route(route, routeName, cb);
    },

    execute: function(cb, args, routeName) {
        var context = this._routeContext[routeName],
            prepare = this.getSuffixMethod(context, 'Prepare');

        if (this._previousRouteName) {
            var prevContext = this._routeContext[this._previousRouteName],
                cleanUp = this.getSuffixMethod(prevContext, 'CleanUp');
            this._sequence = this._sequence.then(function() {
                return cleanUp.apply(null, args);
            });
        }

        var self = this;
        this._sequence = this._sequence.then(function() {
            var result = prepare.apply(null, args);
            // Assume result is a promise if an object is returned.
            if (_.isObject(result)) {
                self._previousRouteName = routeName;
                return result.then(function() {
                    cb.apply(null, args);
                });
            } else if (result !== false) {
                // Only execute the route callback if prepare
                // did not return false.
                self._previousRouteName = routeName;
                cb.apply(null, args);
            } else {
                // Don't execute the route function
                // and cancel the route transition
                return false;
            }
        });
    },

    getSuffixMethod: function(context, suffix) {
        var methodName = context.methodName + suffix,
            cb = context.controller[methodName];
        return cb ? cb : _.identity;
    }
});

var router = new AppRouter();

module.exports = {
    router: router,
    AppRouter: AppRouter
};
<commit_msg>Simplify router<commit_after>'use strict';

var Backbone = require('../shim/backbone'),
    _ = require('underscore');

var AppRouter = Backbone.Marionette.AppRouter.extend({
    // Key of last route context.
    _previousRouteName: null,

    // Map of { routeName: { controller: [Object], methodName: [String] }, ... }
    _routeContext: null,

    initialize: function() {
        this._routeContext = {};
    },

    addRoute: function(route, controller, methodName) {
        var routeName = route.toString(),
            cb = controller[methodName];

        this._routeContext[routeName] = {
            controller: controller,
            methodName: methodName
        };

        this.route(route, routeName, cb);
    },

    execute: function(cb, args, routeName) {
        var context = this._routeContext[routeName],
            prepare = this.getSuffixMethod(context, 'Prepare');

        if (this._previousRouteName) {
            var prevContext = this._routeContext[this._previousRouteName],
                cleanUp = this.getSuffixMethod(prevContext, 'CleanUp');
            cleanUp.apply(null, args);
        }

        prepare.apply(null, args);
        cb.apply(null, args);

        this._previousRouteName = routeName;
    },

    getSuffixMethod: function(context, suffix) {
        var methodName = context.methodName + suffix,
            cb = context.controller[methodName];
        return cb ? cb : _.identity;
    }
});

var router = new AppRouter();

module.exports = {
    router: router,
    AppRouter: AppRouter
};
<|endoftext|>


<commit_before>const path = require('path');
const { AkairoClient } = require('discord-akairo');
const { Collection } = require('discord.js');

const Database = require('./Database');
const SettingsProvider = require('./SettingsProviders');
const Setting = require('../models/settings');

class HoshiClient extends AkairoClient {
	constructor(config) {
		super({
			aliasReplacement: /-/g,
			prefix: message => this.settings.get(message.guild, 'prefix', '*'),
			allowMention: true,
			handleEdits: true,
			commandUtilLifetime: 3e5,
			ownerID: config.owner,
			commandDirectory: path.join(__dirname, '..', 'commands'),
			inhibitorDirectory: path.join(__dirname, '..', 'inhibitors'),
			listenerDirectory: path.join(__dirname, '..', 'listeners'),
			defaultPrompt: {
				modifyStart: (text, msg) => text && `${msg.author} **::** ${text}\nType \`cancel\` to cancel this command.`,
				modifyRetry: (text, msg) => text && `${msg.author} **::** ${text}\nType \`cancel\` to cancel this command.`,
				timeout: msg => `${msg.author} **::** Time ran out, command has been cancelled.`,
				ended: msg => `${msg.author} **::** Too many retries, command has been cancelled.`,
				cancel: msg => `${msg.author} **::** Command has been cancelled.`,
				retries: 4,
				time: 30000
			}
		}, {
			disableEveryone: true,
			disabledEvents: ['TYPING_START']
		});

		this.config = config;
		this.settings = new SettingsProvider(Setting);
		this.starboards = new Collection();
	}

	async start() {
		await Database.authenticate();
		await this.settings.init();
		return this.login(this.config.token);
	}
}

module.exports = HoshiClient;
<commit_msg>Fetch uncached members<commit_after>const path = require('path');
const { AkairoClient } = require('discord-akairo');
const { Collection } = require('discord.js');

const Database = require('./Database');
const SettingsProvider = require('./SettingsProviders');
const Setting = require('../models/settings');

class HoshiClient extends AkairoClient {
	constructor(config) {
		super({
			aliasReplacement: /-/g,
			prefix: message => this.settings.get(message.guild, 'prefix', '*'),
			allowMention: true,
			handleEdits: true,
			fetchMembers: true,
			commandUtilLifetime: 3e5,
			ownerID: config.owner,
			commandDirectory: path.join(__dirname, '..', 'commands'),
			inhibitorDirectory: path.join(__dirname, '..', 'inhibitors'),
			listenerDirectory: path.join(__dirname, '..', 'listeners'),
			defaultPrompt: {
				modifyStart: (text, msg) => text && `${msg.author} **::** ${text}\nType \`cancel\` to cancel this command.`,
				modifyRetry: (text, msg) => text && `${msg.author} **::** ${text}\nType \`cancel\` to cancel this command.`,
				timeout: msg => `${msg.author} **::** Time ran out, command has been cancelled.`,
				ended: msg => `${msg.author} **::** Too many retries, command has been cancelled.`,
				cancel: msg => `${msg.author} **::** Command has been cancelled.`,
				retries: 4,
				time: 30000
			}
		}, {
			disableEveryone: true,
			disabledEvents: ['TYPING_START']
		});

		this.config = config;
		this.settings = new SettingsProvider(Setting);
		this.starboards = new Collection();
	}

	async start() {
		await Database.authenticate();
		await this.settings.init();
		return this.login(this.config.token);
	}
}

module.exports = HoshiClient;
<|endoftext|>


<commit_before>/** @jsx React.DOM */

"use strict";

const _ = require("lodash");
const path = require("path");
const React = require('react-atom-fork');
const floop = require("../common/floop");
const fs = require("fs");
const $ = require('atom-space-pen-views').$;
const utils = require("../utils");

module.exports = React.createClass({
  treeize_: function (obj) {
    var node, tree;
    node = tree = {};
    _.each(obj, function (p) {
      node = tree;
      p.split(path.sep).forEach(function (p) {
        if (p in node) {
          node = node[p];
          return;
        }
        node[p] = {};
        node = node[p];
      });
    });
    return tree;
  },
  getInitialState: function () {
    return {
      enabled: true,
      clicked: "",
    };
  },
  componentDidMount: function () {
    const local = this.refs.local;
    if (!local) {
      return;
    }
    $(local.getDOMNode()).focus();
  },
  onClick: function (id) {
    console.log(id);
  },
  render_: function (name, items) {
    return (

{name}

          {
            _.map(items, function(b, id) {
              var path = b.path;
              return (
{path}
);
            }, this)
          }

    );
  },
  remote_: function () {
    this.setState({enabled: false});
    _.each(this.props.different, function (b, id) {
      let encoding = b.encoding || "utf8";
      floop.send_set_buf({
        id: id,
        buf: b.txt.toString(encoding),
        md5: b.md5,
        encoding: b,
      });
    });
    // ST3 behavior
    // self.send({'name': 'saved', 'id': existing_buf['id']})

    _.each(this.props.missing, function (b, id) {
      floop.send_delete_buf({id: id});
    });

    _.each(this.props.newFiles, function (b, rel) {
      fs.readFile(b.path, function (err, data) {
        if (err) {
          console.log(err);
          return;
        }
        var encoding = utils.is_binary(data, data.length) ? "base64" : "utf8";

        floop.send_create_buf({
          path: rel,
          buf: data.toString(encoding),
          encoding: encoding,
          md5: utils.md5(data),
        });
      });
    });
    this.props.onHandledConflicts();
  },
  local_: function () {
    const fetch = _.merge(this.props.missing, this.props.different);
    this.setState({enabled: false});
    _.each(fetch, function (b, id) {
      floop.send_get_buf(id);
    });
    this.props.onHandledConflicts();
  },
  cancel_: function () {
    this.setState({enabled: false});
    require("../floobits").leave_workspace();
  },
  render_created_workspace: function() {
    const newFiles = this.render_("Uploading: ", this.props.newFiles);

    return (

You just created {fl.floourl ? fl.floourl.toString() : "the workspace"}.

      { newFiles }

)
  },
  render_conflicts: function () {
    const missing = this.render_("Missing", this.props.missing);
    const different = this.render_("Different", this.props.different);
    const newFiles = this.render_("New", this.props.newFiles);
    const ignored = _.map(this.props.ignored, function (p) {
      return
{p}
;
    });
    const tooBig = _.map(this.props.tooBig, function (size, p) {
      return
{p}: {size}
;
    });

    return (

Your local files are different from the workspace.

      <button disabled={!this.state.enabled} onClick={this.remote_}>Overwrite Remote Files</button>
      <button ref="local" disabled={!this.state.enabled} onClick={this.local_}>Overwrite Local Files</button>
      <button disabled={!this.state.enabled} onClick={this.cancel_}>Cancel</button>

      {missing}
      {different}
      {newFiles}
      {!this.props.ignored.length ? "" :

Ignored

            { ignored }

      }
      {!tooBig.length ? "" :

Too Big

            { tooBig }

      }

)
  },
  render: function() {
    const body = this.props.justUpload ? this.render_created_workspace() : this.render_conflicts();
    return (
      // overlay is an atom styling hack (used for modals)

        {body}

    );
  }
});
<commit_msg>now uploads files when creating workspace<commit_after>/** @jsx React.DOM */

"use strict";

const _ = require("lodash");
const path = require("path");
const React = require('react-atom-fork');
const floop = require("../common/floop");
const fs = require("fs");
const $ = require('atom-space-pen-views').$;
const utils = require("../utils");

module.exports = React.createClass({
  treeize_: function (obj) {
    var node, tree;
    node = tree = {};
    _.each(obj, function (p) {
      node = tree;
      p.split(path.sep).forEach(function (p) {
        if (p in node) {
          node = node[p];
          return;
        }
        node[p] = {};
        node = node[p];
      });
    });
    return tree;
  },
  getInitialState: function () {
    return {
      enabled: true,
      clicked: "",
    };
  },
  componentDidMount: function () {
    if (this.props.justUpload) {
      const upload = this.remote_;
      setTimeout(function () {
        upload();
      }, 0);
    }

    const local = this.refs.local;
    if (!local) {
      return;
    }
    $(local.getDOMNode()).focus();
  },
  onClick: function (id) {
    console.log(id);
  },
  render_: function (name, items) {
    return (

{name}

          {
            _.map(items, function(b, id) {
              var path = b.path;
              return (
{path}
);
            }, this)
          }

    );
  },
  remote_: function () {
    this.setState({enabled: false});
    _.each(this.props.different, function (b, id) {
      let encoding = b.encoding || "utf8";
      floop.send_set_buf({
        id: id,
        buf: b.txt.toString(encoding),
        md5: b.md5,
        encoding: b,
      });
    });
    // ST3 behavior
    // self.send({'name': 'saved', 'id': existing_buf['id']})

    _.each(this.props.missing, function (b, id) {
      floop.send_delete_buf({id: id});
    });

    _.each(this.props.newFiles, function (b, rel) {
      fs.readFile(b.path, function (err, data) {
        if (err) {
          console.log(err);
          return;
        }
        var encoding = utils.is_binary(data, data.length) ? "base64" : "utf8";

        floop.send_create_buf({
          path: rel,
          buf: data.toString(encoding),
          encoding: encoding,
          md5: utils.md5(data),
        });
      });
    });
    this.props.onHandledConflicts();
  },
  local_: function () {
    const fetch = _.merge(this.props.missing, this.props.different);
    this.setState({enabled: false});
    _.each(fetch, function (b, id) {
      floop.send_get_buf(id);
    });
    this.props.onHandledConflicts();
  },
  cancel_: function () {
    this.setState({enabled: false});
    require("../floobits").leave_workspace();
  },
  render_created_workspace: function() {
    const newFiles = this.render_("Uploading: ", this.props.newFiles);

    return (

You just created {fl.floourl ? fl.floourl.toString() : "the workspace"}.

      { newFiles }

);
  },
  render_conflicts: function () {
    const missing = this.render_("Missing", this.props.missing);
    const different = this.render_("Different", this.props.different);
    const newFiles = this.render_("New", this.props.newFiles);
    const ignored = _.map(this.props.ignored, function (p) {
      return
{p}
;
    });
    const tooBig = _.map(this.props.tooBig, function (size, p) {
      return
{p}: {size}
;
    });

    return (

Your local files are different from the workspace.

      <button disabled={!this.state.enabled} onClick={this.remote_}>Overwrite Remote Files</button>
      <button ref="local" disabled={!this.state.enabled} onClick={this.local_}>Overwrite Local Files</button>
      <button disabled={!this.state.enabled} onClick={this.cancel_}>Cancel</button>

      {missing}
      {different}
      {newFiles}
      {!this.props.ignored.length ? "" :

Ignored

            { ignored }

      }
      {!tooBig.length ? "" :

Too Big

            { tooBig }

      }

)
  },
  render: function() {
    const body = this.props.justUpload ? this.render_created_workspace() : this.render_conflicts();
    return (
      // overlay is an atom styling hack (used for modals)

        {body}

    );
  }
});
<|endoftext|>


<commit_before>const {Subject} = require('rxjs')
const {deserializeError} = require('serialize-error')
const _ = require('lodash')
const PooledWorker = require('./pooled')
// const log = require('sepalLog')('job')

// const {submit$} = require('./single')
// const {submit$} = PooledWorker({
//     concurrency: 100,
//     defaultMinIdleCount: 10,
//     defaultMaxIdleMilliseconds: 5000
// })

// NOTE: ctx is three-state:
//
// - <undefined> when called from worker
// - <object> when called from main thread
// - <null> wren called from main thread as a dependency

const isDependency = ctx => ctx === null
const isWorker = ctx => ctx === undefined

const depArgs = (deps = []) =>
    deps.map((m => m(null)))

const depWorker$ = (deps = []) =>
    deps.map((m => m()))

const worker = ({jobName, before, worker$}) =>
    _.flatten([...depWorker$(before), {jobName, worker$}])

const evaluateArgs = (argFuncs, ctx) =>
    _.chain(argFuncs)
        .flattenDeep()
        .map(argFunc => argFunc(ctx))
        .value()

const workerGroups = {}

const getWorkerGroup = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds}) => {
    if (!workerGroups[jobName]) {
        workerGroups[jobName] = PooledWorker({
            jobName,
            jobPath,
            maxConcurrency,
            minIdleCount,
            maxIdleMilliseconds
        })
    }
    return workerGroups[jobName]
}

const unwrap$ = wrapped$ => {
    const unwrapped$ = new Subject()
    wrapped$.subscribe({
        next: ({value, error}) => {
            value && unwrapped$.next(value)
            error && unwrapped$.error(deserializeError(error))
        },
        error: error => unwrapped$.error(error),
        complete: () => unwrapped$.complete()
    })
    return unwrapped$
}

const main = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before, args, ctx}) => {
    const argFuncs = [...depArgs(before), args]
    return isDependency(ctx)
        ? argFuncs
        : unwrap$(
            getWorkerGroup({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds}).submit$({
                jobName,
                jobPath,
                args: evaluateArgs(argFuncs, ctx),
                args$: ctx.args$
            })
        )
}

const assert = (arg, func, msg, required = false) => {
    const valid = required
        ? !_.isNil(arg) && func(arg)
        : _.isNil(arg) || func(arg)
    if (!valid) {
        throw new Error([
            msg,
            required ? '(required)' : ''
        ].join(' '))
    }
}

const job = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before = [], worker$, args = () => []}) => {
    assert(jobName, _.isString, 'jobName must be a string', true)
    assert(worker$, _.isFunction, 'worker$ must be a function', true)
    assert(args, _.isFunction, 'args must be a function', true)
    assert(before, _.isArray, 'before must be an array', false)
    assert(minIdleCount, _.isNumber, 'minIdleCount must be a number', false)
    assert(maxIdleMilliseconds, _.isNumber, 'maxIdleMilliseconds must be a number', false)

    return ctx => isWorker(ctx)
        ? worker({jobName, before, worker$})
        : main({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before, args, ctx})
}

module.exports = job
<commit_msg>Used ReplaySubject for unwrapping to prevent lost values.<commit_after>const {ReplaySubject} = require('rxjs')
const {deserializeError} = require('serialize-error')
const _ = require('lodash')
const PooledWorker = require('./pooled')
// const log = require('sepalLog')('job')

// const {submit$} = require('./single')
// const {submit$} = PooledWorker({
//     concurrency: 100,
//     defaultMinIdleCount: 10,
//     defaultMaxIdleMilliseconds: 5000
// })

// NOTE: ctx is three-state:
//
// - <undefined> when called from worker
// - <object> when called from main thread
// - <null> wren called from main thread as a dependency

const isDependency = ctx => ctx === null
const isWorker = ctx => ctx === undefined

const depArgs = (deps = []) =>
    deps.map((m => m(null)))

const depWorker$ = (deps = []) =>
    deps.map((m => m()))

const worker = ({jobName, before, worker$}) =>
    _.flatten([...depWorker$(before), {jobName, worker$}])

const evaluateArgs = (argFuncs, ctx) =>
    _.chain(argFuncs)
        .flattenDeep()
        .map(argFunc => argFunc(ctx))
        .value()

const workerGroups = {}

const getWorkerGroup = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds}) => {
    if (!workerGroups[jobName]) {
        workerGroups[jobName] = PooledWorker({
            jobName,
            jobPath,
            maxConcurrency,
            minIdleCount,
            maxIdleMilliseconds
        })
    }
    return workerGroups[jobName]
}

const unwrap$ = wrapped$ => {
    const unwrapped$ = new ReplaySubject()
    wrapped$.subscribe({
        next: ({value, error}) => {
            value && unwrapped$.next(value)
            error && unwrapped$.error(deserializeError(error))
        },
        error: error => unwrapped$.error(error),
        complete: () => unwrapped$.complete()
    })
    return unwrapped$
}

const main = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before, args, ctx}) => {
    const argFuncs = [...depArgs(before), args]
    return isDependency(ctx)
        ? argFuncs
        : unwrap$(
            getWorkerGroup({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds}).submit$({
                jobName,
                jobPath,
                args: evaluateArgs(argFuncs, ctx),
                args$: ctx.args$
            })
        )
}

const assert = (arg, func, msg, required = false) => {
    const valid = required
        ? !_.isNil(arg) && func(arg)
        : _.isNil(arg) || func(arg)
    if (!valid) {
        throw new Error([
            msg,
            required ? '(required)' : ''
        ].join(' '))
    }
}

const job = ({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before = [], worker$, args = () => []}) => {
    assert(jobName, _.isString, 'jobName must be a string', true)
    assert(worker$, _.isFunction, 'worker$ must be a function', true)
    assert(args, _.isFunction, 'args must be a function', true)
    assert(before, _.isArray, 'before must be an array', false)
    assert(minIdleCount, _.isNumber, 'minIdleCount must be a number', false)
    assert(maxIdleMilliseconds, _.isNumber, 'maxIdleMilliseconds must be a number', false)

    return ctx => isWorker(ctx)
        ? worker({jobName, before, worker$})
        : main({jobName, jobPath, maxConcurrency, minIdleCount, maxIdleMilliseconds, before, args, ctx})
}

module.exports = job
<|endoftext|>


<commit_before>//deps dot_style.js

function CurveStyle(attrs) {
  DotStyle.call(this, attrs);
}

CurveStyle.prototype = Object.create(DotStyle.prototype);

CurveStyle.prototype.createChunkView = function(chunk, dataSource) {
  return new CurveChunkView(this._dotAttrs, this.copyAttributes(), chunk, dataSource);
};

exports.CurveStyle = CurveStyle;
<commit_msg>copy attributes for CurveChunkView<commit_after>//deps dot_style.js

function CurveStyle(attrs) {
  DotStyle.call(this, attrs);
}

CurveStyle.prototype = Object.create(DotStyle.prototype);

CurveStyle.prototype.createChunkView = function(chunk, dataSource) {
  return new CurveChunkView(this._dotAttrs.copyAttributes(), this.copyAttributes(), chunk,
    dataSource);
};

exports.CurveStyle = CurveStyle;
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableHighlight,
} from 'react-native';

class Register extends Component{
  constructor() {
    super();

    this.state = {
      name: "",
      username: "",
      location: "",
      bio: "",
      preferred_contact: "",
      skills: "",
      seeking: "",
    }
  }

  render() {

    return (
      <View style={styles.container}>

        <Text>Register with Simul today</Text>

        <TextInput
          onChangeText={ (val)=> this.setState({name: val}) }
          style={styles.input} placeholder="Name"
          />
        <Text>
          {this.state.name}
        </Text>

      </View>
    );
  }
};

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
   fontSize: 20,
   alignSelf: 'center',
   margin: 40
  },
  input: {
    height: 50,
    marginTop: 10,
    padding: 4,
    fontSize: 18,
    borderWidth: 1,
    borderColor: '#48bbec'
  },

});

module.exports = Register;
<commit_msg>Add input component to styles in register.js<commit_after>import React, { Component } from 'react';

import {
  StyleSheet,
  Text,
  View,
  TextInput,
  TouchableHighlight,
} from 'react-native';

class Register extends Component{
  constructor() {
    super();

    this.state = {
      name: "",
      username: "",
      location: "",
      bio: "",
      preferred_contact: "",
      skills: "",
      seeking: "",
    }
  }

  render() {

    return (
      <View style={styles.container}>

        <Text>Register with Simul today</Text>

        <TextInput
          onChangeText={ (val)=> this.setState({name: val}) }
          style={styles.input} placeholder="Name"
          />
        <Text>
          {this.state.name}
        </Text>

      </View>
    );
  }
};

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
   fontSize: 20,
   alignSelf: 'center',
   margin: 40
  },
  input: {
    height: 50,
    marginTop: 10,
    padding: 4,
    fontSize: 15,
    borderWidth: 1,
    borderColor: '#48bbec'
  },
});

module.exports = Register;
<|endoftext|>


<commit_before>// ==UserScript==
// @match          http://*.imagevenue.com/img.php?*
// @match          http://www.hostingfailov.com/photo/*
// @match          http://www.freeporndumpster.com/show.php?*
// ==/UserScript==

// I don't think these sites are the same
$.register({
  rule: {
    host: /(hostingfailov|freeporndumpster|imagevenue)\.com$/,
  },
  run: function () {
    'use strict';

    var i = $('#thepic');
    $.redirect(i.src);
  },
});

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<commit_msg>hotchyx.com<commit_after>// ==UserScript==
// @match          http://*.imagevenue.com/img.php?*
// @match          http://hotchyx.com/d/adult-image-hosting-view-08.php?id=*
// @match          http://www.freeporndumpster.com/show.php?*
// @match          http://www.hostingfailov.com/photo/*
// ==/UserScript==

// I don't think these sites are the same
$.register({
  rule: {
    host: /(hostingfailov|freeporndumpster|imagevenue|hotchyx)\.com$/,
  },
  run: function () {
    'use strict';

    var i = $('#thepic');
    $.redirect(i.src);
  },
});

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<|endoftext|>


<commit_before>(function () {
  'use strict';

  function combineRegExp (res) {
    var re = res.map(function (re) {
      return re.source;
    }).join('|');
    re = new RegExp('^' + re + '$');
    return re;
  }

  // too long, split them
  var hostRule = [
    /(www\.)?adf\.(ly|acb\.im|sazlina\.com)/,
    /[jq]\.gs/,
    /go\.(phpnulledscripts|nicoblog-games)\.com/,
    /ay\.gy/,
    /(chathu|alien)\.apkmania\.co/,
    /ksn\.mx/,
    /goto\.adflytutor\.com/,
    /dl\.apkpro\.net/,
    /adf(ly\.itsrinaldo|\.tuhoctoan)\.net/,
    /.*\.gamecopyworld\.com/,
    /adv\.coder143\.com/,
    /(dl|david)\.nhachot\.info/,
    /file\.tamteo\.com/,
    /(n|u)\.shareme\.in/,
    /ddl\.animesave\.com/,
  ];
  hostRule = combineRegExp(hostRule);

  $.register({
    rule: {
      host: hostRule,
      path: /\/locked$/,
      query: /url=([^&]+)/,
    },
    start: function (m) {
      $.resetCookies();
      $.openLink('/' + m.query[1]);
    },
  });

  $.register({
    rule: {
      host: hostRule,
      // FIXME this pattern is not stable
      path: /^\/([a-z\/]{2,})?$/,
    },
  });

  $.register({
    rule: {
      host: hostRule,
    },
    ready: function () {
      $.removeNodes('iframe');

      var h = unsafeWindow.eu;
      if (!h) {
        h = $('#adfly_bar');
        unsafeWindow.close_bar();
        return;
      }
      var a = h.indexOf('!HiTommy'), b = '';
      if (a >= 0) {
        h = h.substring(0, a);
      }
      a = '';
      for (var i = 0; i < h.length; ++i) {
        if (i % 2 === 0) {
          a = a + h.charAt(i);
        } else {
          b = h.charAt(i) + b;
        }
      }
      h = atob(a + b);
      h = h.substr(2);
      if (location.hash) {
        h += location.hash;
      }
      $.openLink(h);
    },
  });

})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<commit_msg>Fix adf.ly path pattern.<commit_after>(function () {
  'use strict';

  function combineRegExp (res) {
    var re = res.map(function (re) {
      return re.source;
    }).join('|');
    re = new RegExp('^' + re + '$');
    return re;
  }

  // too long, split them
  var hostRule = [
    /(www\.)?adf\.(ly|acb\.im|sazlina\.com)/,
    /[jq]\.gs/,
    /go\.(phpnulledscripts|nicoblog-games)\.com/,
    /ay\.gy/,
    /(chathu|alien)\.apkmania\.co/,
    /ksn\.mx/,
    /goto\.adflytutor\.com/,
    /dl\.apkpro\.net/,
    /adf(ly\.itsrinaldo|\.tuhoctoan)\.net/,
    /.*\.gamecopyworld\.com/,
    /adv\.coder143\.com/,
    /(dl|david)\.nhachot\.info/,
    /file\.tamteo\.com/,
    /(n|u)\.shareme\.in/,
    /ddl\.animesave\.com/,
  ];
  hostRule = combineRegExp(hostRule);

  $.register({
    rule: {
      host: hostRule,
      path: /\/locked$/,
      query: /url=([^&]+)/,
    },
    start: function (m) {
      $.resetCookies();
      $.openLink('/' + m.query[1]);
    },
  });

  $.register({
    rule: {
      host: hostRule,
    },
    ready: function () {
      // check if this is ad page
      var h = $.$('#adfly_html'), b = $.$('#home');
      if (!h || !b || h.nodeName !== 'HTML' || b.nodeName !== 'BODY') {
        // this is not a ad page
        return;
      }

      $.removeNodes('iframe');

      h = unsafeWindow.eu;
      if (!h) {
        h = $('#adfly_bar');
        unsafeWindow.close_bar();
        return;
      }
      var a = h.indexOf('!HiTommy'), b = '';
      if (a >= 0) {
        h = h.substring(0, a);
      }
      a = '';
      for (var i = 0; i < h.length; ++i) {
        if (i % 2 === 0) {
          a = a + h.charAt(i);
        } else {
          b = h.charAt(i) + b;
        }
      }
      h = atob(a + b);
      h = h.substr(2);
      if (location.hash) {
        h += location.hash;
      }
      $.openLink(h);
    },
  });

})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<|endoftext|>