<commit_before>/* jshint node:true */
'use strict';

var events = require('events');
var util = require('util');
var extend = require('util')._extend;

var request = require('request');
var xml2js = require('xml2js');

var MessageChannel = require('./MessageChannel');

DeviceManager.appStates = {
  STARTING: 'starting',
  RUNNING: 'running',
  STOPPED: 'stopped'
};

DeviceManager.launchTypes = {
  LAUNCH: 'launch',
  RELAUNCH: 'relaunch',
  JOIN: 'join'
};

DeviceManager.systemControlTypes = {
  GET_VOLUME: 'GET_VOLUME',
  SET_VOLUME: 'SET_VOLUME',
  GET_MUTED: 'GET_MUTED',
  SET_MUTED: 'SET_MUTED'
};

function DeviceManager(device) {
  var self = this;

  events.EventEmitter.call(self);

  self.device = device;

  self.appID = null;
  self.appURL = null;

  self.appToken = null;
  self.appHeartbeatDelay = 0;

  self._heartbeatInterval = null;
  self._launchStateInterval = null;
}

util.inherits(DeviceManager, events.EventEmitter);

DeviceManager.prototype._clear = function () {
  var self = this;

  self.appToken = null;
  self.appHeartbeatDelay = 0;

  if (self._heartbeatInterval) {
    clearInterval(self._heartbeatInterval);
    self._heartbeatInterval = null;
  }
  if (self._launchStateInterval) {
    clearInterval(self._launchStateInterval);
    self._launchStateInterval = null;
  }
};

DeviceManager.prototype._setupHeartbeat = function () {
  var self = this;

  var heartbeatFunction = function () {
    var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
    var headers = {
      'Accept': 'application/xml; charset=utf8',
      'Authorization': self.appToken
    };
    request({uri: uri, method: 'GET', headers: headers}, function (err, res, body) {
      // 🎈
    });
  };

  self._heartbeatInterval = setInterval(function () {
    heartbeatFunction();
  }, self.appHeartbeatDelay);
  heartbeatFunction();
};

DeviceManager.prototype._setupLaunchStatePolling = function () {
  var self = this;

  var stateHandler = function (err, state, data) {
    if (err || !data.additionalData || !data.additionalData[0].channelBaseUrl) {
      return;
    }

    // clean up
    clearInterval(self._launchStateInterval);
    self._launchStateInterval = null;

    var url = data.additionalData[0].channelBaseUrl[0];
    var channel = new MessageChannel(url);
    // TODO - no callback pairing?
    self.emit('channel', channel);
  };

  self._launchStateInterval = setInterval(function () {
    self.getAppState(stateHandler);
  }, 750);
  self.getAppState(stateHandler);
};

DeviceManager.prototype.getAppState = function (cb) {
  var self = this;

  var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
  var headers = {
    'Accept': 'application/xml; charset=utf8'
  };
  if (self.appToken) {
    extend(headers, {'Authorization': self.appToken});
  }
  request({uri: uri, method: 'GET', headers: headers}, function (err, res, body) {
    var parser = new xml2js.Parser();
    parser.parseString(body, function (err, result) {
      if (err) {
        if (typeof cb === 'function') {
          cb(err);
        } else {
          self.emit('error', err);
        }
        return;
      }

      var state = result.service.state[0];

      if (typeof cb === 'function') {
        cb(null, state, result.service);
      } else {
        self.emit('state', state, result.service);
      }
    });
  });
};

DeviceManager.prototype.launchApp = function (appURL, options, cb) {
  var self = this;

  if (typeof options === 'function') {
    cb = options;
  }

  self._clear();
  self.appURL = appURL;

  options = extend({
    type: DeviceManager.launchTypes.LAUNCH,
    messageChannel: false, // needs WebSocket channel
    maxInactive: -1 // when useIpc is false, receiver running time in ms, -1 is forever
  }, options);

  var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
  var body = {
    type: options.type,
    app_info: {
      url: self.appURL,
      useIpc: options.messageChannel,
      maxInactive: options.maxInactive
    }
  };
  request({uri: uri, method: 'POST', body: body, json: true}, function (err, res, body) {
    if (err) {
      if (typeof cb === 'function') {
        cb(err);
      } else {
        self.emit('error', err);
      }
      return;
    }
    if (res.statusCode == 400) {
      var e = new Error(util.format('unable to %s', options.type));
      if (typeof cb === 'function') {
        cb(e);
      } else {
        self.emit('error', e);
      }
      return;
    }

    // NB: res.statusCode === 201 launched by request, === 200 already running
    self.appToken = body.token;
    self.appHeartbeatDelay = body.interval;

    if (options.messageChannel) {
      // ping-pong to keep the token alive
      self._setupHeartbeat();

      // poll app state for the message channel URL
      self._setupLaunchStatePolling();
    }

    if (typeof cb === 'function') {
      cb(null);
    } else {
      self.emit('launch');
    }
  });
};

DeviceManager.prototype.quitApp = function (cb) {
  var self = this;

  var instance = 'run';
  var uri = util.format('%s/%s/%s', self.device.applicationUrl, self.appID, instance);
  var headers = {
    'Authorization': self.appToken
  };
  request({uri: uri, method: 'DELETE', headers: headers}, function (err, res, body) {
    if (err) {
      if (typeof cb === 'function') {
        cb(err);
      } else {
        self.emit('error', err);
      }
      return;
    }

    // NB: res.statusCode === 400 stale appID === 404 app not found, === 200 good to go

    if (typeof cb === 'function') {
      cb(null);
    } else {
      self.emit('quit');
    }
  });

  self._clear();
};

DeviceManager.prototype.systemControl = function (type, value, cb) {
  var self = this;

  if (typeof value === 'function') {
    cb = value;
  }

  // really odd REST semantics https://github.com/openflint/flingd-coffee/issues/2
  var uri = util.format('%s/system/control', self.device.applicationUrl.replace(/\/apps$/, ''));
  var body = {
    type: type
  };
  if (type === DeviceManager.systemControlTypes.SET_VOLUME) {
    extend(body, {level: value});
  } else if (type === DeviceManager.systemControlTypes.SET_MUTED) {
    extend(body, {muted: value});
  }
  request({uri: uri, method: 'POST', body: body, json: true}, function (err, res, body) {
    if (err) {
      if (typeof cb === 'function') {
        cb(err);
      } else {
        self.emit('error', err);
      }
      return;
    }

    // only the GET_* requests return content https://github.com/openflint/flingd-coffee/issues/1
    var result = null;
    if (body && body.success) {
      if (body.request_type === DeviceManager.systemControlTypes.GET_VOLUME) {
          result = body.level;
      } else if (body.request_type === DeviceManager.systemControlTypes.GET_MUTED) {
        result = body.muted;
      }
    }

    cb(err, result);
  });
};

DeviceManager.prototype.getVolume = function (cb) {
  this.systemControl(DeviceManager.systemControlTypes.GET_VOLUME, cb);
};

DeviceManager.prototype.setVolume = function (value, cb) {
  this.systemControl(DeviceManager.systemControlTypes.SET_VOLUME, value, cb);
};

DeviceManager.prototype.getMuted = function (cb) {
  this.systemControl(DeviceManager.systemControlTypes.GET_MUTED, cb);
};

DeviceManager.prototype.setMuted = function (value, cb) {
  this.systemControl(DeviceManager.systemControlTypes.SET_MUTED, value, cb);
};

module.exports = DeviceManager;
<commit_msg>centralize the callback and emitter handler<commit_after>/* jshint node:true */
'use strict';

var events = require('events');
var util = require('util');
var extend = require('util')._extend;

var request = require('request');
var xml2js = require('xml2js');

var MessageChannel = require('./MessageChannel');

DeviceManager.appStates = {
  STARTING: 'starting',
  RUNNING: 'running',
  STOPPED: 'stopped'
};

DeviceManager.launchTypes = {
  LAUNCH: 'launch',
  RELAUNCH: 'relaunch',
  JOIN: 'join'
};

DeviceManager.systemControlTypes = {
  GET_VOLUME: 'GET_VOLUME',
  SET_VOLUME: 'SET_VOLUME',
  GET_MUTED: 'GET_MUTED',
  SET_MUTED: 'SET_MUTED'
};

function DeviceManager(device) {
  var self = this;

  events.EventEmitter.call(self);

  self.device = device;

  self.appID = null;
  self.appURL = null;

  self.appToken = null;
  self.appHeartbeatDelay = 0;

  self._heartbeatInterval = null;
  self._launchStateInterval = null;
}

util.inherits(DeviceManager, events.EventEmitter);

DeviceManager.prototype._clear = function () {
  var self = this;

  self.appToken = null;
  self.appHeartbeatDelay = 0;

  if (self._heartbeatInterval) {
    clearInterval(self._heartbeatInterval);
    self._heartbeatInterval = null;
  }
  if (self._launchStateInterval) {
    clearInterval(self._launchStateInterval);
    self._launchStateInterval = null;
  }
};

DeviceManager.prototype._callbackOrEmit = function (cb, event, err) {
  var self = this;

  var args = Array.prototype.slice.call(arguments);

  if (typeof cb === 'function') {
    args.splice(0, 2);
    cb.apply(self, args);
  } else {
    args.splice(0, 1);
    if (!err) {
      args.splice(1, 1);
    }
    self.emit.apply(self, args);
  }
};

DeviceManager.prototype._setupHeartbeat = function () {
  var self = this;

  var heartbeatFunction = function () {
    var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
    var headers = {
      'Accept': 'application/xml; charset=utf8',
      'Authorization': self.appToken
    };
    request({uri: uri, method: 'GET', headers: headers}, function (err, res, body) {
      // 🎈
    });
  };

  self._heartbeatInterval = setInterval(function () {
    heartbeatFunction();
  }, self.appHeartbeatDelay);
  heartbeatFunction();
};

DeviceManager.prototype._setupLaunchStatePolling = function () {
  var self = this;

  var stateHandler = function (err, state, data) {
    if (err || !data.additionalData || !data.additionalData[0].channelBaseUrl) {
      return;
    }

    // clean up
    clearInterval(self._launchStateInterval);
    self._launchStateInterval = null;

    var url = data.additionalData[0].channelBaseUrl[0];
    var channel = new MessageChannel(url);
    // TODO - no callback pairing?
    self.emit('channel', channel);
  };

  self._launchStateInterval = setInterval(function () {
    self.getAppState(stateHandler);
  }, 750);
  self.getAppState(stateHandler);
};

DeviceManager.prototype.getAppState = function (cb) {
  var self = this;

  var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
  var headers = {
    'Accept': 'application/xml; charset=utf8'
  };
  if (self.appToken) {
    extend(headers, {'Authorization': self.appToken});
  }
  request({uri: uri, method: 'GET', headers: headers}, function (err, res, body) {
    var parser = new xml2js.Parser();
    parser.parseString(body, function (err, result) {
      if (err) {
        self._callbackOrEmit(cb, 'error', err);
        return;
      }

      var state = result.service.state[0];

      self._callbackOrEmit(cb, 'state', null, state, result.service);
    });
  });
};

DeviceManager.prototype.launchApp = function (appURL, options, cb) {
  var self = this;

  if (typeof options === 'function') {
    cb = options;
  }

  self._clear();
  self.appURL = appURL;

  options = extend({
    type: DeviceManager.launchTypes.LAUNCH,
    messageChannel: false, // needs WebSocket channel
    maxInactive: -1 // when useIpc is false, receiver running time in ms, -1 is forever
  }, options);

  var uri = util.format('%s/%s', self.device.applicationUrl, self.appID);
  var body = {
    type: options.type,
    app_info: {
      url: self.appURL,
      useIpc: options.messageChannel,
      maxInactive: options.maxInactive
    }
  };
  request({uri: uri, method: 'POST', body: body, json: true}, function (err, res, body) {
    if (err) {
      self._callbackOrEmit(cb, 'error', err);
      return;
    }
    if (res.statusCode == 400) {
      var e = new Error(util.format('unable to %s', options.type));
      self._callbackOrEmit(cb, 'error', e);
      return;
    }

    // NB: res.statusCode === 201 launched by request, === 200 already running
    self.appToken = body.token;
    self.appHeartbeatDelay = body.interval;

    if (options.messageChannel) {
      // ping-pong to keep the token alive
      self._setupHeartbeat();

      // poll app state for the message channel URL
      self._setupLaunchStatePolling();
    }

    self._callbackOrEmit(cb, 'launch');
  });
};

DeviceManager.prototype.quitApp = function (cb) {
  var self = this;

  var instance = 'run';
  var uri = util.format('%s/%s/%s', self.device.applicationUrl, self.appID, instance);
  var headers = {
    'Authorization': self.appToken
  };
  request({uri: uri, method: 'DELETE', headers: headers}, function (err, res, body) {
    if (err) {
      self._callbackOrEmit(cb, 'error', err);
      return;
    }

    // NB: res.statusCode === 400 stale appID === 404 app not found, === 200 good to go

    self._callbackOrEmit(cb, 'quit');
  });

  self._clear();
};

DeviceManager.prototype.systemControl = function (type, value, cb) {
  var self = this;

  if (typeof value === 'function') {
    cb = value;
  }

  // really odd REST semantics https://github.com/openflint/flingd-coffee/issues/2
  var uri = util.format('%s/system/control', self.device.applicationUrl.replace(/\/apps$/, ''));
  var body = {
    type: type
  };
  if (type === DeviceManager.systemControlTypes.SET_VOLUME) {
    extend(body, {level: value});
  } else if (type === DeviceManager.systemControlTypes.SET_MUTED) {
    extend(body, {muted: value});
  }
  request({uri: uri, method: 'POST', body: body, json: true}, function (err, res, body) {
    if (err) {
      self._callbackOrEmit(cb, 'error', err);
      return;
    }

    // only the GET_* requests return content https://github.com/openflint/flingd-coffee/issues/1
    var result = null;
    if (body && body.success) {
      if (body.request_type === DeviceManager.systemControlTypes.GET_VOLUME) {
          result = body.level;
      } else if (body.request_type === DeviceManager.systemControlTypes.GET_MUTED) {
        result = body.muted;
      }
    }

    self._callbackOrEmit(cb, '???-EVENT', null, result);
  });
};

DeviceManager.prototype.getVolume = function (cb) {
  this.systemControl(DeviceManager.systemControlTypes.GET_VOLUME, cb);
};

DeviceManager.prototype.setVolume = function (value, cb) {
  this.systemControl(DeviceManager.systemControlTypes.SET_VOLUME, value, cb);
};

DeviceManager.prototype.getMuted = function (cb) {
  this.systemControl(DeviceManager.systemControlTypes.GET_MUTED, cb);
};

DeviceManager.prototype.setMuted = function (value, cb) {
  this.systemControl(DeviceManager.systemControlTypes.SET_MUTED, value, cb);
};

module.exports = DeviceManager;
<|endoftext|>


<commit_before>const path = require('path');
const Client = require('./Client');
const Log = require('../Util/Log');
const bot = new Client({
    name: 'Yappy, the GitLab Monitor',
    disableEveryone: true,
    disabledEvents: [
        'CHANNEL_UPDATE',
        'GUILD_UPDATE',
        'GUILD_BAN_ADD',
        'GUILD_BAN_REMOVE',
        'GUILD_EMOJIS_UPDATE',
        'GUILD_MEMBER_ADD',
        'GUILD_MEMBER_REMOVE',
        'GUILD_MEMBER_UPDATE',
        'GUILD_MEMBERS_CHUNK',
        'GUILD_ROLE_CREATE',
        'GUILD_ROLE_UPDATE',
        'GUILD_ROLE_DELETE',
        'MESSAGE_UPDATE',
        'MESSAGE_DELETE',
        'MESSAGE_DELETE_BULK',
        'PRESENCE_UPDATE',
        'TYPING_START',
        'USER_SETTINGS_UPDATE',
        'USER_UPDATE',
        'VOICE_STATE_UPDATE',
        'VOICE_SERVER_UPDATE',
    ],
    prefix: 'GL! ',
    owner: '175008284263186437',
    messageCacheMaxSize: 1,
    messageCacheLifetime: 300,
    messageSweepInterval: 300,
});
const logger = new (require('@YappyBots/addons').discord.logger)(bot, 'main');
const TOKEN = process.env.DISCORD_TOKEN;

const initialization = require('../Models/initialization');

bot.booted = {
    date: new Date().toLocaleDateString(),
    time: new Date().toLocaleTimeString(),
};
bot.statuses = ['Online', 'Connecting', 'Reconnecting', 'Idle', 'Nearly', 'Offline'];
bot.statusColors = ['lightgreen', 'orange', 'orange', 'orange', 'green', 'red'];

bot.on('ready', () => {
    Log.info('Bot | Logged In');
    logger.log('Logged in', null, 'GREEN');
    initialization(bot);
});
bot.on('disconnect', e => {
    Log.warn(`Bot | Disconnected (${e.code}).`);
    logger.log('Disconnected', e.code, 'ORANGE');
});
bot.on('error', e => {
    Log.error(e);
    logger.log(e.message || 'An error occurred', e.stack || e, 'RED');
});
bot.on('warn', e => {
    Log.warn(e);
    logger.log(e.message || 'Warning', e.stack || e, 'ORANGE');
});

bot.on('message', msg => {
    try {
        bot.run(msg);
    } catch (e) {
        bot.emit('error', e);
    }
});

bot.loadCommands(path.resolve(__dirname, 'Commands'));
bot.loadModules(path.resolve(__dirname, 'Modules'));

// === LOGIN ===
Log.info(`Bot | Logging in with prefix ${bot.prefix}...`);

bot.login(TOKEN).catch(err => {
    Log.error('Bot: Unable to log in');
    Log.error(err);
});

module.exports = bot;
<commit_msg>restart on disconnect<commit_after>const path = require('path');
const Client = require('./Client');
const Log = require('../Util/Log');
const bot = new Client({
    name: 'Yappy, the GitLab Monitor',
    disableEveryone: true,
    disabledEvents: [
        'CHANNEL_UPDATE',
        'GUILD_UPDATE',
        'GUILD_BAN_ADD',
        'GUILD_BAN_REMOVE',
        'GUILD_EMOJIS_UPDATE',
        'GUILD_MEMBER_ADD',
        'GUILD_MEMBER_REMOVE',
        'GUILD_MEMBER_UPDATE',
        'GUILD_MEMBERS_CHUNK',
        'GUILD_ROLE_CREATE',
        'GUILD_ROLE_UPDATE',
        'GUILD_ROLE_DELETE',
        'MESSAGE_UPDATE',
        'MESSAGE_DELETE',
        'MESSAGE_DELETE_BULK',
        'PRESENCE_UPDATE',
        'TYPING_START',
        'USER_SETTINGS_UPDATE',
        'USER_UPDATE',
        'VOICE_STATE_UPDATE',
        'VOICE_SERVER_UPDATE',
    ],
    prefix: 'GL! ',
    owner: '175008284263186437',
    messageCacheMaxSize: 1,
    messageCacheLifetime: 300,
    messageSweepInterval: 300,
});
const logger = new (require('@YappyBots/addons').discord.logger)(bot, 'main');
const TOKEN = process.env.DISCORD_TOKEN;

const initialization = require('../Models/initialization');

bot.booted = {
    date: new Date().toLocaleDateString(),
    time: new Date().toLocaleTimeString(),
};
bot.statuses = ['Online', 'Connecting', 'Reconnecting', 'Idle', 'Nearly', 'Offline'];
bot.statusColors = ['lightgreen', 'orange', 'orange', 'orange', 'green', 'red'];

bot.on('ready', () => {
    Log.info('Bot | Logged In');
    logger.log('Logged in', null, 'GREEN');
    initialization(bot);
});
bot.on('disconnect', e => {
    Log.warn(`Bot | Disconnected (${e.code}).`);
    logger.log('Disconnected', e.code, 'ORANGE');
    process.exit();
});
bot.on('error', e => {
    Log.error(e);
    logger.log(e.message || 'An error occurred', e.stack || e, 'RED');
});
bot.on('warn', e => {
    Log.warn(e);
    logger.log(e.message || 'Warning', e.stack || e, 'ORANGE');
});

bot.on('message', msg => {
    try {
        bot.run(msg);
    } catch (e) {
        bot.emit('error', e);
    }
});

bot.loadCommands(path.resolve(__dirname, 'Commands'));
bot.loadModules(path.resolve(__dirname, 'Modules'));

// === LOGIN ===
Log.info(`Bot | Logging in with prefix ${bot.prefix}...`);

bot.login(TOKEN).catch(err => {
    Log.error('Bot: Unable to log in');
    Log.error(err);
});

module.exports = bot;
<|endoftext|>


<commit_before>'use strict';

export default class LayoutManager {

  columns;
  horizontalMargin;
  verticalMargin;
  layoutWidth;
  itemHeight;
  itemWidth;
  rowHeight;

  constructor(options, width){
    this.update(options, width);
  }

  update(options, width){

    //Calculates layout
    this.layoutWidth = width;
    this.zoom = options.zoom;
    this.itemWidth = Math.round(options.itemWidth * this.zoom);
    this.itemHeight = Math.round(options.itemHeight * this.zoom);
    this.columns = Math.floor(this.layoutWidth / this.itemWidth);
    this.horizontalMargin = (this.columns === 1) ? 0 : Math.round(this.layoutWidth - (this.columns * this.itemWidth)) / (this.columns - 1);
    this.verticalMargin = (options.verticalMargin === -1) ? this.horizontalMargin : options.verticalMargin;
    this.rowHeight = this.itemHeight + this.verticalMargin;
  }

  getTotalHeight(filteredTotal){
    return (Math.ceil(filteredTotal / this.columns) * this.rowHeight) - this.verticalMargin;
  }

  getRow(index){
    return Math.floor(index / this.columns);
  }

  getColumn(index){
    return index - (this.getRow(index) * this.columns);
  }

  getPosition(index){
    var col = this.getColumn(index);
    var row = this.getRow(index);
    var margin = this.horizontalMargin;
    var width = this.itemWidth;

    return {
      x: Math.round((col * width) + (col * margin)),
      y: Math.round((this.itemHeight + this.verticalMargin) * row)
    };
  }

  getTransform(index){
    var position = this.getPosition(index);
    return 'translate3d(' + position.x + 'px, ' + position.y + 'px, 0)';
  }

  getStyle(index, animation, isFiltered){

    var style = {
      width: this.itemWidth + 'px',
      height: this.itemHeight + 'px',
      transform: this.getTransform(index),
      position: 'absolute',
      boxSizing: 'border-box',
      display: isFiltered ? 'none' : 'block'
    };

    if(animation){
      style.WebkitTransition = '-webkit-' + animation;
      style.MozTransition = '-moz-' + animation;
      style.msTransition = 'ms-' + animation;
      style.transition = animation;
    }

    return style;
  }
}
<commit_msg>Fix safari<commit_after>'use strict';

export default class LayoutManager {

  columns;
  horizontalMargin;
  verticalMargin;
  layoutWidth;
  itemHeight;
  itemWidth;
  rowHeight;

  constructor(options, width){
    this.update(options, width);
  }

  update(options, width){

    //Calculates layout
    this.layoutWidth = width;
    this.zoom = options.zoom;
    this.itemWidth = Math.round(options.itemWidth * this.zoom);
    this.itemHeight = Math.round(options.itemHeight * this.zoom);
    this.columns = Math.floor(this.layoutWidth / this.itemWidth);
    this.horizontalMargin = (this.columns === 1) ? 0 : Math.round(this.layoutWidth - (this.columns * this.itemWidth)) / (this.columns - 1);
    this.verticalMargin = (options.verticalMargin === -1) ? this.horizontalMargin : options.verticalMargin;
    this.rowHeight = this.itemHeight + this.verticalMargin;
  }

  getTotalHeight(filteredTotal){
    return (Math.ceil(filteredTotal / this.columns) * this.rowHeight) - this.verticalMargin;
  }

  getRow(index){
    return Math.floor(index / this.columns);
  }

  getColumn(index){
    return index - (this.getRow(index) * this.columns);
  }

  getPosition(index){
    var col = this.getColumn(index);
    var row = this.getRow(index);
    var margin = this.horizontalMargin;
    var width = this.itemWidth;

    return {
      x: Math.round((col * width) + (col * margin)),
      y: Math.round((this.itemHeight + this.verticalMargin) * row)
    };
  }

  getTransform(index){
    var position = this.getPosition(index);
    return 'translate3d(' + position.x + 'px, ' + position.y + 'px, 0)';
  }

  getStyle(index, animation, isFiltered){

    var transform = this.getTransform(index);
    var style = {
      width: this.itemWidth + 'px',
      height: this.itemHeight + 'px',
      WebkitTransform: transform,
      MozTransform: transform,
      msTransform: transform,
      transform: transform,
      position: 'absolute',
      boxSizing: 'border-box',
      display: isFiltered ? 'none' : 'block'
    };

    if(animation){
      style.WebkitTransition = '-webkit-' + animation;
      style.MozTransition = '-moz-' + animation;
      style.msTransition = 'ms-' + animation;
      style.transition = animation;
    }

    return style;
  }
}
<|endoftext|>


<commit_before>/**
 * 网络方法处理
 * @type {Object}
 */
var querystring = require("querystring");
var url = require("url");
var cookie = thinkRequire("Cookie");
var EventEmitter = require('events').EventEmitter;

module.exports = Class(function(){
    "use strict";
    var multiReg = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i;
    return {
        req: null,
        res: null,
        http: null,
        init: function(req, res){
            this.req = req;
            this.res = res;
            this.initHttp();
        },
        /**
         * 初始化http对象
         * @return {[type]} [description]
         */
        initHttp: function(){
            this.http = new EventEmitter();
            this.http.startTime = Date.now();
        },
        run: function(callback){
            this._request();
            this._response();
            var method = this.req.method.toLowerCase();
            var methods = ["post", "put", "patch"];
            if (methods.indexOf(method) > -1) {
                return this.getPostData(callback);
            }
            return callback && callback(this.http);
        },
        /**
         * 检测含有post数据
         * @return {Boolean} [description]
         */
        hasPostData: function(){
            var encoding = 'transfer-encoding' in this.req.headers;
            var length = ('content-length' in this.req.headers) && this.req.headers['content-length'] !== '0';
            return encoding || length;
        },
        /**
         * 获取POST过来的数据，包含上传的文件
         * 依赖multiparty库
         * @param  {Function} callback [description]
         * @return {[type]}            [description]
         */
        getPostData: function(callback){
            //没有post数据，直接回调
            if (!this.hasPostData()) {
                return callback && callback(this.http);
            }
            var self = this;
            var contentType = this.req.headers["content-type"] || "";
            var deferred = getDefer();
            //异步获取post数据
            var postDataAsync = C('post_data_async');
            //表单数据提交
            if (multiReg.test(contentType)) {
                var multiparty = require("multiparty");
                var form = new multiparty.Form();
                form.on("file", function(name, value){
                    self.http.file[name] = value;
                });
                form.on("field", function(name, value){
                    self.http.post[name] = value;
                });
                form.on("close", function(){
                    if (postDataAsync) {
                        return deferred.resolve(self.http.post);
                    }
                    return callback && callback(self.http);
                });
                form.parse(this.req);
            }else{
                var buffer = "";
                this.req.setEncoding(C('encoding'));
                this.req.on("data", function(chunk){
                    buffer += chunk;
                });
                this.req.on("end", function(){
                    //json数据格式
                    var jsonConentType = C('post_json_content_type');
                    if (!isArray(jsonConentType)) {
                        jsonConentType = [jsonConentType];
                    }
                    if (jsonConentType.indexOf(contentType) > -1) {
                        self.http.post = JSON.parse(buffer) || {};
                    }else{
                        self.http.post = querystring.parse(buffer) || {};
                    }
                    //请求内容
                    self.http.payload = buffer;
                    if (postDataAsync) {
                        return deferred.resolve(self.http.post);
                    }
                    return callback && callback(self.http);
                });
            }
            if (postDataAsync) {
                self.http.postPromise = deferred.promise;
                return callback && callback(self.http);
            }
        },
        _request: function(){
            var req = {
                version: this.req.httpVersion,
                method: this.req.method.toLowerCase(),
                headers: this.req.headers,
                getHeader: function(name){
                    if (name === 'referer') {
                        name = 'referrer';
                    }
                    return this.headers[name] || "";
                },
                post: {},
                file: {},
                ip: function(){
                    var ip = this.req.connection.remoteAddress || this.req.socket.remoteAddress;
                    if (ip && ip !== "127.0.0.1") {
                        return ip;
                    }
                    return this.headers["x-forwarded-for"] || this.headers["x-real-ip"];
                },
                cookie: cookie.parse(this.req.headers.cookie || "")
            };
            extend(this.http, req);

            //解析url中的参数
            var urlInfo = url.parse("//" + req.headers.host + this.req.url, true, true);
            this.http.pathname = urlInfo.pathname;
            //query只记录?后面的参数
            this.http.query = urlInfo.query;
            //get包含路由解析追加的参数
            this.http.get = extend({}, urlInfo.query);
            //主机名，带端口
            this.http.host = urlInfo.host;
            //主机名，不带端口
            this.http.hostname = urlInfo.hostname;

            this.http.req = this.req;
        },
        _response: function(){
            var res = {
                //需要发送的cookie
                _cookie: {},
                /**
                 * 发送header
                 * @param {[type]} name  [description]
                 * @param {[type]} value [description]
                 */
                setHeader: function(name, value){
                    this.res.setHeader(name, value);
                },
                /**
                 * 设置cookie
                 * @param {[type]} name    [description]
                 * @param {[type]} value   [description]
                 * @param {[type]} options [description]
                 */
                setCookie: function(name, value, options){
                    options = options || {};
                    if (typeof options === 'number') {
                        options = {expires: options};
                    }
                    var expires = options.expires;
                    if (expires === undefined) {
                        expires = C('cookie_expires');
                    }
                    delete options.expires;
                    //if value is null, remove cookie
                    if (value === null) {
                        expires = -1000;
                    }
                    var defaultOptions = {
                        path: "/",
                        expires: new Date (Date.now() + expires)
                    };
                    if (expires === 0) {
                        delete defaultOptions.expires;
                    }
                    options = extend(defaultOptions, options);
                    value = cookie.stringify(name, value, options);
                    this._cookie[name] = value;
                },
                /**
                 * 将队列中的cookie发送出去
                 * @return {[type]} [description]
                 */
                sendCookie: function(){
                    var cookies = Object.values(this._cookie);
                    if (cookies.length) {
                        this.setHeader("Set-Cookie", cookies);
                        this._cookie = {};
                    }
                },
                /**
                 * url跳转
                 * @param  {[type]} url  [description]
                 * @param  {[type]} code [description]
                 * @return {[type]}      [description]
                 */
                redirect: function(url, code){
                    this.res.statusCode = code || 302;
                    this.res.setHeader("Location", url);
                    this.end();
                },
                /**
                 * 发送执行时间
                 * @param  {[type]} name [description]
                 * @return {[type]}      [description]
                 */
                sendTime: function(name){
                    var time = Date.now() - this.startTime;
                    this.setHeader("X-" + name, time + "ms");
                },
                /**
                 * 输出内容
                 * @param  {[type]} obj      [description]
                 * @param  {[type]} encoding [description]
                 * @return {[type]}          [description]
                 */
                echo: function(obj, encoding){
                    this.sendCookie();
                    if (isArray(obj) || isObject(obj)) {
                        obj = JSON.stringify(obj);
                    }
                    if (!isString(obj) && !(obj instanceof Buffer)) {
                        obj += "";
                    }
                    this.res.write(obj, encoding || C('encoding'));
                },
                /**
                 * 结束URL
                 * @return {[type]} [description]
                 */
                end: function(){
                    this.emit("beforeEnd", this);
                    this.sendCookie();
                    this.res.end();
                    this.emit("afterEnd", this);
                }
            };
            extend(this.http, res);
            this.http.res = this.res;
        }
    };
});
/**
 * 获取默认的http信息
 * @param  {[type]} data [description]
 * @return {[type]}      [description]
 */
module.exports.getDefaultHttp = function(data){
    "use strict";
    data = data || {};
    if (isString(data)) {
        data = data.indexOf('=') > -1 ? querystring.parse(data) : {url: data};
    }
    var fn = function(){
        return "";
    };
    var url = data.url || "";
    if (url.indexOf("/") !== 0) {
        url = '/' + url;
    }
    return {
        req: {
            httpVersion: "1.1",
            method: data.method || "GET",
            url: url,
            headers: extend({
                host: data.host || "127.0.0.1"
            }, data.headers || {}),
            connection: {
                remoteAddress: data.ip || "127.0.0.1"
            }
        },
        res: {
            end: data.end || fn,
            write: data.write || fn,
            setHeader: fn
        }
    };
};<commit_msg>修复设置cookie单位为秒的bug<commit_after>/**
 * 网络方法处理
 * @type {Object}
 */
var querystring = require("querystring");
var url = require("url");
var cookie = thinkRequire("Cookie");
var EventEmitter = require('events').EventEmitter;

module.exports = Class(function(){
    "use strict";
    var multiReg = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i;
    return {
        req: null,
        res: null,
        http: null,
        init: function(req, res){
            this.req = req;
            this.res = res;
            this.initHttp();
        },
        /**
         * 初始化http对象
         * @return {[type]} [description]
         */
        initHttp: function(){
            this.http = new EventEmitter();
            this.http.startTime = Date.now();
        },
        run: function(callback){
            this._request();
            this._response();
            var method = this.req.method.toLowerCase();
            var methods = ["post", "put", "patch"];
            if (methods.indexOf(method) > -1) {
                return this.getPostData(callback);
            }
            return callback && callback(this.http);
        },
        /**
         * 检测含有post数据
         * @return {Boolean} [description]
         */
        hasPostData: function(){
            var encoding = 'transfer-encoding' in this.req.headers;
            var length = ('content-length' in this.req.headers) && this.req.headers['content-length'] !== '0';
            return encoding || length;
        },
        /**
         * 获取POST过来的数据，包含上传的文件
         * 依赖multiparty库
         * @param  {Function} callback [description]
         * @return {[type]}            [description]
         */
        getPostData: function(callback){
            //没有post数据，直接回调
            if (!this.hasPostData()) {
                return callback && callback(this.http);
            }
            var self = this;
            var contentType = this.req.headers["content-type"] || "";
            var deferred = getDefer();
            //异步获取post数据
            var postDataAsync = C('post_data_async');
            //表单数据提交
            if (multiReg.test(contentType)) {
                var multiparty = require("multiparty");
                var form = new multiparty.Form();
                form.on("file", function(name, value){
                    self.http.file[name] = value;
                });
                form.on("field", function(name, value){
                    self.http.post[name] = value;
                });
                form.on("close", function(){
                    if (postDataAsync) {
                        return deferred.resolve(self.http.post);
                    }
                    return callback && callback(self.http);
                });
                form.parse(this.req);
            }else{
                var buffer = "";
                this.req.setEncoding(C('encoding'));
                this.req.on("data", function(chunk){
                    buffer += chunk;
                });
                this.req.on("end", function(){
                    //json数据格式
                    var jsonConentType = C('post_json_content_type');
                    if (!isArray(jsonConentType)) {
                        jsonConentType = [jsonConentType];
                    }
                    if (jsonConentType.indexOf(contentType) > -1) {
                        self.http.post = JSON.parse(buffer) || {};
                    }else{
                        self.http.post = querystring.parse(buffer) || {};
                    }
                    //请求内容
                    self.http.payload = buffer;
                    if (postDataAsync) {
                        return deferred.resolve(self.http.post);
                    }
                    return callback && callback(self.http);
                });
            }
            if (postDataAsync) {
                self.http.postPromise = deferred.promise;
                return callback && callback(self.http);
            }
        },
        _request: function(){
            var req = {
                version: this.req.httpVersion,
                method: this.req.method.toLowerCase(),
                headers: this.req.headers,
                getHeader: function(name){
                    if (name === 'referer') {
                        name = 'referrer';
                    }
                    return this.headers[name] || "";
                },
                post: {},
                file: {},
                ip: function(){
                    var ip = this.req.connection.remoteAddress || this.req.socket.remoteAddress;
                    if (ip && ip !== "127.0.0.1") {
                        return ip;
                    }
                    return this.headers["x-forwarded-for"] || this.headers["x-real-ip"];
                },
                cookie: cookie.parse(this.req.headers.cookie || "")
            };
            extend(this.http, req);

            //解析url中的参数
            var urlInfo = url.parse("//" + req.headers.host + this.req.url, true, true);
            this.http.pathname = urlInfo.pathname;
            //query只记录?后面的参数
            this.http.query = urlInfo.query;
            //get包含路由解析追加的参数
            this.http.get = extend({}, urlInfo.query);
            //主机名，带端口
            this.http.host = urlInfo.host;
            //主机名，不带端口
            this.http.hostname = urlInfo.hostname;

            this.http.req = this.req;
        },
        _response: function(){
            var res = {
                //需要发送的cookie
                _cookie: {},
                /**
                 * 发送header
                 * @param {[type]} name  [description]
                 * @param {[type]} value [description]
                 */
                setHeader: function(name, value){
                    this.res.setHeader(name, value);
                },
                /**
                 * 设置cookie
                 * @param {[type]} name    [description]
                 * @param {[type]} value   [description]
                 * @param {[type]} options [description]
                 */
                setCookie: function(name, value, options){
                    options = options || {};
                    if (typeof options === 'number') {
                        options = {timeout: options};
                    }
                    var timeout = options.timeout;
                    if (timeout === undefined) {
                        timeout = C('cookie_timeout');
                    }
                    delete options.timeout;
                    //if value is null, remove cookie
                    if (value === null) {
                        timeout = -1000;
                    }
                    var defaultOptions = {
                        path: C('cookie_path'),
                        domain: C('cookie_domain'),
                        expires: new Date (Date.now() + timeout * 1000)
                    };
                    if (timeout === 0) {
                        delete defaultOptions.expires;
                    }
                    options = extend(defaultOptions, options);
                    value = cookie.stringify(name, value, options);
                    this._cookie[name] = value;
                },
                /**
                 * 将队列中的cookie发送出去
                 * @return {[type]} [description]
                 */
                sendCookie: function(){
                    var cookies = Object.values(this._cookie);
                    if (cookies.length) {
                        this.setHeader("Set-Cookie", cookies);
                        this._cookie = {};
                    }
                },
                /**
                 * url跳转
                 * @param  {[type]} url  [description]
                 * @param  {[type]} code [description]
                 * @return {[type]}      [description]
                 */
                redirect: function(url, code){
                    this.res.statusCode = code || 302;
                    this.res.setHeader("Location", url);
                    this.end();
                },
                /**
                 * 发送执行时间
                 * @param  {[type]} name [description]
                 * @return {[type]}      [description]
                 */
                sendTime: function(name){
                    var time = Date.now() - this.startTime;
                    this.setHeader("X-" + name, time + "ms");
                },
                /**
                 * 输出内容
                 * @param  {[type]} obj      [description]
                 * @param  {[type]} encoding [description]
                 * @return {[type]}          [description]
                 */
                echo: function(obj, encoding){
                    this.sendCookie();
                    if (isArray(obj) || isObject(obj)) {
                        obj = JSON.stringify(obj);
                    }
                    if (!isString(obj) && !(obj instanceof Buffer)) {
                        obj += "";
                    }
                    this.res.write(obj, encoding || C('encoding'));
                },
                /**
                 * 结束URL
                 * @return {[type]} [description]
                 */
                end: function(){
                    this.emit("beforeEnd", this);
                    this.sendCookie();
                    this.res.end();
                    this.emit("afterEnd", this);
                }
            };
            extend(this.http, res);
            this.http.res = this.res;
        }
    };
});
/**
 * 获取默认的http信息
 * @param  {[type]} data [description]
 * @return {[type]}      [description]
 */
module.exports.getDefaultHttp = function(data){
    "use strict";
    data = data || {};
    if (isString(data)) {
        data = data.indexOf('=') > -1 ? querystring.parse(data) : {url: data};
    }
    var fn = function(){
        return "";
    };
    var url = data.url || "";
    if (url.indexOf("/") !== 0) {
        url = '/' + url;
    }
    return {
        req: {
            httpVersion: "1.1",
            method: data.method || "GET",
            url: url,
            headers: extend({
                host: data.host || "127.0.0.1"
            }, data.headers || {}),
            connection: {
                remoteAddress: data.ip || "127.0.0.1"
            }
        },
        res: {
            end: data.end || fn,
            write: data.write || fn,
            setHeader: fn
        }
    };
};<|endoftext|>


<commit_before>/* mousetrap v1.6.2 craig.is/killing/mice */
(function(p,t,h){function u(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function y(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return m[a.which]?m[a.which]:q[a.which]?q[a.which]:String.fromCharCode(a.which).toLowerCase()}function E(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function v(a){return"shift"==a||"ctrl"==a||"alt"==a||
"meta"==a}function z(a,b){var d,e=[];var c=a;"+"===c?c=["+"]:(c=c.replace(/\+{2}/g,"+plus"),c=c.split("+"));for(d=0;d<c.length;++d){var k=c[d];A[k]&&(k=A[k]);b&&"keypress"!=b&&B[k]&&(k=B[k],e.push("shift"));v(k)&&e.push(k)}c=k;d=b;if(!d){if(!n){n={};for(var h in m)95<h&&112>h||m.hasOwnProperty(h)&&(n[m[h]]=h)}d=n[c]?"keydown":"keypress"}"keypress"==d&&e.length&&(d="keydown");return{key:k,modifiers:e,action:d}}function C(a,b){return null===a||a===t?!1:a===b?!0:C(a.parentNode,b)}function e(a){function b(a){a=
a||{};var b=!1,l;for(l in n)a[l]?b=!0:n[l]=0;b||(w=!1)}function d(a,b,r,g,F,e){var l,D=[],h=r.type;if(!f._callbacks[a])return[];"keyup"==h&&v(a)&&(b=[a]);for(l=0;l<f._callbacks[a].length;++l){var d=f._callbacks[a][l];if((g||!d.seq||n[d.seq]==d.level)&&h==d.action){var c;(c="keypress"==h&&!r.metaKey&&!r.ctrlKey)||(c=d.modifiers,c=b.sort().join(",")===c.sort().join(","));c&&(c=g&&d.seq==g&&d.level==e,(!g&&d.combo==F||c)&&f._callbacks[a].splice(l,1),D.push(d))}}return D}function h(a,b,d,g){f.stopCallback(b,
b.target||b.srcElement,d,g)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function c(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=y(a);b&&("keyup"==a.type&&x===b?x=!1:f.handleKey(b,E(a),a))}function k(a,d,r,g){function l(d){return function(){w=d;++n[a];clearTimeout(p);p=setTimeout(b,1E3)}}function e(d){h(r,d,a);"keyup"!==g&&(x=y(d));setTimeout(b,10)}for(var c=n[a]=0;c<d.length;++c){var f=c+1===d.length?e:l(g||
z(d[c+1]).action);m(d[c],f,g,a,c)}}function m(a,b,c,g,e){f._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var h=a.split(" ");1<h.length?k(a,h,b,c):(c=z(a,c),f._callbacks[c.key]=f._callbacks[c.key]||[],d(c.key,c.modifiers,{type:c.action},g,a,e),f._callbacks[c.key][g?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:g,level:e,combo:a}))}var f=this;a=a||t;if(!(f instanceof e))return new e(a);f.target=a;f._callbacks={};f._directMap={};var n={},p,x=!1,q=!1,w=!1;f._handleKey=function(a,
c,e){var g=d(a,c,e),f;c={};var l=0,k=!1;for(f=0;f<g.length;++f)g[f].seq&&(l=Math.max(l,g[f].level));for(f=0;f<g.length;++f)g[f].seq?g[f].level==l&&(k=!0,c[g[f].seq]=1,h(g[f].callback,e,g[f].combo,g[f].seq)):k||h(g[f].callback,e,g[f].combo);g="keypress"==e.type&&q;e.type!=w||v(a)||g||b(c);q=k&&"keydown"==e.type};f._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)m(a[d],b,c)};u(a,"keypress",c);u(a,"keydown",c);u(a,"keyup",c)}if(p){var m={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},q={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},B={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},A={option:"alt",command:"meta","return":"enter",
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(h=1;20>h;++h)m[111+h]="f"+h;for(h=0;9>=h;++h)m[h+96]=h.toString();e.prototype.bind=function(a,b,d){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,d);return this};e.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};e.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};e.prototype.reset=function(){this._callbacks={};
this._directMap={};return this};e.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||C(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};e.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};e.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(m[b]=a[b]);n=null};e.init=function(){var a=e(t),b;for(b in a)"_"!==b.charAt(0)&&(e[b]=function(b){return function(){return a[b].apply(a,
arguments)}}(b))};e.init();p.Mousetrap=e;"undefined"!==typeof module&&module.exports&&(module.exports=e);"function"===typeof define&&define.amd&&define(function(){return e})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
<commit_msg>Updated mousetrap.js to v6.1.5<commit_after>/* mousetrap v1.6.5 craig.is/killing/mice */
(function(q,u,c){function v(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function z(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return n[a.which]?n[a.which]:r[a.which]?r[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||
"meta"==a}function A(a,b){var g,d=[];var e=a;"+"===e?e=["+"]:(e=e.replace(/\+{2}/g,"+plus"),e=e.split("+"));for(g=0;g<e.length;++g){var m=e[g];B[m]&&(m=B[m]);b&&"keypress"!=b&&C[m]&&(m=C[m],d.push("shift"));w(m)&&d.push(m)}e=m;g=b;if(!g){if(!p){p={};for(var c in n)95<c&&112>c||n.hasOwnProperty(c)&&(p[n[c]]=c)}g=p[e]?"keydown":"keypress"}"keypress"==g&&d.length&&(g="keydown");return{key:m,modifiers:d,action:g}}function D(a,b){return null===a||a===u?!1:a===b?!0:D(a.parentNode,b)}function d(a){function b(a){a=
a||{};var b=!1,l;for(l in p)a[l]?b=!0:p[l]=0;b||(x=!1)}function g(a,b,t,f,g,d){var l,E=[],h=t.type;if(!k._callbacks[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(l=0;l<k._callbacks[a].length;++l){var c=k._callbacks[a][l];if((f||!c.seq||p[c.seq]==c.level)&&h==c.action){var e;(e="keypress"==h&&!t.metaKey&&!t.ctrlKey)||(e=c.modifiers,e=b.sort().join(",")===e.sort().join(","));e&&(e=f&&c.seq==f&&c.level==d,(!f&&c.combo==g||e)&&k._callbacks[a].splice(l,1),E.push(c))}}return E}function c(a,b,c,f){k.stopCallback(b,
b.target||b.srcElement,c,f)||!1!==a(b,c)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function e(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=z(a);b&&("keyup"==a.type&&y===b?y=!1:k.handleKey(b,F(a),a))}function m(a,g,t,f){function h(c){return function(){x=c;++p[a];clearTimeout(q);q=setTimeout(b,1E3)}}function l(g){c(t,g,a);"keyup"!==f&&(y=z(g));setTimeout(b,10)}for(var d=p[a]=0;d<g.length;++d){var e=d+1===g.length?l:h(f||
A(g[d+1]).action);n(g[d],e,f,a,d)}}function n(a,b,c,f,d){k._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var e=a.split(" ");1<e.length?m(a,e,b,c):(c=A(a,c),k._callbacks[c.key]=k._callbacks[c.key]||[],g(c.key,c.modifiers,{type:c.action},f,a,d),k._callbacks[c.key][f?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:f,level:d,combo:a}))}var k=this;a=a||u;if(!(k instanceof d))return new d(a);k.target=a;k._callbacks={};k._directMap={};var p={},q,y=!1,r=!1,x=!1;k._handleKey=function(a,
d,e){var f=g(a,d,e),h;d={};var k=0,l=!1;for(h=0;h<f.length;++h)f[h].seq&&(k=Math.max(k,f[h].level));for(h=0;h<f.length;++h)f[h].seq?f[h].level==k&&(l=!0,d[f[h].seq]=1,c(f[h].callback,e,f[h].combo,f[h].seq)):l||c(f[h].callback,e,f[h].combo);f="keypress"==e.type&&r;e.type!=x||w(a)||f||b(d);r=l&&"keydown"==e.type};k._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)n(a[d],b,c)};v(a,"keypress",e);v(a,"keydown",e);v(a,"keyup",e)}if(q){var n={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},r={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},C={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},B={option:"alt",command:"meta","return":"enter",
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p;for(c=1;20>c;++c)n[111+c]="f"+c;for(c=0;9>=c;++c)n[c+96]=c.toString();d.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};d.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};d.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};d.prototype.reset=function(){this._callbacks={};
this._directMap={};return this};d.prototype.stopCallback=function(a,b){if(-1<(" "+b.className+" ").indexOf(" mousetrap ")||D(b,this.target))return!1;if("composedPath"in a&&"function"===typeof a.composedPath){var c=a.composedPath()[0];c!==a.target&&(b=c)}return"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};d.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};d.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(n[b]=a[b]);p=null};
d.init=function(){var a=d(u),b;for(b in a)"_"!==b.charAt(0)&&(d[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};d.init();q.Mousetrap=d;"undefined"!==typeof module&&module.exports&&(module.exports=d);"function"===typeof define&&define.amd&&define(function(){return d})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
<|endoftext|>


<commit_before>var request = require('request');

function noop() {}

module.exports = function(bot, config) {
    var slack_api = {
        api_url: 'https://slack.com/api/'
    };

    // Slack API methods: https://api.slack.com/methods
    var slackApiMethods = [
        'auth.test',
        'oauth.access',
        'channels.archive',
        'channels.create',
        'channels.history',
        'channels.info',
        'channels.invite',
        'channels.join',
        'channels.kick',
        'channels.leave',
        'channels.list',
        'channels.mark',
        'channels.rename',
        'channels.setPurpose',
        'channels.setTopic',
        'channels.unarchive',
        'chat.delete',
        'chat.postMessage',
        'chat.update',
        'emoji.list',
        'files.delete',
        'files.info',
        'files.list',
        'files.upload',
        'groups.archive',
        'groups.close',
        'groups.create',
        'groups.createChild',
        'groups.history',
        'groups.info',
        'groups.invite',
        'groups.kick',
        'groups.leave',
        'groups.list',
        'groups.mark',
        'groups.open',
        'groups.rename',
        'groups.setPurpose',
        'groups.setTopic',
        'groups.unarchive',
        'im.close',
        'im.history',
        'im.list',
        'im.mark',
        'im.open',
        'mpim.close',
        'mpim.history',
        'mpim.list',
        'mpim.mark',
        'mpim.open',
        'pins.add',
        'pins.list',
        'pins.remove',
        'reactions.add',
        'reactions.get',
        'reactions.list',
        'reactions.remove',
        'rtm.start',
        'search.all',
        'search.files',
        'search.messages',
        'stars.list',
        'team.accessLogs',
        'team.info',
        'users.getPresence',
        'users.info',
        'users.list',
        'users.setActive'
    ];

    slack_api.callAPI = function(command, options, cb) {
        cb = cb || noop;

        bot.log('** API CALL: ' + slack_api.api_url + command);
        if (!options.token) {
            options.token = config.token;
        }

        bot.debug(command, options);
        request.post(slack_api.api_url + command, function(error, response, body) {
            bot.debug('Got response', error, body);
            if (!error && response.statusCode == 200) {
                var json;
                try {
                    json = JSON.parse(body);
                } catch (err) {
                    return cb(err);
                }

                if (json.ok) {
                    return cb(null, json);
                }

                return cb(json.error, json);
            }

            return cb(error || 'Invalid response');
        }).form(options);
    };

    slack_api.callAPIWithoutToken = function(command, options, cb) {
        cb = cb || noop;

        bot.log('** API CALL: ' + slack_api.api_url + command);
        if (!options.client_id) {
            options.client_id = bot.config.clientId;
        }
        if (!options.client_secret) {
            options.client_secret = bot.config.clientSecret;
        }
        if (!options.redirect_uri) {
            options.redirect_uri = bot.config.redirectUri;
        }
        request.post(slack_api.api_url + command, function(error, response, body) {
            bot.debug('Got response', error, body);
            if (!error && response.statusCode == 200) {
                var json;
                try {
                    json = JSON.parse(body);
                } catch (err) {
                    return cb(err || 'Invalid JSON');
                }

                if (json.ok) {
                    return cb(null, json);
                }
                return cb(json.error, json);
            }
            return cb(error || 'Invalid response');
        }).form(options);
    };

    // generate all API methods
    slackApiMethods.forEach(function(slackMethod) {
        // most slack api methods are only two parts, of the form group.method, e.g. auth.test
        // some have three parts: group.subgroup.method, e.g, users.profile.get
        // this method loops through all groups in a method, ensures they exist,
        // then adds the method to the terminal group

        var groups = slackMethod.split('.');
        var method = groups.pop();
        var currentGroup = slack_api;
        var nextGroupName;

        for (var i = 0; i < groups.length; i++) {
            nextGroupName = groups[i];
            if (!currentGroup[nextGroupName]) {
                currentGroup[nextGroupName] = {};
            }
            currentGroup = currentGroup[nextGroupName];
        }

        currentGroup[method] = function(options, cb) {
            slack_api.callAPI(slackMethod, options, cb);
        };

    });

    // overwrite default behavior
    slack_api.chat.postMessage = function(options, cb) {
        if (options.attachments && typeof(options.attachments) != 'string') {
            options.attachments = JSON.stringify(options.attachments);
        }
        slack_api.callAPI('chat.postMessage', options, cb);
    };

    return slack_api;
};
<commit_msg>removes code duplication for callAPI and callAPIWithoutToken, improves error handling<commit_after>var request = require('request');

function noop() {}

module.exports = function(bot, config) {
    var slack_api = {
        api_url: 'https://slack.com/api/'
    };

    // Slack API methods: https://api.slack.com/methods
    var slackApiMethods = [
        'auth.test',
        'oauth.access',
        'channels.archive',
        'channels.create',
        'channels.history',
        'channels.info',
        'channels.invite',
        'channels.join',
        'channels.kick',
        'channels.leave',
        'channels.list',
        'channels.mark',
        'channels.rename',
        'channels.setPurpose',
        'channels.setTopic',
        'channels.unarchive',
        'chat.delete',
        'chat.postMessage',
        'chat.update',
        'emoji.list',
        'files.delete',
        'files.info',
        'files.list',
        'files.upload',
        'groups.archive',
        'groups.close',
        'groups.create',
        'groups.createChild',
        'groups.history',
        'groups.info',
        'groups.invite',
        'groups.kick',
        'groups.leave',
        'groups.list',
        'groups.mark',
        'groups.open',
        'groups.rename',
        'groups.setPurpose',
        'groups.setTopic',
        'groups.unarchive',
        'im.close',
        'im.history',
        'im.list',
        'im.mark',
        'im.open',
        'mpim.close',
        'mpim.history',
        'mpim.list',
        'mpim.mark',
        'mpim.open',
        'pins.add',
        'pins.list',
        'pins.remove',
        'reactions.add',
        'reactions.get',
        'reactions.list',
        'reactions.remove',
        'rtm.start',
        'search.all',
        'search.files',
        'search.messages',
        'stars.list',
        'team.accessLogs',
        'team.info',
        'users.getPresence',
        'users.info',
        'users.list',
        'users.setActive'
    ];

    slack_api.callAPI = function(command, options, cb) {
        if (!options.token) {
            options.token = config.token;
        }

        bot.debug(command, options);
        postForm(slack_api.api_url + command, options, cb);
    };

    slack_api.callAPIWithoutToken = function(command, options, cb) {
        if (!options.client_id) {
            options.client_id = bot.config.clientId;
        }
        if (!options.client_secret) {
            options.client_secret = bot.config.clientSecret;
        }
        if (!options.redirect_uri) {
            options.redirect_uri = bot.config.redirectUri;
        }

        // DON'T log options: that could expose the client secret!
        postForm(slack_api.api_url + command, options, cb);
    };

    // generate all API methods
    slackApiMethods.forEach(function(slackMethod) {
        // most slack api methods are only two parts, of the form group.method, e.g. auth.test
        // some have three parts: group.subgroup.method, e.g, users.profile.get
        // this method loops through all groups in a method, ensures they exist,
        // then adds the method to the terminal group

        var groups = slackMethod.split('.');
        var method = groups.pop();
        var currentGroup = slack_api;
        var nextGroupName;

        for (var i = 0; i < groups.length; i++) {
            nextGroupName = groups[i];
            if (!currentGroup[nextGroupName]) {
                currentGroup[nextGroupName] = {};
            }
            currentGroup = currentGroup[nextGroupName];
        }

        currentGroup[method] = function(options, cb) {
            slack_api.callAPI(slackMethod, options, cb);
        };

    });

    // overwrite default behavior
    slack_api.chat.postMessage = function(options, cb) {
        if (options.attachments && typeof(options.attachments) != 'string') {
            options.attachments = JSON.stringify(options.attachments);
        }
        slack_api.callAPI('chat.postMessage', options, cb);
    };

    return slack_api;

    /**
     * Makes a POST request as a form to the given url with the options as data
     * @param url
     * @param options
     * @param cb
     */
    function postForm(url, options, cb) {
        cb = cb || noop;

        bot.log('** API CALL: ' + url);
        request.post(url, function(error, response, body) {
            bot.debug('Got response', error, body);
            if (!error && response.statusCode == 200) {
                var json;
                try {
                    json = JSON.parse(body);
                } catch (parseError) {
                    return cb(parseError);
                }

                if (json.ok) {
                    return cb(null, json);
                }
                return cb(json.error, json);
            }
            return cb(error || new Error('Invalid response'));
        }).form(options);
    }
};
<|endoftext|>


<commit_before>var fs		= require("fs");
var crypto  = require("crypto");
var zlib    = require("zlib");
var Storage	= require("./Storage");
var _		= require("../utils");
var log 	= console.log;

module.exports = _.class(Storage, {

	constructor: function() {
		Storage.constructor.apply(this, arguments);
	},

    initStorage: function(server) {
        Storage.initStorage.apply(this, arguments);
        var filename = this.dir + '/storage.dat';
        this.storagePos = fs.existsSync(filename) && fs.statSync(filename).size || 0;
        this.storage = fs.createWriteStream(filename, { flags: 'a+' });
        this.storage.on('error', function(err) {
            throw err;
        });
    },

	//-------- public methods ----------------
	getRingSize: function() {
		return 1 << this.ring * 3; // 8^ring
	},

	getAllocatedSize: function() {
		return (4 << this.ring) *_.GiB; // 2^ring * 4GiB
	},

	getMaxPostSize: function() {
		return (1 << this.ring * 2) * 2 * _.MiB; // 4^ring 2MiB
	},

	//-------- DB -----------
	createDataTables: function() {
		this.db.exec(
			// ------ data ---------
			"CREATE TABLE IF NOT EXISTS data (			" +
			"	hash        BLOB    NOT NULL,			" + // uid. hash of file. (binary32)
			"	pos         INT     NOT NULL,           " + // data position in storage
			"	len         INT     NOT NULL,           " + // data length

			"	aid         TEXT    NOT NULL,			" + // author-id  (char20)
			"	sign        BLOB    NOT NULL,           " + // sign data by pubkey   sign(author, hash)

			"	seq         INT     NOT NULL,           " + // last sequence in data source
			"	ds          TEXT    NOT NULL,			" + // data source.
			"	dshash      BLOB    NOT NULL,           " + // ds-hash := sha256(<pre-dshash>|<hash>)
			"	dssign      BLOB    NOT NULL,           " + // sign(dsPubkey, "<hash:binary><seg:str><seq:int>")
			"	PRIMARY KEY(hash, ds)                   " +
			");" +

			"CREATE UNIQUE INDEX IF NOT EXISTS idx_data_seq  ON data (ds, seq);" +

			// ------ triggers ---------
			"CREATE TRIGGER IF NOT EXISTS data_insert AFTER INSERT ON data " +
			" BEGIN " +
			"  UPDATE sources SET size = size + length(new.len), seq = new.seq, ts = current_timestamp WHERE ds = new.ds; " +
			"  UPDATE authors SET size = size + length(new.len) WHERE aid = new.aid; " +
			" END; "
		);

		this.prepareSQL({
			insertData:
				"INSERT INTO data(hash, aid, sign, pos, len, ds, seq, dshash, dssign)" +
				"VALUES($hash, $aid, $sign, $pos, $len, $ds, $seq, $dshash, $dssign)"
			,
			selectData:
				"SELECT pos, len, sign," +
                    "authors.cert as author" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE hash=$hash" +
				" LIMIT 1"
			,
			selectSourceData:
				"SELECT " +
					"seq," +
					"authors.cert as author," +
					"sign," +
					"hash," +
					"dshash," +
					"dssign" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE ds=$ds AND seq>$seq" +
				" ORDER BY seq" +
				" LIMIT $limit"
		});
	},

	//------------- data ----------------
	prepareData: function(post, src, nid) {
		var hash = _.b64ToHex(post.hash);
		var sign = _.b64ToHex(post.sign);
		var cert = this.parseCertificate(post.author);
		if(!cert) return { err: 'Incorrect author-certificate' };
		if(!this.checkUid(hash)) return { err: 'Hash is not appropriate' };
		if(this.ring == 0 && !cert.isRegistrar()) return { err: 'Method allowed only for registrar' };
		if(!nid || this.ring == 0 || Math.random() < 0.10) { // data is uploaded by client or by registrar
			if(!cert.isRegistrar() && !cert.isSignedByRootRegistrar()) return { err: 'Certificate is not signed by registrar' };
			if(!cert.verify(this.seg + hash, sign)) return { err: 'Invalid sign' };
		}
		this.saveCertificate(cert);
		var aid = cert.getID();
		if(nid) { // data is uploaded from node
			return {
				_nid:     nid,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     post.len|0
			}
		} else { // data is uploaded by client
			var file = post.file || {};
			if(hash !== file.hash) return { err: 'Invalid hash' };
			return {
				_tmpfile: file.tmpfile,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     file.size|0
			}
		}
	},

	_appendToFileStorage: function(hash, filepath, size, fn) {
		if(this.storageLocked) return fn('Storage is locked');
		this.storageLocked = true;
		var pos = this.storagePos;
		var rs = fs.createReadStream(filepath);
		rs.on('data', function(chunk){
			this.storage.write(chunk, 'binary');
			this.storagePos += chunk.length;
		}.bind(this));
		rs.on('end', function(){
			this.storageLocked = false;
			_.unlink(filepath);
			fn(null, pos);
		}.bind(this));
	},

	_insertData: function(data, pos, fn) {
		data.$pos = pos;
		Storage.insertData.call(this, data, fn);
	},

	insertData: function(data, fn) {
		this._sql.selectData.get({ $hash: data.$hash }, function(err, row) { // check existed hash
			if(err) return fn(err);
			if(row && row.len) {
				if(data._tmpfile) return fn(); // upload by user
				return this._insertData(data, row.pos, fn);
			}
			var saveData = function(tmpfile) {
				this._appendToFileStorage(data.$hash, tmpfile, data.$len, function(err, pos) {
					if(err) return fn(err);
					this._insertData(data, pos, fn);
				}.bind(this));
			}.bind(this);
			if(data._tmpfile) {
				saveData(data._tmpfile);
			} else { // http request to node
				var hash = data.$hash.toString('hex');
				this.server.requestToNode({
					nid: data._nid,
					path: '/-/'+this.seg+'/data/'+hash,
					type: 'file',
					onFinish: function(err, file) {
						if(err) return fn(err);
						if(hash !== file.hash) return fn('Invalid hash');
						data.$len = file.size;
						saveData(file.tmpfile);
					}
				});
			}
		}.bind(this));
	},

	//--------------- HTTP -----------------
	/**
	 * process GET-request:
	 * 		/-/<seg>/data/<uid:hex>.<ext:str>
	 */
	httpCommandData: function(conn) {
		if(conn.method !== 'GET') return conn.response400('Method Not Allowed');
		var q = _.str(conn.urlParts[4]).split('.');
		var uid = q[0], ext = q[1]||'';
		if(!this.checkUid(uid)) return conn.response400('Isn`t appropriate uid');
		this._sql.selectData.get({
			$hash: new Buffer(uid, 'hex')
		}, function(err, row) {
			if(err) return conn.response500('Can`t read DB');
			if(!row) return conn.response(404, 'UID not found');
            var len = row.len;
            conn.setExpire();
			conn.setResponseHeader("Accept-Ranges", "bytes");
			conn.setResponseHeader('x-base-author', row.author.toString("base64") + '; sign=' + row.sign.toString("base64"), true);
			conn.setContentType(ext);

            var code = 200, rsOp = { start:row.pos, end:row.pos + len-1 };
            if(conn._req.headers.range) {
                var range = String(conn._req.headers.range).replace(/bytes=/, "").split("-");
                var rng0 = Math.max(0, Math.min(len-1, range[0]|0));
                var rng1 = Math.max(0, Math.min(len-1, range[1]|0));
                if(rng1 <= rng0) rng1 = len - 1;
                rsOp.start = row.pos + rng0;
                rsOp.end   = row.pos + rng1;
                code = 206;
			    conn.setResponseHeader("Content-Range", "bytes " + rng0 + "-" + rng1 + "/" + len);
            } else {
                conn.setResponseHeader("Content-Length", len);
            }
            var rs = fs.createReadStream(this.dir + '/storage.dat', rsOp);
            conn.response(code, rs);
		}.bind(this));
	}

});
<commit_msg>fix file-storage<commit_after>var fs		= require("fs");
var crypto  = require("crypto");
var zlib    = require("zlib");
var Storage	= require("./Storage");
var _		= require("../utils");
var log 	= console.log;

module.exports = _.class(Storage, {

	constructor: function() {
		Storage.constructor.apply(this, arguments);
	},

    initStorage: function(server) {
        Storage.initStorage.apply(this, arguments);
        var filename = this.dir + '/storage.dat';
        this.storagePos = fs.existsSync(filename) && fs.statSync(filename).size || 0;
        this.storage = fs.createWriteStream(filename, { flags: 'a+' });
        this.storage.on('error', function(err) {
            throw err;
        });
    },

	//-------- public methods ----------------
	getRingSize: function() {
		return 1 << this.ring * 3; // 8^ring
	},

	getAllocatedSize: function() {
		return (4 << this.ring) *_.GiB; // 2^ring * 4GiB
	},

	getMaxPostSize: function() {
		return (1 << this.ring * 2) * 2 * _.MiB; // 4^ring 2MiB
	},

	//-------- DB -----------
	createDataTables: function() {
		this.db.exec(
			// ------ data ---------
			"CREATE TABLE IF NOT EXISTS data (			" +
			"	hash        BLOB    NOT NULL,			" + // uid. hash of file. (binary32)
			"	pos         INT     NOT NULL,           " + // data position in storage
			"	len         INT     NOT NULL,           " + // data length

			"	aid         TEXT    NOT NULL,			" + // author-id  (char20)
			"	sign        BLOB    NOT NULL,           " + // sign data by pubkey   sign(author, hash)

			"	seq         INT     NOT NULL,           " + // last sequence in data source
			"	ds          TEXT    NOT NULL,			" + // data source.
			"	dshash      BLOB    NOT NULL,           " + // ds-hash := sha256(<pre-dshash>|<hash>)
			"	dssign      BLOB    NOT NULL,           " + // sign(dsPubkey, "<hash:binary><seg:str><seq:int>")
			"	PRIMARY KEY(hash, ds)                   " +
			");" +

			"CREATE UNIQUE INDEX IF NOT EXISTS idx_data_seq  ON data (ds, seq);" +

			// ------ triggers ---------
			"CREATE TRIGGER IF NOT EXISTS data_insert AFTER INSERT ON data " +
			" BEGIN " +
			"  UPDATE sources SET size = size + length(new.len), seq = new.seq, ts = current_timestamp WHERE ds = new.ds; " +
			"  UPDATE authors SET size = size + length(new.len) WHERE aid = new.aid; " +
			" END; "
		);

		this.prepareSQL({
			insertData:
				"INSERT INTO data(hash, aid, sign, pos, len, ds, seq, dshash, dssign)" +
				"VALUES($hash, $aid, $sign, $pos, $len, $ds, $seq, $dshash, $dssign)"
			,
			selectData:
				"SELECT pos, len, sign," +
                    "authors.cert as author" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE hash=$hash" +
				" LIMIT 1"
			,
			selectSourceData:
				"SELECT " +
					"seq," +
					"authors.cert as author," +
					"sign," +
					"hash," +
					"dshash," +
					"dssign" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE ds=$ds AND seq>$seq" +
				" ORDER BY seq" +
				" LIMIT $limit"
		});
	},

	//------------- data ----------------
	prepareData: function(post, src, nid) {
		var hash = _.b64ToHex(post.hash);
		var sign = _.b64ToHex(post.sign);
		var cert = this.parseCertificate(post.author);
		if(!cert) return { err: 'Incorrect author-certificate' };
		if(!this.checkUid(hash)) return { err: 'Hash is not appropriate' };
		if(this.ring == 0 && !cert.isRegistrar()) return { err: 'Method allowed only for registrar' };
		if(!nid || this.ring == 0 || Math.random() < 0.10) { // data is uploaded by client or by registrar
			if(!cert.isRegistrar() && !cert.isSignedByRootRegistrar()) return { err: 'Certificate is not signed by registrar' };
			if(!cert.verify(this.seg + hash, sign)) return { err: 'Invalid sign' };
		}
		this.saveCertificate(cert);
		var aid = cert.getID();
		if(nid) { // data is uploaded from node
			return {
				_nid:     nid,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     post.len|0
			}
		} else { // data is uploaded by client
			var file = post.file || {};
			if(hash !== file.hash) return { err: 'Invalid hash' };
			return {
				_tmpfile: file.tmpfile,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     file.size|0
			}
		}
	},

	_appendToFileStorage: function(hash, filepath, size, fn) {
		if(this.storageLocked) return fn('Storage is locked');
		this.storageLocked = true;
		var pos = this.storagePos;
		var rs = fs.createReadStream(filepath);
		rs.on('data', function(chunk){
			this.storage.write(chunk, 'binary');
			this.storagePos += chunk.length;
		}.bind(this));
		rs.on('end', function(){
			this.storageLocked = false;
			_.unlink(filepath);
			fn(null, pos);
		}.bind(this));
	},

	_insertData: function(data, pos, fn) {
		data.$pos = pos;
		Storage.insertData.call(this, data, fn);
	},

	insertData: function(data, fn) {
		this._sql.selectData.get({ $hash: data.$hash }, function(err, row) { // check existed hash
			if(err) return fn(err);
			if(row && row.len) {
				if(data._tmpfile) return fn(); // upload by user
				return this._insertData(data, row.pos, fn);
			}
			var saveData = function(tmpfile) {
				this._appendToFileStorage(data.$hash, tmpfile, data.$len, function(err, pos) {
					if(err) return fn(err);
					this._insertData(data, pos, fn);
				}.bind(this));
			}.bind(this);
			if(data._tmpfile) {
				saveData(data._tmpfile);
			} else { // http request to node
				var hash = data.$hash.toString('hex');
				this.server.requestToNode({
					nid: data._nid,
					path: '/-/'+this.seg+'/data/'+hash,
					type: 'file',
					onFinish: function(err, file) {
						if(err) return fn(err);
						if(hash !== file.hash) return fn('Invalid hash');
						data.$len = file.size;
						saveData(file.tmpfile);
					}
				});
			}
		}.bind(this));
	},

	//--------------- HTTP -----------------
	/**
	 * process GET-request:
	 * 		/-/<seg>/data/<uid:hex>.<ext:str>
	 */
	httpCommandData: function(conn) {
		if(conn.method !== 'GET') return conn.response400('Method Not Allowed');
		var q = _.str(conn.urlParts[4]).split('.');
		var uid = q[0], ext = q[1]||'';
		if(!this.checkUid(uid)) return conn.response400('Isn`t appropriate uid');
		this._sql.selectData.get({
			$hash: new Buffer(uid, 'hex')
		}, function(err, row) {
			if(err) return conn.response500('Can`t read DB');
			if(!row) return conn.response(404, 'UID not found');
            var len = row.len;
            conn.setExpire();
			conn.setResponseHeader("Accept-Ranges", "bytes");
			conn.setResponseHeader('x-base-author', row.author.toString("base64") + '; sign=' + row.sign.toString("base64"), true);
			conn.setContentType(ext);

            var code = 200, rsOp = { start:row.pos, end:row.pos + len-1 };
            if(conn._req.headers.range) {
                var range = String(conn._req.headers.range).replace(/bytes=/, "").split("-");
                var rng0 = Math.max(0, Math.min(len-1, range[0]|0));
                var rng1 = Math.max(0, Math.min(len-1, range[1]|0));
                if(rng1 <= rng0) rng1 = len - 1;
                rsOp.start = row.pos + rng0;
                rsOp.end   = row.pos + rng1;
                code = 206;
			    conn.setResponseHeader("Content-Range", "bytes " + rng0 + "-" + rng1 + "/" + len);
            }
            var rs = fs.createReadStream(this.dir + '/storage.dat', rsOp);
            conn.response(code, rs);
		}.bind(this));
	}

});
<|endoftext|>


<commit_before>'use strict'

function getUnregisteredContacts (contact) {
  return {
    UnregisteredContactParameter: [{
      Address: contact.address,
      ContactCompanyName: contact.name,
      ContactName: contact.name,
      IsUnofficial: false,
      ReferenceNumber: contact.enterpriseNumber,
      Role: contact.role || 'Mottaker',
      ZipCode: contact.zipCode,
      ZipPlace: contact.zipPlace
    }]
  }
}

module.exports = (options) => {
  if (!options) {
    throw new Error('Missing required input: options')
  }
  if (!options.title) {
    throw new Error('Missing required input: options.title')
  }
  if (!options.unofficialTitle) {
    throw new Error('Missing required input: options.unofficialTitle')
  }
  if (!options.accessCode) {
    throw new Error('Missing required input: options.accessCode')
  }
  if (!options.file) {
    throw new Error('Missing required input: options.file')
  }
  if (!options.file.title) {
    throw new Error('Missing required input: options.file.title')
  }
  if (!options.file.data) {
    throw new Error('Missing required input: options.file.data')
  }
  if (!options.caseNumber) {
    throw new Error('Missing required input: options.caseNumber')
  }
  if (!options.category) {
    throw new Error('Missing required input: options.category')
  }
  if (!options.contacts && !options.unregisteredContacts) {
    throw new Error('Missing required input: options.contacts or options.unregisteredContacts')
  }

  const meta = {
    clientService: 'DocumentService',
    clientMethod: 'CreateDocument',
    args: {
      parameter: {
        AccessCode: options.accessCode || 'J', // Codetable: Accesscode
        AccessGroup: options.accessGroup || 'Alle', // Codetable: Tilgangsgruppe navn
        Category: options.category, // Codetable: Document category
        Contacts: [
          {
            DocumentContactParameter: options.contacts
          }
        ],
        DocumentDate: options.documentCreated || undefined,
        Files: [
          {
            CreateFileParameter: {
              Base64Data: options.file.data, // Must be base64 encoded
              Category: options.file.category || '1', // Codetable: File category
              Format: options.file.format || 'pdf', // Codetable: File format
              Status: options.file.status || 'F', // Codetable: FileStatus
              Title: options.file.title,
              VersionFormat: options.file.versionFormat || 'A' // Codetable: File status
            }
          }
        ],
        Paragraph: options.paragraph || undefined, // Codetable: Paragraph
        ResponsibleEnterpriseRecno: options.responsibleEnterpriseRecno || undefined, // Recnr ansvarlig virksomhet
        ResponsibleEnterpriseNumber: options.responsibleEnterpriseNumber || undefined,
        ResponsiblePersonRecno: options.responsiblePersonRecno || undefined,
        Status: options.status || 'J', // Codetable: Document status
        Title: options.title,
        UnofficialTitle: options.unofficialTitle,
        UnregisteredContacts: options.unregisteredContacts ? getUnregisteredContacts(options.unregisteredContacts) : undefined,
        Archive: options.archive || 'Saksdokument', // Codetable: Document archive
        CaseNumber: options.caseNumber,
        SendersReference: options.sendersReference || undefined
      }
    }
  }
  if (options.responsibleEnterpriseNumber) {
    delete meta.args.parameter.ResponsibleEnterpriseRecno
    meta.args.parameter.ResponsibleEnterpriseNumber = options.responsibleEnterpriseNumber
  } else {
    delete meta.args.parameter.ResponsibleEnterpriseNumber
  }
  return meta
}
<commit_msg>Contact should not be a mandatory field, and if not set - remove the contact property from the metadata<commit_after>'use strict'

function getUnregisteredContacts (contact) {
  return {
    UnregisteredContactParameter: [{
      Address: contact.address,
      ContactCompanyName: contact.name,
      ContactName: contact.name,
      IsUnofficial: false,
      ReferenceNumber: contact.enterpriseNumber,
      Role: contact.role || 'Mottaker',
      ZipCode: contact.zipCode,
      ZipPlace: contact.zipPlace
    }]
  }
}

module.exports = (options) => {
  if (!options) {
    throw new Error('Missing required input: options')
  }
  if (!options.title) {
    throw new Error('Missing required input: options.title')
  }
  if (!options.unofficialTitle) {
    throw new Error('Missing required input: options.unofficialTitle')
  }
  if (!options.accessCode) {
    throw new Error('Missing required input: options.accessCode')
  }
  if (!options.file) {
    throw new Error('Missing required input: options.file')
  }
  if (!options.file.title) {
    throw new Error('Missing required input: options.file.title')
  }
  if (!options.file.data) {
    throw new Error('Missing required input: options.file.data')
  }
  if (!options.caseNumber) {
    throw new Error('Missing required input: options.caseNumber')
  }
  if (!options.category) {
    throw new Error('Missing required input: options.category')
  }

  const meta = {
    clientService: 'DocumentService',
    clientMethod: 'CreateDocument',
    args: {
      parameter: {
        AccessCode: options.accessCode || 'J', // Codetable: Accesscode
        AccessGroup: options.accessGroup || 'Alle', // Codetable: Tilgangsgruppe navn
        Category: options.category, // Codetable: Document category
        Contacts: [
          {
            DocumentContactParameter: options.contacts
          }
        ],
        DocumentDate: options.documentCreated || undefined,
        Files: [
          {
            CreateFileParameter: {
              Base64Data: options.file.data, // Must be base64 encoded
              Category: options.file.category || '1', // Codetable: File category
              Format: options.file.format || 'pdf', // Codetable: File format
              Status: options.file.status || 'F', // Codetable: FileStatus
              Title: options.file.title,
              VersionFormat: options.file.versionFormat || 'A' // Codetable: File status
            }
          }
        ],
        Paragraph: options.paragraph || undefined, // Codetable: Paragraph
        ResponsibleEnterpriseRecno: options.responsibleEnterpriseRecno || undefined, // Recnr ansvarlig virksomhet
        ResponsibleEnterpriseNumber: options.responsibleEnterpriseNumber || undefined,
        ResponsiblePersonRecno: options.responsiblePersonRecno || undefined,
        Status: options.status || 'J', // Codetable: Document status
        Title: options.title,
        UnofficialTitle: options.unofficialTitle,
        UnregisteredContacts: options.unregisteredContacts ? getUnregisteredContacts(options.unregisteredContacts) : undefined,
        Archive: options.archive || 'Saksdokument', // Codetable: Document archive
        CaseNumber: options.caseNumber,
        SendersReference: options.sendersReference || undefined
      }
    }
  }

  // Remove contact object if no contact was defined
  if (!options.contact) {
    delete meta.args.parameter.Contacts
  }

  if (options.responsibleEnterpriseNumber) {
    delete meta.args.parameter.ResponsibleEnterpriseRecno
    meta.args.parameter.ResponsibleEnterpriseNumber = options.responsibleEnterpriseNumber
  } else {
    delete meta.args.parameter.ResponsibleEnterpriseNumber
  }
  return meta
}
<|endoftext|>


<commit_before>// Copyright 2016, EMC Inc.

'use strict';

var injector = require('../../../index.js').injector;
var controller = injector.get('Http.Services.Swagger').controller;
var addLinks = injector.get('Http.Services.Swagger').addLinksHeader;
var nodes = injector.get('Http.Services.Api.Nodes');
var waterline = injector.get('Services.Waterline');
//var workflowApiService = injector.get('Http.Services.Api.Workflows');
var _ = injector.get('_'); // jshint ignore:line
var constants = injector.get('Constants');
var Errors = injector.get('Errors');

var nodesGetAll = controller(function(req, res) {
    var options = {
        skip: req.swagger.query.$skip,
        limit: req.swagger.query.$top
    };
    return nodes.getAllNodes(req.query, options)
    .tap(function(nodes) {
        return addLinks(req, res, 'nodes', req.query);
    });
});

var nodesPost = controller({success: 201}, function(req) {
    return nodes.postNode(req.body);
});

var nodesGetById = controller(function(req) {
    return nodes.getNodeById(req.swagger.params.identifier.value);
});

var nodesPatchById = controller(function(req) {
    return nodes.patchNodeById(req.swagger.params.identifier.value, req.body);
});

var nodesDelById = controller({success: 204}, function(req) {
    return nodes.delNodeById(req.swagger.params.identifier.value);
});

var nodesGetSshById = controller(function(req) {
    return nodes.getNodeSshById(req.swagger.params.identifier.value);
});

var nodesPostSshById = controller({success: 201}, function(req) {
    return nodes.postNodeSshById(req.swagger.params.identifier.value, req.body);
});

var nodesGetCatalogById = controller(function(req) {
    return nodes.getNodeCatalogById(req.swagger.params.identifier.value, req.query);
});

var nodesGetCatalogSourceById = controller(function(req) {
    return nodes.getNodeCatalogSourceById(req.swagger.params.identifier.value,
                                          req.swagger.params.source.value);
});

var nodesGetPollersById = controller(function(req) {
    return nodes.getPollersByNodeId(req.swagger.params.identifier.value);
});

var nodesGetWorkflowById = controller(function(req) {
    return nodes.getNodeWorkflowById(req.swagger.params.identifier.raw);
});

var nodesPostWorkflowById = controller({success: 201}, function(req) {
    var config = _.defaults(
        req.swagger.params.name.value ? { name: req.swagger.params.name.value } : {},
        req.swagger.params.body.value || {}
    );
    return nodes.setNodeWorkflow(config, req.swagger.params.identifier.value);
});

var nodesWorkflowActionById = controller({success: 202}, function(req, res) {
    var command = req.body.command;

    var actionFunctions = {
        cancel: function() {
            return nodes.delActiveWorkflowById(req.swagger.params.identifier.value)
                .then(function(graph) {
                    res.setHeader('Location', '/api/2.0/workflows/' + graph.id);
                    return graph;
                });
        }
    };

    if(!_(actionFunctions).has(command)) {
        throw new Errors.BadRequestError(
            command + ' is not a valid workflow action'
        );
    }
    return actionFunctions[command]();

});

var nodesGetTagsById = controller(function(req) {
    return nodes.getTagsById(req.swagger.params.identifier.value);
});

var nodesDelTagById = controller({success: 204}, function(req) {
    return nodes.removeTagsById(req.swagger.params.identifier.value,
                                req.swagger.params.tagName.value);
});

var nodesPatchTagById = controller(function(req) {
    return nodes.addTagsById(req.swagger.params.identifier.value,
                             req.body.tags);
});

var nodesMasterDelTagById = controller({success: 204}, function(req) {
    return nodes.masterDelTagById(req.swagger.params.tagName.value);
});

var nodesDelRelations = controller(function(req) {
   return nodes.editNodeRelations(req.swagger.params.identifier.value,
           req.body,
           nodes._removeRelation
        );
});

var nodesAddRelations = controller(function(req) {
   return nodes.editNodeRelations(req.swagger.params.identifier.value,
           req.body,
           nodes._addRelation
        );
});

var nodesGetRelations = controller(function(req) {
   return nodes.getNodeRelations(req.swagger.params.identifier.value);
});

var nodesGetObmsByNodeId = controller(function(req) {
    return nodes.getObmsByNodeId(req.swagger.params.identifier.value);
});

var nodesPutObmsByNodeId = controller({success: 201}, function(req) {
    return nodes.putObmsByNodeId(req.swagger.params.identifier.value, req.body);
});

module.exports = {
    nodesGetAll: nodesGetAll,
    nodesPost: nodesPost,
    nodesGetById: nodesGetById,
    nodesPatchById: nodesPatchById,
    nodesDelById: nodesDelById,
    nodesGetSshById: nodesGetSshById,
    nodesPostSshById: nodesPostSshById,
    nodesGetCatalogById: nodesGetCatalogById,
    nodesGetCatalogSourceById: nodesGetCatalogSourceById,
    nodesGetPollersById: nodesGetPollersById,
    nodesGetWorkflowById: nodesGetWorkflowById,
    nodesPostWorkflowById: nodesPostWorkflowById,
    nodesWorkflowActionById: nodesWorkflowActionById,
    nodesGetTagsById: nodesGetTagsById,
    nodesDelTagById: nodesDelTagById,
    nodesPatchTagById: nodesPatchTagById,
    nodesMasterDelTagById: nodesMasterDelTagById,
    nodesDelRelations: nodesDelRelations,
    nodesAddRelations: nodesAddRelations,
    nodesGetRelations: nodesGetRelations,
    nodesGetObmsByNodeId: nodesGetObmsByNodeId,
    nodesPutObmsByNodeId: nodesPutObmsByNodeId
};
<commit_msg>Removed old commented line injecting a service we don't need<commit_after>// Copyright 2016, EMC Inc.

'use strict';

var injector = require('../../../index.js').injector;
var controller = injector.get('Http.Services.Swagger').controller;
var addLinks = injector.get('Http.Services.Swagger').addLinksHeader;
var nodes = injector.get('Http.Services.Api.Nodes');
var waterline = injector.get('Services.Waterline');
var _ = injector.get('_'); // jshint ignore:line
var constants = injector.get('Constants');
var Errors = injector.get('Errors');

var nodesGetAll = controller(function(req, res) {
    var options = {
        skip: req.swagger.query.$skip,
        limit: req.swagger.query.$top
    };
    return nodes.getAllNodes(req.query, options)
    .tap(function(nodes) {
        return addLinks(req, res, 'nodes', req.query);
    });
});

var nodesPost = controller({success: 201}, function(req) {
    return nodes.postNode(req.body);
});

var nodesGetById = controller(function(req) {
    return nodes.getNodeById(req.swagger.params.identifier.value);
});

var nodesPatchById = controller(function(req) {
    return nodes.patchNodeById(req.swagger.params.identifier.value, req.body);
});

var nodesDelById = controller({success: 204}, function(req) {
    return nodes.delNodeById(req.swagger.params.identifier.value);
});

var nodesGetSshById = controller(function(req) {
    return nodes.getNodeSshById(req.swagger.params.identifier.value);
});

var nodesPostSshById = controller({success: 201}, function(req) {
    return nodes.postNodeSshById(req.swagger.params.identifier.value, req.body);
});

var nodesGetCatalogById = controller(function(req) {
    return nodes.getNodeCatalogById(req.swagger.params.identifier.value, req.query);
});

var nodesGetCatalogSourceById = controller(function(req) {
    return nodes.getNodeCatalogSourceById(req.swagger.params.identifier.value,
                                          req.swagger.params.source.value);
});

var nodesGetPollersById = controller(function(req) {
    return nodes.getPollersByNodeId(req.swagger.params.identifier.value);
});

var nodesGetWorkflowById = controller(function(req) {
    return nodes.getNodeWorkflowById(req.swagger.params.identifier.raw);
});

var nodesPostWorkflowById = controller({success: 201}, function(req) {
    var config = _.defaults(
        req.swagger.params.name.value ? { name: req.swagger.params.name.value } : {},
        req.swagger.params.body.value || {}
    );
    return nodes.setNodeWorkflow(config, req.swagger.params.identifier.value);
});

var nodesWorkflowActionById = controller({success: 202}, function(req, res) {
    var command = req.body.command;

    var actionFunctions = {
        cancel: function() {
            return nodes.delActiveWorkflowById(req.swagger.params.identifier.value)
                .then(function(graph) {
                    res.setHeader('Location', '/api/2.0/workflows/' + graph.id);
                    return graph;
                });
        }
    };

    if(!_(actionFunctions).has(command)) {
        throw new Errors.BadRequestError(
            command + ' is not a valid workflow action'
        );
    }
    return actionFunctions[command]();

});

var nodesGetTagsById = controller(function(req) {
    return nodes.getTagsById(req.swagger.params.identifier.value);
});

var nodesDelTagById = controller({success: 204}, function(req) {
    return nodes.removeTagsById(req.swagger.params.identifier.value,
                                req.swagger.params.tagName.value);
});

var nodesPatchTagById = controller(function(req) {
    return nodes.addTagsById(req.swagger.params.identifier.value,
                             req.body.tags);
});

var nodesMasterDelTagById = controller({success: 204}, function(req) {
    return nodes.masterDelTagById(req.swagger.params.tagName.value);
});

var nodesDelRelations = controller(function(req) {
   return nodes.editNodeRelations(req.swagger.params.identifier.value,
           req.body,
           nodes._removeRelation
        );
});

var nodesAddRelations = controller(function(req) {
   return nodes.editNodeRelations(req.swagger.params.identifier.value,
           req.body,
           nodes._addRelation
        );
});

var nodesGetRelations = controller(function(req) {
   return nodes.getNodeRelations(req.swagger.params.identifier.value);
});

var nodesGetObmsByNodeId = controller(function(req) {
    return nodes.getObmsByNodeId(req.swagger.params.identifier.value);
});

var nodesPutObmsByNodeId = controller({success: 201}, function(req) {
    return nodes.putObmsByNodeId(req.swagger.params.identifier.value, req.body);
});

module.exports = {
    nodesGetAll: nodesGetAll,
    nodesPost: nodesPost,
    nodesGetById: nodesGetById,
    nodesPatchById: nodesPatchById,
    nodesDelById: nodesDelById,
    nodesGetSshById: nodesGetSshById,
    nodesPostSshById: nodesPostSshById,
    nodesGetCatalogById: nodesGetCatalogById,
    nodesGetCatalogSourceById: nodesGetCatalogSourceById,
    nodesGetPollersById: nodesGetPollersById,
    nodesGetWorkflowById: nodesGetWorkflowById,
    nodesPostWorkflowById: nodesPostWorkflowById,
    nodesWorkflowActionById: nodesWorkflowActionById,
    nodesGetTagsById: nodesGetTagsById,
    nodesDelTagById: nodesDelTagById,
    nodesPatchTagById: nodesPatchTagById,
    nodesMasterDelTagById: nodesMasterDelTagById,
    nodesDelRelations: nodesDelRelations,
    nodesAddRelations: nodesAddRelations,
    nodesGetRelations: nodesGetRelations,
    nodesGetObmsByNodeId: nodesGetObmsByNodeId,
    nodesPutObmsByNodeId: nodesPutObmsByNodeId
};
<|endoftext|>