<commit_before>var _ = require('lodash');
var Q = require('q');

// TODO... what indexes should be created and should this be responsible for creating them?

function MessageQueue() {
  var self = this;

  self.errorHandler = console.error;

  self.databasePromise = null;
  self.collectionName = '_queue';

  self.pollingInterval = 1000;
  self.processingTimeout = 30 * 1000;
  self.maxWorkers = 5;

  var _workers = {};
  var _numWorkers = 0;
  var _pollingIntervalId = null;

  self.registerWorker = function(type, promise) {
    _workers[type] = promise;
    _startPolling();
  };

  self.stopPolling = function() {
    _stopPolling();
  };

  self.enqueue = function(type, message, options) {
    var queueItem = {
      dateCreated: new Date(),
      type: type,
      message: message
    };
    if (options && options.nextReceivableTime) {
      queueItem.nextReceivableTime = options.nextReceivableTime;
    }

    // The priority range is 1-10, 1 being the highest.
    queueItem.priority = _.get(options, 'priority', 1);

    return _enqueue(queueItem);
  };

  self.enqueueAndProcess = function(type, message) {
    var queueItem = {
      dateCreated: new Date(),
      type: type,
      message: message,
      receivedTime: new Date()
    };
    return _enqueue(queueItem)
      .then(function() {
        return _process(queueItem);
      });
  };

  self.remove = function(type, messageQuery) {
    var query = _buildQueueItemQuery(type, messageQuery);

    return _removeByQuery(query);
  };

  //region Private Helper Methods

  function _startPolling() {
    if (!_pollingIntervalId) {
      // Try and find work at least once every pollingInterval
      _pollingIntervalId = setInterval(_poll, self.pollingInterval);
    }
  }

  function _stopPolling() {
    if (_pollingIntervalId) {
      clearInterval(_pollingIntervalId);
    }
  }

  function _poll() {
    if (_numWorkers < self.maxWorkers) {
      _numWorkers++;
      _receive()
        .then(function(queueItem) {
          if (queueItem) {
            return _process(queueItem)
              .then(function() {
                // Look for more work to do immediately if we just processed something
                setImmediate(_poll);
              });
          }
        })
        .catch(self.errorHandler)
        .finally(function() {
          _numWorkers--;
        });
    }
  }

  function _process(queueItem) {
    var worker = _workers[queueItem.type];
    if (!worker) { return Q.reject(new Error("No worker registered for type: " + queueItem.type)); }

    return worker(queueItem)
      .then(function(status) {
        switch (status) {
          case "Completed":
            return _dequeue(queueItem);
          case "Retry":
            return _release(queueItem);
          case "Rejected":
            return _reject(queueItem);
          default:
            throw new Error("Unknown status: " + status);
        }
      });
  }

  function _enqueue(queueItem) {
    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).insertOne(queueItem);
      })
      .then(function(result) {
        return result.ops[0];
      });
  }

  function _dequeue(queueItem) {
    return _removeByQuery({_id: queueItem._id});
  }

  function _removeByQuery(query) {
    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).deleteOne(query);
      })
      .then(function(result) {
        return result.deletedCount;
      });
  }

  function _release(queueItem) {
    var update = {
      $unset: {
        receivedTime: ""
      },
      $set: {
        retryCount: queueItem.retryCount ? queueItem.retryCount + 1 : 1,
        nextReceivableTime: queueItem.nextReceivableTime ? queueItem.nextReceivableTime : new Date()
      },
      $push: {
        releaseHistory: {
          retryCount: queueItem.retryCount ? queueItem.retryCount : 0,
          receivedTime: queueItem.receivedTime,
          releasedTime: new Date(),
          releasedReason: queueItem.releasedReason
        }
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).updateOne({_id: queueItem._id}, update)
      })
      .then(function(result) {
        return result.modifiedCount;
      });
  }

  function _reject(queueItem) {
    var update = {
      $unset: {
        receivedTime: "",
        nextReceivableTime: ""
      },
      $set: {
        rejectedTime: new Date(),
        rejectionReason: queueItem.rejectionReason
      },
      $push: {
        releaseHistory: {
          retryCount: queueItem.retryCount ? queueItem.retryCount : 0,
          receivedTime: queueItem.receivedTime,
          releasedTime: new Date(),
          releasedReason: queueItem.releasedReason
        }
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).updateOne({_id: queueItem._id}, update);
      })
      .then(function(result) {
        return result.modifiedCount;
      });
  }

  function _receive() {
    var query = {
      type: {$in: _.keys(_workers)},
      rejectedTime: {$exists: false},
      $and: [
        {
          $or: [
            {nextReceivableTime: {$lt: new Date()}},
            {nextReceivableTime: {$exists: false}}
          ]
        },
        {
          $or: [
            {receivedTime: {$lt: new Date(Date.now() - self.processingTimeout)}},
            {receivedTime: {$exists: false}}
          ]
        }
      ]
    };
    var update = {
      $set: {
        receivedTime: new Date()
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).findOneAndUpdate(query, update, {returnOriginal: false, sort: 'priority'});
      })
      .then(function(result) {
        return result.value;
      });
  }

  function _buildQueueItemQuery(type, messageQuery) {
    var query = {type: type};

    _.forEach(messageQuery, function(value, key) {
      var property = 'message.' + key;
      query[property] = value;
    });

    return query;
  }

  function _buildQueueItemUpdate(messageUpdate) {
    var update = {$set: {}};

    _.forEach(messageUpdate, function(value, key) {
      var property = 'message.' + key;
      update.$set[property] = value;
    });

    return update;
  }

  //endregion
}

module.exports = MessageQueue;
<commit_msg>implement ensureIndexes method<commit_after>var _ = require('lodash');
var Q = require('q');

function MessageQueue() {
  var self = this;

  self.errorHandler = console.error;

  self.databasePromise = null;
  self.collectionName = '_queue';

  self.pollingInterval = 1000;
  self.processingTimeout = 30 * 1000;
  self.maxWorkers = 5;

  var _workers = {};
  var _numWorkers = 0;
  var _pollingIntervalId = null;

  self.registerWorker = function(type, promise) {
    _workers[type] = promise;
    _startPolling();
  };

  self.stopPolling = function() {
    _stopPolling();
  };

  self.enqueue = function(type, message, options) {
    var queueItem = {
      dateCreated: new Date(),
      type: type,
      message: message
    };
    if (options && options.nextReceivableTime) {
      queueItem.nextReceivableTime = options.nextReceivableTime;
    }

    // The priority range is 1-10, 1 being the highest.
    queueItem.priority = _.get(options, 'priority', 1);

    return _enqueue(queueItem);
  };

  self.enqueueAndProcess = function(type, message) {
    var queueItem = {
      dateCreated: new Date(),
      type: type,
      message: message,
      receivedTime: new Date()
    };
    return _enqueue(queueItem)
      .then(function() {
        return _process(queueItem);
      });
  };

  self.remove = function(type, messageQuery) {
    var query = _buildQueueItemQuery(type, messageQuery);

    return _removeByQuery(query);
  };

  self.ensureIndexes = function(keyPatterns, options) {
    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        var ensureIndexPromises = [];
        keyPatterns.forEach(function(keyPattern) {
          ensureIndexPromises.push(collection(self.collectionName).ensureIndex(keyPattern, options));
        });
        return Q.all(ensureIndexPromises);
      })
  };

  //region Private Helper Methods

  function _startPolling() {
    if (!_pollingIntervalId) {
      // Try and find work at least once every pollingInterval
      _pollingIntervalId = setInterval(_poll, self.pollingInterval);
    }
  }

  function _stopPolling() {
    if (_pollingIntervalId) {
      clearInterval(_pollingIntervalId);
    }
  }

  function _poll() {
    if (_numWorkers < self.maxWorkers) {
      _numWorkers++;
      _receive()
        .then(function(queueItem) {
          if (queueItem) {
            return _process(queueItem)
              .then(function() {
                // Look for more work to do immediately if we just processed something
                setImmediate(_poll);
              });
          }
        })
        .catch(self.errorHandler)
        .finally(function() {
          _numWorkers--;
        });
    }
  }

  function _process(queueItem) {
    var worker = _workers[queueItem.type];
    if (!worker) { return Q.reject(new Error("No worker registered for type: " + queueItem.type)); }

    return worker(queueItem)
      .then(function(status) {
        switch (status) {
          case "Completed":
            return _dequeue(queueItem);
          case "Retry":
            return _release(queueItem);
          case "Rejected":
            return _reject(queueItem);
          default:
            throw new Error("Unknown status: " + status);
        }
      });
  }

  function _enqueue(queueItem) {
    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).insertOne(queueItem);
      })
      .then(function(result) {
        return result.ops[0];
      });
  }

  function _dequeue(queueItem) {
    return _removeByQuery({_id: queueItem._id});
  }

  function _removeByQuery(query) {
    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).deleteOne(query);
      })
      .then(function(result) {
        return result.deletedCount;
      });
  }

  function _release(queueItem) {
    var update = {
      $unset: {
        receivedTime: ""
      },
      $set: {
        retryCount: queueItem.retryCount ? queueItem.retryCount + 1 : 1,
        nextReceivableTime: queueItem.nextReceivableTime ? queueItem.nextReceivableTime : new Date()
      },
      $push: {
        releaseHistory: {
          retryCount: queueItem.retryCount ? queueItem.retryCount : 0,
          receivedTime: queueItem.receivedTime,
          releasedTime: new Date(),
          releasedReason: queueItem.releasedReason
        }
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).updateOne({_id: queueItem._id}, update)
      })
      .then(function(result) {
        return result.modifiedCount;
      });
  }

  function _reject(queueItem) {
    var update = {
      $unset: {
        receivedTime: "",
        nextReceivableTime: ""
      },
      $set: {
        rejectedTime: new Date(),
        rejectionReason: queueItem.rejectionReason
      },
      $push: {
        releaseHistory: {
          retryCount: queueItem.retryCount ? queueItem.retryCount : 0,
          receivedTime: queueItem.receivedTime,
          releasedTime: new Date(),
          releasedReason: queueItem.releasedReason
        }
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).updateOne({_id: queueItem._id}, update);
      })
      .then(function(result) {
        return result.modifiedCount;
      });
  }

  function _receive() {
    var query = {
      type: {$in: _.keys(_workers)},
      rejectedTime: {$exists: false},
      $and: [
        {
          $or: [
            {nextReceivableTime: {$lt: new Date()}},
            {nextReceivableTime: {$exists: false}}
          ]
        },
        {
          $or: [
            {receivedTime: {$lt: new Date(Date.now() - self.processingTimeout)}},
            {receivedTime: {$exists: false}}
          ]
        }
      ]
    };
    var update = {
      $set: {
        receivedTime: new Date()
      }
    };

    if (!self.databasePromise) { return Q.reject(new Error("No database configured")); }
    return self.databasePromise()
      .then(function(db) {
        return db.collection(self.collectionName).findOneAndUpdate(query, update, {returnOriginal: false, sort: 'priority'});
      })
      .then(function(result) {
        return result.value;
      });
  }

  function _buildQueueItemQuery(type, messageQuery) {
    var query = {type: type};

    _.forEach(messageQuery, function(value, key) {
      var property = 'message.' + key;
      query[property] = value;
    });

    return query;
  }

  function _buildQueueItemUpdate(messageUpdate) {
    var update = {$set: {}};

    _.forEach(messageUpdate, function(value, key) {
      var property = 'message.' + key;
      update.$set[property] = value;
    });

    return update;
  }

  //endregion
}

module.exports = MessageQueue;
<|endoftext|>


<commit_before>// Migrator
// -------
"use strict";

var fs       = require('fs');
var path     = require('path');
var _        = require('lodash');
var mkdirp   = require('mkdirp');
var Promise  = require('../promise');

// Validates that migrations are present in the appropriate directories.
function validateMigrationList(all, completed) {
  var diff = _.difference(completed, all);
  if (!_.isEmpty(diff)) {
    throw new Error(
      'The migration directory is corrupt, the following files are missing: ' + diff.join(', ')
    );
  }
}

// Ensure that we have 2 places for each of the date segments
var padDate = function(segment) {
  segment = segment.toString();
  return segment[1] ? segment : '0' + segment;
};

// Get a date object in the correct format, without requiring
// a full out library like "moment.js".
var yyyymmddhhmmss = function() {
  var d = new Date();
  return d.getFullYear().toString() +
      padDate(d.getMonth() + 1) +
      padDate(d.getDate()) +
      padDate(d.getHours()) +
      padDate(d.getMinutes()) +
      padDate(d.getSeconds());
};

// The new migration we're performing, typically called from the `knex.migrate`
// interface on the main `knex` object. Passes the `knex` instance performing
// the migration.
function Migrator(knex) {
  this.knex   = knex;
  this.config = this.setConfig(knex.client.migrationConfig);
}

// Migrators to the latest configuration.
Migrator.prototype.latest = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return this._migrationData()
    .tap(validateMigrationList)
    .bind(this)
    .spread(function(all, completed) {
      return this._runBatch(_.difference(all, completed), 'up');
    });
});

// Rollback the last "batch" of migrations that were run.
Migrator.prototype.rollback = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return this._migrationData()
    .tap(validateMigrationList)
    .bind(this)
    .then(this._getLastBatch)
    .then(function(migrations) {
      return this._runBatch(_.pluck(migrations, 'name'), 'down');
    });
});

// Retrieves and returns the current migration version
// we're on, as a promise. If there aren't any migrations run yet,
// return "none" as the value for the `currentVersion`.
Migrator.prototype.currentVersion = function(config) {
  this.config = this.setConfig(config);
  return this._listCompleted(config).then(function(completed) {
    var val = _.chain(completed).map(function(value) {
      return value.split('_')[0];
    }).max().value();
    return (val === -Infinity ? 'none' : val);
  });
};

// Creates a new migration, with a given name.
Migrator.prototype.make = function(name, config) {
  this.config = this.setConfig(config);
  if (!name) Promise.rejected(new Error('A name must be specified for the generated migration'));
  return this._ensureFolder(config)
    .bind(this)
    .then(this._generateStubTemplate)
    .then(this._writeNewMigration(name));
};

// Lists all available migration versions, as a sorted array.
Migrator.prototype._listAll = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return Promise.promisify(fs.readdir, fs)(this._absoluteConfigDir())
    .bind(this)
    .then(function(migrations) {
      return _.filter(migrations, function(value) {
        var extension = path.extname(value);
        return _.contains(['.co', '.coffee', '.iced', '.js', '.litcoffee', '.ls'], extension);
      }).sort();
    });
});

// Ensures a folder for the migrations exist, dependent on the
// migration config settings.
Migrator.prototype._ensureFolder = function() {
  var dir = this._absoluteConfigDir();
  return Promise.promisify(fs.stat, fs)(dir)
    .catch(function() {
      return Promise.promisify(mkdirp)(dir);
    });
};

// Ensures that the proper table has been created,
// dependent on the migration config settings.
Migrator.prototype._ensureTable = Promise.method(function() {
  var table = this.config.tableName;
  return this.knex.schema.hasTable(table)
    .bind(this)
    .then(function(exists) {
      if (!exists) return this._createMigrationTable(table);
    });
});

// Create the migration table, if it doesn't already exist.
Migrator.prototype._createMigrationTable = function(tableName) {
  return this.knex.schema.createTable(tableName, function(t) {
    t.increments();
    t.string('name');
    t.integer('batch');
    t.timestamp('migration_time');
  });
};

// Run a batch of current migrations, in sequence.
Migrator.prototype._runBatch = function(migrations, direction) {
  return Promise.all(_.map(migrations, this._validateMigrationStructure, this))
    .bind(this)
    .then(function(migrations) {
      return Promise.bind(this)
        .then(this._latestBatchNumber)
        .then(function(batchNo) {
          if (direction === 'up') batchNo++;
          return batchNo;
        })
        .then(function(batchNo) {
          return this._waterfallBatch(batchNo, migrations, direction);
        });
    });
};

// Validates some migrations by requiring and checking for an `up` and `down` function.
Migrator.prototype._validateMigrationStructure = function(name) {
  var migration = require(path.join(this._absoluteConfigDir(), name));
  if (!_.isFunction(migration.up) || !_.isFunction(migration.down)) {
    throw new Error('Invalid migration: ' + name + ' must have both an up and down function');
  }
  return name;
};

// Lists all migrations that have been completed for the current db, as an array.
Migrator.prototype._listCompleted = Promise.method(function() {
  var tableName = this.config.tableName;
  return this._ensureTable(tableName)
    .bind(this)
    .then(function () {
      return this.knex(tableName).orderBy('id').select('name');
    })
    .then(function(migrations) {
      return _.pluck(migrations, 'name');
    });
});

// Gets the migration list from the specified migration directory,
// as well as the list of completed migrations to check what
// should be run.
Migrator.prototype._migrationData = function() {
  return Promise.all([
    this._listAll(),
    this._listCompleted()
  ]);
};

// Generates the stub template for the current migration, returning a compiled template.
Migrator.prototype._generateStubTemplate = function() {
  var stubPath = this.config.stub || path.join(__dirname, 'stub', this.config.extension + '.stub');
  return Promise.promisify(fs.readFile, fs)(stubPath).then(function(stub) {
    return _.template(stub.toString(), null, {variable: 'd'});
  });
};

// Write a new migration to disk, using the config and generated filename,
// passing any `variables` given in the config to the template.
Migrator.prototype._writeNewMigration = function(name) {
  var config = this.config;
  var dir = this._absoluteConfigDir();
  return function(tmpl) {
    if (name[0] === '-') name = name.slice(1);
    var filename  = yyyymmddhhmmss() + '_' + name + '.' + config.extension;
    return Promise.promisify(fs.writeFile, fs)(
      path.join(dir, filename),
      tmpl(config.variables || {})
    ).return(path.join(dir, filename));
  };
};

// Get the last batch of migrations, by name, ordered by insert id
// in reverse order.
Migrator.prototype._getLastBatch = function() {
  var knex = this.knex;
  var tableName = this.config.tableName;
  return this.knex(tableName)
    .where('batch', function() {
      this.select(knex.raw('MAX(batch)')).from(tableName);
    })
    .orderBy('id', 'desc');
};

// Returns the latest batch number.
Migrator.prototype._latestBatchNumber = function() {
  return this.knex(this.config.tableName)
    .max('batch as batchNo').then(function(obj) {
      return (obj[0].batchNo || 0);
    });
};

// Runs a batch of `migrations` in a specified `direction`,
// saving the appropriate database information as the migrations are run.
Migrator.prototype._waterfallBatch = function(batchNo, migrations, direction) {
  var knex      = this.knex;
  var tableName = this.config.tableName;
  var directory = this._absoluteConfigDir();
  var current   = Promise.bind({failed: false, failedOn: 0});
  var log       = [];
  _.each(migrations, function(migration) {
    var name  = migration;
    migration = require(directory + '/' + name);

    // We're going to run each of the migrations in the current "up"
    current = current.then(function() {
      return migration[direction](knex, Promise);
    }).then(function() {
      log.push(path.join(directory, name));
      if (direction === 'up') {
        return knex(tableName).insert({
          name: name,
          batch: batchNo,
          migration_time: new Date()
        });
      }
      if (direction === 'down') {
        return knex(tableName).where({name: name}).del();
      }
    });
  });

  return current.thenReturn([batchNo, log]);
};

Migrator.prototype._absoluteConfigDir = function() {
  return path.join(process.cwd(), this.config.directory);
};

Migrator.prototype.setConfig = function(config) {
  return _.extend({
    extension: 'js',
    tableName: 'knex_migrations',
    directory: '/migrations'
  }, this.config || {}, config);
};

module.exports = Migrator;
<commit_msg>fix _absoluteConfigDir to support both absolute and relative paths<commit_after>// Migrator
// -------
"use strict";

var fs       = require('fs');
var path     = require('path');
var _        = require('lodash');
var mkdirp   = require('mkdirp');
var Promise  = require('../promise');

// Validates that migrations are present in the appropriate directories.
function validateMigrationList(all, completed) {
  var diff = _.difference(completed, all);
  if (!_.isEmpty(diff)) {
    throw new Error(
      'The migration directory is corrupt, the following files are missing: ' + diff.join(', ')
    );
  }
}

// Ensure that we have 2 places for each of the date segments
var padDate = function(segment) {
  segment = segment.toString();
  return segment[1] ? segment : '0' + segment;
};

// Get a date object in the correct format, without requiring
// a full out library like "moment.js".
var yyyymmddhhmmss = function() {
  var d = new Date();
  return d.getFullYear().toString() +
      padDate(d.getMonth() + 1) +
      padDate(d.getDate()) +
      padDate(d.getHours()) +
      padDate(d.getMinutes()) +
      padDate(d.getSeconds());
};

// The new migration we're performing, typically called from the `knex.migrate`
// interface on the main `knex` object. Passes the `knex` instance performing
// the migration.
function Migrator(knex) {
  this.knex   = knex;
  this.config = this.setConfig(knex.client.migrationConfig);
}

// Migrators to the latest configuration.
Migrator.prototype.latest = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return this._migrationData()
    .tap(validateMigrationList)
    .bind(this)
    .spread(function(all, completed) {
      return this._runBatch(_.difference(all, completed), 'up');
    });
});

// Rollback the last "batch" of migrations that were run.
Migrator.prototype.rollback = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return this._migrationData()
    .tap(validateMigrationList)
    .bind(this)
    .then(this._getLastBatch)
    .then(function(migrations) {
      return this._runBatch(_.pluck(migrations, 'name'), 'down');
    });
});

// Retrieves and returns the current migration version
// we're on, as a promise. If there aren't any migrations run yet,
// return "none" as the value for the `currentVersion`.
Migrator.prototype.currentVersion = function(config) {
  this.config = this.setConfig(config);
  return this._listCompleted(config).then(function(completed) {
    var val = _.chain(completed).map(function(value) {
      return value.split('_')[0];
    }).max().value();
    return (val === -Infinity ? 'none' : val);
  });
};

// Creates a new migration, with a given name.
Migrator.prototype.make = function(name, config) {
  this.config = this.setConfig(config);
  if (!name) Promise.rejected(new Error('A name must be specified for the generated migration'));
  return this._ensureFolder(config)
    .bind(this)
    .then(this._generateStubTemplate)
    .then(this._writeNewMigration(name));
};

// Lists all available migration versions, as a sorted array.
Migrator.prototype._listAll = Promise.method(function(config) {
  this.config = this.setConfig(config);
  return Promise.promisify(fs.readdir, fs)(this._absoluteConfigDir())
    .bind(this)
    .then(function(migrations) {
      return _.filter(migrations, function(value) {
        var extension = path.extname(value);
        return _.contains(['.co', '.coffee', '.iced', '.js', '.litcoffee', '.ls'], extension);
      }).sort();
    });
});

// Ensures a folder for the migrations exist, dependent on the
// migration config settings.
Migrator.prototype._ensureFolder = function() {
  var dir = this._absoluteConfigDir();
  return Promise.promisify(fs.stat, fs)(dir)
    .catch(function() {
      return Promise.promisify(mkdirp)(dir);
    });
};

// Ensures that the proper table has been created,
// dependent on the migration config settings.
Migrator.prototype._ensureTable = Promise.method(function() {
  var table = this.config.tableName;
  return this.knex.schema.hasTable(table)
    .bind(this)
    .then(function(exists) {
      if (!exists) return this._createMigrationTable(table);
    });
});

// Create the migration table, if it doesn't already exist.
Migrator.prototype._createMigrationTable = function(tableName) {
  return this.knex.schema.createTable(tableName, function(t) {
    t.increments();
    t.string('name');
    t.integer('batch');
    t.timestamp('migration_time');
  });
};

// Run a batch of current migrations, in sequence.
Migrator.prototype._runBatch = function(migrations, direction) {
  return Promise.all(_.map(migrations, this._validateMigrationStructure, this))
    .bind(this)
    .then(function(migrations) {
      return Promise.bind(this)
        .then(this._latestBatchNumber)
        .then(function(batchNo) {
          if (direction === 'up') batchNo++;
          return batchNo;
        })
        .then(function(batchNo) {
          return this._waterfallBatch(batchNo, migrations, direction);
        });
    });
};

// Validates some migrations by requiring and checking for an `up` and `down` function.
Migrator.prototype._validateMigrationStructure = function(name) {
  var migration = require(path.join(this._absoluteConfigDir(), name));
  if (!_.isFunction(migration.up) || !_.isFunction(migration.down)) {
    throw new Error('Invalid migration: ' + name + ' must have both an up and down function');
  }
  return name;
};

// Lists all migrations that have been completed for the current db, as an array.
Migrator.prototype._listCompleted = Promise.method(function() {
  var tableName = this.config.tableName;
  return this._ensureTable(tableName)
    .bind(this)
    .then(function () {
      return this.knex(tableName).orderBy('id').select('name');
    })
    .then(function(migrations) {
      return _.pluck(migrations, 'name');
    });
});

// Gets the migration list from the specified migration directory,
// as well as the list of completed migrations to check what
// should be run.
Migrator.prototype._migrationData = function() {
  return Promise.all([
    this._listAll(),
    this._listCompleted()
  ]);
};

// Generates the stub template for the current migration, returning a compiled template.
Migrator.prototype._generateStubTemplate = function() {
  var stubPath = this.config.stub || path.join(__dirname, 'stub', this.config.extension + '.stub');
  return Promise.promisify(fs.readFile, fs)(stubPath).then(function(stub) {
    return _.template(stub.toString(), null, {variable: 'd'});
  });
};

// Write a new migration to disk, using the config and generated filename,
// passing any `variables` given in the config to the template.
Migrator.prototype._writeNewMigration = function(name) {
  var config = this.config;
  var dir = this._absoluteConfigDir();
  return function(tmpl) {
    if (name[0] === '-') name = name.slice(1);
    var filename  = yyyymmddhhmmss() + '_' + name + '.' + config.extension;
    return Promise.promisify(fs.writeFile, fs)(
      path.join(dir, filename),
      tmpl(config.variables || {})
    ).return(path.join(dir, filename));
  };
};

// Get the last batch of migrations, by name, ordered by insert id
// in reverse order.
Migrator.prototype._getLastBatch = function() {
  var knex = this.knex;
  var tableName = this.config.tableName;
  return this.knex(tableName)
    .where('batch', function() {
      this.select(knex.raw('MAX(batch)')).from(tableName);
    })
    .orderBy('id', 'desc');
};

// Returns the latest batch number.
Migrator.prototype._latestBatchNumber = function() {
  return this.knex(this.config.tableName)
    .max('batch as batchNo').then(function(obj) {
      return (obj[0].batchNo || 0);
    });
};

// Runs a batch of `migrations` in a specified `direction`,
// saving the appropriate database information as the migrations are run.
Migrator.prototype._waterfallBatch = function(batchNo, migrations, direction) {
  var knex      = this.knex;
  var tableName = this.config.tableName;
  var directory = this._absoluteConfigDir();
  var current   = Promise.bind({failed: false, failedOn: 0});
  var log       = [];
  _.each(migrations, function(migration) {
    var name  = migration;
    migration = require(directory + '/' + name);

    // We're going to run each of the migrations in the current "up"
    current = current.then(function() {
      return migration[direction](knex, Promise);
    }).then(function() {
      log.push(path.join(directory, name));
      if (direction === 'up') {
        return knex(tableName).insert({
          name: name,
          batch: batchNo,
          migration_time: new Date()
        });
      }
      if (direction === 'down') {
        return knex(tableName).where({name: name}).del();
      }
    });
  });

  return current.thenReturn([batchNo, log]);
};

Migrator.prototype._absoluteConfigDir = function() {
  return path.resolve(process.cwd(), this.config.directory);
};

Migrator.prototype.setConfig = function(config) {
  return _.extend({
    extension: 'js',
    tableName: 'knex_migrations',
    directory: '/migrations'
  }, this.config || {}, config);
};

module.exports = Migrator;
<|endoftext|>


<commit_before>var Stex    = require("stex");
var errors  = Stex.errors;
var Promise = Stex.Promise;
var _       = Stex._;

var wallet  = module.exports;
var hash    = require("../util/hash");

wallet.errors             = {};
wallet.errors.Invalid     = Error.subclass("wallet.Invalid");
wallet.errors.InvalidHash = wallet.errors.Invalid.subclass("wallet.InvalidHash");

/**
 * Retrieves the wallet at id `id` from `db`
 */
wallet.get = function(id) {
  return hash.locator(id).then(function(hashedId) {
    return db("wallets").where("id", hashedId).select().then(function(results) {
      return results[0];
    });
  });
};

wallet.getByRecoveryId = function(recoveryId) {
  return hash.locator(recoveryId).then(function(hashedId) {
    return db("wallets").where("recoveryId", hashedId).select().then(function(results) {
      return results[0];
    });
  });
};

wallet.create = function(params) {
  return Promise.resolve(params)
    .then(validatePresent("id"))
    .then(validatePresent("authToken"))
    .then(validatePresent("mainData"))
    .then(validatePresent("keychainData"))
    .then(validateHash("mainData"))
    .then(validateHash("keychainData"))
    .then(function(params) {

      params.id            = hash.locator(params.id);
      params.recoveryId    = params.recoveryId ? hash.locator(params.recoveryId) : null;
      params.authTokenHash = hash.sha2(params.authToken);
      params.createdAt     = new Date();
      params.updatedAt     = params.createdAt;
      params               = _.omit(params, ['mainDataHash', 'keychainDataHash', 'authToken']);

      return Promise.props(params);
    })
    .then(function(params) {
      var e;

      return db("wallets").insert(params).catch(function(e) {
        e = e.clientError.cause; // unwrap the original exception

        if(e.errno === 1062) {
          e = new errors.DuplicateRecord(e.message);
        }

        return Promise.reject(e);
      });
    });
};

wallet.update  = function(id, authToken, changes) {

  return wallet.get(id).then(function(w) {

    // if we couldn't find a wallet, fail
    if(typeof w === 'undefined') {
      return Promise.reject(new errors.RecordNotFound());
    }

    // if we don't have access to the wallet, fail
    if(w.authTokenHash !== hash.sha2(authToken)) {
      return Promise.reject(new errors.Forbidden());
    }

    return Promise.resolve(changes)
      .then(validateHash('mainData',     {allowBlank: true}))
      .then(validateHash('keychainData', {allowBlank: true}))
      .then(function(changes){
        changes = _.pick(changes, ['mainData', 'keychainData']);

        if(_.isEmpty(changes.mainData)) {
          delete changes.mainData;
        }
        // strip out empty changes
        if(_.isEmpty(changes.keychainData)) {
          delete changes.keychainData;
        }

        return db('wallets')
          .where('id', w.id)
          .update(changes);
      });
  });
};

wallet.replace = function(oldId, oldAuthToken, newId, newAuthToken) {
  return Promise.all([
      wallet.get(oldId),
      wallet.get(newId)
    ])
    .then(function(wallets) {
      if(!_.all(wallets)) {
        return Promise.reject(new errors.RecordNotFound());
      }

      var oldWallet = wallets[0];
      var newWallet = wallets[1];

      if(oldWallet.authTokenHash !== hash.sha2(oldAuthToken)) {
        return Promise.reject(new errors.Forbidden());
      }

      if(newWallet.authTokenHash !== hash.sha2(newAuthToken)) {
        return Promise.reject(new errors.Forbidden());
      }

      return db('wallets')
        .where('id', oldWallet.id)
        .del();
    });
};

wallet.createRecovery = function(id, authToken, changes) {

  return wallet.get(id)
    .then(function(w) {

    // if we couldn't find a wallet, fail
    if(typeof w === 'undefined') {
      return Promise.reject(new errors.RecordNotFound());
    }

    // if we don't have access to the wallet, fail
    if(w.authTokenHash !== hash.sha2(authToken)) {
      return Promise.reject(new errors.Forbidden());
    }

    return Promise.resolve(changes)
      .then(validatePresent('recoveryId'))
      .then(validatePresent('recoveryData'))
      .then(validateHash('recoveryData'))
      .then(function(changes){
        changes = _.pick(changes, ['recoveryId', 'recoveryData']);

        return hash.locator(changes.recoveryId)
          .then(function(recoveryId){
            changes.recoveryId = recoveryId;
          })
          .then(function(){
            return db('wallets')
              .where('id', w.id)
              .update(changes);
          });
      });
  });
};

function validateHash(prop, options) {
  if(!options) {
    options = {};
  }

  return function(data) {
    var value     = data[prop];
    var valueHash = data[prop + "Hash"];
    var e;

    if(_.isEmpty(value)) {
      if(options.allowBlank) {
        return Promise.resolve(data);
      } else {
        e = new wallet.errors.InvalidHash(prop + " is corrupt");
        e.field = prop;
        return Promise.reject(e);
      }
    }

    var dataHash = hash.sha1(value);

    if(dataHash !== valueHash) {
      e = new wallet.errors.InvalidHash(prop + " is corrupt");
      e.field = prop;
      return Promise.reject(e);
    } else {
      return Promise.resolve(data);
    }
  };
}

function validatePresent(prop) {
  return function(data) {
    if(_.isEmpty(data[prop])) {
      var e = new errors.MissingField(prop + " is blank");
      e.field = prop;
      return Promise.reject(e);
    } else {
      return Promise.resolve(data);
    }
  };
}

<commit_msg>Fixes our duplicate record listener, since knex no longer wraps the error<commit_after>var Stex    = require("stex");
var errors  = Stex.errors;
var Promise = Stex.Promise;
var _       = Stex._;

var wallet  = module.exports;
var hash    = require("../util/hash");

wallet.errors             = {};
wallet.errors.Invalid     = Error.subclass("wallet.Invalid");
wallet.errors.InvalidHash = wallet.errors.Invalid.subclass("wallet.InvalidHash");

/**
 * Retrieves the wallet at id `id` from `db`
 */
wallet.get = function(id) {
  return hash.locator(id).then(function(hashedId) {
    return db("wallets").where("id", hashedId).select().then(function(results) {
      return results[0];
    });
  });
};

wallet.getByRecoveryId = function(recoveryId) {
  return hash.locator(recoveryId).then(function(hashedId) {
    return db("wallets").where("recoveryId", hashedId).select().then(function(results) {
      return results[0];
    });
  });
};

wallet.create = function(params) {
  return Promise.resolve(params)
    .then(validatePresent("id"))
    .then(validatePresent("authToken"))
    .then(validatePresent("mainData"))
    .then(validatePresent("keychainData"))
    .then(validateHash("mainData"))
    .then(validateHash("keychainData"))
    .then(function(params) {

      params.id            = hash.locator(params.id);
      params.recoveryId    = params.recoveryId ? hash.locator(params.recoveryId) : null;
      params.authTokenHash = hash.sha2(params.authToken);
      params.createdAt     = new Date();
      params.updatedAt     = params.createdAt;
      params               = _.omit(params, ['mainDataHash', 'keychainDataHash', 'authToken']);

      return Promise.props(params);
    })
    .then(function(params) {
      var e;

      return db("wallets").insert(params).catch(function(e) {
        if(e.errno === 1062) {
          e = new errors.DuplicateRecord(e.message);
        }

        return Promise.reject(e);
      });
    });
};

wallet.update  = function(id, authToken, changes) {

  return wallet.get(id).then(function(w) {

    // if we couldn't find a wallet, fail
    if(typeof w === 'undefined') {
      return Promise.reject(new errors.RecordNotFound());
    }

    // if we don't have access to the wallet, fail
    if(w.authTokenHash !== hash.sha2(authToken)) {
      return Promise.reject(new errors.Forbidden());
    }

    return Promise.resolve(changes)
      .then(validateHash('mainData',     {allowBlank: true}))
      .then(validateHash('keychainData', {allowBlank: true}))
      .then(function(changes){
        changes = _.pick(changes, ['mainData', 'keychainData']);

        if(_.isEmpty(changes.mainData)) {
          delete changes.mainData;
        }
        // strip out empty changes
        if(_.isEmpty(changes.keychainData)) {
          delete changes.keychainData;
        }

        return db('wallets')
          .where('id', w.id)
          .update(changes);
      });
  });
};

wallet.replace = function(oldId, oldAuthToken, newId, newAuthToken) {
  return Promise.all([
      wallet.get(oldId),
      wallet.get(newId)
    ])
    .then(function(wallets) {
      if(!_.all(wallets)) {
        return Promise.reject(new errors.RecordNotFound());
      }

      var oldWallet = wallets[0];
      var newWallet = wallets[1];

      if(oldWallet.authTokenHash !== hash.sha2(oldAuthToken)) {
        return Promise.reject(new errors.Forbidden());
      }

      if(newWallet.authTokenHash !== hash.sha2(newAuthToken)) {
        return Promise.reject(new errors.Forbidden());
      }

      return db('wallets')
        .where('id', oldWallet.id)
        .del();
    });
};

wallet.createRecovery = function(id, authToken, changes) {

  return wallet.get(id)
    .then(function(w) {

    // if we couldn't find a wallet, fail
    if(typeof w === 'undefined') {
      return Promise.reject(new errors.RecordNotFound());
    }

    // if we don't have access to the wallet, fail
    if(w.authTokenHash !== hash.sha2(authToken)) {
      return Promise.reject(new errors.Forbidden());
    }

    return Promise.resolve(changes)
      .then(validatePresent('recoveryId'))
      .then(validatePresent('recoveryData'))
      .then(validateHash('recoveryData'))
      .then(function(changes){
        changes = _.pick(changes, ['recoveryId', 'recoveryData']);

        return hash.locator(changes.recoveryId)
          .then(function(recoveryId){
            changes.recoveryId = recoveryId;
          })
          .then(function(){
            return db('wallets')
              .where('id', w.id)
              .update(changes);
          });
      });
  });
};

function validateHash(prop, options) {
  if(!options) {
    options = {};
  }

  return function(data) {
    var value     = data[prop];
    var valueHash = data[prop + "Hash"];
    var e;

    if(_.isEmpty(value)) {
      if(options.allowBlank) {
        return Promise.resolve(data);
      } else {
        e = new wallet.errors.InvalidHash(prop + " is corrupt");
        e.field = prop;
        return Promise.reject(e);
      }
    }

    var dataHash = hash.sha1(value);

    if(dataHash !== valueHash) {
      e = new wallet.errors.InvalidHash(prop + " is corrupt");
      e.field = prop;
      return Promise.reject(e);
    } else {
      return Promise.resolve(data);
    }
  };
}

function validatePresent(prop) {
  return function(data) {
    if(_.isEmpty(data[prop])) {
      var e = new errors.MissingField(prop + " is blank");
      e.field = prop;
      return Promise.reject(e);
    } else {
      return Promise.resolve(data);
    }
  };
}

<|endoftext|>


<commit_before>/**
 * mongoose-uuid
 * git@github.com:dlamp/mongoose-uuid.git
 *
 * Copyright (c) 2014 Darin Lampson
 * Licensed under the MIT license.
 */

var uuid = require('node-uuid');

(function(){
	'use strict';

	/**
	 * Injects plugin on a Schema
	 * @param  Schema schema Schema that will be modified.
	 * @return {Middleware} Invokes next middleware.
	 */
	module.exports = function(schema, options) {

		if(!schema.paths._id) {
			schema.add({
				'_id': {
					type: String,
					index: {
						unique: true
					}
				}
			});
		}

		schema.pre('save', function(next) {
			if(this.isNew)
				this._id = uuid.v4();

			return next();
		});
	};
})();<commit_msg>Lib refactored<commit_after>/**
 * mongoose-uuid
 * git@github.com:dlamp/mongoose-uuid.git
 *
 * Copyright (c) 2014 Darin Lampson
 * Licensed under the MIT license.
 */

var uuid = require('node-uuid');

(function(){
	'use strict';

	/**
	 * Injects plugin on a Schema
	 * @param  Schema schema Schema that will be modified.
	 * @return {Middleware} Invokes next middleware.
	 */
	module.exports = function(schema, options) {

		if(!schema.paths._id) {
			schema.add({
				'_id': {
					type: String,
					index: {
						unique: true
					}
				}
			});
		}

		schema.pre('save', function(next) {
			if(this.isNew)
				this._id = uuid.v4();

			return next();
		});
	};

})();<|endoftext|>


<commit_before>"use strict";

const d3 = require("d3");
const Color = require("color");
const _ = require("lodash");

import event_parsing from "../util/event_parsing";
import color from "../util/color.js"

var time = require("../util/time.js");

/*
 * script
 * apptimeline
 * titleinfo_list (hidden titleinfo)
 * titleinfo_container (visible titleinfo)
 * */

function create(container) {
  // Clear element
  container.innerHTML = "";

  // Animation functions for setting hover color and titlebox content
  var elem = document.createElement("script");
  elem.text =  'function set_color(elem_id, color) {'
  elem.text += '  var rect = document.getElementById(elem_id).children[0];'
  elem.text += '  rect.style.fill = color;'
  elem.text += '};'
  elem.text += 'function show_info(elem_id) {'
  elem.text += '  var title_event_box = document.getElementById(elem_id);'
  elem.text += '  var titleinfo = document.getElementById("titleinfo_container");'
  elem.text += '  titleinfo.innerHTML = title_event_box.innerHTML;'
  elem.text += '  titleinfo.style.height = title_event_box.getAttribute("height");'
  elem.text += '};'
  container.appendChild(elem);

  // svg for the colored timeline
  let timeline = d3.select(container).append("svg")
    .attr("viewBox", "0 0 100 10")
    .attr("width", "100%")
    .attr("class", "apptimeline");

  // Hidden svg image that stores all titleinfo for each timeperiod
  let titleinfo_list = d3.select(container).append("svg")
    .attr("display", "none")
    .attr("class", "titleinfo_list");

  // Container for titleinfo that has a fixed size and a overflow scroll
  let titleinfo_container = document.createElement("div");
  titleinfo_container.style.width = "100%";
  titleinfo_container.style.height = "200px";
  titleinfo_container.style.overflowY = "scroll";
  container.appendChild(titleinfo_container);

  // Titleinfo box that changes content depending on what was timeperiod was last recently hovered on
  let titleinfo = d3.select(titleinfo_container).append("svg")
    .attr("width", "100%")
    .attr("class", "titleinfo_container");
}

function set_status(container, text){
  console.log(container);
  console.log(text);
  let timeline_elem = container.querySelector(".apptimeline");
  let titleinfo_list_elem = container.querySelector(".titleinfo_list");
  let titleinfo_container_elem = container.querySelector(".titleinfo_container");

  let timeline = d3.select(timeline_elem);
  timeline_elem.innerHTML = "";
  titleinfo_list_elem.innerHTML = "";
  titleinfo_container_elem.innerHTML = "";

  timeline.append("text")
   .attr("x", "0")
   .attr("y", "3")
   .text(text)
   .attr("font-family", "sans-serif")
   .attr("font-size", "3")
   .attr("fill", "black")
}

function update(container, events, total_duration){
  let timeline_elem = container.querySelector(".apptimeline");
  let titleinfo_list_elem = container.querySelector(".titleinfo_list");
  let titleinfo_container_elem = container.querySelector(".titleinfo_container");

  let timeline = d3.select(timeline_elem);
  timeline_elem.innerHTML = "";

  let titleinfo_list = d3.select(titleinfo_list_elem);
  titleinfo_list_elem.innerHTML = "";

  let titleinfo_container = d3.select(titleinfo_container_elem);
  titleinfo_container_elem.innerHTML = "";

  if (events.length <= 0){
    set_status(container, "No data");
    return;
  }

  // Iterate over each app timeperiod
  let curr_x = 0;
  _.each(events, function(e, i) {
    let eg = timeline.append("g")
      .attr("id", "timeline_event_"+i);

    // Timeline rect
    var e_width = e.duration / total_duration * 100;
    var appcolor = color.getAppColor(e.appname);
    var hovercolor = Color(appcolor).darken(0.4).rgbString();
    eg.append("rect")
     .attr("x", curr_x)
     .attr("y", 0)
     .attr("width", e_width)
     .attr("height", 10)
     .attr("onmouseover", "set_color('timeline_event_"+i+"', '"+hovercolor+"'); show_info('titleinfo_event_"+i+"')")
     .attr("onmouseout", "set_color('timeline_event_"+i+"', '"+appcolor+"')")
     .style("fill", color.getAppColor(e.appname));

    // Titleinfo box
    var infobox = titleinfo_list.append("g")
      .attr("id", "titleinfo_event_"+i)

    // Appname and duration text
    infobox.append("text")
      .attr("x", "10px")
      .attr("y", "20px")
      .text(e.appname + " (" + time.seconds_to_duration(e.duration) + ")")
      .attr("font-family", "sans-serif")
      .attr("font-size", "20px")
      .attr("fill", "black");

    // Titleinfo
    var curr_y = 45
    _.each(e.titles, function(t, i){
      // Clocktime
      var clocktime = t.timestamp.split("T")[1].split(".")[0];
      infobox.append("text")
        .attr("x", "10px")
        .attr("y", curr_y+"px")
        .text(clocktime)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      // Duration
      var duration = time.seconds_to_duration(t.duration);
      infobox.append("text")
        .attr("x", "100px")
        .attr("y", curr_y+"px")
        .text(duration)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      // Title
      infobox.append("text")
        .attr("x", "170px")
        .attr("y", curr_y+"px")
        .text(t.title)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      curr_y += 20;
    });
    infobox.attr("height", curr_y+"px");

    curr_x += e_width
  });

  return container;
}

module.exports = {
  "create": create,
  "update": update,
  "set_status": set_status,
};
<commit_msg>Fixed broken titleinfo in timeline<commit_after>"use strict";

const d3 = require("d3");
const Color = require("color");
const _ = require("lodash");

import event_parsing from "../util/event_parsing";
import color from "../util/color.js"

var time = require("../util/time.js");

/*
 * script
 * apptimeline
 * titleinfo_list (hidden titleinfo)
 * titleinfo_container (visible titleinfo)
 * */

function create(container) {
  // Clear element
  container.innerHTML = "";

  // Animation functions for setting hover color and titlebox content
  var elem = document.createElement("script");
  elem.text =  'function set_color(elem_id, color) {'
  elem.text += '  var rect = document.getElementById(elem_id).children[0];'
  elem.text += '  rect.style.fill = color;'
  elem.text += '};'
  elem.text += 'function show_info(elem_id) {'
  elem.text += '  var title_event_box = document.getElementById(elem_id);'
  elem.text += '  var titleinfo = document.getElementById("titleinfo_container");'
  elem.text += '  titleinfo.innerHTML = title_event_box.innerHTML;'
  elem.text += '  titleinfo.style.height = title_event_box.getAttribute("height");'
  elem.text += '};'
  container.appendChild(elem);

  // svg for the colored timeline
  let timeline = d3.select(container).append("svg")
    .attr("viewBox", "0 0 100 10")
    .attr("width", "100%")
    .attr("class", "apptimeline");

  // Hidden svg image that stores all titleinfo for each timeperiod
  let titleinfo_list = d3.select(container).append("svg")
    .attr("display", "none")
    .attr("class", "titleinfo_list");

  // Container for titleinfo that has a fixed size and a overflow scroll
  let titleinfo_container = document.createElement("div");
  titleinfo_container.style.width = "100%";
  titleinfo_container.style.height = "200px";
  titleinfo_container.style.overflowY = "scroll";
  container.appendChild(titleinfo_container);

  // Titleinfo box that changes content depending on what was timeperiod was last recently hovered on
  let titleinfo = d3.select(titleinfo_container).append("svg")
    .attr("width", "100%")
    .attr("id", "titleinfo_container");
}

function set_status(container, text){
  console.log(container);
  console.log(text);
  let timeline_elem = container.querySelector(".apptimeline");
  let titleinfo_list_elem = container.querySelector(".titleinfo_list");
  let titleinfo_container_elem = container.querySelector("#titleinfo_container");

  let timeline = d3.select(timeline_elem);
  timeline_elem.innerHTML = "";
  titleinfo_list_elem.innerHTML = "";
  titleinfo_container_elem.innerHTML = "";

  timeline.append("text")
   .attr("x", "0")
   .attr("y", "3")
   .text(text)
   .attr("font-family", "sans-serif")
   .attr("font-size", "3")
   .attr("fill", "black")
}

function update(container, events, total_duration){
  let timeline_elem = container.querySelector(".apptimeline");
  let titleinfo_list_elem = container.querySelector(".titleinfo_list");
  let titleinfo_container_elem = container.querySelector("#titleinfo_container");

  let timeline = d3.select(timeline_elem);
  timeline_elem.innerHTML = "";

  let titleinfo_list = d3.select(titleinfo_list_elem);
  titleinfo_list_elem.innerHTML = "";

  let titleinfo_container = d3.select(titleinfo_container_elem);
  titleinfo_container_elem.innerHTML = "";

  if (events.length <= 0){
    set_status(container, "No data");
    return;
  }

  // Iterate over each app timeperiod
  let curr_x = 0;
  _.each(events, function(e, i) {
    let eg = timeline.append("g")
      .attr("id", "timeline_event_"+i);

    // Timeline rect
    var e_width = e.duration / total_duration * 100;
    var appcolor = color.getAppColor(e.appname);
    var hovercolor = Color(appcolor).darken(0.4).rgbString();
    eg.append("rect")
     .attr("x", curr_x)
     .attr("y", 0)
     .attr("width", e_width)
     .attr("height", 10)
     .attr("onmouseover", "set_color('timeline_event_"+i+"', '"+hovercolor+"'); show_info('titleinfo_event_"+i+"')")
     .attr("onmouseout", "set_color('timeline_event_"+i+"', '"+appcolor+"')")
     .style("fill", color.getAppColor(e.appname));

    // Titleinfo box
    var infobox = titleinfo_list.append("g")
      .attr("id", "titleinfo_event_"+i)

    // Appname and duration text
    infobox.append("text")
      .attr("x", "10px")
      .attr("y", "20px")
      .text(e.appname + " (" + time.seconds_to_duration(e.duration) + ")")
      .attr("font-family", "sans-serif")
      .attr("font-size", "20px")
      .attr("fill", "black");

    // Titleinfo
    var curr_y = 45
    _.each(e.titles, function(t, i){
      // Clocktime
      var clocktime = t.timestamp.split("T")[1].split(".")[0];
      infobox.append("text")
        .attr("x", "10px")
        .attr("y", curr_y+"px")
        .text(clocktime)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      // Duration
      var duration = time.seconds_to_duration(t.duration);
      infobox.append("text")
        .attr("x", "100px")
        .attr("y", curr_y+"px")
        .text(duration)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      // Title
      infobox.append("text")
        .attr("x", "170px")
        .attr("y", curr_y+"px")
        .text(t.title)
        .attr("font-family", "sans-serif")
        .attr("font-size", "15px")
      curr_y += 20;
    });
    infobox.attr("height", curr_y+"px");

    curr_x += e_width
  });

  return container;
}

module.exports = {
  "create": create,
  "update": update,
  "set_status": set_status,
};
<|endoftext|>


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

  angular.module('topFive.search')
    .controller('searchComponentController', searchComponentCtrl);

  searchComponentCtrl.$inject = ['searchFactory'];

  function searchComponentCtrl(searchFactory){

  }

})();
<commit_msg>create #getResults function to process search results<commit_after>(function(){
  'use strict';

  angular.module('topFive.search')
    .controller('searchComponentController', searchComponentCtrl);

  searchComponentCtrl.$inject = ['searchFactory'];

  function searchComponentCtrl(searchFactory){
    var vm = this;
    vm.options = {
      type:'',
      query:'',
      limit:20
    }
    vm.getResults = getResults;

    function getResults(resultsArr){
      vm.onSearch(resultsArr);
    }
  }

})();
<|endoftext|>


<commit_before>/* jslint node: true */

/**
 *
 * sclang - boots a supercollider language interpreter process
 *  and enables stdin/out and system level event responders
 *
 *  SuperCollider comes with an executable called sclang
 *  which can be communicated with via stdin/stdout
 *  or via OSC.
 *
 * methods:
 *   boot  - boot an sclang process
 *   write - write to stdin of the sclang process
 *   quit
 *
 */

var
  _ = require('underscore'),
  events = require('events'),
  spawn = require('child_process').spawn,
  path = require('path'),
  Logger = require('./logger'),
  Q = require('q'),
  uuid = require('node-uuid'),
  join = require('path').join;

/**
 *
 * Local sclang - a language interpreter running on the same machine
 *
 * @constructor
 * @param {dict} options - sclang command line options
 */
function SCLang(options) {
  this.options = options;
  this.process = null;
  this.calls = {};
  this.state = null;
  this.log = new Logger(this.options.debug, this.options.echo);
  this.log.dbug(this.options);
}

SCLang.super_ = events.EventEmitter;
SCLang.prototype = Object.create(events.EventEmitter.prototype, {
  constructor: {
    value: SCLang,
    enumerable: false
  }
});

/**
 * build args for sclang
 *
 *   -d <path>                      Set runtime directory
 *   -D                             Enter daemon mode (no input)
 *   -g <memory-growth>[km]         Set heap growth (default 256k)
 *   -h                             Display this message and exit
 *   -l <path>                      Set library configuration file
 *   -m <memory-space>[km]          Set initial heap size (default 2m)
 *   -r                             Call Main.run on startup
 *   -s                             Call Main.stop on shutdown
 *   -u <network-port-number>       Set UDP listening port (default 57120)
 *   -i <ide-name>                  Specify IDE name (for enabling IDE-specific class code, default "none")
 *   -a                             Standalone mode
 */
SCLang.prototype.args = function() {
  var o = [];
  if (this.options.executeFile) {
    o.push(this.options.executeFile);
  }
  o.push('-u', this.options.langPort);
  return o;
};

/**
 * boot
 *
 * start sclang and establish a pipe connection
 * to receive stdout and stderr
 */
SCLang.prototype.boot = function() {
  var
    deferred = Q.defer(),
    execPath = this.options.sclang,
    self = this;
  this.log.dbug('path:' + execPath);
  this.setState('booting');
  this.process = spawn(execPath, this.args(),
    {
      cwd: path.dirname(execPath)
    });

  function resolver(state) {
    if (state === 'ready') {
      deferred.resolve();
      self.removeListener('state', resolver);
    } else if (state === 'compileError') {
      deferred.reject();
      self.removeListener('state', resolver);
    }
  }
  // temporary listener till booted
  this.addListener('state', resolver);

  // long term listeners
  this.installListeners();
  return deferred.promise;
};

/**
  * installListeners
  */
SCLang.prototype.installListeners = function() {

  var self = this,
    states = {
      booting: [
        {
          re: /^compiling class library/m,
          fn: function() {
            self.setState('compiling');
            // and collect potential compile errors
          }
        }
      ],
      compiling: [
        {
          re: /^compile done/m,
          fn: function() {
            self.setState('compiled');
            // and collect potential startup errors
          }
        },
        {
          re: /^Library has not been compiled successfully/m,
          fn: function() {
            self.setState('compileError');
          }
        }
      ],
      compileError: [

      ],
      compiled: [
        {
          re: /^Welcome to SuperCollider/m,
          fn: function() {
            self.setState('ready');
          }
        }
      ],
      ready: [
        {
          // callback
          re: /^SUPERCOLLIDERJS\:([0-9a-f\-]+)\:([A-Za-z]+)\:(.+)$/m,
          fn: function(match) {
            var
              guid = match[1],
              type = match[2],
              json = match[3],
              obj = JSON.parse(json);
            if (guid in self.calls) {
              if (type === 'Result') {
                self.calls[guid].resolve(obj);
              } else {
                self.calls[guid].reject({type: type, error: obj});
              }
              delete self.calls[guid];
              return true;
            }
          }
        },
        {
          // interpreter.scd posts this at the end
          re: /^SUPERCOLLIDERJS-interpreter-loaded$/m,
          fn: function() {
            self.emit('interpreterLoaded');
            return true;
          }
        },
        {
          // interpret calls post this
          re: /^SUPERCOLLIDERJS.interpreted$/m,
          fn: function() {
            // supress output
            return true;
          }
        }
      ]
    };

  this.process.stdout.on('data', function(data) {
    var text = '' + data,
        passthru = true;
    states[self.state].forEach(function(stf) {
      var match = stf.re.exec(text);
      if (match) {
        // do not post if handler returns true
        if (stf.fn(match) === true) {
          passthru = false;
        }
      }
    });
    if (passthru) {
      self.log.stdout(text);
      self.emit('stdout', text);
    }
  });

  this.process.on('error', function(err) {
    self.log.err('ERROR:' + err, 'error');
    self.emit('stderr', err);
  });
  this.process.on('close', function(code, signal) {
    self.log.dbug('close ' + code + signal);
    self.emit('exit', code);
    self.setState(null);
  });
  this.process.on('exit', function(code, signal) {
    self.log.dbug('exit ' + code + signal);
    self.emit('exit', code);
    self.setState(null);
  });
  this.process.on('disconnect', function() {
    self.log.dbug('disconnect');
    self.emit('exit');
    self.setState(null);
  });

  this.process.stderr.on('data', function(data) {
    self.log.stderr('' + data);
    self.emit('stderr', '' + data);
  });

  if (this.options.stdin) {
    // global top level nodejs process
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function(chunk) {
      if (chunk) {
        self.write(chunk, null, true);
      }
    });
  }
};

/**
 * write
 *
 * send a raw string to sclang to be interpreted
 * callback is called after write is complete
 */
SCLang.prototype.write = function(chunk, callback, noEcho) {
  if (!noEcho) {
    this.log.stdin(chunk);
  }
  this.process.stdin.write(chunk, 'UTF-8');
  // escape character means execute the currently accumulated command line as SC code
  this.process.stdin.write('\x0c', null, callback);
};

/**
  * initInterpreter
  *
  * make sclang execute the interpret.scd file
  * to load the functions used by interpret
  */
SCLang.prototype.initInterpreter = function() {
  var path = join(__dirname, '../sc/interpret.scd'),
      deferred = Q.defer(),
      self = this;
  this.once('interpreterLoaded', function() {
    deferred.resolve(self);
  });
  this.write('thisProcess.interpreter.executeFile("' + path + '")',
    null, true);
};

/**
 * interpret
 *
 * evaluates code in sclang
 * and returns a promise
 *
 * resolves promise with result or rejects with error as JSON
 *
 * @param {string} code
 *        source code to evaluate
 * @param {string} nowExecutingPath
          set thisProcess.nowExecutingPath
 *        for use in a REPL to evaluate text in a file
 *        and let sclang know what file it is executing.
 * @param {Boolean} asString
 *        return result .asString for post window
 *        rather than as a full JSON object
 * @returns {Promise}
 */
SCLang.prototype.interpret = function(code, nowExecutingPath, asString) {
  var deferred = Q.defer(),
      escaped = code.replace(/[\n\r]/g, '__NL__').replace(/\"/g, '\\"'),
      scstring = '"' + escaped + '"',
      guid = uuid.v1(),
      guidString = '"' + guid + '"',
      nep = nowExecutingPath ? '"' + nowExecutingPath + '"' : 'nil',
      rras = asString ? 'true' : 'false',
      args = [guidString, scstring, nep, rras].join(','),
      scode;

  scode = 'Library.at(\\supercolliderjs, \\interpret).value(' + args +
    ');"SUPERCOLLIDERJS.interpreted".postln;';

  // register listener
  this.calls[guid] = deferred;
  this.write(scode, null, true);
  return deferred.promise;
};

/**
  * setState
  * set state to one of null, 'booting' 'compiling' 'compileError' 'compiled' 'ready'
  * and emit
  */
SCLang.prototype.setState = function(state) {
  if (state !== this.state) {
    this.state = state;
    this.emit('state', state);
    this.log.dbug('state: ' + state);
  }
};

/**
 * quit
 *
 * stop the sclang process
 */
SCLang.prototype.quit = function() {
  var deferred = Q.defer();
  if (this.process) {
    this.process.once('exit', function() {
      deferred.resolve();
    });
    // should do shutdown first
    this.process.kill('SIGINT');
    this.process = null;
  } else {
    deferred.resolve();
  }
  this.setState(null);
  return deferred.promise;
};

/**
  * alternate constructor
  * resolves options, boots and loads interpreter
  * @returns: a promise
  */
SCLang.boot = function(options) {
  var resolveOptions = require('./resolveOptions'),
      deferred = Q.defer();

  resolveOptions(null, options).then(function(options) {
    var sclang = new SCLang(options);

    sclang.boot()
      .then(function() {
        return sclang.initInterpreter();
      })
      .then(function() {
        deferred.resolve(sclang);
      })
      .catch(deferred.reject);

  });

  return deferred.promise;
};

module.exports = SCLang;
<commit_msg>fix interpret for 3.6.x which always requires a \n<commit_after>/* jslint node: true */

/**
 *
 * sclang - boots a supercollider language interpreter process
 *  and enables stdin/out and system level event responders
 *
 *  SuperCollider comes with an executable called sclang
 *  which can be communicated with via stdin/stdout
 *  or via OSC.
 *
 * methods:
 *   boot  - boot an sclang process
 *   write - write to stdin of the sclang process
 *   quit
 *
 */

var
  _ = require('underscore'),
  events = require('events'),
  spawn = require('child_process').spawn,
  path = require('path'),
  Logger = require('./logger'),
  Q = require('q'),
  uuid = require('node-uuid'),
  join = require('path').join;

/**
 *
 * Local sclang - a language interpreter running on the same machine
 *
 * @constructor
 * @param {dict} options - sclang command line options
 */
function SCLang(options) {
  this.options = options;
  this.process = null;
  this.calls = {};
  this.state = null;
  this.log = new Logger(this.options.debug, this.options.echo);
  this.log.dbug(this.options);
}

SCLang.super_ = events.EventEmitter;
SCLang.prototype = Object.create(events.EventEmitter.prototype, {
  constructor: {
    value: SCLang,
    enumerable: false
  }
});

/**
 * build args for sclang
 *
 *   -d <path>                      Set runtime directory
 *   -D                             Enter daemon mode (no input)
 *   -g <memory-growth>[km]         Set heap growth (default 256k)
 *   -h                             Display this message and exit
 *   -l <path>                      Set library configuration file
 *   -m <memory-space>[km]          Set initial heap size (default 2m)
 *   -r                             Call Main.run on startup
 *   -s                             Call Main.stop on shutdown
 *   -u <network-port-number>       Set UDP listening port (default 57120)
 *   -i <ide-name>                  Specify IDE name (for enabling IDE-specific class code, default "none")
 *   -a                             Standalone mode
 */
SCLang.prototype.args = function() {
  var o = [];
  if (this.options.executeFile) {
    o.push(this.options.executeFile);
  }
  o.push('-u', this.options.langPort);
  return o;
};

/**
 * boot
 *
 * start sclang and establish a pipe connection
 * to receive stdout and stderr
 */
SCLang.prototype.boot = function() {
  var
    deferred = Q.defer(),
    execPath = this.options.sclang,
    self = this;
  this.log.dbug('path:' + execPath);
  this.setState('booting');
  this.process = spawn(execPath, this.args(),
    {
      cwd: path.dirname(execPath)
    });

  function resolver(state) {
    if (state === 'ready') {
      deferred.resolve();
      self.removeListener('state', resolver);
    } else if (state === 'compileError') {
      deferred.reject();
      self.removeListener('state', resolver);
    }
  }
  // temporary listener till booted
  this.addListener('state', resolver);

  // long term listeners
  this.installListeners();
  return deferred.promise;
};

/**
  * installListeners
  */
SCLang.prototype.installListeners = function() {

  var self = this,
    states = {
      booting: [
        {
          re: /^compiling class library/m,
          fn: function() {
            self.setState('compiling');
            // and collect potential compile errors
          }
        }
      ],
      compiling: [
        {
          re: /^compile done/m,
          fn: function() {
            self.setState('compiled');
            // and collect potential startup errors
          }
        },
        {
          re: /^Library has not been compiled successfully/m,
          fn: function() {
            self.setState('compileError');
          }
        }
      ],
      compileError: [

      ],
      compiled: [
        {
          re: /^Welcome to SuperCollider/m,
          fn: function() {
            self.setState('ready');
          }
        }
      ],
      ready: [
        {
          // callback
          re: /^SUPERCOLLIDERJS\:([0-9a-f\-]+)\:([A-Za-z]+)\:(.+)$/m,
          fn: function(match) {
            var
              guid = match[1],
              type = match[2],
              json = match[3],
              obj = JSON.parse(json);
            if (guid in self.calls) {
              if (type === 'Result') {
                self.calls[guid].resolve(obj);
              } else {
                self.calls[guid].reject({type: type, error: obj});
              }
              delete self.calls[guid];
              return true;
            }
          }
        },
        {
          // interpreter.scd posts this at the end
          re: /^SUPERCOLLIDERJS-interpreter-loaded$/m,
          fn: function() {
            self.emit('interpreterLoaded');
            return true;
          }
        },
        {
          // interpret calls post this
          re: /^SUPERCOLLIDERJS.interpreted$/m,
          fn: function() {
            // supress output
            return true;
          }
        }
      ]
    };

  this.process.stdout.on('data', function(data) {
    var text = '' + data,
        passthru = true;
    states[self.state].forEach(function(stf) {
      var match = stf.re.exec(text);
      if (match) {
        // do not post if handler returns true
        if (stf.fn(match) === true) {
          passthru = false;
        }
      }
    });
    if (passthru) {
      self.log.stdout(text);
      self.emit('stdout', text);
    }
  });

  this.process.on('error', function(err) {
    self.log.err('ERROR:' + err, 'error');
    self.emit('stderr', err);
  });
  this.process.on('close', function(code, signal) {
    self.log.dbug('close ' + code + signal);
    self.emit('exit', code);
    self.setState(null);
  });
  this.process.on('exit', function(code, signal) {
    self.log.dbug('exit ' + code + signal);
    self.emit('exit', code);
    self.setState(null);
  });
  this.process.on('disconnect', function() {
    self.log.dbug('disconnect');
    self.emit('exit');
    self.setState(null);
  });

  this.process.stderr.on('data', function(data) {
    self.log.stderr('' + data);
    self.emit('stderr', '' + data);
  });

  if (this.options.stdin) {
    // global top level nodejs process
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', function(chunk) {
      if (chunk) {
        self.write(chunk, null, true);
      }
    });
  }
};

/**
 * write
 *
 * send a raw string to sclang to be interpreted
 * callback is called after write is complete
 */
SCLang.prototype.write = function(chunk, callback, noEcho) {
  if (!noEcho) {
    this.log.stdin(chunk);
  }
  this.process.stdin.write(chunk, 'UTF-8');
  // escape character means execute the currently accumulated command line as SC code
  this.process.stdin.write('\x0c', null, callback);
};

/**
  * initInterpreter
  *
  * make sclang execute the interpret.scd file
  * to load the functions used by interpret
  */
SCLang.prototype.initInterpreter = function() {
  var path = join(__dirname, '../sc/interpret.scd'),
      deferred = Q.defer(),
      self = this;
  this.once('interpreterLoaded', function() {
    deferred.resolve(self);
  });
    null, true);
  this.write('thisProcess.interpreter.executeFile("' + path + '");\n',
};

/**
 * interpret
 *
 * evaluates code in sclang
 * and returns a promise
 *
 * resolves promise with result or rejects with error as JSON
 *
 * @param {string} code
 *        source code to evaluate
 * @param {string} nowExecutingPath
          set thisProcess.nowExecutingPath
 *        for use in a REPL to evaluate text in a file
 *        and let sclang know what file it is executing.
 * @param {Boolean} asString
 *        return result .asString for post window
 *        rather than as a full JSON object
 * @returns {Promise}
 */
SCLang.prototype.interpret = function(code, nowExecutingPath, asString) {
  var deferred = Q.defer(),
      escaped = code.replace(/[\n\r]/g, '__NL__').replace(/\"/g, '\\"'),
      scstring = '"' + escaped + '"',
      guid = uuid.v1(),
      guidString = '"' + guid + '"',
      nep = nowExecutingPath ? '"' + nowExecutingPath + '"' : 'nil',
      rras = asString ? 'true' : 'false',
      args = [guidString, scstring, nep, rras].join(','),
      scode;

  scode = 'Library.at(\\supercolliderjs, \\interpret).value(' + args +
    ');"SUPERCOLLIDERJS.interpreted".postln;\n';

  // register listener
  this.calls[guid] = deferred;
  this.write(scode, null, true);
  return deferred.promise;
};

/**
  * setState
  * set state to one of null, 'booting' 'compiling' 'compileError' 'compiled' 'ready'
  * and emit
  */
SCLang.prototype.setState = function(state) {
  if (state !== this.state) {
    this.state = state;
    this.emit('state', state);
    this.log.dbug('state: ' + state);
  }
};

/**
 * quit
 *
 * stop the sclang process
 */
SCLang.prototype.quit = function() {
  var deferred = Q.defer();
  if (this.process) {
    this.process.once('exit', function() {
      deferred.resolve();
    });
    // should do shutdown first
    this.process.kill('SIGINT');
    this.process = null;
  } else {
    deferred.resolve();
  }
  this.setState(null);
  return deferred.promise;
};

/**
  * alternate constructor
  * resolves options, boots and loads interpreter
  * @returns: a promise
  */
SCLang.boot = function(options) {
  var resolveOptions = require('./resolveOptions'),
      deferred = Q.defer();

  resolveOptions(null, options).then(function(options) {
    var sclang = new SCLang(options);

    sclang.boot()
      .then(function() {
        return sclang.initInterpreter();
      })
      .then(function() {
        deferred.resolve(sclang);
      })
      .catch(deferred.reject);

  });

  return deferred.promise;
};

module.exports = SCLang;
<|endoftext|>


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

  var EpisodesIndexController = function($auth, viewStyle, episodes, $stateParams, episodeFilter, Episode, toastr, $state, usSpinnerService) {
    var vm = this;
    vm.episodes = episodes.data;
    vm.filters = $stateParams;
    vm.currentUser = $auth.getPayload();
    vm.busy = false;
    vm.end = false;
    episodeFilter.setTag($stateParams.tags_name_eq);
    episodeFilter.setType($stateParams.episode_type_name_eq);

    var removeTag = function() {
      episodeFilter.setTag(null);
    };

    var removeType = function() {
      episodeFilter.setType(null);
    };

    var publishEpisode = function(episode) {
      usSpinnerService.spin('spinner-1');
      Episode.update(episode.id, { episode: { publish: true } }).then(function() {
        usSpinnerService.stop('spinner-1');
        $state.reload();
        toastr.success('You have published a new episode');
      }).catch(function(res) {
        usSpinnerService.stop('spinner-1');
        toastr.error.apply(this, res.data.errors);
      })
    };

    var unPublishEpisode = function(episode) {
      usSpinnerService.spin('spinner-1');
      Episode.update(episode.id, { episode: { unpublish: true } }).then(function() {
        usSpinnerService.stop('spinner-1');
        $state.reload();
        toastr.success('You have unpublished an episode');
      }).catch(function(res) {
        usSpinnerService.stop('spinner-1');
        toastr.error.apply(this, res.data.errors);
      })
    };

    var nextPage = function() {
      if (vm.busy) { return; }
      vm.busy = true;
      var params = $stateParams;
      params.page = Math.ceil(vm.episodes.length / 30) + 1;
      Episode.query(params).then(function(res) {
        if (res.data.length < 30) {
          vm.end = true;
        }
        vm.episodes = vm.episodes.concat(res.data);
        vm.busy = false
      });
    };

    vm.removeType = removeType;
    vm.removeTag = removeTag;
    vm.getCurrentStyle = viewStyle.getCurrentStyle;
    vm.publishEpisode = publishEpisode;
    vm.unPublishEpisode = unPublishEpisode;
    vm.nextPage = nextPage;
  };

  EpisodesIndexController.$inject = ['$auth', 'viewStyle', 'episodes', '$stateParams', 'episodeFilter', 'Episode', 'toastr', '$state', 'usSpinnerService'];

  angular.module('yujihomo')
    .controller('EpisodesIndexController', EpisodesIndexController);
})();
<commit_msg>add simcolon<commit_after>(function() {
  'use strict';

  var EpisodesIndexController = function($auth, viewStyle, episodes, $stateParams, episodeFilter, Episode, toastr, $state, usSpinnerService) {
    var vm = this;
    vm.episodes = episodes.data;
    vm.filters = $stateParams;
    vm.currentUser = $auth.getPayload();
    vm.busy = false;
    vm.end = false;
    episodeFilter.setTag($stateParams.tags_name_eq);
    episodeFilter.setType($stateParams.episode_type_name_eq);

    var removeTag = function() {
      episodeFilter.setTag(null);
    };

    var removeType = function() {
      episodeFilter.setType(null);
    };

    var publishEpisode = function(episode) {
      usSpinnerService.spin('spinner-1');
      Episode.update(episode.id, { episode: { publish: true } }).then(function() {
        usSpinnerService.stop('spinner-1');
        $state.reload();
        toastr.success('You have published a new episode');
      }).catch(function(res) {
        usSpinnerService.stop('spinner-1');
        toastr.error.apply(this, res.data.errors);
      });
    };

    var unPublishEpisode = function(episode) {
      usSpinnerService.spin('spinner-1');
      Episode.update(episode.id, { episode: { unpublish: true } }).then(function() {
        usSpinnerService.stop('spinner-1');
        $state.reload();
        toastr.success('You have unpublished an episode');
      }).catch(function(res) {
        usSpinnerService.stop('spinner-1');
        toastr.error.apply(this, res.data.errors);
      });
    };

    var nextPage = function() {
      if (vm.busy) { return; }
      vm.busy = true;
      var params = $stateParams;
      params.page = Math.ceil(vm.episodes.length / 30) + 1;
      Episode.query(params).then(function(res) {
        if (res.data.length < 30) {
          vm.end = true;
        }
        vm.episodes = vm.episodes.concat(res.data);
        vm.busy = false;
      });
    };

    vm.removeType = removeType;
    vm.removeTag = removeTag;
    vm.getCurrentStyle = viewStyle.getCurrentStyle;
    vm.publishEpisode = publishEpisode;
    vm.unPublishEpisode = unPublishEpisode;
    vm.nextPage = nextPage;
  };

  EpisodesIndexController.$inject = ['$auth', 'viewStyle', 'episodes', '$stateParams', 'episodeFilter', 'Episode', 'toastr', '$state', 'usSpinnerService'];

  angular.module('yujihomo')
    .controller('EpisodesIndexController', EpisodesIndexController);
})();
<|endoftext|>


<commit_before>'use strict';

var SockJS = require('sockjs-client-node'),
	fetch = require('fetch'),
	_ = require('lodash');

module.exports = Listener;

function Listener(apiToken) {

	var handlers = {},
		self = this,
		clientStarted = false;

	this.startClient = function() {
		if (clientStarted) {
			return;
		}
		clientStarted = true;

		var client = new SockJS("https://channel.pipedrive.com/sockjs");

		client.onopen = function () {
			var options = {
				rejectUnauthorized: false
			};
			fetch.fetchUrl('https://api.pipedrive.com/v1/authorizations/nonce?api_token=' + encodeURIComponent(apiToken), options, function(error, meta, body) {
				var data = {}
				try {
					data = JSON.parse(body);
					if (data.data) {
						data = data.data;
					}
				}
				catch (e) {
					throw new Error('Could not parse API response');
				}

				if (data && data.nonce) {
					client.send(JSON.stringify({
							company_id: data.company_id,
							user_id: data.user_id,
							user_name: 'client-nodejs-user',
							host: 'app.pipedrive.com',
							timestamp: Math.round(new Date().getTime() / 1000),
							nonce: data.nonce
					}));
				}
				else {
					throw new Error('Authorization failed');
				}
			});
		};
		client.onmessage = function (msg) {
			if (msg && msg.type === 'message') {
				var data = {};
				try {
					data = JSON.parse(msg.data);
				}
				catch (e) {
					throw new Error('Malformed JSON received from socket');
				}

				if (data && data.meta && data.meta.v === 1) {
					if (handlers[data.meta.action + '.' + data.meta.object]) {
						_.each(handlers[data.meta.action + '.' + data.meta.object], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers['*.' + data.meta.object]) {
						_.each(handlers['*.' + data.meta.object], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers[data.meta.action + '.*']) {
						_.each(handlers[data.meta.action + '.*'], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers['*.*']) {
						_.each(handlers['*.*'], function(handler) {
							handler(data, data.data);
						});
					}
				}

				if (data.rabbitStateChange === 'open') {
					if (handlers['listening']) {
						_.each(handlers['listening'], function(handler) {
							handler();
						});
					}
				}
			}
		};
		client.onclose = function (e) {
			handlers = {};
		};
	}

	this.on = function(method, handler) {
		if (!clientStarted) {
			self.startClient();
		}
		handlers[method] = handlers[method] || [];
		handlers[method].push(handler);
	};

	this.removeListener = function(method, handler) {
		var index = handlers[method].indexOf(handler);
		if (index > -1) {
			handlers[method].splice(index, 1);
		}
	}

	this.removeAllListeners = function() {
		handlers = {};
	}

}
<commit_msg>add ENV var support for listener lib<commit_after>'use strict';

var SockJS = require('sockjs-client-node'),
	fetch = require('fetch'),
	_ = require('lodash');

module.exports = Listener;

function Listener(apiToken) {

	var handlers = {},
		self = this,
		clientStarted = false;

	this.startClient = function() {
		if (clientStarted) {
			return;
		}
		clientStarted = true;

		var client = new SockJS((process.env.PIPEDRIVE_API_PROTOCOL || 'https')+'://'+(process.env.PIPEDRIVE_CHANNEL_HOST || 'channel.pipedrive.com')+'/sockjs');

		client.onopen = function () {
			var options = {
				rejectUnauthorized: false
			};
			fetch.fetchUrl((process.env.PIPEDRIVE_API_PROTOCOL || 'https')+'://'+(process.env.PIPEDRIVE_API_HOST || 'api.pipedrive.com')+'/v1/authorizations/nonce?api_token=' + encodeURIComponent(apiToken), options, function(error, meta, body) {
				var data = {}
				try {
					data = JSON.parse(body);
					if (data.data) {
						data = data.data;
					}
				}
				catch (e) {
					throw new Error('Could not parse API response');
				}

				if (data && data.nonce) {
					client.send(JSON.stringify({
							company_id: data.company_id,
							user_id: data.user_id,
							user_name: 'client-nodejs-user',
							host: 'app.pipedrive.com',
							timestamp: Math.round(new Date().getTime() / 1000),
							nonce: data.nonce
					}));
				}
				else {
					throw new Error('Authorization failed');
				}
			});
		};
		client.onmessage = function (msg) {
			if (msg && msg.type === 'message') {
				var data = {};
				try {
					data = JSON.parse(msg.data);
				}
				catch (e) {
					throw new Error('Malformed JSON received from socket');
				}

				if (data && data.meta && data.meta.v === 1) {
					if (handlers[data.meta.action + '.' + data.meta.object]) {
						_.each(handlers[data.meta.action + '.' + data.meta.object], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers['*.' + data.meta.object]) {
						_.each(handlers['*.' + data.meta.object], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers[data.meta.action + '.*']) {
						_.each(handlers[data.meta.action + '.*'], function(handler) {
							handler(data, data.data);
						});
					}

					if (handlers['*.*']) {
						_.each(handlers['*.*'], function(handler) {
							handler(data, data.data);
						});
					}
				}

				if (data.rabbitStateChange === 'open') {
					if (handlers['listening']) {
						_.each(handlers['listening'], function(handler) {
							handler();
						});
					}
				}
			}
		};
		client.onclose = function (e) {
			handlers = {};
		};
	}

	this.on = function(method, handler) {
		if (!clientStarted) {
			self.startClient();
		}
		handlers[method] = handlers[method] || [];
		handlers[method].push(handler);
	};

	this.removeListener = function(method, handler) {
		var index = handlers[method].indexOf(handler);
		if (index > -1) {
			handlers[method].splice(index, 1);
		}
	}

	this.removeAllListeners = function() {
		handlers = {};
	}

}
<|endoftext|>


<commit_before>"use strict";

var forEach = require('lodash/collection/forEach')
var util = require('../util')
var Observable = require('../observable')
var Operator = require('./')
var Results = require('./results')
var isEmpty = require('../util/isempty')

exports.$inject = require('./shared')

exports.$flags = {
  $sort: new Operator({
    $key: '$sort',
    $operator: function( val, operator, origin ) {

      if(typeof val === 'object') {

        var operated = this

        var results = operator.$makeResults( val, true, true )
        var params = operator.$parseValue( val, origin )

        var subsObj = new Observable()
        results.sort( params, subsObj )

        console.log('sorted, lets keep it up to date!', subsObj)
        if(val instanceof Observable) {
          val.$subscribe(subsObj, {
            $change: function(event, meta) {
              console.error('sort listener!!', this, event, meta)
              var moved = results.sort(params, null, event)
              console.log('hur haha moved!', moved)
              if(moved) {
                results.$emit('$operatorChain', event, moved)
              }
              //
            }
          })
          val.on('$operatorChain', function(event, delta){
            console.log('omg an operator before me is kicking me!')
          })
        }

        return results
      } else {
        console.error('how ya want me to sort', val, 'ya foo!')
      }

      return val
    }
  })
}
<commit_msg>rewrote isEmpty<commit_after>"use strict";

var forEach = require('lodash/collection/forEach')
var util = require('../util')
var Observable = require('../observable')
var Operator = require('./')
var Results = require('./results')
var isEmpty = util.isEmpty

exports.$inject = require('./shared')

exports.$flags = {
  $sort: new Operator({
    $key: '$sort',
    $operator: function( val, operator, origin ) {

      if(typeof val === 'object') {

        var operated = this

        var results = operator.$makeResults( val, true, true )
        var params = operator.$parseValue( val, origin )

        var subsObj = new Observable()
        results.sort( params, subsObj )

        console.log('sorted, lets keep it up to date!', subsObj)
        if(val instanceof Observable) {
          val.$subscribe(subsObj, {
            $change: function(event, meta) {
              console.error('sort listener!!', this, event, meta)
              var moved = results.sort(params, null, event)
              console.log('hur haha moved!', moved)
              if(moved) {
                results.$emit('$operatorChain', event, moved)
              }
              //
            }
          })
          val.on('$operatorChain', function(event, delta){
            console.log('omg an operator before me is kicking me!')
          })
        }

        return results
      } else {
        console.error('how ya want me to sort', val, 'ya foo!')
      }

      return val
    }
  })
}
<|endoftext|>


<commit_before>TouchUI.prototype.DOM.init = function() {

	// Create new tab with printer status and make it active
	this.DOM.create.printer.init(this.DOM.create.tabbar);
	this.DOM.create.printer.menu.$elm.find('a').trigger("click");

	// Create a new persistent dropdown
	this.DOM.create.dropdown.init.call( this.DOM.create.dropdown );

	// Move all other items from tabbar into dropdown
	this.DOM.move.tabbar.init.call(this);
	this.DOM.move.navbar.init.call(this);
	this.DOM.move.afterTabAndNav.call(this );
	this.DOM.move.overlays.init.call(this);
	this.DOM.move.terminal.init.call(this);

	// Move connection sidebar into a new modal
	this.DOM.move.connection.init(this.DOM.create.tabbar);

	// Manipulate controls div
	this.DOM.move.controls.init();

	// Disable these bootstrap/jquery plugins
	this.DOM.overwrite.tabdrop.call(self);
	this.DOM.overwrite.modal.call(self);

	// Add a webcam tab if it's defined
	if ($("#webcam_container").length > 0) {
		this.DOM.create.webcam.init(this.DOM.create.tabbar);
	}

	// Add class with how many tab-items
	$("#tabs, #navbar").addClass("items-" + $("#tabs li:not(.hidden_touch)").length);

	// Remove active class when clicking on a tab in the tabbar
	$('#tabs [data-toggle=tab]').on("click", function() {
		$("#all_touchui_settings").removeClass("item_active");
	});

}
<commit_msg>:art: Allow scrolling in dropdown in TouchEmulator<commit_after>TouchUI.prototype.DOM.init = function() {

	// Create new tab with printer status and make it active
	this.DOM.create.printer.init(this.DOM.create.tabbar);
	this.DOM.create.printer.menu.$elm.find('a').trigger("click");

	// Create a new persistent dropdown
	this.DOM.create.dropdown.init.call( this.DOM.create.dropdown );

	// Move all other items from tabbar into dropdown
	this.DOM.move.tabbar.init.call(this);
	this.DOM.move.navbar.init.call(this);
	this.DOM.move.afterTabAndNav.call(this );
	this.DOM.move.overlays.init.call(this);
	this.DOM.move.terminal.init.call(this);

	// Move connection sidebar into a new modal
	this.DOM.move.connection.init(this.DOM.create.tabbar);

	// Manipulate controls div
	this.DOM.move.controls.init();

	// Disable these bootstrap/jquery plugins
	this.DOM.overwrite.tabdrop.call(self);
	this.DOM.overwrite.modal.call(self);

	// Add a webcam tab if it's defined
	if ($("#webcam_container").length > 0) {
		this.DOM.create.webcam.init(this.DOM.create.tabbar);
	}

	// Add class with how many tab-items
	$("#tabs, #navbar").addClass("items-" + $("#tabs li:not(.hidden_touch)").length);

	// Remove active class when clicking on a tab in the tabbar
	$('#tabs [data-toggle=tab]').on("click", function() {
		$("#all_touchui_settings").removeClass("item_active");
	});

	// If touch emulator is enabled, then disable dragging of a menu item for scrolling
	if(!this.settings.hasTouch) {
		$("#navbar ul.nav > li a").on("dragstart drop", function(e) {
			return false;
		});
	}
}
<|endoftext|>