<commit_before>/* jslint node: true */
"use strict";

var _ = require('lodash');
var moment = require('moment-timezone');
var debug = require('debug')('massive-tyrion:helpers');
var request = require('request');

var calculateOffsetFromTime = function calculateOffsetFromTime(time) {
  var serverUnix = new Date().valueOf() / 1000;
  var offset = serverUnix - time;
  return {
    offset: offset,
    server: serverUnix,
    received: time
  };
};

var fixObjectBooleanKey = function fixObjectBooleanKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
  }

  var trueIsh = (obj[key] === 'true' || obj[key] === '1' || obj[key] === 1);
  var falseIsh = (obj[key] === 'false' || obj[key] === '0' || obj[key] === 0);

  if (trueIsh) {
    obj[key] = true;
  } else if (falseIsh) {
    obj[key] = false;
  }
};

var fixObjectNumberKey = function fixObjectNumberKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
    return;
  }

  if (!_.isNumber(obj[key]) && _.isNumber(parseInt(obj[key]))) {
    obj[key] = parseInt(obj[key]);
  }
};

var fixObjectStringKey = function fixObjectStringKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
    return;
  }
};

var sortWebListsForCollection = function sortWebListsForCollection(list, collectionName) {
  if (!_.isArray(list)) {
    return list;
  }

  if (collectionName === 'battalion') {
    var listWithFields = _.map(list, function(item) {
      fixObjectBooleanKey(item, 'isMandatory', false);
      fixObjectBooleanKey(item, 'active', true);
      fixObjectNumberKey(item, 'position', 0);
      return item;
    });

    return _.sortByOrder(listWithFields, ['isMandatory', 'active', 'position'], ['desc', 'desc', 'asc']);
  }

  //    cmd.sort = [['isMandatory', -1], ['position', 1], ['friendly_id', 1]];
  if (collectionName === 'unit') {
    var unitsListWithFields = _.map(list, function(item) {
      fixObjectBooleanKey(item, 'isMandatory', false);
      fixObjectNumberKey(item, 'position', 0);
      fixObjectStringKey(item, 'friendly_id', "");
      return item;
    });

    return _.sortByOrder(unitsListWithFields, ['isMandatory', 'position', 'friendly_id'], ['desc', 'asc', 'asc']);
  }

  // Default, return the same list
  return list;
};

var joinParentChildCollections = function(parents, childs, parent_api_id, parent_local_id, parent_name, parent_uuid, parent_dest) {
  var mapLocalIdItems = _.map(_.filter(childs, function(item) {
    return _.has(item, parent_local_id) && !_.has(item, parent_api_id);
  }), function(item) {
    return {
      id: item[parent_local_id],
      item: item
    };
  });

  var mapApiIdItems = _.map(_.filter(childs, function(item) {
    return _.has(item, parent_api_id);
  }), function(item) {
    return {
      id: item[parent_api_id],
      item: item
    };
  });

  var unmergedItems = _.flatten([mapApiIdItems, mapLocalIdItems]);
  var reducedIds = _.reduce(unmergedItems, function(memo, i) {
    if (!_.has(memo, i.id)) {
      memo[i.id] = [];
    }

    memo[i.id].push(i.item);
    return memo;
  }, {});

  _.each(parents, function(parent) {
    var itemsByParentId = [];
    var itemsByApiParentId = [];
    if (_.has(parent, 'local_id') && _.has(reducedIds, parent.local_id)) {
      itemsByParentId = reducedIds[parent.local_id];
    }

    if (_.has(parent, 'id') && _.has(reducedIds, parent.id)) {
      itemsByApiParentId = reducedIds[parent.id];
    }

    var mergedItems = _.flatten([itemsByParentId, itemsByApiParentId]);
    var enhancedItems = _.map(mergedItems, function(item) {
      item[parent_api_id] = parent.id;

      if (_.has(parent, 'name')) {
        item[parent_name] = parent.name;
      }

      if (_.has(parent, 'uuid')) {
        item[parent_uuid] = parent.uuid;
      }

      return item;
    });

    parent[parent_dest] = enhancedItems;
  });

  return parents;
};

var itemIsTrue = function itemIsTrue(item, key) {
  if (_.isUndefined(item) || _.isNull(item)) {
    return false;
  }

  if (_.isUndefined(item[key]) || _.isNull(item[key])) {
    return false;
  }

  var itemTrue = item[key] === true || item[key] === "true";
  var itemOne = item[key] === 1 || item[key] === "1";

  return itemTrue || itemOne;
};

var isAdmin = function isAdmin(item) {
  return itemIsTrue(item, 'admin');
};

var isSuper = function isSuper(item) {
  return itemIsTrue(item, 'superuser');
};

var isActive = function isActive(item) {
  return itemIsTrue(item, 'active');
};

var verifyJson = function(req, res, buf) {
  try {
    JSON.parse(buf);
  } catch (err) {
    var message = 'Invalid JSON:' + buf;
    console.log(message);
  }
};

var makeId = function makeId(length) {
  var text = "";
  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (var i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return text;
};

var hasFeature = function hasFeature(dept, feature) {
  var value = 0;

  var hasKey = !_.isUndefined(dept[feature]) && !_.isNull(dept[feature]);
  var keyIsTrue = dept[feature] === true || dept[feature] === 'true';
  var keyIsOne = dept[feature] === 1 || dept[feature] === '1';

  if (hasKey && (keyIsTrue || keyIsOne)) {
    value = 1;
  }
  return value;
};

var isItemValidOnMap = function isItemValidOnMap(item) {
  var invalidDegreeLimit = 5.0;

  var parsedLat = parseInt(item.latitude);
  var parsedLon = parseInt(item.longitude);

  if (_.isNaN(parsedLat) || _.isNaN(parsedLon)) {
    return false;
  }

  if (Math.abs(parsedLat) < invalidDegreeLimit && Math.abs(parsedLon) < invalidDegreeLimit) {
    return false;
  }

  return true;
};

var stripSessionFields = function stripSessionFields(value, key) {
  var fields = ['pass', 'salt', 'when'];
  var skipFields = _.isString(key) && _.includes(fields, key.toLowerCase());
  var filterSeneca = _.isString(key) && _.trimRight(key, '$') != key;

  return filterSeneca || skipFields;
};

var cleanupUser = function cleanupUser(user) {
  return _.omit(user, stripSessionFields);
};

var resolveUser = function resolveUser(args, callback) {
  var hasSeneca = _.isObject(args) &&
    _.isObject(args.req$) &&
    _.isObject(args.req$.seneca);
  var hasSenecaUser = hasSeneca && _.isObject(args.req$.seneca.user);
  var hasArgsUser = _.isObject(args) && _.isObject(args.user);
  var hasHeaders = _.isObject(args.req$) && _.isObject(args.req$.headers);
  if (!hasSenecaUser && !hasArgsUser) {
    if (hasHeaders) {
      debug("No user. Headers were:", args.req$.headers);
    }

    debug("No hasSenecaUser and no hasArgsUser");
    return callback(null, null);
  }

  var resolvedUser = null;
  if (hasSenecaUser) {
    resolvedUser = args.req$.seneca.user;
  } else if (hasArgsUser) {
    resolvedUser = args.user;
  }

  var session = {};
  if (hasSeneca && _.isObject(args.req$.seneca.login)) {
    session = args.req$.seneca.login;
  }

  var userInactive = !itemIsTrue(resolvedUser, 'active');
  var sessionInactive = !itemIsTrue(session, 'active');
  // sessionInactive = false; // TODO: remove this once all the users are active

  if (_.isNull(resolvedUser) || userInactive || sessionInactive) {
    debug("User or session not active for", resolvedUser.nick, session.id);
    return callback(null, null, null);
  }

  var user = cleanupUser(resolvedUser);
  return callback(null, user, session);
};

var resolveLogin = function resolveLogin(args, callback) {
  if (!_.isObject(args) ||
    !_.isObject(args.req$) ||
    !_.isObject(args.req$.seneca) ||
    !_.isObject(args.req$.seneca.login) ||
    !itemIsTrue(args.req$.seneca.login, 'active')
  ) {
    return callback(null, null);
  }

  var login = cleanupUser(args.req$.seneca.login);
  return callback(null, login);
};

var getClosedOrDate = function getClosedOrDate() {
  var nowForClosedDateUnixDate = moment().valueOf() / 1000.0;
  var closedOr = [{
    closed_unix_date: 0
  }, {
    closed_unix_date: {
      $gt: nowForClosedDateUnixDate
    }
  }];

  return closedOr;
};

var extractInfoFromDevice = function extractInfoFromDevice(device) {
  var maxDaysSinceEvent = 120;
  var info = {};
  info.appVer = 'Unknown';
  info.osVer = 'Unknown';
  info.env = 'beta';
  info.daysSinceEvent = maxDaysSinceEvent; //max days

  var unixDate = moment().valueOf() / 1000.0;
  var dayAsSeconds = 60 * 60 * 24;

  if (!_.isUndefined(device.env) && !_.isNull(device.env)) {
    info.env = device.env;
  }

  if (!_.isUndefined(device.ver) && !_.isNull(device.ver)) {
    info.appVer = device.ver;
  }

  if (!_.isUndefined(device.ua) && !_.isNull(device.ua)) {
    var partsAppVer = '';
    var partsOsVer = '';

    var userAgentParts = device.ua.match(/(.*)\((.*)\)/i);

    if (userAgentParts && userAgentParts[1]) {
      partsAppVer = userAgentParts[1];
    }

    if (userAgentParts && userAgentParts[2]) {
      partsOsVer = userAgentParts[2];
    }

    if (partsAppVer.length > 0) {
      info.appVer = partsAppVer.trim();
    }

    if (partsOsVer.length > 0) {
      var osSplitParts = partsOsVer.match(/(.*);(.*);(.*)/i);

      var foundOsVer = '';
      if (osSplitParts && osSplitParts[2]) {
        foundOsVer = osSplitParts[2];
      }

      if (foundOsVer.length > 0) {
        info.osVer = foundOsVer.trim();
      }
    }
  }

  if (!_.isUndefined(device.time) && !_.isNull(device.time)) {
    var secondsAgo = unixDate - parseFloat(device.time);
    info.daysSinceEvent = Math.floor(secondsAgo / dayAsSeconds);
  }

  return info;
};

var headersToDevice = function headersToDevice(token, headers) {
  var env = 'production';
  if (_.has(headers, 'x-tc-apn-environment') &&
    headers['x-tc-apn-environment'] === 'beta') {
    env = 'beta';
  }

  var appVersion = '';
  if (_.has(headers, 'x-tc-app-version')) {
    appVersion = headers['x-tc-app-version'];
  }

  var userAgent = '';
  if (_.has(headers, 'user-agent')) {
    userAgent = headers['user-agent'];
  }

  var bundleIdentifier = 'com.simple-track.Tablet-CMD';
  if (_.has(headers, 'x-tc-bundle-identifier') &&
    _.isString(headers['x-tc-bundle-identifier']) &&
    _.size(headers['x-tc-bundle-identifier']) > 0) {
    bundleIdentifier = headers['x-tc-bundle-identifier'];
  } else if (appVersion.match(/Tablet CMD Beta/i)) {
    bundleIdentifier = 'com.simple-track.beta.Tablet-CMD';
  }

  var silentEnabled = itemIsTrue(headers, 'x-tc-silent-enabled');
  var richEnabled = itemIsTrue(headers, 'x-tc-rich-enabled');

  var unixtime = moment().valueOf() / 1000.0;
  var deviceInfo = {
    token: token,
    env: env,
    ver: appVersion,
    ua: userAgent,
    time: unixtime,
    bundleIdentifier: bundleIdentifier,
    silentEnabled: silentEnabled,
    richEnabled: richEnabled
  };
  return deviceInfo;
};

var logUserDevice = function logUserDevice(postUrl, authToken, user, session, headers) {
  var device = headersToDevice('', headers);
  var info = extractInfoFromDevice(device);

  var item = {
    userId: user.id,
    departmentId: user.departmentId,
    nick: user.nick,
    appVer: info.appVer,
    osVer: info.osVer,
    ua: device.ua,
    t: device.time,
    session: session.id
  };

  var filter = [
    "Tablet CMD/1.5.8",
    "Tablet CMD/1.5.9",
    "Tablet CMD/1.6.0"
  ];

  var shouldFilter = false;
  if (shouldFilter && _.contains(filter, item.appVer)) {
    return;
  }

  return requestPost(postUrl, authToken, item);
};

var requestPost = function requestPost(postUrl, authToken, item, callback) {
  if (!_.isFunction(callback)) {
    callback = function defaultCallback() {}
  }
  var reqOpts = {
    url: postUrl,
    method: 'POST',
    json: item,
    headers: {
      'x-tc-auth-token': authToken
    }
  };
  return request(reqOpts, callback);
};

var configureMomentOpts = function configureMomentOpts() {
  moment.updateLocale('en', {
    relativeTime: {
      future: "in %s",
      past: "%s ago",
      s: "%ds",
      ss: "%ds",
      m: "%dmin",
      mm: "%dmin",
      h: "%dh",
      hh: "%dh",
      d: "%dd",
      dd: "%dd",
      M: "%dmon",
      MM: "%dmon",
      y: "%dy",
      yy: "%dy"
    }
  });
};

module.exports = {
  calculateOffsetFromTime: calculateOffsetFromTime,
  fixObjectBooleanKey: fixObjectBooleanKey,
  sortWebListsForCollection: sortWebListsForCollection,
  joinParentChildCollections: joinParentChildCollections,
  itemIsTrue: itemIsTrue,
  isAdmin: isAdmin,
  isSuper: isSuper,
  isActive: isActive,
  verifyJson: verifyJson,
  makeId: makeId,
  hasFeature: hasFeature,
  isItemValidOnMap: isItemValidOnMap,
  resolveUser: resolveUser,
  resolveLogin: resolveLogin,
  getClosedOrDate: getClosedOrDate,
  cleanupUser: cleanupUser,
  extractInfoFromDevice: extractInfoFromDevice,
  headersToDevice: headersToDevice,
  logUserDevice: logUserDevice,
  configureMomentOpts: configureMomentOpts,
  requestPost: requestPost,
};
<commit_msg>Remove filetered app versions<commit_after>/* jslint node: true */
"use strict";

var _ = require('lodash');
var moment = require('moment-timezone');
var debug = require('debug')('massive-tyrion:helpers');
var request = require('request');

var calculateOffsetFromTime = function calculateOffsetFromTime(time) {
  var serverUnix = new Date().valueOf() / 1000;
  var offset = serverUnix - time;
  return {
    offset: offset,
    server: serverUnix,
    received: time
  };
};

var fixObjectBooleanKey = function fixObjectBooleanKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
  }

  var trueIsh = (obj[key] === 'true' || obj[key] === '1' || obj[key] === 1);
  var falseIsh = (obj[key] === 'false' || obj[key] === '0' || obj[key] === 0);

  if (trueIsh) {
    obj[key] = true;
  } else if (falseIsh) {
    obj[key] = false;
  }
};

var fixObjectNumberKey = function fixObjectNumberKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
    return;
  }

  if (!_.isNumber(obj[key]) && _.isNumber(parseInt(obj[key]))) {
    obj[key] = parseInt(obj[key]);
  }
};

var fixObjectStringKey = function fixObjectStringKey(obj, key, defaultValue) {
  if (!_.has(obj, key)) {
    obj[key] = defaultValue;
    return;
  }
};

var sortWebListsForCollection = function sortWebListsForCollection(list, collectionName) {
  if (!_.isArray(list)) {
    return list;
  }

  if (collectionName === 'battalion') {
    var listWithFields = _.map(list, function(item) {
      fixObjectBooleanKey(item, 'isMandatory', false);
      fixObjectBooleanKey(item, 'active', true);
      fixObjectNumberKey(item, 'position', 0);
      return item;
    });

    return _.sortByOrder(listWithFields, ['isMandatory', 'active', 'position'], ['desc', 'desc', 'asc']);
  }

  //    cmd.sort = [['isMandatory', -1], ['position', 1], ['friendly_id', 1]];
  if (collectionName === 'unit') {
    var unitsListWithFields = _.map(list, function(item) {
      fixObjectBooleanKey(item, 'isMandatory', false);
      fixObjectNumberKey(item, 'position', 0);
      fixObjectStringKey(item, 'friendly_id', "");
      return item;
    });

    return _.sortByOrder(unitsListWithFields, ['isMandatory', 'position', 'friendly_id'], ['desc', 'asc', 'asc']);
  }

  // Default, return the same list
  return list;
};

var joinParentChildCollections = function(parents, childs, parent_api_id, parent_local_id, parent_name, parent_uuid, parent_dest) {
  var mapLocalIdItems = _.map(_.filter(childs, function(item) {
    return _.has(item, parent_local_id) && !_.has(item, parent_api_id);
  }), function(item) {
    return {
      id: item[parent_local_id],
      item: item
    };
  });

  var mapApiIdItems = _.map(_.filter(childs, function(item) {
    return _.has(item, parent_api_id);
  }), function(item) {
    return {
      id: item[parent_api_id],
      item: item
    };
  });

  var unmergedItems = _.flatten([mapApiIdItems, mapLocalIdItems]);
  var reducedIds = _.reduce(unmergedItems, function(memo, i) {
    if (!_.has(memo, i.id)) {
      memo[i.id] = [];
    }

    memo[i.id].push(i.item);
    return memo;
  }, {});

  _.each(parents, function(parent) {
    var itemsByParentId = [];
    var itemsByApiParentId = [];
    if (_.has(parent, 'local_id') && _.has(reducedIds, parent.local_id)) {
      itemsByParentId = reducedIds[parent.local_id];
    }

    if (_.has(parent, 'id') && _.has(reducedIds, parent.id)) {
      itemsByApiParentId = reducedIds[parent.id];
    }

    var mergedItems = _.flatten([itemsByParentId, itemsByApiParentId]);
    var enhancedItems = _.map(mergedItems, function(item) {
      item[parent_api_id] = parent.id;

      if (_.has(parent, 'name')) {
        item[parent_name] = parent.name;
      }

      if (_.has(parent, 'uuid')) {
        item[parent_uuid] = parent.uuid;
      }

      return item;
    });

    parent[parent_dest] = enhancedItems;
  });

  return parents;
};

var itemIsTrue = function itemIsTrue(item, key) {
  if (_.isUndefined(item) || _.isNull(item)) {
    return false;
  }

  if (_.isUndefined(item[key]) || _.isNull(item[key])) {
    return false;
  }

  var itemTrue = item[key] === true || item[key] === "true";
  var itemOne = item[key] === 1 || item[key] === "1";

  return itemTrue || itemOne;
};

var isAdmin = function isAdmin(item) {
  return itemIsTrue(item, 'admin');
};

var isSuper = function isSuper(item) {
  return itemIsTrue(item, 'superuser');
};

var isActive = function isActive(item) {
  return itemIsTrue(item, 'active');
};

var verifyJson = function(req, res, buf) {
  try {
    JSON.parse(buf);
  } catch (err) {
    var message = 'Invalid JSON:' + buf;
    console.log(message);
  }
};

var makeId = function makeId(length) {
  var text = "";
  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (var i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }

  return text;
};

var hasFeature = function hasFeature(dept, feature) {
  var value = 0;

  var hasKey = !_.isUndefined(dept[feature]) && !_.isNull(dept[feature]);
  var keyIsTrue = dept[feature] === true || dept[feature] === 'true';
  var keyIsOne = dept[feature] === 1 || dept[feature] === '1';

  if (hasKey && (keyIsTrue || keyIsOne)) {
    value = 1;
  }
  return value;
};

var isItemValidOnMap = function isItemValidOnMap(item) {
  var invalidDegreeLimit = 5.0;

  var parsedLat = parseInt(item.latitude);
  var parsedLon = parseInt(item.longitude);

  if (_.isNaN(parsedLat) || _.isNaN(parsedLon)) {
    return false;
  }

  if (Math.abs(parsedLat) < invalidDegreeLimit && Math.abs(parsedLon) < invalidDegreeLimit) {
    return false;
  }

  return true;
};

var stripSessionFields = function stripSessionFields(value, key) {
  var fields = ['pass', 'salt', 'when'];
  var skipFields = _.isString(key) && _.includes(fields, key.toLowerCase());
  var filterSeneca = _.isString(key) && _.trimRight(key, '$') != key;

  return filterSeneca || skipFields;
};

var cleanupUser = function cleanupUser(user) {
  return _.omit(user, stripSessionFields);
};

var resolveUser = function resolveUser(args, callback) {
  var hasSeneca = _.isObject(args) &&
    _.isObject(args.req$) &&
    _.isObject(args.req$.seneca);
  var hasSenecaUser = hasSeneca && _.isObject(args.req$.seneca.user);
  var hasArgsUser = _.isObject(args) && _.isObject(args.user);
  var hasHeaders = _.isObject(args.req$) && _.isObject(args.req$.headers);
  if (!hasSenecaUser && !hasArgsUser) {
    if (hasHeaders) {
      debug("No user. Headers were:", args.req$.headers);
    }

    debug("No hasSenecaUser and no hasArgsUser");
    return callback(null, null);
  }

  var resolvedUser = null;
  if (hasSenecaUser) {
    resolvedUser = args.req$.seneca.user;
  } else if (hasArgsUser) {
    resolvedUser = args.user;
  }

  var session = {};
  if (hasSeneca && _.isObject(args.req$.seneca.login)) {
    session = args.req$.seneca.login;
  }

  var userInactive = !itemIsTrue(resolvedUser, 'active');
  var sessionInactive = !itemIsTrue(session, 'active');
  // sessionInactive = false; // TODO: remove this once all the users are active

  if (_.isNull(resolvedUser) || userInactive || sessionInactive) {
    debug("User or session not active for", resolvedUser.nick, session.id);
    return callback(null, null, null);
  }

  var user = cleanupUser(resolvedUser);
  return callback(null, user, session);
};

var resolveLogin = function resolveLogin(args, callback) {
  if (!_.isObject(args) ||
    !_.isObject(args.req$) ||
    !_.isObject(args.req$.seneca) ||
    !_.isObject(args.req$.seneca.login) ||
    !itemIsTrue(args.req$.seneca.login, 'active')
  ) {
    return callback(null, null);
  }

  var login = cleanupUser(args.req$.seneca.login);
  return callback(null, login);
};

var getClosedOrDate = function getClosedOrDate() {
  var nowForClosedDateUnixDate = moment().valueOf() / 1000.0;
  var closedOr = [{
    closed_unix_date: 0
  }, {
    closed_unix_date: {
      $gt: nowForClosedDateUnixDate
    }
  }];

  return closedOr;
};

var extractInfoFromDevice = function extractInfoFromDevice(device) {
  var maxDaysSinceEvent = 120;
  var info = {};
  info.appVer = 'Unknown';
  info.osVer = 'Unknown';
  info.env = 'beta';
  info.daysSinceEvent = maxDaysSinceEvent; //max days

  var unixDate = moment().valueOf() / 1000.0;
  var dayAsSeconds = 60 * 60 * 24;

  if (!_.isUndefined(device.env) && !_.isNull(device.env)) {
    info.env = device.env;
  }

  if (!_.isUndefined(device.ver) && !_.isNull(device.ver)) {
    info.appVer = device.ver;
  }

  if (!_.isUndefined(device.ua) && !_.isNull(device.ua)) {
    var partsAppVer = '';
    var partsOsVer = '';

    var userAgentParts = device.ua.match(/(.*)\((.*)\)/i);

    if (userAgentParts && userAgentParts[1]) {
      partsAppVer = userAgentParts[1];
    }

    if (userAgentParts && userAgentParts[2]) {
      partsOsVer = userAgentParts[2];
    }

    if (partsAppVer.length > 0) {
      info.appVer = partsAppVer.trim();
    }

    if (partsOsVer.length > 0) {
      var osSplitParts = partsOsVer.match(/(.*);(.*);(.*)/i);

      var foundOsVer = '';
      if (osSplitParts && osSplitParts[2]) {
        foundOsVer = osSplitParts[2];
      }

      if (foundOsVer.length > 0) {
        info.osVer = foundOsVer.trim();
      }
    }
  }

  if (!_.isUndefined(device.time) && !_.isNull(device.time)) {
    var secondsAgo = unixDate - parseFloat(device.time);
    info.daysSinceEvent = Math.floor(secondsAgo / dayAsSeconds);
  }

  return info;
};

var headersToDevice = function headersToDevice(token, headers) {
  var env = 'production';
  if (_.has(headers, 'x-tc-apn-environment') &&
    headers['x-tc-apn-environment'] === 'beta') {
    env = 'beta';
  }

  var appVersion = '';
  if (_.has(headers, 'x-tc-app-version')) {
    appVersion = headers['x-tc-app-version'];
  }

  var userAgent = '';
  if (_.has(headers, 'user-agent')) {
    userAgent = headers['user-agent'];
  }

  var bundleIdentifier = 'com.simple-track.Tablet-CMD';
  if (_.has(headers, 'x-tc-bundle-identifier') &&
    _.isString(headers['x-tc-bundle-identifier']) &&
    _.size(headers['x-tc-bundle-identifier']) > 0) {
    bundleIdentifier = headers['x-tc-bundle-identifier'];
  } else if (appVersion.match(/Tablet CMD Beta/i)) {
    bundleIdentifier = 'com.simple-track.beta.Tablet-CMD';
  }

  var silentEnabled = itemIsTrue(headers, 'x-tc-silent-enabled');
  var richEnabled = itemIsTrue(headers, 'x-tc-rich-enabled');

  var unixtime = moment().valueOf() / 1000.0;
  var deviceInfo = {
    token: token,
    env: env,
    ver: appVersion,
    ua: userAgent,
    time: unixtime,
    bundleIdentifier: bundleIdentifier,
    silentEnabled: silentEnabled,
    richEnabled: richEnabled
  };
  return deviceInfo;
};

var logUserDevice = function logUserDevice(postUrl, authToken, user, session, headers) {
  var device = headersToDevice('', headers);
  var info = extractInfoFromDevice(device);

  var item = {
    userId: user.id,
    departmentId: user.departmentId,
    nick: user.nick,
    appVer: info.appVer,
    osVer: info.osVer,
    ua: device.ua,
    t: device.time,
    session: session.id
  };

  var filter = [];

  var shouldFilter = false;
  if (shouldFilter && _.contains(filter, item.appVer)) {
    return;
  }

  return requestPost(postUrl, authToken, item);
};

var requestPost = function requestPost(postUrl, authToken, item, callback) {
  if (!_.isFunction(callback)) {
    callback = function defaultCallback() {}
  }
  var reqOpts = {
    url: postUrl,
    method: 'POST',
    json: item,
    headers: {
      'x-tc-auth-token': authToken
    }
  };
  return request(reqOpts, callback);
};

var configureMomentOpts = function configureMomentOpts() {
  moment.updateLocale('en', {
    relativeTime: {
      future: "in %s",
      past: "%s ago",
      s: "%ds",
      ss: "%ds",
      m: "%dmin",
      mm: "%dmin",
      h: "%dh",
      hh: "%dh",
      d: "%dd",
      dd: "%dd",
      M: "%dmon",
      MM: "%dmon",
      y: "%dy",
      yy: "%dy"
    }
  });
};

module.exports = {
  calculateOffsetFromTime: calculateOffsetFromTime,
  fixObjectBooleanKey: fixObjectBooleanKey,
  sortWebListsForCollection: sortWebListsForCollection,
  joinParentChildCollections: joinParentChildCollections,
  itemIsTrue: itemIsTrue,
  isAdmin: isAdmin,
  isSuper: isSuper,
  isActive: isActive,
  verifyJson: verifyJson,
  makeId: makeId,
  hasFeature: hasFeature,
  isItemValidOnMap: isItemValidOnMap,
  resolveUser: resolveUser,
  resolveLogin: resolveLogin,
  getClosedOrDate: getClosedOrDate,
  cleanupUser: cleanupUser,
  extractInfoFromDevice: extractInfoFromDevice,
  headersToDevice: headersToDevice,
  logUserDevice: logUserDevice,
  configureMomentOpts: configureMomentOpts,
  requestPost: requestPost,
};
<|endoftext|>


<commit_before>// @flow

import { getPinnedParticipant, getParticipantCount } from '../base/participants';
import { isYoutubeVideoPlaying } from '../youtube-player/functions';

import { LAYOUTS } from './constants';

declare var interfaceConfig: Object;

/**
 * Returns the {@code LAYOUTS} constant associated with the layout
 * the application should currently be in.
 *
 * @param {Object} state - The redux state.
 * @returns {string}
 */
export function getCurrentLayout(state: Object) {
    if (shouldDisplayTileView(state)) {
        return LAYOUTS.TILE_VIEW;
    } else if (interfaceConfig.VERTICAL_FILMSTRIP) {
        return LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
    }

    return LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW;
}

/**
 * Returns how many columns should be displayed in tile view. The number
 * returned will be between 1 and 5, inclusive.
 *
 * @returns {number}
 */
export function getMaxColumnCount() {
    const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || 5;

    return Math.min(Math.max(configuredMax, 1), 5);
}

/**
 * Returns the cell count dimensions for tile view. Tile view tries to uphold
 * equal count of tiles for height and width, until maxColumn is reached in
 * which rows will be added but no more columns.
 *
 * @param {Object} state - The redux store state.
 * @param {number} maxColumns - The maximum number of columns that can be
 * displayed.
 * @returns {Object} An object is return with the desired number of columns,
 * rows, and visible rows (the rest should overflow) for the tile view layout.
 */
export function getTileViewGridDimensions(state: Object, maxColumns: number = getMaxColumnCount()) {
    // When in tile view mode, we must discount ourselves (the local participant) because our
    // tile is not visible.
    const { iAmRecorder } = state['features/base/config'];
    const numberOfParticipants = state['features/base/participants'].length - (iAmRecorder ? 1 : 0);

    const columnsToMaintainASquare = Math.ceil(Math.sqrt(numberOfParticipants));
    const columns = Math.min(columnsToMaintainASquare, maxColumns);
    const rows = Math.ceil(numberOfParticipants / columns);
    const visibleRows = Math.min(maxColumns, rows);

    return {
        columns,
        visibleRows
    };
}

/**
 * Selector for determining if the UI layout should be in tile view. Tile view
 * is determined by more than just having the tile view setting enabled, as
 * one-on-one calls should not be in tile view, as well as etherpad editing.
 *
 * @param {Object} state - The redux state.
 * @returns {boolean} True if tile view should be displayed.
 */
export function shouldDisplayTileView(state: Object = {}) {
    const participantCount = getParticipantCount(state);

    // In case of a lonely meeting, we don't allow tile view.
    // But it's a special case too, as we don't even render the button,
    // see TileViewButton component.
    if (participantCount < 2) {
        return false;
    }

    const { tileViewEnabled } = state['features/video-layout'];

    if (tileViewEnabled !== undefined) {
        // If the user explicitly requested a view mode, we
        // do that.
        return tileViewEnabled;
    }

    // None tile view mode is easier to calculate (no need for many negations), so we do
    // that and negate it only once.
    const shouldDisplayNormalMode = Boolean(

        // Reasons for normal mode:

        // Editing etherpad
        state['features/etherpad']?.editing

        // We pinned a participant
        || getPinnedParticipant(state)

        // It's a 1-on-1 meeting
        || participantCount < 3

        // There is a shared YouTube video in the meeting
        || isYoutubeVideoPlaying(state)
    );

    return !shouldDisplayNormalMode;
}
<commit_msg>feat: Skips the default tile view when jibri is loading.<commit_after>// @flow

import { getPinnedParticipant, getParticipantCount } from '../base/participants';
import { isYoutubeVideoPlaying } from '../youtube-player/functions';

import { LAYOUTS } from './constants';

declare var interfaceConfig: Object;

/**
 * Returns the {@code LAYOUTS} constant associated with the layout
 * the application should currently be in.
 *
 * @param {Object} state - The redux state.
 * @returns {string}
 */
export function getCurrentLayout(state: Object) {
    if (shouldDisplayTileView(state)) {
        return LAYOUTS.TILE_VIEW;
    } else if (interfaceConfig.VERTICAL_FILMSTRIP) {
        return LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
    }

    return LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW;
}

/**
 * Returns how many columns should be displayed in tile view. The number
 * returned will be between 1 and 5, inclusive.
 *
 * @returns {number}
 */
export function getMaxColumnCount() {
    const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || 5;

    return Math.min(Math.max(configuredMax, 1), 5);
}

/**
 * Returns the cell count dimensions for tile view. Tile view tries to uphold
 * equal count of tiles for height and width, until maxColumn is reached in
 * which rows will be added but no more columns.
 *
 * @param {Object} state - The redux store state.
 * @param {number} maxColumns - The maximum number of columns that can be
 * displayed.
 * @returns {Object} An object is return with the desired number of columns,
 * rows, and visible rows (the rest should overflow) for the tile view layout.
 */
export function getTileViewGridDimensions(state: Object, maxColumns: number = getMaxColumnCount()) {
    // When in tile view mode, we must discount ourselves (the local participant) because our
    // tile is not visible.
    const { iAmRecorder } = state['features/base/config'];
    const numberOfParticipants = state['features/base/participants'].length - (iAmRecorder ? 1 : 0);

    const columnsToMaintainASquare = Math.ceil(Math.sqrt(numberOfParticipants));
    const columns = Math.min(columnsToMaintainASquare, maxColumns);
    const rows = Math.ceil(numberOfParticipants / columns);
    const visibleRows = Math.min(maxColumns, rows);

    return {
        columns,
        visibleRows
    };
}

/**
 * Selector for determining if the UI layout should be in tile view. Tile view
 * is determined by more than just having the tile view setting enabled, as
 * one-on-one calls should not be in tile view, as well as etherpad editing.
 *
 * @param {Object} state - The redux state.
 * @returns {boolean} True if tile view should be displayed.
 */
export function shouldDisplayTileView(state: Object = {}) {
    const participantCount = getParticipantCount(state);

    // In case of a lonely meeting, we don't allow tile view.
    // But it's a special case too, as we don't even render the button,
    // see TileViewButton component.
    if (participantCount < 2) {
        return false;
    }

    const { tileViewEnabled } = state['features/video-layout'];

    if (tileViewEnabled !== undefined) {
        // If the user explicitly requested a view mode, we
        // do that.
        return tileViewEnabled;
    }

    const { iAmRecorder } = state['features/base/config'];

    // None tile view mode is easier to calculate (no need for many negations), so we do
    // that and negate it only once.
    const shouldDisplayNormalMode = Boolean(

        // Reasons for normal mode:

        // Editing etherpad
        state['features/etherpad']?.editing

        // We pinned a participant
        || getPinnedParticipant(state)

        // It's a 1-on-1 meeting
        || participantCount < 3

        // There is a shared YouTube video in the meeting
        || isYoutubeVideoPlaying(state)

        // We want jibri to use stage view by default
        || iAmRecorder
    );

    return !shouldDisplayNormalMode;
}
<|endoftext|>


<commit_before>/*
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

var loglevel_path = null;
var logging_handler = function( response, text_status, xhr )
{
  var self = this;
  var loggers = response.loggers;

  var levels = '

' + "\n"
             + 'null' + "\n"
             + '
' + "\n";

  for( var key in response.levels )
  {
    var level = response.levels[key].esc();
    levels += '
' + level + '
' + "\n";
  }

  levels += '
UNSET
' + "\n"
         + '
' + "\n"
         + '[x]' + "\n"
         + '

';

  var logger_tree = function( filter )
  {
    var logger_content = '';
    var filter_regex = new RegExp( '^' + filter + '\\.\\w+$' );

    for( var i in loggers )
    {
      var logger = loggers[i];
      var continue_matcher = false;

      if( !filter )
      {
        continue_matcher = logger.name.indexOf( '.' ) !== -1;
      }
      else
      {
        continue_matcher = !logger.name.match( filter_regex );
      }

      if( continue_matcher )
      {
        continue;
      }

      var logger_class = '';

      if( logger.set )
      {
        logger_class = 'set';
      }

      if( !logger.level )
      {
        logger_class = 'null';
      }

      var logger_name = logger.name.split( '.' );
      var display_name = logger_name.pop();

      var leaf_class = 'jstree-leaf';
      if( logger.level )
      {
        leaf_class += ' level-' + logger.level.esc().toLowerCase();
      }

      logger_content += '
';
      logger_content += ' ' + "\n";
      logger_content += '' + "\n";

      if( 0 !== logger_name.length )
      {
        logger_content += '' + logger_name.join( '.' ).esc() + '.';
      }

      logger_content += '' + display_name.esc() + '' + "\n";
      logger_content += '';

      logger_content += levels;

      var child_logger_content = logger_tree( logger.name );
      if( child_logger_content )
      {
        logger_content += '
';
        logger_content += child_logger_content;
        logger_content += '
';
      }

      logger_content += '
';
    }

    return logger_content;
  };

  var logger_content = '
' + "\n"
                     + '
' + response.watcher.esc() + '
' + "\n"
                     + '
' + logger_tree( null ) + '
' + "\n"
                     + '
';

  self
    .html( logger_content );

  $( 'li:last-child', this )
    .addClass( 'jstree-last' );

  $( 'li.jstree-leaf > a', this )
    .each
    (
      function( index, element )
      {
        element = $( element );
        var level = element.attr( 'rel' );

        if( level )
        {
          var selector = $( '.selector-holder', element.closest( 'li' ) );

          var trigger = $( 'a.trigger', selector );

          trigger
            .text( level.esc() );

          if( element.hasClass( 'set' ) )
          {
            trigger.first()
              .addClass( 'set' );
          }

          $( 'ul a[data-level="' + level + '"]', selector ).first()
            .addClass( 'level' );
        }
      }
    )

  $( '.trigger, .selector .close', this )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        $( '.selector-holder', $( this ).parents( 'li' ).first() ).first()
          .trigger( 'toggle' );

        return false;
      }
    );

  $( '.selector-holder', this )
    .die( 'toggle')
    .live
    (
      'toggle',
      function( event )
      {
        var row = $( this ).closest( 'li' );

        $( 'a:first', row )
          .toggleClass( 'open' );

        $( '.selector-holder:first', row )
          .toggleClass( 'open' );
      }
    );

  $( '.selector ul a', this )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        var element = $( this );

        $.ajax
        (
          {
            url : loglevel_path,
            dataType : 'json',
            data : {
              'wt' : 'json',
              'set' : $( this ).parents( 'li[data-logger]' ).data( 'logger' ) + ':' + element.data( 'level' )
            },
            type : 'POST',
            context : self,
            beforeSend : function( xhr, settings )
            {
              element
                .addClass( 'loader' );
            },
            success : logging_handler
          }
        );

        return false;
      }
    );

};

// #/~logging
sammy.get
(
  /^#\/~(logging)$/,
  function( context )
  {
    var core_basepath = $( 'li[data-basepath]', app.menu_element ).attr( 'data-basepath' );
    loglevel_path = core_basepath + '/admin/logging';
    var content_element = $( '#content' );

    content_element
      .html( '

' );

    $.ajax
    (
      {
        url : loglevel_path + '?wt=json',
        dataType : 'json',
        context : $( '#logging', content_element ),
        beforeSend : function( xhr, settings )
        {
          this
            .html( '
Loading ...
' );
        },
        success : logging_handler
      }
    );
  }
);<commit_msg>SOLR-3364 ensure that we have only one open selection at time<commit_after>/*
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

var loglevel_path = null;
var logging_handler = function( response, text_status, xhr )
{
  var self = this;
  var loggers = response.loggers;

  var levels = '

' + "\n"
             + 'null' + "\n"
             + '
' + "\n";

  for( var key in response.levels )
  {
    var level = response.levels[key].esc();
    levels += '
' + level + '
' + "\n";
  }

  levels += '
UNSET
' + "\n"
         + '
' + "\n"
         + '[x]' + "\n"
         + '

';

  var logger_tree = function( filter )
  {
    var logger_content = '';
    var filter_regex = new RegExp( '^' + filter + '\\.\\w+$' );

    for( var i in loggers )
    {
      var logger = loggers[i];
      var continue_matcher = false;

      if( !filter )
      {
        continue_matcher = logger.name.indexOf( '.' ) !== -1;
      }
      else
      {
        continue_matcher = !logger.name.match( filter_regex );
      }

      if( continue_matcher )
      {
        continue;
      }

      var logger_class = '';

      if( logger.set )
      {
        logger_class = 'set';
      }

      if( !logger.level )
      {
        logger_class = 'null';
      }

      var logger_name = logger.name.split( '.' );
      var display_name = logger_name.pop();

      var leaf_class = 'jstree-leaf';
      if( logger.level )
      {
        leaf_class += ' level-' + logger.level.esc().toLowerCase();
      }

      logger_content += '
';
      logger_content += ' ' + "\n";
      logger_content += '' + "\n";

      if( 0 !== logger_name.length )
      {
        logger_content += '' + logger_name.join( '.' ).esc() + '.';
      }

      logger_content += '' + display_name.esc() + '' + "\n";
      logger_content += '';

      logger_content += levels;

      var child_logger_content = logger_tree( logger.name );
      if( child_logger_content )
      {
        logger_content += '
';
        logger_content += child_logger_content;
        logger_content += '
';
      }

      logger_content += '
';
    }

    return logger_content;
  };

  var logger_content = '
' + "\n"
                     + '
' + response.watcher.esc() + '
' + "\n"
                     + '
' + logger_tree( null ) + '
' + "\n"
                     + '
';

  self
    .html( logger_content );

  self
    .die( 'clear' )
    .live
    (
      'clear',
      function( event )
      {
        $( '.open', this )
          .removeClass( 'open' );
      }
    );

  $( 'li:last-child', this )
    .addClass( 'jstree-last' );

  $( 'li.jstree-leaf > a', this )
    .each
    (
      function( index, element )
      {
        element = $( element );
        var level = element.attr( 'rel' );

        if( level )
        {
          var selector = $( '.selector-holder', element.closest( 'li' ) );

          var trigger = $( 'a.trigger', selector );

          trigger
            .text( level.esc() );

          if( element.hasClass( 'set' ) )
          {
            trigger.first()
              .addClass( 'set' );
          }

          $( 'ul a[data-level="' + level + '"]', selector ).first()
            .addClass( 'level' );
        }
      }
    )

  $( '.trigger', this )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        self.trigger( 'clear' );

        $( '.selector-holder', $( this ).parents( 'li' ).first() ).first()
          .trigger( 'toggle' );

        return false;
      }
    );

  $( '.selector .close', this )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        self.trigger( 'clear' );
        return false;
      }
    );

  $( '.selector-holder', this )
    .die( 'toggle')
    .live
    (
      'toggle',
      function( event )
      {
        var row = $( this ).closest( 'li' );

        $( 'a:first', row )
          .toggleClass( 'open' );

        $( '.selector-holder:first', row )
          .toggleClass( 'open' );
      }
    );

  $( '.selector ul a', this )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        var element = $( this );

        $.ajax
        (
          {
            url : loglevel_path,
            dataType : 'json',
            data : {
              'wt' : 'json',
              'set' : $( this ).parents( 'li[data-logger]' ).data( 'logger' ) + ':' + element.data( 'level' )
            },
            type : 'POST',
            context : self,
            beforeSend : function( xhr, settings )
            {
              element
                .addClass( 'loader' );
            },
            success : logging_handler
          }
        );

        return false;
      }
    );

};

// #/~logging
sammy.get
(
  /^#\/~(logging)$/,
  function( context )
  {
    var core_basepath = $( 'li[data-basepath]', app.menu_element ).attr( 'data-basepath' );
    loglevel_path = core_basepath + '/admin/logging';
    var content_element = $( '#content' );

    content_element
      .html( '

' );

    $.ajax
    (
      {
        url : loglevel_path + '?wt=json',
        dataType : 'json',
        context : $( '#logging', content_element ),
        beforeSend : function( xhr, settings )
        {
          this
            .html( '
Loading ...
' );
        },
        success : logging_handler
      }
    );
  }
);<|endoftext|>


<commit_before>// This file is concerned with altering the Firefox 4+ Add-ons Manager window,
// for those sorts of functionality we want that the API does not handle.  (As
// opposed to addons4.jsm which is responsible for what the API does handle.)
(function() {
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://greasemonkey/addons4.js");

var sortersContainer;
var sortExecuteOrderButton;
var stringBundle;

window.addEventListener('load', init, false);

// Patch the default createItem() to add our custom property.
_createItemOrig = createItem;
createItem = function GM_createItem(aObj, aIsInstall, aIsRemote) {
  var item = _createItemOrig(aObj, aIsInstall, aIsRemote);
  if ('user-script' == aObj.type) {
   // Save a reference to this richlistitem on the Addon object, so we can
   // fix the 'executionIndex' attribute if/when it changes.
   aObj.richlistitem = item;
   setRichlistitemExecutionIndex(aObj);
  }
  return item;
}

// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //

function addonIsInstalledScript(aAddon) {
  if (!aAddon) return false;
  if ('user-script' != aAddon.type) return false;
  if (aAddon._script.needsUninstall) return false;
  return true;
}

function init() {
  gViewController.commands.cmd_userscript_edit = {
      isEnabled: addonIsInstalledScript,
      doCommand: function(aAddon) { GM_openInEditor(aAddon._script); }
    };
  gViewController.commands.cmd_userscript_show = {
      isEnabled: addonIsInstalledScript,
      doCommand: function(aAddon) { GM_openFolder(aAddon._script.file); }
    };

  gViewController.commands.cmd_userscript_execute_first = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, -9999); }
    };
  gViewController.commands.cmd_userscript_execute_sooner = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, -1); }
    };
  gViewController.commands.cmd_userscript_execute_later = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, 1); }
    };
  gViewController.commands.cmd_userscript_execute_last = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, 9999); }
    };

  document.getElementById('addonitem-popup').addEventListener(
      'popupshowing', onContextPopupShowing, false);

  window.addEventListener('ViewChanged', onViewChanged, false);

  var stringBundleService = Components
      .classes['@mozilla.org/intl/stringbundle;1']
      .getService(Ci.nsIStringBundleService);
  var stringBundle = stringBundleService
      .createBundle('chrome://greasemonkey/locale/gm-addons.properties');

  // Inject this content into an XBL binding (where it can't be overlayed).
  sortExecuteOrderButton = document.createElement('button');
  sortExecuteOrderButton.setAttribute('checkState', '0');
  sortExecuteOrderButton.setAttribute('class', 'sorter');
  sortExecuteOrderButton.setAttribute(
      'label', stringBundle.GetStringFromName('executionorder'));
  sortExecuteOrderButton.setAttribute(
      'tooltiptext', stringBundle.GetStringFromName('executionorder.tooltip'));
  sortExecuteOrderButton.collapsed = true;
  sortersContainer = document.getElementById('list-sorters');
  sortersContainer.appendChild(sortExecuteOrderButton);
  sortersContainer.addEventListener('click', onSortersClicked, true);
}

function onContextPopupShowing(aEvent) {
  var popup = aEvent.target;
  var viewIsUserScripts = (
      'addons://list/user-script' == gViewController.currentViewId);
  for (var i = 0, menuitem = null; menuitem = popup.children[i]; i++) {
    var menuitemIsUserScript = ('user-script' == menuitem.getAttribute('type'));
    menuitem.collapsed = viewIsUserScripts != menuitemIsUserScript;
  }
}

function onExecuteSortCommand(aEvent) {
  // Uncheck all sorters.
  var sorters = document.getAnonymousNodes(sortersContainer);
  for (var i=0, el=null; el=sorters[i]; i++) {
    el.setAttribute('checkState', '0');
  }
  // Check our sorter.
  aEvent.target.setAttribute('checkState', 1);
  // Actually sort the elements.
  sortScriptsByExecution();
}

function onSortersClicked(aEvent) {
  if (aEvent.target == sortExecuteOrderButton) {
    onExecuteSortCommand(aEvent);
  } else {
    // When a sorter other than ours is clicked, uncheck ours.
    sortExecuteOrderButton.setAttribute('checkState', 0);
  }
}

function onViewChanged(aEvent) {
  // If we _were_ visible (execute sorter is not collapsed) ...
  if (!sortExecuteOrderButton.collapsed
    // And execute sorter is selected ...
    && (1 == sortExecuteOrderButton.getAttribute('checkState'))
  ) {
    // Deselect us.
    sortExecuteOrderButton.setAttribute('checkState', 0);
    // Select name.
    var sorters = document.getAnonymousNodes(sortersContainer);
    sorters[0].setAttribute('checkState', 1);
    // Sort by name.
    gViewController.currentViewObj.onSortChanged('name', true);
  }
  // Hide the execute order sorter, when the view is not ours.
  sortExecuteOrderButton.collapsed =
      'addons://list/user-script' != gViewController.currentViewId;
};

function sortScriptsByExecution() {
  if (1 != sortExecuteOrderButton.getAttribute('checkState')) {
    return;
  }
  var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
    getService(Ci.nsIXULSortService);
  sortService.sort(gListView._listBox, 'executionIndex', 'ascending');
}

function reorderScriptExecution(aAddon, moveBy) {
  GM_getConfig().move(aAddon._script, moveBy);
  AddonManager.getAddonsByTypes(['user-script'], function(aAddons) {
      // Fix all the 'executionOrder' attributes.
      for (var i=0, addon=null; addon=aAddons[i]; i++) {
        setRichlistitemExecutionIndex(addon);
      }
      // Re-sort the list, with these fixed attributes.
      sortScriptsByExecution();
      // Ensure the selected element is still visible.
      var richlistbox = document.getElementById('addon-list');
      richlistbox.ensureElementIsVisible(richlistbox.currentItem);
    });
}

function setRichlistitemExecutionIndex(aAddon) {
  aAddon.richlistitem.setAttribute('executionIndex',
      // String format with leading zeros, so it will sort properly.
      ('0000' + aAddon.executionIndex).substr(-5));
}
})();
<commit_msg>Uninstall scripts, on closing Add Ons Manager.<commit_after>// This file is concerned with altering the Firefox 4+ Add-ons Manager window,
// for those sorts of functionality we want that the API does not handle.  (As
// opposed to addons4.jsm which is responsible for what the API does handle.)
(function() {
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://greasemonkey/addons4.js");

var sortersContainer;
var sortExecuteOrderButton;
var stringBundle;

window.addEventListener('load', init, false);
window.addEventListener('unload', unload, false);

// Patch the default createItem() to add our custom property.
_createItemOrig = createItem;
createItem = function GM_createItem(aObj, aIsInstall, aIsRemote) {
  var item = _createItemOrig(aObj, aIsInstall, aIsRemote);
  if ('user-script' == aObj.type) {
   // Save a reference to this richlistitem on the Addon object, so we can
   // fix the 'executionIndex' attribute if/when it changes.
   aObj.richlistitem = item;
   setRichlistitemExecutionIndex(aObj);
  }
  return item;
};

// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //

function addonIsInstalledScript(aAddon) {
  if (!aAddon) return false;
  if ('user-script' != aAddon.type) return false;
  if (aAddon._script.needsUninstall) return false;
  return true;
};

function init() {
  gViewController.commands.cmd_userscript_edit = {
      isEnabled: addonIsInstalledScript,
      doCommand: function(aAddon) { GM_openInEditor(aAddon._script); }
    };
  gViewController.commands.cmd_userscript_show = {
      isEnabled: addonIsInstalledScript,
      doCommand: function(aAddon) { GM_openFolder(aAddon._script.file); }
    };

  gViewController.commands.cmd_userscript_execute_first = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, -9999); }
    };
  gViewController.commands.cmd_userscript_execute_sooner = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, -1); }
    };
  gViewController.commands.cmd_userscript_execute_later = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, 1); }
    };
  gViewController.commands.cmd_userscript_execute_last = {
      isEnabled: function() { return true; },
      doCommand: function(aAddon) { reorderScriptExecution(aAddon, 9999); }
    };

  document.getElementById('addonitem-popup').addEventListener(
      'popupshowing', onContextPopupShowing, false);

  window.addEventListener('ViewChanged', onViewChanged, false);

  var stringBundleService = Components
      .classes['@mozilla.org/intl/stringbundle;1']
      .getService(Ci.nsIStringBundleService);
  var stringBundle = stringBundleService
      .createBundle('chrome://greasemonkey/locale/gm-addons.properties');

  // Inject this content into an XBL binding (where it can't be overlayed).
  sortExecuteOrderButton = document.createElement('button');
  sortExecuteOrderButton.setAttribute('checkState', '0');
  sortExecuteOrderButton.setAttribute('class', 'sorter');
  sortExecuteOrderButton.setAttribute(
      'label', stringBundle.GetStringFromName('executionorder'));
  sortExecuteOrderButton.setAttribute(
      'tooltiptext', stringBundle.GetStringFromName('executionorder.tooltip'));
  sortExecuteOrderButton.collapsed = true;
  sortersContainer = document.getElementById('list-sorters');
  sortersContainer.appendChild(sortExecuteOrderButton);
  sortersContainer.addEventListener('click', onSortersClicked, true);
};

function onContextPopupShowing(aEvent) {
  var popup = aEvent.target;
  var viewIsUserScripts = (
      'addons://list/user-script' == gViewController.currentViewId);
  for (var i = 0, menuitem = null; menuitem = popup.children[i]; i++) {
    var menuitemIsUserScript = ('user-script' == menuitem.getAttribute('type'));
    menuitem.collapsed = viewIsUserScripts != menuitemIsUserScript;
  }
};

function onExecuteSortCommand(aEvent) {
  // Uncheck all sorters.
  var sorters = document.getAnonymousNodes(sortersContainer);
  for (var i=0, el=null; el=sorters[i]; i++) {
    el.setAttribute('checkState', '0');
  }
  // Check our sorter.
  aEvent.target.setAttribute('checkState', 1);
  // Actually sort the elements.
  sortScriptsByExecution();
};

function onSortersClicked(aEvent) {
  if (aEvent.target == sortExecuteOrderButton) {
    onExecuteSortCommand(aEvent);
  } else {
    // When a sorter other than ours is clicked, uncheck ours.
    sortExecuteOrderButton.setAttribute('checkState', 0);
  }
};

function onViewChanged(aEvent) {
  // If we _were_ visible (execute sorter is not collapsed) ...
  if (!sortExecuteOrderButton.collapsed
    // And execute sorter is selected ...
    && (1 == sortExecuteOrderButton.getAttribute('checkState'))
  ) {
    // Deselect us.
    sortExecuteOrderButton.setAttribute('checkState', 0);
    // Select name.
    var sorters = document.getAnonymousNodes(sortersContainer);
    sorters[0].setAttribute('checkState', 1);
    // Sort by name.
    gViewController.currentViewObj.onSortChanged('name', true);
  }
  // Hide the execute order sorter, when the view is not ours.
  sortExecuteOrderButton.collapsed =
      'addons://list/user-script' != gViewController.currentViewId;
};

function sortScriptsByExecution() {
  if (1 != sortExecuteOrderButton.getAttribute('checkState')) {
    return;
  }
  var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
    getService(Ci.nsIXULSortService);
  sortService.sort(gListView._listBox, 'executionIndex', 'ascending');
};

function reorderScriptExecution(aAddon, moveBy) {
  GM_getConfig().move(aAddon._script, moveBy);
  AddonManager.getAddonsByTypes(['user-script'], function(aAddons) {
      // Fix all the 'executionOrder' attributes.
      for (var i = 0, addon = null; addon = aAddons[i]; i++) {
        setRichlistitemExecutionIndex(addon);
      }
      // Re-sort the list, with these fixed attributes.
      sortScriptsByExecution();
      // Ensure the selected element is still visible.
      var richlistbox = document.getElementById('addon-list');
      richlistbox.ensureElementIsVisible(richlistbox.currentItem);
    });
};

function setRichlistitemExecutionIndex(aAddon) {
  aAddon.richlistitem.setAttribute('executionIndex',
      // String format with leading zeros, so it will sort properly.
      ('0000' + aAddon.executionIndex).substr(-5));
};

function unload() {
  var GM_config = GM_getConfig();
  // Since .getAddonsByTypes() is asynchronous, AddonManager gets destroyed
  // by the time the callback runs.  Cache this value we need from it.
  var pending_uninstall = AddonManager.PENDING_UNINSTALL;

  AddonManager.getAddonsByTypes(['user-script'], function(aAddons) {
      for (var i = 0, addon = null; addon = aAddons[i]; i++) {
        if (addon.pendingOperations & pending_uninstall) {
          // Todo: This without dipping into private members.
          GM_config.uninstall(addon._script);
        }
      }
      // Guarantee that the config.xml is saved to disk.
      // Todo: This without dipping into private members.
      GM_config._save(true);
    });
};
})();
<|endoftext|>


<commit_before><commit_msg>Switch back to a custom onTapLink and limit ‘[href]’ selector to only anchors<commit_after><|endoftext|>


<commit_before>import TopComponent from '../../components/TopComponent/TopComponent';
import Table from '../../components/Table/Table';
import BackButton from '../../components/BackButton/BackButton';

import './Scoreboard.scss';
import Button from '../../components/Button/Button';

const TMP_DATA = {
    head: [
        'User',
        'Singleplayer',
        'Multiplayer'
    ],
    body: [
        ['test', 1000, 1000],
        ['test2', 500, 500],
        ['test3', 300, 300]
    ]
};

export default class Scoreboard extends TopComponent {
    constructor() {
        super('div', {class: 'content__scoreboard'});
        this.isFull = false;
        this.moreButton = null;
    }

    rerender() {
        if (this._components) {
            this._components.forEach(element => element.remove());
        }
        this.build();
    }

    build() {
        this.table = new Table(TMP_DATA, 'content__scoreboard__table');
        this._buildMoreButton();

        this._components = [
            this.table,
            this.moreButton,
            new BackButton()
        ];
        this._components.forEach(element => this.append(element.render()));
        this.renderTo('content');
    }

    _rerenderMoreButton() {
        const _table = this.table.getElement();
        this.moreButton.getElement().setAttribute('class', _table.rows.length % 2 ? 'button-more_odd' : 'button-more_even');
    }

    _initButton() {
        this.moreButton = new Button({text: '. . .'});
        this.moreButton.getElement().addMultiEvents('click touchend', () => {
            this.table.addRow(TMP_DATA.body);
            this._rerenderMoreButton();
        });
    }

    _buildMoreButton() {
        if (!this.isFull) {
            if (!this.moreButton) {
                this._initButton();
            }
            this._rerenderMoreButton();
        }
    }
}
<commit_msg>Real data<commit_after>import TopComponent from '../../components/TopComponent/TopComponent';
import Table from '../../components/Table/Table';
import BackButton from '../../components/BackButton/BackButton';
import Transport from '../../modules/Transport/Transport';

import './Scoreboard.scss';
import Button from '../../components/Button/Button';

const TMP_DATA = {
    head: [
        'User',
        'Singleplayer',
        'Multiplayer'
    ],
    body: [
    ]
};

const SCORE_COUNT = 5;

export default class Scoreboard extends TopComponent {
    constructor() {
        super('div', {class: 'content__scoreboard'});
        this.isFull = false;
        this.moreButton = null;
        this.count = 0;
    }

    rerender() {
        if (this._components) {
            this._components.forEach(element => element.remove());
        }
        this.build();
    }

    build() {
        this.table = new Table(TMP_DATA, 'content__scoreboard__table');
        this._buildMoreButton();
        this.addRows();

        this._components = [
            this.table,
            this.moreButton,
            new BackButton()
        ];
        this._components.forEach(element => this.append(element.render()));
        this.renderTo('content');
    }

    async getRows() {
        const rows = [];
        const result = await Transport.get(`/stop?limit=${SCORE_COUNT}&since=${this.count}`);
        result.forEach(({login, sscore, mscore}) => {
            rows.push([login, sscore, mscore]);
        });

        this.count += rows.length;
        return rows;
    }

    async addRows() {
        const rows = await this.getRows();
        this.table.addRow(rows);
        this._rerenderMoreButton();
    }

    _rerenderMoreButton() {
        const _table = this.table.getElement();
        this.moreButton.getElement().setAttribute('class', _table.rows.length % 2 ? 'button-more_odd' : 'button-more_even');
    }

    _initButton() {
        this.moreButton = new Button({text: '. . .'});
        this.moreButton.getElement().addMultiEvents('click touchend', () => {
            this.addRows();
        });
    }

    _buildMoreButton() {
        if (!this.isFull) {
            if (!this.moreButton) {
                this._initButton();
            }
            this._rerenderMoreButton();
        }
    }
}
<|endoftext|>


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

export default class Image extends Component {
  constructor(props){
    super(props);
    this.state = {
      naturalSizeList: {}
    }

    this.newImageId = 1;

    this.$wrapper = null;
  }

  componentDidMount(){
    this.$wrapper = $(this.refs.wrapper);
    if(this.props.data.count > 1){
      this.$wrapper
        .sortable({
  			  opacity: 0.8,
          stop: (ev, ui) => {
            const objValues = {};
            this.props.values.forEach(image => objValues[image.id] = image);

            const newValues = [];
            this.$wrapper.find(".image-wrapper").each((key, elem) => {
              const id = elem.getAttribute("data-id");
              newValues.push(objValues[id]);
            })

            this.props.onValueChange({
              code: this.props.data.code,
              values: newValues,
            });
          }
        })
    }
  }

  onLoadImage(e){
    const img = e.currentTarget;
    const newList = {...this.state.naturalSizeList};
    newList[img.getAttribute("data-id")] = {
      width: img.naturalWidth,
      height: img.naturalHeight,
    }
    this.setState({"naturalSizeList": newList});
  }

  displayNaturalSize(image){
    if(this.state.naturalSizeList[image.id]){
      return (

          W:{this.state.naturalSizeList[image.id].width}
          H:{this.state.naturalSizeList[image.id].height}

      )
    } else {
      return null;
    }
  }

  onClickRemove(e){
    const imgId = e.currentTarget.getAttribute("data-id");
    this.props.onValueChange({
      code: this.props.data.code,
      values: this.props.values.filter(image => image.id != imgId),
    });
  }

  onChangeInput(e){
    const input = e.currentTarget;
    if (input.files && input.files.length > 0) {
        for (var i = 0; i < input.files.length; i++) {
          const imageId = 'new_' + this.newImageId++;
          const reader = new FileReader();
          const file = input.files[i];
          reader.onload = (e) => {
            this.props.onValueChange({
              code: this.props.data.code,
              values: [...this.props.values, {id: imageId, path: e.target.result, file: file}],
            });
          }
          reader.readAsDataURL(file)
        }
    }
  }

  render() {
    return (

        <input className="form-control" type="file" onChange={e => this.onChangeInput(e)} multiple />

          {this.props.values.map(image => {
            return (

                  <button data-id={image.id} onClick={e => this.onClickRemove(e)} className="btn btn-danger btn-sm" type="button">

                  </button>

                   this.onLoadImage(e)}
                    src={image.path} data-id={image.id}
                    style={{
                      maxHeight: this.props.thumbHeight,
                      maxWidth: this.props.thumbWidth,
                      display: 'block',
                      margin: 'auto',
                      width: 'auto',
                      height: 'auto',
                    }}
                  />

                {this.displayNaturalSize(image)}

            )
          })}

    )
  }
}

Image.defaultProps = {
  thumbHeight: "180px",
  thumbWidth: "180px",
  onClickRemove: ()=>{},
}
<commit_msg>使い勝手を考えて画像のIDはユニークにしました。<commit_after>import React, { Component } from 'react'

export default class Image extends Component {
  constructor(props){
    super(props);
    this.state = {
      naturalSizeList: {}
    }

    this.$wrapper = null;
  }

  componentDidMount(){
    this.$wrapper = $(this.refs.wrapper);
    if(this.props.data.count > 1){
      this.$wrapper
        .sortable({
  			  opacity: 0.8,
          stop: (ev, ui) => {
            const objValues = {};
            this.props.values.forEach(image => objValues[image.id] = image);

            const newValues = [];
            this.$wrapper.find(".image-wrapper").each((key, elem) => {
              const id = elem.getAttribute("data-id");
              newValues.push(objValues[id]);
            })

            this.props.onValueChange({
              code: this.props.data.code,
              values: newValues,
            });
          }
        })
    }
  }

  onLoadImage(e){
    const img = e.currentTarget;
    const newList = {...this.state.naturalSizeList};
    newList[img.getAttribute("data-id")] = {
      width: img.naturalWidth,
      height: img.naturalHeight,
    }
    this.setState({"naturalSizeList": newList});
  }

  displayNaturalSize(image){
    if(this.state.naturalSizeList[image.id]){
      return (

          W:{this.state.naturalSizeList[image.id].width}
          H:{this.state.naturalSizeList[image.id].height}

      )
    } else {
      return null;
    }
  }

  onClickRemove(e){
    const imgId = e.currentTarget.getAttribute("data-id");
    this.props.onValueChange({
      code: this.props.data.code,
      values: this.props.values.filter(image => image.id != imgId),
    });
  }

  onChangeInput(e){
    const input = e.currentTarget;
    if (input.files && input.files.length > 0) {
        for (var i = 0; i < input.files.length; i++) {
          const imageId = 'new_' + Image.newId++;
          const reader = new FileReader();
          const file = input.files[i];
          reader.onload = (e) => {
            this.props.onValueChange({
              code: this.props.data.code,
              values: [...this.props.values, {id: imageId, path: e.target.result, file: file}],
            });
          }
          reader.readAsDataURL(file)
        }
    }
  }

  render() {
    return (

        <input className="form-control" type="file" onChange={e => this.onChangeInput(e)} multiple />

          {this.props.values.map(image => {
            return (

                  <button data-id={image.id} onClick={e => this.onClickRemove(e)} className="btn btn-danger btn-sm" type="button">

                  </button>

                   this.onLoadImage(e)}
                    src={image.path} data-id={image.id}
                    style={{
                      maxHeight: this.props.thumbHeight,
                      maxWidth: this.props.thumbWidth,
                      display: 'block',
                      margin: 'auto',
                      width: 'auto',
                      height: 'auto',
                    }}
                  />

                {this.displayNaturalSize(image)}

            )
          })}

    )
  }
}

Image.defaultProps = {
  thumbHeight: "180px",
  thumbWidth: "180px",
  onClickRemove: ()=>{},
}

//画像はPOST時のキー名がidになるので全体でユニークである必要があります。
Image.newId = 1;
<|endoftext|>


<commit_before>var db = require('./connectordb');
var uuid = require('node-uuid');
var crypto = require('crypto');

function getDeviceInfo() {
    return db.findOne({name: 'skynet-mindwave-connector'})
        .then(function (deviceInfo) {
            if (deviceInfo) {
                console.log('found device info');
                return deviceInfo;
            }
            var deviceInfo = {
                name: "skynet-mindwave-connector",
                type: "device",
                deviceType: "mindwave",
                uuid: uuid.v1(),
                token: crypto.createHash('sha1').update((new Date()).valueOf().toString() + Math.random().toString()).digest('hex')
            };

            console.log('storing device info', deviceInfo);
            return db.insert(deviceInfo)
                .then(function () {
                    return db.findOne({name: 'skynet-mindwave-connector'});
                });

        });
}

module.exports = {
    getDeviceInfo : getDeviceInfo
};<commit_msg>Adding the ability to set an OWNER_UUID as an environment variable so it will be registered to a Meshblu resource.<commit_after>var db = require('./connectordb');
var uuid = require('node-uuid');
var crypto = require('crypto');

function getDeviceInfo() {
    return db.findOne({name: 'skynet-mindwave-connector'})
        .then(function (deviceInfo) {

            if (deviceInfo) {
                console.log('found device info');
                return deviceInfo;
            }

            var deviceInfo = {
                name: "skynet-mindwave-connector",
                type: "device",
                subtype: "mindwave",
                uuid: uuid.v1(),
                token: crypto.createHash('sha1').update((new Date()).valueOf().toString() + Math.random().toString()).digest('hex')
            };

            if(process.env.OWNER_UUID){
                deviceInfo.owner = process.env.OWNER_UUID;
            }

            console.log('storing device info', deviceInfo);
            return db.insert(deviceInfo)
                .then(function () {
                    return db.findOne({name: 'skynet-mindwave-connector'});
                });

        });
}

module.exports = {
    getDeviceInfo : getDeviceInfo
};<|endoftext|>


<commit_before>var https = require("https");
var urln = require("url");

var GitHubApiV3 = function(token, repo) {
    if (!token || !repo) {
        throw new Error('Not enough arguments');
    }
    this.validateToken(token);
    this.validateRepo(repo);

    this.token = token;
    this.repo = repo;
}

GitHubApiV3.prototype.makeApiRequest = function(url, callback) {
    var options = {
        headers: {
            Authorization: 'token ' + this.token,
            'User-Agent': 'release.gh3.io'
        },
        hostname: 'api.github.com',
        path: url
    };

    var req = https.request(options, function(res) {
        var body = '';
        if (res.statusCode === '200') {

        }
        res.on('data', function(d) {
                body += d;
            }
        );
        res.on('end', function() {
            callback(null, body);
        })
    });
    req.on('error', function(err) {
        callback(err, null);
    });
    req.end();
}
GitHubApiV3.prototype.validateToken = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }

    if (valid && v.length !== 40) {
        valid = false;
    }

    if (!valid) {
        throw new Error('token is invalid', v);
    }
    return valid;
}
GitHubApiV3.prototype.validateRepo = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }

    if (valid && !v.length) {
       valid = false;
    }
    if (!valid) {
        throw new Error('repo is invalid', v);
    }

    return valid;
}

GitHubApiV3.prototype.validateDiff = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }
    if (valid && !v.length) {
       valid = false;
    }
    if (valid && v.indexOf('...') === -1) {
       valid = false;
    }
    if (!valid) {
        throw new Error('diff is invalid', v);
    }

    return valid;
}

GitHubApiV3.prototype.getNotes = function(diff) {
    this.validateDiff(diff);
    var url = ['/repos/', this.repo, '/compare/', diff].join('');
    this.makeApiRequest(url, function(err, res) {
        if (err) {
            console.error(err);
        }
        if (res) {
            this.getCommentsUrlsFromDiff(JSON.parse(res));
        }
    }.bind(this))
}

GitHubApiV3.prototype.getCommentsUrlsFromDiff = function(diff) {
    diff.commits.filter(function getNotEmptyComments(commit) {
        return commit.commit.comment_count > 0;
    })
    .map(function getCommentUrl(comment) {
        return comment.comments_url;
    })
    .forEach(function getCommentBody(comment_url) {
        var url = urln.parse(comment_url)['pathname'];
        return this.makeApiRequest(url, function onCommentBody(err, res) {
            if (err) {
                console.log('Comment body fetch failed. url:', url, 'Err:', err)
            }
            var comments = JSON.parse(res);
            comments.forEach(function(comment) {
                console.log(comment.body.trim());
            });
        });
    }, this);
}

module.exports = GitHubApiV3;
<commit_msg>Diff validator: minimum length added<commit_after>var https = require("https");
var urln = require("url");

var GitHubApiV3 = function(token, repo) {
    if (!token || !repo) {
        throw new Error('Not enough arguments');
    }
    this.validateToken(token);
    this.validateRepo(repo);

    this.token = token;
    this.repo = repo;
}

GitHubApiV3.prototype.makeApiRequest = function(url, callback) {
    var options = {
        headers: {
            Authorization: 'token ' + this.token,
            'User-Agent': 'release.gh3.io'
        },
        hostname: 'api.github.com',
        path: url
    };

    var req = https.request(options, function(res) {
        var body = '';
        if (res.statusCode === '200') {

        }
        res.on('data', function(d) {
                body += d;
            }
        );
        res.on('end', function() {
            callback(null, body);
        })
    });
    req.on('error', function(err) {
        callback(err, null);
    });
    req.end();
}
GitHubApiV3.prototype.validateToken = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }

    if (valid && v.length !== 40) {
        valid = false;
    }

    if (!valid) {
        throw new Error('token is invalid', v);
    }
    return valid;
}
GitHubApiV3.prototype.validateRepo = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }

    if (valid && !v.length) {
       valid = false;
    }
    if (!valid) {
        throw new Error('repo is invalid', v);
    }

    return valid;
}

GitHubApiV3.prototype.validateDiff = function(v) {
    var valid = true;
    if (!v) {
        valid = false;
    }
    if (valid && v.length < 5) {
       valid = false;
    }
    if (valid && v.indexOf('...') === -1) {
       valid = false;
    }
    if (!valid) {
        throw new Error('diff is invalid', v);
    }

    return valid;
}

GitHubApiV3.prototype.getNotes = function(diff) {
    this.validateDiff(diff);
    var url = ['/repos/', this.repo, '/compare/', diff].join('');
    this.makeApiRequest(url, function(err, res) {
        if (err) {
            console.error(err);
        }
        if (res) {
            this.getCommentsUrlsFromDiff(JSON.parse(res));
        }
    }.bind(this))
}

GitHubApiV3.prototype.getCommentsUrlsFromDiff = function(diff) {
    diff.commits.filter(function getNotEmptyComments(commit) {
        return commit.commit.comment_count > 0;
    })
    .map(function getCommentUrl(comment) {
        return comment.comments_url;
    })
    .forEach(function getCommentBody(comment_url) {
        var url = urln.parse(comment_url)['pathname'];
        return this.makeApiRequest(url, function onCommentBody(err, res) {
            if (err) {
                console.log('Comment body fetch failed. url:', url, 'Err:', err)
            }
            var comments = JSON.parse(res);
            comments.forEach(function(comment) {
                console.log(comment.body.trim());
            });
        });
    }, this);
}

module.exports = GitHubApiV3;
<|endoftext|>