<commit_before>// Checks if certain RegExp props are supported.
const supportedRegExpProps = {
	sticky: "sticky" in RegExp.prototype,
	unicode: "unicode" in RegExp.prototype,
	flags: "flags" in RegExp.prototype
};
/**
* assert - Logs or throws an Error if `boolean` is false,
*  If `boolean` is `true`, nothing happens.
*  If `errorType` is set, throws a new Error of type `errorType` instead of logging to console.
* @param  {Boolean} boolean         The activation Boolean.
* @param  {String} message          The message to log, or include in the Error.
* @param  {Error} errorType = null  If not `null`, throws a new error of `errorType`.
*/
function assert(boolean, message, errorType = null) {
	if (boolean) return;
	if (errorType !== null && (errorType === Error || Error.isPrototypeOf(errorType))) {
		throw new errorType(message);
	} else {
		console.error(message);
	}
}
// Thunks to `assert` for method argument type checking.
assert.props = function (input, props, argName) {
	for (var prop of props[Symbol.iterator]()) {
		assert(prop in input, "Argument " + argName + " must have a \"" + prop + "\" property.", TypeError);
	}
};
assert.argType = (boolean, typeString, argName) => assert(boolean, "Argument " + argName + " must be " + typeString, TypeError);
assert.string = (input, argName) => assert.argType(typeof input === "string", "a String", argName);
assert.number = (input, argName) => assert.argType(typeof input === "number", "a Number", argName);
assert.boolean = (input, argName) => assert.argType(typeof input === "boolean", "a Boolean", argName);
assert.function = (input, argName) => assert.argType(typeof input === "function", "a Function", argName);
assert.object = (input, argName) => assert.argType(isObject(input), "an Object", argName);
assert.array = (input, argName) => assert.argType(Array.isArray(input), "an Array", argName);
assert.container = (input, argName) => assert.argType(isContainer(input), "an Object or Array", argName);
/**
* isContainer - Returns `true` if `input` is an Object or Array, otherwise returns `false`.
* @param {any} input
* @returns {Boolean}
*/
function isContainer(input) {
	return input !== null && (Array.isArray(input) || typeof input === "object");
}
/**
* isObject - Returns `true` if `input` is an Object and not an Array, or `false` if otherwise.
* @param {any} input
* @returns {Boolean}
*/
function isObject(input) {
	return input !== null && (typeof (input) === "object" && !Array.isArray(input));
}
/**
* isObject - Returns `true` if `input` is a Primitive, or `false` if otherwise.
* @param {any} input
* @returns {Boolean}
*/
var primitives = ["string", "boolean", "number", "symbol"];
function isPrimitive(input) {
	return input === null || primitives.includes(typeof input);
}
/**
* createContainer - Returns a new empty Array/Object matching the type of `input`.
*  If `input` is not an Object or Array, `null` is returned.
* @param {(Object|Array)} input   An Object or Array.
* @returns {(Object|Array|null)}
*/
function createContainer(input) {
	if (Array.isArray(input)) {
		return new Array();
	}
	if (input !== null && typeof input === "object") {
		return new Object();
	}
	throw new TypeError("The given parameter must be an Object or Array");
}
/**
* getContainerLength - Returns the number of enumerable indexes/properties of `input`.
*  If `input` is not an Object or Array, a TypeError is thrown.
* @param {(Object|Array)} input
* @returns {Number}            The number of enumerable properties/indexes in `input`.
*/
function getContainerLength(input) {
	if (Array.isArray(input)) {
		return input.length;
	}
	if (input !== null && typeof input === "object") {
		return Object.keys(input).length;
	}
	throw new TypeError("The given parameter must be an Object or Array");
}
/**
* runCallback - Executes `state.parameters.callback` and returns whatever the callback does.
* @param {Object} state  A reference to the state flyweight yielded by `searchIterator`.
* @returns {any}
*/
function runCallback(state) {
	return state.parameters.callback(state.currentValue, state.accessor, state.tuple.subject);
}
module.exports = {
	supportedRegExpProps,
	assert,
	isContainer,
	isObject,
	isPrimitive,
	createContainer,
	getContainerLength,
	runCallback
};<commit_msg>Moved runCallback out<commit_after>// Checks if certain RegExp props are supported.
const supportedRegExpProps = {
	sticky: "sticky" in RegExp.prototype,
	unicode: "unicode" in RegExp.prototype,
	flags: "flags" in RegExp.prototype
};
/**
* assert - Logs or throws an Error if `boolean` is false,
*  If `boolean` is `true`, nothing happens.
*  If `errorType` is set, throws a new Error of type `errorType` instead of logging to console.
* @param  {Boolean} boolean         The activation Boolean.
* @param  {String} message          The message to log, or include in the Error.
* @param  {Error} errorType = null  If not `null`, throws a new error of `errorType`.
*/
function assert(boolean, message, errorType = null) {
	if (boolean) return;
	if (errorType !== null && (errorType === Error || Error.isPrototypeOf(errorType))) {
		throw new errorType(message);
	} else {
		console.error(message);
	}
}
// Thunks to `assert` for method argument type checking.
assert.props = function (input, props, argName) {
	for (var prop of props[Symbol.iterator]()) {
		assert(prop in input, "Argument " + argName + " must have a \"" + prop + "\" property.", TypeError);
	}
};
assert.argType = (boolean, typeString, argName) => assert(boolean, "Argument " + argName + " must be " + typeString, TypeError);
assert.string = (input, argName) => assert.argType(typeof input === "string", "a String", argName);
assert.number = (input, argName) => assert.argType(typeof input === "number", "a Number", argName);
assert.boolean = (input, argName) => assert.argType(typeof input === "boolean", "a Boolean", argName);
assert.function = (input, argName) => assert.argType(typeof input === "function", "a Function", argName);
assert.object = (input, argName) => assert.argType(isObject(input), "an Object", argName);
assert.array = (input, argName) => assert.argType(Array.isArray(input), "an Array", argName);
assert.container = (input, argName) => assert.argType(isContainer(input), "an Object or Array", argName);
/**
* isContainer - Returns `true` if `input` is an Object or Array, otherwise returns `false`.
* @param {any} input
* @returns {Boolean}
*/
function isContainer(input) {
	return input !== null && (Array.isArray(input) || typeof input === "object");
}
/**
* isObject - Returns `true` if `input` is an Object and not an Array, or `false` if otherwise.
* @param {any} input
* @returns {Boolean}
*/
function isObject(input) {
	return input !== null && (typeof (input) === "object" && !Array.isArray(input));
}
/**
* isObject - Returns `true` if `input` is a Primitive, or `false` if otherwise.
* @param {any} input
* @returns {Boolean}
*/
var primitives = ["string", "boolean", "number", "symbol"];
function isPrimitive(input) {
	return input === null || primitives.includes(typeof input);
}
/**
* createContainer - Returns a new empty Array/Object matching the type of `input`.
*  If `input` is not an Object or Array, `null` is returned.
* @param {(Object|Array)} input   An Object or Array.
* @returns {(Object|Array|null)}
*/
function createContainer(input) {
	if (Array.isArray(input)) {
		return new Array();
	}
	if (input !== null && typeof input === "object") {
		return new Object();
	}
	throw new TypeError("The given parameter must be an Object or Array");
}
/**
* getContainerLength - Returns the number of enumerable indexes/properties of `input`.
*  If `input` is not an Object or Array, a TypeError is thrown.
* @param {(Object|Array)} input
* @returns {Number}            The number of enumerable properties/indexes in `input`.
*/
function getContainerLength(input) {
	if (Array.isArray(input)) {
		return input.length;
	}
	if (input !== null && typeof input === "object") {
		return Object.keys(input).length;
	}
	throw new TypeError("The given parameter must be an Object or Array");
}
module.exports = {
	supportedRegExpProps,
	assert,
	isContainer,
	isObject,
	isPrimitive,
	createContainer,
	getContainerLength,
	runCallback
};<|endoftext|>


<commit_before>/*! resol-vbus | Copyright (c) 2013-2015, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var utils = {

    /**
     * @see http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
     */
    generateGUID: function() {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        };

        var guid = [
            s4(),
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            s4(),
            s4(),
        ].join('');

        return guid;
    },

    promise: function(callback, thisArg) {
        var deferred = Q.defer();
        var promise = deferred.promise;

        var resolve = function(result) {
            if (deferred) {
                deferred.resolve(result);

                deferred = null;
            }
        };

        var reject = function(reason) {
            if (deferred) {
                deferred.reject(reason);

                deferred = null;
            }
        };

        var notify = function(value) {
            if (deferred) {
                deferred.notify(value);
            }
        };

        try {
            callback.call(thisArg, resolve, reject, notify);
        } catch (ex) {
            reject(ex);
        }

        return promise;
    },

    cancelablePromise: function(callback, thisArg) {
        var cancelDeferred = Q.defer();
        var cancelPromise = cancelDeferred.promise;
        var isCanceled = false;

        var cancel = function(reason) {
            if (!isCanceled) {
                cancelPromise.then(function(result) {
                    result.cancel(reason);
                });

                isCanceled = true;
            }
        };

        var checkCanceled = function(result) {
            if (!isCanceled) {
                return result;
            } else {
                throw new Error('Canceled');
            }
        };

        var promise = utils.promise(function(resolve, reject, notify) {
            if (cancelDeferred) {
                cancelDeferred.resolve({ cancel: reject });

                cancelDeferred = null;
            }

            return Q.fcall(function() {
                return cancelPromise;
            }).then(function() {
                return callback.call(thisArg, resolve, reject, notify, checkCanceled);
            });
        });

        promise.cancel = cancel;

        return promise;
    },

    roundNumber: function(value, exp) {
        if ((value === undefined) || (exp === undefined) || (+exp === 0)) {
            return value;
        }

        value = +value;
        exp = +exp;

        if (_.isNaN(value) || (exp % 1 !== 0)) {
            return NaN;
        }

        var valueParts = value.toString().split('e');
        var baseExp = valueParts [1] ? +valueParts [1] : 0;

        value = Math.round(+(valueParts [0] + 'e' + (baseExp - exp)));

        valueParts = value.toString().split('e');
        baseExp = valueParts [1] ? +valueParts [1] : 0;

        value = +(valueParts [0] + 'e' + (baseExp + exp));

        return value;
    },

};

module.exports = utils;
<commit_msg>Start aligning `utils.[Pp]romise` implementation with ES2015 spec.<commit_after>/*! resol-vbus | Copyright (c) 2013-2015, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var utils = {

    /**
     * @see http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
     */
    generateGUID: function() {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        };

        var guid = [
            s4(),
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            s4(),
            s4(),
        ].join('');

        return guid;
    },

    cancelablePromise: function(callback, thisArg) {
        var cancelDeferred = Q.defer();
        var cancelPromise = cancelDeferred.promise;
        var isCanceled = false;

        var cancel = function(reason) {
            if (!isCanceled) {
                cancelPromise.then(function(result) {
                    result.cancel(reason);
                });

                isCanceled = true;
            }
        };

        var checkCanceled = function(result) {
            if (!isCanceled) {
                return result;
            } else {
                throw new Error('Canceled');
            }
        };

        var promise = utils.promise(function(resolve, reject, notify) {
            if (cancelDeferred) {
                cancelDeferred.resolve({ cancel: reject });

                cancelDeferred = null;
            }

            return Q.fcall(function() {
                return cancelPromise;
            }).then(function() {
                return callback.call(thisArg, resolve, reject, notify, checkCanceled);
            });
        });

        promise.cancel = cancel;

        return promise;
    },

    roundNumber: function(value, exp) {
        if ((value === undefined) || (exp === undefined) || (+exp === 0)) {
            return value;
        }

        value = +value;
        exp = +exp;

        if (_.isNaN(value) || (exp % 1 !== 0)) {
            return NaN;
        }

        var valueParts = value.toString().split('e');
        var baseExp = valueParts [1] ? +valueParts [1] : 0;

        value = Math.round(+(valueParts [0] + 'e' + (baseExp - exp)));

        valueParts = value.toString().split('e');
        baseExp = valueParts [1] ? +valueParts [1] : 0;

        value = +(valueParts [0] + 'e' + (baseExp + exp));

        return value;
    },

};

var Promise = function(callback, thisArg) {
    var deferred = Q.defer();
    var promise = deferred.promise;

    var resolve = function(result) {
        if (deferred) {
            deferred.resolve(result);

            deferred = null;
        }
    };

    var reject = function(reason) {
        if (deferred) {
            deferred.reject(reason);

            deferred = null;
        }
    };

    var notify = function(value) {
        if (deferred) {
            deferred.notify(value);
        }
    };

    try {
        callback.call(thisArg, resolve, reject, notify);
    } catch (ex) {
        reject(ex);
    }

    return promise;
};

Promise.resolve = function(value) {
    return new Promise(function(resolve) {
        resolve(value);
    });
};

Promise.reject = function(reason) {
    return new Promise(function(resolve, reject) {
        reject(reason);
    });
};

utils.Promise = Promise;

// NOTE(daniel): Legacy naming
utils.promise = Promise;

module.exports = utils;
<|endoftext|>


<commit_before>/**
 * [isAllElements description]
 * @param  {[type]}  type [description]
 * @param  {[type]}  el   [description]
 * @return {Boolean}      [description]
 */
const isAllElements = (type, el) => {
  if (el.innerText !== '') {
    let elc = el.cloneNode(true)
    let array = [...elc.querySelectorAll(type)]
    for (let cell of array) {
      cell.remove();
    }
    return elc.innerText === ''
  } else {
    return false;
  }
}

/**
 * [matchById description]
 * @param  {[type]} el [description]
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
const matchById = (el, id) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.id === id;
}

/**
 * [matchByClass description]
 * @param  {[type]} el        [description]
 * @param  {[type]} className [description]
 * @return {[type]}           [description]
 */
const matchByClass = (el, className) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.classList.contains(className);
}

/**
 * [matchByTag description]
 * @param  {[type]} el  [description]
 * @param  {[type]} tag [description]
 * @return {[type]}     [description]
 */
const matchByTag = (el, tag) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.nodeName.toLowerCase() === tag;
}

/**
 * [judgeStart description]
 * @param  {[type]} reg [description]
 * @return {[type]}     [description]
 */
const judgeStart = reg => {
  let start = reg.charAt(0);
  switch (start) {
    case '#':
      return [matchById, reg.substr(1)];
      break;
    case '.':
      return [matchByClass, reg.substr(1)];
      break;
    default:
      return [matchByTag, reg];
      break;
  }
}

/**
 * [removeEmpty description]
 * @param  {HtmlCollection,NodeList,Array} collection this val must be live or inside some root constructure
 * @return {[type]}            [description]
 */
const removeEmpty = function(collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText === '') {
      removeList.push(el)
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const addTag = function(tag, collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  //supposed to be a span collection
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText == '') {
      removeList.push(el);
    } else {
      el.innerHTML = `<${tag}>${el.innerHTML.replace(`<${tag}>`, '').replace(`</${tag}>`, '')}</${tag}>`
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const removeTag = function(tag, collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText === '') {
      removeList.push(el);
    } else {
      el.innerHTML = el.innerHTML.replace(`<${tag}>`, '').replace(`</${tag}>`, '')
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const addTagInPara = function(tag, collection) {
  // let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    // if (el.innerText === '') {
    //   if (index != 0 || index != collection.length) {
    //     removeList.push(el)
    //   }
    // } else {
    addTag(tag, el.children);
    // }

  }
  // for (let [index,element] of removeList.entries()) {
  //   element.remove();
  // }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const removeTagInPara = function(tag, collection) {
  // let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    // if (el.innerText === '') {
    //   if (index != 0 || index != collection.length) {
    //     removeList.push(el)
    //   }
    // } else {
    removeTag(tag, el.children);
    // }

  }
  // for (let [index,element] of removeList.entries()) {
  //   element.remove();
  // }
}

/**
 * compare obj & foo is equal
 * @param  {Object} obj [description]
 * @param  {Object} foo [description]
 * @return {Boolean}     [description]
 */
const isEqual = function(obj, foo) {
  let result = false;
  let oarr = Object.keys(obj).sort(),
    farr = Object.keys(foo).sort();
  if (oarr.toString() == farr.toString()) {
    result = true;
    oarr.forEach(key => {
      if (typeof obj[key] == 'object') {
        result = isEqual(obj[key], foo[key]) && result;
      } else {
        result = (obj[key] == foo[key]) && result;
      }
    })
    return result;
  }
  return result;
}

/**
 * wrap content with ele.parentNode til the div.para element.
 * @param  {Node} ele     document's node
 * @param  {Node} content node to be wraped
 * @return {Node}         wrapedSpanElement or undefined
 */
const wrapParent = (reg, ele, content) => {
  let [match,name] = judgeStart(reg);
  let found = undefined;
  let proxy = ele;
  let tmp;
  if (content) {
    tmp = content;
  } else {
    tmp = ele.cloneNode(true);
  }
  while (!found) {
    if (match(proxy, name)) {
      found = proxy.cloneNode(false);
      found.appendChild(tmp);
    } else if (proxy.nodeName == 'BODY') {
      return;
    } else {
      if (proxy.nodeName != '#text') {
        let tp = proxy.cloneNode(false);
        tp.appendChild(tmp);
        tmp = tp;
      }
      proxy = proxy.parentNode;
    }
  }
  return found;
}

const getChildrenStyle = (type, el) => {
  let spans = el.childNodes;
  let arr = [];
  if (spans && spans.length > 0) {
    [...spans].forEach(c => {
      arr.push(c.style[type])
    })
  }
  return arr;
}

const getParent = (ele, reg) => {
  let [match,
    name] = judgeStart(reg)
  let found = undefined;
  let proxy = ele;
  while (!found) {
    if (match(proxy, name)) {
      found = proxy;
    } else if (proxy.nodeName == 'BODY') {
      return;
    } else {
      proxy = proxy.parentNode
    }
  }
  return found;
}

function isFirstInPara(el) {
  let s = el;
  if (!isFirst(el)) {
    return false;
  } else {
    if (el.parentNode.classList.contains('para')) {
      return true;
    } else {
      return isFirstInPara(el.parentNode)
    }
  }
}

function isFirst(el) {
  return el.parentNode.firstChild == el
}

function replaceElementIn(reg,node){
  let newFrag = document.createDocumentFragment();
  [...node.querySelectorAll(reg)].forEach(el=>{
    [...el.childNodes].forEach(i=>{
      newFrag.appendChild(i);
    })
    el.parentNode.insertBefore(newFrag,el);
    el.remove();
  })
  node.normalize();
}

module.exports = {
  isAllElements,
  matchById,
  matchByTag,
  matchByClass,
  judgeStart,
  removeEmpty,
  addTag,
  removeTag,
  addTagInPara,
  removeTagInPara,
  isEqual,
  wrapParent,
  getChildrenStyle,
  getParent,
  isFirstInPara,
  isFirst,
  replaceElementIn
}
<commit_msg>add isLastInPara & isLast<commit_after>/**
 * [isAllElements description]
 * @param  {[type]}  type [description]
 * @param  {[type]}  el   [description]
 * @return {Boolean}      [description]
 */
const isAllElements = (type, el) => {
  if (el.innerText !== '') {
    let elc = el.cloneNode(true)
    let array = [...elc.querySelectorAll(type)]
    for (let cell of array) {
      cell.remove();
    }
    return elc.innerText === ''
  } else {
    return false;
  }
}

/**
 * [matchById description]
 * @param  {[type]} el [description]
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
const matchById = (el, id) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.id === id;
}

/**
 * [matchByClass description]
 * @param  {[type]} el        [description]
 * @param  {[type]} className [description]
 * @return {[type]}           [description]
 */
const matchByClass = (el, className) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.classList.contains(className);
}

/**
 * [matchByTag description]
 * @param  {[type]} el  [description]
 * @param  {[type]} tag [description]
 * @return {[type]}     [description]
 */
const matchByTag = (el, tag) => {
  if (el.nodeName === '#text') {
    return false;
  }
  return el.nodeName.toLowerCase() === tag;
}

/**
 * [judgeStart description]
 * @param  {[type]} reg [description]
 * @return {[type]}     [description]
 */
const judgeStart = reg => {
  let start = reg.charAt(0);
  switch (start) {
    case '#':
      return [matchById, reg.substr(1)];
      break;
    case '.':
      return [matchByClass, reg.substr(1)];
      break;
    default:
      return [matchByTag, reg];
      break;
  }
}

/**
 * [removeEmpty description]
 * @param  {HtmlCollection,NodeList,Array} collection this val must be live or inside some root constructure
 * @return {[type]}            [description]
 */
const removeEmpty = function(collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText === '') {
      removeList.push(el)
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const addTag = function(tag, collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  //supposed to be a span collection
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText == '') {
      removeList.push(el);
    } else {
      el.innerHTML = `<${tag}>${el.innerHTML.replace(`<${tag}>`, '').replace(`</${tag}>`, '')}</${tag}>`
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const removeTag = function(tag, collection) {
  //childNodes{documentFragment.childNodes,document.querySelectorAll(),}
  //htmlCollection{documentFragment.children,document.getElementByTagName(),...}
  let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    if (el.innerText === '') {
      removeList.push(el);
    } else {
      el.innerHTML = el.innerHTML.replace(`<${tag}>`, '').replace(`</${tag}>`, '')
    }
  }
  for (let [index,
    element]of removeList.entries()) {
    element.remove();
  }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const addTagInPara = function(tag, collection) {
  // let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    // if (el.innerText === '') {
    //   if (index != 0 || index != collection.length) {
    //     removeList.push(el)
    //   }
    // } else {
    addTag(tag, el.children);
    // }

  }
  // for (let [index,element] of removeList.entries()) {
  //   element.remove();
  // }
}

/**
 * [description]
 * @param  {[type]} tag        [description]
 * @param  {[type]} collection [description]
 * @return {[type]}            [description]
 */
const removeTagInPara = function(tag, collection) {
  // let removeList = [];
  for (let index = 0; index < collection.length; index++) {
    let el = collection[index]
    // if (el.innerText === '') {
    //   if (index != 0 || index != collection.length) {
    //     removeList.push(el)
    //   }
    // } else {
    removeTag(tag, el.children);
    // }

  }
  // for (let [index,element] of removeList.entries()) {
  //   element.remove();
  // }
}

/**
 * compare obj & foo is equal
 * @param  {Object} obj [description]
 * @param  {Object} foo [description]
 * @return {Boolean}     [description]
 */
const isEqual = function(obj, foo) {
  let result = false;
  let oarr = Object.keys(obj).sort(),
    farr = Object.keys(foo).sort();
  if (oarr.toString() == farr.toString()) {
    result = true;
    oarr.forEach(key => {
      if (typeof obj[key] == 'object') {
        result = isEqual(obj[key], foo[key]) && result;
      } else {
        result = (obj[key] == foo[key]) && result;
      }
    })
    return result;
  }
  return result;
}

/**
 * wrap content with ele.parentNode til the div.para element.
 * @param  {Node} ele     document's node
 * @param  {Node} content node to be wraped
 * @return {Node}         wrapedSpanElement or undefined
 */
const wrapParent = (reg, ele, content) => {
  let [match,name] = judgeStart(reg);
  let found = undefined;
  let proxy = ele;
  let tmp;
  if (content) {
    tmp = content;
  } else {
    tmp = ele.cloneNode(true);
  }
  while (!found) {
    if (match(proxy, name)) {
      found = proxy.cloneNode(false);
      found.appendChild(tmp);
    } else if (proxy.nodeName == 'BODY') {
      return;
    } else {
      if (proxy.nodeName != '#text') {
        let tp = proxy.cloneNode(false);
        tp.appendChild(tmp);
        tmp = tp;
      }
      proxy = proxy.parentNode;
    }
  }
  return found;
}

const getChildrenStyle = (type, el) => {
  let spans = el.childNodes;
  let arr = [];
  if (spans && spans.length > 0) {
    [...spans].forEach(c => {
      arr.push(c.style[type])
    })
  }
  return arr;
}

const getParent = (ele, reg) => {
  let [match,
    name] = judgeStart(reg)
  let found = undefined;
  let proxy = ele;
  while (!found) {
    if (match(proxy, name)) {
      found = proxy;
    } else if (proxy.nodeName == 'BODY') {
      return;
    } else {
      proxy = proxy.parentNode
    }
  }
  return found;
}

function isFirstInPara(el) {
  if (!isFirst(el)) {
    return false;
  } else {
    if (el.parentNode.classList.contains('para')) {
      return true;
    } else {
      return isFirstInPara(el.parentNode)
    }
  }
}

function isLastInPara(el){
  if(!isLast(el)){
    return false;
  }else{
    if(el.parentNode.classList.contains('para')){
      return true;
    }else{
      return isLastInPara(el)
    }
  }
}

function isFirst(el) {
  return el.parentNode.firstChild == el
}

function isLast(el){
  return el.parentNode.lastChild == el
}

function replaceElementIn(reg,node){
  let newFrag = document.createDocumentFragment();
  [...node.querySelectorAll(reg)].forEach(el=>{
    [...el.childNodes].forEach(i=>{
      newFrag.appendChild(i);
    })
    el.parentNode.insertBefore(newFrag,el);
    el.remove();
  })
  node.normalize();
}

module.exports = {
  isAllElements,
  matchById,
  matchByTag,
  matchByClass,
  judgeStart,
  removeEmpty,
  addTag,
  removeTag,
  addTagInPara,
  removeTagInPara,
  isEqual,
  wrapParent,
  getChildrenStyle,
  getParent,
  isFirstInPara,
  isFirst,
  replaceElementIn
}
<|endoftext|>


<commit_before>import base64url from 'base64url'
import eventToPromise from 'event-to-promise'
import forEach from 'lodash/forEach'
import has from 'lodash/has'
import highland from 'highland'
import humanFormat from 'human-format'
import invert from 'lodash/invert'
import isArray from 'lodash/isArray'
import isString from 'lodash/isString'
import keys from 'lodash/keys'
import kindOf from 'kindof'
import mapToArray from 'lodash/map'
import multiKeyHashInt from 'multikey-hash'
import pick from 'lodash/pick'
import tmp from 'tmp'
import xml2js from 'xml2js'
import { resolve } from 'path'

// Moment timezone can be loaded only one time, it's a workaround to load
// the latest version because cron module uses an old version of moment which
// does not implement `guess` function for example.
import 'moment-timezone'

import through2 from 'through2'
import { CronJob } from 'cron'
import { utcFormat, utcParse } from 'd3-time-format'
import {
  all as pAll,
  defer,
  fromCallback,
  isPromise,
  promisify,
  reflect as pReflect
} from 'promise-toolbox'
import {
  createHash,
  randomBytes
} from 'crypto'

// ===================================================================

// Similar to map() + Promise.all() but wait for all promises to
// settle before rejecting (with the first error)
export const asyncMap = (collection, iteratee) => {
  if (isPromise(collection)) {
    return collection.then(collection => asyncMap(collection, iteratee))
  }

  let errorContainer
  const onError = error => {
    if (errorContainer === undefined) {
      errorContainer = { error }
    }
  }

  return Promise.all(mapToArray(collection, (item, key, collection) =>
    new Promise(resolve => {
      resolve(iteratee(item, key, collection))
    }).catch(onError)
  )).then(values => {
    if (errorContainer !== undefined) {
      throw errorContainer.error
    }
    return values
  })
}

// -------------------------------------------------------------------

export streamToBuffer from './stream-to-new-buffer'

// -------------------------------------------------------------------

export function camelToSnakeCase (string) {
  return string.replace(
    /([a-z0-9])([A-Z])/g,
    (_, prevChar, currChar) => `${prevChar}_${currChar.toLowerCase()}`
  )
}

// -------------------------------------------------------------------

// Returns an empty object without prototype (if possible).
export const createRawObject = Object.create
  ? (createObject => () => createObject(null))(Object.create)
  : () => ({})

// -------------------------------------------------------------------

// Only works with string items!
export const diffItems = (coll1, coll2) => {
  const removed = createRawObject()
  forEach(coll2, value => {
    removed[value] = true
  })

  const added = []
  forEach(coll1, value => {
    if (value in removed) {
      delete removed[value]
    } else {
      added.push(value)
    }
  })

  return [ added, keys(removed) ]
}

// -------------------------------------------------------------------

const ALGORITHM_TO_ID = {
  md5: '1',
  sha256: '5',
  sha512: '6'
}

const ID_TO_ALGORITHM = invert(ALGORITHM_TO_ID)

// Wrap a readable stream in a stream with a checksum promise
// attribute which is resolved at the end of an input stream.
// (Finally .checksum contains the checksum of the input stream)
//
// Example:
// const sourceStream = ...
// const targetStream = ...
// const checksumStream = addChecksumToReadStream(sourceStream)
// await Promise.all([
//   eventToPromise(checksumStream.pipe(targetStream), 'finish'),
//   checksumStream.checksum.then(console.log)
// ])
export const addChecksumToReadStream = (stream, algorithm = 'md5') => {
  const algorithmId = ALGORITHM_TO_ID[algorithm]

  if (!algorithmId) {
    throw new Error(`unknown algorithm: ${algorithm}`)
  }

  const hash = createHash(algorithm)
  const { promise, resolve } = defer()

  const wrapper = stream.pipe(through2(
    (chunk, enc, callback) => {
      hash.update(chunk)
      callback(null, chunk)
    },
    callback => {
      resolve(hash.digest('hex'))
      callback()
    }
  ))

  stream.on('error', error => wrapper.emit('error', error))
  wrapper.checksum = promise.then(hash => `$${algorithmId}$$${hash}`)

  return wrapper
}

// Check if the checksum of a readable stream is equals to an expected checksum.
// The given stream is wrapped in a stream which emits an error event
// if the computed checksum is not equals to the expected checksum.
export const validChecksumOfReadStream = (stream, expectedChecksum) => {
  const algorithmId = expectedChecksum.slice(1, expectedChecksum.indexOf('$', 1))

  if (!algorithmId) {
    throw new Error(`unknown algorithm: ${algorithmId}`)
  }

  const hash = createHash(ID_TO_ALGORITHM[algorithmId])

  const wrapper = stream.pipe(through2(
    { highWaterMark: 0 },
    (chunk, enc, callback) => {
      hash.update(chunk)
      callback(null, chunk)
    },
    callback => {
      const checksum = `$${algorithmId}$$${hash.digest('hex')}`

      callback(
        checksum !== expectedChecksum
          ? new Error(`Bad checksum (${checksum}), expected: ${expectedChecksum}`)
          : null
      )
    }
  ))

  stream.on('error', error => wrapper.emit('error', error))
  wrapper.checksumVerified = eventToPromise(wrapper, 'end')

  return wrapper
}

// -------------------------------------------------------------------

// Ensure the value is an array, wrap it if necessary.
export function ensureArray (value) {
  if (value === undefined) {
    return []
  }

  return isArray(value) ? value : [value]
}

// -------------------------------------------------------------------

// Returns the value of a property and removes it from the object.
export function extractProperty (obj, prop) {
  const value = obj[prop]
  delete obj[prop]
  return value
}

// -------------------------------------------------------------------

// Returns the first defined (non-undefined) value.
export const firstDefined = function () {
  const n = arguments.length
  for (let i = 0; i < n; ++i) {
    const arg = arguments[i]
    if (arg !== undefined) {
      return arg
    }
  }
}

// -------------------------------------------------------------------

export const getUserPublicProperties = user => pick(
  user.properties || user,
  'id', 'email', 'groups', 'permission', 'preferences', 'provider'
)

// -------------------------------------------------------------------

export const getPseudoRandomBytes = n => {
  const bytes = Buffer.allocUnsafe(n)

  const odd = n & 1
  for (let i = 0, m = n - odd; i < m; i += 2) {
    bytes.writeUInt16BE(Math.random() * 65536 | 0, i)
  }

  if (odd) {
    bytes.writeUInt8(Math.random() * 256 | 0, n - 1)
  }

  return bytes
}

export const generateUnsecureToken = (n = 32) => base64url(getPseudoRandomBytes(n))

// Generate a secure random Base64 string.
export const generateToken = (randomBytes => {
  return (n = 32) => randomBytes(n).then(base64url)
})(promisify(randomBytes))

// -------------------------------------------------------------------

export const formatXml = (function () {
  const builder = new xml2js.Builder({
    headless: true
  })

  return (...args) => builder.buildObject(...args)
})()

export const parseXml = (function () {
  const opts = {
    mergeAttrs: true,
    explicitArray: false
  }

  return (xml) => {
    let result

    // xml2js.parseString() use a callback for synchronous code.
    xml2js.parseString(xml, opts, (error, result_) => {
      if (error) {
        throw error
      }

      result = result_
    })

    return result
  }
})()

// -------------------------------------------------------------------

// Very light and fast set.
//
// - works only with strings
// - methods are already bound and chainable
export const lightSet = collection => {
  const data = createRawObject()
  if (collection) {
    forEach(collection, value => {
      data[value] = true
    })
    collection = null
  }

  const set = {
    add: value => {
      data[value] = true
      return set
    },
    clear: () => {
      for (const value in data) {
        delete data[value]
      }
      return set
    },
    delete: value => {
      delete data[value]
      return set
    },
    has: value => data[value],
    toArray: () => keys(data)
  }
  return set
}

// -------------------------------------------------------------------

// This function does nothing and returns undefined.
//
// It is often used to swallow promise's errors.
export const noop = () => {}

// -------------------------------------------------------------------

// Usage: pDebug(promise, name) or promise::pDebug(name)
export function pDebug (promise, name) {
  if (arguments.length === 1) {
    name = promise
    promise = this
  }

  Promise.resolve(promise).then(
    value => {
      console.log(
        '%s',
        `Promise ${name} resolved${value !== undefined ? ` with ${kindOf(value)}` : ''}`
      )
    },
    reason => {
      console.log(
        '%s',
        `Promise ${name} rejected${reason !== undefined ? ` with ${kindOf(reason)}` : ''}`
      )
    }
  )

  return promise
}

// Given a collection (array or object) which contains promises,
// return a promise that is fulfilled when all the items in the
// collection are either fulfilled or rejected.
//
// This promise will be fulfilled with a collection (of the same type,
// array or object) containing promise inspections.
//
// Usage: pSettle(promises) or promises::pSettle()
export function pSettle (promises) {
  return (this || promises)::pAll(p => p::pReflect())
}

// -------------------------------------------------------------------

export {
  all as pAll,
  delay as pDelay,
  fromCallback as pFromCallback,
  lastly as pFinally,
  promisify,
  promisifyAll,
  reflect as pReflect
} from 'promise-toolbox'

// -------------------------------------------------------------------

export function parseSize (size) {
  if (!isString(size)) {
    return size
  }

  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

const _has = Object.prototype.hasOwnProperty

// Removes an own property from an object and returns its value.
export const popProperty = obj => {
  for (const prop in obj) {
    if (_has.call(obj, prop)) {
      return extractProperty(obj, prop)
    }
  }
}

// -------------------------------------------------------------------

// Format a date in ISO 8601 in a safe way to be used in filenames
// (even on Windows).
export const safeDateFormat = utcFormat('%Y%m%dT%H%M%SZ')

export const safeDateParse = utcParse('%Y%m%dT%H%M%SZ')

// -------------------------------------------------------------------

// This functions are often used throughout xo-server.
//
// Exports them from here to avoid direct dependencies on lodash/
export { default as forEach } from 'lodash/forEach'
export { default as isArray } from 'lodash/isArray'
export { default as isBoolean } from 'lodash/isBoolean'
export { default as isEmpty } from 'lodash/isEmpty'
export { default as isFunction } from 'lodash/isFunction'
export { default as isInteger } from 'lodash/isInteger'
export { default as isObject } from 'lodash/isObject'
export { default as isString } from 'lodash/isString'
export { default as mapToArray } from 'lodash/map'

// -------------------------------------------------------------------

// Special value which can be returned to stop an iteration in map()
// and mapInPlace().
export const DONE = {}

// Fill `target` by running each element in `collection` through
// `iteratee`.
//
// If `target` is undefined, it defaults to a new array if
// `collection` is array-like (has a `length` property), otherwise an
// object.
//
// The context of `iteratee` can be specified via `thisArg`.
//
// Note: the Mapping can be interrupted by returning the special value
// `DONE` provided as the fourth argument.
//
// Usage: map(collection, item => item + 1)
export function map (
  collection,
  iteratee,
  target = has(collection, 'length') ? [] : {}
) {
  forEach(collection, (item, i) => {
    const value = iteratee(item, i, collection, DONE)
    if (value === DONE) {
      return false
    }

    target[i] = value
  })

  return target
}

// -------------------------------------------------------------------

// Create a hash from multiple values.
export const multiKeyHash = (...args) => new Promise(resolve => {
  const hash = multiKeyHashInt(...args)

  const buf = Buffer.allocUnsafe(4)
  buf.writeUInt32LE(hash, 0)

  resolve(base64url(buf))
})

// -------------------------------------------------------------------

export const resolveSubpath = (root, path) =>
  resolve(root, `./${resolve('/', path)}`)

// -------------------------------------------------------------------

export const streamToArray = (stream, {
  filter,
  mapper
} = {}) => new Promise((resolve, reject) => {
  stream = highland(stream).stopOnError(reject)
  if (filter) {
    stream = stream.filter(filter)
  }
  if (mapper) {
    stream = stream.map(mapper)
  }
  stream.toArray(resolve)
})

// -------------------------------------------------------------------

export const scheduleFn = (cronTime, fn, timeZone) => {
  let running = false

  const job = new CronJob({
    cronTime,
    onTick: async () => {
      if (running) {
        return
      }

      running = true

      try {
        await fn()
      } catch (error) {
        console.error('[WARN] scheduled function:', (error && error.stack) || error)
      } finally {
        running = false
      }
    },
    start: true,
    timeZone
  })

  return () => {
    job.stop()
  }
}

// -------------------------------------------------------------------

// Create a serializable object from an error.
export const serializeError = error => ({
  message: error.message,
  stack: error.stack,
  ...error // Copy enumerable properties.
})

// -------------------------------------------------------------------

// Create an array which contains the results of one thunk function.
// Only works with synchronous thunks.
export const thunkToArray = thunk => {
  const values = []
  thunk(::values.push)
  return values
}

// -------------------------------------------------------------------

// Creates a new function which throws an error.
//
// ```js
// promise.catch(throwFn('an error has occured'))
//
// function foo (param = throwFn('param is required')()) {}
// ```
export const throwFn = error => () => {
  throw (
    isString(error)
      ? new Error(error)
      : error
  )
}

// -------------------------------------------------------------------

export const tmpDir = () => fromCallback(cb => tmp.dir(cb))

// -------------------------------------------------------------------

// Wrap a value in a function.
export const wrap = value => () => value

// -------------------------------------------------------------------

export const mapFilter = (collection, iteratee) => {
  const result = []
  forEach(collection, (...args) => {
    const value = iteratee(...args)
    if (value) {
      result.push(value)
    }
  })
  return result
}

// -------------------------------------------------------------------

export const splitFirst = (string, separator) => {
  const i = string.indexOf(separator)
  return i === -1 ? null : [
    string.slice(0, i),
    string.slice(i + separator.length)
  ]
}

// -------------------------------------------------------------------

export const getFirstPropertyName = object => {
  for (const key in object) {
    if (Object.prototype.hasOwnProperty.call(object, key)) {
      return key
    }
  }
}
<commit_msg>chore(utils/lightSet): minor improvement in clear method<commit_after>import base64url from 'base64url'
import eventToPromise from 'event-to-promise'
import forEach from 'lodash/forEach'
import has from 'lodash/has'
import highland from 'highland'
import humanFormat from 'human-format'
import invert from 'lodash/invert'
import isArray from 'lodash/isArray'
import isString from 'lodash/isString'
import keys from 'lodash/keys'
import kindOf from 'kindof'
import mapToArray from 'lodash/map'
import multiKeyHashInt from 'multikey-hash'
import pick from 'lodash/pick'
import tmp from 'tmp'
import xml2js from 'xml2js'
import { resolve } from 'path'

// Moment timezone can be loaded only one time, it's a workaround to load
// the latest version because cron module uses an old version of moment which
// does not implement `guess` function for example.
import 'moment-timezone'

import through2 from 'through2'
import { CronJob } from 'cron'
import { utcFormat, utcParse } from 'd3-time-format'
import {
  all as pAll,
  defer,
  fromCallback,
  isPromise,
  promisify,
  reflect as pReflect
} from 'promise-toolbox'
import {
  createHash,
  randomBytes
} from 'crypto'

// ===================================================================

// Similar to map() + Promise.all() but wait for all promises to
// settle before rejecting (with the first error)
export const asyncMap = (collection, iteratee) => {
  if (isPromise(collection)) {
    return collection.then(collection => asyncMap(collection, iteratee))
  }

  let errorContainer
  const onError = error => {
    if (errorContainer === undefined) {
      errorContainer = { error }
    }
  }

  return Promise.all(mapToArray(collection, (item, key, collection) =>
    new Promise(resolve => {
      resolve(iteratee(item, key, collection))
    }).catch(onError)
  )).then(values => {
    if (errorContainer !== undefined) {
      throw errorContainer.error
    }
    return values
  })
}

// -------------------------------------------------------------------

export streamToBuffer from './stream-to-new-buffer'

// -------------------------------------------------------------------

export function camelToSnakeCase (string) {
  return string.replace(
    /([a-z0-9])([A-Z])/g,
    (_, prevChar, currChar) => `${prevChar}_${currChar.toLowerCase()}`
  )
}

// -------------------------------------------------------------------

// Returns an empty object without prototype (if possible).
export const createRawObject = Object.create
  ? (createObject => () => createObject(null))(Object.create)
  : () => ({})

// -------------------------------------------------------------------

// Only works with string items!
export const diffItems = (coll1, coll2) => {
  const removed = createRawObject()
  forEach(coll2, value => {
    removed[value] = true
  })

  const added = []
  forEach(coll1, value => {
    if (value in removed) {
      delete removed[value]
    } else {
      added.push(value)
    }
  })

  return [ added, keys(removed) ]
}

// -------------------------------------------------------------------

const ALGORITHM_TO_ID = {
  md5: '1',
  sha256: '5',
  sha512: '6'
}

const ID_TO_ALGORITHM = invert(ALGORITHM_TO_ID)

// Wrap a readable stream in a stream with a checksum promise
// attribute which is resolved at the end of an input stream.
// (Finally .checksum contains the checksum of the input stream)
//
// Example:
// const sourceStream = ...
// const targetStream = ...
// const checksumStream = addChecksumToReadStream(sourceStream)
// await Promise.all([
//   eventToPromise(checksumStream.pipe(targetStream), 'finish'),
//   checksumStream.checksum.then(console.log)
// ])
export const addChecksumToReadStream = (stream, algorithm = 'md5') => {
  const algorithmId = ALGORITHM_TO_ID[algorithm]

  if (!algorithmId) {
    throw new Error(`unknown algorithm: ${algorithm}`)
  }

  const hash = createHash(algorithm)
  const { promise, resolve } = defer()

  const wrapper = stream.pipe(through2(
    (chunk, enc, callback) => {
      hash.update(chunk)
      callback(null, chunk)
    },
    callback => {
      resolve(hash.digest('hex'))
      callback()
    }
  ))

  stream.on('error', error => wrapper.emit('error', error))
  wrapper.checksum = promise.then(hash => `$${algorithmId}$$${hash}`)

  return wrapper
}

// Check if the checksum of a readable stream is equals to an expected checksum.
// The given stream is wrapped in a stream which emits an error event
// if the computed checksum is not equals to the expected checksum.
export const validChecksumOfReadStream = (stream, expectedChecksum) => {
  const algorithmId = expectedChecksum.slice(1, expectedChecksum.indexOf('$', 1))

  if (!algorithmId) {
    throw new Error(`unknown algorithm: ${algorithmId}`)
  }

  const hash = createHash(ID_TO_ALGORITHM[algorithmId])

  const wrapper = stream.pipe(through2(
    { highWaterMark: 0 },
    (chunk, enc, callback) => {
      hash.update(chunk)
      callback(null, chunk)
    },
    callback => {
      const checksum = `$${algorithmId}$$${hash.digest('hex')}`

      callback(
        checksum !== expectedChecksum
          ? new Error(`Bad checksum (${checksum}), expected: ${expectedChecksum}`)
          : null
      )
    }
  ))

  stream.on('error', error => wrapper.emit('error', error))
  wrapper.checksumVerified = eventToPromise(wrapper, 'end')

  return wrapper
}

// -------------------------------------------------------------------

// Ensure the value is an array, wrap it if necessary.
export function ensureArray (value) {
  if (value === undefined) {
    return []
  }

  return isArray(value) ? value : [value]
}

// -------------------------------------------------------------------

// Returns the value of a property and removes it from the object.
export function extractProperty (obj, prop) {
  const value = obj[prop]
  delete obj[prop]
  return value
}

// -------------------------------------------------------------------

// Returns the first defined (non-undefined) value.
export const firstDefined = function () {
  const n = arguments.length
  for (let i = 0; i < n; ++i) {
    const arg = arguments[i]
    if (arg !== undefined) {
      return arg
    }
  }
}

// -------------------------------------------------------------------

export const getUserPublicProperties = user => pick(
  user.properties || user,
  'id', 'email', 'groups', 'permission', 'preferences', 'provider'
)

// -------------------------------------------------------------------

export const getPseudoRandomBytes = n => {
  const bytes = Buffer.allocUnsafe(n)

  const odd = n & 1
  for (let i = 0, m = n - odd; i < m; i += 2) {
    bytes.writeUInt16BE(Math.random() * 65536 | 0, i)
  }

  if (odd) {
    bytes.writeUInt8(Math.random() * 256 | 0, n - 1)
  }

  return bytes
}

export const generateUnsecureToken = (n = 32) => base64url(getPseudoRandomBytes(n))

// Generate a secure random Base64 string.
export const generateToken = (randomBytes => {
  return (n = 32) => randomBytes(n).then(base64url)
})(promisify(randomBytes))

// -------------------------------------------------------------------

export const formatXml = (function () {
  const builder = new xml2js.Builder({
    headless: true
  })

  return (...args) => builder.buildObject(...args)
})()

export const parseXml = (function () {
  const opts = {
    mergeAttrs: true,
    explicitArray: false
  }

  return (xml) => {
    let result

    // xml2js.parseString() use a callback for synchronous code.
    xml2js.parseString(xml, opts, (error, result_) => {
      if (error) {
        throw error
      }

      result = result_
    })

    return result
  }
})()

// -------------------------------------------------------------------

// Very light and fast set.
//
// - works only with strings
// - methods are already bound and chainable
export const lightSet = collection => {
  let data = createRawObject()
  if (collection) {
    forEach(collection, value => {
      data[value] = true
    })
    collection = null
  }

  const set = {
    add: value => {
      data[value] = true
      return set
    },
    clear: () => {
      data = createRawObject()
      return set
    },
    delete: value => {
      delete data[value]
      return set
    },
    has: value => data[value],
    toArray: () => keys(data)
  }
  return set
}

// -------------------------------------------------------------------

// This function does nothing and returns undefined.
//
// It is often used to swallow promise's errors.
export const noop = () => {}

// -------------------------------------------------------------------

// Usage: pDebug(promise, name) or promise::pDebug(name)
export function pDebug (promise, name) {
  if (arguments.length === 1) {
    name = promise
    promise = this
  }

  Promise.resolve(promise).then(
    value => {
      console.log(
        '%s',
        `Promise ${name} resolved${value !== undefined ? ` with ${kindOf(value)}` : ''}`
      )
    },
    reason => {
      console.log(
        '%s',
        `Promise ${name} rejected${reason !== undefined ? ` with ${kindOf(reason)}` : ''}`
      )
    }
  )

  return promise
}

// Given a collection (array or object) which contains promises,
// return a promise that is fulfilled when all the items in the
// collection are either fulfilled or rejected.
//
// This promise will be fulfilled with a collection (of the same type,
// array or object) containing promise inspections.
//
// Usage: pSettle(promises) or promises::pSettle()
export function pSettle (promises) {
  return (this || promises)::pAll(p => p::pReflect())
}

// -------------------------------------------------------------------

export {
  all as pAll,
  delay as pDelay,
  fromCallback as pFromCallback,
  lastly as pFinally,
  promisify,
  promisifyAll,
  reflect as pReflect
} from 'promise-toolbox'

// -------------------------------------------------------------------

export function parseSize (size) {
  if (!isString(size)) {
    return size
  }

  let bytes = humanFormat.parse.raw(size, { scale: 'binary' })
  if (bytes.unit && bytes.unit !== 'B') {
    bytes = humanFormat.parse.raw(size)

    if (bytes.unit && bytes.unit !== 'B') {
      throw new Error('invalid size: ' + size)
    }
  }
  return Math.floor(bytes.value * bytes.factor)
}

// -------------------------------------------------------------------

const _has = Object.prototype.hasOwnProperty

// Removes an own property from an object and returns its value.
export const popProperty = obj => {
  for (const prop in obj) {
    if (_has.call(obj, prop)) {
      return extractProperty(obj, prop)
    }
  }
}

// -------------------------------------------------------------------

// Format a date in ISO 8601 in a safe way to be used in filenames
// (even on Windows).
export const safeDateFormat = utcFormat('%Y%m%dT%H%M%SZ')

export const safeDateParse = utcParse('%Y%m%dT%H%M%SZ')

// -------------------------------------------------------------------

// This functions are often used throughout xo-server.
//
// Exports them from here to avoid direct dependencies on lodash/
export { default as forEach } from 'lodash/forEach'
export { default as isArray } from 'lodash/isArray'
export { default as isBoolean } from 'lodash/isBoolean'
export { default as isEmpty } from 'lodash/isEmpty'
export { default as isFunction } from 'lodash/isFunction'
export { default as isInteger } from 'lodash/isInteger'
export { default as isObject } from 'lodash/isObject'
export { default as isString } from 'lodash/isString'
export { default as mapToArray } from 'lodash/map'

// -------------------------------------------------------------------

// Special value which can be returned to stop an iteration in map()
// and mapInPlace().
export const DONE = {}

// Fill `target` by running each element in `collection` through
// `iteratee`.
//
// If `target` is undefined, it defaults to a new array if
// `collection` is array-like (has a `length` property), otherwise an
// object.
//
// The context of `iteratee` can be specified via `thisArg`.
//
// Note: the Mapping can be interrupted by returning the special value
// `DONE` provided as the fourth argument.
//
// Usage: map(collection, item => item + 1)
export function map (
  collection,
  iteratee,
  target = has(collection, 'length') ? [] : {}
) {
  forEach(collection, (item, i) => {
    const value = iteratee(item, i, collection, DONE)
    if (value === DONE) {
      return false
    }

    target[i] = value
  })

  return target
}

// -------------------------------------------------------------------

// Create a hash from multiple values.
export const multiKeyHash = (...args) => new Promise(resolve => {
  const hash = multiKeyHashInt(...args)

  const buf = Buffer.allocUnsafe(4)
  buf.writeUInt32LE(hash, 0)

  resolve(base64url(buf))
})

// -------------------------------------------------------------------

export const resolveSubpath = (root, path) =>
  resolve(root, `./${resolve('/', path)}`)

// -------------------------------------------------------------------

export const streamToArray = (stream, {
  filter,
  mapper
} = {}) => new Promise((resolve, reject) => {
  stream = highland(stream).stopOnError(reject)
  if (filter) {
    stream = stream.filter(filter)
  }
  if (mapper) {
    stream = stream.map(mapper)
  }
  stream.toArray(resolve)
})

// -------------------------------------------------------------------

export const scheduleFn = (cronTime, fn, timeZone) => {
  let running = false

  const job = new CronJob({
    cronTime,
    onTick: async () => {
      if (running) {
        return
      }

      running = true

      try {
        await fn()
      } catch (error) {
        console.error('[WARN] scheduled function:', (error && error.stack) || error)
      } finally {
        running = false
      }
    },
    start: true,
    timeZone
  })

  return () => {
    job.stop()
  }
}

// -------------------------------------------------------------------

// Create a serializable object from an error.
export const serializeError = error => ({
  message: error.message,
  stack: error.stack,
  ...error // Copy enumerable properties.
})

// -------------------------------------------------------------------

// Create an array which contains the results of one thunk function.
// Only works with synchronous thunks.
export const thunkToArray = thunk => {
  const values = []
  thunk(::values.push)
  return values
}

// -------------------------------------------------------------------

// Creates a new function which throws an error.
//
// ```js
// promise.catch(throwFn('an error has occured'))
//
// function foo (param = throwFn('param is required')()) {}
// ```
export const throwFn = error => () => {
  throw (
    isString(error)
      ? new Error(error)
      : error
  )
}

// -------------------------------------------------------------------

export const tmpDir = () => fromCallback(cb => tmp.dir(cb))

// -------------------------------------------------------------------

// Wrap a value in a function.
export const wrap = value => () => value

// -------------------------------------------------------------------

export const mapFilter = (collection, iteratee) => {
  const result = []
  forEach(collection, (...args) => {
    const value = iteratee(...args)
    if (value) {
      result.push(value)
    }
  })
  return result
}

// -------------------------------------------------------------------

export const splitFirst = (string, separator) => {
  const i = string.indexOf(separator)
  return i === -1 ? null : [
    string.slice(0, i),
    string.slice(i + separator.length)
  ]
}

// -------------------------------------------------------------------

export const getFirstPropertyName = object => {
  for (const key in object) {
    if (Object.prototype.hasOwnProperty.call(object, key)) {
      return key
    }
  }
}
<|endoftext|>


<commit_before>var assert = require('assert');
var request = require('superagent');
var async = require('async');
var throttle = require('throttleme');

function btcToSatoshi(value) {
  return Math.round(1e8 * parseFloat(value))
}

function assertJSend(body) {
  assert.notEqual(body.status, 'error', body.message || 'Invalid JSend response:' + JSON.stringify(body));
  assert.notEqual(body.status, 'fail', body.data || 'Invalid JSend response: ' + JSON.stringify(body));

  assert.equal(body.status, 'success', 'Unexpected JSend response: ' + body);
  assert.notEqual(body.data, undefined, 'Unexpected JSend response: ' + body)
}

function handleJSend(callback) {
  return function(err, response) {
    if (err) return callback(err);

    var body = JSON.parse(response.text);
    try {
      assertJSend(body)
    } catch (exception) {
      return callback(exception)
    }

    callback(null, body.data)
  }
}

function batchRequest(uri, items, options, callback) {
  items = [].concat(items);

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

  var itemsPerBatch = options.itemsPerBatch || 20;
  var params = options.params;

  var batches = [];
  while(items.length > itemsPerBatch){
    var batch = items.splice(0, itemsPerBatch);
    batches.push(batch)
  }

  if(items.length > 0) batches.push(items);

  var requests = batches.map(function(batch) {
    return function(cb) {
      module.exports.makeRequest(uri + batch.join(','), params, cb)
    }
  });

  var consolidated = [];
  async.parallel(requests, function(err, results) {
    if(err) return callback(err);

    results.forEach(function(r) {
      consolidated = consolidated.concat(r)
    });

    callback(null, consolidated)
  })
}

function makeRequest(uri, params, callback){
  if (Array.isArray(params)){
    uri +=  '?' + params.join('&')
  } else if ('function' === typeof params) {
    callback = params
  }

  request
    .get(uri)
    .timeout(20000)
    .end(handleJSend(callback))
}

function makePostRequest(uri, form, callback){
  request
    .post(uri)
    .timeout(20000)
    .send(form)
    .end(handleJSend(callback))
}

function throttleGet (millis) {
  module.exports.makeRequest = throttle(makeRequest, millis)
}

function throttlePost (millis) {
  module.exports.makePostRequest = throttle(makePostRequest, millis)
}

module.exports = {
  handleJSend: handleJSend,
  btcToSatoshi: btcToSatoshi,
  batchRequest: batchRequest,
  makeRequest: makeRequest,
  makePostRequest: makePostRequest,
  throttleGet: throttleGet,
  throttlePost: throttlePost
};
<commit_msg>(MISC): Fixed assertions<commit_after>var assert = require('assert');
var request = require('superagent');
var async = require('async');
var throttle = require('throttleme');

function btcToSatoshi(value) {
  return Math.round(1e8 * parseFloat(value))
}

function handleJSend(callback) {
  return function(err, response) {
    if (err) return callback(err);

    var body = JSON.parse(response.text);
    if (!body)
      callback(new Error("Retrieved response isn't correct JSON object: {" + response.text + "}"));
    else
      callback(null, body)
  }
}

function batchRequest(uri, items, options, callback) {
  items = [].concat(items);

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

  var itemsPerBatch = options.itemsPerBatch || 20;
  var params = options.params;

  var batches = [];
  while(items.length > itemsPerBatch){
    var batch = items.splice(0, itemsPerBatch);
    batches.push(batch)
  }

  if(items.length > 0) batches.push(items);

  var requests = batches.map(function(batch) {
    return function(cb) {
      module.exports.makeRequest(uri + batch.join(','), params, cb)
    }
  });

  var consolidated = [];
  async.parallel(requests, function(err, results) {
    if(err) return callback(err);

    results.forEach(function(r) {
      consolidated = consolidated.concat(r)
    });

    callback(null, consolidated)
  })
}

function makeRequest(uri, params, callback){
  if (Array.isArray(params)){
    uri +=  '?' + params.join('&')
  } else if ('function' === typeof params) {
    callback = params
  }

  request
    .get(uri)
    .timeout(20000)
    .end(handleJSend(callback))
}

function makePostRequest(uri, form, callback){
  request
    .post(uri)
    .timeout(20000)
    .send(form)
    .end(handleJSend(callback))
}

function throttleGet (millis) {
  module.exports.makeRequest = throttle(makeRequest, millis)
}

function throttlePost (millis) {
  module.exports.makePostRequest = throttle(makePostRequest, millis)
}

module.exports = {
  handleJSend: handleJSend,
  btcToSatoshi: btcToSatoshi,
  batchRequest: batchRequest,
  makeRequest: makeRequest,
  makePostRequest: makePostRequest,
  throttleGet: throttleGet,
  throttlePost: throttlePost
};
<|endoftext|>


<commit_before>'use strict';

(function () {

  var MATCHES_LEADING_AND_TRAILING_SPACES = /(^\s+|\s+$)/;
  var MATCHES_TRAILING_SPACES = /\s+$/;
  var MATCHES_SPACE = /\s/;
  var MATCHES_BAD_NAME_CHARS = /[^a-z0-9-]/i;

  var VALID_CHILD_NODES = [
    'arg',
    'flag',
    'kwarg',
    'command',
    'require-any',
    'require-all',
    'required'
  ];

  var TABLE_OPTIONS = {
    indentation: '    ',
    margin: '  ',
    width: 80
  };

  function find (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        return arr[i];
      }
    }

    return null;
  }

  function each (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      fn(arr[i], i);
    }
  }

  function any (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        return true;
      }
    }

    return false;
  }

  function several (arr, fn) {
    var count = 0;

    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        count += 1;
      }

      if (count > 1) {
        return true;
      }
    }

    return count > 1;
  }

  function sum (arr) {
    var total = 0;

    for (var i = 0; i < arr.length; i += 1) {
      total += arr[i];
    }

    return total;
  }

  function argsToArray (args) {
    return Array.prototype.slice.call(args);
  }

  function validateChildren (children, validTypes) {
    var names = [];
    var aliases = [];

    each(children, function (node) {
      if (typeof node !== 'object') {
        throw new Error('Invalid child node of type ' + (typeof node));
      }

      if (validTypes.indexOf(node._type) < 0) {
        throw new Error('Invalid child node with type ' + node._type +
          '. Child nodes may only be ' + validTypes.join(', '));
      }

      if (node.name) {
        if (names.indexOf(node.name) >= 0) {
          throw new Error('More than one node with the name "' + node.name + '" at the same level');
        }

        names.push(node.name);
      }

      if (node.options && node.options.alias) {
        if (aliases.indexOf(node.options.alias) >= 0) {
          throw new Error('More than one node with the alias "' + node.options.alias + '" at the same level');
        }

        aliases.push(node.options.alias);
      }
    });
  }

  function getNodeProperties (args, getChildren) {
    var children = argsToArray(args);
    var name = children.shift();
    var options = children.shift() || {};

    var properties = {
      name: name,
      options: options
    };

    if (getChildren) {
      validateChildren(children, VALID_CHILD_NODES);

      properties._requireAll = [];
      properties._requireAny = [];
      properties.children = [];

      each(children, function (child) {
        switch (child._type) {
          case 'required':
          case 'require-all':
            properties._requireAll = properties._requireAll.concat(child.children);
            properties.children = properties.children.concat(child.children);
            break;
          case 'require-any':
            properties._requireAny.push(child.children);
            properties.children = properties.children.concat(child.children);
            break;
          default:
            properties.children = properties.children.concat(child);
            break;
        }
      });

      var moreThanOneCommand = several(properties._requireAll, function (child) {
        return child._type === 'command';
      });

      if (moreThanOneCommand) {
        throw new Error('More than one required Command at the same level. Use RequireAny');
      }
    } else if (children.length) {
      throw new Error('Only commands can have children');
    }

    return properties;
  }

  function validateName (name) {
    if (typeof name !== 'string') {
      throw new Error('Names and aliases must be a string');
    }

    if (!name) {
      throw new Error('Names and aliases cannot be empty');
    }

    if (MATCHES_BAD_NAME_CHARS.test(name)) {
      throw new Error('Names and aliases may only contain letters, numbers, and hyphens');
    }

    if (name.indexOf('-') === 0) {
      throw new Error('Names and aliases cannot begin with \'-\'');
    }
  }

  function serializeOptions (options, validOptions) {
    if (!(typeof options === 'undefined' || typeof options === 'object') || Array.isArray(options)) {
      throw new Error('Options must be an object');
    }

    if ('_type' in options) {
      throw new Error('It looks like you\'ve accidentally passed a node as another node\'s second argument (options)');
    }

    for (var key in options) {
      var option = options[key];

      if (!(key in validOptions)) {
        throw new Error('Invalid option \'' + key + '\'');
      }

      if (key === 'alias') {
        validateName(option);
      }

      var valid = validOptions[key];

      if (
        (valid.type === 'string' && typeof option !== 'string') ||
        (valid.type === 'number' && typeof option !== 'number') ||
        (valid.type === 'object' && (typeof option !== 'object' || option === null)) ||
        (valid.type === 'array' && !Array.isArray(option)) ||
        (valid.type === 'boolean' && typeof option !== 'boolean') ||
        (valid.type === 'function' && typeof option !== 'function')
      ) {
        throw new Error('Option ' + key + ' must be of type ' + valid.type);
      }

      if (valid.length && option.length !== valid.length) {
        throw new Error('Option ' + key + ' must be of length ' + valid.length);
      }
    }

    for (var validKey in validOptions) {
      var validOption = validOptions[validKey];

      if (('default' in validOption) && !(validKey in options)) {
        options[validKey] = validOption.default;
      }
    }
  }

  /*
    Usage: program <command> [options]

    Commands:
      command  Do a thing

    Options:
      --help, -h     Show help                                             [boolean]
      --version, -v  Return the version number                             [boolean]

    Examples:
      program command --flag                                   (A brief description)

    Unknown argument: unknown
  */

  function getMaxTableWidths (table) {
    var maxWidths = [];

    each(table, function (row) {
      each(row, function (cell, index) {
        if (typeof maxWidths[index] === 'undefined' || cell.length > maxWidths[index]) {
          maxWidths[index] = cell.length;
        }
      });
    });

    return maxWidths;
  }

  function getRemainingSpace (maxWidths, options) {
    var remainingSpace = TABLE_OPTIONS.width - TABLE_OPTIONS.indentation.length;

    each(maxWidths, function (value, index) {
      if (options.wrap.indexOf(index) < 0) {
        remainingSpace -= value;
      }

      if (index < maxWidths.length - 1) {
        remainingSpace -= TABLE_OPTIONS.margin.length;
      }
    });

    return remainingSpace;
  }

  function createSpaces (length) {
    var spaces = '';

    for (var i = 0; i < length; i += 1) {
      spaces += ' ';
    }

    return spaces;
  }

  function pad (str, length, right) {
    if (!right) {
      return (str + createSpaces(length)).substring(0, length);
    }

    return (createSpaces(length) + str).substring(str.length, str.length + length);
  }

  function wrapText (text, availableSpace, currentConcat, nextConcats, alignRight) {
    var wrappedWords = text.split(MATCHES_SPACE);
    var wrappedLineIndex = 0;

    function indentLine (lineIndex) {
      if (typeof nextConcats[lineIndex] === 'undefined') {
        nextConcats[lineIndex] = createSpaces(currentConcat.length);
      } else {
        nextConcats[lineIndex] += TABLE_OPTIONS.margin;
      }
    }

    indentLine(wrappedLineIndex);

    each(wrappedWords, function (word, wordIndex) {
      if (word.length > availableSpace) {
        var hyphenatedWord = word;

        while (hyphenatedWord.length) {
          var withHyphen = availableSpace > 1 &&
            hyphenatedWord.charAt(availableSpace) !== '-' &&
            hyphenatedWord.length > availableSpace;

          nextConcats[wrappedLineIndex] += hyphenatedWord.substring(0, availableSpace - (withHyphen ? 1 : 0)) +
            (withHyphen ? '-' : '');
          hyphenatedWord = hyphenatedWord.substring(availableSpace - (withHyphen ? 1 : 0));

          if (hyphenatedWord.length) {
            wrappedLineIndex += 1;
            indentLine(wrappedLineIndex);
          }
        }
      } else if (nextConcats[wrappedLineIndex].length - currentConcat.length + word.length > availableSpace) {
        wrappedLineIndex += 1;
        indentLine(wrappedLineIndex);
        nextConcats[wrappedLineIndex] += word;
      } else {
        nextConcats[wrappedLineIndex] += word;
      }

      if (
        nextConcats[wrappedLineIndex].length < currentConcat.length + availableSpace &&
        wordIndex < wrappedWords.length - 1
      ) {
        nextConcats[wrappedLineIndex] += ' ';
      }
    });

    each(nextConcats, function (nextConcat, index) {
      nextConcats[index] = nextConcat.substring(0, currentConcat.length) +
        pad(
          nextConcat.substring(currentConcat.length).replace(MATCHES_LEADING_AND_TRAILING_SPACES, ''),
          availableSpace,
          alignRight
        );
    });
  }

  function mapCells (table, options, maxWidths, remainingSpace, row, rowIndex) {
    var currentConcat = TABLE_OPTIONS.indentation;
    var nextConcats = [];

    each(row, function (cell, index) {
      if (options.wrap.indexOf(index) < 0) {
        currentConcat += pad(
          cell,
          maxWidths[index],
          options.alignRight.indexOf(index) >= 0
        );
      } else {
        var totalWrappedMaxWidth = sum(maxWidths.filter(function (width, maxWidthIndex) {
          return options.wrap.indexOf(maxWidthIndex) >= 0;
        }));
        var availableSpace = Math.max(1, Math.round(maxWidths[index] / totalWrappedMaxWidth * remainingSpace));
        var alignRight = options.alignRight.indexOf(index) >= 0;

        wrapText(cell, availableSpace, currentConcat, nextConcats, alignRight);

        currentConcat += pad(
          nextConcats[0].substring(currentConcat.length, currentConcat.length + availableSpace),
          availableSpace,
          alignRight
        );
      }

      if (index < row.length - 1) {
        currentConcat += TABLE_OPTIONS.margin;
      }
    });

    each(nextConcats, function (nextConcat, nextConcatIndex) {
      if (nextConcatIndex > 0) {
        currentConcat += '\n' + nextConcat;
      }
    });

    if (rowIndex < table.length - 1) {
      currentConcat += '\n';
    }

    return currentConcat;
  }

  function mapRows (table, options, maxWidths, remainingSpace) {
    var concat = '';

    each(table, function (row, rowIndex) {
      concat += mapCells(table, options, maxWidths, remainingSpace, row, rowIndex);
    });

    return concat;
  }

  function createTable (table, options, maxWidths, remainingSpace) {
    return mapRows(table, options, maxWidths, remainingSpace).split('\n').map(function (line) {
      return line.replace(MATCHES_TRAILING_SPACES, '');
    }).join('\n');
  }

  function formatTable (table, options) {
    var maxWidths = getMaxTableWidths(table);
    var remainingSpace = getRemainingSpace(maxWidths, options);
    return createTable(table, options, maxWidths, remainingSpace);
  }

  function createCommandsText (commands) {
    return (commands.length ? '  Commands:\n' : '') +
      formatTable(commands.map(function (command) {
        var alias = (command.options.alias ? ', ' + command.options.alias : '');
        return [command.name + alias, command.options.description];
      }), {wrap: [1], alignRight: [2]}) +
      (commands.length ? '\n\n' : '');
  }

  function createOptionsText (options) {
    return (options.length ? '  Options:\n' : '') +
      formatTable(options.map(function (option) {
        var namePrefix = option._type === 'arg' ? '<' : '--';
        var nameSuffix = option._type === 'arg' ? '>' : '';
        var aliasPrefix = namePrefix.substring(0, 1);
        var alias = (option.options.alias ? ', ' + aliasPrefix + option.options.alias : '');
        var type = option.options.type ? '   [' + option.options.type + ']' : '';
        return [namePrefix + option.name + nameSuffix + alias, option.options.description, type];
      }), {wrap: [1], alignRight: [2]}) +
      (options.length ? '\n\n' : '');
  }

  function createExamplesText (examples) {
    return (examples.length ? '  Examples:\n' : '') +
      examples.map(function (example) {
        return '    ' + example;
      }).join('\n') +
      (examples.length ? '\n\n' : '');
  }

  function sortByName (a, b) {
    if (a.name < b.name) {
      return -1;
    }

    if (b.name < a.name) {
      return 1;
    }

    return 0;
  }

  function createHelp (schema, globals, error) {
    var commands = [];
    var flags = [];
    var kwargs = [];
    var args = [];

    each(schema.children, function (node) {
      if (node._type === 'command') {
        commands.push(node);
      } else if (node._type === 'flag') {
        flags.push(node);
      } else if (node._type === 'kwarg') {
        kwargs.push(node);
      } else if (node._type === 'arg') {
        args.push(node);
      }
    });

    if (globals.help) {
      flags.unshift(globals.help);
    }

    commands.sort(sortByName);
    flags.sort(sortByName);
    kwargs.sort(sortByName);
    args.sort(sortByName);

    var options = flags.concat(kwargs).concat(args);

    return '\n' +
      (schema.options.usage ? '  Usage: ' + schema.options.usage + '\n\n' : '') +
      createCommandsText(commands) +
      createOptionsText(options) +
      createExamplesText(schema.options.examples) +
      (error ? ('  ' + error + '\n\n') : '');
  }

  /* istanbul ignore next */
  function exitWithHelp (help) {
    process.stderr.write(help);
    process.exit(1);
  }

  function formatNodeName (node) {
    var prefix = node._type === 'flag' || node._type === 'kwarg' ? '--' : '';
    return prefix + node.name;
  }

  function formatRequiredList (nodes) {
    return nodes.map(formatNodeName).join(', ');
  }

  module.exports = {
    find: find,
    each: each,
    any: any,
    several: several,
    sum: sum,
    argsToArray: argsToArray,
    validateChildren: validateChildren,
    getNodeProperties: getNodeProperties,
    validateName: validateName,
    serializeOptions: serializeOptions,
    formatTable: formatTable,
    createHelp: createHelp,
    exitWithHelp: exitWithHelp,
    formatNodeName: formatNodeName,
    formatRequiredList: formatRequiredList
  };

})();
<commit_msg>Do not add help to output if already existent<commit_after>'use strict';

(function () {

  var MATCHES_LEADING_AND_TRAILING_SPACES = /(^\s+|\s+$)/;
  var MATCHES_TRAILING_SPACES = /\s+$/;
  var MATCHES_SPACE = /\s/;
  var MATCHES_BAD_NAME_CHARS = /[^a-z0-9-]/i;

  var VALID_CHILD_NODES = [
    'arg',
    'flag',
    'kwarg',
    'command',
    'require-any',
    'require-all',
    'required'
  ];

  var TABLE_OPTIONS = {
    indentation: '    ',
    margin: '  ',
    width: 80
  };

  function find (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        return arr[i];
      }
    }

    return null;
  }

  function each (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      fn(arr[i], i);
    }
  }

  function any (arr, fn) {
    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        return true;
      }
    }

    return false;
  }

  function several (arr, fn) {
    var count = 0;

    for (var i = 0; i < arr.length; i += 1) {
      if (fn(arr[i], i)) {
        count += 1;
      }

      if (count > 1) {
        return true;
      }
    }

    return count > 1;
  }

  function sum (arr) {
    var total = 0;

    for (var i = 0; i < arr.length; i += 1) {
      total += arr[i];
    }

    return total;
  }

  function argsToArray (args) {
    return Array.prototype.slice.call(args);
  }

  function validateChildren (children, validTypes) {
    var names = [];
    var aliases = [];

    each(children, function (node) {
      if (typeof node !== 'object') {
        throw new Error('Invalid child node of type ' + (typeof node));
      }

      if (validTypes.indexOf(node._type) < 0) {
        throw new Error('Invalid child node with type ' + node._type +
          '. Child nodes may only be ' + validTypes.join(', '));
      }

      if (node.name) {
        if (names.indexOf(node.name) >= 0) {
          throw new Error('More than one node with the name "' + node.name + '" at the same level');
        }

        names.push(node.name);
      }

      if (node.options && node.options.alias) {
        if (aliases.indexOf(node.options.alias) >= 0) {
          throw new Error('More than one node with the alias "' + node.options.alias + '" at the same level');
        }

        aliases.push(node.options.alias);
      }
    });
  }

  function getNodeProperties (args, getChildren) {
    var children = argsToArray(args);
    var name = children.shift();
    var options = children.shift() || {};

    var properties = {
      name: name,
      options: options
    };

    if (getChildren) {
      validateChildren(children, VALID_CHILD_NODES);

      properties._requireAll = [];
      properties._requireAny = [];
      properties.children = [];

      each(children, function (child) {
        switch (child._type) {
          case 'required':
          case 'require-all':
            properties._requireAll = properties._requireAll.concat(child.children);
            properties.children = properties.children.concat(child.children);
            break;
          case 'require-any':
            properties._requireAny.push(child.children);
            properties.children = properties.children.concat(child.children);
            break;
          default:
            properties.children = properties.children.concat(child);
            break;
        }
      });

      var moreThanOneCommand = several(properties._requireAll, function (child) {
        return child._type === 'command';
      });

      if (moreThanOneCommand) {
        throw new Error('More than one required Command at the same level. Use RequireAny');
      }
    } else if (children.length) {
      throw new Error('Only commands can have children');
    }

    return properties;
  }

  function validateName (name) {
    if (typeof name !== 'string') {
      throw new Error('Names and aliases must be a string');
    }

    if (!name) {
      throw new Error('Names and aliases cannot be empty');
    }

    if (MATCHES_BAD_NAME_CHARS.test(name)) {
      throw new Error('Names and aliases may only contain letters, numbers, and hyphens');
    }

    if (name.indexOf('-') === 0) {
      throw new Error('Names and aliases cannot begin with \'-\'');
    }
  }

  function serializeOptions (options, validOptions) {
    if (!(typeof options === 'undefined' || typeof options === 'object') || Array.isArray(options)) {
      throw new Error('Options must be an object');
    }

    if ('_type' in options) {
      throw new Error('It looks like you\'ve accidentally passed a node as another node\'s second argument (options)');
    }

    for (var key in options) {
      var option = options[key];

      if (!(key in validOptions)) {
        throw new Error('Invalid option \'' + key + '\'');
      }

      if (key === 'alias') {
        validateName(option);
      }

      var valid = validOptions[key];

      if (
        (valid.type === 'string' && typeof option !== 'string') ||
        (valid.type === 'number' && typeof option !== 'number') ||
        (valid.type === 'object' && (typeof option !== 'object' || option === null)) ||
        (valid.type === 'array' && !Array.isArray(option)) ||
        (valid.type === 'boolean' && typeof option !== 'boolean') ||
        (valid.type === 'function' && typeof option !== 'function')
      ) {
        throw new Error('Option ' + key + ' must be of type ' + valid.type);
      }

      if (valid.length && option.length !== valid.length) {
        throw new Error('Option ' + key + ' must be of length ' + valid.length);
      }
    }

    for (var validKey in validOptions) {
      var validOption = validOptions[validKey];

      if (('default' in validOption) && !(validKey in options)) {
        options[validKey] = validOption.default;
      }
    }
  }

  /*
    Usage: program <command> [options]

    Commands:
      command  Do a thing

    Options:
      --help, -h     Show help                                             [boolean]
      --version, -v  Return the version number                             [boolean]

    Examples:
      program command --flag                                   (A brief description)

    Unknown argument: unknown
  */

  function getMaxTableWidths (table) {
    var maxWidths = [];

    each(table, function (row) {
      each(row, function (cell, index) {
        if (typeof maxWidths[index] === 'undefined' || cell.length > maxWidths[index]) {
          maxWidths[index] = cell.length;
        }
      });
    });

    return maxWidths;
  }

  function getRemainingSpace (maxWidths, options) {
    var remainingSpace = TABLE_OPTIONS.width - TABLE_OPTIONS.indentation.length;

    each(maxWidths, function (value, index) {
      if (options.wrap.indexOf(index) < 0) {
        remainingSpace -= value;
      }

      if (index < maxWidths.length - 1) {
        remainingSpace -= TABLE_OPTIONS.margin.length;
      }
    });

    return remainingSpace;
  }

  function createSpaces (length) {
    var spaces = '';

    for (var i = 0; i < length; i += 1) {
      spaces += ' ';
    }

    return spaces;
  }

  function pad (str, length, right) {
    if (!right) {
      return (str + createSpaces(length)).substring(0, length);
    }

    return (createSpaces(length) + str).substring(str.length, str.length + length);
  }

  function wrapText (text, availableSpace, currentConcat, nextConcats, alignRight) {
    var wrappedWords = text.split(MATCHES_SPACE);
    var wrappedLineIndex = 0;

    function indentLine (lineIndex) {
      if (typeof nextConcats[lineIndex] === 'undefined') {
        nextConcats[lineIndex] = createSpaces(currentConcat.length);
      } else {
        nextConcats[lineIndex] += TABLE_OPTIONS.margin;
      }
    }

    indentLine(wrappedLineIndex);

    each(wrappedWords, function (word, wordIndex) {
      if (word.length > availableSpace) {
        var hyphenatedWord = word;

        while (hyphenatedWord.length) {
          var withHyphen = availableSpace > 1 &&
            hyphenatedWord.charAt(availableSpace) !== '-' &&
            hyphenatedWord.length > availableSpace;

          nextConcats[wrappedLineIndex] += hyphenatedWord.substring(0, availableSpace - (withHyphen ? 1 : 0)) +
            (withHyphen ? '-' : '');
          hyphenatedWord = hyphenatedWord.substring(availableSpace - (withHyphen ? 1 : 0));

          if (hyphenatedWord.length) {
            wrappedLineIndex += 1;
            indentLine(wrappedLineIndex);
          }
        }
      } else if (nextConcats[wrappedLineIndex].length - currentConcat.length + word.length > availableSpace) {
        wrappedLineIndex += 1;
        indentLine(wrappedLineIndex);
        nextConcats[wrappedLineIndex] += word;
      } else {
        nextConcats[wrappedLineIndex] += word;
      }

      if (
        nextConcats[wrappedLineIndex].length < currentConcat.length + availableSpace &&
        wordIndex < wrappedWords.length - 1
      ) {
        nextConcats[wrappedLineIndex] += ' ';
      }
    });

    each(nextConcats, function (nextConcat, index) {
      nextConcats[index] = nextConcat.substring(0, currentConcat.length) +
        pad(
          nextConcat.substring(currentConcat.length).replace(MATCHES_LEADING_AND_TRAILING_SPACES, ''),
          availableSpace,
          alignRight
        );
    });
  }

  function mapCells (table, options, maxWidths, remainingSpace, row, rowIndex) {
    var currentConcat = TABLE_OPTIONS.indentation;
    var nextConcats = [];

    each(row, function (cell, index) {
      if (options.wrap.indexOf(index) < 0) {
        currentConcat += pad(
          cell,
          maxWidths[index],
          options.alignRight.indexOf(index) >= 0
        );
      } else {
        var totalWrappedMaxWidth = sum(maxWidths.filter(function (width, maxWidthIndex) {
          return options.wrap.indexOf(maxWidthIndex) >= 0;
        }));
        var availableSpace = Math.max(1, Math.round(maxWidths[index] / totalWrappedMaxWidth * remainingSpace));
        var alignRight = options.alignRight.indexOf(index) >= 0;

        wrapText(cell, availableSpace, currentConcat, nextConcats, alignRight);

        currentConcat += pad(
          nextConcats[0].substring(currentConcat.length, currentConcat.length + availableSpace),
          availableSpace,
          alignRight
        );
      }

      if (index < row.length - 1) {
        currentConcat += TABLE_OPTIONS.margin;
      }
    });

    each(nextConcats, function (nextConcat, nextConcatIndex) {
      if (nextConcatIndex > 0) {
        currentConcat += '\n' + nextConcat;
      }
    });

    if (rowIndex < table.length - 1) {
      currentConcat += '\n';
    }

    return currentConcat;
  }

  function mapRows (table, options, maxWidths, remainingSpace) {
    var concat = '';

    each(table, function (row, rowIndex) {
      concat += mapCells(table, options, maxWidths, remainingSpace, row, rowIndex);
    });

    return concat;
  }

  function createTable (table, options, maxWidths, remainingSpace) {
    return mapRows(table, options, maxWidths, remainingSpace).split('\n').map(function (line) {
      return line.replace(MATCHES_TRAILING_SPACES, '');
    }).join('\n');
  }

  function formatTable (table, options) {
    var maxWidths = getMaxTableWidths(table);
    var remainingSpace = getRemainingSpace(maxWidths, options);
    return createTable(table, options, maxWidths, remainingSpace);
  }

  function createCommandsText (commands) {
    return (commands.length ? '  Commands:\n' : '') +
      formatTable(commands.map(function (command) {
        var alias = (command.options.alias ? ', ' + command.options.alias : '');
        return [command.name + alias, command.options.description];
      }), {wrap: [1], alignRight: [2]}) +
      (commands.length ? '\n\n' : '');
  }

  function createOptionsText (options) {
    return (options.length ? '  Options:\n' : '') +
      formatTable(options.map(function (option) {
        var namePrefix = option._type === 'arg' ? '<' : '--';
        var nameSuffix = option._type === 'arg' ? '>' : '';
        var aliasPrefix = namePrefix.substring(0, 1);
        var alias = (option.options.alias ? ', ' + aliasPrefix + option.options.alias : '');
        var type = option.options.type ? '   [' + option.options.type + ']' : '';
        return [namePrefix + option.name + nameSuffix + alias, option.options.description, type];
      }), {wrap: [1], alignRight: [2]}) +
      (options.length ? '\n\n' : '');
  }

  function createExamplesText (examples) {
    return (examples.length ? '  Examples:\n' : '') +
      examples.map(function (example) {
        return '    ' + example;
      }).join('\n') +
      (examples.length ? '\n\n' : '');
  }

  function sortByName (a, b) {
    if (a.name < b.name) {
      return -1;
    }

    if (b.name < a.name) {
      return 1;
    }

    return 0;
  }

  function createHelp (schema, globals, error) {
    var commands = [];
    var flags = [];
    var kwargs = [];
    var args = [];
    var flagAndKwargNames = [];

    each(schema.children, function (node) {
      if (node._type === 'command') {
        commands.push(node);
      } else if (node._type === 'flag') {
        flags.push(node);
        flagAndKwargNames.push(node.name);
      } else if (node._type === 'kwarg') {
        kwargs.push(node);
        flagAndKwargNames.push(node.name);
      } else if (node._type === 'arg') {
        args.push(node);
      }
    });

    if (globals.help && flagAndKwargNames.indexOf(globals.help.name) < 0) {
      flags.unshift(globals.help);
    }

    commands.sort(sortByName);
    flags.sort(sortByName);
    kwargs.sort(sortByName);
    args.sort(sortByName);

    var options = flags.concat(kwargs).concat(args);

    return '\n' +
      (schema.options.usage ? '  Usage: ' + schema.options.usage + '\n\n' : '') +
      createCommandsText(commands) +
      createOptionsText(options) +
      createExamplesText(schema.options.examples) +
      (error ? ('  ' + error + '\n\n') : '');
  }

  /* istanbul ignore next */
  function exitWithHelp (help) {
    process.stderr.write(help);
    process.exit(1);
  }

  function formatNodeName (node) {
    var prefix = node._type === 'flag' || node._type === 'kwarg' ? '--' : '';
    return prefix + node.name;
  }

  function formatRequiredList (nodes) {
    return nodes.map(formatNodeName).join(', ');
  }

  module.exports = {
    find: find,
    each: each,
    any: any,
    several: several,
    sum: sum,
    argsToArray: argsToArray,
    validateChildren: validateChildren,
    getNodeProperties: getNodeProperties,
    validateName: validateName,
    serializeOptions: serializeOptions,
    formatTable: formatTable,
    createHelp: createHelp,
    exitWithHelp: exitWithHelp,
    formatNodeName: formatNodeName,
    formatRequiredList: formatRequiredList
  };

})();
<|endoftext|>