<commit_before>/*! wordfreq - Text corpus calculation in Javascript.

  Author: timdream <http://timc.idv.tw/>

  This file contains the following library (unmodified but minified):
  http://tartarus.org/~martin/PorterStemmer/js.txt
  // Porter stemmer in Javascript
  // Release 1 be 'andargor', Jul 2004
  // Release 2 (substantially revised) by Christopher McKenzie, Aug 2009

*/

'use strict';

(function (global) {

// http://tartarus.org/~martin/PorterStemmer/js.txt
// Porter stemmer in Javascript
// Release 1 be 'andargor', Jul 2004
// Release 2 (substantially revised) by Christopher McKenzie, Aug 2009
var stemmer=function(){var g={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},h={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""};return function(a){var d,b,e,c,f;if(a.length<3)return a;e=a.substr(0,1);if(e=="y")a=e.toUpperCase()+a.substr(1);c=/^(.+?)(ss|i)es$/;b=/^(.+?)([^s])s$/; if(c.test(a))a=a.replace(c,"$1$2");else if(b.test(a))a=a.replace(b,"$1$2");c=/^(.+?)eed$/;b=/^(.+?)(ed|ing)$/;if(c.test(a)){b=c.exec(a);c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(b[1])){c=/.$/;a=a.replace(c,"")}}else if(b.test(a)){b=b.exec(a);d=b[1];b=/^([^aeiou][^aeiouy]*)?[aeiouy]/;if(b.test(d)){a=d;b=/(at|bl|iz)$/;f=/([^aeiouylsz])\1$/;d=/^[^aeiou][^aeiouy]*[aeiouy][^aeiouwxy]$/;if(b.test(a))a+="e";else if(f.test(a)){c=/.$/;a=a.replace(c,"")}else if(d.test(a))a+="e"}}c= /^(.+?)y$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy]/;if(c.test(d))a=d+"i"}c=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;if(c.test(a)){b=c.exec(a);d=b[1];b=b[2];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(d))a=d+g[b]}c=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;if(c.test(a)){b=c.exec(a);d=b[1];b=b[2];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/; if(c.test(d))a=d+h[b]}c=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;b=/^(.+?)(s|t)(ion)$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(d))a=d}else if(b.test(a)){b=b.exec(a);d=b[1]+b[2];b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(b.test(d))a=d}c=/^(.+?)e$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/; b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*([aeiouy][aeiou]*)?$/;f=/^[^aeiou][^aeiouy]*[aeiouy][^aeiouwxy]$/;if(c.test(d)||b.test(d)&&!f.test(d))a=d}c=/ll$/;b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(a)&&b.test(a)){c=/.$/;a=a.replace(c,"")}if(e=="y")a=e.toLowerCase()+a.substr(1);return a}}();

var WordFreqSync = function WordFreqSync(options) {
  var list = [];
  var terms = Object.create(null);

  options = options || {};

  // fill some defaults
  options.languages = options.languages || ['chinese', 'english'];
  options.stopWordSets = options.stopWordSets || ['cjk', 'english1', 'english2'];

  if (!Array.isArray(options.stopWords)) {
    options.stopWords = [];
  }

  options.stopWordSets.forEach(function (stopWordSet) {
    switch (stopWordSet) {
      case 'cjk':
        options.stopWords = options.stopWords.concat([
          '\u3092', /* Japanese wo */
          '\u3067\u3059', /* Japanese desu */
          '\u3059\u308b', /* Japanese suru */
          '\u306e', /* Japanese no */
          '\u308c\u3089' /* Japanese rera */]);

        break;

      case 'english1':
        options.stopWords = options.stopWords.concat([
          'i','a','about', 'an','and','are','as','at',
          'be', 'been','by','com','for', 'from','how','in',
          'is','it','not', 'of','on','or','that',
          'the','this','to','was', 'what','when','where', 'which',
          'who','will','with', 'www','the']);
        break;

      case 'english2':
        options.stopWords = options.stopWords.concat([
          'we', 'us', 'our', 'ours',
          'they', 'them', 'their', 'he', 'him', 'his',
          'she', 'her', 'hers', 'it', 'its', 'you', 'yours', 'your',
          'has', 'have', 'would', 'could', 'should', 'shall',
          'can', 'may', 'if', 'then', 'else', 'but',
          'there', 'these', 'those']);
        break;
    }
  });

  if (typeof options.filterSubstring !== 'boolean')
    options.filterSubstring = true;

  options.maxiumPhraseLength = options.maxiumPhraseLength || 8;
  options.minimumCount = options.minimumCount || 2;

  // Return all possible substrings of a give string in an array
  // If there is no maxLength is unrestricted, array will contain
  // (2 * str.length) substrings.
  var getAllSubStrings = function getAllSubStrings(str, maxLength) {
    if (!str.length)
      return [];

    var result = [];
    var i = Math.min(str.length, maxLength);
    do {
      result.push(str.substr(0,i));
    } while (--i);

    if (str.length > 1)
      result = result.concat(getAllSubStrings(str.substr(1), maxLength));

    return result;
  }

  return {
    process: function process(text) {
      if (typeof text !== 'string')
        throw 'You need to supply text for processing.'

      // English
      if (options.languages.indexOf('english') !== -1) {
        // For English, we count "stems" instead of words,
        // and decide how to represent that stem at the end
        // according to the counts.
        var stems = Object.create(null);

        // say bye bye to characters that is not belongs to a word
        var words = text.split(/[^A-Za-zéÉ'’_\-0-9@\.]+/);

        var stopWords = options.stopWords;

        words.forEach(function (word) {
          word = word
            .replace(/\.+/g, '.') // replace multiple full stops
            .replace(/(.{3,})\./g, '$1') // replace single full stop
            .replace(/n[\'’]t\b/ig, '') // get rid of ~n't
            .replace(/[\'’](s|ll|d|ve)?\b/ig, ''); // get rid of ’ and '

          // skip if the word is shorter than two characters
          // (i.e. exactly one letter)
          if (!word || word.length < 2)
            return;

          // that's not a word unless it contains at least an alphabet
					if (/^[0-9\.@\-]+$/.test(word))
					  return;

          // skip if this is a stop word
          if (stopWords.indexOf(word.toLowerCase()) !== -1)
            return;

          var stem = stemmer(word).toLowerCase();

          // count++ for the stem
          if (!(stem in stems))
            stems[stem] = { count: 0, word: word};
          stems[stem].count++;

          // if the current word representing the stem is longer than
          // this one, use this word instead (booking -> book)
          if (word.length < stems[stem].word.length)
            stems[stem].word = word;

          // if the current word representing the stem is of the same
          // length but with different form,
          // use the lower-case representation (Book -> book)
          if (word.length === stems[stem].word.length &&
              word !== stems[stem].word)
            stems[stem].word = word.toLowerCase();
        });

        // Push each "stem" into terms as word
        for (var stem in stems) {
          var term = stems[stem].word;
          if (!(term in terms)) {
            terms[term] = stems[stem].count;
          } else {
            terms[term] += stems[stem].count;
          }
        }

        stems = undefined;
      }

      // Chinese
      if (options.languages.indexOf('chinese') !== -1) {
        // Chinese is a language without word boundary.
        // We must use N-gram here to extract meaningful terms.

        // say good bye to non-Chinese (Kanji) characters
        // TBD: Cannot match CJK characters beyond BMP,
        // e.g. \u20000-\u2A6DF at plane B.

        // Han: \u4E00-\u9FFF\u3400-\u4DBF
        // Kana: \u3041-\u309f\u30a0-\u30ff
        var regexp = /[^\u4E00-\u9FFF\u3400-\u4DBF]+/g;
        text = text.replace(regexp, '\n');

        // Use the stop words as separators -- replace them.
        options.stopWords.forEach(function (stopWord) {
          // Not handling that stop word if it's not a Chinese word.
          if (!(/^[\u4E00-\u9FFF\u3400-\u4DBF]+$/).test(stopWord))
            return;

          text = text.replace(new RegExp(stopWord, 'g'), '$1\n');
        });

        var chunks = text.split(/\n+/);

        var pendingTerms = Object.create(null);

        // counts all the chunks (and it's substrings) in pendingTerms
        chunks.forEach(function processChunk(chunk) {
          if (chunk.length <= 1)
            return;

          var substrings = getAllSubStrings(chunk, options.maxiumPhraseLength);
          substrings.forEach(function (substr) {
            if (substr.length <= 1)
              return;

            if (!(substr in pendingTerms))
              pendingTerms[substr] = 0;

            pendingTerms[substr]++;
          });
        });

        // if filterSubstring is true, remove the substrings with the exact
        // same count as the longer term (implying they are only present in
        // the longer terms)
        if (options.filterSubstring) {
          for (var term in pendingTerms) {
            var substrings = getAllSubStrings(term, options.maxiumPhraseLength);
            substrings.forEach(function (substr) {
              if (term === substr)
                return;

              if ((substr in pendingTerms) &&
                  pendingTerms[substr] === pendingTerms[term]) {
                delete pendingTerms[substr];
              }
            });
          }
        }

        // add the pendingTerms into terms
        for (var term in pendingTerms) {
          if (!(term in terms))
            terms[term] = 0;

          terms[term] += pendingTerms[term];
        }

        pendingTerms = undefined;
      }

      // Update the list
      list = [];
      for (var term in terms) {
        if (terms[term] < options.minimumCount)
          continue;

        list.push([term, terms[term]]);
      }
      list = list.sort(function (a, b) {
        if (a[1] > b[1]) return -1;
        if (a[1] < b[1]) return 1;
        if (a[0] === b[0])
          return 0;
        var t = ([a[0], b[0]]).sort();
        if (t[0] !== a[0]) return 1;
        return -1;
			});

      return list;
    },

    empty: function empty() {
      list = [];
      terms = Object.create(null);
      return true;
    },

    getList: function getList() {
      return list;
    },

    getLength: function getLength() {
      return list.length;
    },

    getVolume: function getVolume() {
      var volume = 0;
      list.forEach(function eachListItem(item) {
        volume += item[0].length * Math.pow(item[1], 2);
      });

      return volume;
    },

    stop: function stop() { /* Nothing to do. */ }
  };
};

WordFreqSync.isSupported = !!(global.Worker &&
  Array.prototype.push &&
  Array.prototype.indexOf &&
  Array.prototype.forEach &&
  Array.isArray &&
  Object.create);

if (typeof define === 'function' && define.amd) {
  // Expose the library as an AMD module
  define('wordfreqsync', [], function() { return WordFreqSync; });
} else {
  // expose WordFreqSync as a global interface.
  global.WordFreqSync = WordFreqSync;

  /* If those conditions are met, assume we are in Web Workers.
   * Web Workers script that importScripts() us must have their onmessage
   * handler set-up first.
   * 1. the global object must have a self property that is assigned to itself.
   * 2. the global object must NOT have an window property that is assigned to
   *    itself.
   */
  if (('self' in global) && (global === self) &&
      !(('window' in global) && window === global) &&
      !self.onmessage && self.postMessage && self.importScripts) {
    var wordfreqsync;
    self.onmessage = function gotMessage(evt) {
      var msg = evt.data;
      var method = msg.method;
      var params = msg.params;

      if (method === 'init') {
        wordfreqsync = WordFreqSync.apply(global, params);
        self.postMessage(true);
        return;
      }

      if (!wordfreqsync)
        throw 'You must init to create an instance first';

      if (!method || !wordfreqsync[method])
        throw 'No such method';

      var result = wordfreqsync[method].apply(wordfreqsync, params);
      self.postMessage(result);
    };
  }
}

})(this);
<commit_msg>WordFreqSync does not depend on Web Workers<commit_after>/*! wordfreq - Text corpus calculation in Javascript.

  Author: timdream <http://timc.idv.tw/>

  This file contains the following library (unmodified but minified):
  http://tartarus.org/~martin/PorterStemmer/js.txt
  // Porter stemmer in Javascript
  // Release 1 be 'andargor', Jul 2004
  // Release 2 (substantially revised) by Christopher McKenzie, Aug 2009

*/

'use strict';

(function (global) {

// http://tartarus.org/~martin/PorterStemmer/js.txt
// Porter stemmer in Javascript
// Release 1 be 'andargor', Jul 2004
// Release 2 (substantially revised) by Christopher McKenzie, Aug 2009
var stemmer=function(){var g={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},h={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""};return function(a){var d,b,e,c,f;if(a.length<3)return a;e=a.substr(0,1);if(e=="y")a=e.toUpperCase()+a.substr(1);c=/^(.+?)(ss|i)es$/;b=/^(.+?)([^s])s$/; if(c.test(a))a=a.replace(c,"$1$2");else if(b.test(a))a=a.replace(b,"$1$2");c=/^(.+?)eed$/;b=/^(.+?)(ed|ing)$/;if(c.test(a)){b=c.exec(a);c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(b[1])){c=/.$/;a=a.replace(c,"")}}else if(b.test(a)){b=b.exec(a);d=b[1];b=/^([^aeiou][^aeiouy]*)?[aeiouy]/;if(b.test(d)){a=d;b=/(at|bl|iz)$/;f=/([^aeiouylsz])\1$/;d=/^[^aeiou][^aeiouy]*[aeiouy][^aeiouwxy]$/;if(b.test(a))a+="e";else if(f.test(a)){c=/.$/;a=a.replace(c,"")}else if(d.test(a))a+="e"}}c= /^(.+?)y$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy]/;if(c.test(d))a=d+"i"}c=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;if(c.test(a)){b=c.exec(a);d=b[1];b=b[2];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(d))a=d+g[b]}c=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;if(c.test(a)){b=c.exec(a);d=b[1];b=b[2];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*/; if(c.test(d))a=d+h[b]}c=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;b=/^(.+?)(s|t)(ion)$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(d))a=d}else if(b.test(a)){b=b.exec(a);d=b[1]+b[2];b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(b.test(d))a=d}c=/^(.+?)e$/;if(c.test(a)){b=c.exec(a);d=b[1];c=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/; b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*([aeiouy][aeiou]*)?$/;f=/^[^aeiou][^aeiouy]*[aeiouy][^aeiouwxy]$/;if(c.test(d)||b.test(d)&&!f.test(d))a=d}c=/ll$/;b=/^([^aeiou][^aeiouy]*)?[aeiouy][aeiou]*[^aeiou][^aeiouy]*[aeiouy][aeiou]*[^aeiou][^aeiouy]*/;if(c.test(a)&&b.test(a)){c=/.$/;a=a.replace(c,"")}if(e=="y")a=e.toLowerCase()+a.substr(1);return a}}();

var WordFreqSync = function WordFreqSync(options) {
  var list = [];
  var terms = Object.create(null);

  options = options || {};

  // fill some defaults
  options.languages = options.languages || ['chinese', 'english'];
  options.stopWordSets = options.stopWordSets || ['cjk', 'english1', 'english2'];

  if (!Array.isArray(options.stopWords)) {
    options.stopWords = [];
  }

  options.stopWordSets.forEach(function (stopWordSet) {
    switch (stopWordSet) {
      case 'cjk':
        options.stopWords = options.stopWords.concat([
          '\u3092', /* Japanese wo */
          '\u3067\u3059', /* Japanese desu */
          '\u3059\u308b', /* Japanese suru */
          '\u306e', /* Japanese no */
          '\u308c\u3089' /* Japanese rera */]);

        break;

      case 'english1':
        options.stopWords = options.stopWords.concat([
          'i','a','about', 'an','and','are','as','at',
          'be', 'been','by','com','for', 'from','how','in',
          'is','it','not', 'of','on','or','that',
          'the','this','to','was', 'what','when','where', 'which',
          'who','will','with', 'www','the']);
        break;

      case 'english2':
        options.stopWords = options.stopWords.concat([
          'we', 'us', 'our', 'ours',
          'they', 'them', 'their', 'he', 'him', 'his',
          'she', 'her', 'hers', 'it', 'its', 'you', 'yours', 'your',
          'has', 'have', 'would', 'could', 'should', 'shall',
          'can', 'may', 'if', 'then', 'else', 'but',
          'there', 'these', 'those']);
        break;
    }
  });

  if (typeof options.filterSubstring !== 'boolean')
    options.filterSubstring = true;

  options.maxiumPhraseLength = options.maxiumPhraseLength || 8;
  options.minimumCount = options.minimumCount || 2;

  // Return all possible substrings of a give string in an array
  // If there is no maxLength is unrestricted, array will contain
  // (2 * str.length) substrings.
  var getAllSubStrings = function getAllSubStrings(str, maxLength) {
    if (!str.length)
      return [];

    var result = [];
    var i = Math.min(str.length, maxLength);
    do {
      result.push(str.substr(0,i));
    } while (--i);

    if (str.length > 1)
      result = result.concat(getAllSubStrings(str.substr(1), maxLength));

    return result;
  }

  return {
    process: function process(text) {
      if (typeof text !== 'string')
        throw 'You need to supply text for processing.'

      // English
      if (options.languages.indexOf('english') !== -1) {
        // For English, we count "stems" instead of words,
        // and decide how to represent that stem at the end
        // according to the counts.
        var stems = Object.create(null);

        // say bye bye to characters that is not belongs to a word
        var words = text.split(/[^A-Za-zéÉ'’_\-0-9@\.]+/);

        var stopWords = options.stopWords;

        words.forEach(function (word) {
          word = word
            .replace(/\.+/g, '.') // replace multiple full stops
            .replace(/(.{3,})\./g, '$1') // replace single full stop
            .replace(/n[\'’]t\b/ig, '') // get rid of ~n't
            .replace(/[\'’](s|ll|d|ve)?\b/ig, ''); // get rid of ’ and '

          // skip if the word is shorter than two characters
          // (i.e. exactly one letter)
          if (!word || word.length < 2)
            return;

          // that's not a word unless it contains at least an alphabet
					if (/^[0-9\.@\-]+$/.test(word))
					  return;

          // skip if this is a stop word
          if (stopWords.indexOf(word.toLowerCase()) !== -1)
            return;

          var stem = stemmer(word).toLowerCase();

          // count++ for the stem
          if (!(stem in stems))
            stems[stem] = { count: 0, word: word};
          stems[stem].count++;

          // if the current word representing the stem is longer than
          // this one, use this word instead (booking -> book)
          if (word.length < stems[stem].word.length)
            stems[stem].word = word;

          // if the current word representing the stem is of the same
          // length but with different form,
          // use the lower-case representation (Book -> book)
          if (word.length === stems[stem].word.length &&
              word !== stems[stem].word)
            stems[stem].word = word.toLowerCase();
        });

        // Push each "stem" into terms as word
        for (var stem in stems) {
          var term = stems[stem].word;
          if (!(term in terms)) {
            terms[term] = stems[stem].count;
          } else {
            terms[term] += stems[stem].count;
          }
        }

        stems = undefined;
      }

      // Chinese
      if (options.languages.indexOf('chinese') !== -1) {
        // Chinese is a language without word boundary.
        // We must use N-gram here to extract meaningful terms.

        // say good bye to non-Chinese (Kanji) characters
        // TBD: Cannot match CJK characters beyond BMP,
        // e.g. \u20000-\u2A6DF at plane B.

        // Han: \u4E00-\u9FFF\u3400-\u4DBF
        // Kana: \u3041-\u309f\u30a0-\u30ff
        var regexp = /[^\u4E00-\u9FFF\u3400-\u4DBF]+/g;
        text = text.replace(regexp, '\n');

        // Use the stop words as separators -- replace them.
        options.stopWords.forEach(function (stopWord) {
          // Not handling that stop word if it's not a Chinese word.
          if (!(/^[\u4E00-\u9FFF\u3400-\u4DBF]+$/).test(stopWord))
            return;

          text = text.replace(new RegExp(stopWord, 'g'), '$1\n');
        });

        var chunks = text.split(/\n+/);

        var pendingTerms = Object.create(null);

        // counts all the chunks (and it's substrings) in pendingTerms
        chunks.forEach(function processChunk(chunk) {
          if (chunk.length <= 1)
            return;

          var substrings = getAllSubStrings(chunk, options.maxiumPhraseLength);
          substrings.forEach(function (substr) {
            if (substr.length <= 1)
              return;

            if (!(substr in pendingTerms))
              pendingTerms[substr] = 0;

            pendingTerms[substr]++;
          });
        });

        // if filterSubstring is true, remove the substrings with the exact
        // same count as the longer term (implying they are only present in
        // the longer terms)
        if (options.filterSubstring) {
          for (var term in pendingTerms) {
            var substrings = getAllSubStrings(term, options.maxiumPhraseLength);
            substrings.forEach(function (substr) {
              if (term === substr)
                return;

              if ((substr in pendingTerms) &&
                  pendingTerms[substr] === pendingTerms[term]) {
                delete pendingTerms[substr];
              }
            });
          }
        }

        // add the pendingTerms into terms
        for (var term in pendingTerms) {
          if (!(term in terms))
            terms[term] = 0;

          terms[term] += pendingTerms[term];
        }

        pendingTerms = undefined;
      }

      // Update the list
      list = [];
      for (var term in terms) {
        if (terms[term] < options.minimumCount)
          continue;

        list.push([term, terms[term]]);
      }
      list = list.sort(function (a, b) {
        if (a[1] > b[1]) return -1;
        if (a[1] < b[1]) return 1;
        if (a[0] === b[0])
          return 0;
        var t = ([a[0], b[0]]).sort();
        if (t[0] !== a[0]) return 1;
        return -1;
			});

      return list;
    },

    empty: function empty() {
      list = [];
      terms = Object.create(null);
      return true;
    },

    getList: function getList() {
      return list;
    },

    getLength: function getLength() {
      return list.length;
    },

    getVolume: function getVolume() {
      var volume = 0;
      list.forEach(function eachListItem(item) {
        volume += item[0].length * Math.pow(item[1], 2);
      });

      return volume;
    },

    stop: function stop() { /* Nothing to do. */ }
  };
};

WordFreqSync.isSupported = !!(Array.prototype.push &&
  Array.prototype.indexOf &&
  Array.prototype.forEach &&
  Array.isArray &&
  Object.create);

if (typeof define === 'function' && define.amd) {
  // Expose the library as an AMD module
  define('wordfreqsync', [], function() { return WordFreqSync; });
} else {
  // expose WordFreqSync as a global interface.
  global.WordFreqSync = WordFreqSync;

  /* If those conditions are met, assume we are in Web Workers.
   * Web Workers script that importScripts() us must have their onmessage
   * handler set-up first.
   * 1. the global object must have a self property that is assigned to itself.
   * 2. the global object must NOT have an window property that is assigned to
   *    itself.
   */
  if (('self' in global) && (global === self) &&
      !(('window' in global) && window === global) &&
      !self.onmessage && self.postMessage && self.importScripts) {
    var wordfreqsync;
    self.onmessage = function gotMessage(evt) {
      var msg = evt.data;
      var method = msg.method;
      var params = msg.params;

      if (method === 'init') {
        wordfreqsync = WordFreqSync.apply(global, params);
        self.postMessage(true);
        return;
      }

      if (!wordfreqsync)
        throw 'You must init to create an instance first';

      if (!method || !wordfreqsync[method])
        throw 'No such method';

      var result = wordfreqsync[method].apply(wordfreqsync, params);
      self.postMessage(result);
    };
  }
}

})(this);
<|endoftext|>


<commit_before>/*
Copyright (c) 2014, EDINA.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
   list of conditions and the following disclaimer in the documentation and/or
   other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
   display the following acknowledgement: This product includes software
   developed by the EDINA.
4. Neither the name of the EDINA nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY EDINA ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL EDINA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

"use strict";

/* global OpenLayers */

/**
 * Fieldtrip settings module.
 */
define(function(){
    var vals;

    /**
     * For a given setting entry, save control value.
     * @param i The index id of the control.
     * param div The HTML div
     */
    var getControlValue = function(i, div){
        var id = $(div).attr('id').split('settings-')[1];
        var $control = $(div);

        if(typeof($control) !== 'undefined'){
            var tag = $control.prop('tagName').toLowerCase();
            var values = [];
            if(tag === 'select'){
                $.each($control.find('option'), function(i, el){
                    values.push(el.value);
                });
            }

            if($control.attr('data-role') === 'flipswitch'){
                // a slider is a select tag but
                // can be identified by its data-role
                tag = 'flipswitch';
            }

            vals[id] = {
                'type': tag,
                'values': values,
                'val': $control.val()
            };
        }
        else{
            vals[id] = undefined;
        }
    };

    vals = {};
    $.get('settings.html', function(data){
        // Load the actual values into vals
        $.each($(data).find('[name=settings-entry]'), getControlValue);

        // Get the stored settings string
        var storedStr = localStorage.getItem('settings');
        if(storedStr){
            var stored = JSON.parse(storedStr);

            for(var key in stored){
                var value = vals[key];
                // if the value exist in the setting values check for its validity
                if(value !== undefined){
                    var valids = vals[key].values;

                    // Use the value if it's valid
                    if(valids && valids.indexOf(stored[key].val) >= 0){
                        vals[key].val = stored[key].val;
                    }
                }
            }
        }

        // Store the settings
        localStorage.setItem('settings', JSON.stringify(vals, undefined, 2));
    });

    /************************** public interface  ******************************/

return{

    /**
     * Initialise settings page.
     */
    init: function(){
        var devClickCount = 0;
        require(['utils'], function(utils){
            $('#settings-project').text(utils.version);
            $('#settings-jquery').text($().jquery);
            $('#settings-jqm').text(jQuery.mobile.version);

            if(typeof(OpenLayers) !== "undefined"){
                $('#settings-ol').text(OpenLayers.VERSION_NUMBER);
            }

            if(utils.isMobileDevice()){
                $('#settings-cordova').text(device.cordova);
            }
            else{
                $('#settings-cordova').text('n/a');
            }

            $('#settings-ftgb').on('vclick', function(){
                if(++devClickCount >= 10){
                    var devMode = ! JSON.parse(localStorage.getItem('devMode'));
                    if(devMode){
                        console.debug('Developer mode activated');
                    }else{
                        console.debug('Developer mode deactivated');
                    }
                    localStorage.setItem('devMode', devMode);
                    devClickCount = 0;
                }
            });

            $.each(vals, function(name, entry){
                var id = '#settings-' + name;
                if(typeof(entry) !== 'undefined'){
                    if(entry.type === 'select'){
                        utils.selectVal(id, vals[name].val);
                    }
                    else if(entry.type === 'flipswitch'){
                        utils.flipswitchVal(id, vals[name].val);
                    }
                    else{
                        $(id).val(vals[name].val);
                    }
                    $(id).trigger('change');
                }
            });
        });
    },

    /**
     * Get setting for named value.
     * @param name The name of the setting.
     */
    get: function(name){
        var val;
        if(vals[name]){
            val = vals[name].val;
        }
        return val;
    },

    /**
     * Save current settings to localstorage.
     */
    save: function(){
        $.each($('[name=settings-entry]'), getControlValue);
        localStorage.setItem('settings', JSON.stringify(vals, undefined, 2));
    }
};

});
<commit_msg>Use a more generic selector to activate developer mode<commit_after>/*
Copyright (c) 2014, EDINA.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
   list of conditions and the following disclaimer in the documentation and/or
   other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
   display the following acknowledgement: This product includes software
   developed by the EDINA.
4. Neither the name of the EDINA nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY EDINA ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL EDINA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

"use strict";

/* global OpenLayers */

/**
 * Fieldtrip settings module.
 */
define(function(){
    var vals;

    /**
     * For a given setting entry, save control value.
     * @param i The index id of the control.
     * param div The HTML div
     */
    var getControlValue = function(i, div){
        var id = $(div).attr('id').split('settings-')[1];
        var $control = $(div);

        if(typeof($control) !== 'undefined'){
            var tag = $control.prop('tagName').toLowerCase();
            var values = [];
            if(tag === 'select'){
                $.each($control.find('option'), function(i, el){
                    values.push(el.value);
                });
            }

            if($control.attr('data-role') === 'flipswitch'){
                // a slider is a select tag but
                // can be identified by its data-role
                tag = 'flipswitch';
            }

            vals[id] = {
                'type': tag,
                'values': values,
                'val': $control.val()
            };
        }
        else{
            vals[id] = undefined;
        }
    };

    vals = {};
    $.get('settings.html', function(data){
        // Load the actual values into vals
        $.each($(data).find('[name=settings-entry]'), getControlValue);

        // Get the stored settings string
        var storedStr = localStorage.getItem('settings');
        if(storedStr){
            var stored = JSON.parse(storedStr);

            for(var key in stored){
                var value = vals[key];
                // if the value exist in the setting values check for its validity
                if(value !== undefined){
                    var valids = vals[key].values;

                    // Use the value if it's valid
                    if(valids && valids.indexOf(stored[key].val) >= 0){
                        vals[key].val = stored[key].val;
                    }
                }
            }
        }

        // Store the settings
        localStorage.setItem('settings', JSON.stringify(vals, undefined, 2));
    });

    /************************** public interface  ******************************/

return{

    /**
     * Initialise settings page.
     */
    init: function(){
        var devClickCount = 0;
        require(['utils'], function(utils){
            $('#settings-project').text(utils.version);
            $('#settings-jquery').text($().jquery);
            $('#settings-jqm').text(jQuery.mobile.version);

            if(typeof(OpenLayers) !== "undefined"){
                $('#settings-ol').text(OpenLayers.VERSION_NUMBER);
            }

            if(utils.isMobileDevice()){
                $('#settings-cordova').text(device.cordova);
            }
            else{
                $('#settings-cordova').text('n/a');
            }

            $(document).on('vclick', '#settings-project', function(){
                if(++devClickCount >= 10){
                    var devMode = ! JSON.parse(localStorage.getItem('devMode'));
                    if(devMode){
                        console.debug('Developer mode activated');
                    }else{
                        console.debug('Developer mode deactivated');
                    }
                    localStorage.setItem('devMode', devMode);
                    devClickCount = 0;
                }
            });

            $.each(vals, function(name, entry){
                var id = '#settings-' + name;
                if(typeof(entry) !== 'undefined'){
                    if(entry.type === 'select'){
                        utils.selectVal(id, vals[name].val);
                    }
                    else if(entry.type === 'flipswitch'){
                        utils.flipswitchVal(id, vals[name].val);
                    }
                    else{
                        $(id).val(vals[name].val);
                    }
                    $(id).trigger('change');
                }
            });
        });
    },

    /**
     * Get setting for named value.
     * @param name The name of the setting.
     */
    get: function(name){
        var val;
        if(vals[name]){
            val = vals[name].val;
        }
        return val;
    },

    /**
     * Save current settings to localstorage.
     */
    save: function(){
        $.each($('[name=settings-entry]'), getControlValue);
        localStorage.setItem('settings', JSON.stringify(vals, undefined, 2));
    }
};

});
<|endoftext|>


<commit_before><commit_msg>css fix.<commit_after><|endoftext|>


<commit_before>(function(timbre) {
    "use strict";

    var fn = timbre.fn;
    var timevalue = timbre.utils.timevalue;

    function TimeoutNode(_args) {
        var isonce = false;
        if (typeof _args[0] === "object" && _args[0].constructor === Object) {
            if (_args[0].once) {
                isonce = true;
            }
        }
        timbre.Object.call(this, _args);
        fn.timer(this);
        fn.fixKR(this);

        var _ = this._;
        _.currentTime = 0;
        _.currentTimeIncr = timbre.cellsize * 1000 / timbre.samplerate;
        _.samplesMax = 0;
        _.samples    = 0;
        _.isEnded = true;
        _.isonce = isonce;

        this.once("init", oninit);
        this.on("start", onstart);

        if (_.isonce) {
            fn.deferred(this);
            this.on("stop", onstop);
        }
    }

    fn.extend(TimeoutNode);

    var oninit = function() {
        if (!this._.timeout) {
            this.timeout = 1000;
        }
    };

    var onstart = function() {
        this._.isEnded = false;
    };
    Object.defineProperty(onstart, "unremovable", {
        value:true, writable:false
    });
    var onstop = function() {
        var _ = this._;
        if (_.isonce && !this.isResolved) {
            _.samplesMax = Infinity;
            _.isEnded = true;
            _.deferred.rejectWith(this);
            this.start = this.stop = fn.nop;
        }
    };
    Object.defineProperty(onstop, "unremovable", {
        value:true, writable:false
    });
    var onended = function() {
        var _ = this._;
        _.isEnded = true;
        if (_.isonce && !this.isResolved) {
            _.samplesMax = Infinity;
            _.emit("ended");
            _.deferred.resolveWith(this);
            var stop = this.stop;
            this.start = this.stop = fn.nop;
            stop.call(this);
        } else {
            _.emit("ended");
        }
    };

    var $ = TimeoutNode.prototype;

    Object.defineProperties($, {
        timeout: {
            set: function(value) {
                var _ = this._;
                if (typeof value === "string") {
                    value = timevalue(value);
                }
                if (typeof value === "number" && value >= 0) {
                    _.timeout = value;
                    _.samplesMax = (timbre.samplerate * (value * 0.001))|0;
                    _.samples = _.samplesMax;
                    _.isEnded = false;
                }
            },
            get: function() {
                return this._.timeout;
            }
        },
        currentTime: {
            get: function() {
                return this._.currentTime;
            }
        }
    });

    $.bang = function() {
        var _ = this._;
        if (!_.isonce ) {
            _.samples = _.samplesMax;
            _.currentTime = 0;
            _.isEnded = false;
        }
        _.emit("bang");
        return this;
    };

    $.process = function(tickID) {
        var cell = this.cell;
        var _ = this._;

        if (_.isEnded) {
            return cell;
        }

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            if (_.samples > 0) {
                _.samples -= cell.length;
            }

            if (_.samples <= 0) {
                var inputs = this.inputs;
                for (var i = 0, imax = inputs.length; i < imax; ++i) {
                    inputs[i].bang();
                }
                fn.nextTick(onended.bind(this));
            }
            _.currentTime += _.currentTimeIncr;
        }
        return cell;
    };

    fn.register("timeout", TimeoutNode);

})(timbre);
<commit_msg>update T("timeout")<commit_after>(function(timbre) {
    "use strict";

    var fn = timbre.fn;
    var timevalue = timbre.utils.timevalue;

    function TimeoutNode(_args) {
        timbre.Object.call(this, _args);
        fn.timer(this);
        fn.fixKR(this);

        var _ = this._;
        _.currentTime = 0;
        _.currentTimeIncr = timbre.cellsize * 1000 / timbre.samplerate;
        _.samplesMax = 0;
        _.samples    = 0;
        _.isEnded = true;

        this.once("init", oninit);
        this.on("start", onstart);

        if (_.deferred) {
            fn.deferred(this);
            this.on("stop", onstop);
        }
    }

    fn.extend(TimeoutNode);

    var oninit = function() {
        if (!this._.timeout) {
            this.timeout = 1000;
        }
    };

    var onstart = function() {
        this._.isEnded = false;
    };
    Object.defineProperty(onstart, "unremovable", {
        value:true, writable:false
    });
    var onstop = function() {
        var _ = this._;
        if (_.deferred && !this.isResolved) {
            _.samplesMax = Infinity;
            _.isEnded = true;
            _.deferred.rejectWith(this);
            this.start = this.stop = fn.nop;
        }
    };
    Object.defineProperty(onstop, "unremovable", {
        value:true, writable:false
    });
    var onended = function() {
        var _ = this._;
        _.isEnded = true;
        if (_.deferred && !this.isResolved) {
            _.samplesMax = Infinity;
            _.emit("ended");
            _.deferred.resolveWith(this);
            var stop = this.stop;
            this.start = this.stop = fn.nop;
            stop.call(this);
        } else {
            _.emit("ended");
        }
    };

    var $ = TimeoutNode.prototype;

    Object.defineProperties($, {
        timeout: {
            set: function(value) {
                var _ = this._;
                if (typeof value === "string") {
                    value = timevalue(value);
                }
                if (typeof value === "number" && value >= 0) {
                    _.timeout = value;
                    _.samplesMax = (timbre.samplerate * (value * 0.001))|0;
                    _.samples = _.samplesMax;
                    _.isEnded = false;
                }
            },
            get: function() {
                return this._.timeout;
            }
        },
        currentTime: {
            get: function() {
                return this._.currentTime;
            }
        }
    });

    $.bang = function() {
        var _ = this._;
        _.samples = _.samplesMax;
        _.currentTime = 0;
        _.isEnded = false;
        _.emit("bang");
        return this;
    };

    $.process = function(tickID) {
        var cell = this.cell;
        var _ = this._;

        if (_.isEnded) {
            return cell;
        }

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            if (_.samples > 0) {
                _.samples -= cell.length;
            }

            if (_.samples <= 0) {
                var inputs = this.inputs;
                for (var i = 0, imax = inputs.length; i < imax; ++i) {
                    inputs[i].bang();
                }
                fn.nextTick(onended.bind(this));
            }
            _.currentTime += _.currentTimeIncr;
        }
        return cell;
    };

    fn.register("timeout", TimeoutNode);

})(timbre);
<|endoftext|>


<commit_before>(function(){function t(t){if(!t.attributes["srcset"])return false;var n,r,i,s=t.attributes["srcset"].nodeValue.split(",");for(i=0;i<s.length;i++){n=s[i].match(/[^\s]+/)[0];r=s[i].match(/(\d+)x/)[1];if(e==r)t.src=n}}var e=window.devicePixelRatio||1;var n=document.getElementsByTagName("img");for(var r=0;r<n.length;r++){t(n[r])}})()<commit_msg>Added test to see if srcset is supported, then proceed or cancel execution<commit_after>(function(){function t(t){if(!t.attributes["srcset"])return false;var n,r,i,s=t.attributes["srcset"].nodeValue.split(",");for(i=0;i<s.length;i++){n=s[i].match(/[^\s]+/)[0];r=s[i].match(/(\d+)x/)[1];if(e==r)t.src=n}}if("srcset"in document.createElement("img"))return false;var e=window.devicePixelRatio||1;var n=document.getElementsByTagName("img");for(var r=0;r<n.length;r++){t(n[r])}})()<|endoftext|>


<commit_before>import * as BHelper from '../js/util/browserHelper';
import { schemeWhitelist } from '../js/util/thumbnails';

import $ from 'jquery';
import _ from 'lodash';
import Prism from 'prismjs';
import marked from 'marked';
import queryString from 'query-string';
import fecha from 'fecha';

import jsEmoji from 'js-emoji';
const Emoji = new jsEmoji.EmojiConvertor();
Emoji.img_set = 'twitter';
Emoji.replace_mode = 'css';
Emoji.supports_css = true;
Emoji.use_sheet = true;

function refreshPreviews(settings) {
  if (settings.nm_disp) {
    let html;

    switch (settings.nm_disp) {
      case 'username':
        html = 'BetterTDeck';
        break;

      case 'fullname':
        html = 'Better TweetDeck';
        break;

      case 'inverted':
        html = 'BetterTDeck Better TweetDeck';
        break;

      default:
        html = 'Better TweetDeck BetterTDeck';
        break;
    }

    $('.tweet-preview.-name-time:not(.-display) .username').html(html);
  }

  if (settings.ts) {
    let html;

    switch (settings.ts) {
      case 'absolute_us':
        html = fecha.format(new Date(), 'MM/DD/YY hh:mm a');
        break;

      case 'absolute_metric':
        html = fecha.format(new Date(), 'DD/MM/YY HH:mm');
        break;

      case 'custom':
        html = fecha.format(new Date(), $('[name="custom_ts.full"]').val());
        break;

      default:
        html = 'now';
        break;
    }

    $('.tweet-preview.-name-time:not(.-display) .date').html(html);
  }

  if (_.isBoolean(settings.no_hearts)) {
    const el = $('.tweet-preview.-display .heart');
    el.removeClass('-hidden');

    if (settings.no_hearts) {
      el.addClass('-hidden');
    }
  }

  if (!settings.css) {
    return;
  }

  if (_.isBoolean(settings.css.round_pic)) {
    const el = $('.tweet-preview.-display .avatar');
    el.removeClass('-rounded');

    if (settings.css.round_pic) {
      el.addClass('-rounded');
    }
  }

  if (_.isBoolean(settings.css.show_verified)) {
    const el = $('.tweet-preview.-display .verified');
    el.addClass('-hidden');

    if (settings.css.show_verified) {
      el.removeClass('-hidden');
    }
  }

  if (_.isBoolean(settings.css.hide_context)) {
    const el = $('.tweet-preview.-display .context');
    el.addClass('-hidden');

    if (settings.css.hide_context) {
      el.removeClass('-hidden');
    }
  }

  if (_.isBoolean(settings.css.actions_on_right)) {
    const el = $('.tweet-preview.-display .actions');
    el.removeClass('-right');

    if (settings.css.actions_on_right) {
      el.addClass('-right');
    }
  }
}

/**
 * When got the settings we initialise the view
 */
BHelper.settings.getAll(settings => {
  const settingsStr = JSON.stringify(settings, null, 2);

  $('.settings-dump').html(Prism.highlight(settingsStr, Prism.languages.js));

  /**
   * We go through the settings object and check or not the inputs accordingly
   */
  _.forEach(settings, (val, key) => {
    if (_.isObject(val)) {
      _.forEach(val, (value, keyname) => {
        const name = `${key}.${keyname}`;

        if (value) {
          $(`input[name="${name}"]`).prop('checked', true);
        }

        if (key === 'custom_ts') {
          if (settings.ts === 'custom') {
            $('input[name="ts"]#custom ~ ul input').removeAttr('disabled');
          }

          $(`input[name="${name}"]`).val(settings.custom_ts[keyname]);
        }

        if (key === 'flash_tweets') {
          if (settings.flash_tweets) {
            $('input[name="flash_tweets.enabled"]').prop('checked', settings.flash_tweets.enabled);
            $('input[name="flash_tweets.enabled"] ~ ul input').removeAttr('disabled');
          }

          $('input[name="flash_tweets.mode"]').removeAttr('checked');
          $('input[name="flash_tweets.mode"]').prop('disabled', !settings.flash_tweets.enabled);
          $(`input[name="flash_tweets.mode"]#${settings.flash_tweets.mode}`).prop('checked', settings.flash_tweets.enabled && true);
        }

        if (key === 'custom_columns_width') {
          if (settings.custom_columns_width) {
            $('input[name="custom_columns_width.enabled"]').prop('checked', settings.custom_columns_width.enabled);
            $('input[name="custom_columns_width.enabled"] ~ ul input').removeAttr('disabled');
          }

          $('input[name="custom_columns_width.size"]').removeAttr('checked');
          $('input[name="custom_columns_width.size"]').prop('disabled', !settings.custom_columns_width.enabled);
          $('input[name="custom_columns_width.size"]').prop('value', settings.custom_columns_width.size);
        }
      });
    } else {
      const name = key;

      if (_.isBoolean(val)) {
        $(`input[name="${name}"]`).prop('checked', val);
      } else {
        $(`input[name="${name}"]#${val}`).prop('checked', true);
      }
    }
  });

  function getFaviconURL(scheme) {
    if (scheme.setting.includes('_')) {
      return `https://plus.google.com/_/favicon?domain=${scheme.setting.replace('_', '.')}`;
    }

    return `https://plus.google.com/_/favicon?domain=${scheme.setting}.com`;
  }

  /**
   * Special treatment for thumb providers list who gets created from the source directly
   */
  schemeWhitelist.forEach(scheme => {
    const isEnabled = _.isBoolean(settings.thumbnails[scheme.setting]) ? settings.thumbnails[scheme.setting] : scheme.default;

    $('.settings-thumbnails-providers-list').append(`

        <input type="checkbox" name="thumbnails.${scheme.setting}" id="${scheme.setting}" ${isEnabled ? 'checked' : ''}>

        ${scheme.name}

    `);
  });

  refreshPreviews(settings);

  /**
   * Updating the settings when inputs change
   */

  $('input[name]').on('change input', (e) => {
    $('.save-button').text(chrome.i18n.getMessage('save_save')).removeAttr('disabled');

    if (e.target.type === 'radio' && e.target.name === 'ts') {
      if (e.target.hasAttribute('data-ghost')) {
        $(e.target).parent()
                   .find('ul input')
                   .removeAttr('disabled');
      } else {
        $('[data-ghost] ~ ul input').attr('disabled', '');
      }
    }

    if (e.target.type === 'checkbox' && e.target.hasAttribute('data-ghost')) {
      const els = $(`[data-ghost][name="${e.target.name}"] ~ ul input`);

      if (e.target.checked) {
        els.removeAttr('disabled');
      } else {
        els.attr('disabled', '');
      }
    }

    const name = e.target.name;

    if (['nm_disp', 'ts'].includes(name)) {
      refreshPreviews({ [name]: e.target.id });
    }

    if (name.includes('custom_ts.')) {
      refreshPreviews({ ts: 'custom' });
    }

    if (['no_hearts'].includes(name)) {
      refreshPreviews({ [name]: e.target.checked });
    }

    if (name.includes('css.')) {
      refreshPreviews({
        css: {
          [name.split('.')[1]]: e.target.checked,
        },
      });
    }
  });

  $('button.save-button').click(() => {
    const newSettings = {};

    $('input[name]').each((i, el) => {
      const input = el;
      const type = input.getAttribute('type').toLowerCase();
      const name = input.getAttribute('name');
      const nameArr = name.split('.');
      const isChecked = $(el).is(':checked');

      if (name.includes('.')) {
        if (!newSettings[nameArr[0]]) {
          newSettings[nameArr[0]] = {};
        }

        if (type === 'radio' && isChecked) {
          newSettings[nameArr[0]][nameArr[1]] = input.getAttribute('id');
        } else if (type === 'checkbox') {
          newSettings[nameArr[0]][nameArr[1]] = isChecked;
        } else if (type === 'text' || type === 'number') {
          newSettings[nameArr[0]][nameArr[1]] = input.value;
        }
      } else {
        if (type === 'radio' && isChecked) {
          newSettings[name] = input.getAttribute('id');
        } else if (type === 'checkbox') {
          newSettings[name] = isChecked;
        } else if (type === 'text') {
          newSettings[name] = input.value;
        }
      }
    });

    BHelper.settings.set(newSettings);
    $('.save-button').text(chrome.i18n.getMessage('save_no')).attr('disabled', '');
  });
});

// Auto-select sidebar items
$('.sidebar-nav:first-child a:first-child, .content-block:first-child').addClass('-selected');

// Automatically add target=_blank on external links
$('.sidebar-nav a:not([href^="#"])').each((i, el) => el.setAttribute('target', '_blank'));

// "Animation" logic
$('.sidebar-nav a[href^="#"]').on('click', (ev) => {
  ev.preventDefault();
  const href = ev.target.getAttribute('href').slice(1);

  $('.sidebar-nav a, .content-block').removeClass('-selected');
  $(`.sidebar-nav a[href="#${href}"], .content-block#${href}`).addClass('-selected');
});

// Open a specific section when needed
if (Object.keys(queryString.parse(location.search)).length > 0) {
  const QS = queryString.parse(location.search);

  if (QS.on === 'install') {
    $('.sidebar-nav a, .content-block').removeClass('-selected');
    $('.sidebar-nav a[href="#onInstall"], .content-block#onInstall').addClass('-selected');
  }

  if (QS.on === 'update') {
    $('.sidebar-nav a, .content-block').removeClass('-selected');
    $('.sidebar-nav a[href="#changelog"], .content-block#changelog').addClass('-selected');
  }
}

// Write UA/version infos
$('.topbar-version-number').text(`v${BHelper.getVersion()}`);
$('.settings-version-number').text(BHelper.getVersion());
$('.settings-user-agent').text(BHelper.getUA());

// Get GitHub infos
fetch('https://api.github.com/repos/eramdam/BetterTweetDeck/contributors').then(res => {
  res.json().then(json => {
    json.forEach(contributor => {
      if (contributor.login === 'eramdam') {
        return;
      }

      const commitsLinks = `https://github.com/eramdam/BetterTweetDeck/commits?author=${contributor.login}`;

      $('.settings-contributors').append(`

          ${contributor.login}
          ${contributor.contributions} commit(s)

      `);
    });
  });
});

const labels = ['Feature', 'Bugfix', 'Improvement', 'Meta'];

fetch(chrome.extension.getURL('options/CHANGELOG.md')).then(res => res.text()).then(body => {
  const changelogMarkup =
    Emoji.replace_colons(marked(body))
    .replace(/\/emoji-data\/sheet_twitter_64.png/g, chrome.extension.getURL('emojis/sheet_twitter_64.png'))
    .replace(new RegExp(`(\\[(${labels.join('|')})\\])`, 'gi'), (match, p1, p2) => {
      return `
        ${p2}
      `;
    });

  // console.log(changelogMarkup.match(new RegExp(`(\\[(${labels.join('|')})\\])`, 'gi')));

  $('.settings-section.changelog')[0].innerHTML = changelogMarkup;
});

// Because nobody got time to write that HTML by hand, right?
const usedDeps = [
  { name: 'BabelJS', url: 'https://babeljs.io/' },
  { name: 'Embed.ly', url: 'http://embed.ly/' },
  { name: 'ESLint', url: 'http://eslint.org/' },
  { name: 'Gulp', url: 'http://gulpjs.com/' },
  { name: 'jQuery', url: 'http://jquery.com/' },
  { name: 'PostCSS', url: 'http://postcss.org/' },
  { name: 'See other dependencies', url: 'https://github.com/eramdam/BetterTweetDeck/blob/master/package.json' },
];

usedDeps.forEach(dep => {
  $('.settings-deps').append(`

      ${dep.name}

  `);
});

// Easy-peasy-lemon-sqeezy i18n code
[...document.querySelectorAll('[data-lang]')].forEach(el => {
  const msg = el.getAttribute('data-lang');
  el.innerHTML = BHelper.getMessage(msg);
});

// Add the "Are you sure you wanna leave?" alert when the save button isn't clicked
window.onbeforeunload = () => {
  if (!$('.save-button').is(':disabled')) {
    return false;
  }

  return null;
};
<commit_msg>:scissors: remove Embed.ly from list of ressources<commit_after>import * as BHelper from '../js/util/browserHelper';
import { schemeWhitelist } from '../js/util/thumbnails';

import $ from 'jquery';
import _ from 'lodash';
import Prism from 'prismjs';
import marked from 'marked';
import queryString from 'query-string';
import fecha from 'fecha';

import jsEmoji from 'js-emoji';
const Emoji = new jsEmoji.EmojiConvertor();
Emoji.img_set = 'twitter';
Emoji.replace_mode = 'css';
Emoji.supports_css = true;
Emoji.use_sheet = true;

function refreshPreviews(settings) {
  if (settings.nm_disp) {
    let html;

    switch (settings.nm_disp) {
      case 'username':
        html = 'BetterTDeck';
        break;

      case 'fullname':
        html = 'Better TweetDeck';
        break;

      case 'inverted':
        html = 'BetterTDeck Better TweetDeck';
        break;

      default:
        html = 'Better TweetDeck BetterTDeck';
        break;
    }

    $('.tweet-preview.-name-time:not(.-display) .username').html(html);
  }

  if (settings.ts) {
    let html;

    switch (settings.ts) {
      case 'absolute_us':
        html = fecha.format(new Date(), 'MM/DD/YY hh:mm a');
        break;

      case 'absolute_metric':
        html = fecha.format(new Date(), 'DD/MM/YY HH:mm');
        break;

      case 'custom':
        html = fecha.format(new Date(), $('[name="custom_ts.full"]').val());
        break;

      default:
        html = 'now';
        break;
    }

    $('.tweet-preview.-name-time:not(.-display) .date').html(html);
  }

  if (_.isBoolean(settings.no_hearts)) {
    const el = $('.tweet-preview.-display .heart');
    el.removeClass('-hidden');

    if (settings.no_hearts) {
      el.addClass('-hidden');
    }
  }

  if (!settings.css) {
    return;
  }

  if (_.isBoolean(settings.css.round_pic)) {
    const el = $('.tweet-preview.-display .avatar');
    el.removeClass('-rounded');

    if (settings.css.round_pic) {
      el.addClass('-rounded');
    }
  }

  if (_.isBoolean(settings.css.show_verified)) {
    const el = $('.tweet-preview.-display .verified');
    el.addClass('-hidden');

    if (settings.css.show_verified) {
      el.removeClass('-hidden');
    }
  }

  if (_.isBoolean(settings.css.hide_context)) {
    const el = $('.tweet-preview.-display .context');
    el.addClass('-hidden');

    if (settings.css.hide_context) {
      el.removeClass('-hidden');
    }
  }

  if (_.isBoolean(settings.css.actions_on_right)) {
    const el = $('.tweet-preview.-display .actions');
    el.removeClass('-right');

    if (settings.css.actions_on_right) {
      el.addClass('-right');
    }
  }
}

/**
 * When got the settings we initialise the view
 */
BHelper.settings.getAll(settings => {
  const settingsStr = JSON.stringify(settings, null, 2);

  $('.settings-dump').html(Prism.highlight(settingsStr, Prism.languages.js));

  /**
   * We go through the settings object and check or not the inputs accordingly
   */
  _.forEach(settings, (val, key) => {
    if (_.isObject(val)) {
      _.forEach(val, (value, keyname) => {
        const name = `${key}.${keyname}`;

        if (value) {
          $(`input[name="${name}"]`).prop('checked', true);
        }

        if (key === 'custom_ts') {
          if (settings.ts === 'custom') {
            $('input[name="ts"]#custom ~ ul input').removeAttr('disabled');
          }

          $(`input[name="${name}"]`).val(settings.custom_ts[keyname]);
        }

        if (key === 'flash_tweets') {
          if (settings.flash_tweets) {
            $('input[name="flash_tweets.enabled"]').prop('checked', settings.flash_tweets.enabled);
            $('input[name="flash_tweets.enabled"] ~ ul input').removeAttr('disabled');
          }

          $('input[name="flash_tweets.mode"]').removeAttr('checked');
          $('input[name="flash_tweets.mode"]').prop('disabled', !settings.flash_tweets.enabled);
          $(`input[name="flash_tweets.mode"]#${settings.flash_tweets.mode}`).prop('checked', settings.flash_tweets.enabled && true);
        }

        if (key === 'custom_columns_width') {
          if (settings.custom_columns_width) {
            $('input[name="custom_columns_width.enabled"]').prop('checked', settings.custom_columns_width.enabled);
            $('input[name="custom_columns_width.enabled"] ~ ul input').removeAttr('disabled');
          }

          $('input[name="custom_columns_width.size"]').removeAttr('checked');
          $('input[name="custom_columns_width.size"]').prop('disabled', !settings.custom_columns_width.enabled);
          $('input[name="custom_columns_width.size"]').prop('value', settings.custom_columns_width.size);
        }
      });
    } else {
      const name = key;

      if (_.isBoolean(val)) {
        $(`input[name="${name}"]`).prop('checked', val);
      } else {
        $(`input[name="${name}"]#${val}`).prop('checked', true);
      }
    }
  });

  function getFaviconURL(scheme) {
    if (scheme.setting.includes('_')) {
      return `https://plus.google.com/_/favicon?domain=${scheme.setting.replace('_', '.')}`;
    }

    return `https://plus.google.com/_/favicon?domain=${scheme.setting}.com`;
  }

  /**
   * Special treatment for thumb providers list who gets created from the source directly
   */
  schemeWhitelist.forEach(scheme => {
    const isEnabled = _.isBoolean(settings.thumbnails[scheme.setting]) ? settings.thumbnails[scheme.setting] : scheme.default;

    $('.settings-thumbnails-providers-list').append(`

        <input type="checkbox" name="thumbnails.${scheme.setting}" id="${scheme.setting}" ${isEnabled ? 'checked' : ''}>

        ${scheme.name}

    `);
  });

  refreshPreviews(settings);

  /**
   * Updating the settings when inputs change
   */

  $('input[name]').on('change input', (e) => {
    $('.save-button').text(chrome.i18n.getMessage('save_save')).removeAttr('disabled');

    if (e.target.type === 'radio' && e.target.name === 'ts') {
      if (e.target.hasAttribute('data-ghost')) {
        $(e.target).parent()
                   .find('ul input')
                   .removeAttr('disabled');
      } else {
        $('[data-ghost] ~ ul input').attr('disabled', '');
      }
    }

    if (e.target.type === 'checkbox' && e.target.hasAttribute('data-ghost')) {
      const els = $(`[data-ghost][name="${e.target.name}"] ~ ul input`);

      if (e.target.checked) {
        els.removeAttr('disabled');
      } else {
        els.attr('disabled', '');
      }
    }

    const name = e.target.name;

    if (['nm_disp', 'ts'].includes(name)) {
      refreshPreviews({ [name]: e.target.id });
    }

    if (name.includes('custom_ts.')) {
      refreshPreviews({ ts: 'custom' });
    }

    if (['no_hearts'].includes(name)) {
      refreshPreviews({ [name]: e.target.checked });
    }

    if (name.includes('css.')) {
      refreshPreviews({
        css: {
          [name.split('.')[1]]: e.target.checked,
        },
      });
    }
  });

  $('button.save-button').click(() => {
    const newSettings = {};

    $('input[name]').each((i, el) => {
      const input = el;
      const type = input.getAttribute('type').toLowerCase();
      const name = input.getAttribute('name');
      const nameArr = name.split('.');
      const isChecked = $(el).is(':checked');

      if (name.includes('.')) {
        if (!newSettings[nameArr[0]]) {
          newSettings[nameArr[0]] = {};
        }

        if (type === 'radio' && isChecked) {
          newSettings[nameArr[0]][nameArr[1]] = input.getAttribute('id');
        } else if (type === 'checkbox') {
          newSettings[nameArr[0]][nameArr[1]] = isChecked;
        } else if (type === 'text' || type === 'number') {
          newSettings[nameArr[0]][nameArr[1]] = input.value;
        }
      } else {
        if (type === 'radio' && isChecked) {
          newSettings[name] = input.getAttribute('id');
        } else if (type === 'checkbox') {
          newSettings[name] = isChecked;
        } else if (type === 'text') {
          newSettings[name] = input.value;
        }
      }
    });

    BHelper.settings.set(newSettings);
    $('.save-button').text(chrome.i18n.getMessage('save_no')).attr('disabled', '');
  });
});

// Auto-select sidebar items
$('.sidebar-nav:first-child a:first-child, .content-block:first-child').addClass('-selected');

// Automatically add target=_blank on external links
$('.sidebar-nav a:not([href^="#"])').each((i, el) => el.setAttribute('target', '_blank'));

// "Animation" logic
$('.sidebar-nav a[href^="#"]').on('click', (ev) => {
  ev.preventDefault();
  const href = ev.target.getAttribute('href').slice(1);

  $('.sidebar-nav a, .content-block').removeClass('-selected');
  $(`.sidebar-nav a[href="#${href}"], .content-block#${href}`).addClass('-selected');
});

// Open a specific section when needed
if (Object.keys(queryString.parse(location.search)).length > 0) {
  const QS = queryString.parse(location.search);

  if (QS.on === 'install') {
    $('.sidebar-nav a, .content-block').removeClass('-selected');
    $('.sidebar-nav a[href="#onInstall"], .content-block#onInstall').addClass('-selected');
  }

  if (QS.on === 'update') {
    $('.sidebar-nav a, .content-block').removeClass('-selected');
    $('.sidebar-nav a[href="#changelog"], .content-block#changelog').addClass('-selected');
  }
}

// Write UA/version infos
$('.topbar-version-number').text(`v${BHelper.getVersion()}`);
$('.settings-version-number').text(BHelper.getVersion());
$('.settings-user-agent').text(BHelper.getUA());

// Get GitHub infos
fetch('https://api.github.com/repos/eramdam/BetterTweetDeck/contributors').then(res => {
  res.json().then(json => {
    json.forEach(contributor => {
      if (contributor.login === 'eramdam') {
        return;
      }

      const commitsLinks = `https://github.com/eramdam/BetterTweetDeck/commits?author=${contributor.login}`;

      $('.settings-contributors').append(`

          ${contributor.login}
          ${contributor.contributions} commit(s)

      `);
    });
  });
});

const labels = ['Feature', 'Bugfix', 'Improvement', 'Meta'];

fetch(chrome.extension.getURL('options/CHANGELOG.md')).then(res => res.text()).then(body => {
  const changelogMarkup =
    Emoji.replace_colons(marked(body))
    .replace(/\/emoji-data\/sheet_twitter_64.png/g, chrome.extension.getURL('emojis/sheet_twitter_64.png'))
    .replace(new RegExp(`(\\[(${labels.join('|')})\\])`, 'gi'), (match, p1, p2) => {
      return `
        ${p2}
      `;
    });

  // console.log(changelogMarkup.match(new RegExp(`(\\[(${labels.join('|')})\\])`, 'gi')));

  $('.settings-section.changelog')[0].innerHTML = changelogMarkup;
});

// Because nobody got time to write that HTML by hand, right?
const usedDeps = [
  { name: 'BabelJS', url: 'https://babeljs.io/' },
  { name: 'ESLint', url: 'http://eslint.org/' },
  { name: 'Gulp', url: 'http://gulpjs.com/' },
  { name: 'jQuery', url: 'http://jquery.com/' },
  { name: 'PostCSS', url: 'http://postcss.org/' },
  { name: 'See other dependencies', url: 'https://github.com/eramdam/BetterTweetDeck/blob/master/package.json' },
];

usedDeps.forEach(dep => {
  $('.settings-deps').append(`

      ${dep.name}

  `);
});

// Easy-peasy-lemon-sqeezy i18n code
[...document.querySelectorAll('[data-lang]')].forEach(el => {
  const msg = el.getAttribute('data-lang');
  el.innerHTML = BHelper.getMessage(msg);
});

// Add the "Are you sure you wanna leave?" alert when the save button isn't clicked
window.onbeforeunload = () => {
  if (!$('.save-button').is(':disabled')) {
    return false;
  }

  return null;
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */

import type {DependencyRequestPattern, Manifest} from './types.js';
import type PackageResolver from './package-resolver.js';
import type {Reporter} from './reporters/index.js';
import type Config from './config.js';
import type {VisibilityAction} from './package-reference.js';
import Lockfile from './lockfile/wrapper.js';
import {USED as USED_VISIBILITY, default as PackageReference} from './package-reference.js';
import {registries as registryResolvers} from './resolvers/index.js';
import {MessageError} from './errors.js';
import {entries} from './util/misc.js';
import * as constants from './constants.js';
import * as versionUtil from './util/version.js';
import * as resolvers from './resolvers/index.js';

const invariant = require('invariant');

type ResolverRegistryNames = $Keys<typeof registryResolvers>;

export default class PackageRequest {
  constructor(req: DependencyRequestPattern, resolver: PackageResolver) {
    this.parentRequest = req.parentRequest;
    this.visibility = req.visibility;
    this.lockfile = resolver.lockfile;
    this.registry = req.registry;
    this.reporter = resolver.reporter;
    this.resolver = resolver;
    this.optional = req.optional;
    this.pattern = req.pattern;
    this.config = resolver.config;

    resolver.usedRegistries.add(req.registry);
  }

  static getExoticResolver(pattern: string): ?Function { // TODO make this type more refined
    for (let [, Resolver] of entries(resolvers.exotics)) {
      if (Resolver.isVersion(pattern)) {
        return Resolver;
      }
    }
    return null;
  }

  parentRequest: ?PackageRequest;
  lockfile: Lockfile;
  reporter: Reporter;
  resolver: PackageResolver;
  pattern: string;
  config: Config;
  registry: ResolverRegistryNames;
  visibility: VisibilityAction;
  optional: boolean;

  getHuman(): string {
    const chain = [];

    let delegator = this;
    do {
      chain.push(delegator.pattern);
    } while (delegator = delegator.parentRequest);

    return chain.reverse().join(' > ');
  }

  getLocked(remoteType: string): ?Object {
    // always prioritise root lockfile
    let shrunk = this.lockfile.getLocked(this.pattern);

    if (shrunk) {
      const resolvedParts = versionUtil.explodeHashedUrl(shrunk.resolved);

      return {
        name: shrunk.name,
        version: shrunk.version,
        _uid: shrunk.uid,
        _remote: {
          resolved: shrunk.resolved,
          type: remoteType,
          reference: resolvedParts.url,
          hash: resolvedParts.hash,
          registry: shrunk.registry,
        },
        dependencies: shrunk.dependencies,
      };
    } else {
      return null;
    }
  }

  /**
   * If the input pattern matches a registry one then attempt to find it on the registry.
   * Otherwise fork off to an exotic resolver if one matches.
   */

  async findVersionOnRegistry(pattern: string): Promise<Manifest> {
    let {range, name} = PackageRequest.normalisePattern(pattern);

    const exoticResolver = PackageRequest.getExoticResolver(range);
    if (exoticResolver) {
      let data = await this.findExoticVersionInfo(exoticResolver, range);

      // clone data as we're manipulating it in place and this could be resolved multiple
      // times
      data = Object.assign({}, data);

      // this is so the returned package response uses the overridden name. ie. if the
      // package's actual name is `bar`, but it's been specified in the manifest like:
      //   "foo": "http://foo.com/bar.tar.gz"
      // then we use the foo name
      data.name = name;

      return data;
    }

    const Resolver = this.getRegistryResolver();
    const resolver = new Resolver(this, name, range);
    return resolver.resolve();
  }

  /**
   * Get the registry resolver associated with this package request.
   */

  getRegistryResolver(): Function {
    const Resolver = registryResolvers[this.registry];
    if (Resolver) {
      return Resolver;
    } else {
      throw new Error(`Unknown registry resolver ${this.registry}`);
    }
  }

  /**
   * Explode and normalise a pattern into it's name and range.
   */

  static normalisePattern(pattern: string): {
    hasVersion: boolean,
    name: string,
    range: string,
  } {
    let hasVersion = false;
    let range = 'latest';
    let name = pattern;

    // if we're a scope then remove the @ and add it back later
    let isScoped = false;
    if (name[0] === '@') {
      isScoped = true;
      name = name.slice(1);
    }

    // take first part as the name
    const parts = name.split('@');
    if (parts.length > 1) {
      name = parts.shift();
      range = parts.join('@');

      if (range) {
        hasVersion = true;
      } else {
        range = '*';
      }
    }

    // add back @ scope suffix
    if (isScoped) {
      name = `@${name}`;
    }

    return {name, range, hasVersion};
  }

  /**
   * Construct an exotic resolver instance with the input `ExoticResolver` and `range`.
   */

  async findExoticVersionInfo(ExoticResolver: Function, range: string): Promise<Manifest> {
    const resolver = new ExoticResolver(this, range);
    return resolver.resolve();
  }

  /**
   * If the current pattern matches an exotic resolver then delegate to it or else try
   * the registry.
   */

  async findVersionInfo(): Promise<Manifest> {
    const exoticResolver = PackageRequest.getExoticResolver(this.pattern);
    if (exoticResolver) {
      return await this.findExoticVersionInfo(exoticResolver, this.pattern);
    } else {
      return await this.findVersionOnRegistry(this.pattern);
    }
  }

  /**
   * TODO description
   */

  async find(): Promise<void> {
    // find version info for this package pattern
    let info: ?Manifest = await this.findVersionInfo();
    if (!info) {
      throw new MessageError(`Couldn't find package ${this.pattern}`);
    }

    // check if while we were resolving this dep we've already resolved one that satisfies
    // the same range
    const resolved: ?Manifest = this.resolver.getExactVersionMatch(info.name, info.version);
    if (resolved) {
      const ref = resolved._reference;
      invariant(ref, 'Resolved package info has no package reference');
      ref.addRequest(this);
      ref.addPattern(this.pattern, resolved);
      ref.addOptional(this.optional);
      ref.addVisibility(this.visibility);
      return;
    }

    // validate version info
    PackageRequest.validateVersionInfo(info);

    //
    const remote = info._remote;
    invariant(remote, 'Missing remote');

    // set package reference
    const ref = new PackageReference(this, info, remote);

    info._reference = ref;
    info._remote = remote;

    // start installation of dependencies
    const promises = [];
    const deps = [];

    // normal deps
    for (const depName in info.dependencies) {
      const depPattern = depName + '@' + info.dependencies[depName];
      deps.push(depPattern);
      promises.push(this.resolver.find({
        pattern: depPattern,
        registry: remote.registry,
        visibility: USED_VISIBILITY,
        optional: false,
        parentRequest: this,
      }));
    }

    // optional deps
    for (const depName in info.optionalDependencies) {
      const depPattern = depName + '@' + info.optionalDependencies[depName];
      deps.push(depPattern);
      promises.push(this.resolver.find({
        pattern: depPattern,
        registry: remote.registry,
        visibility: USED_VISIBILITY,
        optional: true,
        parentRequest: this,
      }));
    }

    await Promise.all(promises);

    ref.addPattern(this.pattern, info);
    ref.addDependencies(deps);
    ref.addOptional(this.optional);
    ref.addVisibility(this.visibility);
  }

  /**
   * TODO description
   */

  static validateVersionInfo(info: Manifest) {
    // human readable name to use in errors
    const human = `${info.name}@${info.version}`;

    info.version = PackageRequest.getPackageVersion(info);

    for (const key of constants.REQUIRED_PACKAGE_KEYS) {
      if (!info[key]) {
        throw new MessageError(`Package ${human} doesn't have a ${key}`);
      }
    }
  }

  /**
   * Returns the package version if present, else defaults to the uid
   */

  static getPackageVersion(info: Manifest): string {
    // TODO possibly reconsider this behaviour
    return info.version === undefined ? info._uid : info.version;
  }
}
<commit_msg>Move package ref setup to creation instead of delegating it (#295)<commit_after>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */

import type {DependencyRequestPattern, Manifest} from './types.js';
import type PackageResolver from './package-resolver.js';
import type {Reporter} from './reporters/index.js';
import type Config from './config.js';
import type {VisibilityAction} from './package-reference.js';
import Lockfile from './lockfile/wrapper.js';
import {USED as USED_VISIBILITY, default as PackageReference} from './package-reference.js';
import {registries as registryResolvers} from './resolvers/index.js';
import {MessageError} from './errors.js';
import {entries} from './util/misc.js';
import * as constants from './constants.js';
import * as versionUtil from './util/version.js';
import * as resolvers from './resolvers/index.js';

const invariant = require('invariant');

type ResolverRegistryNames = $Keys<typeof registryResolvers>;

export default class PackageRequest {
  constructor(req: DependencyRequestPattern, resolver: PackageResolver) {
    this.parentRequest = req.parentRequest;
    this.visibility = req.visibility;
    this.lockfile = resolver.lockfile;
    this.registry = req.registry;
    this.reporter = resolver.reporter;
    this.resolver = resolver;
    this.optional = req.optional;
    this.pattern = req.pattern;
    this.config = resolver.config;

    resolver.usedRegistries.add(req.registry);
  }

  static getExoticResolver(pattern: string): ?Function { // TODO make this type more refined
    for (let [, Resolver] of entries(resolvers.exotics)) {
      if (Resolver.isVersion(pattern)) {
        return Resolver;
      }
    }
    return null;
  }

  parentRequest: ?PackageRequest;
  lockfile: Lockfile;
  reporter: Reporter;
  resolver: PackageResolver;
  pattern: string;
  config: Config;
  registry: ResolverRegistryNames;
  visibility: VisibilityAction;
  optional: boolean;

  getHuman(): string {
    const chain = [];

    let delegator = this;
    do {
      chain.push(delegator.pattern);
    } while (delegator = delegator.parentRequest);

    return chain.reverse().join(' > ');
  }

  getLocked(remoteType: string): ?Object {
    // always prioritise root lockfile
    let shrunk = this.lockfile.getLocked(this.pattern);

    if (shrunk) {
      const resolvedParts = versionUtil.explodeHashedUrl(shrunk.resolved);

      return {
        name: shrunk.name,
        version: shrunk.version,
        _uid: shrunk.uid,
        _remote: {
          resolved: shrunk.resolved,
          type: remoteType,
          reference: resolvedParts.url,
          hash: resolvedParts.hash,
          registry: shrunk.registry,
        },
        dependencies: shrunk.dependencies,
      };
    } else {
      return null;
    }
  }

  /**
   * If the input pattern matches a registry one then attempt to find it on the registry.
   * Otherwise fork off to an exotic resolver if one matches.
   */

  async findVersionOnRegistry(pattern: string): Promise<Manifest> {
    let {range, name} = PackageRequest.normalisePattern(pattern);

    const exoticResolver = PackageRequest.getExoticResolver(range);
    if (exoticResolver) {
      let data = await this.findExoticVersionInfo(exoticResolver, range);

      // clone data as we're manipulating it in place and this could be resolved multiple
      // times
      data = Object.assign({}, data);

      // this is so the returned package response uses the overridden name. ie. if the
      // package's actual name is `bar`, but it's been specified in the manifest like:
      //   "foo": "http://foo.com/bar.tar.gz"
      // then we use the foo name
      data.name = name;

      return data;
    }

    const Resolver = this.getRegistryResolver();
    const resolver = new Resolver(this, name, range);
    return resolver.resolve();
  }

  /**
   * Get the registry resolver associated with this package request.
   */

  getRegistryResolver(): Function {
    const Resolver = registryResolvers[this.registry];
    if (Resolver) {
      return Resolver;
    } else {
      throw new Error(`Unknown registry resolver ${this.registry}`);
    }
  }

  /**
   * Explode and normalise a pattern into it's name and range.
   */

  static normalisePattern(pattern: string): {
    hasVersion: boolean,
    name: string,
    range: string,
  } {
    let hasVersion = false;
    let range = 'latest';
    let name = pattern;

    // if we're a scope then remove the @ and add it back later
    let isScoped = false;
    if (name[0] === '@') {
      isScoped = true;
      name = name.slice(1);
    }

    // take first part as the name
    const parts = name.split('@');
    if (parts.length > 1) {
      name = parts.shift();
      range = parts.join('@');

      if (range) {
        hasVersion = true;
      } else {
        range = '*';
      }
    }

    // add back @ scope suffix
    if (isScoped) {
      name = `@${name}`;
    }

    return {name, range, hasVersion};
  }

  /**
   * Construct an exotic resolver instance with the input `ExoticResolver` and `range`.
   */

  async findExoticVersionInfo(ExoticResolver: Function, range: string): Promise<Manifest> {
    const resolver = new ExoticResolver(this, range);
    return resolver.resolve();
  }

  /**
   * If the current pattern matches an exotic resolver then delegate to it or else try
   * the registry.
   */

  async findVersionInfo(): Promise<Manifest> {
    const exoticResolver = PackageRequest.getExoticResolver(this.pattern);
    if (exoticResolver) {
      return await this.findExoticVersionInfo(exoticResolver, this.pattern);
    } else {
      return await this.findVersionOnRegistry(this.pattern);
    }
  }

  /**
   * TODO description
   */

  async find(): Promise<void> {
    // find version info for this package pattern
    let info: ?Manifest = await this.findVersionInfo();
    if (!info) {
      throw new MessageError(`Couldn't find package ${this.pattern}`);
    }

    // check if while we were resolving this dep we've already resolved one that satisfies
    // the same range
    const resolved: ?Manifest = this.resolver.getExactVersionMatch(info.name, info.version);
    if (resolved) {
      const ref = resolved._reference;
      invariant(ref, 'Resolved package info has no package reference');
      ref.addRequest(this);
      ref.addPattern(this.pattern, resolved);
      ref.addOptional(this.optional);
      ref.addVisibility(this.visibility);
      return;
    }

    // validate version info
    PackageRequest.validateVersionInfo(info);

    //
    const remote = info._remote;
    invariant(remote, 'Missing remote');

    // set package reference
    const ref = new PackageReference(this, info, remote);
    ref.addPattern(this.pattern, info);
    ref.addOptional(this.optional);
    ref.addVisibility(this.visibility);

    info._reference = ref;
    info._remote = remote;

    // start installation of dependencies
    const promises = [];
    const deps = [];

    // normal deps
    for (const depName in info.dependencies) {
      const depPattern = depName + '@' + info.dependencies[depName];
      deps.push(depPattern);
      promises.push(this.resolver.find({
        pattern: depPattern,
        registry: remote.registry,
        visibility: USED_VISIBILITY,
        optional: false,
        parentRequest: this,
      }));
    }

    // optional deps
    for (const depName in info.optionalDependencies) {
      const depPattern = depName + '@' + info.optionalDependencies[depName];
      deps.push(depPattern);
      promises.push(this.resolver.find({
        pattern: depPattern,
        registry: remote.registry,
        visibility: USED_VISIBILITY,
        optional: true,
        parentRequest: this,
      }));
    }

    await Promise.all(promises);

    ref.addDependencies(deps);
  }

  /**
   * TODO description
   */

  static validateVersionInfo(info: Manifest) {
    // human readable name to use in errors
    const human = `${info.name}@${info.version}`;

    info.version = PackageRequest.getPackageVersion(info);

    for (const key of constants.REQUIRED_PACKAGE_KEYS) {
      if (!info[key]) {
        throw new MessageError(`Package ${human} doesn't have a ${key}`);
      }
    }
  }

  /**
   * Returns the package version if present, else defaults to the uid
   */

  static getPackageVersion(info: Manifest): string {
    // TODO possibly reconsider this behaviour
    return info.version === undefined ? info._uid : info.version;
  }
}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import {fetch} from '../../helpers/dbcalls'
import {base} from '../../config/constants'
import Flipcard from '../../components/Flipcard/Flipcard'
import SimpleState from 'react-simple-state'
import Warningwindow from '../../components/Warningwindow/Warningwindow'
const simpleState = new SimpleState()

export default class Gameadmin extends Component {

  constructor(props) {
    super(props);
    this.state = {
      cards: [],
      displayName: null
    }
  }

  gameDone = () => {
    this
      .props
      .history
      .push("main")
  }

  componentDidMount(){
    const userId = base
      .app()
      .INTERNAL
      .getUid()
    simpleState.evoke("loader", true)
    if(simpleState.getState("gameId").id === "" && sessionStorage.lobbyNumber === undefined){
      this.props.history.push("join")
    }else{
      let gameId = "";
      if(simpleState.getState("gameId").id !== ""){
        gameId = simpleState.getState("gameId").id;
      }else if(sessionStorage.lobbyNumber !== undefined){
        gameId = sessionStorage.lobbyNumber
      }
      let getCurrentCard = new Promise((resolve, reject) => {
        const collection = 'activegame/' + gameId + "/memberarray/" + userId
        fetch(resolve, reject, collection);
      })
      getCurrentCard.then((data) => {
        if(data.length > 0){
          this.setState({displayName: data[1]})
          let getCardInfos = new Promise((resolve, reject) => {
            const collection = 'cards/' + data[0]
            fetch(resolve, reject, collection);
          })
          getCardInfos.then((data) => {
            let cardObj = {
              description: data[0],
              name: data[1],
              pictureback: data[2],
              picturefront: data[3]
            }
            this.setState({cards: cardObj})
            simpleState.evoke("loader", false)
            base.listenTo('activegame/' + gameId + '/state', {
              context: this,
              asArray: false,
              then(data){
                console.log(data)
                if(data !== "ready"){
                  this
                  .dialog
                  .handleOpen()
                }
              }
            })
          })
        }else{
          this
          .props
          .history
          .push("join")
        }
      })
    }
  }
  render() {
    return (

{this.state.displayName} your card is:

      <Flipcard data={this.state.cards}/>
      <Warningwindow
        message={"Game finished"}
          secondAction={this.gameDone}
          secondActionShow={true}
          secondActionLabel={"Leave game"}
          ref={(dialog) => {
          this.dialog = dialog
        }}/>

    )
  }
}<commit_msg>changed way to know if game still active<commit_after>import React, {Component} from 'react'
import {fetch} from '../../helpers/dbcalls'
import {base} from '../../config/constants'
import Flipcard from '../../components/Flipcard/Flipcard'
import SimpleState from 'react-simple-state'
import Warningwindow from '../../components/Warningwindow/Warningwindow'
const simpleState = new SimpleState()

export default class Gameadmin extends Component {

  constructor(props) {
    super(props);
    this.state = {
      cards: [],
      displayName: null
    }
  }

  gameDone = () => {
    this
      .props
      .history
      .push("main")
  }

  componentDidMount(){
    const userId = base
      .app()
      .INTERNAL
      .getUid()
    simpleState.evoke("loader", true)
    if(simpleState.getState("gameId").id === "" && sessionStorage.lobbyNumber === undefined){
      this.props.history.push("join")
    }else{
      let gameId = "";
      if(simpleState.getState("gameId").id !== ""){
        gameId = simpleState.getState("gameId").id;
      }else if(sessionStorage.lobbyNumber !== undefined){
        gameId = sessionStorage.lobbyNumber
      }
      let getCurrentCard = new Promise((resolve, reject) => {
        const collection = 'activegame/' + gameId + "/memberarray/" + userId
        fetch(resolve, reject, collection);
      })
      getCurrentCard.then((data) => {
        if(data.length > 0){
          this.setState({displayName: data[1]})
          let getCardInfos = new Promise((resolve, reject) => {
            const collection = 'cards/' + data[0]
            fetch(resolve, reject, collection);
          })
          getCardInfos.then((data) => {
            let cardObj = {
              description: data[0],
              name: data[1],
              pictureback: data[2],
              picturefront: data[3]
            }
            this.setState({cards: cardObj})
            simpleState.evoke("loader", false)
            base.listenTo('activegame/' + gameId + "/memberarray/" + userId, {
              context: this,
              asArray: false,
              then(data){
                console.log(data)
                if(data.card === undefined){
                  this
                  .dialog
                  .handleOpen()
                }
              }
            })
          })
        }else{
          this
          .props
          .history
          .push("join")
        }
      })
    }
  }
  render() {
    return (

{this.state.displayName} your card is:

      <Flipcard data={this.state.cards}/>
      <Warningwindow
        message={"Game finished"}
          secondAction={this.gameDone}
          secondActionShow={true}
          secondActionLabel={"Leave game"}
          ref={(dialog) => {
          this.dialog = dialog
        }}/>

    )
  }
}<|endoftext|>