<commit_before>module.exports = licensee

var blueOakList = require('@blueoak/list')
var Arborist = require('@npmcli/arborist')
var correctLicenseMetadata = require('correct-license-metadata')
var has = require('has')
var npmLicenseCorrections = require('npm-license-corrections')
var osi = require('spdx-osi')
var parse = require('spdx-expression-parse')
var satisfies = require('semver').satisfies
var spdxAllowed = require('spdx-whitelisted')

function licensee (configuration, path, callback) {
  if (!validConfiguration(configuration)) {
    return callback(new Error('Invalid configuration'))
  }
  configuration.licenses = compileLicenseAllowlist(configuration)
  configuration.licensesParsed = (configuration.licenses || [])
    .reduce(function (allowlist, element) {
      try {
        var parsed = parse(element)
        if (has(parsed, 'conjunction')) {
          throw new Error('Cannot match against "' + JSON.stringify(element) + '".')
        }
        return allowlist.concat(parsed)
      } catch (e) {
        return allowlist
      }
    }, [])
  if (
    configuration.licenses.length === 0 &&
    (!configuration.packages || Object.keys(configuration.packages).length === 0)
  ) {
    callback(new Error('No licenses or packages allowed.'))
  } else {
    var arborist = new Arborist({ path })
    arborist.loadActual()
      .catch(function (error) {
        return callback(error)
      })
      .then(function (tree) {
        var children = Array.from(tree.children.values())
        if (configuration.filterPackages) {
          children = configuration.filterPackages(children)
        }
        callback(null, findIssues(configuration, children, []))
      })
  }
}

function validConfiguration (configuration) {
  return (
    isObject(configuration) &&
    has(configuration, 'licenses') &&
    isObject(configuration.licenses) &&
    has(configuration, 'packages')
      ? (
        // Validate `packages` property.
        isObject(configuration.packages) &&
        Object.keys(configuration.packages)
          .every(function (key) {
            return isString(configuration.packages[key])
          })
      ) : true
  )
}

function isObject (argument) {
  return argument && typeof argument === 'object'
}

function isString (argument) {
  return typeof argument === 'string'
}

function findIssues (configuration, children, results) {
  if (children) {
    children.forEach(function (child) {
      if (
        configuration.productionOnly &&
        child.dev
      ) return
      var result = resultForPackage(configuration, child)
      // Deduplicate.
      var existing = results.find(function (existing) {
        return (
          existing.name === result.name &&
          existing.version === result.version
        )
      })
      if (existing) {
        if (existing.duplicates) {
          existing.duplicates.push(result)
        } else {
          existing.duplicates = [result]
        }
      } else {
        results.push(result)
      }
    })
    return results
  } else return results
}

function resultForPackage (configuration, tree) {
  var packageAllowlist = configuration.packages || {}
  console.log('%s is %j', 'tree.package', tree.package)
  var result = {
    name: tree.name,
    version: tree.version,
    license: tree.package.license,
    author: tree.package.author,
    contributors: tree.package.contributors,
    repository: tree.package.repository,
    homepage: tree.package.homepage,
    parent: tree.parent,
    path: tree.realpath
  }

  // Find and apply any manual license metadata correction.
  var manualCorrection = (
    configuration.corrections &&
    npmLicenseCorrections.find(function (correction) {
      return (
        correction.name === result.name &&
        correction.version === result.version
      )
    })
  )
  if (manualCorrection) {
    result.license = manualCorrection.license
    result.corrected = 'manual'
  }

  // Find and apply any automatic license metadata correction.
  var automaticCorrection = (
    configuration.corrections &&
    correctLicenseMetadata(tree.package)
  )
  if (automaticCorrection) {
    result.license = automaticCorrection
    result.corrected = 'automatic'
  }

  // Check if ignored.
  var ignore = configuration.ignore
  if (ignore && Array.isArray(ignore)) {
    var ignored = ignore.some(function (ignore) {
      if (typeof ignore !== 'object') return false
      if (
        ignore.prefix &&
        typeof ignore.prefix === 'string' &&
        startsWith(result.name, ignore.prefix)
      ) return true
      if (
        ignore.scope &&
        typeof ignore.scope === 'string' &&
        startsWith(result.name, '@' + ignore.scope + '/')
      ) return true
      if (
        ignore.author &&
        typeof ignore.author === 'string' &&
        personMatches(result.author, ignore.author)
      ) return true
      return false
    })
    if (ignored) {
      result.approved = true
      result.ignored = ignored
      return result
    }
  }

  result.approved = false

  var packageAllowed = Object.keys(packageAllowlist)
    .some(function (name) {
      return (
        result.name === name &&
        satisfies(result.version, packageAllowlist[name]) === true
      )
    })
  if (packageAllowed) {
    result.approved = true
    result.package = true
    return result
  }

  if (!result.license || typeof result.license !== 'string') {
    return result
  }

  var validSPDX = true
  var parsed
  try {
    parsed = parse(result.license)
  } catch (e) {
    validSPDX = false
  }

  var licenseAllowlist = configuration.licensesParsed
  // Check against licensing rule.
  var licenseAllowed = (
    validSPDX &&
    spdxAllowed(parsed, licenseAllowlist)
  )
  if (licenseAllowed) {
    result.approved = true
  }

  return result
}

function startsWith (string, prefix) {
  return string.toLowerCase().indexOf(prefix.toLowerCase()) === 0
}

function personMatches (person, string) {
  if (!person) return false
  if (typeof person === 'string') {
    return contains(person, string)
  }
  if (typeof person === 'object') {
    if (matches('name')) return true
    if (matches('email')) return true
    if (matches('url')) return true
  }
  return false

  function matches (key) {
    return (
      person[key] &&
      typeof person[key] === 'string' &&
      contains(person[key], string)
    )
  }
}

function contains (string, substring) {
  return string.toLowerCase().indexOf(substring.toLowerCase()) !== -1
}

function licensesFromBlueOak (rating) {
  rating = rating.toLowerCase()
  var ids = []
  for (var index = 0; index < blueOakList.length; index++) {
    var element = blueOakList[index]
    element.licenses.forEach(function (license) {
      try {
        parse(license.id)
        ids.push(license.id)
      } catch (e) {
        // pass
      }
    })
    if (rating === element.name.toLowerCase()) break
  }
  return ids
}

function compileLicenseAllowlist (configuration) {
  var licenses = configuration.licenses
  var allowlist = []
  var spdx = licenses.spdx
  if (spdx) pushMissing(spdx, allowlist)
  var blueOak = licenses.blueOak
  if (blueOak) pushMissing(licensesFromBlueOak(blueOak), allowlist)
  if (licenses.osi) pushMissing(osi, allowlist)
  return allowlist
}

function pushMissing (source, sink) {
  source.forEach(function (element) {
    if (sink.indexOf(element) === -1) sink.push(element)
  })
}
<commit_msg>Remove console.log call<commit_after>module.exports = licensee

var blueOakList = require('@blueoak/list')
var Arborist = require('@npmcli/arborist')
var correctLicenseMetadata = require('correct-license-metadata')
var has = require('has')
var npmLicenseCorrections = require('npm-license-corrections')
var osi = require('spdx-osi')
var parse = require('spdx-expression-parse')
var satisfies = require('semver').satisfies
var spdxAllowed = require('spdx-whitelisted')

function licensee (configuration, path, callback) {
  if (!validConfiguration(configuration)) {
    return callback(new Error('Invalid configuration'))
  }
  configuration.licenses = compileLicenseAllowlist(configuration)
  configuration.licensesParsed = (configuration.licenses || [])
    .reduce(function (allowlist, element) {
      try {
        var parsed = parse(element)
        if (has(parsed, 'conjunction')) {
          throw new Error('Cannot match against "' + JSON.stringify(element) + '".')
        }
        return allowlist.concat(parsed)
      } catch (e) {
        return allowlist
      }
    }, [])
  if (
    configuration.licenses.length === 0 &&
    (!configuration.packages || Object.keys(configuration.packages).length === 0)
  ) {
    callback(new Error('No licenses or packages allowed.'))
  } else {
    var arborist = new Arborist({ path })
    arborist.loadActual()
      .catch(function (error) {
        return callback(error)
      })
      .then(function (tree) {
        var children = Array.from(tree.children.values())
        if (configuration.filterPackages) {
          children = configuration.filterPackages(children)
        }
        callback(null, findIssues(configuration, children, []))
      })
  }
}

function validConfiguration (configuration) {
  return (
    isObject(configuration) &&
    has(configuration, 'licenses') &&
    isObject(configuration.licenses) &&
    has(configuration, 'packages')
      ? (
        // Validate `packages` property.
        isObject(configuration.packages) &&
        Object.keys(configuration.packages)
          .every(function (key) {
            return isString(configuration.packages[key])
          })
      ) : true
  )
}

function isObject (argument) {
  return argument && typeof argument === 'object'
}

function isString (argument) {
  return typeof argument === 'string'
}

function findIssues (configuration, children, results) {
  if (children) {
    children.forEach(function (child) {
      if (
        configuration.productionOnly &&
        child.dev
      ) return
      var result = resultForPackage(configuration, child)
      // Deduplicate.
      var existing = results.find(function (existing) {
        return (
          existing.name === result.name &&
          existing.version === result.version
        )
      })
      if (existing) {
        if (existing.duplicates) {
          existing.duplicates.push(result)
        } else {
          existing.duplicates = [result]
        }
      } else {
        results.push(result)
      }
    })
    return results
  } else return results
}

function resultForPackage (configuration, tree) {
  var packageAllowlist = configuration.packages || {}
  var result = {
    name: tree.name,
    version: tree.version,
    license: tree.package.license,
    author: tree.package.author,
    contributors: tree.package.contributors,
    repository: tree.package.repository,
    homepage: tree.package.homepage,
    parent: tree.parent,
    path: tree.realpath
  }

  // Find and apply any manual license metadata correction.
  var manualCorrection = (
    configuration.corrections &&
    npmLicenseCorrections.find(function (correction) {
      return (
        correction.name === result.name &&
        correction.version === result.version
      )
    })
  )
  if (manualCorrection) {
    result.license = manualCorrection.license
    result.corrected = 'manual'
  }

  // Find and apply any automatic license metadata correction.
  var automaticCorrection = (
    configuration.corrections &&
    correctLicenseMetadata(tree.package)
  )
  if (automaticCorrection) {
    result.license = automaticCorrection
    result.corrected = 'automatic'
  }

  // Check if ignored.
  var ignore = configuration.ignore
  if (ignore && Array.isArray(ignore)) {
    var ignored = ignore.some(function (ignore) {
      if (typeof ignore !== 'object') return false
      if (
        ignore.prefix &&
        typeof ignore.prefix === 'string' &&
        startsWith(result.name, ignore.prefix)
      ) return true
      if (
        ignore.scope &&
        typeof ignore.scope === 'string' &&
        startsWith(result.name, '@' + ignore.scope + '/')
      ) return true
      if (
        ignore.author &&
        typeof ignore.author === 'string' &&
        personMatches(result.author, ignore.author)
      ) return true
      return false
    })
    if (ignored) {
      result.approved = true
      result.ignored = ignored
      return result
    }
  }

  result.approved = false

  var packageAllowed = Object.keys(packageAllowlist)
    .some(function (name) {
      return (
        result.name === name &&
        satisfies(result.version, packageAllowlist[name]) === true
      )
    })
  if (packageAllowed) {
    result.approved = true
    result.package = true
    return result
  }

  if (!result.license || typeof result.license !== 'string') {
    return result
  }

  var validSPDX = true
  var parsed
  try {
    parsed = parse(result.license)
  } catch (e) {
    validSPDX = false
  }

  var licenseAllowlist = configuration.licensesParsed
  // Check against licensing rule.
  var licenseAllowed = (
    validSPDX &&
    spdxAllowed(parsed, licenseAllowlist)
  )
  if (licenseAllowed) {
    result.approved = true
  }

  return result
}

function startsWith (string, prefix) {
  return string.toLowerCase().indexOf(prefix.toLowerCase()) === 0
}

function personMatches (person, string) {
  if (!person) return false
  if (typeof person === 'string') {
    return contains(person, string)
  }
  if (typeof person === 'object') {
    if (matches('name')) return true
    if (matches('email')) return true
    if (matches('url')) return true
  }
  return false

  function matches (key) {
    return (
      person[key] &&
      typeof person[key] === 'string' &&
      contains(person[key], string)
    )
  }
}

function contains (string, substring) {
  return string.toLowerCase().indexOf(substring.toLowerCase()) !== -1
}

function licensesFromBlueOak (rating) {
  rating = rating.toLowerCase()
  var ids = []
  for (var index = 0; index < blueOakList.length; index++) {
    var element = blueOakList[index]
    element.licenses.forEach(function (license) {
      try {
        parse(license.id)
        ids.push(license.id)
      } catch (e) {
        // pass
      }
    })
    if (rating === element.name.toLowerCase()) break
  }
  return ids
}

function compileLicenseAllowlist (configuration) {
  var licenses = configuration.licenses
  var allowlist = []
  var spdx = licenses.spdx
  if (spdx) pushMissing(spdx, allowlist)
  var blueOak = licenses.blueOak
  if (blueOak) pushMissing(licensesFromBlueOak(blueOak), allowlist)
  if (licenses.osi) pushMissing(osi, allowlist)
  return allowlist
}

function pushMissing (source, sink) {
  source.forEach(function (element) {
    if (sink.indexOf(element) === -1) sink.push(element)
  })
}
<|endoftext|>


<commit_before>const Discord = require('discord.io');
const schedule = require('node-schedule');
const statusUtils = require('./status-utils.js');
const persistence = require('./persistence');
const getPrice = require('./get-price');

if (!process.env.DISCORD_TOKEN) {
  console.error('DISCORD_TOKEN not set!');
  process.exit(1);
}

const bot = new Discord.Client({
  token: process.env.DISCORD_TOKEN,
  autorun: true,
});

const DEV_NULL_CHANNEL = '349421970083020801';
const DEV_NULL_CHECK_FREQUENCY = 10;
const DEV_NULL_MIN_AGE = 5;

const TECH_CHANNEL = '262866463675777024';
const isEvents = channel => channel === '262864567695048705';

const triggers = [];

const addMessage = (trigger, action, ignoreEvents = true) => {
  if (trigger instanceof Array) {
    for (let i = 0; i < trigger.length; i += 1) {
      triggers.push({ trigger: trigger[i], action, ignoreEvents });
    }
  } else {
    triggers.push({ trigger, action, ignoreEvents });
  }
};

bot.on('ready', () => {
  persistence.init(() => {
    console.log('Logged in as %s - %s\n', bot.username, bot.id);

    bot.on('disconnect', () => {
      console.log('Disconnected, attempting to reconnect');
      bot.connect();
    });

    const printHelp = () => triggers
        .filter(trigger => typeof trigger.trigger === 'string')
        .map(trigger => trigger.trigger)
        .sort()
        .reduce((a, b) => `${a}\n${b}`);

    addMessage('!help', printHelp);
    addMessage('!ping', () => {
      console.log('pinged');
      return 'pong';
    });
    addMessage(/saren/i, () => (Math.random() < 0.2 ? 'Who is this Saren character??? Hey Scur do you know who Saren is??' : null));

    addMessage('!status', (opts) => {
      let message = '';
      statusUtils.getBattery.then((battery) => {
        // Add battry status to response if available
        message = message.concat(battery).concat('\n');
      }).catch(() => {}) // Ignore errors
      .then(() => {
        // Add uptime information to response, whether battery check was successful or not, then send message
        message = message.concat(statusUtils.getUptime());
        opts.bot.sendMessage({
          to: opts.channelId,
          message,
        });
      });
    });
    addMessage('!battery', (opts) => {
      statusUtils.getBattery().then((battery) => {
        opts.bot.sendMessage({
          to: opts.channelId,
          message: battery,
        });
      }).catch(() => {
        opts.bot.sendMessage({
          to: opts.channelId,
          message: 'Error getting battery data.',
        });
      });
    });
    addMessage('!uptime', statusUtils.getUptime);
    addMessage('!events',
        opts => (isEvents(opts.channelId) ? 'Check out the pinned messages!' : 'Check out the pinned messages in the #Events channel!'),
        false);

    addMessage(/!markets\s?(.*)/i, (opts) => {
      let query;
      if (opts.matches[1] !== '') {
        query = getPrice.single(opts.matches[1]);
      } else {
        query = getPrice.all();
      }
      query.then(prices => opts.bot.sendMessage({
        to: opts.channelId,
        message: prices.reduce((a, b) => `${a}\n\n${b}`),
      })).catch(err => opts.bot.sendMessage({
        to: opts.channelId,
        message: err,
      }));
    });

    try {
      require('./secret-triggers.js').init(addMessage); // eslint-disable-line global-require
    } catch (e) {
      console.warn('secret-triggers.js does not exist, ignoring.');
    }

    const handleMessage = (user, userId, channelId, message, event) => {
      let wasTrigger = false;

      // Ignore mysertious non-existant messages
      if (!message) {
        return;
      }

      // Ignore own messages
      if (userId === bot.id) {
        return;
      }

      // Ignore messages for the music bot
      if (message.startsWith('pls')) {
        return;
      }

      // Ignore image posts
      if (event.d.attachments.length !== 0) {
        return;
      }

      const doTrigger = (trigger, matches) => {
        console.log('triggered');
        wasTrigger = true;
        const result = trigger.action({
          user,
          userId,
          channelId,
          message,
          event,
          bot,
          matches,
        });

        console.log('sending this:');
        console.log(result);
        console.log(`to channel: ${channelId}`);

        if (result && typeof result === 'string') {
          bot.sendMessage({
            to: channelId,
            message: result,
          });
        }
      };

      for (let i = 0; i < triggers.length; i += 1) {
        const trigger = triggers[i];

        // ignore events channel
        if (trigger.ignoreEvents && isEvents(channelId)) {
          // Skipping because this is the events channel.
        } else if (trigger.trigger instanceof RegExp) {
          const matches = message.match(trigger.trigger);
          if (matches) {
            doTrigger(trigger, matches);
          }
        } else if (trigger.trigger instanceof Function) {
          if (trigger.trigger()) {
            doTrigger(trigger);
          }
        } else if (message === trigger.trigger) {
          doTrigger(trigger);
        }
      }

      if (!wasTrigger && !isEvents(channelId)) {
        persistence.checkMessage(message, () => {
          console.log(`message exists: ${message}`);

          // TODO: Add support for delete reason in the audit log once discord.io adds support

          bot.deleteMessage({
            channelID: channelId,
            messageID: event.d.id,
          }, (err) => {
            if (err) {
              console.log(err);
            }
          });
        }, () => console.log(`message is new: ${message}`), err => console.error(err));
      }
    };

    bot.on('message', handleMessage);

    bot.on('any', (event) => {
      if (event.t === 'MESSAGE_UPDATE' && !event.d.pinned) {
        handleMessage(event.d.author ? event.d.author.username : null,
            event.d.author ? event.d.author.id : null,
            event.d.channel_id,
            event.d.content,
            event);
      }
    });

    schedule.scheduleJob(`*/${DEV_NULL_CHECK_FREQUENCY} * * * *`, () => {
      console.log('trying to clear messages.');
      bot.getMessages({ channelID: DEV_NULL_CHANNEL, limit: 100 }, (err, messages) => {
        if (err) {
          console.error(err);
        } else {
          bot.deleteMessages({
            channelID: DEV_NULL_CHANNEL,
            messageIDs: messages.filter(m => Date.now() - new Date(m.timestamp).valueOf() > DEV_NULL_MIN_AGE * 60 * 1000).map(m => m.id),
          });
        }
      });
    });

    schedule.scheduleJob('30 5 * * 1-5', () => getPrice.all().then(prices => bot.sendMessage({
      to: TECH_CHANNEL,
      message: prices.reduce((a, b) => `${a}\n\n${b}`),
    })).catch(err => bot.sendMessage({
      to: TECH_CHANNEL,
      message: `SCRIPT CRASH! ${err}`,
    })));
  });
});

exports.addMessage = addMessage;
<commit_msg>Fix !markets command in the middle of a message triggering the bot<commit_after>const Discord = require('discord.io');
const schedule = require('node-schedule');
const statusUtils = require('./status-utils.js');
const persistence = require('./persistence');
const getPrice = require('./get-price');

if (!process.env.DISCORD_TOKEN) {
  console.error('DISCORD_TOKEN not set!');
  process.exit(1);
}

const bot = new Discord.Client({
  token: process.env.DISCORD_TOKEN,
  autorun: true,
});

const DEV_NULL_CHANNEL = '349421970083020801';
const DEV_NULL_CHECK_FREQUENCY = 10;
const DEV_NULL_MIN_AGE = 5;

const TECH_CHANNEL = '262866463675777024';
const isEvents = channel => channel === '262864567695048705';

const triggers = [];

const addMessage = (trigger, action, ignoreEvents = true) => {
  if (trigger instanceof Array) {
    for (let i = 0; i < trigger.length; i += 1) {
      triggers.push({ trigger: trigger[i], action, ignoreEvents });
    }
  } else {
    triggers.push({ trigger, action, ignoreEvents });
  }
};

bot.on('ready', () => {
  persistence.init(() => {
    console.log('Logged in as %s - %s\n', bot.username, bot.id);

    bot.on('disconnect', () => {
      console.log('Disconnected, attempting to reconnect');
      bot.connect();
    });

    const printHelp = () => triggers
        .filter(trigger => typeof trigger.trigger === 'string')
        .map(trigger => trigger.trigger)
        .sort()
        .reduce((a, b) => `${a}\n${b}`);

    addMessage('!help', printHelp);
    addMessage('!ping', () => {
      console.log('pinged');
      return 'pong';
    });
    addMessage(/saren/i, () => (Math.random() < 0.2 ? 'Who is this Saren character??? Hey Scur do you know who Saren is??' : null));

    addMessage('!status', (opts) => {
      let message = '';
      statusUtils.getBattery.then((battery) => {
        // Add battry status to response if available
        message = message.concat(battery).concat('\n');
      }).catch(() => {}) // Ignore errors
      .then(() => {
        // Add uptime information to response, whether battery check was successful or not, then send message
        message = message.concat(statusUtils.getUptime());
        opts.bot.sendMessage({
          to: opts.channelId,
          message,
        });
      });
    });
    addMessage('!battery', (opts) => {
      statusUtils.getBattery().then((battery) => {
        opts.bot.sendMessage({
          to: opts.channelId,
          message: battery,
        });
      }).catch(() => {
        opts.bot.sendMessage({
          to: opts.channelId,
          message: 'Error getting battery data.',
        });
      });
    });
    addMessage('!uptime', statusUtils.getUptime);
    addMessage('!events',
        opts => (isEvents(opts.channelId) ? 'Check out the pinned messages!' : 'Check out the pinned messages in the #Events channel!'),
        false);

    addMessage(/^!markets\s?(.*)$/, (opts) => {
      let query;
      if (opts.matches[1] !== '') {
        query = getPrice.single(opts.matches[1]);
      } else {
        query = getPrice.all();
      }
      query.then(prices => opts.bot.sendMessage({
        to: opts.channelId,
        message: prices.reduce((a, b) => `${a}\n\n${b}`),
      })).catch(err => opts.bot.sendMessage({
        to: opts.channelId,
        message: err,
      }));
    });

    try {
      require('./secret-triggers.js').init(addMessage); // eslint-disable-line global-require
    } catch (e) {
      console.warn('secret-triggers.js does not exist, ignoring.');
    }

    const handleMessage = (user, userId, channelId, message, event) => {
      let wasTrigger = false;

      // Ignore mysertious non-existant messages
      if (!message) {
        return;
      }

      // Ignore own messages
      if (userId === bot.id) {
        return;
      }

      // Ignore messages for the music bot
      if (message.startsWith('pls')) {
        return;
      }

      // Ignore image posts
      if (event.d.attachments.length !== 0) {
        return;
      }

      const doTrigger = (trigger, matches) => {
        console.log('triggered');
        wasTrigger = true;
        const result = trigger.action({
          user,
          userId,
          channelId,
          message,
          event,
          bot,
          matches,
        });

        console.log('sending this:');
        console.log(result);
        console.log(`to channel: ${channelId}`);

        if (result && typeof result === 'string') {
          bot.sendMessage({
            to: channelId,
            message: result,
          });
        }
      };

      for (let i = 0; i < triggers.length; i += 1) {
        const trigger = triggers[i];

        // ignore events channel
        if (trigger.ignoreEvents && isEvents(channelId)) {
          // Skipping because this is the events channel.
        } else if (trigger.trigger instanceof RegExp) {
          const matches = message.match(trigger.trigger);
          if (matches) {
            doTrigger(trigger, matches);
          }
        } else if (trigger.trigger instanceof Function) {
          if (trigger.trigger()) {
            doTrigger(trigger);
          }
        } else if (message === trigger.trigger) {
          doTrigger(trigger);
        }
      }

      if (!wasTrigger && !isEvents(channelId)) {
        persistence.checkMessage(message, () => {
          console.log(`message exists: ${message}`);

          // TODO: Add support for delete reason in the audit log once discord.io adds support

          bot.deleteMessage({
            channelID: channelId,
            messageID: event.d.id,
          }, (err) => {
            if (err) {
              console.log(err);
            }
          });
        }, () => console.log(`message is new: ${message}`), err => console.error(err));
      }
    };

    bot.on('message', handleMessage);

    bot.on('any', (event) => {
      if (event.t === 'MESSAGE_UPDATE' && !event.d.pinned) {
        handleMessage(event.d.author ? event.d.author.username : null,
            event.d.author ? event.d.author.id : null,
            event.d.channel_id,
            event.d.content,
            event);
      }
    });

    schedule.scheduleJob(`*/${DEV_NULL_CHECK_FREQUENCY} * * * *`, () => {
      console.log('trying to clear messages.');
      bot.getMessages({ channelID: DEV_NULL_CHANNEL, limit: 100 }, (err, messages) => {
        if (err) {
          console.error(err);
        } else {
          bot.deleteMessages({
            channelID: DEV_NULL_CHANNEL,
            messageIDs: messages.filter(m => Date.now() - new Date(m.timestamp).valueOf() > DEV_NULL_MIN_AGE * 60 * 1000).map(m => m.id),
          });
        }
      });
    });

    schedule.scheduleJob('30 5 * * 1-5', () => getPrice.all().then(prices => bot.sendMessage({
      to: TECH_CHANNEL,
      message: prices.reduce((a, b) => `${a}\n\n${b}`),
    })).catch(err => bot.sendMessage({
      to: TECH_CHANNEL,
      message: `SCRIPT CRASH! ${err}`,
    })));
  });
});

exports.addMessage = addMessage;
<|endoftext|>


<commit_before>var loader = require('./loader.js');
var request = require('request');
var jsdom = require('jsdom');
var jsonfile = require('jsonfile');
var paramCase = require('param-case');

var walk = require('walk');
var files = [];

var st = {};

// Walker options
var walker  = walk.walk('./webidl/webidl', { followLinks: false });

walker.on('file', function(root, stat, next) {
  st = loader.file(root + '/' + stat.name, st);
  next();
});

walker.on('end', function() {
  "use strict";

  var interfaces = st.interfaces;
  var keys = [];

  for (let name in interfaces) {
    let inter = interfaces[name];
    let platform = 'API';
    if (name === 'CSS2Properties') {
      platform = 'CSS';
    }
    keys.push([`${name}`, `${platform}/${name}`]);
    if (inter.members) {
      for (let i in inter.members) {
        let member = inter.members[i];
        if (name === 'CSS2Properties') {
          keys.push([`${name}/${member.name}`, `${platform}/${paramCase(member.name)}`]);
        } else {
          keys.push([`${name}/${member.name}`, `${platform}/${name}/${member.name}`]);
        }
      }
    }
  }

  // var def = jsonfile.readFileSync('webidl.json');
  var def = {};
  var walk = function (i) {
    if (i === keys.length) {
      output();
      return;
    }
    let key = keys[i][0];
    if (def[key]) {
      walk(i+1);
      return;
    }
    var name = keys[i][1];
    var url = `http://developer.mozilla.org/en-US/docs/Web/${name}`;
    var timeout = setTimeout(function () {
        console.log(i, key, 'timeout');
      walk(i+1);
    }, 30 * 1000);

    request({
      method: 'GET',
      uri: url
    }, function (error, response, body) {
      clearTimeout(timeout);
      if (!error && response.statusCode === 200) {
        var doc = null;
        jsdom.env({
          url: url,
          scripts: ["http://code.jquery.com/jquery.js"],
          done: function (errors, window) {
            var $ = window.$;
            var nodes = $('p');
            nodes.each(function () {
              var text = $.trim($(this).text());
              if (text !== ''
               && text !== 'This article is in need of a technical review.'
               && text !== '« SVG Attribute reference home'
               && !/^Draft/.test(text)
               && !/^Non-standard/.test(text)
               && !/^This is an experimental technology/.test(text)) {
                doc = text;
                console.log(i, key, doc);
                def[key] = {
                  "!url": url,
                  "!doc": doc
                }
                return false;
              }
            });
            if (!doc) {
              console.log(i, key, 'nodoc');
            }
            walk(i+1);
          }
        });
        // request({
          // method: 'GET',
          // uri: response.headers['location']
        // }, function (error, response, body) {
          // let p = cheerio.load(body)('p');
          // console.log(p);
        // });
      } else {
        console.log(i, key, 'notfound');
        walk(i+1);
      }
    });
  };

  var output = function () {
    console.log(JSON.stringify(def, null, 2));
  };

  walk(0);
});

<commit_msg>Skip not-active<commit_after>var loader = require('./loader.js');
var request = require('request');
var jsdom = require('jsdom');
var jsonfile = require('jsonfile');
var paramCase = require('param-case');

var walk = require('walk');
var files = [];

var st = {};

// Walker options
var walker  = walk.walk('./webidl/webidl', { followLinks: false });

walker.on('file', function(root, stat, next) {
  st = loader.file(root + '/' + stat.name, st);
  next();
});

walker.on('end', function() {
  "use strict";

  var interfaces = st.interfaces;
  var keys = [];

  for (let name in interfaces) {
    let inter = interfaces[name];
    let platform = 'API';
    if (name === 'CSS2Properties') {
      platform = 'CSS';
    }
    keys.push([`${name}`, `${platform}/${name}`]);
    if (inter.members) {
      for (let i in inter.members) {
        let member = inter.members[i];
        if (name === 'CSS2Properties') {
          keys.push([`${name}/${member.name}`, `${platform}/${paramCase(member.name)}`]);
        } else {
          keys.push([`${name}/${member.name}`, `${platform}/${name}/${member.name}`]);
        }
      }
    }
  }

  // var def = jsonfile.readFileSync('webidl.json');
  var def = {};
  var walk = function (i) {
    if (i === keys.length) {
      output();
      return;
    }
    let key = keys[i][0];
    if (def[key]) {
      walk(i+1);
      return;
    }
    var name = keys[i][1];
    var url = `http://developer.mozilla.org/en-US/docs/Web/${name}`;
    var timeout = setTimeout(function () {
        console.log(i, key, 'timeout');
      walk(i+1);
    }, 30 * 1000);

    request({
      method: 'GET',
      uri: url
    }, function (error, response, body) {
      clearTimeout(timeout);
      if (!error && response.statusCode === 200) {
        var doc = null;
        jsdom.env({
          url: url,
          scripts: ["http://code.jquery.com/jquery.js"],
          done: function (errors, window) {
            var $ = window.$;
            var nodes = $('p');
            nodes.each(function () {
              var text = $.trim($(this).text());
              if (text !== ''
               && text !== 'This article is in need of a technical review.'
               && text !== '« SVG Attribute reference home'
               && !/^Draft/.test(text)
               && !/^Not native/.test(text)
               && !/^Non-standard/.test(text)
               && !/^This is an experimental technology/.test(text)) {
                doc = text;
                console.log(i, key, doc);
                def[key] = {
                  "!url": url,
                  "!doc": doc
                }
                return false;
              }
            });
            if (!doc) {
              console.log(i, key, 'nodoc');
            }
            walk(i+1);
          }
        });
        // request({
          // method: 'GET',
          // uri: response.headers['location']
        // }, function (error, response, body) {
          // let p = cheerio.load(body)('p');
          // console.log(p);
        // });
      } else {
        console.log(i, key, 'notfound');
        walk(i+1);
      }
    });
  };

  var output = function () {
    console.log(JSON.stringify(def, null, 2));
  };

  walk(0);
});

<|endoftext|>


<commit_before>
var fse = require("fs-extra");
var colors = require("colors");

function success(name, src, dest) {
    console.log("(" + name + ") '" + src.green + "' -> '" + dest.green + "' (" +  "\u2714".green + ")");
}

function fatal(name, src, dest, err) {
    console.error("(" + name + ") '" + src.red + "' -> '" + dest.red + "' (" +  "\u2718".red + ")");
    console.error();
    console.error("    " + err);
    process.exit(err.errno);
}

module.exports = function(options={}) {
    const { verbose=false } = options;
    const name = "rollup-plugin-copy";

    return {
        name: name,
        ongenerate: function(object) {

            for (key in options) {
                if (key == "verbose") continue;
                const src = key;
                const dest = options[key];

                fse.copy(src, dest).then( () => {
                    if (verbose) success(name, src, dest);
                }).catch( (err) => {
                    fatal(name, src, dest, err);
                });
            }

        }
    }
};

<commit_msg>removed deprecated ongenerate and replace with generateBundle hook<commit_after>
var fse = require("fs-extra");
var colors = require("colors");

function success(name, src, dest) {
    console.log("(" + name + ") '" + src.green + "' -> '" + dest.green + "' (" +  "\u2714".green + ")");
}

function fatal(name, src, dest, err) {
    console.error("(" + name + ") '" + src.red + "' -> '" + dest.red + "' (" +  "\u2718".red + ")");
    console.error();
    console.error("    " + err);
    process.exit(err.errno);
}

module.exports = function(options={}) {
    const { verbose=false } = options;
    const name = "rollup-plugin-copy";

    return {
        name: name,
        generateBundle: function(object) {

            for (key in options) {
                if (key == "verbose") continue;
                const src = key;
                const dest = options[key];

                fse.copy(src, dest).then( () => {
                    if (verbose) success(name, src, dest);
                }).catch( (err) => {
                    fatal(name, src, dest, err);
                });
            }

        }
    }
};

<|endoftext|>


<commit_before>function get(date, timezone) {
	// check if timezone uses DST, and then test if it is in dst
	return timezone.dst ? timezone[timezone.dst(date) ? 'saving' : 'standard'] : timezone;
}

function from(timezone, date) {

	// TODO make sure date argument is valid
	if(date instanceof Date && date.tz) {
		var current = date.getTimezoneOffset();

		date = new Date(date.getTime() - (date.tz[2] + current) * 60000);

		delete date.tz;
	} else if (date.time && date.date) {
		var time = _.defaults(date.time.split(''), '00:00:00.000Z'.split('')).join('');

		date = new Date(date.date + 'T' + time);

		// TODO make sure this handle time just around DST switch
		var tz = get(date, timezone);

		date = new Date(date.getTime() - tz[2] * 60000);
	}

	return date;
}

function to(timezone, date) {
	if(!(date instanceof Date)) date = new Date(date);

	// get actual timezone (DST or standard time)
	var tz = get(date, timezone),
		current = date.getTimezoneOffset();

	// use timezones offset to calculate new date, tz[2] is in minutes, so multiply by 60000 (60 seconds * 10000 milliseconds)
	date = new Date(date.getTime() + (tz[2] + current) * 60000);

	// save reference to tz
	date.tz = tz;

	return date;
}

module.exports = {
	to: to,

	from: from,

	factory: function factory(timezone) {
		if(!timezone)
			throw new Error('Timezone is required!');

		return {
			to: to.bind(void 0, timezone),

			from: from.bind(void 0, timezone)
		};
	}
};
<commit_msg>Removed use of Lo-Dash.<commit_after>function get(date, timezone) {
	// check if timezone uses DST, and then test if it is in dst
	return timezone.dst ? timezone[timezone.dst(date) ? 'saving' : 'standard'] : timezone;
}

function from(timezone, date) {
	// TODO make sure date argument is valid
	if(date instanceof Date && date.tz) {
		var current = date.getTimezoneOffset();

		date = new Date(date.getTime() - (date.tz[2] + current) * 60000);

		delete date.tz;
	} else if (date.time && date.date) {
		// fill any missing values... ie turn 01:00 into 01:00:00.000Z
		var time = date.time +  '00:00:00.000Z'.slice(date.time.length);

		date = new Date(date.date + 'T' + time);

		// TODO make sure this handle time just around DST switch, eg 2016-03-27T01:30:00:00Z
		var tz = get(date, timezone);

		date = new Date(date.getTime() - tz[2] * 60000);
	}

	return date;
}

function to(timezone, date) {
	if(!(date instanceof Date)) date = new Date(date);

	// get actual timezone (DST or standard time)
	var tz = get(date, timezone),
		current = date.getTimezoneOffset();

	// use timezones offset to calculate new date, tz[2] is in minutes, so multiply by 60000 (60 seconds * 10000 milliseconds)
	date = new Date(date.getTime() + (tz[2] + current) * 60000);

	// save reference to tz
	date.tz = tz;

	return date;
}

module.exports = {
	to: to,

	from: from,

	factory: function factory(timezone) {
		if(!timezone)
			throw new Error('Timezone is required!');

		return {
			to: to.bind(void 0, timezone),

			from: from.bind(void 0, timezone)
		};
	}
};
<|endoftext|>


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

var fs = require("fs");
var rc = require("rc")("npm");
var npm = require("npm");

//We will store all registry links at user level in .npmregistry file
var RPATH = process.env.HOME + "/.npmregistry";
var FILENAME = ".registryInfo";

/**
 * Module exports.
 * @public
 */

module.exports.usage = usage;
module.exports.init = init;
module.exports.ls = list;
module.exports.list = list;
module.exports.add = add;
module.exports.remove = remove;
module.exports.use = change;
module.exports.change = change;
module.exports.checkArgs = checkArgs;

function checkArgs (possibleActions, cmd) {
    for (var act in possibleActions) {
        if (act === cmd) {
            return true;
        }
    }
}

/**
 * [usage description]
 * @return {[type]} [description]
 */
function usage (message) {

    if (message === "error") {
        console.log("Oopps there is something wrong. Check your params.");
    }

    console.log("");
    console.log("   You can run Switch Registry as ");
    console.log("");
    console.log("   switch-registry {command} {arguments}");
    console.log("");
    console.log("   ----------------------------------------------");
    console.log("   Command       Description                     ");
    console.log("   ----------------------------------------------");
    console.log("   init        | Initialize base files and entries");
    console.log("   usage       | Display this help");
    console.log("   ls          | Display list of added registries");
    console.log("   add         | Add a new registry");
    console.log("   remove      | Remove an existing registry");
    console.log("   use         | Change to other existing registry");
    console.log("");
    console.log("   Short Command npmrs");
    console.log("   --------------------");
    console.log("   npmrs {command} {arguments}");
    console.log("");

    return '';
}

/**
 * [fetchFileData description]
 * @param  {[type]}  str [description]
 * @return {Boolean}     [description]
 */
var fetchFileData = function (str) {
    var data = "";
    try {
        return JSON.parse(str);
    } catch (e) {
        return {}; //Just return blank if there is any error
        //TODO : Need to check if this failure will case any issue.
    }
}

function showFormatedData(data) {
    var displayStr = "";
    for (var key in data) {
        if (data.hasOwnProperty(key)) {
            if (displayStr !== "") {
                displayStr += " | ";
            }

            if (key === "active" && data[key] === true) {
                displayStr = ">  " + displayStr;
            } else {
                displayStr += data[key];
            }
        }
    }

    return displayStr;
}

function addToFile(data) {
    fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(data), "utf8", function (
        err
    ) {
        if (err) console.log(err);
    });
}

function getCurrentRegistry() {
    //Fetch current registry
    //TODO: Think of a better way to manage this, we can use exec but
    //TODO: that is also not a very good idea
    try {
        return rc.registry.slice(-1) === "/" ? rc.registry : rc.registry + "/";
    } catch (e) {
        return "https://registry.npmjs.org/";
    }
}

function initRegistryFile(data) {
    //Convert file data to Object -- At this point it will be blank
    //TODO: Have to check if data is there
    //TODO: If data is there then normal additon will happen only if it's not there
    var url = getCurrentRegistry();

    var currData = fetchFileData(data);

    //Adding default entry
    currData.Default = { name: "Default", active: true, url: url };
    //Convert back to JSON string
    addToFile(currData);
}

function init() {
    //Create npmregistry
    if (!fs.existsSync(RPATH)) {
        //Create file
        fs.mkdirSync(RPATH);
    }
    //Save current registry to registry file if its npmjs default registry
    //TODO : Will check for npmjs thing later, right now considering current one as default

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function readFileCallback(
        err,
        data
    ) {
        if (err) {
            fs.open(RPATH + "/" + FILENAME, "w+", function readFileCallback(
                err,
                data
            ) {
                console.log("this is another place");
                initRegistryFile("");
            });
            //TODO: Create file here and call add data function
        } else {
            initRegistryFile(data);
        }
    });
}

/**
 * Function will list down all added registry
 * @param {[type]} args [description]
 */
function list(args) {
    fs.readFile(RPATH + "/" + FILENAME, "utf8", function readFileCallback(
        err,
        data
    ) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object -- At this point it will be blank
            var currData = fetchFileData(data);

            for (var key in currData) {
                console.log(showFormatedData(currData[key]));
            }
        }
    });
}

/**
 * Function will add new custom registry
 * @param {[type]} args [description]
 */
function add(args) {
    var currData = {};

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object
            currData = fetchFileData(data);

            if (typeof currData[args[1]] === "undefined") {
                currData[args[1]] = { name: args[1], active: false, url: args[2] };

                fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(currData), function (
                    err
                ) {
                    if (err) throw err;
                    console.log("complete");
                });
            } else {
                console.log(
                    "entry with this key already exist, Please use another key to add"
                );
            }
        }
    });
}

/**
 * Function will remove existing custom registry by name
 * @param  {[type]} args [description]
 */
function remove(args) {
    var currData = {};

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object
            currData = fetchFileData(data);

            if (currData[args[1]]) {
                delete currData[args[1]];

                fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(currData), function (
                    err
                ) {
                    if (err) throw err;
                    console.log("complete");
                });
            }
        }
    });
}

/**
 * Function will change any existing custom registry by name
 * @param  {[type]} args [description]
 */
function change(args) {
    npm.load(function (err) {
        if (err) return "";

        fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
            if (err) {
                init();
                //TODO: Create file here and call add data function
            } else {
                //Convert file data to Object
                var currData = fetchFileData(data);

                if (currData[args[1]]) {
                    var changeTo = currData[args[1]];

                    npm.commands.config(["set", "registry", changeTo.url], function (
                        err,
                        data
                    ) {
                        if (err) return "";
                        console.log("                        ");
                        var newR = npm.config.get("registry");
                        console.log(["", "   Registry has been set to: " + newR, ""]);
                    });
                }
            }
        });
    });
}
<commit_msg>NPM test fix<commit_after>/*jslint node: true */
"use strict";

var fs = require("fs");
var rc = require("rc")("npm");
var npm = require("npm");

//We will store all registry links at user level in .npmregistry file
var RPATH = process.env.HOME + "/.npmregistry";
var FILENAME = ".registryInfo";

/**
 * Module exports.
 * @public
 */

module.exports.usage = usage;
module.exports.init = init;
module.exports.ls = list;
module.exports.list = list;
module.exports.add = add;
module.exports.remove = remove;
module.exports.use = change;
module.exports.change = change;
module.exports.checkArgs = checkArgs;

function checkArgs (possibleActions, cmd) {
    for (var act in possibleActions) {
        if (act === cmd) {
            return true;
        }
    }
}

/**
 * [usage description]
 * @return {[type]} [description]
 */
function usage (message) {

    if (message === "error") {
        console.log("Oopps there is something wrong. Check your params.");
    }

    console.log("");
    console.log("   You can run Switch Registry as ");
    console.log("");
    console.log("   switch-registry {command} {arguments}");
    console.log("");
    console.log("   ----------------------------------------------");
    console.log("   Command       Description                     ");
    console.log("   ----------------------------------------------");
    console.log("   init        | Initialize base files and entries");
    console.log("   usage       | Display this help");
    console.log("   ls          | Display list of added registries");
    console.log("   add         | Add a new registry");
    console.log("   remove      | Remove an existing registry");
    console.log("   use         | Change to other existing registry");
    console.log("");
    console.log("   Short Command npmrs");
    console.log("   --------------------");
    console.log("   npmrs {command} {arguments}");
    console.log("");

    return '';
}

/**
 * [fetchFileData description]
 * @param  {[type]}  str [description]
 * @return {Boolean}     [description]
 */
function fetchFileData (str) {
    var data = "";
    try {
        return JSON.parse(str);
    } catch (e) {
        return {}; //Just return blank if there is any error
        //TODO : Need to check if this failure will case any issue.
    }
}

function showFormatedData(data) {
    var displayStr = "";
    for (var key in data) {
        if (data.hasOwnProperty(key)) {
            if (displayStr !== "") {
                displayStr += " | ";
            }

            if (key === "active" && data[key] === true) {
                displayStr = ">  " + displayStr;
            } else {
                displayStr += data[key];
            }
        }
    }

    return displayStr;
}

function addToFile(data) {
    fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(data), "utf8", function (
        err
    ) {
        if (err) console.log(err);
    });
}

function getCurrentRegistry() {
    //Fetch current registry
    //TODO: Think of a better way to manage this, we can use exec but
    //TODO: that is also not a very good idea
    try {
        return rc.registry.slice(-1) === "/" ? rc.registry : rc.registry + "/";
    } catch (e) {
        return "https://registry.npmjs.org/";
    }
}

function initRegistryFile(data) {
    //Convert file data to Object -- At this point it will be blank
    //TODO: Have to check if data is there
    //TODO: If data is there then normal additon will happen only if it's not there
    var url = getCurrentRegistry();

    var currData = fetchFileData(data);

    //Adding default entry
    currData.Default = { name: "Default", active: true, url: url };
    //Convert back to JSON string
    addToFile(currData);
}

function init() {
    //Create npmregistry
    if (!fs.existsSync(RPATH)) {
        //Create file
        fs.mkdirSync(RPATH);
    }
    //Save current registry to registry file if its npmjs default registry
    //TODO : Will check for npmjs thing later, right now considering current one as default

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function readFileCallback(
        err,
        data
    ) {
        if (err) {
            fs.open(RPATH + "/" + FILENAME, "w+", function readFileCallback(
                err,
                data
            ) {
                console.log("this is another place");
                initRegistryFile("");
            });
            //TODO: Create file here and call add data function
        } else {
            initRegistryFile(data);
        }
    });
}

/**
 * Function will list down all added registry
 * @param {[type]} args [description]
 */
function list(args) {
    fs.readFile(RPATH + "/" + FILENAME, "utf8", function readFileCallback(
        err,
        data
    ) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object -- At this point it will be blank
            var currData = fetchFileData(data);

            for (var key in currData) {
                console.log(showFormatedData(currData[key]));
            }
        }
    });
}

/**
 * Function will add new custom registry
 * @param {[type]} args [description]
 */
function add(args) {
    var currData = {};

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object
            currData = fetchFileData(data);

            if (typeof currData[args[1]] === "undefined") {
                currData[args[1]] = { name: args[1], active: false, url: args[2] };

                fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(currData), function (
                    err
                ) {
                    if (err) throw err;
                    console.log("complete");
                });
            } else {
                console.log(
                    "entry with this key already exist, Please use another key to add"
                );
            }
        }
    });
}

/**
 * Function will remove existing custom registry by name
 * @param  {[type]} args [description]
 */
function remove(args) {
    var currData = {};

    fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
        if (err) {
            init();
            //TODO: Create file here and call add data function
        } else {
            //Convert file data to Object
            currData = fetchFileData(data);

            if (currData[args[1]]) {
                delete currData[args[1]];

                fs.writeFile(RPATH + "/" + FILENAME, JSON.stringify(currData), function (
                    err
                ) {
                    if (err) throw err;
                    console.log("complete");
                });
            }
        }
    });
}

/**
 * Function will change any existing custom registry by name
 * @param  {[type]} args [description]
 */
function change(args) {
    npm.load(function (err) {
        if (err) return "";

        fs.readFile(RPATH + "/" + FILENAME, "utf8", function (err, data) {
            if (err) {
                init();
                //TODO: Create file here and call add data function
            } else {
                //Convert file data to Object
                var currData = fetchFileData(data);

                if (currData[args[1]]) {
                    var changeTo = currData[args[1]];

                    npm.commands.config(["set", "registry", changeTo.url], function (
                        err,
                        data
                    ) {
                        if (err) return "";
                        console.log("                        ");
                        var newR = npm.config.get("registry");
                        console.log(["", "   Registry has been set to: " + newR, ""]);
                    });
                }
            }
        });
    });
}
<|endoftext|>


<commit_before>/**
 * @providesModule InputScrollView
 * @author Junjie.Bai
 * @license MIT
 */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
    StyleSheet,
    View,
    ScrollView,
    TextInput,
    KeyboardAvoidingView,
    Keyboard,
    Platform,
    Animated
} from 'react-native';
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';

const isIOS = Platform.OS === 'ios';

let debounce;

if (isIOS) {
    debounce = function (func, wait) {
        wait = wait || 0;
        let id, count;
        let action = function (event) {
            if (count) {
                count--;
                id = requestAnimationFrame(() => action.call(this, event));
            } else {
                func.call(this, event);
            }
        };
        return function ({ ...event }) {
            cancelAnimationFrame(id);
            count = wait;
            action.call(this, event);
        };
    };
} else {
    debounce = function (func, wait) {
        wait = wait || 0;
        let id, count;
        let action = function (event) {
            if (count) {
                count--;
                id = setTimeout(() => action.call(this, event));
            } else {
                func.call(this, event);
            }
        };
        return function ({ ...event }) {
            clearTimeout(id);
            count = wait;
            action.call(this, event);
        };
    };
}

export default class extends Component {
    static propTypes = {
        keyboardOffset: PropTypes.number,
        multilineInputStyle: PropTypes.oneOfType([
            PropTypes.object,
            PropTypes.array,
            PropTypes.number,
        ]),
        useAnimatedScrollView: PropTypes.bool,
    };

    static defaultProps = {
        keyboardOffset: 40,
        multilineInputStyle: { fontSize: 17 },
        useAnimatedScrollView: false,
    };

    state = {
        measureInputVisible: false,
        measureInputValue: '',
        measureInputWidth: 0,
        contentBottomOffset: 0,
    };

    componentWillMount() {
        this._root = null;
        this._measureCallback = null;
        this._keyboardShow = false;
        this._topOffset = 0;
        this._inputInfoMap = {};

        this._addListener();
        this._extendScrollViewFunc();
    }

    componentWillUnmount() {
        this._removeListener();
    }

    render() {
        const {
            keyboardOffset,
            multilineInputStyle,
            children,
            useAnimatedScrollView,
            ...otherProps,
        } = this.props;

        const {
            measureInputVisible,
            measureInputValue,
            measureInputWidth,
            contentBottomOffset,
        } = this.state;

        const newChildren = this._cloneDeepComponents(children);

        const ScrollComponent =
            useAnimatedScrollView
                ? Animated.ScrollView
                : ScrollView;

        return (
            <KeyboardAvoidingView behavior={isIOS ? 'padding' : null}>
                <View style={styles.wrap}>
                    <ScrollComponent ref={this._onRef}
                        onMomentumScrollEnd={this._onMomentumScrollEnd}
                        onFocusCapture={this._onFocus} {...otherProps}>
                        <View style={{ marginBottom: contentBottomOffset }}
                            onStartShouldSetResponderCapture={isIOS ? this._onTouchStart : null}
                            onResponderMove={this._onTouchMove}
                            onResponderRelease={this._onTouchEnd}>
                            {newChildren}
                            <View style={styles.hidden}
                                pointerEvents="none">
                                {
                                    measureInputVisible &&
                                    <TextInput style={[multilineInputStyle, { width: measureInputWidth }]}
                                        value={measureInputValue}
                                        onContentSizeChange={this._onContentSizeChangeMeasureInput}
                                        editable={false}
                                        multiline />
                                }
                            </View>
                        </View>
                    </ScrollComponent>
                </View>
            </KeyboardAvoidingView>
        );
    }

    _addListener() {
        this._keyboardShowListener = Keyboard.addListener(isIOS ? 'keyboardWillShow' : 'keyboardDidShow', this._onKeyboardShow);
        this._keyboardHideListener = Keyboard.addListener(isIOS ? 'keyboardWillHide' : 'keyboardDidHide', this._onKeyboardHide);
    }

    _removeListener() {
        this._keyboardShowListener && this._keyboardShowListener.remove();
        this._keyboardHideListener && this._keyboardHideListener.remove();
        this._keyboardShowListener = null;
        this._keyboardHideListener = null;
    }

    _extendScrollViewFunc() {
        const funcArray = [
            'scrollTo',
            'scrollToEnd',
        ];

        funcArray.forEach(funcName => {
            this[funcName] = (...args) => {
                this._root[funcName](...args);
            };
        });
    }

    _cloneDeepComponents(Component) {
        if (isArray(Component)) {
            return Component.map(subComponent => this._cloneDeepComponents(subComponent));
        } else if (Component && Component.props && Component.props.children) {
            const newComponent = { ...Component };
            newComponent.props = { ...Component.props };
            newComponent.props.children = this._cloneDeepComponents(Component.props.children);
            return newComponent;
        } else if (Component && Component.props && Component.props.multiline) {
            const newComponent = { ...Component };
            newComponent.props = { ...Component.props };
            return this._addMultilineHandle(newComponent);
        } else {
            return Component;
        }
    }

    _addMultilineHandle(Component) {
        const onChange = Component.props.onChange;
        const onSelectionChange = Component.props.onSelectionChange;
        const onContentSizeChange = Component.props.onContentSizeChange;

        Component.props.onChange = (event) => {
            this._onChange(event);
            onChange &&
                onChange(event);
        };

        Component.props.onSelectionChange = ({ ...event }) => {
            if (isIOS) {
                // 确保处理代码在 onChange 之后执行
                // release 版本必须使用 requestAnimationFrame
                requestAnimationFrame(() => this._onSelectionChange(event));
            } else {
                setTimeout(() => this._onSelectionChange(event));
            }
            onSelectionChange &&
                onSelectionChange(event);
        };

        Component.props.onContentSizeChange = ({ ...event }) => {
            this._onContentSizeChange(event);
            onContentSizeChange &&
                onContentSizeChange(event);
        };

        return Component;
    }

    _getInputInfo(target) {
        return this._inputInfoMap[target] = this._inputInfoMap[target] || {};
    }

    _measureCursorPosition(text, width, callback) {
        this._measureCallback = callback;
        this.setState({
            measureInputVisible: true,
            measureInputValue: text,
            measureInputWidth: width,
        });
    }

    // 这里必须使用防抖函数
    // 因为在真机上，当行数增多时，每调整一次 measureInputValue 的值，onContentSizeChange 都会触发多次。
    // 如果不使用防抖函数，那么在 onContentSizeChange 第一次触发时，measureInputVisible 就会被设置为 false，导致无法获取正确的值。
    // 但在模拟器上没有这个问题。
    _onContentSizeChangeMeasureInput = debounce(({ nativeEvent: event }) => {
        if (!this._measureCallback) return;
        this._measureCallback(event.contentSize.height);
        this._measureCallback = null;
        this.setState({ measureInputVisible: false });
    }, 3);

    _onRef = root => {
        const { useAnimatedScrollView } = this.props;
        if (!root) return;
        this._root = root;

        if (useAnimatedScrollView && this._root._component) {
            this._root = this._root._component;
        };

        setTimeout(() => {
            this._root._innerViewRef.measureInWindow((x, y, width, height) => {
                this._topOffset = y;
            });
        });
    };

    _onMomentumScrollEnd = ({ nativeEvent: event }) => {
        if (!this._keyboardShow) return;
        const contentBottomOffset = Math.max(
            0,
            this.state.contentBottomOffset +
            event.layoutMeasurement.height + // layoutMeasurement 可视区域的大小
            event.contentOffset.y -
            event.contentSize.height
        );
        this.setState({ contentBottomOffset });
    };

    _scrollToKeyboardRequest = () => {
        if (!this._keyboardShow) return;

        const curFocusTarget = TextInputState.currentlyFocusedField();
        if (!curFocusTarget) return;

        const { text, selectionEnd, width, height } = this._getInputInfo(curFocusTarget);
        const cursorAtLastLine = !text ||
            selectionEnd === undefined ||
            text.length === selectionEnd;

        if (cursorAtLastLine) {
            return this._scrollToKeyboard(curFocusTarget, 0);
        }

        this._measureCursorPosition(
            text.substr(0, selectionEnd),
            width,
            cursorRelativeTopOffset => {
                this._scrollToKeyboard(
                    curFocusTarget,
                    Math.max(0, height - cursorRelativeTopOffset)
                );
            }
        );
    };

    _scrollToKeyboard = (target, offset) => {
        const toKeyboardOffset = this._topOffset + this.props.keyboardOffset - offset;

        const scrollResponder = this._getScrollResponder();

        if (!target || !scrollResponder) {
            return;
        };

        UIManager.viewIsDescendantOf(
            target,
            scrollResponder.getInnerViewNode(),
            (isAncestor) => {
                if (isAncestor) {
                    this._root.scrollResponderScrollNativeHandleToKeyboard(target, toKeyboardOffset, true);
                };
            }
        );
    };

    _getScrollResponder = () => {
        return (
            this._root &&
            this._root.getScrollResponder()
        );
    };

    _onKeyboardShow = () => {
        this._keyboardShow = true;
        this._scrollToKeyboardRequest();
    };

    _onKeyboardHide = () => {
        this._keyboardShow = false;
        let atBottom = !!this.state.contentBottomOffset;
        this.setState({ contentBottomOffset: 0 }, () => {
            if (atBottom) {
                setTimeout(() => {
                    this._root.scrollToEnd({ animated: true });
                });
            }
        });
    };

    // 这个方法是为了防止 ScrollView 在滑动结束后触发 TextInput 的 focus 事件
    _onTouchStart = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        if (target === TextInputState.currentlyFocusedField()) return false;

        const targetInst = event._targetInst;
        let uiViewClassName;
        uiViewClassName = targetInst.type || // >= react-native 0.49
            targetInst.viewConfig.uiViewClassName; // <= react-native 0.48
        return uiViewClassName === 'RCTTextField' || uiViewClassName === 'RCTTextView';
    };

    // 在单行 TextInput 中
    // onFocus 在 keyboardWillShow 与 keyboardDidShow 之前触发
    // 在多行 TextInput 中
    // onFocus 在 keyboardDidShow 之前触发
    // onFocus 在 keyboardWillShow 之后触发
    _onFocus = ({ ...event }) => {
        // 当 onStartShouldSetResponderCapture 返回 true 时
        // 被激活的 TextInput 无法使用 Keyboard.dismiss() 来收起键盘
        // TextInputState.currentlyFocusedField() 也无法获取当前焦点ID
        // 原因可能是系统并未判定 TextInput 获取焦点，这可能是一个 bug
        // 通常需要在 onStartShouldSetResponderCapture 返回 false 的情况下再点击一次 TextInput 才能恢复正常
        // 所以这里手动再设置一次焦点
        const target = event.target || event.currentTarget;
        TextInputState.focusTextInput(target);

        const inputInfo = this._getInputInfo(target);
        const multiline = getProps(event._targetInst).multiline;

        if (multiline) {
            if (inputInfo.text === undefined) {
                inputInfo.text = getProps(event._targetInst).value;
            }

            if (!isIOS) return;

            inputInfo.onFocusRequireScroll = true;
            setTimeout(() => {
                // 如果 onSelectionChange 没有触发，则在这里执行
                if (this._keyboardShow && inputInfo.onFocusRequireScroll) {
                    inputInfo.onFocusRequireScroll = false;
                    this._scrollToKeyboardRequest();
                }
            }, 250);
        } else {
            if (isIOS) this._scrollToKeyboardRequest();
        }
    };

    // onChange 在 onContentSizeChange 之前触发
    // onChange 在 onSelectionChange 之后触发
    _onChange = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.text = event.nativeEvent.text;
    }

    // onSelectionChange 在 keyboardDidShow 之前触发
    // onSelectionChange 在 onContentSizeChange 之前触发
    // onSelectionChange 在 onFocus 之后触发
    _onSelectionChange = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.selectionEnd = event.nativeEvent.selection.end;
        if (inputInfo.text === undefined) {
            inputInfo.text = getProps(event._targetInst).value;
        }

        if (!isIOS) return;

        if (inputInfo.onFocusRequireScroll) {
            inputInfo.onFocusRequireScroll = false;
            this._scrollToKeyboardRequest();
        }
    };

    // 使用防抖函数有两个目的
    // - 确保 scrollToKeyboardRequest 在 onSelectionChange 之后执行
    // - 短时间内不会重复执行 onContentSizeChange，因为当一次粘贴进许多行文本时，可能会连续触发多次 onContentSizeChange
    _onContentSizeChange = debounce(({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.width = event.nativeEvent.contentSize.width;
        inputInfo.height = event.nativeEvent.contentSize.height;
        if (inputInfo.text === undefined) {
            inputInfo.text = getProps(event._targetInst).value;
        }
        this._scrollToKeyboardRequest(true);
    }, 2);
}

function getProps(targetNode) {
    return targetNode.memoizedProps || // >= react-native 0.49
        targetNode._currentElement.props; // <= react-native 0.48
}

function isArray(arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';
}

const styles = StyleSheet.create({
    wrap: {
        height: '100%',
    },

    hidden: {
        position: 'absolute',
        top: 0,
        left: 0,
        opacity: 0,
    },
});
<commit_msg>Removing direct link dependency.<commit_after>/**
 * @providesModule InputScrollView
 * @author Junjie.Bai
 * @license MIT
 */

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
    StyleSheet,
    View,
    ScrollView,
    TextInput,
    KeyboardAvoidingView,
    Keyboard,
    Platform,
    Animated,
    UIManager
} from 'react-native';

const isIOS = Platform.OS === 'ios';

let debounce;

if (isIOS) {
    debounce = function (func, wait) {
        wait = wait || 0;
        let id, count;
        let action = function (event) {
            if (count) {
                count--;
                id = requestAnimationFrame(() => action.call(this, event));
            } else {
                func.call(this, event);
            }
        };
        return function ({ ...event }) {
            cancelAnimationFrame(id);
            count = wait;
            action.call(this, event);
        };
    };
} else {
    debounce = function (func, wait) {
        wait = wait || 0;
        let id, count;
        let action = function (event) {
            if (count) {
                count--;
                id = setTimeout(() => action.call(this, event));
            } else {
                func.call(this, event);
            }
        };
        return function ({ ...event }) {
            clearTimeout(id);
            count = wait;
            action.call(this, event);
        };
    };
}

export default class extends Component {
    static propTypes = {
        keyboardOffset: PropTypes.number,
        multilineInputStyle: PropTypes.oneOfType([
            PropTypes.object,
            PropTypes.array,
            PropTypes.number,
        ]),
        useAnimatedScrollView: PropTypes.bool,
    };

    static defaultProps = {
        keyboardOffset: 40,
        multilineInputStyle: { fontSize: 17 },
        useAnimatedScrollView: false,
    };

    state = {
        measureInputVisible: false,
        measureInputValue: '',
        measureInputWidth: 0,
        contentBottomOffset: 0,
    };

    componentWillMount() {
        this._root = null;
        this._measureCallback = null;
        this._keyboardShow = false;
        this._topOffset = 0;
        this._inputInfoMap = {};

        this._addListener();
        this._extendScrollViewFunc();
    }

    componentWillUnmount() {
        this._removeListener();
    }

    render() {
        const {
            keyboardOffset,
            multilineInputStyle,
            children,
            useAnimatedScrollView,
            ...otherProps,
        } = this.props;

        const {
            measureInputVisible,
            measureInputValue,
            measureInputWidth,
            contentBottomOffset,
        } = this.state;

        const newChildren = this._cloneDeepComponents(children);

        const ScrollComponent =
            useAnimatedScrollView
                ? Animated.ScrollView
                : ScrollView;

        return (
            <KeyboardAvoidingView behavior={isIOS ? 'padding' : null}>
                <View style={styles.wrap}>
                    <ScrollComponent ref={this._onRef}
                        onMomentumScrollEnd={this._onMomentumScrollEnd}
                        onFocusCapture={this._onFocus} {...otherProps}>
                        <View style={{ marginBottom: contentBottomOffset }}
                            onStartShouldSetResponderCapture={isIOS ? this._onTouchStart : null}
                            onResponderMove={this._onTouchMove}
                            onResponderRelease={this._onTouchEnd}>
                            {newChildren}
                            <View style={styles.hidden}
                                pointerEvents="none">
                                {
                                    measureInputVisible &&
                                    <TextInput style={[multilineInputStyle, { width: measureInputWidth }]}
                                        value={measureInputValue}
                                        onContentSizeChange={this._onContentSizeChangeMeasureInput}
                                        editable={false}
                                        multiline />
                                }
                            </View>
                        </View>
                    </ScrollComponent>
                </View>
            </KeyboardAvoidingView>
        );
    }

    _addListener() {
        this._keyboardShowListener = Keyboard.addListener(isIOS ? 'keyboardWillShow' : 'keyboardDidShow', this._onKeyboardShow);
        this._keyboardHideListener = Keyboard.addListener(isIOS ? 'keyboardWillHide' : 'keyboardDidHide', this._onKeyboardHide);
    }

    _removeListener() {
        this._keyboardShowListener && this._keyboardShowListener.remove();
        this._keyboardHideListener && this._keyboardHideListener.remove();
        this._keyboardShowListener = null;
        this._keyboardHideListener = null;
    }

    _extendScrollViewFunc() {
        const funcArray = [
            'scrollTo',
            'scrollToEnd',
        ];

        funcArray.forEach(funcName => {
            this[funcName] = (...args) => {
                this._root[funcName](...args);
            };
        });
    }

    _cloneDeepComponents(Component) {
        if (isArray(Component)) {
            return Component.map(subComponent => this._cloneDeepComponents(subComponent));
        } else if (Component && Component.props && Component.props.children) {
            const newComponent = { ...Component };
            newComponent.props = { ...Component.props };
            newComponent.props.children = this._cloneDeepComponents(Component.props.children);
            return newComponent;
        } else if (Component && Component.props && Component.props.multiline) {
            const newComponent = { ...Component };
            newComponent.props = { ...Component.props };
            return this._addMultilineHandle(newComponent);
        } else {
            return Component;
        }
    }

    _addMultilineHandle(Component) {
        const onChange = Component.props.onChange;
        const onSelectionChange = Component.props.onSelectionChange;
        const onContentSizeChange = Component.props.onContentSizeChange;

        Component.props.onChange = (event) => {
            this._onChange(event);
            onChange &&
                onChange(event);
        };

        Component.props.onSelectionChange = ({ ...event }) => {
            if (isIOS) {
                // 确保处理代码在 onChange 之后执行
                // release 版本必须使用 requestAnimationFrame
                requestAnimationFrame(() => this._onSelectionChange(event));
            } else {
                setTimeout(() => this._onSelectionChange(event));
            }
            onSelectionChange &&
                onSelectionChange(event);
        };

        Component.props.onContentSizeChange = ({ ...event }) => {
            this._onContentSizeChange(event);
            onContentSizeChange &&
                onContentSizeChange(event);
        };

        return Component;
    }

    _getInputInfo(target) {
        return this._inputInfoMap[target] = this._inputInfoMap[target] || {};
    }

    _measureCursorPosition(text, width, callback) {
        this._measureCallback = callback;
        this.setState({
            measureInputVisible: true,
            measureInputValue: text,
            measureInputWidth: width,
        });
    }

    // 这里必须使用防抖函数
    // 因为在真机上，当行数增多时，每调整一次 measureInputValue 的值，onContentSizeChange 都会触发多次。
    // 如果不使用防抖函数，那么在 onContentSizeChange 第一次触发时，measureInputVisible 就会被设置为 false，导致无法获取正确的值。
    // 但在模拟器上没有这个问题。
    _onContentSizeChangeMeasureInput = debounce(({ nativeEvent: event }) => {
        if (!this._measureCallback) return;
        this._measureCallback(event.contentSize.height);
        this._measureCallback = null;
        this.setState({ measureInputVisible: false });
    }, 3);

    _onRef = root => {
        const { useAnimatedScrollView } = this.props;
        if (!root) return;
        this._root = root;

        if (useAnimatedScrollView && this._root._component) {
            this._root = this._root._component;
        };

        setTimeout(() => {
            this._root._innerViewRef.measureInWindow((x, y, width, height) => {
                this._topOffset = y;
            });
        });
    };

    _onMomentumScrollEnd = ({ nativeEvent: event }) => {
        if (!this._keyboardShow) return;
        const contentBottomOffset = Math.max(
            0,
            this.state.contentBottomOffset +
            event.layoutMeasurement.height + // layoutMeasurement 可视区域的大小
            event.contentOffset.y -
            event.contentSize.height
        );
        this.setState({ contentBottomOffset });
    };

    _scrollToKeyboardRequest = () => {
        if (!this._keyboardShow) return;

        const curFocusTarget = TextInput.State.currentlyFocusedField();
        if (!curFocusTarget) return;

        const { text, selectionEnd, width, height } = this._getInputInfo(curFocusTarget);
        const cursorAtLastLine = !text ||
            selectionEnd === undefined ||
            text.length === selectionEnd;

        if (cursorAtLastLine) {
            return this._scrollToKeyboard(curFocusTarget, 0);
        }

        this._measureCursorPosition(
            text.substr(0, selectionEnd),
            width,
            cursorRelativeTopOffset => {
                this._scrollToKeyboard(
                    curFocusTarget,
                    Math.max(0, height - cursorRelativeTopOffset)
                );
            }
        );
    };

    _scrollToKeyboard = (target, offset) => {
        const toKeyboardOffset = this._topOffset + this.props.keyboardOffset - offset;

        const scrollResponder = this._getScrollResponder();

        if (!target || !scrollResponder) {
            return;
        };

        UIManager.viewIsDescendantOf(
            target,
            scrollResponder.getInnerViewNode(),
            (isAncestor) => {
                if (isAncestor) {
                    this._root.scrollResponderScrollNativeHandleToKeyboard(target, toKeyboardOffset, true);
                };
            }
        );
    };

    _getScrollResponder = () => {
        return (
            this._root &&
            this._root.getScrollResponder()
        );
    };

    _onKeyboardShow = () => {
        this._keyboardShow = true;
        this._scrollToKeyboardRequest();
    };

    _onKeyboardHide = () => {
        this._keyboardShow = false;
        let atBottom = !!this.state.contentBottomOffset;
        this.setState({ contentBottomOffset: 0 }, () => {
            if (atBottom) {
                setTimeout(() => {
                    this._root.scrollToEnd({ animated: true });
                });
            }
        });
    };

    // 这个方法是为了防止 ScrollView 在滑动结束后触发 TextInput 的 focus 事件
    _onTouchStart = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        if (target === TextInput.State.currentlyFocusedField()) return false;

        const targetInst = event._targetInst;
        let uiViewClassName;
        uiViewClassName = targetInst.type || // >= react-native 0.49
            targetInst.viewConfig.uiViewClassName; // <= react-native 0.48
        return uiViewClassName === 'RCTTextField' || uiViewClassName === 'RCTTextView';
    };

    // 在单行 TextInput 中
    // onFocus 在 keyboardWillShow 与 keyboardDidShow 之前触发
    // 在多行 TextInput 中
    // onFocus 在 keyboardDidShow 之前触发
    // onFocus 在 keyboardWillShow 之后触发
    _onFocus = ({ ...event }) => {
        // 当 onStartShouldSetResponderCapture 返回 true 时
        // 被激活的 TextInput 无法使用 Keyboard.dismiss() 来收起键盘
        // TextInput.State.currentlyFocusedField() 也无法获取当前焦点ID
        // 原因可能是系统并未判定 TextInput 获取焦点，这可能是一个 bug
        // 通常需要在 onStartShouldSetResponderCapture 返回 false 的情况下再点击一次 TextInput 才能恢复正常
        // 所以这里手动再设置一次焦点
        const target = event.target || event.currentTarget;
        TextInput.State.focusTextInput(target);

        const inputInfo = this._getInputInfo(target);
        const multiline = getProps(event._targetInst).multiline;

        if (multiline) {
            if (inputInfo.text === undefined) {
                inputInfo.text = getProps(event._targetInst).value;
            }

            if (!isIOS) return;

            inputInfo.onFocusRequireScroll = true;
            setTimeout(() => {
                // 如果 onSelectionChange 没有触发，则在这里执行
                if (this._keyboardShow && inputInfo.onFocusRequireScroll) {
                    inputInfo.onFocusRequireScroll = false;
                    this._scrollToKeyboardRequest();
                }
            }, 250);
        } else {
            if (isIOS) this._scrollToKeyboardRequest();
        }
    };

    // onChange 在 onContentSizeChange 之前触发
    // onChange 在 onSelectionChange 之后触发
    _onChange = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.text = event.nativeEvent.text;
    }

    // onSelectionChange 在 keyboardDidShow 之前触发
    // onSelectionChange 在 onContentSizeChange 之前触发
    // onSelectionChange 在 onFocus 之后触发
    _onSelectionChange = ({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.selectionEnd = event.nativeEvent.selection.end;
        if (inputInfo.text === undefined) {
            inputInfo.text = getProps(event._targetInst).value;
        }

        if (!isIOS) return;

        if (inputInfo.onFocusRequireScroll) {
            inputInfo.onFocusRequireScroll = false;
            this._scrollToKeyboardRequest();
        }
    };

    // 使用防抖函数有两个目的
    // - 确保 scrollToKeyboardRequest 在 onSelectionChange 之后执行
    // - 短时间内不会重复执行 onContentSizeChange，因为当一次粘贴进许多行文本时，可能会连续触发多次 onContentSizeChange
    _onContentSizeChange = debounce(({ ...event }) => {
        const target = event.target || event.currentTarget;
        const inputInfo = this._getInputInfo(target);
        inputInfo.width = event.nativeEvent.contentSize.width;
        inputInfo.height = event.nativeEvent.contentSize.height;
        if (inputInfo.text === undefined) {
            inputInfo.text = getProps(event._targetInst).value;
        }
        this._scrollToKeyboardRequest(true);
    }, 2);
}

function getProps(targetNode) {
    return targetNode.memoizedProps || // >= react-native 0.49
        targetNode._currentElement.props; // <= react-native 0.48
}

function isArray(arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';
}

const styles = StyleSheet.create({
    wrap: {
        height: '100%',
    },

    hidden: {
        position: 'absolute',
        top: 0,
        left: 0,
        opacity: 0,
    },
});
<|endoftext|>


<commit_before>'use strict';
const path = require('path');
const childProcess = require('child_process');
const pify = require('pify');

const bin = path.join(__dirname, 'main');
const xprop = 'xprop -id $(xprop -root 32x \'\\t$0\' _NET_ACTIVE_WINDOW | cut -f 2)';

function parse(stdout) {
	if (process.platform === 'darwin') {
		const parts = stdout.trim().split('\n');

		return {
			title: parts[0] || null,
			id: Number(parts[1]),
			app: parts[2],
			pid: Number(parts[3])
		};
	} else if (process.platform === 'linux') {
		const result = {};

		for (const row of stdout.trim().split('\n')) {
			if (row.includes('=')) {
				const parts = row.split('=');
				result[parts[0].trim()] = parts[1].trim();
			} else if (row.includes(':')) {
				const parts = row.split(':');
				result[parts[0].trim()] = parts[1].trim();
			}
		}

		return {
			title: JSON.parse(result['_NET_WM_NAME(UTF8_STRING)']) || null,
			id: parseInt(result['WM_CLIENT_LEADER(WINDOW)'].split('#').pop(), 16),
			app: JSON.parse(result['WM_CLASS(STRING)'].split(',').pop()),
			pid: parseInt(result['_NET_WM_PID(CARDINAL)'], 10)
		};
	}

	throw new Error('macOS and Linux only');
}

module.exports = () => {
	if (process.platform === 'darwin') {
		return pify(childProcess.execFile)(bin).then(parse);
	} else if (process.platform === 'linux') {
		return pify(childProcess.exec)(xprop).then(parse);
	}
	return Promise.reject(new Error('macOS and Linux only'));
};

module.exports.sync = () => {
	if (process.platform === 'darwin') {
		return parse(childProcess.execFileSync(bin, {encoding: 'utf8'}));
	} else if (process.platform === 'linux') {
		return parse(childProcess.execSync(xprop, {encoding: 'utf8'}));
	}

	throw new Error('macOS and Linux only');
};
<commit_msg>handle `title` not always being available<commit_after>'use strict';
const path = require('path');
const childProcess = require('child_process');
const pify = require('pify');

const bin = path.join(__dirname, 'main');
const xprop = 'xprop -id $(xprop -root 32x \'\\t$0\' _NET_ACTIVE_WINDOW | cut -f 2)';

function parse(stdout) {
	if (process.platform === 'darwin') {
		const parts = stdout.trimRight().split('\n');

		return {
			title: parts[0] || null,
			id: Number(parts[1]),
			app: parts[2],
			pid: Number(parts[3])
		};
	} else if (process.platform === 'linux') {
		const result = {};

		for (const row of stdout.trim().split('\n')) {
			if (row.includes('=')) {
				const parts = row.split('=');
				result[parts[0].trim()] = parts[1].trim();
			} else if (row.includes(':')) {
				const parts = row.split(':');
				result[parts[0].trim()] = parts[1].trim();
			}
		}

		return {
			title: JSON.parse(result['_NET_WM_NAME(UTF8_STRING)']) || null,
			id: parseInt(result['WM_CLIENT_LEADER(WINDOW)'].split('#').pop(), 16),
			app: JSON.parse(result['WM_CLASS(STRING)'].split(',').pop()),
			pid: parseInt(result['_NET_WM_PID(CARDINAL)'], 10)
		};
	}

	throw new Error('macOS and Linux only');
}

module.exports = () => {
	if (process.platform === 'darwin') {
		return pify(childProcess.execFile)(bin).then(parse);
	} else if (process.platform === 'linux') {
		return pify(childProcess.exec)(xprop).then(parse);
	}
	return Promise.reject(new Error('macOS and Linux only'));
};

module.exports.sync = () => {
	if (process.platform === 'darwin') {
		return parse(childProcess.execFileSync(bin, {encoding: 'utf8'}));
	} else if (process.platform === 'linux') {
		return parse(childProcess.execSync(xprop, {encoding: 'utf8'}));
	}

	throw new Error('macOS and Linux only');
};
<|endoftext|>


<commit_before>'use strict';

const EventEmitter = require('events');
const { fork } = require('child_process');
const path = require('path');
const fs = require('fs');
const ImageCompare = require('./lib/ImageCompare');

class MotionDetectionModule extends EventEmitter {
  constructor(options) {
    super();
    this.config = Object.assign({
      captureDirectory: null, // Directory to store tmp photos and video captures
      continueAfterMotion: false, // Flag to control if motion detection will continue after detection
      captureVideoOnMotion: false, // Flag to control video capture on motion detection
    }, options);

    this.continueToCapture = true; // Flag to control internal state of photo capture
    this.capturingPhoto = false; // State of the module capturing photos

    // Verify and create (if needed) capture directories
    captureDirsCheck(this.config.captureDirectory);
    emptyImagesDir(path.resolve(this.config.captureDirectory, 'images'));
  }

  watch() {
    const self = this;
    const imageCaptureChild = fork(path.resolve(__dirname, 'lib', 'ImageCapture.js'), [ path.resolve(self.config.captureDirectory, 'images', '%d.jpg') ]);
    const videoCaptureChild = fork(path.resolve(__dirname, 'lib', 'VideoCapture.js'), [ path.resolve(self.config.captureDirectory, 'videos', 'video.h264') ]);
    const videoRenameChild = fork(path.resolve(__dirname, 'lib', 'VideoRename.js'), [ path.resolve(self.config.captureDirectory, 'videos') ]);
    const imageCompare = new ImageCompare(path.resolve(self.config.captureDirectory, 'images'));

    imageCaptureChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        console.log('Captured photo');
        if (self.continueToCapture) {
          self.capturingPhoto = true;
          imageCaptureChild.send({});
        }
      }
      else {
        console.log(`Message from imageCaptureChild: ${ message }`);
      }
    });

    imageCompare.on('motion', () => {
      self.capturingPhoto = false;
      self.continueToCapture = false;
      self.emit('motion');

      if (self.config.captureVideoOnMotion) {
        if (self.capturingPhoto) {
          self.emit('error', 'Hit possible race condition, not capturing video at this time.');
        }
        else {
          console.log('It should be safe to capture video');
          videoCaptureChild.send({});
        }
      }
    });
    imageCompare.on('error', (error) => {
      self.emit('error', error);
    });

    videoCaptureChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        self.continueToCapture = true;
        imageCaptureChild.send({});
      }
      else {
        console.log(`Message from videoCaptureChild: ${ message }`);
      }
    });

    videoRenameChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        // I don't think this ever gets hit...
      }
      else {
        console.log(`Message from videoCaptureChild: ${ message }`);
      }
    });

    // Start the magic
    imageCompare.start();
    self.capturingPhoto = true;
    imageCaptureChild.send({});
  }
}

function captureDirsCheck(base) {
  if (!base) {
    throw new Error(`'captureDirectory' can't be null`);
  }

  const dirsToCheck = [ base, path.resolve(base, 'images'), path.resolve(base, 'videos') ];

  dirsToCheck.forEach((dir) => {
    try {
      fs.accessSync(dir);
    }
    catch (e) {
      // Doesn't exist, create it
      fs.mkdirSync(dir);
    }
  });
}

function emptyImagesDir(imagesDir) {
  const files = fs.readdirSync(imagesDir);

  files.forEach((file) => {
    fs.unlink(path.resolve(imagesDir, file), (error) => {
      if (error) {
        throw error;
      }
    });
  });
}

module.exports = MotionDetectionModule;
<commit_msg>Comment console.log calls<commit_after>'use strict';

const EventEmitter = require('events');
const { fork } = require('child_process');
const path = require('path');
const fs = require('fs');
const ImageCompare = require('./lib/ImageCompare');

class MotionDetectionModule extends EventEmitter {
  constructor(options) {
    super();
    this.config = Object.assign({
      captureDirectory: null, // Directory to store tmp photos and video captures
      continueAfterMotion: false, // Flag to control if motion detection will continue after detection
      captureVideoOnMotion: false, // Flag to control video capture on motion detection
    }, options);

    this.continueToCapture = true; // Flag to control internal state of photo capture
    this.capturingPhoto = false; // State of the module capturing photos

    // Verify and create (if needed) capture directories
    captureDirsCheck(this.config.captureDirectory);
    emptyImagesDir(path.resolve(this.config.captureDirectory, 'images'));
  }

  watch() {
    const self = this;
    const imageCaptureChild = fork(path.resolve(__dirname, 'lib', 'ImageCapture.js'), [ path.resolve(self.config.captureDirectory, 'images', '%d.jpg') ]);
    const videoCaptureChild = fork(path.resolve(__dirname, 'lib', 'VideoCapture.js'), [ path.resolve(self.config.captureDirectory, 'videos', 'video.h264') ]);
    const videoRenameChild = fork(path.resolve(__dirname, 'lib', 'VideoRename.js'), [ path.resolve(self.config.captureDirectory, 'videos') ]);
    const imageCompare = new ImageCompare(path.resolve(self.config.captureDirectory, 'images'));

    imageCaptureChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        // console.log('Captured photo');
        if (self.continueToCapture) {
          self.capturingPhoto = true;
          imageCaptureChild.send({});
        }
      }
      else {
        console.log(`Message from imageCaptureChild: ${ message }`);
      }
    });

    imageCompare.on('motion', () => {
      self.capturingPhoto = false;
      self.continueToCapture = false;
      self.emit('motion');

      if (self.config.captureVideoOnMotion) {
        if (self.capturingPhoto) {
          self.emit('error', 'Hit possible race condition, not capturing video at this time.');
        }
        else {
          // console.log('It should be safe to capture video');
          videoCaptureChild.send({});
        }
      }
    });
    imageCompare.on('error', (error) => {
      self.emit('error', error);
    });

    videoCaptureChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        self.continueToCapture = true;
        imageCaptureChild.send({});
      }
      else {
        // console.log(`Message from videoCaptureChild: ${ message }`);
      }
    });

    videoRenameChild.on('message', (message) => {
      if (message.result === 'failure') {
        self.emit('error', message.error);
      }
      else if (message.result === 'success') {
        // I don't think this ever gets hit...
      }
      else {
        // console.log(`Message from videoCaptureChild: ${ message }`);
      }
    });

    // Start the magic
    imageCompare.start();
    self.capturingPhoto = true;
    imageCaptureChild.send({});
  }
}

function captureDirsCheck(base) {
  if (!base) {
    throw new Error(`'captureDirectory' can't be null`);
  }

  const dirsToCheck = [ base, path.resolve(base, 'images'), path.resolve(base, 'videos') ];

  dirsToCheck.forEach((dir) => {
    try {
      fs.accessSync(dir);
    }
    catch (e) {
      // Doesn't exist, create it
      fs.mkdirSync(dir);
    }
  });
}

function emptyImagesDir(imagesDir) {
  const files = fs.readdirSync(imagesDir);

  files.forEach((file) => {
    fs.unlink(path.resolve(imagesDir, file), (error) => {
      if (error) {
        throw error;
      }
    });
  });
}

module.exports = MotionDetectionModule;
<|endoftext|>


<commit_before><commit_msg>Fix offset error with rotation<commit_after><|endoftext|>