<commit_before>const wrapSymbols = ['*', '_', '~', '^', '+', '=']
export function wrapTextWith (editor, cm, symbol) {
  if (!cm.getSelection()) {
    return CodeMirror.Pass
  } else {
    let ranges = cm.listSelections()
    for (let i = 0; i < ranges.length; i++) {
      let range = ranges[i]
      if (!range.empty()) {
        const from = range.from()
        const to = range.to()

        if (symbol !== 'Backspace') {
          let selection = cm.getRange(from, to)
          let anchorIndex = editor.indexFromPos(ranges[i].anchor)
          let headIndex = editor.indexFromPos(ranges[i].head)
          cm.replaceRange(symbol + selection + symbol, from, to, '+input')
          if (anchorIndex > headIndex) {
            ranges[i].anchor.ch+= symbol.length
            ranges[i].head.ch+= symbol.length
          } else {
            ranges[i].head.ch+= symbol.length
            ranges[i].anchor.ch+= symbol.length
          }
          cm.setSelections(ranges)
        } else {
          let preEndPos = {
            line: to.line,
            ch: to.ch + symbol.length
          }
          let preText = cm.getRange(to, preEndPos)
          let preIndex = wrapSymbols.indexOf(preText)
          let postEndPos = {
            line: from.line,
            ch: from.ch - symbol.length
          }
          let postText = cm.getRange(postEndPos, from)
          let postIndex = wrapSymbols.indexOf(postText)
          // check if surround symbol are list in array and matched
          if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
            cm.replaceRange('', to, preEndPos, '+delete')
            cm.replaceRange('', postEndPos, from, '+delete')
          }
        }
      }
    }
  }
}

export function insertText (cm, text, cursorEnd = 0) {
  let cursor = cm.getCursor()
  cm.replaceSelection(text, cursor, cursor)
  cm.focus()
  cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd})
}

export function insertLink(cm, isImage) {
  let cursor = cm.getCursor()
  let ranges = cm.listSelections()
  const linkEnd = '](https://)'
  const symbol = (isImage) ? '![' : '['

  for (let i = 0; i < ranges.length; i++) {
    let range = ranges[i]
    if (!range.empty()) {
      const from = range.from()
      const to = range.to()
      let anchorIndex = editor.indexFromPos(ranges[i].anchor)
      let headIndex = editor.indexFromPos(ranges[i].head)
      let selection = cm.getRange(from, to)
      selection = symbol + selection + linkEnd
      cm.replaceRange(selection, from, to)
      if (anchorIndex > headIndex) {
        ranges[i].anchor.ch+= symbol.length
        ranges[i].head.ch+= symbol.length
      } else {
        ranges[i].head.ch+= symbol.length
        ranges[i].anchor.ch+= symbol.length
      }
      cm.setSelections(ranges)
    } else {
      cm.replaceRange(symbol + linkEnd, cursor, cursor)
      cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length + linkend.length})
    }
  }
  cm.focus()
}

export function insertHeader (cm) {
  let cursor = cm.getCursor()
  let startOfLine = {line: cursor.line, ch: 0}
  let startOfLineText = cm.getRange(startOfLine, {line: cursor.line, ch: 1})
  // See if it is already a header
  if (startOfLineText === '#') {
    cm.replaceRange('#', startOfLine, startOfLine)
  } else {
    cm.replaceRange('# ', startOfLine, startOfLine)
  }
  cm.focus()
}

export function insertOnStartOfLines (cm, symbol) {
  let cursor = cm.getCursor()
  let ranges = cm.listSelections()

  for (let i = 0; i < ranges.length; i++) {
    let range = ranges[i]
    if (!range.empty()) {
      const from = range.from()
      const to = range.to()
      let selection = cm.getRange({line: from.line, ch: 0}, to)
      selection = selection.replace(/\n/g, '\n' + symbol)
      selection = symbol + selection
      cm.replaceRange(selection, from, to)
    } else {
      cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0})
    }
  }
  cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length})
  cm.focus()
}
<commit_msg>Fix all newly introduced linting issues<commit_after>const wrapSymbols = ['*', '_', '~', '^', '+', '=']
export function wrapTextWith (editor, cm, symbol) {
  if (!cm.getSelection()) {
    return CodeMirror.Pass
  } else {
    let ranges = cm.listSelections()
    for (let i = 0; i < ranges.length; i++) {
      let range = ranges[i]
      if (!range.empty()) {
        const from = range.from()
        const to = range.to()

        if (symbol !== 'Backspace') {
          let selection = cm.getRange(from, to)
          let anchorIndex = editor.indexFromPos(ranges[i].anchor)
          let headIndex = editor.indexFromPos(ranges[i].head)
          cm.replaceRange(symbol + selection + symbol, from, to, '+input')
          if (anchorIndex > headIndex) {
            ranges[i].anchor.ch += symbol.length
            ranges[i].head.ch += symbol.length
          } else {
            ranges[i].head.ch += symbol.length
            ranges[i].anchor.ch += symbol.length
          }
          cm.setSelections(ranges)
        } else {
          let preEndPos = {
            line: to.line,
            ch: to.ch + symbol.length
          }
          let preText = cm.getRange(to, preEndPos)
          let preIndex = wrapSymbols.indexOf(preText)
          let postEndPos = {
            line: from.line,
            ch: from.ch - symbol.length
          }
          let postText = cm.getRange(postEndPos, from)
          let postIndex = wrapSymbols.indexOf(postText)
          // check if surround symbol are list in array and matched
          if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
            cm.replaceRange('', to, preEndPos, '+delete')
            cm.replaceRange('', postEndPos, from, '+delete')
          }
        }
      }
    }
  }
}

export function insertText (cm, text, cursorEnd = 0) {
  let cursor = cm.getCursor()
  cm.replaceSelection(text, cursor, cursor)
  cm.focus()
  cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd})
}

export function insertLink (cm, isImage) {
  let cursor = cm.getCursor()
  let ranges = cm.listSelections()
  const linkEnd = '](https://)'
  const symbol = (isImage) ? '![' : '['

  for (let i = 0; i < ranges.length; i++) {
    let range = ranges[i]
    if (!range.empty()) {
      const from = range.from()
      const to = range.to()
      let anchorIndex = editor.indexFromPos(ranges[i].anchor)
      let headIndex = editor.indexFromPos(ranges[i].head)
      let selection = cm.getRange(from, to)
      selection = symbol + selection + linkEnd
      cm.replaceRange(selection, from, to)
      if (anchorIndex > headIndex) {
        ranges[i].anchor.ch += symbol.length
        ranges[i].head.ch += symbol.length
      } else {
        ranges[i].head.ch += symbol.length
        ranges[i].anchor.ch += symbol.length
      }
      cm.setSelections(ranges)
    } else {
      cm.replaceRange(symbol + linkEnd, cursor, cursor)
      cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length + linkEnd.length})
    }
  }
  cm.focus()
}

export function insertHeader (cm) {
  let cursor = cm.getCursor()
  let startOfLine = {line: cursor.line, ch: 0}
  let startOfLineText = cm.getRange(startOfLine, {line: cursor.line, ch: 1})
  // See if it is already a header
  if (startOfLineText === '#') {
    cm.replaceRange('#', startOfLine, startOfLine)
  } else {
    cm.replaceRange('# ', startOfLine, startOfLine)
  }
  cm.focus()
}

export function insertOnStartOfLines (cm, symbol) {
  let cursor = cm.getCursor()
  let ranges = cm.listSelections()

  for (let i = 0; i < ranges.length; i++) {
    let range = ranges[i]
    if (!range.empty()) {
      const from = range.from()
      const to = range.to()
      let selection = cm.getRange({line: from.line, ch: 0}, to)
      selection = selection.replace(/\n/g, '\n' + symbol)
      selection = symbol + selection
      cm.replaceRange(selection, from, to)
    } else {
      cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0})
    }
  }
  cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length})
  cm.focus()
}
<|endoftext|>


<commit_before>import {validate} from 'jsonschema';
import mapper from 'object-mapper';
import {invert} from 'lodash';
import {getRelatedModel} from '../include_related';

/**
 * @file
 * Class for making requests to Elvis. Exposes methods for CRUD request.
 *
 * @todo
 * Extend with methods for making nested queries.
 */

const QueryTypeMap = {
  entity: {
    single: 'Entity',
    list: 'Entities'
  },
  action: {
    single: 'Action',
    list: 'Actions'
  },
  profile: {
    single: 'Profile',
    list: 'Profiles'
  }
};

const orderPossibilities = [
  'descending',
  'ascending'
];

export function communityRequest(context, params) {
  const baseurl = context.get('services.communityservice') || 'http://localhost:3000/v1';
  const id = context.get('communityservice.id') || 1;
  const name = `${baseurl}/community/${id}/${params.path}`;
  delete params.path;

  return context.request(name, params);
}

export default class EntityRequest {
  constructor(type, elvisType, call, map, schema) {
    this._elvisType = elvisType;
    this._type = type;
    this._call = call;
    this._map = map;
    this._schema = schema;
  }

  _mapperToElvis(data) {
    return mapper(data, this._map);
  }

  _mapperFromElvis(data) {
    return mapper(data, invert(this._map));
  }

  /**
   * Return response (json string or object) as object.
   *
   * @param response {String|Object}
   * @returns {Object}
   * @private
   */
  _parseResponse(response) {
    return (typeof response === 'string') && JSON.parse(response) || response;
  }

  /**
   * Create Response object.
   *
   * @param data {Object}
   * @param errors {Array}
   * @returns {Object}
   * @private
   */
  _createResponse(data = {}, errors = []) {
    if (errors && errors.length) {
      return this._parseErrors(errors);
    }
    return {
      status: 200,
      data: data,
      errors: errors
    };
  }

  /**
   * Convert errors to response.
   * @param errors
   * @returns {{status: (*|number), error: (boolean|*), data: Array}}
   * @private
   */
  _parseErrors(errors) {
    const error = Array.isArray(errors) && errors[0] || errors;
    const status = error.status || 500;
    delete error.status;
    return {
      status,
      error,
      data: []
    };
  }

  /**
   * Validate object against utility method.
   *
   * @param object
   * @returns {Object|null} Returns response object if validation errors exists. Otherwise null.
   * @private
   */
  _validate(data) {
    const validateErrors = validate(data, this._schema).errors;
    if (validateErrors.length) {
      return {
        status: 400,
        errors: validateErrors.map(o => String(o.stack).replace(/^instance\.?/, '')).join('\n'),
        data: []
      };
    }
    return null;
  }

  _getCounts(name, count) {
    const countQuery = {};
    if (typeof count === 'string') {
      const relatedModel = getRelatedModel(name, count);
      if (relatedModel.Entities || relatedModel.Entity) {
        countQuery.CountEntities = relatedModel.Entities || relatedModel.Entity;
      }

      if (relatedModel.Profiles || relatedModel.Profile) {
        countQuery.CountProfiles = relatedModel.Profiles || relatedModel.Profile;
      }

      if (relatedModel.Action || relatedModel.Actions) {
        countQuery.CountActions = relatedModel.Action || relatedModel.Actions;
      }
    }

    return countQuery;
  }

  _filterInclude(Include = {}, filter = []) {
    if (Array.isArray(filter)) {
      filter.forEach(filterKey => {
        if (Include[filterKey]) {
          delete Include[filterKey];
        }
      });
    }

    return Include;
  }

  /**
   * Generates an Include object for the JSON sent to the community service.
   * Can take nested objects and different input types.
   * It also filters out any unwanted parameters.
   * @param include
   * @param filter
   * @param _map
   * @returns {*}
   * @private
   */
  _getInclude(include = {}, filter = [], name = '', _map = {}) {
    const Include = Object.assign({}, _map);
    if (typeof include === 'string') {
      try {
        const reqInclude = JSON.parse(include);
        include = Array.isArray(reqInclude) ? reqInclude : [reqInclude];
      }
      catch (e) {
        include = [include];
      }
    }
    else if (Array.isArray(include)) {
      include = include;
    }

    if (Array.isArray(include)) {
      include.forEach(item => {
        if (typeof item === 'string') {
          const related = getRelatedModel(name, item);
          if (related) {
            Include[item] = related;
          }
        }
        else if (typeof item === 'object' && item.name) {
          console.log(item);
          const related = getRelatedModel(name, item.name, item.limit, item.offset);
          if (related) {
            if (item.include) {
              if (typeof item.include === 'string') {
                item.include = [item.include];
              }

              related.Include = Object.assign({}, related.Include, this._getInclude(item.include, item.filter, item.name));
            }
            related.Include = this._filterInclude(related.Include, item.filter);

            Include[item.name] = related;
            if (item.counts) {
              item.counts = typeof item.counts === 'string' ? [item.counts] : item.counts;
              Array.isArray(item.counts) && item.counts.forEach(count => {
                const additional = {};
                additional[`${count}Count`] = this._getCounts(item.name, count);
                Include[item.name].Include = Object.assign({}, Include[item.name].Include, additional);
              });
            }
          }
        }
      });
    }

    return this._filterInclude(Include, filter);
  }

  /**
   * Make request to Elvis.
   *
   * @param path {String} Endpoint in Elvis
   * @param method {String} post|get|put|delete
   * @param params {Object} parameters object. most often json or empty.
   * @returns {Promise|Object}
   * @private
   */
  async _request(path, method = 'get', params = {}) {
    const options = Object.assign({path, method}, params);
    try {
      const response = await this._call(options);
      return this._parseResponse(response);
    }
    catch (error) {
      return {errors: error};
    }
  }

  async get(id, include = [], counts = []) {
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {id: id};
    if (this._type) {
      selector.type = this._type;
    }
    const json = {
      [selectorKey]: selector,
      Limit: 1,
      Include: this._getInclude(include, [], this._type || this._elvisType, this._map)
    };

    if (typeof counts === 'string') {
      counts = [counts];
    }

    counts.forEach(count => {
      json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
    });

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data && data.List[0], errors);
  }

  async post(object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }
    const json = this._mapperToElvis(object);
    if (this._elvisType === 'action') {
      if (json.attributes.reference.type === 'profile') {
        json.profile_ref = json.attributes.reference.id;
      }
      else {
        json.entity_ref = json.attributes.reference.id;
      }
    }
    if (this._type) {
      json.type = this._type;
    }
    const {data, errors} = await this._request(this._elvisType, 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async put(id, object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }

    const json = this._mapperToElvis(object);
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async delete(id, modified_by) {
    const json = {modified_by};
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  /**
   * Get all objects of type
   *
   * @returns {{status, data, errors}|*}
   */
  async getList(req) {
    let filter = [];
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {};
    if (this._type) {
      selector.type = this._type;
    }

    if (Array.isArray(req.filter)) {
      filter = req.filter;
    }

    if (typeof req.filter === 'string') {
      filter.push(req.filter);
    }

    const json = {
      [selectorKey]: selector,
      SortBy: 'id',
      Order: 'descending',
      Limit: 2,
      Offset: 0,
      Include: this._getInclude(req.include, filter, this._type || this._elvisType, this._map)
    };

    if (req.offset && !isNaN(parseFloat(req.offset)) && isFinite(req.offset)) {
      json.Offset = req.offset;
    }

    if (req.order && orderPossibilities.indexOf(req.order) >= 0) {
      json.Order = req.order;
    }

    if (typeof req.limit !== 'undefined' && !isNaN(parseFloat(req.limit)) && isFinite(req.limit)) {
      json.Limit = req.limit;
    }

    if (typeof req.sort === 'string') {
      json.SortBy = req.sort;
    }

    if (typeof req.counts === 'string') {
      try {
        req.counts = JSON.parse(req.counts);
      }
      catch (er) {
        req.counts = [req.counts];
      }
    }

    if (Array.isArray(req.counts)) {
      req.counts.forEach(count => {
        json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
      });
    }

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data, errors);
  }
}
<commit_msg>#691: remove console log<commit_after>import {validate} from 'jsonschema';
import mapper from 'object-mapper';
import {invert} from 'lodash';
import {getRelatedModel} from '../include_related';

/**
 * @file
 * Class for making requests to Elvis. Exposes methods for CRUD request.
 *
 * @todo
 * Extend with methods for making nested queries.
 */

const QueryTypeMap = {
  entity: {
    single: 'Entity',
    list: 'Entities'
  },
  action: {
    single: 'Action',
    list: 'Actions'
  },
  profile: {
    single: 'Profile',
    list: 'Profiles'
  }
};

const orderPossibilities = [
  'descending',
  'ascending'
];

export function communityRequest(context, params) {
  const baseurl = context.get('services.communityservice') || 'http://localhost:3000/v1';
  const id = context.get('communityservice.id') || 1;
  const name = `${baseurl}/community/${id}/${params.path}`;
  delete params.path;

  return context.request(name, params);
}

export default class EntityRequest {
  constructor(type, elvisType, call, map, schema) {
    this._elvisType = elvisType;
    this._type = type;
    this._call = call;
    this._map = map;
    this._schema = schema;
  }

  _mapperToElvis(data) {
    return mapper(data, this._map);
  }

  _mapperFromElvis(data) {
    return mapper(data, invert(this._map));
  }

  /**
   * Return response (json string or object) as object.
   *
   * @param response {String|Object}
   * @returns {Object}
   * @private
   */
  _parseResponse(response) {
    return (typeof response === 'string') && JSON.parse(response) || response;
  }

  /**
   * Create Response object.
   *
   * @param data {Object}
   * @param errors {Array}
   * @returns {Object}
   * @private
   */
  _createResponse(data = {}, errors = []) {
    if (errors && errors.length) {
      return this._parseErrors(errors);
    }
    return {
      status: 200,
      data: data,
      errors: errors
    };
  }

  /**
   * Convert errors to response.
   * @param errors
   * @returns {{status: (*|number), error: (boolean|*), data: Array}}
   * @private
   */
  _parseErrors(errors) {
    const error = Array.isArray(errors) && errors[0] || errors;
    const status = error.status || 500;
    delete error.status;
    return {
      status,
      error,
      data: []
    };
  }

  /**
   * Validate object against utility method.
   *
   * @param object
   * @returns {Object|null} Returns response object if validation errors exists. Otherwise null.
   * @private
   */
  _validate(data) {
    const validateErrors = validate(data, this._schema).errors;
    if (validateErrors.length) {
      return {
        status: 400,
        errors: validateErrors.map(o => String(o.stack).replace(/^instance\.?/, '')).join('\n'),
        data: []
      };
    }
    return null;
  }

  _getCounts(name, count) {
    const countQuery = {};
    if (typeof count === 'string') {
      const relatedModel = getRelatedModel(name, count);
      if (relatedModel.Entities || relatedModel.Entity) {
        countQuery.CountEntities = relatedModel.Entities || relatedModel.Entity;
      }

      if (relatedModel.Profiles || relatedModel.Profile) {
        countQuery.CountProfiles = relatedModel.Profiles || relatedModel.Profile;
      }

      if (relatedModel.Action || relatedModel.Actions) {
        countQuery.CountActions = relatedModel.Action || relatedModel.Actions;
      }
    }

    return countQuery;
  }

  _filterInclude(Include = {}, filter = []) {
    if (Array.isArray(filter)) {
      filter.forEach(filterKey => {
        if (Include[filterKey]) {
          delete Include[filterKey];
        }
      });
    }

    return Include;
  }

  /**
   * Generates an Include object for the JSON sent to the community service.
   * Can take nested objects and different input types.
   * It also filters out any unwanted parameters.
   * @param include
   * @param filter
   * @param _map
   * @returns {*}
   * @private
   */
  _getInclude(include = {}, filter = [], name = '', _map = {}) {
    const Include = Object.assign({}, _map);
    if (typeof include === 'string') {
      try {
        const reqInclude = JSON.parse(include);
        include = Array.isArray(reqInclude) ? reqInclude : [reqInclude];
      }
      catch (e) {
        include = [include];
      }
    }
    else if (Array.isArray(include)) {
      include = include;
    }

    if (Array.isArray(include)) {
      include.forEach(item => {
        if (typeof item === 'string') {
          const related = getRelatedModel(name, item);
          if (related) {
            Include[item] = related;
          }
        }
        else if (typeof item === 'object' && item.name) {
          const related = getRelatedModel(name, item.name, item.limit, item.offset);
          if (related) {
            if (item.include) {
              if (typeof item.include === 'string') {
                item.include = [item.include];
              }

              related.Include = Object.assign({}, related.Include, this._getInclude(item.include, item.filter, item.name));
            }
            related.Include = this._filterInclude(related.Include, item.filter);

            Include[item.name] = related;
            if (item.counts) {
              item.counts = typeof item.counts === 'string' ? [item.counts] : item.counts;
              Array.isArray(item.counts) && item.counts.forEach(count => {
                const additional = {};
                additional[`${count}Count`] = this._getCounts(item.name, count);
                Include[item.name].Include = Object.assign({}, Include[item.name].Include, additional);
              });
            }
          }
        }
      });
    }

    return this._filterInclude(Include, filter);
  }

  /**
   * Make request to Elvis.
   *
   * @param path {String} Endpoint in Elvis
   * @param method {String} post|get|put|delete
   * @param params {Object} parameters object. most often json or empty.
   * @returns {Promise|Object}
   * @private
   */
  async _request(path, method = 'get', params = {}) {
    const options = Object.assign({path, method}, params);
    try {
      const response = await this._call(options);
      return this._parseResponse(response);
    }
    catch (error) {
      return {errors: error};
    }
  }

  async get(id, include = [], counts = []) {
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {id: id};
    if (this._type) {
      selector.type = this._type;
    }
    const json = {
      [selectorKey]: selector,
      Limit: 1,
      Include: this._getInclude(include, [], this._type || this._elvisType, this._map)
    };

    if (typeof counts === 'string') {
      counts = [counts];
    }

    counts.forEach(count => {
      json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
    });

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data && data.List[0], errors);
  }

  async post(object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }
    const json = this._mapperToElvis(object);
    if (this._elvisType === 'action') {
      if (json.attributes.reference.type === 'profile') {
        json.profile_ref = json.attributes.reference.id;
      }
      else {
        json.entity_ref = json.attributes.reference.id;
      }
    }
    if (this._type) {
      json.type = this._type;
    }
    const {data, errors} = await this._request(this._elvisType, 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async put(id, object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }

    const json = this._mapperToElvis(object);
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async delete(id, modified_by) {
    const json = {modified_by};
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  /**
   * Get all objects of type
   *
   * @returns {{status, data, errors}|*}
   */
  async getList(req) {
    let filter = [];
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {};
    if (this._type) {
      selector.type = this._type;
    }

    if (Array.isArray(req.filter)) {
      filter = req.filter;
    }

    if (typeof req.filter === 'string') {
      filter.push(req.filter);
    }

    const json = {
      [selectorKey]: selector,
      SortBy: 'id',
      Order: 'descending',
      Limit: 2,
      Offset: 0,
      Include: this._getInclude(req.include, filter, this._type || this._elvisType, this._map)
    };

    if (req.offset && !isNaN(parseFloat(req.offset)) && isFinite(req.offset)) {
      json.Offset = req.offset;
    }

    if (req.order && orderPossibilities.indexOf(req.order) >= 0) {
      json.Order = req.order;
    }

    if (typeof req.limit !== 'undefined' && !isNaN(parseFloat(req.limit)) && isFinite(req.limit)) {
      json.Limit = req.limit;
    }

    if (typeof req.sort === 'string') {
      json.SortBy = req.sort;
    }

    if (typeof req.counts === 'string') {
      try {
        req.counts = JSON.parse(req.counts);
      }
      catch (er) {
        req.counts = [req.counts];
      }
    }

    if (Array.isArray(req.counts)) {
      req.counts.forEach(count => {
        json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
      });
    }

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data, errors);
  }
}
<|endoftext|>


<commit_before>import oboe from "oboe";

const
  getJSON = url => {
    return new Promise((resolve, reject) => {
      oboe(url)
        .done(resolve)
        .fail(reject);
    });
  };

export {
  getJSON
};
<commit_msg>Add getJSONProp util<commit_after>import oboe from "oboe";

const
  getJSON = url => {
    return new Promise((resolve, reject) => {
      oboe(url)
        .done(resolve)
        .fail(reject);
    });
  },

  getJSONProp = (url, prop) => {
    return new Promise((resolve, reject) => {
      let found = false;

      oboe(url)
        .node(prop, value => {
          found = true;
          resolve(value);

          // Found value; hang up
          this.abort();
        })
        .done(() => {
          if (!found)
            reject(new Error(`Property ${ prop } not found`));
        })
        .fail(reject);
    });
  };

export {
  getJSON,
  getJSONProp
};
<|endoftext|>


<commit_before>/**
 * Synchronize zooming and/or selections between a set of dygraphs.
 *
 * Usage:
 *
 *   var g1 = new Dygraph(...),
 *       g2 = new Dygraph(...),
 *       ...;
 *   var sync = Dygraph.synchronize(g1, g2, ...);
 *   // charts are now synchronized
 *   sync.detach();
 *   // charts are no longer synchronized
 *
 * You can set options using the last parameter, for example:
 *
 *   var sync = Dygraph.synchronize(g1, g2, g3, {
 *      selection: true,
 *      zoom: true
 *   });
 *
 * The default is to synchronize both of these.
 *
 * Instead of passing one Dygraph object as each parameter, you may also pass an
 * array of dygraphs:
 *
 *   var sync = Dygraph.synchronize([g1, g2, g3], {
 *      selection: false,
 *      zoom: true
 *   });
 *
 * You may also set `range: false` if you wish to only sync the x-axis.
 * The `range` option has no effect unless `zoom` is true (the default).
 */
(function() {
/* global Dygraph:false */
'use strict';

Dygraph.synchronize = function(/* dygraphs..., opts */) {
  if (arguments.length === 0) {
    throw 'Invalid invocation of Dygraph.synchronize(). Need >= 1 argument.';
  }

  var OPTIONS = ['selection', 'zoom', 'range'];
  var opts = {
    selection: true,
    zoom: true,
    range: true
  };
  var dygraphs = [];
  var prevCallbacks = [];

  var parseOpts = function(obj) {
    if (!(obj instanceof Object)) {
      throw 'Last argument must be either Dygraph or Object.';
    } else {
      for (var i = 0; i < OPTIONS.length; i++) {
        var optName = OPTIONS[i];
        if (obj.hasOwnProperty(optName)) opts[optName] = obj[optName];
      }
    }
  };

  if (arguments[0] instanceof Dygraph) {
    // Arguments are Dygraph objects.
    for (var i = 0; i < arguments.length; i++) {
      if (arguments[i] instanceof Dygraph) {
        dygraphs.push(arguments[i]);
      } else {
        break;
      }
    }
    if (i < arguments.length - 1) {
      throw 'Invalid invocation of Dygraph.synchronize(). ' +
            'All but the last argument must be Dygraph objects.';
    } else if (i == arguments.length - 1) {
      parseOpts(arguments[arguments.length - 1]);
    }
  } else if (arguments[0].length) {
    // Invoked w/ list of dygraphs, options
    for (var i = 0; i < arguments[0].length; i++) {
      dygraphs.push(arguments[0][i]);
    }
    if (arguments.length == 2) {
      parseOpts(arguments[1]);
    } else if (arguments.length > 2) {
      throw 'Invalid invocation of Dygraph.synchronize(). ' +
            'Expected two arguments: array and optional options argument.';
    }  // otherwise arguments.length == 1, which is fine.
  } else {
    throw 'Invalid invocation of Dygraph.synchronize(). ' +
          'First parameter must be either Dygraph or list of Dygraphs.';
  }

  if (dygraphs.length < 2) {
    throw 'Invalid invocation of Dygraph.synchronize(). ' +
          'Need two or more dygraphs to synchronize.';
  }

  var readycount = dygraphs.length;
  for (var i = 0; i < dygraphs.length; i++) {
    var g = dygraphs[i];
    g.ready( function() {
      if (--readycount == 0) {
        // store original callbacks
        var callBackTypes = ['drawCallback', 'highlightCallback', 'unhighlightCallback'];
        for (var j = 0; j < dygraphs.length; j++) {
          if (!prevCallbacks[j]) {
            prevCallbacks[j] = {};
          }
          for (var k in callBackTypes) {
            prevCallbacks[j][callBackTypes[k]] = dygraphs[j].getFunctionOption(callBackTypes[k]);
          }
        }

        // Listen for draw, highlight, unhighlight callbacks.
        if (opts.zoom) {
          attachZoomHandlers(dygraphs, opts, prevCallbacks);
        }

        if (opts.selection) {
          attachSelectionHandlers(dygraphs, prevCallbacks);
        }
      }
    });
  }

  return {
    detach: function() {
      for (var i = 0; i < dygraphs.length; i++) {
        var g = dygraphs[i];
        if (opts.zoom) {
          g.updateOptions({drawCallback: prevCallbacks[i].drawCallback});
        }
        if (opts.selection) {
          g.updateOptions({
            highlightCallback: prevCallbacks[i].highlightCallback,
            unhighlightCallback: prevCallbacks[i].unhighlightCallback
          });
        }
      }
      // release references & make subsequent calls throw.
      dygraphs = null;
      opts = null;
      prevCallbacks = null;
    }
  };
};

function attachZoomHandlers(gs, syncOpts, prevCallbacks) {
  var block = false;
  for (var i = 0; i < gs.length; i++) {
    var g = gs[i];
    g.updateOptions({
      drawCallback: function(me, initial) {
        if (block || initial) return;
        block = true;
        var opts = {
          dateWindow: me.xAxisRange()
        };
        if (syncOpts.range) opts.valueRange = me.yAxisRange();

        for (var j = 0; j < gs.length; j++) {
          if (gs[j] == me) {
            if (prevCallbacks[j] && prevCallbacks[j].drawCallback) {
              prevCallbacks[j].drawCallback(me, initial);
            }
            continue;
          }
          gs[j].updateOptions(opts);
        }
        block = false;
      }
    }, false /* no need to redraw */);
  }
}

function attachSelectionHandlers(gs, prevCallbacks) {
  var block = false;
  for (var i = 0; i < gs.length; i++) {
    var g = gs[i];

    g.updateOptions({
      highlightCallback: function(event, x, points, row, seriesName) {
        if (block) return;
        block = true;
        var me = this;
        for (var i = 0; i < gs.length; i++) {
          if (me == gs[i]) {
            if (prevCallbacks[i] && prevCallbacks[i].highlightCallback) {
              prevCallbacks[i].highlightCallback(event, x, points, row, seriesName);
            }
            continue;
          }
          var idx = gs[i].getRowForX(x);
          if (idx !== null) {
            gs[i].setSelection(idx, seriesName);
          }
        }
        block = false;
      },
      unhighlightCallback: function(event) {
        if (block) return;
        block = true;
        var me = this;
        for (var i = 0; i < gs.length; i++) {
          if (me == gs[i]) {
            if (prevCallbacks[i] && prevCallbacks[i].unhighlightCallback) {
              prevCallbacks[i].unhighlightCallback(event);
            }
            continue;
          }
          gs[i].clearSelection();
        }
        block = false;
      }
    });
  }
}

})();
<commit_msg>for..in -> normal for loop<commit_after>/**
 * Synchronize zooming and/or selections between a set of dygraphs.
 *
 * Usage:
 *
 *   var g1 = new Dygraph(...),
 *       g2 = new Dygraph(...),
 *       ...;
 *   var sync = Dygraph.synchronize(g1, g2, ...);
 *   // charts are now synchronized
 *   sync.detach();
 *   // charts are no longer synchronized
 *
 * You can set options using the last parameter, for example:
 *
 *   var sync = Dygraph.synchronize(g1, g2, g3, {
 *      selection: true,
 *      zoom: true
 *   });
 *
 * The default is to synchronize both of these.
 *
 * Instead of passing one Dygraph object as each parameter, you may also pass an
 * array of dygraphs:
 *
 *   var sync = Dygraph.synchronize([g1, g2, g3], {
 *      selection: false,
 *      zoom: true
 *   });
 *
 * You may also set `range: false` if you wish to only sync the x-axis.
 * The `range` option has no effect unless `zoom` is true (the default).
 */
(function() {
/* global Dygraph:false */
'use strict';

Dygraph.synchronize = function(/* dygraphs..., opts */) {
  if (arguments.length === 0) {
    throw 'Invalid invocation of Dygraph.synchronize(). Need >= 1 argument.';
  }

  var OPTIONS = ['selection', 'zoom', 'range'];
  var opts = {
    selection: true,
    zoom: true,
    range: true
  };
  var dygraphs = [];
  var prevCallbacks = [];

  var parseOpts = function(obj) {
    if (!(obj instanceof Object)) {
      throw 'Last argument must be either Dygraph or Object.';
    } else {
      for (var i = 0; i < OPTIONS.length; i++) {
        var optName = OPTIONS[i];
        if (obj.hasOwnProperty(optName)) opts[optName] = obj[optName];
      }
    }
  };

  if (arguments[0] instanceof Dygraph) {
    // Arguments are Dygraph objects.
    for (var i = 0; i < arguments.length; i++) {
      if (arguments[i] instanceof Dygraph) {
        dygraphs.push(arguments[i]);
      } else {
        break;
      }
    }
    if (i < arguments.length - 1) {
      throw 'Invalid invocation of Dygraph.synchronize(). ' +
            'All but the last argument must be Dygraph objects.';
    } else if (i == arguments.length - 1) {
      parseOpts(arguments[arguments.length - 1]);
    }
  } else if (arguments[0].length) {
    // Invoked w/ list of dygraphs, options
    for (var i = 0; i < arguments[0].length; i++) {
      dygraphs.push(arguments[0][i]);
    }
    if (arguments.length == 2) {
      parseOpts(arguments[1]);
    } else if (arguments.length > 2) {
      throw 'Invalid invocation of Dygraph.synchronize(). ' +
            'Expected two arguments: array and optional options argument.';
    }  // otherwise arguments.length == 1, which is fine.
  } else {
    throw 'Invalid invocation of Dygraph.synchronize(). ' +
          'First parameter must be either Dygraph or list of Dygraphs.';
  }

  if (dygraphs.length < 2) {
    throw 'Invalid invocation of Dygraph.synchronize(). ' +
          'Need two or more dygraphs to synchronize.';
  }

  var readycount = dygraphs.length;
  for (var i = 0; i < dygraphs.length; i++) {
    var g = dygraphs[i];
    g.ready( function() {
      if (--readycount == 0) {
        // store original callbacks
        var callBackTypes = ['drawCallback', 'highlightCallback', 'unhighlightCallback'];
        for (var j = 0; j < dygraphs.length; j++) {
          if (!prevCallbacks[j]) {
            prevCallbacks[j] = {};
          }
          for (var k = callBackTypes.length - 1; k >= 0; k--) {
            prevCallbacks[j][callBackTypes[k]] = dygraphs[j].getFunctionOption(callBackTypes[k]);
          }
        }

        // Listen for draw, highlight, unhighlight callbacks.
        if (opts.zoom) {
          attachZoomHandlers(dygraphs, opts, prevCallbacks);
        }

        if (opts.selection) {
          attachSelectionHandlers(dygraphs, prevCallbacks);
        }
      }
    });
  }

  return {
    detach: function() {
      for (var i = 0; i < dygraphs.length; i++) {
        var g = dygraphs[i];
        if (opts.zoom) {
          g.updateOptions({drawCallback: prevCallbacks[i].drawCallback});
        }
        if (opts.selection) {
          g.updateOptions({
            highlightCallback: prevCallbacks[i].highlightCallback,
            unhighlightCallback: prevCallbacks[i].unhighlightCallback
          });
        }
      }
      // release references & make subsequent calls throw.
      dygraphs = null;
      opts = null;
      prevCallbacks = null;
    }
  };
};

function attachZoomHandlers(gs, syncOpts, prevCallbacks) {
  var block = false;
  for (var i = 0; i < gs.length; i++) {
    var g = gs[i];
    g.updateOptions({
      drawCallback: function(me, initial) {
        if (block || initial) return;
        block = true;
        var opts = {
          dateWindow: me.xAxisRange()
        };
        if (syncOpts.range) opts.valueRange = me.yAxisRange();

        for (var j = 0; j < gs.length; j++) {
          if (gs[j] == me) {
            if (prevCallbacks[j] && prevCallbacks[j].drawCallback) {
              prevCallbacks[j].drawCallback(me, initial);
            }
            continue;
          }
          gs[j].updateOptions(opts);
        }
        block = false;
      }
    }, false /* no need to redraw */);
  }
}

function attachSelectionHandlers(gs, prevCallbacks) {
  var block = false;
  for (var i = 0; i < gs.length; i++) {
    var g = gs[i];

    g.updateOptions({
      highlightCallback: function(event, x, points, row, seriesName) {
        if (block) return;
        block = true;
        var me = this;
        for (var i = 0; i < gs.length; i++) {
          if (me == gs[i]) {
            if (prevCallbacks[i] && prevCallbacks[i].highlightCallback) {
              prevCallbacks[i].highlightCallback(event, x, points, row, seriesName);
            }
            continue;
          }
          var idx = gs[i].getRowForX(x);
          if (idx !== null) {
            gs[i].setSelection(idx, seriesName);
          }
        }
        block = false;
      },
      unhighlightCallback: function(event) {
        if (block) return;
        block = true;
        var me = this;
        for (var i = 0; i < gs.length; i++) {
          if (me == gs[i]) {
            if (prevCallbacks[i] && prevCallbacks[i].unhighlightCallback) {
              prevCallbacks[i].unhighlightCallback(event);
            }
            continue;
          }
          gs[i].clearSelection();
        }
        block = false;
      }
    });
  }
}

})();
<|endoftext|>


<commit_before>/* global describe it beforeEach afterEach */
'use strict'

const chai = require('chai')
const requireInject = require('require-inject')
const sinon = require('sinon')

const expect = chai.expect

describe('authorize', function () {
  let stubs
  const authOptions = {}

  beforeEach('create stubs', function () {
    stubs = {
      ghauth: null
    }
    stubs[require.resolve('../../lib/logging')] = {
      logger: {
        debug: sinon.spy(),
        error: sinon.spy()
      }
    }
    sinon.stub(process, 'exit')
  })

  afterEach('restore stubs', function () {
    process.exit.restore()
  })

  it('returns authorization data on success', function (done) {
    const expected = {user: 'some user', token: 'some token'}
    stubs.ghauth = function (options, cb) {
      cb(null, expected)
    }
    const github = requireInject('../../lib/github', stubs)

    github.authorize(authOptions).then(
      function (authData) {
        expect(authData).to.equal(expected)
        done()
      }
    )
  })

  it('exits on failure', function (done) {
    stubs.ghauth = function (options, cb) {
      cb(new Error(), null)
    }
    const github = requireInject('../../lib/github', stubs)

    github.authorize(authOptions).then(
      function () {
        expect(process.exit).to.have.been.calledWith(1)
        done()
      }
    )
  })
})

describe('writeComments', function () {
  let stubs
  let logger
  const authData = {}

  beforeEach('create stubs', function () {
    logger = {
      debug: sinon.spy(),
      info: sinon.spy(),
      error: sinon.spy()
    }
    stubs = {
      ghissues: {}
    }
    stubs[require.resolve('../../lib/logging')] = {
      logger
    }
    stubs[require.resolve('../../lib/github')] = {
      getSemverComments: sinon.stub().returns([])
    }
  })

  it('gets comments for each PR', function (done) {
    const listComments = sinon.spy()
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      listComments.apply(this, arguments)
      cb(null, ['some comment', 'another comment'])
    }
    const github = requireInject('../../lib/github', stubs)
    const program = {
      user: 'some user',
      project: 'some project',
      dryRun: true
    }
    const prs = [1, 2, 3, 4]
    const comment = 'some semver tag used as comment'

    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        prs.forEach(function (pr) {
          expect(listComments).to.have.been.calledWith(
            authData, program.user, program.project, pr)
        })
        done()
      })
  })
})
<commit_msg>Add more writeComments test cases<commit_after>/* global describe it beforeEach afterEach */
'use strict'

const chai = require('chai')
const requireInject = require('require-inject')
const sinon = require('sinon')

const expect = chai.expect

describe('authorize', function () {
  let stubs
  const authOptions = {}

  beforeEach('create stubs', function () {
    stubs = {
      ghauth: null
    }
    stubs[require.resolve('../../lib/logging')] = {
      logger: {
        debug: sinon.spy(),
        error: sinon.spy()
      }
    }
    sinon.stub(process, 'exit')
  })

  afterEach('restore stubs', function () {
    process.exit.restore()
  })

  it('returns authorization data on success', function (done) {
    const expected = {user: 'some user', token: 'some token'}
    stubs.ghauth = function (options, cb) {
      cb(null, expected)
    }
    const github = requireInject('../../lib/github', stubs)

    github.authorize(authOptions).then(
      function (authData) {
        expect(authData).to.equal(expected)
        done()
      }
    )
  })

  it('exits on failure', function (done) {
    stubs.ghauth = function (options, cb) {
      cb(new Error(), null)
    }
    const github = requireInject('../../lib/github', stubs)

    github.authorize(authOptions).then(
      function () {
        expect(process.exit).to.have.been.calledWith(1)
        done()
      }
    )
  })
})

describe('writeComments', function () {
  let stubs
  let logger
  let getSemverComments
  let writeComment

  const authData = {}
  let program
  const prs = [1, 2, 3, 4]
  const comment = 'some semver tag used as comment'

  beforeEach('create stubs', function () {
    logger = {
      info: sinon.spy(),
      warn: sinon.spy(),
      error: sinon.spy()
    }
    getSemverComments = sinon.stub()
    writeComment = sinon.stub()
    stubs = {
      ghissues: {}
    }
    stubs[require.resolve('../../lib/logging')] = {
      logger
    }
    stubs[require.resolve('../../lib/github/util')] = {
      getSemverComments,
      writeComment
    }

    program = {
      user: 'some user',
      project: 'some project'
    }
  })

  it('gets comments for each PR', function (done) {
    const listComments = sinon.spy()
    getSemverComments.returns([])
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      listComments.apply(this, arguments)
      cb(null, ['some comment', 'another comment'])
    }
    const github = requireInject('../../lib/github', stubs)

    program.dryRun = true
    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        prs.forEach(function (pr) {
          expect(listComments).to.have.been.calledWith(
            authData, program.user, program.project, pr)
        })
        done()
      })
  })

  it('logs errors when retrieving comments', function (done) {
    const error = 'some error'
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      cb(error, null)
    }
    const github = requireInject('../../lib/github', stubs)

    program.dryRun = true
    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        prs.forEach(function (pr) {
          expect(logger.error).to.have.been.calledWith(
            'Error checking PR#%d comments: %s', pr, error)
        })
        done()
      })
  })

  it('writes comments if dryRun is not set', function (done) {
    getSemverComments.returns([])
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      cb(null, ['some comment', 'another comment'])
    }
    const github = requireInject('../../lib/github', stubs)

    program.dryRun = false
    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        prs.forEach(function (pr) {
          expect(writeComment).to.have.been.calledWith(
            authData, program.user, program.project, pr, comment)
        })
        done()
      })
  })

  it('does not write comments if dryRun is set', function (done) {
    getSemverComments.returns([])
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      cb(null, ['some comment', 'another comment'])
    }
    const github = requireInject('../../lib/github', stubs)

    program.dryRun = true
    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        expect(writeComment).to.not.have.been.called
        done()
      })
  })

  it('does not write comments if semver comments are found', function (done) {
    const semverComments = ['some semver comment']
    getSemverComments.returns(semverComments)
    stubs.ghissues.listComments = function (
        authData, user, project, pr, cb) {
      cb(null, ['some comment', 'another comment'])
    }
    const github = requireInject('../../lib/github', stubs)

    program.dryRun = false
    github.writeComments(authData, program, prs, comment).then(
      function (commentList) {
        prs.forEach(function (pr) {
          expect(logger.warn).to.have.been.calledWith(
            'Semver comments found in PR#%d: %s',
            pr, JSON.stringify(semverComments))
        })
        expect(writeComment).to.not.have.been.called
        done()
      })
  })
})
<|endoftext|>


<commit_before>/**
 * An object containing some utilities.
 * @type {Object}
 */
export const Utils = {

	/**
	 * UInt conversion.
	 * @type {Object}
	 */
	uint: {

		/**
		 * Converts an ArrayBuffer to an integer.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {number} Integer.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let num = 0;

			for (let i = 0; i < arr.length; i++) {
				if (arr[i] !== 0) {
					num *= 256;
					num += arr[i];
				}
			}

			return num;
		},

		/**
		 * Converts an integer to an ArrayBuffer.
		 * @param {number} num Integer.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: num => {
			const buf = new ArrayBuffer(8);
			const arr = new Uint8Array(buf);
			let acc = num;

			for (let i = 7; i >= 0; i--) {
				if (acc === 0) break;

				arr[i] = acc & 255;
				acc -= arr[i];
				acc /= 256;
			}

			return buf;
		}

	},

	/**
	 * Raw string conversion.
	 * @type {Object}
	 */
	raw: {

		/**
		 * Converts an ArrayBuffer to a string.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} String.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				str += String.fromCharCode(arr[i]);
			}

			return str;
		},

		/**
		 * Converts a string to an ArrayBuffer.
		 * @param {string} str String.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			const buf = new ArrayBuffer(str.length);
			const arr = new Uint8Array(buf);

			for (let i = 0; i < str.length; i++) {
				arr[i] = str.charCodeAt(i);
			}

			return buf;
		}

	},

	/**
	 * Base32 string conversion.
	 * @type {Object}
	 */
	b32: {

		/**
		 * RFC 4648 base32 alphabet without pad.
		 * @type {string}
		 */
		alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',

		/**
		 * Converts an ArrayBuffer to a base32 string (RFC 4648)
		 * (https://github.com/LinusU/base32-encode).
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} Base32 string.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);

			let bits = 0;
			let value = 0;
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				value = (value << 8) | arr[i];
				bits += 8;

				while (bits >= 5) {
					str += Utils.b32.alphabet[(value >>> bits - 5) & 31];
					bits -= 5;
				}
			}

			if (bits > 0) {
				str += Utils.b32.alphabet[(value << 5 - bits) & 31];
			}

			return str;
		},

		/**
		 * Converts a base32 string to an ArrayBuffer (RFC 4648)
		 * (https://github.com/LinusU/base32-decode).
		 * @param {string} str Base32 string.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			// Canonicalize to all upper case and remove padding if it exists.
			str = str.toUpperCase().replace(/=+$/, '');

			const buf = new ArrayBuffer((str.length * 5) / 8 | 0);
			const arr = new Uint8Array(buf);

			let bits = 0;
			let value = 0;
			let index = 0;

			for (let i = 0; i < str.length; i++) {
				const idx = Utils.b32.alphabet.indexOf(str[i]);
				if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);

				value = (value << 5) | idx;
				bits += 5;

				if (bits >= 8) {
					arr[index++] = (value >>> bits - 8) & 255;
					bits -= 8;
				}
			}

			return buf;
		}

	},

	/**
	 * Hexadecimal string conversion.
	 * @type {Object}
	 */
	hex: {

		/**
		 * Converts an ArrayBuffer to a hexadecimal string.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} Hexadecimal string.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				const hex = arr[i].toString(16);
				str += hex.length === 2 ? hex : `0${hex}`;
			}

			return str.toUpperCase();
		},

		/**
		 * Converts a hexadecimal string to an ArrayBuffer.
		 * @param {string} str Hexadecimal string.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			const buf = new ArrayBuffer(str.length / 2);
			const arr = new Uint8Array(buf);

			for (let i = 0, j = 0; i < arr.length; i += 1, j += 2) {
				arr[i] = parseInt(str.substr(j, 2), 16);
			}

			return buf;
		}

	},

	/**
	 * Pads a number with leading zeros.
	 * @param {number|string} num Number.
	 * @param {number} digits Digits.
	 * @returns {string} Padded number.
	 */
	pad: (num, digits) => {
		let prefix = '';
		let repeat = digits - String(num).length;
		while (repeat-- > 0) prefix += '0';
		return `${prefix}${num}`;
	}

};

/**
 * An object containing some utilities (for internal use only).
 * @private
 * @type {Object}
 */
export const InternalUtils = {

	/**
	 * "globalThis" ponyfill
	 * (https://mathiasbynens.be/notes/globalthis).
	 * @type {Object}
	 */
	get globalThis() {
		let _globalThis;

		/* eslint-disable no-extend-native, no-restricted-globals, no-undef */
		if (typeof globalThis === 'object') {
			_globalThis = globalThis;
		} else {
			Object.defineProperty(Object.prototype, '__magicalGlobalThis__', {
				get() { return this; },
				configurable: true
			});
			try {
				_globalThis = __magicalGlobalThis__;
			} finally {
				delete Object.prototype.__magicalGlobalThis__;
			}
		}

		if (typeof _globalThis === 'undefined') {
			// Still unable to determine "globalThis", fall back to a naive method:
			if (typeof self !== 'undefined') {
				_globalThis = self;
			} else if (typeof window !== 'undefined') {
				_globalThis = window;
			} else if (typeof global !== 'undefined') {
				_globalThis = global;
			}
		}
		/* eslint-enable */

		Object.defineProperty(this, 'globalThis', {
			enumerable: true,
			value: _globalThis
		});

		return this.globalThis;
	},

	/**
	 * "console" ponyfill.
	 * @type {Object}
	 */
	get console() {
		let _console;

		if (typeof InternalUtils.globalThis.console === 'object') {
			_console = InternalUtils.globalThis.console;
		} else {
			_console = {};
			const properties = [
				'memory'
			];
			const methods = [
				'assert', 'clear', 'count', 'countReset', 'debug', 'error', 'info', 'log',
				'table', 'trace', 'warn', 'dir', 'dirxml', 'group', 'groupCollapsed', 'groupEnd',
				'time', 'timeLog', 'timeEnd', 'exception', 'timeStamp', 'profile', 'profileEnd'
			];
			for (const method of methods) _console[method] = () => {};
			for (const property of properties) _console[property] = {};
		}

		Object.defineProperty(this, 'console', {
			enumerable: true,
			value: _console
		});

		return this.console;
	},

	/**
	 * Detect if running in "Node.js".
	 * @type {boolean}
	 */
	get isNode() {
		const _isNode = Object.prototype.toString.call(InternalUtils.globalThis.process) === '[object process]';

		Object.defineProperty(this, 'isNode', {
			enumerable: true,
			value: _isNode
		});

		return this.isNode;
	},

	/**
	 * Dynamically import "Node.js" modules.
	 * (`eval` is used to prevent bundlers from including the module,
	 * e.g., [webpack/webpack#8826](https://github.com/webpack/webpack/issues/8826))
	 * @param {string} name Name.
	 * @returns {Object} Module.
	 */
	// eslint-disable-next-line no-eval
	nodeRequire: name => eval('require')(name)

};
<commit_msg>Do not assume that all methods of the "console" object exist<commit_after>/**
 * An object containing some utilities.
 * @type {Object}
 */
export const Utils = {

	/**
	 * UInt conversion.
	 * @type {Object}
	 */
	uint: {

		/**
		 * Converts an ArrayBuffer to an integer.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {number} Integer.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let num = 0;

			for (let i = 0; i < arr.length; i++) {
				if (arr[i] !== 0) {
					num *= 256;
					num += arr[i];
				}
			}

			return num;
		},

		/**
		 * Converts an integer to an ArrayBuffer.
		 * @param {number} num Integer.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: num => {
			const buf = new ArrayBuffer(8);
			const arr = new Uint8Array(buf);
			let acc = num;

			for (let i = 7; i >= 0; i--) {
				if (acc === 0) break;

				arr[i] = acc & 255;
				acc -= arr[i];
				acc /= 256;
			}

			return buf;
		}

	},

	/**
	 * Raw string conversion.
	 * @type {Object}
	 */
	raw: {

		/**
		 * Converts an ArrayBuffer to a string.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} String.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				str += String.fromCharCode(arr[i]);
			}

			return str;
		},

		/**
		 * Converts a string to an ArrayBuffer.
		 * @param {string} str String.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			const buf = new ArrayBuffer(str.length);
			const arr = new Uint8Array(buf);

			for (let i = 0; i < str.length; i++) {
				arr[i] = str.charCodeAt(i);
			}

			return buf;
		}

	},

	/**
	 * Base32 string conversion.
	 * @type {Object}
	 */
	b32: {

		/**
		 * RFC 4648 base32 alphabet without pad.
		 * @type {string}
		 */
		alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',

		/**
		 * Converts an ArrayBuffer to a base32 string (RFC 4648)
		 * (https://github.com/LinusU/base32-encode).
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} Base32 string.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);

			let bits = 0;
			let value = 0;
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				value = (value << 8) | arr[i];
				bits += 8;

				while (bits >= 5) {
					str += Utils.b32.alphabet[(value >>> bits - 5) & 31];
					bits -= 5;
				}
			}

			if (bits > 0) {
				str += Utils.b32.alphabet[(value << 5 - bits) & 31];
			}

			return str;
		},

		/**
		 * Converts a base32 string to an ArrayBuffer (RFC 4648)
		 * (https://github.com/LinusU/base32-decode).
		 * @param {string} str Base32 string.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			// Canonicalize to all upper case and remove padding if it exists.
			str = str.toUpperCase().replace(/=+$/, '');

			const buf = new ArrayBuffer((str.length * 5) / 8 | 0);
			const arr = new Uint8Array(buf);

			let bits = 0;
			let value = 0;
			let index = 0;

			for (let i = 0; i < str.length; i++) {
				const idx = Utils.b32.alphabet.indexOf(str[i]);
				if (idx === -1) throw new TypeError(`Invalid character found: ${str[i]}`);

				value = (value << 5) | idx;
				bits += 5;

				if (bits >= 8) {
					arr[index++] = (value >>> bits - 8) & 255;
					bits -= 8;
				}
			}

			return buf;
		}

	},

	/**
	 * Hexadecimal string conversion.
	 * @type {Object}
	 */
	hex: {

		/**
		 * Converts an ArrayBuffer to a hexadecimal string.
		 * @param {ArrayBuffer} buf ArrayBuffer.
		 * @returns {string} Hexadecimal string.
		 */
		fromBuf: buf => {
			const arr = new Uint8Array(buf);
			let str = '';

			for (let i = 0; i < arr.length; i++) {
				const hex = arr[i].toString(16);
				str += hex.length === 2 ? hex : `0${hex}`;
			}

			return str.toUpperCase();
		},

		/**
		 * Converts a hexadecimal string to an ArrayBuffer.
		 * @param {string} str Hexadecimal string.
		 * @returns {ArrayBuffer} ArrayBuffer.
		 */
		toBuf: str => {
			const buf = new ArrayBuffer(str.length / 2);
			const arr = new Uint8Array(buf);

			for (let i = 0, j = 0; i < arr.length; i += 1, j += 2) {
				arr[i] = parseInt(str.substr(j, 2), 16);
			}

			return buf;
		}

	},

	/**
	 * Pads a number with leading zeros.
	 * @param {number|string} num Number.
	 * @param {number} digits Digits.
	 * @returns {string} Padded number.
	 */
	pad: (num, digits) => {
		let prefix = '';
		let repeat = digits - String(num).length;
		while (repeat-- > 0) prefix += '0';
		return `${prefix}${num}`;
	}

};

/**
 * An object containing some utilities (for internal use only).
 * @private
 * @type {Object}
 */
export const InternalUtils = {

	/**
	 * "globalThis" ponyfill
	 * (https://mathiasbynens.be/notes/globalthis).
	 * @type {Object}
	 */
	get globalThis() {
		let _globalThis;

		/* eslint-disable no-extend-native, no-restricted-globals, no-undef */
		if (typeof globalThis === 'object') {
			_globalThis = globalThis;
		} else {
			Object.defineProperty(Object.prototype, '__magicalGlobalThis__', {
				get() { return this; },
				configurable: true
			});
			try {
				_globalThis = __magicalGlobalThis__;
			} finally {
				delete Object.prototype.__magicalGlobalThis__;
			}
		}

		if (typeof _globalThis === 'undefined') {
			// Still unable to determine "globalThis", fall back to a naive method:
			if (typeof self !== 'undefined') {
				_globalThis = self;
			} else if (typeof window !== 'undefined') {
				_globalThis = window;
			} else if (typeof global !== 'undefined') {
				_globalThis = global;
			}
		}
		/* eslint-enable */

		Object.defineProperty(this, 'globalThis', {
			enumerable: true,
			value: _globalThis
		});

		return this.globalThis;
	},

	/**
	 * "console" ponyfill.
	 * @type {Object}
	 */
	get console() {
		const _console = {};

		const methods = [
			'assert', 'clear', 'context', 'count', 'countReset', 'debug', 'dir', 'dirxml',
			'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'profile',
			'profileEnd', 'table', 'time', 'timeEnd', 'timeLog', 'timeStamp', 'trace', 'warn'
		];

		if (typeof InternalUtils.globalThis.console === 'object') {
			for (const method of methods) {
				_console[method] = typeof InternalUtils.globalThis.console[method] === 'function'
					? InternalUtils.globalThis.console[method]
					: () => {};
			}
		} else {
			for (const method of methods) {
				_console[method] = () => {};
			}
		}

		Object.defineProperty(this, 'console', {
			enumerable: true,
			value: _console
		});

		return this.console;
	},

	/**
	 * Detect if running in "Node.js".
	 * @type {boolean}
	 */
	get isNode() {
		const _isNode = Object.prototype.toString.call(InternalUtils.globalThis.process) === '[object process]';

		Object.defineProperty(this, 'isNode', {
			enumerable: true,
			value: _isNode
		});

		return this.isNode;
	},

	/**
	 * Dynamically import "Node.js" modules.
	 * (`eval` is used to prevent bundlers from including the module,
	 * e.g., [webpack/webpack#8826](https://github.com/webpack/webpack/issues/8826))
	 * @param {string} name Name.
	 * @returns {Object} Module.
	 */
	// eslint-disable-next-line no-eval
	nodeRequire: name => eval('require')(name)

};
<|endoftext|>


<commit_before>import Kefir from "kefir"
import R     from "ramda"
import React from "react"

const toObservable = x =>
  x instanceof Kefir.Observable ? x : Kefir.constant(x)

function array() { return Array.from(arguments) }

const combineAsArray = obs =>
  obs.length === 1
  ? toObservable(obs[0]).map(array)
  : Kefir.combine(obs.map(toObservable), array)

const combineAsObject = template => {
  const values = []
  const keys = []
  for (const key in template) {
    keys.push(key)
    values.push(toObservable(template[key]))
  }
  return combineAsArray(values).map(values => {
    const result = {}
    const n = keys.length
    for (let i=0; i<n; ++i)
      result[keys[i]] = values[keys[i]]
    return result
  })
}

//

export const config = {
  onError: e => {throw e}
}

const nullState = {dispose: null, rendered: null}

const common = {
  getInitialState() {
    return nullState
  },
  tryDispose() {
    const {dispose} = this.state
    if (dispose)
      dispose()
  },
  componentWillReceiveProps(nextProps) {
    this.trySubscribe(nextProps)
  },
  componentWillMount() {
    this.trySubscribe(this.props)
  },
  shouldComponentUpdate(np, ns) {
    return ns.rendered !== this.state.rendered
  },
  componentWillUnmount() {
    this.tryDispose()
    this.setState(nullState)
  },
  render() {
    return this.state.rendered
  }
}

//

const FromKefir = React.createClass({
  ...common,
  trySubscribe({kefir}) {
    this.tryDispose()

    const callback = rendered => this.setState({rendered})

    this.setState({dispose: () => kefir.offValue(callback)})
  }
})

export const fromKefir = kefir =>
  React.createElement(FromKefir, {kefir})

//

const FromClass = React.createClass({
  ...common,
  trySubscribe({props}) {
    this.tryDispose()

    const vals = {}
    const obsKeys = []
    const obsStreams = []

    for (const key in props) {
      const val = props[key]
      const keyOut = "mount" === key ? "ref" : key
      if (val instanceof Kefir.Observable) {
        obsKeys.push(keyOut)
        obsStreams.push(val)
      } else if ("children" === key &&
                 val instanceof Array &&
                 val.find(c => c instanceof Kefir.Observable)) {
        obsKeys.push(keyOut)
        obsStreams.push(combineAsArray(val))
      } else {
        vals[keyOut] = val
      }
    }

    const callback = obsVals => {
      const props = {}
      let children = null
      for (const key in vals) {
        const val = vals[key]
        if ("children" === key) {children = val} else {props[key] = val}
      }
      for (let i=0, n=obsKeys.length; i<n; ++i) {
        const key = obsKeys[i]
        const val = obsVals[i]
        if ("children" === key) {children = val} else {props[key] = val}
      }
      this.setState({rendered: React.createElement(this.props.Class,
                                                   props,
                                                   children)})
    }

    const observable = combineAsArray(obsStreams)

    observable.onValue(callback)

    this.setState({dispose: () => observable.offValue(callback)})
  }
})

export const fromClass =
  Class => props => React.createElement(FromClass, {Class, props})

export const fromClasses = classes => {
  const result = {}
  for (const k in classes)
    result[k] = fromClass(classes[k])
  return result
}

//

function K() {
  const nm1 = arguments.length-1
  if (1 === nm1) {
    return toObservable(arguments[0]).map(arguments[1])
      .skipDuplicates(R.equals).toProperty()
  } else {
    const xs = Array(nm1)
    for (let i=0; i<nm1; ++i) {
      const x = arguments[i]
      const c = x && x.constructor
      if (c === Array)
        xs[i] = combineAsArray(x)
      else if (c === Object)
        xs[i] = combineAsObject(x)
      else
        xs[i] = toObservable(x)
    }
    return Kefir.combine(xs, arguments[nm1]).skipDuplicates(R.equals)
  }
}

["a", "abbr", "address", "area", "article", "aside", "audio",
 "b", "base", "bdi", "bdo", "big", "blockquote", "body", "br", "button",
 "canvas", "caption", "circle", "cite", "clipPath", "code", "col", "colgroup",
 "data", "datalist", "dd", "defs", "del", "details", "dfn", "dialog", "div", "dl", "dt",
 "ellipse", "em", "embed",
 "fieldset", "figcaption", "figure", "footer", "form",
 "g",
 "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
 "i", "iframe", "image", "img", "input", "ins",
 "kbd", "keygen",
 "label", "legend",
 "li", "line", "linearGradient", "link",
 "main", "map", "mark", "mask", "menu", "menuitem", "meta", "meter",
 "nav", "noscript",
 "object", "ol", "optgroup", "option", "output",
 "p", "param", "path", "pattern", "picture", "polygon", "polyline", "pre", "progress",
 "q",
 "radialGradient", "rect", "rp", "rt", "ruby",
 "s", "samp", "script", "section", "select", "small", "source", "span", "stop", "strong", "style", "sub", "summary", "sup", "svg",
 "table", "tbody", "td", "text", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tspan",
 "u", "ul",
 "var", "video",
 "wbr"].forEach(c => K[c] = fromClass(c))

// Helpers

const classesImmediate = cs => {
  let result = ""
  for (let i=0, n=cs.length; i<n; ++i) {
    const a = cs[i]
    if (a) {
      if (result)
        result += " "
      result += a
    }
  }
  return result
}

export const classes = (...cs) =>
  ({className: (cs.find(c => c instanceof Kefir.Observable)
                ? combineAsArray(cs).map(classesImmediate)
                : classesImmediate(cs))})

export const bind = template => ({...template, onChange: ({target}) => {
  for (const k in template)
    template[k].set(target[k])
}})

//

Kefir.fromIds = (ids, fromId) => ids.scan(([oldIds], ids) => {
  const newIds = {}
  const newVs = []
  ids.forEach(id => {
    const newV = id in oldIds ? oldIds[id] : fromId(id)
    newIds[id] = newV
    newVs.push(newV)
  })
  return [newIds, newVs]
}, [{}, []]).map(s => s[1])

//

export default K
<commit_msg>Optimized to reduce space usage.<commit_after>import Kefir from "kefir"
import R     from "ramda"
import React from "react"

const toObservable = x =>
  x instanceof Kefir.Observable ? x : Kefir.constant(x)

function array() { return Array.from(arguments) }

const combineAsArray = obs =>
  obs.length === 1
  ? toObservable(obs[0]).map(array)
  : Kefir.combine(obs.map(toObservable), array)

const combineAsObject = template => {
  const values = []
  const keys = []
  for (const key in template) {
    keys.push(key)
    values.push(toObservable(template[key]))
  }
  return combineAsArray(values).map(values => {
    const result = {}
    const n = keys.length
    for (let i=0; i<n; ++i)
      result[keys[i]] = values[keys[i]]
    return result
  })
}

//

export const config = {
  onError: e => {throw e}
}

const nullState = {dispose: null, rendered: null}

const common = {
  getInitialState() {
    return nullState
  },
  tryDispose() {
    const {dispose} = this.state
    if (dispose)
      dispose()
  },
  componentWillReceiveProps(nextProps) {
    this.trySubscribe(nextProps)
  },
  componentWillMount() {
    this.trySubscribe(this.props)
  },
  shouldComponentUpdate(np, ns) {
    return ns.rendered !== this.state.rendered
  },
  componentWillUnmount() {
    this.tryDispose()
    this.setState(nullState)
  },
  render() {
    return this.state.rendered
  }
}

//

const FromKefir = React.createClass({
  ...common,
  trySubscribe({kefir}) {
    this.tryDispose()

    const callback = rendered => this.setState({rendered})

    this.setState({dispose: () => kefir.offValue(callback)})
  }
})

export const fromKefir = kefir =>
  React.createElement(FromKefir, {kefir})

//

const FromClass = React.createClass({
  ...common,
  trySubscribe({props}) {
    this.tryDispose()

    const obsStreams = []

    for (const key in props) {
      const val = props[key]
      if (val instanceof Kefir.Observable) {
        obsStreams.push(val)
      } else if ("children" === key &&
                 val instanceof Array) {
        for (let i=0, n=val.length; i<n; ++i) {
          const valI = val[i]
          if (valI instanceof Kefir.Observable)
            obsStreams.push(valI)
        }
      }
    }

    const callback = obsVals => {
      const newProps = {}
      let newChildren = null

      let k = -1

      for (const key in props) {
        const val = props[key]
        if (val instanceof Kefir.Observable) {
          const valO = obsVals[++k]
          if ("children" === key)
            newChildren = valO
          else if ("mount" === key)
            newProps["ref"] = valO
          else
            newProps[key] = valO
        } else if ("children" === key &&
                   val instanceof Array) {
          newChildren = []
          for (let i=0, n=val.length; i<n; ++i) {
            const valI = val[i]
            newChildren.push(valI instanceof Kefir.Observable
                             ? obsVals[++k]
                             : valI)
          }
        } else {
          if ("children" === key)
            newChildren = val
          else if ("mount" === key)
            newProps["ref"] = val
          else
            newProps[key] = val
        }
      }

      this.setState({rendered: React.createElement(this.props.Class,
                                                   newProps,
                                                   newChildren)})
    }

    const observable = combineAsArray(obsStreams)

    observable.onValue(callback)

    this.setState({dispose: () => observable.offValue(callback)})
  }
})

export const fromClass =
  Class => props => React.createElement(FromClass, {Class, props})

export const fromClasses = classes => {
  const result = {}
  for (const k in classes)
    result[k] = fromClass(classes[k])
  return result
}

//

function K() {
  const nm1 = arguments.length-1
  if (1 === nm1) {
    return toObservable(arguments[0]).map(arguments[1])
      .skipDuplicates(R.equals).toProperty()
  } else {
    const xs = Array(nm1)
    for (let i=0; i<nm1; ++i) {
      const x = arguments[i]
      const c = x && x.constructor
      if (c === Array)
        xs[i] = combineAsArray(x)
      else if (c === Object)
        xs[i] = combineAsObject(x)
      else
        xs[i] = toObservable(x)
    }
    return Kefir.combine(xs, arguments[nm1]).skipDuplicates(R.equals)
  }
}

["a", "abbr", "address", "area", "article", "aside", "audio",
 "b", "base", "bdi", "bdo", "big", "blockquote", "body", "br", "button",
 "canvas", "caption", "circle", "cite", "clipPath", "code", "col", "colgroup",
 "data", "datalist", "dd", "defs", "del", "details", "dfn", "dialog", "div", "dl", "dt",
 "ellipse", "em", "embed",
 "fieldset", "figcaption", "figure", "footer", "form",
 "g",
 "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
 "i", "iframe", "image", "img", "input", "ins",
 "kbd", "keygen",
 "label", "legend",
 "li", "line", "linearGradient", "link",
 "main", "map", "mark", "mask", "menu", "menuitem", "meta", "meter",
 "nav", "noscript",
 "object", "ol", "optgroup", "option", "output",
 "p", "param", "path", "pattern", "picture", "polygon", "polyline", "pre", "progress",
 "q",
 "radialGradient", "rect", "rp", "rt", "ruby",
 "s", "samp", "script", "section", "select", "small", "source", "span", "stop", "strong", "style", "sub", "summary", "sup", "svg",
 "table", "tbody", "td", "text", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tspan",
 "u", "ul",
 "var", "video",
 "wbr"].forEach(c => K[c] = fromClass(c))

// Helpers

const classesImmediate = cs => {
  let result = ""
  for (let i=0, n=cs.length; i<n; ++i) {
    const a = cs[i]
    if (a) {
      if (result)
        result += " "
      result += a
    }
  }
  return result
}

export const classes = (...cs) =>
  ({className: (cs.find(c => c instanceof Kefir.Observable)
                ? combineAsArray(cs).map(classesImmediate)
                : classesImmediate(cs))})

export const bind = template => ({...template, onChange: ({target}) => {
  for (const k in template)
    template[k].set(target[k])
}})

//

Kefir.fromIds = (ids, fromId) => ids.scan(([oldIds], ids) => {
  const newIds = {}
  const newVs = []
  ids.forEach(id => {
    const newV = id in oldIds ? oldIds[id] : fromId(id)
    newIds[id] = newV
    newVs.push(newV)
  })
  return [newIds, newVs]
}, [{}, []]).map(s => s[1])

//

export default K
<|endoftext|>


<commit_before>import {validate} from 'jsonschema';
import mapper from 'object-mapper';
import {invert} from 'lodash';
import {getRelatedModel} from '../include_related';

/**
 * @file
 * Class for making requests to Elvis. Exposes methods for CRUD request.
 *
 * @todo
 * Extend with methods for making nested queries.
 */

const QueryTypeMap = {
  entity: {
    single: 'Entity',
    list: 'Entities'
  },
  action: {
    single: 'Action',
    list: 'Actions'
  },
  profile: {
    single: 'Profile',
    list: 'Profiles'
  }
};

const orderPossibilities = [
  'descending',
  'ascending'
];

export function communityRequest(context, params) {
  const id = context.get('communityservice.id');
  const baseurl = context.get('services.communityservice');

  if (!id) {
    throw 'Community-API is not available for the current client. ' +
    'Client needs to be configured with a valid community ID';
  }

  if (!baseurl) {
    throw 'Community-API is not available for the current client. ' +
    'Client needs to be configured with a valid community service url';
  }

  const name = `${baseurl}/community/${id}/${params.path}`;
  delete params.path;

  return context.request(name, params);
}

export default class EntityRequest {
  constructor(type, elvisType, call, map, schema) {
    this._elvisType = elvisType;
    this._type = type;
    this._call = call;
    this._map = map;
    this._schema = schema;
  }

  _mapperToElvis(data) {
    return mapper(data, this._map);
  }

  _mapperFromElvis(data) {
    return mapper(data, invert(this._map));
  }

  /**
   * Return response (json string or object) as object.
   *
   * @param response {String|Object}
   * @returns {Object}
   * @private
   */
  _parseResponse(response) {
    return (typeof response === 'string') && JSON.parse(response) || response;
  }

  /**
   * Create Response object.
   *
   * @param data {Object}
   * @param errors {Array}
   * @returns {Object}
   * @private
   */
  _createResponse(data = {}, errors = []) {
    if (errors && errors.length) {
      return this._parseErrors(errors);
    }
    return {
      status: 200,
      data: data,
      errors: errors
    };
  }

  /**
   * Convert errors to response.
   * @param errors
   * @returns {{status: (*|number), error: (boolean|*), data: Array}}
   * @private
   */
  _parseErrors(errors) {
    const error = Array.isArray(errors) && errors[0] || errors;
    const status = error.status || 500;
    delete error.status;
    return {
      status,
      error,
      data: []
    };
  }

  /**
   * Validate object against utility method.
   *
   * @param object
   * @returns {Object|null} Returns response object if validation errors exists. Otherwise null.
   * @private
   */
  _validate(data, ignore_required = false) {
    const schema = Object.assign({}, this._schema);
    if (ignore_required && schema.required) {
      delete schema.required;
    }
    const validateErrors = validate(data, schema).errors;
    if (validateErrors.length) {
      return {
        status: 400,
        errors: validateErrors.map(o => String(o.stack).replace(/^instance\.?/, '')).join('\n'),
        data: []
      };
    }
    return null;
  }

  _getCounts(name, count) {
    const countQuery = {};
    if (typeof count === 'string') {
      const relatedModel = getRelatedModel(name, count);
      if (relatedModel.Entities || relatedModel.Entity) {
        countQuery.CountEntities = relatedModel.Entities || relatedModel.Entity;
      }

      if (relatedModel.Profiles || relatedModel.Profile) {
        countQuery.CountProfiles = relatedModel.Profiles || relatedModel.Profile;
      }

      if (relatedModel.Action || relatedModel.Actions) {
        countQuery.CountActions = relatedModel.Action || relatedModel.Actions;
      }
    }

    return countQuery;
  }

  /**
   * Generates an Include object for the JSON sent to the community service.
   * Can take nested objects and different input types.
   * It also filters out any unwanted parameters.
   * @param include
   * @param filter
   * @param _map
   * @returns {*}
   * @private
   */
  _getInclude(include = {}, filter = [], name = '', _map = {}) {
    const Include = Object.assign({}, _map);
    if (typeof include === 'string') {
      try {
        const reqInclude = JSON.parse(include);
        include = Array.isArray(reqInclude) ? reqInclude : [reqInclude];
      }
      catch (e) {
        include = [include];
      }
    }
    else if (Array.isArray(include)) {
      include = include;
    }

    if (Array.isArray(include)) {
      include.forEach(item => {
        if (typeof item === 'string') {
          const related = getRelatedModel(name, item);
          if (related) {
            Include[item] = related;
          }
        }
        else if (typeof item === 'object' && item.name) {
          const related = getRelatedModel(name, item.name, item.limit, item.offset, item.filter);
          if (related) {
            if (item.include) {
              if (typeof item.include === 'string') {
                item.include = [item.include];
              }

              related.Include = Object.assign({}, related.Include, this._getInclude(item.include, item.filter, item.name));
            }

            Include[item.name] = related;
            if (item.counts) {
              item.counts = typeof item.counts === 'string' ? [item.counts] : item.counts;
              Array.isArray(item.counts) && item.counts.forEach(count => {
                const additional = {};
                additional[`${count}Count`] = this._getCounts(item.name, count);
                Include[item.name].Include = Object.assign({}, Include[item.name].Include, additional);
              });
            }
          }
        }
      });
    }

    if (Array.isArray(filter)) {
      filter.forEach(filterKey => {
        if (Include[filterKey]) {
          delete Include[filterKey];
        }
      });
    }

    return Include;
  }

  /**
   * Make request to Elvis.
   *
   * @param path {String} Endpoint in Elvis
   * @param method {String} post|get|put|delete
   * @param params {Object} parameters object. most often json or empty.
   * @returns {Promise|Object}
   * @private
   */
  async _request(path, method = 'get', params = {}) {
    const options = Object.assign({path, method}, params);
    try {
      const response = await this._call(options);
      return this._parseResponse(response);
    }
    catch (error) {
      return {errors: error};
    }
  }

  async get(id, include = [], counts = []) {
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {id: id};
    if (this._type) {
      selector.type = this._type;
    }
    const json = {
      [selectorKey]: selector,
      Limit: 1,
      Include: this._getInclude(include, [], this._type || this._elvisType, this._map)
    };

    if (typeof counts === 'string') {
      counts = [counts];
    }

    counts.forEach(count => {
      json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
    });

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data && data.List[0], errors);
  }

  async getSingleProperty(_selector) {
    const selectorKey = QueryTypeMap[this._elvisType].single;
    const selector = _selector;
    const include = this._map;

    if (this._elvisType === 'entity') {
      selector.type = this._type;
    }

    const json = {
      [selectorKey]: selector,
      Include: include
    };

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async post(object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }
    const json = this._mapperToElvis(object);
    if (this._elvisType === 'action') {
      if (json.attributes.reference.type === 'profile') {
        json.profile_ref = json.attributes.reference.id;
      }
      else {
        json.entity_ref = json.attributes.reference.id;
      }
    }
    if (this._type) {
      json.type = this._type;
    }
    const {data, errors} = await this._request(this._elvisType, 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async put(id, object) {
    const validationError = this._validate(object, true);
    if (validationError) {
      return validationError;
    }

    const json = this._mapperToElvis(object);
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async delete(id, modified_by) {
    const json = {modified_by};
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  /**
   * Get all objects of type
   *
   * @returns {{status, data, errors}|*}
   */
  async getList(req) {
    let filter = [];
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = req.selector || {};
    if (this._type) {
      selector.type = this._type;
    }

    if (Array.isArray(req.filter)) {
      filter = req.filter;
    }

    if (typeof req.filter === 'string') {
      filter.push(req.filter);
    }

    const json = {
      [selectorKey]: selector,
      SortBy: 'id',
      Order: 'descending',
      Limit: 2,
      Offset: 0,
      Include: this._getInclude(req.include, filter, this._type || this._elvisType, this._map)
    };

    if (req.offset && !isNaN(parseFloat(req.offset)) && isFinite(req.offset)) {
      json.Offset = req.offset;
    }

    if (req.order && orderPossibilities.indexOf(req.order) >= 0) {
      json.Order = req.order;
    }

    if (typeof req.limit !== 'undefined' && !isNaN(parseFloat(req.limit)) && isFinite(req.limit)) {
      json.Limit = req.limit;
    }

    if (typeof req.sort === 'string') {
      json.SortBy = req.sort;
    }

    if (typeof req.counts === 'string') {
      try {
        req.counts = JSON.parse(req.counts);
      }
      catch (er) {
        req.counts = [req.counts];
      }
    }

    if (Array.isArray(req.counts)) {
      req.counts.forEach(count => {
        json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
      });
    }

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data, errors);
  }
}
<commit_msg>Check if likes and follows exist before creating a new one.<commit_after>import {validate} from 'jsonschema';
import mapper from 'object-mapper';
import {invert} from 'lodash';
import {getRelatedModel} from '../include_related';

/**
 * @file
 * Class for making requests to Elvis. Exposes methods for CRUD request.
 *
 * @todo
 * Extend with methods for making nested queries.
 */

const QueryTypeMap = {
  entity: {
    single: 'Entity',
    list: 'Entities'
  },
  action: {
    single: 'Action',
    list: 'Actions'
  },
  profile: {
    single: 'Profile',
    list: 'Profiles'
  }
};

const orderPossibilities = [
  'descending',
  'ascending'
];

export function communityRequest(context, params) {
  const id = context.get('communityservice.id');
  const baseurl = context.get('services.communityservice');

  if (!id) {
    throw 'Community-API is not available for the current client. ' +
    'Client needs to be configured with a valid community ID';
  }

  if (!baseurl) {
    throw 'Community-API is not available for the current client. ' +
    'Client needs to be configured with a valid community service url';
  }

  const name = `${baseurl}/community/${id}/${params.path}`;
  delete params.path;

  return context.request(name, params);
}

export default class EntityRequest {
  constructor(type, elvisType, call, map, schema) {
    this._elvisType = elvisType;
    this._type = type;
    this._call = call;
    this._map = map;
    this._schema = schema;
  }

  _mapperToElvis(data) {
    return mapper(data, this._map);
  }

  _mapperFromElvis(data) {
    return mapper(data, invert(this._map));
  }

  /**
   * Return response (json string or object) as object.
   *
   * @param response {String|Object}
   * @returns {Object}
   * @private
   */
  _parseResponse(response) {
    return (typeof response === 'string') && JSON.parse(response) || response;
  }

  /**
   * Create Response object.
   *
   * @param data {Object}
   * @param errors {Array}
   * @returns {Object}
   * @private
   */
  _createResponse(data = {}, errors = []) {
    if (errors && errors.length) {
      return this._parseErrors(errors);
    }
    return {
      status: 200,
      data: data,
      errors: errors
    };
  }

  /**
   * Convert errors to response.
   * @param errors
   * @returns {{status: (*|number), error: (boolean|*), data: Array}}
   * @private
   */
  _parseErrors(errors) {
    const error = Array.isArray(errors) && errors[0] || errors;
    const status = error.status || 500;
    delete error.status;
    return {
      status,
      error: error.error || error,
      data: []
    };
  }

  /**
   * Validate object against utility method.
   *
   * @param object
   * @returns {Object|null} Returns response object if validation errors exists. Otherwise null.
   * @private
   */
  _validate(data, ignore_required = false) {
    const schema = Object.assign({}, this._schema);
    if (ignore_required && schema.required) {
      delete schema.required;
    }
    const validateErrors = validate(data, schema).errors;
    if (validateErrors.length) {
      return {
        status: 400,
        errors: validateErrors.map(o => String(o.stack).replace(/^instance\.?/, '')).join('\n'),
        data: []
      };
    }
    return null;
  }

  async actionExists(json) {
    const validateJson = {
      Actions: {
        type: this._type,
        profile_ref: json.profile_ref || null,
        entity_ref: json.entity_ref || null,
        owner_id: json.owner_id
      },
      Include: {id: 'id'},
      Limit: 1
    };
    const {data} = await this._request('query', 'post', {json: validateJson});
    return (Number(data.Total) > 0);
  }

  _getCounts(name, count) {
    const countQuery = {};
    if (typeof count === 'string') {
      const relatedModel = getRelatedModel(name, count);
      if (relatedModel.Entities || relatedModel.Entity) {
        countQuery.CountEntities = relatedModel.Entities || relatedModel.Entity;
      }

      if (relatedModel.Profiles || relatedModel.Profile) {
        countQuery.CountProfiles = relatedModel.Profiles || relatedModel.Profile;
      }

      if (relatedModel.Action || relatedModel.Actions) {
        countQuery.CountActions = relatedModel.Action || relatedModel.Actions;
      }
    }

    return countQuery;
  }

  /**
   * Generates an Include object for the JSON sent to the community service.
   * Can take nested objects and different input types.
   * It also filters out any unwanted parameters.
   * @param include
   * @param filter
   * @param _map
   * @returns {*}
   * @private
   */
  _getInclude(include = {}, filter = [], name = '', _map = {}) {
    const Include = Object.assign({}, _map);
    if (typeof include === 'string') {
      try {
        const reqInclude = JSON.parse(include);
        include = Array.isArray(reqInclude) ? reqInclude : [reqInclude];
      }
      catch (e) {
        include = [include];
      }
    }
    else if (Array.isArray(include)) {
      include = include;
    }

    if (Array.isArray(include)) {
      include.forEach(item => {
        if (typeof item === 'string') {
          const related = getRelatedModel(name, item);
          if (related) {
            Include[item] = related;
          }
        }
        else if (typeof item === 'object' && item.name) {
          const related = getRelatedModel(name, item.name, item.limit, item.offset, item.filter);
          if (related) {
            if (item.include) {
              if (typeof item.include === 'string') {
                item.include = [item.include];
              }

              related.Include = Object.assign({}, related.Include, this._getInclude(item.include, item.filter, item.name));
            }

            Include[item.name] = related;
            if (item.counts) {
              item.counts = typeof item.counts === 'string' ? [item.counts] : item.counts;
              Array.isArray(item.counts) && item.counts.forEach(count => {
                const additional = {};
                additional[`${count}Count`] = this._getCounts(item.name, count);
                Include[item.name].Include = Object.assign({}, Include[item.name].Include, additional);
              });
            }
          }
        }
      });
    }

    if (Array.isArray(filter)) {
      filter.forEach(filterKey => {
        if (Include[filterKey]) {
          delete Include[filterKey];
        }
      });
    }

    return Include;
  }

  /**
   * Make request to Elvis.
   *
   * @param path {String} Endpoint in Elvis
   * @param method {String} post|get|put|delete
   * @param params {Object} parameters object. most often json or empty.
   * @returns {Promise|Object}
   * @private
   */
  async _request(path, method = 'get', params = {}) {
    const options = Object.assign({path, method}, params);
    try {
      const response = await this._call(options);
      return this._parseResponse(response);
    }
    catch (error) {
      return {errors: error};
    }
  }

  async get(id, include = [], counts = []) {
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = {id: id};
    if (this._type) {
      selector.type = this._type;
    }
    const json = {
      [selectorKey]: selector,
      Limit: 1,
      Include: this._getInclude(include, [], this._type || this._elvisType, this._map)
    };

    if (typeof counts === 'string') {
      counts = [counts];
    }

    counts.forEach(count => {
      json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
    });

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data && data.List[0], errors);
  }

  async getSingleProperty(_selector) {
    const selectorKey = QueryTypeMap[this._elvisType].single;
    const selector = _selector;
    const include = this._map;

    if (this._elvisType === 'entity') {
      selector.type = this._type;
    }

    const json = {
      [selectorKey]: selector,
      Include: include
    };

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async post(object) {
    const validationError = this._validate(object);
    if (validationError) {
      return validationError;
    }
    const json = this._mapperToElvis(object);
    if (this._elvisType === 'action') {
      if (json.attributes.reference.type === 'profile') {
        json.profile_ref = json.attributes.reference.id;
      }
      else {
        json.entity_ref = json.attributes.reference.id;
      }
    }
    if (this._type) {
      json.type = this._type;
    }
    if (this._type === 'follow' || this._type === 'like') {
      if (await this.actionExists(json)) {
        return this._createResponse({}, [{error: `${this._type} allready exists`, status: 400}]);
      }
    }

    const {data, errors} = await this._request(this._elvisType, 'post', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async put(id, object) {
    const validationError = this._validate(object, true);
    if (validationError) {
      return validationError;
    }

    const json = this._mapperToElvis(object);
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  async delete(id, modified_by) {
    const json = {modified_by};
    const {data, errors} = await this._request(`${this._elvisType}/${id}`, 'put', {json});
    return this._createResponse(this._mapperFromElvis(data), errors);
  }

  /**
   * Get all objects of type
   *
   * @returns {{status, data, errors}|*}
   */
  async getList(req) {
    let filter = [];
    const selectorKey = QueryTypeMap[this._elvisType].list;
    const selector = req.selector || {};
    if (this._type) {
      selector.type = this._type;
    }

    if (Array.isArray(req.filter)) {
      filter = req.filter;
    }

    if (typeof req.filter === 'string') {
      filter.push(req.filter);
    }

    const json = {
      [selectorKey]: selector,
      SortBy: 'id',
      Order: 'descending',
      Limit: 2,
      Offset: 0,
      Include: this._getInclude(req.include, filter, this._type || this._elvisType, this._map)
    };

    if (req.offset && !isNaN(parseFloat(req.offset)) && isFinite(req.offset)) {
      json.Offset = req.offset;
    }

    if (req.order && orderPossibilities.indexOf(req.order) >= 0) {
      json.Order = req.order;
    }

    if (typeof req.limit !== 'undefined' && !isNaN(parseFloat(req.limit)) && isFinite(req.limit)) {
      json.Limit = req.limit;
    }

    if (typeof req.sort === 'string') {
      json.SortBy = req.sort;
    }

    if (typeof req.counts === 'string') {
      try {
        req.counts = JSON.parse(req.counts);
      }
      catch (er) {
        req.counts = [req.counts];
      }
    }

    if (Array.isArray(req.counts)) {
      req.counts.forEach(count => {
        json.Include[`${count}Count`] = this._getCounts(this._type || this._elvisType, count);
      });
    }

    const {data, errors} = await this._request('query', 'post', {json});
    return this._createResponse(data, errors);
  }
}
<|endoftext|>