<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { formatNumber, formatPercentage } from 'common/format';
import Toggle from 'react-toggle';
import ReactTooltip from 'react-tooltip';
import PerformanceBar from 'common/performanceBar';
import SpellLink from 'common/SpellLink';

class HealingEfficiencyBreakdown extends React.Component {
  static propTypes = {
    tracker: PropTypes.object.isRequired,
  };

  constructor() {
    super();
    this.state = {
      showHealing: true,
      detailedView: false,
      showCooldowns: false,
    };
  }

  HealingEfficiencyTable = (props) => {
    const { tracker } = props;
    const { spells, topHpm, topDpm, topHpet, topDpet } = tracker.getAllSpellStats(this.state.showCooldowns);

    const spellArray = Object.values(spells);

    spellArray.sort((a, b) => {
      if (this.state.showHealing) {
        if (a.hpm  b.hpm) {
          return -1;
        }
      } else {
        if (a.dpm  b.dpm) {
          return -1;
        }
      }

      return 0;
    });

    const spellRows = spellArray.map((spellDetail => {
      if (spellDetail.casts > 0) {
        return this.HealingEfficiencySpellRow(spellDetail, topHpm, topDpm, topHpet, topDpet);
      }
      return null;
    }));

    return spellRows;
  };

  HealingEfficiencySpellRow = (spellDetail, topHpm, topDpm, topHpet, topDpet) => {
    return (

          <SpellLink id={spellDetail.spell.id} />

        {this.state.detailedView ? <this.DetailView spellDetail={spellDetail} /> : <this.BarView spellDetail={spellDetail} topHpm={topHpm} topDpm={topDpm} topHpet={topHpet} topDpet={topDpet} />}

    );
  };

  BarHeader = () => {
    return (
      <>
        Mana Spent
        {this.state.showHealing && (
          <>
            Healing per mana spent
            Healing per second spent casting
          </>
        )}
        {!this.state.showHealing && (
          <>
            Damage per mana spent
            Damage per second spent casting
          </>
        )}
      </>
    );
  };

  BarView = (props) => {
    const { spellDetail, topHpm, topDpm, topHpet, topDpet } = props;
    const hasHealing = spellDetail.healingDone;
    const hasDamage = spellDetail.damageDone > 0;
    const barWidth = 20;

    return (
      <>

          {formatNumber(spellDetail.manaSpent)}
          {' (' + formatPercentage(spellDetail.manaPercentSpent) + '%)'}

        {this.state.showHealing && (
        <>
          {hasHealing ? formatNumber(spellDetail.hpm) : '-'}
          <PerformanceBar percent={spellDetail.hpm / topHpm} />

          {hasHealing ? formatNumber(spellDetail.hpet * 1000) : '-'}
          <PerformanceBar percent={(spellDetail.hpet / topHpet)} />
        </>
        )}
        {!this.state.showHealing && (
        <>
          {hasDamage ? formatNumber(spellDetail.dpm) : '-'}
          <PerformanceBar percent={spellDetail.dpm / topDpm} />

          {hasDamage ? formatNumber(spellDetail.dpet * 1000) : '-'}
          <PerformanceBar percent={(spellDetail.dpet / topDpet)} />
        </>
        )}
      </>
    );
  };

  DetailHeader = () => {
    return (
      <>

          <dfn data-tip={`Total Casts (Number of targets hit)`}>Casts</dfn>

        Mana Spent
        {this.state.showHealing && (
        <>
          Healing Done
          Overhealing

            <dfn data-tip={`Healing per mana spent casting the spell`}>HPM</dfn>

            <dfn data-tip={`Healing per second spent casting the spell`}>HPET</dfn>

        </>
        )}
        {!this.state.showHealing && (
        <>
          Damage Done

            <dfn data-tip={`Damage per mana spent casting the spell`}>DPM</dfn>

            <dfn data-tip={`Damage per second spent casting the spell`}>DPET</dfn>

        </>
          )}
      </>
    );
  };

  DetailView = (props) => {
    const { spellDetail } = props;
    const hasHealing = spellDetail.healingDone;
    const hasOverhealing = spellDetail.healingDone > 0 || spellDetail.overhealingDone > 0;
    const hasDamage = spellDetail.damageDone > 0;

    return (
      <>
        {spellDetail.casts} ({Math.floor(spellDetail.healingHits + spellDetail.damageHits)})

          {formatNumber(spellDetail.manaSpent)}
          {' (' + formatPercentage(spellDetail.manaPercentSpent) + '%)'}

        {this.state.showHealing && (
          <>

              {hasHealing ? formatNumber(spellDetail.healingDone) : '-'}
              {hasHealing ? ' (' + formatPercentage(spellDetail.percentHealingDone) + '%)' : ''}

              {hasOverhealing ? formatNumber(spellDetail.overhealingDone) : '-'}
              {hasOverhealing ? ' (' + formatPercentage(spellDetail.percentOverhealingDone) + '%)' : ''}

            {hasHealing ? formatNumber(spellDetail.hpm) : '-'}
            {hasHealing ? formatNumber(spellDetail.hpet * 1000) : '-'}
          </>
        )}
        {!this.state.showHealing && (
          <>

              {hasDamage ? formatNumber(spellDetail.damageDone) : '-'}
              {hasDamage ? ' (' + formatPercentage(spellDetail.percentDamageDone) + '%)' : ''}

            {hasDamage ? formatNumber(spellDetail.dpm) : '-'}
            {hasDamage ? formatNumber(spellDetail.dpet * 1000) : '-'}
          </>
        )}
      </>
    );
  };

  componentDidUpdate() {
    ReactTooltip.rebuild();
  }

  render() {
    const { tracker } = this.props;

    return (

                <Toggle
                  defaultChecked={false}
                  icons={false}
                  onChange={event => this.setState({ detailedView: event.target.checked })}
                  id="detailed-toggle"
                />

                  Detailed View

                <Toggle
                  defaultChecked={false}
                  icons={false}
                  onChange={event => this.setState({ showCooldowns: event.target.checked })}
                  id="cooldown-toggle"
                />

                  Show Cooldowns

                  Show Damage

                <Toggle
                  defaultChecked
                  icons={false}
                  onChange={event => this.setState({ showHealing: event.target.checked })}
                  id="healing-toggle"
                />

                  Show Healing

                  Ability
                  {this.state.detailedView ? <this.DetailHeader /> : <this.BarHeader />}

                <this.HealingEfficiencyTable tracker={tracker} showHealing={this.state.showHealing} />

    );
  }
}

export default HealingEfficiencyBreakdown;
<commit_msg>splitting damage and healing hits<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { formatNumber, formatPercentage } from 'common/format';
import Toggle from 'react-toggle';
import ReactTooltip from 'react-tooltip';
import PerformanceBar from 'common/performanceBar';
import SpellLink from 'common/SpellLink';

class HealingEfficiencyBreakdown extends React.Component {
  static propTypes = {
    tracker: PropTypes.object.isRequired,
  };

  constructor() {
    super();
    this.state = {
      showHealing: true,
      detailedView: false,
      showCooldowns: false,
    };
  }

  HealingEfficiencyTable = (props) => {
    const { tracker } = props;
    const { spells, topHpm, topDpm, topHpet, topDpet } = tracker.getAllSpellStats(this.state.showCooldowns);

    const spellArray = Object.values(spells);

    spellArray.sort((a, b) => {
      if (this.state.showHealing) {
        if (a.hpm  b.hpm) {
          return -1;
        }
      } else {
        if (a.dpm  b.dpm) {
          return -1;
        }
      }

      return 0;
    });

    const spellRows = spellArray.map((spellDetail => {
      if (spellDetail.casts > 0) {
        return this.HealingEfficiencySpellRow(spellDetail, topHpm, topDpm, topHpet, topDpet);
      }
      return null;
    }));

    return spellRows;
  };

  HealingEfficiencySpellRow = (spellDetail, topHpm, topDpm, topHpet, topDpet) => {
    return (

          <SpellLink id={spellDetail.spell.id} />

        {this.state.detailedView ? <this.DetailView spellDetail={spellDetail} /> : <this.BarView spellDetail={spellDetail} topHpm={topHpm} topDpm={topDpm} topHpet={topHpet} topDpet={topDpet} />}

    );
  };

  BarHeader = () => {
    return (
      <>
        Mana Spent
        {this.state.showHealing && (
          <>
            Healing per mana spent
            Healing per second spent casting
          </>
        )}
        {!this.state.showHealing && (
          <>
            Damage per mana spent
            Damage per second spent casting
          </>
        )}
      </>
    );
  };

  BarView = (props) => {
    const { spellDetail, topHpm, topDpm, topHpet, topDpet } = props;
    const hasHealing = spellDetail.healingDone;
    const hasDamage = spellDetail.damageDone > 0;
    const barWidth = 20;

    return (
      <>

          {formatNumber(spellDetail.manaSpent)}
          {' (' + formatPercentage(spellDetail.manaPercentSpent) + '%)'}

        {this.state.showHealing && (
          <>
            {hasHealing ? formatNumber(spellDetail.hpm) : '-'}
            <PerformanceBar percent={spellDetail.hpm / topHpm} />

            {hasHealing ? formatNumber(spellDetail.hpet * 1000) : '-'}
            <PerformanceBar percent={(spellDetail.hpet / topHpet)} />
          </>
        )}
        {!this.state.showHealing && (
          <>
            {hasDamage ? formatNumber(spellDetail.dpm) : '-'}
            <PerformanceBar percent={spellDetail.dpm / topDpm} />

            {hasDamage ? formatNumber(spellDetail.dpet * 1000) : '-'}
            <PerformanceBar percent={(spellDetail.dpet / topDpet)} />
          </>
        )}
      </>
    );
  };

  DetailHeader = () => {
    return (
      <>

          <dfn data-tip={`Total Casts (Number of targets hit)`}>Casts</dfn>

        Mana Spent
        {this.state.showHealing && (
          <>
            Healing Done
            Overhealing

              <dfn data-tip={`Healing per mana spent casting the spell`}>HPM</dfn>

              <dfn data-tip={`Healing per second spent casting the spell`}>HPET</dfn>

          </>
        )}
        {!this.state.showHealing && (
          <>
            Damage Done

              <dfn data-tip={`Damage per mana spent casting the spell`}>DPM</dfn>

              <dfn data-tip={`Damage per second spent casting the spell`}>DPET</dfn>

          </>
        )}
      </>
    );
  };

  DetailView = (props) => {
    const { spellDetail } = props;
    const hasHealing = spellDetail.healingDone;
    const hasOverhealing = spellDetail.healingDone > 0 || spellDetail.overhealingDone > 0;
    const hasDamage = spellDetail.damageDone > 0;

    return (
      <>
        {spellDetail.casts} ({this.state.showHealing ? Math.floor(spellDetail.healingHits) : Math.floor(spellDetail.damageHits)})

          {formatNumber(spellDetail.manaSpent)}
          {' (' + formatPercentage(spellDetail.manaPercentSpent) + '%)'}

        {this.state.showHealing && (
          <>

              {hasHealing ? formatNumber(spellDetail.healingDone) : '-'}
              {hasHealing ? ' (' + formatPercentage(spellDetail.percentHealingDone) + '%)' : ''}

              {hasOverhealing ? formatNumber(spellDetail.overhealingDone) : '-'}
              {hasOverhealing ? ' (' + formatPercentage(spellDetail.percentOverhealingDone) + '%)' : ''}

            {hasHealing ? formatNumber(spellDetail.hpm) : '-'}
            {hasHealing ? formatNumber(spellDetail.hpet * 1000) : '-'}
          </>
        )}
        {!this.state.showHealing && (
          <>

              {hasDamage ? formatNumber(spellDetail.damageDone) : '-'}
              {hasDamage ? ' (' + formatPercentage(spellDetail.percentDamageDone) + '%)' : ''}

            {hasDamage ? formatNumber(spellDetail.dpm) : '-'}
            {hasDamage ? formatNumber(spellDetail.dpet * 1000) : '-'}
          </>
        )}
      </>
    );
  };

  componentDidUpdate() {
    ReactTooltip.rebuild();
  }

  render() {
    const { tracker } = this.props;
console.log(tracker);
    return (

                <Toggle
                  defaultChecked={false}
                  icons={false}
                  onChange={event => this.setState({ detailedView: event.target.checked })}
                  id="detailed-toggle"
                />

                  Detailed View

                <Toggle
                  defaultChecked={false}
                  icons={false}
                  onChange={event => this.setState({ showCooldowns: event.target.checked })}
                  id="cooldown-toggle"
                />

                  Show Cooldowns

                  Show Damage

                <Toggle
                  defaultChecked
                  icons={false}
                  onChange={event => this.setState({ showHealing: event.target.checked })}
                  id="healing-toggle"
                />

                  Show Healing

                  Ability
                  {this.state.detailedView ? <this.DetailHeader /> : <this.BarHeader />}

                <this.HealingEfficiencyTable tracker={tracker} showHealing={this.state.showHealing} />

    );
  }
}

export default HealingEfficiencyBreakdown;
<|endoftext|>


<commit_before>import {addEvt, removeEvt} from '../event';
import {root} from '../root';

const JSON = root.JSON;
const location = root.location;
const decodeURIComponent = root.decodeURIComponent;

export const hasHashChange = () => {
    let docMode = root.documentMode;
    return ('onhashchange' in root) && (docMode === undefined || docMode > 7);
};

/**
 * Manages the URL hash reflecting the features state to be persisted
 *
 * @export
 * @class Hash
 */
export class Hash {

    /**
     * Creates an instance of Hash
     *
     * @param {State} state Instance of State
     */
    constructor(state) {
        this.state = state;
        this.lastHash = null;
        this.emitter = state.emitter;
    }

    /**
     * Initializes the Hash object
     */
    init() {
        if (!hasHashChange()) {
            return;
        }

        this.lastHash = location.hash;

        this.emitter.on(['state-changed'], (tf, state) => this.update(state));
        this.emitter.on(['initialized'], () => this.sync());
        addEvt(root, 'hashchange', () => this.sync());
    }

    /**
     * Updates the URL hash based on a state change
     *
     * @param {State} state Instance of State
     */
    update(state) {
        let hash = `#${JSON.stringify(state)}`;
        if (this.lastHash === hash) {
            return;
        }

        location.hash = hash;
        this.lastHash = hash;
    }

    /**
     * Converts a URL hash into a state JSON object
     *
     * @param {String} hash URL hash fragment
     * @returns {Object} JSON object
     */
    parse(hash) {
        if (hash.indexOf('#') === -1) {
            return null;
        }
        hash = hash.substr(1);
        return JSON.parse(decodeURIComponent(hash));
    }

    /**
     * Applies current hash state to features
     */
    sync() {
        let state = this.parse(location.hash);
        if (!state) {
            return;
        }
        // override current state with persisted one and sync features
        this.state.overrideAndSync(state);
    }

    /**
     * Release Hash event subscriptions and clear fields
     */
    destroy() {
        this.emitter.off(['state-changed'], (tf, state) => this.update(state));
        this.emitter.off(['initialized'], () => this.sync());
        removeEvt(root, 'hashchange', () => this.sync());

        this.state = null;
        this.lastHash = null;
        this.emitter = null;
    }
}
<commit_msg>Store the Sync wrapper in a variable for clean removal.<commit_after>import {addEvt, removeEvt} from '../event';
import {root} from '../root';

const JSON = root.JSON;
const location = root.location;
const decodeURIComponent = root.decodeURIComponent;

export const hasHashChange = () => {
    let docMode = root.documentMode;
    return ('onhashchange' in root) && (docMode === undefined || docMode > 7);
};

/**
 * Manages the URL hash reflecting the features state to be persisted
 *
 * @export
 * @class Hash
 */
export class Hash {

    /**
     * Creates an instance of Hash
     *
     * @param {State} state Instance of State
     */
    constructor(state) {
        this.state = state;
        this.lastHash = null;
        this.emitter = state.emitter;
    }

    /**
     * Initializes the Hash object
     */
    init() {
        if (!hasHashChange()) {
            return;
        }

        this.lastHash = location.hash;
        this.boundSync = this.sync.bind(this); //Store a bound sync wrapper for future use.

        this.emitter.on(['state-changed'], (tf, state) => this.update(state));
        this.emitter.on(['initialized'], this.boundSync);
        addEvt(root, 'hashchange', this.boundSync);
    }

    /**
     * Updates the URL hash based on a state change
     *
     * @param {State} state Instance of State
     */
    update(state) {
        let hash = `#${JSON.stringify(state)}`;
        if (this.lastHash === hash) {
            return;
        }

        location.hash = hash;
        this.lastHash = hash;
    }

    /**
     * Converts a URL hash into a state JSON object
     *
     * @param {String} hash URL hash fragment
     * @returns {Object} JSON object
     */
    parse(hash) {
        if (hash.indexOf('#') === -1) {
            return null;
        }
        hash = hash.substr(1);
        return JSON.parse(decodeURIComponent(hash));
    }

    /**
     * Applies current hash state to features
     */
    sync() {
        let state = this.parse(location.hash);
        if (!state) {
            return;
        }
        // override current state with persisted one and sync features
        this.state.overrideAndSync(state);
    }

    /**
     * Release Hash event subscriptions and clear fields
     */
    destroy() {
        this.emitter.off(['state-changed'], (tf, state) => this.update(state));
        this.emitter.off(['initialized'], () => this.boundSync);
        removeEvt(root, 'hashchange', () => this.boundSync);

        this.state = null;
        this.lastHash = null;
        this.emitter = null;
    }
}
<|endoftext|>


<commit_before>/*****
 * fullscreen.js
 * Copyright (c) 2015 Carter Hinsley
 * MIT License
 *****/

/**
 * Attempt to activate fullscreen mode. If the provided element exposes the
 * fullscreen interface, return `true`, `false` otherwise.
 **/
function activateFullscreen(element) {
    if (element.requestFullscreen) {
        element.requestFullscreen();
        return true;
    } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
        return true;
    } else if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
        return true;
    } else if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen();
        return true;
    } else {
        return false;
    }
}

/**
 * Deactivate fullscreen mode. Pretty simple.
 **/
function deactivateFullscreen() {
    if (document.exitFullscreen) {
        document.exitFullscreen();
        return true;
    } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
        return true;
    } else if (document.msExitFullscreen) {
        document.msExitFullscreen();
        return true;
    } else if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
        return true;
    } else {
        return false;
    }
}

/**
 * Return `true` if fullscreen mode is active, `false` otherwise.
 **/
function fullscreenActive() {
    return Boolean(fullscreenElement);
}

/**
 * Register an event handler which is to be activated when fullscreen mode is
 * toggled on or off.
 **/
function fullscreenChange(callback) {
    var registerCallback = function (event_title) {
        document.addEventListener(event_title, callback);
    };
    registerCallback('fullscreenchange');
    registerCallback('mozfullscreenchange');
    registerCallback('MSFullscreenChange');
    registerCallback('webkitfullscreenchange');
}

/**
 * If fullscreen mode is active, return the element which is being displayed
 * in fullscreen view. Otherwise, return `null`.
 **/
function fullscreenElement() {
    return document.fullscreenElement    ||
           document.mozFullScreenElement ||
           document.msFullscreenElement  ||
           document.webkitFullscreenElement;
}

/**
 * Return `true` if fullscreen mode is supported by the current browser,
 * `false` otherwise.
 **/
function fullscreenEnabled() {
    return document.fullscreenEnabled    ||
           document.mozFullScreenEnabled ||
           document.msFullscreenEnabled  ||
           document.webkitFullscreenEnabled;
}

/**
 * Register an event handler which is to be activated when fullscreen mode
 * triggers an error.
 **/
function fullscreenError(callback) {
    var registerCallback = function (event_title) {
        document.addEventListener(event_title, callback);
    };
    registerCallback('fullscreenerror');
    registerCallback('mozfullscreenerror');
    registerCallback('MSFullscreenError');
    registerCallback('webkitfullscreenerror');
}
<commit_msg>Compliance with JSLint<commit_after>/*****
 * fullscreen.js
 * Copyright (c) 2015 Carter Hinsley
 * MIT License
 *****/

/**
 * Attempt to activate fullscreen mode. If the provided element exposes the
 * fullscreen interface, return `true`, `false` otherwise.
 **/
function activateFullscreen(element) {
    if (element.requestFullscreen) {
        element.requestFullscreen();
        return true;
    }
    if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
        return true;
    }
    if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
        return true;
    }
    if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen();
        return true;
    }
    return false;
}

/**
 * Deactivate fullscreen mode. Pretty simple.
 **/
function deactivateFullscreen() {
    if (document.exitFullscreen) {
        document.exitFullscreen();
        return true;
    }
    if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
        return true;
    }
    if (document.msExitFullscreen) {
        document.msExitFullscreen();
        return true;
    }
    if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
        return true;
    }
    return false;
}

/**
 * Register an event handler which is to be activated when fullscreen mode is
 * toggled on or off.
 **/
function fullscreenChange(callback) {
    var registerCallback = function (event_title) {
        document.addEventListener(event_title, callback);
    };
    registerCallback('fullscreenchange');
    registerCallback('mozfullscreenchange');
    registerCallback('MSFullscreenChange');
    registerCallback('webkitfullscreenchange');
}

/**
 * If fullscreen mode is active, return the element which is being displayed
 * in fullscreen view. Otherwise, return `null`.
 **/
function fullscreenElement() {
    return document.fullscreenElement    ||
           document.mozFullScreenElement ||
           document.msFullscreenElement  ||
           document.webkitFullscreenElement;
}

/**
 * Return `true` if fullscreen mode is active, `false` otherwise.
 **/
function fullscreenActive() {
    return Boolean(fullscreenElement);
}

/**
 * Return `true` if fullscreen mode is supported by the current browser,
 * `false` otherwise.
 **/
function fullscreenEnabled() {
    return document.fullscreenEnabled    ||
           document.mozFullScreenEnabled ||
           document.msFullscreenEnabled  ||
           document.webkitFullscreenEnabled;
}

/**
 * Register an event handler which is to be activated when fullscreen mode
 * triggers an error.
 **/
function fullscreenError(callback) {
    var registerCallback = function (event_title) {
        document.addEventListener(event_title, callback);
    };
    registerCallback('fullscreenerror');
    registerCallback('mozfullscreenerror');
    registerCallback('MSFullscreenError');
    registerCallback('webkitfullscreenerror');
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { OptionRenderer } from '@keystonejs/ui/src/primitives/filters';

function isOptionSelected(opt, selected) {
  return Boolean(selected.filter(x => x.path === opt.path).length);
}
function getOptionValue(opt) {
  return opt.path;
}

/**
 * Why does this exist?
 * --------------------
 * Because fields can contain an `options` property, which react-select
 * interprets as an OptionGroup.
 *
 * How does it solve the problem?
 * ------------------------------
 * By removing the `options` property before passing fields on to react-select,
 * and returning it during `onChange`.
 */

type FieldType = Object;
export type FieldSelectProps = {
  fields: Array<FieldType>,
  onChange: FieldType => void,
  value: FieldType | Array<FieldType>,
};

export default class FieldSelect extends Component<FieldSelectProps> {
  getSanitizedOptions = () => {
    const { fields } = this.props;
    return fields.map(({ options, ...field }) => field);
  };
  onChangeReturnFieldClass = selected => {
    const { fields: listFields, isMulti, onChange } = this.props;

    const fields = listFields.filter(lf => {
      return isMulti
        ? selected.filter(sf => sf.path === lf.path).length
        : selected.path === lf.path;
    });

    const value = isMulti ? fields : fields[0];

    onChange(value);
  };

  render() {
    return (
      <OptionRenderer
        isOptionSelected={isOptionSelected}
        getOptionValue={getOptionValue}
        {...this.props}
        options={this.getSanitizedOptions()}
        onChange={this.onChangeReturnFieldClass}
      />
    );
  }
}
<commit_msg>tidier field select fns;<commit_after>import React, { Component } from 'react';
import { OptionRenderer } from '@keystonejs/ui/src/primitives/filters';

function isOptionSelected(opt, selected) {
  return Boolean(selected.filter(x => x.path === opt.path).length);
}
function getOptionValue(opt) {
  return opt.path;
}
function mapToPath(acc, i) {
  acc[i.path] = true;
  return acc;
}

/**
 * Why does this exist?
 * --------------------
 * Because fields can contain an `options` property, which react-select
 * interprets as an OptionGroup.
 *
 * How does it solve the problem?
 * ------------------------------
 * By removing the `options` property before passing fields on to react-select,
 * and returning it during `onChange`.
 */

type FieldType = Object;
export type FieldSelectProps = {
  fields: Array<FieldType>,
  onChange: FieldType => void,
  value: FieldType | Array<FieldType>,
};

export default class FieldSelect extends Component<FieldSelectProps> {
  getSanitizedOptions = () => {
    const { fields } = this.props;
    return fields.map(({ options, ...field }) => field);
  };
  onChangeReturnFieldClass = selected => {
    const { fields: listFields, isMulti, onChange } = this.props;

    const diffMap = selected.reduce(mapToPath, {});
    const fields = listFields.filter(i => diffMap[i.path]);
    const value = isMulti ? fields : fields[0];

    onChange(value);
  };

  render() {
    return (
      <OptionRenderer
        isOptionSelected={isOptionSelected}
        getOptionValue={getOptionValue}
        {...this.props}
        options={this.getSanitizedOptions()}
        onChange={this.onChangeReturnFieldClass}
      />
    );
  }
}
<|endoftext|>


<commit_before>var device = function() {
		this.deviceData;

		this.$el;
		this.$hardware;
		this.$iframe;
		this.$name;
		this.$size;

		this.elSize = {
			h: null,
			w: null
		};
		this.hardwareSize = {
			h: null,
			w: null
		};
	},
	p = device.prototype;

p.init = function(deviceData) {
	this.createDevice();
	this.events();

	this.update(deviceData);

	return this;
}

p.createDevice = function() {
	// main el
	this.$el = document.createElement('div');
	this.$el.classList.add('device');

	// hardware
	this.$hardware = document.createElement('div');
	this.$hardware.classList.add('hardware');

	// frame
	this.$iframe = document.createElement('iframe');

	// info
	this.$name = document.createElement('div');
	this.$name.classList.add('name');

	this.$size = document.createElement('div');
	this.$size.classList.add('size');

	this.$el.appendChild(this.$name);
	this.$el.appendChild(this.$size);

	this.$hardware.appendChild(this.$iframe);
	this.$el.appendChild(this.$hardware);

	_stage.$el.appendChild(this.$el);
}

p.events = function() {
	//this.$el.addEventListener('resize', this.$el);
	//this.$iframe.addEventListener('focus', this.$el);

	//this.$iframe.addEventListener('load', this.sendIframeLoad);
}

p.update = function(data) {
	this.deviceData = data;

	this.$name.innerText = data.name;
	this.$size.innerText = data.w  + 'x' + data.h;
	this.$iframe.height = data.h;
	this.$iframe.width = data.w;

	// remove other types ... lame
    this.$hardware.className = 'hardware';
	this.$hardware.classList.add(data.type);

	this.getHardwareSize();
}

p.getHardwareSize = function() {
	var $dummy = this.$el.cloneNode(true),
		$dummyHardware = $dummy.querySelector('.hardware');
	$dummy.classList.add('dummy');
	$dummy.querySelector('iframe').src = '';
	$dummyHardware.style.width = "auto";
	$dummyHardware.style.height = "auto";
	$dummy.style.width = "auto";
	$dummy.style.height = "auto";
	_stage.$el.appendChild($dummy);

	// console.log(['dummy Sizing', $dummy.offsetWidth, $dummy.offsetHeight]);
	this.elSize = {
		h: $dummy.offsetHeight,
		w: $dummy.offsetWidth
	};
	this.hardwareSize = {
		h: $dummyHardware.offsetHeight,
		w: $dummyHardware.offsetWidth
	};
	//$dummy.remove();
	_stage.$el.removeChild($dummy);
}

p.sendIframeLoad = function(e) {
	console.log(['iframe load', e, this.src]);
}

p.updateHref = function(href) {
	this.$iframe.src = href;
}

p.updateScale = function(scale) {
	this.$el.style.width = (this.elSize.w) * scale;
	this.$el.style.height = (this.elSize.h) * scale;
	this.$hardware.style.width = this.hardwareSize.w;
	//this.$hardware.style.height = this.hardwareSize.g;
	this.$hardware.style.left = Math.floor(0-(this.hardwareSize.w - (this.hardwareSize.w * scale)) / 2);
	//this.$hardware.style.top = Math.floor(0-(this.hardwareSize.h - (this.hardwareSize.h * scale)) / 2);

	//this.$el.style.minHeight = (this.elSize.h) * scale;
	this.$hardware.style.webkitTransform = "scale(" + scale + ")";
}

p.destroy = function() {
	_stage.$el.removeChild(this.$el);
}<commit_msg>cleanup<commit_after>var device = function() {
		this.deviceData;

		this.$el;
		this.$hardware;
		this.$iframe;
		this.$name;
		this.$size;

		this.elSize = {
			h: null,
			w: null
		};
		this.hardwareSize = {
			h: null,
			w: null
		};
	},
	p = device.prototype;

p.init = function(deviceData) {
	this.createDevice();
	this.events();

	this.update(deviceData);

	return this;
}

p.createDevice = function() {
	// main el
	this.$el = document.createElement('div');
	this.$el.classList.add('device');

	// hardware
	this.$hardware = document.createElement('div');
	this.$hardware.classList.add('hardware');

	// frame
	this.$iframe = document.createElement('iframe');

	// info
	this.$name = document.createElement('div');
	this.$name.classList.add('name');

	this.$size = document.createElement('div');
	this.$size.classList.add('size');

	this.$el.appendChild(this.$name);
	this.$el.appendChild(this.$size);

	this.$hardware.appendChild(this.$iframe);
	this.$el.appendChild(this.$hardware);

	_stage.$el.appendChild(this.$el);
}

p.events = function() {
	//this.$el.addEventListener('resize', this.$el);
	//this.$iframe.addEventListener('focus', this.$el);

	//this.$iframe.addEventListener('load', this.sendIframeLoad);
}

p.update = function(data) {
	this.deviceData = data;

	this.$name.innerText = data.name;
	this.$size.innerText = data.w  + 'x' + data.h;
	this.$iframe.height = data.h;
	this.$iframe.width = data.w;

	// remove other types ... lame
    this.$hardware.className = 'hardware';
	this.$hardware.classList.add(data.type);

	this.getHardwareSize();
}

p.getHardwareSize = function() {
	var $dummy = this.$el.cloneNode(true),
		$dummyHardware = $dummy.querySelector('.hardware');
	$dummy.classList.add('dummy');
	$dummy.querySelector('iframe').src = '';
	$dummyHardware.style.width = "auto";
	$dummyHardware.style.height = "auto";
	$dummy.style.width = "auto";
	$dummy.style.height = "auto";
	_stage.$el.appendChild($dummy);

	// console.log(['dummy Sizing', $dummy.offsetWidth, $dummy.offsetHeight]);
	this.elSize = {
		h: $dummy.offsetHeight,
		w: $dummy.offsetWidth
	};
	this.hardwareSize = {
		h: $dummyHardware.offsetHeight,
		w: $dummyHardware.offsetWidth
	};
	//$dummy.remove();
	_stage.$el.removeChild($dummy);
}

p.sendIframeLoad = function(e) {
	console.log(['iframe load', e, this.src]);
}

p.updateHref = function(href) {
	this.$iframe.src = href;
}

// resizes a device based on a scale factor
p.updateScale = function(scale) {
	this.$el.style.width = (this.elSize.w) * scale;
	this.$el.style.height = (this.elSize.h) * scale;

	this.$hardware.style.width = this.hardwareSize.w;
	this.$hardware.style.left = Math.floor(0-(this.hardwareSize.w - (this.hardwareSize.w * scale)) / 2);

	this.$hardware.style.webkitTransform = "scale(" + scale + ")";
}

p.destroy = function() {
	_stage.$el.removeChild(this.$el);
}<|endoftext|>


<commit_before>var sys = require('sys'),
  Metric = require('metric').Metric,
  TimeBucketers = require('aggregates').TimeBucketers;

var Weekly = {};

Weekly.reducer = function(obj, prev) {
  if(obj.name == 'all_views') {
    prev.total += obj.data.total; prev.cartAdds += obj.data.cartAdds;
  } else {
    if(!prev.sales) { prev.sales = {}; }

    for(var saleKey in obj.data.sales) {
      prev.sales[saleKey] = (prev.sales[saleKey] || 0) + obj.data.sales[saleKey];
    }
  }
};

Weekly.findByDay = function(db, callback) {
  db.collection('metrics', function(err, collection) {
    if(err) {
      sys.log(JSON.stringify(err, null, 2));
    }

    var today = TimeBucketers.closestDayFor((new Date()).getTime());
    var _oneDay = 1000 * 60 * 60 * 24;

    var result = collection.find({ day: { $gte: new Date(today - _oneDay * 7) } }, { sort: [['day', 'ascending']] }, function(err, cursor) {

      cursor.toArray(function(err, items) {
        if(err) {
          sys.log(JSON.stringify(err, null, 2));
        }

        // Turn the sales hash into an array, for sorting
        for(var i = 0; i < items.length; i++) {
          var item = items[i];

          item.day = item.day.getTime();
          delete item._id;

          Metric.allMetrics(function(metric) {
            if(metric.aggregateCallback) { metric.aggregateCallback(item); }
          });

        }

        callback(JSON.stringify(items, null, 2))
      });
    });
  });
};

exports.findByDay = Weekly.findByDay;<commit_msg>Sort weekly stats descending<commit_after>var sys = require('sys'),
  Metric = require('metric').Metric,
  TimeBucketers = require('aggregates').TimeBucketers;

var Weekly = {};

Weekly.reducer = function(obj, prev) {
  if(obj.name == 'all_views') {
    prev.total += obj.data.total; prev.cartAdds += obj.data.cartAdds;
  } else {
    if(!prev.sales) { prev.sales = {}; }

    for(var saleKey in obj.data.sales) {
      prev.sales[saleKey] = (prev.sales[saleKey] || 0) + obj.data.sales[saleKey];
    }
  }
};

Weekly.findByDay = function(db, callback) {
  db.collection('metrics', function(err, collection) {
    if(err) {
      sys.log(JSON.stringify(err, null, 2));
    }

    var today = TimeBucketers.closestDayFor((new Date()).getTime());
    var _oneDay = 1000 * 60 * 60 * 24;

    var result = collection.find({ day: { $gte: new Date(today - _oneDay * 7) } }, { sort: [['day', 'descending']] }, function(err, cursor) {

      cursor.toArray(function(err, items) {
        if(err) {
          sys.log(JSON.stringify(err, null, 2));
        }

        // Turn the sales hash into an array, for sorting
        for(var i = 0; i < items.length; i++) {
          var item = items[i];

          item.day = item.day.getTime();
          delete item._id;

          Metric.allMetrics(function(metric) {
            if(metric.aggregateCallback) { metric.aggregateCallback(item); }
          });

        }

        callback(JSON.stringify(items, null, 2))
      });
    });
  });
};

exports.findByDay = Weekly.findByDay;<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/*
 * NB: browser-only tests
 */

/*jshint expr: true */
/*global describe, it, before, after, beforeEach, afterEach */

var chai = require('chai');
var assert = chai.assert;
var expect = chai.expect;

var $ = window.$;
var d3 = window.d3;
var preprocess = window.tideline.preprocess;
var blip = window.tideline.blip;
var chartDailyFactory = blip.oneday;

var imagesBaseUrl = '../../img';

var testpage = require('../../dev/testpage/index');

var tideline = require('../../js/index');
var containerId = '#testContainer';

describe('the tideline container', function(done) {
  // this set of tests isn't really testing anything...EXCEPT
  // we're using the dimensions of the container to test some other things below
  // so these tests are basically canaries to help us debug potentially breaking tests
  before(function() {
    $(containerId).append('

');
  });

  it('should have a width of 960px', function() {
    expect($('.tideline-container').width()).to.equal(960);
  });

  it('should have a height of 590px', function() {
    expect($('.tideline-container').height()).to.equal(590);
  });

  after(function() {
    $(containerId).empty();
  });
});

describe('one-day view', function() {
  var i = 0, testPatterns = testpage.main;

  describe('full day of data', function() {
    var container, oneDay;

    before(function() {
      $(containerId).append('

');
      var el = document.getElementById('tideline_' + i);
      oneDay = chartDailyFactory(el, {imagesBaseUrl: imagesBaseUrl}).setupPools();
      var data = preprocess.processData(testPatterns.full());
      oneDay.load(data).locate();
      container = $('#tideline_' + i);
      i++;
    });

    describe('cbg data', function() {
      it('should display cbgs with radius 2.5', function() {
        expect(parseFloat(container.find('.d3-circle-cbg').attr('r'),10)).to.equal(2.5);
      });

      it('should display 289 cbgs with color #FF8B7C, when seed is 79 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-low');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#FF8B7C'.toLowerCase());
      });

      it('should display 289 cbgs with color #9AD859, when seed is 100 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-target');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#9AD859'.toLowerCase());
      });

      it('should display 289 cbgs with color #BB9AE7, when seed is 181 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-high');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#BB9AE7'.toLowerCase());
      });

      describe('left edge cbg elements', function() {
        var leftLow, leftTarget, leftHigh;

        it('should have an x value at the axis gutter', function() {
          leftLow = container.find('.d3-circle-cbg.d3-bg-low').filter(':first');
          leftTarget = container.find('.d3-circle-cbg.d3-bg-target').filter(':first');
          leftHigh = container.find('.d3-circle-cbg.d3-bg-high').filter(':first');
          expect(parseInt(leftLow.attr('cx'), 10)).to.equal(oneDay.axisGutter());
          expect(parseInt(leftTarget.attr('cx'), 10)).to.equal(oneDay.axisGutter());
          expect(parseInt(leftHigh.attr('cx'), 10)).to.equal(oneDay.axisGutter());
        });

        it('(low and target) should offer a right and up tooltip (text = 60, 110) on hover', function() {
          leftLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + leftLow.attr('id').replace('cbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_low.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('60');
          leftTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + leftTarget.attr('id').replace('cbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_target.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('110');
        });

        it('(high) should offer a right and down tooltip (text = 300) on hover', function() {
          leftHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + leftHigh.attr('id').replace('cbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_high.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('300');
        });
      });

      describe('right edge cbg elements', function() {
        var rightLow, rightTarget, rightHigh;

        it('should have an x value at the right edge', function() {
          rightLow = container.find('.d3-circle-cbg.d3-bg-low').filter(':last');
          rightTarget = container.find('.d3-circle-cbg.d3-bg-target').filter(':last');
          rightHigh = container.find('.d3-circle-cbg.d3-bg-high').filter(':last');
          expect(parseInt(rightLow.attr('cx'), 10)).to.equal(960);
          expect(parseInt(rightTarget.attr('cx'), 10)).to.equal(960);
          expect(parseInt(rightHigh.attr('cx'), 10)).to.equal(960);
        });

        it('(low and target) should offer a left and up tooltip (text = 60, 110) on hover', function() {
          rightLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + rightLow.attr('id').replace('cbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_low_left.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('60');
          rightTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + rightTarget.attr('id').replace('cbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_target_left.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('110');
        });

        it('(high) should offer a left and down tooltip (text = 300) on hover', function() {
          rightHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + rightHigh.attr('id').replace('cbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_high_left.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('300');
        });
      });
    });
  });
});<commit_msg>done with one-day unit testing through carbs, minus interaction of carbs and bolus tooltips<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

/*
 * NB: browser-only tests
 */

/*jshint expr: true */
/*global describe, it, before, after, beforeEach, afterEach */

var chai = require('chai');
var assert = chai.assert;
var expect = chai.expect;

var $ = window.$;
var d3 = window.d3;
var preprocess = window.tideline.preprocess;
var blip = window.tideline.blip;
var chartDailyFactory = blip.oneday;

var imagesBaseUrl = '../../img';

var testpage = require('../../dev/testpage/index');

var tideline = require('../../js/index');
var containerId = '#testContainer';

var width = 960, height = 590;

describe('the tideline container', function(done) {
  // this set of tests isn't really testing anything...EXCEPT
  // we're using the dimensions of the container to test some other things below
  // so these tests are basically canaries to help us debug potentially breaking tests
  before(function() {
    $(containerId).append('

');
  });

  it('should have a width of 960px', function() {
    expect($('.tideline-container').width()).to.equal(width);
  });

  it('should have a height of 590px', function() {
    expect($('.tideline-container').height()).to.equal(height);
  });

  after(function() {
    $(containerId).empty();
  });
});

describe('one-day view', function() {
  var i = 0, testPatterns = testpage.main;

  describe('full day of data', function() {
    var container, oneDay;

    before(function() {
      $(containerId).append('

');
      var el = document.getElementById('tideline_' + i);
      oneDay = chartDailyFactory(el, {imagesBaseUrl: imagesBaseUrl}).setupPools();
      var data = preprocess.processData(testPatterns.full());
      oneDay.load(data).locate();
      container = $('#tideline_' + i);
      i++;
    });

    describe('bg pool', function() {
      it('should have a label', function() {
        expect(container.find('#poolBG_label_0').size()).to.be.above(0);
        expect(container.find('#poolBG_label_0').html()).to.equal('Blood Glucose<tspan> (mg/dL)</tspan>');
      });

      it('should have a legend', function() {
        expect(container.find('#poolBG_legend_bg').size()).to.be.above(0);
      });
    });

    describe('cbg data', function() {
      it('should display cbgs with radius 2.5', function() {
        expect(container.find('.d3-circle-cbg').attr('r')).to.equal('2.5');
      });

      it('should display 289 cbgs with color #FF8B7C, when seed is 79 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-low');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#FF8B7C'.toLowerCase());
      });

      it('should display 289 cbgs with color #9AD859, when seed is 100 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-target');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#9AD859'.toLowerCase());
      });

      it('should display 289 cbgs with color #BB9AE7, when seed is 181 mg/dL', function() {
        var these = container.find('.d3-circle-cbg.d3-bg-high');
        expect(these.length).to.equal(289);
        expect(d3.rgb(these.css('fill')).toString()).to.equal('#BB9AE7'.toLowerCase());
      });

      describe('left edge cbg elements', function() {
        var leftLow, leftTarget, leftHigh;

        it('should have an x value at the axis gutter', function() {
          leftLow = container.find('.d3-circle-cbg.d3-bg-low').filter(':first');
          leftTarget = container.find('.d3-circle-cbg.d3-bg-target').filter(':first');
          leftHigh = container.find('.d3-circle-cbg.d3-bg-high').filter(':first');

          expect(parseInt(leftLow.attr('cx'), 10)).to.equal(oneDay.axisGutter());
          expect(parseInt(leftTarget.attr('cx'), 10)).to.equal(oneDay.axisGutter());
          expect(parseInt(leftHigh.attr('cx'), 10)).to.equal(oneDay.axisGutter());
        });

        it('(low and target) should yield a right and up tooltip (text = 60, 110) on hover', function() {
          leftLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + leftLow.attr('id').replace('cbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_low.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('60');

          leftTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + leftTarget.attr('id').replace('cbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_target.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('110');
        });

        it('(high) should yield a right and down tooltip (text = 300) on hover', function() {
          leftHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + leftHigh.attr('id').replace('cbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_high.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('300');
        });

        it('should have tooltips that disappear on mouseout', function() {
          leftLow.simulate('mouseout');
          leftTarget.simulate('mouseout');
          leftHigh.simulate('mouseout');
          expect(container.find('#tidelineTooltips_cbg .d3-tooltip').size()).to.equal(0);
          leftLow.simulate('mouseover');
          leftTarget.simulate('mouseover');
          leftHigh.simulate('mouseover');
        });
      });

      describe('right edge cbg elements', function() {
        var rightLow, rightTarget, rightHigh;

        it('should have an x value at the right edge', function() {
          rightLow = container.find('.d3-circle-cbg.d3-bg-low').filter(':last');
          rightTarget = container.find('.d3-circle-cbg.d3-bg-target').filter(':last');
          rightHigh = container.find('.d3-circle-cbg.d3-bg-high').filter(':last');

          expect(parseInt(rightLow.attr('cx'), 10)).to.equal(width);
          expect(parseInt(rightTarget.attr('cx'), 10)).to.equal(width);
          expect(parseInt(rightHigh.attr('cx'), 10)).to.equal(width);
        });

        it('(low and target) should yield a left and up tooltip (text = 60, 110) on hover', function() {
          rightLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + rightLow.attr('id').replace('cbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_low_left.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('60');

          rightTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + rightTarget.attr('id').replace('cbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_target_left.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('110');
        });

        it('(high) should yield a left and down tooltip (text = 300) on hover', function() {
          rightHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + rightHigh.attr('id').replace('cbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/cbg/cbg_tooltip_high_left.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('300');
        });

        it('should have tooltips that disappear on mouseout', function() {
          rightLow.simulate('mouseout');
          rightTarget.simulate('mouseout');
          rightHigh.simulate('mouseout');
          // 3 not zero because left tooltips get added back for visual inspection purposes
          expect(container.find('#tidelineTooltips_cbg .d3-tooltip').size()).to.equal(3);
          rightLow.simulate('mouseover');
          rightTarget.simulate('mouseover');
          rightHigh.simulate('mouseover');
        });
      });
    });

    describe('smbg data', function() {
      var circleSize = 16, highestLow, lowestHigh;

      it('should display smbgs with width and height of 16', function() {
        expect(parseFloat(container.find('.d3-image-smbg').attr('width'),10)).to.equal(circleSize);
        expect(parseFloat(container.find('.d3-image-smbg').attr('height'),10)).to.equal(circleSize);
      });

      it('should display 8 low smbgs, 2 of which are very low at bottom of pool', function() {
        var lows = container.find('.d3-image-smbg.d3-bg-low');
        // parent group itself doesn't have a pixel height, so compare to first fill rectangle instead
        var fillRectHeight = container.find('#poolBG_fill').find('rect').filter(':first').attr('height');

        lows.each(function() {
          var isVeryLow = $(this).attr('href') === '../../img/smbg/very_low.svg';
          var isLow = $(this).attr('href') === '../../img/smbg/low.svg';
          if (isVeryLow) {
            expect(parseFloat($(this).attr('y'))).to.equal(fillRectHeight - circleSize);
          }
          else if (isLow) {
            highestLow = parseFloat($(this).attr('y'));
            expect(highestLow).to.be.below(fillRectHeight - circleSize);
          }
          else {
            expect(!(isVeryLow || isLow)).to.be.false;
          }
        });
        expect(lows.size()).to.equal(8);
      });

      it('should display 8 high smbgs, 2 of which are very high at top of pool', function() {
        var highs = container.find('.d3-image-smbg.d3-bg-high');

        highs.each(function() {
          var isVeryHigh = $(this).attr('href') === '../../img/smbg/very_high.svg';
          var isHigh = $(this).attr('href') === '../../img/smbg/high.svg';
          if (isVeryHigh) {
            expect(parseFloat($(this).attr('y'))).to.equal(0);
          }
          else if (isHigh) {
            lowestHigh = parseFloat($(this).attr('y'));
            expect(lowestHigh).to.be.above(0);
          }
          else {
            expect(!(isVeryHigh || isHigh)).to.be.false;
          }
        });
        expect(highs.size()).to.equal(8);
      });

      it('should display 8 target smbgs with y attributes between highestLow and lowestHigh', function() {
        var targets = container.find('.d3-image-smbg.d3-bg-target');
        expect(targets.size()).to.equal(8);
        targets.each(function() {
          expect(parseFloat($(this).attr('y'))).to.be.below(highestLow);
          expect(parseFloat($(this).attr('y'))).to.be.above(lowestHigh);
        });
      });

      describe('left edge smbg elements', function() {
        var leftLow, leftTarget, leftHigh;

        it('(low and target) should yield a right and up tooltip (text = 20, 80) on hover', function() {
          leftLow = container.find('.d3-image-smbg.d3-bg-low').filter(':first');
          leftTarget = container.find('.d3-image-smbg.d3-bg-target').filter(':first');
          leftHigh = container.find('.d3-image-smbg.d3-bg-high').filter(':first');

          leftLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + leftLow.attr('id').replace('smbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_low.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('20');
          // check for AM not am because CSS handles the transform to lowercase
          expect(lowTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 12:30 AM');
          expect(lowTooltipGroup.find('rect').size()).to.be.above(0);

          leftTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + leftTarget.attr('id').replace('smbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_target.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('80');
          expect(targetTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 1:30 AM');
          expect(targetTooltipGroup.find('rect').size()).to.be.above(0);
        });

        it('(high) should yield a right and down tooltip (text = 181) on hover', function() {
          leftHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + leftHigh.attr('id').replace('smbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_high.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('181');
          expect(highTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 2:30 AM');
          expect(highTooltipGroup.find('rect').size()).to.be.above(0);
        });

        it('should have tooltips that disappear on mouseout', function() {
          leftLow.simulate('mouseout');
          leftTarget.simulate('mouseout');
          leftHigh.simulate('mouseout');
          expect(container.find('#tidelineTooltips_smbg .d3-tooltip').size()).to.equal(0);
          leftLow.simulate('mouseover');
          leftTarget.simulate('mouseover');
          leftHigh.simulate('mouseover');
        });
      });

      describe('right edge smbg elements', function() {
        var rightLow, rightTarget, rightHigh;

        it('(low and target) should yield a left and up tooltip (text = 20, 80) on hover', function() {
          rightLow = container.find('.d3-image-smbg.d3-bg-low').filter(':last');
          rightTarget = container.find('.d3-image-smbg.d3-bg-target').filter(':last');
          rightHigh = container.find('.d3-image-smbg.d3-bg-high').filter(':last');

          rightLow.simulate('mouseover');
          var lowTooltipGroup = container.find('#tooltip_' + rightLow.attr('id').replace('smbg_', ''));
          expect(lowTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_low_left.svg');
          expect(lowTooltipGroup.find('text').html()).to.equal('20');
          expect(lowTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 11:30 PM');
          expect(lowTooltipGroup.find('rect').size()).to.be.above(0);

          rightTarget.simulate('mouseover');
          var targetTooltipGroup = container.find('#tooltip_' + rightTarget.attr('id').replace('smbg_', ''));
          expect(targetTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_target_left.svg');
          expect(targetTooltipGroup.find('text').html()).to.equal('80');
          expect(targetTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 10:30 PM');
          expect(targetTooltipGroup.find('rect').size()).to.be.above(0);
        });

        it('(high) should yield a left and down tooltip (text = 181) on hover', function() {
          rightHigh.simulate('mouseover');
          var highTooltipGroup = container.find('#tooltip_' + rightHigh.attr('id').replace('smbg_', ''));
          expect(highTooltipGroup.find('image').attr('href')).to.equal('../../img/smbg/smbg_tooltip_high_left.svg');
          expect(highTooltipGroup.find('text').html()).to.equal('181');
          expect(highTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 9:30 PM');
          expect(highTooltipGroup.find('rect').size()).to.be.above(0);
        });

        it('should have tooltips that disappear on mouseout', function() {
          rightLow.simulate('mouseout');
          rightTarget.simulate('mouseout');
          rightHigh.simulate('mouseout');
          // 3 not zero because left tooltips get added back for visual inspection purposes
          expect(container.find('#tidelineTooltips_smbg .d3-tooltip').size()).to.equal(3);
          rightLow.simulate('mouseover');
          rightTarget.simulate('mouseover');
          rightHigh.simulate('mouseover');
        });
      });
    });

    describe('carbs data', function() {
      var carbs, leftCarb, rightCarb;

      it('should display carbs events with a width of 12 and color #FFD382', function() {
        carbs = container.find('.d3-rect-carbs');
        expect(carbs.size()).to.equal(16);
        expect(carbs.attr('width')).to.equal('12');
        expect(d3.rgb(carbs.css('fill')).toString()).to.equal('#FFD382'.toLowerCase());
      });

      it('should yield a right and down tooltip if on the left edge', function() {
        leftCarb = carbs.filter(':first');
        leftCarb.simulate('mouseover');
        var leftTooltipGroup = container.find('#tooltip_' + leftCarb.attr('id').replace('carbs_', ''));
        expect(leftTooltipGroup.find('image').attr('href')).to.equal('../../img/carbs/tooltip_carbs.svg');
        expect(leftTooltipGroup.find('text').html()).to.equal('100g');
        expect(leftTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 1:00 AM');
        expect(leftTooltipGroup.find('rect').size()).to.be.above(0);
      });

      it('should yield a left and down tooltip if on the right edge', function() {
        rightCarb = carbs.filter(':last');
        rightCarb.simulate('mouseover');
        var rightTooltipGroup = container.find('#tooltip_' + rightCarb.attr('id').replace('carbs_', ''));
        expect(rightTooltipGroup.find('image').attr('href')).to.equal('../../img/carbs/tooltip_carbs_left.svg');
        expect(rightTooltipGroup.find('text').html()).to.equal('10g');
        expect(rightTooltipGroup.find('text').filter('.d3-tooltip-timestamp').html()).to.equal('at 11:30 PM');
        expect(rightTooltipGroup.find('rect').size()).to.be.above(0);
      });

      it('should have tooltips that disappear on mouseout', function() {
        leftCarb.simulate('mouseout');
        rightCarb.simulate('mouseout');
        expect(container.find('#tidelineTooltips_carbs .d3-tooltip').size()).to.equal(0);
        leftCarb.simulate('mouseover');
        rightCarb.simulate('mouseover');
      });
    });
  });
});<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS/index';
import ITEMS from 'common/ITEMS/index';
import Analyzer from 'parser/core/Analyzer';
import UptimeIcon from 'interface/icons/Uptime';
import PrimaryStatIcon from 'interface/icons/PrimaryStat';
import ItemStatistic from 'interface/statistics/ItemStatistic';
import BoringItemValueText from 'interface/statistics/components/BoringItemValueText';
import { formatPercentage, formatNumber } from 'common/format';
import { calculatePrimaryStat } from 'common/stats';
import Abilities from 'parser/core/modules/Abilities';

/**
 * Dread Gladiator's Badge -
 * Increases primary stat by 657 for 15 sec. (2 Min Cooldown)
 */
class DreadGladiatorsBadge extends Analyzer {
  static dependencies = {
    abilities: Abilities,
  };

  statBuff = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.hasTrinket(ITEMS.DREAD_GLADIATORS_BADGE.id);

    if (this.active) {
      this.statBuff = calculatePrimaryStat(385, 1746, this.selectedCombatant.getItem(ITEMS.DREAD_GLADIATORS_BADGE.id).itemLevel);
      this.abilities.add({
        spell: SPELLS.DIG_DEEP,
        buffSpellId: SPELLS.DIG_DEEP.id,
        name: ITEMS.DREAD_GLADIATORS_BADGE.name,
        category: Abilities.SPELL_CATEGORIES.ITEMS,
        cooldown: 120,
        castEfficiency: {
          suggestion: true,
        },
      });
    }
  }

  get totalBuffUptime() {
    return this.selectedCombatant.getBuffUptime(SPELLS.DIG_DEEP.id) / this.owner.fightDuration;
  }

  statistic() {
    console.log(this.selectedCombatant.spec.primaryStat);
    return (
      <ItemStatistic
        size="flexible"
      >
        <BoringItemValueText item={ITEMS.DREAD_GLADIATORS_BADGE}>
          <UptimeIcon /> {formatPercentage(this.totalBuffUptime)}% uptime

          <PrimaryStatIcon stat={this.selectedCombatant.spec.primaryStat} /> {formatNumber(this.totalBuffUptime * this.statBuff)} average {this.selectedCombatant.spec.primaryStat} gained
        </BoringItemValueText>
      </ItemStatistic>
    );
  }
}

        </BoringItemValueText>
      </ItemStatistic>
    );
  }
}

export default DreadGladiatorsBadge;
<commit_msg>Update DreadGladiatorsBadge.js<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS/index';
import ITEMS from 'common/ITEMS/index';
import Analyzer from 'parser/core/Analyzer';
import UptimeIcon from 'interface/icons/Uptime';
import PrimaryStatIcon from 'interface/icons/PrimaryStat';
import ItemStatistic from 'interface/statistics/ItemStatistic';
import BoringItemValueText from 'interface/statistics/components/BoringItemValueText';
import { formatPercentage, formatNumber } from 'common/format';
import { calculatePrimaryStat } from 'common/stats';
import Abilities from 'parser/core/modules/Abilities';

/**
 * Dread Gladiator's Badge -
 * Increases primary stat by 657 for 15 sec. (2 Min Cooldown)
 */
class DreadGladiatorsBadge extends Analyzer {
  static dependencies = {
    abilities: Abilities,
  };

  statBuff = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.hasTrinket(ITEMS.DREAD_GLADIATORS_BADGE.id);

    if (this.active) {
      this.statBuff = calculatePrimaryStat(385, 1746, this.selectedCombatant.getItem(ITEMS.DREAD_GLADIATORS_BADGE.id).itemLevel);
      this.abilities.add({
        spell: SPELLS.DIG_DEEP,
        buffSpellId: SPELLS.DIG_DEEP.id,
        name: ITEMS.DREAD_GLADIATORS_BADGE.name,
        category: Abilities.SPELL_CATEGORIES.ITEMS,
        cooldown: 120,
        castEfficiency: {
          suggestion: true,
        },
      });
    }
  }

  get totalBuffUptime() {
    return this.selectedCombatant.getBuffUptime(SPELLS.DIG_DEEP.id) / this.owner.fightDuration;
  }

  statistic() {
    console.log(this.selectedCombatant.spec.primaryStat);
    return (
      <ItemStatistic
        size="flexible"
      >
        <BoringItemValueText item={ITEMS.DREAD_GLADIATORS_BADGE}>
          <UptimeIcon /> {formatPercentage(this.totalBuffUptime)}% uptime

          <PrimaryStatIcon stat={this.selectedCombatant.spec.primaryStat} /> {formatNumber(this.totalBuffUptime * this.statBuff)} average {this.selectedCombatant.spec.primaryStat} gained
        </BoringItemValueText>
      </ItemStatistic>
    );
  }
}

export default DreadGladiatorsBadge;
<|endoftext|>


<commit_before>$(document).ready(function () {
    makeFireworkGroup("canvasForfireworks", 3, 300);
});

function makeFireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    function shotFireworkGroup(fireworkGroup) {
        var fireworks = fireworkGroup.getFireworks();
        fireworks.forEach(function(firework) {
            shotFirework(firework);
        });
    }

    function shotFirework(firework) {
        firework.shot();

        var wait = randomInRange(1200, 1600);
        setTimeout(shotFirework, wait, firework);
    }

    function renderAndUpdate(fireworks) {
        return function() {
            fireworks.render();
            fireworks.update();
        };
    }

    var fireworks = new FireworkGroup(canvasId,
                                      numberOfFireworks,
                                      numberOfParticles);
    shotFireworkGroup(fireworks);

    var renderAndUpdateFunc = renderAndUpdate(fireworks)
    setInterval(renderAndUpdateFunc, 15);
}

function FireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    var fireworkGroupElement = document.getElementById(canvasId);
    var context = fireworkGroupElement.getContext("2d");

    var width = fireworkGroupElement.clientWidth;
    var height = fireworkGroupElement.clientHeight;

    var fireworks = initFireworkGroup(width, height);

    this.getFireworks = function() {
        return fireworks;
    }

    this.render = function() {
        context.fillStyle = "#010212";
        context.fillRect(0, 0, width, height)

        fireworks.forEach(function(firework) {
           firework.render(context);
        });
    }

    this.update = function() {
        fireworks.forEach(function(firework) {
           firework.update();
        });
    }

    this.shot = function() {
        fireworks.forEach(function(firework) {
           firework.shot();
        });
    }

    function initFireworkGroup(width, height) {
        var fireworks = [];
        for(var i = 0; i < numberOfFireworks; ++i) {
            var pos = {
                x: Math.round((width / numberOfFireworks) * (i + 0.5)),
                y: height * 0.95
            };
            var canvasSize = {
                width: width,
                height: height
            };

            fireworks[i] = new Firework(pos, canvasSize, numberOfParticles);
        }

        return fireworks;
    }
}

function Firework(pos, canvasSize, numberOfParticles) {
    var shots = [];
    this.render = function(context) {
        shots.forEach(function(shot) {
            shot.render(context);
        });
    }

    this.update = function() {
        removeDeadShots();

        shots.forEach(function(shot) {
            shot.update();
        });
    }

    this.shot = function() {
        var newShot = new ParticleGroup(pos, canvasSize, numberOfParticles);
        shots.push(newShot);
    }

    function removeDeadShots() {
        for(var i = 0; i < shots.length; ++i) {
            shot = shots[i];
            if (shot.isDead()) {
                shots.splice(i, 1);
            }
        }
    }
}

function ParticleGroup(pos, canvasSize, numberOfParticles) {
    var shotHeight = randomInRange(canvasSize.height * 0.50,
                                   canvasSize.height * 0.75);
    var life = 100;
    var age = 0;
    var particles = initParticles(pos, canvasSize);
    var color = pickColor();

    this.render = function(context) {
        var strColor = color.toString();

        particles.forEach(function(particle) {
            particle.render(context, strColor);
        });
    }

    this.update = function() {
        age++;

        updateColor();

        particles.forEach(function(particle) {
            particle.update();
        });
    }

    this.isDead = function() {
        return age >= life;
    }

    function initParticles(pos, canvasSize) {
        var particles = [];

        var particlePos = {
            x: pos.x,
            y: pos.y - shotHeight
        }

        var resistance = 0.985;
        var gravity = {
            x: 0,
            y: 0.005
        }
        var size = 2;

        var maxSpeed = randomInRange(2.4, 3.2);

        for(var i = 0; i < numberOfParticles; ++i) {
            var angle = randomInRange(0, Math.PI * 2);
            var linearSpeed = randomInRange(0, maxSpeed);
            var speed = {
                x: linearSpeed * Math.cos(angle),
                y: linearSpeed * Math.sin(angle),
            }

            var particle = new Particle(particlePos, speed, resistance,
                                        gravity, size);
            particles.push(particle);
        }

        return particles;
    }

    function updateColor() {
        var alpha = 1.0;
        var oldness = age / life;
        if (oldness > 0.90) {
            alpha = 10 * (1 - oldness);
            color.setAlpha(alpha);
        }
    }
}

function Particle(pos, speed, resistance, gravity, size) {
    var curPos = {
        x: pos.x,
        y: pos.y
    };
    var curSpeed = speed;

    this.render = function(context, color) {
        context.fillStyle = color;
        context.beginPath();
        context.arc(curPos.x, curPos.y, size, 0, Math.PI * 2, true);
        context.closePath();
        context.fill();
    }

    this.update = function() {
        curSpeed.x = curSpeed.x * resistance + gravity.x;
        curSpeed.y = curSpeed.y * resistance + gravity.y;

        curPos.x += curSpeed.x;
        curPos.y += curSpeed.y;
    }
}

function randomInRange(min, max) {
    return Math.random() * (max - min) + min;
}

var pickColor = (function() {
    var colors = [
        new Color(0x00, 0xFF, 0xFF), // Aqua
        new Color(0x8A, 0x2B, 0xE2), // BlueViolet
        new Color(0xDC, 0x14, 0x3C), // Crimson
        new Color(0xFF, 0x14, 0x93), // DeepPink
        new Color(0x22, 0x8B, 0x22), // ForestGreen
        new Color(0xAD, 0xFF, 0x2F), // GreenYello
        new Color(0xFF, 0x69, 0xB4), // HotPink
        new Color(0xCD, 0x5C, 0x5C), // IndianRed
        new Color(0xF0, 0xE6, 0x8C), // Khaki
        new Color(0x7C, 0xFC, 0x00), // LawGreen
        new Color(0x00, 0xFA, 0x9A), // MediumSrpingGreen
        new Color(0xFF, 0xA5, 0x00), // Orange
        new Color(0x80, 0x00, 0x00), // Purple
        new Color(0xFF, 0x00, 0x00), // Red
        new Color(0x87, 0xCE, 0xEB), // SkyBlue
        new Color(0xFF, 0x63, 0x47), // Tomato
        new Color(0xEE, 0x82, 0xEE), // Violet
        new Color(0xF5, 0xDE, 0xB3), // Wheat
        new Color(0xFF, 0xFF, 0x00)  // Yellow
              ];

    return function() {
        var index = Math.round(randomInRange(0, colors.length - 1));
        return colors[index].clone();
    }
})();

function Color(red, green, blue, alpha) {
    var r = red,
        g = green,
        b = blue,
        a = alpha;

    this.toString = function() {
        if (a === undefined) {
            return "rgb(" + r + "," + g + "," + b + ")";
        }

        return "rgba(" + r + "," + g + "," + b + "," + a + ")";
    }

    this.setAlpha = function(newAlpha) {
        a = newAlpha;
    }

    this.clone = function() {
        return new Color(r, g, b, a);
    }
}<commit_msg>Rename functions shot as shoot<commit_after>$(document).ready(function () {
    makeFireworkGroup("canvasForfireworks", 3, 300);
});

function makeFireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    function shootFireworkGroup(fireworkGroup) {
        var fireworks = fireworkGroup.getFireworks();
        fireworks.forEach(function(firework) {
            shootFirework(firework);
        });
    }

    function shootFirework(firework) {
        firework.shoot();

        var wait = randomInRange(1200, 1600);
        setTimeout(shootFirework, wait, firework);
    }

    function renderAndUpdate(fireworks) {
        return function() {
            fireworks.render();
            fireworks.update();
        };
    }

    var fireworks = new FireworkGroup(canvasId,
                                      numberOfFireworks,
                                      numberOfParticles);
    shootFireworkGroup(fireworks);

    var renderAndUpdateFunc = renderAndUpdate(fireworks)
    setInterval(renderAndUpdateFunc, 15);
}

function FireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    var fireworkGroupElement = document.getElementById(canvasId);
    var context = fireworkGroupElement.getContext("2d");

    var width = fireworkGroupElement.clientWidth;
    var height = fireworkGroupElement.clientHeight;

    var fireworks = initFireworkGroup(width, height);

    this.getFireworks = function() {
        return fireworks;
    }

    this.render = function() {
        context.fillStyle = "#010212";
        context.fillRect(0, 0, width, height)

        fireworks.forEach(function(firework) {
           firework.render(context);
        });
    }

    this.update = function() {
        fireworks.forEach(function(firework) {
           firework.update();
        });
    }

    this.shoot = function() {
        fireworks.forEach(function(firework) {
           firework.shoot();
        });
    }

    function initFireworkGroup(width, height) {
        var fireworks = [];
        for(var i = 0; i < numberOfFireworks; ++i) {
            var pos = {
                x: Math.round((width / numberOfFireworks) * (i + 0.5)),
                y: height * 0.95
            };
            var canvasSize = {
                width: width,
                height: height
            };

            fireworks[i] = new Firework(pos, canvasSize, numberOfParticles);
        }

        return fireworks;
    }
}

function Firework(pos, canvasSize, numberOfParticles) {
    var shots = [];
    this.render = function(context) {
        shots.forEach(function(shot) {
            shot.render(context);
        });
    }

    this.update = function() {
        removeDeadShots();

        shots.forEach(function(shot) {
            shot.update();
        });
    }

    this.shoot = function() {
        var newShot = new ParticleGroup(pos, canvasSize, numberOfParticles);
        shots.push(newShot);
    }

    function removeDeadShots() {
        for(var i = 0; i < shots.length; ++i) {
            shot = shots[i];
            if (shot.isDead()) {
                shots.splice(i, 1);
            }
        }
    }
}

function ParticleGroup(pos, canvasSize, numberOfParticles) {
    var shotHeight = randomInRange(canvasSize.height * 0.50,
                                   canvasSize.height * 0.75);
    var life = 100;
    var age = 0;
    var particles = initParticles(pos, canvasSize);
    var color = pickColor();

    this.render = function(context) {
        var strColor = color.toString();

        particles.forEach(function(particle) {
            particle.render(context, strColor);
        });
    }

    this.update = function() {
        age++;

        updateColor();

        particles.forEach(function(particle) {
            particle.update();
        });
    }

    this.isDead = function() {
        return age >= life;
    }

    function initParticles(pos, canvasSize) {
        var particles = [];

        var particlePos = {
            x: pos.x,
            y: pos.y - shotHeight
        }

        var resistance = 0.985;
        var gravity = {
            x: 0,
            y: 0.005
        }
        var size = 2;

        var maxSpeed = randomInRange(2.4, 3.2);

        for(var i = 0; i < numberOfParticles; ++i) {
            var angle = randomInRange(0, Math.PI * 2);
            var linearSpeed = randomInRange(0, maxSpeed);
            var speed = {
                x: linearSpeed * Math.cos(angle),
                y: linearSpeed * Math.sin(angle),
            }

            var particle = new Particle(particlePos, speed, resistance,
                                        gravity, size);
            particles.push(particle);
        }

        return particles;
    }

    function updateColor() {
        var alpha = 1.0;
        var oldness = age / life;
        if (oldness > 0.90) {
            alpha = 10 * (1 - oldness);
            color.setAlpha(alpha);
        }
    }
}

function Particle(pos, speed, resistance, gravity, size) {
    var curPos = {
        x: pos.x,
        y: pos.y
    };
    var curSpeed = speed;

    this.render = function(context, color) {
        context.fillStyle = color;
        context.beginPath();
        context.arc(curPos.x, curPos.y, size, 0, Math.PI * 2, true);
        context.closePath();
        context.fill();
    }

    this.update = function() {
        curSpeed.x = curSpeed.x * resistance + gravity.x;
        curSpeed.y = curSpeed.y * resistance + gravity.y;

        curPos.x += curSpeed.x;
        curPos.y += curSpeed.y;
    }
}

function randomInRange(min, max) {
    return Math.random() * (max - min) + min;
}

var pickColor = (function() {
    var colors = [
        new Color(0x00, 0xFF, 0xFF), // Aqua
        new Color(0x8A, 0x2B, 0xE2), // BlueViolet
        new Color(0xDC, 0x14, 0x3C), // Crimson
        new Color(0xFF, 0x14, 0x93), // DeepPink
        new Color(0x22, 0x8B, 0x22), // ForestGreen
        new Color(0xAD, 0xFF, 0x2F), // GreenYello
        new Color(0xFF, 0x69, 0xB4), // HotPink
        new Color(0xCD, 0x5C, 0x5C), // IndianRed
        new Color(0xF0, 0xE6, 0x8C), // Khaki
        new Color(0x7C, 0xFC, 0x00), // LawGreen
        new Color(0x00, 0xFA, 0x9A), // MediumSrpingGreen
        new Color(0xFF, 0xA5, 0x00), // Orange
        new Color(0x80, 0x00, 0x00), // Purple
        new Color(0xFF, 0x00, 0x00), // Red
        new Color(0x87, 0xCE, 0xEB), // SkyBlue
        new Color(0xFF, 0x63, 0x47), // Tomato
        new Color(0xEE, 0x82, 0xEE), // Violet
        new Color(0xF5, 0xDE, 0xB3), // Wheat
        new Color(0xFF, 0xFF, 0x00)  // Yellow
              ];

    return function() {
        var index = Math.round(randomInRange(0, colors.length - 1));
        return colors[index].clone();
    }
})();

function Color(red, green, blue, alpha) {
    var r = red,
        g = green,
        b = blue,
        a = alpha;

    this.toString = function() {
        if (a === undefined) {
            return "rgb(" + r + "," + g + "," + b + ")";
        }

        return "rgba(" + r + "," + g + "," + b + "," + a + ")";
    }

    this.setAlpha = function(newAlpha) {
        a = newAlpha;
    }

    this.clone = function() {
        return new Color(r, g, b, a);
    }
}<|endoftext|>


<commit_before>import styled from "styled-components";
import { colors } from "@crave/farmblocks-theme";

const scale = props => (props.scale ? 1.219 : 1);
const activeItemWidth = props => props.itemConfig.width * scale(props);
const activeItemHeight = props => props.itemConfig.height * scale(props);
const spaceBetweenItems = props => props.itemConfig.margin * 2;
const stepDistance = props => props.itemConfig.width + spaceBetweenItems(props);
const activeItemMargin = props => {
  const { itemConfig } = props;
  const scaledWidth = itemConfig.width * scale(props);

  // We get the difference from scaled width and normal width.
  // This difference is the total margin we have to compensate due to transform: scale.
  // Then we divide it by 2 because we need to add this on left and right margin
  const scaledMargin = (scaledWidth - itemConfig.width) / 2;

  // Just add the default margin to it
  return scaledMargin + itemConfig.margin;
};

const Container = styled.div`
  position: relative;
  ul {
    position: relative;
    left: 50%;
    margin-left: ${props => -(activeItemWidth(props) / 2)}px;
    height: ${props => activeItemHeight(props) + spaceBetweenItems(props)}px;
    list-style: none;
    padding: 0;
    display: inline-flex;
    align-items: center;

    transform: translateX(
      ${props => -(props.activeItem * stepDistance(props))}px
    );
    transition: transform ${props => props.itemConfig.transitionTime}s;
    will-change: transform;

    li {
      margin: ${props => props.itemConfig.margin}px;
      width: ${props => props.itemConfig.width}px;
      height: ${props => props.itemConfig.height}px;

      transition: all ${props => props.itemConfig.transitionTime}s;
      will-change: transform;

      .image {
        box-shadow: 0 4px 40px 0 ${colors.GREY_32};
      }

      &.active {
        transform: scale(${scale});
        transform-origin: bottom;
        margin: 0 ${activeItemMargin}px;
      }
    }
  }
`;

export default Container;
<commit_msg>fix(Carousel): add min-width to carousel items<commit_after>import styled from "styled-components";
import { colors } from "@crave/farmblocks-theme";

const scale = props => (props.scale ? 1.219 : 1);
const activeItemWidth = props => props.itemConfig.width * scale(props);
const activeItemHeight = props => props.itemConfig.height * scale(props);
const spaceBetweenItems = props => props.itemConfig.margin * 2;
const stepDistance = props => props.itemConfig.width + spaceBetweenItems(props);
const activeItemMargin = props => {
  const { itemConfig } = props;
  const scaledWidth = itemConfig.width * scale(props);

  // We get the difference from scaled width and normal width.
  // This difference is the total margin we have to compensate due to transform: scale.
  // Then we divide it by 2 because we need to add this on left and right margin
  const scaledMargin = (scaledWidth - itemConfig.width) / 2;

  // Just add the default margin to it
  return scaledMargin + itemConfig.margin;
};

const Container = styled.div`
  position: relative;
  ul {
    position: relative;
    left: 50%;
    margin-left: ${props => -(activeItemWidth(props) / 2)}px;
    height: ${props => activeItemHeight(props) + spaceBetweenItems(props)}px;
    list-style: none;
    padding: 0;
    display: inline-flex;
    align-items: center;

    transform: translateX(
      ${props => -(props.activeItem * stepDistance(props))}px
    );
    transition: transform ${props => props.itemConfig.transitionTime}s;
    will-change: transform;

    li {
      margin: ${props => props.itemConfig.margin}px;
      min-width: ${props => props.itemConfig.width}px;
      width: ${props => props.itemConfig.width}px;
      height: ${props => props.itemConfig.height}px;

      transition: all ${props => props.itemConfig.transitionTime}s;
      will-change: transform;

      .image {
        box-shadow: 0 4px 40px 0 ${colors.GREY_32};
      }

      &.active {
        transform: scale(${scale});
        transform-origin: bottom;
        margin: 0 ${activeItemMargin}px;
      }
    }
  }
`;

export default Container;
<|endoftext|>


<commit_before>var Log = require('./log.js');
var Api = require('node-trello');

function Trello(trelloConfig) {
  this.config = trelloConfig;
  this.api = new Api(this.config.key, this.config.token);
  this.values = {};
  this.initialized = false;

  // initialize
  this.init = function(){
    self = this;
    Promise.all([
      self.loadMe(),
      self.loadBoard()
    ]).then(function(res) {
      return Promise.resolve(
        Promise.all([
          self.loadCards(),
          self.loadList()
        ])
      );
    }).then(function(res) {
      Log.i('Trello data loaded!!');
      Log.d(self);

      self.initialized = true;
    }).catch(function(error) {
      Log.e(error);
    });
  };

  // API request GET me
  this.loadMe = function() {
    Log.i('GET trello me: ');

    self = this;
    return new Promise(function(resolve, reject) {
      self.api.get('/1/members/me', function(err, data) {
        if (err) reject(err);
        Log.i('GET trello me: Me ["' + data.fullName + '"] found');
        Log.d(data);

        self.values.me = data.id;
        resolve(true);
      });
    });
  };

  // API request GET board
  this.loadBoard = function() {
    Log.i('GET trello board: ');

    self = this;
    return new Promise(function(resolve, reject) {
      self.api.get('/1/members/me/boards', function(err, data) {
        if (err) reject(err);
        Log.d(data);

        for (board of data) {
          if (new RegExp(self.config.board.name).test(board.name)) {
            Log.i('GET trello board: Board ["' + self.config.board.name + '"] found');
            Log.d(board);

            self.values.board = board.id;
            resolve(true);
          }
        }
        reject(new Error('Not found board'));
      });
    });
  };

  // API request GET cards
  this.loadCards = function() {
    Log.i('GET trello my card: ');
    self = this;

    return new Promise(function(resolve, reject) {
      self.api.get('/1/boards/' + self.values.board + '/cards?fields=id,idMembers,name', function(err, data) {
        if (err) reject(err);
        Log.d(data);
        self.values.cards = [];
        for (card of data) {
          for (member of card.idMembers) {
            if (member == self.values.me) {
              Log.i('GET trello my card: My Card ["' + card.name + '"] found');
              Log.d(card);

              self.values.mycard = card.id;
            }
          }
          self.values.cards.push(card.id);
        }
        resolve(true);
      });
    });
  };

  // API request GET list
  this.loadList = function() {
    Log.i('GET trello list: ');
    self = this;

    return new Promise(function(resolve, reject) {
      self.api.get('/1/boards/' + self.values.board + '/lists', function(err, data) {
        if (err) reject(err);
        Log.d(data);

        states = ['online', 'offline', 'away'];
        for (list of data) {
          for (state of states) {
            if (new RegExp(eval('self.config.board.list.' + state)).test(list.name)) {
              Log.i('GET trello list: List ["' + list.name + '"] found');
              Log.d(list);

              eval('self.values.' + state + ' = list.id');
            }
          }
        }
        resolve(true);
      });
    });
  };

  // API request PUT card
  this.moveCard = function(card, listName) {
    Log.i('PUT trello card: ');
    if (listName == 'online') {
      list = this.values.online;
    } else if (listName == 'offline') {
      list = this.values.offline;
    } else if (listName == 'away') {
      list = this.values.away;
    } else {
      throw new Error('Invalid list name');
    }

    this.api.put('/1/cards/' + card + '/idList?value=' + list, function(err, data) {
      if (err) throw err;
      Log.i('PUT trello card: ' + card + ' was moved to "' + listName + '"');
      Log.d(data);
    });
  };

  // API request PUT card
  this.moveMyCard = function(listName) {
    this.moveCard(this.values.mycard, listName);
  }

  // API requrest PUT cards
  this.moveAllCards = function(listName) {
    for (card of this.values.cards) {
      this.moveCard(card, listName);
    }
  }
};

module.exports = Trello;
<commit_msg>Fix error handling for trello<commit_after>var Log = require('./log.js');
var Api = require('node-trello');

function Trello(trelloConfig) {
  this.config = trelloConfig;
  this.api = new Api(this.config.key, this.config.token);
  this.values = {};
  this.initialized = false;

  // initialize
  this.init = function(){
    self = this;
    Promise.all([
      self.loadMe(),
      self.loadBoard()
    ]).then(function(res) {
      return Promise.resolve(
        Promise.all([
          self.loadCards(),
          self.loadList()
        ])
      );
    }).then(function(res) {
      Log.i('Trello data loaded!!');
      Log.d(self);

      self.initialized = true;
    }).catch(function(error) {
      Log.e(error);
      process.exit();
    });
  };

  // API request GET me
  this.loadMe = function() {
    Log.i('GET trello me: ');

    self = this;
    return new Promise(function(resolve, reject) {
      self.api.get('/1/members/me', function(err, data) {
        if (err) reject(err);
        Log.i('GET trello me: Me ["' + data.fullName + '"] found');
        Log.d(data);

        self.values.me = data.id;
        resolve(true);
      });
    });
  };

  // API request GET board
  this.loadBoard = function() {
    Log.i('GET trello board: ');

    self = this;
    return new Promise(function(resolve, reject) {
      self.api.get('/1/members/me/boards', function(err, data) {
        if (err) reject(err);
        Log.d(data);

        for (board of data) {
          if (new RegExp(self.config.board.name).test(board.name)) {
            Log.i('GET trello board: Board ["' + self.config.board.name + '"] found');
            Log.d(board);

            self.values.board = board.id;
            resolve(true);
          }
        }
        reject(new Error('Not found board'));
      });
    });
  };

  // API request GET cards
  this.loadCards = function() {
    Log.i('GET trello my card: ');
    self = this;

    return new Promise(function(resolve, reject) {
      self.api.get('/1/boards/' + self.values.board + '/cards?fields=id,idMembers,name', function(err, data) {
        if (err) reject(err);
        Log.d(data);
        self.values.cards = [];
        for (card of data) {
          for (member of card.idMembers) {
            if (member == self.values.me) {
              Log.i('GET trello my card: My Card ["' + card.name + '"] found');
              Log.d(card);

              self.values.mycard = card.id;
            }
          }
          self.values.cards.push(card.id);
        }
        resolve(true);
      });
    });
  };

  // API request GET list
  this.loadList = function() {
    Log.i('GET trello list: ');
    self = this;

    return new Promise(function(resolve, reject) {
      self.api.get('/1/boards/' + self.values.board + '/lists', function(err, data) {
        if (err) reject(err);
        Log.d(data);

        states = ['online', 'offline', 'away'];
        for (list of data) {
          for (state of states) {
            if (new RegExp(eval('self.config.board.list.' + state)).test(list.name)) {
              Log.i('GET trello list: List ["' + list.name + '"] found');
              Log.d(list);

              eval('self.values.' + state + ' = list.id');
            }
          }
        }
        resolve(true);
      });
    });
  };

  // API request PUT card
  this.moveCard = function(card, listName) {
    Log.i('PUT trello card: ');
    if (listName == 'online') {
      list = this.values.online;
    } else if (listName == 'offline') {
      list = this.values.offline;
    } else if (listName == 'away') {
      list = this.values.away;
    } else {
      throw new Error('Invalid list name');
    }

    this.api.put('/1/cards/' + card + '/idList?value=' + list, function(err, data) {
      if (err) {
        Log.e(err);
        return;
      }
      Log.i('PUT trello card: ' + card + ' was moved to "' + listName + '"');
      Log.d(data);
    });
  };

  // API request PUT card
  this.moveMyCard = function(listName) {
    this.moveCard(this.values.mycard, listName);
  }

  // API requrest PUT cards
  this.moveAllCards = function(listName) {
    for (card of this.values.cards) {
      this.moveCard(card, listName);
    }
  }
};

module.exports = Trello;
<|endoftext|>