<commit_before>import React from 'react';

import Trip from './trip.js';
import * as DATABASE from '../../constants/database.js';

/**
 * Returns a promise of a query object containg the array of Trip Documents
 * corresponding to the trips that the current user is a collaborator on.
 *
 * @param {firebase.firestore.Firestore} db The Firestore database instance.
 * @param {string} userEmail The email corresponding to the current user
 *    logged in.
 * @return {Promise<!firebase.firestore.QuerySnapshot>} Promise object
 *    containing the query results with zero or more Trip  documents.
 */
function queryUserTrips(db, userEmail) {
  return db.collection(DATABASE.COLLECTION_TRIPS)
      .where(DATABASE.TRIPS_COLLABORATORS, 'array-contains', userEmail)
      .get();
}

/**
 * Grabs Trips query result from `queryUserTrips()` and returns an array of
 * `<Trip>` elements as defined in `trip.js`.
 *
 * @param {Promise<!firebase.firestore.QuerySnapshot>} querySnapshot Promise
 *    object containing the query results with zero or more Trip documents.
 * @return {Promise<!Array<Trip>>} Promise object containing an array
 *    of Trip React/HTML elements corresponding to the Trip documents included
 *    in `querySnapshot`.
 */
function serveTrips(querySnapshot) {
  return new Promise(function(resolve) {
    const tripsContainer = querySnapshot.docs.map(doc =>
        ( <Trip key={doc.id} tripObj={doc.data()} tripId={doc.id} /> ));
    resolve(tripsContainer);
  });
}

/**
 * Returns a `<div>` element with the specified error message.
 *
 * @param {string} error Error message in `componentDidMount()` catch statement.
 * @return {Promise<HTMLDivElement>} Promise object containing a `<div>` element
 *    with the error message `error` inside.
 */
function getErrorElement(error) {
  return new Promise(function(resolve) {
    console.log(`Error in Trips Container: ${error}`);
  resolve((

Error: Unable to load your trips.

 ));
  });
}

/**
 * Component corresponding to the container containing a user's trips.
 * props
 *
 * @param {Object} props These are the props for this component:
 * - db: Firestore database instance.
 * - userEmail: The current user's email.
 * @extends React.Component
 */
class TripsContainer extends React.Component {
  /** @inheritdoc */
  constructor(props) {
    super(props);
    this.state = {trips: []};
  }

  /** @inheritdoc */
  async componentDidMount() {
    try {
      const querySnapshot = await queryUserTrips(
          this.props.db, this.props.userEmail);
      let tripsContainer = await serveTrips(querySnapshot);
      this.setState({ trips: tripsContainer });
    }
    catch (error) {
      let errorElement = await getErrorElement(error);
      this.setState({ trips: errorElement });
    }
  }

  /** @inheritdoc */
  render() {
    return (

{this.state.trips}

    );
  }
}

export default TripsContainer;
<commit_msg>Change database import for consistency between trips and activities pages.<commit_after>import React from 'react';

import Trip from './trip.js';
import * as DB from '../../constants/database.js';

/**
 * Returns a promise of a query object containg the array of Trip Documents
 * corresponding to the trips that the current user is a collaborator on.
 *
 * @param {firebase.firestore.Firestore} db The Firestore database instance.
 * @param {string} userEmail The email corresponding to the current user
 *    logged in.
 * @return {Promise<!firebase.firestore.QuerySnapshot>} Promise object
 *    containing the query results with zero or more Trip  documents.
 */
function queryUserTrips(db, userEmail) {
  return db.collection(DB.COLLECTION_TRIPS)
      .where(DB.TRIPS_COLLABORATORS, 'array-contains', userEmail)
      .get();
}

/**
 * Grabs Trips query result from `queryUserTrips()` and returns an array of
 * `<Trip>` elements as defined in `trip.js`.
 *
 * @param {Promise<!firebase.firestore.QuerySnapshot>} querySnapshot Promise
 *    object containing the query results with zero or more Trip documents.
 * @return {Promise<!Array<Trip>>} Promise object containing an array
 *    of Trip React/HTML elements corresponding to the Trip documents included
 *    in `querySnapshot`.
 */
function serveTrips(querySnapshot) {
  return new Promise(function(resolve) {
    const tripsContainer = querySnapshot.docs.map(doc =>
        ( <Trip key={doc.id} tripObj={doc.data()} tripId={doc.id} /> ));
    resolve(tripsContainer);
  });
}

/**
 * Returns a `<div>` element with the specified error message.
 *
 * @param {string} error Error message in `componentDidMount()` catch statement.
 * @return {Promise<HTMLDivElement>} Promise object containing a `<div>` element
 *    with the error message `error` inside.
 */
function getErrorElement(error) {
  return new Promise(function(resolve) {
    console.log(`Error in Trips Container: ${error}`);
  resolve((

Error: Unable to load your trips.

 ));
  });
}

/**
 * Component corresponding to the container containing a user's trips.
 * props
 *
 * @param {Object} props These are the props for this component:
 * - db: Firestore database instance.
 * - userEmail: The current user's email.
 * @extends React.Component
 */
class TripsContainer extends React.Component {
  /** @inheritdoc */
  constructor(props) {
    super(props);
    this.state = {trips: []};
  }

  /** @inheritdoc */
  async componentDidMount() {
    try {
      const querySnapshot = await queryUserTrips(
          this.props.db, this.props.userEmail);
      let tripsContainer = await serveTrips(querySnapshot);
      this.setState({ trips: tripsContainer });
    }
    catch (error) {
      let errorElement = await getErrorElement(error);
      this.setState({ trips: errorElement });
    }
  }

  /** @inheritdoc */
  render() {
    return (

{this.state.trips}

    );
  }
}

export default TripsContainer;
<|endoftext|>


<commit_before>'use strict';

var assign = require('object-assign')

var PLACEHOLDER = '_'

var DIGIT = '#'

function isFormatCharacter(char) {
  return char === DIGIT
}

function isValidCharacter(char, format) {
  if (format == DIGIT) { return /^\d$/.test(char) }
  throw new Error('Unknown format character: ' + format)
}

/**
 * @param {Array.<string>} value
 * @param {Array.<string>} pattern
 * @return {Array.<string>}
 */
function formatValueToPattern(value, pattern) {
  var valueBuffer = new Array(pattern.length)
  var valueIndex = 0
  for (var i = 0, l = pattern.length; i < l ; i++) {
    if (isFormatCharacter(pattern[i])) {
      valueBuffer[i] = value.length > valueIndex && isValidCharacter(value[valueIndex], pattern[i])
                       ? value[valueIndex]
                       : PLACEHOLDER
      valueIndex++
    }
    else {
      valueBuffer[i] = pattern[i]
      // Also allow the value to contain static values from the pattern by
      // advancing its index.
      if (value.length > valueIndex && value[valueIndex] === pattern[i]) {
        valueIndex++
      }
    }
  }
  return valueBuffer
}

function InputMask(options) {
  if (!(this instanceof InputMask)) { return new InputMask(options) }

  options = assign({
    pattern: null,
    selection: {start: 0, end: 0},
    value: ''
  }, options)

  if (options.pattern == null) {
    throw new Error('InputMask: you must provide a pattern')
  }

  this.pattern = options.pattern.split('')
  this.selection = options.selection
  this.value = formatValueToPattern(options.value.split(''), this.pattern)
}

/**
 * Applies a single character of input based on the current selection.
 * @param {string} input a single character of input.
 * @return {boolean} true if a change has been made to value or selection as a
 *    result of the input, false otherwise.
 */
InputMask.prototype.input = function input(input) {
  // Ignore additional input if the cursor's at the end of the pattern
  if (this.selection.start === this.selection.end &&
      this.selection.start === this.pattern.length) {
    return false
  }

  var format = this.pattern[this.selection.start]

  // Bail out or add the character to input
  if (isFormatCharacter(format)) {
    if (!isValidCharacter(input, format)) {
      return false
    }
    this.value[this.selection.start] = input
  }
  else {
    // The user must have placed the cursor prior to, or began a selection with,
    // a static part of the pattern, so skip over it.
    this.value[this.selection.start] = format
  }

  // If multiple chatacters were selected, blank the remainder out based on the
  // pattern.
  while (this.selection.end > this.selection.start) {
    format = this.pattern[this.selection.end]
    this.value[this.selection.end] = isFormatCharacter(format) ? PLACEHOLDER : format
    this.selection.end--
  }

  // Advance the cursor to the next character
  this.selection.start++
  this.selection.end++

  // Skip over any subsequent static characters
  while (this.pattern.length > this.selection.start &&
         !isFormatCharacter(this.pattern[this.selection.start])) {
    this.value[this.selection.start] = this.pattern[this.selection.start]
    this.selection.start++
    this.selection.end++
  }

  return true
}

InputMask.prototype.backspace = function backspace() {
  // If the cursor is at the start there's nothing to do
  if (this.selection.start === 0 && this.selection.end === 0) {
    return false
  }

  var format

  // No range selected - work on the character preceding the cursor
  if (this.selection.start === this.selection.end) {
    format = this.pattern[this.selection.start - 1]
    this.value[this.selection.start - 1] = isFormatCharacter(format) ? PLACEHOLDER : format
    this.selection.start--
    this.selection.end--
  }
  // Range selected - delete characters and leave the cursor at the start of the selection
  else {
    var end = this.selection.end
    while (end >= this.selection.start) {
      format = this.pattern[end]
      this.value[end] = isFormatCharacter(format) ? PLACEHOLDER : format
      end--
    }
    this.selection.end = this.selection.start
  }

  return true
}

InputMask.prototype.getValue = function getValue() {
  return this.value.join('')
}

InputMask.formatValueToPattern = formatValueToPattern

module.exports = InputMask<commit_msg>Added a very naive implementation of mask.paste()<commit_after>'use strict';

var assign = require('object-assign')

var PLACEHOLDER = '_'

var DIGIT = '#'

function isFormatCharacter(char) {
  return char === DIGIT
}

function isValidCharacter(char, format) {
  if (format == DIGIT) { return /^\d$/.test(char) }
  throw new Error('Unknown format character: ' + format)
}

/**
 * @param {Array.<string>} value
 * @param {Array.<string>} pattern
 * @return {Array.<string>}
 */
function formatValueToPattern(value, pattern) {
  var valueBuffer = new Array(pattern.length)
  var valueIndex = 0
  for (var i = 0, l = pattern.length; i < l ; i++) {
    if (isFormatCharacter(pattern[i])) {
      valueBuffer[i] = value.length > valueIndex && isValidCharacter(value[valueIndex], pattern[i])
                       ? value[valueIndex]
                       : PLACEHOLDER
      valueIndex++
    }
    else {
      valueBuffer[i] = pattern[i]
      // Also allow the value to contain static values from the pattern by
      // advancing its index.
      if (value.length > valueIndex && value[valueIndex] === pattern[i]) {
        valueIndex++
      }
    }
  }
  return valueBuffer
}

function InputMask(options) {
  if (!(this instanceof InputMask)) { return new InputMask(options) }

  options = assign({
    pattern: null,
    selection: {start: 0, end: 0},
    value: ''
  }, options)

  if (options.pattern == null) {
    throw new Error('InputMask: you must provide a pattern')
  }

  this.pattern = options.pattern.split('')
  this.selection = options.selection
  this.value = formatValueToPattern(options.value.split(''), this.pattern)
}

/**
 * Applies a single character of input based on the current selection.
 * @param {string} input a single character of input.
 * @return {boolean} true if a change has been made to value or selection as a
 *    result of the input, false otherwise.
 */
InputMask.prototype.input = function input(input) {
  // Ignore additional input if the cursor's at the end of the pattern
  if (this.selection.start === this.selection.end &&
      this.selection.start === this.pattern.length) {
    return false
  }

  var format = this.pattern[this.selection.start]

  // Bail out or add the character to input
  if (isFormatCharacter(format)) {
    if (!isValidCharacter(input, format)) {
      return false
    }
    this.value[this.selection.start] = input
  }
  else {
    // The user must have placed the cursor prior to, or began a selection with,
    // a static part of the pattern, so skip over it.
    this.value[this.selection.start] = format
  }

  // If multiple chatacters were selected, blank the remainder out based on the
  // pattern.
  while (this.selection.end > this.selection.start) {
    format = this.pattern[this.selection.end]
    this.value[this.selection.end] = isFormatCharacter(format) ? PLACEHOLDER : format
    this.selection.end--
  }

  // Advance the cursor to the next character
  this.selection.start++
  this.selection.end++

  // Skip over any subsequent static characters
  while (this.pattern.length > this.selection.start &&
         !isFormatCharacter(this.pattern[this.selection.start])) {
    this.value[this.selection.start] = this.pattern[this.selection.start]
    this.selection.start++
    this.selection.end++
  }

  return true
}

InputMask.prototype.backspace = function backspace() {
  // If the cursor is at the start there's nothing to do
  if (this.selection.start === 0 && this.selection.end === 0) {
    return false
  }

  var format

  // No range selected - work on the character preceding the cursor
  if (this.selection.start === this.selection.end) {
    format = this.pattern[this.selection.start - 1]
    this.value[this.selection.start - 1] = isFormatCharacter(format) ? PLACEHOLDER : format
    this.selection.start--
    this.selection.end--
  }
  // Range selected - delete characters and leave the cursor at the start of the selection
  else {
    var end = this.selection.end
    while (end >= this.selection.start) {
      format = this.pattern[end]
      this.value[end] = isFormatCharacter(format) ? PLACEHOLDER : format
      end--
    }
    this.selection.end = this.selection.start
  }

  return true
}

InputMask.prototype.paste = function paste(input) {
  for (var i = 0, l = input.length; i < l; i++) {
    this.input(input.charAt(i))
  }
}

InputMask.prototype.getValue = function getValue() {
  return this.value.join('')
}

InputMask.formatValueToPattern = formatValueToPattern

module.exports = InputMask<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var handler = module.exports;
var consts = require('../../../consts/consts');
var logger = require('pomelo-logger').getLogger(__filename);
var Fightskill = require('../../../util/dataApi').fightskill;
var Code = require('../../../../../shared/code');
/**
 * Action of attack.
 * Handle the request from client, and response result to client
 * if error, the code is consts.MESSAGE.ERR. Or the code is consts.MESSAGE.RES
 *
 * @param {Object} msg
 * @param {Object} session
 * @api public
 */
handler.attack = function(msg, session, next) {
	var player = session.area.getPlayer(session.get('playerId'));
	var target = session.area.getEntity(msg.targetId);

	if(!target || !player || (player.target === target.entityId) || (player.entityId === target.entityId) || target.died){
		next();
		return;
	}

	session.area.timer.abortAction('move', player.entityId);
	player.target = target.entityId;

	// next();
  next(null, {});
};

/**
 * Player attacks his target with the skill.
 * Handle the request from client, and response result to client
 * if target exists, move to player.attack, or return.
 *
 * @param {Object} msg
 * @param {Object} session
 * @api public
 */
handler.useSkill = function(msg, session, next) {
	var playerId = msg.playerId;
	var skillId = msg.skillId;
	var player = session.area.getPlayer(msg.playerId);
	var target = session.area.getEntity(player.target);
	if (!target || (target.type !== consts.EntityType.PLAYER && target.type !== consts.EntityType.MOB)) {
		next();
		return;
	}

	next();
	player.attack(target, skillId);
};

<commit_msg>attack: no target return a msg<commit_after>/**
 * Module dependencies
 */
var handler = module.exports;
var consts = require('../../../consts/consts');
var logger = require('pomelo-logger').getLogger(__filename);
var Fightskill = require('../../../util/dataApi').fightskill;
var Code = require('../../../../../shared/code');
/**
 * Action of attack.
 * Handle the request from client, and response result to client
 * if error, the code is consts.MESSAGE.ERR. Or the code is consts.MESSAGE.RES
 *
 * @param {Object} msg
 * @param {Object} session
 * @api public
 */
handler.attack = function(msg, session, next) {
	var player = session.area.getPlayer(session.get('playerId'));
	var target = session.area.getEntity(msg.targetId);

	if(!target || !player || (player.target === target.entityId) || (player.entityId === target.entityId) || target.died){
		// next();
    next(null, {});
		return;
	}

	session.area.timer.abortAction('move', player.entityId);
	player.target = target.entityId;

	// next();
  next(null, {});
};

/**
 * Player attacks his target with the skill.
 * Handle the request from client, and response result to client
 * if target exists, move to player.attack, or return.
 *
 * @param {Object} msg
 * @param {Object} session
 * @api public
 */
handler.useSkill = function(msg, session, next) {
	var playerId = msg.playerId;
	var skillId = msg.skillId;
	var player = session.area.getPlayer(msg.playerId);
	var target = session.area.getEntity(player.target);
	if (!target || (target.type !== consts.EntityType.PLAYER && target.type !== consts.EntityType.MOB)) {
		next();
		return;
	}

	next();
	player.attack(target, skillId);
};

<|endoftext|>


<commit_before>var Option = require('./option');
var Argument = require('./argument');
var Command = require('./command');
var Error = require('./parse-argv').CliError;
var showCommandHelp = require('./help');

module.exports = {
    Error,
    Argument,
    Command,
    Option,

    set color(value) {
        return showCommandHelp.color = Boolean(value);
    },
    get color() {
        return showCommandHelp.color;
    },

    command: function(name, params, config) {
        name = name || require('path').basename(process.argv[1]) || 'command';

        return new Command(name, params, config);
    },

    confirm: function(message, fn) {
        process.stdout.write(message);
        process.stdin.setEncoding('utf8');
        process.stdin.once('data', function(val) {
            process.stdin.pause();
            fn(/^y|yes|ok|true$/i.test(val.trim()));
        });
        process.stdin.resume();
    }
};
<commit_msg>Remove confirm()<commit_after>var Option = require('./option');
var Argument = require('./argument');
var Command = require('./command');
var Error = require('./parse-argv').CliError;
var showCommandHelp = require('./help');

module.exports = {
    Error,
    Argument,
    Command,
    Option,

    set color(value) {
        return showCommandHelp.color = Boolean(value);
    },
    get color() {
        return showCommandHelp.color;
    },

    command: function(name, params, config) {
        name = name || require('path').basename(process.argv[1]) || 'command';

        return new Command(name, params, config);
    }
};
<|endoftext|>


<commit_before>'use strict';

const Regex = require('../resources/regex');
const Currency = require('../resources/currency');
const Endpoint = require('../resources/endpoint');

const Listing = require('./classes/Listing');
const ListingUser = require('./classes/ListingUser');
const ListingItem = require('./classes/ListingItem');

const requestp = require('request-promise');
const cheerio = require('cheerio');

class SteamCrawler {
    constructor(options) {
        options = options || {};

        this._cheerio = cheerio;

        // Default currrency
        this.currency = options.currency || Currency.USD;
        // Add popularity index to class when searching for items?
        this.popularity = {
            use: options.usePopularityIndex || false,
            divider: options.popularityDivider || 10000
        };
        // Use HTTP/HTTPS proxy to mask requests
        this.proxy = options.proxy || null;
        // Set timeout for requests
        this.timeout = options.timeout || 5000;
        // Set MAX_RETRIES for requests
        this.maxRetries = options.maxRetries || 3;
        // Regex's
        this.Regex = Regex;
        // Currencies
        this.Currency = Currency;
        // Endpoints
        this.Endpoint = Endpoint;

        /**
         * Mostly options that are necessary for SteamApis,
         * you can safely ignore these.
         */
        // Use WebProxy to proxy the requests
        this.webProxy = options.webProxy || false;
        // Use Base64 encoded strings as URL's where possible,
        // good when using webProxy - other than that pointless.
        this.base64 = options.base64 || false;
        // Base64 prefix
        this.base64Prefix = options.base64Prefix || 'base64';

        /**
         * Request and its defaults
         */
        this._request = requestp.defaults({
            proxy: this.proxy,
            headers: {
                'accept-charset': 'utf-8'
            },
            timeout: this.timeout,
            maxRedirects: 5
        });
    }

    request(url, json, retries) {
        if( ! json) {
            return this._request({
                url: url
            }).then((body) => {
                return Promise.resolve(body);
            }).catch((error) => {
                // Check if we can retry
                const retryable = this.retryable(retries);
                if(retryable.shouldRetry) {
                    return this.request(url, json, retryable.retries);
                } else {
                    return Promise.reject(error);
                }
            });
        } else {
            return this._request({
                url: url,
                transform: (body) => {
                    return JSON.parse(body);
                },
                transform2xxOnly: true
            }).then((body) => {
                // We received a bad response, lets try to get a good one
                if(typeof body === 'undefined' || ! body || body.success === false) {
                    // Check if we can retry
                    const retryable = this.retryable(retries);
                    if(retryable.shouldRetry) {
                        return this.request(url, json, retryable.retries);
                    } else {
                        // The request succeeded but we received a bad/malformed response
                        return Promise.reject('Received bad response from Steam.');
                    }
                } else {
                    return Promise.resolve(body);
                }
            }).catch((error) => {
                // Check if we can retry
                const retryable = this.retryable(retries);
                if(retryable.shouldRetry) {
                    return this.request(url, json, retryable.retries);
                } else {
                    return Promise.reject(error);
                }
            });
        }
    }

    retryable(retries) {
        // Check if retries is set, otherwise set it at 0
        if(typeof retries === 'undefined') {
            retries = 0;
        } else {
            retries++;
        }
        // Not hit the max retries yet
        if(retries <= this.maxRetries) {
            return { retries: retries, shouldRetry: true };
        } else {
            // We hit the max retry amount
            return { retries: retries, shouldRetry: false };
        }
    }

    getSearch(parameters) {
        const url = this.buildUrl(Endpoint.search(parameters));

        return this.request(url, false).then((body) => {
            let results = this.seperateListingsFromHTML(body);
            let listings = [];

            for(let i in results) {
                listings.push(new Listing(results[i], parameters, this.popularity));
            }

            // return the Listings array
            return Promise.resolve(listings);
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getSearchRender(parameters) {
        let returnResponseOnly = false;
        if(parameters.responseOnly) {
            returnResponseOnly = true;
            delete parameters.responseOnly;
        }

        const url = this.buildUrl(Endpoint.searchRender(parameters));

        return this.request(url, true).then((body) => {
            if( ! returnResponseOnly) {
                let results = this.seperateListingsFromHTML(body.results_html);
                let listings = [];

                for(let i in results) {
                    listings.push(new Listing(results[i], parameters, this.popularity));
                }

                // return the Listings array
                body = listings;
            }

            // returns either listings arr or body
            return Promise.resolve(body);
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getListings(appID, market_hash_name, loadHistogram) {
        const url = this.buildUrl(Endpoint.listings(appID, market_hash_name));

        return this.request(url, false).then((body) => {
            body = this._cheerio.load(body);
            let Listing = new ListingItem(body);

            if(loadHistogram) {
                return Promise.all([body, this.getHistogram(Listing)]);
            } else {
                return [body, Listing];
            }
        }).then((results) => {
            // return the Listing item
            return Promise.resolve(results[1]);
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getHistogram(ListingItem) {
        if( ! ListingItem) {
            return Promise.reject('You have to provide the `nameID` value of the ListingItem');
        }
        let nameID;
        if(typeof ListingItem === 'object') {
            nameID = ListingItem.nameID;
        } else {
            nameID = ListingItem;
        }
        if(typeof nameID === 'undefined' || ! nameID) {
            return Promise.reject('Invalid `nameID` value.');
        }

        const url = this.buildUrl(Endpoint.itemordershistogram(nameID));

        return this.request(url, true).then((data) => {
            // If we got an object, return data with the object
            if(typeof ListingItem === 'object') {
                ListingItem.histogram = data;
                return Promise.resolve(ListingItem);
            }
            return Promise.resolve(data);
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getRecentActivity(ListingItem) {
        if( ! ListingItem) {
            return Promise.reject('You have to provide the `nameID` value of the ListingItem');
        }
        let nameID;
        if(typeof ListingItem === 'object') {
            nameID = ListingItem.nameID;
        } else {
            nameID = ListingItem;
        }
        if(typeof nameID === 'undefined' || ! nameID) {
            return Promise.reject('Invalid `nameID` value.');
        }

        const url = this.buildUrl(Endpoint.itemordersactivity(nameID));

        return this.request(url, true).then((data) => {
            // If we got an object, return data with the object
            if(typeof ListingItem === 'object') {
                ListingItem.recent_activity = data;
                return Promise.resolve(ListingItem);
            }
            return Promise.resolve(data);
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getPopular(start, count) {
        const url = this.buildUrl(Endpoint.popular(start, count));

        return this.request(url, true).then((data) => {
            let listings = [];

            for(let i in data.results_html) {
                let $ = this._cheerio.load(data.results_html[i].toString('utf8'));
                listings.push(new Listing($));
            }

            // return the Listings array
            return Promise.resolve(listings);
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getRecent() {
        const url = this.buildUrl(Endpoint.recent());

        return this.request(url, true).then((data) => {
            let results = this.seperateListingsFromHTML(data.results_html);
            let listings = [];

            for(let i in results) {
                listings.push(new ListingUser(results[i]));
            }

            // return the Listings array
            return Promise.resolve(listings);
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getRecentCompleted() {
        const url = this.buildUrl(Endpoint.recentcompleted());

        return this.request(url, true).then((data) => {
            let results = this.seperateListingsFromHTML(data.results_html);
            let listings = [];

            for(let i in results) {
                listings.push(new ListingUser(results[i]));
            }

            // return the Listings array
            return Promise.resolve(listings);
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    buildUrl(endpoint) {
        // Check if we need to use base64 or default url
        const URL = (this.base64 && endpoint.base64) ? this.base64Prefix + endpoint.base64 : endpoint.url;
        // Check if webProxy was specified
        if(this.webProxy) {
            return `${this.webProxy}&contains=${endpoint.contains}&url=${URL}`;
        }
        // No webProxy - return default url
        return endpoint.url;
    }

    seperateListingsFromHTML(html) {
        const $ = cheerio.load(html.toString('utf8'));
        let results = [];

        let selector = '.market_listing_row';
        if($('.market_listing_row_link').length) {
            selector = '.market_listing_row_link';
        }

        $(selector).each((index, element) => {
            results.push(cheerio.load(element));
        });

        return results;
    }
}

module.exports = SteamCrawler;
<commit_msg>Remove some Promise.resolves and fix new Listing<commit_after>'use strict';

const Regex = require('../resources/regex');
const Currency = require('../resources/currency');
const Endpoint = require('../resources/endpoint');

const Listing = require('./classes/Listing');
const ListingUser = require('./classes/ListingUser');
const ListingItem = require('./classes/ListingItem');

const requestp = require('request-promise');
const cheerio = require('cheerio');

class SteamCrawler {
    constructor(options) {
        options = options || {};

        this._cheerio = cheerio;

        // Default currrency
        this.currency = options.currency || Currency.USD;
        // Add popularity index to class when searching for items?
        this.popularity = {
            use: options.usePopularityIndex || false,
            divider: options.popularityDivider || 10000
        };
        // Use HTTP/HTTPS proxy to mask requests
        this.proxy = options.proxy || null;
        // Set timeout for requests
        this.timeout = options.timeout || 5000;
        // Set MAX_RETRIES for requests
        this.maxRetries = options.maxRetries || 3;
        // Regex's
        this.Regex = Regex;
        // Currencies
        this.Currency = Currency;
        // Endpoints
        this.Endpoint = Endpoint;

        /**
         * Mostly options that are necessary for SteamApis,
         * you can safely ignore these.
         */
        // Use WebProxy to proxy the requests
        this.webProxy = options.webProxy || false;
        // Use Base64 encoded strings as URL's where possible,
        // good when using webProxy - other than that pointless.
        this.base64 = options.base64 || false;
        // Base64 prefix
        this.base64Prefix = options.base64Prefix || 'base64';

        /**
         * Request and its defaults
         */
        this._request = requestp.defaults({
            proxy: this.proxy,
            headers: {
                'accept-charset': 'utf-8'
            },
            timeout: this.timeout,
            maxRedirects: 5
        });
    }

    request(url, json, retries) {
        if( ! json) {
            return this._request({
                url: url
            }).then((body) => {
                return body;
            }).catch((error) => {
                // Check if we can retry
                const retryable = this.retryable(retries);
                if(retryable.shouldRetry) {
                    return this.request(url, json, retryable.retries);
                } else {
                    return Promise.reject(error);
                }
            });
        } else {
            return this._request({
                url: url,
                transform: (body) => {
                    return JSON.parse(body);
                },
                transform2xxOnly: true
            }).then((body) => {
                // We received a bad response, lets try to get a good one
                if(typeof body === 'undefined' || ! body || body.success === false) {
                    // Check if we can retry
                    const retryable = this.retryable(retries);
                    if(retryable.shouldRetry) {
                        return this.request(url, json, retryable.retries);
                    } else {
                        // The request succeeded but we received a bad/malformed response
                        return Promise.reject('Received bad response from Steam.');
                    }
                } else {
                    return body;
                }
            }).catch((error) => {
                // Check if we can retry
                const retryable = this.retryable(retries);
                if(retryable.shouldRetry) {
                    return this.request(url, json, retryable.retries);
                } else {
                    return Promise.reject(error);
                }
            });
        }
    }

    retryable(retries) {
        // Check if retries is set, otherwise set it at 0
        if(typeof retries === 'undefined') {
            retries = 0;
        } else {
            retries++;
        }
        // Not hit the max retries yet
        if(retries <= this.maxRetries) {
            return { retries: retries, shouldRetry: true };
        } else {
            // We hit the max retry amount
            return { retries: retries, shouldRetry: false };
        }
    }

    getSearch(parameters) {
        const url = this.buildUrl(Endpoint.search(parameters));

        return this.request(url, false).then((body) => {
            let results = this.seperateListingsFromHTML(body);
            let listings = [];

            for(let i in results) {
                listings.push(new Listing(results[i], parameters, this.popularity));
            }

            // return the Listings array
            return listings;
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getSearchRender(parameters) {
        let returnResponseOnly = false;
        if(parameters.responseOnly) {
            returnResponseOnly = true;
            delete parameters.responseOnly;
        }

        const url = this.buildUrl(Endpoint.searchRender(parameters));

        return this.request(url, true).then((body) => {
            if( ! returnResponseOnly) {
                let results = this.seperateListingsFromHTML(body.results_html);
                let listings = [];

                for(let i in results) {
                    listings.push(new Listing(results[i], parameters, this.popularity));
                }

                // return the Listings array
                body = listings;
            }

            // returns either listings arr or body
            return body;
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getListings(appID, market_hash_name, loadHistogram) {
        const url = this.buildUrl(Endpoint.listings(appID, market_hash_name));

        return this.request(url, false).then((body) => {
            body = this._cheerio.load(body);
            let Listing = new ListingItem(body);

            if(loadHistogram) {
                return Promise.all([body, this.getHistogram(Listing)]);
            } else {
                return [body, Listing];
            }
        }).then((results) => {
            // return the Listing item
            return results[1];
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getHistogram(ListingItem) {
        if( ! ListingItem) {
            return Promise.reject('You have to provide the `nameID` value of the ListingItem');
        }
        let nameID;
        if(typeof ListingItem === 'object') {
            nameID = ListingItem.nameID;
        } else {
            nameID = ListingItem;
        }
        if(typeof nameID === 'undefined' || ! nameID) {
            return Promise.reject('Invalid `nameID` value.');
        }

        const url = this.buildUrl(Endpoint.itemordershistogram(nameID));

        return this.request(url, true).then((data) => {
            // If we got an object, return data with the object
            if(typeof ListingItem === 'object') {
                ListingItem.histogram = data;
                return ListingItem;
            }
            return data;
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getRecentActivity(ListingItem) {
        if( ! ListingItem) {
            return Promise.reject('You have to provide the `nameID` value of the ListingItem');
        }
        let nameID;
        if(typeof ListingItem === 'object') {
            nameID = ListingItem.nameID;
        } else {
            nameID = ListingItem;
        }
        if(typeof nameID === 'undefined' || ! nameID) {
            return Promise.reject('Invalid `nameID` value.');
        }

        const url = this.buildUrl(Endpoint.itemordersactivity(nameID));

        return this.request(url, true).then((data) => {
            // If we got an object, return data with the object
            if(typeof ListingItem === 'object') {
                ListingItem.recent_activity = data;
                return ListingItem;
            }
            return data;
        }).catch((error) => {
            return Promise.reject(error);
        });
    }

    getPopular(start, count) {
        const url = this.buildUrl(Endpoint.popular(start, count));

        return this.request(url, true).then((data) => {
            let listings = [];

            for(let i in data.results_html) {
                let $ = this._cheerio.load(data.results_html[i].toString('utf8'));
                listings.push(new Listing($, {}, this.popularity));
            }

            // return the Listings array
            return listings;
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getRecent() {
        const url = this.buildUrl(Endpoint.recent());

        return this.request(url, true).then((data) => {
            let results = this.seperateListingsFromHTML(data.results_html);
            let listings = [];

            for(let i in results) {
                listings.push(new ListingUser(results[i], true));
            }

            // return the Listings array
            return listings;
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    getRecentCompleted() {
        const url = this.buildUrl(Endpoint.recentcompleted());

        return this.request(url, true).then((data) => {
            let results = this.seperateListingsFromHTML(data.results_html);
            let listings = [];

            for(let i in results) {
                listings.push(new ListingUser(results[i]));
            }

            // return the Listings array
            return listings;
        }).catch((error) => {
            // return error
            return Promise.reject(error);
        });
    }

    buildUrl(endpoint) {
        // Check if we need to use base64 or default url
        const URL = (this.base64 && endpoint.base64) ? this.base64Prefix + endpoint.base64 : endpoint.url;
        // Check if webProxy was specified
        if(this.webProxy) {
            return `${this.webProxy}&contains=${endpoint.contains}&url=${URL}`;
        }
        // No webProxy - return default url
        return endpoint.url;
    }

    seperateListingsFromHTML(html) {
        const $ = cheerio.load(html.toString('utf8'));
        let results = [];

        let selector = '.market_listing_row';
        if($('.market_listing_row_link').length) {
            selector = '.market_listing_row_link';
        }

        $(selector).each((index, element) => {
            results.push(cheerio.load(element));
        });

        return results;
    }
}

module.exports = SteamCrawler;
<|endoftext|>


<commit_before>"use strict";

var WebdriverIO = require('webdriverio/lib/webdriverio')
var MultiDevice = require('./multidevice')
var Flow = require('./flow/flow')
var Device = require('./flow/device')
var Checkpoint = require('./flow/checkpoint')
var Step = require('./flow/step')
var templates = require('./templates')
var path = require('path')
var fs = require('fs')

/**
 * @param options
 * @param modifier
 * @param {Flow|null} flow Required for and only for instances of a multi device, but not for the multi device collection itself.
 * @returns {WebdriverIO.Client}
 */
function remote(options, modifier, flow) {

    options = options || {};

    let flowDevice = new Device(options)
    if (flow) {
        flow.addDevices([flowDevice])
    }

    let screenshotBlackList = {
        protocolQueries: ['element', 'elementActive', 'elementIdAttribute', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elements', 'title', 'windowHandle', 'windowHandles'],
        stateQueries: ['isEnabled', 'isExisting', 'isSelected', 'isVisible', 'isVisibleWithinViewport'],
        cookieCommands: ['deleteCookie', 'getCookie', 'setCookie', 'cookie'],
        elementPropertyQueries: ['getAttribute', 'getCssProperty', 'getElementSize', 'getHTML', 'getLocation', 'getLocationInView', 'getSource', 'getTagName', 'getText', 'getTitle', 'getUrl', 'getValue'],
        interferingCommands: ['init', 'end', 'endAll', 'session', 'sessions', 'saveScreenshot', 'screenshot'],
        otherCommandsWithoutSideEffects: ['checkpoint', 'timeouts', 'timeoutsAsyncScript', 'timeoutsImplicitWait', 'windowHandlePosition', 'getCommandHistory', 'getTabIds', 'getViewportSize'],
        internalWebElementCommands: ['elementIdAttribute', 'elementIdClear', 'elementIdClick', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elementIdValue']
    };
    // Merge all blacklists into one list
    screenshotBlackList = Object.keys(screenshotBlackList).map(key => screenshotBlackList[key]).reduce((previous, current) => previous.concat(current), [])

    /**
     * Initialise monad
     */
    var unit = WebdriverIO(options, modifier)

    unit.lift('checkpoint', function (checkpointName) {
        return this.saveScreenshot(function (err, screenshot, response) {
            // Create new step with screenshot
            flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))

            // Create new checkpoint
            flowDevice.addCheckpoint(new Checkpoint(flow.generateCheckpointId(), checkpointName))
        })
    })

    unit.lift('name', function (name) {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            flow.setName(name)
        })
    })

    unit.lift('getFlow', function () {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            return flow
        })
    })

    /**
     * Add the webdriverIO commands
     * Build prototype: commands
     */
    ;['protocol', 'commands'].forEach(function (commandType) {
        var dir = path.join(__dirname, '../node_modules/webdriverio', 'lib', commandType)
        var files = fs.readdirSync(dir)

        files.forEach(function (filename) {
            let commandName = filename.slice(0, -3)
            let commandFunc = require(path.join(dir, filename))

            /**
             * Command proxy: Take a screenshot between commands.
             * Proxy is executed for each device
             * @returns {WebdriverIO.Client}
             */
            let proxyCommandFunc = function () {
                // Default: no additional behaviour
                let intermediate = this

                if (commandName === 'end' || commandName === 'endAll') {
                    // Serialize and store the steps and screenshots only at the end.
                    // Storing flows with large screenshots can take a while.
                    flow.storeIfChanged()

                } else if (screenshotBlackList.indexOf(commandName) === -1) {

                    let commandLabel = commandName
                    if (typeof arguments[0] == "string") {
                        commandLabel += "('" + arguments[0] + "')"
                    }
                    flowDevice.addStep(new Step(flow.generateStepId(), commandLabel, null))

                    // Take a screenshot before each command
                    // This does not override the original commands return value
                    // TODO think about taking screenshots on all devices to capture side effects
                    intermediate = this.saveScreenshot(function (err, screenshot, response) {
                        if (err) {
                            console.log(err)
                            throw new err
                        }
                        flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
                    })
                }

                // Execute function after the intermediate
                return commandFunc.apply(intermediate, arguments);
            }

            unit.lift(commandName, proxyCommandFunc);
        })
    })

    var prototype = unit()
    prototype.defer.resolve()

    return prototype
}

module.exports.remote = remote

/**
 * @param options
 * @returns {WebdriverIO.Client}
 */
function multiremote(options) {

    // Add id key to options
    Object.keys(options).forEach(function (id) {
        options[id]['id'] = id
        options[id]['waitforTimeout'] = options[id]['waitforTimeout'] || waitForTimeout
    })

    var multiDevice = new MultiDevice(options)
    var flow = new Flow()

    Object.keys(options).forEach(function (browserName) {
        multiDevice.addInstance(
            browserName,
            remote(options[browserName], multiDevice.getInstanceModifier(), flow)
        )
    })

    return remote(options, multiDevice.getModifier(), null)
}

module.exports.multiremote = multiremote

const setupsFile = '/xd-testing.json'

/**
 * Load setups from the setup file.
 * Replace device string references by the full options.
 * @returns {Array.<Object.<String, Object>>}
 */
function loadSetups() {
    var config = require(path.join(process.cwd(), setupsFile))

    // Resolve templates: Replace device string references by the full options
    config['setups'].forEach(setup => {
        Object.keys(setup.devices).forEach(id => {
            var deviceConfig = setup.devices[id]
            if (typeof deviceConfig == "string" && templates.devices[deviceConfig]) {
                // Replace template reference
                setup.devices[id] = templates.devices[deviceConfig]()
            }
        })
    })
    return config['setups']
}

module.exports.templates = templates
module.exports.loadSetups = loadSetups

/**
 * Config
 */

/**
 * @type {number}
 */
var waitForTimeout = 30 * 1000
module.exports.waitForTimeout = waitForTimeout

/**
 * @type {string}
 */
module.exports.setupsFile = setupsFile

/**
 * Url to load on initialization of multiremote instances.
 * @type {string|null}
 */
module.exports.baseUrl = null

/*
 * Default adapters
 */
module.exports.adapter = {
    /**
     * @return {xdmvc}
     */
    xdmvc: require('./adapter/xdmvc')
}

/**
 * @type {Function|xdmvc}
 */
module.exports.appFramework = null
<commit_msg>Fix JSDoc<commit_after>"use strict";

var WebdriverIO = require('webdriverio/lib/webdriverio')
var MultiDevice = require('./multidevice')
var Flow = require('./flow/flow')
var Device = require('./flow/device')
var Checkpoint = require('./flow/checkpoint')
var Step = require('./flow/step')
var templates = require('./templates')
var path = require('path')
var fs = require('fs')

/**
 * @param options
 * @param modifier
 * @param {Flow|null} flow Required for and only for instances of a multi device, but not for the multi device collection itself.
 * @returns {WebdriverIO.Client}
 */
function remote(options, modifier, flow) {

    options = options || {};

    let flowDevice = new Device(options)
    if (flow) {
        flow.addDevices([flowDevice])
    }

    let screenshotBlackList = {
        protocolQueries: ['element', 'elementActive', 'elementIdAttribute', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elements', 'title', 'windowHandle', 'windowHandles'],
        stateQueries: ['isEnabled', 'isExisting', 'isSelected', 'isVisible', 'isVisibleWithinViewport'],
        cookieCommands: ['deleteCookie', 'getCookie', 'setCookie', 'cookie'],
        elementPropertyQueries: ['getAttribute', 'getCssProperty', 'getElementSize', 'getHTML', 'getLocation', 'getLocationInView', 'getSource', 'getTagName', 'getText', 'getTitle', 'getUrl', 'getValue'],
        interferingCommands: ['init', 'end', 'endAll', 'session', 'sessions', 'saveScreenshot', 'screenshot'],
        otherCommandsWithoutSideEffects: ['checkpoint', 'timeouts', 'timeoutsAsyncScript', 'timeoutsImplicitWait', 'windowHandlePosition', 'getCommandHistory', 'getTabIds', 'getViewportSize'],
        internalWebElementCommands: ['elementIdAttribute', 'elementIdClear', 'elementIdClick', 'elementIdCssProperty', 'elementIdDisplayed', 'elementIdElement', 'elementIdElements', 'elementIdEnabled', 'elementIdLocation', 'elementIdLocationInView', 'elementIdName', 'elementIdSelected', 'elementIdSize', 'elementIdText', 'elementIdValue']
    };
    // Merge all blacklists into one list
    screenshotBlackList = Object.keys(screenshotBlackList).map(key => screenshotBlackList[key]).reduce((previous, current) => previous.concat(current), [])

    /**
     * Initialise monad
     */
    var unit = WebdriverIO(options, modifier)

    unit.lift('checkpoint', function (checkpointName) {
        return this.saveScreenshot(function (err, screenshot, response) {
            // Create new step with screenshot
            flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))

            // Create new checkpoint
            flowDevice.addCheckpoint(new Checkpoint(flow.generateCheckpointId(), checkpointName))
        })
    })

    unit.lift('name', function (name) {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            flow.setName(name)
        })
    })

    unit.lift('getFlow', function () {
        // For some reasons this.then(...) is not working, but the screenshot is.
        // Except for the overhead there should be no drawback
        return this.saveScreenshot(function () {
            return flow
        })
    })

    /**
     * Add the webdriverIO commands
     * Build prototype: commands
     */
    ;['protocol', 'commands'].forEach(function (commandType) {
        var dir = path.join(__dirname, '../node_modules/webdriverio', 'lib', commandType)
        var files = fs.readdirSync(dir)

        files.forEach(function (filename) {
            let commandName = filename.slice(0, -3)
            let commandFunc = require(path.join(dir, filename))

            /**
             * Command proxy: Take a screenshot between commands.
             * Proxy is executed for each device
             * @returns {WebdriverIO.Client}
             */
            let proxyCommandFunc = function () {
                // Default: no additional behaviour
                let intermediate = this

                if (commandName === 'end' || commandName === 'endAll') {
                    // Serialize and store the steps and screenshots only at the end.
                    // Storing flows with large screenshots can take a while.
                    flow.storeIfChanged()

                } else if (screenshotBlackList.indexOf(commandName) === -1) {

                    let commandLabel = commandName
                    if (typeof arguments[0] == "string") {
                        commandLabel += "('" + arguments[0] + "')"
                    }
                    flowDevice.addStep(new Step(flow.generateStepId(), commandLabel, null))

                    // Take a screenshot before each command
                    // This does not override the original commands return value
                    // TODO think about taking screenshots on all devices to capture side effects
                    intermediate = this.saveScreenshot(function (err, screenshot, response) {
                        if (err) {
                            console.log(err)
                            throw new err
                        }
                        flowDevice.addStep(new Step(flow.generateStepId(), [], screenshot))
                    })
                }

                // Execute function after the intermediate
                return commandFunc.apply(intermediate, arguments);
            }

            unit.lift(commandName, proxyCommandFunc);
        })
    })

    var prototype = unit()
    prototype.defer.resolve()

    return prototype
}

module.exports.remote = remote

/**
 * @param options
 * @returns {WebdriverIO.Client}
 */
function multiremote(options) {

    // Add id key to options
    Object.keys(options).forEach(function (id) {
        options[id]['id'] = id
        options[id]['waitforTimeout'] = options[id]['waitforTimeout'] || waitForTimeout
    })

    var multiDevice = new MultiDevice(options)
    var flow = new Flow()

    Object.keys(options).forEach(function (browserName) {
        multiDevice.addInstance(
            browserName,
            remote(options[browserName], multiDevice.getInstanceModifier(), flow)
        )
    })

    return remote(options, multiDevice.getModifier(), null)
}

module.exports.multiremote = multiremote

const setupsFile = '/xd-testing.json'

/**
 * Load setups from the setup file.
 * Replace device string references by the full options.
 * @returns {Array.<Object.<String, Object>>}
 */
function loadSetups() {
    var config = require(path.join(process.cwd(), setupsFile))

    // Resolve templates: Replace device string references by the full options
    config['setups'].forEach(setup => {
        Object.keys(setup.devices).forEach(id => {
            var deviceConfig = setup.devices[id]
            if (typeof deviceConfig == "string" && templates.devices[deviceConfig]) {
                // Replace template reference
                setup.devices[id] = templates.devices[deviceConfig]()
            }
        })
    })
    return config['setups']
}

module.exports.templates = templates
module.exports.loadSetups = loadSetups

/**
 * Config
 */

/**
 * @type {number}
 */
var waitForTimeout = 30 * 1000
module.exports.waitForTimeout = waitForTimeout

/**
 * @type {string}
 */
module.exports.setupsFile = setupsFile

/**
 * Url to load on initialization of multiremote instances.
 * @type {string|null}
 */
module.exports.baseUrl = null

/*
 * Default adapters
 */
module.exports.adapter = {
    /**
     * @return {xdmvc}
     */
    xdmvc: require('./adapter/xdmvc')
}

/**
 * @return {Function|Function.<xdmvc>}
 */
module.exports.appFramework = null
<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs');
var util = require('util');

var pkg = require('../package.json');

var debug = require('debug')('express-mincer-spa');
debug.log = console.log.bind(console);

var requireDir = require('require-dir');
var express = require('express');
var _ = require('lodash');
var favicon = require('serve-favicon');
var ConnectMincer = require('connect-mincer');
var Mincer = require('mincer');
var liveReload = require('livereload');

/**
 *
 * @param {string} root
 * @param {Object} opts
 * @return {Object} app
 */
exports = module.exports = function(root, opts) {
    if(_.isUndefined(root)) {
        throw new Error('Parameter "root" have to be defined');
    }

    debug('setup express application');
    var app = express();

    app.bootstrap = function(config) {
        debug('look for custom startup config');
        if(config == null) {
            config = {};
        }

        debug('setup default options');
        var defaultConfig = exports.getDefaultConfig(root, app.get('env'));

        debug('merge options to app configuration');
        app.config = _.assign({}, defaultConfig);
        _.merge(app.config, opts);
        _.merge(app.config, config);

        debug('scan directories and merge found asset paths');
        _.merge(
            app.config.assets.paths,
            exports.scanDirectories(
                app.config.assets.root,
                app.config.assets.scanDirectories
            )
        );

        // view engine
        if(_.isArray(app.config.views)) {
            app.config.views.push(path.resolve(__dirname, 'views'));
        } else {
            app.config.views = [app.config.views, path.resolve(__dirname, 'views')];
        }
        app.set('views', app.config.views);
        app.set('view engine', app.config.engine);
        app.engine('jade', require('jade').__express);

        // setup AssetPipeline
        debug('setup asset pipeline');
        app.assetPipeline = new ConnectMincer(app.config.assets);
        if(_.isFunction(app.config.configureAssetPipeline)) {
            app.config.configureAssetPipeline(app.assetPipeline);
        }
    };

    app.setup = function(config) {
        app.bootstrap(config);

        // middlewares
        if(_.isFunction(app.config.configureExpressBeforeMiddlewares)) {
            app.config.configureExpressBeforeMiddlewares(app);
        }
        debug('setup express static for public folder');
        app.use(app.config.middlewareForStaticPublic, express.static(app.config.public));
        debug('setup favicon');
        app.use(favicon(app.config.favicon));

        app.use(app.assetPipeline.assets());

        if(app.get('env') !== 'production') {
            app.use(app.config.assets.mountPoint, app.assetPipeline.createServer());
            exports.livereload(app);
        }

        if(_.isFunction(app.config.configureExpress)) {
            app.config.configureExpress(app);
        }

        if(app.config.useSpaRoute !== false) {
            app.use('/*', function(req, res) {
                res.render('index');
            });
        }

        app.use(app.config.notFoundHandler);
        app.use(app.config.errorHandler);
    };

    app.start = function(config, done) {
        if(done == null) {
            done = function(err) {
                if(err) {
                    throw err;
                }

                if(app.log) {
                    app.log.info("Application running on %s", app.config.host());
                } else {
                    console.log("Application running on %s", app.config.host());
                }
            };
        }

        app.setup(config);
        app.listen(app.config.port, app.config.ip, done);
    };

    return app;
};

exports.getDefaultConfig = function(root, env) {
    return {
        port: process.env.PORT || 3000,
        ip: process.env.IP || '',

        host: function() {
            return exports.host(this.ip, this.port);
        },

        engine: 'jade',
        views: path.resolve(root, 'views'),
        public: path.resolve(root, 'public'),
        favicon: path.resolve(root, 'public', 'favicon.ico'),

        useSpaRoute: true,
        middlewareForStaticPublic: [],
        configureAssetPipeline: function(assetPipeline)  {},
        configureExpressBeforeMiddlewares: function(app)  {},
        configureExpress: function(app)  {},

        notFoundHandler: function (req, res, next) {
            var error = new Error('Not Found');
            error.status = 404;
            next(error);
        },
        errorHandler: function(err, req, res, next) {
            res.status(err.status || 500);
            var resp = {
                message: err.message
            };
            if(req.app.get('env') !== 'production') {
                resp.error = err;
                resp.stack = err.stack.split('\n');
            }
            res.render('error.jade', resp);
        },

        assets: {
            mincer: Mincer,
            root: root,
            production: env === 'production',
            mountPoint: '/assets',
            manifestFile: path.resolve(root, 'public', 'assets', 'manifest.json'),
            paths: [],
            scanDirectories: [
                'assets'
            ]
        },

        precompile: {
            target: path.resolve(root, 'public', 'assets'),
            images: true,
            fonts: true,
            views: false,
            viewFiles: [
                'index'
            ],
            viewsTarget: path.resolve(root, 'public'),
            updateAppBeforePrecompileViews: function(app) {},
            files: [],
            options: {}
        },

        livereload: {
            active: env === 'development',
            ip: process.env.LIVERELOAD_IP || 'localhost',
            port: process.env.LIVERELOAD_PORT || 35729,
            script: function () {
                return util.format('//%s:%s/livereload.js', this.ip, this.port);
            },
            host: function() {
                return exports.host(this.ip, this.port);
            },
            watch: [],
            debug: env === 'development',
            exts: [
                'js',
                'coffee',
                'json',
                'html',
                'jade',
                'ejs',
                'css',
                'styl',
                'less',
                'png',
                'gif',
                'jpg',
                'svg',
                'ico',
                'eof',
                'ttf',
                'woff',
                'woff2'
            ]
        }
    };
};

/**
 *
 * @param {string} root
 * @param {Array} directories
 * @return {Array} asset paths
 */
exports.scanDirectories = function(root, directories) {
    var results = [];
    directories.forEach(function(directory) {
        var directoryPath = path.resolve(root, directory);
        if(fs.existsSync(directoryPath)) {
            fs.readdirSync(directoryPath).forEach(function(file) {
               var stat = fs.lstatSync(path.resolve(directoryPath, file));
               if(stat.isDirectory()) {
                   results.push(path.join(directory, file));
               }
            });
        } else {
            debug('Directory "%s" does not exist', directory);
        }
    });

    return results;
};

exports.livereload = function (app) {
    if(app.config.livereload.active) {
        debug('enable livereload server');
        // Bind LiveReload Server
        app.liveReloadServer = liveReload.createServer(_.merge({}, app.config.livereload));

        // Override Debug Logger
        // const _debug = app.liveReloadServer.debug;
        app.liveReloadServer.debug = function (str) {
            debug("LiveReload Server:", str);
        };

        // Load Watch Directories
        var watch = app.config.livereload.watch;
        exports.arrayPush(watch, app.get('views') + "/*");
        exports.arrayPush(watch, app.config.assets.paths, function(p) {
            return path.resolve(app.config.assets.root, p);
        });

        debug("LiveReload enabled.");
        debug("Watching:", watch);
        debug("LiveReload Server on", app.config.livereload.host());
        // Start Server
        app.liveReloadServer.watch(watch);

        // Bind LRScript Middleware
        app.use(function(req, res, next) {
            res.locals.LRScriptFile = app.config.livereload.script();
            res.locals.LRScriptTag = "<script src=\""+res.locals.LRScriptFile+"\"></'+'script>";
            res.locals.LRScript = "<script>document.write('"+res.locals.LRScriptTag+"')</script>";
            next();
        });
    } else {
        debug('disable livereload server');
        app.use(function (req, res, next) {
            res.locals.LRScript = "<!-- LiveReload disabled -->";
            next();
        });
    }
};

exports.arrayPush = function (target, item, transform) {
    if (_.isUndefined(transform)) {
        transform = function(x) { return x; };
    }
    item = _.isArray(item) ? item : [item];

    item.forEach(function (p) {
        target.push(transform(p));
    });

    return target;
};

exports.host = function(ip, port) {
    if(_.isEmpty(ip) === false) {
        return util.format('%s:%s', ip, port);
    }

    return util.format('localhost:%s', port);
};

exports.getPrecompileFonts = function() {
    return [
        '*.eot',
        '*.svg',
        '*.ttf',
        '*.woff',
        '*.woff2',
        '**/*.eot',
        '**/*.svg',
        '**/*.ttf',
        '**/*.woff',
        '**/*.woff2'
    ];
};

exports.getPrecompileImages = function() {
    return [
        '*.png',
        '*.gif',
        '*.jpg',
        '*.ico',
        '**/*.png',
        '**/*.gif',
        '**/*.jpg',
        '**/*.ico'
    ];
};

exports.toAssetUrl = function(source, config) {
    return (config.assetHost ? config.assetHost : '') + config.mountPoint + '/' + source;
};

exports.pkg = pkg;

exports.utils = requireDir(path.resolve(__dirname, 'utils'));
<commit_msg>allow options for express.static of public folder<commit_after>var path = require('path');
var fs = require('fs');
var util = require('util');

var pkg = require('../package.json');

var debug = require('debug')('express-mincer-spa');
debug.log = console.log.bind(console);

var requireDir = require('require-dir');
var express = require('express');
var _ = require('lodash');
var favicon = require('serve-favicon');
var ConnectMincer = require('connect-mincer');
var Mincer = require('mincer');
var liveReload = require('livereload');

/**
 *
 * @param {string} root
 * @param {Object} opts
 * @return {Object} app
 */
exports = module.exports = function(root, opts) {
    if(_.isUndefined(root)) {
        throw new Error('Parameter "root" have to be defined');
    }

    debug('setup express application');
    var app = express();

    app.bootstrap = function(config) {
        debug('look for custom startup config');
        if(config == null) {
            config = {};
        }

        debug('setup default options');
        var defaultConfig = exports.getDefaultConfig(root, app.get('env'));

        debug('merge options to app configuration');
        app.config = _.assign({}, defaultConfig);
        _.merge(app.config, opts);
        _.merge(app.config, config);

        debug('scan directories and merge found asset paths');
        _.merge(
            app.config.assets.paths,
            exports.scanDirectories(
                app.config.assets.root,
                app.config.assets.scanDirectories
            )
        );

        // view engine
        if(_.isArray(app.config.views)) {
            app.config.views.push(path.resolve(__dirname, 'views'));
        } else {
            app.config.views = [app.config.views, path.resolve(__dirname, 'views')];
        }
        app.set('views', app.config.views);
        app.set('view engine', app.config.engine);
        app.engine('jade', require('jade').__express);

        // setup AssetPipeline
        debug('setup asset pipeline');
        app.assetPipeline = new ConnectMincer(app.config.assets);
        if(_.isFunction(app.config.configureAssetPipeline)) {
            app.config.configureAssetPipeline(app.assetPipeline);
        }
    };

    app.setup = function(config) {
        app.bootstrap(config);

        // middlewares
        if(_.isFunction(app.config.configureExpressBeforeMiddlewares)) {
            app.config.configureExpressBeforeMiddlewares(app);
        }
        debug('setup express static for public folder');
        app.use(app.config.middlewareForStaticPublic, express.static(app.config.public, app.confog.publicOptions));
        debug('setup favicon');
        app.use(favicon(app.config.favicon));

        app.use(app.assetPipeline.assets());

        if(app.get('env') !== 'production') {
            app.use(app.config.assets.mountPoint, app.assetPipeline.createServer());
            exports.livereload(app);
        }

        if(_.isFunction(app.config.configureExpress)) {
            app.config.configureExpress(app);
        }

        if(app.config.useSpaRoute !== false) {
            app.use('/*', function(req, res) {
                res.render('index');
            });
        }

        app.use(app.config.notFoundHandler);
        app.use(app.config.errorHandler);
    };

    app.start = function(config, done) {
        if(done == null) {
            done = function(err) {
                if(err) {
                    throw err;
                }

                if(app.log) {
                    app.log.info("Application running on %s", app.config.host());
                } else {
                    console.log("Application running on %s", app.config.host());
                }
            };
        }

        app.setup(config);
        app.listen(app.config.port, app.config.ip, done);
    };

    return app;
};

exports.getDefaultConfig = function(root, env) {
    return {
        port: process.env.PORT || 3000,
        ip: process.env.IP || '',

        host: function() {
            return exports.host(this.ip, this.port);
        },

        engine: 'jade',
        views: path.resolve(root, 'views'),
        public: path.resolve(root, 'public'),
        publicOptions: {},
        favicon: path.resolve(root, 'public', 'favicon.ico'),

        useSpaRoute: true,
        middlewareForStaticPublic: [],
        configureAssetPipeline: function(assetPipeline)  {},
        configureExpressBeforeMiddlewares: function(app)  {},
        configureExpress: function(app)  {},

        notFoundHandler: function (req, res, next) {
            var error = new Error('Not Found');
            error.status = 404;
            next(error);
        },
        errorHandler: function(err, req, res, next) {
            res.status(err.status || 500);
            var resp = {
                message: err.message
            };
            if(req.app.get('env') !== 'production') {
                resp.error = err;
                resp.stack = err.stack.split('\n');
            }
            res.render('error.jade', resp);
        },

        assets: {
            mincer: Mincer,
            root: root,
            production: env === 'production',
            mountPoint: '/assets',
            manifestFile: path.resolve(root, 'public', 'assets', 'manifest.json'),
            paths: [],
            scanDirectories: [
                'assets'
            ]
        },

        precompile: {
            target: path.resolve(root, 'public', 'assets'),
            images: true,
            fonts: true,
            views: false,
            viewFiles: [
                'index'
            ],
            viewsTarget: path.resolve(root, 'public'),
            updateAppBeforePrecompileViews: function(app) {},
            files: [],
            options: {}
        },

        livereload: {
            active: env === 'development',
            ip: process.env.LIVERELOAD_IP || 'localhost',
            port: process.env.LIVERELOAD_PORT || 35729,
            script: function () {
                return util.format('//%s:%s/livereload.js', this.ip, this.port);
            },
            host: function() {
                return exports.host(this.ip, this.port);
            },
            watch: [],
            debug: env === 'development',
            exts: [
                'js',
                'coffee',
                'json',
                'html',
                'jade',
                'ejs',
                'css',
                'styl',
                'less',
                'png',
                'gif',
                'jpg',
                'svg',
                'ico',
                'eof',
                'ttf',
                'woff',
                'woff2'
            ]
        }
    };
};

/**
 *
 * @param {string} root
 * @param {Array} directories
 * @return {Array} asset paths
 */
exports.scanDirectories = function(root, directories) {
    var results = [];
    directories.forEach(function(directory) {
        var directoryPath = path.resolve(root, directory);
        if(fs.existsSync(directoryPath)) {
            fs.readdirSync(directoryPath).forEach(function(file) {
               var stat = fs.lstatSync(path.resolve(directoryPath, file));
               if(stat.isDirectory()) {
                   results.push(path.join(directory, file));
               }
            });
        } else {
            debug('Directory "%s" does not exist', directory);
        }
    });

    return results;
};

exports.livereload = function (app) {
    if(app.config.livereload.active) {
        debug('enable livereload server');
        // Bind LiveReload Server
        app.liveReloadServer = liveReload.createServer(_.merge({}, app.config.livereload));

        // Override Debug Logger
        // const _debug = app.liveReloadServer.debug;
        app.liveReloadServer.debug = function (str) {
            debug("LiveReload Server:", str);
        };

        // Load Watch Directories
        var watch = app.config.livereload.watch;
        exports.arrayPush(watch, app.get('views') + "/*");
        exports.arrayPush(watch, app.config.assets.paths, function(p) {
            return path.resolve(app.config.assets.root, p);
        });

        debug("LiveReload enabled.");
        debug("Watching:", watch);
        debug("LiveReload Server on", app.config.livereload.host());
        // Start Server
        app.liveReloadServer.watch(watch);

        // Bind LRScript Middleware
        app.use(function(req, res, next) {
            res.locals.LRScriptFile = app.config.livereload.script();
            res.locals.LRScriptTag = "<script src=\""+res.locals.LRScriptFile+"\"></'+'script>";
            res.locals.LRScript = "<script>document.write('"+res.locals.LRScriptTag+"')</script>";
            next();
        });
    } else {
        debug('disable livereload server');
        app.use(function (req, res, next) {
            res.locals.LRScript = "<!-- LiveReload disabled -->";
            next();
        });
    }
};

exports.arrayPush = function (target, item, transform) {
    if (_.isUndefined(transform)) {
        transform = function(x) { return x; };
    }
    item = _.isArray(item) ? item : [item];

    item.forEach(function (p) {
        target.push(transform(p));
    });

    return target;
};

exports.host = function(ip, port) {
    if(_.isEmpty(ip) === false) {
        return util.format('%s:%s', ip, port);
    }

    return util.format('localhost:%s', port);
};

exports.getPrecompileFonts = function() {
    return [
        '*.eot',
        '*.svg',
        '*.ttf',
        '*.woff',
        '*.woff2',
        '**/*.eot',
        '**/*.svg',
        '**/*.ttf',
        '**/*.woff',
        '**/*.woff2'
    ];
};

exports.getPrecompileImages = function() {
    return [
        '*.png',
        '*.gif',
        '*.jpg',
        '*.ico',
        '**/*.png',
        '**/*.gif',
        '**/*.jpg',
        '**/*.ico'
    ];
};

exports.toAssetUrl = function(source, config) {
    return (config.assetHost ? config.assetHost : '') + config.mountPoint + '/' + source;
};

exports.pkg = pkg;

exports.utils = requireDir(path.resolve(__dirname, 'utils'));
<|endoftext|>


<commit_before>$(document).ready(function() {
  $(".event").mouseenter(function() {
    $(this).css("background-color", "#2EAC6A");
    return false;
  })
  $(".event").mouseleave(function() {
    $(this).css("background-color", "#9aa4ad");
    return false;
  })

})

<commit_msg>added mouseover highlighting support for ajax delivered calendars<commit_after>$(document).ready(function() {
  $(".event").live("mouseover", function() {
    $(this).css("background-color", "#2EAC6A");
  });
  $(".event").live("mouseout", function() {
    $(this).css("background-color", "#9aa4ad");
  });
})
<|endoftext|>