<commit_before>import _ from 'underscore';
import {getReposFromStr, convertRepoInfosToStr, KANBAN_LABEL, UNCATEGORIZED_NAME} from './helpers';

const RELEVANT_PATH_SEGMENT = 2;
let ROUTER_HISTORY = null;
export function setRouterHistory(history) {
  ROUTER_HISTORY = history;
}

function toQueryString(options) {
  if (!options || options === {}) {
    return '';
  }
  let params = [];
  const ref = options || {};
  for (const key in ref) {
    let values = ref[key];
    if (!Array.isArray(values)) {
      values = [values];
    }
    values.forEach((val) => {
      params.push(`${key}=${encodeURIComponent(val)}`);
    });
  }
  if (params.length > 0) {
    return `?${params.join('&')}`;
  } else {
    return '';
  }
};

function addParams(options, key, vals) {
  const arr = options[key] || [];
  if (Array.isArray(vals)) {
    options[key] = arr.concat(vals);
  } else if (vals) {
    arr.push(vals);
    options[key] = arr;
  } else {
    // it was null, so ignore it
  }
};

// Generate a URL based on various filters and whatnot
// `/r/:repoStr(/m/:milestonesStr)(/t/:tagsStr)(/u/:user)(/x/:columnRegExp)/:name(/:startShas)(/:endShas)
export function buildRoute(name, {repoInfos, milestoneTitles, tagNames, userName, columnRegExp, routeSegmentName}={}, ...otherFields) {
  repoInfos = repoInfos || [];
  milestoneTitles = milestoneTitles || [];
  tagNames = tagNames || [];

  // when not changing the page then keep the original path segment
  // **BUT:** Allow setting '' explicitly to override this
  if (name === null) {
    name = routeSegmentName;
  }

  const repoStr = convertRepoInfosToStr(repoInfos);

  const options = {};
  addParams(options, 'm', milestoneTitles);
  addParams(options, 'l', tagNames);
  addParams(options, 'u', userName);
  if (columnRegExp) {
    const re = columnRegExp.toString();
    // Strip off the wrapping `/` marks
    addParams(options, 'x', re.substring(1, re.length-1));
  }

  const parts = [];
  if (repoStr) { parts.push(`/r/${repoStr}`); }

  if (name) { parts.push(`/${name}`); }
  otherFields.forEach((field) => {
    parts.push(`/${field}`);
  });
  return encodeURI(parts.join('')) + toQueryString(options);
}

function parseArray(x) {
  if (Array.isArray(x)) {
    return x;
  } else {
    return [x];
  }
}

export function parseRoute({params, routes, location}) {
  let {repoStr, milestonesStr, tagsStr, userName, columnRegExpStr} = params;
  // Note: routeSegmentName can be null if it's the index path (the kanban view)
  if (!routes[RELEVANT_PATH_SEGMENT]) { throw new Error('BUG! looks like you are calling parseRoute (or setFilters) outside of the "magic" route which contains all the filter criteria'); }
  const routeSegmentName = routes[RELEVANT_PATH_SEGMENT].path; // kanban is the "index" path
  if (/[:\/]/.test(routeSegmentName)) { // Check for paths containing a '/' or a ':'
    throw new Error('BUG! the path segment should be simple so we can create links with it');
  }
  let repoInfos = [];
  let milestoneTitles = [];
  let tagNames = [];
  let columnRegExp;

  // TODO: remove these fallbacks once URL's are updated.
  if (repoStr) { repoInfos = getReposFromStr(repoStr); }
  if (milestonesStr) { milestoneTitles = milestonesStr.split('|'); }
  if (tagsStr) { tagNames = tagsStr.split('|'); }
  if (columnRegExpStr) { columnRegExp = new RegExp(columnRegExpStr); }

  const {query} = location;
  if (query.m) { milestoneTitles = parseArray(query.m); }
  if (query.l) { tagNames = parseArray(query.l); }
  if (query.u) { userName = query.u; }
  if (query.x) { columnRegExp = new RegExp(query.x); }

  return {repoInfos, milestoneTitles, tagNames, userName, columnRegExp, routeSegmentName};
}

class FilterState {
  constructor(state) {
    this.state = state;
  }
  _chain(obj) {
    return new FilterState(_.defaults(obj, this.state));
  }
  _toggleKey(key, value) {
    let current = this.state[key];
    let i = current.indexOf(value);
    // if the string isn't found, try adding/removing the '-' to see if
    // there is an exclusion string that we can still remove
    // if (i < 0) {
    //   if (value[0] === '-') {
    //     i = current.indexOf(value.substring(1));
    //   } else {
    //     i = current.indexOf(`-${value}`);
    //   }
    // }

    if (i < 0) {
      current = current.concat(value);
    } else {
      current = current.slice(0, i).concat(current.slice(i+1)); // or _.without(current, value)
    }
    const obj = {};
    obj[key] = current;
    return this._chain(obj);
  }
  // addMilestone(ms)
  // removeMilestone(ms)
  toggleMilestoneTitle(title) {
    return this._toggleKey('milestoneTitles', title);
  }
  clearMilestoneTitles() {
    return this._chain({milestoneTitles: []});
  }
  // addTag(tag)
  // removeTag(tag)
  toggleTagName(tagName) {
    return this._toggleKey('tagNames', tagName);
  }
  // setUser(user)
  // clearUser()
  toggleUserName(name) {
    const {userName} = this.state;
    if (userName) {
      name = null;
    }
    return this._chain({userName: name});
  }
  // setColumnRegExp(reStr)
  setRouteName(routeSegmentName) {
    return this._chain({routeSegmentName});
  }
  go() {
    ROUTER_HISTORY.pushState(null, this.url());
  }
  url() {
    return buildRoute(null, this.state);
  }
  getState() {
    return _.defaults({}, this.state, {columnRegExp: KANBAN_LABEL});
  }
}

const DEFAULTS = {
  repoInfos: [],
  milestoneTitles: [],
  tagNames: [],
  columnRegExp: undefined
};

let FILTER_STATE = new FilterState(DEFAULTS);
// method signature is from react-router onEnter:
// https://github.com/rackt/react-router/blob/master/docs/API.md#onenternextstate-replacestate-callback
export function setFilters(routerState/*,replaceState,callback*/) {
  FILTER_STATE = new FilterState(_.defaults(parseRoute(routerState), DEFAULTS));
}

export function getFilters() {
  // TODO: inject defaults from localStorage (esp the checkboxes)
  return FILTER_STATE;
}

export const LABEL_CACHE = {}; // keys are label names and values are the label object (contains color)

function isUser(issue, userName) {
  if (issue.assignee) {
    if (issue.assignee.login !== userName) { return false; }
  }
  if (issue.user.login !== userName) { return false; }
  return true;
};

// Filters the list of cards by the criteria set in the URL.
// Used by IssueStore.fetchIssues()
export function filterCardsByFilter(cards) {
  const {milestoneTitles, userName, columnRegExp} = getFilters().getState();
  let {tagNames} = getFilters().getState(); // We might remove UNCATEGORIZED_NAME from the list
  const includedTagNames = tagNames.filter((tagName) => { return tagName[0] !== '-'; });
  const excludedTagNames = tagNames.filter((tagName) => { return tagName[0] === '-'; }).map((tagName) => { return tagName.substring(1); });

  const includedMilestoneTitles = milestoneTitles.filter((milestoneTitle) => { return milestoneTitle[0] !== '-'; });
  const excludedMilestoneTitles = milestoneTitles.filter((milestoneTitle) => { return milestoneTitle[0] === '-'; }).map((milestoneTitle) => { return milestoneTitle.substring(1); });

  return cards.filter((card) => {
    const {issue} = card;
    // Add all the labels for lookup later (like the color)
    issue.labels.forEach((label) => {
      LABEL_CACHE[label.name] = label;
    });
    // issue must match the user if one is selected (either assignee or creator (if none))
    if (userName) {
      // Support negating the username
      if (userName[0] === '-') {
        if (isUser(issue, userName.substring(1))) { return false; }
      } else {
        if (!isUser(issue, userName)) { return false; }
      }
    }
    // issue must be in one of the milestones (if milestones are selected)
    if (milestoneTitles.length > 0) {
      if (includedMilestoneTitles.length > 0) {
        if (!issue.milestone) { return false; } // TODO: Read the settings to see if no milestones are allowed
      }
      if (issue.milestone) {
        if (includedMilestoneTitles.length > 0 && includedMilestoneTitles.indexOf(issue.milestone.title) < 0) { return false; }
        if (excludedMilestoneTitles.length > 0 && excludedMilestoneTitles.indexOf(issue.milestone.title) >= 0) { return false; }
      }
    }
    // If one of the tagNames is UNCATEGORIZED_NAME then do something special
    if (tagNames.indexOf(UNCATEGORIZED_NAME) >= 0) {
      let isUnlabeled = true;
      issue.labels.forEach((label) => {
        if (columnRegExp.test(label.name)) {
          isUnlabeled = false;
        }
      });
      if (!isUnlabeled) { return false; }
    }
    const labelNames = issue.labels.map((label) => { return label.name; });
    // issue must have all the tags (except UNCATEGORIZED_NAME)
    // and MUST NOT have any of the excluded tags
    if (_.difference(_.without(includedTagNames, UNCATEGORIZED_NAME), labelNames).length > 0) { return false; }
    if (_.intersection(excludedTagNames, labelNames).length > 0) { return false; }
    return true;
  });
}
<commit_msg>support excluding uncategorized issues<commit_after>import _ from 'underscore';
import {getReposFromStr, convertRepoInfosToStr, KANBAN_LABEL, UNCATEGORIZED_NAME} from './helpers';

const RELEVANT_PATH_SEGMENT = 2;
let ROUTER_HISTORY = null;
export function setRouterHistory(history) {
  ROUTER_HISTORY = history;
}

function toQueryString(options) {
  if (!options || options === {}) {
    return '';
  }
  let params = [];
  const ref = options || {};
  for (const key in ref) {
    let values = ref[key];
    if (!Array.isArray(values)) {
      values = [values];
    }
    values.forEach((val) => {
      params.push(`${key}=${encodeURIComponent(val)}`);
    });
  }
  if (params.length > 0) {
    return `?${params.join('&')}`;
  } else {
    return '';
  }
};

function addParams(options, key, vals) {
  const arr = options[key] || [];
  if (Array.isArray(vals)) {
    options[key] = arr.concat(vals);
  } else if (vals) {
    arr.push(vals);
    options[key] = arr;
  } else {
    // it was null, so ignore it
  }
};

// Generate a URL based on various filters and whatnot
// `/r/:repoStr(/m/:milestonesStr)(/t/:tagsStr)(/u/:user)(/x/:columnRegExp)/:name(/:startShas)(/:endShas)
export function buildRoute(name, {repoInfos, milestoneTitles, tagNames, userName, columnRegExp, routeSegmentName}={}, ...otherFields) {
  repoInfos = repoInfos || [];
  milestoneTitles = milestoneTitles || [];
  tagNames = tagNames || [];

  // when not changing the page then keep the original path segment
  // **BUT:** Allow setting '' explicitly to override this
  if (name === null) {
    name = routeSegmentName;
  }

  const repoStr = convertRepoInfosToStr(repoInfos);

  const options = {};
  addParams(options, 'm', milestoneTitles);
  addParams(options, 'l', tagNames);
  addParams(options, 'u', userName);
  if (columnRegExp) {
    const re = columnRegExp.toString();
    // Strip off the wrapping `/` marks
    addParams(options, 'x', re.substring(1, re.length-1));
  }

  const parts = [];
  if (repoStr) { parts.push(`/r/${repoStr}`); }

  if (name) { parts.push(`/${name}`); }
  otherFields.forEach((field) => {
    parts.push(`/${field}`);
  });
  return encodeURI(parts.join('')) + toQueryString(options);
}

function parseArray(x) {
  if (Array.isArray(x)) {
    return x;
  } else {
    return [x];
  }
}

export function parseRoute({params, routes, location}) {
  let {repoStr, milestonesStr, tagsStr, userName, columnRegExpStr} = params;
  // Note: routeSegmentName can be null if it's the index path (the kanban view)
  if (!routes[RELEVANT_PATH_SEGMENT]) { throw new Error('BUG! looks like you are calling parseRoute (or setFilters) outside of the "magic" route which contains all the filter criteria'); }
  const routeSegmentName = routes[RELEVANT_PATH_SEGMENT].path; // kanban is the "index" path
  if (/[:\/]/.test(routeSegmentName)) { // Check for paths containing a '/' or a ':'
    throw new Error('BUG! the path segment should be simple so we can create links with it');
  }
  let repoInfos = [];
  let milestoneTitles = [];
  let tagNames = [];
  let columnRegExp;

  // TODO: remove these fallbacks once URL's are updated.
  if (repoStr) { repoInfos = getReposFromStr(repoStr); }
  if (milestonesStr) { milestoneTitles = milestonesStr.split('|'); }
  if (tagsStr) { tagNames = tagsStr.split('|'); }
  if (columnRegExpStr) { columnRegExp = new RegExp(columnRegExpStr); }

  const {query} = location;
  if (query.m) { milestoneTitles = parseArray(query.m); }
  if (query.l) { tagNames = parseArray(query.l); }
  if (query.u) { userName = query.u; }
  if (query.x) { columnRegExp = new RegExp(query.x); }

  return {repoInfos, milestoneTitles, tagNames, userName, columnRegExp, routeSegmentName};
}

class FilterState {
  constructor(state) {
    this.state = state;
  }
  _chain(obj) {
    return new FilterState(_.defaults(obj, this.state));
  }
  _toggleKey(key, value) {
    let current = this.state[key];
    let i = current.indexOf(value);
    // if the string isn't found, try adding/removing the '-' to see if
    // there is an exclusion string that we can still remove
    // if (i < 0) {
    //   if (value[0] === '-') {
    //     i = current.indexOf(value.substring(1));
    //   } else {
    //     i = current.indexOf(`-${value}`);
    //   }
    // }

    if (i < 0) {
      current = current.concat(value);
    } else {
      current = current.slice(0, i).concat(current.slice(i+1)); // or _.without(current, value)
    }
    const obj = {};
    obj[key] = current;
    return this._chain(obj);
  }
  // addMilestone(ms)
  // removeMilestone(ms)
  toggleMilestoneTitle(title) {
    return this._toggleKey('milestoneTitles', title);
  }
  clearMilestoneTitles() {
    return this._chain({milestoneTitles: []});
  }
  // addTag(tag)
  // removeTag(tag)
  toggleTagName(tagName) {
    return this._toggleKey('tagNames', tagName);
  }
  // setUser(user)
  // clearUser()
  toggleUserName(name) {
    const {userName} = this.state;
    if (userName) {
      name = null;
    }
    return this._chain({userName: name});
  }
  // setColumnRegExp(reStr)
  setRouteName(routeSegmentName) {
    return this._chain({routeSegmentName});
  }
  go() {
    ROUTER_HISTORY.pushState(null, this.url());
  }
  url() {
    return buildRoute(null, this.state);
  }
  getState() {
    return _.defaults({}, this.state, {columnRegExp: KANBAN_LABEL});
  }
}

const DEFAULTS = {
  repoInfos: [],
  milestoneTitles: [],
  tagNames: [],
  columnRegExp: undefined
};

let FILTER_STATE = new FilterState(DEFAULTS);
// method signature is from react-router onEnter:
// https://github.com/rackt/react-router/blob/master/docs/API.md#onenternextstate-replacestate-callback
export function setFilters(routerState/*,replaceState,callback*/) {
  FILTER_STATE = new FilterState(_.defaults(parseRoute(routerState), DEFAULTS));
}

export function getFilters() {
  // TODO: inject defaults from localStorage (esp the checkboxes)
  return FILTER_STATE;
}

export const LABEL_CACHE = {}; // keys are label names and values are the label object (contains color)

function isUser(issue, userName) {
  if (issue.assignee) {
    if (issue.assignee.login !== userName) { return false; }
  }
  if (issue.user.login !== userName) { return false; }
  return true;
};

// Filters the list of cards by the criteria set in the URL.
// Used by IssueStore.fetchIssues()
export function filterCardsByFilter(cards) {
  const {milestoneTitles, userName, columnRegExp} = getFilters().getState();
  let {tagNames} = getFilters().getState(); // We might remove UNCATEGORIZED_NAME from the list
  const includedTagNames = tagNames.filter((tagName) => { return tagName[0] !== '-'; });
  const excludedTagNames = tagNames.filter((tagName) => { return tagName[0] === '-'; }).map((tagName) => { return tagName.substring(1); });

  const includedMilestoneTitles = milestoneTitles.filter((milestoneTitle) => { return milestoneTitle[0] !== '-'; });
  const excludedMilestoneTitles = milestoneTitles.filter((milestoneTitle) => { return milestoneTitle[0] === '-'; }).map((milestoneTitle) => { return milestoneTitle.substring(1); });

  return cards.filter((card) => {
    const {issue} = card;
    // Add all the labels for lookup later (like the color)
    issue.labels.forEach((label) => {
      LABEL_CACHE[label.name] = label;
    });
    // issue must match the user if one is selected (either assignee or creator (if none))
    if (userName) {
      // Support negating the username
      if (userName[0] === '-') {
        if (isUser(issue, userName.substring(1))) { return false; }
      } else {
        if (!isUser(issue, userName)) { return false; }
      }
    }
    // issue must be in one of the milestones (if milestones are selected)
    if (milestoneTitles.length > 0) {
      if (includedMilestoneTitles.length > 0) {
        if (!issue.milestone) { return false; } // TODO: Read the settings to see if no milestones are allowed
      }
      if (issue.milestone) {
        if (includedMilestoneTitles.length > 0 && includedMilestoneTitles.indexOf(issue.milestone.title) < 0) { return false; }
        if (excludedMilestoneTitles.length > 0 && excludedMilestoneTitles.indexOf(issue.milestone.title) >= 0) { return false; }
      }
    }
    // If one of the tagNames is UNCATEGORIZED_NAME then do something special
    if (tagNames.indexOf(UNCATEGORIZED_NAME) >= 0) {
      let isUnlabeled = true;
      issue.labels.forEach((label) => {
        if (columnRegExp.test(label.name)) {
          isUnlabeled = false;
        }
      });
      if (!isUnlabeled) { return false; }
    }
    if (tagNames.indexOf(`-${UNCATEGORIZED_NAME}`) >= 0) {
      let isUnlabeled = true;
      issue.labels.forEach((label) => {
        if (columnRegExp.test(label.name)) {
          isUnlabeled = false;
        }
      });
      if (isUnlabeled) { return false; }
    }
    const labelNames = issue.labels.map((label) => { return label.name; });
    // issue must have all the tags (except UNCATEGORIZED_NAME)
    // and MUST NOT have any of the excluded tags
    if (_.difference(_.without(includedTagNames, UNCATEGORIZED_NAME), labelNames).length > 0) { return false; }
    if (_.intersection(excludedTagNames, labelNames).length > 0) { return false; }
    return true;
  });
}
<|endoftext|>


<commit_before>import canEvent from 'can-event';
import hydrate from './hooks/hydrate/';
import cache from './hooks/cache/';
import translateIds from './hooks/translate-ids/';
import deepAssign from 'can-util/js/deep-assign/deep-assign';

export default function ObservableService(options = {}){
  if (!options.Map) {
    throw new Error('Please provide a Map Constructor.');
  }
  if (!options.Map.List && !options.List) {
    throw new Error('Please provide a List Constructor.');
  }
  if (!options.service) {
    throw new Error('Please provide a feathers service.');
  }

  let name = options.name || options.Map.constructor.prototype.name;
  if (name === 'Map') {
    if(steal){
      steal.dev.log('Debugging will be easier if you give your Map a name.');
    }
  }

  const service = options.service;
  delete options.service;

  if (!service.before || !service.after) {
    throw new Error('The feathers-hooks plugin is required.');
  }

  // If provided, flatten the cache object into the options.
  if (options.cache) {
    options.cacheService = options.cache.service;
    options.cacheIdProp = options.cache.cacheIdProp;
  }

  const Map = options.Map;
  const List = Map.List || options.List;
  const defaults = {
    idProp: '_id',
    modelName: name,
    Map,
    List
  };
  Object.assign(service, defaults, options);

  // Extend the Map
  Object.assign(Map, {
    idProp: service.idProp,

    // The model store.
    store: {},

    /**
     * `Map.find` will run the provided query against the internal Feathers service.
     * If a cacheService was provided and `$fromCache` is provided in the query, the
     * query will only run against the `cacheService`.
     */
    find(query){
      if (service.cacheService && query.$fromCache) {
        delete query.$fromCache;
        return service.cacheService.find({query});
      }
      return service.find({query});
    },

    /**
     * `Map.get` will run the provided query against the internal Feathers service.
     * If a cacheService was provided and `$fromCache` is provided in the params, the
     * query will only run against the `cacheService`.
     */
    get(id, params){
      if (service.cacheService && params.$fromCache) {
        delete params.$fromCache;
        return service.cacheService.get(id, params);
      }
      return service.get(id, params);
    }
  }, canEvent);

  // store a reference to the original Map.newInstance function
  var _newInstance = Map.newInstance;

  // override the Map's newInstance function
  Map.newInstance = function(props) {
    let id = props[service.idProp];

    // If there's an id, look in the Map.store to see if the object already exists
    if (id) {
      var cachedInst = Map.store[id];
      if(cachedInst) {
        // Copy all new attributes to the cached instance and return it.
        cachedInst = deepAssign(cachedInst, props);
        return cachedInst;
      }
    }

    // There was no id, call the original newInstance function and return a new
    // instance of Person.
    var newInst = _newInstance.apply(this, arguments);
    if (id) {
      Map.store[id] = newInst;
    }
    return newInst;
  };

  // Setup access to cache service through the Map.cache object.
  if (service.cacheService) {
    Object.assign(Map, {
      cache: {
        find(query){
          return service.cacheService.find({query});
        },

        get(id, params){
          return service.cacheService.get(id, params);
        }
      }
    });

    service.cacheService.before({
      all: [
        // Prevent overwriting the idProp of the cached data.
        translateIds({
          remoteIdProp: service.idProp,
          cacheIdProp: service.cacheIdProp
        })
      ]
    });

    service.cacheService.after({
      all: [
        // Restore the original idProp of the cached data.
        translateIds({
          remoteIdProp: service.idProp,
          cacheIdProp: service.cacheIdProp
        }),
        // Make sure results from the cache are DefineMaps and DefineLists
        hydrate({ Map, List })
      ]
    });

    Object.assign(Map.constructor.prototype, {
      cache(){
        return service.cacheService.create(this.serialize()).then(cachedInstance => {
          Map.dispatch('cached', [cachedInstance]);
          return cachedInstance;
        });
      }
    });
  }

  // Extend the Map instances.
  Object.assign(Map.constructor.prototype, {

    /**
     * When instances are saved, if they have an `idProp` already, run `service.update`
     * on the instance and trigger the `updated` event on the Map when the data returns.
     * If there is no `idProp`, run `service.create` and trigger the `created` event.
     * @return {[type]} [description]
     */
    save(){
      let id = this[Map.idProp];
      if (id) {
        return service.update(id, this.serialize()).then(response => {
          Map.dispatch('updated', [response]);
          return response;
        });
      } else {
        return service.create(this.serialize()).then(response => {
          Map.dispatch('created', [response]);
          return response;
        });
      }
      return ;
    },

    /**
     * `patch()` currently requires passing in a props object.  In the future,
     * we should update it to watch for props that change and mark them as dirty,
     * then it can only send the dirty props.  For now, passing the props will
     * send those props as the patch request.
     */
    patch(props = {}){
      let id = this[Map.idProp];
      return service.patch(id, props).then(response => {
        Map.dispatch('patched', [response]);
        return response;
      });
    },

    destroy(){
      let id = this[Map.idProp];
      if (id) {
        return service.remove(id).then(response => {
          Map.dispatch('destroyed', [response]);
          return response;
        });
      }
      return Promise.resolve(this);
    }
  });

  // Extend the List instances
  Object.assign(service.List.constructor.prototype, {
    save(){
      return true;
    },
    patch(){
      return true;
    },
    destroy(){
      return true;
    }
  });

  // Add before hooks.
  service.before({
    all: [],
    find: [
      cache.find()
    ],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  });

  // Add after hooks.
  service.after({
    all: [
      hydrate()
    ],
    find: [
      cache.merge()
    ],
    create: [
      cache.mergeOne()
    ]
  });

  return service;
}
<commit_msg>idProp is required, now.<commit_after>import canEvent from 'can-event';
import hydrate from './hooks/hydrate/';
import cache from './hooks/cache/';
import translateIds from './hooks/translate-ids/';
import deepAssign from 'can-util/js/deep-assign/deep-assign';

export default function ObservableService(options = {}){
  if (!options.Map) {
    throw new Error('Please provide a Map Constructor.');
  }
  if (!options.Map.List && !options.List) {
    throw new Error('Please provide a List Constructor.');
  }
  if (!options.service) {
    throw new Error('Please provide a feathers service.');
  }
  if (!options.idProp) {
    throw new Error('Please provide an idProp.');
  }

  let name = options.name || options.Map.constructor.prototype.name;
  if (name === 'Map') {
    if(steal){
      steal.dev.log('Debugging will be easier if you give your Map a name.');
    }
  }

  const service = options.service;
  delete options.service;

  if (!service.before || !service.after) {
    throw new Error('The feathers-hooks plugin is required.');
  }

  // If provided, flatten the cache object into the options.
  if (options.cache) {
    options.cacheService = options.cache.service;
    options.cacheIdProp = options.cache.cacheIdProp;
  }

  const Map = options.Map;
  const List = Map.List || options.List;
  const defaults = {
    idProp: undefined,
    modelName: name,
    Map,
    List
  };
  Object.assign(service, defaults, options);

  // Extend the Map
  Object.assign(Map, {
    idProp: service.idProp,

    // The model store.
    store: {},

    /**
     * `Map.find` will run the provided query against the internal Feathers service.
     * If a cacheService was provided and `$fromCache` is provided in the query, the
     * query will only run against the `cacheService`.
     */
    find(query){
      if (service.cacheService && query.$fromCache) {
        delete query.$fromCache;
        return service.cacheService.find({query});
      }
      return service.find({query});
    },

    /**
     * `Map.get` will run the provided query against the internal Feathers service.
     * If a cacheService was provided and `$fromCache` is provided in the params, the
     * query will only run against the `cacheService`.
     */
    get(id, params){
      if (service.cacheService && params.$fromCache) {
        delete params.$fromCache;
        return service.cacheService.get(id, params);
      }
      return service.get(id, params);
    }
  }, canEvent);

  // store a reference to the original Map.newInstance function
  var _newInstance = Map.newInstance;

  // override the Map's newInstance function
  Map.newInstance = function(props) {
    let id = props[service.idProp];

    // If there's an id, look in the Map.store to see if the object already exists
    if (id) {
      var cachedInst = Map.store[id];
      if(cachedInst) {
        // Copy all new attributes to the cached instance and return it.
        cachedInst = deepAssign(cachedInst, props);
        return cachedInst;
      }
    }

    // There was no id, call the original newInstance function and return a new
    // instance of Person.
    var newInst = _newInstance.apply(this, arguments);
    if (id) {
      Map.store[id] = newInst;
    }
    return newInst;
  };

  // Setup access to cache service through the Map.cache object.
  if (service.cacheService) {
    Object.assign(Map, {
      cache: {
        find(query){
          return service.cacheService.find({query});
        },

        get(id, params){
          return service.cacheService.get(id, params);
        }
      }
    });

    service.cacheService.before({
      all: [
        // Prevent overwriting the idProp of the cached data.
        translateIds({
          remoteIdProp: service.idProp,
          cacheIdProp: service.cacheIdProp
        })
      ]
    });

    service.cacheService.after({
      all: [
        // Restore the original idProp of the cached data.
        translateIds({
          remoteIdProp: service.idProp,
          cacheIdProp: service.cacheIdProp
        }),
        // Make sure results from the cache are DefineMaps and DefineLists
        hydrate({ Map, List })
      ]
    });

    Object.assign(Map.constructor.prototype, {
      cache(){
        return service.cacheService.create(this.serialize()).then(cachedInstance => {
          Map.dispatch('cached', [cachedInstance]);
          return cachedInstance;
        });
      }
    });
  }

  // Extend the Map instances.
  Object.assign(Map.constructor.prototype, {

    /**
     * When instances are saved, if they have an `idProp` already, run `service.update`
     * on the instance and trigger the `updated` event on the Map when the data returns.
     * If there is no `idProp`, run `service.create` and trigger the `created` event.
     * @return {[type]} [description]
     */
    save(){
      let id = this[Map.idProp];
      if (id) {
        return service.update(id, this.serialize()).then(response => {
          Map.dispatch('updated', [response]);
          return response;
        });
      } else {
        return service.create(this.serialize()).then(response => {
          Map.dispatch('created', [response]);
          return response;
        });
      }
      return ;
    },

    /**
     * `patch()` currently requires passing in a props object.  In the future,
     * we should update it to watch for props that change and mark them as dirty,
     * then it can only send the dirty props.  For now, passing the props will
     * send those props as the patch request.
     */
    patch(props = {}){
      let id = this[Map.idProp];
      return service.patch(id, props).then(response => {
        Map.dispatch('patched', [response]);
        return response;
      });
    },

    destroy(){
      let id = this[Map.idProp];
      if (id) {
        return service.remove(id).then(response => {
          Map.dispatch('destroyed', [response]);
          return response;
        });
      }
      return Promise.resolve(this);
    }
  });

  // Extend the List instances
  Object.assign(service.List.constructor.prototype, {
    save(){
      return true;
    },
    patch(){
      return true;
    },
    destroy(){
      return true;
    }
  });

  // Add before hooks.
  service.before({
    all: [],
    find: [
      cache.find()
    ],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  });

  // Add after hooks.
  service.after({
    all: [
      hydrate()
    ],
    find: [
      cache.merge()
    ],
    create: [
      cache.mergeOne()
    ]
  });

  return service;
}
<|endoftext|>


<commit_before>import colors from 'colors';
import Draft from './draft';
import Login from './login';
import Submit from './submit';
import DraftRecord from './draft_record';
import {
  user,
  report
} from '../config';
import workday from '../workday';
import yargs from 'yargs';
import pkg from '../package';

const argv = yargs
  .option('u', {
    alias: 'username',
    demand: true,
    describe: 'esap.huiyuanit.com login username'
  })
  .option('p', {
    alias: 'password',
    demand: true,
    describe: 'esap.huiyuanit.com login password'
  })
  .option('o', {
    alias: 'option',
    demand: true,
    describe: 'draft or submit',
    type: 'string'
  })
  .option('pi', {
    alias: 'project-id',
    demand: true,
    default: 144,
    describe: 'project id',
    type: 'int'
  })
  .option('pi', {
    alias: 'project-id',
    demand: true,
    default: 144,
    describe: 'project id',
    type: 'int'
  })
  .option('t', {
    alias: 'trip',
    describe: 'is trip',
    boolean: true
  })
  .option('c', {
    alias: 'city',
    describe: 'the trip city',
    type: 'string'
  })
  .alias('v', 'version')
  .argv;

let draft = new Draft();
let login = new Login();
let draft_record = new DraftRecord();
let submit = new Submit();

if ('draft' === argv.o) {
  login.login(argv.u, argv.p).then(
    (cookie) => write_draft(cookie),
    (err) => error(err)
  );
} else if ('submit' === argv.o) {
  login.login(argv.u, argv.p).then(
    (cookie) => submit_all_draft(cookie),
    (err) => error(err)
  );
} else {
  console.log(`We don't have this option: ${argv.o}. Expect draft or submit`.red);
  process.exit(1);
}

function write_draft(cookie) {
  const days = report_dates();
  if (days) {
    days.map((day) => {
      const report_date = `${report.year}-${report.month}-${day}`;
      draft.write_report(report_date, cookie).then(
        (data) => console.log(`DRAFT: ${report_date} ${data}`.green),
        (err) => console.log(`DRAFT: ${report_date} ${err}`.red)
      );
    });
  }
}

function submit_all_draft(cookie) {
  draft_record.record_ids(cookie).then((record_ids) => {
    record_ids.map((record_id) => {
      submit.submit_report(record_id, cookie).then(
        (data) => console.log(`SUBMIT: ${record_id} ${data}`.green),
        (err) => console.log(`SUBMIT: ${record_id} ${err}`.red)
      );
    });
    if (0 === record_ids.length) {
      console.log('Nothing to submit'.green);
    }
  }, (err) => error(err));
}

function report_dates() {
  let year = workday[report.year];
  let days = year[report.month];

  if (year && days) {
    // get before now days
    const now_date = new Date().getDate();
    let before_days = [];
    for (let i in days) {
      const day = days[i];
      if (Number(day) > now_date) {
        break;
      } else {
        before_days.push(day);
      }
    }
    return before_days;
  } else {
    console.log(`Missing workday data month ${report.year}-${report.month}`.red);
  }
}

function is_report_time_after_now() {
  let now = new Date();
  if (Number(report.year) > now.getFullYear()) {
    return true;
  }
  return Number(report.month) > (now.getMonth() + 1)
}

function error(err) {
  console.log(err.red);
  process.exit(1);
}
<commit_msg>Update index.js<commit_after>import colors from 'colors';
import Draft from './draft';
import Login from './login';
import Submit from './submit';
import DraftRecord from './draft_record';
import workday from '../workday';
import yargs from 'yargs';
import pkg from '../package';

const argv = yargs
  .option('u', {
    alias: 'username',
    demand: true,
    describe: 'esap.huiyuanit.com login username'
  })
  .option('p', {
    alias: 'password',
    demand: true,
    describe: 'esap.huiyuanit.com login password'
  })
  .option('pi', {
    alias: 'project-id',
    demand: true,
    default: 144,
    describe: 'project id',
    type: 'int'
  })
  .option('pi', {
    alias: 'project-id',
    demand: true,
    default: 144,
    describe: 'project id',
    type: 'int'
  })
  .option('t', {
    alias: 'trip',
    describe: 'is trip',
    boolean: true
  })
  .option('c', {
    alias: 'city',
    describe: 'the trip city',
    type: 'string'
  })
  .option('s', {
    alias: 'submit',
    demand: true,
    describe: 'Submit draft',
    boolean: true
  })
  .alias('v', 'version')
  .argv;

let draft = new Draft();
let login = new Login();
let draft_record = new DraftRecord();
let submit = new Submit();

if (argv.s) {
  login.login(argv.u, argv.p).then(
    (cookie) => submit_all_draft(cookie),
    (err) => error(err)
  );
} else {
  login.login(argv.u, argv.p).then(
    (cookie) => write_draft(cookie),
    (err) => error(err)
  );
}

function write_draft(cookie) {
  let now = new Date();
  const year = now.getFullYear();
  const month = now.getMonth() + 1;
  const days = report_dates(year, month);

  if (days) {
    days.map((day) => {
      const report_date = `${year}-${month}-${day}`;
      draft.write_report(report_date, cookie).then(
        (data) => console.log(`DRAFT: ${report_date} ${data}`.green),
        (err) => console.log(`DRAFT: ${report_date} ${err}`.red)
      );
    });
  }
}

function submit_all_draft(cookie) {
  draft_record.record_ids(cookie).then((record_ids) => {
    record_ids.map((record_id) => {
      submit.submit_report(record_id, cookie).then(
        (data) => console.log(`SUBMIT: ${record_id} ${data}`.green),
        (err) => console.log(`SUBMIT: ${record_id} ${err}`.red)
      );
    });
    if (0 === record_ids.length) {
      console.log('Nothing to submit'.green);
    }
  }, (err) => error(err));
}

function report_dates($year, $month) {
  let year = workday[$year];
  let days = year[$month];

  if (year && days) {
    // get before now days
    const now_date = new Date().getDate();
    let before_days = [];
    for (let i in days) {
      const day = days[i];
      if (Number(day) > now_date) {
        break;
      } else {
        before_days.push(day);
      }
    }
    return before_days;
  } else {
    console.log(`Missing workday data month ${report.year}-${report.month}`.red);
  }
}

function error(err) {
  console.log(err.red);
  process.exit(1);
}
<|endoftext|>


<commit_before>'use strict';

function draw_logic(){
    // Create gradient fillstyle for all walls.
    var gradient = buffer.createLinearGradient(
      0,
      window.innerHeight / 20,
      0,
      window.innerHeight
    );
    gradient.addColorStop(
      0,
      '#000'
    );
    gradient.addColorStop(
      1,
      '#a99'
    );
    buffer.fillStyle = gradient;

    buffer.translate(
      x,
      y
    );

    // Rotate 90 degrees each time and draw a wall.
    var loop_counter = 3;
    var rotate_math = 90 * (Math.PI / 180);
    do{
        buffer.rotate(rotate_math);

        buffer.beginPath();
        buffer.moveTo(
          0,
          0
        );
        buffer.lineTo(
          width,
          width
        );
        buffer.lineTo(
          -width,
          width
        );
        buffer.closePath();
        buffer.fill();
    }while(loop_counter--);
}

function resize_logic(){
    draw();
}

window.onload = init_canvas;
<commit_msg>updated code to new use common canvas.js<commit_after>'use strict';

function draw_logic(){
    console.log(1);
    buffer.save();

    // Create gradient fillstyle for all walls.
    var gradient = buffer.createLinearGradient(
      0,
      window.innerHeight / 20,
      0,
      window.innerHeight
    );
    gradient.addColorStop(
      0,
      '#000'
    );
    gradient.addColorStop(
      1,
      '#a99'
    );
    buffer.fillStyle = gradient;

    buffer.translate(
      x,
      y
    );

    // Rotate 90 degrees each time and draw a wall.
    var loop_counter = 3;
    var rotate_math = 90 * (Math.PI / 180);
    do{
        buffer.rotate(rotate_math);

        buffer.beginPath();
        buffer.moveTo(
          0,
          0
        );
        buffer.lineTo(
          width,
          width
        );
        buffer.lineTo(
          -width,
          width
        );
        buffer.closePath();
        buffer.fill();
    }while(loop_counter--);

    buffer.restore();
}

function resize_logic(){
    draw();
}

window.onload = init_canvas;
<|endoftext|>


<commit_before>'use strict';

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

const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError, InsufficientFunds } = require ('./base/errors');

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

module.exports = class bcex extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bcex',
            'name': 'BCEX',
            'countries': [ 'CN', 'CA' ],
            'version': '1',
            'has': {
                'fetchBalance': true,
                'fetchMarkets': true,
                'createOrder': true,
                'cancelOrder': true,
                'fetchTicker': true,
                'fetchTickers': false,
                'fetchTrades': true,
                'fetchOrder': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg',
                'api': 'https://www.bcex.top',
                'www': 'https://www.bcex.top',
                'doc': 'https://www.bcex.top/api_market/market/',
                'fees': 'http://bcex.udesk.cn/hc/articles/57085',
                'referral': 'https://www.bcex.top/user/reg/type/2/pid/758978',
            },
            'api': {
                'public': {
                    'get': [
                        'Api_Market/getPriceList', // tickers
                        'Api_Order/ticker', // last ohlcv candle (ticker)
                        'Api_Order/depth', // orderbook
                        'Api_Market/getCoinTrade', // ticker
                        'Api_Order/marketOrder', // trades...
                    ],
                    'post': [
                        'Api_Market/getPriceList', // tickers
                        'Api_Order/ticker', // last ohlcv candle (ticker)
                        'Api_Order/depth', // orderbook
                        'Api_Market/getCoinTrade', // ticker
                        'Api_Order/marketOrder', // trades...
                    ],
                },
                'private': {
                    'post': [
                        'Api_Order/cancel',
                        'Api_Order/coinTrust', // limit order
                        'Api_Order/orderList', // open / all orders (my trades?)
                        'Api_Order/orderInfo',
                        'Api_Order/tradeList', // open / all orders
                        'Api_Order/trustList', // ?
                        'Api_User/userBalance',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': false,
                    'percentage': true,
                    'bid': 0.0,
                    'ask': 0.02 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': false,
                    'withdraw': {
                        'ckusd': 0.0,
                        'other': 0.05 / 100,
                    },
                    'deposit': {},
                },
            },
            'exceptions': {
                '该币不存在,非法操作': ExchangeError, // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
                '公钥不合法': AuthenticationError, // { code: 1, msg: '公钥不合法' } - wrong public key
                '您的可用余额不足': InsufficientFunds, // { code: 1, msg: '您的可用余额不足' } - your available balance is insufficient
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetApiMarketGetPriceList ();
        let result = [];
        let keys = Object.keys (response);
        for (let i = 0; i < keys.length; i++) {
            let currentMarketId = keys[i];
            let currentMarkets = response[currentMarketId];
            for (let j = 0; j < currentMarkets.length; j++) {
                let market = currentMarkets[j];
                let baseId = market['coin_from'];
                let quoteId = market['coin_to'];
                let base = baseId.toUpperCase ();
                let quote = quoteId.toUpperCase ();
                base = this.commonCurrencyCode (base);
                quote = this.commonCurrencyCode (quote);
                let id = baseId + '2' + quoteId;
                let symbol = base + '/' + quote;
                let active = true;
                let precision = {
                    'amount': undefined, // todo: might need this for proper order placement
                    'price': undefined, // todo: find a way to get these values
                };
                let limits = {
                    'amount': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                    'cost': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                };
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'active': active,
                    'precision': precision,
                    'limits': limits,
                    'info': market,
                });
            }
        }
        return result;
    }

    parseTrade (trade, market = undefined) {
        let symbol = undefined;
        if (typeof market !== 'undefined') {
            symbol = market['symbol'];
        }
        let timestamp = this.safeInteger2 (trade, 'date', 'created');
        if (typeof timestamp !== 'undefined') {
            timestamp = timestamp * 1000;
        }
        let id = this.safeString (trade, 'tid');
        let orderId = this.safeString (trade, 'order_id');
        let amount = this.safeFloat2 (trade, 'number', 'amount');
        let price = this.safeFloat (trade, 'price');
        let cost = undefined;
        if (typeof price !== 'undefined') {
            if (typeof amount !== 'undefined') {
                cost = amount * price;
            }
        }
        let side = this.safeString (trade, 'type');
        return {
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'order': orderId,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'symbol': this.marketId (symbol),
        };
        if (typeof limit !== 'undefined') {
            request['limit'] = limit;
        }
        let market = this.market (symbol);
        let response = await this.publicPostApiOrderMarketOrder (this.extend (request, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostApiUserUserBalance (params);
        let data = response['data'];
        let keys = Object.keys (data);
        let result = { };
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i];
            let amount = this.safeFloat (data, key);
            let parts = key.split ('_');
            let currencyId = parts[0];
            let lockOrOver = parts[1];
            let code = currencyId.toUpperCase ();
            if (currencyId in this.currencies_by_id) {
                code = this.currencies_by_id[currencyId]['code'];
            } else {
                code = this.commonCurrencyCode (code);
            }
            if (!(code in result)) {
                let account = this.account ();
                result[code] = account;
            }
            if (lockOrOver === 'lock') {
                result[code]['used'] = parseFloat (amount);
            } else {
                result[code]['free'] = parseFloat (amount);
            }
        }
        keys = Object.keys (result);
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i];
            let total = this.sum (result[key]['used'], result[key]['total']);
            result[key]['total'] = total;
        }
        result['info'] = data;
        return this.parseBalance (result);
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.markets[symbol];
        let request = {
            'part': market['quoteId'],
            'coin': market['baseId'],
        };
        let response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params));
        let timestamp = this.milliseconds ();
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (response, 'max'),
            'low': this.safeFloat (response, 'min'),
            'bid': this.safeFloat (response, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (response, 'sale'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': this.safeFloat (response, 'price'),
            'last': this.safeFloat (response, 'price'),
            'previousClose': undefined,
            'change': undefined,
            'percentage': this.safeFloat (response, 'change_24h'),
            'average': undefined,
            'baseVolume': this.safeFloat (response, 'volume_24h'),
            'quoteVolume': undefined,
            'info': response,
        };
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let marketId = this.marketId (symbol);
        let request = {
            'symbol': marketId,
        };
        let response = await this.publicPostApiOrderDepth (this.extend (request, params));
        let data = response['data'];
        let orderbook = this.parseOrderBook (data, data['date'] * 1000);
        return orderbook;
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'symbol': market['id'],
        };
        let response = await this.privatePostApiOrderOrderList (this.extend (request, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    parseOrderStatus (status) {
        let statuses = {
            '0': 'open',
            '1': 'open', // partially filled
            '2': 'closed',
            '3': 'canceled',
        };
        if (status in statuses) {
            return statuses[status];
        }
        return status;
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'symbol': this.marketId (symbol),
            'trust_id': id,
        };
        let response = await this.privatePostApiOrderOrderInfo (this.extend (request, params));
        let order = response['data'];
        let timestamp = order['created'] * 1000;
        let status = this.parseOrderStatus (order['status']);
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': order['flag'],
            'side': undefined,
            'price': order['price'],
            'cost': undefined,
            'average': undefined,
            'amount': order['number'],
            'filled': order['numberdeal'],
            'remaining': order['numberover'],
            'status': status,
            'fee': undefined,
        };
        return result;
    }

    parseOrder (order, market = undefined) {
        let id = this.safeString (order, 'id');
        let timestamp = order['datetime'] * 1000;
        let iso8601 = this.iso8601 (timestamp);
        let symbol = market['symbol'];
        let type = undefined;
        let side = order['type'];
        let price = order['price'];
        let average = order['avg_price'];
        let amount = order['amount'];
        let remaining = order['amount_outstanding'];
        let filled = amount - remaining;
        let status = this.safeString (order, 'status');
        status = this.parseOrderStatus (status);
        let cost = filled * price;
        let fee = undefined;
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': iso8601,
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
        };
        return result;
    }

    async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'type': type,
        };
        let market = undefined;
        if (typeof symbol !== 'undefined') {
            market = this.market (symbol);
            request['symbol'] = market['id'];
        }
        let response = await this.privatePostApiOrderTradeList (this.extend (request, params));
        if ('data' in response) {
            return this.parseOrders (response['data'], market, since, limit);
        }
        return [];
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrdersByType ('open', symbol, since, limit, params);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrdersByType ('all', symbol, since, limit, params);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let order = {
            'symbol': this.marketId (symbol),
            'type': side,
            'price': price,
            'number': amount,
        };
        let response = await this.privatePostApiOrderCoinTrust (this.extend (order, params));
        let data = response['data'];
        return {
            'info': response,
            'id': this.safeString (data, 'order_id'),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {};
        if (typeof symbol !== 'undefined') {
            request['symbol'] = this.marketId (symbol);
        }
        if (typeof id !== 'undefined') {
            request['order_id'] = id;
        }
        let response = await this.privatePostApiOrderCancel (this.extend (request, params));
        return response;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let payload = this.urlencode ({ 'api_key': this.apiKey });
            if (Object.keys (query).length) {
                payload += '&' + this.urlencode (this.keysort (query));
            }
            let auth = payload + '&secret_key=' + this.secret;
            let signature = this.hash (this.encode (auth));
            body = payload + '&sign=' + signature;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if (typeof body !== 'string')
            return; // fallback to default error handler
        if (body.length < 2)
            return; // fallback to default error handler
        if ((body[0] === '{') || (body[0] === '[')) {
            let response = JSON.parse (body);
            let feedback = this.id + ' ' + body;
            let code = this.safeValue (response, 'code');
            if (typeof code !== 'undefined') {
                if (code !== 0) {
                    //
                    // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
                    // { code: 1, msg: '公钥不合法' } - wrong public key
                    //
                    let message = this.safeString (response, 'msg');
                    let exceptions = this.exceptions;
                    if (message in exceptions) {
                        throw new exceptions[message] (feedback);
                    } else {
                        throw new ExchangeError (feedback);
                    }
                }
            }
        }
    }
};
<commit_msg>Correct fee structure<commit_after>'use strict';

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

const Exchange = require ('./base/Exchange');
const { ExchangeError, AuthenticationError, InsufficientFunds } = require ('./base/errors');

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

module.exports = class bcex extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bcex',
            'name': 'BCEX',
            'countries': [ 'CN', 'CA' ],
            'version': '1',
            'has': {
                'fetchBalance': true,
                'fetchMarkets': true,
                'createOrder': true,
                'cancelOrder': true,
                'fetchTicker': true,
                'fetchTickers': false,
                'fetchTrades': true,
                'fetchOrder': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/43362240-21c26622-92ee-11e8-9464-5801ec526d77.jpg',
                'api': 'https://www.bcex.top',
                'www': 'https://www.bcex.top',
                'doc': 'https://www.bcex.top/api_market/market/',
                'fees': 'http://bcex.udesk.cn/hc/articles/57085',
                'referral': 'https://www.bcex.top/user/reg/type/2/pid/758978',
            },
            'api': {
                'public': {
                    'get': [
                        'Api_Market/getPriceList', // tickers
                        'Api_Order/ticker', // last ohlcv candle (ticker)
                        'Api_Order/depth', // orderbook
                        'Api_Market/getCoinTrade', // ticker
                        'Api_Order/marketOrder', // trades...
                    ],
                    'post': [
                        'Api_Market/getPriceList', // tickers
                        'Api_Order/ticker', // last ohlcv candle (ticker)
                        'Api_Order/depth', // orderbook
                        'Api_Market/getCoinTrade', // ticker
                        'Api_Order/marketOrder', // trades...
                    ],
                },
                'private': {
                    'post': [
                        'Api_Order/cancel',
                        'Api_Order/coinTrust', // limit order
                        'Api_Order/orderList', // open / all orders (my trades?)
                        'Api_Order/orderInfo',
                        'Api_Order/tradeList', // open / all orders
                        'Api_Order/trustList', // ?
                        'Api_User/userBalance',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': false,
                    'percentage': true,
                    'maker': 0.0,
                    'taker': 0.2 / 100,
                },
                'funding': {
                    'tierBased': false,
                    'percentage': false,
                    'withdraw': {
                        'ckusd': 0.0,
                        'other': 0.05 / 100,
                    },
                    'deposit': {},
                },
            },
            'exceptions': {
                '该币不存在,非法操作': ExchangeError, // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
                '公钥不合法': AuthenticationError, // { code: 1, msg: '公钥不合法' } - wrong public key
                '您的可用余额不足': InsufficientFunds, // { code: 1, msg: '您的可用余额不足' } - your available balance is insufficient
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetApiMarketGetPriceList ();
        let result = [];
        let keys = Object.keys (response);
        for (let i = 0; i < keys.length; i++) {
            let currentMarketId = keys[i];
            let currentMarkets = response[currentMarketId];
            for (let j = 0; j < currentMarkets.length; j++) {
                let market = currentMarkets[j];
                let baseId = market['coin_from'];
                let quoteId = market['coin_to'];
                let base = baseId.toUpperCase ();
                let quote = quoteId.toUpperCase ();
                base = this.commonCurrencyCode (base);
                quote = this.commonCurrencyCode (quote);
                let id = baseId + '2' + quoteId;
                let symbol = base + '/' + quote;
                let active = true;
                let precision = {
                    'amount': undefined, // todo: might need this for proper order placement
                    'price': undefined, // todo: find a way to get these values
                };
                let limits = {
                    'amount': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                    'cost': {
                        'min': undefined, // todo
                        'max': undefined,
                    },
                };
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'active': active,
                    'precision': precision,
                    'limits': limits,
                    'info': market,
                });
            }
        }
        return result;
    }

    parseTrade (trade, market = undefined) {
        let symbol = undefined;
        if (typeof market !== 'undefined') {
            symbol = market['symbol'];
        }
        let timestamp = this.safeInteger2 (trade, 'date', 'created');
        if (typeof timestamp !== 'undefined') {
            timestamp = timestamp * 1000;
        }
        let id = this.safeString (trade, 'tid');
        let orderId = this.safeString (trade, 'order_id');
        let amount = this.safeFloat2 (trade, 'number', 'amount');
        let price = this.safeFloat (trade, 'price');
        let cost = undefined;
        if (typeof price !== 'undefined') {
            if (typeof amount !== 'undefined') {
                cost = amount * price;
            }
        }
        let side = this.safeString (trade, 'type');
        return {
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'order': orderId,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'symbol': this.marketId (symbol),
        };
        if (typeof limit !== 'undefined') {
            request['limit'] = limit;
        }
        let market = this.market (symbol);
        let response = await this.publicPostApiOrderMarketOrder (this.extend (request, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostApiUserUserBalance (params);
        let data = response['data'];
        let keys = Object.keys (data);
        let result = { };
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i];
            let amount = this.safeFloat (data, key);
            let parts = key.split ('_');
            let currencyId = parts[0];
            let lockOrOver = parts[1];
            let code = currencyId.toUpperCase ();
            if (currencyId in this.currencies_by_id) {
                code = this.currencies_by_id[currencyId]['code'];
            } else {
                code = this.commonCurrencyCode (code);
            }
            if (!(code in result)) {
                let account = this.account ();
                result[code] = account;
            }
            if (lockOrOver === 'lock') {
                result[code]['used'] = parseFloat (amount);
            } else {
                result[code]['free'] = parseFloat (amount);
            }
        }
        keys = Object.keys (result);
        for (let i = 0; i < keys.length; i++) {
            let key = keys[i];
            let total = this.sum (result[key]['used'], result[key]['total']);
            result[key]['total'] = total;
        }
        result['info'] = data;
        return this.parseBalance (result);
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.markets[symbol];
        let request = {
            'part': market['quoteId'],
            'coin': market['baseId'],
        };
        let response = await this.publicPostApiMarketGetCoinTrade (this.extend (request, params));
        let timestamp = this.milliseconds ();
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (response, 'max'),
            'low': this.safeFloat (response, 'min'),
            'bid': this.safeFloat (response, 'buy'),
            'bidVolume': undefined,
            'ask': this.safeFloat (response, 'sale'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': this.safeFloat (response, 'price'),
            'last': this.safeFloat (response, 'price'),
            'previousClose': undefined,
            'change': undefined,
            'percentage': this.safeFloat (response, 'change_24h'),
            'average': undefined,
            'baseVolume': this.safeFloat (response, 'volume_24h'),
            'quoteVolume': undefined,
            'info': response,
        };
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let marketId = this.marketId (symbol);
        let request = {
            'symbol': marketId,
        };
        let response = await this.publicPostApiOrderDepth (this.extend (request, params));
        let data = response['data'];
        let orderbook = this.parseOrderBook (data, data['date'] * 1000);
        return orderbook;
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'symbol': market['id'],
        };
        let response = await this.privatePostApiOrderOrderList (this.extend (request, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    parseOrderStatus (status) {
        let statuses = {
            '0': 'open',
            '1': 'open', // partially filled
            '2': 'closed',
            '3': 'canceled',
        };
        if (status in statuses) {
            return statuses[status];
        }
        return status;
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'symbol': this.marketId (symbol),
            'trust_id': id,
        };
        let response = await this.privatePostApiOrderOrderInfo (this.extend (request, params));
        let order = response['data'];
        let timestamp = order['created'] * 1000;
        let status = this.parseOrderStatus (order['status']);
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': order['flag'],
            'side': undefined,
            'price': order['price'],
            'cost': undefined,
            'average': undefined,
            'amount': order['number'],
            'filled': order['numberdeal'],
            'remaining': order['numberover'],
            'status': status,
            'fee': undefined,
        };
        return result;
    }

    parseOrder (order, market = undefined) {
        let id = this.safeString (order, 'id');
        let timestamp = order['datetime'] * 1000;
        let iso8601 = this.iso8601 (timestamp);
        let symbol = market['symbol'];
        let type = undefined;
        let side = order['type'];
        let price = order['price'];
        let average = order['avg_price'];
        let amount = order['amount'];
        let remaining = order['amount_outstanding'];
        let filled = amount - remaining;
        let status = this.safeString (order, 'status');
        status = this.parseOrderStatus (status);
        let cost = filled * price;
        let fee = undefined;
        let result = {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': iso8601,
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
        };
        return result;
    }

    async fetchOrdersByType (type, symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {
            'type': type,
        };
        let market = undefined;
        if (typeof symbol !== 'undefined') {
            market = this.market (symbol);
            request['symbol'] = market['id'];
        }
        let response = await this.privatePostApiOrderTradeList (this.extend (request, params));
        if ('data' in response) {
            return this.parseOrders (response['data'], market, since, limit);
        }
        return [];
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrdersByType ('open', symbol, since, limit, params);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return this.fetchOrdersByType ('all', symbol, since, limit, params);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let order = {
            'symbol': this.marketId (symbol),
            'type': side,
            'price': price,
            'number': amount,
        };
        let response = await this.privatePostApiOrderCoinTrust (this.extend (order, params));
        let data = response['data'];
        return {
            'info': response,
            'id': this.safeString (data, 'order_id'),
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {};
        if (typeof symbol !== 'undefined') {
            request['symbol'] = this.marketId (symbol);
        }
        if (typeof id !== 'undefined') {
            request['order_id'] = id;
        }
        let response = await this.privatePostApiOrderCancel (this.extend (request, params));
        return response;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            let payload = this.urlencode ({ 'api_key': this.apiKey });
            if (Object.keys (query).length) {
                payload += '&' + this.urlencode (this.keysort (query));
            }
            let auth = payload + '&secret_key=' + this.secret;
            let signature = this.hash (this.encode (auth));
            body = payload + '&sign=' + signature;
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    handleErrors (code, reason, url, method, headers, body) {
        if (typeof body !== 'string')
            return; // fallback to default error handler
        if (body.length < 2)
            return; // fallback to default error handler
        if ((body[0] === '{') || (body[0] === '[')) {
            let response = JSON.parse (body);
            let feedback = this.id + ' ' + body;
            let code = this.safeValue (response, 'code');
            if (typeof code !== 'undefined') {
                if (code !== 0) {
                    //
                    // { code: 1, msg: "该币不存在,非法操作" } - returned when a required symbol parameter is missing in the request (also, maybe on other types of errors as well)
                    // { code: 1, msg: '公钥不合法' } - wrong public key
                    //
                    let message = this.safeString (response, 'msg');
                    let exceptions = this.exceptions;
                    if (message in exceptions) {
                        throw new exceptions[message] (feedback);
                    } else {
                        throw new ExchangeError (feedback);
                    }
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>const assert = require('assert')
const byline = require('byline')
const Staccato = require('staccato')
const events = require('events')

class Consolidator extends events.EventEmitter {
    constructor (queue) {
        super()
        this._eos = false
        this._series = 0
        this._queue = queue
        this._readable = { destroy: () => {} }
        this._writable = { destroy: () => {} }
    }

    async asynchronous (input, output) {
        this._readable = new Staccato.Readable(byline(input))
        this._writable = new Staccato.Writable(output)
        for await (const line of this._readable) {
            const json = JSON.parse(line.toString())
            assert.equal(json.series, this._series, 'series mismatch')
            this._series = (this._series + 1) & 0xffffff
            this._queue.push(json)
            await this._writable.write(JSON.stringify({
                method: 'receipt',
                series: json.series
            }) + '\n')
        }
    }

    synchronous (json) {
        this._readable.destroy()
        this._writable.destroy()
        if (json == null) {
            this._queue.push(null)
        } else if (json.series == this._series) {
            this._series = (this._series + 1) & 0xffffff
            this._resumed = true
            this._queue.push(json)
        }
    }
}

module.exports = Consolidator
<commit_msg>Removed unused member variable.<commit_after>const assert = require('assert')
const byline = require('byline')
const Staccato = require('staccato')
const events = require('events')

class Consolidator extends events.EventEmitter {
    constructor (queue) {
        super()
        this._eos = false
        this._series = 0
        this._queue = queue
        this._readable = { destroy: () => {} }
        this._writable = { destroy: () => {} }
    }

    async asynchronous (input, output) {
        this._readable = new Staccato.Readable(byline(input))
        this._writable = new Staccato.Writable(output)
        for await (const line of this._readable) {
            const json = JSON.parse(line.toString())
            assert.equal(json.series, this._series, 'series mismatch')
            this._series = (this._series + 1) & 0xffffff
            this._queue.push(json)
            await this._writable.write(JSON.stringify({
                method: 'receipt',
                series: json.series
            }) + '\n')
        }
    }

    synchronous (json) {
        this._readable.destroy()
        this._writable.destroy()
        if (json == null) {
            this._queue.push(null)
        } else if (json.series == this._series) {
            this._series = (this._series + 1) & 0xffffff
            this._queue.push(json)
        }
    }
}

module.exports = Consolidator
<|endoftext|>


<commit_before>/**
 * @file summon command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

exports.exec = async (Bastion, message) => {
  try {
    if (!message.guild.music.enabled) {
      if (Bastion.user.id === '267035345537728512') {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicDisabledPublic'), message.channel);
      }
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicDisabled'), message.channel);
    }

    if (message.guild.music.textChannelID && message.guild.music.textChannelID !== message.channel.id) {
      return Bastion.log.info('Music channels have been set, so music commands will only work in the Music Text Channel.');
    }

    let voiceConnection = message.guild.voiceConnection, voiceChannel;

    if (voiceConnection) {
      if (voiceConnection.speaking) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'isSpeaking'), message.channel);
      }
      voiceChannel = voiceConnection.channel;
    }
    else {
      if (Bastion.credentials.ownerId.includes(message.author.id) || message.member.roles.has(message.guild.music.masterRoleID)) {
        voiceChannel = message.member.voiceChannel;
      }
      else if (message.guild.music.voiceChannelID) {
        voiceChannel = message.guild.channels.filter(c => c.type === 'voice').get(message.guild.music.voiceChannelID);
        if (!voiceChannel) {
          return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'invalidMusicChannel'), message.channel);
        }
      }
      else {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicChannelNotFound'), message.channel);
      }

      if (!voiceChannel) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'userNoVC', message.author.tag), message.channel);
      }
      if (!voiceChannel.joinable) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'noPermission', 'join', voiceChannel.name), message.channel);
      }

      voiceConnection = await voiceChannel.join();
    }

    if (!voiceChannel.speakable) {
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'noPermission', 'speak', `in ${voiceChannel.name}`), message.channel);
    }

    message.guild.me.setMute(false).catch(() => {});
    message.guild.me.setDeaf(true).catch(() => {});

    if (!voiceConnection.speaking) {
      voiceConnection.playFile('./assets/greeting.mp3', {
        passes: (Bastion.config.music && Bastion.config.music.passes) || 1,
        bitrate: 'auto'
      });
    }
  }
  catch (e) {
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [ 'join' ],
  enabled: true
};

exports.help = {
  name: 'summon',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'summon',
  example: []
};
<commit_msg>commands: add handlers for voiceConnection events in summon command<commit_after>/**
 * @file summon command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

exports.exec = async (Bastion, message) => {
  try {
    if (!message.guild.music.enabled) {
      if (Bastion.user.id === '267035345537728512') {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicDisabledPublic'), message.channel);
      }
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicDisabled'), message.channel);
    }

    if (message.guild.music.textChannelID && message.guild.music.textChannelID !== message.channel.id) {
      return Bastion.log.info('Music channels have been set, so music commands will only work in the Music Text Channel.');
    }

    let voiceConnection = message.guild.voiceConnection, voiceChannel;

    if (voiceConnection) {
      if (voiceConnection.speaking) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'isSpeaking'), message.channel);
      }
      voiceChannel = voiceConnection.channel;
    }
    else {
      if (Bastion.credentials.ownerId.includes(message.author.id) || message.member.roles.has(message.guild.music.masterRoleID)) {
        voiceChannel = message.member.voiceChannel;
      }
      else if (message.guild.music.voiceChannelID) {
        voiceChannel = message.guild.channels.filter(c => c.type === 'voice').get(message.guild.music.voiceChannelID);
        if (!voiceChannel) {
          return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'invalidMusicChannel'), message.channel);
        }
      }
      else {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'musicChannelNotFound'), message.channel);
      }

      if (!voiceChannel) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'userNoVC', message.author.tag), message.channel);
      }
      if (!voiceChannel.joinable) {
        return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'noPermission', 'join', voiceChannel.name), message.channel);
      }

      voiceConnection = await voiceChannel.join();
    }

    if (!voiceChannel.speakable) {
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'noPermission', 'speak', `in ${voiceChannel.name}`), message.channel);
    }

    message.guild.me.setMute(false).catch(() => {});
    message.guild.me.setDeaf(true).catch(() => {});

    if (!voiceConnection.speaking) {
      voiceConnection.playFile('./assets/greeting.mp3', {
        passes: (Bastion.config.music && Bastion.config.music.passes) || 1,
        bitrate: 'auto'
      });
    }

    voiceConnection.on('error', Bastion.log.error);
    voiceConnection.on('failed', Bastion.log.error);
  }
  catch (e) {
    Bastion.log.error(e);
  }
};

exports.config = {
  aliases: [ 'join' ],
  enabled: true
};

exports.help = {
  name: 'summon',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'summon',
  example: []
};
<|endoftext|>


<commit_before>var React = require('react');
var marked = require('marked');
var ent = require('ent');
var CodeComponent = React.createFactory(require('./CodeComponent.js'));
var renderer = new marked.Renderer();
var options = {};
var inlineIds = 0;
var keys = 0;
var inlines = {};
var result = [];
var toc = [];

// Converts inline IDs to actual elements
var createBlockContent = function (content) {

  var textWithInlines = content.split(/(\{\{.*?\}\})/);
  content = textWithInlines.map(function (text) {
    var inline = text.match(/\{\{(.*)\}\}/);
    if (inline) {
      return inlines[inline[1]];
    } else {
      return ent.decode(text);
    }
  });
  return content;
};

var getTocPosition = function (toc, level) {
  var currentLevel = toc.children;
  while (true) {
    if (!currentLevel.length || currentLevel[currentLevel.length - 1].level === level) {
      return currentLevel;
    } else {
      currentLevel = currentLevel[currentLevel.length - 1].children;
    }
  }
};

renderer.code = function (code, language) {
  var props = {
    key: keys++,
    language: language,
    code: code
  };

  if (options.code) {
    result.push(React.createElement(options.code, props));
  } else {
    result.push(CodeComponent(props));
  }
};

renderer.blockquote = function (text) {
  result.pop();
  result.push(React.createElement(options.blockquote || 'blockquote', null,
    React.createElement('p', {key: keys++}, createBlockContent(text))
  ));
};

// How does this happen?
renderer.html = function (html) {
  return html;
};

renderer.heading = function (text, level) {
  var type = 'h' + level;
  type = options[type] || type;
  var id = text.replace(/\s/g, '-').toLowerCase();
  var lastToc = toc[toc.length -1];
  if (!lastToc || lastToc.level > level) {
    toc.push({
      id: id,
      title: text,
      level: level,
      children: []
    });
  } else {
    var tocPosition = getTocPosition(lastToc, level);
    tocPosition.push({
      id: id,
      title: text,
      level: level,
      children: []
    });
  }
  result.push(React.createElement(type, {
    key: keys++,
    id: id
  },
    createBlockContent(text)));
};

renderer.hr = function () {
  result.push(React.createElement(options.hr || 'hr', {key: keys++}));
};

renderer.list = function (body, ordered) {
  result.push(React.createElement(ordered ? options.ol || 'ol' : options.ul || 'ul', {key: keys++}, createBlockContent(body)));
};

renderer.listitem = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.li || 'li', {key: keys++}, createBlockContent(text));
  return '{{' + id + '}}';
};

renderer.paragraph = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.p || 'p', {key: keys++}, createBlockContent(text));
  result.push(inlines[id]);
  return '{{' + id + '}}';
};

renderer.table = function (header, body) {
  result.push(React.createElement(options.table || 'table', {key: keys++},
    createBlockContent(header),
    createBlockContent(body)
  ));
};

renderer.tablerow = function (content) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.tr || 'tr', {key: keys++}, createBlockContent(content));
  return '{{' + id + '}}';
};

renderer.tablecell = function (content, flags) {
  var id = inlineIds++;
  var props =  flags.align ? {className: 'text-' + flags.align} : {key: keys++};
  inlines[id] = React.createElement(flags.header ? options.th || 'th' : options.td || 'td', props, createBlockContent(content));
  return '{{' + id + '}}';
};

renderer.link = function (href, title, text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.a || 'a', {
    href: href,
    title: title,
    key: keys++,
    target: 'new'
  }, createBlockContent(text));
  return '{{' + id + '}}';
};

renderer.strong = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.strong || 'strong', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.em = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.em || 'em', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.codespan = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement('code', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.br = function () {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.br || 'br', {key: keys++});
  return '{{' + id + '}}';
};

renderer.del = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.del || 'del', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.image = function (href, title, text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.img || 'img', {src: href, alt: title, key: keys++});
  return '{{' + id + '}}';
};

var exec = function (content) {
  result = [];
  toc = [];
  inlines = {};
  keys = 0;
  marked(content, {renderer: renderer, smartypants: true});
  return {
    tree: result,
    toc: toc
  };
};

exec.configure = function (newOptions) {
  options = newOptions;
};

module.exports = exec;
<commit_msg> #8 remove unnecessary nested p in blockquote<commit_after>var React = require('react');
var marked = require('marked');
var ent = require('ent');
var CodeComponent = React.createFactory(require('./CodeComponent.js'));
var renderer = new marked.Renderer();
var options = {};
var inlineIds = 0;
var keys = 0;
var inlines = {};
var result = [];
var toc = [];

// Converts inline IDs to actual elements
var createBlockContent = function (content) {

  var textWithInlines = content.split(/(\{\{.*?\}\})/);
  content = textWithInlines.map(function (text) {
    var inline = text.match(/\{\{(.*)\}\}/);
    if (inline) {
      return inlines[inline[1]];
    } else {
      return ent.decode(text);
    }
  });
  return content;
};

var getTocPosition = function (toc, level) {
  var currentLevel = toc.children;
  while (true) {
    if (!currentLevel.length || currentLevel[currentLevel.length - 1].level === level) {
      return currentLevel;
    } else {
      currentLevel = currentLevel[currentLevel.length - 1].children;
    }
  }
};

renderer.code = function (code, language) {
  var props = {
    key: keys++,
    language: language,
    code: code
  };

  if (options.code) {
    result.push(React.createElement(options.code, props));
  } else {
    result.push(CodeComponent(props));
  }
};

renderer.blockquote = function (text) {
  result.pop();
  result.push(React.createElement(options.blockquote || 'blockquote', {key: keys++}, createBlockContent(text)));
};

// How does this happen?
renderer.html = function (html) {
  return html;
};

renderer.heading = function (text, level) {
  var type = 'h' + level;
  type = options[type] || type;
  var id = text.replace(/\s/g, '-').toLowerCase();
  var lastToc = toc[toc.length -1];
  if (!lastToc || lastToc.level > level) {
    toc.push({
      id: id,
      title: text,
      level: level,
      children: []
    });
  } else {
    var tocPosition = getTocPosition(lastToc, level);
    tocPosition.push({
      id: id,
      title: text,
      level: level,
      children: []
    });
  }
  result.push(React.createElement(type, {
    key: keys++,
    id: id
  },
    createBlockContent(text)));
};

renderer.hr = function () {
  result.push(React.createElement(options.hr || 'hr', {key: keys++}));
};

renderer.list = function (body, ordered) {
  result.push(React.createElement(ordered ? options.ol || 'ol' : options.ul || 'ul', {key: keys++}, createBlockContent(body)));
};

renderer.listitem = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.li || 'li', {key: keys++}, createBlockContent(text));
  return '{{' + id + '}}';
};

renderer.paragraph = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.p || 'p', {key: keys++}, createBlockContent(text));
  result.push(inlines[id]);
  return '{{' + id + '}}';
};

renderer.table = function (header, body) {
  result.push(React.createElement(options.table || 'table', {key: keys++},
    createBlockContent(header),
    createBlockContent(body)
  ));
};

renderer.tablerow = function (content) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.tr || 'tr', {key: keys++}, createBlockContent(content));
  return '{{' + id + '}}';
};

renderer.tablecell = function (content, flags) {
  var id = inlineIds++;
  var props =  flags.align ? {className: 'text-' + flags.align} : {key: keys++};
  inlines[id] = React.createElement(flags.header ? options.th || 'th' : options.td || 'td', props, createBlockContent(content));
  return '{{' + id + '}}';
};

renderer.link = function (href, title, text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.a || 'a', {
    href: href,
    title: title,
    key: keys++,
    target: 'new'
  }, createBlockContent(text));
  return '{{' + id + '}}';
};

renderer.strong = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.strong || 'strong', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.em = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.em || 'em', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.codespan = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement('code', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.br = function () {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.br || 'br', {key: keys++});
  return '{{' + id + '}}';
};

renderer.del = function (text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.del || 'del', {key: keys++}, ent.decode(text));
  return '{{' + id + '}}';
};

renderer.image = function (href, title, text) {
  var id = inlineIds++;
  inlines[id] = React.createElement(options.img || 'img', {src: href, alt: title, key: keys++});
  return '{{' + id + '}}';
};

var exec = function (content) {
  result = [];
  toc = [];
  inlines = {};
  keys = 0;
  marked(content, {renderer: renderer, smartypants: true});
  return {
    tree: result,
    toc: toc
  };
};

exec.configure = function (newOptions) {
  options = newOptions;
};

module.exports = exec;
<|endoftext|>