<commit_before>/*

### withList

Paginated items container

Options:

  - queryName: an arbitrary name for the query
  - collection: the collection to fetch the documents from
  - fragment: the fragment that defines which properties to fetch
  - fragmentName: the name of the fragment, passed to getFragment
  - limit: the number of documents to show initially
  - pollInterval: how often the data should be updated, in ms (set to 0 to disable polling)
  - terms: an object that defines which documents to fetch

Props Received:

  - terms: an object that defines which documents to fetch

Terms object can have the following properties:

  - view: String
  - userId: String
  - cat: String
  - date: String
  - after: String
  - before: String
  - enableCache: Boolean
  - listId: String
  - query: String # search query
  - postId: String
  - limit: String

*/

import React, { PropTypes, Component } from 'react';
import { withApollo, graphql } from 'react-apollo';
import gql from 'graphql-tag';
import update from 'immutability-helper';
import { getFragment, getFragmentName } from 'meteor/vulcan:core';
import Mingo from 'mingo';
import compose from 'recompose/compose';
import withState from 'recompose/withState';

const withList = (options) => {

  const { collection, limit = 10, pollInterval = 20000 } = options,
        queryName = options.queryName || `${collection.options.collectionName}ListQuery`,
        fragment = options.fragment || getFragment(options.fragmentName),
        fragmentName = getFragmentName(fragment),
        listResolverName = collection.options.resolvers.list && collection.options.resolvers.list.name,
        totalResolverName = collection.options.resolvers.total && collection.options.resolvers.total.name;

  // build graphql query from options
  const query = gql`
    query ${queryName}($terms: JSON) {
      ${totalResolverName}(terms: $terms)
      ${listResolverName}(terms: $terms) {
        __typename
        ...${fragmentName}
      }
    }
    ${fragment}
  `;

  return compose(

    // wrap component with Apollo HoC to give it access to the store
    withApollo,

    // wrap component with HoC that manages the terms object via its state
    withState('paginationTerms', 'setPaginationTerms', props => {

      // get initial limit from props, or else options
      const paginationLimit = props.terms && props.terms.limit || limit;
      const paginationTerms = {
        limit: paginationLimit,
        itemsPerPage: paginationLimit,
      };

      return paginationTerms;
    }),

    // wrap component with graphql HoC
    graphql(

      query,

      {
        alias: 'withList',

        // graphql query options
        options({terms, paginationTerms, client: apolloClient}) {
          // get terms from options, then props, then pagination
          const mergedTerms = {...options.terms, ...terms, ...paginationTerms};
          return {
            variables: {
              terms: mergedTerms,
            },
            // note: pollInterval can be set to 0 to disable polling (20s by default)
            pollInterval,
            reducer: (previousResults, action) => {

              // see queryReducer function defined below
              return queryReducer(previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient);

            },
          };
        },

        // define props returned by graphql HoC
        props(props) {

          const refetch = props.data.refetch,
                // results = Utils.convertDates(collection, props.data[listResolverName]),
                results = props.data[listResolverName],
                totalCount = props.data[totalResolverName],
                networkStatus = props.data.networkStatus,
                loading = props.data.loading,
                error = props.data.error;

          if (error) {
            console.log(error);
          }

          return {
            // see https://github.com/apollostack/apollo-client/blob/master/src/queries/store.ts#L28-L36
            // note: loading will propably change soon https://github.com/apollostack/apollo-client/issues/831
            loading: networkStatus === 1,
            results,
            totalCount,
            refetch,
            networkStatus,
            error,
            count: results && results.length,

            // regular load more (reload everything)
            loadMore(providedTerms) {
              // if new terms are provided by presentational component use them, else default to incrementing current limit once
              const newTerms = typeof providedTerms === 'undefined' ? { /*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms, limit: results.length + props.ownProps.paginationTerms.itemsPerPage } : providedTerms;

              props.ownProps.setPaginationTerms(newTerms);
            },

            // incremental loading version (only load new content)
            // note: not compatible with polling
            loadMoreInc(providedTerms) {

              // get terms passed as argument or else just default to incrementing the offset
              const newTerms = typeof providedTerms === 'undefined' ? { ...props.ownProps.terms, ...props.ownProps.paginationTerms, offset: results.length } : providedTerms;

              return props.data.fetchMore({
                variables: { terms: newTerms }, // ??? not sure about 'terms: newTerms'
                updateQuery(previousResults, { fetchMoreResult }) {
                  // no more post to fetch
                  if (!fetchMoreResult.data) {
                    return previousResults;
                  }
                  const newResults = {};
                  newResults[listResolverName] = [...previousResults[listResolverName], ...fetchMoreResult.data[listResolverName]];
                  // return the previous results "augmented" with more
                  return {...previousResults, ...newResults};
                },
              });
            },

            fragmentName,
            fragment,
            ...props.ownProps // pass on the props down to the wrapped component
          };
        },
      }
    )
  );
}

// define query reducer separately
const queryReducer = (previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient) => {

  // if collection has no mutations defined, just return previous results
  if (!collection.options.mutations) {
    return previousResults;
  }

  const newMutationName = collection.options.mutations.new && collection.options.mutations.new.name;
  const editMutationName = collection.options.mutations.edit && collection.options.mutations.edit.name;
  const removeMutationName = collection.options.mutations.remove && collection.options.mutations.remove.name;

  let newResults = previousResults;

  // get mongo selector and options objects based on current terms
  const { selector, options } = collection.getParameters(mergedTerms, apolloClient);
  const mingoQuery = Mingo.Query(selector);

  // function to remove a document from a results object, used by edit and remove cases below
  const removeFromResults = (results, document) => {
    const listWithoutDocument = results[listResolverName].filter(doc => doc._id !== document._id);
    const newResults = update(results, {
      [listResolverName]: { $set: listWithoutDocument }, // ex: postsList
      [totalResolverName]: { $set: results[totalResolverName] - 1 } // ex: postsListTotal
    });
    return newResults;
  }

  // add document to a results object
  const addToResults = (results, document) => {

    return update(results, {
      [listResolverName]: { $unshift: [document] },
      [totalResolverName]: { $set: results[totalResolverName] + 1 }
    });
  }

  // reorder results according to a sort
  const reorderResults = (results, sort) => {
    const list = results[listResolverName];
    // const convertedList = Utils.convertDates(collection, list); // convert date strings to date objects
    const convertedList = list;
    const cursor = mingoQuery.find(convertedList);
    const sortedList = cursor.sort(sort).all();
    results[listResolverName] = sortedList;
    return results;
  }

  // console.log('// withList reducer');
  // console.log('queryName: ', queryName);
  // console.log('terms: ', mergedTerms);
  // console.log('selector: ', selector);
  // console.log('options: ', options);
  // console.log('previousResults: ', previousResults);
  // console.log('previous titles: ', _.pluck(previousResults[listResolverName], 'title'))
  // console.log('action: ', action);

  switch (action.operationName) {

    case newMutationName:
      // if new document belongs to current list (based on view selector), add it
      const newDocument = action.result.data[newMutationName];
      if (mingoQuery.test(newDocument)) {
        newResults = addToResults(previousResults, newDocument);
        newResults = reorderResults(newResults, options.sort);
      }
      // console.log('** new **')
      // console.log('newDocument: ', newDocument)
      // console.log('belongs to list: ', mingoQuery.test(newDocument))
      break;

    case editMutationName:
      const editedDocument = action.result.data[editMutationName];
      if (mingoQuery.test(editedDocument)) {
        // edited document belongs to the list
        if (!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id})) {
          // if document wasn't already in list, add it
          newResults = addToResults(previousResults, editedDocument);
        }
        newResults = reorderResults(newResults, options.sort);
      } else {
        // if edited doesn't belong to current list anymore (based on view selector), remove it
        newResults = removeFromResults(previousResults, editedDocument);
      }
      // console.log('** edit **')
      // console.log('editedDocument: ', editedDocument)
      // console.log('belongs to list: ', mingoQuery.test(editedDocument))
      // console.log('exists in list: ', !!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id}))
      break;

    case removeMutationName:
      const removedDocument = action.result.data[removeMutationName];
      newResults = removeFromResults(previousResults, removedDocument);
      // console.log('** remove **')
      // console.log('removedDocument: ', removedDocument)
      break;

    case 'vote':
      // console.log('** vote **')
      // reorder results in case vote changed the order
      newResults = reorderResults(newResults, options.sort);
      break;

    default:
      // console.log('** no action **')
      return previousResults;
  }

  // console.log('newResults: ', newResults)
  // console.log('new titles: ', _.pluck(newResults[listResolverName], 'title'))
  // console.log('\n\n')

  // copy over arrays explicitely to ensure new sort is taken into account
  return {
    [listResolverName]: [...newResults[listResolverName]],
    [totalResolverName]: newResults[totalResolverName],
  }

}

export default withList;
<commit_msg>add totalResolver option to withList to enable/disable total resolver (enabled by default)<commit_after>/*

### withList

Paginated items container

Options:

  - queryName: an arbitrary name for the query
  - collection: the collection to fetch the documents from
  - fragment: the fragment that defines which properties to fetch
  - fragmentName: the name of the fragment, passed to getFragment
  - limit: the number of documents to show initially
  - pollInterval: how often the data should be updated, in ms (set to 0 to disable polling)
  - terms: an object that defines which documents to fetch

Props Received:

  - terms: an object that defines which documents to fetch

Terms object can have the following properties:

  - view: String
  - userId: String
  - cat: String
  - date: String
  - after: String
  - before: String
  - enableCache: Boolean
  - listId: String
  - query: String # search query
  - postId: String
  - limit: String

*/

import React, { PropTypes, Component } from 'react';
import { withApollo, graphql } from 'react-apollo';
import gql from 'graphql-tag';
import update from 'immutability-helper';
import { getFragment, getFragmentName } from 'meteor/vulcan:core';
import Mingo from 'mingo';
import compose from 'recompose/compose';
import withState from 'recompose/withState';

const withList = (options) => {

  const { collection, limit = 10, pollInterval = 20000, totalResolver = true } = options,
        queryName = options.queryName || `${collection.options.collectionName}ListQuery`,
        fragment = options.fragment || getFragment(options.fragmentName),
        fragmentName = getFragmentName(fragment),
        listResolverName = collection.options.resolvers.list && collection.options.resolvers.list.name,
        totalResolverName = collection.options.resolvers.total && collection.options.resolvers.total.name;

  // build graphql query from options
  const query = gql`
    query ${queryName}($terms: JSON) {
      ${totalResolver ? `${totalResolverName}(terms: $terms)` : ``}
      ${listResolverName}(terms: $terms) {
        __typename
        ...${fragmentName}
      }
    }
    ${fragment}
  `;

  return compose(

    // wrap component with Apollo HoC to give it access to the store
    withApollo,

    // wrap component with HoC that manages the terms object via its state
    withState('paginationTerms', 'setPaginationTerms', props => {

      // get initial limit from props, or else options
      const paginationLimit = props.terms && props.terms.limit || limit;
      const paginationTerms = {
        limit: paginationLimit,
        itemsPerPage: paginationLimit,
      };

      return paginationTerms;
    }),

    // wrap component with graphql HoC
    graphql(

      query,

      {
        alias: 'withList',

        // graphql query options
        options({terms, paginationTerms, client: apolloClient}) {
          // get terms from options, then props, then pagination
          const mergedTerms = {...options.terms, ...terms, ...paginationTerms};
          return {
            variables: {
              terms: mergedTerms,
            },
            // note: pollInterval can be set to 0 to disable polling (20s by default)
            pollInterval,
            reducer: (previousResults, action) => {

              // see queryReducer function defined below
              return queryReducer(previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient);

            },
          };
        },

        // define props returned by graphql HoC
        props(props) {

          const refetch = props.data.refetch,
                // results = Utils.convertDates(collection, props.data[listResolverName]),
                results = props.data[listResolverName],
                totalCount = props.data[totalResolverName],
                networkStatus = props.data.networkStatus,
                loading = props.data.loading,
                error = props.data.error;

          if (error) {
            console.log(error);
          }

          return {
            // see https://github.com/apollostack/apollo-client/blob/master/src/queries/store.ts#L28-L36
            // note: loading will propably change soon https://github.com/apollostack/apollo-client/issues/831
            loading: networkStatus === 1,
            results,
            totalCount,
            refetch,
            networkStatus,
            error,
            count: results && results.length,

            // regular load more (reload everything)
            loadMore(providedTerms) {
              // if new terms are provided by presentational component use them, else default to incrementing current limit once
              const newTerms = typeof providedTerms === 'undefined' ? { /*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms, limit: results.length + props.ownProps.paginationTerms.itemsPerPage } : providedTerms;

              props.ownProps.setPaginationTerms(newTerms);
            },

            // incremental loading version (only load new content)
            // note: not compatible with polling
            loadMoreInc(providedTerms) {

              // get terms passed as argument or else just default to incrementing the offset
              const newTerms = typeof providedTerms === 'undefined' ? { ...props.ownProps.terms, ...props.ownProps.paginationTerms, offset: results.length } : providedTerms;

              return props.data.fetchMore({
                variables: { terms: newTerms }, // ??? not sure about 'terms: newTerms'
                updateQuery(previousResults, { fetchMoreResult }) {
                  // no more post to fetch
                  if (!fetchMoreResult.data) {
                    return previousResults;
                  }
                  const newResults = {};
                  newResults[listResolverName] = [...previousResults[listResolverName], ...fetchMoreResult.data[listResolverName]];
                  // return the previous results "augmented" with more
                  return {...previousResults, ...newResults};
                },
              });
            },

            fragmentName,
            fragment,
            ...props.ownProps // pass on the props down to the wrapped component
          };
        },
      }
    )
  );
}

// define query reducer separately
const queryReducer = (previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient) => {

  // if collection has no mutations defined, just return previous results
  if (!collection.options.mutations) {
    return previousResults;
  }

  const newMutationName = collection.options.mutations.new && collection.options.mutations.new.name;
  const editMutationName = collection.options.mutations.edit && collection.options.mutations.edit.name;
  const removeMutationName = collection.options.mutations.remove && collection.options.mutations.remove.name;

  let newResults = previousResults;

  // get mongo selector and options objects based on current terms
  const { selector, options } = collection.getParameters(mergedTerms, apolloClient);
  const mingoQuery = Mingo.Query(selector);

  // function to remove a document from a results object, used by edit and remove cases below
  const removeFromResults = (results, document) => {
    const listWithoutDocument = results[listResolverName].filter(doc => doc._id !== document._id);
    const newResults = update(results, {
      [listResolverName]: { $set: listWithoutDocument }, // ex: postsList
      [totalResolverName]: { $set: results[totalResolverName] - 1 } // ex: postsListTotal
    });
    return newResults;
  }

  // add document to a results object
  const addToResults = (results, document) => {

    return update(results, {
      [listResolverName]: { $unshift: [document] },
      [totalResolverName]: { $set: results[totalResolverName] + 1 }
    });
  }

  // reorder results according to a sort
  const reorderResults = (results, sort) => {
    const list = results[listResolverName];
    // const convertedList = Utils.convertDates(collection, list); // convert date strings to date objects
    const convertedList = list;
    const cursor = mingoQuery.find(convertedList);
    const sortedList = cursor.sort(sort).all();
    results[listResolverName] = sortedList;
    return results;
  }

  // console.log('// withList reducer');
  // console.log('queryName: ', queryName);
  // console.log('terms: ', mergedTerms);
  // console.log('selector: ', selector);
  // console.log('options: ', options);
  // console.log('previousResults: ', previousResults);
  // console.log('previous titles: ', _.pluck(previousResults[listResolverName], 'title'))
  // console.log('action: ', action);

  switch (action.operationName) {

    case newMutationName:
      // if new document belongs to current list (based on view selector), add it
      const newDocument = action.result.data[newMutationName];
      if (mingoQuery.test(newDocument)) {
        newResults = addToResults(previousResults, newDocument);
        newResults = reorderResults(newResults, options.sort);
      }
      // console.log('** new **')
      // console.log('newDocument: ', newDocument)
      // console.log('belongs to list: ', mingoQuery.test(newDocument))
      break;

    case editMutationName:
      const editedDocument = action.result.data[editMutationName];
      if (mingoQuery.test(editedDocument)) {
        // edited document belongs to the list
        if (!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id})) {
          // if document wasn't already in list, add it
          newResults = addToResults(previousResults, editedDocument);
        }
        newResults = reorderResults(newResults, options.sort);
      } else {
        // if edited doesn't belong to current list anymore (based on view selector), remove it
        newResults = removeFromResults(previousResults, editedDocument);
      }
      // console.log('** edit **')
      // console.log('editedDocument: ', editedDocument)
      // console.log('belongs to list: ', mingoQuery.test(editedDocument))
      // console.log('exists in list: ', !!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id}))
      break;

    case removeMutationName:
      const removedDocument = action.result.data[removeMutationName];
      newResults = removeFromResults(previousResults, removedDocument);
      // console.log('** remove **')
      // console.log('removedDocument: ', removedDocument)
      break;

    case 'vote':
      // console.log('** vote **')
      // reorder results in case vote changed the order
      newResults = reorderResults(newResults, options.sort);
      break;

    default:
      // console.log('** no action **')
      return previousResults;
  }

  // console.log('newResults: ', newResults)
  // console.log('new titles: ', _.pluck(newResults[listResolverName], 'title'))
  // console.log('\n\n')

  // copy over arrays explicitely to ensure new sort is taken into account
  return {
    [listResolverName]: [...newResults[listResolverName]],
    [totalResolverName]: newResults[totalResolverName],
  }

}

export default withList;
<|endoftext|>


<commit_before>/** @module supertask-cluster */
/// Required Core Modules
var cluster = require('cluster');
var os = require('os');
///
/// External Modules
var SuperTask = require('supertask');
var shortid = require('shortid');
var async = require('async');
///
/// Internal Modules

///
// No Operation function
function noop() { return null; }

/**
 * Creates new instance.
 * @constructor
 * @example Creating a new instance.
 * var SuperTaskCluster = require('supertask-cluster');
 * var TaskCluster = new SuperTaskCluster();
 *
 * @returns {Instance} Returns a new instance of the module.
 */
var SuperTaskCluster = SuperTask;
var ClusterMap = new Map();

/* AUTHOR'S NOTE *
The cluster's children can be called as
Worker/(child)process/thread/core/etc. depending
on your perspective on the underlying implementation
but for the sake of consistency they will be referred
to as Worker(s) in this module.
*/

SuperTaskCluster.prototype._STC_HEAD_COUNT = function STC_HEAD_COUNT() {
    // Finds the number of alive workers
    var headCount = 0;
    Object.keys(cluster.workers).forEach(function(id) {
        if(!cluster.workers[id].isDead()) {
            headCount++;
        }
    });
    return headCount;
};

SuperTaskCluster.prototype._STC_GET_ALIVE = function STC_GET_ALIVE() {
    var aliveWorkers = {};
    Object.keys(cluster.workers).forEach(function(id) {
        if(!cluster.workers[id].isDead()) {
            aliveWorkers[id] = cluster.workers[id];
        }
    });
    return aliveWorkers;
};

SuperTaskCluster.prototype._STC_HANDLER = function STC_HANDLER() {
    var args = Array.prototype.slice.call(arguments);
    var name = args.shift(), context = args.shift(), callback = args.pop();
    // Run task on a free Worker
    var MIN_LOAD = { v: Infinity, i: 'master' };
    // Find the best worker
    for (var Worker of ClusterMap) {
        if(!Worker[1].load) return;
        /* Prioritizing the last worker
        by using <= we set the priority to the
        last worker.
        */
        if(Worker[1].load.size <= MIN_LOAD.v) {
            MIN_LOAD.v = Worker[1].load.size;
            MIN_LOAD.i = Worker[0];
        }
    }
    if((MIN_LOAD.i === 'master') || (!cluster.workers[MIN_LOAD.i])) {
        // Apply locally
        this.get(name).model.func.apply(context, [callback]);
    }else{
        // Send to Cluster Worker
        var ticket = this._STC_SEND(MIN_LOAD.i, {
            type: "do",
            name: name,
            args: args
        }, function(error, success, response) {
            // Delete Load Ticket
            ClusterMap.get(MIN_LOAD.i).load.delete(ticket);
            if(error || !success) return callback(error || new Error("Unknown error occurred"));
            callback.apply(null, response.args || []);
        });
        ClusterMap.get(MIN_LOAD.i).load.set(ticket, true);
    }
};

SuperTaskCluster.prototype._STC_MESSAGE_HANDLER = function STC_MESSAGE_HANDLER(id, response) {
    if(response.ticket) {
        this.emit('CLUSTER_CALLBACK::' + id + "::" + response.ticket, response);
    }
};

SuperTaskCluster.prototype._STC_BROADCAST = function STC_BROADCAST(message, callback) {
    var _this = this;
    var successfulResponses = 0;
    var errors = [];
    async.each(cluster.workers, function(worker, callback) {
        _this._STC_SEND(worker.id, message, function(error, success) {
            if(error) errors.push(error);
            else if(success) successfulResponses++;
            callback();
        });
    }, function(error){
        callback(error, [successfulResponses, Object.keys(cluster.workers).length], errors);
    });
};

SuperTaskCluster.prototype._STC_SEND = function STC_SEND(id, message, callback) {
    // Check if ID is valid
    if(!cluster.workers[id]) return callback(new Error('Worker with the given ID was not found.'));
    // Create new ticket for message
    message.ticket = shortid.generate();
    cluster.workers[id].send(message);
    // Response timeout
    var STC_SEND_TIMEOUT = setTimeout(STC_SEND_CALLBACK, (this.CLUSTER_TIMEOUT || 30000)*1);
    var STC_SEND_ISDONE = false;
    function STC_SEND_CALLBACK(response) {
        // TimedOut
        if(STC_SEND_ISDONE === true) return;

        STC_SEND_ISDONE = true;
        clearTimeout(STC_SEND_TIMEOUT);

        if(!response) response = {
            error: "Failed to process. TimedOut!",
            success: false
        };

        if(callback) callback((response.error)?(new Error(response.error)):null, response.success, response);
    }
    if(callback) this.on('CLUSTER_CALLBACK::' + id + "::" + message.ticket, STC_SEND_CALLBACK);
    return message.ticket;
};

SuperTaskCluster.prototype._STC_KILL = function STC_KILL(id, callback) {
    cluster.workers[id].kill();
    // Call callback once worker is dead
    if(callback) this.once("CLUSTER_WORKER_DEAD::" + id, function(code, signal){
        callback(null, code, signal);
    });
};

SuperTaskCluster.prototype._STC_GRACEFUL_KILL = function STC_KILL(id, callback) {
    var _this = this;
    this._STC_SEND(id, { type: 'STC_KILL_YOURSELF_PLEASE' }, function(error) {
        // Worker denied to kill itself
        if(error && callback) return callback(error || new Error('Worker did not respect a graceful kill.'));

        _this._STC_KILL(id, callback);
    });
};

/**
 * Deploy/Redeploy workers based on the maximum number of workers. Use {@link SuperTaskCluster#setMaxWorkers} to set total workers.
 *
 * @param {Number} maxTotalWorkers - The maximum number of workers
 * that should be deployed at any given time.
 */
SuperTaskCluster.prototype.deploy = function STC_DEPLOY_CLUSTER(maxTotalWorkers) {
    var _this = this;
    this.STC_MAX_TOTAL_WORKERS = maxTotalWorkers || this.STC_MAX_TOTAL_WORKERS || os.cpus().length;
    var WorkersRequired = Math.max(this.STC_MAX_TOTAL_WORKERS - this._STC_HEAD_COUNT(), 0) || 0;

    // Prevent reseting setupMaster & recreating event listeners
    if(!this.STC_IS_CLUSTER_SETUP) {
        // Setup Master & Worker script
        cluster.setupMaster({
            exec: (this.STC_DEBUG)?'./lib/Worker.debug.js':'./lib/Worker.js',
            args: [],
            silent: (this.STC_DEBUG)?false:true
        });
        this.STC_IS_CLUSTER_SETUP = true;

        // Listen & Create ClusterMap
        cluster.on('fork', function(worker) {
            ClusterMap.set(worker.id, {
                load: new Map()
            });
            worker.on('message', function(response) {
                _this._STC_MESSAGE_HANDLER(worker.id, response);
            });
        });
        // Listen & respawn Workers
        cluster.on('exit', function CLUSTER_EXIT_LISTENER(worker, code, signal) {
            // Emit dead event
            _this.emit('CLUSTER_WORKER_DEAD::' + worker.id, code, signal);
            // Delete Map & inner Map
            if(ClusterMap.get(worker.id).load) ClusterMap.get(worker.id).load.clear();
            ClusterMap.delete(worker.id);
            if(this.STC_DEBUG) console.log('worker #' + worker.id + ' died');
            // Replace dead workers
            // this uses STC_MAX_TOTAL_WORKERS property
            // to prevent deploying extra workers
            _this.deploy();
        });
    }

    // Fork workers.
    for (var i = 0; i < WorkersRequired; i++) {
        // Give the Worker an id
        // Note that NODE_UNIQUE_ID doesn't seem to work with setupMaster
        // as env is not passed down
        cluster.setupMaster({args: [i]});
        cluster.fork();
    }
};

/**
 * Add a new task to SuperTask.
 *
 * @param {String} name - Unique name of the task.
 * @param {(String|Function)} source - Source/Function of the task.
 * @param {AddCallback} callback - The callback that handles the response.
 */
SuperTaskCluster.prototype.addShared = function STC_ADD_SHARED(name, source, callback) {
    // Overrides addShared implementation
    // VM requires a String source to compile
    // If given source is a function convert it to source (context is lost)
    if (typeof source === 'function') {
        source = 'module.exports = ' + source.toString();
    }
    var _this = this;
    this._addTask(name, source, this._STC_HANDLER.bind(this), SuperTask.ST_SHARED_TYPE, function(error, task) {
        if(error) return callback(error);
        // Extend Task Model
        task.distribute = function STC__TASK_MODEL_DISTRIBUTE(callback) {
            // Distribute task across cluster
            _this._STC_BROADCAST({
                type: "task",
                name: task.model.name,
                source: task.model.source,
                isModule: task.model.isModule,
                priority: task.model.priority,
                defaultContext: task.model.defaultContext,
                access: task.model.access
            }, (callback)?callback:noop);
        };
        /**
         * @callback AddCallback
         * @param {Object} task
         */
        callback(null, task);
    });
};

/**
 * Get all alive Workers.
 *
 * @returns {Object} an Object including Worker objects indexed (keyed) by Worker ID.
 */
SuperTaskCluster.prototype.getWorkers = function STC_GET_WORKERS() {
    // Get all alive workers
    return this._STC_GET_ALIVE();
};

/**
 * Get total number of alive Workers.
 *
 * @returns {Number}
 */
SuperTaskCluster.prototype.totalWorkers = function STC_TOTAL_WORKERS() {
    // Returs total number of alive workers
    return this._STC_HEAD_COUNT();
};

/**
 * Set total number of Workers and deploy new Workers if higher than before. Note that this does
 * not reduce the number of workers (kill/terminate) but will add
 * new workers to the cluster if the given argument exceeds
 * previous maximum.
 *
 * @param {Number} maxTotalWorkers - The maximum number of workers
 * that should be deployed at any given time.
 */
SuperTaskCluster.prototype.setMaxWorkers = function STC_SET_MAX_WORKERS(n) {
    // Another interface to deploy
    this.deploy(n);
};

/**
 * Add and deploy new Workers to the Cluster.
 *
 * @param {Number} n - Adds n number of workers to the cluster.
 */
SuperTaskCluster.prototype.addWorkers = function STC_ADD_WORKERS(n) {
    // Add Additional workers
    this.STC_MAX_TOTAL_WORKERS += n;
    this.deploy();
};

/**
 * Forcefully/Gracefully kills a Worker. Note that another worker is immediately
 * forked to replace the killed Worker. In order to reduce the number of workers
 * set setMaxWorkers before calling this function.
 *
 * @param {Number} workerID - ID of the Worker
 * @param {Boolean} graceful=false - Determine if the Worker should be give the
 * chance to finish tasks before killing itself.
 * @param {Function} [callback] - An optional callback to determine when the
 * worker was actually killed. Calls with error, exitCode, signal arguments.
 */
SuperTaskCluster.prototype.killWorker = function STC_KILL_WORKER(workerID, graceful, callback) {
    if(!graceful) {
        this._STC_KILL(workerID, callback);
    }else{
        this._STC_GRACEFUL_KILL(workerID, callback);
    }
};

/**
 * Set the cluster to debug. Note that this uses a separate Worker code
 * therefore currently online Workers will not log debug information.
 *
 * @param {Boolean} toggle - Toggles debug.
 */
SuperTaskCluster.prototype.setClusterDebug = function STC_SET_DEBUG(toggle) {
    this.STC_DEBUG = (!!toggle);
};

module.exports = SuperTaskCluster;<commit_msg>Fixed Blanket bug<commit_after>/** @module supertask-cluster */
/// Required Core Modules
var cluster = require('cluster');
var os = require('os');
///
/// External Modules
var SuperTask = require('supertask');
var shortid = require('shortid');
var async = require('async');
///
/// Internal Modules

///
// No Operation function
function noop() { return null; }

/**
 * Creates new instance.
 * @constructor
 * @example Creating a new instance.
 * var SuperTaskCluster = require('supertask-cluster');
 * var TaskCluster = new SuperTaskCluster();
 *
 * @returns {Instance} Returns a new instance of the module.
 */
var SuperTaskCluster = SuperTask;
var ClusterMap = new Map();

/* AUTHOR'S NOTE *
The cluster's children can be called as
Worker/(child)process/thread/core/etc. depending
on your perspective on the underlying implementation
but for the sake of consistency they will be referred
to as Worker(s) in this module.
*/

SuperTaskCluster.prototype._STC_HEAD_COUNT = function STC_HEAD_COUNT() {
    // Finds the number of alive workers
    var headCount = 0;
    Object.keys(cluster.workers).forEach(function(id) {
        if(!cluster.workers[id].isDead()) {
            headCount++;
        }
    });
    return headCount;
};

SuperTaskCluster.prototype._STC_GET_ALIVE = function STC_GET_ALIVE() {
    var aliveWorkers = {};
    Object.keys(cluster.workers).forEach(function(id) {
        if(!cluster.workers[id].isDead()) {
            aliveWorkers[id] = cluster.workers[id];
        }
    });
    return aliveWorkers;
};

SuperTaskCluster.prototype._STC_HANDLER = function STC_HANDLER() {
    var args = Array.prototype.slice.call(arguments);
    var name = args.shift(), context = args.shift(), callback = args.pop();
    // Run task on a free Worker
    var MIN_LOAD = { v: Infinity, i: 'master' };
    // Find the best worker
    // Intialize Worker (Blanket throws if var is used in the for of loop)
    var Worker = [];
    for (Worker of ClusterMap) {
        if(!Worker[1].load) return;
        /* Prioritizing the last worker
        by using <= we set the priority to the
        last worker.
        */
        if(Worker[1].load.size <= MIN_LOAD.v) {
            MIN_LOAD.v = Worker[1].load.size;
            MIN_LOAD.i = Worker[0];
        }
    }
    if((MIN_LOAD.i === 'master') || (!cluster.workers[MIN_LOAD.i])) {
        // Apply locally
        this.get(name).model.func.apply(context, [callback]);
    }else{
        // Send to Cluster Worker
        var ticket = this._STC_SEND(MIN_LOAD.i, {
            type: "do",
            name: name,
            args: args
        }, function(error, success, response) {
            // Delete Load Ticket
            ClusterMap.get(MIN_LOAD.i).load.delete(ticket);
            if(error || !success) return callback(error || new Error("Unknown error occurred"));
            callback.apply(null, response.args || []);
        });
        ClusterMap.get(MIN_LOAD.i).load.set(ticket, true);
    }
};

SuperTaskCluster.prototype._STC_MESSAGE_HANDLER = function STC_MESSAGE_HANDLER(id, response) {
    if(response.ticket) {
        this.emit('CLUSTER_CALLBACK::' + id + "::" + response.ticket, response);
    }
};

SuperTaskCluster.prototype._STC_BROADCAST = function STC_BROADCAST(message, callback) {
    var _this = this;
    var successfulResponses = 0;
    var errors = [];
    async.each(cluster.workers, function(worker, callback) {
        _this._STC_SEND(worker.id, message, function(error, success) {
            if(error) errors.push(error);
            else if(success) successfulResponses++;
            callback();
        });
    }, function(error){
        callback(error, [successfulResponses, Object.keys(cluster.workers).length], errors);
    });
};

SuperTaskCluster.prototype._STC_SEND = function STC_SEND(id, message, callback) {
    // Check if ID is valid
    if(!cluster.workers[id]) return callback(new Error('Worker with the given ID was not found.'));
    // Create new ticket for message
    message.ticket = shortid.generate();
    cluster.workers[id].send(message);
    // Response timeout
    var STC_SEND_TIMEOUT = setTimeout(STC_SEND_CALLBACK, (this.CLUSTER_TIMEOUT || 30000)*1);
    var STC_SEND_ISDONE = false;
    function STC_SEND_CALLBACK(response) {
        // TimedOut
        if(STC_SEND_ISDONE === true) return;

        STC_SEND_ISDONE = true;
        clearTimeout(STC_SEND_TIMEOUT);

        if(!response) response = {
            error: "Failed to process. TimedOut!",
            success: false
        };

        if(callback) callback((response.error)?(new Error(response.error)):null, response.success, response);
    }
    if(callback) this.on('CLUSTER_CALLBACK::' + id + "::" + message.ticket, STC_SEND_CALLBACK);
    return message.ticket;
};

SuperTaskCluster.prototype._STC_KILL = function STC_KILL(id, callback) {
    cluster.workers[id].kill();
    // Call callback once worker is dead
    if(callback) this.once("CLUSTER_WORKER_DEAD::" + id, function(code, signal){
        callback(null, code, signal);
    });
};

SuperTaskCluster.prototype._STC_GRACEFUL_KILL = function STC_KILL(id, callback) {
    var _this = this;
    this._STC_SEND(id, { type: 'STC_KILL_YOURSELF_PLEASE' }, function(error) {
        // Worker denied to kill itself
        if(error && callback) return callback(error || new Error('Worker did not respect a graceful kill.'));

        _this._STC_KILL(id, callback);
    });
};

/**
 * Deploy/Redeploy workers based on the maximum number of workers. Use {@link SuperTaskCluster#setMaxWorkers} to set total workers.
 *
 * @param {Number} maxTotalWorkers - The maximum number of workers
 * that should be deployed at any given time.
 */
SuperTaskCluster.prototype.deploy = function STC_DEPLOY_CLUSTER(maxTotalWorkers) {
    var _this = this;
    this.STC_MAX_TOTAL_WORKERS = maxTotalWorkers || this.STC_MAX_TOTAL_WORKERS || os.cpus().length;
    var WorkersRequired = Math.max(this.STC_MAX_TOTAL_WORKERS - this._STC_HEAD_COUNT(), 0) || 0;

    // Prevent reseting setupMaster & recreating event listeners
    if(!this.STC_IS_CLUSTER_SETUP) {
        // Setup Master & Worker script
        cluster.setupMaster({
            exec: (this.STC_DEBUG)?'./lib/Worker.debug.js':'./lib/Worker.js',
            args: [],
            silent: (this.STC_DEBUG)?false:true
        });
        this.STC_IS_CLUSTER_SETUP = true;

        // Listen & Create ClusterMap
        cluster.on('fork', function(worker) {
            ClusterMap.set(worker.id, {
                load: new Map()
            });
            worker.on('message', function(response) {
                _this._STC_MESSAGE_HANDLER(worker.id, response);
            });
        });
        // Listen & respawn Workers
        cluster.on('exit', function CLUSTER_EXIT_LISTENER(worker, code, signal) {
            // Emit dead event
            _this.emit('CLUSTER_WORKER_DEAD::' + worker.id, code, signal);
            // Delete Map & inner Map
            if(ClusterMap.get(worker.id).load) ClusterMap.get(worker.id).load.clear();
            ClusterMap.delete(worker.id);
            if(this.STC_DEBUG) console.log('worker #' + worker.id + ' died');
            // Replace dead workers
            // this uses STC_MAX_TOTAL_WORKERS property
            // to prevent deploying extra workers
            _this.deploy();
        });
    }

    // Fork workers.
    for (var i = 0; i < WorkersRequired; i++) {
        // Give the Worker an id
        // Note that NODE_UNIQUE_ID doesn't seem to work with setupMaster
        // as env is not passed down
        cluster.setupMaster({args: [i]});
        cluster.fork();
    }
};

/**
 * Add a new task to SuperTask.
 *
 * @param {String} name - Unique name of the task.
 * @param {(String|Function)} source - Source/Function of the task.
 * @param {AddCallback} callback - The callback that handles the response.
 */
SuperTaskCluster.prototype.addShared = function STC_ADD_SHARED(name, source, callback) {
    // Overrides addShared implementation
    // VM requires a String source to compile
    // If given source is a function convert it to source (context is lost)
    if (typeof source === 'function') {
        source = 'module.exports = ' + source.toString();
    }
    var _this = this;
    this._addTask(name, source, this._STC_HANDLER.bind(this), SuperTask.ST_SHARED_TYPE, function(error, task) {
        if(error) return callback(error);
        // Extend Task Model
        task.distribute = function STC__TASK_MODEL_DISTRIBUTE(callback) {
            // Distribute task across cluster
            _this._STC_BROADCAST({
                type: "task",
                name: task.model.name,
                source: task.model.source,
                isModule: task.model.isModule,
                priority: task.model.priority,
                defaultContext: task.model.defaultContext,
                access: task.model.access
            }, (callback)?callback:noop);
        };
        /**
         * @callback AddCallback
         * @param {Object} task
         */
        callback(null, task);
    });
};

/**
 * Get all alive Workers.
 *
 * @returns {Object} an Object including Worker objects indexed (keyed) by Worker ID.
 */
SuperTaskCluster.prototype.getWorkers = function STC_GET_WORKERS() {
    // Get all alive workers
    return this._STC_GET_ALIVE();
};

/**
 * Get total number of alive Workers.
 *
 * @returns {Number}
 */
SuperTaskCluster.prototype.totalWorkers = function STC_TOTAL_WORKERS() {
    // Returs total number of alive workers
    return this._STC_HEAD_COUNT();
};

/**
 * Set total number of Workers and deploy new Workers if higher than before. Note that this does
 * not reduce the number of workers (kill/terminate) but will add
 * new workers to the cluster if the given argument exceeds
 * previous maximum.
 *
 * @param {Number} maxTotalWorkers - The maximum number of workers
 * that should be deployed at any given time.
 */
SuperTaskCluster.prototype.setMaxWorkers = function STC_SET_MAX_WORKERS(n) {
    // Another interface to deploy
    this.deploy(n);
};

/**
 * Add and deploy new Workers to the Cluster.
 *
 * @param {Number} n - Adds n number of workers to the cluster.
 */
SuperTaskCluster.prototype.addWorkers = function STC_ADD_WORKERS(n) {
    // Add Additional workers
    this.STC_MAX_TOTAL_WORKERS += n;
    this.deploy();
};

/**
 * Forcefully/Gracefully kills a Worker. Note that another worker is immediately
 * forked to replace the killed Worker. In order to reduce the number of workers
 * set setMaxWorkers before calling this function.
 *
 * @param {Number} workerID - ID of the Worker
 * @param {Boolean} graceful=false - Determine if the Worker should be give the
 * chance to finish tasks before killing itself.
 * @param {Function} [callback] - An optional callback to determine when the
 * worker was actually killed. Calls with error, exitCode, signal arguments.
 */
SuperTaskCluster.prototype.killWorker = function STC_KILL_WORKER(workerID, graceful, callback) {
    if(!graceful) {
        this._STC_KILL(workerID, callback);
    }else{
        this._STC_GRACEFUL_KILL(workerID, callback);
    }
};

/**
 * Set the cluster to debug. Note that this uses a separate Worker code
 * therefore currently online Workers will not log debug information.
 *
 * @param {Boolean} toggle - Toggles debug.
 */
SuperTaskCluster.prototype.setClusterDebug = function STC_SET_DEBUG(toggle) {
    this.STC_DEBUG = (!!toggle);
};

module.exports = SuperTaskCluster;<|endoftext|>


<commit_before>import React from 'react';
import { getClassName, handleA11yAction } from '@appbaseio/reactivecore/lib/utils/helper';
import types from '@appbaseio/reactivecore/lib/utils/types';

import Button, { pagination } from '../../../styles/Button';

function getStartPage(totalPages, currentPage, showEndPage) {
	const midValue = parseInt(totalPages / 2, 10);
	const start = currentPage - (showEndPage ? Math.ceil(midValue / 2) - 1 : midValue);
	return start > 1 ? start : 2;
}

const buildPaginationDOM = (props, position) => {
	const {
		pages,
		currentPage,
		setPage,
		totalPages,
		innerClass,
		fragmentName,
		showEndPage,
	} = props;
	let start
		= position === 'start'
			? getStartPage(pages, currentPage, showEndPage)
			: Math.ceil(totalPages - ((pages - 1) / 2)) + 1;
	const paginationButtons = [];
	if (start <= totalPages) {
		let totalPagesToShow = pages < totalPages ? start + (pages - 1) : totalPages + 1;
		if (showEndPage) {
			totalPagesToShow
				= position === 'start'
					? start + (Math.ceil(pages / 2) - (pages % 2))
					: totalPages + 1;
		}
		if (currentPage > (totalPages - pages) + 2) {
			start = (totalPages - pages) + 2;
		}
		if (totalPages <= pages) start = 2;
		for (let i = start; i < totalPagesToShow; i += 1) {
			const primary = currentPage === i - 1;
			const innerClassName = getClassName(innerClass, 'button');
			const className
				= innerClassName || primary ? `${innerClassName} ${primary ? 'active' : ''}` : null;
			const pageBtn = (
				<Button
					className={className}
					primary={primary}
					key={i - 1}
					tabIndex="0"
					onKeyPress={event => handleA11yAction(event, () => setPage(i - 1))}
					onClick={(e) => {
						e.preventDefault();
						setPage(i - 1);
					}}
					alt={`Page ${i}`}
					href={`?${fragmentName}=${i}`}
				>
					{i}
				</Button>
			);
			if (i <= totalPages + 1) {
				paginationButtons.push(pageBtn);
			}
		}
	}
	return paginationButtons;
};

class Pagination extends React.PureComponent {
	render() {
		const {
			pages,
			currentPage,
			setPage,
			totalPages,
			innerClass,
			fragmentName,
			showEndPage,
		} = this.props;
		if (!totalPages) {
			return null;
		}

		const onPrevPage = (e) => {
			e.preventDefault();
			if (currentPage) {
				setPage(currentPage - 1);
			}
		};

		const onNextPage = (e) => {
			e.preventDefault();
			if (currentPage < totalPages - 1) {
				setPage(currentPage + 1);
			}
		};

		const innerClassName = getClassName(innerClass, 'button');
		const primary = currentPage === 0;
		const className
			= innerClassName || primary ? `${innerClassName} ${primary ? 'active' : ''}` : null;

		let prevHrefProp = {};
		let nextHrefProp = {};

		if (currentPage >= 1) {
			prevHrefProp = {
				href: `?${fragmentName}=${currentPage}`,
				alt: `Page ${currentPage}`,
				rel: 'prev',
			};
		}

		if (currentPage < totalPages - 1) {
			nextHrefProp = {
				href: `?${fragmentName}=${currentPage + 2}`,
				rel: 'next',
				alt: `Page ${currentPage + 2}`,
			};
		}
		return (
			<div className={`${pagination} ${getClassName(innerClass, 'pagination')}`}>
				<Button
					className={getClassName(innerClass, 'button') || null}
					disabled={currentPage === 0}
					onKeyPress={event => handleA11yAction(event, onPrevPage)}
					onClick={onPrevPage}
					tabIndex={currentPage === 0 ? '-1' : '0'}
					{...prevHrefProp}
				>
					Prev
				</Button>
				{
					<Button
						className={className}
						primary={primary}
						onKeyPress={event => handleA11yAction(event, () => setPage(0))}
						onClick={(e) => {
							e.preventDefault();
							setPage(0);
						}}
						tabIndex="0"
						href={`?${fragmentName}=1`}
						alt="Page 1"
					>
						1
					</Button>
				}
				{showEndPage && currentPage >= Math.floor(pages / 2) + !!(pages % 2) ? (
					...
				) : null}
				{(currentPage <= (totalPages - pages) + 2 || totalPages <= pages) && buildPaginationDOM(this.props, 'start')}
				{showEndPage && pages > 2 && currentPage <= totalPages - Math.ceil(pages * 0.75) ? (
					...
				) : null}
				{showEndPage && buildPaginationDOM(this.props, 'end')}
				<Button
					className={getClassName(innerClass, 'button') || null}
					disabled={currentPage >= totalPages - 1}
					onKeyPress={event => handleA11yAction(event, onNextPage)}
					onClick={onNextPage}
					tabIndex={currentPage >= totalPages - 1 ? '-1' : '0'}
					{...nextHrefProp}
				>
					Next
				</Button>

		);
	}
}

Pagination.propTypes = {
	currentPage: types.number,
	innerClass: types.style,
	pages: types.number,
	setPage: types.func,
	totalPages: types.number,
	fragmentName: types.string,
	showEndPage: types.bool,
};

export default Pagination;
<commit_msg>fix(web): edge cases for pagination (#1258)<commit_after>import React from 'react';
import { getClassName, handleA11yAction } from '@appbaseio/reactivecore/lib/utils/helper';
import types from '@appbaseio/reactivecore/lib/utils/types';

import Button, { pagination } from '../../../styles/Button';

function getStartPage(totalPages, currentPage, showEndPage) {
	const midValue = parseInt(totalPages / 2, 10);
	const start = currentPage - (showEndPage ? Math.ceil(midValue / 2) - 1 : midValue);
	return start > 1 ? start : 2;
}

const buildPaginationDOM = (props, position) => {
	const {
		pages,
		currentPage,
		setPage,
		totalPages,
		innerClass,
		fragmentName,
		showEndPage,
	} = props;
	let start
		= position === 'start'
			? getStartPage(pages, currentPage, showEndPage)
			: Math.max(2, Math.ceil((totalPages - ((pages - 1) / 2)) + 1));
	const paginationButtons = [];
	if (start <= totalPages) {
		let totalPagesToShow = pages < totalPages ? start + (pages - 1) : totalPages + 1;
		if (showEndPage) {
			totalPagesToShow
				= position === 'start'
					? start + (Math.ceil(pages / 2) - (pages % 2))
					: totalPages + 1;
		}
		if (currentPage > (totalPages - pages) + 2) {
			start = Math.max(2, (totalPages - pages) + 2);
			totalPagesToShow = start + pages;
		}
		for (let i = start; i < Math.min(totalPages + 1, totalPagesToShow); i += 1) {
			const primary = currentPage === i - 1;
			const innerClassName = getClassName(innerClass, 'button');
			const className
				= innerClassName || primary ? `${innerClassName} ${primary ? 'active' : ''}` : null;
			const pageBtn = (
				<Button
					className={className}
					primary={primary}
					key={i - 1}
					tabIndex="0"
					onKeyPress={event => handleA11yAction(event, () => setPage(i - 1))}
					onClick={(e) => {
						e.preventDefault();
						setPage(i - 1);
					}}
					alt={`Page ${i}`}
					href={`?${fragmentName}=${i}`}
				>
					{i}
				</Button>
			);
			if (i <= totalPages + 1) {
				paginationButtons.push(pageBtn);
			}
		}
	}
	return paginationButtons;
};

class Pagination extends React.PureComponent {
	buildIntermediatePaginationDom() {
		const {
			showEndPage, currentPage, totalPages, pages,
		} = this.props;
		if (!showEndPage) return buildPaginationDOM(this.props, 'start');
		if (currentPage <= (totalPages - pages) + 2 || totalPages <= pages) {
			return buildPaginationDOM(this.props, 'start');
		}
		return null;
	}

	render() {
		const {
			pages,
			currentPage,
			setPage,
			totalPages,
			innerClass,
			fragmentName,
			showEndPage,
		} = this.props;
		if (!totalPages) {
			return null;
		}

		const onPrevPage = (e) => {
			e.preventDefault();
			if (currentPage) {
				setPage(currentPage - 1);
			}
		};

		const onNextPage = (e) => {
			e.preventDefault();
			if (currentPage < totalPages - 1) {
				setPage(currentPage + 1);
			}
		};

		const innerClassName = getClassName(innerClass, 'button');
		const primary = currentPage === 0;
		const className
			= innerClassName || primary ? `${innerClassName} ${primary ? 'active' : ''}` : null;

		let prevHrefProp = {};
		let nextHrefProp = {};

		if (currentPage >= 1) {
			prevHrefProp = {
				href: `?${fragmentName}=${currentPage}`,
				alt: `Page ${currentPage}`,
				rel: 'prev',
			};
		}

		if (currentPage < totalPages - 1) {
			nextHrefProp = {
				href: `?${fragmentName}=${currentPage + 2}`,
				rel: 'next',
				alt: `Page ${currentPage + 2}`,
			};
		}
		return (
			<div className={`${pagination} ${getClassName(innerClass, 'pagination')}`}>
				<Button
					className={getClassName(innerClass, 'button') || null}
					disabled={currentPage === 0}
					onKeyPress={event => handleA11yAction(event, onPrevPage)}
					onClick={onPrevPage}
					tabIndex={currentPage === 0 ? '-1' : '0'}
					{...prevHrefProp}
				>
					Prev
				</Button>
				{
					<Button
						className={className}
						primary={primary}
						onKeyPress={event => handleA11yAction(event, () => setPage(0))}
						onClick={(e) => {
							e.preventDefault();
							setPage(0);
						}}
						tabIndex="0"
						href={`?${fragmentName}=1`}
						alt="Page 1"
					>
						1
					</Button>
				}
				{showEndPage && currentPage >= Math.floor(pages / 2) + !!(pages % 2) ? (
					...
				) : null}
				{this.buildIntermediatePaginationDom()}
				{showEndPage && pages > 2 && currentPage <= totalPages - Math.ceil(pages * 0.75) ? (
					...
				) : null}
				{showEndPage && totalPages >= pages && buildPaginationDOM(this.props, 'end')}
				<Button
					className={getClassName(innerClass, 'button') || null}
					disabled={currentPage >= totalPages - 1}
					onKeyPress={event => handleA11yAction(event, onNextPage)}
					onClick={onNextPage}
					tabIndex={currentPage >= totalPages - 1 ? '-1' : '0'}
					{...nextHrefProp}
				>
					Next
				</Button>

		);
	}
}

Pagination.propTypes = {
	currentPage: types.number,
	innerClass: types.style,
	pages: types.number,
	setPage: types.func,
	totalPages: types.number,
	fragmentName: types.string,
	showEndPage: types.bool,
};

export default Pagination;
<|endoftext|>


<commit_before>let Discord = require('discord.js'),
    config = require('./config'),
    token = config.TOKEN,
    bot = new Discord.Client(),
    server,
    version,
    exec,
    vconnection = null,
    stream = null,
    meta = null,
    endManual = false,
    prTimeout = false;

/* VERSION */
function getVersion(callback) {
    exec = exec || require('child_process').exec;

    exec('git rev-parse --short=4 HEAD', function (error, version) {
        if (error) {
            console.log('Error getting version', error);
            return callback('unknown');
        }

        callback(version.trim());
    });
}

/* BOT EVENTS */
bot.on('ready', function () {
    online();
    console.log(getDateTime() + 'I am ready!');
    getVersion(function (v) {
        version = v;
        bot.user.setGame('version ' + version);

        if (config.DEBUG) bot.channels.get(config.TEXT_CH).sendMessage('I am ready, running version `' + version + '`!');
    });

    if (!bot.guilds.has(config.SERVER_ID)) {
        console.log('Bot is not connected to the selected server!');
        process.exit();
    }

    server = bot.guilds.get(config.SERVER_ID);

    playRadio();
});

function playRadio() {
    endManual = true;
    bot.voiceConnections.filter((connection) => {
        return connection.channel.guild.id == config.SERVER_ID;
    }).forEach((connection) => {
        connection.disconnect();
    });
    endManual = false;

    const streamOptions = {volume: 0.1};

    const channel = server.channels.get(config.VOICE_CH);

    channel.join().then(function (connection) {
        let disconnectTriggerd = false;

        console.log(getDateTime() + 'Voice connect');

        connection.on('disconnect', function () {
            console.log(getDateTime() + 'Voice disconnect');

            if (disconnectTriggerd || endManual) return;

            disconnectTriggerd = true;

            setTimeout(function () {
                playRadio();
            }, 2000);
        });

        connection.on('error', function (err) {
            console.log(getDateTime() + 'Voice error ' + err);
        });

        const icy = require('icy');
        const url = require('url');

        const opts = url.parse(config.STREAM);
        opts.headers = {'User-Agent': config.USER_AGENT};

        icy.get(opts, function (res) {

            if (config.DEBUG) console.log(res.headers);

            res.on('metadata', function (metadata) {
                meta = icy.parse(metadata);
                bot.user.setGame(meta.StreamTitle);
            });

            stream = connection.playStream(res, streamOptions);

            stream.on('start', function () {
                console.log(getDateTime() + 'Stream started');
                endmanual = false;
            });

            stream.on('end', function () {
                //connection.disconnect();
                console.log(getDateTime() + 'Stream ended');
            });

            stream.on('error', function (error) {
                console.log(getDateTime() + 'Stream error ');
                console.log(error);
            });
        });

        vconnection = connection;
    })
        .catch(console.error);
}

function onMessage(message) {
    if (message.author.id == bot.user.id) {
        return;
    }

    if (message.channel.type == 'group') {
        return;
    }

    function handleCommand() {
        let match = /^[\/!]([a-zA-Z]+).*$/.exec(message.content);

        if (message.channel.type == 'dm') {
            match = /^[\/!]?([a-zA-Z]+).*$/.exec(message.content);
        }

        if (match) {
            const args = message.content.split(' ').splice(1);

            processCommand(message, match[1].toLowerCase(), args);
        }
    }

    if (server.channels.has(message.channel.id)) {
        handleCommand();
    } else {
        if (server.members.has(message.author.id)) {
            handleCommand();
        } else {
            return message.channel.sendMessage('You have to be member ' + server.name + '!');
        }
    }
}

bot.on('message', onMessage);

bot.on('messageUpdate', function (oldMessage, newMessage) {
    if (typeof newMessage.author === 'undefined')
        return;

    onMessage(newMessage);
});

function respond(message, response, mention, pm) {
    if (typeof mention === 'undefined') {
        mention = true;
    }

    if (typeof pm === 'undefined') {
        pm = false;
    }

    if (pm) {
        message.author.sendMessage(response);
    } else {
        if (mention) {
            message.reply(response);
        } else {
            message.channel.sendMessage(response);
        }
    }
}

/* COMMAND PROCESSING */
function processCommand(message, command, args) {
    switch (command) {
        case 'radio':
        case 'rv':
            (function () {
                respond(message, "Running DiscordRadio by DerAtrox, Version: `" + version + "`.\nAktuellster Commit: https://github.com/DerAtrox/DiscordRadio/commit/" + version);
            })();
            break;
        case 'nowplaying':
        case 'np':
            (function () {
                if (vconnection == null) {
                    respond(message, 'Der Stream ist aktuell nicht aktiv!', false);
                    return;
                }

                if (meta == null) {
                    respond(message, 'Keine Metadaten gefunden!', false);
                    return;
                }

                let text;

                let metaS = meta.StreamTitle.split(' - ');
                if (metaS.length == 2) {
                    text = '🎶 Derzeit läuft **' + metaS[1] + '** von **' + metaS[0] + '**.';
                } else {
                    text = '🎶 Derzeit läuft **' + meta + '**.';
                }

                const YouTube = require('youtube-node');

                const youTube = new YouTube();

                youTube.setKey(config.YOUTUBE_KEY);

                youTube.addParam('type', 'video');
                youTube.search(meta.StreamTitle.replace(' - ', ' '), 1, function (error, result) {
                    if (error) {
                        console.log(error);
                    }
                    else {
                        if (result.items.length == 1) {
                            text += "\n\nAuf YouTube anhören: https://www.youtube.com/watch?v=" + result.items[0].id.videoId;
                            if (config.DEBUG) console.log(JSON.stringify(result, null, 2));
                        }
                    }

                    return respond(message, text, false);
                });
            })();
            break;
        case 'playradio':
        case 'pr':
            (function () {
                if (!server.members.has(message.author.id)) {
                    console.log(getDateTime() + '!pr: Nutzer nicht Member des Servers! ' + message.author.username + '#' + message.author.discriminator);
                    return;
                }

                if (!server.roles.exists('name', config.PLAYRADIO_MINRANK)) {
                    console.log(getDateTime() + '!pr: Rang nicht gefunden! ' + config.PLAYRADIO_MINRANK);
                    return;
                }

                if (server.members.get(message.author.id).highestRole.comparePositionTo(server.roles.find('name', config.PLAYRADIO_MINRANK)) < 0) {
                    return respond(message, 'Nicht genügend Rechte!', true, false);
                }

                if (server.roles.exists('name', config.PLAYRADIO_MINRANK_FORCE)) {
                    if (server.members.get(message.author.id).highestRole.comparePositionTo(server.roles.find('name', config.PLAYRADIO_MINRANK_FORCE)) >= 0) {
                        playRadio();
                        console.log(getDateTime() + '!pr: Stream restarted Mod by ' + message.author.username + '#' + message.author.discriminator);
                        return respond(message, 'Radio Stream wird neugestartet.', true, false);
                    }
                }

                if (prTimeout) {
                    return respond(message, 'Radio Stream wurde bereits vor kurzem neugestartet, versuche es bitte später erneut.', true, false);
                }

                prTimeout = true;
                setTimeout(() => {
                    prTimeout = false;
                }, 60000 * 5);

                if (vconnection == null) {
                    playRadio();
                } else {
                    try {
                        vconnection.disconnect();
                    } catch (e) {
                        console.log(getDateTime() + 'Could not disconnect voice. Trying playRadio');
                        playRadio();
                    }
                }

                console.log(getDateTime() + '!pr: Stream restarted by ' + message.author.username + '#' + message.author.discriminator);
                return respond(message, 'Radio Stream wird neugestartet.', true, false);
            })();
            break;
    }
}

/* GENERAL APPLICATION STUFF */
process.on('exit', idle);

process.on('SIGINT', function () {
    idle();
    process.exit();

});

function getDateTime() {
    return "[" + new Date().toLocaleString() + "] ";
}

function idle() {
    bot.user.setStatus('idle');
}

function online() {
    bot.user.setStatus('online');
}

/* LOGIN */
bot.login(token);

<commit_msg>Bugfix<commit_after>let Discord = require('discord.js'),
    config = require('./config'),
    token = config.TOKEN,
    bot = new Discord.Client(),
    server,
    version,
    exec,
    vconnection = null,
    stream = null,
    meta = null,
    endManual = false,
    prTimeout = false;

/* VERSION */
function getVersion(callback) {
    exec = exec || require('child_process').exec;

    exec('git rev-parse --short=4 HEAD', function (error, version) {
        if (error) {
            console.log('Error getting version', error);
            return callback('unknown');
        }

        callback(version.trim());
    });
}

/* BOT EVENTS */
bot.on('ready', function () {
    online();
    console.log(getDateTime() + 'I am ready!');
    getVersion(function (v) {
        version = v;
        bot.user.setGame('version ' + version);

        if (config.DEBUG) bot.channels.get(config.TEXT_CH).sendMessage('I am ready, running version `' + version + '`!');
    });

    if (!bot.guilds.has(config.SERVER_ID)) {
        console.log('Bot is not connected to the selected server!');
        process.exit();
    }

    server = bot.guilds.get(config.SERVER_ID);

    playRadio();
});

function playRadio() {
    endManual = true;
    bot.voiceConnections.filter((connection) => {
        return connection.channel.guild.id == config.SERVER_ID;
    }).forEach((connection) => {
        connection.disconnect();
    });
    endManual = false;

    const streamOptions = {volume: 0.1};

    const channel = server.channels.get(config.VOICE_CH);

    channel.join().then(function (connection) {
        let disconnectTriggerd = false;

        console.log(getDateTime() + 'Voice connect');

        connection.on('disconnect', function () {
            console.log(getDateTime() + 'Voice disconnect');

            if (disconnectTriggerd || endManual) return;

            disconnectTriggerd = true;

            setTimeout(function () {
                playRadio();
            }, 2000);
        });

        connection.on('error', function (err) {
            console.log(getDateTime() + 'Voice error ' + err);
        });

        const icy = require('icy');
        const url = require('url');

        const opts = url.parse(config.STREAM);
        opts.headers = {'User-Agent': config.USER_AGENT};

        icy.get(opts, function (res) {

            if (config.DEBUG) console.log(res.headers);

            res.on('metadata', function (metadata) {
                meta = icy.parse(metadata);
                bot.user.setGame(meta.StreamTitle);
            });

            stream = connection.playStream(res, streamOptions);

            stream.on('start', function () {
                console.log(getDateTime() + 'Stream started');
            });

            stream.on('end', function () {
                connection.disconnect();
                console.log(getDateTime() + 'Stream ended');
            });

            stream.on('error', function (error) {
                console.log(getDateTime() + 'Stream error ');
                console.log(error);
            });
        });

        vconnection = connection;
    })
        .catch(console.error);
}

function onMessage(message) {
    if (message.author.id == bot.user.id) {
        return;
    }

    if (message.channel.type == 'group') {
        return;
    }

    function handleCommand() {
        let match = /^[\/!]([a-zA-Z]+).*$/.exec(message.content);

        if (message.channel.type == 'dm') {
            match = /^[\/!]?([a-zA-Z]+).*$/.exec(message.content);
        }

        if (match) {
            const args = message.content.split(' ').splice(1);

            processCommand(message, match[1].toLowerCase(), args);
        }
    }

    if (server.channels.has(message.channel.id)) {
        handleCommand();
    } else {
        if (server.members.has(message.author.id)) {
            handleCommand();
        } else {
            return message.channel.sendMessage('You have to be member ' + server.name + '!');
        }
    }
}

bot.on('message', onMessage);

bot.on('messageUpdate', function (oldMessage, newMessage) {
    if (typeof newMessage.author === 'undefined')
        return;

    onMessage(newMessage);
});

function respond(message, response, mention, pm) {
    if (typeof mention === 'undefined') {
        mention = true;
    }

    if (typeof pm === 'undefined') {
        pm = false;
    }

    if (pm) {
        message.author.sendMessage(response);
    } else {
        if (mention) {
            message.reply(response);
        } else {
            message.channel.sendMessage(response);
        }
    }
}

/* COMMAND PROCESSING */
function processCommand(message, command, args) {
    switch (command) {
        case 'radio':
        case 'rv':
            (function () {
                respond(message, "Running DiscordRadio by DerAtrox, Version: `" + version + "`.\nAktuellster Commit: https://github.com/DerAtrox/DiscordRadio/commit/" + version);
            })();
            break;
        case 'nowplaying':
        case 'np':
            (function () {
                if (vconnection == null) {
                    respond(message, 'Der Stream ist aktuell nicht aktiv!', false);
                    return;
                }

                if (meta == null) {
                    respond(message, 'Keine Metadaten gefunden!', false);
                    return;
                }

                let text;

                let metaS = meta.StreamTitle.split(' - ');
                if (metaS.length == 2) {
                    text = '🎶 Derzeit läuft **' + metaS[1] + '** von **' + metaS[0] + '**.';
                } else {
                    text = '🎶 Derzeit läuft **' + meta + '**.';
                }

                const YouTube = require('youtube-node');

                const youTube = new YouTube();

                youTube.setKey(config.YOUTUBE_KEY);

                youTube.addParam('type', 'video');
                youTube.search(meta.StreamTitle.replace(' - ', ' '), 1, function (error, result) {
                    if (error) {
                        console.log(error);
                    }
                    else {
                        if (result.items.length == 1) {
                            text += "\n\nAuf YouTube anhören: https://www.youtube.com/watch?v=" + result.items[0].id.videoId;
                            if (config.DEBUG) console.log(JSON.stringify(result, null, 2));
                        }
                    }

                    return respond(message, text, false);
                });
            })();
            break;
        case 'playradio':
        case 'pr':
            (function () {
                if (!server.members.has(message.author.id)) {
                    console.log(getDateTime() + '!pr: Nutzer nicht Member des Servers! ' + message.author.username + '#' + message.author.discriminator);
                    return;
                }

                if (!server.roles.exists('name', config.PLAYRADIO_MINRANK)) {
                    console.log(getDateTime() + '!pr: Rang nicht gefunden! ' + config.PLAYRADIO_MINRANK);
                    return;
                }

                if (server.members.get(message.author.id).highestRole.comparePositionTo(server.roles.find('name', config.PLAYRADIO_MINRANK)) < 0) {
                    return respond(message, 'Nicht genügend Rechte!', true, false);
                }

                if (server.roles.exists('name', config.PLAYRADIO_MINRANK_FORCE)) {
                    if (server.members.get(message.author.id).highestRole.comparePositionTo(server.roles.find('name', config.PLAYRADIO_MINRANK_FORCE)) >= 0) {
                        playRadio();
                        console.log(getDateTime() + '!pr: Stream restarted Mod by ' + message.author.username + '#' + message.author.discriminator);
                        return respond(message, 'Radio Stream wird neugestartet.', true, false);
                    }
                }

                if (prTimeout) {
                    return respond(message, 'Radio Stream wurde bereits vor kurzem neugestartet, versuche es bitte später erneut.', true, false);
                }

                prTimeout = true;
                setTimeout(() => {
                    prTimeout = false;
                }, 60000 * 5);

                if (vconnection == null) {
                    playRadio();
                } else {
                    try {
                        vconnection.disconnect();
                    } catch (e) {
                        console.log(getDateTime() + 'Could not disconnect voice. Trying playRadio');
                        playRadio();
                    }
                }

                console.log(getDateTime() + '!pr: Stream restarted by ' + message.author.username + '#' + message.author.discriminator);
                return respond(message, 'Radio Stream wird neugestartet.', true, false);
            })();
            break;
    }
}

/* GENERAL APPLICATION STUFF */
process.on('exit', idle);

process.on('SIGINT', function () {
    idle();
    process.exit();

});

function getDateTime() {
    return "[" + new Date().toLocaleString() + "] ";
}

function idle() {
    bot.user.setStatus('idle');
}

function online() {
    bot.user.setStatus('online');
}

/* LOGIN */
bot.login(token);

<|endoftext|>


<commit_before>import {off, once} from "../../../../dom/events";
import QUnit from "qunitjs";
import {findOne} from "../../../../dom/traverse";

QUnit.module("dom/events/once()", {
    beforeEach ()
    {
        document.getElementById("qunit-fixture").innerHTML = `

                <button type="button" id="button"></button>
                <input type="text" id="input-element">

        `;
    },
});

QUnit.test("once() callback only called once", (assert) => {
    assert.expect(1);
    const el = findOne("#button");
    const done = assert.async();

    once(el, "click", () => {
        console.log("oh hai");
        assert.ok(true, "event listener triggered");
        done();
    });

    console.log(el);
    console.log("oh bai");

    el.click();
    el.click();
});

QUnit.test("once() removes event listener after execution", (assert) => {
    assert.expect(3);
    const el = findOne("#button");
    const done = assert.async();

    assert.equal(el._listeners, undefined, "listeners not defined");

    once(el, "click", () => {});

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    el.click();

    window.setTimeout(
        () => {
            assert.equal(el._listeners.click.length, 0, "listener was successfully removed");
            done();
        }
    );
});

QUnit.test("once removes event listener", (assert) => {
    assert.expect(3);
    const el = findOne("#button");
    const done = assert.async();

    assert.equal(el._listeners, undefined, "listeners not defined");

    once(el, "click", () => {});

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    el.click();

    window.setTimeout(
        () => {
            assert.equal(el._listeners.click.length, 0, "listener was successfully removed");
            done();
        }
    );
});

QUnit.test("once() can be unregistered", (assert) => {
    assert.expect(3);
    const el = findOne("#button");

    assert.equal(el._listeners, undefined, "listeners not defined");

    const token = once(el, "click", () => {
        assert.ok(false, "event listener triggered although it should have been removed");
        done();
    });

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    off(el, "click", token);
    assert.equal(el._listeners.click.length, 0, "listener was successfully removed");

    el.click();
});
<commit_msg>Remove highly professional debug output 8)<commit_after>import {off, once} from "../../../../dom/events";
import QUnit from "qunitjs";
import {findOne} from "../../../../dom/traverse";

QUnit.module("dom/events/once()", {
    beforeEach ()
    {
        document.getElementById("qunit-fixture").innerHTML = `

                <button type="button" id="button"></button>
                <input type="text" id="input-element">

        `;
    },
});

QUnit.test("once() callback only called once", (assert) => {
    assert.expect(1);
    const el = findOne("#button");
    const done = assert.async();

    once(el, "click", () => {
        assert.ok(true, "event listener triggered");
        done();
    });

    el.click();
    el.click();
});

QUnit.test("once() removes event listener after execution", (assert) => {
    assert.expect(3);
    const el = findOne("#button");
    const done = assert.async();

    assert.equal(el._listeners, undefined, "listeners not defined");

    once(el, "click", () => {});

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    el.click();

    window.setTimeout(
        () => {
            assert.equal(el._listeners.click.length, 0, "listener was successfully removed");
            done();
        }
    );
});

QUnit.test("once removes event listener", (assert) => {
    assert.expect(3);
    const el = findOne("#button");
    const done = assert.async();

    assert.equal(el._listeners, undefined, "listeners not defined");

    once(el, "click", () => {});

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    el.click();

    window.setTimeout(
        () => {
            assert.equal(el._listeners.click.length, 0, "listener was successfully removed");
            done();
        }
    );
});

QUnit.test("once() can be unregistered", (assert) => {
    assert.expect(3);
    const el = findOne("#button");

    assert.equal(el._listeners, undefined, "listeners not defined");

    const token = once(el, "click", () => {
        assert.ok(false, "event listener triggered although it should have been removed");
        done();
    });

    assert.equal(el._listeners.click.length, 1, "1 listener registered");

    off(el, "click", token);
    assert.equal(el._listeners.click.length, 0, "listener was successfully removed");

    el.click();
});
<|endoftext|>


<commit_before>Application.Router.create(module, {
  home: function() {
    var view = this.view('hello-world/index');
    this.setView(view);
  }
});
<commit_msg>rename home method -> index<commit_after>Application.Router.create(module, {
  index: function() {
    var view = this.view('hello-world/index');
    this.setView(view);
  }
});
<|endoftext|>


<commit_before>var cmds =  {
  ":q": function() {
    remote.require('app').quit();
  },
  ":devtools": function() {
    remote.getCurrentWindow().toggleDevTools();
  },
  ":time": function() {
    addNode(null, null, '| (• ◡•)| (❍ᴥ❍ʋ)');
  },
  ":searchcode": function() {
    shell.openExternal('https://searchcode.com/');
  },
  ":about": function() {
    addNode('https://github.com/de-luca/Pinata', 'About', 'Piñata - Hit it and get your candy (or documentation)', 'A menu bar application using searchcode.com API');
    addNode('https://searchcode.com', 'About', null, 'More about Searchcode');
    addNode('https://github.com/thomaspark/bootswatch', 'About', null, 'Theme using Slate by Bootswatch');
    addNode('https://de-luca.io', 'About', null, 'A simple tool by Bastien de Luca');
  },
  ":help": function() {
    addNode(null, 'Help', 'Use Up arrow and Down arrow to navigate throught the current session history.');
    addNode(null, 'Help', ':about', 'Display info about Piñata');
    addNode(null, 'Help', ':q', 'Quit Pinata');
    addNode(null, 'Help', ':hotkey', 'Display HotKey configuration');
    addNode(null, 'Help', ':position', 'Display Position configuration');
  },
  ":position": function(query) {
    switch (query[1]) {
      case 'set':
        if(!query[2]) {
          addNode(null, 'Position', 'Error!', 'Position cannot be null');
        } else {
          ipcRenderer.sendSync('position-set', query[2]);
          addNode(null, 'Position', 'Saved!', ''+query[2]+' is the new Position.');
        }
        break;
      case 'reset':
        ipcRenderer.sendSync('position-set', 'topRight');
        addNode(null, 'Position', 'Done!', 'Position reset to topRight (default).');
        break;
      default:
        addNode(null, 'Position',
                'Current Position: '+config.get('position')+'');
        addNode('https://github.com/jenslind/electron-positioner#position',
                'Position',
                ':position set <position-name>',
                'Set the new position.
Click this node to get help about supported positions.');
        addNode(null, 'Position', ':position reset', 'Reset Position to topRight.');
        break;
    }
  },
  ":hotkey": function(query) {
    switch (query[1]) {
      case 'set':
        if(!query[2]) {
          addNode(null, 'HotKey', 'Error!', 'HotKey cannot be null');
        } else {
          if(ipcRenderer.sendSync('hotkey-set', query[2])) {
            addNode(null, 'HotKey', 'Saved!', ''+query[2]+' is the new HotKey.');
          } else {
            addNode(null, 'HotKey', 'Error!', ''+query[2]+' could not be set, another application already uses this shortcut.');
          }
        }
        break;
      case 'remove':
        ipcRenderer.sendSync('hotkey-remove');
        addNode(null, 'HotKey', 'Done!', 'HotKey removed.');
        break;
      default:
        addNode(null, 'HotKey', 'Current HotKey: '+config.get('hotkey')+'');
        addNode('https://github.com/atom/electron/blob/master/docs/api/accelerator.md',
                'HotKey',
                ':hotkey set <combo>',
                'Set the combo as the new HotKey.
Click this node to get help about key codes.');
        addNode(null, 'HotKey', ':hotkey remove', 'Remove the saved HotKey');
        break;
    }
  }
};

module.exports = {
  "cmds": cmds
};
<commit_msg>Add alias for commands<commit_after>var quit = function() {
  remote.require('app').quit();
};

var help = function() {
  addNode(null, 'Help', 'Use Up arrow and Down arrow to navigate throught the current session history.');
  addNode(null, 'Help', ':about', 'Display info about Piñata');
  addNode(null, 'Help', ':q', 'Quit Pinata');
  addNode(null, 'Help', ':hotkey', 'Display HotKey configuration');
  addNode(null, 'Help', ':position', 'Display Position configuration');
};

var cmds =  {
  ":q": quit,
  ":quit": quit,
  ":devtools": function() {
    remote.getCurrentWindow().toggleDevTools();
  },
  ":time": function() {
    addNode(null, null, '| (• ◡•)| (❍ᴥ❍ʋ)');
  },
  ":searchcode": function() {
    shell.openExternal('https://searchcode.com/');
  },
  ":about": function() {
    addNode('https://github.com/de-luca/Pinata', 'About', 'Piñata - Hit it and get your candy (or documentation)', 'A menubar application using searchcode.com API');
    addNode('https://searchcode.com', 'About', null, 'More about Searchcode');
    addNode('https://github.com/thomaspark/bootswatch', 'About', null, 'Theme is Slate by Bootswatch');
    addNode('https://de-luca.io', 'About', null, 'A simple tool by Bastien de Luca');
  },
  ":?": help,
  ":help": help,
  ":position": function(query) {
    switch (query[1]) {
      case 'set':
        if(!query[2]) {
          addNode(null, 'Position', 'Error!', 'Position cannot be null');
        } else {
          ipcRenderer.sendSync('position-set', query[2]);
          addNode(null, 'Position', 'Saved!', ''+query[2]+' is the new Position.');
        }
        break;
      case 'reset':
        ipcRenderer.sendSync('position-set', 'topRight');
        addNode(null, 'Position', 'Done!', 'Position reset to topRight (default).');
        break;
      default:
        addNode(null, 'Position',
                'Current Position: '+config.get('position')+'');
        addNode('https://github.com/jenslind/electron-positioner#position',
                'Position',
                ':position set <position-name>',
                'Set the new position.
Click this node to get help about supported positions.');
        addNode(null, 'Position', ':position reset', 'Reset Position to topRight.');
        break;
    }
  },
  ":hotkey": function(query) {
    switch (query[1]) {
      case 'set':
        if(!query[2]) {
          addNode(null, 'HotKey', 'Error!', 'HotKey cannot be null');
        } else {
          if(ipcRenderer.sendSync('hotkey-set', query[2])) {
            addNode(null, 'HotKey', 'Saved!', ''+query[2]+' is the new HotKey.');
          } else {
            addNode(null, 'HotKey', 'Error!', ''+query[2]+' could not be set, another application already uses this shortcut.');
          }
        }
        break;
      case 'remove':
        ipcRenderer.sendSync('hotkey-remove');
        addNode(null, 'HotKey', 'Done!', 'HotKey removed.');
        break;
      default:
        addNode(null, 'HotKey', 'Current HotKey: '+config.get('hotkey')+'');
        addNode('https://github.com/atom/electron/blob/master/docs/api/accelerator.md',
                'HotKey',
                ':hotkey set <combo>',
                'Set the combo as the new HotKey.
Click this node to get help about key codes.');
        addNode(null, 'HotKey', ':hotkey remove', 'Remove the saved HotKey');
        break;
    }
  }
};

module.exports = {
  "cmds": cmds
};
<|endoftext|>


<commit_before>import Child from '../../src/components/Child';

import React from 'react';
import { shallow, mount } from 'enzyme';

describe('child component', () => {
  it('displays the param it recieves in props', () => {
    const child = shallow(<Child params={ { id: 'foo' } } />);

    expect(child.find('h3').length).toBe(1);
    expect(child.find('h3').text()).toEqual('received param: foo');
  });

  it('has a link back to home', () => {
    const child = mount(<Child params={ { id: 'foo' } } />);
    const homeLink = child.find('a');

    expect(homeLink.length).toBe(1);
    homeLink.simulate('click');
    expect(window.location).toEqual('/');
  });
});
<commit_msg>Cover the Child component<commit_after>import Child from '../../src/components/Child';

import React from 'react';
import { shallow } from 'enzyme';

describe('child component', () => {
  it('displays the param it recieves in props', () => {
    const child = shallow(<Child params={ { id: 'foo' } } />);

    expect(child.find('h3').length).toBe(1);
    expect(child.find('h3').text()).toEqual('received param: foo');
  });

  it('has a link back to home', () => {
    const child = shallow(<Child params={ { id: 'foo' } } />);
    const homeLink = child.find('Link');

    expect(homeLink.length).toBe(1);
    expect(homeLink.prop('to')).toEqual('/');
  });
});
<|endoftext|>


<commit_before>"use strict";

module.exports = function testAssertions(code, stdout, stderr) {
	code.should.be.exactly(0);

	stdout.should.be.ok();
	stdout[3].should.containEql("Hash: ");
	stdout[4].should.containEql("Version: ");
	stdout[5].should.containEql("Time: ");
	stdout[7].should.containEql("null.js");
	stdout[8].should.containEql("./index.js");
	stdout[8].should.containEql("[built]");

	stderr.should.be.empty();
};
<commit_msg>test tests uglifyjs<commit_after>"use strict";

module.exports = function testAssertions(code, stdout, stderr) {
	code.should.be.exactly(0);

	stdout.should.be.ok();
	stdout[4].should.containEql("bytes"); // without uglifyjs it's multiple kBs

	stderr.should.be.empty();
};
<|endoftext|>


<commit_before>// Custom bezier drawing function for this specific application.
// Computes an array of equidistant dots along a bezier curve
//
// ctrlPointArray: An array of bezier curve control coordinates
// numRenderedPoints: The number of points to draw
// pathWidth: The width the curve must cover
// pathHeight: The height the curve must cover - this is only used for the first point
function bezierCurvePath(ctrlPointArray, numRenderedPoints, pathWidth, pathHeight) {

	var bezierCoordArray = [];

	if (ctrlPointArray.length > 3) {										// At least four points are necessary for a bezier

		ctrlPointArray[0].x = pathWidth - ctrlPointArray[ctrlPointArray.length-1].x;				// In order to smoothly curve from the end into the beginning and also to always go through the point 0,0
		ctrlPointArray[0].y = pathHeight - ctrlPointArray[ctrlPointArray.length-1].y;				// Set the first point's x y value so that it is the compliment of the last point's x y value

		// The first curve is a special case where it starts at the midpoint between the last control point and the first control point (0,0)
		var startx = 0;
		var starty = (ctrlPointArray[0].y + ctrlPointArray[ctrlPointArray.length-1].y) / 2;
 		// and the last point is the midpoint between point 3 and point 4
		var midx = (ctrlPointArray[1].x + ctrlPointArray[2].x) / 2;
		var midy = (ctrlPointArray[1].y + ctrlPointArray[2].y) / 2;
		var n = numRenderedPoints / (ctrlPointArray.length / 4);
		for (t = 0; t <= 1; t+=(1/n)) {
			x = Math.pow(1 - t, 3)*startx + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[0].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[1].x + Math.pow(t,3)*midx;
			y = Math.pow(1 - t, 3)*starty + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[0].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[1].y + Math.pow(t,3)*midy;
			bezierCoordArray[bezierCoordArray.length] = {x:x, y:y};
		}

		var lastmidx;
		var lastmidy;
		// the remainder of the curves are drawn from prior curves midpoint to the next curves midpoint thus only using three points
		for (i = 2; i < ctrlPointArray.length - 3; i+=2)
		{
			lastmidx = midx;
			lastmidy = midy;
			var midx = (ctrlPointArray[i+1].x + ctrlPointArray[i+2].x) / 2;
			var midy = (ctrlPointArray[i+1].y + ctrlPointArray[i+2].y) / 2;
			var n = numRenderedPoints / (ctrlPointArray.length / 4);

			for (t = 0; t <= 1; t+=(1/n)) {
				x = Math.pow(1 - t, 3)*lastmidx + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[i].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[i+1].x + Math.pow(t,3)*midx;
				y = Math.pow(1 - t, 3)*lastmidy + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[i].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[i+1].y + Math.pow(t,3)*midy;
				bezierCoordArray[bezierCoordArray.length] = {x:x, y:y};
			}
		}

		// The last curve ends at a midpoint between the last control point and the first control point
		lastmidx = midx;
		lastmidy = midy;
		var endx = pathWidth
		var endy = (ctrlPointArray[ctrlPointArray.length-1].y + ctrlPointArray[0].y) / 2
		var n = numRenderedPoints / (ctrlPointArray.length / 4);

		for (t = 0; t <= 1; t+=(1/n)) {
			x = Math.pow(1 - t, 3)*lastmidx + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[ctrlPointArray.length-2].x + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[ctrlPointArray.length-1].x + Math.pow(t,3)*endx;
			y = Math.pow(1 - t, 3)*lastmidy + 3*Math.pow(1 -  t,2)*t*ctrlPointArray[ctrlPointArray.length-2].y + 3*(1 -  t)*Math.pow(t,2)*ctrlPointArray[ctrlPointArray.length-1].y + Math.pow(t,3)*endy;
			bezierCoordArray[bezierCoordArray.length] = {x:x, y:y};
		}
	}

	// Once I have all the points for the bezier curve I need to loop through all of them looking for evenly spaced forward moving values of x

	// I should have the whole canvas for values of x
	var len = pathWidth
	var incr = len / numRenderedPoints;
	var j = 0;

	var evenSpacedArray = [];
	var i = 1;
	while ( i < bezierCoordArray.length) {
		// if the smaller of the adjacent points x values is equal to j for then store it
		if(j == bezierCoordArray[i-1].x) {
			evenSpacedArray[evenSpacedArray.length] = {x:bezierCoordArray[i-1].x, y:bezierCoordArray[i-1].y};
			j+=incr;
		}
		// if j is between the two points then find j along the line between the two points and store the y value
		else if(j <= bezierCoordArray[i].x  && j > bezierCoordArray[i-1] < j) {
			// compute the slope
			var m = (bezierCoordArray[i].y - bezierCoordArray[i-1].y) / (bezierCoordArray[i].x - bezierCoordArray[i-1].x)
			// compute the value of y at x = j
			var y = m*(j - bezierCoordArray[i-1].x) + bezierCoordArray[i-1].y
			// store the point
			evenSpacedArray[evenSpacedArray.length] = {x:j, y:y};
			j+=incr;
		}

		// if j is smaller than the first points x value then increment j
		else if (j < bezierCoordArray[i-1].x) { j+=incr; }
		// if j is larger than the last points x value then increment i
		else if (j > bezierCoordArray[i].x) { i++; }

	}

	return evenSpacedArray;

}<commit_msg>refactored the bezier function a bit<commit_after>function computeBezierPoint(t, p1, p2, p3, p4) {
  var x = Math.pow(1-t, 3)*p1.x + 3*Math.pow(1-t,2)*t*p2.x + 3*(1-t)*Math.pow(t,2)*p3.x + Math.pow(t,3)*p4.x;
  var y = Math.pow(1-t, 3)*p1.y + 3*Math.pow(1-t,2)*t*p2.y + 3*(1-t)*Math.pow(t,2)*p3.y + Math.pow(t,3)*p4.y;
  return {x: x, y: y};
}

function midPoint(p1, p2) {
  var x = (p1.x + p2.x) / 2;
  var y = (p1.y + p2.y) / 2;
  return {x: x, y: y};
}

// Custom bezier drawing function for this specific application.
// Computes an array of equidistant dots along a bezier curve
//
// ctrlPointArray: An array of bezier curve control coordinates
// numRenderedPoints: The number of points to render
// pathWidth: The width the curve must cover
// pathHeight: The height the curve must cover - this is only used for the first point
function bezierCurvePath(ctrlPointArray, numRenderedPoints, pathWidth, pathHeight) {

  var bezierCoordArray = [];

  if (ctrlPointArray.length > 0 && ctrlPointArray.length % 2 == 0) {										// At least four points are necessary for a bezier

    ctrlPointArray[0].x = pathWidth - ctrlPointArray[ctrlPointArray.length-1].x
    ctrlPointArray[0].y = pathHeight - ctrlPointArray[ctrlPointArray.length-1].y								// In order to smoothly curve from the end into the beginning and also to always go through the point 0,0
																		// Set the first point's x y value so that it is the compliment of the last point's x y value
    var endPoint = midPoint(ctrlPointArray[0], ctrlPointArray[ctrlPointArray.length-1]);
    endPoint.x = 0;
    var n = numRenderedPoints / (ctrlPointArray.length / 4);

    // curves are drawn using midpoints to ensure a continuous smooth bezier
    for (i = 0; i < ctrlPointArray.length - 1; i+=2)
    {
      startPoint = endPoint;

      if (i == ctrlPointArray.length - 2) {
	endPoint = midPoint(ctrlPointArray[0], ctrlPointArray[ctrlPointArray.length-1]);
	endPoint.x = pathWidth;
      }
      else {
        endPoint = midPoint(ctrlPointArray[i+1], ctrlPointArray[i+2]); // and the last point is the midpoint between point 3 and point 4
      }

      for (t = 0; t <= 1; t+=(1/n)) {
	bezierCoordArray[bezierCoordArray.length] = computeBezierPoint(t, startPoint, ctrlPointArray[i], ctrlPointArray[i+1], endPoint);
      }

    }

  }

  // Once I have all the points for the bezier curve I need to loop through all of them looking for evenly spaced forward moving values of x

  // I should have the whole canvas for values of x
  var len = pathWidth
  var incr = len / numRenderedPoints;
  var j = 0;

  var evenSpacedArray = [];
  var i = 1;
  while ( i < bezierCoordArray.length) {
    // if the smaller of the adjacent points x values is equal to j for then store it
    if(j == bezierCoordArray[i-1].x) {
      evenSpacedArray[evenSpacedArray.length] = {x:bezierCoordArray[i-1].x, y:bezierCoordArray[i-1].y};
      j+=incr;
    }
    // if j is between the two points then find j along the line between the two points and store the y value
    else if(j <= bezierCoordArray[i].x  && j > bezierCoordArray[i-1] < j) {
      // compute the slope
      var m = (bezierCoordArray[i].y - bezierCoordArray[i-1].y) / (bezierCoordArray[i].x - bezierCoordArray[i-1].x)
      // compute the value of y at x = j
      var y = m*(j - bezierCoordArray[i-1].x) + bezierCoordArray[i-1].y
      // store the point
      evenSpacedArray[evenSpacedArray.length] = {x:j, y:y};
      j+=incr;
    }

    // if j is smaller than the first points x value then increment j
    else if (j < bezierCoordArray[i-1].x) { j+=incr; }
    // if j is larger than the last points x value then increment i
    else if (j > bezierCoordArray[i].x) { i++; }

  }

  return evenSpacedArray;}<|endoftext|>