<commit_before>/*
 * Url module. Required for resolving relative paths.
 */

var url = require("url");

/*
 * Regexp from origin Arc90 Readability.
 */

var regexps = {
  unlikelyCandidatesRe: /combx|pager|comment|disqus|foot|header|menu|meta|nav|rss|shoutbox|sidebar|sponsor|share|bookmark|social|advert|leaderboard|instapaper_ignore/i,
  okMaybeItsACandidateRe: /and|article|body|column|main/i,
  positiveRe: /article|body|content|entry|hentry|page|pagination|post|text/i,
  negativeRe: /combx|comment|captcha|contact|foot|footer|footnote|link|media|meta|promo|related|scroll|shoutbox|sponsor|utility|tags|widget/i,
  divToPElementsRe: /<(a|blockquote|dl|div|img|ol|p|pre|table|ul)/i,
  replaceBrsRe: /(
]*>[ \n\r\t]*){2,}/gi,
  replaceFontsRe: /<(\/?)font[^>]*>/gi,
  trimRe: /^\s+|\s+$/g,
  normalizeRe: /\s{2,}/g,
  killBreaksRe: /(
(\s| ?)*){1,}/g,
  videoRe: /http:\/\/(www\.)?(youtube|vimeo|youku|tudou|56|yinyuetai)\.com/i
};

/**
 * Node Types and their classification
 **/

var nodeTypes = {
  'mostPositive': ['div'],
  'positive': ['pre', 'td', 'blockquote'],
  'negative': ['address', 'ol', 'ul', 'dl', 'dd', 'dt', 'li'],
  'mostNegative': ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'th']
}

/**
 * We're getting only the readable content + images, no other nodes are needed.
 **/

var trashNodes = 'meta,iframe,noscript,style,aside,object,form,script';

/**
 * Select the TopCandidate from all possible candidates
 **/

function getArticle(candidates, $) {
  var topCandidate = null;

  candidates.forEach(function(elem) {
    var linkDensity = getLinkDensity(elem, $);
    var score = elem.data('readabilityScore');
    var siblings = elem.find("*").length;

    elem.data('readabilityScore', Math.max(2, siblings) * score * (1 - linkDensity));
    //console.log([elem.get(0).name, elem.attr('class'), elem.data('readabilityScore')]);
    if (!topCandidate || elem.data('readabilityScore') > topCandidate.data('readabilityScore')) {
      topCandidate = elem;
    }
  });
  /**
   * If we still have no top candidate, just use the body as a last resort.
   * We also have to copy the body node so it is something we can modify.
   **/
  if (topCandidate === null) {
    // With no top candidate, bail out if no body tag exists as last resort.
    if (!$('body')) return new Error("No body tag was found.");
    return $('body');
  }
  // Perhaps the topCandidate is the parent?
  if (topCandidate.children().length > 5) {
    return filterCandidates(topCandidate, topCandidate.children(), $);
  } else {
    return filterCandidates(topCandidate, topCandidate.parent().children(), $);
  }
}

/**
 * Filter TopCandidate Siblings (Children) based on their Link Density, and readabilityScore
 * Append the nodes to articleContent
 **/

function filterCandidates(topCandidate, siblings, $) {

  var articleContent = $("

");
  var siblingScoreThreshold = Math.max(10, topCandidate.data('readabilityScore') * 0.2);

  siblings.each(function(index, elem) {
    var sibling = $(this);
    var append = false;
    var type = sibling.get(0).name;
    var score = sibling.data('readabilityScore');
    var children = siblings.contents().length;

    if (sibling === topCandidate || score > siblingScoreThreshold) {
      append = true;
    }
    if (children > 0) {
      siblings.contents().each(function(index, elem) {
        if (elem.name == "img") append = true;
      });
    }

    if (!append && type == 'p') {
      var linkDensity = getLinkDensity(sibling, $);
      var len = sibling.text().length;
      if (len < 3) return;

      if (len > 80 && linkDensity < 0.25) {
        append = true;
      } else if (len < 80 && linkDensity == 0 && sibling.text().replace(regexps.trimRe, "")) {
        append = true;
      }
    }
    if (append) {
      articleContent.append(sibling);
    }
  });
  return articleContent;
}

/**
 * Traverse all Nodes and remove unlikely Candidates.
 **/

function getCandidates($, base) {

  $(trashNodes).remove();
  // Candidates Array
  var candidates = [];

  // Iterate over all Nodes in body
  $('*', 'body').each(function(index, element) {
    var node = $(this);
    if (!node) return; // Avoid Illegal Access error
    var nodeType = node.get(0).name;

    // Remove Unlikely Candidates
    var classAndID = (node.attr('class') || "") + (node.attr('id') || "");
    if (classAndID.search(regexps.unlikelyCandidatesRe) !== -1 && classAndID.search(regexps.okMaybeItsACandidateRe) == -1) {
      node.remove();
      return;
    }

    // Remove Elements that have no children and have no content
    if (nodeType == "div" && node.children().length < 1 && node.html().length < 1) {
      node.remove();
      return;
    }

    // Remove Style
    node.removeAttr('style');

    if (nodeType == "p") {
      var txt = node.text();
      // Ignore too small nodes
      if (txt.length < 25) return;

      var contentScore = 0;

      // Add a point for the paragraph itself as a base. */
      ++contentScore;

      // Add points for any commas within this paragraph */
      // support Chinese commas.
      var commas = txt.match(/[,，.。;；?？、]/g);
      if(commas && commas.length){
        contentScore += commas.length;
      }

      // For every 100 characters in this paragraph, add another point. Up to 3 points. */
      contentScore += Math.min(Math.floor(txt.length / 100), 3);

      // Initialize Parent and Grandparent
      // First initialize the parent node with contentScore / 1, then grandParentNode with contentScore / 2
      var parent = node.parent();

      if(parent && parent.get(0) != "undefined"){
        scoreCandicate(parent, contentScore, candidates);
        var grandParent = parent.parent();
        if(grandParent && grandParent.get(0) != "undefined"){
          scoreCandicate(parent, contentScore/2, candidates);
        }
      }
    }

    // Resolve URLs
    if(base){
      if (nodeType == "img" && typeof node.attr('href') != "undefined") {
        node.attr('src', url.resolve(base, node.attr('src')));
      }
      if (nodeType == "a" && typeof node.attr('href') != "undefined") {
        node.attr('href', url.resolve(base, node.attr('href')));
      }
    }

    // Clean the headers
    if (["h1", "h2", "h3", "h4", "h5", "h6"].indexOf(nodeType) !== -1) {
      var weight = getClassWeight(node, $);
      var density = getLinkDensity(node, $);
      if (weight < 0 || density > 0.3) {
        node.remove();
      }
    }

  });
  return candidates;
}

function scoreCandicate(parent, contentScore, candidates){
  var score;
  if (typeof parent.data('readabilityScore') == "undefined") {
    score = initializeNode(parent);
    candidates.push(parent);
  }else{
    score = parent.data('readabilityScore') || 0;
  }
  parent.data('readabilityScore', score + contentScore)
}
/**
 * Check the type of node, and get its Weight
 **/

function initializeNode(node) {
  if (typeof node.get(0) == "undefined") return 0;
  var tag = node.get(0).name;
  if (nodeTypes['mostPositive'].indexOf(tag)) return 5 + getClassWeight(node);
  if (nodeTypes['positive'].indexOf(tag)) return 3 + getClassWeight(node);
  if (nodeTypes['negative'].indexOf(tag)) return -3 + getClassWeight(node);
  if (nodeTypes['mostNegative'].indexOf(tag)) return -5 + getClassWeight(node);
}

/**
 * Node Weight is calculated based on className and ID of the node.
 **/

function getClassWeight(node) {
  if (node == null) return 0;
  var classAndID = (node.attr('class') || "") + (node.attr('id') || "");
  var weight = 0;

  if (node.get(0).name == "article") weight += 25;
  if (classAndID.search(regexps.negativeRe) !== -1) weight -= 25;
  if (classAndID.search(regexps.positiveRe) !== -1) weight += 25;

  return weight;
}

/**
 * Get Link density of this node.
 * Total length of link text in this node divided by the total text of the node.
 * Relative links are not included.
 **/

function getLinkDensity(node, $) {
  var links = node.find('a');
  var textLength = node.text().length;
  var linkLength = 0;
  links.each(function(index, elem) {
    var href = $(this).attr('href');
    if (!href || (href.length > 0 && href[0] === '#')) return;
    linkLength += $(this).text().length;
  });
  return (linkLength / textLength) || 0;
}

/**
 * Main method
 * If the first run does not succeed, try the body element;
 **/

module.exports.extract = function($, base) {
  var candidates = getCandidates($, base);
  article = getArticle(candidates, $);
  if (article.length < 1) {
    article = getArticle([$('body')], $)
  }
  return article;
}<commit_msg>remove console comment<commit_after>/*
 * Url module. Required for resolving relative paths.
 */

var url = require("url");

/*
 * Regexp from origin Arc90 Readability.
 */

var regexps = {
  unlikelyCandidatesRe: /combx|pager|comment|disqus|foot|header|menu|meta|nav|rss|shoutbox|sidebar|sponsor|share|bookmark|social|advert|leaderboard|instapaper_ignore/i,
  okMaybeItsACandidateRe: /and|article|body|column|main/i,
  positiveRe: /article|body|content|entry|hentry|page|pagination|post|text/i,
  negativeRe: /combx|comment|captcha|contact|foot|footer|footnote|link|media|meta|promo|related|scroll|shoutbox|sponsor|utility|tags|widget/i,
  divToPElementsRe: /<(a|blockquote|dl|div|img|ol|p|pre|table|ul)/i,
  replaceBrsRe: /(
]*>[ \n\r\t]*){2,}/gi,
  replaceFontsRe: /<(\/?)font[^>]*>/gi,
  trimRe: /^\s+|\s+$/g,
  normalizeRe: /\s{2,}/g,
  killBreaksRe: /(
(\s| ?)*){1,}/g,
  videoRe: /http:\/\/(www\.)?(youtube|vimeo|youku|tudou|56|yinyuetai)\.com/i
};

/**
 * Node Types and their classification
 **/

var nodeTypes = {
  'mostPositive': ['div'],
  'positive': ['pre', 'td', 'blockquote'],
  'negative': ['address', 'ol', 'ul', 'dl', 'dd', 'dt', 'li'],
  'mostNegative': ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'th']
}

/**
 * We're getting only the readable content + images, no other nodes are needed.
 **/

var trashNodes = 'meta,iframe,noscript,style,aside,object,form,script';

/**
 * Select the TopCandidate from all possible candidates
 **/

function getArticle(candidates, $) {
  var topCandidate = null;

  candidates.forEach(function(elem) {
    var linkDensity = getLinkDensity(elem, $);
    var score = elem.data('readabilityScore');
    var siblings = elem.find("*").length;

    elem.data('readabilityScore', Math.max(2, siblings) * score * (1 - linkDensity));
    if (!topCandidate || elem.data('readabilityScore') > topCandidate.data('readabilityScore')) {
      topCandidate = elem;
    }
  });
  /**
   * If we still have no top candidate, just use the body as a last resort.
   * We also have to copy the body node so it is something we can modify.
   **/
  if (topCandidate === null) {
    // With no top candidate, bail out if no body tag exists as last resort.
    if (!$('body')) return new Error("No body tag was found.");
    return $('body');
  }
  // Perhaps the topCandidate is the parent?
  if (topCandidate.children().length > 5) {
    return filterCandidates(topCandidate, topCandidate.children(), $);
  } else {
    return filterCandidates(topCandidate, topCandidate.parent().children(), $);
  }
}

/**
 * Filter TopCandidate Siblings (Children) based on their Link Density, and readabilityScore
 * Append the nodes to articleContent
 **/

function filterCandidates(topCandidate, siblings, $) {

  var articleContent = $("

");
  var siblingScoreThreshold = Math.max(10, topCandidate.data('readabilityScore') * 0.2);

  siblings.each(function(index, elem) {
    var sibling = $(this);
    var append = false;
    var type = sibling.get(0).name;
    var score = sibling.data('readabilityScore');
    var children = siblings.contents().length;

    if (sibling === topCandidate || score > siblingScoreThreshold) {
      append = true;
    }
    if (children > 0) {
      siblings.contents().each(function(index, elem) {
        if (elem.name == "img") append = true;
      });
    }

    if (!append && type == 'p') {
      var linkDensity = getLinkDensity(sibling, $);
      var len = sibling.text().length;
      if (len < 3) return;

      if (len > 80 && linkDensity < 0.25) {
        append = true;
      } else if (len < 80 && linkDensity == 0 && sibling.text().replace(regexps.trimRe, "")) {
        append = true;
      }
    }
    if (append) {
      articleContent.append(sibling);
    }
  });
  return articleContent;
}

/**
 * Traverse all Nodes and remove unlikely Candidates.
 **/

function getCandidates($, base) {

  $(trashNodes).remove();
  // Candidates Array
  var candidates = [];

  // Iterate over all Nodes in body
  $('*', 'body').each(function(index, element) {
    var node = $(this);
    if (!node) return; // Avoid Illegal Access error
    var nodeType = node.get(0).name;

    // Remove Unlikely Candidates
    var classAndID = (node.attr('class') || "") + (node.attr('id') || "");
    if (classAndID.search(regexps.unlikelyCandidatesRe) !== -1 && classAndID.search(regexps.okMaybeItsACandidateRe) == -1) {
      node.remove();
      return;
    }

    // Remove Elements that have no children and have no content
    if (nodeType == "div" && node.children().length < 1 && node.html().length < 1) {
      node.remove();
      return;
    }

    // Remove Style
    node.removeAttr('style');

    if (nodeType == "p") {
      var txt = node.text();
      // Ignore too small nodes
      if (txt.length < 25) return;

      var contentScore = 0;

      // Add a point for the paragraph itself as a base. */
      ++contentScore;

      // Add points for any commas within this paragraph */
      // support Chinese commas.
      var commas = txt.match(/[,，.。;；?？、]/g);
      if(commas && commas.length){
        contentScore += commas.length;
      }

      // For every 100 characters in this paragraph, add another point. Up to 3 points. */
      contentScore += Math.min(Math.floor(txt.length / 100), 3);

      // Initialize Parent and Grandparent
      // First initialize the parent node with contentScore / 1, then grandParentNode with contentScore / 2
      var parent = node.parent();

      if(parent && parent.get(0) != "undefined"){
        scoreCandicate(parent, contentScore, candidates);
        var grandParent = parent.parent();
        if(grandParent && grandParent.get(0) != "undefined"){
          scoreCandicate(parent, contentScore/2, candidates);
        }
      }
    }

    // Resolve URLs
    if(base){
      if (nodeType == "img" && typeof node.attr('href') != "undefined") {
        node.attr('src', url.resolve(base, node.attr('src')));
      }
      if (nodeType == "a" && typeof node.attr('href') != "undefined") {
        node.attr('href', url.resolve(base, node.attr('href')));
      }
    }

    // Clean the headers
    if (["h1", "h2", "h3", "h4", "h5", "h6"].indexOf(nodeType) !== -1) {
      var weight = getClassWeight(node, $);
      var density = getLinkDensity(node, $);
      if (weight < 0 || density > 0.3) {
        node.remove();
      }
    }

  });
  return candidates;
}

function scoreCandicate(parent, contentScore, candidates){
  var score;
  if (typeof parent.data('readabilityScore') == "undefined") {
    score = initializeNode(parent);
    candidates.push(parent);
  }else{
    score = parent.data('readabilityScore') || 0;
  }
  parent.data('readabilityScore', score + contentScore)
}
/**
 * Check the type of node, and get its Weight
 **/

function initializeNode(node) {
  if (typeof node.get(0) == "undefined") return 0;
  var tag = node.get(0).name;
  if (nodeTypes['mostPositive'].indexOf(tag)) return 5 + getClassWeight(node);
  if (nodeTypes['positive'].indexOf(tag)) return 3 + getClassWeight(node);
  if (nodeTypes['negative'].indexOf(tag)) return -3 + getClassWeight(node);
  if (nodeTypes['mostNegative'].indexOf(tag)) return -5 + getClassWeight(node);
}

/**
 * Node Weight is calculated based on className and ID of the node.
 **/

function getClassWeight(node) {
  if (node == null) return 0;
  var classAndID = (node.attr('class') || "") + (node.attr('id') || "");
  var weight = 0;

  if (node.get(0).name == "article") weight += 25;
  if (classAndID.search(regexps.negativeRe) !== -1) weight -= 25;
  if (classAndID.search(regexps.positiveRe) !== -1) weight += 25;

  return weight;
}

/**
 * Get Link density of this node.
 * Total length of link text in this node divided by the total text of the node.
 * Relative links are not included.
 **/

function getLinkDensity(node, $) {
  var links = node.find('a');
  var textLength = node.text().length;
  var linkLength = 0;
  links.each(function(index, elem) {
    var href = $(this).attr('href');
    if (!href || (href.length > 0 && href[0] === '#')) return;
    linkLength += $(this).text().length;
  });
  return (linkLength / textLength) || 0;
}

/**
 * Main method
 * If the first run does not succeed, try the body element;
 **/

module.exports.extract = function($, base) {
  var candidates = getCandidates($, base);
  article = getArticle(candidates, $);
  if (article.length < 1) {
    article = getArticle([$('body')], $)
  }
  return article;
}<|endoftext|>


<commit_before>/*
 * EJS Embedded JavaScript templates
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

/**
 * Private utility functions
 * @module utils
 * @private
 */

'use strict';

var regExpChars = /[|\\{}()[\]^$+*?.]/g;

/**
 * Escape characters reserved in regular expressions.
 *
 * If `string` is `undefined` or `null`, the empty string is returned.
 *
 * @param {String} string Input string
 * @return {String} Escaped string
 * @static
 * @private
 */
exports.escapeRegExpChars = function (string) {
  // istanbul ignore if
  if (!string) {
    return '';
  }
  return String(string).replace(regExpChars, '\\$&');
};

var _ENCODE_HTML_RULES = {
      '&': '&'
    , '<': '<'
    , '>': '>'
    , '"': '"'
    , "'": '''
    }
  , _MATCH_HTML = /[&<>\'"]/g;

function encode_char(c) {
  return _ENCODE_HTML_RULES[c] || c;
};

/**
 * Stringified version of constants used by {@link module:utils.escapeXML}.
 *
 * It is used in the process of generating {@link ClientFunction}s.
 *
 * @readonly
 * @type {String}
 */

var escapeFuncStr =
  'var _ENCODE_HTML_RULES = {\n'
+ '      "&": "&"\n'
+ '    , "<": "<"\n'
+ '    , ">": ">"\n'
+ '    , \'"\': """\n'
+ '    , "\'": "'"\n'
+ '    }\n'
+ '  , _MATCH_HTML = /[&<>\'"]/g;\n'
+ 'function encode_char(c) {\n'
+ '  return _ENCODE_HTML_RULES[c] || c;\n'
+ '};\n';

/**
 * Escape characters reserved in XML.
 *
 * If `markup` is `undefined` or `null`, the empty string is returned.
 *
 * @implements {EscapeCallback}
 * @param {String} markup Input string
 * @return {String} Escaped string
 * @static
 * @private
 */

exports.escapeXML = function (markup) {
  return markup == undefined
    ? ''
    : String(markup)
        .replace(_MATCH_HTML, encode_char);
};
exports.escapeXML.toString = function () {
  return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr
};

/**
 * Copy all properties from one object to another, in a shallow fashion.
 *
 * @param  {Object} to   Destination object
 * @param  {Object} from Source object
 * @return {Object}      Destination object
 * @static
 * @private
 */
exports.shallowCopy = function (to, from) {
  from = from || {};
  for (var p in from) {
    to[p] = from[p];
  }
  return to;
};

/**
 * Simple in-process cache implementation. Does not implement limits of any
 * sort.
 *
 * @implements Cache
 * @static
 * @private
 */
exports.cache = {
  _data: {},
  set: function (key, val) {
    this._data[key] = val;
  },
  get: function (key) {
    return this._data[key];
  },
  reset: function () {
    this._data = {};
  }
};

/**
 * wrap a generator object to promise with given arguments.
 *
 * @param  {GeneratorFunction} gen       generator function you want to use
 * @param  {Object}            self      self object for generator
 * @param  {Function}          Promise   promise factory to use with this function
 * @param  {...*}              args      arguments that with pass to generator
 * @return {Promise}                     es6 promise object
 * @static
 * @private
 */
exports.wrapToPromise = function wrapToPromise(gen, self, Promise, args) {
  var defered = {};
  var args = [].slice.call(arguments, 3);
  var inst = gen.apply(self, args);

  var interrupted = false;

  var resultPromise = new Promise(function (resolve, reject) {
    defered.resolve = resolve
    defered.reject = reject

    // hook to prevent the promise from end instantlly and allow us to alter the result
    defered.onResolve = null;
    // hook to prevent the promise from end instantlly and allow us to alter the result
    defered.onReject = null;

    defered.interrupt = function () {
      interrupted = true
      if (defered.onReject) {
        defered.onReject(defered, new Error('interrupted by user'));
      } else {
        defered.reject(new Error('interrupted by user'));
      }
    }
  });
  resultPromise.defered = defered;
  resultPromise.execute = function () {
    wrapThrow(function () {
      next(inst, {done: false}, defered)
    });
    return resultPromise;
  }
  function wrapThrow(fn) {
    try {
      fn();
    } catch (err) {
      // interrupt may also be called before here
      if (interrupted) return;

      if ("function" === typeof defered.onReject) {
        defered.onReject(defered, err);
      } else {
        defered.reject(err);
      }
      return false;
    }
    return true;
  }

  function next(inst, result, defered) {
    // if this was interrupted by user, abort all calls
    if (interrupted) return;

    var isPromise;
    while (true) {
      // interrupt may also be called before here
      if (interrupted) return;

      isPromise =
        'object' === typeof result.value &&
        'function' === typeof result.value.then;

      if (result.done) {
        if ("function" === typeof defered.onResolve) {
          return defered.onResolve(defered);
        } else {
          return defered.resolve();
        }
      }
      if (isPromise) {
        // it is a promise
        result.value.then(function (value) {
          wrapThrow(function () {
            next(inst, inst.next(value), defered);
          });
        }, function (err) {
          wrapThrow(function () {
            next(inst, inst.throw(err), defered);
          });
        })
      } else {
        if (wrapThrow(function () {
          result = inst.next(result.value);
        })) {
          continue;
        } else {
          break;
        }
      }
      break;
    }
  }
  return resultPromise
}<commit_msg>add missed function required for interrupt<commit_after>/*
 * EJS Embedded JavaScript templates
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

/**
 * Private utility functions
 * @module utils
 * @private
 */

'use strict';

var regExpChars = /[|\\{}()[\]^$+*?.]/g;

/**
 * Escape characters reserved in regular expressions.
 *
 * If `string` is `undefined` or `null`, the empty string is returned.
 *
 * @param {String} string Input string
 * @return {String} Escaped string
 * @static
 * @private
 */
exports.escapeRegExpChars = function (string) {
  // istanbul ignore if
  if (!string) {
    return '';
  }
  return String(string).replace(regExpChars, '\\$&');
};

var _ENCODE_HTML_RULES = {
      '&': '&'
    , '<': '<'
    , '>': '>'
    , '"': '"'
    , "'": '''
    }
  , _MATCH_HTML = /[&<>\'"]/g;

function encode_char(c) {
  return _ENCODE_HTML_RULES[c] || c;
};

/**
 * Stringified version of constants used by {@link module:utils.escapeXML}.
 *
 * It is used in the process of generating {@link ClientFunction}s.
 *
 * @readonly
 * @type {String}
 */

var escapeFuncStr =
  'var _ENCODE_HTML_RULES = {\n'
+ '      "&": "&"\n'
+ '    , "<": "<"\n'
+ '    , ">": ">"\n'
+ '    , \'"\': """\n'
+ '    , "\'": "'"\n'
+ '    }\n'
+ '  , _MATCH_HTML = /[&<>\'"]/g;\n'
+ 'function encode_char(c) {\n'
+ '  return _ENCODE_HTML_RULES[c] || c;\n'
+ '};\n';

/**
 * Escape characters reserved in XML.
 *
 * If `markup` is `undefined` or `null`, the empty string is returned.
 *
 * @implements {EscapeCallback}
 * @param {String} markup Input string
 * @return {String} Escaped string
 * @static
 * @private
 */

exports.escapeXML = function (markup) {
  return markup == undefined
    ? ''
    : String(markup)
        .replace(_MATCH_HTML, encode_char);
};
exports.escapeXML.toString = function () {
  return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr
};

/**
 * Copy all properties from one object to another, in a shallow fashion.
 *
 * @param  {Object} to   Destination object
 * @param  {Object} from Source object
 * @return {Object}      Destination object
 * @static
 * @private
 */
exports.shallowCopy = function (to, from) {
  from = from || {};
  for (var p in from) {
    to[p] = from[p];
  }
  return to;
};

/**
 * Simple in-process cache implementation. Does not implement limits of any
 * sort.
 *
 * @implements Cache
 * @static
 * @private
 */
exports.cache = {
  _data: {},
  set: function (key, val) {
    this._data[key] = val;
  },
  get: function (key) {
    return this._data[key];
  },
  reset: function () {
    this._data = {};
  }
};

/**
 * wrap a generator object to promise with given arguments.
 *
 * @param  {GeneratorFunction} gen       generator function you want to use
 * @param  {Object}            self      self object for generator
 * @param  {Function}          Promise   promise factory to use with this function
 * @param  {...*}              args      arguments that with pass to generator
 * @return {Promise}                     es6 promise object
 * @static
 * @private
 */
exports.wrapToPromise = function wrapToPromise(gen, self, Promise, args) {
  var defered = {};
  var args = [].slice.call(arguments, 3);
  var inst = gen.apply(self, args);

  var interrupted = false;

  var resultPromise = new Promise(function (resolve, reject) {
    defered.resolve = resolve
    defered.reject = reject

    // hook to prevent the promise from end instantlly and allow us to alter the result
    defered.onResolve = null;
    // hook to prevent the promise from end instantlly and allow us to alter the result
    defered.onReject = null;

    defered.onInterrupt = null;
    defered.interrupt = function () {
      if (defered.onInterrupt) {
        return _interrupt();
      }
      defered.onInterrupt(_interrupt);
    }

    var _interrupt = function () {
      interrupted = true
      if (defered.onReject) {
        defered.onReject(defered, new Error('interrupted by user'));
      } else {
        defered.reject(new Error('interrupted by user'));
      }
    }
  });
  resultPromise.defered = defered;
  resultPromise.execute = function () {
    wrapThrow(function () {
      next(inst, {done: false}, defered)
    });
    return resultPromise;
  }
  function wrapThrow(fn) {
    try {
      fn();
    } catch (err) {
      // interrupt may also be called before here
      if (interrupted) return;

      if ("function" === typeof defered.onReject) {
        defered.onReject(defered, err);
      } else {
        defered.reject(err);
      }
      return false;
    }
    return true;
  }

  function next(inst, result, defered) {
    // if this was interrupted by user, abort all calls
    if (interrupted) return;

    var isPromise;
    while (true) {
      // interrupt may also be called before here
      if (interrupted) return;

      isPromise =
        'object' === typeof result.value &&
        'function' === typeof result.value.then;

      if (result.done) {
        if ("function" === typeof defered.onResolve) {
          return defered.onResolve(defered);
        } else {
          return defered.resolve();
        }
      }
      if (isPromise) {
        // it is a promise
        result.value.then(function (value) {
          wrapThrow(function () {
            next(inst, inst.next(value), defered);
          });
        }, function (err) {
          wrapThrow(function () {
            next(inst, inst.throw(err), defered);
          });
        })
      } else {
        if (wrapThrow(function () {
          result = inst.next(result.value);
        })) {
          continue;
        } else {
          break;
        }
      }
      break;
    }
  }
  return resultPromise
}<|endoftext|>


<commit_before>var winston = require('winston'),
    path    = require('path');

var logLevel = 'warn';

var sharedLogger = function(filename) {
  return new winston.Logger({
    level: logLevel,
    transports: [
      new winston.transports.Console({
        prettyPrint: true,
        timestamp: true,
        level: logLevel,
        label: path.basename(filename, '.js')
      })
    ]
  });
};

sharedLogger.setLevel = function (val) {
  logLevel = val;
}

function failedPromiseHandler(logger) {
  logger = logger || sharedLogger;

  return function(err) {
    logger.error(err.stack);
  };
}

function uuid() {
	return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

module.exports = {
  logger: sharedLogger,
  failedPromiseHandler: failedPromiseHandler,
  uuid: uuid
}
<commit_msg>Create default logger object if one is not sent<commit_after>var winston = require('winston'),
    path    = require('path');

var logLevel = 'warn';

var sharedLogger = function(filename) {
  return new winston.Logger({
    level: logLevel,
    transports: [
      new winston.transports.Console({
        prettyPrint: true,
        timestamp: true,
        level: logLevel,
        label: path.basename(filename, '.js')
      })
    ]
  });
};

sharedLogger.setLevel = function (val) {
  logLevel = val;
}

function failedPromiseHandler(logger) {
  logger = logger || sharedLogger(__filename);

  return function(err) {
    logger.error(err.stack);
  };
}

function uuid() {
	return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

module.exports = {
  logger: sharedLogger,
  failedPromiseHandler: failedPromiseHandler,
  uuid: uuid
}
<|endoftext|>


<commit_before>
// Taken from https://muffinresearch.co.uk/removing-leading-whitespace-in-es6-template-strings/
export const singleLineString = (strings, ...values) => {
  let output = '';
  for (let i = 0; i < values.length; i++) {
    output += strings[i] + values[i];
  }
  output += strings[values.length];

  let lines = output.split(/(?:\r\n|\n|\r)/);

  return lines.map((line) => {
    return line.replace(/^\s+/gm, '');
  }).join(' ').trim();
};

export const print = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    const msg = method.apply(this, args);
    console.log(singleLineString`${msg}`);
    args[0] ? console.log(err) : void(0);
    return this;
  }
}

export const exit = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    method.apply(this, args);
    process.exit(1);
    return this;
  }
}

export const promiseWrapper = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    try {
      fn.apply(this, args);
      resolve();
    } catch (err) {
      reject(err);
    }
  });
};
<commit_msg>fix(print): Print out errors now uses right variable<commit_after>
// Taken from https://muffinresearch.co.uk/removing-leading-whitespace-in-es6-template-strings/
export const singleLineString = (strings, ...values) => {
  let output = '';
  for (let i = 0; i < values.length; i++) {
    output += strings[i] + values[i];
  }
  output += strings[values.length];

  let lines = output.split(/(?:\r\n|\n|\r)/);

  return lines.map((line) => {
    return line.replace(/^\s+/gm, '');
  }).join(' ').trim();
};

export const print = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    const msg = method.apply(this, args);
    console.log(singleLineString`${msg}`);
    if (args[0]) console.error(args[0]);
    return this;
  }
}

export const exit = (target, name, descriptor) => {
  const method = descriptor.value;

  descriptor.value = function (...args) {
    method.apply(this, args);
    process.exit(1);
    return this;
  }
}

export const promiseWrapper = (fn, ...args) => {
  return new Promise((resolve, reject) => {
    try {
      fn.apply(this, args);
      resolve();
    } catch (err) {
      reject(err);
    }
  });
};
<|endoftext|>


<commit_before>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var exec = require('child_process').exec
  , utils = new (function () {

  this.exec = function (arr, callback, opts) {
    var options = opts || {}
      , stdout = options.stdout
      , stderr = options.stderr
      , breakOnError = typeof options.breakOnError != 'undefined' ?
            options.breakOnError : true
    var run = function (cmd) {
      exec(cmd, function (err, stdout, stderr) {
        var next;
        if (err && breakOnError) {
          this.fail('Error: ' + JSON.stringify(err));
        }
        if (stderr && options.stderr) {
          console.log('Error: ' + stderr);
        }
        if (stdout && options.stdout) {
          console.log(stdout);
        }
        next = arr.shift();
        if (next) {
          run(next);
        }
        else {
          if (callback) {
            callback();
          }
        }
      });
    };
    run(arr.shift());
  };

})();

module.exports = utils;
<commit_msg>Cleaner exec iteration.<commit_after>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var exec = require('child_process').exec
  , utils = new (function () {

  this.exec = function (arr, callback, opts) {
    var options = opts || {}
      , list = arr.slice()
      , stdout = options.stdout
      , stderr = options.stderr
      , breakOnError = typeof options.breakOnError != 'undefined' ?
            options.breakOnError : true
      , run;

    run = function () {
      var next = list.shift();
      if (next) {
        exec(next, function (err, stdout, stderr) {
          if (err && breakOnError) {
            this.fail('Error: ' + JSON.stringify(err));
          }
          if (stderr && options.stderr) {
            console.log('Error: ' + stderr);
          }
          if (stdout && options.stdout) {
            console.log(stdout);
          }
          run();
        });
      }
      else {
        if (callback) {
          callback();
        }
      }
    };

    run();
  };

})();

module.exports = utils;
<|endoftext|>


<commit_before>function convertOptionTagName(tag, toReadable){
	var response;
	if(toReadable){
		response = tag;
	}
	else{
		response = tag.toLowerCase().replace(" ", "-");
	}
	return response;
}<commit_msg>Completed second case tag converter<commit_after>function convertOptionTagName(tag, toReadable){
	var response;
	if(toReadable){
		response = tag;
	}
	else{
		response = tag.replace(/\s+/g, '-').toLowerCase();
	}
	return response;
}<|endoftext|>


<commit_before>/* jslint node: true */
const fs = require('fs')
const colors = require('colors/safe')
const notifications = require('../data/datacache').notifications
const challenges = require('../data/datacache').challenges
const packageJson = require('../package.json')
const sanitizeHtml = require('sanitize-html')
const jsSHA = require('jssha')
const Entities = require('html-entities').AllHtmlEntities
const config = require('config')
const entities = new Entities()
const download = require('download')
const crypto = require('crypto')
const clarinet = require('clarinet')
const isDocker = require('is-docker')
const isHeroku = require('is-heroku')
const logger = require('../lib/logger')

const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

let ctfKey
if (process.env.CTF_KEY !== undefined && process.env.CTF_KEY !== '') {
  ctfKey = process.env.CTF_KEY
} else {
  fs.readFile('ctf.key', 'utf8', (err, data) => {
    if (err) {
      throw err
    }
    ctfKey = data
  })
}

exports.queryResultToJson = (data, status) => {
  let wrappedData = {}
  if (data) {
    if (!data.length && data.dataValues) {
      wrappedData = data.dataValues
    } else if (data.length > 0) {
      wrappedData = []
      for (let i = 0; i < data.length; i++) {
        wrappedData.push(data[i] && data[i].dataValues ? data[i].dataValues : data[i])
      }
    } else {
      wrappedData = data
    }
  }
  return {
    status: status || 'success',
    data: wrappedData
  }
}

exports.startsWith = (str, prefix) => str ? str.indexOf(prefix) === 0 : false

exports.endsWith = (str, suffix) => str ? str.indexOf(suffix, str.length - suffix.length) !== -1 : false

exports.contains = (str, element) => str ? str.includes(element) : false

exports.containsEscaped = function (str, element) {
  return this.contains(str, element.replace(/"/g, '\\"'))
}

exports.containsOrEscaped = function (str, element) {
  return this.contains(str, element) || this.containsEscaped(str, element)
}

exports.unquote = function (str) {
  if (str && this.startsWith(str, '"') && this.endsWith(str, '"')) {
    return str.substring(1, str.length - 1)
  } else {
    return str
  }
}

exports.trunc = function (str, length) {
  str = str.replace(/(\r\n|\n|\r)/gm, '')
  return (str.length > length) ? str.substr(0, length - 1) + '...' : str
}

exports.version = module => {
  if (module) {
    return packageJson.dependencies[module]
  } else {
    return packageJson.version
  }
}

exports.ctfFlag = text => {
  const shaObj = new jsSHA('SHA-1', 'TEXT') // eslint-disable-line new-cap
  shaObj.setHMACKey(ctfKey, 'TEXT')
  shaObj.update(text)
  return shaObj.getHMAC('HEX')
}

exports.solve = function (challenge, isRestore) {
  const self = this
  challenge.solved = true
  challenge.save().then(solvedChallenge => {
    solvedChallenge.description = entities.decode(sanitizeHtml(solvedChallenge.description, {
      allowedTags: [],
      allowedAttributes: []
    }))
    logger.info(colors.green('Solved') + ' challenge ' + colors.cyan(solvedChallenge.name) + ' (' + solvedChallenge.description + ')')
    self.sendNotification(solvedChallenge, isRestore)
  })
}

exports.sendNotification = function (challenge, isRestore) {
  if (!this.notSolved(challenge)) {
    const flag = this.ctfFlag(challenge.name)
    const notification = {
      key: challenge.key,
      name: challenge.name,
      challenge: challenge.name + ' (' + challenge.description + ')',
      flag: flag,
      hidden: !config.get('application.showChallengeSolvedNotifications'),
      isRestore: isRestore
    }
    const wasPreviouslyShown = notifications.find(({ key }) => key === challenge.key) !== undefined
    notifications.push(notification)

    if (global.io && (isRestore || wasPreviouslyShown)) {
      global.io.emit('challenge solved', notification)
    }
  }
}

exports.notSolved = challenge => challenge && !challenge.solved

exports.findChallenge = challengeName => {
  for (const name in challenges) {
    if (challenges.hasOwnProperty(name)) {
      if (challenges[name].name === challengeName) {
        return challenges[name]
      }
    }
  }
  logger.warn('Missing challenge with name: ' + challengeName)
}

exports.toMMMYY = date => {
  const month = date.getMonth()
  const year = date.getFullYear()
  return months[month] + year.toString().substring(2, 4)
}

exports.toISO8601 = date => {
  let day = '' + date.getDate()
  let month = '' + (date.getMonth() + 1)
  const year = date.getFullYear()

  if (month.length < 2) month = '0' + month
  if (day.length < 2) day = '0' + day

  return [year, month, day].join('-')
}

exports.downloadToFile = (url, dest) => {
  download(url).then(data => {
    fs.writeFileSync(dest, data)
  }).catch(err => {
    logger.warn('Failed to download ' + url + ' (' + err.statusMessage + ')')
  })
}

exports.jwtFrom = ({ headers }) => {
  if (headers && headers.authorization) {
    const parts = headers.authorization.split(' ')
    if (parts.length === 2) {
      const scheme = parts[0]
      const token = parts[1]

      if (/^Bearer$/i.test(scheme)) {
        return token
      }
    }
  }
  return undefined
}

exports.randomHexString = (length) => {
  return crypto.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length)
}

exports.disableOnContainerEnv = () => {
  return (isDocker() || isHeroku) && !config.get('challenges.safetyOverride')
}

exports.determineDisabledContainerEnv = (disabledEnv) => {
  if (isDocker()) {
    return disabledEnv && (disabledEnv === 'Docker' || disabledEnv.includes('Docker')) ? 'Docker' : null
  } else if (isHeroku) {
    return disabledEnv && (disabledEnv === 'Heroku' || disabledEnv.includes('Heroku')) ? 'Heroku' : null
  }
  return null
}

exports.parseJsonCustom = (jsonString) => {
  const parser = clarinet.parser()
  const result = []
  parser.onkey = parser.onopenobject = k => {
    result.push({ key: k, value: null })
  }
  parser.onvalue = v => {
    result[result.length - 1].value = v
  }
  parser.write(jsonString).close()
  return result
}

exports.toSimpleIpAddress = (ipv6) => {
  if (this.startsWith(ipv6, '::ffff:')) {
    return ipv6.substr(7)
  } else if (ipv6 === '::1') {
    return '127.0.0.1'
  } else {
    return ipv6
  }
}
<commit_msg>Fix challenge solved notification not beeing send<commit_after>/* jslint node: true */
const fs = require('fs')
const colors = require('colors/safe')
const notifications = require('../data/datacache').notifications
const challenges = require('../data/datacache').challenges
const packageJson = require('../package.json')
const sanitizeHtml = require('sanitize-html')
const jsSHA = require('jssha')
const Entities = require('html-entities').AllHtmlEntities
const config = require('config')
const entities = new Entities()
const download = require('download')
const crypto = require('crypto')
const clarinet = require('clarinet')
const isDocker = require('is-docker')
const isHeroku = require('is-heroku')
const logger = require('../lib/logger')

const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

let ctfKey
if (process.env.CTF_KEY !== undefined && process.env.CTF_KEY !== '') {
  ctfKey = process.env.CTF_KEY
} else {
  fs.readFile('ctf.key', 'utf8', (err, data) => {
    if (err) {
      throw err
    }
    ctfKey = data
  })
}

exports.queryResultToJson = (data, status) => {
  let wrappedData = {}
  if (data) {
    if (!data.length && data.dataValues) {
      wrappedData = data.dataValues
    } else if (data.length > 0) {
      wrappedData = []
      for (let i = 0; i < data.length; i++) {
        wrappedData.push(data[i] && data[i].dataValues ? data[i].dataValues : data[i])
      }
    } else {
      wrappedData = data
    }
  }
  return {
    status: status || 'success',
    data: wrappedData
  }
}

exports.startsWith = (str, prefix) => str ? str.indexOf(prefix) === 0 : false

exports.endsWith = (str, suffix) => str ? str.indexOf(suffix, str.length - suffix.length) !== -1 : false

exports.contains = (str, element) => str ? str.includes(element) : false

exports.containsEscaped = function (str, element) {
  return this.contains(str, element.replace(/"/g, '\\"'))
}

exports.containsOrEscaped = function (str, element) {
  return this.contains(str, element) || this.containsEscaped(str, element)
}

exports.unquote = function (str) {
  if (str && this.startsWith(str, '"') && this.endsWith(str, '"')) {
    return str.substring(1, str.length - 1)
  } else {
    return str
  }
}

exports.trunc = function (str, length) {
  str = str.replace(/(\r\n|\n|\r)/gm, '')
  return (str.length > length) ? str.substr(0, length - 1) + '...' : str
}

exports.version = module => {
  if (module) {
    return packageJson.dependencies[module]
  } else {
    return packageJson.version
  }
}

exports.ctfFlag = text => {
  const shaObj = new jsSHA('SHA-1', 'TEXT') // eslint-disable-line new-cap
  shaObj.setHMACKey(ctfKey, 'TEXT')
  shaObj.update(text)
  return shaObj.getHMAC('HEX')
}

exports.solve = function (challenge, isRestore) {
  const self = this
  challenge.solved = true
  challenge.save().then(solvedChallenge => {
    solvedChallenge.description = entities.decode(sanitizeHtml(solvedChallenge.description, {
      allowedTags: [],
      allowedAttributes: []
    }))
    logger.info(colors.green('Solved') + ' challenge ' + colors.cyan(solvedChallenge.name) + ' (' + solvedChallenge.description + ')')
    self.sendNotification(solvedChallenge, isRestore)
  })
}

exports.sendNotification = function (challenge, isRestore) {
  if (!this.notSolved(challenge)) {
    const flag = this.ctfFlag(challenge.name)
    const notification = {
      key: challenge.key,
      name: challenge.name,
      challenge: challenge.name + ' (' + challenge.description + ')',
      flag: flag,
      hidden: !config.get('application.showChallengeSolvedNotifications'),
      isRestore: isRestore
    }
    const wasPreviouslyShown = notifications.find(({ key }) => key === challenge.key) !== undefined
    notifications.push(notification)

    if (global.io && (isRestore || !wasPreviouslyShown)) {
      global.io.emit('challenge solved', notification)
    }
  }
}

exports.notSolved = challenge => challenge && !challenge.solved

exports.findChallenge = challengeName => {
  for (const name in challenges) {
    if (challenges.hasOwnProperty(name)) {
      if (challenges[name].name === challengeName) {
        return challenges[name]
      }
    }
  }
  logger.warn('Missing challenge with name: ' + challengeName)
}

exports.toMMMYY = date => {
  const month = date.getMonth()
  const year = date.getFullYear()
  return months[month] + year.toString().substring(2, 4)
}

exports.toISO8601 = date => {
  let day = '' + date.getDate()
  let month = '' + (date.getMonth() + 1)
  const year = date.getFullYear()

  if (month.length < 2) month = '0' + month
  if (day.length < 2) day = '0' + day

  return [year, month, day].join('-')
}

exports.downloadToFile = (url, dest) => {
  download(url).then(data => {
    fs.writeFileSync(dest, data)
  }).catch(err => {
    logger.warn('Failed to download ' + url + ' (' + err.statusMessage + ')')
  })
}

exports.jwtFrom = ({ headers }) => {
  if (headers && headers.authorization) {
    const parts = headers.authorization.split(' ')
    if (parts.length === 2) {
      const scheme = parts[0]
      const token = parts[1]

      if (/^Bearer$/i.test(scheme)) {
        return token
      }
    }
  }
  return undefined
}

exports.randomHexString = (length) => {
  return crypto.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length)
}

exports.disableOnContainerEnv = () => {
  return (isDocker() || isHeroku) && !config.get('challenges.safetyOverride')
}

exports.determineDisabledContainerEnv = (disabledEnv) => {
  if (isDocker()) {
    return disabledEnv && (disabledEnv === 'Docker' || disabledEnv.includes('Docker')) ? 'Docker' : null
  } else if (isHeroku) {
    return disabledEnv && (disabledEnv === 'Heroku' || disabledEnv.includes('Heroku')) ? 'Heroku' : null
  }
  return null
}

exports.parseJsonCustom = (jsonString) => {
  const parser = clarinet.parser()
  const result = []
  parser.onkey = parser.onopenobject = k => {
    result.push({ key: k, value: null })
  }
  parser.onvalue = v => {
    result[result.length - 1].value = v
  }
  parser.write(jsonString).close()
  return result
}

exports.toSimpleIpAddress = (ipv6) => {
  if (this.startsWith(ipv6, '::ffff:')) {
    return ipv6.substr(7)
  } else if (ipv6 === '::1') {
    return '127.0.0.1'
  } else {
    return ipv6
  }
}
<|endoftext|>


<commit_before>let reduce = require('lodash/collection/reduce');
let forEach = require('lodash/collection/forEach');
let find = require('lodash/collection/find');
let get = require('lodash/object/get');

let utils = {
  getContainerNode,
  bemHelper,
  prepareTemplateProps,
  isSpecialClick,
  isDomElement,
  getRefinements
};

/**
 * Return the container. If it's a string, it is considered a
 * css selector and retrieves the first matching element. Otherwise
 * test if it validates that it's a correct DOMElement.
 * @param {string|DOMElement} selectorOrHTMLElement a selector or a node
 * @return {DOMElement}
 * @throws Error when the type is not correct
 */
function getContainerNode(selectorOrHTMLElement) {
  let isFromString = (typeof selectorOrHTMLElement === 'string');
  let domElement;
  if (isFromString) {
    domElement = document.querySelector(selectorOrHTMLElement);
  } else {
    domElement = selectorOrHTMLElement;
  }

  if (!isDomElement(domElement)) {
    let errorMessage = 'Container must be `string` or `HTMLElement`.';
    if (isFromString) {
      errorMessage += ' Unable to find ' + selectorOrHTMLElement;
    }
    throw new Error(errorMessage);
  }

  return domElement;
}

/**
 * Returns true if the parameter is a DOMElement.
 * @param {any} o the value to test
 * @return {boolean} true if o is a DOMElement
 */
function isDomElement(o) {
  return o instanceof HTMLElement || !!o && o.nodeType > 0;
}

function isSpecialClick(event) {
  return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
}

/**
 * Creates BEM class name according the vanilla BEM style.
 * @param {string} block the main block
 * @return {function} function that takes up to 2 parameters
 * that determine the element and the modifier of the BEM class.
 */
function bemHelper(block) {
  return function(element, modifier) {
    // block
    if (!element && !modifier) {
      return block;
    }
    // block--element
    if (element && !modifier) {
      return `${block}--${element}`;
    }
    // block--element__modifier
    if (element && modifier) {
      return `${block}--${element}__${modifier}`;
    }
    // block__modifier
    if (!element && modifier) {
      return `${block}__${modifier}`;
    }
  };
}

/**
 * Prepares an object to be passed to the Template widget
 * @param {object} unknownBecauseES6 an object with the following attributes:
 *  - transformData
 *  - defaultTemplate
 *  - templates
 *  - templatesConfig
 * @return {object} the configuration with the attributes:
 *  - transformData
 *  - defaultTemplate
 *  - templates
 *  - useCustomCompileOptions
 */
function prepareTemplateProps({
  transformData,
  defaultTemplates,
  templates,
  templatesConfig
}) {
  let preparedTemplates = prepareTemplates(defaultTemplates, templates);

  return {
    transformData,
    templatesConfig,
    ...preparedTemplates
  };
}

function prepareTemplates(defaultTemplates, templates) {
  return reduce(defaultTemplates, (config, defaultTemplate, key) => {
    let isCustomTemplate = templates && templates[key] !== undefined && (templates[key] !== defaultTemplate);
    if (isCustomTemplate) {
      config.templates[key] = templates[key];
      config.useCustomCompileOptions[key] = true;
    } else {
      config.templates[key] = defaultTemplate;
      config.useCustomCompileOptions[key] = false;
    }
    return config;
  }, {templates: {}, useCustomCompileOptions: {}});
}

function getRefinement(attributeName, name, resultsFacets) {
  let res = {attributeName, name};
  let facet = find(resultsFacets, (_facet) => { return _facet.name === attributeName; });
  if (facet !== undefined) {
    const count = get(facet, 'data.' + name);
    const exhaustive = get(facet, 'exhaustive');
    if (count !== undefined) {
      res.count = count;
    }
    if (exhaustive !== undefined) {
      res.exhaustive = exhaustive;
    }
  }
  return res;
}

function getRefinements(results, state) {
  let res = [];

  forEach(state.facetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.facets));
    });
  });

  forEach(state.facetsExcludes, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push({attributeName, name, exclude: true});
    });
  });

  forEach(state.disjunctiveFacetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.disjunctiveFacets));
    });
  });

  forEach(state.hierarchicalFacetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.hierarchicalFacets));
    });
  });

  forEach(state.numericRefinements, (operators, attributeName) => {
    forEach(operators, (values, operator) => {
      forEach(values, (name) => {
        res.push({attributeName, name, operator});
      });
    });
  });

  forEach(state.tagRefinements, (name) => {
    res.push({attributeName: '_tags', name});
  });

  return res;
}

module.exports = utils;
<commit_msg>fix(middle-click): Allow middle click on links<commit_after>let reduce = require('lodash/collection/reduce');
let forEach = require('lodash/collection/forEach');
let find = require('lodash/collection/find');
let get = require('lodash/object/get');

let utils = {
  getContainerNode,
  bemHelper,
  prepareTemplateProps,
  isSpecialClick,
  isDomElement,
  getRefinements
};

/**
 * Return the container. If it's a string, it is considered a
 * css selector and retrieves the first matching element. Otherwise
 * test if it validates that it's a correct DOMElement.
 * @param {string|DOMElement} selectorOrHTMLElement a selector or a node
 * @return {DOMElement}
 * @throws Error when the type is not correct
 */
function getContainerNode(selectorOrHTMLElement) {
  let isFromString = (typeof selectorOrHTMLElement === 'string');
  let domElement;
  if (isFromString) {
    domElement = document.querySelector(selectorOrHTMLElement);
  } else {
    domElement = selectorOrHTMLElement;
  }

  if (!isDomElement(domElement)) {
    let errorMessage = 'Container must be `string` or `HTMLElement`.';
    if (isFromString) {
      errorMessage += ' Unable to find ' + selectorOrHTMLElement;
    }
    throw new Error(errorMessage);
  }

  return domElement;
}

/**
 * Returns true if the parameter is a DOMElement.
 * @param {any} o the value to test
 * @return {boolean} true if o is a DOMElement
 */
function isDomElement(o) {
  return o instanceof HTMLElement || !!o && o.nodeType > 0;
}

function isSpecialClick(event) {
  let isMiddleClick = (event.button === 1);
  return isMiddleClick || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
}

/**
 * Creates BEM class name according the vanilla BEM style.
 * @param {string} block the main block
 * @return {function} function that takes up to 2 parameters
 * that determine the element and the modifier of the BEM class.
 */
function bemHelper(block) {
  return function(element, modifier) {
    // block
    if (!element && !modifier) {
      return block;
    }
    // block--element
    if (element && !modifier) {
      return `${block}--${element}`;
    }
    // block--element__modifier
    if (element && modifier) {
      return `${block}--${element}__${modifier}`;
    }
    // block__modifier
    if (!element && modifier) {
      return `${block}__${modifier}`;
    }
  };
}

/**
 * Prepares an object to be passed to the Template widget
 * @param {object} unknownBecauseES6 an object with the following attributes:
 *  - transformData
 *  - defaultTemplate
 *  - templates
 *  - templatesConfig
 * @return {object} the configuration with the attributes:
 *  - transformData
 *  - defaultTemplate
 *  - templates
 *  - useCustomCompileOptions
 */
function prepareTemplateProps({
  transformData,
  defaultTemplates,
  templates,
  templatesConfig
}) {
  let preparedTemplates = prepareTemplates(defaultTemplates, templates);

  return {
    transformData,
    templatesConfig,
    ...preparedTemplates
  };
}

function prepareTemplates(defaultTemplates, templates) {
  return reduce(defaultTemplates, (config, defaultTemplate, key) => {
    let isCustomTemplate = templates && templates[key] !== undefined && (templates[key] !== defaultTemplate);
    if (isCustomTemplate) {
      config.templates[key] = templates[key];
      config.useCustomCompileOptions[key] = true;
    } else {
      config.templates[key] = defaultTemplate;
      config.useCustomCompileOptions[key] = false;
    }
    return config;
  }, {templates: {}, useCustomCompileOptions: {}});
}

function getRefinement(attributeName, name, resultsFacets) {
  let res = {attributeName, name};
  let facet = find(resultsFacets, (_facet) => { return _facet.name === attributeName; });
  if (facet !== undefined) {
    const count = get(facet, 'data.' + name);
    const exhaustive = get(facet, 'exhaustive');
    if (count !== undefined) {
      res.count = count;
    }
    if (exhaustive !== undefined) {
      res.exhaustive = exhaustive;
    }
  }
  return res;
}

function getRefinements(results, state) {
  let res = [];

  forEach(state.facetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.facets));
    });
  });

  forEach(state.facetsExcludes, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push({attributeName, name, exclude: true});
    });
  });

  forEach(state.disjunctiveFacetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.disjunctiveFacets));
    });
  });

  forEach(state.hierarchicalFacetsRefinements, (refinements, attributeName) => {
    forEach(refinements, (name) => {
      res.push(getRefinement(attributeName, name, results.hierarchicalFacets));
    });
  });

  forEach(state.numericRefinements, (operators, attributeName) => {
    forEach(operators, (values, operator) => {
      forEach(values, (name) => {
        res.push({attributeName, name, operator});
      });
    });
  });

  forEach(state.tagRefinements, (name) => {
    res.push({attributeName: '_tags', name});
  });

  return res;
}

module.exports = utils;
<|endoftext|>


<commit_before>let fs = require('fs');
let path = require('path');
let http = require('http');
/**
 *
 * @param jsonArray
 * @returns {Array}
 */
function jsonArrayUnique(jsonArray) {
    let n = {}, r = [];
    for (let i = 0; i < jsonArray.length; i++) {
        if (!n[jsonArray[i].path]) {
            n[jsonArray[i].path] = true;
            r.push(jsonArray[i]);
        }
    }
    return r;
}

exports.jsonArrayUnique = jsonArrayUnique;

/**
 *
 * @param errorInfo
 */
function errorHandler(errorInfo) {
    console.log(errorInfo);
    process.exit();
}

exports.errorHandler = errorHandler;

/**
 * 判断是否为整数
 * @param x
 * @returns {boolean}
 */
function isInt(x) {
    let y = parseInt(x, 10);
    return !isNaN(y) && x == y && x.toString() == y.toString();
}

exports.isInt = isInt;

/**
 *
 * @param arr
 * @param search
 * @returns {boolean}
 */
function isInArray(arr, search) {
    if (typeof arr == 'object' && typeof arr.length == 'number') {
        for (let i = 0; i < arr.length; i++) {
            if (arr[i] == search) {
                return true;
            }
        }
    }

    return false;
}

exports.isInArray = isInArray;

/**
 *
 * @param path
 * @returns {string}
 */
function normalizePath(path) {
    if (typeof path == 'string') {
        return path.replace(/[\\|\\\\|//|////]/ig, '/');
    }

    return path;
}

exports.normalizePath = normalizePath;

/**
 * 获取本地调试下的静态资源前缀
 * @returns {RegExp}
 */
function getRegexpStaticFilesPrefix() {
    global.debugDomain.lastIndex = 0;
    return global.debugDomain;
}

exports.getRegexpStaticFilesPrefix = getRegexpStaticFilesPrefix;

/**
 *
 * @param dir
 * @param list
 * @param extension
 * @returns {*}
 */
function getAllFilesByDir(dir, list, extension) {
    if (!(list instanceof Array)) {
        list = [];
    }

    let fileList = fs.readdirSync(dir);

    for (let i = fileList.length - 1; i >= 0; i--) {
        let filePath = path.join(dir, fileList[i]);

        let stat = fs.statSync(filePath);

        if (stat.isDirectory()) {
            getAllFilesByDir(filePath, list, extension);
        } else {
            if (isInArray(extension, path.extname(filePath))) {
                list.push(normalizePath(filePath));
            }
        }
    }

    return list;
}
exports.getAllFilesByDir = getAllFilesByDir;

/**
 *
 * @param dir
 */
function mkdirRecursive(dir) {
    if (fs.existsSync(dir)) {
        return;
    }

    if (!fs.existsSync(path.dirname(dir))) {
        mkdirRecursive(path.dirname(dir));
    }

    fs.mkdirSync(dir);
}

exports.mkdirRecursive = mkdirRecursive;

/**
 *
 * @param argv
 * @param search
 * @returns {boolean}
 */
function hasArgument(argv, search) {
    let ret = false;

    for (let i = 0; i < argv.length; i++) {
        if (argv[i] == search) {
            ret = true;
            break;
        }
    }
    return ret;
}
exports.hasArgument = hasArgument;

/**
 * 获取当前机器IP
 */
function getLocalIP() {
    let os = require('os');
    let interfaceList = os.networkInterfaces();
    let ip = '127.0.0.1';
    Object.keys(interfaceList).forEach(function (interfaceName) {
        interfaceList[interfaceName].forEach(function (interfaceItem) {
            //跳过非IPV4项
            if ('IPv4' !== interfaceItem.family || interfaceItem.internal !== false) {
                return;
            }

            //暂时不考虑有多个IPV4地址的情况
            ip = interfaceItem.address;
        });
    });

    return ip;
}

exports.getLocalIP = getLocalIP;

/**
 * 启动WebSocket服务, 用于监听文件修改信号
 */
function startWebSocketServer(isHttps) {
    /*let app = require('http').createServer(function (req, res) {

     });
     let io = require('socket.io')(app);*/

    if (isHttps) {
        let options = {
            key: fs.readFileSync(path.join(__dirname, '/ssl', 'client.key')),
            cert: fs.readFileSync(path.join(__dirname, '/ssl/', 'wenke.crt'))
        };
        let appHttps = require('https').createServer(options);
        let ioHttps = require('socket.io')(appHttps);

        appHttps.listen(global.httpsLivereloadPort);
        ioHttps.on('connection', function (httpsSocket) {
            global.httpsSocket = httpsSocket;
        });

        console.log("Https WebSocketServer running... port: " + global.httpsLivereloadPort);
    }

    let app = require('http').createServer();
    let io = require('socket.io')(app);

    app.listen(global.livereloadPort);

    io.on('connection', function (socket) {
        global.socket = socket;
    });

    console.log("WebSocketServer running... port: " + global.livereloadPort);
    verifyVersion();

}

exports.startWebSocketServer = startWebSocketServer;

/**
 * 扩展配置
 * @param originalConfig
 * @param additionalConfig
 */
function extendConfig(originalConfig, additionalConfig) {
    if (typeof originalConfig == 'object' && typeof additionalConfig == 'object') {
        for (let k in additionalConfig) {
            if (additionalConfig.hasOwnProperty(k)) {
                originalConfig[k] = additionalConfig[k];
            }
        }
    }
}

exports.extendConfig = extendConfig;

/**
 * 获取script标签正则
 * @returns {RegExp}
 */
function getRegexpScriptElements() {
    return /<script([\s\S\w\W]*?)>{1}?[\s\S\w\W]*?(<\/script>){1}?/ig;
}

exports.getRegexpScriptElements = getRegexpScriptElements;

/**
 * 获取script标签src属性匹配正则
 * @returns {RegExp}
 */
function getRegexpScriptElementSrcAttrValue() {
    return /<script[\s\S\w\W]+?src="([\s\S\w\W]+?)"[\s\S\w\W]*?>[\s\S\w\W]*?<\/script>/gi;
}

exports.getRegexpScriptElementSrcAttrValue = getRegexpScriptElementSrcAttrValue;

/**
 * 获取link标签正则
 * @returns {RegExp}
 */
function getRegexpCSSLinkElements() {
    return /<link((?![>])[\s\S\w\W])+?rel="stylesheet"((?![>])[\s\S\w\W])*?>{1}?/gi;
}

exports.getRegexpCSSLinkElements = getRegexpCSSLinkElements;

/**
 * 获取link标签的href匹配正则
 * @returns {RegExp}
 */
function getRegexpCSSHrefValue() {
    return /<link([\s\S\w\W]+?)href="(.+?)"([\s\S\w\W]*?)>{1}?/gi;
}

exports.getRegexpCSSHrefValue = getRegexpCSSHrefValue;

/**
 * 获取img标签正则
 * @returns {RegExp}
 */
function getRegexpImgElements() {
    return /<img[\S\s\W\w]+?src="(.+?)"[\S\s\W\w]+?/ig;
}

exports.getRegexpImgElements = getRegexpImgElements;

/**
 * 获取内联style属性里的backgroundImage匹配正则
 * @returns {RegExp}
 */
function getRegexpBackgroundImage() {
    return /:[\s]?url\(['|"]??(\$!?(\{)?.+?\(\)(})?[\w\W]+?)['|"]??\)/ig;
}

exports.getRegexpBackgroundImage = getRegexpBackgroundImage;

/**
 * 获取公用Loaders列表
 * @returns JSON Array
 */
function getRules() {
    return [
        {
            test: /\.(jpe?g|png|gif|svg|eot|ttf|woff|woff2)$/i,
            use: [
                {
                    loader: "file-loader",
                    options: {
                        "name": "[name].[ext]"
                    }
                }
            ]
        },
        {
            test: /\.(html|tpl)$/i, use: [
            {
                loader: "raw-loader"
            }
        ]
        },
        {
            test: /\.css$/i, use: [
            {
                loader: "style-loader"
            },
            {
                loader: "css-loader"
            }
        ]
        },
        {
            test: /\.vue/i, use: [
            {
                loader: "vue-loader"
            }
        ]
        }
    ];
}

exports.getRules = getRules;

/**
 *
 * @param successCallback
 * @param failCallback
 */
function verifyVersion(successCallback, failCallback) {
    http.get('http://registry.npmjs.org/wenke-dev', function (res) {
        var data = '';
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            data += chunk.toString();
        });

        res.on('end', function () {
            //检测本地版本与最新版本是否一致
            let packageConfig = require('../package.json');
            let localVersion = packageConfig.version;
            let latestVersion = data && JSON.parse(data)['dist-tags'].latest;
            if (localVersion && latestVersion && localVersion === latestVersion) {
                typeof successCallback === 'function' && successCallback();
            } else {
                console.log(`
 **************************************************************
 *                                                            *
 *   .=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-.       *
 *    |                     ______                     |      *
 *    |                  .-"      "-.                  |      *
 *    |                 /            \\                 |      *
 *    |     _          |              |          _     |      *
 *    |    ( \\         |,  .-.  .-.  ,|         / )    |      *
 *    |     > "=._     | )(__/  \\__)( |     _.=" <     |      *
 *    |    (_/"=._"=._ |/     /\\     \\| _.="_.="\\_)    |      *
 *    |           "=._"(_     ^^     _)"_.="           |      *
 *    |               "=\\__|IIIIII|__/="               |      *
 *    |              _.="| \\IIIIII/ |"=._              |      *
 *    |    _     _.="_.="\\          /"=._"=._     _    |      *
 *    |   ( \\_.="_.="     \`--------\`     "=._"=._/ )   |      *
 *    |    > _.="                            "=._ <    |      *
 *    |   (_/                                    \\_)   |      *
 *    |                                                |      *
 *    '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='      *
 *                                                            *
 *        wenke-dev工具当前版本（${localVersion}）不是最新版本，        *
 *               建议升级到最新版本(${latestVersion})。                  *
 **************************************************************
 `);
                typeof failCallback === 'function' && failCallback();
            }
        });
    }).on('error', function () {
    });
}
exports.verifyVersion = verifyVersion;<commit_msg>preact 添加devtools调试<commit_after>let fs = require('fs');
let path = require('path');
let http = require('http');
/**
 *
 * @param jsonArray
 * @returns {Array}
 */
function jsonArrayUnique(jsonArray) {
    let n = {}, r = [];
    for (let i = 0; i < jsonArray.length; i++) {
        if (!n[jsonArray[i].path]) {
            n[jsonArray[i].path] = true;
            r.push(jsonArray[i]);
        }
    }
    return r;
}

exports.jsonArrayUnique = jsonArrayUnique;

/**
 *
 * @param errorInfo
 */
function errorHandler(errorInfo) {
    console.log(errorInfo);
    process.exit();
}

exports.errorHandler = errorHandler;

/**
 * 判断是否为整数
 * @param x
 * @returns {boolean}
 */
function isInt(x) {
    let y = parseInt(x, 10);
    return !isNaN(y) && x == y && x.toString() == y.toString();
}

exports.isInt = isInt;

/**
 *
 * @param arr
 * @param search
 * @returns {boolean}
 */
function isInArray(arr, search) {
    if (typeof arr == 'object' && typeof arr.length == 'number') {
        for (let i = 0; i < arr.length; i++) {
            if (arr[i] == search) {
                return true;
            }
        }
    }

    return false;
}

exports.isInArray = isInArray;

/**
 *
 * @param path
 * @returns {string}
 */
function normalizePath(path) {
    if (typeof path == 'string') {
        return path.replace(/[\\|\\\\|//|////]/ig, '/');
    }

    return path;
}

exports.normalizePath = normalizePath;

/**
 * 获取本地调试下的静态资源前缀
 * @returns {RegExp}
 */
function getRegexpStaticFilesPrefix() {
    global.debugDomain.lastIndex = 0;
    return global.debugDomain;
}

exports.getRegexpStaticFilesPrefix = getRegexpStaticFilesPrefix;

/**
 *
 * @param dir
 * @param list
 * @param extension
 * @returns {*}
 */
function getAllFilesByDir(dir, list, extension) {
    if (!(list instanceof Array)) {
        list = [];
    }

    let fileList = fs.readdirSync(dir);

    for (let i = fileList.length - 1; i >= 0; i--) {
        let filePath = path.join(dir, fileList[i]);

        let stat = fs.statSync(filePath);

        if (stat.isDirectory()) {
            getAllFilesByDir(filePath, list, extension);
        } else {
            if (isInArray(extension, path.extname(filePath))) {
                list.push(normalizePath(filePath));
            }
        }
    }

    return list;
}
exports.getAllFilesByDir = getAllFilesByDir;

/**
 *
 * @param dir
 */
function mkdirRecursive(dir) {
    if (fs.existsSync(dir)) {
        return;
    }

    if (!fs.existsSync(path.dirname(dir))) {
        mkdirRecursive(path.dirname(dir));
    }

    fs.mkdirSync(dir);
}

exports.mkdirRecursive = mkdirRecursive;

/**
 *
 * @param argv
 * @param search
 * @returns {boolean}
 */
function hasArgument(argv, search) {
    let ret = false;

    for (let i = 0; i < argv.length; i++) {
        if (argv[i] == search) {
            ret = true;
            break;
        }
    }
    return ret;
}
exports.hasArgument = hasArgument;

/**
 * 获取当前机器IP
 */
function getLocalIP() {
    let os = require('os');
    let interfaceList = os.networkInterfaces();
    let ip = '127.0.0.1';
    Object.keys(interfaceList).forEach(function (interfaceName) {
        interfaceList[interfaceName].forEach(function (interfaceItem) {
            //跳过非IPV4项
            if ('IPv4' !== interfaceItem.family || interfaceItem.internal !== false) {
                return;
            }

            //暂时不考虑有多个IPV4地址的情况
            ip = interfaceItem.address;
        });
    });

    return ip;
}

exports.getLocalIP = getLocalIP;

/**
 * 启动WebSocket服务, 用于监听文件修改信号
 */
function startWebSocketServer(isHttps) {
    /*let app = require('http').createServer(function (req, res) {

     });
     let io = require('socket.io')(app);*/

    if (isHttps) {
        let options = {
            key: fs.readFileSync(path.join(__dirname, '/ssl', 'client.key')),
            cert: fs.readFileSync(path.join(__dirname, '/ssl/', 'wenke.crt'))
        };
        let appHttps = require('https').createServer(options);
        let ioHttps = require('socket.io')(appHttps);

        appHttps.listen(global.httpsLivereloadPort);
        ioHttps.on('connection', function (httpsSocket) {
            global.httpsSocket = httpsSocket;
        });

        console.log("Https WebSocketServer running... port: " + global.httpsLivereloadPort);
    }

    let app = require('http').createServer();
    let io = require('socket.io')(app);

    app.listen(global.livereloadPort);

    io.on('connection', function (socket) {
        global.socket = socket;
    });

    console.log("WebSocketServer running... port: " + global.livereloadPort);
    verifyVersion();

}

exports.startWebSocketServer = startWebSocketServer;

/**
 * 扩展配置
 * @param originalConfig
 * @param additionalConfig
 */
function extendConfig(originalConfig, additionalConfig) {
    if (typeof originalConfig == 'object' && typeof additionalConfig == 'object') {
        for (let k in additionalConfig) {
            if (additionalConfig.hasOwnProperty(k)) {
                originalConfig[k] = additionalConfig[k];
            }
        }
    }
}

exports.extendConfig = extendConfig;

/**
 * 获取script标签正则
 * @returns {RegExp}
 */
function getRegexpScriptElements() {
    return /<script([\s\S\w\W]*?)>{1}?[\s\S\w\W]*?(<\/script>){1}?/ig;
}

exports.getRegexpScriptElements = getRegexpScriptElements;

/**
 * 获取script标签src属性匹配正则
 * @returns {RegExp}
 */
function getRegexpScriptElementSrcAttrValue() {
    return /<script[\s\S\w\W]+?src="([\s\S\w\W]+?)"[\s\S\w\W]*?>[\s\S\w\W]*?<\/script>/gi;
}

exports.getRegexpScriptElementSrcAttrValue = getRegexpScriptElementSrcAttrValue;

/**
 * 获取link标签正则
 * @returns {RegExp}
 */
function getRegexpCSSLinkElements() {
    return /<link((?![>])[\s\S\w\W])+?rel="stylesheet"((?![>])[\s\S\w\W])*?>{1}?/gi;
}

exports.getRegexpCSSLinkElements = getRegexpCSSLinkElements;

/**
 * 获取link标签的href匹配正则
 * @returns {RegExp}
 */
function getRegexpCSSHrefValue() {
    return /<link([\s\S\w\W]+?)href="(.+?)"([\s\S\w\W]*?)>{1}?/gi;
}

exports.getRegexpCSSHrefValue = getRegexpCSSHrefValue;

/**
 * 获取img标签正则
 * @returns {RegExp}
 */
function getRegexpImgElements() {
    return /<img[\S\s\W\w]+?src="(.+?)"[\S\s\W\w]+?/ig;
}

exports.getRegexpImgElements = getRegexpImgElements;

/**
 * 获取内联style属性里的backgroundImage匹配正则
 * @returns {RegExp}
 */
function getRegexpBackgroundImage() {
    return /:[\s]?url\(['|"]??(\$!?(\{)?.+?\(\)(})?[\w\W]+?)['|"]??\)/ig;
}

exports.getRegexpBackgroundImage = getRegexpBackgroundImage;

/**
 * 获取公用Loaders列表
 * @returns JSON Array
 */
function getRules() {
    return [
        {
            test: /\.(jpe?g|png|gif|svg|eot|ttf|woff|woff2)$/i,
            use: [
                {
                    loader: "file-loader",
                    options: {
                        "name": "[name].[ext]"
                    }
                }
            ]
        },
        {
            test: /\.(html|tpl)$/i, use: [
            {
                loader: "raw-loader"
            }
        ]
        },
        {
            test: /\.css$/i, use: [
            {
                loader: "style-loader"
            },
            {
                loader: "css-loader"
            }
        ]
        },
        {
            test: /\.vue/i, use: [
            {
                loader: "vue-loader"
            }
        ]
        }
    ];
}

exports.getRules = getRules;

/**
 *
 * @param successCallback
 * @param failCallback
 */
function verifyVersion(successCallback, failCallback) {
    http.get('http://registry.npmjs.org/wenke-dev', function (res) {
        var data = '';
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            data += chunk.toString();
        });

        res.on('end', function () {
            //检测本地版本与最新版本是否一致
            let packageConfig = require('../package.json');
            let localVersion = packageConfig.version;
            let latestVersion = data && JSON.parse(data)['dist-tags'].latest;
            if (localVersion && latestVersion && localVersion < latestVersion) {
                typeof successCallback === 'function' && successCallback();
            } else {
                console.log(`
 **************************************************************
 *                                                            *
 *   .=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-.       *
 *    |                     ______                     |      *
 *    |                  .-"      "-.                  |      *
 *    |                 /            \\                 |      *
 *    |     _          |              |          _     |      *
 *    |    ( \\         |,  .-.  .-.  ,|         / )    |      *
 *    |     > "=._     | )(__/  \\__)( |     _.=" <     |      *
 *    |    (_/"=._"=._ |/     /\\     \\| _.="_.="\\_)    |      *
 *    |           "=._"(_     ^^     _)"_.="           |      *
 *    |               "=\\__|IIIIII|__/="               |      *
 *    |              _.="| \\IIIIII/ |"=._              |      *
 *    |    _     _.="_.="\\          /"=._"=._     _    |      *
 *    |   ( \\_.="_.="     \`--------\`     "=._"=._/ )   |      *
 *    |    > _.="                            "=._ <    |      *
 *    |   (_/                                    \\_)   |      *
 *    |                                                |      *
 *    '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-='      *
 *                                                            *
 *        wenke-dev工具当前版本（${localVersion}）不是最新版本，        *
 *               建议升级到最新版本(${latestVersion})。                  *
 **************************************************************
 `);
                typeof failCallback === 'function' && failCallback();
            }
        });
    }).on('error', function () {
    });
}
exports.verifyVersion = verifyVersion;<|endoftext|>


<commit_before>'use strict';

/**
 * Function to find the power of 10 as an integer
 * @param  {number} value Number whos power is to be found
 * @return {integer}       power value
 */
exports.findExpFor10 = function findExpFor10(value){
    var i = 0;
    var abs_value = Math.abs(value);
    if(abs_value === 0 || abs_value ===1) {
        return 0;
    }

    if(abs_value > 1) {
        while((abs_value /= 10) >= 1){
            i++;
        }
    } else if(abs_value < 1) {
        while(abs_value < 1) {
            i--;
            abs_value *= 10;
        }
    }
    return i;
};

/**
 * Reverse the key-value mapping to value-key
 * @param  {object} map The map to be reversed
 * @return {object}     reversed Map
 */
exports.reverseMap = function reverseMap(map){
    var reverse = {};
    for (var key in map) {
        if(typeof map[key] !== 'object'){
            reverse[map[key]] = key;
        }
    }
    return reverse;
};<commit_msg>add convert notation function and unit test case<commit_after>'use strict';

/**
 * Function to find the power of 10 as an integer
 * @param  {number} value Number whos power is to be found
 * @return {integer}       power value
 */
exports.findExpFor10 = function findExpFor10(value){
    var i = 0;
    var abs_value = Math.abs(value);
    if(abs_value === 0 || abs_value ===1) {
        return 0;
    }

    if(abs_value > 1) {
        while((abs_value /= 10) >= 1){
            i++;
        }
    } else if(abs_value < 1) {
        while(abs_value < 1) {
            i--;
            abs_value *= 10;
        }
    }
    return i;
};

/**
 * Reverse the key-value mapping to value-key
 * @param  {object} map The map to be reversed
 * @return {object}     reversed Map
 */
exports.reverseMap = function reverseMap(map){
    var reverse = {};
    for (var key in map) {
        if(typeof map[key] !== 'object'){
            reverse[map[key]] = key;
        }
    }
    return reverse;
};

var mulMap = {
    'm' : Math.pow(10,6),
    'k' : Math.pow(10,3),
    'r' : Math.pow(10,0)
};

/**
 * converter BS 1852 notation to value
 * @param  {string} valueInNotaion value in BS 1852 notation
 * @return {number}                value
 */
exports.convertFromNotation = function convertFromNotation(valueInNotaion){
    var value = valueInNotaion || 0;
    if (typeof value === 'string' && value.trim()) {
        var match = value.match(/[mkr]/i);
        var mul = 1;
        if(match){
            match = match[0];
            mul = mulMap[match.toLowerCase()];
        }

        var values = value.split(match);
        if (values.length > 1){
            values[1] = '0.' + values[1];
        }

        return values
                .map(function(val){
                    return val * mul;
                })
                .reduce(function(prev,cur){
                    return prev + cur;
                });
    }

    return value;
};

<|endoftext|>


<commit_before>var Boom = require('boom');
var fetch = require('./fetch');
var hoodie = require('./hoodie');
var stripe = require('./stripe');
var taxamo = require('./taxamo');

var utils = {
	fetch: fetch,
	hoodie: hoodie,
	stripe: stripe,
	taxamo: taxamo,
};

utils.checkCurrency = function( context ) {
	// There's a special option to only accept plans priced
	// in euro for EU customers.
	if (
		context.hoodie.config.get('euroInEU') &&
		context.userDoc.taxamo &&
		context.userDoc.taxamo['tax_region'] === 'EU' &&
		context.args[0] &&
		context.args[0]['currency_code'] &&
		context.args[0]['currency_code'] !== 'EUR'
	) {
		throw Boom.forbidden(
			'European customers must choose a plan in euro.'
		);
	}
}

utils.replyError = function( hoodie, context, error ) {
	// Boomify Stripe errors
	if ( error.type && /^Stripe/.test(error.type) ) {
		error = Boom[ /^StripeInvalidReq/.test(error.type) ?
				'badRequest' :
				'badImplementation'
			]( error.message, error );
	}

	// include error details in the payload
	if ( error.isBoom ) {
		if ( error.data ) {
			error.output.payload.details = error.data;
		}
		// Hoodie uses error.reason as the message :-/
		error.output.payload.reason = error.message;
	}

	// log error to the client (if the option is active)
	context.log( error.message, error.stack );
	// log error on the server
	hoodie.database('plugin/stripe').add('error', {
		message: error.message,
		details: error.data,
		stack: error.stack,
		request: context.request,
	});
	return context.reply( error );
};

module.exports = utils;
<commit_msg>callback argument is required<commit_after>var Boom = require('boom');
var fetch = require('./fetch');
var hoodie = require('./hoodie');
var stripe = require('./stripe');
var taxamo = require('./taxamo');

var utils = {
	fetch: fetch,
	hoodie: hoodie,
	stripe: stripe,
	taxamo: taxamo,
};

utils.checkCurrency = function( context ) {
	// There's a special option to only accept plans priced
	// in euro for EU customers.
	if (
		context.hoodie.config.get('euroInEU') &&
		context.userDoc.taxamo &&
		context.userDoc.taxamo['tax_region'] === 'EU' &&
		context.args[0] &&
		context.args[0]['currency_code'] &&
		context.args[0]['currency_code'] !== 'EUR'
	) {
		throw Boom.forbidden(
			'European customers must choose a plan in euro.'
		);
	}
}

utils.replyError = function( hoodie, context, error ) {
	// Boomify Stripe errors
	if ( error.type && /^Stripe/.test(error.type) ) {
		error = Boom[ /^StripeInvalidReq/.test(error.type) ?
				'badRequest' :
				'badImplementation'
			]( error.message, error );
	}

	// include error details in the payload
	if ( error.isBoom ) {
		if ( error.data ) {
			error.output.payload.details = error.data;
		}
		// Hoodie uses error.reason as the message :-/
		error.output.payload.reason = error.message;
	}

	// log error to the client (if the option is active)
	context.log( error.message, error.stack );
	// log error on the server
	hoodie.database('plugin/stripe').add('error', {
		message: error.message,
		details: error.data,
		stack: error.stack,
		request: context.request,
	}, function() {});
	return context.reply( error );
};

module.exports = utils;
<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require('fs'),
    util = require('util');

var utils = {

  // Turn dates into strings
  date: function(date){
    date = date || new Date();
    return JSON.stringify(date); // e.g. "2016-01-28T22:14:52.844Z"
  },

  // Return whether a directory exists or not
  dirExists: function(path){
    try {
      return fs.lstatSync(path).isDirectory();
    } catch(e) {
      return false;
    }
  },

  // Extend an object with another
  extend: util._extend,

  // Wrap boolean, date and numeric data
  wrap: function(value){
    if (typeof value === 'object' && typeof value.getTime === 'function') {
      return {__plano__: JSON.stringify(value)}; // wrap dates as JSON strings
    }
    if (typeof value === 'object' && Array.isArray(value)) {
      return {__plano__: value}; // avoid JSON parsing headaches with arrays
    }
    if (typeof value === 'boolean' || typeof value === 'number') {
      return {__plano__: value};
    }
    return value;
  },

  // Wrap boolean, date and numeric values in a hash of key/value pairs
  wrapAll: function(hash){
    if (typeof hash !== 'object') return;
    for (var key in hash) {
      hash[key] = this.wrap(hash[key]);
    }
    return hash;
  },

  // Unwrap boolean, date and numeric data
  unwrap: function(value){
    if (typeof value === 'object' && value.__plano__ !== undefined) {
      if (typeof value.__plano__ === 'string') { // the only strings are dates
        return new Date(JSON.parse(value.__plano__));
      } else {
        return value.__plano__;
      }
    }
    return value;
  },

  // Unwrap boolean, date and numeric values in a hash of key/value pairs
  unwrapAll: function(hash){
    if (typeof hash !== 'object') return;
    for (var key in hash) {
      hash[key] = this.unwrap(hash[key]);
    }
    return hash;
  }
}

module.exports = utils;
<commit_msg>clean up utils.js with better comments<commit_after>#!/usr/bin/env node

var fs = require('fs'),
    util = require('util');

var utils = {

  // Return whether a directory exists or not
  dirExists: function(path){
    try {
      return fs.lstatSync(path).isDirectory();
    } catch(e) {
      return false;
    }
  },

  // Extend an object with another
  extend: util._extend,

  // Wrap Array, boolean, Date and numeric data
  wrap: function(value){
    if (typeof value === 'object' && typeof value.getTime === 'function') {
      return {__plano__: JSON.stringify(value)}; // wrap dates as JSON strings
    }
    if (typeof value === 'object' && Array.isArray(value)) {
      return {__plano__: value}; // avoid JSON parsing headaches with arrays
    }
    if (typeof value === 'boolean' || typeof value === 'number') {
      return {__plano__: value};
    }
    return value;
  },

  // Wrap Array, boolean, Date and numeric values in a hash of key/value pairs
  wrapAll: function(hash){
    if (typeof hash !== 'object') return;
    for (var key in hash) {
      hash[key] = this.wrap(hash[key]);
    }
    return hash;
  },

  // Unwrap Array, boolean, Date and numeric data
  unwrap: function(value){
    if (typeof value === 'object' && value.__plano__ !== undefined) {
      if (typeof value.__plano__ === 'string') { // the only strings are dates
        return new Date(JSON.parse(value.__plano__));
      } else {
        return value.__plano__;
      }
    }
    return value;
  },

  // Unwrap Array, boolean, Date and numeric values in a hash of key/value pairs
  unwrapAll: function(hash){
    if (typeof hash !== 'object') return;
    for (var key in hash) {
      hash[key] = this.unwrap(hash[key]);
    }
    return hash;
  }
}

module.exports = utils;
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')

/**
 * Crawls commands directory and returns all command names
 * @return {Array} filename array without filetypes
 */
module.exports.getCommands = function () {
  const srcpath = path.resolve('./commands')
  return fs.readdirSync(srcpath)
    .filter(file => fs.lstatSync(path.join(srcpath, file)))
    .map(filename => filename.split('.')[0])
}
<commit_msg>Updated commands path<commit_after>const fs = require('fs')
const path = require('path')

/**
 * Crawls commands directory and returns all command names
 * @return {Array} filename array without filetypes
 */
module.exports.getCommands = function () {
  const srcpath = path.join(__dirname, '../commands')
  return fs.readdirSync(srcpath)
    .filter(file => fs.lstatSync(path.join(srcpath, file)))
    .map(filename => filename.split('.')[0])
}
<|endoftext|>


<commit_before>var controllerDir;
var appName;

function getAppName() {
    var parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

// Get js-controller directory to load libs
function getControllerDir(isInstall) {
    var fs = require('fs');
    // Find the js-controller location
    var controllerDir = __dirname.replace(/\\/g, '/');
    controllerDir = controllerDir.split('/');
    if (controllerDir[controllerDir.length - 3] === 'adapter') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
    } else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
        if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
            controllerDir += '/node_modules/' + appName + '.js-controller';
        } else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
            controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
        } else if (!fs.existsSync(controllerDir + '/controller.js')) {
            if (!isInstall) {
                console.log('Cannot find js-controller');
                process.exit(10);
            } else {
                process.exit();
            }
        }
    } else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
        controllerDir.splice(controllerDir.length - 2, 2);
        return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
    } else {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    return controllerDir;
}

// Read controller configuration file
function getConfig() {
    var fs = require('fs');
    if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
    } else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       require(controllerDir + '/lib/adapter.js');
exports.appName =       appName;
<commit_msg>update utils.js<commit_after>'use strict';

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

let controllerDir;
let appName;

/**
 * returns application name
 *
 * The name of the application can be different and this function finds it out.
 *
 * @returns {string}
 */
 function getAppName() {
    const parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

/**
 * looks for js-controller home folder
 *
 * @param {boolean} isInstall
 * @returns {string}
 */
function getControllerDir(isInstall) {
    // Find the js-controller location
    const possibilities = [
        'iobroker.js-controller',
        'ioBroker.js-controller',
    ];
    /** @type {string} */
    let controllerPath;
    for (const pkg of possibilities) {
        try {
            const possiblePath = require.resolve(pkg);
            if (fs.existsSync(possiblePath)) {
                controllerPath = possiblePath;
                break;
            }
        } catch (e) { /* not found */ }
    }
    if (controllerPath == null) {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    // we found the controller
    return path.dirname(controllerPath);
}

/**
 * reads controller base settings
 *
 * @alias getConfig
 * @returns {object}
 */
 function getConfig() {
    let configPath;
    if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', appName + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       adapter;
exports.appName =       appName;
<|endoftext|>


<commit_before>// bluebird is faster than builtin promises
const Promise = require('bluebird');
// deque is faster then builtin array fifo/lifo methods
const Deque = require('dobule-ended-queue');
// req-fast is faster then request module and also handles json
const req = Promise.promisify(require('req-fast'));

// Map<token, deque<enqueue arguments[]>>
const queueByToken = new Map;

/**
 *
 * Request data via api.vk.com immediatly
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
const raw = (method, data, token) => {
	return req({
		url: `https://api.vk.com/method/${method}?v=5.5` + token ? `&access_token=${token}` : '',
		data: data,
		method: 'GET',
		timeout: 10000 // 10s
	}).then(response => {
		return response.body;
	});
}

/**
 * Enqueue request.
 * Doesn't make sense for anonymous requests (without token)
 * vk.com has a limit for requests per second per each user session
 * to avoid api errors we need to throttle request interval client side
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
const enqueue = (method, data, token) => {
	return new Promise(resolve => {
		const deque = queueByToken.get(token) || new Deque(100);
		deque.push([method, data, token, resolve]);
		if (deque.length - 1 == 0) {
			queueByToken.set(token, deque);
			const interval = setInterval(() => {
				const curTask = deque.shift();
				const curResolve = curTask.pop();
				curResolve(raw.apply(null, curTask))
				if (!deque.length) clearInterval(interval);
			}, 400);
		}
	});
}

/**
 *
 * Create vk api request
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
exports.request = (method, params, token) => {
	if (token) return enqueue(method, params, token);
	return raw(method, data);
};<commit_msg>[feature] error handling<commit_after>// bluebird is faster than builtin promises
const Promise = require('bluebird');
// deque is faster then builtin array fifo/lifo methods
const Deque = require('dobule-ended-queue');
// req-fast is faster then request module and also handles json
const req = Promise.promisify(require('req-fast'));

// Map<token, deque<enqueue arguments[]>>
const queueByToken = new Map;

/**
 *
 * Request data via api.vk.com immediatly
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
const raw = (method, data, token) => {
	return req({
		url: `https://api.vk.com/method/${method}?v=5.60` + token ? `&access_token=${token}` : '',
		data: data,
		method: 'GET',
		timeout: 10000 // 10s
	}).then(response => {
		if (response.response) {
			return response.response;
		} else {
			throw response.error;
		}
	});
}

/**
 * Enqueue request.
 * Doesn't make sense for anonymous requests (without token)
 * vk.com has a limit for requests per second per each user session
 * to avoid api errors we need to throttle request interval client side
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
const enqueue = (method, data, token) => {
	return new Promise(resolve => {
		const deque = queueByToken.get(token) || new Deque(100);
		deque.push([method, data, token, reject, resolve]);
		if (deque.length - 1 == 0) {
			queueByToken.set(token, deque);
			const interval = setInterval(() => {
				const curTask = deque.shift();
				const curResolve = curTask.pop();
				const curReject = curTask.pop();
				try {
					const result = raw.apply(null, curTask);
					resolve(result);
				} catch(error) {
					reject(error);
				}
				if (!deque.length) clearInterval(interval);
			}, 400);
		}
	});
}

/**
 *
 * Create vk api request
 *
 * @param {String} method 	api method
 * @param {Object} data 	key:value request data
 * @param {String} token 	access_token
 * @returns Promise<APIResponse>
 */
exports.request = (method, params, token) => {
	if (token) return enqueue(method, params, token);
	return raw(method, data);
};<|endoftext|>