<commit_before>define([
	"dojo/_base/declare", "dijit/_WidgetBase", "dojo/on",
	"dojo/dom-class", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
	"dojo/text!./templates/SelectionToGroup.html", "dojo/_base/lang",
	"../WorkspaceManager", "dojo/dom-style", "dojo/parser", "dijit/form/Select", "./WorkspaceFilenameValidationTextBox",
	"./WorkspaceObjectSelector"
], function(declare, WidgetBase, on,
			domClass, Templated, WidgetsInTemplate,
			Template, lang, WorkspaceManager, domStyle, Parser, Select, WorkspaceFilenameValidationTextBox,
			WorkspaceObjectSelector){
	return declare([WidgetBase, Templated, WidgetsInTemplate], {
		"baseClass": "Panel",
		"disabled": false,
		templateString: Template,
		selection: null,
		path: null,
		type: "genome_group",
		idType: null,
		inputType: null,
		conversionTypes: {
			"feature_data": [{label: "Feature", value: "feature_group"}, {label: "Genome",value: "genome_group"}]
		},
		selectType: false,
		_setTypeAttr: function(t){
			this.type = t;
			if(this.workspaceObjectSelector){
				this.workspaceObjectSelector.set("type", [t]);
			}
		},

		_setPathAttr: function(path){
			this.path = path;

			if(this.workspaceObjectSelector){
				this.workspaceObjectSelector.set("path", this.path);
			}

			// for new groups
			if(this.groupPathSelector){
				this.groupPathSelector.set("path", '/'+window.App.user.id);
				this.groupPathSelector.set("value", this.path);
			}
			if(this.groupNameBox){
				this.groupNameBox.set('path', this.path);
			}
		},
		onChangeOutputType: function(){
			this.set('type', this.groupTypeSelect.get('value'));
			this.set("path", WorkspaceManager.getDefaultFolder(this.type));
			this.onChangeTarget(this.type);
			if(this.type == "genome_group"){
				this.idType = "genome_id";
			}
			else if(this.type == "feature_group"){
				this.idType = "feature_id";
			}
		},

		// only used for new groups
		onChangeGroupPath: function(newPath, thing) {
			// need to update path of group name box since validation
			// and value (full path) state is kept and fetched from there.
			this.groupNameBox.set('path', newPath);
		},

		onChangeTarget: function(target){
			if(!this._started){
				return;
			}
			var targetType = this.targetType.get('value');
			var val;

			if(targetType == "existing"){
				domClass.remove(this.workspaceObjectSelector.domNode, "dijitHidden");

				// only if new group
				domClass.add(this.groupPathLabel, "dijitHidden");
				domClass.add(this.groupPathSelector.domNode, "dijitHidden");
				domClass.add(this.groupNameBox.domNode, "dijitHidden");

				val = this.workspaceObjectSelector.get('value');

			}else{
				domClass.add(this.workspaceObjectSelector.domNode, "dijitHidden");

				// only if new group
				domClass.remove(this.groupPathLabel, "dijitHidden");
				domClass.remove(this.groupPathSelector.domNode, "dijitHidden");
				domClass.remove(this.groupNameBox.domNode, "dijitHidden");

				val = this.groupNameBox.isValid() ? this.groupNameBox.get('value') : false;
			}
			//console.log("Target Val: ", val);
			this.value = val;
			if(val){
				this.copyButton.set('disabled', false);
			}else{
				this.copyButton.set('disabled', true);
			}
		},
		startup: function(){
			var _self = this;
			if(this._started){
				return;
			}
			var currentIcon;
			this.watch("selection", lang.hitch(this, function(prop, oldVal, item){
				console.log("set selection(): ", arguments);
			}));
			if(!this.path){

				this.path = WorkspaceManager.getDefaultFolder(this.type)
				this.set("path", WorkspaceManager.getDefaultFolder(this.type));

			}
			this.inherited(arguments);
			//this.groupNameBox.set('path', this.path);  // removing because redundant?
			//this.workspaceObjectSelector.set('path', this.path); //removing because redundant?
			//this.workspaceObjectSelector.set('type', [this.type]); // removing because redundant?
			if(this.inputType in this.conversionTypes){
				this.selectType = true;
				domClass.remove(this.groupTypeBox, "dijitHidden");
				this.groupTypeSelect.set("options", this.conversionTypes[this.inputType]);
				this.groupTypeSelect.set("value", this.conversionTypes[this.inputType][0]["value"]);
				this.groupTypeSelect.set("displayedValue", this.conversionTypes[this.inputType][0]["label"]);
			}
		},

		onCancel: function(evt){
			on.emit(this.domNode, "dialogAction", {action: "close", bubbles: true});
		},
		onCopy: function(evt){

			if(!this.idType){
				this.idType = "genome_id";
				if(this.type == "genome_group"){
					this.idType = "genome_id";
				}
				else if(this.type == "feature_group"){
					this.idType = "feature_id";
				}
				else if(this.type == "experiment_group"){
					this.idType = "eid";
				}
			}
			var def;
			if(this.targetType.get("value") == "existing"){
				def = WorkspaceManager.addToGroup(this.value, this.idType, this.selection.filter(lang.hitch(this, function(d){
					return this.idType in d
				})).map(lang.hitch(this, function(o){
					return o[this.idType];
				})));
			}else{
				def = WorkspaceManager.createGroup(this.value, this.type, this.path, this.idType, this.selection.filter(lang.hitch(this, function(d){
					return this.idType in d
				})).map(lang.hitch(this, function(o){
					return o[this.idType];
				})));
			}
			def.then(lang.hitch(this, function(){
				on.emit(this.domNode, "dialogAction", {action: "close", bubbles: true});
			}));
		}

	});
});
<commit_msg>remove unneeded lines<commit_after>define([
	"dojo/_base/declare", "dijit/_WidgetBase", "dojo/on",
	"dojo/dom-class", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
	"dojo/text!./templates/SelectionToGroup.html", "dojo/_base/lang",
	"../WorkspaceManager", "dojo/dom-style", "dojo/parser", "dijit/form/Select", "./WorkspaceFilenameValidationTextBox",
	"./WorkspaceObjectSelector"
], function(declare, WidgetBase, on,
			domClass, Templated, WidgetsInTemplate,
			Template, lang, WorkspaceManager, domStyle, Parser, Select, WorkspaceFilenameValidationTextBox,
			WorkspaceObjectSelector){
	return declare([WidgetBase, Templated, WidgetsInTemplate], {
		"baseClass": "Panel",
		"disabled": false,
		templateString: Template,
		selection: null,
		path: null,
		type: "genome_group",
		idType: null,
		inputType: null,
		conversionTypes: {
			"feature_data": [{label: "Feature", value: "feature_group"}, {label: "Genome",value: "genome_group"}]
		},
		selectType: false,
		_setTypeAttr: function(t){
			this.type = t;
			if(this.workspaceObjectSelector){
				this.workspaceObjectSelector.set("type", [t]);
			}
		},

		_setPathAttr: function(path){
			this.path = path;

			if(this.workspaceObjectSelector){
				this.workspaceObjectSelector.set("path", this.path);
			}

			// for new groups
			if(this.groupPathSelector){
				this.groupPathSelector.set("path", '/'+window.App.user.id);
				this.groupPathSelector.set("value", this.path);
			}
			if(this.groupNameBox){
				this.groupNameBox.set('path', this.path);
			}
		},
		onChangeOutputType: function(){
			this.set('type', this.groupTypeSelect.get('value'));
			this.set("path", WorkspaceManager.getDefaultFolder(this.type));
			this.onChangeTarget(this.type);
			if(this.type == "genome_group"){
				this.idType = "genome_id";
			}
			else if(this.type == "feature_group"){
				this.idType = "feature_id";
			}
		},

		// only used for new groups
		onChangeGroupPath: function(newPath, thing) {
			// need to update path of group name box since validation
			// and value (full path) state is kept and fetched from there.
			this.groupNameBox.set('path', newPath);
		},

		onChangeTarget: function(target){
			if(!this._started){
				return;
			}
			var targetType = this.targetType.get('value');
			var val;

			if(targetType == "existing"){
				domClass.remove(this.workspaceObjectSelector.domNode, "dijitHidden");

				// only if new group
				domClass.add(this.groupPathLabel, "dijitHidden");
				domClass.add(this.groupPathSelector.domNode, "dijitHidden");
				domClass.add(this.groupNameBox.domNode, "dijitHidden");

				val = this.workspaceObjectSelector.get('value');

			}else{
				domClass.add(this.workspaceObjectSelector.domNode, "dijitHidden");

				// only if new group
				domClass.remove(this.groupPathLabel, "dijitHidden");
				domClass.remove(this.groupPathSelector.domNode, "dijitHidden");
				domClass.remove(this.groupNameBox.domNode, "dijitHidden");

				val = this.groupNameBox.isValid() ? this.groupNameBox.get('value') : false;
			}
			//console.log("Target Val: ", val);
			this.value = val;
			if(val){
				this.copyButton.set('disabled', false);
			}else{
				this.copyButton.set('disabled', true);
			}
		},
		startup: function(){
			var _self = this;
			if(this._started){
				return;
			}
			var currentIcon;
			this.watch("selection", lang.hitch(this, function(prop, oldVal, item){
				console.log("set selection(): ", arguments);
			}));
			if(!this.path){

				this.path = WorkspaceManager.getDefaultFolder(this.type)
				this.set("path", WorkspaceManager.getDefaultFolder(this.type));

			}
			this.inherited(arguments);

			if(this.inputType in this.conversionTypes){
				this.selectType = true;
				domClass.remove(this.groupTypeBox, "dijitHidden");
				this.groupTypeSelect.set("options", this.conversionTypes[this.inputType]);
				this.groupTypeSelect.set("value", this.conversionTypes[this.inputType][0]["value"]);
				this.groupTypeSelect.set("displayedValue", this.conversionTypes[this.inputType][0]["label"]);
			}
		},

		onCancel: function(evt){
			on.emit(this.domNode, "dialogAction", {action: "close", bubbles: true});
		},
		onCopy: function(evt){

			if(!this.idType){
				this.idType = "genome_id";
				if(this.type == "genome_group"){
					this.idType = "genome_id";
				}
				else if(this.type == "feature_group"){
					this.idType = "feature_id";
				}
				else if(this.type == "experiment_group"){
					this.idType = "eid";
				}
			}
			var def;
			if(this.targetType.get("value") == "existing"){
				def = WorkspaceManager.addToGroup(this.value, this.idType, this.selection.filter(lang.hitch(this, function(d){
					return this.idType in d
				})).map(lang.hitch(this, function(o){
					return o[this.idType];
				})));
			}else{
				def = WorkspaceManager.createGroup(this.value, this.type, this.path, this.idType, this.selection.filter(lang.hitch(this, function(d){
					return this.idType in d
				})).map(lang.hitch(this, function(o){
					return o[this.idType];
				})));
			}
			def.then(lang.hitch(this, function(){
				on.emit(this.domNode, "dialogAction", {action: "close", bubbles: true});
			}));
		}

	});
});
<|endoftext|>


<commit_before>var utils = {
  // pulls resources from containers or the ground
  collectResource: function(creep) {
    let target = creep.pos.findClosestByPath(FIND_DROPPED_RESOURCES, { filter: res => res.resourceType === RESOURCE_ENERGY });
    if(target) {
      switch(creep.pickup(target)) {
        case ERR_NOT_IN_RANGE:
          creep.doMove(target, {visualizePathStyle: {stroke: '#00aaff'}})
        case OK:
          return;
      }
    }

    // look for full containers
    target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_CONTAINER && bldg.store[RESOURCE_ENERGY] === bldg.storageCapacity});

    // look for any non-empty containers
    if(!target) {
      target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_CONTAINER && bldg.store[RESOURCE_ENERGY] > 0 });
    }

    // upgraders can also pull from extensions.
    // this ensures that upgrades continue
    if(!target && creep.memory.role === 'upgrader') {
      target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_EXTENSION && bldg.energy > 0 });
    }

    if(target) {
      if(creep.withdraw(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
        creep.doMove(target, {visualizePathStyle: {stroke: '#00aaff'}});
      }
    }
  },

  getBodyForRole: function(role) {
    switch(role) {
      case 'soldier':
        return [MOVE,MOVE,MOVE,ATTACK,ATTACK];
      case 'hauler':
        return [MOVE,MOVE,MOVE,CARRY,CARRY,CARRY];
      case 'harvester':
        return [MOVE,CARRY,WORK,WORK,WORK];
      default:
        return [MOVE,MOVE,CARRY,WORK,WORK];
    }
  },

  cleanupMemory: function(creepName) {
    const oldMem = Memory.creeps[creepName];

    if(oldMem.role === 'harvester' && oldMem.reservedSource) {
      Memory.sources[oldMem.reservedSource].recalc = true;
    }

    delete Memory.creeps[creepName];
    console.log('Clearing non-existing creep memory:', creepName);
  }
}

module.exports = utils;
<commit_msg>Make soldiers TOUGHer<commit_after>var utils = {
  // pulls resources from containers or the ground
  collectResource: function(creep) {
    let target = creep.pos.findClosestByPath(FIND_DROPPED_RESOURCES, { filter: res => res.resourceType === RESOURCE_ENERGY });
    if(target) {
      switch(creep.pickup(target)) {
        case ERR_NOT_IN_RANGE:
          creep.doMove(target, {visualizePathStyle: {stroke: '#00aaff'}})
        case OK:
          return;
      }
    }

    // look for full containers
    target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_CONTAINER && bldg.store[RESOURCE_ENERGY] === bldg.storageCapacity});

    // look for any non-empty containers
    if(!target) {
      target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_CONTAINER && bldg.store[RESOURCE_ENERGY] > 0 });
    }

    // upgraders can also pull from extensions.
    // this ensures that upgrades continue
    if(!target && creep.memory.role === 'upgrader') {
      target = creep.pos.findClosestByPath(FIND_STRUCTURES, { filter: bldg => bldg.structureType === STRUCTURE_EXTENSION && bldg.energy > 0 });
    }

    if(target) {
      if(creep.withdraw(target, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
        creep.doMove(target, {visualizePathStyle: {stroke: '#00aaff'}});
      }
    }
  },

  getBodyForRole: function(role) {
    switch(role) {
      case 'soldier':
        return [MOVE,MOVE,TOUGH,ATTACK,ATTACK];
      case 'hauler':
        return [MOVE,MOVE,MOVE,CARRY,CARRY,CARRY];
      case 'harvester':
        return [MOVE,CARRY,WORK,WORK,WORK];
      default:
        return [MOVE,MOVE,CARRY,WORK,WORK];
    }
  },

  cleanupMemory: function(creepName) {
    const oldMem = Memory.creeps[creepName];

    if(oldMem.role === 'harvester' && oldMem.reservedSource) {
      Memory.sources[oldMem.reservedSource].recalc = true;
    }

    delete Memory.creeps[creepName];
    console.log('Clearing non-existing creep memory:', creepName);
  }
}

module.exports = utils;
<|endoftext|>


<commit_before>const Eris = require("eris");
const bot = require("./bot");
const moment = require("moment");
const humanizeDuration = require("humanize-duration");
const publicIp = require("public-ip");
const config = require("./cfg");

class BotError extends Error {}

const userMentionRegex = /^<@!?([0-9]+?)>$/;

let inboxGuild = null;
let mainGuilds = [];
let logChannel = null;

/**
 * @returns {Eris~Guild}
 */
function getInboxGuild() {
  if (! inboxGuild) inboxGuild = bot.guilds.find(g => g.id === config.inboxServerId);
  if (! inboxGuild) throw new BotError("The bot is not on the modmail (inbox) server!");
  return inboxGuild;
}

/**
 * @returns {Eris~Guild[]}
 */
function getMainGuilds() {
  if (mainGuilds.length === 0) {
    mainGuilds = bot.guilds.filter(g => config.mainServerId.includes(g.id));
  }

  if (mainGuilds.length !== config.mainServerId.length) {
    if (config.mainServerId.length === 1) {
      console.warn("[WARN] The bot hasn't joined the main guild!");
    } else {
      console.warn("[WARN] The bot hasn't joined one or more main guilds!");
    }
  }

  return mainGuilds;
}

/**
 * Returns the designated log channel, or the default channel if none is set
 * @returns {Eris~TextChannel}
 */
function getLogChannel() {
  const _inboxGuild = getInboxGuild();
  const _logChannel = _inboxGuild.channels.get(config.logChannelId);

  if (! _logChannel) {
    throw new BotError("Log channel (logChannelId) not found!");
  }

  if (! (_logChannel instanceof Eris.TextChannel)) {
    throw new BotError("Make sure the logChannelId option is set to a text channel!");
  }

  return _logChannel;
}

function postLog(...args) {
  return getLogChannel().createMessage(...args);
}

function postError(channel, str, opts = {}) {
  return channel.createMessage({
    ...opts,
    content: `⚠ ${str}`
  });
}

/**
 * Returns whether the given member has permission to use modmail commands
 * @param {Eris.Member} member
 * @returns {boolean}
 */
function isStaff(member) {
  if (! member) return false;
  if (config.inboxServerPermission.length === 0) return true;
  if (member.guild.ownerID === member.id) return true;

  return config.inboxServerPermission.some(perm => {
    if (isSnowflake(perm)) {
      // If perm is a snowflake, check it against the member's user id and roles
      if (member.id === perm) return true;
      if (member.roles.includes(perm)) return true;
    } else {
      // Otherwise assume perm is the name of a permission
      return member.permission.has(perm);
    }

    return false;
  });
}

/**
 * Returns whether the given message is on the inbox server
 * @param msg
 * @returns {boolean}
 */
function messageIsOnInboxServer(msg) {
  if (! msg.channel.guild) return false;
  if (msg.channel.guild.id !== getInboxGuild().id) return false;
  return true;
}

/**
 * Returns whether the given message is on the main server
 * @param msg
 * @returns {boolean}
 */
function messageIsOnMainServer(msg) {
  if (! msg.channel.guild) return false;

  return getMainGuilds()
    .some(g => msg.channel.guild.id === g.id);
}

/**
 * @param attachment
 * @returns {Promise<string>}
 */
async function formatAttachment(attachment, attachmentUrl) {
  let filesize = attachment.size || 0;
  filesize /= 1024;

  return `**Attachment:** ${attachment.filename} (${filesize.toFixed(1)}KB)\n${attachmentUrl}`;
}

/**
 * Returns the user ID of the user mentioned in str, if any
 * @param {String} str
 * @returns {String|null}
 */
function getUserMention(str) {
  if (! str) return null;

  str = str.trim();

  if (isSnowflake(str)) {
    // User ID
    return str;
  } else {
    let mentionMatch = str.match(userMentionRegex);
    if (mentionMatch) return mentionMatch[1];
  }

  return null;
}

/**
 * Returns the current timestamp in an easily readable form
 * @returns {String}
 */
function getTimestamp(...momentArgs) {
  return moment.utc(...momentArgs).format("HH:mm");
}

/**
 * Disables link previews in the given string by wrapping links in < >
 * @param {String} str
 * @returns {String}
 */
function disableLinkPreviews(str) {
  return str.replace(/(^|[^<])(https?:\/\/\S+)/ig, "$1<$2>");
}

/**
 * Returns a URL to the bot's web server
 * @param {String} path
 * @returns {Promise<String>}
 */
async function getSelfUrl(path = "") {
  if (config.url) {
    return `${config.url}/${path}`;
  } else {
    const port = config.port || 8890;
    const ip = await publicIp.v4();
    return `http://${ip}:${port}/${path}`;
  }
}

/**
 * Returns the highest hoisted role of the given member
 * @param {Eris~Member} member
 * @returns {Eris~Role}
 */
function getMainRole(member) {
  const roles = member.roles.map(id => member.guild.roles.get(id));
  roles.sort((a, b) => a.position > b.position ? -1 : 1);
  return roles.find(r => r.hoist);
}

/**
 * Splits array items into chunks of the specified size
 * @param {Array|String} items
 * @param {Number} chunkSize
 * @returns {Array}
 */
function chunk(items, chunkSize) {
  const result = [];

  for (let i = 0; i < items.length; i += chunkSize) {
    result.push(items.slice(i, i + chunkSize));
  }

  return result;
}

/**
 * Trims every line in the string
 * @param {String} str
 * @returns {String}
 */
function trimAll(str) {
  return str
    .split("\n")
    .map(_str => _str.trim())
    .join("\n");
}

const delayStringRegex = /^([0-9]+)(?:([dhms])[a-z]*)?/i;

/**
 * Turns a "delay string" such as "1h30m" to milliseconds
 * @param {String} str
 * @returns {Number|null}
 */
function convertDelayStringToMS(str) {
  let match;
  let ms = 0;

  str = str.trim();

  while (str !== "" && (match = str.match(delayStringRegex)) !== null) {
    if (match[2] === "d") ms += match[1] * 1000 * 60 * 60 * 24;
    else if (match[2] === "h") ms += match[1] * 1000 * 60 * 60;
    else if (match[2] === "s") ms += match[1] * 1000;
    else if (match[2] === "m" || ! match[2]) ms += match[1] * 1000 * 60;

    str = str.slice(match[0].length);
  }

  // Invalid delay string
  if (str !== "") {
    return null;
  }

  return ms;
}

function getInboxMention() {
  const mentionRoles = Array.isArray(config.mentionRole) ? config.mentionRole : [config.mentionRole];
  const mentions = [];
  for (const role of mentionRoles) {
    if (role == null) continue;
    else if (role === "here") mentions.push("@here");
    else if (role === "everyone") mentions.push("@everyone");
    else mentions.push(`<@&${role}>`);
  }
  return mentions.join(" ") + " ";
}

function getInboxMentionAllowedMentions() {
  const mentionRoles = Array.isArray(config.mentionRole) ? config.mentionRole : [config.mentionRole];
  const allowedMentions = {
    everyone: false,
    roles: [],
  };

  for (const role of mentionRoles) {
    if (role == null || role === "none" || role === "") continue;
    else if (role === "here" || role === "everyone") allowedMentions.everyone = true;
    else allowedMentions.roles.push(role);
  }

  return allowedMentions;
}

function postSystemMessageWithFallback(channel, thread, text) {
  if (thread) {
    thread.postSystemMessage(text);
  } else {
    channel.createMessage(text);
  }
}

/**
 * A normalized way to set props in data models, fixing some inconsistencies between different DB drivers in knex
 * @param {Object} target
 * @param {Object} props
 */
function setDataModelProps(target, props) {
  for (const prop in props) {
    if (! props.hasOwnProperty(prop)) continue;
    // DATETIME fields are always returned as Date objects in MySQL/MariaDB
    if (props[prop] instanceof Date) {
      // ...even when NULL, in which case the date's set to unix epoch
      if (props[prop].getUTCFullYear() === 1970) {
        target[prop] = null;
      } else {
        // Set the value as a string in the same format it's returned in SQLite
        target[prop] = moment.utc(props[prop]).format("YYYY-MM-DD HH:mm:ss");
      }
    } else {
      target[prop] = props[prop];
    }
  }
}

const snowflakeRegex = /^[0-9]{17,}$/;
function isSnowflake(str) {
  return str && snowflakeRegex.test(str);
}

const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: " and "}, opts));

const markdownCharsRegex = /([\\_*|`~])/g;
function escapeMarkdown(str) {
  return str.replace(markdownCharsRegex, "\\$1");
}

function disableInlineCode(str) {
  return str.replace(/`/g, "'");
}

function disableCodeBlocks(str) {
  return str.replace(/`/g, "`\u200b");
}

function readMultilineConfigValue(str) {
  return Array.isArray(str) ? str.join("\n") : str;
}

function noop() {}

// https://discord.com/developers/docs/resources/channel#create-message-params
const MAX_MESSAGE_CONTENT_LENGTH = 2000;

// https://discord.com/developers/docs/resources/channel#embed-limits
const MAX_EMBED_CONTENT_LENGTH = 6000;

/**
 * Checks if the given message content is within Discord's message length limits.
 *
 * Based on testing, Discord appears to enforce length limits (at least in the client)
 * the same way JavaScript does, using the UTF-16 byte count as the number of characters.
 *
 * @param {string|Eris.MessageContent} content
 */
function messageContentIsWithinMaxLength(content) {
  if (typeof content === "string") {
    content = { content };
  }

  if (content.content && content.content.length > MAX_MESSAGE_CONTENT_LENGTH) {
    return false;
  }

  if (content.embed) {
    let embedContentLength = 0;

    if (content.embed.title) embedContentLength += content.embed.title.length;
    if (content.embed.description) embedContentLength += content.embed.description.length;
    if (content.embed.footer && content.embed.footer.text) {
      embedContentLength += content.embed.footer.text.length;
    }
    if (content.embed.author && content.embed.author.name) {
      embedContentLength += content.embed.author.name.length;
    }

    if (content.embed.fields) {
      for (const field of content.embed.fields) {
        if (field.title) embedContentLength += field.name.length;
        if (field.description) embedContentLength += field.value.length;
      }
    }

    if (embedContentLength > MAX_EMBED_CONTENT_LENGTH) {
      return false;
    }
  }

  return true;
}

/**
 * Splits a string into chunks, preferring to split at a newline
 * @param {string} str
 * @param {number} [maxChunkLength=2000]
 * @returns {string[]}
 */
function chunkByLines(str, maxChunkLength = 2000) {
  if (str.length < maxChunkLength) {
    return [str];
  }

  const chunks = [];

  while (str.length) {
    if (str.length <= maxChunkLength) {
      chunks.push(str);
      break;
    }

    const slice = str.slice(0, maxChunkLength);

    const lastLineBreakIndex = slice.lastIndexOf("\n");
    if (lastLineBreakIndex === -1) {
      chunks.push(str.slice(0, maxChunkLength));
      str = str.slice(maxChunkLength);
    } else {
      chunks.push(str.slice(0, lastLineBreakIndex));
      str = str.slice(lastLineBreakIndex + 1);
    }
  }

  return chunks;
}

/**
 * Chunks a long message to multiple smaller messages, retaining leading and trailing line breaks, open code blocks, etc.
 *
 * Default maxChunkLength is 1990, a bit under the message length limit of 2000, so we have space to add code block
 * shenanigans to the start/end when needed. Take this into account when choosing a custom maxChunkLength as well.
 */
function chunkMessageLines(str, maxChunkLength = 1990) {
  const chunks = chunkByLines(str, maxChunkLength);
  let openCodeBlock = false;

  return chunks.map(_chunk => {
    // If the chunk starts with a newline, add an invisible unicode char so Discord doesn't strip it away
    if (_chunk[0] === "\n") _chunk = "\u200b" + _chunk;
    // If the chunk ends with a newline, add an invisible unicode char so Discord doesn't strip it away
    if (_chunk[_chunk.length - 1] === "\n") _chunk = _chunk + "\u200b";
    // If the previous chunk had an open code block, open it here again
    if (openCodeBlock) {
      openCodeBlock = false;
      if (_chunk.startsWith("```")) {
        // Edge case: chunk starts with a code block delimiter, e.g. the previous chunk and this one were split right before the end of a code block
        // Fix: just strip the code block delimiter away from here, we don't need it anymore
        _chunk = _chunk.slice(3);
      } else {
        _chunk = "```" + _chunk;
      }
    }
    // If the chunk has an open code block, close it and open it again in the next chunk
    const codeBlockDelimiters = _chunk.match(/```/g);
    if (codeBlockDelimiters && codeBlockDelimiters.length % 2 !== 0) {
      _chunk += "```";
      openCodeBlock = true;
    }

    return _chunk;
  });
}

module.exports = {
  BotError,

  getInboxGuild,
  getMainGuilds,
  getLogChannel,
  postError,
  postLog,

  isStaff,
  messageIsOnInboxServer,
  messageIsOnMainServer,

  formatAttachment,

  getUserMention,
  getTimestamp,
  disableLinkPreviews,
  getSelfUrl,
  getMainRole,
  delayStringRegex,
  convertDelayStringToMS,
  getInboxMention,
  getInboxMentionAllowedMentions,
  postSystemMessageWithFallback,

  chunk,
  trimAll,

  setDataModelProps,

  isSnowflake,

  humanizeDelay,

  escapeMarkdown,
  disableInlineCode,
  disableCodeBlocks,

  readMultilineConfigValue,

  messageContentIsWithinMaxLength,
  chunkMessageLines,

  noop,
};
<commit_msg>Allow 'off' to disable mentionRole<commit_after>const Eris = require("eris");
const bot = require("./bot");
const moment = require("moment");
const humanizeDuration = require("humanize-duration");
const publicIp = require("public-ip");
const config = require("./cfg");

class BotError extends Error {}

const userMentionRegex = /^<@!?([0-9]+?)>$/;

let inboxGuild = null;
let mainGuilds = [];
let logChannel = null;

/**
 * @returns {Eris~Guild}
 */
function getInboxGuild() {
  if (! inboxGuild) inboxGuild = bot.guilds.find(g => g.id === config.inboxServerId);
  if (! inboxGuild) throw new BotError("The bot is not on the modmail (inbox) server!");
  return inboxGuild;
}

/**
 * @returns {Eris~Guild[]}
 */
function getMainGuilds() {
  if (mainGuilds.length === 0) {
    mainGuilds = bot.guilds.filter(g => config.mainServerId.includes(g.id));
  }

  if (mainGuilds.length !== config.mainServerId.length) {
    if (config.mainServerId.length === 1) {
      console.warn("[WARN] The bot hasn't joined the main guild!");
    } else {
      console.warn("[WARN] The bot hasn't joined one or more main guilds!");
    }
  }

  return mainGuilds;
}

/**
 * Returns the designated log channel, or the default channel if none is set
 * @returns {Eris~TextChannel}
 */
function getLogChannel() {
  const _inboxGuild = getInboxGuild();
  const _logChannel = _inboxGuild.channels.get(config.logChannelId);

  if (! _logChannel) {
    throw new BotError("Log channel (logChannelId) not found!");
  }

  if (! (_logChannel instanceof Eris.TextChannel)) {
    throw new BotError("Make sure the logChannelId option is set to a text channel!");
  }

  return _logChannel;
}

function postLog(...args) {
  return getLogChannel().createMessage(...args);
}

function postError(channel, str, opts = {}) {
  return channel.createMessage({
    ...opts,
    content: `⚠ ${str}`
  });
}

/**
 * Returns whether the given member has permission to use modmail commands
 * @param {Eris.Member} member
 * @returns {boolean}
 */
function isStaff(member) {
  if (! member) return false;
  if (config.inboxServerPermission.length === 0) return true;
  if (member.guild.ownerID === member.id) return true;

  return config.inboxServerPermission.some(perm => {
    if (isSnowflake(perm)) {
      // If perm is a snowflake, check it against the member's user id and roles
      if (member.id === perm) return true;
      if (member.roles.includes(perm)) return true;
    } else {
      // Otherwise assume perm is the name of a permission
      return member.permission.has(perm);
    }

    return false;
  });
}

/**
 * Returns whether the given message is on the inbox server
 * @param msg
 * @returns {boolean}
 */
function messageIsOnInboxServer(msg) {
  if (! msg.channel.guild) return false;
  if (msg.channel.guild.id !== getInboxGuild().id) return false;
  return true;
}

/**
 * Returns whether the given message is on the main server
 * @param msg
 * @returns {boolean}
 */
function messageIsOnMainServer(msg) {
  if (! msg.channel.guild) return false;

  return getMainGuilds()
    .some(g => msg.channel.guild.id === g.id);
}

/**
 * @param attachment
 * @returns {Promise<string>}
 */
async function formatAttachment(attachment, attachmentUrl) {
  let filesize = attachment.size || 0;
  filesize /= 1024;

  return `**Attachment:** ${attachment.filename} (${filesize.toFixed(1)}KB)\n${attachmentUrl}`;
}

/**
 * Returns the user ID of the user mentioned in str, if any
 * @param {String} str
 * @returns {String|null}
 */
function getUserMention(str) {
  if (! str) return null;

  str = str.trim();

  if (isSnowflake(str)) {
    // User ID
    return str;
  } else {
    let mentionMatch = str.match(userMentionRegex);
    if (mentionMatch) return mentionMatch[1];
  }

  return null;
}

/**
 * Returns the current timestamp in an easily readable form
 * @returns {String}
 */
function getTimestamp(...momentArgs) {
  return moment.utc(...momentArgs).format("HH:mm");
}

/**
 * Disables link previews in the given string by wrapping links in < >
 * @param {String} str
 * @returns {String}
 */
function disableLinkPreviews(str) {
  return str.replace(/(^|[^<])(https?:\/\/\S+)/ig, "$1<$2>");
}

/**
 * Returns a URL to the bot's web server
 * @param {String} path
 * @returns {Promise<String>}
 */
async function getSelfUrl(path = "") {
  if (config.url) {
    return `${config.url}/${path}`;
  } else {
    const port = config.port || 8890;
    const ip = await publicIp.v4();
    return `http://${ip}:${port}/${path}`;
  }
}

/**
 * Returns the highest hoisted role of the given member
 * @param {Eris~Member} member
 * @returns {Eris~Role}
 */
function getMainRole(member) {
  const roles = member.roles.map(id => member.guild.roles.get(id));
  roles.sort((a, b) => a.position > b.position ? -1 : 1);
  return roles.find(r => r.hoist);
}

/**
 * Splits array items into chunks of the specified size
 * @param {Array|String} items
 * @param {Number} chunkSize
 * @returns {Array}
 */
function chunk(items, chunkSize) {
  const result = [];

  for (let i = 0; i < items.length; i += chunkSize) {
    result.push(items.slice(i, i + chunkSize));
  }

  return result;
}

/**
 * Trims every line in the string
 * @param {String} str
 * @returns {String}
 */
function trimAll(str) {
  return str
    .split("\n")
    .map(_str => _str.trim())
    .join("\n");
}

const delayStringRegex = /^([0-9]+)(?:([dhms])[a-z]*)?/i;

/**
 * Turns a "delay string" such as "1h30m" to milliseconds
 * @param {String} str
 * @returns {Number|null}
 */
function convertDelayStringToMS(str) {
  let match;
  let ms = 0;

  str = str.trim();

  while (str !== "" && (match = str.match(delayStringRegex)) !== null) {
    if (match[2] === "d") ms += match[1] * 1000 * 60 * 60 * 24;
    else if (match[2] === "h") ms += match[1] * 1000 * 60 * 60;
    else if (match[2] === "s") ms += match[1] * 1000;
    else if (match[2] === "m" || ! match[2]) ms += match[1] * 1000 * 60;

    str = str.slice(match[0].length);
  }

  // Invalid delay string
  if (str !== "") {
    return null;
  }

  return ms;
}

function getInboxMention() {
  const mentionRoles = Array.isArray(config.mentionRole) ? config.mentionRole : [config.mentionRole];
  const mentions = [];
  for (const role of mentionRoles) {
    if (role == null || role === "none" || role === "off" || role === "") continue;
    else if (role === "here") mentions.push("@here");
    else if (role === "everyone") mentions.push("@everyone");
    else mentions.push(`<@&${role}>`);
  }
  return mentions.join(" ") + " ";
}

function getInboxMentionAllowedMentions() {
  const mentionRoles = Array.isArray(config.mentionRole) ? config.mentionRole : [config.mentionRole];
  const allowedMentions = {
    everyone: false,
    roles: [],
  };

  for (const role of mentionRoles) {
    if (role == null || role === "none" || role === "") continue;
    else if (role === "here" || role === "everyone") allowedMentions.everyone = true;
    else allowedMentions.roles.push(role);
  }

  return allowedMentions;
}

function postSystemMessageWithFallback(channel, thread, text) {
  if (thread) {
    thread.postSystemMessage(text);
  } else {
    channel.createMessage(text);
  }
}

/**
 * A normalized way to set props in data models, fixing some inconsistencies between different DB drivers in knex
 * @param {Object} target
 * @param {Object} props
 */
function setDataModelProps(target, props) {
  for (const prop in props) {
    if (! props.hasOwnProperty(prop)) continue;
    // DATETIME fields are always returned as Date objects in MySQL/MariaDB
    if (props[prop] instanceof Date) {
      // ...even when NULL, in which case the date's set to unix epoch
      if (props[prop].getUTCFullYear() === 1970) {
        target[prop] = null;
      } else {
        // Set the value as a string in the same format it's returned in SQLite
        target[prop] = moment.utc(props[prop]).format("YYYY-MM-DD HH:mm:ss");
      }
    } else {
      target[prop] = props[prop];
    }
  }
}

const snowflakeRegex = /^[0-9]{17,}$/;
function isSnowflake(str) {
  return str && snowflakeRegex.test(str);
}

const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: " and "}, opts));

const markdownCharsRegex = /([\\_*|`~])/g;
function escapeMarkdown(str) {
  return str.replace(markdownCharsRegex, "\\$1");
}

function disableInlineCode(str) {
  return str.replace(/`/g, "'");
}

function disableCodeBlocks(str) {
  return str.replace(/`/g, "`\u200b");
}

function readMultilineConfigValue(str) {
  return Array.isArray(str) ? str.join("\n") : str;
}

function noop() {}

// https://discord.com/developers/docs/resources/channel#create-message-params
const MAX_MESSAGE_CONTENT_LENGTH = 2000;

// https://discord.com/developers/docs/resources/channel#embed-limits
const MAX_EMBED_CONTENT_LENGTH = 6000;

/**
 * Checks if the given message content is within Discord's message length limits.
 *
 * Based on testing, Discord appears to enforce length limits (at least in the client)
 * the same way JavaScript does, using the UTF-16 byte count as the number of characters.
 *
 * @param {string|Eris.MessageContent} content
 */
function messageContentIsWithinMaxLength(content) {
  if (typeof content === "string") {
    content = { content };
  }

  if (content.content && content.content.length > MAX_MESSAGE_CONTENT_LENGTH) {
    return false;
  }

  if (content.embed) {
    let embedContentLength = 0;

    if (content.embed.title) embedContentLength += content.embed.title.length;
    if (content.embed.description) embedContentLength += content.embed.description.length;
    if (content.embed.footer && content.embed.footer.text) {
      embedContentLength += content.embed.footer.text.length;
    }
    if (content.embed.author && content.embed.author.name) {
      embedContentLength += content.embed.author.name.length;
    }

    if (content.embed.fields) {
      for (const field of content.embed.fields) {
        if (field.title) embedContentLength += field.name.length;
        if (field.description) embedContentLength += field.value.length;
      }
    }

    if (embedContentLength > MAX_EMBED_CONTENT_LENGTH) {
      return false;
    }
  }

  return true;
}

/**
 * Splits a string into chunks, preferring to split at a newline
 * @param {string} str
 * @param {number} [maxChunkLength=2000]
 * @returns {string[]}
 */
function chunkByLines(str, maxChunkLength = 2000) {
  if (str.length < maxChunkLength) {
    return [str];
  }

  const chunks = [];

  while (str.length) {
    if (str.length <= maxChunkLength) {
      chunks.push(str);
      break;
    }

    const slice = str.slice(0, maxChunkLength);

    const lastLineBreakIndex = slice.lastIndexOf("\n");
    if (lastLineBreakIndex === -1) {
      chunks.push(str.slice(0, maxChunkLength));
      str = str.slice(maxChunkLength);
    } else {
      chunks.push(str.slice(0, lastLineBreakIndex));
      str = str.slice(lastLineBreakIndex + 1);
    }
  }

  return chunks;
}

/**
 * Chunks a long message to multiple smaller messages, retaining leading and trailing line breaks, open code blocks, etc.
 *
 * Default maxChunkLength is 1990, a bit under the message length limit of 2000, so we have space to add code block
 * shenanigans to the start/end when needed. Take this into account when choosing a custom maxChunkLength as well.
 */
function chunkMessageLines(str, maxChunkLength = 1990) {
  const chunks = chunkByLines(str, maxChunkLength);
  let openCodeBlock = false;

  return chunks.map(_chunk => {
    // If the chunk starts with a newline, add an invisible unicode char so Discord doesn't strip it away
    if (_chunk[0] === "\n") _chunk = "\u200b" + _chunk;
    // If the chunk ends with a newline, add an invisible unicode char so Discord doesn't strip it away
    if (_chunk[_chunk.length - 1] === "\n") _chunk = _chunk + "\u200b";
    // If the previous chunk had an open code block, open it here again
    if (openCodeBlock) {
      openCodeBlock = false;
      if (_chunk.startsWith("```")) {
        // Edge case: chunk starts with a code block delimiter, e.g. the previous chunk and this one were split right before the end of a code block
        // Fix: just strip the code block delimiter away from here, we don't need it anymore
        _chunk = _chunk.slice(3);
      } else {
        _chunk = "```" + _chunk;
      }
    }
    // If the chunk has an open code block, close it and open it again in the next chunk
    const codeBlockDelimiters = _chunk.match(/```/g);
    if (codeBlockDelimiters && codeBlockDelimiters.length % 2 !== 0) {
      _chunk += "```";
      openCodeBlock = true;
    }

    return _chunk;
  });
}

module.exports = {
  BotError,

  getInboxGuild,
  getMainGuilds,
  getLogChannel,
  postError,
  postLog,

  isStaff,
  messageIsOnInboxServer,
  messageIsOnMainServer,

  formatAttachment,

  getUserMention,
  getTimestamp,
  disableLinkPreviews,
  getSelfUrl,
  getMainRole,
  delayStringRegex,
  convertDelayStringToMS,
  getInboxMention,
  getInboxMentionAllowedMentions,
  postSystemMessageWithFallback,

  chunk,
  trimAll,

  setDataModelProps,

  isSnowflake,

  humanizeDelay,

  escapeMarkdown,
  disableInlineCode,
  disableCodeBlocks,

  readMultilineConfigValue,

  messageContentIsWithinMaxLength,
  chunkMessageLines,

  noop,
};
<|endoftext|>


<commit_before>var tags = {};
var xhttp = new XMLHttpRequest();

function scrollToBody() {
	console.log("scrolling to body");
	$("html, body").animate({
		scrollTop: $("#body").offset().top
	}, 1000);
}

$(document).ready(function(){
	$(window).resize();

    showAllPostPreviews();
    var hash = decodeURI(window.location.hash);
    if (hash) {
        showPost(hash.substring(1, hash.length))
    }
});

$(window).resize(function(){
	if ($(this).width() < 1000) {
		$("#otherInfos").css("width", "90%");
		$("#content").css("width", "90%");
	} else {
		$("#otherInfos").css("width", "25%");
		$("#content").css("width", "60%");
	}
});

function showPostsPreviews(posts) {
	$("#shareLink").html("");
    if (posts != null) {
        var html = "";
        for (i in posts) {
			xhttp.onreadystatechange = function() {
			    if (this.readyState == 4 && this.status == 200) {
					var info = JSON.parse(xhttp.responseText);
		            if (info == null) {
		                return;
		            }

		            html += "

";
		            html += info.title;
		            html += "

";
		            html += info.timestamp;
		            html += "

";
		            html += info.extract;
		            html += " Read more...

Tags: ";
		            for (j in info.tags) {
		                html += "";
		                html += info.tags[j] + ", ";

		                // add tags to tags
		                if (tags[info.tags[j]] == null) {
		                    tags[info.tags[j]] = [posts[i]];
		                } else {
		                    if ($.inArray(posts[i], tags[info.tags[j]]) == -1) {
		                        tags[info.tags[j]].push(posts[i]);
		                    }
		                }
		            }
		            html = html.substring(0, html.length - 2);
		            html += "

";
			    }
			};
            xhttp.open("GET", posts[i] + "/info.json", true);
            xhttp.send();
        }

        if (html != "") {
            $("#content").html(html);
        }
    } else {
        // no posts
    }
}

function showAllPostPreviews() {
	xhttp.onreadystatechange = function() {
	    if (this.readyState == 4 && this.status == 200) {
			var posts = JSON.parse(xhttp.responseText).posts;
			showPostsPreviews(posts);
		}
	};
    xhttp.open("GET", "postSum.json", true);
    xhttp.send();

}

function showTagPostsPreviews(tagName) {
	showPostsPreviews(tags[tagName]);
	$("#content").html("
Tag: " + tagName + "
" + $("#content").html());
}

function showPost(name) {
	var html = "";
	html = $("
").append($("#" + name).clone()).html();
	$("#content").html(html);
	$("#" + name + ">.postTitle").css("font-size", "40px");

	xhttp.onreadystatechange = function() {
	    if (this.readyState == 4 && this.status == 200) {
			if (xhttp.responseText != null) {
				var escapedContent = xhttp.responseText.replace(/&/g, "&")
									.replace(/\n/g, "
")
									.replace(/        /g, "        ")
									.replace(/\t/g, "        ")
									//.replace(/</g, "<")
									//.replace(/>/g, ">")
									//.replace(/"/g, """)
									.replace(/'/g, "'")
									;

				$("#content>#" + name + ">.postExtract").html("
"+ escapedContent + "
");
				$("#shareLink").html("http://blog.zhukaihan.com/index.html#" + name);
		    } else {
		        $("#content>#" + name + ">.postExtract").html("
No Such Article.
");
		    }
		}
	};
	xhttp.open("GET", name + "/content", true);
	xhttp.send();

	scrollToBody();
}
<commit_msg>Update Code<commit_after>var tags = {};

function scrollToBody() {
	console.log("scrolling to body");
	$("html, body").animate({
		scrollTop: $("#body").offset().top
	}, 1000);
}

$(document).ready(function(){
	$(window).resize();

    showAllPostPreviews();
    var hash = decodeURI(window.location.hash);
    if (hash) {
        showPost(hash.substring(1, hash.length))
    }
});

$(window).resize(function(){
	if ($(this).width() < 1000) {
		$("#otherInfos").css("width", "90%");
		$("#content").css("width", "90%");
	} else {
		$("#otherInfos").css("width", "25%");
		$("#content").css("width", "60%");
	}
});

function showPostsPreviews(posts) {
	$("#shareLink").html("");
    if (posts != null) {
        var html = "";
        for (i in posts) {
			var xhttp = new XMLHttpRequest();
			xhttp.onreadystatechange = function() {
			    if (this.readyState == 4 && this.status == 200) {
					var info = JSON.parse(xhttp.responseText);
		            if (info == null) {
		                return;
		            }

		            html += "

";
		            html += info.title;
		            html += "

";
		            html += info.timestamp;
		            html += "

";
		            html += info.extract;
		            html += " Read more...

Tags: ";
		            for (j in info.tags) {
		                html += "";
		                html += info.tags[j] + ", ";

		                // add tags to tags
		                if (tags[info.tags[j]] == null) {
		                    tags[info.tags[j]] = [posts[i]];
		                } else {
		                    if ($.inArray(posts[i], tags[info.tags[j]]) == -1) {
		                        tags[info.tags[j]].push(posts[i]);
		                    }
		                }
		            }
		            html = html.substring(0, html.length - 2);
		            html += "

";
			    }
			};
            xhttp.open("GET", posts[i] + "/info.json", true);
            xhttp.send();
        }

        if (html != "") {
            $("#content").html(html);
        }
    } else {
        // no posts
    }
}

function showAllPostPreviews() {
	var xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
	    if (this.readyState == 4 && this.status == 200) {
			var posts = JSON.parse(xhttp.responseText).posts;
			showPostsPreviews(posts);
		}
	};
    xhttp.open("GET", "postSum.json", true);
    xhttp.send();

}

function showTagPostsPreviews(tagName) {
	showPostsPreviews(tags[tagName]);
	$("#content").html("
Tag: " + tagName + "
" + $("#content").html());
}

function showPost(name) {
	var html = "";
	html = $("
").append($("#" + name).clone()).html();
	$("#content").html(html);
	$("#" + name + ">.postTitle").css("font-size", "40px");

	var xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
	    if (this.readyState == 4 && this.status == 200) {
			if (xhttp.responseText != null) {
				var escapedContent = xhttp.responseText.replace(/&/g, "&")
									.replace(/\n/g, "
")
									.replace(/        /g, "        ")
									.replace(/\t/g, "        ")
									//.replace(/</g, "<")
									//.replace(/>/g, ">")
									//.replace(/"/g, """)
									.replace(/'/g, "'")
									;

				$("#content>#" + name + ">.postExtract").html("
"+ escapedContent + "
");
				$("#shareLink").html("http://blog.zhukaihan.com/index.html#" + name);
		    } else {
		        $("#content>#" + name + ">.postExtract").html("
No Such Article.
");
		    }
		}
	};
	xhttp.open("GET", name + "/content", true);
	xhttp.send();

	scrollToBody();
}
<|endoftext|>


<commit_before>import HomeModule from "./home";
import Authentication from "../../common/authentication/authentication";

const { module } = angular.mock;

describe("Home", () => {
  let $rootScope, $componentController;

  beforeEach(module(Authentication)); //to load hookProvider
  beforeEach(module(HomeModule));

  beforeEach(inject(($injector) => {
    $rootScope = $injector.get("$rootScope");
    $componentController = $injector.get("$componentController");
  }));

  describe("Module", () => {
    // top-level specs: i.e., routes, injection, naming
  });

  describe("Controller", () => {
    // controller specs
    let controller;
    beforeEach(() => {
      controller = $componentController("home", {
        $scope: $rootScope.$new()
      });
    });

    it("has a name property", () => { // erase if removing this.name from the controller
      expect(controller).to.have.property("name");
    });
  });

  describe("View", () => {
    // view layer specs.
  });
});
<commit_msg>Add redirection test<commit_after>import HomeModule from "./home";

const { module } = angular.mock;

describe("Home", () => {
  beforeEach(() => {
    module(HomeModule);
  });

  describe("Module", () => {
    it("is named home", () => {
      expect(HomeModule).to.equal("home");
    });
  });

  describe("Controller", () => {
    let $componentController, $httpBackend, $state, Authentication;
    beforeEach(() => {
      inject((_$componentController_, _$httpBackend_, _$state_, _Authentication_) => {
        $componentController = _$componentController_;
        $httpBackend = _$httpBackend_;
        $state = _$state_;
        Authentication = _Authentication_;
      });
      Authentication.data = { id: 1 };
      sinon.stub($state, "go");
    });

    afterEach(() => {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });

    let groupData = [
      { id: 50 },
      { id: 99 }
    ];

    it("should redirect user", () => {
      $httpBackend.expectGET("/api/groups/?members=1").respond(200, groupData);
      $componentController("home", {});
      $httpBackend.flush();
      expect($state.go).to.have.been.calledWith("groupDetail", { id: 50 });
    });
  });
});
<|endoftext|>


<commit_before>const getFiles = require("./get-files");

function parseFileStringToArr(fileStr) {
    "use strict";

    return fileStr.split("---")
        .map(section => section
                .trim()
                .split("\n")
                .filter(entry => entry));
}

function getHeader(headerArr) {
    "use strict";
    const headerObj = {};

    headerArr.forEach(line => {
        const [prop, value] = line.split(": ");
        headerObj[prop] = value;
    });

    return headerObj;
}

function makePost(file) {
    "use strict";
    const postArr = parseFileStringToArr(file),
          post = {};

    post.head = getHeader(postArr[0]);
    post.body = postArr[1];

    return post;
}

async function getPosts(path) {
    const posts = getFiles(path)
        .then(files => {
            return files.map(file => makePost(file));
        });

    return await posts;
}

console.log(getPosts("./posts"));

/* TEST
const path = "./posts";

const blogPosts = getFiles(path)
    .then(files => {
        console.log(files.map(file => makePost(file)));
    });
*/

/* OUTPUT = Array of posts with format:
 *
 * post = {
 *   preamble: {
 *     title: title,
 *     author: author,
 *     date: date,
 *     img-url: url
 *   },
 *   content: [
 *      "# Title",
 *      "Lorem ipsum..",
 *      "## Subtitle",
 *      "Morem ipsum.."
 *   ]
 * }
 */
<commit_msg>Swapped to reduce<commit_after>const getFiles = require("./get-files");

function parseFileStringToArr(fileStr) {
    "use strict";

    return fileStr.split("---")
        .map(section => section
                .trim()
                .split("\n")
                .filter(entry => entry));
}

function getHeader(headerArr) {
    "use strict";

    return headerArr.reduce((header, line) => {
        const [prop, value] = line.split(": ");
        header[prop] = value;
        return header;
    }, {});
}

function makePost(file) {
    "use strict";
    const postArr = parseFileStringToArr(file),
          post = {};

    post.head = getHeader(postArr[0]);
    post.body = postArr[1];

    return post;
}

module.exports = makePost;

/*
async function getPosts(path) {
    const posts = getFiles(path)
        .then(files => {
            return files.map(file => makePost(file));
        });

    return await posts;
}

console.log(getPosts("./posts"));
*/

// TEST
const path = "./posts";

const blogPosts = getFiles(path)
    .then(files => {
        console.log(files.map(file => makePost(file)));
    });

/* OUTPUT = Array of posts with format:
 *
 * post = {
 *   preamble: {
 *     title: title,
 *     author: author,
 *     date: date,
 *     img-url: url
 *   },
 *   content: [
 *      "# Title",
 *      "Lorem ipsum..",
 *      "## Subtitle",
 *      "Morem ipsum.."
 *   ]
 * }
 */
<|endoftext|>


<commit_before>// @flow
import React, {Component} from 'react'
import {DragSource} from 'react-dnd'
import cx from 'classnames'
import compact from 'lodash/compact'
import filter from 'lodash/filter'
import isNull from 'lodash/isNull'
import map from 'lodash/map'

import {IDENT_COURSE} from '../../core/student-format/item-types'

import List from './list'
import CourseTitle from './course-title'
import {buildDeptNum} from 'modules/schools/stolaf'
import Icon from './icon'
import ModalCourse from './modal-course'

import './inline-course.scss'

type InlineCourseProps = {
	className?: string,
	conflicts?: any[],
	connectDragSource: () => any,  // react-dnd
	course: Object,
	index?: number,
	isDragging: boolean,  // react-dnd
	scheduleId?: string,
	studentId?: string,
};

class InlineCourse extends Component {
	props: InlineCourseProps;

	state = {
		isOpen: false,
	};

	shouldComponentUpdate(nextProps: InlineCourseProps, nextState) {
		return (
			this.props.course !== nextProps.course ||
			this.props.conflicts !== nextProps.conflicts ||
			this.state.isOpen !== nextState.isOpen ||
			this.props.isDragging !== nextProps.isDragging
		)
	}

	closeModal = () => {
		this.setState({isOpen: false})
	};

	openModal = () => {
		this.setState({isOpen: true})
	};

	render() {
		const { course, conflicts=[], index, scheduleId, studentId } = this.props
		const warnings = conflicts[index || 0]
		const hasWarnings = compact(warnings).length

		const validWarnings = filter(warnings, w => !isNull(w) && w.warning === true)
		const warningEls = map(validWarnings, (w, idx) =>

<Icon>{w.icon}</Icon> {w.msg}
)

		let classSet = cx(this.props.className, 'course', {
			'expanded': this.state.isOpen,
			'has-warnings': hasWarnings,
			'is-dragging': this.props.isDragging,
		})

		const warningList = warningEls.length && (
			<List type='inline' className='course-warnings'>{warningEls}</List>
		)

		return this.props.connectDragSource(

				{warningList || null}

				<CourseTitle className='course-row' title={course.title} name={course.name} type={course.type} />

						{buildDeptNum(course, true)}

					{course.type !== 'Research' ? {course.type} : null}
					{course.gereqs &&

						{map(course.gereqs, (ge, idx) =>

{ge}
)}

}
					{course.prerequisites &&
						Prereq}

					{course.times}

				{this.state.isOpen
					? <ModalCourse onClose={this.closeModal} course={course} scheduleId={scheduleId} studentId={studentId} />
					: null}

		)
	}
}

// Implements the drag source contract.
const courseSource = {
	beginDrag(props) {
		let scheduleId = props.scheduleId || null
		return {
			isFromSchedule: scheduleId !== null,
			isFromSearch: scheduleId === null,
			clbid: props.course.clbid,
			groupid: props.course.groupid,
			fromScheduleId: scheduleId,
		}
	},
}

// Specifies the props to inject into your component.
function collect(connect, monitor) {
	return {
		connectDragSource: connect.dragSource(),
		isDragging: monitor.isDragging(),
	}
}

export default DragSource(IDENT_COURSE, courseSource, collect)(InlineCourse)
<commit_msg>disable flow on InlineCourse for now<commit_after>import React, {Component} from 'react'
import {DragSource} from 'react-dnd'
import cx from 'classnames'
import compact from 'lodash/compact'
import filter from 'lodash/filter'
import isNull from 'lodash/isNull'
import map from 'lodash/map'

import {IDENT_COURSE} from '../../core/student-format/item-types'

import List from './list'
import CourseTitle from './course-title'
import {buildDeptNum} from 'modules/schools/stolaf'
import Icon from './icon'
import ModalCourse from './modal-course'

import './inline-course.scss'

type InlineCourseProps = {
	className?: string,
	conflicts?: any[],
	connectDragSource: () => any,  // react-dnd
	course: Object,
	index?: number,
	isDragging: boolean,  // react-dnd
	scheduleId?: string,
	studentId?: string,
};

class InlineCourse extends Component {
	props: InlineCourseProps;

	state = {
		isOpen: false,
	};

	shouldComponentUpdate(nextProps: InlineCourseProps, nextState) {
		return (
			this.props.course !== nextProps.course ||
			this.props.conflicts !== nextProps.conflicts ||
			this.state.isOpen !== nextState.isOpen ||
			this.props.isDragging !== nextProps.isDragging
		)
	}

	closeModal = () => {
		this.setState({isOpen: false})
	};

	openModal = () => {
		this.setState({isOpen: true})
	};

	render() {
		const { course, conflicts=[], index, scheduleId, studentId } = this.props
		const warnings = conflicts[index || 0]
		const hasWarnings = compact(warnings).length

		const validWarnings = filter(warnings, w => !isNull(w) && w.warning === true)
		const warningEls = map(validWarnings, (w, idx) =>

<Icon>{w.icon}</Icon> {w.msg}
)

		let classSet = cx(this.props.className, 'course', {
			'expanded': this.state.isOpen,
			'has-warnings': hasWarnings,
			'is-dragging': this.props.isDragging,
		})

		const warningList = warningEls.length && (
			<List type='inline' className='course-warnings'>{warningEls}</List>
		)

		return this.props.connectDragSource(

				{warningList || null}

				<CourseTitle className='course-row' title={course.title} name={course.name} type={course.type} />

						{buildDeptNum(course, true)}

					{course.type !== 'Research' ? {course.type} : null}
					{course.gereqs &&

						{map(course.gereqs, (ge, idx) =>

{ge}
)}

}
					{course.prerequisites &&
						Prereq}

					{course.times}

				{this.state.isOpen
					? <ModalCourse onClose={this.closeModal} course={course} scheduleId={scheduleId} studentId={studentId} />
					: null}

		)
	}
}

// Implements the drag source contract.
const courseSource = {
	beginDrag(props) {
		let scheduleId = props.scheduleId || null
		return {
			isFromSchedule: scheduleId !== null,
			isFromSearch: scheduleId === null,
			clbid: props.course.clbid,
			groupid: props.course.groupid,
			fromScheduleId: scheduleId,
		}
	},
}

// Specifies the props to inject into your component.
function collect(connect, monitor) {
	return {
		connectDragSource: connect.dragSource(),
		isDragging: monitor.isDragging(),
	}
}

export default DragSource(IDENT_COURSE, courseSource, collect)(InlineCourse)
<|endoftext|>


<commit_before>// Copyright 2015 Endless Mobile, Inc.

/* exported Item */

const Gtk = imports.gi.Gtk;

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const InfiniteScrolledWindow = imports.app.widgets.infiniteScrolledWindow;
const Module = imports.app.interfaces.module;

/**
 * Class: Item
 * A module that displays all items in a set as cards in an arrangement
 *
 * This container displays cards delivered in batches using
 * <Actions.APPEND_ITEMS>.
 * Any cards lazily loaded after the first batch are faded in.
 *
 * Slots:
 *   arrangement
 */
const Item = new Module.Class({
    Name: 'ContentGroup.Item',
    Extends: Gtk.Frame,

    Slots: {
        'arrangement': {},
    },

    _init: function (props={}) {
        this.parent(props);
        let arrangement = this.create_submodule('arrangement');
        arrangement.connect('card-clicked', (arrangement, model) => {
            Dispatcher.get_default().dispatch({
                action_type: Actions.ITEM_CLICKED,
                model: model,
                context: arrangement.get_models(),
            });
        });
        this.add(arrangement);

        let dispatcher = Dispatcher.get_default();
        if (arrangement instanceof InfiniteScrolledWindow.InfiniteScrolledWindow) {
            arrangement.connect('need-more-content', () => dispatcher.dispatch({
                action_type: Actions.NEED_MORE_ITEMS,
            }));
        }
        dispatcher.register((payload) => {
            switch(payload.action_type) {
                case Actions.CLEAR_ITEMS:
                    arrangement.clear();
                    break;
                case Actions.APPEND_ITEMS:
                    arrangement.fade_cards = (arrangement.get_count() > 0);
                    arrangement.set_models(payload.models);
                    if (arrangement instanceof InfiniteScrolledWindow.InfiniteScrolledWindow) {
                        arrangement.new_content_added();
                    }
                    break;
                case Actions.HIGHLIGHT_ITEM:
                    arrangement.highlight(payload.model);
                    break;
                case Actions.CLEAR_HIGHLIGHTED_ITEM:
                    arrangement.clear_highlight();
                    break;
            }
        });
    },
});
<commit_msg>When setting models on arrangement, include the existing models as well<commit_after>// Copyright 2015 Endless Mobile, Inc.

/* exported Item */

const Gtk = imports.gi.Gtk;

const Actions = imports.app.actions;
const Dispatcher = imports.app.dispatcher;
const InfiniteScrolledWindow = imports.app.widgets.infiniteScrolledWindow;
const Module = imports.app.interfaces.module;

/**
 * Class: Item
 * A module that displays all items in a set as cards in an arrangement
 *
 * This container displays cards delivered in batches using
 * <Actions.APPEND_ITEMS>.
 * Any cards lazily loaded after the first batch are faded in.
 *
 * Slots:
 *   arrangement
 */
const Item = new Module.Class({
    Name: 'ContentGroup.Item',
    Extends: Gtk.Frame,

    Slots: {
        'arrangement': {},
    },

    _init: function (props={}) {
        this.parent(props);
        let arrangement = this.create_submodule('arrangement');
        arrangement.connect('card-clicked', (arrangement, model) => {
            Dispatcher.get_default().dispatch({
                action_type: Actions.ITEM_CLICKED,
                model: model,
                context: arrangement.get_models(),
            });
        });
        this.add(arrangement);

        let dispatcher = Dispatcher.get_default();
        if (arrangement instanceof InfiniteScrolledWindow.InfiniteScrolledWindow) {
            arrangement.connect('need-more-content', () => dispatcher.dispatch({
                action_type: Actions.NEED_MORE_ITEMS,
            }));
        }
        dispatcher.register((payload) => {
            switch(payload.action_type) {
                case Actions.CLEAR_ITEMS:
                    arrangement.clear();
                    break;
                case Actions.APPEND_ITEMS:
                    arrangement.fade_cards = (arrangement.get_count() > 0);
                    arrangement.set_models(arrangement.get_models().concat(payload.models));
                    if (arrangement instanceof InfiniteScrolledWindow.InfiniteScrolledWindow) {
                        arrangement.new_content_added();
                    }
                    break;
                case Actions.HIGHLIGHT_ITEM:
                    arrangement.highlight(payload.model);
                    break;
                case Actions.CLEAR_HIGHLIGHTED_ITEM:
                    arrangement.clear_highlight();
                    break;
            }
        });
    },
});
<|endoftext|>


<commit_before>const pinStyles = require("./Pin/styles.css");

module.exports = {
  itemSelector: "." + pinStyles.pinContainer,
  //columnWidth: 200
};
<commit_msg>change masonry opt<commit_after>const pinStyles = require("./Pin/styles.css");

module.exports = {
  itemSelector: "." + pinStyles.pinContainer,
  //columnWidth: 200,
};
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed 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.

/**
 * @fileoverview Tests for the closure implementation of Open Location Code.
 * This uses the test data from the github project,
 * http://github.com/google/openlocationcode/test_data
 * The test data is duplicated here because using bazel to run the tests doesn't
 * allow synchronous loading of the data files.
 */
goog.module('openlocationcode_test');
goog.setTestOnly('openlocationcode_test');

var openlocationcode = goog.require('google.openlocationcode');
var testSuite = goog.require('goog.testing.testSuite');
goog.require('goog.testing.asserts');

testSuite({
  testEncode: function() {
    // Each test has a code, the lat/lng of the center, lat/lng lo and lat/lng hi.
    var tests = [
      [
        '7FG49Q00+',
        20.375,
        2.775,
        20.35,
        2.75,
        20.4,
        2.8,
      ],
      [
        '7FG49QCJ+2V',
        20.3700625,
        2.7821875,
        20.37,
        2.782125,
        20.370125,
        2.78225,
      ],
      [
        '7FG49QCJ+2VX',
        20.3701125,
        2.782234375,
        20.3701,
        2.78221875,
        20.370125,
        2.78225,
      ],
      [
        '7FG49QCJ+2VXGJ',
        20.3701135,
        2.78223535156,
        20.370113,
        2.782234375,
        20.370114,
        2.78223632813,
      ],
      [
        '8FVC2222+22',
        47.0000625,
        8.0000625,
        47.0,
        8.0,
        47.000125,
        8.000125,
      ],
      [
        '4VCPPQGP+Q9',
        -41.2730625,
        174.7859375,
        -41.273125,
        174.785875,
        -41.273,
        174.786,
      ],
      [
        '62G20000+',
        0.5,
        -179.5,
        0.0,
        -180.0,
        1,
        -179,
      ],
      [
        '22220000+',
        -89.5,
        -179.5,
        -90,
        -180,
        -89,
        -179,
      ],
      [
        '7FG40000+',
        20.5,
        2.5,
        20.0,
        2.0,
        21.0,
        3.0,
      ],
      [
        '22222222+22',
        -89.9999375,
        -179.9999375,
        -90.0,
        -180.0,
        -89.999875,
        -179.999875,
      ],
      [
        '6VGX0000+',
        0.5,
        179.5,
        0,
        179,
        1,
        180,
      ],
      // Special cases over 90 latitude and 180 longitude
      [
        'CFX30000+',
        90,
        1,
        89,
        1,
        90,
        2,
      ],
      [
        'CFX30000+',
        92,
        1,
        89,
        1,
        90,
        2,
      ],
      [
        '62H20000+',
        1,
        180,
        1,
        -180,
        2,
        -179,
      ],
      ['62H30000+', 1, 181, 1, -179, 2, -178],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      // Decode the code.
      var ca = openlocationcode.decode(td[0]);
      // Encode the center coordinates.
      var code = openlocationcode.encode(td[1], td[2], ca.codeLength);
      // Did we get the same code?
      assertEquals('Test ' + 1, td[0], code);
      // Check that the decode gave the correct coordinates.
      assertRoughlyEquals('Test ' + 1, ca.latitudeLo, td[3], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.longitudeLo, td[4], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.latitudeHi, td[5], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.longitudeHi, td[6], 1e-10);
    }
  },
  testShortCodes: function() {
    var tests = [
      // full code, lat, lng, shortcode
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.217765625,
        '+2VX',
      ],
      // Adjust so we can't trim by 8 (+/- .000755)
      [
        '9C3W9QCJ+2VX',
        51.3708675,
        -1.217765625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3693575,
        -1.217765625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.218520625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.217010625,
        'CJ+2VX',
      ],
      // Adjust so we can't trim by 6 (+/- .0151)
      [
        '9C3W9QCJ+2VX',
        51.3852125,
        -1.217765625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3550125,
        -1.217765625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.232865625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.202665625,
        '9QCJ+2VX',
      ],
      // Added to detect error in recoverNearest functionality
      [
        '8FJFW222+',
        42.899,
        9.012,
        '22+',
      ],
      [
        '796RXG22+',
        14.95125,
        -23.5001,
        '22+',
      ],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      // Shorten the code.
      var short = openlocationcode.shorten(td[0], td[1], td[2]);
      assertEquals('Test ' + i, short, td[3]);
      var recovered = openlocationcode.recoverNearest(short, td[1], td[2]);
      assertEquals('Test ' + i, recovered, td[0]);
    }
  },
  testValidity: function() {
    var tests = [
      //   code,isValid,isShort,isFull
      // Valid full codes:
      [
        '8FWC2345+G6',
        true,
        false,
        true,
      ],
      [
        '8FWC2345+G6G',
        true,
        false,
        true,
      ],
      [
        '8fwc2345+',
        true,
        false,
        true,
      ],
      [
        '8FWCX400+',
        true,
        false,
        true,
      ],
      // Valid short codes:
      [
        'WC2345+G6g',
        true,
        true,
        false,
      ],
      [
        '2345+G6',
        true,
        true,
        false,
      ],
      [
        '45+G6',
        true,
        true,
        false,
      ],
      [
        '+G6',
        true,
        true,
        false,
      ],
      // Invalid codes
      [
        'G+',
        false,
        false,
        false,
      ],
      [
        '+',
        false,
        false,
        false,
      ],
      [
        '8FWC2345+G',
        false,
        false,
        false,
      ],
      [
        '8FWC2_45+G6',
        false,
        false,
        false,
      ],
      [
        '8FWC2η45+G6',
        false,
        false,
        false,
      ],
      [
        '8FWC2345+G6+',
        false,
        false,
        false,
      ],
      [
        '8FWC2300+G6',
        false,
        false,
        false,
      ],
      [
        'WC2300+G6g',
        false,
        false,
        false,
      ],
      [
        'WC2345+G',
        false,
        false,
        false,
      ],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      assertEquals('Test ' + i, openlocationcode.isValid(td[0]), td[1]);
      assertEquals('Test ' + i, openlocationcode.isShort(td[0]), td[2]);
      assertEquals('Test ' + i, openlocationcode.isFull(td[0]), td[3]);
    }
  },
});
<commit_msg>Update for changed namespace<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed 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.

/**
 * @fileoverview Tests for the closure implementation of Open Location Code.
 * This uses the test data from the github project,
 * http://github.com/google/openlocationcode/test_data
 * The test data is duplicated here because using bazel to run the tests doesn't
 * allow synchronous loading of the data files.
 */
goog.module('openlocationcode_test');
goog.setTestOnly('openlocationcode_test');

var OpenLocationCode = goog.require('openlocationcode.OpenLocationCode');
var testSuite = goog.require('goog.testing.testSuite');
goog.require('goog.testing.asserts');

testSuite({
  testEncode: function() {
    // Each test has a code, the lat/lng of the center, lat/lng lo and lat/lng hi.
    var tests = [
      [
        '7FG49Q00+',
        20.375,
        2.775,
        20.35,
        2.75,
        20.4,
        2.8,
      ],
      [
        '7FG49QCJ+2V',
        20.3700625,
        2.7821875,
        20.37,
        2.782125,
        20.370125,
        2.78225,
      ],
      [
        '7FG49QCJ+2VX',
        20.3701125,
        2.782234375,
        20.3701,
        2.78221875,
        20.370125,
        2.78225,
      ],
      [
        '7FG49QCJ+2VXGJ',
        20.3701135,
        2.78223535156,
        20.370113,
        2.782234375,
        20.370114,
        2.78223632813,
      ],
      [
        '8FVC2222+22',
        47.0000625,
        8.0000625,
        47.0,
        8.0,
        47.000125,
        8.000125,
      ],
      [
        '4VCPPQGP+Q9',
        -41.2730625,
        174.7859375,
        -41.273125,
        174.785875,
        -41.273,
        174.786,
      ],
      [
        '62G20000+',
        0.5,
        -179.5,
        0.0,
        -180.0,
        1,
        -179,
      ],
      [
        '22220000+',
        -89.5,
        -179.5,
        -90,
        -180,
        -89,
        -179,
      ],
      [
        '7FG40000+',
        20.5,
        2.5,
        20.0,
        2.0,
        21.0,
        3.0,
      ],
      [
        '22222222+22',
        -89.9999375,
        -179.9999375,
        -90.0,
        -180.0,
        -89.999875,
        -179.999875,
      ],
      [
        '6VGX0000+',
        0.5,
        179.5,
        0,
        179,
        1,
        180,
      ],
      // Special cases over 90 latitude and 180 longitude
      [
        'CFX30000+',
        90,
        1,
        89,
        1,
        90,
        2,
      ],
      [
        'CFX30000+',
        92,
        1,
        89,
        1,
        90,
        2,
      ],
      [
        '62H20000+',
        1,
        180,
        1,
        -180,
        2,
        -179,
      ],
      ['62H30000+', 1, 181, 1, -179, 2, -178],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      // Decode the code.
      var ca = OpenLocationCode.decode(td[0]);
      // Encode the center coordinates.
      var code = OpenLocationCode.encode(td[1], td[2], ca.codeLength);
      // Did we get the same code?
      assertEquals('Test ' + 1, td[0], code);
      // Check that the decode gave the correct coordinates.
      assertRoughlyEquals('Test ' + 1, ca.latitudeLo, td[3], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.longitudeLo, td[4], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.latitudeHi, td[5], 1e-10);
      assertRoughlyEquals('Test ' + 1, ca.longitudeHi, td[6], 1e-10);
    }
  },
  testShortCodes: function() {
    var tests = [
      // full code, lat, lng, shortcode
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.217765625,
        '+2VX',
      ],
      // Adjust so we can't trim by 8 (+/- .000755)
      [
        '9C3W9QCJ+2VX',
        51.3708675,
        -1.217765625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3693575,
        -1.217765625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.218520625,
        'CJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.217010625,
        'CJ+2VX',
      ],
      // Adjust so we can't trim by 6 (+/- .0151)
      [
        '9C3W9QCJ+2VX',
        51.3852125,
        -1.217765625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3550125,
        -1.217765625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.232865625,
        '9QCJ+2VX',
      ],
      [
        '9C3W9QCJ+2VX',
        51.3701125,
        -1.202665625,
        '9QCJ+2VX',
      ],
      // Added to detect error in recoverNearest functionality
      [
        '8FJFW222+',
        42.899,
        9.012,
        '22+',
      ],
      [
        '796RXG22+',
        14.95125,
        -23.5001,
        '22+',
      ],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      // Shorten the code.
      var short = OpenLocationCode.shorten(td[0], td[1], td[2]);
      assertEquals('Test ' + i, short, td[3]);
      var recovered = OpenLocationCode.recoverNearest(short, td[1], td[2]);
      assertEquals('Test ' + i, recovered, td[0]);
    }
  },
  testValidity: function() {
    var tests = [
      //   code,isValid,isShort,isFull
      // Valid full codes:
      [
        '8FWC2345+G6',
        true,
        false,
        true,
      ],
      [
        '8FWC2345+G6G',
        true,
        false,
        true,
      ],
      [
        '8fwc2345+',
        true,
        false,
        true,
      ],
      [
        '8FWCX400+',
        true,
        false,
        true,
      ],
      // Valid short codes:
      [
        'WC2345+G6g',
        true,
        true,
        false,
      ],
      [
        '2345+G6',
        true,
        true,
        false,
      ],
      [
        '45+G6',
        true,
        true,
        false,
      ],
      [
        '+G6',
        true,
        true,
        false,
      ],
      // Invalid codes
      [
        'G+',
        false,
        false,
        false,
      ],
      [
        '+',
        false,
        false,
        false,
      ],
      [
        '8FWC2345+G',
        false,
        false,
        false,
      ],
      [
        '8FWC2_45+G6',
        false,
        false,
        false,
      ],
      [
        '8FWC2η45+G6',
        false,
        false,
        false,
      ],
      [
        '8FWC2345+G6+',
        false,
        false,
        false,
      ],
      [
        '8FWC2300+G6',
        false,
        false,
        false,
      ],
      [
        'WC2300+G6g',
        false,
        false,
        false,
      ],
      [
        'WC2345+G',
        false,
        false,
        false,
      ],
    ];
    for (var i = 0; i < tests.length; i++) {
      var td = tests[i];
      assertEquals('Test ' + i, OpenLocationCode.isValid(td[0]), td[1]);
      assertEquals('Test ' + i, OpenLocationCode.isShort(td[0]), td[2]);
      assertEquals('Test ' + i, OpenLocationCode.isFull(td[0]), td[3]);
    }
  },
});
<|endoftext|>


<commit_before>/* jshint globalstrict:false, strict:false, maxlen: 5000 */
/* global describe, after, afterEach, before, it */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2017 ArangoDB GmbH, Cologne, Germany
// /
// / Licensed 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.
// /
// / Copyright holder is ArangoDB GmbH, Cologne, Germany
// /
// / @author Andreas Streichardt
// //////////////////////////////////////////////////////////////////////////////

const expect = require('chai').expect;
const request = require('@arangodb/request');
const users = require('@arangodb/users');
const db = require('@arangodb').db;

describe('Grants', function() {
  before(function() {
    db._create('grants');
  });
  afterEach(function() {
    users.remove('hans');
    let resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'default'},
      json: true,
    });
    expect(resp.statusCode).to.equal(200);
  });
  after(function() {
    db._drop('grants');
    // wait for readonly mode to reset
    require('internal').wait(5.0);
  });
  it('should show the effective rights for a user', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    let resp = request.get({
      url: '/_api/user/hans/database/_system',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });

  it('should show the effective rights when readonly mode is on', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'ro');
  });

  it('should show the configured rights when readonly mode is on and configured is requested', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system?configured=true',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });

  it('should show the effective rights when readonly mode is on', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    users.grantCollection('hans', '_system', 'grants');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system/grants',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'ro');
  });

  it('should show the configured rights when readonly mode is on and configured is requested', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    users.grantCollection('hans', '_system', 'grants');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system/grants?configured=true',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });
});
<commit_msg>add wait to test (#4224)<commit_after>/* jshint globalstrict:false, strict:false, maxlen: 5000 */
/* global describe, after, afterEach, before, beforeEach, it */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2017 ArangoDB GmbH, Cologne, Germany
// /
// / Licensed 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.
// /
// / Copyright holder is ArangoDB GmbH, Cologne, Germany
// /
// / @author Andreas Streichardt
// //////////////////////////////////////////////////////////////////////////////

const expect = require('chai').expect;
const request = require('@arangodb/request');
const users = require('@arangodb/users');
const db = require('@arangodb').db;

describe('Grants', function() {
  before(function() {
    db._drop('grants');
    db._create('grants');
  });
  beforeEach(function() {
    try {
      // remove any such user if it exists
      users.remove('hans');
    } catch (err) {}
  });
  afterEach(function() {
    let resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'default'},
      json: true,
    });
    expect(resp.statusCode).to.equal(200);
    // wait for readonly mode to reset
    require('internal').wait(5.0);
    users.remove('hans');
  });
  after(function() {
    db._drop('grants');
  });
  it('should show the effective rights for a user', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    let resp = request.get({
      url: '/_api/user/hans/database/_system',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });

  it('should show the effective rights when readonly mode is on', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'ro');
  });

  it('should show the configured rights when readonly mode is on and configured is requested', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system?configured=true',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });

  it('should show the effective rights when readonly mode is on', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    users.grantCollection('hans', '_system', 'grants');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system/grants',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'ro');
  });

  it('should show the configured rights when readonly mode is on and configured is requested', function() {
    users.save('hans');
    users.grantDatabase('hans', '_system', 'rw');
    users.grantCollection('hans', '_system', 'grants');

    let resp;
    resp = request.put({
      url: '/_admin/server/mode',
      body: {'mode': 'readonly'},
      json: true,
    });
    resp = request.get({
      url: '/_api/user/hans/database/_system/grants?configured=true',
      json: true,
    });
    expect(JSON.parse(resp.body)).to.have.property('result', 'rw');
  });
});
<|endoftext|>


<commit_before><commit_msg>Fixed linter error<commit_after><|endoftext|>


<commit_before>"use strict";

var React = require("react");
var Button = require("react-bootstrap").Button;
var Router = require("react-router");
var Link = require("react-router").Link;

var CKPTDispatcher = require("../dispatcher/CKPTDispatcher.js");
var NewsEditModal = require("./NewsEditModal.react");
var NewsCommentModal = require("./NewsCommentModal.react");
var NewsComments = require("./NewsComments.react");
var PlayerStore = require("../stores/PlayerStore.react");
var NewsStore = require("../stores/NewsStore.react");
var PlayerActionCreators = require("../actions/PlayerActionCreators.react");
var NewsActionCreators = require("../actions/NewsActionCreators.react");
var Authentication = require("../utils/Authentication");

var moment = require("moment");
var momentLocale = require("moment/locale/nb.js");
moment.locale("nb", momentLocale);
var _ = require("underscore");

var NewsItemPage = React.createClass({

  displayName: "News Page",
  mixins: [ Authentication, Router.State ],

  getInitialState: function() {
    return {
      newsitem: {
        title: "",
        leadin: "",
        body: "",
        created: moment(),
        comments: [],
        author: ""
      },
      emShow: false,
      acShow: false,
      errors: []
    };
  },

  componentDidMount: function() {
    PlayerStore.addChangeListener(this._onChange);
    NewsStore.addChangeListener(this._onChange);
    PlayerActionCreators.loadPlayers();
    NewsActionCreators.loadNewsItems();
  },

  componentWillUnmount: function() {
    PlayerStore.removeChangeListener(this._onChange);
    NewsStore.removeChangeListener(this._onChange);
  },

  _onChange: function() {
    var reloadNeeded = !NewsStore.isValid("news");
    setTimeout(function() {
      if (!CKPTDispatcher.isDispatching() && reloadNeeded) {
        NewsActionCreators.loadNewsItems();
      }
    }, 5);

    var id = this.getParams().newsId;
    var newsItem = NewsStore.getByUUID(id);
    if (newsItem && newsItem.comments) {
      newsItem.comments.reverse();
    }
    this.setState({
      newsitem: newsItem,
      errors: [
        PlayerStore.getErrors() +
        NewsStore.getErrors()
      ]
    });
  },

  emOpen: function(e) {
    this.setState({emShow: true});
  },

  emClose: function(e) {
    this.setState({emShow: false});
  },

  acOpen: function(e) {
    this.setState({acShow: true});
  },

  acClose: function(e) {
    this.setState({acShow: false});
  },

  render: function() {
    var tagTitle = {
      0: "nyheter",
      1: "analyser",
      2: "strategiavsløringer",
      3: "mattips"
    };

    var nick = "Ukjent";
    var p = this.state.newsitem ? PlayerStore.getFromUUID(this.state.newsitem.author) : null;
    if (p && p.nick) {
      nick = p.nick;
    }

    var currentLoggedInPlayer = PlayerStore.getFromUser(this.props.user);
    var editButton = "";
    var addButton = <Button bsSize="xsmall" bsStyle="primary" onClick={this.acOpen}>Ny kommentar</Button>;

    if ( (currentLoggedInPlayer && currentLoggedInPlayer.uuid == p.uuid) || this.props.isAdmin) {
      editButton = <Button bsSize="xsmall" bsStyle="primary" onClick={this.emOpen}>Rediger sak</Button>;
    }

    var created = this.state.newsitem ? moment(this.state.newsitem.created).fromNow() : ";"
    var title = this.state.newsitem ? this.state.newsitem.title : "";
    var leadin = this.state.newsitem ? this.state.newsitem.leadin : "";
    var body = this.state.newsitem ? this.state.newsitem.body : "";

    return (

              Skrevet av {nick} -- {created}
              {editButton}  {addButton}

{title}

              {leadin.split('\\n').map(function(p, i) {
                  return (
                  {p}

                  );
                })}

              {body.split('\\n').map(function(p, i) {
                  return (

{p}

                  );
                })}

          <NewsComments item={this.state.newsitem} />

        <NewsEditModal item={this.state.newsitem} show={this.state.emShow} onHide={this.emClose} />
        <NewsCommentModal item={this.state.newsitem} show={this.state.acShow} onHide={this.acClose} />

    );
  }
});

module.exports = NewsItemPage;
<commit_msg>Dont reverse comment order.<commit_after>"use strict";

var React = require("react");
var Button = require("react-bootstrap").Button;
var Router = require("react-router");
var Link = require("react-router").Link;

var CKPTDispatcher = require("../dispatcher/CKPTDispatcher.js");
var NewsEditModal = require("./NewsEditModal.react");
var NewsCommentModal = require("./NewsCommentModal.react");
var NewsComments = require("./NewsComments.react");
var PlayerStore = require("../stores/PlayerStore.react");
var NewsStore = require("../stores/NewsStore.react");
var PlayerActionCreators = require("../actions/PlayerActionCreators.react");
var NewsActionCreators = require("../actions/NewsActionCreators.react");
var Authentication = require("../utils/Authentication");

var moment = require("moment");
var momentLocale = require("moment/locale/nb.js");
moment.locale("nb", momentLocale);
var _ = require("underscore");

var NewsItemPage = React.createClass({

  displayName: "News Page",
  mixins: [ Authentication, Router.State ],

  getInitialState: function() {
    return {
      newsitem: {
        title: "",
        leadin: "",
        body: "",
        created: moment(),
        comments: [],
        author: ""
      },
      emShow: false,
      acShow: false,
      errors: []
    };
  },

  componentDidMount: function() {
    PlayerStore.addChangeListener(this._onChange);
    NewsStore.addChangeListener(this._onChange);
    PlayerActionCreators.loadPlayers();
    NewsActionCreators.loadNewsItems();
  },

  componentWillUnmount: function() {
    PlayerStore.removeChangeListener(this._onChange);
    NewsStore.removeChangeListener(this._onChange);
  },

  _onChange: function() {
    var reloadNeeded = !NewsStore.isValid("news");
    setTimeout(function() {
      if (!CKPTDispatcher.isDispatching() && reloadNeeded) {
        NewsActionCreators.loadNewsItems();
      }
    }, 5);

    var id = this.getParams().newsId;
    var newsItem = NewsStore.getByUUID(id);
    //if (newsItem && newsItem.comments) {
    //  newsItem.comments.reverse();
    //}
    this.setState({
      newsitem: newsItem,
      errors: [
        PlayerStore.getErrors() +
        NewsStore.getErrors()
      ]
    });
  },

  emOpen: function(e) {
    this.setState({emShow: true});
  },

  emClose: function(e) {
    this.setState({emShow: false});
  },

  acOpen: function(e) {
    this.setState({acShow: true});
  },

  acClose: function(e) {
    this.setState({acShow: false});
  },

  render: function() {
    var tagTitle = {
      0: "nyheter",
      1: "analyser",
      2: "strategiavsløringer",
      3: "mattips"
    };

    var nick = "Ukjent";
    var p = this.state.newsitem ? PlayerStore.getFromUUID(this.state.newsitem.author) : null;
    if (p && p.nick) {
      nick = p.nick;
    }

    var currentLoggedInPlayer = PlayerStore.getFromUser(this.props.user);
    var editButton = "";
    var addButton = <Button bsSize="xsmall" bsStyle="primary" onClick={this.acOpen}>Ny kommentar</Button>;

    if ( (currentLoggedInPlayer && currentLoggedInPlayer.uuid == p.uuid) || this.props.isAdmin) {
      editButton = <Button bsSize="xsmall" bsStyle="primary" onClick={this.emOpen}>Rediger sak</Button>;
    }

    var created = this.state.newsitem ? moment(this.state.newsitem.created).fromNow() : ";"
    var title = this.state.newsitem ? this.state.newsitem.title : "";
    var leadin = this.state.newsitem ? this.state.newsitem.leadin : "";
    var body = this.state.newsitem ? this.state.newsitem.body : "";

    return (

              Skrevet av {nick} -- {created}
              {editButton}  {addButton}

{title}

              {leadin.split('\\n').map(function(p, i) {
                  return (
                  {p}

                  );
                })}

              {body.split('\\n').map(function(p, i) {
                  return (

{p}

                  );
                })}

          <NewsComments item={this.state.newsitem} />

        <NewsEditModal item={this.state.newsitem} show={this.state.emShow} onHide={this.emClose} />
        <NewsCommentModal item={this.state.newsitem} show={this.state.acShow} onHide={this.acClose} />

    );
  }
});

module.exports = NewsItemPage;
<|endoftext|>