<commit_before>export const DEBUG_FLAG = false;

// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
// import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')

// You can mock if needed. Example below
// beforeEach(() => {
//     cy.server();
//     cy.route('/countries*', {});
// })

/* returning false here prevents Cypress from failing the test */
Cypress.on('uncaught:exception', (err, runnable) => false);

Cypress.Commands.overwrite(
  'type',
  (originalFn, subject, string, options) => originalFn(
    subject,
    string,
    Object.assign({}, options, { delay: 100 }),
  ),
);

function getItem(selector, counter) {
  cy.wait(999, { log: DEBUG_FLAG })
    .get('#storybook-preview-iframe', { log: DEBUG_FLAG })
    .then(($iframe) => {
      const doc = $iframe.contents();
      if (!doc.find(selector).length && counter > 0) {
        return getItem(selector, --counter);
      }
      return cy.wrap(doc[0].body);
    })
    .find(selector);
}

Cypress.Commands.add('iFrame', (selector) => { getItem(selector, 50); });

before(() => {
  cy.wait(1000, { log: DEBUG_FLAG });
});

Cypress.Screenshot.defaults({ screenshotOnRunFailure: DEBUG_FLAG });
<commit_msg>added wait<commit_after>export const DEBUG_FLAG = false;

// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
// import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')

// You can mock if needed. Example below
// beforeEach(() => {
//     cy.server();
//     cy.route('/countries*', {});
// })

/* returning false here prevents Cypress from failing the test */
Cypress.on('uncaught:exception', (err, runnable) => false);

Cypress.Commands.overwrite(
  'type',
  (originalFn, subject, string, options) => originalFn(
    subject,
    string,
    Object.assign({}, options, { delay: 100 }),
  ),
);

function getItem(selector, counter) {
  cy.wait(999, { log: DEBUG_FLAG })
    .get('#storybook-preview-iframe', { log: DEBUG_FLAG })
    .then(($iframe) => {
      const doc = $iframe.contents();
      if (!doc.find(selector).length && counter > 0) {
        return getItem(selector, --counter);
      }
      cy.wait(250, { log: DEBUG_FLAG });
      return cy.wrap(doc.find('body'));
    })
    .find(selector);
}

Cypress.Commands.add('iFrame', (selector) => { getItem(selector, 50); });

before(() => {
  cy.wait(1000, { log: DEBUG_FLAG });
});

Cypress.Screenshot.defaults({ screenshotOnRunFailure: DEBUG_FLAG });
<|endoftext|>


<commit_before>import React from "react";
import { Dimensions } from "react-native";

import _ from "lodash";

import DateFns from "./DateFns";

import DateSliderComponent from "./DateSliderComponent";

const END_THRESHOLD = 4;
const EXTEND_WEEKS_BY = 8;

class DateSlider extends React.Component {
  static propTypes = {
    isoStartDay: React.PropTypes.number,
    locale: React.PropTypes.object,
    onDateSelected: React.PropTypes.func,
    onWeekChanged: React.PropTypes.func,
    selectedDate: React.PropTypes.object
  };

  static defaultProps = {
    isoStartDay: 1
  };

  flatlist = undefined;
  selectedIndex = 0;
  viewableIndex = -1;

  constructor(props) {
    super(props);

    if (props.locale && props.locale.name && props.locale.config) {
      DateFns.initLocale(props.locale.name, props.locale.config);
    }

    let selectedDate;
    if (this.props.selectedDate) {
      selectedDate = DateFns.initMoment(this.props.selectedDate, props.locale);
    } else {
      selectedDate = DateFns.initMoment(new Date(), props.locale);
    }

    // The very first date in the FlatList
    const startingDate = DateFns.getStartOfWeek(
      selectedDate,
      props.isoStartDay
    ).subtract(EXTEND_WEEKS_BY, "weeks");

    this.state = {
      viewWidth: Dimensions.get("window").width,
      arrDates: this._generateDates(
        startingDate,
        EXTEND_WEEKS_BY * 2 + 1,
        selectedDate,
        props.isoStartDay
      ),
      selectedDate: selectedDate
    };
  }

  componentWillReceiveProps(nextProps) {
    if (
      nextProps.selectedDate &&
      !this.state.selectedDate.isSame(nextProps.selectedDate, "day")
    ) {
      this._setSelectedDate(
        DateFns.initMoment(nextProps.selectedDate, nextProps.locale)
      );
      this._scrollTo(this.selectedIndex);
    }
  }

  render() {
    return (
      <DateSliderComponent
        arrDates={this.state.arrDates}
        captureFlatListRef={this._captureFlatListRef}
        viewWidth={this.state.viewWidth}
        getItemLayout={this._getItemLayout}
        onDateSelected={this._onDateSelected}
        onEndReached={this._onEndReached}
        onEndReachedThreshold={END_THRESHOLD}
        onLayout={this._onLayout}
        onViewableItemsChanged={this._onViewableItemsChanged}
      />
    );
  }

  /**
   * Pure function to generate an array of objects that can be used by the FlatList.
   *
   * The FlatList renders a 'week' as a new item. This function
   * creates a number of entries equal to the weeks parameter
   * and starting from the fromDate paramater. The fromDate
   * needs to be set to the correct weekday used to start each WeekView
   * by the callee.
   *
   * @param {moment} fromDate
   *  The starting date for the array of weeks to be generated
   * @param {integer} weeks
   *  The number of weeks to be generated
   * @param {moment} selectedDate
   *  The date that is current selected
   * @returns {Array<Object>}
   *
   * @memberof DateSlider
   */
  _generateDates(fromDate, weeks, selectedDate, isoStartDay) {
    const startOfSelectedWeek = DateFns.getStartOfWeek(
      selectedDate,
      isoStartDay
    );

    return _.map(_.range(weeks), i => {
      const weekStartDate = fromDate.clone().add(i, "weeks");
      if (startOfSelectedWeek.isSame(weekStartDate, "day")) {
        this.selectedIndex = i;
      }
      return {
        key: weekStartDate.toISOString(),
        date: weekStartDate,
        arrSelected: this._generateArrSelected(
          weekStartDate,
          startOfSelectedWeek,
          selectedDate,
          isoStartDay
        )
      };
    });
  }

  /**
   * A pure function to generate an array of boolean values
   * that specify if one of the days of the week is selected.
   *
   * This array is to be used as a prop on the WeekView component.
   *
   * @param {moment} weekStartDate
   *  The start date of the week this is to be generated
   * @param {moment} startOfSelectedWeek
   *  The start of the week that contains the selectedDate
   * @param {moment} selectedDate
   *  The date that is selected
   *
   * @returns {Array<boolean>}
   *
   * @memberof DateSlider
   */
  _generateArrSelected(
    weekStartDate,
    startOfSelectedWeek,
    selectedDate,
    isoStartDay
  ) {
    let result = [false, false, false, false, false, false, false];
    if (weekStartDate && weekStartDate.isSame(startOfSelectedWeek, "day")) {
      result[DateFns.getSelectedDayOfWeek(selectedDate, isoStartDay)] = true;
    }
    return result;
  }

  /**
   * Captures the FlatList instance ref, so that
   * the scrollTo method can be called on it.
   * This is populated from the DateSliderComponent.
   *
   * @param {FlatList} flatlist
   *   The FlatList instance
   *
   * @memberof DateSlider
   */
  _captureFlatListRef = flatlist => (this.flatlist = flatlist);

  /**
   * ReactNative method called by the FlatList
   * This needs to be here as it uses viewWidth held in state
   *
   * @param {Object} data
   * @param {number} index
   *
   * @memberof DateSlider
   */
  _getItemLayout = (data, index) => {
    return {
      length: this.state.viewWidth,
      offset: this.state.viewWidth * index,
      index
    };
  };

  /**
   * ReactNative method called by the view containing the FlatList
   * This needs to be here as handles setting the viewWidth
   * based on what the containing view reports.
   *
   * @param {Object} event
   *  The event generated from the RN onLayout call
   *
   * @memberof DateSlider
   */
  _onLayout = event => {
    const nativeEvent = event.nativeEvent;
    this.setState({ viewWidth: nativeEvent.layout.width });
    if (this.viewableIndex === -1) {
      this._scrollTo(this.selectedIndex);
    } else {
      this._scrollTo(this.viewableIndex);
    }
  };

  /**
   * Scrolls the flatlist to the specified index.
   * This wraps the call in a setTimeout(_,0) Promise
   * to stop issues when the index to be scrolled to has not yet been
   * rendered.
   *
   * @param {number} index
   *  The index to scroll the flatlist to
   *
   * @memberof DateSlider
   */
  _scrollTo = index => {
    const scrollToPromise = new Promise(resolve => setTimeout(resolve, 0));
    scrollToPromise.then(() => {
      this.flatlist.scrollToIndex({
        viewPosition: 0,
        index: index,
        animated: false
      });
    });
  };

  _onEndReached = () => {
    const lastDate = this.state.arrDates[this.state.arrDates.length - 1].date;
    const appendWeeksFrom = lastDate.clone().add(1, "weeks");
    const arrNewDates = this._generateDates(
      appendWeeksFrom,
      EXTEND_WEEKS_BY,
      this.state.selectedDate
    );
    this.setState({ arrDates: [...this.state.arrDates, ...arrNewDates] });
  };

  _onStartReached = () => {
    const firstDate = this.state.arrDates[0].date;
    const prependWeeksFrom = firstDate
      .clone()
      .subtract(EXTEND_WEEKS_BY, "weeks");
    const arrNewDates = this._generateDates(
      prependWeeksFrom,
      EXTEND_WEEKS_BY,
      this.state.selectedDate
    );
    this.setState({ arrDates: [...arrNewDates, ...this.state.arrDates] });
    this.selectedIndex = this.selectedIndex + EXTEND_WEEKS_BY;
    this._scrollTo(END_THRESHOLD - 1 + EXTEND_WEEKS_BY);
  };

  _onViewableItemsChanged = ({ viewableItems, changed }) => {
    if (viewableItems.length === 1) {
      const viewableIndex = viewableItems[0].index;
      const changedIndex = changed[0].index;
      this.viewableIndex = viewableIndex;

      if (changedIndex === viewableIndex - 1) {
        // Week forward
        const date = this.state.arrDates[viewableIndex].date;
        this.props.onWeekChanged(date);
      }

      if (changedIndex === viewableIndex + 1) {
        // week back
        const date = this.state.arrDates[viewableIndex].date;
        this.props.onWeekChanged(date);
      }

      if (
        changedIndex === END_THRESHOLD &&
        viewableIndex === changedIndex - 1
      ) {
        this._onStartReached();
      }
    }
  };

  _onDateSelected = date => {
    this._setSelectedDate(date);
    this.props.onDateSelected(date);
  };

  _setSelectedDate = date => {
    const startOfSelectedWeek = DateFns.getStartOfWeek(
      date,
      this.props.isoStartDay
    );

    let arrDates = [...this.state.arrDates];

    const prevSelectedIndex = this.selectedIndex;

    const selectedIndex = _.findIndex(arrDates, [
      "key",
      startOfSelectedWeek.toISOString()
    ]);

    arrDates[prevSelectedIndex].arrSelected = this._generateArrSelected();
    arrDates[selectedIndex].arrSelected = this._generateArrSelected(
      arrDates[selectedIndex].date,
      startOfSelectedWeek,
      date,
      this.props.isoStartDay
    );

    this.selectedIndex = selectedIndex;

    this.setState({
      arrDates: arrDates,
      selectedDate: date
    });
  };
}

export default DateSlider;
<commit_msg>Some comments<commit_after>import React from "react";
import { Dimensions } from "react-native";

import _ from "lodash";

import DateFns from "./DateFns";

import DateSliderComponent from "./DateSliderComponent";

const END_THRESHOLD = 4;
const EXTEND_WEEKS_BY = 8;

class DateSlider extends React.Component {
  static propTypes = {
    isoStartDay: React.PropTypes.number,
    locale: React.PropTypes.object,
    onDateSelected: React.PropTypes.func,
    onWeekChanged: React.PropTypes.func,
    selectedDate: React.PropTypes.object
  };

  static defaultProps = {
    isoStartDay: 1
  };

  flatlist = undefined;
  selectedIndex = 0;
  viewableIndex = -1;

  constructor(props) {
    super(props);

    if (props.locale && props.locale.name && props.locale.config) {
      DateFns.initLocale(props.locale.name, props.locale.config);
    }

    let selectedDate;
    if (this.props.selectedDate) {
      selectedDate = DateFns.initMoment(this.props.selectedDate, props.locale);
    } else {
      selectedDate = DateFns.initMoment(new Date(), props.locale);
    }

    // The very first date in the FlatList
    const startingDate = DateFns.getStartOfWeek(
      selectedDate,
      props.isoStartDay
    ).subtract(EXTEND_WEEKS_BY, "weeks");

    this.state = {
      viewWidth: Dimensions.get("window").width,
      arrDates: this._generateDates(
        startingDate,
        EXTEND_WEEKS_BY * 2 + 1,
        selectedDate,
        props.isoStartDay
      ),
      selectedDate: selectedDate
    };
  }

  componentWillReceiveProps(nextProps) {
    if (
      nextProps.selectedDate &&
      !this.state.selectedDate.isSame(nextProps.selectedDate, "day")
    ) {
      this._setSelectedDate(
        DateFns.initMoment(nextProps.selectedDate, nextProps.locale)
      );
      this._scrollTo(this.selectedIndex);
    }
  }

  render() {
    return (
      <DateSliderComponent
        arrDates={this.state.arrDates}
        captureFlatListRef={this._captureFlatListRef}
        viewWidth={this.state.viewWidth}
        getItemLayout={this._getItemLayout}
        onDateSelected={this._onDateSelected}
        onEndReached={this._onEndReached}
        onEndReachedThreshold={END_THRESHOLD}
        onLayout={this._onLayout}
        onViewableItemsChanged={this._onViewableItemsChanged}
      />
    );
  }

  /**
   * Pure function to generate an array of objects that can be used by the FlatList.
   *
   * The FlatList renders a 'week' as a new item. This function
   * creates a number of entries equal to the weeks parameter
   * and starting from the fromDate paramater. The fromDate
   * needs to be set to the correct weekday used to start each WeekView
   * by the callee.
   *
   * @param {moment} fromDate
   *  The starting date for the array of weeks to be generated
   * @param {integer} weeks
   *  The number of weeks to be generated
   * @param {moment} selectedDate
   *  The date that is current selected
   * @param {number} isoStartDay
   *  The ISOWeekday number that starts the week being displayed (1 - 7)
   * @returns {Array<Object>}
   *
   * @memberof DateSlider
   */
  _generateDates(fromDate, weeks, selectedDate, isoStartDay) {
    const startOfSelectedWeek = DateFns.getStartOfWeek(
      selectedDate,
      isoStartDay
    );

    return _.map(_.range(weeks), i => {
      const weekStartDate = fromDate.clone().add(i, "weeks");
      if (startOfSelectedWeek.isSame(weekStartDate, "day")) {
        this.selectedIndex = i;
      }
      return {
        key: weekStartDate.toISOString(),
        date: weekStartDate,
        arrSelected: this._generateArrSelected(
          weekStartDate,
          startOfSelectedWeek,
          selectedDate,
          isoStartDay
        )
      };
    });
  }

  /**
   * A pure function to generate an array of boolean values
   * that specify if one of the days of the week is selected.
   *
   * This array is to be used as a prop on the WeekView component.
   *
   * @param {moment} weekStartDate
   *  The start date of the week this is to be generated
   * @param {moment} startOfSelectedWeek
   *  The start of the week that contains the selectedDate
   * @param {moment} selectedDate
   *  The date that is selected
   * @param {number} isoStartDay
   *  The ISOWeekday number that starts the week being displayed (1 - 7)
   *
   * @returns {Array<boolean>}
   *
   * @memberof DateSlider
   */
  _generateArrSelected(
    weekStartDate,
    startOfSelectedWeek,
    selectedDate,
    isoStartDay
  ) {
    let result = [false, false, false, false, false, false, false];
    if (weekStartDate && weekStartDate.isSame(startOfSelectedWeek, "day")) {
      result[DateFns.getSelectedDayOfWeek(selectedDate, isoStartDay)] = true;
    }
    return result;
  }

  /**
   * Captures the FlatList instance ref, so that
   * the scrollTo method can be called on it.
   * This is populated from the DateSliderComponent.
   *
   * @param {FlatList} flatlist
   *   The FlatList instance
   *
   * @memberof DateSlider
   */
  _captureFlatListRef = flatlist => (this.flatlist = flatlist);

  /**
   * ReactNative method called by the FlatList
   * This needs to be here as it uses viewWidth held in state
   *
   * @param {Object} data
   * @param {number} index
   *
   * @memberof DateSlider
   */
  _getItemLayout = (data, index) => {
    return {
      length: this.state.viewWidth,
      offset: this.state.viewWidth * index,
      index
    };
  };

  /**
   * ReactNative method called by the view containing the FlatList
   * This needs to be here as handles setting the viewWidth
   * based on what the containing view reports.
   *
   * @param {Object} event
   *  The event generated from the RN onLayout call
   *
   * @memberof DateSlider
   */
  _onLayout = event => {
    const nativeEvent = event.nativeEvent;
    this.setState({ viewWidth: nativeEvent.layout.width });
    if (this.viewableIndex === -1) {
      this._scrollTo(this.selectedIndex);
    } else {
      this._scrollTo(this.viewableIndex);
    }
  };

  /**
   * Scrolls the flatlist to the specified index.
   * This wraps the call in a setTimeout(_,0) Promise
   * to stop issues when the index to be scrolled to has not yet been
   * rendered.
   *
   * @param {number} index
   *  The index to scroll the flatlist to
   *
   * @memberof DateSlider
   */
  _scrollTo = index => {
    const scrollToPromise = new Promise(resolve => setTimeout(resolve, 0));
    scrollToPromise.then(() => {
      this.flatlist.scrollToIndex({
        viewPosition: 0,
        index: index,
        animated: false
      });
    });
  };

  _onEndReached = () => {
    const lastDate = this.state.arrDates[this.state.arrDates.length - 1].date;
    const appendWeeksFrom = lastDate.clone().add(1, "weeks");
    const arrNewDates = this._generateDates(
      appendWeeksFrom,
      EXTEND_WEEKS_BY,
      this.state.selectedDate
    );
    this.setState({ arrDates: [...this.state.arrDates, ...arrNewDates] });
  };

  _onStartReached = () => {
    const firstDate = this.state.arrDates[0].date;
    const prependWeeksFrom = firstDate
      .clone()
      .subtract(EXTEND_WEEKS_BY, "weeks");
    const arrNewDates = this._generateDates(
      prependWeeksFrom,
      EXTEND_WEEKS_BY,
      this.state.selectedDate
    );
    this.setState({ arrDates: [...arrNewDates, ...this.state.arrDates] });
    this.selectedIndex = this.selectedIndex + EXTEND_WEEKS_BY;
    this._scrollTo(END_THRESHOLD - 1 + EXTEND_WEEKS_BY);
  };

  _onViewableItemsChanged = ({ viewableItems, changed }) => {
    if (viewableItems.length === 1) {
      const viewableIndex = viewableItems[0].index;
      const changedIndex = changed[0].index;
      this.viewableIndex = viewableIndex;

      if (changedIndex === viewableIndex - 1) {
        // Week forward
        const date = this.state.arrDates[viewableIndex].date;
        this.props.onWeekChanged(date);
      }

      if (changedIndex === viewableIndex + 1) {
        // week back
        const date = this.state.arrDates[viewableIndex].date;
        this.props.onWeekChanged(date);
      }

      if (
        changedIndex === END_THRESHOLD &&
        viewableIndex === changedIndex - 1
      ) {
        this._onStartReached();
      }
    }
  };

  _onDateSelected = date => {
    this._setSelectedDate(date);
    this.props.onDateSelected(date);
  };

  _setSelectedDate = date => {
    const startOfSelectedWeek = DateFns.getStartOfWeek(
      date,
      this.props.isoStartDay
    );

    let arrDates = [...this.state.arrDates];

    const prevSelectedIndex = this.selectedIndex;

    const selectedIndex = _.findIndex(arrDates, [
      "key",
      startOfSelectedWeek.toISOString()
    ]);

    arrDates[prevSelectedIndex].arrSelected = this._generateArrSelected();
    arrDates[selectedIndex].arrSelected = this._generateArrSelected(
      arrDates[selectedIndex].date,
      startOfSelectedWeek,
      date,
      this.props.isoStartDay
    );

    this.selectedIndex = selectedIndex;

    this.setState({
      arrDates: arrDates,
      selectedDate: date
    });
  };
}

export default DateSlider;
<|endoftext|>


<commit_before>/**
 * @author Tzuyang Tsai (shiyou0130011)
 * @license Apache-2.0
 */

/**
 * @constructor
 * @param variables {String|Object} An url string or an object
 */
function URLVariables(variables){
	if(!(this instanceof URLVariables)){
		return new URLVariables(variables)
	}
	switch(typeof variables){
		case "string":
			this.decode(variables)
			break
		case "object":
			for(var i in variables){
				this[i] = variables[i];
			}
			break
	}
}
/**
 * Converts the url string to the specified URLVariables object.
 * @return {URLVariables}	this
 */
URLVariables.prototype.decode = function(source){
	if(typeof source != "string")
		return this

	var url = source.split("?");
	var urlvar = url[url.length-1].split("&");
	for(var i in urlvar){
		var data = urlvar[i].split("=")
		var name = decodeURIComponent(data[0]), value = decodeURIComponent(data[1])
		if(!(this[name] instanceof Array)){
			this[name] = [value]
		}else{
			this[name].push(value);
		}
	}
	return this
}

/**
 * @return The string containing all variables, in the MIME content encoding application/x-www-form-urlencoded.
 */
URLVariables.prototype.toString = function(){
	var urlvar_keys = []
	for(var i in this){
		if(typeof this[i] != "function")
			urlvar_keys.push(i)
	}
	urlvar_keys.sort()

	var l = []
	for(i in urlvar_keys){
		var key = urlvar_keys[i]
		this[key].sort()
		this[key].forEach(function(val, index, array){
			l.push(encodeURIComponent(urlvar_keys[i]) + "=" + encodeURIComponent(val))
		})
	}
	return l.join("&")
}<commit_msg>忘記有設variable key<commit_after>/**
 * @author Tzuyang Tsai (shiyou0130011)
 * @license Apache-2.0
 */

/**
 * @constructor
 * @param variables {String|Object} An url string or an object
 */
function URLVariables(variables){
	if(!(this instanceof URLVariables)){
		return new URLVariables(variables)
	}
	switch(typeof variables){
		case "string":
			this.decode(variables)
			break
		case "object":
			for(var i in variables){
				this[i] = variables[i];
			}
			break
	}
}
/**
 * Converts the url string to the specified URLVariables object.
 * @return {URLVariables}	this
 */
URLVariables.prototype.decode = function(source){
	if(typeof source != "string")
		return this

	var url = source.split("?");
	var urlvar = url[url.length-1].split("&");
	for(var i in urlvar){
		var data = urlvar[i].split("=")
		var name = decodeURIComponent(data[0]), value = decodeURIComponent(data[1])
		if(!(this[name] instanceof Array)){
			this[name] = [value]
		}else{
			this[name].push(value);
		}
	}
	return this
}

/**
 * @return The string containing all variables, in the MIME content encoding application/x-www-form-urlencoded.
 */
URLVariables.prototype.toString = function(){
	var urlvar_keys = []
	for(var i in this){
		if(typeof this[i] != "function")
			urlvar_keys.push(i)
	}
	urlvar_keys.sort()

	var l = []
	for(i in urlvar_keys){
		var key = urlvar_keys[i]
		this[key].sort()
		this[key].forEach(function(val, index, array){
			l.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
		})
	}
	return l.join("&")
}<|endoftext|>


<commit_before>import {bindable, customAttribute} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding';
import {TaskQueue} from 'aurelia-task-queue';
import {inject} from 'aurelia-dependency-injection';
import {getLogger} from 'aurelia-logging';
import {getBooleanFromAttributeValue} from '../common/attributes';
import {DatePickerDefaultParser} from './datepicker-default-parser';

@inject(Element, TaskQueue, DatePickerDefaultParser)
@customAttribute('md-datepicker')
export class MdDatePicker {
  @bindable() container;
  @bindable() translation;
  @bindable({defaultBindingMode: bindingMode.twoWay}) value;
  @bindable({defaultBindingMode: bindingMode.twoWay}) parsers = [];
  @bindable({defaultBindingMode: bindingMode.oneTime}) selectMonths = true;
  @bindable({defaultBindingMode: bindingMode.oneTime}) selectYears = 15;
  @bindable({defaultBindingMode: bindingMode.oneTime}) options = {};

  constructor(element, taskQueue, defaultParser) {
    this.element = element;
    this.log = getLogger('md-datepicker');
    this.taskQueue = taskQueue;
    this.parsers.push(defaultParser);
  }

  bind() {
    this.selectMonths = getBooleanFromAttributeValue(this.selectMonths);
    this.selectYears = parseInt(this.selectYears, 10);
    this.element.classList.add('date-picker');

    let options = {
      selectMonths: this.selectMonths,
      selectYears: this.selectYears,
      onClose: function() {
        // see https://github.com/Dogfalo/materialize/issues/2067
        // and: https://github.com/amsul/pickadate.js/issues/160
        $(document.activeElement).blur();
        // $(this.element).blur();
      }
    };
    let i18n = {};
    // let i18n = {
    //   selectMonths: true, // Creates a dropdown to control month
    //   selectYears: 15, // Creates a dropdown of 15 years to control year
    //   monthsFull: [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
    //   monthsShort: [ 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez' ],
    //   weekdaysFull: [ 'Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag' ],
    //   weekdaysShort: [ 'So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa' ],
    //   today: 'Heute',
    //   clear: 'Löschen',
    //   close: 'Schließen',
    //   firstDay: 1,
    //   format: 'dddd, dd. mmmm yyyy',
    //   formatSubmit: 'yyyy/mm/dd'
    // };
    Object.assign(options, i18n);

    if (this.options) {
      Object.assign(options, this.options);
      //merge callback methods if there is a hook in the advanced options
      if (this.options.onClose) {
        options.onClose = function() {
          this.options.onClose();
          $(document.activeElement).blur();
        };
      }
    }
    if (this.container) {
      options.container = this.container;
    }
    this.picker = $(this.element).pickadate(options).pickadate('picker');
    this.picker.on({
      'close': this.onClose.bind(this),
      'set': this.onSet.bind(this)
    });

    if (this.value) {
      this.picker.set('select', this.value);
    }
    if (this.options && this.options.editable) {
      $(this.element).on('keydown', (e)=> {
        if (e.keyCode === 13 || e.keyCode === 9) {
          if (this.parseDate($(this.element).val())) {
            this.closeDatePicker();
          } else {
            this.openDatePicker();
          }
        } else {
          this.value = null;
        }
      });
    } else {
      $(this.element).on('focusin', () => {
        this.openDatePicker();
      });
    }
    if (this.options.showIcon) {
      this.element.classList.add('left');
      let calendarIcon = document.createElement('i');
      calendarIcon.classList.add('right');
      calendarIcon.classList.add('material-icons');
      calendarIcon.textContent = 'today';
      this.element.parentNode.insertBefore(calendarIcon, this.element.nextSibling);
      $(calendarIcon).on('click', this.onCalendarIconClick.bind(this));
    }

    this.movePickerCloserToSrc();
  }

  parseDate(value) {
    if (this.parsers && this.parsers.length && this.parsers.length > 0) {
      for (const parser of this.parsers) {
        if (parser.canParse(value)) {
          const parsedDate = parser.parse(value);
          if (parsedDate !== null) {
            this.picker.set('select', parsedDate);
            return true;
          }
        }
      }
    }
    return false;
  }

  movePickerCloserToSrc() {
    $(this.picker.$root).appendTo($(this.element).parent());
  }

  detached() {
    if (this.picker) {
      this.picker.stop();
    }
  }

  openDatePicker() {
    $(this.element).pickadate('open');
  }

  closeDatePicker() {
    $(this.element).pickadate('close');
  }

  onClose() {
    let selected = this.picker.get('select');
    this.value = selected ? selected.obj : null;
  }

  onCalendarIconClick(event) {
    event.stopPropagation();
    this.openDatePicker();
  }

  onSet(value) {
    //handle this ourselves since Dogfalo removed this functionality from the original plugin
    if (this.options && this.options.closeOnSelect && value.select) {
      this.value = value.select;
      this.picker.close();
    }
    // this.value = new Date(value.select);
  }

  valueChanged(newValue) {
    if (this.options.max && newValue > this.options.max) {
      this.value = this.options.max;
    }
    this.log.debug('selectedChanged', this.value);
    // this.taskQueue.queueTask(() => {
    this.picker.set('select', this.value);
    // });
  }

}
<commit_msg>Add the ability to bind the showErrortext data attribute to the input type date.<commit_after>import {bindable, customAttribute} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding';
import {TaskQueue} from 'aurelia-task-queue';
import {inject} from 'aurelia-dependency-injection';
import {getLogger} from 'aurelia-logging';
import {getBooleanFromAttributeValue} from '../common/attributes';
import {DatePickerDefaultParser} from './datepicker-default-parser';

@inject(Element, TaskQueue, DatePickerDefaultParser)
@customAttribute('md-datepicker')
export class MdDatePicker {
  @bindable() container;
  @bindable() translation;
  @bindable({defaultBindingMode: bindingMode.twoWay}) value;
  @bindable({defaultBindingMode: bindingMode.twoWay}) parsers = [];
  @bindable({defaultBindingMode: bindingMode.oneTime}) selectMonths = true;
  @bindable({defaultBindingMode: bindingMode.oneTime}) selectYears = 15;
  @bindable({defaultBindingMode: bindingMode.oneTime}) options = {};
  @bindable() showErrortext = true;

  constructor(element, taskQueue, defaultParser) {
    this.element = element;
    this.log = getLogger('md-datepicker');
    this.taskQueue = taskQueue;
    this.parsers.push(defaultParser);
  }

  bind() {
    this.selectMonths = getBooleanFromAttributeValue(this.selectMonths);
    this.selectYears = parseInt(this.selectYears, 10);
    this.element.classList.add('date-picker');

    let options = {
      selectMonths: this.selectMonths,
      selectYears: this.selectYears,
      onClose: function() {
        // see https://github.com/Dogfalo/materialize/issues/2067
        // and: https://github.com/amsul/pickadate.js/issues/160
        $(document.activeElement).blur();
        // $(this.element).blur();
      }
    };
    let i18n = {};
    // let i18n = {
    //   selectMonths: true, // Creates a dropdown to control month
    //   selectYears: 15, // Creates a dropdown of 15 years to control year
    //   monthsFull: [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
    //   monthsShort: [ 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez' ],
    //   weekdaysFull: [ 'Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag' ],
    //   weekdaysShort: [ 'So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa' ],
    //   today: 'Heute',
    //   clear: 'Löschen',
    //   close: 'Schließen',
    //   firstDay: 1,
    //   format: 'dddd, dd. mmmm yyyy',
    //   formatSubmit: 'yyyy/mm/dd'
    // };
    Object.assign(options, i18n);

    if (this.options) {
      Object.assign(options, this.options);
      //merge callback methods if there is a hook in the advanced options
      if (this.options.onClose) {
        options.onClose = function() {
          this.options.onClose();
          $(document.activeElement).blur();
        };
      }
    }
    if (this.container) {
      options.container = this.container;
    }
    this.picker = $(this.element).pickadate(options).pickadate('picker');
    this.picker.on({
      'close': this.onClose.bind(this),
      'set': this.onSet.bind(this)
    });

    if (this.value) {
      this.picker.set('select', this.value);
    }
    if (this.options && this.options.editable) {
      $(this.element).on('keydown', (e)=> {
        if (e.keyCode === 13 || e.keyCode === 9) {
          if (this.parseDate($(this.element).val())) {
            this.closeDatePicker();
          } else {
            this.openDatePicker();
          }
        } else {
          this.value = null;
        }
      });
    } else {
      $(this.element).on('focusin', () => {
        this.openDatePicker();
      });
    }
    if (this.options.showIcon) {
      this.element.classList.add('left');
      let calendarIcon = document.createElement('i');
      calendarIcon.classList.add('right');
      calendarIcon.classList.add('material-icons');
      calendarIcon.textContent = 'today';
      this.element.parentNode.insertBefore(calendarIcon, this.element.nextSibling);
      $(calendarIcon).on('click', this.onCalendarIconClick.bind(this));
    }

    this.movePickerCloserToSrc();
    this.setErrorTextAttribute();
  }

  parseDate(value) {
    if (this.parsers && this.parsers.length && this.parsers.length > 0) {
      for (const parser of this.parsers) {
        if (parser.canParse(value)) {
          const parsedDate = parser.parse(value);
          if (parsedDate !== null) {
            this.picker.set('select', parsedDate);
            return true;
          }
        }
      }
    }
    return false;
  }

  movePickerCloserToSrc() {
    $(this.picker.$root).appendTo($(this.element).parent());
  }

  detached() {
    if (this.picker) {
      this.picker.stop();
    }
  }

  openDatePicker() {
    $(this.element).pickadate('open');
  }

  closeDatePicker() {
    $(this.element).pickadate('close');
  }

  onClose() {
    let selected = this.picker.get('select');
    this.value = selected ? selected.obj : null;
  }

  onCalendarIconClick(event) {
    event.stopPropagation();
    this.openDatePicker();
  }

  onSet(value) {
    //handle this ourselves since Dogfalo removed this functionality from the original plugin
    if (this.options && this.options.closeOnSelect && value.select) {
      this.value = value.select;
      this.picker.close();
    }
    // this.value = new Date(value.select);
  }

  valueChanged(newValue) {
    if (this.options.max && newValue > this.options.max) {
      this.value = this.options.max;
    }
    this.log.debug('selectedChanged', this.value);
    // this.taskQueue.queueTask(() => {
    this.picker.set('select', this.value);
    // });
  }

  showErrortextChanged() {
    this.setErrorTextAttribute();
  }

  setErrorTextAttribute() {
    const element = this.element;
    if (!element) return;
    this.log.debug('showErrortextChanged: ' + this.showErrortext);
    element.setAttribute('data-show-errortext', getBooleanFromAttributeValue(this.showErrortext));
  }
}
<|endoftext|>


<commit_before>var Crafty = require('./core.js'),
    document = window.document;

/**@
 * #DebugCanvas
 * @category Debug
 * @trigger Draw - when the entity is ready to be drawn to the stage
 * @trigger NoCanvas - if the browser does not support canvas
 *
 * When this component is added to an entity it will be drawn by the DebugCanvas layer.
 *
 * Crafty.debugCanvas.init() will be automatically called if it is not called already to initialize the canvas element.
 *
 * To visualise an object's MBR, use "VisibleMBR".  To visualise a "Collision" object's hitbox, use "WiredHitBox" or "SolidHitBox".
 * @see DebugPolygon,  DebugRectangle
 */
Crafty.c("DebugCanvas", {
    init: function () {
        this.requires("2D");
        if (!Crafty.DebugCanvas.context)
            Crafty.DebugCanvas.init();
        Crafty.DebugCanvas.add(this);
        this._debug = {
            alpha: 1.0,
            lineWidth: 1
        };
        this.bind("RemoveComponent", this.onDebugRemove);
        this.bind("Remove", this.onDebugDestroy);
    },

    // When component is removed
    onDebugRemove: function (id) {
        if (id === "DebugCanvas") {
            Crafty.DebugCanvas.remove(this);
        }
    },

    //When entity is destroyed
    onDebugDestroy: function (id) {
        Crafty.DebugCanvas.remove(this);
    },

    /**@
     * #.debugAlpha
     * @comp DebugCanvas
     * @sign public  .debugAlpha(Number alpha)
     * @param alpha - The alpha level the component will be drawn with
     */
    debugAlpha: function (alpha) {
        this._debug.alpha = alpha;
        return this;
    },

    /**@
     * #.debugFill
     * @comp DebugCanvas
     * @sign public  .debugFill([String fillStyle])
     * @param fillStyle - The color the component will be filled with.  Defaults to "red". Pass the boolean false to turn off filling.
     * ~~~
     * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple")
     *~~~
     */
    debugFill: function (fillStyle) {
        if (typeof fillStyle === 'undefined')
            fillStyle = "red";
        this._debug.fillStyle = fillStyle;
        return this;
    },

    /**@
     * #.debugStroke
     * @comp DebugCanvas
     * @sign public  .debugStroke([String strokeStyle])
     * @param strokeStyle - The color the component will be outlined with.  Defaults to "red".  Pass the boolean false to turn this off.
     * ~~~
     * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white")
     *~~~
     */
    debugStroke: function (strokeStyle) {
        if (typeof strokeStyle === 'undefined')
            strokeStyle = "red";
        this._debug.strokeStyle = strokeStyle;
        return this;
    },

    debugDraw: function (ctx) {
        var ga = ctx.globalAlpha;
        var props = this._debug;

        if (props.alpha)
            ctx.globalAlpha = this._debug.alpha;

        if (props.strokeStyle)
            ctx.strokeStyle = props.strokeStyle;

        if (props.lineWidth)
            ctx.lineWidth = props.lineWidth;

        if (props.fillStyle)
            ctx.fillStyle = props.fillStyle;

        this.trigger("DebugDraw");

        ctx.globalAlpha = ga;

    }

});

/**@
 * #DebugRectangle
 * @category Debug
 *
 * A component for rendering an object with a position and dimensions to the debug canvas.
 *
 *
 * ~~~
 * var myEntity = Crafty.e("2D, DebugRectangle")
 *                      .attr({x: 13, y: 37, w: 42, h: 42})
 *                      .debugStroke("green");
 * myEntity.debugRectangle(myEntity)
 *~~~
 * @see DebugCanvas
 */
Crafty.c("DebugRectangle", {
    init: function () {
        this.requires("2D, DebugCanvas");
    },

    /**@
     * #.debugRectangle
     * @comp DebugRectangle
     * @sign public  .debugRectangle(Object rect)
     * @param rect - an object with _x, _y, _w, and _h to draw
     * Sets the rectangle that this component draws to the debug canvas.
     *
     */
    debugRectangle: function (rect) {
        this.debugRect = rect;
        this.unbind("DebugDraw", this.drawDebugRect);
        this.bind("DebugDraw", this.drawDebugRect);
        return this;

    },

    drawDebugRect: function () {

        ctx = Crafty.DebugCanvas.context;
        var rect = this.debugRect;
        if (rect === null || rect === undefined)
            return;
        if (rect._h && rect._w) {
            if (this._debug.fillStyle)
                ctx.fillRect(rect._x, rect._y, rect._w, rect._h);
            if (this._debug.strokeStyle)
                ctx.strokeRect(rect._x, rect._y, rect._w, rect._h);
        }

    }

});

/**@
 * #VisibleMBR
 * @category Debug
 *
 * Adding this component to an entity will cause it's MBR to be drawn to the debug canvas.
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see 2D, DebugRectangle, DebugCanvas
 */
Crafty.c("VisibleMBR", {
    init: function () {
        this.requires("DebugRectangle")
            .debugFill("purple")
            .bind("EnterFrame", this._assignRect);
    },

    // Internal method for updating the MBR drawn.
    _assignRect: function () {
        if (this._mbr)
            this.debugRectangle(this._mbr);
        else
            this.debugRectangle(this);

    }

});

/**@
 * #DebugPolygon
 * @category Debug
 *
 * For drawing a polygon to the debug canvas
 *
 * The methods of DebugCanvas can be used to control this component's appearance -- by default it is neither filled nor outlined
 *
 * For debugging hitboxes, use WiredHitBox or SolidHitBox.  For debugging MBR, use VisibleMBR
 *
 * @see DebugCanvas
 */
Crafty.c("DebugPolygon", {
    init: function () {
        this.requires("2D, DebugCanvas");
    },

    /**@
     * #.debugPolygon
     * @comp DebugPolygon
     * @sign public  .debugPolygon(Polygon poly)
     * @param poly - a polygon to render
     * Sets the polygon that this component renders to the debug canvas.
     *
     */
    debugPolygon: function (poly) {
        this.polygon = poly;
        this.unbind("DebugDraw", this.drawDebugPolygon);
        this.bind("DebugDraw", this.drawDebugPolygon);
        return this;
    },

    drawDebugPolygon: function () {
        ctx = Crafty.DebugCanvas.context;
        ctx.beginPath();
        for (var p in this.polygon.points) {
            ctx.lineTo(this.map.points[p][0], this.map.points[p][1]);
        }
        ctx.closePath();

        if (this._debug.fillStyle)
            ctx.fill();
        if (this._debug.strokeStyle)
            ctx.stroke();
    }
});

/**@
 * #WiredHitBox
 * @category Debug
 *
 * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas as an outline
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see DebugPolygon, DebugCanvas
 */
Crafty.c("WiredHitBox", {
    init: function () {
        this.requires("DebugPolygon")
            .debugStroke("red")
            .matchHitBox();
        this.bind("NewHitbox", this.matchHitBox);
    },
    matchHitBox: function () {
        this.debugPolygon(this.map);
    }
});

/**@
 * #SolidHitBox
 * @category Debug
 *
 * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas, with a default alpha level of 0.7.
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see DebugPolygon, DebugCanvas
 */
Crafty.c("SolidHitBox", {
    init: function () {
        this.requires("Collision, DebugPolygon")
            .debugFill("orange").debugAlpha(0.7)
            .matchHitBox();
        this.bind("NewHitbox", this.matchHitBox);
    },
    matchHitBox: function () {
        this.debugPolygon(this.map);
    }
});

Crafty.DebugCanvas = {
    context: null,
    entities: [],
    onetimeEntities: [],
    add: function (ent) {
        this.entities.push(ent);
    },

    remove: function (ent) {
        var list = this.entities;
        for (var i = list.length - 1; i >= 0; i--)
            if (list[i] == ent)
                list.splice(i, 1);

    },

    // Mostly copied from canvas.init()
    // Called the first time a "DebugCanvas" component is added to an entity
    // We should consider how to abstract the idea of multiple canvases
    init: function () {
        if (!Crafty.DebugCanvas.context) {
            //check if canvas is supported
            if (!Crafty.support.canvas) {
                Crafty.trigger("NoCanvas");
                Crafty.stop();
                return;
            }

            //create an empty canvas element
            var c;
            c = document.createElement("canvas");
            c.width = Crafty.viewport.width;
            c.height = Crafty.viewport.height;
            c.style.position = 'absolute';
            c.style.left = "0px";
            c.style.top = "0px";
            c.id = "debug-canvas";
            // The debug canvas should be on the very top; the highest a regular zindex can get is ~10000
            c.style.zIndex = 100000;

            Crafty.stage.elem.appendChild(c);
            Crafty.DebugCanvas.context = c.getContext('2d');
            Crafty.DebugCanvas._canvas = c;

        }
        //Bind rendering of canvas context (see drawing.js)
        Crafty.unbind("RenderScene", Crafty.DebugCanvas.renderScene);
        Crafty.bind("RenderScene", Crafty.DebugCanvas.renderScene);

    },

    // copied from drawAll()
    renderScene: function (rect) {
        rect = rect || Crafty.viewport.rect();
        var q = Crafty.DebugCanvas.entities,
            i = 0,
            l = q.length,
            ctx = Crafty.DebugCanvas.context,
            current;

        var view = Crafty.viewport;
        ctx.setTransform(view._scale, 0, 0, view._scale, view._x, view._y);

        ctx.clearRect(rect._x, rect._y, rect._w, rect._h);

        //sort the objects by the global Z
        //q.sort(zsort);
        for (; i < l; i++) {
            current = q[i];
            current.debugDraw(ctx);
        }

    }

};
<commit_msg>Logic error in "DebugPolygon"<commit_after>var Crafty = require('./core.js'),
    document = window.document;

/**@
 * #DebugCanvas
 * @category Debug
 * @trigger Draw - when the entity is ready to be drawn to the stage
 * @trigger NoCanvas - if the browser does not support canvas
 *
 * When this component is added to an entity it will be drawn by the DebugCanvas layer.
 *
 * Crafty.debugCanvas.init() will be automatically called if it is not called already to initialize the canvas element.
 *
 * To visualise an object's MBR, use "VisibleMBR".  To visualise a "Collision" object's hitbox, use "WiredHitBox" or "SolidHitBox".
 * @see DebugPolygon,  DebugRectangle
 */
Crafty.c("DebugCanvas", {
    init: function () {
        this.requires("2D");
        if (!Crafty.DebugCanvas.context)
            Crafty.DebugCanvas.init();
        Crafty.DebugCanvas.add(this);
        this._debug = {
            alpha: 1.0,
            lineWidth: 1
        };
        this.bind("RemoveComponent", this.onDebugRemove);
        this.bind("Remove", this.onDebugDestroy);
    },

    // When component is removed
    onDebugRemove: function (id) {
        if (id === "DebugCanvas") {
            Crafty.DebugCanvas.remove(this);
        }
    },

    //When entity is destroyed
    onDebugDestroy: function (id) {
        Crafty.DebugCanvas.remove(this);
    },

    /**@
     * #.debugAlpha
     * @comp DebugCanvas
     * @sign public  .debugAlpha(Number alpha)
     * @param alpha - The alpha level the component will be drawn with
     */
    debugAlpha: function (alpha) {
        this._debug.alpha = alpha;
        return this;
    },

    /**@
     * #.debugFill
     * @comp DebugCanvas
     * @sign public  .debugFill([String fillStyle])
     * @param fillStyle - The color the component will be filled with.  Defaults to "red". Pass the boolean false to turn off filling.
     * ~~~
     * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple")
     *~~~
     */
    debugFill: function (fillStyle) {
        if (typeof fillStyle === 'undefined')
            fillStyle = "red";
        this._debug.fillStyle = fillStyle;
        return this;
    },

    /**@
     * #.debugStroke
     * @comp DebugCanvas
     * @sign public  .debugStroke([String strokeStyle])
     * @param strokeStyle - The color the component will be outlined with.  Defaults to "red".  Pass the boolean false to turn this off.
     * ~~~
     * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white")
     *~~~
     */
    debugStroke: function (strokeStyle) {
        if (typeof strokeStyle === 'undefined')
            strokeStyle = "red";
        this._debug.strokeStyle = strokeStyle;
        return this;
    },

    debugDraw: function (ctx) {
        var ga = ctx.globalAlpha;
        var props = this._debug;

        if (props.alpha)
            ctx.globalAlpha = this._debug.alpha;

        if (props.strokeStyle)
            ctx.strokeStyle = props.strokeStyle;

        if (props.lineWidth)
            ctx.lineWidth = props.lineWidth;

        if (props.fillStyle)
            ctx.fillStyle = props.fillStyle;

        this.trigger("DebugDraw");

        ctx.globalAlpha = ga;

    }

});

/**@
 * #DebugRectangle
 * @category Debug
 *
 * A component for rendering an object with a position and dimensions to the debug canvas.
 *
 *
 * ~~~
 * var myEntity = Crafty.e("2D, DebugRectangle")
 *                      .attr({x: 13, y: 37, w: 42, h: 42})
 *                      .debugStroke("green");
 * myEntity.debugRectangle(myEntity)
 *~~~
 * @see DebugCanvas
 */
Crafty.c("DebugRectangle", {
    init: function () {
        this.requires("2D, DebugCanvas");
    },

    /**@
     * #.debugRectangle
     * @comp DebugRectangle
     * @sign public  .debugRectangle(Object rect)
     * @param rect - an object with _x, _y, _w, and _h to draw
     * Sets the rectangle that this component draws to the debug canvas.
     *
     */
    debugRectangle: function (rect) {
        this.debugRect = rect;
        this.unbind("DebugDraw", this.drawDebugRect);
        this.bind("DebugDraw", this.drawDebugRect);
        return this;

    },

    drawDebugRect: function () {

        ctx = Crafty.DebugCanvas.context;
        var rect = this.debugRect;
        if (rect === null || rect === undefined)
            return;
        if (rect._h && rect._w) {
            if (this._debug.fillStyle)
                ctx.fillRect(rect._x, rect._y, rect._w, rect._h);
            if (this._debug.strokeStyle)
                ctx.strokeRect(rect._x, rect._y, rect._w, rect._h);
        }

    }

});

/**@
 * #VisibleMBR
 * @category Debug
 *
 * Adding this component to an entity will cause it's MBR to be drawn to the debug canvas.
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see 2D, DebugRectangle, DebugCanvas
 */
Crafty.c("VisibleMBR", {
    init: function () {
        this.requires("DebugRectangle")
            .debugFill("purple")
            .bind("EnterFrame", this._assignRect);
    },

    // Internal method for updating the MBR drawn.
    _assignRect: function () {
        if (this._mbr)
            this.debugRectangle(this._mbr);
        else
            this.debugRectangle(this);

    }

});

/**@
 * #DebugPolygon
 * @category Debug
 *
 * For drawing a polygon to the debug canvas
 *
 * The methods of DebugCanvas can be used to control this component's appearance -- by default it is neither filled nor outlined
 *
 * For debugging hitboxes, use WiredHitBox or SolidHitBox.  For debugging MBR, use VisibleMBR
 *
 * @see DebugCanvas
 */
Crafty.c("DebugPolygon", {
    init: function () {
        this.requires("2D, DebugCanvas");
    },

    /**@
     * #.debugPolygon
     * @comp DebugPolygon
     * @sign public  .debugPolygon(Polygon poly)
     * @param poly - a polygon to render
     * Sets the polygon that this component renders to the debug canvas.
     *
     */
    debugPolygon: function (poly) {
        this.polygon = poly;
        this.unbind("DebugDraw", this.drawDebugPolygon);
        this.bind("DebugDraw", this.drawDebugPolygon);
        return this;
    },

    drawDebugPolygon: function () {
        if (typeof this.polygon === "undefined")
            return;

        ctx = Crafty.DebugCanvas.context;
        ctx.beginPath();
        for (var p in this.polygon.points) {
            ctx.lineTo(this.polygon.points[p][0], this.polygon.points[p][1]);
        }
        ctx.closePath();

        if (this._debug.fillStyle)
            ctx.fill();
        if (this._debug.strokeStyle)
            ctx.stroke();
    }
});

/**@
 * #WiredHitBox
 * @category Debug
 *
 * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas as an outline
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see DebugPolygon, DebugCanvas
 */
Crafty.c("WiredHitBox", {
    init: function () {
        this.requires("DebugPolygon")
            .debugStroke("red")
            .matchHitBox();
        this.bind("NewHitbox", this.matchHitBox);
    },
    matchHitBox: function () {
        this.debugPolygon(this.map);
    }
});

/**@
 * #SolidHitBox
 * @category Debug
 *
 * Adding this component to an entity with a Collision component will cause its collision polygon to be drawn to the debug canvas, with a default alpha level of 0.7.
 *
 * The methods of DebugCanvas can be used to control this component's appearance.
 * @see DebugPolygon, DebugCanvas
 */
Crafty.c("SolidHitBox", {
    init: function () {
        this.requires("Collision, DebugPolygon")
            .debugFill("orange").debugAlpha(0.7)
            .matchHitBox();
        this.bind("NewHitbox", this.matchHitBox);
    },
    matchHitBox: function () {
        this.debugPolygon(this.map);
    }
});

Crafty.DebugCanvas = {
    context: null,
    entities: [],
    onetimeEntities: [],
    add: function (ent) {
        this.entities.push(ent);
    },

    remove: function (ent) {
        var list = this.entities;
        for (var i = list.length - 1; i >= 0; i--)
            if (list[i] == ent)
                list.splice(i, 1);

    },

    // Mostly copied from canvas.init()
    // Called the first time a "DebugCanvas" component is added to an entity
    // We should consider how to abstract the idea of multiple canvases
    init: function () {
        if (!Crafty.DebugCanvas.context) {
            //check if canvas is supported
            if (!Crafty.support.canvas) {
                Crafty.trigger("NoCanvas");
                Crafty.stop();
                return;
            }

            //create an empty canvas element
            var c;
            c = document.createElement("canvas");
            c.width = Crafty.viewport.width;
            c.height = Crafty.viewport.height;
            c.style.position = 'absolute';
            c.style.left = "0px";
            c.style.top = "0px";
            c.id = "debug-canvas";
            // The debug canvas should be on the very top; the highest a regular zindex can get is ~10000
            c.style.zIndex = 100000;

            Crafty.stage.elem.appendChild(c);
            Crafty.DebugCanvas.context = c.getContext('2d');
            Crafty.DebugCanvas._canvas = c;

        }
        //Bind rendering of canvas context (see drawing.js)
        Crafty.unbind("RenderScene", Crafty.DebugCanvas.renderScene);
        Crafty.bind("RenderScene", Crafty.DebugCanvas.renderScene);

    },

    // copied from drawAll()
    renderScene: function (rect) {
        rect = rect || Crafty.viewport.rect();
        var q = Crafty.DebugCanvas.entities,
            i = 0,
            l = q.length,
            ctx = Crafty.DebugCanvas.context,
            current;

        var view = Crafty.viewport;
        ctx.setTransform(view._scale, 0, 0, view._scale, view._x, view._y);

        ctx.clearRect(rect._x, rect._y, rect._w, rect._h);

        //sort the objects by the global Z
        //q.sort(zsort);
        for (; i < l; i++) {
            current = q[i];
            current.debugDraw(ctx);
        }

    }

};
<|endoftext|>


<commit_before>import React from 'react';
import { Link } from 'react-router';

import Icon from '../widgets/Icon';
import Avatar from '../widgets/Avatar';

import './UserList.scss';

export default class UserList extends React.Component {
  render() {
    return (

        <Avatar md username={this.props.username} className="mrm" />
        <Link to={`/@${this.props.username}`}>@{this.props.username}</Link>{ ' ' }
        <Icon name="person_add" />

    );
  }
};
<commit_msg>Remove add follower button in user list<commit_after>import React from 'react';
import { Link } from 'react-router';

import Icon from '../widgets/Icon';
import Avatar from '../widgets/Avatar';

import './UserList.scss';

export default class UserList extends React.Component {
  render() {
    return (

        <Avatar md username={this.props.username} className="mrm" />
        <Link to={`/@${this.props.username}`}>@{this.props.username}</Link>

    );
  }
};
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')
const Discord = require('discord.js')
const createLogger = require('./logger/create.js')
const channelTracker = require('../util/channelTracker.js')
const storage = require('./storage.js')
const getConfig = require('../config.js').get
const MANAGE_CHANNELS_PERM = Discord.Permissions.FLAGS.MANAGE_CHANNELS
const EMBED_LINKS_PERM = Discord.Permissions.FLAGS.EMBED_LINKS
const MANAGE_ROLES_OR_PERMISSIONS_PERM = Discord.Permissions.FLAGS.MANAGE_ROLES

const PERMISSIONS = Object.keys(Discord.Permissions.FLAGS)

const loadOwnerCommand = async (file, message) => {
  const func = require(`../commands/owner/${file}.js`)
  await func(message)
}

const loadCommand = async (file, message, name) => {
  const func = require(`../commands/${file}.js`)
  await func(message, name)
}

const list = {
  help: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  add: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  remove: {
    initLevel: 2,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  list: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  text: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  embed: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  filters: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  date: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  mention: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  test: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  sub: {
    initLevel: 1,
    botPerm: MANAGE_ROLES_OR_PERMISSIONS_PERM
  },
  unsub: {
    initLevel: 1,
    botPerm: MANAGE_ROLES_OR_PERMISSIONS_PERM
  },
  refresh: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  options: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  split: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  move: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM,
    botPerm: EMBED_LINKS_PERM
  },
  clone: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  backup: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  dump: {
    initLevel: 1,
    botPerm: ['ATTACH_FILES', EMBED_LINKS_PERM],
    userPerm: MANAGE_CHANNELS_PERM
  },
  stats: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  webhook: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  prefix: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  alert: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  locale: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  invite: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  version: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  patron: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  compare: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  }
}

exports.list = list
exports.has = message => {
  const config = getConfig()
  const first = message.content.split(' ')[0]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  return list.hasOwnProperty(first.substr(prefix.length))
}
exports.run = async message => {
  const config = getConfig()
  const log = createLogger(message.guild.shard.id)
  const bot = message.client
  const first = message.content.split(' ')[0]
  const guildPrefix = storage.prefixes[message.guild.id]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  let name = first.substr(prefix.length)
  if (!list.hasOwnProperty(name)) {
    return log.error({
      guild: message.guild
    }, `Failed to run ${name} - nonexistent command`)
  }

  const cmdInfo = list[name]
  const channel = message.channel
  const guild = bot.guilds.cache.get(channel.guild.id)
  if (cmdInfo && cmdInfo.aliasFor) name = cmdInfo.aliasFor
  if (!cmdInfo) {
    return log.error({
      guild: message.guild
    }, `Could not run command "${name}" because command data does not exist`)
  }
  const botPerm = cmdInfo.botPerm
  const userPerm = cmdInfo.userPerm

  try {
    if (guildPrefix && !message.content.startsWith(guildPrefix)) {
      await message.channel.send(`Invalid command prefix. You are not using the prefix you set for your server (${guildPrefix}).`)
      return log.info({
        guild: message.guild
      }, `Ignoring command ${name} due to incorrect prefix (${prefix})`)
    } else if (!guildPrefix && !message.content.startsWith(config.bot.prefix)) {
      return
    }
    log.info({
      guild: message.guild
    }, `Used ${message.content}`)
    if (cmdInfo.initLevel !== undefined && cmdInfo.initLevel > storage.initialized) {
      const m = await message.channel.send(`This command is disabled while booting up, please wait.`)
      await m.delete({ timeout: 4000 })
    }

    // Check bot perm
    let botPermitted
    if (typeof botPerm === 'string') {
      botPermitted = PERMISSIONS.includes(botPerm) && !guild.members.cache.get(bot.user.id).permissionsIn(channel).has(botPerm)
    } else if (Array.isArray(botPerm)) {
      for (var i = 0; i < botPerm.length; ++i) {
        const thisPerm = PERMISSIONS.includes(botPerm) && !guild.members.cache.get(bot.user.id).permissionsIn(channel).has(botPerm)
        botPermitted = botPermitted === undefined ? thisPerm : botPermitted && thisPerm
      }
    }

    if (botPermitted) {
      log.info({
        guild: message.guild
      }, `Missing bot permission ${botPerm} for bot, blocked ${message.content}`)
      return await message.channel.send(`This command has been disabled due to missing bot permission \`${botPerm}\`.`)
    }

    // Check user perm
    const member = await message.guild.members.fetch(message.author)
    if (!message.member) message.member = member

    if (!userPerm || !PERMISSIONS.includes(userPerm) || config.bot.ownerIDs.includes(message.author.id)) {
      return loadCommand(name, message, name)
    }
    const serverPerm = member.permissions.has(userPerm)
    const channelPerm = member.permissionsIn(channel).has(userPerm)

    if (serverPerm || channelPerm) {
      return loadCommand(name, message, name)
    }
    log.info({
      user: message.author,
      guild: message.guild
    }, `Missing user permissions for blocked ${message.content}`)
    await message.channel.send(`You do not have the permission \`${userPerm}\` to use this command.`)
  } catch (err) {
    channelTracker.remove(message.channel.id)
    if (err.code !== 50013) {
      log.error(err, `Command ${name}`)
      message.channel.send(err.message)
        .catch(err => log.error(err, `Failed to send error message to channel for command ${name}`))
    } else {
      log.warn(err, `Command ${name} (non-50013 error)`)
    }
  }
}

exports.runOwner = async message => {
  const config = getConfig()
  const first = message.content.split(' ')[0]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  const command = first.substr(prefix.length)
  if (!message.content.startsWith(prefix)) {
    return
  }
  if (!fs.existsSync(path.join(__dirname, '..', 'commands', 'owner', `${command}.js`))) {
    return
  }
  if (storage.initialized < 2) {
    return message.channel.send(`This command is disabled while booting up, please wait.`).then(m => m.delete(4000))
  }
  try {
    await loadOwnerCommand(command, message)
  } catch (err) {
    const log = createLogger(message.guild.shard.id)
    if (err.code !== 50013) {
      log.error(err, `Owner Command ${command}`)
      message.channel.send(err.message)
        .catch(err => log.error(err, `Failed to send error message to channel for command ${command}`))
    } else {
      log.warn(err, `Owner Command ${command} (non-50013 error)`)
    }
  }
}
<commit_msg>Fix unhandled errors for commands<commit_after>const fs = require('fs')
const path = require('path')
const Discord = require('discord.js')
const createLogger = require('./logger/create.js')
const channelTracker = require('../util/channelTracker.js')
const storage = require('./storage.js')
const getConfig = require('../config.js').get
const MANAGE_CHANNELS_PERM = Discord.Permissions.FLAGS.MANAGE_CHANNELS
const EMBED_LINKS_PERM = Discord.Permissions.FLAGS.EMBED_LINKS
const MANAGE_ROLES_OR_PERMISSIONS_PERM = Discord.Permissions.FLAGS.MANAGE_ROLES

const PERMISSIONS = Object.keys(Discord.Permissions.FLAGS)

const loadOwnerCommand = async (file, message) => {
  const func = require(`../commands/owner/${file}.js`)
  await func(message)
}

const loadCommand = async (file, message, name) => {
  const func = require(`../commands/${file}.js`)
  await func(message, name)
}

const list = {
  help: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  add: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  remove: {
    initLevel: 2,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  list: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  text: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  embed: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  filters: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  date: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  mention: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  test: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  sub: {
    initLevel: 1,
    botPerm: MANAGE_ROLES_OR_PERMISSIONS_PERM
  },
  unsub: {
    initLevel: 1,
    botPerm: MANAGE_ROLES_OR_PERMISSIONS_PERM
  },
  refresh: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  options: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  split: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  move: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM,
    botPerm: EMBED_LINKS_PERM
  },
  clone: {
    initLevel: 1,
    botPerm: EMBED_LINKS_PERM,
    userPerm: MANAGE_CHANNELS_PERM
  },
  backup: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  dump: {
    initLevel: 1,
    botPerm: ['ATTACH_FILES', EMBED_LINKS_PERM],
    userPerm: MANAGE_CHANNELS_PERM
  },
  stats: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  webhook: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  prefix: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  alert: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  locale: {
    initLevel: 1,
    userPerm: MANAGE_CHANNELS_PERM
  },
  invite: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  version: {
    initLevel: 0,
    userPerm: MANAGE_CHANNELS_PERM
  },
  patron: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  },
  compare: {
    initLevel: 2,
    userPerm: MANAGE_CHANNELS_PERM
  }
}

exports.list = list
exports.has = message => {
  const config = getConfig()
  const first = message.content.split(' ')[0]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  return list.hasOwnProperty(first.substr(prefix.length))
}
exports.run = async message => {
  const config = getConfig()
  const log = createLogger(message.guild.shard.id)
  const bot = message.client
  const first = message.content.split(' ')[0]
  const guildPrefix = storage.prefixes[message.guild.id]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  let name = first.substr(prefix.length)
  if (!list.hasOwnProperty(name)) {
    return log.error({
      guild: message.guild
    }, `Failed to run ${name} - nonexistent command`)
  }

  const cmdInfo = list[name]
  const channel = message.channel
  const guild = bot.guilds.cache.get(channel.guild.id)
  if (cmdInfo && cmdInfo.aliasFor) name = cmdInfo.aliasFor
  if (!cmdInfo) {
    return log.error({
      guild: message.guild
    }, `Could not run command "${name}" because command data does not exist`)
  }
  const botPerm = cmdInfo.botPerm
  const userPerm = cmdInfo.userPerm

  try {
    if (guildPrefix && !message.content.startsWith(guildPrefix)) {
      await message.channel.send(`Invalid command prefix. You are not using the prefix you set for your server (${guildPrefix}).`)
      return log.info({
        guild: message.guild
      }, `Ignoring command ${name} due to incorrect prefix (${prefix})`)
    } else if (!guildPrefix && !message.content.startsWith(config.bot.prefix)) {
      return
    }
    log.info({
      guild: message.guild
    }, `Used ${message.content}`)
    if (cmdInfo.initLevel !== undefined && cmdInfo.initLevel > storage.initialized) {
      const m = await message.channel.send(`This command is disabled while booting up, please wait.`)
      await m.delete({ timeout: 4000 })
    }

    // Check bot perm
    let botPermitted
    if (typeof botPerm === 'string') {
      botPermitted = PERMISSIONS.includes(botPerm) && !guild.members.cache.get(bot.user.id).permissionsIn(channel).has(botPerm)
    } else if (Array.isArray(botPerm)) {
      for (var i = 0; i < botPerm.length; ++i) {
        const thisPerm = PERMISSIONS.includes(botPerm) && !guild.members.cache.get(bot.user.id).permissionsIn(channel).has(botPerm)
        botPermitted = botPermitted === undefined ? thisPerm : botPermitted && thisPerm
      }
    }

    if (botPermitted) {
      log.info({
        guild: message.guild
      }, `Missing bot permission ${botPerm} for bot, blocked ${message.content}`)
      return await message.channel.send(`This command has been disabled due to missing bot permission \`${botPerm}\`.`)
    }

    // Check user perm
    const member = await message.guild.members.fetch(message.author)
    if (!message.member) message.member = member

    if (!userPerm || !PERMISSIONS.includes(userPerm) || config.bot.ownerIDs.includes(message.author.id)) {
      return await loadCommand(name, message, name)
    }
    const serverPerm = member.permissions.has(userPerm)
    const channelPerm = member.permissionsIn(channel).has(userPerm)

    if (serverPerm || channelPerm) {
      return await loadCommand(name, message, name)
    }
    log.info({
      user: message.author,
      guild: message.guild
    }, `Missing user permissions for blocked ${message.content}`)
    await message.channel.send(`You do not have the permission \`${userPerm}\` to use this command.`)
  } catch (err) {
    channelTracker.remove(message.channel.id)
    if (err.code !== 50013) {
      log.error(err, `Command ${name}`)
      message.channel.send(err.message)
        .catch(err => log.error(err, `Failed to send error message to channel for command ${name}`))
    } else {
      log.warn(err, `Command ${name} (non-50013 error)`)
    }
  }
}

exports.runOwner = async message => {
  const config = getConfig()
  const first = message.content.split(' ')[0]
  const prefix = storage.prefixes[message.guild.id] || config.bot.prefix
  const command = first.substr(prefix.length)
  if (!message.content.startsWith(prefix)) {
    return
  }
  if (!fs.existsSync(path.join(__dirname, '..', 'commands', 'owner', `${command}.js`))) {
    return
  }
  if (storage.initialized < 2) {
    return message.channel.send(`This command is disabled while booting up, please wait.`).then(m => m.delete(4000))
  }
  try {
    await loadOwnerCommand(command, message)
  } catch (err) {
    const log = createLogger(message.guild.shard.id)
    if (err.code !== 50013) {
      log.error(err, `Owner Command ${command}`)
      message.channel.send(err.message)
        .catch(err => log.error(err, `Failed to send error message to channel for command ${command}`))
    } else {
      log.warn(err, `Owner Command ${command} (non-50013 error)`)
    }
  }
}
<|endoftext|>


<commit_before>window.glStats = function () {

    var _rS = null;

    var _totalDrawArraysCalls = 0,
        _totalDrawElementsCalls = 0,
        _totalUseProgramCalls = 0,
        _totalFaces = 0,
        _totalVertices = 0,
        _totalPoints = 0,
        _totalBindTexures = 0;

    function _h ( f, c ) {
        return function () {
            c.apply( this, arguments );
            f.apply( this, arguments );
        };
    }

    WebGLRenderingContext.prototype.drawArrays = _h( WebGLRenderingContext.prototype.drawArrays, function () {
        _totalDrawArraysCalls++;
        if ( arguments[ 0 ] == this.POINTS ) _totalPoints += arguments[ 2 ];
        else _totalVertices += arguments[ 2 ];
    } );

    WebGLRenderingContext.prototype.drawElements = _h( WebGLRenderingContext.prototype.drawElements, function () {
        _totalDrawElementsCalls++;
        _totalFaces += arguments[ 1 ] / 3;
        _totalVertices += arguments[ 1 ];
    } );

    WebGLRenderingContext.prototype.useProgram = _h( WebGLRenderingContext.prototype.useProgram, function () {
        _totalUseProgramCalls++;
    } );

    WebGLRenderingContext.prototype.bindTexture = _h( WebGLRenderingContext.prototype.bindTexture, function () {
        _totalBindTexures++;
    } );

    var _values = {
        allcalls: {
            over: 3000,
            caption: 'Calls (hook)'
        },
        drawelements: {
            caption: 'drawElements (hook)'
        },
        drawarrays: {
            caption: 'drawArrays (hook)'
        }
    };

    var _groups = [ {
        caption: 'WebGL',
        values: [ 'allcalls', 'drawelements', 'drawarrays', 'useprogram', 'bindtexture', 'glfaces', 'glvertices', 'glpoints' ]
    } ];

    var _fractions = [ {
        base: 'allcalls',
        steps: [ 'drawelements', 'drawarrays' ]
    } ];

    function _update () {
        _rS( 'allcalls' ).set( _totalDrawArraysCalls + _totalDrawElementsCalls );
        _rS( 'drawElements' ).set( _totalDrawElementsCalls );
        _rS( 'drawArrays' ).set( _totalDrawArraysCalls );
        _rS( 'bindTexture' ).set( _totalBindTexures );
        _rS( 'useProgram' ).set( _totalUseProgramCalls );
        _rS( 'glfaces' ).set( _totalFaces );
        _rS( 'glvertices' ).set( _totalVertices );
        _rS( 'glpoints' ).set( _totalPoints );
    }

    function _start () {
        _totalDrawArraysCalls = 0;
        _totalDrawElementsCalls = 0;
        _totalUseProgramCalls = 0;
        _totalFaces = 0;
        _totalVertices = 0;
        _totalPoints = 0;
        _totalBindTexures = 0;
    }

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

window.threeStats = function ( renderer ) {

    var _rS = null;

    var _values = {
        'renderer.info.memory.geometries': {
            caption: 'Geometries'
        },
        'renderer.info.memory.textures': {
            caption: 'Textures'
        },
        'renderer.info.programs': {
            caption: 'Programs'
        },
        'renderer.info.render.calls': {
            caption: 'Calls'
        },
        'renderer.info.render.faces': {
            caption: 'Faces',
            over: 1000
        },
        'renderer.info.render.points': {
            caption: 'Points'
        },
        'renderer.info.render.vertices': {
            caption: 'Vertices'
        }
    };

    var _groups = [ {
        caption: 'Three.js - Memory',
        values: [ 'renderer.info.memory.geometries', 'renderer.info.programs', 'renderer.info.memory.textures' ]
    }, {
        caption: 'Three.js - Render',
        values: [ 'renderer.info.render.calls', 'renderer.info.render.faces', 'renderer.info.render.points', 'renderer.info.render.vertices' ]
    } ];

    var _fractions = [];

    function _update () {

        _rS( 'renderer.info.memory.geometries' ).set( renderer.info.memory.geometries );
        _rS( 'renderer.info.programs' ).set( renderer.info.programs.length );
        _rS( 'renderer.info.memory.textures' ).set( renderer.info.memory.textures );
        _rS( 'renderer.info.render.calls' ).set( renderer.info.render.calls );
        _rS( 'renderer.info.render.faces' ).set( renderer.info.render.faces );
        _rS( 'renderer.info.render.points' ).set( renderer.info.render.points );
        _rS( 'renderer.info.render.vertices' ).set( renderer.info.render.vertices );

    }

    function _start () {}

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

/*
 *   From https://github.com/paulirish/memory-stats.js
 */

window.BrowserStats = function () {

    var _rS = null;

    var _usedJSHeapSize = 0,
        _totalJSHeapSize = 0;

    if ( window.performance && !performance.memory ) {
        performance.memory = {
            usedJSHeapSize: 0,
            totalJSHeapSize: 0
        };
    }

    if ( performance.memory.totalJSHeapSize === 0 ) {
        console.warn( 'totalJSHeapSize === 0... performance.memory is only available in Chrome .' );
    }

    var _values = {
        memory: {
            caption: 'Used Memory',
            average: true,
            avgMs: 1000,
            over: 22
        },
        total: {
            caption: 'Total Memory'
        }
    };

    var _groups = [ {
        caption: 'Browser',
        values: [ 'memory', 'total' ]
    } ];

    var _fractions = [ {
        base: 'total',
        steps: [ 'memory' ]
    } ];

    var log1024 = Math.log( 1024 );

    function _size ( v ) {

        var precision = 100; //Math.pow(10, 2);
        var i = Math.floor( Math.log( v ) / log1024 );
        return Math.round( v * precision / Math.pow( 1024, i ) ) / precision; // + ' ' + sizes[i];

    }

    function _update () {
        _usedJSHeapSize = _size( performance.memory.usedJSHeapSize );
        _totalJSHeapSize = _size( performance.memory.totalJSHeapSize );

        _rS( 'memory' ).set( _usedJSHeapSize );
        _rS( 'total' ).set( _totalJSHeapSize );
    }

    function _start () {
        _usedJSHeapSize = 0;
    }

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

if (typeof module === 'object') {
  module.exports = {
    glStats: window.glStats,
    threeStats: window.threeStats,
    BrowserStats: window.BrowserStats
  };
}
<commit_msg>Fix NaN on faces & vertices in the stats panel (#3573)<commit_after>window.glStats = function () {

    var _rS = null;

    var _totalDrawArraysCalls = 0,
        _totalDrawElementsCalls = 0,
        _totalUseProgramCalls = 0,
        _totalFaces = 0,
        _totalVertices = 0,
        _totalPoints = 0,
        _totalBindTexures = 0;

    function _h ( f, c ) {
        return function () {
            c.apply( this, arguments );
            f.apply( this, arguments );
        };
    }

    WebGLRenderingContext.prototype.drawArrays = _h( WebGLRenderingContext.prototype.drawArrays, function () {
        _totalDrawArraysCalls++;
        if ( arguments[ 0 ] == this.POINTS ) _totalPoints += arguments[ 2 ];
        else _totalVertices += arguments[ 2 ];
    } );

    WebGLRenderingContext.prototype.drawElements = _h( WebGLRenderingContext.prototype.drawElements, function () {
        _totalDrawElementsCalls++;
        _totalFaces += arguments[ 1 ] / 3;
        _totalVertices += arguments[ 1 ];
    } );

    WebGLRenderingContext.prototype.useProgram = _h( WebGLRenderingContext.prototype.useProgram, function () {
        _totalUseProgramCalls++;
    } );

    WebGLRenderingContext.prototype.bindTexture = _h( WebGLRenderingContext.prototype.bindTexture, function () {
        _totalBindTexures++;
    } );

    var _values = {
        allcalls: {
            over: 3000,
            caption: 'Calls (hook)'
        },
        drawelements: {
            caption: 'drawElements (hook)'
        },
        drawarrays: {
            caption: 'drawArrays (hook)'
        }
    };

    var _groups = [ {
        caption: 'WebGL',
        values: [ 'allcalls', 'drawelements', 'drawarrays', 'useprogram', 'bindtexture', 'glfaces', 'glvertices', 'glpoints' ]
    } ];

    var _fractions = [ {
        base: 'allcalls',
        steps: [ 'drawelements', 'drawarrays' ]
    } ];

    function _update () {
        _rS( 'allcalls' ).set( _totalDrawArraysCalls + _totalDrawElementsCalls );
        _rS( 'drawElements' ).set( _totalDrawElementsCalls );
        _rS( 'drawArrays' ).set( _totalDrawArraysCalls );
        _rS( 'bindTexture' ).set( _totalBindTexures );
        _rS( 'useProgram' ).set( _totalUseProgramCalls );
        _rS( 'glfaces' ).set( _totalFaces );
        _rS( 'glvertices' ).set( _totalVertices );
        _rS( 'glpoints' ).set( _totalPoints );
    }

    function _start () {
        _totalDrawArraysCalls = 0;
        _totalDrawElementsCalls = 0;
        _totalUseProgramCalls = 0;
        _totalFaces = 0;
        _totalVertices = 0;
        _totalPoints = 0;
        _totalBindTexures = 0;
    }

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

window.threeStats = function ( renderer ) {

    var _rS = null;

    var _values = {
        'renderer.info.memory.geometries': {
            caption: 'Geometries'
        },
        'renderer.info.memory.textures': {
            caption: 'Textures'
        },
        'renderer.info.programs': {
            caption: 'Programs'
        },
        'renderer.info.render.calls': {
            caption: 'Calls'
        },
        'renderer.info.render.triangles': {
            caption: 'Triangles',
            over: 1000
        },
        'renderer.info.render.points': {
            caption: 'Points'
        },
        'renderer.info.render.vertices': {
            caption: 'Vertices'
        }
    };

    var _groups = [ {
        caption: 'Three.js - Memory',
        values: [ 'renderer.info.memory.geometries', 'renderer.info.programs', 'renderer.info.memory.textures' ]
    }, {
        caption: 'Three.js - Render',
        values: [ 'renderer.info.render.calls', 'renderer.info.render.triangles', 'renderer.info.render.points', 'renderer.info.render.vertices' ]
    } ];

    var _fractions = [];

    function _update () {

        _rS( 'renderer.info.memory.geometries' ).set( renderer.info.memory.geometries );
        _rS( 'renderer.info.programs' ).set( renderer.info.programs.length );
        _rS( 'renderer.info.memory.textures' ).set( renderer.info.memory.textures );
        _rS( 'renderer.info.render.calls' ).set( renderer.info.render.calls );
        _rS( 'renderer.info.render.triangles' ).set( renderer.info.render.triangles );
        _rS( 'renderer.info.render.points' ).set( renderer.info.render.points );
        _rS( 'renderer.info.render.vertices' ).set( renderer.info.render.triangles / 3 );

    }

    function _start () {}

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

/*
 *   From https://github.com/paulirish/memory-stats.js
 */

window.BrowserStats = function () {

    var _rS = null;

    var _usedJSHeapSize = 0,
        _totalJSHeapSize = 0;

    if ( window.performance && !performance.memory ) {
        performance.memory = {
            usedJSHeapSize: 0,
            totalJSHeapSize: 0
        };
    }

    if ( performance.memory.totalJSHeapSize === 0 ) {
        console.warn( 'totalJSHeapSize === 0... performance.memory is only available in Chrome .' );
    }

    var _values = {
        memory: {
            caption: 'Used Memory',
            average: true,
            avgMs: 1000,
            over: 22
        },
        total: {
            caption: 'Total Memory'
        }
    };

    var _groups = [ {
        caption: 'Browser',
        values: [ 'memory', 'total' ]
    } ];

    var _fractions = [ {
        base: 'total',
        steps: [ 'memory' ]
    } ];

    var log1024 = Math.log( 1024 );

    function _size ( v ) {

        var precision = 100; //Math.pow(10, 2);
        var i = Math.floor( Math.log( v ) / log1024 );
        return Math.round( v * precision / Math.pow( 1024, i ) ) / precision; // + ' ' + sizes[i];

    }

    function _update () {
        _usedJSHeapSize = _size( performance.memory.usedJSHeapSize );
        _totalJSHeapSize = _size( performance.memory.totalJSHeapSize );

        _rS( 'memory' ).set( _usedJSHeapSize );
        _rS( 'total' ).set( _totalJSHeapSize );
    }

    function _start () {
        _usedJSHeapSize = 0;
    }

    function _end () {}

    function _attach ( r ) {
        _rS = r;
    }

    return {
        update: _update,
        start: _start,
        end: _end,
        attach: _attach,
        values: _values,
        groups: _groups,
        fractions: _fractions
    };

};

if (typeof module === 'object') {
  module.exports = {
    glStats: window.glStats,
    threeStats: window.threeStats,
    BrowserStats: window.BrowserStats
  };
}
<|endoftext|>


<commit_before>import gravity from '../lib/loaders/gravity';
import { map, omit } from 'lodash';
import Artwork from './artwork';
import {
  ArtworksAggregationResultsType,
  ArtworksAggregation,
} from './aggregations/filter_artworks_aggregation';
import {
  GraphQLList,
  GraphQLObjectType,
  GraphQLString,
  GraphQLBoolean,
  GraphQLNonNull,
  GraphQLInt,
} from 'graphql';

export const FilterArtworksType = new GraphQLObjectType({
  name: 'FilterArtworks',
  fields: () => ({
    hits: {
      type: new GraphQLList(Artwork.type),
    },
    total: {
      type: GraphQLInt,
      resolve: ({ aggregations }) => aggregations.total.value,
    },
    aggregations: {
      type: new GraphQLList(ArtworksAggregationResultsType),
      resolve: ({ aggregations }) =>
        map(omit(aggregations, ['total']), (counts, slice) => ({ slice, counts })),
    },
  }),
});

const FilterArtworks = {
  type: FilterArtworksType,
  description: 'Artworks Elastic Search results',
  args: {
    aggregations: {
      type: new GraphQLNonNull(new GraphQLList(ArtworksAggregation)),
    },
    artist_id: {
      type: GraphQLInt,
    },
    color: {
      type: GraphQLString,
    },
    dimension_range: {
      type: GraphQLString,
    },
    extra_aggregation_gene_ids: {
      type: new GraphQLList(GraphQLString),
    },
    for_sale: {
      type: GraphQLBoolean,
    },
    gene_id: {
      type: GraphQLString,
    },
    gene_ids: {
      type: new GraphQLList(GraphQLString),
    },
    height: {
      type: GraphQLString,
    },
    width: {
      type: GraphQLString,
    },
    medium: {
      type: GraphQLString,
    },
    period: {
      type: GraphQLString,
    },
    price_range: {
      type: GraphQLString,
    },
    page: {
      type: GraphQLInt,
    },
    size: {
      type: GraphQLInt,
    },
    sort: {
      type: GraphQLString,
    },
  },
  resolve: (root, options) => gravity('filter/artworks', options),
};

export default FilterArtworks;
<commit_msg>Make aggregations not required for filter artworks<commit_after>import gravity from '../lib/loaders/gravity';
import { map, omit } from 'lodash';
import Artwork from './artwork';
import {
  ArtworksAggregationResultsType,
  ArtworksAggregation,
} from './aggregations/filter_artworks_aggregation';
import {
  GraphQLList,
  GraphQLObjectType,
  GraphQLString,
  GraphQLBoolean,
  GraphQLInt,
} from 'graphql';

export const FilterArtworksType = new GraphQLObjectType({
  name: 'FilterArtworks',
  fields: () => ({
    hits: {
      type: new GraphQLList(Artwork.type),
    },
    total: {
      type: GraphQLInt,
      resolve: ({ aggregations }) => aggregations.total.value,
    },
    aggregations: {
      type: new GraphQLList(ArtworksAggregationResultsType),
      resolve: ({ aggregations }) =>
        map(omit(aggregations, ['total']), (counts, slice) => ({ slice, counts })),
    },
  }),
});

const FilterArtworks = {
  type: FilterArtworksType,
  description: 'Artworks Elastic Search results',
  args: {
    aggregations: {
      type: new GraphQLList(ArtworksAggregation),
    },
    artist_id: {
      type: GraphQLInt,
    },
    color: {
      type: GraphQLString,
    },
    dimension_range: {
      type: GraphQLString,
    },
    extra_aggregation_gene_ids: {
      type: new GraphQLList(GraphQLString),
    },
    for_sale: {
      type: GraphQLBoolean,
    },
    gene_id: {
      type: GraphQLString,
    },
    gene_ids: {
      type: new GraphQLList(GraphQLString),
    },
    height: {
      type: GraphQLString,
    },
    width: {
      type: GraphQLString,
    },
    medium: {
      type: GraphQLString,
    },
    period: {
      type: GraphQLString,
    },
    price_range: {
      type: GraphQLString,
    },
    page: {
      type: GraphQLInt,
    },
    size: {
      type: GraphQLInt,
    },
    sort: {
      type: GraphQLString,
    },
  },
  resolve: (root, options) => gravity('filter/artworks', options),
};

export default FilterArtworks;
<|endoftext|>


<commit_before>const userAgent = navigator.userAgent;
/*
const checkVRFeatures = () => {
  Object.assign(feature, {
    hasVR: false,
    hasExternalDisplay: false,
    has3DOF: false,
    has6DOF: false,
  });
  if (navigator.getVRDisplays !== undefined) {
    navigator.getVRDisplays().then((displays) => {
      if (!!displays && displays.length) {
        feature.hasVR = true;
        //  We’re going to take the FIRST display and call it a day.
        //  I look forward to the day this becomes an issue ;)
        const display = displays[0];
        if (display.capabilities !== undefined) {
          Object.assign(feature, {
            hasExternalDisplay: !!display.capabilities.hasExternalDisplay,
            has3DOF: !!display.capabilities.hasPosition +
              !!display.capabilities.hasOrientation === 1,
            has6DOF: !!display.capabilities.hasPosition && !!display.capabilities.hasOrientation,
          });
        }
      }
    });
  }
};
*/

const checkHasExternalDisplay = () => (
  new Promise((resolve, reject) => {
    if (navigator.getVRDisplays === undefined) {
      resolve(false);
      return;
    }
    navigator.getVRDisplays().then(
      (displays) => {
        resolve(
          !!displays[0] &&
          !!displays[0].capabilities &&
          !!displays[0].capabilities.hasExternalDisplay
        );
      }
    ).catch(reject);
  })
);

const checkHasVR = () => (
  new Promise((resolve) => {
    if (!navigator.getVRDisplays) {
      resolve(false);
      return;
    }
    navigator
      .getVRDisplays()
      .then(() => {
        resolve(true);
      })
      .catch((error) => {
        console.log(error);
        resolve(false);
      });
  })
);

const checkHas6DOF = () => (
  new Promise((resolve, reject) => {
    if (navigator.getVRDisplays === undefined) {
      resolve(false);
      return;
    }
    navigator.getVRDisplays().then(
      (displays) => {
        resolve(
          !!displays[0] &&
          !!displays[0].capabilities &&
          !!displays[0].capabilities.hasPosition &&
          !!displays[0].capabilities.hasOrientation
        );
      }
    ).catch(reject);
  })
);

const feature = {
  isMobile: /android|ipad|iphone|iemobile/i.test(userAgent),
  isAndroid: /android/i.test(userAgent),
  isChrome: /chrome/i.test(userAgent),
  stats: /fps/.test(window.location.hash),
  prepare: () => (
    Promise.all([
      checkHasExternalDisplay().then((hasExternalDisplay) => {
        feature.hasExternalDisplay = hasExternalDisplay;
      }),
      checkHasVR().then((hasVR) => {
        feature.hasVR = hasVR;
      }),
      checkHas6DOF().then((has6DOF) => {
        feature.has6DOF = has6DOF;
      }),
    ])
  ),
};

export default feature;
<commit_msg>clean<commit_after>const userAgent = navigator.userAgent;
const vrSupported = navigator.getVRDisplays !== undefined;

const checkHasExternalDisplay = () => (
  new Promise((resolve, reject) => {
    if (!vrSupported) {
      resolve(false);
      return;
    }
    navigator
      .getVRDisplays()
      .then(
        (displays) => {
          resolve(
            !!displays[0] &&
            !!displays[0].capabilities &&
            !!displays[0].capabilities.hasExternalDisplay
          );
        }
      )
      .catch(reject);
  })
);

const checkHasVR = () => (
  new Promise((resolve) => {
    if (!vrSupported) {
      resolve(false);
      return;
    }
    navigator
      .getVRDisplays()
      .then(() => {
        resolve(true);
      })
      .catch((error) => {
        console.log(error);
        resolve(false);
      });
  })
);

const checkHas6DOF = () => (
  new Promise((resolve) => {
    if (!vrSupported) {
      resolve(false);
      return;
    }
    navigator
      .getVRDisplays()
      .then((displays) => {
        resolve(
          !!displays[0] &&
          !!displays[0].capabilities &&
          !!displays[0].capabilities.hasPosition &&
          !!displays[0].capabilities.hasOrientation
        );
      })
      .catch((error) => {
        console.log(error);
        resolve(false);
      });
  })
);

const feature = {
  isMobile: /android|ipad|iphone|iemobile/i.test(userAgent),
  isAndroid: /android/i.test(userAgent),
  isChrome: /chrome/i.test(userAgent),
  stats: /fps/.test(window.location.hash),
  prepare: () => (
    Promise.all([
      checkHasExternalDisplay().then((hasExternalDisplay) => {
        feature.hasExternalDisplay = hasExternalDisplay;
      }),
      checkHasVR().then((hasVR) => {
        feature.hasVR = hasVR;
      }),
      checkHas6DOF().then((has6DOF) => {
        feature.has6DOF = has6DOF;
      }),
    ])
  ),
};

export default feature;
<|endoftext|>


<commit_before>
import { Map } from 'immutable'

/**
 * The leaf node of a cache tree.
 *
 * An object, so that immutable maps will key it by reference.
 *
 * @type {Object}
 */

const LEAF = {}

/**
 * Memoize all of the `properties` on a `object`.
 *
 * @param {Object} object
 * @param {Array} properties
 * @return {Record}
 */

function memoize(object, properties) {
  for (const property of properties) {
    const original = object[property]

    if (!original) {
      throw new Error(`Object does not have a property named "${property}".`)
    }

    object[property] = function (...args) {
      const keys = [property, ...args, LEAF]
      const cache = this.__cache = this.__cache || new Map()

      if (cache.hasIn(keys)) return cache.getIn(keys)

      const value = original.apply(this, args)
      this.__cache = cache.setIn(keys, value)
      return value
    }
  }
}

/**
 * Export.
 */

export default memoize
<commit_msg>Only lookup entry once on memoize cache hit<commit_after>
import { Map } from 'immutable'

/**
 * The leaf node of a cache tree.
 *
 * An object, so that immutable maps will key it by reference.
 *
 * @type {Object}
 */

const LEAF = {}

/**
 * An unique value used to detect cache misses
 *
 * @type {Object}
 */
const NO_SET = {}

/**
 * Memoize all of the `properties` on a `object`.
 *
 * @param {Object} object
 * @param {Array} properties
 * @return {Record}
 */

function memoize(object, properties) {
  for (const property of properties) {
    const original = object[property]

    if (!original) {
      throw new Error(`Object does not have a property named "${property}".`)
    }

    object[property] = function (...args) {
      const keys = [property, ...args, LEAF]
      const cache = this.__cache = this.__cache || new Map()

      const cachedValue = cache.getIn(keys, NO_SET)
      if (cachedValue !== NO_SET) {
        return cachedValue
      }

      const value = original.apply(this, args)
      this.__cache = cache.setIn(keys, value)
      return value
    }
  }
}

/**
 * Export.
 */

export default memoize
<|endoftext|>