<commit_before>'use strict';
try {
    var tracing = require('tracing');
} catch (e) {
    if (e.code !== 'MODULE_NOT_FOUND') throw e;
    require('async-listener');
    tracing = process;
}
var util = require('util');
var PATH_PREFIX = process.cwd().toLowerCase();
var sep = require('path').sep;

var settings = {
    // `null`ing a style will remove it from the trace output
    tracingModuleStyle: null,
//    tracingModuleStyle: "\x1B[31m",
    modulesStyle: "\x1B[32m",
    globalsStyle: "\x1B[33m",
    coreStyle: "\x1B[34m",
    ownStyle: "\x1B[1m",
    mocha: true,
    BOUNDARY: '    [sync boundary]',
    useColors: true
};

tracing.addAsyncListener({
    'create': asyncFunctionInitialized,
    'before': asyncCallbackBefore,
    'error': asyncCallbackError,
    'after': asyncCallbackAfter
});

function asyncFunctionInitialized(oldFrames) {
    oldFrames = oldFrames || Error._frames || [];
    var frames = StackError.getStackFrames(asyncFunctionInitialized);
    if (frames[1] && frames[1].functionName === 'createTCP') return oldFrames;
    frames.unshift(settings.BOUNDARY);
    frames.push.apply(frames, oldFrames);
    Error._frames = frames;
    return frames;
}

function asyncCallbackBefore(__, frames) {
    Error._frames = frames;
}

function asyncCallbackAfter(__, frames) {
    Error._frames = frames;
}

function asyncCallbackError(oldFrames, error) {
    if (error._passed) return;
    var frames = (oldFrames || []).reduce(reducer, []);
    error.stack += v8StackFormating('', frames);
    error._passed = true;
}

/* ===================== stack chain util ======================== */

function StackError(otp) {
    Error.captureStackTrace(this, otp);
    Error.prepareStackTrace = function justStoreStackStace(error, frames) {
        error._frames = frames.map(function (frame) {
            return {
                string: frame.toString(),
                fileName: frame.getFileName(),
                functionName: frame.getFunctionName(),
                methodName: frame.getMethodName()
            };
        });
        return '';
    };
    this.stack;  // jshint ignore:line
    delete Error.prepareStackTrace;
}
StackError.getStackFrames = function getStackFrames(otp) {
    return (new this(otp))._frames;
};
util.inherits(StackError, Error);

/* ===================== stack chain manipulation & formating ======================== */

function categorizeFrame(frame) {
    var name = frame && frame.fileName && frame.fileName.toLowerCase();
    if (!name) return (frame._section = 'core');
    if (name === 'tracing.js') return (frame._section = 'tracingModule');
    if (!~name.indexOf(sep)) return (frame._section = 'core');
    if (name.indexOf(PATH_PREFIX) !== 0) return (frame._section = 'globals');
    if (~(name.replace(PATH_PREFIX, '')).indexOf('node_modules')) return (frame._section = 'modules');
    frame._section = 'own';
}

function reducer(seed, frame) {
    if (typeof frame == 'string') {
        if (frame != seed[seed.length - 1]) seed.push(frame);
        return seed;
    }
    categorizeFrame(frame);
    seed.push(frame);
    return seed;
}

function v8StackFormating(error, frames) {
    var lines = [];
    lines.push(error.toString());
    frames.push({ string: '<the nexus>\n', _section: 'core' });
    for (var i = 0; i < frames.length; i++) {
        var frame = frames[i];
        if (typeof frame == 'string') {
            lines.push(frame);
            continue;
        }
        var line;
        try {
            line = frame.string;
        } catch (e) {
            try {
                line = "<error: " + e + ">";
            } catch (ee) {
                // Any code that reaches this point is seriously nasty!
                line = "<error>";
            }
        }
        var style = getStyle(frame._section);
        var prefix = style + "    at ";
        var suffix = settings.useColors ? "\x1B[0m" : '';
        if (typeof style == 'string') lines.push(prefix + line + suffix);
    }
    return lines.join("\n");
}

function getStyle(sec) {
    return (settings.useColors) ? settings[sec + 'Style'] : '';
}

/* ===================== 3rd party integrations ======================== */

function setupForMocha() {
    try {
        var mocha = Object.keys(require.cache)
            .filter(function (k) { return ~k.search(/mocha.index\.js/); })
            .map(function (k) { return require.cache[k].exports; })
            .pop();
        var shimmer = require('shimmer');
        shimmer.wrap(mocha.prototype, 'run', function (original) {
            return function () {
                var runner = original.apply(this, arguments);
                settings.useColors = this.options.useColors;
                runner.on('test', function () {
                    Error._frames = null;
                });
            };
        });
    } catch (e) {
        if (e.code !== 'MODULE_NOT_FOUND') console.error(e);
    }
}
if (settings.mocha) setupForMocha();
<commit_msg>Sometimes the stack trace is linear (thank you harmony)<commit_after>'use strict';
Error.stackTraceLimit = Infinity;
try {
    var tracing = require('tracing');
} catch (e) {
    if (e.code !== 'MODULE_NOT_FOUND') throw e;
    require('async-listener');
    tracing = process;
}
var util = require('util');
var PATH_PREFIX = process.cwd().toLowerCase();
var sep = require('path').sep;

var settings = {
    // `null`ing a style will remove it from the trace output
    tracingModuleStyle: null,
//    tracingModuleStyle: "\x1B[31m",
    modulesStyle: "\x1B[32m",
    globalsStyle: "\x1B[33m",
    coreStyle: "\x1B[34m",
    ownStyle: "\x1B[1m",
    mocha: true,
    BOUNDARY: '    [sync boundary]',
    useColors: true
};

tracing.addAsyncListener({
    'create': asyncFunctionInitialized,
    'before': asyncCallbackBefore,
    'error': asyncCallbackError,
    'after': asyncCallbackAfter
});

function asyncFunctionInitialized(oldFrames) {
    oldFrames = oldFrames || Error._frames || [];
    var frames = StackError.getStackFrames(asyncFunctionInitialized);
    if (frames[1] && frames[1].functionName === 'createTCP') return oldFrames;
    frames.unshift(settings.BOUNDARY);
    frames.push.apply(frames, oldFrames);
    Error._frames = frames;
    return frames;
}

function asyncCallbackBefore(__, frames) {
    Error._frames = frames;
}

function asyncCallbackAfter(__, frames) {
    Error._frames = frames;
}

function asyncCallbackError(oldFrames, error) {
    if (error._passed) return;
    var frames = (oldFrames || []).reduce(reducer, []);
    error.stack += v8StackFormating('', frames);
    error._passed = true;
}

/* ===================== stack chain util ======================== */

function StackError(otp) {
    Error.captureStackTrace(this, otp);
    Error.prepareStackTrace = function justStoreStackStace(error, frames) {
        error._frames = frames.map(function (frame) {
            return {
                string: frame.toString(),
                fileName: frame.getFileName(),
                functionName: frame.getFunctionName(),
                methodName: frame.getMethodName()
            };
        });
        return '';
    };
    this.stack;  // jshint ignore:line
    delete Error.prepareStackTrace;
}
StackError.getStackFrames = function getStackFrames(otp) {
    return (new this(otp))._frames;
};
util.inherits(StackError, Error);

/* ===================== stack chain manipulation & formating ======================== */

function categorizeFrame(frame) {
    var name = frame && frame.fileName && frame.fileName.toLowerCase();
    if (!name) return (frame._section = 'core');
    if (name === 'tracing.js') return (frame._section = 'tracingModule');
    if (!~name.indexOf(sep)) return (frame._section = 'core');
    if (name.indexOf(PATH_PREFIX) !== 0) return (frame._section = 'globals');
    if (~(name.replace(PATH_PREFIX, '')).indexOf('node_modules')) return (frame._section = 'modules');
    frame._section = 'own';
}

function reducer(seed, frame) {
    if (typeof frame == 'string') {
        if (frame != seed[seed.length - 1]) seed.push(frame);
        return seed;
    }
    categorizeFrame(frame);
    seed.push(frame);
    return seed;
}

function v8StackFormating(error, frames) {
    var lines = [];
    lines.push(error.toString());
    frames.push({ string: '<the nexus>\n', _section: 'core' });
    for (var i = 0; i < frames.length; i++) {
        var frame = frames[i];
        if (typeof frame == 'string') {
            lines.push(frame);
            continue;
        }
        var line;
        try {
            line = frame.string;
        } catch (e) {
            try {
                line = "<error: " + e + ">";
            } catch (ee) {
                // Any code that reaches this point is seriously nasty!
                line = "<error>";
            }
        }
        var style = getStyle(frame._section);
        var prefix = style + "    at ";
        var suffix = settings.useColors ? "\x1B[0m" : '';
        if (typeof style == 'string') lines.push(prefix + line + suffix);
    }
    return lines.join("\n");
}

function getStyle(sec) {
    return (settings.useColors) ? settings[sec + 'Style'] : '';
}

/* ===================== 3rd party integrations ======================== */

function setupForMocha() {
    try {
        var mocha = Object.keys(require.cache)
            .filter(function (k) { return ~k.search(/mocha.index\.js/); })
            .map(function (k) { return require.cache[k].exports; })
            .pop();
        var shimmer = require('shimmer');
        shimmer.wrap(mocha.prototype, 'run', function (original) {
            return function () {
                var runner = original.apply(this, arguments);
                settings.useColors = this.options.useColors;
                runner.on('test', function () {
                    Error._frames = null;
                });
            };
        });
    } catch (e) {
        if (e.code !== 'MODULE_NOT_FOUND') console.error(e);
    }
}
if (settings.mocha) setupForMocha();
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2016 The {life-parser} Project Authors. All rights reserved.
 * This code may only be used under the MIT style license found at http://100dayproject.github.io/LICENSE.txt
 * The complete set of authors may be found at http://100dayproject.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://100dayproject.github.io/CONTRIBUTORS.txt
 * Code distributed by 100dayproject as part of the life.
 */

"use strict";

let module_name = 'mod_home',
    _module = new __viewRender(module_name);

_module.index = function (req, res) {
    _module.render(req, res, 'index', {
        title: '100dayproject.org'
    });
};

module.exports = _module;<commit_msg>Update title seo home page<commit_after>/**
 * @license
 * Copyright (c) 2016 The {life-parser} Project Authors. All rights reserved.
 * This code may only be used under the MIT style license found at http://100dayproject.github.io/LICENSE.txt
 * The complete set of authors may be found at http://100dayproject.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://100dayproject.github.io/CONTRIBUTORS.txt
 * Code distributed by 100dayproject as part of the life.
 */

"use strict";

let module_name = 'mod_home',
    _module = new __viewRender(module_name);

_module.index = function (req, res) {
    _module.render(req, res, 'index', {
        title: 'Hainho - The 100dayproject of archives & research collections and quickly save my ideas'
    });
};

module.exports = _module;<|endoftext|>


<commit_before>/**
 * utils functions © 2016 Adrien Valcke
 *
 * size(thing)
 * toBoolean(thing)
 * is(thing, Type)
 * existsSync(pathToContent)
 * isDirectorySync(directory)
 * isFileSync(file)
 * getFilesSync(pathToContent, recursive = false)
 * getFiles(pathToContent, callback)
 * getInt(thing, min = 0)
 */

const fs = require('fs');
const path = require('path');

module.exports = {
  // size
  size(thing) {
    if (thing !== undefined && thing !== null && thing !== NaN) return Object.keys(thing).length;
    else return 0;
  },
  // toBoolean
  toBoolean(thing) {
    if (thing === 'false' || thing <= 0)
      return false;
    if (thing === 'true' || thing >= 1)
      return true;
    if (thing === true || thing === false)
      return thing;
    return false;
  },
  // is
  is(thing, Type) {
    if (!typeof Type || Type === null || Type === undefined || Number.isNaN(Type) || !Type.prototype) return false;
    return Type.prototype.isPrototypeOf(Object(thing));
    // return Object.prototype.isPrototypeOf.call(Type.prototype, Object(thing));
  },
  // has
  has(property, thing) {
    if (thing === null || thing === undefined || Number.isNaN(thing)) return false;
    return Object.prototype.hasOwnProperty.call(thing, property);
  },
  // existsSync
  existsSync(pathToContent){
    try {
      fs.statSync(path.normalize(pathToContent));
      return true;
    } catch (e) {
      return false;
    }
  },
  // isDirectorySync
  isDirectorySync(directory){
    try {
      var stat = fs.statSync(path.normalize(directory));
      if (stat.isDirectory()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // isFileSync
  isFileSync(file){
    try {
      var stat = fs.statSync(path.normalize(file));
      if (stat.isFile()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // getFilesSync
  getFilesSync(pathToContent, recursive = false) {
    let files = [];
    recursive = this.toBoolean(recursive);

    pathToContent = path.normalize(pathToContent);

    if (this.isFileSync(pathToContent)) {
      files.push(pathToContent);
    } else if (this.isDirectorySync(pathToContent)) {
      let contents = fs.readdirSync(pathToContent, 'utf8');
      for(content of contents) {
        let contentPath = path.join(pathToContent, content);
        if (this.isFileSync(contentPath)) files.push(contentPath);
        else if (recursive && this.isDirectorySync(contentPath)) files = files.concat(this.getFilesSync(contentPath, recursive));
      }
    } else {
      console.error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`);
    }
    return files;
  },
  // getFiles Async
  getFiles(pathToContent, callback) {
    let filesArray = [];
    pathToContent = path.normalize(pathToContent);

    fs.stat(pathToContent, (err, stats) => {
      if (err) return callback(err);
      if (stats.isFile()) {
        filesArray.push(pathToContent);
        callback(null, filesArray);
      } else if (stats.isDirectory()) {
        fs.readdir(pathToContent, 'utf8', (err, files) => {
          for(let i = 0, len = files.length; i < len; i++) {
            ((i) => {
              let filePath = path.join(pathToContent, files[i]);
              fs.stat(filePath, (err, stats) => {
                if (err) return callback(err);
                if (stats.isFile()) filesArray.push(filePath);
                if (i === len - 1) callback(null, filesArray);
              });
            })(i);
          }
        });
      } else {
        callback(new Error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`));
      }
    });
  },
  // getInt
  getInt(thing, min = 0){
    thing = parseInt(thing);
    min = parseInt(min);

    if (Number.isNaN(thing)) {
      if (!Number.isNaN(min)) return min;
      else return 0;
    } else {
      if (!Number.isNaN(min)) {
        if (thing > min) return thing;
        else return min;
      } else return thing;
    }
  }
};
<commit_msg>add function has(property, thing) at the head of the file (summary)<commit_after>/**
 * utils functions © 2016 Adrien Valcke
 *
 * size(thing)
 * toBoolean(thing)
 * is(thing, Type)
 * has(property, thing)
 * existsSync(pathToContent)
 * isDirectorySync(directory)
 * isFileSync(file)
 * getFilesSync(pathToContent, recursive = false)
 * getFiles(pathToContent, callback)
 * getInt(thing, min = 0)
 */

const fs = require('fs');
const path = require('path');

module.exports = {
  // size
  size(thing) {
    if (thing !== undefined && thing !== null && thing !== NaN) return Object.keys(thing).length;
    else return 0;
  },
  // toBoolean
  toBoolean(thing) {
    if (thing === 'false' || thing <= 0)
      return false;
    if (thing === 'true' || thing >= 1)
      return true;
    if (thing === true || thing === false)
      return thing;
    return false;
  },
  // is
  is(thing, Type) {
    if (!typeof Type || Type === null || Type === undefined || Number.isNaN(Type) || !Type.prototype) return false;
    return Type.prototype.isPrototypeOf(Object(thing));
    // return Object.prototype.isPrototypeOf.call(Type.prototype, Object(thing));
  },
  // has
  has(property, thing) {
    if (thing === null || thing === undefined || Number.isNaN(thing)) return false;
    return Object.prototype.hasOwnProperty.call(thing, property);
  },
  // existsSync
  existsSync(pathToContent){
    try {
      fs.statSync(path.normalize(pathToContent));
      return true;
    } catch (e) {
      return false;
    }
  },
  // isDirectorySync
  isDirectorySync(directory){
    try {
      var stat = fs.statSync(path.normalize(directory));
      if (stat.isDirectory()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // isFileSync
  isFileSync(file){
    try {
      var stat = fs.statSync(path.normalize(file));
      if (stat.isFile()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // getFilesSync
  getFilesSync(pathToContent, recursive = false) {
    let files = [];
    recursive = this.toBoolean(recursive);

    pathToContent = path.normalize(pathToContent);

    if (this.isFileSync(pathToContent)) {
      files.push(pathToContent);
    } else if (this.isDirectorySync(pathToContent)) {
      let contents = fs.readdirSync(pathToContent, 'utf8');
      for(content of contents) {
        let contentPath = path.join(pathToContent, content);
        if (this.isFileSync(contentPath)) files.push(contentPath);
        else if (recursive && this.isDirectorySync(contentPath)) files = files.concat(this.getFilesSync(contentPath, recursive));
      }
    } else {
      console.error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`);
    }
    return files;
  },
  // getFiles Async
  getFiles(pathToContent, callback) {
    let filesArray = [];
    pathToContent = path.normalize(pathToContent);

    fs.stat(pathToContent, (err, stats) => {
      if (err) return callback(err);
      if (stats.isFile()) {
        filesArray.push(pathToContent);
        callback(null, filesArray);
      } else if (stats.isDirectory()) {
        fs.readdir(pathToContent, 'utf8', (err, files) => {
          for(let i = 0, len = files.length; i < len; i++) {
            ((i) => {
              let filePath = path.join(pathToContent, files[i]);
              fs.stat(filePath, (err, stats) => {
                if (err) return callback(err);
                if (stats.isFile()) filesArray.push(filePath);
                if (i === len - 1) callback(null, filesArray);
              });
            })(i);
          }
        });
      } else {
        callback(new Error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`));
      }
    });
  },
  // getInt
  getInt(thing, min = 0){
    thing = parseInt(thing);
    min = parseInt(min);

    if (Number.isNaN(thing)) {
      if (!Number.isNaN(min)) return min;
      else return 0;
    } else {
      if (!Number.isNaN(min)) {
        if (thing > min) return thing;
        else return min;
      } else return thing;
    }
  }
};
<|endoftext|>


<commit_before>/**
 * @fileOverview The Hello World component.
 */

import React from 'react';
import { Link } from 'react-router';

export default class CreatePost extends React.Component {

  render() {
    return (

<Link to="/">HOME PAGE</Link>

        <textarea rows="4" cols="50" placeholder='Type your post here' onChange={this.props.updatePostValue}></textarea>
        <button disabled={this.props.creating} onClick={this.props.onCreatePostClick}>CREATE POST</button>

    );
  }

}

/** @const {Object} propTypes definition */
CreatePost.propTypes = {
  updatePostValue: React.PropTypes.func,
  onCreatePostClick: React.PropTypes.func
};
<commit_msg>post creation button is disabled on first click, users can no longer create a second post by clicking the create post button a second time immediately, and fixes a validation bug for the creating property<commit_after>/**
 * @fileOverview The Hello World component.
 */

import React from 'react';
import { Link } from 'react-router';

export default class CreatePost extends React.Component {

  render() {
    return (

<Link to="/">HOME PAGE</Link>

        <textarea rows="4" cols="50" placeholder='Type your post here' onChange={this.props.updatePostValue}></textarea>
        <button disabled={this.props.creating} onClick={this.props.onCreatePostClick}>CREATE POST</button>

    );
  }

}

/** @const {Object} propTypes definition */
CreatePost.propTypes = {
  updatePostValue: React.PropTypes.func,
  onCreatePostClick: React.PropTypes.func,
  creating: React.PropTypes.bool
};
<|endoftext|>


<commit_before>import Random from 'random-js'
import denodeify from 'denodeify'
import winston from 'winston'
import {AllHtmlEntities as Entities} from 'html-entities'

import {version as VERSION} from '../package.json'

const log = winston

const MAX_FACTOID_SIZE = Number(process.env.MAX_FACTOID_SIZE || 2048)

const I_DONT_KNOW = [
  "I don't know what that is.",
  'I have no idea.',
  'No idea.',
  "I don't know."
]

const OKAY = [
  'OK, got it.',
  'I got it.',
  'Understood.',
  'Gotcha.',
  'OK'
]

const GREETINGS = [
  'Heya, $who!',
  'Hi $who!',
  'Hello, $who',
  'Hello, $who!',
  'Greetings, $who'
]

const ACKNOWLEDGEMENTS = [
  'Yes?',
  'Yep?',
  'Yeah?'
]

const IGNORED_FACTOIDS = [
  'he',
  'help',
  'hers',
  'his',
  'huh',
  'it',
  "it's",
  'its',
  'she',
  'settings',
  'status',
  'that',
  'them',
  'there',
  'these',
  'they',
  'this',
  'those',
  'what',
  'when',
  'where',
  'who',
  'why'
]

export class MemoryBotEngine {
  constructor (store) {
    this.store = store
    this.userIdsToNames = {}
    this._random = new Random()
  }

  oneOf () {
    let arr
    if (Array.isArray(arguments[0])) {
      arr = arguments[0]
    } else {
      arr = arguments
    }
    return this._random.pick(arr)
  }

  async handleMessage (bot, sender, channel, isDirect, msg) {
    // Ignore ourself.
    if (sender === bot.identity.id || sender === bot.identity.name) return

    const {mbMeta} = bot
    const team = bot.identifyTeam()
    const shouldLearn = isDirect || mbMeta.ambient // Should we be learning factoids?
    const shouldReply = isDirect || !mbMeta.direct // Should we reply to this?
    const isVerbose = mbMeta.verbose // TODO: Combine with shouldLearn

    // Sometimes Slack sends HTML entities.
    msg = Entities.decode(msg)

    // Basic input sanitization.
    msg = msg.substr(0, MAX_FACTOID_SIZE).trim().replace(/\0/g, '').replace(/\n/g, ' ')

    // Simple reply helper.
    const reply = text => bot.reply({channel}, {text})

    // Used to parse a factoid's contents and reply with it.
    const parseAndReply = async (key, value, tell = null) => {
      // Factoids are stored as { key: "foo", value: "is bar" }
      let [, verb, rest] = Array.from(value.match(/^(is|are)\s+(.*)/i))
      value = rest

      // Split on |, but don't split on \|. Use an HTML entity as a separator.
      value = value.replace(/\\\|/g, '\\|')
      value = this.oneOf(value.split(/\|/i)).trim()
      value = value.replace(/|/g, '|')

      const isReply = (/^<reply>\s*/i).test(value)
      if (isReply) value = value.replace(/^<reply>\s*/i, '')

      const isEmote = (/^<action>\s*/i).test(value)
      if (isEmote) value = value.replace(/^<action>\s*/i, '')

      value = value.replace(/\$who/ig, sender)

      if (tell) {
        bot.reply({channel: tell}, {text: `${sender} wants you to know: ${key} is ${value}`})
      } else if (isReply) {
        if (value && value !== '') reply(value)
      } else if (isEmote) {
        await denodeify(bot.api.callAPI.bind(bot.api))('chat.meMessage', {channel, text: value})
      } else {
        reply(`${key} ${verb} ${value}`)
      }
    }

    // Used to update a factoid and reply.
    const update = async (key, value) => {
      // Escape Slack @-groups. (When a user types '@here' we get '<!here|@here>'.)
      value = value.replace(/<!(\w+)\|@\w+>/ig, (_, x) => '`@' + x + '`')

      const lastEdit = `on ${new Date()} by ${sender}`
      await this.store.setFactoid(team, key, value, lastEdit)
      if (isVerbose || isDirect) reply(this.oneOf(OKAY))
    }

    // Status
    if (isDirect && (/^(?:status|settings)\??/ig).test(msg)) {
      const bool = x => x ? ':ballot_box_with_check:' : ':white_medium_square:'
      let count = await this.store.countFactoids(team)
      reply(`\
*Status*
I am memorybot v${VERSION} - https://statico.github.com/memorybot/
I am currently remembering ${count} factoids.
*Settings*
${bool(mbMeta.direct)} \`direct\` - Interactons require direct messages or @-mentions
${bool(mbMeta.ambient)} \`ambient\` - Learn factoids from ambient room chatter
${bool(mbMeta.verbose)} \`verbose\` - Make the bot more chatty with confirmations, greetings, etc.
Tell me "enable setting <name>" or "disable setting <name>" to change the above settings.\
`)

    // Help
    } else if (isDirect && (/^help\??/ig).test(msg)) {
      reply(`Hi alice, I'm a MemoryBot. I remember things and then recall them later when asked. Check out my home page for more information: https://statico.github.io/memorybot/ -- You can also leave feedback or file bugs on my GitHub issues page: https://github.com/statico/memorybot/issues`)

    // Settings
    } else if (isDirect && (/^(enable|disable)\s+setting\s+/i).test(msg)) {
      let [action, key] = msg.split(/\s+setting\s+/i)
      let value = (action === 'enable')

      let result
      switch (key) {
        case 'direct':
          result = `interactions with me ${value ? 'now' : 'no longer'} require direct messages or @-mentions`
          break
        case 'ambient':
          result = `I ${value ? 'will now' : 'will no longer'} learn factoids without being told explicitly`
          break
        case 'verbose':
          result = `I ${value ? 'will now' : 'will no longer'} be extra chatty`
          break
      }

      await this.store.setMetaData(team, key, value)
      await this.store.updateBotMetadata(bot)
      reply(`OK, ${result}.`)

    // A greeting?
    } else if ((/^(hey|hi|hello|waves)$/i).test(msg)) {
      if (shouldReply || isVerbose) reply(this.oneOf(GREETINGS).replace(/\$who/ig, sender))

    // Addressing the bot?
    } else if (bot.identity && msg.toLowerCase() === `${bot.identity.name.toLowerCase()}?`) {
      reply(this.oneOf(ACKNOWLEDGEMENTS))

    // Getting literal factoids
    } else if (shouldReply && (/^literal\s+/i).test(msg)) {
      let key = msg.replace(/^literal\s+/i, '')
      let current = await this.store.getFactoid(team, key)
      if (current != null) {
        reply(`${key} ${current}`)
      } else {
        reply(this.oneOf(I_DONT_KNOW))
      }

    // Getting regular factoids
    } else if (shouldReply && (/^wh?at\s+(is|are)\s+/i).test(msg)) {
      let key = msg.replace(/^wh?at\s+(is|are)\s+/i, '').replace(/\?+$/, '')
      if (IGNORED_FACTOIDS.includes(key.toLowerCase())) return
      let current = await this.store.getFactoid(team, key)
      if (current != null) {
        await parseAndReply(key, current)
      } else {
        reply(this.oneOf(I_DONT_KNOW))
      }

    // Deleting factoids
    } else if (isDirect && (/^forget\s+/i).test(msg)) {
      let key = msg.replace(/^forget\s+/i, '')
      await this.store.deleteFactoid(team, key)
      reply(`OK, I forgot about ${key}`)

    // Tell users about things
    } else if ((/^tell\s+\S+\s+about\s+/i).test(msg)) {
      let res = null
      try {
        res = await denodeify(bot.api.users.list.bind(bot.api.users))({})
      } catch (err) {
        log.error(err)
        reply('There was an error while downloading the list of users. Please try again.')
        return
      }

      msg = msg.replace(/^tell\s+/i, '')
      let [targetName, ...parts] = msg.split(/\s*about\s*/i)
      let key = parts.join(' ')

      let match = targetName.match(/^<@(\w+)>$/)
      let targetID = match ? match[1] : null
      if (!targetID) {
        for (let {id, name} of res.members) {
          this.userIdsToNames[id] = name
          if (name === targetName) {
            targetID = id
          }
        }
      }

      if (targetID === null) {
        reply(`I don't know who ${targetName} is.`)
        return
      }

      let value = await this.store.getFactoid(team, key)
      if (value == null) {
        reply(this.oneOf(I_DONT_KNOW))
        return
      }

      try {
        res = await denodeify(bot.api.im.open.bind(bot.api.im))({user: targetID})
      } catch (err) {
        log.error(err)
        reply(`I could not start an IM session with ${targetName}. Please try again.`)
        return
      }

      let targetChannel = res.channel ? res.channel.id : null
      if (isVerbose) reply(`OK, I told ${targetName} about ${key}`)
      await parseAndReply(key, value, targetChannel)

    // Karma query
    } else if ((/^karma\s+(for\s+)?/i).test(msg)) {
      let key = msg.replace(/^karma\s+(for\s+)?/i, '').replace(/\?+$/, '')
      let current = await this.store.getKarma(team, key)
      if (!current) current = 0
      reply(`${key} has ${current} karma`)

    // Karma increment
    } else if (/\+\+(\s#.+)?$/.test(msg)) {
      if (isDirect) return reply('You cannot secretly change the karma for something!')
      let key = msg.split(/\+\+/)[0]
      let current = await this.store.getKarma(team, key)
      let value = Number(current || 0) + 1
      await this.store.setKarma(team, key, value)

    // Karma decrement
    } else if (/--(\s#.+)?$/.test(msg)) {
      if (isDirect) return reply('You cannot secretly change the karma for something!')
      let key = msg.split(/--/)[0]
      let current = await this.store.getKarma(team, key)
      let value = Number(current || 0) - 1
      await this.store.setKarma(team, key, value)

    // Updating factoids
    } else if (shouldLearn && (/\s+(is|are)\s+/i).test(msg)) {
      let [, key, verb, value] = msg.match(/^(.+?)\s+(is|are)\s+(.*)/i)
      key = key.toLowerCase()
      verb = verb.toLowerCase()

      if (IGNORED_FACTOIDS.includes(key)) return

      let isCorrecting = (/no,?\s+/i).test(key)
      if (isCorrecting) { key = key.replace(/no,?\s+/i, '') }

      let isAppending = (/also,?\s+/i).test(key)
      if (isAppending) { key = key.replace(/also,?\s+/i, '') }

      if (!isAppending) { isAppending = (/also,?\s+/i).test(value) }
      if (isAppending) { value = value.replace(/also,?\s+/i, '') }

      key = key.replace(/^but,?\s+/, '')

      let current = await this.store.getFactoid(team, key)

      if (current && current.replace(/^(is|are)\s+/i, '') === value) {
        reply(this.oneOf('I already know that.', "I've already got it as that."))
      } else if (current && isCorrecting) {
        await update(key, `${verb} ${value}`)
      } else if (current && isAppending) {
        if (/^\|/.test(value)) {
          value = `${current}${value}`
        } else {
          value = `${current} or ${value}`
        }
        await update(key, value)
      } else if (current) {
        current = current.replace(/^(is|are)\s+/i, '')
        reply(`But ${key} ${verb} already ${current}`)
      } else {
        await update(key, `${verb} ${value}`)
      }

    // Getting factoids without an interrogative requires addressing
    } else if (shouldReply && /\?+$/.test(msg)) {
      let key = msg.replace(/\?+$/, '')
      if (IGNORED_FACTOIDS.includes(key.toLowerCase())) return
      let current = await this.store.getFactoid(team, key)
      if (current != null) await parseAndReply(key, current)

    // Getting regular factoids, last chance
    } else {
      if (IGNORED_FACTOIDS.includes(msg.toLowerCase())) return
      let value = await this.store.getFactoid(team, msg)
      if (value != null) await parseAndReply(msg, value)
    }
  }
}
<commit_msg>Add 'which' to ignored keywords (Fixes #11)<commit_after>import Random from 'random-js'
import denodeify from 'denodeify'
import winston from 'winston'
import {AllHtmlEntities as Entities} from 'html-entities'

import {version as VERSION} from '../package.json'

const log = winston

const MAX_FACTOID_SIZE = Number(process.env.MAX_FACTOID_SIZE || 2048)

const I_DONT_KNOW = [
  "I don't know what that is.",
  'I have no idea.',
  'No idea.',
  "I don't know."
]

const OKAY = [
  'OK, got it.',
  'I got it.',
  'Understood.',
  'Gotcha.',
  'OK'
]

const GREETINGS = [
  'Heya, $who!',
  'Hi $who!',
  'Hello, $who',
  'Hello, $who!',
  'Greetings, $who'
]

const ACKNOWLEDGEMENTS = [
  'Yes?',
  'Yep?',
  'Yeah?'
]

const IGNORED_FACTOIDS = [
  'he',
  'help',
  'hers',
  'his',
  'huh',
  'it',
  "it's",
  'its',
  'she',
  'settings',
  'status',
  'that',
  'them',
  'there',
  'these',
  'they',
  'this',
  'those',
  'what',
  'when',
  'where',
  'which',
  'who',
  'why'
]

export class MemoryBotEngine {
  constructor (store) {
    this.store = store
    this.userIdsToNames = {}
    this._random = new Random()
  }

  oneOf () {
    let arr
    if (Array.isArray(arguments[0])) {
      arr = arguments[0]
    } else {
      arr = arguments
    }
    return this._random.pick(arr)
  }

  async handleMessage (bot, sender, channel, isDirect, msg) {
    // Ignore ourself.
    if (sender === bot.identity.id || sender === bot.identity.name) return

    const {mbMeta} = bot
    const team = bot.identifyTeam()
    const shouldLearn = isDirect || mbMeta.ambient // Should we be learning factoids?
    const shouldReply = isDirect || !mbMeta.direct // Should we reply to this?
    const isVerbose = mbMeta.verbose // TODO: Combine with shouldLearn

    // Sometimes Slack sends HTML entities.
    msg = Entities.decode(msg)

    // Basic input sanitization.
    msg = msg.substr(0, MAX_FACTOID_SIZE).trim().replace(/\0/g, '').replace(/\n/g, ' ')

    // Simple reply helper.
    const reply = text => bot.reply({channel}, {text})

    // Used to parse a factoid's contents and reply with it.
    const parseAndReply = async (key, value, tell = null) => {
      // Factoids are stored as { key: "foo", value: "is bar" }
      let [, verb, rest] = Array.from(value.match(/^(is|are)\s+(.*)/i))
      value = rest

      // Split on |, but don't split on \|. Use an HTML entity as a separator.
      value = value.replace(/\\\|/g, '\\|')
      value = this.oneOf(value.split(/\|/i)).trim()
      value = value.replace(/|/g, '|')

      const isReply = (/^<reply>\s*/i).test(value)
      if (isReply) value = value.replace(/^<reply>\s*/i, '')

      const isEmote = (/^<action>\s*/i).test(value)
      if (isEmote) value = value.replace(/^<action>\s*/i, '')

      value = value.replace(/\$who/ig, sender)

      if (tell) {
        bot.reply({channel: tell}, {text: `${sender} wants you to know: ${key} is ${value}`})
      } else if (isReply) {
        if (value && value !== '') reply(value)
      } else if (isEmote) {
        await denodeify(bot.api.callAPI.bind(bot.api))('chat.meMessage', {channel, text: value})
      } else {
        reply(`${key} ${verb} ${value}`)
      }
    }

    // Used to update a factoid and reply.
    const update = async (key, value) => {
      // Escape Slack @-groups. (When a user types '@here' we get '<!here|@here>'.)
      value = value.replace(/<!(\w+)\|@\w+>/ig, (_, x) => '`@' + x + '`')

      const lastEdit = `on ${new Date()} by ${sender}`
      await this.store.setFactoid(team, key, value, lastEdit)
      if (isVerbose || isDirect) reply(this.oneOf(OKAY))
    }

    // Status
    if (isDirect && (/^(?:status|settings)\??/ig).test(msg)) {
      const bool = x => x ? ':ballot_box_with_check:' : ':white_medium_square:'
      let count = await this.store.countFactoids(team)
      reply(`\
*Status*
I am memorybot v${VERSION} - https://statico.github.com/memorybot/
I am currently remembering ${count} factoids.
*Settings*
${bool(mbMeta.direct)} \`direct\` - Interactons require direct messages or @-mentions
${bool(mbMeta.ambient)} \`ambient\` - Learn factoids from ambient room chatter
${bool(mbMeta.verbose)} \`verbose\` - Make the bot more chatty with confirmations, greetings, etc.
Tell me "enable setting <name>" or "disable setting <name>" to change the above settings.\
`)

    // Help
    } else if (isDirect && (/^help\??/ig).test(msg)) {
      reply(`Hi alice, I'm a MemoryBot. I remember things and then recall them later when asked. Check out my home page for more information: https://statico.github.io/memorybot/ -- You can also leave feedback or file bugs on my GitHub issues page: https://github.com/statico/memorybot/issues`)

    // Settings
    } else if (isDirect && (/^(enable|disable)\s+setting\s+/i).test(msg)) {
      let [action, key] = msg.split(/\s+setting\s+/i)
      let value = (action === 'enable')

      let result
      switch (key) {
        case 'direct':
          result = `interactions with me ${value ? 'now' : 'no longer'} require direct messages or @-mentions`
          break
        case 'ambient':
          result = `I ${value ? 'will now' : 'will no longer'} learn factoids without being told explicitly`
          break
        case 'verbose':
          result = `I ${value ? 'will now' : 'will no longer'} be extra chatty`
          break
      }

      await this.store.setMetaData(team, key, value)
      await this.store.updateBotMetadata(bot)
      reply(`OK, ${result}.`)

    // A greeting?
    } else if ((/^(hey|hi|hello|waves)$/i).test(msg)) {
      if (shouldReply || isVerbose) reply(this.oneOf(GREETINGS).replace(/\$who/ig, sender))

    // Addressing the bot?
    } else if (bot.identity && msg.toLowerCase() === `${bot.identity.name.toLowerCase()}?`) {
      reply(this.oneOf(ACKNOWLEDGEMENTS))

    // Getting literal factoids
    } else if (shouldReply && (/^literal\s+/i).test(msg)) {
      let key = msg.replace(/^literal\s+/i, '')
      let current = await this.store.getFactoid(team, key)
      if (current != null) {
        reply(`${key} ${current}`)
      } else {
        reply(this.oneOf(I_DONT_KNOW))
      }

    // Getting regular factoids
    } else if (shouldReply && (/^wh?at\s+(is|are)\s+/i).test(msg)) {
      let key = msg.replace(/^wh?at\s+(is|are)\s+/i, '').replace(/\?+$/, '')
      if (IGNORED_FACTOIDS.includes(key.toLowerCase())) return
      let current = await this.store.getFactoid(team, key)
      if (current != null) {
        await parseAndReply(key, current)
      } else {
        reply(this.oneOf(I_DONT_KNOW))
      }

    // Deleting factoids
    } else if (isDirect && (/^forget\s+/i).test(msg)) {
      let key = msg.replace(/^forget\s+/i, '')
      await this.store.deleteFactoid(team, key)
      reply(`OK, I forgot about ${key}`)

    // Tell users about things
    } else if ((/^tell\s+\S+\s+about\s+/i).test(msg)) {
      let res = null
      try {
        res = await denodeify(bot.api.users.list.bind(bot.api.users))({})
      } catch (err) {
        log.error(err)
        reply('There was an error while downloading the list of users. Please try again.')
        return
      }

      msg = msg.replace(/^tell\s+/i, '')
      let [targetName, ...parts] = msg.split(/\s*about\s*/i)
      let key = parts.join(' ')

      let match = targetName.match(/^<@(\w+)>$/)
      let targetID = match ? match[1] : null
      if (!targetID) {
        for (let {id, name} of res.members) {
          this.userIdsToNames[id] = name
          if (name === targetName) {
            targetID = id
          }
        }
      }

      if (targetID === null) {
        reply(`I don't know who ${targetName} is.`)
        return
      }

      let value = await this.store.getFactoid(team, key)
      if (value == null) {
        reply(this.oneOf(I_DONT_KNOW))
        return
      }

      try {
        res = await denodeify(bot.api.im.open.bind(bot.api.im))({user: targetID})
      } catch (err) {
        log.error(err)
        reply(`I could not start an IM session with ${targetName}. Please try again.`)
        return
      }

      let targetChannel = res.channel ? res.channel.id : null
      if (isVerbose) reply(`OK, I told ${targetName} about ${key}`)
      await parseAndReply(key, value, targetChannel)

    // Karma query
    } else if ((/^karma\s+(for\s+)?/i).test(msg)) {
      let key = msg.replace(/^karma\s+(for\s+)?/i, '').replace(/\?+$/, '')
      let current = await this.store.getKarma(team, key)
      if (!current) current = 0
      reply(`${key} has ${current} karma`)

    // Karma increment
    } else if (/\+\+(\s#.+)?$/.test(msg)) {
      if (isDirect) return reply('You cannot secretly change the karma for something!')
      let key = msg.split(/\+\+/)[0]
      let current = await this.store.getKarma(team, key)
      let value = Number(current || 0) + 1
      await this.store.setKarma(team, key, value)

    // Karma decrement
    } else if (/--(\s#.+)?$/.test(msg)) {
      if (isDirect) return reply('You cannot secretly change the karma for something!')
      let key = msg.split(/--/)[0]
      let current = await this.store.getKarma(team, key)
      let value = Number(current || 0) - 1
      await this.store.setKarma(team, key, value)

    // Updating factoids
    } else if (shouldLearn && (/\s+(is|are)\s+/i).test(msg)) {
      let [, key, verb, value] = msg.match(/^(.+?)\s+(is|are)\s+(.*)/i)
      key = key.toLowerCase()
      verb = verb.toLowerCase()

      if (IGNORED_FACTOIDS.includes(key)) return

      let isCorrecting = (/no,?\s+/i).test(key)
      if (isCorrecting) { key = key.replace(/no,?\s+/i, '') }

      let isAppending = (/also,?\s+/i).test(key)
      if (isAppending) { key = key.replace(/also,?\s+/i, '') }

      if (!isAppending) { isAppending = (/also,?\s+/i).test(value) }
      if (isAppending) { value = value.replace(/also,?\s+/i, '') }

      key = key.replace(/^but,?\s+/, '')

      let current = await this.store.getFactoid(team, key)

      if (current && current.replace(/^(is|are)\s+/i, '') === value) {
        reply(this.oneOf('I already know that.', "I've already got it as that."))
      } else if (current && isCorrecting) {
        await update(key, `${verb} ${value}`)
      } else if (current && isAppending) {
        if (/^\|/.test(value)) {
          value = `${current}${value}`
        } else {
          value = `${current} or ${value}`
        }
        await update(key, value)
      } else if (current) {
        current = current.replace(/^(is|are)\s+/i, '')
        reply(`But ${key} ${verb} already ${current}`)
      } else {
        await update(key, `${verb} ${value}`)
      }

    // Getting factoids without an interrogative requires addressing
    } else if (shouldReply && /\?+$/.test(msg)) {
      let key = msg.replace(/\?+$/, '')
      if (IGNORED_FACTOIDS.includes(key.toLowerCase())) return
      let current = await this.store.getFactoid(team, key)
      if (current != null) await parseAndReply(key, current)

    // Getting regular factoids, last chance
    } else {
      if (IGNORED_FACTOIDS.includes(msg.toLowerCase())) return
      let value = await this.store.getFactoid(team, msg)
      if (value != null) await parseAndReply(msg, value)
    }
  }
}
<|endoftext|>


<commit_before>/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { SyncQueue } from './SyncQueue';
import { generateSyncJson } from './outgoingSyncUtils';
import {
  integrateIncomingRecord,
  sanityCheckIncomingRecord,
} from './incomingSyncUtils';
import { SETTINGS_KEYS } from '../settings';
const {
  SYNC_SERVER_ID,
  SYNC_SITE_ID,
  SYNC_URL,
} = SETTINGS_KEYS;

const BATCH_SIZE = 20; // Number of records to sync at one time

/**
 * Provides core synchronization functionality, initilising the database with an
 * initial full sync, pushing, and pulling (any regular scheduling of synchronization
 * must be coordinated externally)
 */
export class Synchronizer {

  constructor(database, authenticator, settings) {
    this.database = database;
    this.authenticator = authenticator;
    this.settings = settings;
    this.syncQueue = new SyncQueue(database);
    this.synchronize = this.synchronize.bind(this);
    if (this.isInitialised()) this.syncQueue.enable();
  }

  /**
   * Wipe the current database, check that the given url can be synced against
   * using the given name and password, and if so populate the database by pulling
   * from the remote server.
   * @param  {string} serverURL        The URL to be synced against
   * @param  {string} syncSiteName     The username to use in sync auth
   * @param  {string} syncSitePassword The password to use in sync auth
   * @return {Promise}                 Resolve if successfully authenticated and
   *                                   initialised, otherwise throw error
   */
  async initialise(serverURL, syncSiteName, syncSitePassword, setProgress) {
    if (setProgress) setProgress('Initialising...');
    this.syncQueue.disable(); // Stop sync queue listening to database changes
    this.database.write(() => { this.database.deleteAll(); });
    try {
      await this.authenticator.authenticate(serverURL, syncSiteName, syncSitePassword);
      const thisSiteId = this.settings.get(SYNC_SITE_ID);
      const serverId = this.settings.get(SYNC_SERVER_ID);
      await fetch(
        `${serverURL}/sync/v2/initial_dump/?from_site=${thisSiteId}&to_site=${serverId}`,
        {
          headers: {
            Authorization: this.authenticator.getAuthHeader(),
          },
        });
      await this.pull(setProgress);
    } catch (error) { // Did not authenticate or internet failed, wipe db and pass error up
      this.database.write(() => { this.database.deleteAll(); });
      throw error;
    }
    this.syncQueue.enable(); // Begin the sync queue listening to database changes
  }

  /**
   * Return whether or not the synchronizer has been initialised with a URL to sync
   * against.
   * @return {Boolean} Whether the synchronizer is initialised
   */
  isInitialised() {
    const syncURL = this.settings.get(SYNC_URL);
    return syncURL && syncURL.length > 0;
  }

  /**
   * Carry out a synchronization, first pushing any local changes, then pulling
   * down remote changes and integrating them into the local database.
   * @return {[type]} [description]
   */
  async synchronize() {
    if (!this.isInitialised()) throw new Error('Not yet initialised');
    // Using async/await here means that any errors thrown by push or pull
    // will be passed up as a rejection of the promise returned by synchronize
    await this.push();
    console.log('pushed');
    await this.pull();
  }

  /**
   * Push batches of changes to the local database up to the remote server, until
   * all local changes have been synced.
   * @return {Promise} Resolves if successful, or passes up any error thrown
   */
  async push() {
    let recordsToSync;
    let translatedRecords;
    while (this.syncQueue.length() > 0) {
      recordsToSync = this.syncQueue.next(BATCH_SIZE);
      translatedRecords = recordsToSync.map((record) => generateSyncJson(this.database,
                                                                         this.settings,
                                                                         record));
      await this.pushRecords(translatedRecords);
      this.syncQueue.use(recordsToSync);
    }
  }

  /**
   * Pushes a collection of records to the remote sync server
   * @param  {array}   records The records to push
   * @return {Promise}         Resolves if successful, or passes up any error thrown
   */
  async pushRecords(records) {
    const serverURL = this.settings.get(SYNC_URL);
    const thisSiteId = this.settings.get(SYNC_SITE_ID);
    const serverId = this.settings.get(SYNC_SERVER_ID);
    await fetch(
      `${serverURL}/sync/v2/queued_records/?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        method: 'POST',
        headers: {
          Authorization: this.authenticator.getAuthHeader(),
        },
        body: JSON.stringify(records),
      });
  }

  /**
   * Pulls any changes to data on the sync server down to the local database
   * @return {Promise} Resolves if successful, or passes up any error thrown
   */
  async pull(setProgress) {
    const serverURL = this.settings.get(SYNC_URL);
    const thisSiteId = this.settings.get(SYNC_SITE_ID);
    const serverId = this.settings.get(SYNC_SERVER_ID);
    await this.recursivePull(serverURL, thisSiteId, serverId, setProgress);
  }

  /**
   * Recursively checks how many records left to pull, pulls in a batch, and calls
   * itself
   * @param  {string} serverURL  The URL of the sync server
   * @param  {string} thisSiteId The sync ID of this sync site
   * @param  {string} serverId   The sync ID of the server
   * @return {Promise}          Resolves if successful, or passes up any error thrown
   */
  async recursivePull(serverURL, thisSiteId, serverId, setProgress) {
    const authHeader = this.authenticator.getAuthHeader();
    const waitingRecordCount = await this.getWaitingRecordCount(serverURL,
                                                           thisSiteId,
                                                           serverId,
                                                           authHeader);
    if (setProgress) setProgress(`${waitingRecordCount} records to go`);
    if (waitingRecordCount === 0) return; // Done recursing through records

    // Get a batch of records and integrate them
    const incomingRecords = await this.getIncomingRecords(serverURL,
                                                     thisSiteId,
                                                     serverId,
                                                     authHeader,
                                                     BATCH_SIZE);
    this.integrateIncomingRecords(incomingRecords);
    await this.acknowledgeRecords(serverURL, thisSiteId, serverId, authHeader, incomingRecords);

    // Recurse to get the next batch of records from the server
    await this.recursivePull(serverURL, thisSiteId, serverId, setProgress);
  }

  /**
   * Returns the number of records left to pull
   * @param  {string} serverURL  URL of the sync server
   * @param  {string} thisSiteId Sync ID of this sync site
   * @param  {string} serverId   Sync ID of the server
   * @return {Promise}           Resolves with the record count, or passes up any error thrown
   */
  async getWaitingRecordCount(serverURL, thisSiteId, serverId, authHeader) {
    return await fetch(
      `${serverURL}/sync/v2/queued_records/count?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        headers: {
          Authorization: authHeader,
        },
      })
      .then((response) => response.json())
      .then((responseJson) => responseJson.NumRecords);
  }

    /**
   * Returns the next batch of incoming sync records
   * @param  {string}  serverURL  The URL of the sync server
   * @param  {string}  thisSiteId The sync ID of this sync site
   * @param  {string}  serverId   The sync ID of the server
   * @param  {integer} numRecords The number of records to fetch
   * @return {Promise}            Resolves with the records, or passes up any error thrown
   */
  async getIncomingRecords(serverURL, thisSiteId, serverId, authHeader, numRecords) {
    const response = await fetch(
      `${serverURL}/sync/v2/queued_records`
      + `?from_site=${thisSiteId}&to_site=${serverId}&limit=${numRecords}`,
      {
        headers: {
          Authorization: authHeader,
        },
      });
    if (response.status < 200 || response.status >= 300) {
      throw new Error('Connection failure while pulling sync records.');
    }
    const responseJson = await response.json();
    if (responseJson.error && responseJson.error.length > 0) {
      throw new Error(responseJson.error);
    }
    return responseJson;
  }

  /**
   * Parse the batch of incoming records, and integrate them into the local database
   * @param  {Realm}  database The local database
   * @param  {object} records  The json object the server sent to represent records
   * @return {none}
   */
  integrateIncomingRecords(syncJson) {
    this.database.write(() => {
      syncJson.forEach((object) => {
        if (!object.data) return; // No data for the sync record, ignore it
        const record = object.data;
        if (!object.RecordType) return; // RecordType unknown, ignore the record
        const recordType = object.RecordType;
        // If the sync record doesn't have appropriate format for the record type, ignore it
        if (sanityCheckIncomingRecord(recordType, record)) {
          integrateIncomingRecord(this.database, recordType, record);
        }
      });
    });
  }

  /**
   * Sends the sync server a message to indicate the sync records have been consumed
   * @param  {string}  serverURL  Base URL of the sync server
   * @param  {string}  thisSiteId Sync id of this mobile site
   * @param  {string}  serverId   Sync id of the primary server
   * @param  {string}  authHeader Base64 encoded Basic auth header
   * @param  {array}   records    Sync records that have been integrated
   * @return {none}
   */
  async acknowledgeRecords(serverURL, thisSiteId, serverId, authHeader, records) {
    const syncIds = records.map((record) => record.SyncID);
    const requestBody = {
      FromID: parseInt(serverId, 10), // one // three
      ToID: parseInt(thisSiteId, 10),
      acknowledged_records: { ItemFields: syncIds }, // two
    };
    await fetch(
      `${serverURL}/sync/v2/acknowledged_records?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        method: 'POST',
        headers: {
          Authorization: authHeader,
        },
        body: JSON.stringify(requestBody),
      });
  }

}
<commit_msg>Error handling on sync push rejection<commit_after>/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { SyncQueue } from './SyncQueue';
import { generateSyncJson } from './outgoingSyncUtils';
import {
  integrateIncomingRecord,
  sanityCheckIncomingRecord,
} from './incomingSyncUtils';
import { SETTINGS_KEYS } from '../settings';
const {
  SYNC_SERVER_ID,
  SYNC_SITE_ID,
  SYNC_URL,
} = SETTINGS_KEYS;

const BATCH_SIZE = 20; // Number of records to sync at one time

/**
 * Provides core synchronization functionality, initilising the database with an
 * initial full sync, pushing, and pulling (any regular scheduling of synchronization
 * must be coordinated externally)
 */
export class Synchronizer {

  constructor(database, authenticator, settings) {
    this.database = database;
    this.authenticator = authenticator;
    this.settings = settings;
    this.syncQueue = new SyncQueue(database);
    this.synchronize = this.synchronize.bind(this);
    if (this.isInitialised()) this.syncQueue.enable();
  }

  /**
   * Wipe the current database, check that the given url can be synced against
   * using the given name and password, and if so populate the database by pulling
   * from the remote server.
   * @param  {string} serverURL        The URL to be synced against
   * @param  {string} syncSiteName     The username to use in sync auth
   * @param  {string} syncSitePassword The password to use in sync auth
   * @return {Promise}                 Resolve if successfully authenticated and
   *                                   initialised, otherwise throw error
   */
  async initialise(serverURL, syncSiteName, syncSitePassword, setProgress) {
    if (setProgress) setProgress('Initialising...');
    this.syncQueue.disable(); // Stop sync queue listening to database changes
    this.database.write(() => { this.database.deleteAll(); });
    try {
      await this.authenticator.authenticate(serverURL, syncSiteName, syncSitePassword);
      const thisSiteId = this.settings.get(SYNC_SITE_ID);
      const serverId = this.settings.get(SYNC_SERVER_ID);
      await fetch(
        `${serverURL}/sync/v2/initial_dump/?from_site=${thisSiteId}&to_site=${serverId}`,
        {
          headers: {
            Authorization: this.authenticator.getAuthHeader(),
          },
        });
      await this.pull(setProgress);
    } catch (error) { // Did not authenticate or internet failed, wipe db and pass error up
      this.database.write(() => { this.database.deleteAll(); });
      throw error;
    }
    this.syncQueue.enable(); // Begin the sync queue listening to database changes
  }

  /**
   * Return whether or not the synchronizer has been initialised with a URL to sync
   * against.
   * @return {Boolean} Whether the synchronizer is initialised
   */
  isInitialised() {
    const syncURL = this.settings.get(SYNC_URL);
    return syncURL && syncURL.length > 0;
  }

  /**
   * Carry out a synchronization, first pushing any local changes, then pulling
   * down remote changes and integrating them into the local database.
   * @return {[type]} [description]
   */
  async synchronize() {
    if (!this.isInitialised()) throw new Error('Not yet initialised');
    // Using async/await here means that any errors thrown by push or pull
    // will be passed up as a rejection of the promise returned by synchronize
    await this.push();
    console.log('pushed');
    await this.pull();
  }

  /**
   * Push batches of changes to the local database up to the remote server, until
   * all local changes have been synced.
   * @return {Promise} Resolves if successful, or passes up any error thrown
   */
  async push() {
    let recordsToSync;
    let translatedRecords;
    while (this.syncQueue.length() > 0) {
      recordsToSync = this.syncQueue.next(BATCH_SIZE);
      translatedRecords = recordsToSync.map((record) => generateSyncJson(this.database,
                                                                         this.settings,
                                                                         record));
      await this.pushRecords(translatedRecords);
      this.syncQueue.use(recordsToSync);
    }
  }

  /**
   * Pushes a collection of records to the remote sync server
   * @param  {array}   records The records to push
   * @return {Promise}         Resolves if successful, or passes up any error thrown
   */
  async pushRecords(records) {
    const serverURL = this.settings.get(SYNC_URL);
    const thisSiteId = this.settings.get(SYNC_SITE_ID);
    const serverId = this.settings.get(SYNC_SERVER_ID);
    const response = await fetch(
      `${serverURL}/sync/v2/queued_records/?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        method: 'POST',
        headers: {
          Authorization: this.authenticator.getAuthHeader(),
        },
        body: JSON.stringify(records),
      });
    const responseJson = await response.json();
    if (responseJson.error.length > 0) throw new Error('Server rejected pushed records');
  }

  /**
   * Pulls any changes to data on the sync server down to the local database
   * @return {Promise} Resolves if successful, or passes up any error thrown
   */
  async pull(setProgress) {
    const serverURL = this.settings.get(SYNC_URL);
    const thisSiteId = this.settings.get(SYNC_SITE_ID);
    const serverId = this.settings.get(SYNC_SERVER_ID);
    await this.recursivePull(serverURL, thisSiteId, serverId, setProgress);
  }

  /**
   * Recursively checks how many records left to pull, pulls in a batch, and calls
   * itself
   * @param  {string} serverURL  The URL of the sync server
   * @param  {string} thisSiteId The sync ID of this sync site
   * @param  {string} serverId   The sync ID of the server
   * @return {Promise}          Resolves if successful, or passes up any error thrown
   */
  async recursivePull(serverURL, thisSiteId, serverId, setProgress) {
    const authHeader = this.authenticator.getAuthHeader();
    const waitingRecordCount = await this.getWaitingRecordCount(serverURL,
                                                           thisSiteId,
                                                           serverId,
                                                           authHeader);
    if (setProgress) setProgress(`${waitingRecordCount} records to go`);
    if (waitingRecordCount === 0) return; // Done recursing through records

    // Get a batch of records and integrate them
    const incomingRecords = await this.getIncomingRecords(serverURL,
                                                     thisSiteId,
                                                     serverId,
                                                     authHeader,
                                                     BATCH_SIZE);
    this.integrateIncomingRecords(incomingRecords);
    await this.acknowledgeRecords(serverURL, thisSiteId, serverId, authHeader, incomingRecords);

    // Recurse to get the next batch of records from the server
    await this.recursivePull(serverURL, thisSiteId, serverId, setProgress);
  }

  /**
   * Returns the number of records left to pull
   * @param  {string} serverURL  URL of the sync server
   * @param  {string} thisSiteId Sync ID of this sync site
   * @param  {string} serverId   Sync ID of the server
   * @return {Promise}           Resolves with the record count, or passes up any error thrown
   */
  async getWaitingRecordCount(serverURL, thisSiteId, serverId, authHeader) {
    return await fetch(
      `${serverURL}/sync/v2/queued_records/count?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        headers: {
          Authorization: authHeader,
        },
      })
      .then((response) => response.json())
      .then((responseJson) => responseJson.NumRecords);
  }

    /**
   * Returns the next batch of incoming sync records
   * @param  {string}  serverURL  The URL of the sync server
   * @param  {string}  thisSiteId The sync ID of this sync site
   * @param  {string}  serverId   The sync ID of the server
   * @param  {integer} numRecords The number of records to fetch
   * @return {Promise}            Resolves with the records, or passes up any error thrown
   */
  async getIncomingRecords(serverURL, thisSiteId, serverId, authHeader, numRecords) {
    const response = await fetch(
      `${serverURL}/sync/v2/queued_records`
      + `?from_site=${thisSiteId}&to_site=${serverId}&limit=${numRecords}`,
      {
        headers: {
          Authorization: authHeader,
        },
      });
    if (response.status < 200 || response.status >= 300) {
      throw new Error('Connection failure while pulling sync records.');
    }
    const responseJson = await response.json();
    if (responseJson.error && responseJson.error.length > 0) {
      throw new Error(responseJson.error);
    }
    return responseJson;
  }

  /**
   * Parse the batch of incoming records, and integrate them into the local database
   * @param  {Realm}  database The local database
   * @param  {object} records  The json object the server sent to represent records
   * @return {none}
   */
  integrateIncomingRecords(syncJson) {
    this.database.write(() => {
      syncJson.forEach((object) => {
        if (!object.data) return; // No data for the sync record, ignore it
        const record = object.data;
        if (!object.RecordType) return; // RecordType unknown, ignore the record
        const recordType = object.RecordType;
        // If the sync record doesn't have appropriate format for the record type, ignore it
        if (sanityCheckIncomingRecord(recordType, record)) {
          integrateIncomingRecord(this.database, recordType, record);
        }
      });
    });
  }

  /**
   * Sends the sync server a message to indicate the sync records have been consumed
   * @param  {string}  serverURL  Base URL of the sync server
   * @param  {string}  thisSiteId Sync id of this mobile site
   * @param  {string}  serverId   Sync id of the primary server
   * @param  {string}  authHeader Base64 encoded Basic auth header
   * @param  {array}   records    Sync records that have been integrated
   * @return {none}
   */
  async acknowledgeRecords(serverURL, thisSiteId, serverId, authHeader, records) {
    const syncIds = records.map((record) => record.SyncID);
    const requestBody = {
      FromID: parseInt(serverId, 10), // one // three
      ToID: parseInt(thisSiteId, 10),
      acknowledged_records: { ItemFields: syncIds }, // two
    };
    await fetch(
      `${serverURL}/sync/v2/acknowledged_records?from_site=${thisSiteId}&to_site=${serverId}`,
      {
        method: 'POST',
        headers: {
          Authorization: authHeader,
        },
        body: JSON.stringify(requestBody),
      });
  }

}
<|endoftext|>


<commit_before>const MessageHandler = require('../util/messageHandler')

exports.run = async (bot, msg, args) => {
    try {
        if(args.length < 2) {
            msg.reply('Zu wenige Argumente übergeben. Es müssen mindestens 2 sein!')
            return
        }

        const raidID = parseInt(args[0])
        const raid = await bot.database.getRaidByID(raidID)
        if(!raid) {
            msg.reply('Konnte den Raid nicht finden. Prüfe die Eingabe und versuchs nochmal!')
            return
        }

        let registerIDsToConfirm = []
        for(let index = 1; index < args.length; index++) {
            registerIDsToConfirm.push(parseInt(args[index]))
        }

        registerIDsToConfirm.forEach((index) => {
            if(raid.member.registered[index-1]) {
                const player = raid.member.registered[index-1]
                raid.member.registered = raid.member.registered.filter((registeredPlayer) => {
                    return registeredPlayer !== player
                })
                raid.member.confirmed.push(player)
            }
        })

        await bot.database.addOrUpdateRaid(raid)
        MessageHandler.updatePrintedRaid(bot, raid)
        msg.reply(`Alle Spieler, die identifiziert werden konnten, wurden für den Raid ${raid.id} bestätigt`)
    } catch(error) {
        msg.reply(error.message)
        console.log(`confirm:\n${error.stack}`)
    }
}

exports.conf = {
    enabled: true,
    guildOnly: false,
    aliases: ['confirm'],
    permLevel: 0
}

exports.help = {
    name: 'bestätigen',
    description: 'Angemeldete Spieler zum Raid bestätigen',
    usage: 'bestätigen <raidID> <Nummer> <Nummer> <Nummer> ...'
}
<commit_msg>fixed confirmation problem, I guess<commit_after>const MessageHandler = require('../util/messageHandler')

exports.run = async (bot, msg, args) => {
    try {
        if(args.length < 2) {
            msg.reply('Zu wenige Argumente übergeben. Es müssen mindestens 2 sein!')
            return
        }

        const raidID = parseInt(args[0])
        const raid = await bot.database.getRaidByID(raidID)
        if(!raid) {
            msg.reply('Konnte den Raid nicht finden. Prüfe die Eingabe und versuchs nochmal!')
            return
        }

        let registerIDsToConfirm = []
        for(let index = 1; index < args.length; index++) {
            registerIDsToConfirm.push(parseInt(args[index]))
        }

        registerIDsToConfirm.forEach((index) => {
            if(raid.member.registered[index-1]) {
                const player = raid.member.registered[index-1]
                raid.member.confirmed.push(player)
            }
        })

        registerIDsToConfirm.forEach((index) => {
            if(raid.member.registered[index-1]) {
                const player = raid.member.registered[index-1]
                raid.member.registered = raid.member.registered.filter((registeredPlayer) => {
                    return registeredPlayer !== player
                })
            }
        })

        await bot.database.addOrUpdateRaid(raid)
        MessageHandler.updatePrintedRaid(bot, raid)
        msg.reply(`Alle Spieler, die identifiziert werden konnten, wurden für den Raid ${raid.id} bestätigt`)
    } catch(error) {
        msg.reply(error.message)
        console.log(`confirm:\n${error.stack}`)
    }
}

exports.conf = {
    enabled: true,
    guildOnly: false,
    aliases: ['confirm'],
    permLevel: 0
}

exports.help = {
    name: 'bestätigen',
    description: 'Angemeldete Spieler zum Raid bestätigen',
    usage: 'bestätigen <raidID> <Nummer> <Nummer> <Nummer> ...'
}
<|endoftext|>


<commit_before>/*The MIT License (MIT)

Copyright (c) 2015 Jens Malmborg

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

var Utils = require('/utils.js').Utils;
var Quaternion = require('/matrix.js').Quaternion;
var Vector3 = require('/matrix.js').Vector3;
var Matrix = require('/matrix.js').Matrix;

var _forward = new Vector3(0,0,1);
var _up = new Vector3(0,1,0);
var _right = new Vector3(-1,0,0);

function Entity() {
    this.transform = new Transform();
    this.components = {};
}

Entity.prototype.addComponent = function (component) {
    if (component.entity) {
        var name = component.constructor.name.toLowerCase();
        throw new TypeError(
            "Component \'" + name + "\' is owned by another entity.");
    }
    component.entity = this;
    var name = component.constructor.name.toLowerCase();
    this.components[name.replace('component', '')] = component;
};

Entity.prototype.update = function (elapsed) {
    for (var name in this.components) {
        if (!this.components[name].enabled) {
            continue;
        }
        this.components[name].update(elapsed);
    }
};

Entity.prototype.draw = function () {
    for (var name in this.components) {
        if (!this.components[name].visible) {
            continue;
        }
        this.components[name].draw();
    }
};

function Transform() {
    var self = this;

    this._localScale = new Vector3(1,1,1);
    this._localRotation = new Quaternion();
    this._parent = null;
    this._local = new Matrix();
    this._forward = new Vector3();
    this._up = new Vector3();
    this._right = new Vector3();

    Object.defineProperty(this, 'localPosition', {
        get: (function() {
            var _localPosition = new Vector3();

            return function() {
                return _localPosition.set(
                    self._local[12], self._local[13], self._local[14]);
            }
        })(),
        set: function(value) {
            self._local[12] = value[0];
            self._local[13] = value[1];
            self._local[14] = value[2];
        }
    });

    Object.defineProperty(this, 'position', {
        get: (function() {
            var _position = new Vector3();

            return function() {
                _position.set(
                    self._local[12], self._local[13], self._local[14]);
                if (!self._parent) {
                    return _position;
                }
                return _position.transform(self._parent.world, _position);
            }
        })(),
        set: (function() {
            var _inverted = new Matrix();
            var _position = new Vector3();

            return function(value) {
                if (!self._parent) {
                    self._local[12] = value[0];
                    self._local[13] = value[1];
                    self._local[14] = value[2];
                } else {
                    // Convert world space to local.
                    self._parent.world.invert(_inverted);
                    value.transform(_inverted, _position);
                    self._local[12] = _position[0];
                    self._local[13] = _position[1];
                    self._local[14] = _position[2];
                }
            }
        })()
    });

    Object.defineProperty(this, 'localRotation', {
        get: function() {
            return self._localRotation;
        },
        set: (function() {
            var _diff = new Vector3();

            return function(value) {
                value.eulerAngles.sub(self._localRotation.eulerAngles, _diff);
                self.rotate(_diff[0], _diff[1], _diff[2]);
            }
        })()
    });

    Object.defineProperty(this, 'rotation', {
        get: (function() {
            var _rotation = new Quaternion();

            return function() {
                if (!self._parent) {
                    return self._localRotation;
                }
                return self._parent.rotation.mul(
                    self._localRotation, _rotation);
            }
        })()
    });

    Object.defineProperty(this, 'localScale', {
        get: function() {
            return self._localScale;
        },
        set: (function() {
            var _diff = new Vector3();

            return function(value) {
                value.div(self._localScale, _diff);
                self.scale(_diff[0], _diff[1], _diff[2]);
            }
        })()
    });

    Object.defineProperty(this, 'scaling', {
        get: (function() {
            var _scaling = new Vector3();

            return function() {
                if (!self._parent) {
                    return self._localScale;
                }
                return self._parent.scaling.mul(
                    self._localScale, _scaling);
            }
        })()
    });

    Object.defineProperty(this, 'eulerAngles', {
        get: function() {
            return self.rotation.eulerAngles;
        }
    });

    Object.defineProperty(this, 'world', {
        get: (function() {
            var _world = new Matrix();

            return function() {
                if (!self._parent) {
                    return self._local;
                }
                return self._parent.world.multiply(
                    self._local, _world);
            }
        })()
    });

}

Transform.prototype.parent = function(parent) {
    if (parent instanceof Transform) {
        this._parent = parent;
    } else if (parent instanceof Entity) {
        this._parent = parent.transform;
    } else {
        return this._parent;
    }
};

Transform.prototype.translate = function(x, y, z) {
    this._local.translate(x,y,z);
};

Transform.prototype.rotate = function(x, y, z) {
    this._local.rotate(x,y,z);
    this._localRotation.rotateX(x);
    this._localRotation.rotateY(y);
    this._localRotation.rotateZ(z);
};

Transform.prototype.scale = (function() {
    var _scale = new Vector3();

    return function(x, y, z) {
        this._local.scale(x,y,z);
        _scale.set(x,y,z);
        this._localScale.mul(_scale, this._localScale);
    }
})();

Transform.prototype.move = (function() {
    var _move = new Vector3();

    return function(x, y, z) {
        if (arguments.length === 1) {
            _move.set(arguments[0]);
            this.position = this.position.add(_move, _move);
        } else if (arguments.length === 3) {
            _move.set(x,y,z);
            this.position = this.position.add(_move, _move);
        } else {
            throw new TypeError('Wrong number of arguments.');
        }
    }
})();

Transform.prototype.forward = function() {
    this._forward = _forward.transform(this._localRotation, this._forward);
    return this._forward.normalize(this._forward);
};

Transform.prototype.up = function() {
    this._up = _up.transform(this._localRotation, this._up);
    return this._up.normalize(this._up);
};

Transform.prototype.right = function() {
    this._right = _right.transform(this._localRotation, this._right);
    return this._right.normalize(this._right);
};

function Component() {
    this.enabled = true;
    this.visible = true;
}

Component.prototype.update = function(elapsed) {
};

Component.prototype.draw = function() {
};

module.exports.Entity = Entity;
module.exports.Transform = Transform;
module.exports.Component = Component;<commit_msg>Added enabled and destroyed.<commit_after>/*The MIT License (MIT)

Copyright (c) 2015 Jens Malmborg

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

var Utils = require('/utils.js').Utils;
var Quaternion = require('/matrix.js').Quaternion;
var Vector3 = require('/matrix.js').Vector3;
var Matrix = require('/matrix.js').Matrix;

var _forward = new Vector3(0,0,1);
var _up = new Vector3(0,1,0);
var _right = new Vector3(-1,0,0);

function Entity() {
    this.transform = new Transform();
    this.components = {};
    this.enabled = true;
    this.destroyed = false;
}

Entity.prototype.addComponent = function (component) {
    if (component.entity) {
        var name = component.constructor.name.toLowerCase();
        throw new TypeError(
            "Component \'" + name + "\' is owned by another entity.");
    }
    component.entity = this;
    var name = component.constructor.name.toLowerCase();
    this.components[name.replace('component', '')] = component;
};

Entity.prototype.removeComponent = function (component) {
    if (component.entity !== this) {
        throw new TypeError(
            'The specified component doesn\'t belong to this entity');
    }
    for (var name in this.components) {
        if (this.components[name] === component) {
            delete this.components[name];
        }
    }
};

Entity.prototype.update = function (elapsed) {
    if (!this.enabled) {
        return;
    }
    for (var name in this.components) {
        if (!this.components[name].enabled) {
            continue;
        }
        this.components[name].update(elapsed);
    }
};

Entity.prototype.draw = function () {
    if (!this.enabled) {
        return;
    }
    for (var name in this.components) {
        if (!this.components[name].enabled) {
            continue;
        }
        this.components[name].draw();
    }
};

function Transform() {
    var self = this;

    this._localScale = new Vector3(1,1,1);
    this._localRotation = new Quaternion();
    this._parent = null;
    this._local = new Matrix();
    this._forward = new Vector3();
    this._up = new Vector3();
    this._right = new Vector3();

    Object.defineProperty(this, 'localPosition', {
        get: (function() {
            var _localPosition = new Vector3();

            return function() {
                return _localPosition.set(
                    self._local[12], self._local[13], self._local[14]);
            }
        })(),
        set: function(value) {
            self._local[12] = value[0];
            self._local[13] = value[1];
            self._local[14] = value[2];
        }
    });

    Object.defineProperty(this, 'position', {
        get: (function() {
            var _position = new Vector3();

            return function() {
                _position.set(
                    self._local[12], self._local[13], self._local[14]);
                if (!self._parent) {
                    return _position;
                }
                return _position.transform(self._parent.world, _position);
            }
        })(),
        set: (function() {
            var _inverted = new Matrix();
            var _position = new Vector3();

            return function(value) {
                if (!self._parent) {
                    self._local[12] = value[0];
                    self._local[13] = value[1];
                    self._local[14] = value[2];
                } else {
                    // Convert world space to local.
                    self._parent.world.invert(_inverted);
                    value.transform(_inverted, _position);
                    self._local[12] = _position[0];
                    self._local[13] = _position[1];
                    self._local[14] = _position[2];
                }
            }
        })()
    });

    Object.defineProperty(this, 'localRotation', {
        get: function() {
            return self._localRotation;
        },
        set: (function() {
            var _diff = new Vector3();

            return function(value) {
                value.eulerAngles.sub(self._localRotation.eulerAngles, _diff);
                self.rotate(_diff[0], _diff[1], _diff[2]);
            }
        })()
    });

    Object.defineProperty(this, 'rotation', {
        get: (function() {
            var _rotation = new Quaternion();

            return function() {
                if (!self._parent) {
                    return self._localRotation;
                }
                return self._parent.rotation.mul(
                    self._localRotation, _rotation);
            }
        })()
    });

    Object.defineProperty(this, 'localScale', {
        get: function() {
            return self._localScale;
        },
        set: (function() {
            var _diff = new Vector3();

            return function(value) {
                value.div(self._localScale, _diff);
                self.scale(_diff[0], _diff[1], _diff[2]);
            }
        })()
    });

    Object.defineProperty(this, 'scaling', {
        get: (function() {
            var _scaling = new Vector3();

            return function() {
                if (!self._parent) {
                    return self._localScale;
                }
                return self._parent.scaling.mul(
                    self._localScale, _scaling);
            }
        })()
    });

    Object.defineProperty(this, 'eulerAngles', {
        get: function() {
            return self.rotation.eulerAngles;
        }
    });

    Object.defineProperty(this, 'world', {
        get: (function() {
            var _world = new Matrix();

            return function() {
                if (!self._parent) {
                    return self._local;
                }
                return self._parent.world.multiply(
                    self._local, _world);
            }
        })()
    });

}

Transform.prototype.parent = function(parent) {
    if (parent instanceof Transform) {
        this._parent = parent;
    } else if (parent instanceof Entity) {
        this._parent = parent.transform;
    } else {
        return this._parent;
    }
};

Transform.prototype.translate = function(x, y, z) {
    this._local.translate(x,y,z);
};

Transform.prototype.rotate = function(x, y, z) {
    this._local.rotate(x,y,z);
    this._localRotation.rotateX(x);
    this._localRotation.rotateY(y);
    this._localRotation.rotateZ(z);
};

Transform.prototype.scale = (function() {
    var _scale = new Vector3();

    return function(x, y, z) {
        this._local.scale(x,y,z);
        _scale.set(x,y,z);
        this._localScale.mul(_scale, this._localScale);
    }
})();

Transform.prototype.move = (function() {
    var _move = new Vector3();

    return function(x, y, z) {
        if (arguments.length === 1) {
            _move.set(arguments[0]);
            this.position = this.position.add(_move, _move);
        } else if (arguments.length === 3) {
            _move.set(x,y,z);
            this.position = this.position.add(_move, _move);
        } else {
            throw new TypeError('Wrong number of arguments.');
        }
    }
})();

Transform.prototype.forward = function() {
    this._forward = _forward.transform(this._localRotation, this._forward);
    return this._forward.normalize(this._forward);
};

Transform.prototype.up = function() {
    this._up = _up.transform(this._localRotation, this._up);
    return this._up.normalize(this._up);
};

Transform.prototype.right = function() {
    this._right = _right.transform(this._localRotation, this._right);
    return this._right.normalize(this._right);
};

function Component() {
    this.enabled = true;
}

Component.prototype.update = function(elapsed) {
};

Component.prototype.draw = function() {
};

module.exports.Entity = Entity;
module.exports.Transform = Transform;
module.exports.Component = Component;<|endoftext|>


<commit_before>/**
 * Copyright (c) 2011, Lasse Fister lasse@graphicore.de, http://graphicore.de
 *
 * You should have received a copy of the MIT License along with this program.
 * If not, see http://www.opensource.org/licenses/mit-license.php
 *
 */

define(function() {
    "use strict";
    //ufojs errors
    var errors = {}

    /**
     * safe three lines of coding for each error with this factory
     *
     * and observe that extending Error is uncool
     */
    var makeError = function(name, Constructor, prototype, namespace)
    {
        if(prototype === undefined)
            var prototype = new Error;

        if(Constructor === undefined) {
            var Constructor = function(message, stack) {
                if(message !== undefined) {
                    this.name = name;
                    this.message = message || "(no error message)";
                    stack = stack || (new Error).stack || '(no stack available)'
                    this.stack = [name, ' Error: ', this.message, '\n'
                                                       , stack].join('');
                }
            };
        };
        Constructor.prototype = prototype;
        Constructor.prototype.constructor = Constructor;
        if(namespace === undefined)
            var namespace = errors
        namespace[name] = Constructor;
    }
    errors.makeError = makeError;
    /**
     * here the definitions go
     */
    makeError('Error');
    makeError('NotImplemented', undefined , new errors.Error);
    makeError('Assertion', undefined , new errors.Error);
    makeError('Value', undefined , new errors.Error);
    makeError('Type', undefined , new TypeError);
    makeError('Dependency', undefined , new errors.Error);
    makeError('Parser', undefined , new errors.Error);
    makeError('IO', undefined , new errors.Error);
    makeError('IONoEntry', undefined, new errors.IO)
    makeError('IOEntryExists', undefined, new errors.IO)
    makeError('NameTranslation', undefined , new errors.Error);
    makeError('GlifLib', undefined , new errors.Error);
    makeError('Key', undefined , new TypeError);

    /**
     * if expression is false errors.Assertion is thrown
     * pass a message to explain yourself
     **/
    errors.assert = function(exp, message) {
        if (!exp) {
            throw new errors.Assertion(message);
        }
    };
    errors.warn = function(message) {
        if(typeof console !== 'undefined' && console.log)
            console.log('WARNING: ' + message);
    };

    return errors;
});
<commit_msg>add new io errors<commit_after>/**
 * Copyright (c) 2011, Lasse Fister lasse@graphicore.de, http://graphicore.de
 *
 * You should have received a copy of the MIT License along with this program.
 * If not, see http://www.opensource.org/licenses/mit-license.php
 *
 */

define(function() {
    "use strict";
    //ufojs errors
    var errors = {}

    /**
     * safe three lines of coding for each error with this factory
     *
     * and observe that extending Error is uncool
     */
    var makeError = function(name, Constructor, prototype, namespace)
    {
        if(prototype === undefined)
            var prototype = new Error;

        if(Constructor === undefined) {
            var Constructor = function(message, stack) {
                if(message !== undefined) {
                    this.name = name;
                    this.message = message || "(no error message)";
                    stack = stack || (new Error).stack || '(no stack available)'
                    this.stack = [name, ' Error: ', this.message, '\n'
                                                       , stack].join('');
                }
            };
        };
        Constructor.prototype = prototype;
        Constructor.prototype.constructor = Constructor;
        if(namespace === undefined)
            var namespace = errors
        namespace[name] = Constructor;
    }
    errors.makeError = makeError;
    /**
     * here the definitions go
     */
    makeError('Error');
    makeError('NotImplemented', undefined , new errors.Error);
    makeError('Assertion', undefined , new errors.Error);
    makeError('Value', undefined , new errors.Error);
    makeError('Type', undefined , new TypeError);
    makeError('Dependency', undefined , new errors.Error);
    makeError('Parser', undefined , new errors.Error);
    makeError('IO', undefined , new errors.Error);
    makeError('IONoEntry', undefined, new errors.IO)
    makeError('IOEntryExists', undefined, new errors.IO)
    // the following IO errors are optional for IO implementations
    makeError('IONotDir', undefined, new errors.IO)
    makeError('IOIsDir', undefined, new errors.IO)
    makeError('IONotEmpty', undefined, new errors.IO)
    makeError('NameTranslation', undefined , new errors.Error);
    makeError('GlifLib', undefined , new errors.Error);
    makeError('Key', undefined , new TypeError);

    /**
     * if expression is false errors.Assertion is thrown
     * pass a message to explain yourself
     **/
    errors.assert = function(exp, message) {
        if (!exp) {
            throw new errors.Assertion(message);
        }
    };
    errors.warn = function(message) {
        if(typeof console !== 'undefined' && console.log)
            console.log('WARNING: ' + message);
    };

    return errors;
});
<|endoftext|>


<commit_before>_renderVideo = _renderVideo || {}

_renderVideo.vimeo = function _renderVideoVimeo (parent, options) {
  var player_id = options.id + (new Date()).getTime()
  var params = {
    loop: options.loop,
    title: 0,
    badge: 0,
    byline: 0,
    portrait: 0,
    api: 1,
    player_id: player_id
  }
  var vimeoElem = Object.create(VimeoElem)
  vimeoElem.setOptions({
    url:
      [
        '//player.vimeo.com/video/',
        options.id,
        uriEncodeObj(params)
      ].join(''),
    player_id: player_id,
    parent: parent,
    ratio: options.aspectRatio,
    volume: options.volume,
    autoSize: true
  })
  vimeoElem.initializeElem()
}

var VimeoElem = {
  setOptions: function setOptions (options) {
    _.extend(this, options)
  },

  initializeElem: function () {
    var self = this
    self.renderHTML()
    if(self.autoSize) {
      self.updateStyles()
      addEventListener('resize', self.updateStyles.bind(self))
    }
    self.parent.appendChild(self.elem)
    self.elem.addEventListener('load', function () {
      self.callAPI('setVolume', self.volume)
      self.callAPI('play')
    })
  },

  renderHTML: function createVimeoElement () {
    this.elem = document.createElement('iframe')
    this.elem.src = this.url
    this.elem.className = 'videobackground__frame'
    this.elem.setAttribute('frameborder', 0)
    this.elem.id = this.player_id
  },

  setSize: function setSize () {
    if(
      this.parent.clientWidth / this.ratio >
      this.parent.clientHeight
    ) {
      console.log('Height is smaller')
      this.width = this.parent.clientWidth
      this.height = this.parent.clientWidth / this.ratio
    } else {
      console.log('Width is smaller')
      this.width = this.parent.clientHeight * this.ratio
      this.height = this.parent.clientHeight
    }
  },

  updateStyles: function updateStyles () {
    this.setSize()
    this.elem.width = this.width + 'px'
    this.elem.height = this.height + 'px'
    this.elem.style.marginLeft = ['-', 'px'].join(Math.round(this.width / 2))
    this.elem.style.marginTop = ['-', 'px'].join(Math.round(this.height / 2))
  },

  callAPI: function callAPI (method, value) {
    this.elem.contentWindow.postMessage(JSON.stringify({
      method: method,
      value: value
    }), '*')
  }
}
<commit_msg>Remove old console.logs<commit_after>_renderVideo = _renderVideo || {}

_renderVideo.vimeo = function _renderVideoVimeo (parent, options) {
  var player_id = options.id + (new Date()).getTime()
  var params = {
    loop: options.loop,
    title: 0,
    badge: 0,
    byline: 0,
    portrait: 0,
    api: 1,
    player_id: player_id
  }
  var vimeoElem = Object.create(VimeoElem)
  vimeoElem.setOptions({
    url:
      [
        '//player.vimeo.com/video/',
        options.id,
        uriEncodeObj(params)
      ].join(''),
    player_id: player_id,
    parent: parent,
    ratio: options.aspectRatio,
    volume: options.volume,
    autoSize: true
  })
  vimeoElem.initializeElem()
}

var VimeoElem = {
  setOptions: function setOptions (options) {
    _.extend(this, options)
  },

  initializeElem: function () {
    var self = this
    self.renderHTML()
    if(self.autoSize) {
      self.updateStyles()
      addEventListener('resize', self.updateStyles.bind(self))
    }
    self.parent.appendChild(self.elem)
    self.elem.addEventListener('load', function () {
      self.callAPI('setVolume', self.volume)
      self.callAPI('play')
    })
  },

  renderHTML: function createVimeoElement () {
    this.elem = document.createElement('iframe')
    this.elem.src = this.url
    this.elem.className = 'videobackground__frame'
    this.elem.setAttribute('frameborder', 0)
    this.elem.id = this.player_id
  },

  setSize: function setSize () {
    if(
      this.parent.clientWidth / this.ratio >
      this.parent.clientHeight
    ) {
      this.width = this.parent.clientWidth
      this.height = this.parent.clientWidth / this.ratio
    } else {
      this.width = this.parent.clientHeight * this.ratio
      this.height = this.parent.clientHeight
    }
  },

  updateStyles: function updateStyles () {
    this.setSize()
    this.elem.width = this.width + 'px'
    this.elem.height = this.height + 'px'
    this.elem.style.marginLeft = ['-', 'px'].join(Math.round(this.width / 2))
    this.elem.style.marginTop = ['-', 'px'].join(Math.round(this.height / 2))
  },

  callAPI: function callAPI (method, value) {
    this.elem.contentWindow.postMessage(JSON.stringify({
      method: method,
      value: value
    }), '*')
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { NavLink } from 'react-router-dom';
import './AsideMenu.scss';

export default function AsideMenu({ selectedServer }) {
  const serverId = selectedServer ? selectedServer.id : '';

  return (

        <NavLink
          className="aside-menu__item"
          activeClassName="aside-menu__item--selected"
          to={`/server/${serverId}/list-short-urls/1`}
          isActive={match => match && match.params.serverId === serverId}
        >
          List short URLs
        </NavLink>
        <NavLink
          className="aside-menu__item"
          activeClassName="aside-menu__item--selected"
          to={`/server/${serverId}/create-short-url`}
        >
          Create short code
        </NavLink>

        Delete this server

  );
}
<commit_msg>Fixed siude menu active element not being properly selected<commit_after>import React from 'react';
import { NavLink } from 'react-router-dom';
import './AsideMenu.scss';

export default function AsideMenu({ selectedServer }) {
  const serverId = selectedServer ? selectedServer.id : '';
  const isListShortUrlsActive = (match, { pathname }) => {
    // FIXME. Should use the 'match' params, but they are not being properly resolved. Investigate
    const serverIdFromPathname = pathname.split('/')[2];
    return serverIdFromPathname === serverId && pathname.indexOf('list-short-urls') !== -1;
  };

  return (

        <NavLink
          className="aside-menu__item"
          activeClassName="aside-menu__item--selected"
          to={`/server/${serverId}/list-short-urls/1`}
          isActive={isListShortUrlsActive}
        >
          List short URLs
        </NavLink>
        <NavLink
          className="aside-menu__item"
          activeClassName="aside-menu__item--selected"
          to={`/server/${serverId}/create-short-url`}
        >
          Create short code
        </NavLink>

        Delete this server

  );
}
<|endoftext|>


<commit_before>exports.run = (client, message, args) => {
  message.delete();

  regex = new RegExp("^[0-9]");
  let question = args.join(' ');

  if(question.test(regex)){
    try {
      var answer = eval(question);
      message.channel.send(`Question : ${args.join(' ')} =  \`${answer}\``);
    } catch (error) {
      message.channel.send(`⚠Error : ${error.message}`);
    }
  }else{
    message.channel.send("Please Don't Input String, I Know You See Me Using Eval()", {code: 'js'});
  }

}
<commit_msg>Update cal.js<commit_after>exports.run = (client, message, args) => {
  message.delete();

  regex = new RegExp("^\d");
  let question = args.join(' ');

  if(question.test(regex)){
    try {
      var answer = eval(question);
      message.channel.send(`Question : ${args.join(' ')} =  \`${answer}\``);
    } catch (error) {
      message.channel.send(`⚠Error : ${error.message}`);
    }
  }else{
    message.channel.send("Please Don't Input String, I Know You See Me Using Eval()", {code: 'js'});
  }

}
<|endoftext|>