<commit_before>'use strict';
var NodeCache = require('node-cache');
var _ = require('lodash');
var moment = require('moment');
const Promise = require('bluebird');
var r = require('../services/reddit.js');
var parseArgs = require('minimist');
var cache = new NodeCache({stdTTL: 900});
const removedNoteCache = [];
const warningsToWords = {
  none: ['blue', 'none'],
  gooduser: ['green', 'misc', 'misc.', 'miscellaneous'],
  spamwatch: ['pink', 'sketchy'],
  spamwarn: ['purple', 'flair', 'fc'],
  abusewarn: ['yellow', 'orange', 'minor warning', 'warn', 'warning'],
  ban: ['red', 'major warning', 'tempban', 'light red'],
  permban: ['brown', 'dark red', 'permaban', 'permanent ban'],
  botban: ['black', 'old', 'general']
};
let warningTypes = _.keys(warningsToWords);
let usageForNerds = 'Usage: .tag [show | add | append] < -n|--note> "<note>" < --user <username> | /u/<username> >\
 [--sub <subreddit> | /r/<subreddit>] [--url <url> | https://<url>] [--color <color>] [--refresh | --refresh-cache]';
let usageForRegularPeople = 'To view /u/username\'s tags on /r/pokemontrades: .tag /u/username\n\
To view /u/username\'s tags on /r/somewhere_else: .tag /u/username /r/somewhere_else\n\
To add a tag: .tag add --note "Note text goes here" https://reddit.com/some_link\n\
To add a red tag: .tag add --note "Note text goes here" --color red https://reddit.com/some_link\n\
To view advanced options: .tag options';

var wordsToWarnings = {};
for (let i = 0; i < warningTypes.length; i++) {
  warningsToWords[warningTypes[i]].forEach(function (word) {
    wordsToWarnings[word] = warningTypes[i];
  });
}

module.exports = {
  message_regex: /^\.(?:usernote|tag)(?: (.*)|$)/,
  allow: function ({isPM, isAuthenticated, isMod}) {
    return !isPM && isAuthenticated && isMod;
  },
  response: function ({message_match, author_match}) {
    /* Split into words, but keep quoted blocks together. Also parse reddit usernames and correctly.
    e.g. '--option1 word --option2 "quoted block" otherword /u/someone /r/some_sub https://reddit.com/blah/'
    --> ['--option1', 'word', '--option2', 'quoted block', 'otherword', '--user', 'someone', '--subreddit', 'some_sub', '--link', 'reddit.com/blah/'] */
    if (!message_match[1]) {
      return usageForRegularPeople;
    }
    let splitIntoWords = /(http(?:s)?:\/\/)|(\/u\/)|(\/r\/)|([^\s"]+)|(?:"((\\")?(?:[^"\\]|\\\\|\\")*)")+/g;
    if (message_match[1].replace(splitIntoWords, '').trim()) {
      throw {error_message: 'Error: invalid string.'};
    }
    let preparsedArgs = message_match[1].match(splitIntoWords).map(function (str) {
      return str.replace(/\\\\/g, '\\').replace(/\\"/g, '"').replace(/^"(.*)"$/, '$1').replace(/^\/?u\/$/, '--user')
        .replace(/^\/?r\/$/, '--subreddit').replace(/^http(s?):\/\/$/, '--link');
    });
    let parseSettings = {
      default: {type: 'yellow'},
      boolean: ['refresh', 'help'],
      alias: {
        sub: 'subreddit', s: 'subreddit', text: 'note', n: 'note', u: 'user', color: 'type', c: 'type', warning: 'type',
        url: 'link', l: 'link', 'refresh-cache': 'refresh'
      }
    };
    let args = parseArgs(preparsedArgs, parseSettings);
    let command = args._.length ? args._[0].toLowerCase() : 'show';
    args.subreddit = args.link || args.subreddit ? args.subreddit : 'pokemontrades';
    if (command === 'help' || args.help) {
      return usageForRegularPeople;
    }
    if (command === 'show') {
      if (!args.user) {
        throw {error_message: 'Error: No user provided. For help, try `.tag help`.'};
      }
      return getNotes(args.subreddit, {refresh: args.refresh}).then(function (parsed) {
        let notes = _.find(parsed.notes, function (obj, name) {
          if (name.toLowerCase() === args.user.toLowerCase()) {
            args.user = name;
            return true;
          }
        });
        if (notes) {
          return [`Notes on /u/${args.user} on /r/${args.subreddit}:`].concat(
            notes.ns.map((note, i) => `(${i}) ${formatNote(note, args.subreddit)}`)
          );
        }
        return `No notes found for /u/${args.user} on /r/${args.subreddit}.`;
      }).catch(handleErrors);
    } else if (['add', 'create', 'append'].indexOf(command) !== -1) {
      let warning = wordsToWarnings[args.type];
      if (!warning) {
        throw {error_message: `Error: Unknown note type '${args.type}'`};
      }
      if (!args.note) {
        throw {error_message: 'Error: Missing note text.'};
      }
      return getMissingInfo(args).then(props => addNote({
          mod: author_match[0],
          user: props.user,
          subreddit: props.subreddit,
          note: args.note,
          warning,
          link: props.link,
          index: command === 'append' ? undefined : 0
        }).then(result => [`Successfully added note on /u/${props.user}:`, `${formatNote(result, props.subreddit)}`])
      ).catch(handleErrors);
    } else if (['delete', 'rm', 'remove'].indexOf(command) !== -1) {
      if (!args.user) {
        throw {error_message: 'Error: No user provided'};
      }
      args.subreddit = args.subreddit || 'pokemontrades';
      if (!_.isNumber(args._[1])) {
        throw {error_message: 'No index number provided. You must provide the index number of the note to remove.'};
      }
      return removeNote({user: args.user, subreddit: args.subreddit, index: args._[1], requester: author_match[1]}).then(note => {
        return ['Successfully deleted the following note. To undo this action, use ".tag undo-delete".', formatNote(note, args.subreddit)];
      }).catch(handleErrors);
    } else if (command === 'undo-delete') {
      const newNote = removedNoteCache.pop();
      return addNote(newNote).then(result => (
        [`Successfully recreated note on /u/${newNote.user} on /r/${newNote.subreddit}:`, formatNote(result, newNote.subreddit)]
      )).catch(err => {
        removedNoteCache.push(newNote);
        handleErrors(err);
      });
    } else if (command === 'options') {
      return usageForNerds;
    } else {
      throw {error_message: `Error: Unrecognized command "${command}". For help, use ".tag help".`};
    }
  }
};
function decompressBlob (blob) {
  return JSON.parse(require('zlib').inflateSync(new Buffer(blob, 'base64')));
}
function compressBlob (notesObject) {
  return require('zlib').deflateSync(new Buffer(JSON.stringify(notesObject))).toString('base64');
}
function getNotes (subreddit, {refresh = false} = {}) {
  const cached_notes = cache.get(subreddit);
  if (cached_notes && !refresh) {
    return Promise.resolve(cached_notes);
  }
  return r.get_subreddit(subreddit).get_wiki_page('usernotes').content_md.then(JSON.parse).then(pageObject => {
    const parsed = _(pageObject).assign({notes: decompressBlob(pageObject.blob)}).omit('blob').value();
    cache.set(subreddit, parsed);
    return parsed;
  });
}
function parseUrl (url) {
  const urlParser = /^(?:(?:http(?:s?):\/\/)?(?:\w*\.)?reddit.com)?\/(?:r\/(\w{1,21})\/comments\/(\w*)(?:\/[\w-]*\/(\w*))?)|(?:message\/messages\/(\w*))/;
  const matched = url.match(urlParser);
  if (!matched) {
    throw {error_message: 'Error: The provided URL is invalid.'};
  }
  return {subreddit: matched[1], submissionId: matched[2], commentId: matched[3], messageId: matched[4]};
}
function getMissingInfo ({user: providedUser, subreddit: providedSubreddit, link: providedUrl}) {
  /* If no URL was provided, simply use the information that was given. However, fetch the user's information
  on reddit, because their name might have been capitalized incorrectly and it's important that we get the
  correct capitalization. */
  if (!providedUrl) {
    if (!providedUser) {
      throw {error_message: 'Error: Either a user or a URL is required.'};
    }
    if (!providedSubreddit) {
      throw {error_message: 'Error: Either a subreddit or a URL is required.'};
    }
    return Promise.props({user: r.get_user(providedUser).fetch().name, subreddit: providedSubreddit});
  }
  // If a URL was provided, fetch the information as necessary.
  const parsedUrl = parseUrl(providedUrl);
  let link, contentObject;
  if (parsedUrl.messageId) {
    contentObject = r.get_message(parsedUrl.messageId);
    link = `m,${parsedUrl.messageId}`;
  } else if (parsedUrl.commentId) {
    contentObject = r.get_comment(parsedUrl.commentId);
    link = `l,${parsedUrl.submissionId},${parsedUrl.commentId}`;
  } else {
    contentObject = r.get_submission(parsedUrl.submissionId);
    link = `l,${parsedUrl.submissionId}`;
  }
  const user = contentObject.author.name.then(name => {
    if (providedUser) {
      // If the provided user is the author of the content, use the username from the content
      if (name.toLowerCase() === providedUser.toLowerCase()) {
        return name;
      }
      // Otherwise, get the provided user's profile page to make sure the capitalization is correct.
      return r.get_user(providedUser).fetch().name.catchReturn(providedUser);
    }
    if (name === '[deleted]') {
      throw {error_message: 'Error: The linked item was deleted, and no username was provided.'};
    }
    return name;
  });
  // The subreddit is only used as part of a URL, so capitalization doesn't matter.
  const subreddit = providedSubreddit || parsedUrl.subreddit || contentObject.subreddit.display_name;
  return Promise.props({user, subreddit, link});
}
function addNote ({mod, user, subreddit, note, warning = 'abusewarn', link, index, timestamp = moment().unix()}) {
  return getNotes(subreddit, {refresh: true}).then(parsed => {
    _.merge(parsed.notes, {[user]: {ns: []}});
    index = index === undefined ? parsed.notes[user].ns.length : index;
    const newNote = {
      n: note,
      t: timestamp,
      m: (parsed.constants.users.indexOf(mod) + 1 || parsed.constants.users.push(mod)) - 1,
      w: (parsed.constants.warnings.indexOf(warning) + 1 || parsed.constants.warnings.push(warning)) - 1,
      l: link
    };
    parsed.notes[user].ns.splice(index, 0, newNote);
    return r.get_subreddit(subreddit).get_wiki_page('usernotes').edit({
      text: JSON.stringify(_(parsed).assign({blob: compressBlob(parsed.notes)}).omit('notes').value()),
      reason: `Added a note on /u/${user} (on behalf of ${mod})`
    }).then(() => {
      cache.set(subreddit, parsed);
      return newNote;
    });
  });
}
function removeNote ({user, subreddit, index, requester}) {
  return getNotes(subreddit, {refresh: true}).then(parsed => {
    const name = _.findKey(parsed.notes, (obj, username) => username.toLowerCase() === user.toLowerCase());
    if (!name || !_.isInteger(index) || !_.inRange(index, parsed.notes[name].ns.length)) {
      throw {error_message: 'Error: That note was not found.'};
    }
    const removedNote = parsed.notes[name].ns.splice(index, 1)[0];
    if (!parsed.notes[name].ns.length) {
      delete parsed.notes[name];
    }
    return r.get_subreddit(subreddit).get_wiki_page('usernotes').edit({
      text: JSON.stringify(_(parsed).assign({blob: compressBlob(parsed.notes)}).omit('notes').value()),
      reason: `Removed a note on /u/${user}${requester ? `(on behalf of ${requester})` : ''}`
    }).then(() => {
      removedNoteCache.push({
        mod: parsed.constants.users[removedNote.m],
        user,
        subreddit,
        note: removedNote.n,
        warning: parsed.constants.warnings[removedNote.w],
        link: removedNote.l,
        index,
        timestamp: removedNote.t
      });
      cache.set(subreddit, parsed);
      return removedNote;
    });
  });
}
function formatNote (note, subreddit) {
  let parsed = cache.get(subreddit);
  const color = warningsToWords[parsed.constants.warnings[note.w]][0];
  const author = parsed.constants.users[note.m];
  const timestamp = moment.unix(note.t).fromNow();
  let link;
  if (note.l) {
    if (note.l.charAt(0) === 'm') {
      link = `reddit.com/message/messages/${note.l.slice(2)}`;
    } else if (note.l.slice(2).includes(',')) {
      link = `reddit.com/comments/${note.l.slice(2).replace(/,/, '/-/')}`;
    } else {
      link = `reddit.com/${note.l.slice(2)}`;
    }
  }
  const content = note.n;
  return `"${content}" (${timestamp}, by ${author}${link ? `, on link ${link} ` : ''}, colored ${color})`;
}
function handleErrors (err) {
  throw _.assign(err, {
    error_message: err.error_message || (err.statusCode ? `Error: Reddit sent status code ${err.statusCode}.` : 'An unknown error occured.')
  });
}
<commit_msg>Fix for tagging comments in threads with titles containing unicode and/or other special characters (#45)<commit_after>'use strict';
var NodeCache = require('node-cache');
var _ = require('lodash');
var moment = require('moment');
const Promise = require('bluebird');
var r = require('../services/reddit.js');
var parseArgs = require('minimist');
var cache = new NodeCache({stdTTL: 900});
const removedNoteCache = [];
const warningsToWords = {
  none: ['blue', 'none'],
  gooduser: ['green', 'misc', 'misc.', 'miscellaneous'],
  spamwatch: ['pink', 'sketchy'],
  spamwarn: ['purple', 'flair', 'fc'],
  abusewarn: ['yellow', 'orange', 'minor warning', 'warn', 'warning'],
  ban: ['red', 'major warning', 'tempban', 'light red'],
  permban: ['brown', 'dark red', 'permaban', 'permanent ban'],
  botban: ['black', 'old', 'general']
};
let warningTypes = _.keys(warningsToWords);
let usageForNerds = 'Usage: .tag [show | add | append] < -n|--note> "<note>" < --user <username> | /u/<username> >\
 [--sub <subreddit> | /r/<subreddit>] [--url <url> | https://<url>] [--color <color>] [--refresh | --refresh-cache]';
let usageForRegularPeople = 'To view /u/username\'s tags on /r/pokemontrades: .tag /u/username\n\
To view /u/username\'s tags on /r/somewhere_else: .tag /u/username /r/somewhere_else\n\
To add a tag: .tag add --note "Note text goes here" https://reddit.com/some_link\n\
To add a red tag: .tag add --note "Note text goes here" --color red https://reddit.com/some_link\n\
To view advanced options: .tag options';

var wordsToWarnings = {};
for (let i = 0; i < warningTypes.length; i++) {
  warningsToWords[warningTypes[i]].forEach(function (word) {
    wordsToWarnings[word] = warningTypes[i];
  });
}

module.exports = {
  message_regex: /^\.(?:usernote|tag)(?: (.*)|$)/,
  allow: function ({isPM, isAuthenticated, isMod}) {
    return !isPM && isAuthenticated && isMod;
  },
  response: function ({message_match, author_match}) {
    /* Split into words, but keep quoted blocks together. Also parse reddit usernames and correctly.
    e.g. '--option1 word --option2 "quoted block" otherword /u/someone /r/some_sub https://reddit.com/blah/'
    --> ['--option1', 'word', '--option2', 'quoted block', 'otherword', '--user', 'someone', '--subreddit', 'some_sub', '--link', 'reddit.com/blah/'] */
    if (!message_match[1]) {
      return usageForRegularPeople;
    }
    let splitIntoWords = /(http(?:s)?:\/\/)|(\/u\/)|(\/r\/)|([^\s"]+)|(?:"((\\")?(?:[^"\\]|\\\\|\\")*)")+/g;
    if (message_match[1].replace(splitIntoWords, '').trim()) {
      throw {error_message: 'Error: invalid string.'};
    }
    let preparsedArgs = message_match[1].match(splitIntoWords).map(function (str) {
      return str.replace(/\\\\/g, '\\').replace(/\\"/g, '"').replace(/^"(.*)"$/, '$1').replace(/^\/?u\/$/, '--user')
        .replace(/^\/?r\/$/, '--subreddit').replace(/^http(s?):\/\/$/, '--link');
    });
    let parseSettings = {
      default: {type: 'yellow'},
      boolean: ['refresh', 'help'],
      alias: {
        sub: 'subreddit', s: 'subreddit', text: 'note', n: 'note', u: 'user', color: 'type', c: 'type', warning: 'type',
        url: 'link', l: 'link', 'refresh-cache': 'refresh'
      }
    };
    let args = parseArgs(preparsedArgs, parseSettings);
    let command = args._.length ? args._[0].toLowerCase() : 'show';
    args.subreddit = args.link || args.subreddit ? args.subreddit : 'pokemontrades';
    if (command === 'help' || args.help) {
      return usageForRegularPeople;
    }
    if (command === 'show') {
      if (!args.user) {
        throw {error_message: 'Error: No user provided. For help, try `.tag help`.'};
      }
      return getNotes(args.subreddit, {refresh: args.refresh}).then(function (parsed) {
        let notes = _.find(parsed.notes, function (obj, name) {
          if (name.toLowerCase() === args.user.toLowerCase()) {
            args.user = name;
            return true;
          }
        });
        if (notes) {
          return [`Notes on /u/${args.user} on /r/${args.subreddit}:`].concat(
            notes.ns.map((note, i) => `(${i}) ${formatNote(note, args.subreddit)}`)
          );
        }
        return `No notes found for /u/${args.user} on /r/${args.subreddit}.`;
      }).catch(handleErrors);
    } else if (['add', 'create', 'append'].indexOf(command) !== -1) {
      let warning = wordsToWarnings[args.type];
      if (!warning) {
        throw {error_message: `Error: Unknown note type '${args.type}'`};
      }
      if (!args.note) {
        throw {error_message: 'Error: Missing note text.'};
      }
      return getMissingInfo(args).then(props => addNote({
          mod: author_match[0],
          user: props.user,
          subreddit: props.subreddit,
          note: args.note,
          warning,
          link: props.link,
          index: command === 'append' ? undefined : 0
        }).then(result => [`Successfully added note on /u/${props.user}:`, `${formatNote(result, props.subreddit)}`])
      ).catch(handleErrors);
    } else if (['delete', 'rm', 'remove'].indexOf(command) !== -1) {
      if (!args.user) {
        throw {error_message: 'Error: No user provided'};
      }
      args.subreddit = args.subreddit || 'pokemontrades';
      if (!_.isNumber(args._[1])) {
        throw {error_message: 'No index number provided. You must provide the index number of the note to remove.'};
      }
      return removeNote({user: args.user, subreddit: args.subreddit, index: args._[1], requester: author_match[1]}).then(note => {
        return ['Successfully deleted the following note. To undo this action, use ".tag undo-delete".', formatNote(note, args.subreddit)];
      }).catch(handleErrors);
    } else if (command === 'undo-delete') {
      const newNote = removedNoteCache.pop();
      return addNote(newNote).then(result => (
        [`Successfully recreated note on /u/${newNote.user} on /r/${newNote.subreddit}:`, formatNote(result, newNote.subreddit)]
      )).catch(err => {
        removedNoteCache.push(newNote);
        handleErrors(err);
      });
    } else if (command === 'options') {
      return usageForNerds;
    } else {
      throw {error_message: `Error: Unrecognized command "${command}". For help, use ".tag help".`};
    }
  }
};
function decompressBlob (blob) {
  return JSON.parse(require('zlib').inflateSync(new Buffer(blob, 'base64')));
}
function compressBlob (notesObject) {
  return require('zlib').deflateSync(new Buffer(JSON.stringify(notesObject))).toString('base64');
}
function getNotes (subreddit, {refresh = false} = {}) {
  const cached_notes = cache.get(subreddit);
  if (cached_notes && !refresh) {
    return Promise.resolve(cached_notes);
  }
  return r.get_subreddit(subreddit).get_wiki_page('usernotes').content_md.then(JSON.parse).then(pageObject => {
    const parsed = _(pageObject).assign({notes: decompressBlob(pageObject.blob)}).omit('blob').value();
    cache.set(subreddit, parsed);
    return parsed;
  });
}
function parseUrl (url) {
  const urlParser = /^(?:(?:http(?:s?):\/\/)?(?:\w*\.)?reddit.com)?\/(?:r\/(\w{1,21})\/comments\/(\w*)(?:\/[^\/]*\/(\w*))?)|(?:message\/messages\/(\w*))/;
  const matched = url.match(urlParser);
  if (!matched) {
    throw {error_message: 'Error: The provided URL is invalid.'};
  }
  return {subreddit: matched[1], submissionId: matched[2], commentId: matched[3], messageId: matched[4]};
}
function getMissingInfo ({user: providedUser, subreddit: providedSubreddit, link: providedUrl}) {
  /* If no URL was provided, simply use the information that was given. However, fetch the user's information
  on reddit, because their name might have been capitalized incorrectly and it's important that we get the
  correct capitalization. */
  if (!providedUrl) {
    if (!providedUser) {
      throw {error_message: 'Error: Either a user or a URL is required.'};
    }
    if (!providedSubreddit) {
      throw {error_message: 'Error: Either a subreddit or a URL is required.'};
    }
    return Promise.props({user: r.get_user(providedUser).fetch().name, subreddit: providedSubreddit});
  }
  // If a URL was provided, fetch the information as necessary.
  const parsedUrl = parseUrl(providedUrl);
  let link, contentObject;
  if (parsedUrl.messageId) {
    contentObject = r.get_message(parsedUrl.messageId);
    link = `m,${parsedUrl.messageId}`;
  } else if (parsedUrl.commentId) {
    contentObject = r.get_comment(parsedUrl.commentId);
    link = `l,${parsedUrl.submissionId},${parsedUrl.commentId}`;
  } else {
    contentObject = r.get_submission(parsedUrl.submissionId);
    link = `l,${parsedUrl.submissionId}`;
  }
  const user = contentObject.author.name.then(name => {
    if (providedUser) {
      // If the provided user is the author of the content, use the username from the content
      if (name.toLowerCase() === providedUser.toLowerCase()) {
        return name;
      }
      // Otherwise, get the provided user's profile page to make sure the capitalization is correct.
      return r.get_user(providedUser).fetch().name.catchReturn(providedUser);
    }
    if (name === '[deleted]') {
      throw {error_message: 'Error: The linked item was deleted, and no username was provided.'};
    }
    return name;
  });
  // The subreddit is only used as part of a URL, so capitalization doesn't matter.
  const subreddit = providedSubreddit || parsedUrl.subreddit || contentObject.subreddit.display_name;
  return Promise.props({user, subreddit, link});
}
function addNote ({mod, user, subreddit, note, warning = 'abusewarn', link, index, timestamp = moment().unix()}) {
  return getNotes(subreddit, {refresh: true}).then(parsed => {
    _.merge(parsed.notes, {[user]: {ns: []}});
    index = index === undefined ? parsed.notes[user].ns.length : index;
    const newNote = {
      n: note,
      t: timestamp,
      m: (parsed.constants.users.indexOf(mod) + 1 || parsed.constants.users.push(mod)) - 1,
      w: (parsed.constants.warnings.indexOf(warning) + 1 || parsed.constants.warnings.push(warning)) - 1,
      l: link
    };
    parsed.notes[user].ns.splice(index, 0, newNote);
    return r.get_subreddit(subreddit).get_wiki_page('usernotes').edit({
      text: JSON.stringify(_(parsed).assign({blob: compressBlob(parsed.notes)}).omit('notes').value()),
      reason: `Added a note on /u/${user} (on behalf of ${mod})`
    }).then(() => {
      cache.set(subreddit, parsed);
      return newNote;
    });
  });
}
function removeNote ({user, subreddit, index, requester}) {
  return getNotes(subreddit, {refresh: true}).then(parsed => {
    const name = _.findKey(parsed.notes, (obj, username) => username.toLowerCase() === user.toLowerCase());
    if (!name || !_.isInteger(index) || !_.inRange(index, parsed.notes[name].ns.length)) {
      throw {error_message: 'Error: That note was not found.'};
    }
    const removedNote = parsed.notes[name].ns.splice(index, 1)[0];
    if (!parsed.notes[name].ns.length) {
      delete parsed.notes[name];
    }
    return r.get_subreddit(subreddit).get_wiki_page('usernotes').edit({
      text: JSON.stringify(_(parsed).assign({blob: compressBlob(parsed.notes)}).omit('notes').value()),
      reason: `Removed a note on /u/${user}${requester ? `(on behalf of ${requester})` : ''}`
    }).then(() => {
      removedNoteCache.push({
        mod: parsed.constants.users[removedNote.m],
        user,
        subreddit,
        note: removedNote.n,
        warning: parsed.constants.warnings[removedNote.w],
        link: removedNote.l,
        index,
        timestamp: removedNote.t
      });
      cache.set(subreddit, parsed);
      return removedNote;
    });
  });
}
function formatNote (note, subreddit) {
  let parsed = cache.get(subreddit);
  const color = warningsToWords[parsed.constants.warnings[note.w]][0];
  const author = parsed.constants.users[note.m];
  const timestamp = moment.unix(note.t).fromNow();
  let link;
  if (note.l) {
    if (note.l.charAt(0) === 'm') {
      link = `reddit.com/message/messages/${note.l.slice(2)}`;
    } else if (note.l.slice(2).includes(',')) {
      link = `reddit.com/comments/${note.l.slice(2).replace(/,/, '/-/')}`;
    } else {
      link = `reddit.com/${note.l.slice(2)}`;
    }
  }
  const content = note.n;
  return `"${content}" (${timestamp}, by ${author}${link ? `, on link ${link} ` : ''}, colored ${color})`;
}
function handleErrors (err) {
  throw _.assign(err, {
    error_message: err.error_message || (err.statusCode ? `Error: Reddit sent status code ${err.statusCode}.` : 'An unknown error occured.')
  });
}
<|endoftext|>


<commit_before>
var gulp         = require('gulp');
var del          = require('del');
var browsersync  = require('browser-sync').create();
var imagemin     = require('gulp-imagemin');
var autoprefixer = require('gulp-autoprefixer');
var sass         = require('gulp-sass');
var cleancss     = require('gulp-clean-css');
var sourcemaps   = require('gulp-sourcemaps');
var uglify       = require('gulp-uglify');
var concat       = require('gulp-concat');
var jshint       = require('gulp-jshint');
var childprocess = require('child_process');
var argv         = require('yargs').argv;
var gulpif       = require('gulp-if');
var runsequence  = require('run-sequence');
// var wiredep      = require('wiredep').stream;

var paths = {
  source: {
    css: 'scss/**/*.scss',
    html: 'html/**/*.html',
    js: [
      'node_modules/jquery/dist/jquery.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/affix.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/carousel.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/collapse.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/scrollspy.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
      'js/app.js'
    ],
    img: 'img/**/*.{svg,png,gif,jpg,jpeg}'
  },
  deploy: {
    css: 'deploy/css/',
    html: 'deploy/',
    js: 'deploy/js/',
    img: 'deploy/img/'
  }
};

// See https://github.com/austinpray/asset-builder
// var manifest = require('asset-builder')('./manifest.json');

gulp.task('html', function(done) {
  browsersync.notify('Running: jekyll build');
  return childprocess.spawn('jekyll', ['build'], {stdio: 'inherit'})
    .on('close', done);
});

gulp.task('css', function() {
  return gulp.src(paths.source.css)
    .pipe(gulpif(!argv.production, sourcemaps.init()))
    .pipe(sass({ precision: 10 }).on('error', sass.logError))
    // See https://github.com/postcss/autoprefixer
    // and https://github.com/ai/browserslist
    // and http://caniuse.com/usage-table
    .pipe(autoprefixer({ browsers: ['last 2 versions'] }))
    .pipe(gulpif(!argv.production, sourcemaps.write()))
    .pipe(cleancss())
    .pipe(gulp.dest(paths.deploy.css));
});

gulp.task('js', ['lint'], function() {
  return gulp.src(paths.source.js)
    .pipe(sourcemaps.init())
    .pipe(concat('app.js'))
    .pipe(uglify())
    .pipe(gulpif(!argv.production, sourcemaps.write()))
    .pipe(gulp.dest(paths.deploy.js));
});

gulp.task('lint', function() {
  return gulp.src('js/app.js')
    .pipe(jshint({ lookup: false }))
    .pipe(jshint.reporter('default'));
});

gulp.task('img', function() {
  return gulp.src(paths.source.img)
    .pipe(imagemin())
    .pipe(gulp.dest(paths.deploy.img));
});

gulp.task('serve', function() {
  browsersync.init({
    ui: {
      port: 1337
    },
    open: false,
    notify: true,
    files: [
      'deploy/**/*'
    ],
    server: {
      baseDir: 'deploy/'
    }
  });
});

gulp.task('clean', function() {
  return del('deploy');
});

gulp.task('watch', function() {
  gulp.watch(paths.source.css, ['css']);
  gulp.watch(paths.source.html, ['html']);
  gulp.watch(paths.source.js, ['js']);
  gulp.watch(paths.source.img, ['img']);
});

gulp.task('build', ['clean'], function(callback) {
  runsequence(['html', 'css', 'js', 'img'], callback);
});

gulp.task('default', ['build'], function() {
  runsequence('serve', 'watch');
});

// gulp.task('wiredep', function() {
//   return gulp.src('')
//     .pipe(wiredep({
//
//     }))
//     .pipe(gulp.dest(''));
// });
<commit_msg>remove unused bits<commit_after>
var gulp         = require('gulp');
var del          = require('del');
var browsersync  = require('browser-sync').create();
var imagemin     = require('gulp-imagemin');
var autoprefixer = require('gulp-autoprefixer');
var sass         = require('gulp-sass');
var cleancss     = require('gulp-clean-css');
var sourcemaps   = require('gulp-sourcemaps');
var uglify       = require('gulp-uglify');
var concat       = require('gulp-concat');
var jshint       = require('gulp-jshint');
var childprocess = require('child_process');
var argv         = require('yargs').argv;
var gulpif       = require('gulp-if');
var runsequence  = require('run-sequence');

var paths = {
  source: {
    css: 'scss/**/*.scss',
    html: 'html/**/*.html',
    js: [
      'node_modules/jquery/dist/jquery.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/affix.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/carousel.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/collapse.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/scrollspy.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
      'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
      'js/app.js'
    ],
    img: 'img/**/*.{svg,png,gif,jpg,jpeg}'
  },
  deploy: {
    css: 'deploy/css/',
    html: 'deploy/',
    js: 'deploy/js/',
    img: 'deploy/img/'
  }
};

gulp.task('html', function(done) {
  browsersync.notify('Running: jekyll build');
  return childprocess.spawn('jekyll', ['build'], {stdio: 'inherit'})
    .on('close', done);
});

gulp.task('css', function() {
  return gulp.src(paths.source.css)
    .pipe(gulpif(!argv.production, sourcemaps.init()))
    .pipe(sass({ precision: 10 }).on('error', sass.logError))
    // See https://github.com/postcss/autoprefixer
    // and https://github.com/ai/browserslist
    // and http://caniuse.com/usage-table
    .pipe(autoprefixer({ browsers: ['last 2 versions'] }))
    .pipe(gulpif(!argv.production, sourcemaps.write()))
    .pipe(cleancss())
    .pipe(gulp.dest(paths.deploy.css));
});

gulp.task('js', ['lint'], function() {
  return gulp.src(paths.source.js)
    .pipe(sourcemaps.init())
    .pipe(concat('app.js'))
    .pipe(uglify())
    .pipe(gulpif(!argv.production, sourcemaps.write()))
    .pipe(gulp.dest(paths.deploy.js));
});

gulp.task('lint', function() {
  return gulp.src('js/app.js')
    .pipe(jshint({ lookup: false }))
    .pipe(jshint.reporter('default'));
});

gulp.task('img', function() {
  return gulp.src(paths.source.img)
    .pipe(imagemin())
    .pipe(gulp.dest(paths.deploy.img));
});

gulp.task('serve', function() {
  browsersync.init({
    ui: {
      port: 1337
    },
    open: false,
    notify: true,
    files: [
      'deploy/**/*'
    ],
    server: {
      baseDir: 'deploy/'
    }
  });
});

gulp.task('clean', function() {
  return del('deploy');
});

gulp.task('watch', function() {
  gulp.watch(paths.source.css, ['css']);
  gulp.watch(paths.source.html, ['html']);
  gulp.watch(paths.source.js, ['js']);
  gulp.watch(paths.source.img, ['img']);
});

gulp.task('build', ['clean'], function(callback) {
  runsequence(['html', 'css', 'js', 'img'], callback);
});

gulp.task('default', ['build'], function() {
  runsequence('serve', 'watch');
});
<|endoftext|>


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

  var gulp = require('gulp');
  var plugins = require('gulp-load-plugins')();

  gulp.task('bundle', bundle);
  gulp.task('start-webserver', startWebServer);
  gulp.task('watch', watch);
  gulp.task('default', ['bundle', 'start-webserver', 'watch']);

  /////////////////////////

  var jsFiles = [
    'app/**/*.js',
    '!app/bower_components/**/*',
    '!app/content/bundle.js'
  ];

  function bundle() {
    return gulp.src(jsFiles)
      .pipe(plugins.plumber())              // restart gulp on error
      .pipe(plugins.sourcemaps.init())      // let sourcemap watch this pipeline
      .pipe(plugins.babel())                // transpile into ES5
      .pipe(plugins.order([
        'app/**/*.module.js',
        'app/**/*.js',
      ], { base: './' }))                   // order the files before concatenation
      .pipe(plugins.print())                // print files to the console
      .pipe(plugins.concat('bundle.js'))    // concatenate all JS files
      .pipe(plugins.sourcemaps.write('.'))  // emits sourcemap bundle.js.map for debug
      .pipe(gulp.dest('app/content'))       // save bundle.js and bundle.js.map
  }

  function startWebServer() {
    plugins.connect.server({ root: 'app' });
  }

  function watch() {
    gulp.watch('app/**/*', ['bundle']);
  }

})();
<commit_msg>Add main app module first to js files that will be concatenated<commit_after>(function() {
  'use strict';

  var gulp = require('gulp');
  var plugins = require('gulp-load-plugins')();

  gulp.task('bundle', bundle);
  gulp.task('start-webserver', startWebServer);
  gulp.task('watch', watch);
  gulp.task('default', ['bundle', 'start-webserver', 'watch']);

  /////////////////////////

  var jsFiles = [
    'app/**/*.js',
    '!app/bower_components/**/*',
    '!app/content/bundle.js'
  ];

  function bundle() {
    return gulp.src(jsFiles)
      .pipe(plugins.plumber())              // restart gulp on error
      .pipe(plugins.sourcemaps.init())      // let sourcemap watch this pipeline
      .pipe(plugins.babel())                // transpile into ES5
      .pipe(plugins.order([
        'app/app.module.js',
        'app/**/*.module.js',
        'app/**/*.js',
      ], { base: './' }))                   // order the files before concatenation
      .pipe(plugins.print())                // print files to the console
      .pipe(plugins.concat('bundle.js'))    // concatenate all JS files
      .pipe(plugins.sourcemaps.write('.'))  // emits sourcemap bundle.js.map for debug
      .pipe(gulp.dest('app/content'))       // save bundle.js and bundle.js.map
  }

  function startWebServer() {
    plugins.connect.server({ root: 'app' });
  }

  function watch() {
    gulp.watch('app/**/*', ['bundle']);
  }

})();
<|endoftext|>


<commit_before>var objectAssign = require('object-assign');

var calculateBounds = require('./matrix/calculateBounds');
var populate = require('./matrix/populate');

var Matrix = require('./matrix');
var loop = require('./animation/loop');
var defaults = require('./defaults');

class LedCanvas {
	/**
	 * @param  {HTMLCanvasElement} [el] - <canvas> element to draw on
	 * @param  {Object} [options] - LedCanvas options
	 * @param  {Class}  [LedClass] - Led class to use
	 * @param  {Object} [font] - Object describing the font to use for LedCanvas::write
	 * @return {Object} LedCanvas instance
	 */
	constructor(el, options, LedClass, font) {
		this.font = font;
		this.options = objectAssign({}, defaults, options || {});
		this.setup(el, LedClass);
	}

	setup(el, LedClass) {
		el.width = el.clientWidth * window.devicePixelRatio;
		el.height = el.clientHeight * window.devicePixelRatio;
		let bounds = calculateBounds(el.width, el.height, this.options.matrix);
		let leds = populate(bounds.x, bounds.y, bounds.dim, LedClass);

		this.cursor = { x: 0, y: 0 };
		this.context = el.getContext('2d');
		this.matrix = new Matrix(bounds.x, bounds.y, leds);
	}

	start() {
		loop(this);
	}

	/**
	 * Get a single led
	 * @param {Integer} x - x coordinate of the led to retrieve
	 * @param {Integer} y - y coordinate of the led to retrieve
	 */
	get(...args) {
		return this.matrix.get(...args);
	}

	prop(...args) {
		return this.matrix.prop(...args);
	}

	row(y) {
		return this.matrix.row(y);
	}

	column(x) {
		return this.matrix.column(x);
	}

	rect(...args) {
		return this.matrix.rect(...args);
	}

	all() {
		return this.matrix.all();
	}

	/**
	 * Set a single led's state
	 * @param {Integer} x - x coordinate of the led to manipulate
	 * @param {Integer} y - y coordinate of the led to manipulate
	 * @param {Boolean} [state=true] - Target state of the led
	 */
	set(x, y, state = true) {
		return this.matrix.get(x, y).set(state);
	}

	/**
	 * Toggle a single led's state
	 * @param {Integer} x - x coordinate of the led to manipulate
	 * @param {Integer} y - y coordinate of the led to manipulate
	 * @param {Boolean} [state] - Target state of the led
	 */
	toggle(x, y, state) {
		return this.matrix.get(x, y).toggle(state);
	}

	/**
	 * Write a string to the led board
	 * @param {String} str - string to write to the led board
	 * @param {Integer} [x=0] - starting x coordinate to write from
	 * @param {Integer} [y=0] - starting y coordinate to write from
	 */
	write(str, size=1, x = this.cursor.x, y = this.cursor.y) {
		if (typeof str === 'undefined') return;

		if (typeof str !== 'string') {
			str = str.toString();
		}

		this.cursor = { x, y };
		var charSpacing = this.font.meta.charSpacing || 1;

		str.split('').forEach((char) => {
			if (char === ' ') {
				this.cursor.x += charSpacing*3;
			}

			let sign = this.font.chars[char];

			if (this.cursor.x + sign.width + charSpacing*2 >= this.matrix.x) {
				this.cursor = { x: 0, y: this.cursor.y + this.font.meta.lineHeight + charSpacing };
			}

			let offsetX = charSpacing + this.cursor.x;
			let offsetY = charSpacing + this.cursor.y;

			if (! sign) return;
			var self = this;

			sign.data.forEach((blob) => {
				self.set(blob[0] + offsetX, blob[1] + offsetY);
			});

			this.cursor.x += this.font.meta.monospaced ? this.font.meta.charWidth + charSpacing : sign.width + charSpacing;
		});
	}
}

function ledCanvasFactory(el, options, LedClass, font) {
	return new LedCanvas(el, options, LedClass, font);
}

module.exports = ledCanvasFactory;
<commit_msg>Make blanks and unknown chars work again.<commit_after>var objectAssign = require('object-assign');

var calculateBounds = require('./matrix/calculateBounds');
var populate = require('./matrix/populate');

var Matrix = require('./matrix');
var loop = require('./animation/loop');
var defaults = require('./defaults');

class LedCanvas {
	/**
	 * @param  {HTMLCanvasElement} [el] - <canvas> element to draw on
	 * @param  {Object} [options] - LedCanvas options
	 * @param  {Class}  [LedClass] - Led class to use
	 * @param  {Object} [font] - Object describing the font to use for LedCanvas::write
	 * @return {Object} LedCanvas instance
	 */
	constructor(el, options, LedClass, font) {
		this.font = font;
		this.options = objectAssign({}, defaults, options || {});
		this.setup(el, LedClass);
	}

	setup(el, LedClass) {
		el.width = el.clientWidth * window.devicePixelRatio;
		el.height = el.clientHeight * window.devicePixelRatio;
		let bounds = calculateBounds(el.width, el.height, this.options.matrix);
		let leds = populate(bounds.x, bounds.y, bounds.dim, LedClass);

		this.cursor = { x: 0, y: 0 };
		this.context = el.getContext('2d');
		this.matrix = new Matrix(bounds.x, bounds.y, leds);
	}

	start() {
		loop(this);
	}

	/**
	 * Get a single led
	 * @param {Integer} x - x coordinate of the led to retrieve
	 * @param {Integer} y - y coordinate of the led to retrieve
	 */
	get(...args) {
		return this.matrix.get(...args);
	}

	prop(...args) {
		return this.matrix.prop(...args);
	}

	row(y) {
		return this.matrix.row(y);
	}

	column(x) {
		return this.matrix.column(x);
	}

	rect(...args) {
		return this.matrix.rect(...args);
	}

	all() {
		return this.matrix.all();
	}

	/**
	 * Set a single led's state
	 * @param {Integer} x - x coordinate of the led to manipulate
	 * @param {Integer} y - y coordinate of the led to manipulate
	 * @param {Boolean} [state=true] - Target state of the led
	 */
	set(x, y, state = true) {
		return this.matrix.get(x, y).set(state);
	}

	/**
	 * Toggle a single led's state
	 * @param {Integer} x - x coordinate of the led to manipulate
	 * @param {Integer} y - y coordinate of the led to manipulate
	 * @param {Boolean} [state] - Target state of the led
	 */
	toggle(x, y, state) {
		return this.matrix.get(x, y).toggle(state);
	}

	/**
	 * Write a string to the led board
	 * @param {String} str - string to write to the led board
	 * @param {Integer} [x=0] - starting x coordinate to write from
	 * @param {Integer} [y=0] - starting y coordinate to write from
	 */
	write(str, size=1, x = this.cursor.x, y = this.cursor.y) {
		if (typeof str === 'undefined') return;

		if (typeof str !== 'string') {
			str = str.toString();
		}

		this.cursor = { x, y };
		var charSpacing = this.font.meta.charSpacing || 1;

		str.split('').forEach((char) => {
			if (char === ' ') {
				this.cursor.x += charSpacing*3;
				return;
			}

			let sign = this.font.chars[char];
			if (! sign) return;

			if (this.cursor.x + sign.width + charSpacing*2 >= this.matrix.x) {
				this.cursor = { x: 0, y: this.cursor.y + this.font.meta.lineHeight + charSpacing };
			}

			let offsetX = charSpacing + this.cursor.x;
			let offsetY = charSpacing + this.cursor.y;

			var self = this;

			sign.data.forEach((blob) => {
				self.set(blob[0] + offsetX, blob[1] + offsetY);
			});

			this.cursor.x += this.font.meta.monospaced ? this.font.meta.charWidth + charSpacing : sign.width + charSpacing;
		});
	}
}

function ledCanvasFactory(el, options, LedClass, font) {
	return new LedCanvas(el, options, LedClass, font);
}

module.exports = ledCanvasFactory;
<|endoftext|>


<commit_before>var express = require('express');
var fs = require('fs');
var app = express();
var port = process.env.PORT || 8080;
var bp = require('body-parser');
var user = require('./middleware/User.js');
var connect = require('connect-ensure-login');

var products = require('./database/access_products.js');
var users = require('./database/access_users.js');
var passport = require('passport');
var codes = require('./middleware/code.js');
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);

// this is for authentication

//var loggedOn = require('connect-ensure-login');
var geoip = require('geoip-lite');

var bp = require('body-parser');
var jobsFilename = './jobs.json';

// these are used in the authentication
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true,maxAge :20000 }));

// app.use(express.session({secret:'andyisawesome',
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//use for accesing local files
app.use(express.static('/public'));
app.use('/public', express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public'));

app.use(bp.urlencoded({extended:true}));
// Add headers
app.use(function (req, res, next) {
		// Website you wish to allow to connect
		res.setHeader('Access-Control-Allow-Origin', '*');
		// Request methods you wish to allow
		res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE, XHR');
		// Request headers you wish to allow
		res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
		// Set to true if you need the website to include cookies in the requests sent
		// to the API (e.g. in case you use sessions)
		res.setHeader('Access-Control-Allow-Credentials', true);
		// Pass to next layer of middleware
		next();
		});

app.set('public', __dirname + '/public');
app.set('view engine', 'ejs');
app.use(bp.json());

// this is the passprt authentication methods
require('./middleware/Config.js')(passport);
var auth = require('./middleware/authentication.js');

// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
// app.use(passport.session({
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));script src="/dist/js/navbar.js"></

//=====================================
//GET METHODS
//=====================================

// app.get('*',function(req,res,next){
//   if(req.headers['x-forwarded-proto']!='https')//&&process.env.NODE_ENV === 'production')
//     res.redirect('https://'+req.hostname+req.url)
//   else
//     next()
// });

app.get('/', function(req,res){
		res.setHeader('Cache-Control', 'public, max-age=31557600');
		res.render('index',{'user':req.user});
		});

app.get('/search*', products.search);

app.get('/id/*', products.get_from_id);

			  app.get('/men*', products.get_me_something);

			  app.get('/creditCard', function(req, res){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('creditCard', {'user':req.user});
			  })

			  app.post('/buy_kart', users.buy_kart);

			  app.get('/women*', products.get_me_something);

			  app.get('/kids*', products.get_me_something);

			  app.get('/login', function(req, res){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('login',{'user':req.user,'error':null})
			  });

			  app.get('/loginError', function(req, res){
			  res.render('login',{'user':req.user,'error':'Please enter a correct name or password'});
			  });

			  app.get('/profile',checkAuth,function (req, res) {
			  if(req.user ==undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user': req.user});
			  return;
			  }
			  users.get_kart(req, res);
			  });

			  app.get('/logout', function(req, res){
			  req.logout();
			  req.user = undefined;
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index',{'user':req.user});
			  });

			  app.get('/register', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('register',{'user':req.user})
			  });

			  app.get('/aboutus', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('aboutus',{'user':req.user})
			  });

			  app.get('/local', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('local',{user:req.user})
			  });

			  app.get('/add_to_cart/*',checkAuth, function (req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  if(req.user == undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
			  users.add_to_kart(req, res, id);
			  });

			  app.get('/remove_from_kart/*',checkAuth, function (req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  if(req.user == undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
			  users.delete_from_kart(req, res, id);
			  });

			  app.get('/delete_entire_kart',checkAuth, function(req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  users.delete_entire_kart(req, res, id);
			  });

			  app.get('/purchase_item/*',checkAuth, function(req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  users.delete_from_kart(req, res, id);
			  });

			  app.get('/getRecommendations', function (req, res) {
			  var ipAddr = req.headers["x-forwarded-for"];
			  if (ipAddr){
			  var list = ipAddr.split(",");
			  ipAddr = list[list.length-1];
			  } else {
			  ipAddr = req.connection.remoteAddress;
			  }

			  var ipAddr = "130.195.6.167";
			  var geo = geoip.lookup(ipAddr);
			  if(req.user == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
//var country = geo.city!=undefined && geo.city!='' && geo.city!=null ? geo.city : geo.country;
var name = req.user.name;
if(name!==undefined){
users.get_recommendations(name, geo, function(results){
//res.send({recommendation: results});
res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('display', {results: results, user: req.user, kart: false});
});
}
});

//=====================================
//PUT METHODS
//=====================================

app.put('/', function(req,res){

});

app.put('/login', users.put);

//=====================================
//POST METHODS
//=====================================

app.post('/', function(req,res){
if(req.body.item==undefined){

} else{
postData(req.body.item, true);
res.statusCode = 200;
}
res.end();
});

//=====================================
//DELETE METHODS
//=====================================

app.delete('/', function(req,res){

});

//=====================================
//AUTHENTICATION METHODS
//=====================================

//check to see if loggedon with fb and then locally
app.all('/auth/*',checkAuth);
//app.all('/auth/*', connect.ensureLoggedIn();

app.get('/auth/testAuth',auth.testAuth);

app.post('/newUser',auth.newUser);

app.post('/login', function(req,res, next){

passport.authenticate('local',{ failureRedirect: '/'  },function(err,user,info){
console.log("gets into loacl auth");
console.log(user);

if(user!=false){
console.log("user exists");
console.log("username :" + user);
req.session.username = "'" + user + "'";
req.session.save();
		//return res.redirect('/');

		req.logIn(user, function(err) {
		if (err) {
		req.session.messages = "Error";
		console.log('login Error');
		return res.status(401).send(user +" :   " +err);

		}else{
		req.session.messages = "Login successfully";
		var data = { 'name' : user };
		req.session.passport.user = data;
		console.log(data +" : " +user);

		console.log('login successful');
//console.log(res.body);

res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('index',{'user':req.user});
console.log("after render");
//return;

}
});

}else{
console.log("Login unsucessful");
//console.log(res);
res.setHeader('Cache-Control', 'public, max-age=31557600');
return res.render('login',{'user':req.user,'error':'Please enter a correct name or password'});
//res.status(401).send(user);
}
console.log("berfore");

})(req,res,next);
console.log("after");
});

// TODO have a database of vaild tokens
//app.post('/auth/logout',auth.logout);

app.get('/login/facebook',
passport.authenticate('facebook'));

app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {

checkDatabase(res,req.user.displayName,req.user.id);

var data = { 'name' : req.user.displayName };
req.session.passport.user = data;
//console.log(req.user.displayName);
//var data = {'data':req.user.accessToken};
//'res.render('index', {data:data});

res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('index', {'user':data});
//console.log(req.user.accessToken);
});

function checkDatabase(res,name,id){
	users.get(name, res, function(res, password){
			if(password == null || password == undefined){
			//User does not already exist, so add to database
			users.put(name, id);
			users.get(name, res, function(res, returnedDB){
					if(returnedDB == null || returnedDB == undefined){
					console.log("failed to add to the database");
					} else {
					console.log("didn't fail to add to the database");
					}
					});
			return;
			}
			//Otherwise the user alread exists
			console.log("user already exists");
			if(id == password){
			//Correct authentication TODO
			} else{
			//Don't let them in, they have the wrong password TODO
			}
			});
}

app.get( '/auth/facebook/logout',function( request, response ) {
		request.logout();
		response.send( 'Logged out!' );
		res.redirect('/');
		});

function checkAuth(req, res, next) {
	if (req.isAuthenticated()){
		return next();
	}
	else{
		res.status(401).send("Failed to authenticate: please login")
	}

}

app.listen(port, function(){
		console.log('Listening:' + port);
		});

app.get('*', function(req, res){
		res.status(400).send("Sorry, that page doesn't exist.");
		});

<commit_msg>changed getRecommendations to go through authentication first<commit_after>var express = require('express');
var fs = require('fs');
var app = express();
var port = process.env.PORT || 8080;
var bp = require('body-parser');
var user = require('./middleware/User.js');
var connect = require('connect-ensure-login');

var products = require('./database/access_products.js');
var users = require('./database/access_users.js');
var passport = require('passport');
var codes = require('./middleware/code.js');
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);

// this is for authentication

//var loggedOn = require('connect-ensure-login');
var geoip = require('geoip-lite');

var bp = require('body-parser');
var jobsFilename = './jobs.json';

// these are used in the authentication
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true,maxAge :20000 }));

// app.use(express.session({secret:'andyisawesome',
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));

//use for accesing local files
app.use(express.static('/public'));
app.use('/public', express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/public'));

app.use(bp.urlencoded({extended:true}));
// Add headers
app.use(function (req, res, next) {
		// Website you wish to allow to connect
		res.setHeader('Access-Control-Allow-Origin', '*');
		// Request methods you wish to allow
		res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE, XHR');
		// Request headers you wish to allow
		res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
		// Set to true if you need the website to include cookies in the requests sent
		// to the API (e.g. in case you use sessions)
		res.setHeader('Access-Control-Allow-Credentials', true);
		// Pass to next layer of middleware
		next();
		});

app.set('public', __dirname + '/public');
app.set('view engine', 'ejs');
app.use(bp.json());

// this is the passprt authentication methods
require('./middleware/Config.js')(passport);
var auth = require('./middleware/authentication.js');

// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
// app.use(passport.session({
//                             cookie: { maxAge : 20000 } //1 Hour
//                             }));script src="/dist/js/navbar.js"></

//=====================================
//GET METHODS
//=====================================

// app.get('*',function(req,res,next){
//   if(req.headers['x-forwarded-proto']!='https')//&&process.env.NODE_ENV === 'production')
//     res.redirect('https://'+req.hostname+req.url)
//   else
//     next()
// });

app.get('/', function(req,res){
		res.setHeader('Cache-Control', 'public, max-age=31557600');
		res.render('index',{'user':req.user});
		});

app.get('/search*', products.search);

app.get('/id/*', products.get_from_id);

			  app.get('/men*', products.get_me_something);

			  app.get('/creditCard', function(req, res){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('creditCard', {'user':req.user});
			  })

			  app.post('/buy_kart', users.buy_kart);

			  app.get('/women*', products.get_me_something);

			  app.get('/kids*', products.get_me_something);

			  app.get('/login', function(req, res){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('login',{'user':req.user,'error':null})
			  });

			  app.get('/loginError', function(req, res){
			  res.render('login',{'user':req.user,'error':'Please enter a correct name or password'});
			  });

			  app.get('/profile',checkAuth,function (req, res) {
			  if(req.user ==undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user': req.user});
			  return;
			  }
			  users.get_kart(req, res);
			  });

			  app.get('/logout', function(req, res){
			  req.logout();
			  req.user = undefined;
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index',{'user':req.user});
			  });

			  app.get('/register', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('register',{'user':req.user})
			  });

			  app.get('/aboutus', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('aboutus',{'user':req.user})
			  });

			  app.get('/local', function (req, res) {
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('local',{user:req.user})
			  });

			  app.get('/add_to_cart/*',checkAuth, function (req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  if(req.user == undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
			  users.add_to_kart(req, res, id);
			  });

			  app.get('/remove_from_kart/*',checkAuth, function (req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  if(req.user == undefined || req.user.name == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
			  users.delete_from_kart(req, res, id);
			  });

			  app.get('/delete_entire_kart',checkAuth, function(req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  users.delete_entire_kart(req, res, id);
			  });

			  app.get('/purchase_item/*',checkAuth, function(req, res) {
			  var url = "" + req.url;
			  var array = url.split("/");
			  var id = array[2]; //The third item is the id. eg array=[' ', id', '32']
			  users.delete_from_kart(req, res, id);
			  });

			  app.get('/getRecommendations', checkAuth, function (req, res) {
			  var ipAddr = req.headers["x-forwarded-for"];
			  if (ipAddr){
			  var list = ipAddr.split(",");
			  ipAddr = list[list.length-1];
			  } else {
			  ipAddr = req.connection.remoteAddress;
			  }

			  var geo = geoip.lookup(ipAddr);
			  if(req.user == undefined){
			  res.setHeader('Cache-Control', 'public, max-age=31557600');
			  res.render('index', {'user':req.user});
			  return;
			  }
//var country = geo.city!=undefined && geo.city!='' && geo.city!=null ? geo.city : geo.country;
var name = req.user.name;
if(name!==undefined){
users.get_recommendations(name, geo, function(results){
//res.send({recommendation: results});
res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('display', {results: results, user: req.user, kart: false});
});
}
});

//=====================================
//PUT METHODS
//=====================================

app.put('/', function(req,res){

});

app.put('/login', users.put);

//=====================================
//POST METHODS
//=====================================

app.post('/', function(req,res){
if(req.body.item==undefined){

} else{
postData(req.body.item, true);
res.statusCode = 200;
}
res.end();
});

//=====================================
//DELETE METHODS
//=====================================

app.delete('/', function(req,res){

});

//=====================================
//AUTHENTICATION METHODS
//=====================================

//check to see if loggedon with fb and then locally
app.all('/auth/*',checkAuth);
//app.all('/auth/*', connect.ensureLoggedIn();

app.get('/auth/testAuth',auth.testAuth);

app.post('/newUser',auth.newUser);

app.post('/login', function(req,res, next){

passport.authenticate('local',{ failureRedirect: '/'  },function(err,user,info){
console.log("gets into loacl auth");
console.log(user);

if(user!=false){
console.log("user exists");
console.log("username :" + user);
req.session.username = "'" + user + "'";
req.session.save();
		//return res.redirect('/');

		req.logIn(user, function(err) {
		if (err) {
		req.session.messages = "Error";
		console.log('login Error');
		return res.status(401).send(user +" :   " +err);

		}else{
		req.session.messages = "Login successfully";
		var data = { 'name' : user };
		req.session.passport.user = data;
		console.log(data +" : " +user);

		console.log('login successful');
//console.log(res.body);

res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('index',{'user':req.user});
console.log("after render");
//return;

}
});

}else{
console.log("Login unsucessful");
//console.log(res);
res.setHeader('Cache-Control', 'public, max-age=31557600');
return res.render('login',{'user':req.user,'error':'Please enter a correct name or password'});
//res.status(401).send(user);
}
console.log("berfore");

})(req,res,next);
console.log("after");
});

// TODO have a database of vaild tokens
//app.post('/auth/logout',auth.logout);

app.get('/login/facebook',
passport.authenticate('facebook'));

app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {

checkDatabase(res,req.user.displayName,req.user.id);

var data = { 'name' : req.user.displayName };
req.session.passport.user = data;
//console.log(req.user.displayName);
//var data = {'data':req.user.accessToken};
//'res.render('index', {data:data});

res.setHeader('Cache-Control', 'public, max-age=31557600');
res.render('index', {'user':data});
//console.log(req.user.accessToken);
});

function checkDatabase(res,name,id){
	users.get(name, res, function(res, password){
			if(password == null || password == undefined){
			//User does not already exist, so add to database
			users.put(name, id);
			users.get(name, res, function(res, returnedDB){
					if(returnedDB == null || returnedDB == undefined){
					console.log("failed to add to the database");
					} else {
					console.log("didn't fail to add to the database");
					}
					});
			return;
			}
			//Otherwise the user alread exists
			console.log("user already exists");
			if(id == password){
			//Correct authentication TODO
			} else{
			//Don't let them in, they have the wrong password TODO
			}
			});
}

app.get( '/auth/facebook/logout',function( request, response ) {
		request.logout();
		response.send( 'Logged out!' );
		res.redirect('/');
		});

function checkAuth(req, res, next) {
	if (req.isAuthenticated()){
		return next();
	}
	else{
		res.render('login', {'user':req.user});
	}

}

app.listen(port, function(){
		console.log('Listening:' + port);
		});

app.get('*', function(req, res){
		res.status(400).send("Sorry, that page doesn't exist.");
		});

<|endoftext|>


<commit_before>function init(options, cb) {

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

    // override defaults with passed in options
    f.extend(o, options);
    delete o.fixLng; /* passed in each time */

    // override functions: .log(), .detectLanguage(), etc
    if (o.functions) {
        delete o.functions;
        f.extend(f, options.functions);
    }

    // create namespace object if namespace is passed in as string
    if (typeof o.ns == 'string') {
        o.ns = { namespaces: [o.ns], defaultNs: o.ns};
    }

    // fallback namespaces
    if (typeof o.fallbackNS == 'string') {
        o.fallbackNS = [o.fallbackNS];
    }

    // fallback languages
    if (typeof o.fallbackLng == 'string' || typeof o.fallbackLng == 'boolean') {
        o.fallbackLng = [o.fallbackLng];
    }

    // escape prefix/suffix
    o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
    o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);

    if (!o.lng) o.lng = f.detectLanguage();

    languages = f.toLanguages(o.lng);
    currentLng = languages[0];
    f.log('currentLng set to: ' + currentLng);

    if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng){ //cookie is unset or invalid
        f.cookie.create(o.cookieName, currentLng, o.cookieExpirationTime, o.cookieDomain);
    }
    if (o.detectLngFromLocalStorage && typeof document !== 'undefined' && window.localStorage) {
        f.localStorage.setItem('i18next_lng', currentLng);
    }

    var lngTranslate = translate;
    if (options.fixLng) {
        lngTranslate = function(key, options) {
            options = options || {};
            options.lng = options.lng || lngTranslate.lng;
            return translate(key, options);
        };
        lngTranslate.lng = currentLng;
    }

    pluralExtensions.setCurrentLng(currentLng);

    // add JQuery extensions
    if ($ && o.setJqueryExt) addJqueryFunct();

    // jQuery deferred
    var deferred;
    if ($ && $.Deferred) {
        deferred = $.Deferred();
    }

    // return immidiatly if res are passed in
    if (o.resStore) {
        resStore = o.resStore;
        initialized = true;
        if (cb) cb(lngTranslate);
        if (deferred) deferred.resolve(lngTranslate);
        if (deferred) return deferred.promise();
        return;
    }

    // languages to load
    var lngsToLoad = f.toLanguages(o.lng);
    if (typeof o.preload === 'string') o.preload = [o.preload];
    for (var i = 0, l = o.preload.length; i < l; i++) {
        var pres = f.toLanguages(o.preload[i]);
        for (var y = 0, len = pres.length; y < len; y++) {
            if (lngsToLoad.indexOf(pres[y]) < 0) {
                lngsToLoad.push(pres[y]);
            }
        }
    }

    // else load them
    i18n.sync.load(lngsToLoad, o, function(err, store) {
        resStore = store;
        initialized = true;

        if (cb) cb(lngTranslate);
        if (deferred) deferred.resolve(lngTranslate);
    });

    if (deferred) return deferred.promise();
}

function isInitialized() {
    return initialized;
}
<commit_msg>Update i18next.init.js<commit_after>function init(options, cb) {

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

    // override defaults with passed in options
    f.extend(o, options);
    delete o.fixLng; /* passed in each time */

    // override functions: .log(), .detectLanguage(), etc
    if (o.functions) {
        delete o.functions;
        f.extend(f, options.functions);
    }

    // create namespace object if namespace is passed in as string
    if (typeof o.ns == 'string') {
        o.ns = { namespaces: [o.ns], defaultNs: o.ns};
    }

    // fallback namespaces
    if (typeof o.fallbackNS == 'string') {
        o.fallbackNS = [o.fallbackNS];
    }

    // fallback languages
    if (typeof o.fallbackLng == 'string' || typeof o.fallbackLng == 'boolean') {
        o.fallbackLng = [o.fallbackLng];
    }

    // escape prefix/suffix
    o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
    o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);

    if (!o.lng) o.lng = f.detectLanguage();

    languages = f.toLanguages(o.lng);
    currentLng = languages[0];
    f.log('currentLng set to: ' + currentLng);

    if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng){ //cookie is unset or invalid
        f.cookie.create(o.cookieName, currentLng, o.cookieExpirationTime, o.cookieDomain);
    }
    if (o.detectLngFromLocalStorage && typeof document !== 'undefined' && window.localStorage) {
        f.localStorage.setItem('i18next_lng', currentLng);
    }

    var lngTranslate = translate;
    if (options.fixLng) {
        lngTranslate = function(key, options) {
            options = options || {};
            options.lng = options.lng || lngTranslate.lng;
            return translate(key, options);
        };
        lngTranslate.lng = currentLng;
    }

    pluralExtensions.setCurrentLng(currentLng);

    // add JQuery extensions
    if ($ && o.setJqueryExt) {
        addJqueryFunct();
    }
    else {
        addJqueryLikeFunctionality();
    }

    // jQuery deferred
    var deferred;
    if ($ && $.Deferred) {
        deferred = $.Deferred();
    }

    // return immidiatly if res are passed in
    if (o.resStore) {
        resStore = o.resStore;
        initialized = true;
        if (cb) cb(lngTranslate);
        if (deferred) deferred.resolve(lngTranslate);
        if (deferred) return deferred.promise();
        return;
    }

    // languages to load
    var lngsToLoad = f.toLanguages(o.lng);
    if (typeof o.preload === 'string') o.preload = [o.preload];
    for (var i = 0, l = o.preload.length; i < l; i++) {
        var pres = f.toLanguages(o.preload[i]);
        for (var y = 0, len = pres.length; y < len; y++) {
            if (lngsToLoad.indexOf(pres[y]) < 0) {
                lngsToLoad.push(pres[y]);
            }
        }
    }

    // else load them
    i18n.sync.load(lngsToLoad, o, function(err, store) {
        resStore = store;
        initialized = true;

        if (cb) cb(lngTranslate);
        if (deferred) deferred.resolve(lngTranslate);
    });

    if (deferred) return deferred.promise();
}

function isInitialized() {
    return initialized;
}
<|endoftext|>


<commit_before>/*eslint-env browser */
const ASCII_BACKSPACE = 8;
const ASCII_SHIFT = 16;
const ASCII_SPACEBAR = 32;
const ASCII_COMMA = 188;
const ASCII_PERIOD = 190;
/**
 * The width and height of the key.
 */
const KEY_DIMENSIONS = 50;
/**
 * How much the key should grow in size if it is pressed.
 */
const KEY_EXPANSION_SIZE = 5;

var canvasKeyboard = document.getElementById("canvas");
var keyboardCtx = canvasKeyboard.getContext("2d");

/**
 * Whether the shift key is currently being pressed by the user.
 */
var shiftDown = false;
var spaceDown = false;
var keyDown = [
	[ false, false, false, false, false, false, false, false, false, false ],
	[ false, false, false, false, false, false, false, false, false ],
	[ false, false, false, false, false, false, false, false, false ]
];
var keyboard = [
	[ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p' ],
	[ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l' ],
	[ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.' ]
];

/**
 * Draws the outline of a key.
 *
 * @param x the desired starting X position of the key
 * @param y the desired starting Y position of the key
 * @param keydown whether this key is currently being pressed by the user
 */
function drawKeyOutline(x, y, keyDown) {
	var dimensions = KEY_DIMENSIONS;
	if (keyDown) {
		x = x - (KEY_EXPANSION_SIZE / 2);
		y = y - (KEY_EXPANSION_SIZE / 2);
		dimensions = KEY_DIMENSIONS + KEY_EXPANSION_SIZE;
	}

	keyboardCtx.beginPath();
	keyboardCtx.fillStyle = "white";
	keyboardCtx.fillRect(x, y, dimensions, dimensions);
	keyboardCtx.strokeStyle = "black";
	keyboardCtx.strokeRect(x, y, dimensions, dimensions);
	keyboardCtx.closePath();
}

function drawKey(x, y, letter, keyDown) {
	drawKeyOutline(x, y, keyDown);
	drawCharacter(x, y, KEY_DIMENSIONS, KEY_DIMENSIONS, letter, keyDown);
}

function drawCharacter(x, y, height, width, letter, keydown) {
	if (shiftDown) {
		letter = letter.toUpperCase();
	}

	keyboardCtx.beginPath();
	keyboardCtx.font = keydown ? "40pt Verdana" : "20pt Verdana";
	keyboardCtx.textAlign = "center";
	keyboardCtx.textBaseline = "middle";
	keyboardCtx.fillStyle = keydown ? "red" : "blue";
	keyboardCtx.fillText(letter, (x + x + width) / 2, (y + y + height) / 2);
	keyboardCtx.closePath();
}

function drawShiftKey(x, y) {
	drawKeyOutline(x, y);

	keyboardCtx.beginPath();
	keyboardCtx.font = "12pt Verdana";
	keyboardCtx.textAlign = "center";
	keyboardCtx.textBaseline = "middle";
	keyboardCtx.fillStyle = shiftDown ? "red" : "blue";
	keyboardCtx.fillText("shift", (x + x + KEY_DIMENSIONS) / 2, (y + y + KEY_DIMENSIONS) / 2);
	keyboardCtx.closePath();
}

/**
 * Draws the keyboard.
 */

function drawSpacebar() {
	keyboardCtx.beginPath();
	keyboardCtx.strokeStyle = "black";
	keyboardCtx.fillStyle = "white";

	if (spaceDown) {
		keyboardCtx.strokeRect(100 - (KEY_EXPANSION_SIZE / 2), 190 - (KEY_EXPANSION_SIZE / 2), 410 + KEY_EXPANSION_SIZE, KEY_DIMENSIONS + KEY_EXPANSION_SIZE);
		keyboardCtx.fillRect(100 - (KEY_EXPANSION_SIZE / 2), 190 - (KEY_EXPANSION_SIZE / 2), 410 + KEY_EXPANSION_SIZE, KEY_DIMENSIONS + KEY_EXPANSION_SIZE);
	} else {
		keyboardCtx.strokeRect(100, 190, 410, KEY_DIMENSIONS);
		keyboardCtx.fillRect(100, 190, 410, KEY_DIMENSIONS);
	}
	keyboardCtx.closePath();
}

function drawKeyboard() {
	// clear the canvas before redrawing
	keyboardCtx.clearRect(0, 0, canvasKeyboard.width, canvasKeyboard.height);

	// draw the top row of the keyboard
	for (var i = 0; i < keyboard[0].length; i++) {
		drawKey((KEY_DIMENSIONS + 10) * i + (10), 10, keyboard[0][i], keyDown[0][i]);
	}

	// draw the home row of the keyboard
	for (var i = 0; i < keyboard[1].length; i++) {
		drawKey(((KEY_DIMENSIONS + 10) / 2) + (KEY_DIMENSIONS + 10) * i + (10), 20 + (KEY_DIMENSIONS), keyboard[1][i], keyDown[1][i]);
	}

	// draw the bottom row of the keyboard
	drawShiftKey(10, 30 + (KEY_DIMENSIONS * 2));
	for (var i = 0; i < keyboard[2].length; i++) {
		drawKey((KEY_DIMENSIONS + 10) + (KEY_DIMENSIONS + 10) * i + (10), 30 + (KEY_DIMENSIONS * 2), keyboard[2][i], keyDown[2][i]);
	}

	drawSpacebar();
}

function getEventKey(e) {
	if (typeof e.key === "undefined") {
		if (!e.shiftKey) {
			if (e.keyCode === ASCII_COMMA) {
				return ",";
			} else if (e.keyCode === ASCII_PERIOD) {
				return ".";
			}
		}
		// e.key is undefined in Safari 9.0.2 and Chrome 47
		return String.fromCharCode(e.keyCode).toLowerCase();
	}
	return e.key.toLowerCase();
}

function keyUpHandler(e) {
	var input = document.getElementById("input");
	if (input.disabled) {
		return;
	}

	if (e.keyCode === ASCII_SHIFT) {
		shiftDown = false;
		requestAnimationFrame(drawKeyboard);
	} else if (e.keyCode === ASCII_SPACEBAR) {
		spaceDown = false;
		requestAnimationFrame(drawKeyboard);
	} else {
		var key = getEventKey(e);

		for (var i = 0; i < keyboard.length; i++) {
			for (var j = 0; j < keyboard[i].length; j++) {
				if (keyboard[i][j] === key) {
					keyDown[i][j] = false;
					requestAnimationFrame(drawKeyboard);
					return;
				}
			}
		}
	}
}

function keyDownHandler(e) {
	var input = document.getElementById("input");
	if (input.disabled) {
		return;
	}

	if (e.keyCode === ASCII_SHIFT) {
		shiftDown = true;
		requestAnimationFrame(drawKeyboard);
	} else if (e.keyCode === ASCII_SPACEBAR) {
		spaceDown = true;
		requestAnimationFrame(drawKeyboard);
	} else if (e.keyCode === ASCII_BACKSPACE) {
		if (onBackspace()) {
			e.preventDefault();
		}
	} else {
		var key = getEventKey(e);

		for (var i = 0; i < keyboard.length; i++) {
			for (var j = 0; j < keyboard[i].length; j++) {
				if (keyboard[i][j] === key) {
					keyDown[i][j] = true;
					requestAnimationFrame(drawKeyboard);

					onCharacter(shiftDown ? key.toUpperCase() : key);
					return;
				}
			}
		}
	}
}

document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("keydown", keyDownHandler, false);

// detect when the tab has become visible or hidden
document.addEventListener("visibilitychange", function(e) {
	for (var i = 0; i < keyboard.length; i++) {
		for (var j = 0; j < keyboard[i].length; j++) {
			keyDown[i][j] = false;
		}
	}
	shiftDown = false;
	// redraw the keyboard immediately, if we use delay it by using
	// requestAnimationFrame(*), it will only redraw when the tab gains
	// focus again, and that causes a flickering effect as the user will
	// see the red keys for a brief moment
	drawKeyboard();
});

drawKeyboard();<commit_msg>Fix bug that caused keys to be highlighted during a rerun<commit_after>/*eslint-env browser */
const ASCII_BACKSPACE = 8;
const ASCII_SHIFT = 16;
const ASCII_SPACEBAR = 32;
const ASCII_COMMA = 188;
const ASCII_PERIOD = 190;
/**
 * The width and height of the key.
 */
const KEY_DIMENSIONS = 50;
/**
 * How much the key should grow in size if it is pressed.
 */
const KEY_EXPANSION_SIZE = 5;

var canvasKeyboard = document.getElementById("canvas");
var keyboardCtx = canvasKeyboard.getContext("2d");

/**
 * Whether the shift key is currently being pressed by the user.
 */
var shiftDown = false;
var spaceDown = false;
var keyDown = [
	[ false, false, false, false, false, false, false, false, false, false ],
	[ false, false, false, false, false, false, false, false, false ],
	[ false, false, false, false, false, false, false, false, false ]
];
var keyboard = [
	[ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p' ],
	[ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l' ],
	[ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.' ]
];

/**
 * Draws the outline of a key.
 *
 * @param x the desired starting X position of the key
 * @param y the desired starting Y position of the key
 * @param keydown whether this key is currently being pressed by the user
 */
function drawKeyOutline(x, y, keyDown) {
	var dimensions = KEY_DIMENSIONS;
	if (keyDown) {
		x = x - (KEY_EXPANSION_SIZE / 2);
		y = y - (KEY_EXPANSION_SIZE / 2);
		dimensions = KEY_DIMENSIONS + KEY_EXPANSION_SIZE;
	}

	keyboardCtx.beginPath();
	keyboardCtx.fillStyle = "white";
	keyboardCtx.fillRect(x, y, dimensions, dimensions);
	keyboardCtx.strokeStyle = "black";
	keyboardCtx.strokeRect(x, y, dimensions, dimensions);
	keyboardCtx.closePath();
}

function drawKey(x, y, letter, keyDown) {
	drawKeyOutline(x, y, keyDown);
	drawCharacter(x, y, KEY_DIMENSIONS, KEY_DIMENSIONS, letter, keyDown);
}

function drawCharacter(x, y, height, width, letter, keydown) {
	if (shiftDown) {
		letter = letter.toUpperCase();
	}

	keyboardCtx.beginPath();
	keyboardCtx.font = keydown ? "40pt Verdana" : "20pt Verdana";
	keyboardCtx.textAlign = "center";
	keyboardCtx.textBaseline = "middle";
	keyboardCtx.fillStyle = keydown ? "red" : "blue";
	keyboardCtx.fillText(letter, (x + x + width) / 2, (y + y + height) / 2);
	keyboardCtx.closePath();
}

function drawShiftKey(x, y) {
	drawKeyOutline(x, y);

	keyboardCtx.beginPath();
	keyboardCtx.font = "12pt Verdana";
	keyboardCtx.textAlign = "center";
	keyboardCtx.textBaseline = "middle";
	keyboardCtx.fillStyle = shiftDown ? "red" : "blue";
	keyboardCtx.fillText("shift", (x + x + KEY_DIMENSIONS) / 2, (y + y + KEY_DIMENSIONS) / 2);
	keyboardCtx.closePath();
}

/**
 * Draws the keyboard.
 */

function drawSpacebar() {
	keyboardCtx.beginPath();
	keyboardCtx.strokeStyle = "black";
	keyboardCtx.fillStyle = "white";

	if (spaceDown) {
		keyboardCtx.strokeRect(100 - (KEY_EXPANSION_SIZE / 2), 190 - (KEY_EXPANSION_SIZE / 2), 410 + KEY_EXPANSION_SIZE, KEY_DIMENSIONS + KEY_EXPANSION_SIZE);
		keyboardCtx.fillRect(100 - (KEY_EXPANSION_SIZE / 2), 190 - (KEY_EXPANSION_SIZE / 2), 410 + KEY_EXPANSION_SIZE, KEY_DIMENSIONS + KEY_EXPANSION_SIZE);
	} else {
		keyboardCtx.strokeRect(100, 190, 410, KEY_DIMENSIONS);
		keyboardCtx.fillRect(100, 190, 410, KEY_DIMENSIONS);
	}
	keyboardCtx.closePath();
}

function drawKeyboard() {
	// clear the canvas before redrawing
	keyboardCtx.clearRect(0, 0, canvasKeyboard.width, canvasKeyboard.height);

	// draw the top row of the keyboard
	for (var i = 0; i < keyboard[0].length; i++) {
		drawKey((KEY_DIMENSIONS + 10) * i + (10), 10, keyboard[0][i], keyDown[0][i]);
	}

	// draw the home row of the keyboard
	for (var i = 0; i < keyboard[1].length; i++) {
		drawKey(((KEY_DIMENSIONS + 10) / 2) + (KEY_DIMENSIONS + 10) * i + (10), 20 + (KEY_DIMENSIONS), keyboard[1][i], keyDown[1][i]);
	}

	// draw the bottom row of the keyboard
	drawShiftKey(10, 30 + (KEY_DIMENSIONS * 2));
	for (var i = 0; i < keyboard[2].length; i++) {
		drawKey((KEY_DIMENSIONS + 10) + (KEY_DIMENSIONS + 10) * i + (10), 30 + (KEY_DIMENSIONS * 2), keyboard[2][i], keyDown[2][i]);
	}

	drawSpacebar();
}

function getEventKey(e) {
	if (typeof e.key === "undefined") {
		if (!e.shiftKey) {
			if (e.keyCode === ASCII_COMMA) {
				return ",";
			} else if (e.keyCode === ASCII_PERIOD) {
				return ".";
			}
		}
		// e.key is undefined in Safari 9.0.2 and Chrome 47
		return String.fromCharCode(e.keyCode).toLowerCase();
	}
	return e.key.toLowerCase();
}

function keyUpHandler(e) {
	var input = document.getElementById("input");
	if (e.keyCode === ASCII_SHIFT) {
		shiftDown = false;

		if (input.disabled) {
			requestAnimationFrame(drawKeyboard);
		}
	} else if (e.keyCode === ASCII_SPACEBAR) {
		spaceDown = false;

		if (input.disabled) {
			requestAnimationFrame(drawKeyboard);
		}
	} else {
		var key = getEventKey(e);

		for (var i = 0; i < keyboard.length; i++) {
			for (var j = 0; j < keyboard[i].length; j++) {
				if (keyboard[i][j] === key) {
					keyDown[i][j] = false;

					if (input.disabled) {
						requestAnimationFrame(drawKeyboard);
					}
					return;
				}
			}
		}
	}
}

function keyDownHandler(e) {
	var input = document.getElementById("input");
	if (input.disabled) {
		return;
	}

	if (e.keyCode === ASCII_SHIFT) {
		shiftDown = true;
		requestAnimationFrame(drawKeyboard);
	} else if (e.keyCode === ASCII_SPACEBAR) {
		spaceDown = true;
		requestAnimationFrame(drawKeyboard);
	} else if (e.keyCode === ASCII_BACKSPACE) {
		if (onBackspace()) {
			e.preventDefault();
		}
	} else {
		var key = getEventKey(e);

		for (var i = 0; i < keyboard.length; i++) {
			for (var j = 0; j < keyboard[i].length; j++) {
				if (keyboard[i][j] === key) {
					keyDown[i][j] = true;
					requestAnimationFrame(drawKeyboard);

					onCharacter(shiftDown ? key.toUpperCase() : key);
					return;
				}
			}
		}
	}
}

document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("keydown", keyDownHandler, false);

// detect when the tab has become visible or hidden
document.addEventListener("visibilitychange", function(e) {
	for (var i = 0; i < keyboard.length; i++) {
		for (var j = 0; j < keyboard[i].length; j++) {
			keyDown[i][j] = false;
		}
	}
	shiftDown = false;
	// redraw the keyboard immediately, if we use delay it by using
	// requestAnimationFrame(*), it will only redraw when the tab gains
	// focus again, and that causes a flickering effect as the user will
	// see the red keys for a brief moment
	drawKeyboard();
});

drawKeyboard();<|endoftext|>


<commit_before>window.data = [];
window.ranges = [];

function getCodepointDescription(codepoint, name) {
	codepoint = parseInt(codepoint);
	return name + ' ' + ctos([codepoint]);
}

function getRangeFunctionForName(name) {
	return function(codepoint) {
		return getCodepointDescription(codepoint, name);
	}
}

function initAliasData(completion) {
	var client = new XMLHttpRequest();
	client.open('GET', 'NameAliases.txt');
	client.onreadystatechange = function() {
		if (client.readyState == 4 && client.status == 200) {
			var dataStrings = client.responseText.split('\n');
			window.aliases = [];
			window.controlAliases = [];
			for (var i = 0; i < dataStrings.length; ++i) {
				var splitLine = dataStrings[i].split(';');
				var codepoint = parseInt('0x' + splitLine[0]);
				var alias = splitLine[1];
				window.aliases.push({codepoint: codepoint, alias: alias});
				if (splitLine[2] == 'control')
					window.controlAliases.push({codepoint: codepoint, alias: alias});
			}
			completion();
		}
	}
	client.send();
}

function initUnicodeData(completion) {
	initAliasData(function() {
		var client = new XMLHttpRequest();
		client.open('GET', 'UnicodeData.txt');
		client.onreadystatechange = function() {
			if (client.readyState == 4 && client.status == 200) {
				var dataStrings = client.responseText.split('\n');
				window.data = [];
				for (var i in dataStrings) {
					var data_line = dataStrings[i].split(';');
					if (data_line[1].endsWith(', First>')) {
						window.ranges.push([
							parseInt('0x' + data_line[0]),
							parseInt('0x' + dataStrings[parseInt(i)+1].split(';')[0]),
							getRangeFunctionForName(data_line[1].substring(1, data_line[1].length - 8))
						]);
					} else if (data_line[1].endsWith(', Last>')) {
						continue;
					} else if (data_line[1] == '<control>') {
						var name = [];
						var codepoint = parseInt('0x' + data_line[0]);
						for (var j = 0; j < window.controlAliases.length; ++j) {
							if (window.controlAliases[j].codepoint == codepoint) {
								name.push(window.controlAliases[j].alias);
							}
						}
						window.data[parseInt('0x' + data_line[0])] = getCodepointDescription(
							'0x' + data_line[0],
							'<control> (' + name.join(' / ') + ')'
						);
					} else {
						window.data[parseInt('0x' + data_line[0])] = getCodepointDescription('0x' + data_line[0], data_line[1]);
					}
				}
				completion();
			}
		}
		client.send();
	});
}

function getUnicodeData(codepoint) {
	if (window.data[codepoint])
		return window.data[codepoint] + getHanEntry(codepoint);
	for (var i in window.ranges) {
		var range = window.ranges[i];
		if (codepoint >= range[0] && codepoint <= range[1])
			return range[2](codepoint) + getHanEntry(codepoint);
	}
	return getCodepointDescription(codepoint, 'Unknown') + getHanEntry(codepoint);
}

function getSearchString(codepoint) {
	return getUnicodeData(codepoint).toUpperCase();
}

function searchCodepoints(str) {
	var results = [];

	var deduplicate = function(a) {
		var temp = {};
		for (var i = 0; i < a.length; i++)
			temp[a[i]] = true;
		var r = [];
		for (var k in temp)
			r.push(parseInt(k));
		return r;
	}
	var reachedMaxResults = function() {
		if (results.length < 256)
			return false;
		results = deduplicate(results);
		if (results.length < 256)
			return false;
		return true;
	}

	str = str.toUpperCase();
	if (str.startsWith('U+')) {
		str = str.replace('U+', '');
	}

	for (var codepoint in window.data) {
		var searchString = getSearchString(codepoint);
		if (searchString.includes(str) || codepoint.includes(str)) {
			results.push(parseInt(codepoint));
			if (reachedMaxResults())
				break;
		}
	}
	if (!reachedMaxResults() || codepoint > 0x3400) {
		for (var codepoint in window.han_meanings) {
			var searchString = getSearchString(codepoint);
			if (searchString.includes(str) || codepoint.includes(str)) {
				results.push(parseInt(codepoint));
				if (reachedMaxResults())
					break;
			}
		}
	}
	results = deduplicate(results);
	return results;
}<commit_msg>Alias search<commit_after>window.data = [];
window.ranges = [];

function getCodepointDescription(codepoint, name) {
	codepoint = parseInt(codepoint);
	return name + ' ' + ctos([codepoint]);
}

function getRangeFunctionForName(name) {
	return function(codepoint) {
		return getCodepointDescription(codepoint, name);
	}
}

function initAliasData(completion) {
	var client = new XMLHttpRequest();
	client.open('GET', 'NameAliases.txt');
	client.onreadystatechange = function() {
		if (client.readyState == 4 && client.status == 200) {
			var dataStrings = client.responseText.split('\n');
			window.aliases = [];
			window.controlAliases = [];
			for (var i = 0; i < dataStrings.length; ++i) {
				if (dataStrings[i].length == 0 || dataStrings[i][0] == '#')
					continue;
				var splitLine = dataStrings[i].split(';');
				var codepoint = parseInt('0x' + splitLine[0]);
				var alias = splitLine[1];
				window.aliases.push({codepoint: codepoint, alias: alias});
				if (splitLine[2] == 'control')
					window.controlAliases.push({codepoint: codepoint, alias: alias});
			}
			completion();
		}
	}
	client.send();
}

function initUnicodeData(completion) {
	initAliasData(function() {
		var client = new XMLHttpRequest();
		client.open('GET', 'UnicodeData.txt');
		client.onreadystatechange = function() {
			if (client.readyState == 4 && client.status == 200) {
				var dataStrings = client.responseText.split('\n');
				window.data = [];
				for (var i in dataStrings) {
					var data_line = dataStrings[i].split(';');
					if (data_line[1].endsWith(', First>')) {
						window.ranges.push([
							parseInt('0x' + data_line[0]),
							parseInt('0x' + dataStrings[parseInt(i)+1].split(';')[0]),
							getRangeFunctionForName(data_line[1].substring(1, data_line[1].length - 8))
						]);
					} else if (data_line[1].endsWith(', Last>')) {
						continue;
					} else if (data_line[1] == '<control>') {
						var name = [];
						var codepoint = parseInt('0x' + data_line[0]);
						for (var j = 0; j < window.controlAliases.length; ++j) {
							if (window.controlAliases[j].codepoint == codepoint) {
								name.push(window.controlAliases[j].alias);
							}
						}
						window.data[parseInt('0x' + data_line[0])] = getCodepointDescription(
							'0x' + data_line[0],
							'<control> (' + name.join(' / ') + ')'
						);
					} else {
						window.data[parseInt('0x' + data_line[0])] = getCodepointDescription('0x' + data_line[0], data_line[1]);
					}
				}
				completion();
			}
		}
		client.send();
	});
}

function getUnicodeData(codepoint) {
	if (window.data[codepoint])
		return window.data[codepoint] + getHanEntry(codepoint);
	for (var i in window.ranges) {
		var range = window.ranges[i];
		if (codepoint >= range[0] && codepoint <= range[1])
			return range[2](codepoint) + getHanEntry(codepoint);
	}
	return getCodepointDescription(codepoint, 'Unknown') + getHanEntry(codepoint);
}

function getSearchString(codepoint) {
	return getUnicodeData(codepoint).toUpperCase();
}

function searchCodepoints(str) {
	var results = [];

	var deduplicate = function(a) {
		var temp = {};
		for (var i = 0; i < a.length; i++)
			temp[a[i]] = true;
		var r = [];
		for (var k in temp)
			r.push(parseInt(k));
		return r;
	}
	var reachedMaxResults = function() {
		if (results.length < 256)
			return false;
		results = deduplicate(results);
		if (results.length < 256)
			return false;
		return true;
	}

	str = str.toUpperCase();
	if (str.startsWith('U+')) {
		str = str.replace('U+', '');
	}

	for (var i = 0; i < window.aliases.length; ++i) {
		var searchString = window.aliases[i].alias;
		if (searchString.includes(str)) {
			results.push(window.aliases[i].codepoint);
			if (reachedMaxResults())
				break;
		}
	}
	for (var codepoint in window.data) {
		var searchString = getSearchString(codepoint);
		if (searchString.includes(str) || codepoint.includes(str)) {
			results.push(parseInt(codepoint));
			if (reachedMaxResults())
				break;
		}
	}
	if (!reachedMaxResults() || codepoint > 0x3400) {
		for (var codepoint in window.han_meanings) {
			var searchString = getSearchString(codepoint);
			if (searchString.includes(str) || codepoint.includes(str)) {
				results.push(parseInt(codepoint));
				if (reachedMaxResults())
					break;
			}
		}
	}
	results = deduplicate(results);
	return results;
}<|endoftext|>


<commit_before>'use strict';

var argv = require('yargs').argv;
var path = require('path');
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var ts = require('gulp-typescript');
var tslint = require('gulp-tslint');
var mocha = require('gulp-spawn-mocha');

var SRC_FOLDER = argv.single ? singleRule(argv.single) : 'src/**/*.ts';
var TEST_FOLDER = argv.single ? singleTest(argv.single) : 'dist/test/**/*.js';
var DEF_FOLDER = 'typings/**/*.ts'
var tsProject = ts.createProject('tsconfig.json');

gulp.task('readme', ['build'], (gulpCallBack) => {
  var readme = require('./dist/readme');
  readme.updateRuleFiles(() => {
    readme.updateReadme(() => {
      gulpCallBack();
    });
  });
});

gulp.task('fetch', ['build'], function () {
  var fetch = require('./dist/readme/fetch');
  fetch.compareToESLint();
  fetch.compareToTSLint();
});

gulp.task('lint', function lint() {
  return gulp
    .src(SRC_FOLDER)
    .pipe(tslint({
      tslint: require('tslint')
    }))
    .pipe(tslint.report('prose', {
      summarizeFailureOutput: false
    }));
});

gulp.task('build', argv.lint === false ? [] : ['lint'], function build() {
  var tsResult = tsProject
    .src([SRC_FOLDER, DEF_FOLDER])
    .pipe(sourcemaps.init())
    .pipe(tsProject());

  return tsResult.js
    .pipe(sourcemaps.write({
      includeContent: false,
      sourceRoot: path.join(__dirname, '/src')
    }))
    .pipe(gulp.dest('dist'));
});

gulp.task('test', ['build'], function test() {
  return gulp
    .src(TEST_FOLDER)
    .pipe(mocha());
});

gulp.task('watch', function watch() {
  gulp.watch(SRC_FOLDER, ['build']);
});

gulp.task('default', ['watch']);

gulp.task('publish', function build() {
  return tsProject
    .src([SRC_FOLDER, DEF_FOLDER])
    .pipe(tsProject());
});

// ---
function toCamelCase(str){
  return str.replace(/-(.)/g, function (_, char) {
    return char.toUpperCase();
  });
}

function singleRule(name) {
  return 'src/**/?(' + toCamelCase(name) + '*|helper).ts';
}

function singleTest(name) {
  return 'dist/test/rules/' + toCamelCase(name) + 'RuleTests.js';
}
<commit_msg>[fix:fetch-task] gulp waits till the task finishes<commit_after>'use strict';

var argv = require('yargs').argv;
var path = require('path');
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var ts = require('gulp-typescript');
var tslint = require('gulp-tslint');
var mocha = require('gulp-spawn-mocha');

var SRC_FOLDER = argv.single ? singleRule(argv.single) : 'src/**/*.ts';
var TEST_FOLDER = argv.single ? singleTest(argv.single) : 'dist/test/**/*.js';
var DEF_FOLDER = 'typings/**/*.ts'
var tsProject = ts.createProject('tsconfig.json');

gulp.task('readme', ['build'], (gulpCallBack) => {
  var readme = require('./dist/readme');
  readme.updateRuleFiles(() => {
    readme.updateReadme(() => {
      gulpCallBack();
    });
  });
});

gulp.task('fetch', ['build'], (gulpCallBack) => {
  var fetch = require('./dist/readme/fetch');
  Promise.all([
    fetch.compareToESLint(),
    fetch.compareToTSLint()
  ]).then(() => {
    gulpCallBack();
  });
});

gulp.task('lint', function lint() {
  return gulp
    .src(SRC_FOLDER)
    .pipe(tslint({
      tslint: require('tslint')
    }))
    .pipe(tslint.report('prose', {
      summarizeFailureOutput: false
    }));
});

gulp.task('build', argv.lint === false ? [] : ['lint'], function build() {
  var tsResult = tsProject
    .src([SRC_FOLDER, DEF_FOLDER])
    .pipe(sourcemaps.init())
    .pipe(tsProject());

  return tsResult.js
    .pipe(sourcemaps.write({
      includeContent: false,
      sourceRoot: path.join(__dirname, '/src')
    }))
    .pipe(gulp.dest('dist'));
});

gulp.task('test', ['build'], function test() {
  return gulp
    .src(TEST_FOLDER)
    .pipe(mocha());
});

gulp.task('watch', function watch() {
  gulp.watch(SRC_FOLDER, ['build']);
});

gulp.task('default', ['watch']);

gulp.task('publish', function build() {
  return tsProject
    .src([SRC_FOLDER, DEF_FOLDER])
    .pipe(tsProject());
});

// ---
function toCamelCase(str){
  return str.replace(/-(.)/g, function (_, char) {
    return char.toUpperCase();
  });
}

function singleRule(name) {
  return 'src/**/?(' + toCamelCase(name) + '*|helper).ts';
}

function singleTest(name) {
  return 'dist/test/rules/' + toCamelCase(name) + 'RuleTests.js';
}
<|endoftext|>


<commit_before>/**
 * Angular Elastic Input Directive
 * A directive for AngularJS which automatically resizes the width of input field according to the content, while typing.
 * @author: Jacek Pulit <jacek.pulit@gmail.com>
 * @license: The MIT License
 */

'use strict';

angular.module('ngElasticInput', []).directive('elasticInput', function(){
    return {
        restrict: 'A',
        link: function postLink(scope, element, attrs) {

            var mirror = jQuery('');

            element.css('minWidth', scope.$eval(attrs.elasticInputMinwidth) || 50);
            element.css('maxWidth', scope.$eval(attrs.elasticInputMaxwidth) || 250);

            jQuery.each([
                'fontFamily', 'fontSize', 'fontWeight', 'fontStyle',
                'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent',
                'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle',
                'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight'], function(i,val){
                mirror.css(val, element.css(val));
            });

            jQuery('body').append(mirror);

            update();

            element.on("keydown keyup focus input propertychange change", function(){ update(); });

            function update() {
                mirror.text(element.val());
                element.css('width', mirror.outerWidth());
            }

        }
    };
});
<commit_msg>add 1px gap to avoid text movement<commit_after>/**
 * Angular Elastic Input Directive
 * A directive for AngularJS which automatically resizes the width of input field according to the content, while typing.
 * @author: Jacek Pulit <jacek.pulit@gmail.com>
 * @license: The MIT License
 */

'use strict';

angular.module('ngElasticInput', []).directive('elasticInput', function(){
    return {
        restrict: 'A',
        link: function postLink(scope, element, attrs) {

            var mirror = jQuery('');

            element.css('minWidth', scope.$eval(attrs.elasticInputMinwidth) || 50);
            element.css('maxWidth', scope.$eval(attrs.elasticInputMaxwidth) || 250);

            jQuery.each([
                'fontFamily', 'fontSize', 'fontWeight', 'fontStyle',
                'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent',
                'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle',
                'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight'], function(i,val){
                mirror.css(val, element.css(val));
            });

            jQuery('body').append(mirror);

            update();

            element.on("keydown keyup focus input propertychange change", function(){ update(); });

            function update() {
                mirror.text(element.val());
                element.css('width', mirror.outerWidth() + 1);
            }

        }
    };
});
<|endoftext|>