<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { AutoSizer, Column, defaultTableRowRenderer, Table } from 'react-virtualized';
import Toggle from 'react-toggle';
import 'react-toggle/style.css';

import InformationIcon from 'Icons/Information';

import Wrapper from 'common/Wrapper';
import { formatDuration, formatThousands } from 'common/format';
import Icon from 'common/Icon';
import RESOURCE_TYPES from 'common/RESOURCE_TYPES';
import Info from 'common/Alert/Info';
import HIT_TYPES from 'Parser/Core/HIT_TYPES';

import 'react-virtualized/styles.css';
import './EventsTab.css';

const FILTERABLE_TYPES = {
  damage: {
    name: 'Damage',
  },
  heal: {
    name: 'Heal',
  },
  healabsorbed: {
    name: 'Heal Absorbed',
    explanation: 'Triggered in addition to the regular heal event whenever a heal is absorbed. Can be used to determine what buff or debuff was absorbing the healing. This should only be used if you need to know which ability soaked the healing.',
  },
  absorbed: {
    name: 'Absorb',
    explanation: 'Triggered whenever an absorb effect absorbs damage. These are friendly shields to avoid damage and NOT healing absorption shields.',
  },
  begincast: {
    name: 'Begin Cast',
  },
  cast: {
    name: 'Cast Success',
    explanation: 'Triggered whenever a cast was successful. Blizzard also sometimes uses this event type for mechanics and spell ticks or bolts.',
  },
  applybuff: {
    name: 'Buff Apply',
  },
  removebuff: {
    name: 'Buff Remove',
  },
  applybuffstack: {
    name: 'Buff Stack Gained',
  },
  removebuffstack: {
    name: 'Buff Stack Lost',
  },
  refreshbuff: {
    name: 'Buff Refresh',
  },
  applydebuff: {
    name: 'Debuff Apply',
  },
  removedebuff: {
    name: 'Debuff Remove',
  },
  applydebuffstack: {
    name: 'Debuff Stack Gained',
  },
  removedebuffstack: {
    name: 'Debuff Stack Lost',
  },
  refreshdebuff: {
    name: 'Debuff Refresh',
  },
  summon: {
    name: 'Summon',
  },
  combatantinfo: {
    name: 'Player Info',
    explanation: 'Triggered at the start of the fight with advanced combat logging on. This includes gear, talents, etc.',
  },
  energize: {
    name: 'Energize',
  },
  interrupt: {
    name: 'Interrupt',
  },
  death: {
    name: 'Death',
  },
  resurrect: {
    name: 'Resurrect',
  },
};

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

  constructor() {
    super();
    this.state = {
      ...Object.keys(FILTERABLE_TYPES).reduce((obj, type) => {
        obj[type] = true;
        return obj;
      }, {}),
      rawNames: false,
      showFabricated: false,
    };
    this.handleRowClick = this.handleRowClick.bind(this);
  }

  findEntity(id) {
    const friendly = this.props.parser.report.friendlies.find(friendly => friendly.id === id);
    if (friendly) {
      return friendly;
    }
    const enemy = this.props.parser.report.enemies.find(enemy => enemy.id === id);
    if (enemy) {
      return enemy;
    }
    return null;
  }
  renderEntity(entity) {
    if (!entity) {
      return null;
    }
    return {entity.name};
  }
  renderAbility(ability) {
    if (!ability) {
      return null;
    }
    const spellId = ability.guid;

    return (
      <a href={`http://www.wowhead.com/spell=${spellId}`} target="_blank" rel="noopener noreferrer">
        {ability.abilityIcon && <Icon icon={ability.abilityIcon} />} {ability.name}

    );
  }
  eventTypeName(type) {
    return this.state.rawNames ? type : (FILTERABLE_TYPES[type] ? FILTERABLE_TYPES[type].name : type);
  }

  renderEventTypeToggle(type) {
    const name = this.eventTypeName(type);
    const explanation = FILTERABLE_TYPES[type] ? FILTERABLE_TYPES[type].explanation : undefined;
    return this.renderToggle(type, name, explanation);
  }
  renderToggle(prop, label, explanation = null) {
    return (

        <label className="flex-main" htmlFor={`${prop}-toggle`}>
          {label}

        {explanation && (

              <InformationIcon style={{ fontSize: '1.4em' }} />

        )}
        <Toggle
          defaultChecked={this.state[prop]}
          icons={false}
          onChange={event => this.setState({ [prop]: event.target.checked })}
          id={`${prop}-toggle`}
          className="flex-sub"
        />

    );
  }
  renderRow(props) {
    const event = props.rowData;
    return defaultTableRowRenderer({
      ...props,
      className: `${props.className} ${event.__modified ? 'modified' : ''} ${event.__fabricated ? 'fabricated' : ''}`,
    });
  }
  handleRowClick({ rowData }) {
    console.log(rowData);
  }

  renderSearchBox() {
    return (
      <input
        type="text"
        name="search"
        className="form-control"
        onChange={event => this.setState({ search: event.target.value.trim().toLowerCase() })}
        placeholder="Search events (comma separated)"
        autoCorrect="off"
        autoCapitalize="off"
        spellCheck="false"
      />
    );
  }

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

    const events = parser.eventHistory
      .filter(event => {
        if (this.state[event.type] === false) {
          return false;
        }
        if (!this.state.showFabricated && event.__fabricated === true) {
          return false;
        }

        // Search Logic
        if(this.state.search === undefined || this.state.search === '') {
          return true;
        }

        const source = this.findEntity(event.sourceID);
        const target = this.findEntity(event.targetID);

        const searchTerms = this.state.search.split(',');
        for(let i = 0; i < searchTerms.length; i++) {
          const searchTerm = searchTerms[i].trim();
          if(searchTerm === '') {
            return false;
          }
          if(event.ability !== undefined && event.ability.name.toLowerCase().includes(searchTerm)) {
            return true;
          } else if(source !== null && source.name.toLowerCase().includes(searchTerm)) {
            return true;
          } else if(target !== null && target.name.toLowerCase().includes(searchTerm)) {
            return true;
          }
        }

        return false;
      });

    // TODO: Show active buffs like WCL
    // TODO: Allow searching for players by name
    // TODO: Pollish so this can be turned on for everyone

    return (

          <Info className="small" style={{ width: 240 }}>
            This only includes events involving the selected player.
          </Info>

          {this.renderSearchBox()}

          {Object.keys(FILTERABLE_TYPES).map(type => this.renderEventTypeToggle(type))}

          {this.renderToggle('showFabricated', 'Fabricated events', 'These events were not originally found in the combatlog. They were created by us to fix bugs, inconsistencies, or to provide new functionality. You can recognize these events by their green background.')}
          {this.renderToggle('rawNames', 'Raw names')}

            Events with an orange background were <dfn data-tip="This generally means their order was changed from the original combatlog to fix inconsistencies or bugs, but it may include other modifications.">modified</dfn>.

          <AutoSizer disableHeight>
            {({ height, width }) => (

 events[index]}
                rowHeight={25}
                rowRenderer={this.renderRow}
                onRowClick={this.handleRowClick}
                width={width}
              >
                <Column
                  dataKey="timestamp"
                  label="Time"
                  cellRenderer={({ cellData }) => formatDuration((cellData - parser.fight.start_time) / 1000, 3)}
                  disableSort
                  width={30}
                  flexGrow={1}
                />
                <Column
                  dataKey="type"
                  label="Event"
                  cellRenderer={({ cellData }) =>
{this.eventTypeName(cellData)}
}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="sourceID"
                  label="Source"
                  cellRenderer={({ cellData }) => this.renderEntity(this.findEntity(cellData))}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="targetID"
                  label="Target"
                  cellRenderer={({ cellData }) => this.renderEntity(this.findEntity(cellData))}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="ability"
                  label="Ability"
                  cellRenderer={({ cellData }) => this.renderAbility(cellData)}
                  disableSort
                  width={90}
                  flexGrow={1}
                />
                <Column
                  dataKey="effective"
                  label="Effective"
                  className="effect"
                  cellRenderer={({ rowData }) => {
                    if (rowData.type === 'damage') {
                      return (
                        <Wrapper>
                          <span className={`${rowData.type} ${rowData.hitType === HIT_TYPES.CRIT || rowData.hitType === HIT_TYPES.BLOCKED_CRIT ? 'crit' : ''}`}>
                            {formatThousands(rowData.amount)}
                          {' '}
                          {rowData.absorbed ? A: {formatThousands(rowData.absorbed)} : null}{' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'heal') {
                      return (
                        <Wrapper>
                          <span className={`${rowData.type} ${rowData.hitType === HIT_TYPES.CRIT || rowData.hitType === HIT_TYPES.BLOCKED_CRIT ? 'crit' : ''}`}>
                            {formatThousands(rowData.amount)}
                          {' '}
                          {rowData.absorbed ? A: {formatThousands(rowData.absorbed)} : null}{' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'absorbed') {
                      return (
                        <Wrapper>

                            {formatThousands(rowData.amount)}
                          {' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'energize') {
                      const resource = RESOURCE_TYPES[rowData.resourceChangeType];
                      if (resource) {
                        return (
                          <Wrapper>

                              {formatThousands(rowData.resourceChange)} {resource.name}
                            {' '}
                            {resource.icon && <Icon icon={resource.icon} alt={resource.name} />}
                          </Wrapper>
                        );
                      }
                    }
                    return null;
                  }}
                  disableSort
                  width={60}
                  flexGrow={1}
                />
                <Column
                  dataKey="rest"
                  label="Rest"
                  className="effect"
                  cellRenderer={({ rowData }) => {
                    if (rowData.type === 'damage') {
                      return (

                          {rowData.blocked ? B: {formatThousands(rowData.blocked)} : null}

                      );
                    }
                    if (rowData.type === 'heal') {
                      return (

                          {rowData.overheal ? O: {formatThousands(rowData.overheal)} : null}

                      );
                    }
                    return null;
                  }}
                  disableSort
                  width={35}
                  flexGrow={1}
                />

            )}
          </AutoSizer>

    );
  }
}

export default EventsTab;
<commit_msg>Fix code review comments<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { AutoSizer, Column, defaultTableRowRenderer, Table } from 'react-virtualized';
import Toggle from 'react-toggle';
import 'react-toggle/style.css';

import InformationIcon from 'Icons/Information';

import Wrapper from 'common/Wrapper';
import { formatDuration, formatThousands } from 'common/format';
import Icon from 'common/Icon';
import RESOURCE_TYPES from 'common/RESOURCE_TYPES';
import Info from 'common/Alert/Info';
import HIT_TYPES from 'Parser/Core/HIT_TYPES';

import 'react-virtualized/styles.css';
import './EventsTab.css';

const FILTERABLE_TYPES = {
  damage: {
    name: 'Damage',
  },
  heal: {
    name: 'Heal',
  },
  healabsorbed: {
    name: 'Heal Absorbed',
    explanation: 'Triggered in addition to the regular heal event whenever a heal is absorbed. Can be used to determine what buff or debuff was absorbing the healing. This should only be used if you need to know which ability soaked the healing.',
  },
  absorbed: {
    name: 'Absorb',
    explanation: 'Triggered whenever an absorb effect absorbs damage. These are friendly shields to avoid damage and NOT healing absorption shields.',
  },
  begincast: {
    name: 'Begin Cast',
  },
  cast: {
    name: 'Cast Success',
    explanation: 'Triggered whenever a cast was successful. Blizzard also sometimes uses this event type for mechanics and spell ticks or bolts.',
  },
  applybuff: {
    name: 'Buff Apply',
  },
  removebuff: {
    name: 'Buff Remove',
  },
  applybuffstack: {
    name: 'Buff Stack Gained',
  },
  removebuffstack: {
    name: 'Buff Stack Lost',
  },
  refreshbuff: {
    name: 'Buff Refresh',
  },
  applydebuff: {
    name: 'Debuff Apply',
  },
  removedebuff: {
    name: 'Debuff Remove',
  },
  applydebuffstack: {
    name: 'Debuff Stack Gained',
  },
  removedebuffstack: {
    name: 'Debuff Stack Lost',
  },
  refreshdebuff: {
    name: 'Debuff Refresh',
  },
  summon: {
    name: 'Summon',
  },
  combatantinfo: {
    name: 'Player Info',
    explanation: 'Triggered at the start of the fight with advanced combat logging on. This includes gear, talents, etc.',
  },
  energize: {
    name: 'Energize',
  },
  interrupt: {
    name: 'Interrupt',
  },
  death: {
    name: 'Death',
  },
  resurrect: {
    name: 'Resurrect',
  },
};

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

  constructor() {
    super();
    this.state = {
      ...Object.keys(FILTERABLE_TYPES).reduce((obj, type) => {
        obj[type] = true;
        return obj;
      }, {}),
      rawNames: false,
      showFabricated: false,
      search: '',
    };
    this.handleRowClick = this.handleRowClick.bind(this);
  }

  findEntity(id) {
    const friendly = this.props.parser.report.friendlies.find(friendly => friendly.id === id);
    if (friendly) {
      return friendly;
    }
    const enemy = this.props.parser.report.enemies.find(enemy => enemy.id === id);
    if (enemy) {
      return enemy;
    }
    return null;
  }
  renderEntity(entity) {
    if (!entity) {
      return null;
    }
    return {entity.name};
  }
  renderAbility(ability) {
    if (!ability) {
      return null;
    }
    const spellId = ability.guid;

    return (
      <a href={`http://www.wowhead.com/spell=${spellId}`} target="_blank" rel="noopener noreferrer">
        {ability.abilityIcon && <Icon icon={ability.abilityIcon} />} {ability.name}

    );
  }
  eventTypeName(type) {
    return this.state.rawNames ? type : (FILTERABLE_TYPES[type] ? FILTERABLE_TYPES[type].name : type);
  }

  renderEventTypeToggle(type) {
    const name = this.eventTypeName(type);
    const explanation = FILTERABLE_TYPES[type] ? FILTERABLE_TYPES[type].explanation : undefined;
    return this.renderToggle(type, name, explanation);
  }
  renderToggle(prop, label, explanation = null) {
    return (

        <label className="flex-main" htmlFor={`${prop}-toggle`}>
          {label}

        {explanation && (

              <InformationIcon style={{ fontSize: '1.4em' }} />

        )}
        <Toggle
          defaultChecked={this.state[prop]}
          icons={false}
          onChange={event => this.setState({ [prop]: event.target.checked })}
          id={`${prop}-toggle`}
          className="flex-sub"
        />

    );
  }
  renderRow(props) {
    const event = props.rowData;
    return defaultTableRowRenderer({
      ...props,
      className: `${props.className} ${event.__modified ? 'modified' : ''} ${event.__fabricated ? 'fabricated' : ''}`,
    });
  }
  handleRowClick({ rowData }) {
    console.log(rowData);
  }

  renderSearchBox() {
    return (
      <input
        type="text"
        name="search"
        className="form-control"
        onChange={event => this.setState({ search: event.target.value.trim().toLowerCase() })}
        placeholder="Search events (comma separated)"
        autoCorrect="off"
        autoCapitalize="off"
        spellCheck="false"
      />
    );
  }

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

    const searchTerms = this.state.search
      .split(' ')
      .filter(searchTerm => searchTerm !== '');

    const events = parser.eventHistory
      .filter(event => {
        if (this.state[event.type] === false) {
          return false;
        }
        if (!this.state.showFabricated && event.__fabricated === true) {
          return false;
        }

        // Search Logic
        if(searchTerms.length === 0) {
          return true;
        }

        const source = this.findEntity(event.sourceID);
        const target = this.findEntity(event.targetID);

        return searchTerms.some(searchTerm => {
          if(event.ability !== undefined && event.ability.name.toLowerCase().includes(searchTerm)) {
            return true;
          } else if(source !== null && source.name.toLowerCase().includes(searchTerm)) {
            return true;
          } else if(target !== null && target.name.toLowerCase().includes(searchTerm)) {
            return true;
          }
          return false;
        });
      });

    // TODO: Show active buffs like WCL
    // TODO: Allow searching for players by name
    // TODO: Pollish so this can be turned on for everyone

    return (

          <Info className="small" style={{ width: 240 }}>
            This only includes events involving the selected player.
          </Info>

          {this.renderSearchBox()}

          {Object.keys(FILTERABLE_TYPES).map(type => this.renderEventTypeToggle(type))}

          {this.renderToggle('showFabricated', 'Fabricated events', 'These events were not originally found in the combatlog. They were created by us to fix bugs, inconsistencies, or to provide new functionality. You can recognize these events by their green background.')}
          {this.renderToggle('rawNames', 'Raw names')}

            Events with an orange background were <dfn data-tip="This generally means their order was changed from the original combatlog to fix inconsistencies or bugs, but it may include other modifications.">modified</dfn>.

          <AutoSizer disableHeight>
            {({ height, width }) => (

 events[index]}
                rowHeight={25}
                rowRenderer={this.renderRow}
                onRowClick={this.handleRowClick}
                width={width}
              >
                <Column
                  dataKey="timestamp"
                  label="Time"
                  cellRenderer={({ cellData }) => formatDuration((cellData - parser.fight.start_time) / 1000, 3)}
                  disableSort
                  width={30}
                  flexGrow={1}
                />
                <Column
                  dataKey="type"
                  label="Event"
                  cellRenderer={({ cellData }) =>
{this.eventTypeName(cellData)}
}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="sourceID"
                  label="Source"
                  cellRenderer={({ cellData }) => this.renderEntity(this.findEntity(cellData))}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="targetID"
                  label="Target"
                  cellRenderer={({ cellData }) => this.renderEntity(this.findEntity(cellData))}
                  disableSort
                  width={50}
                  flexGrow={1}
                />
                <Column
                  dataKey="ability"
                  label="Ability"
                  cellRenderer={({ cellData }) => this.renderAbility(cellData)}
                  disableSort
                  width={90}
                  flexGrow={1}
                />
                <Column
                  dataKey="effective"
                  label="Effective"
                  className="effect"
                  cellRenderer={({ rowData }) => {
                    if (rowData.type === 'damage') {
                      return (
                        <Wrapper>
                          <span className={`${rowData.type} ${rowData.hitType === HIT_TYPES.CRIT || rowData.hitType === HIT_TYPES.BLOCKED_CRIT ? 'crit' : ''}`}>
                            {formatThousands(rowData.amount)}
                          {' '}
                          {rowData.absorbed ? A: {formatThousands(rowData.absorbed)} : null}{' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'heal') {
                      return (
                        <Wrapper>
                          <span className={`${rowData.type} ${rowData.hitType === HIT_TYPES.CRIT || rowData.hitType === HIT_TYPES.BLOCKED_CRIT ? 'crit' : ''}`}>
                            {formatThousands(rowData.amount)}
                          {' '}
                          {rowData.absorbed ? A: {formatThousands(rowData.absorbed)} : null}{' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'absorbed') {
                      return (
                        <Wrapper>

                            {formatThousands(rowData.amount)}
                          {' '}

                        </Wrapper>
                      );
                    }
                    if (rowData.type === 'energize') {
                      const resource = RESOURCE_TYPES[rowData.resourceChangeType];
                      if (resource) {
                        return (
                          <Wrapper>

                              {formatThousands(rowData.resourceChange)} {resource.name}
                            {' '}
                            {resource.icon && <Icon icon={resource.icon} alt={resource.name} />}
                          </Wrapper>
                        );
                      }
                    }
                    return null;
                  }}
                  disableSort
                  width={60}
                  flexGrow={1}
                />
                <Column
                  dataKey="rest"
                  label="Rest"
                  className="effect"
                  cellRenderer={({ rowData }) => {
                    if (rowData.type === 'damage') {
                      return (

                          {rowData.blocked ? B: {formatThousands(rowData.blocked)} : null}

                      );
                    }
                    if (rowData.type === 'heal') {
                      return (

                          {rowData.overheal ? O: {formatThousands(rowData.overheal)} : null}

                      );
                    }
                    return null;
                  }}
                  disableSort
                  width={35}
                  flexGrow={1}
                />

            )}
          </AutoSizer>

    );
  }
}

export default EventsTab;
<|endoftext|>


<commit_before>import React from "react";
import { addDecorator, configure } from "@storybook/react";
import styled, { ThemeProvider } from "styled-components";
import { withKnobs, select } from "@storybook/addon-knobs";

import reset from "../src/reset";
import themes from "../src/themes";
import SystemFontStack from "../src/components/SystemFontStack";

const req = require.context("../src", true, /.stories.js$/);

function loadStories() {
  req.keys().forEach(filename => req(filename));
}

reset();

const Stage = styled.div`padding: ${props => props.theme.spacingBase()};`;
Stage.displayName = "Stage";

addDecorator(story => {
  const selectedTheme = select("Theme", Object.keys(themes), "default");
  return (
    <ThemeProvider theme={themes[selectedTheme]}>
      <Stage>
        <SystemFontStack>{story()}</SystemFontStack>
      </Stage>
    </ThemeProvider>
  );
});
addDecorator(withKnobs);

configure(loadStories, module);
<commit_msg>remove unnecessary horizontal padding from stage<commit_after>import React from "react";
import { addDecorator, configure } from "@storybook/react";
import styled, { ThemeProvider } from "styled-components";
import { withKnobs, select } from "@storybook/addon-knobs";

import reset from "../src/reset";
import themes from "../src/themes";
import SystemFontStack from "../src/components/SystemFontStack";

const req = require.context("../src", true, /.stories.js$/);

function loadStories() {
  req.keys().forEach(filename => req(filename));
}

reset();

const Stage = styled.div`padding: ${props => props.theme.spacingBase()} 0;`;
Stage.displayName = "Stage";

addDecorator(story => {
  const selectedTheme = select("Theme", Object.keys(themes), "default");
  return (
    <ThemeProvider theme={themes[selectedTheme]}>
      <Stage>
        <SystemFontStack>{story()}</SystemFontStack>
      </Stage>
    </ThemeProvider>
  );
});
addDecorator(withKnobs);

configure(loadStories, module);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, nomen:true, node:true*/
/*global YUI*/

/*
 * Baseline Mojito client testing harness.
 */

/*
 */
YUI.add('mojito', function(Y, NAME) {
    Y.namespace('mojito');
    Y.namespace('mojito.addons.ac');
});

/*
 */
YUI.add('mojito-meta-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-composite-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-params-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-config-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-client-store', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-dispatcher', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-loader', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-logger', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-mojit-proxy', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-output-handler', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-resource-store-adapter', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-route-maker', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-tunnel-client', function(Y, NAME) {
});

/******* DALI ********/

/*
 */
YUI.add('breg', function(Y, NAME) {
    Y.namespace('Dali').beanRegistry = {
        registerBean: function (name, obj) {
            if (!this.beans) {
                this.beans = {};
            }
            this.beans[name] = obj;
        },
        getBean: function (name) {
            return this.beans[name];
        },
        doInjection: function () {
            this.isInjected = true;
        }
    };
});

/*
 */
YUI.add('dali-transport-base', function(Y, NAME) {
});

/*
 */
YUI.add('request-handler', function(Y, NAME) {
});

/*
 */
YUI.add('simple-request-formatter', function(Y, NAME) {
});

/*
 */
YUI.add('requestor', function(Y, NAME) {
});

/*
 */
YUI.add('io-facade', function(Y, NAME) {
});

/*
 */
YUI.add('response-formatter', function(Y, NAME) {
});

/*
 */
YUI.add('response-processor', function(Y, NAME) {
});

/******* END DALI ********/

/*
 */
YUI.add('mojito-util', function(Y, NAME) {
    Y.mojito.util = {
        // TODO: Do we want this to be a real copy operation? For mocking in
        // tests it's likely sufficient that it doesn't copy but side-effects
        // may be possible.
        copy: function(obj) {
            return obj;
        },

        // TODO: this is an extremely simplified version of the function, but meets
        // the demands of the current tests. If we find ourselves writing tests that require
        // more advanced use cases, we should make this function meet those needs.
        metaMerge: function (to, from) {
            return Y.merge(from, to);
        }
    };

    return Y.mojito.util;
});

/*
 * Add a mojito-test module containing the mocking support we want for other
 * Mojito components.
 */
YUI.add('mojito-test', function(Y, NAME) {

    function EasyMock() {

        var mock = Y.Test.Mock();

        mock.expect = function() {
            Y.Array.each(arguments, function(expectation) {
                Y.Test.Mock.expect(mock, expectation);
            });
            return mock;
        };

        mock.verify = function() {
            Y.Test.Mock.verify(mock);
        };

        return mock;
    }

    function createMockAddon(source, name) {
        source._addons.push(name);
        source[name] = new EasyMock();
    }

    function createMockModel(source, name) {
        source.models[name] = new EasyMock();
    }

    function createMockExtra(source, ns, name) {
        var mock = new EasyMock();

        if (!source[ns]) {
            source[ns] = {};
        }
        if (!source._extras[ns]) {
            source._extras[ns] = {};
        }
        source._extras[ns][name] = mock;
        source[ns][name] = mock;
    }

    function MockActionContext(opts) {
        var mock = Y.Test.Mock();

        opts = opts || {};
        mock._addons = [];
        mock.models = {};
        mock._extras = {};

        if (opts.addons) {
            Y.Array.each(opts.addons, function(addon) {
                createMockAddon(mock, addon);
            });
        }
        if (opts.models) {
            Y.Array.each(opts.models, function(model) {
                createMockModel(mock, model);
            });
        }
        if (opts.extras) {
            Y.Object.each(opts.extras, function(extras, namespace) {
                if (Y.Lang.isArray(extras)) {
                    Y.Array.each(extras, function(extra) {
                        createMockExtra(mock, namespace, extra);
                    });
                } else {
                    createMockExtra(mock, namespace, extras);
                }
            });
        }

        mock.expect = function() {
            Y.Array.each(arguments, function(expectation) {
                Y.Test.Mock.expect(mock, expectation);
            });
            return mock;
        };
        mock.verify = function() {
            var i,
                j,
                mockAddon;

            Y.Test.Mock.verify(mock);
            for (i = 0; i < mock._addons.length; i += 1) {
                mockAddon = mock[mock._addons[i]];
                mockAddon.verify();
            }
            for (i in mock.models) {
                if (mock.models.hasOwnProperty(i)) {
                    mock.models[i].verify();
                }
            }
            for (i in mock._extras) {
                if (mock._extras.hasOwnProperty(i)) {
                    for (j in mock._extras[i]) {
                        if (mock._extras[i].hasOwnProperty(j)) {
                            mock._extras[i][j].verify();
                        }
                    }
                }
            }
        };
        return mock;
    }

    Y.mojito.MockActionContext = MockActionContext;
    Y.mojito.EasyMock = EasyMock;

}, '0.1.0', {requires: [
    'event',
    "node",
    "node-event-simulate"
]});
<commit_msg>rename mojito-util to mojito-util-mock for now<commit_after>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, nomen:true, node:true*/
/*global YUI*/

/*
 * Baseline Mojito client testing harness.
 */

/*
 */
YUI.add('mojito', function(Y, NAME) {
    Y.namespace('mojito');
    Y.namespace('mojito.addons.ac');
});

/*
 */
YUI.add('mojito-meta-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-composite-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-params-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-config-addon', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-client-store', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-dispatcher', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-loader', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-logger', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-mojit-proxy', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-output-handler', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-resource-store-adapter', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-route-maker', function(Y, NAME) {
});

/*
 */
YUI.add('mojito-tunnel-client', function(Y, NAME) {
});

/******* DALI ********/

/*
 */
YUI.add('breg', function(Y, NAME) {
    Y.namespace('Dali').beanRegistry = {
        registerBean: function (name, obj) {
            if (!this.beans) {
                this.beans = {};
            }
            this.beans[name] = obj;
        },
        getBean: function (name) {
            return this.beans[name];
        },
        doInjection: function () {
            this.isInjected = true;
        }
    };
});

/*
 */
YUI.add('dali-transport-base', function(Y, NAME) {
});

/*
 */
YUI.add('request-handler', function(Y, NAME) {
});

/*
 */
YUI.add('simple-request-formatter', function(Y, NAME) {
});

/*
 */
YUI.add('requestor', function(Y, NAME) {
});

/*
 */
YUI.add('io-facade', function(Y, NAME) {
});

/*
 */
YUI.add('response-formatter', function(Y, NAME) {
});

/*
 */
YUI.add('response-processor', function(Y, NAME) {
});

/******* END DALI ********/

/*
 *
 */
YUI.add('mojito-util-mock', function(Y, NAME) {
    Y.mojito.util = {
        // TODO: Do we want this to be a real copy operation? For mocking in
        // tests it's likely sufficient that it doesn't copy but side-effects
        // may be possible.
        copy: function(obj) {
            return obj;
        },

        // TODO: this is an extremely simplified version of the function, but meets
        // the demands of the current tests. If we find ourselves writing tests that require
        // more advanced use cases, we should make this function meet those needs.
        metaMerge: function (to, from) {
            return Y.merge(from, to);
        }
    };

    return Y.mojito.util;
});

/*
 * Add a mojito-test module containing the mocking support we want for other
 * Mojito components.
 */
YUI.add('mojito-test', function(Y, NAME) {

    function EasyMock() {

        var mock = Y.Test.Mock();

        mock.expect = function() {
            Y.Array.each(arguments, function(expectation) {
                Y.Test.Mock.expect(mock, expectation);
            });
            return mock;
        };

        mock.verify = function() {
            Y.Test.Mock.verify(mock);
        };

        return mock;
    }

    function createMockAddon(source, name) {
        source._addons.push(name);
        source[name] = new EasyMock();
    }

    function createMockModel(source, name) {
        source.models[name] = new EasyMock();
    }

    function createMockExtra(source, ns, name) {
        var mock = new EasyMock();

        if (!source[ns]) {
            source[ns] = {};
        }
        if (!source._extras[ns]) {
            source._extras[ns] = {};
        }
        source._extras[ns][name] = mock;
        source[ns][name] = mock;
    }

    function MockActionContext(opts) {
        var mock = Y.Test.Mock();

        opts = opts || {};
        mock._addons = [];
        mock.models = {};
        mock._extras = {};

        if (opts.addons) {
            Y.Array.each(opts.addons, function(addon) {
                createMockAddon(mock, addon);
            });
        }
        if (opts.models) {
            Y.Array.each(opts.models, function(model) {
                createMockModel(mock, model);
            });
        }
        if (opts.extras) {
            Y.Object.each(opts.extras, function(extras, namespace) {
                if (Y.Lang.isArray(extras)) {
                    Y.Array.each(extras, function(extra) {
                        createMockExtra(mock, namespace, extra);
                    });
                } else {
                    createMockExtra(mock, namespace, extras);
                }
            });
        }

        mock.expect = function() {
            Y.Array.each(arguments, function(expectation) {
                Y.Test.Mock.expect(mock, expectation);
            });
            return mock;
        };
        mock.verify = function() {
            var i,
                j,
                mockAddon;

            Y.Test.Mock.verify(mock);
            for (i = 0; i < mock._addons.length; i += 1) {
                mockAddon = mock[mock._addons[i]];
                mockAddon.verify();
            }
            for (i in mock.models) {
                if (mock.models.hasOwnProperty(i)) {
                    mock.models[i].verify();
                }
            }
            for (i in mock._extras) {
                if (mock._extras.hasOwnProperty(i)) {
                    for (j in mock._extras[i]) {
                        if (mock._extras[i].hasOwnProperty(j)) {
                            mock._extras[i][j].verify();
                        }
                    }
                }
            }
        };
        return mock;
    }

    Y.mojito.MockActionContext = MockActionContext;
    Y.mojito.EasyMock = EasyMock;

}, '0.1.0', {requires: [
    'event',
    "node",
    "node-event-simulate"
]});
<|endoftext|>


<commit_before>var Texture      = require('./index.js');
var createCanvas = require('../domUtils/createCanvas');
var batcher      = require('../webGL/batcher');
var renderers    = batcher.renderers;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var CHAR_WIDTH    = 4;
var CHAR_HEIGHT   = 6;
var CHAR_PER_LINE = 16;
var OX            = 0;
var OY            = 0;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var MINITEXT = [
	// 219,   438,   511,   14016, 14043, 14326, 14335, 28032, 28123, 28086, 28159, 32704, 32731, 32758, 32767, 128,
	// 146,   384,   402,   9344,  9362,  9600,  9618,  192,   210,   448,   466,   9408,  9426,  9664,  9682,  32767,
	0,     8338,  45,    11962, 5588,  21157, 29354, 10,    17556, 5265,  21973, 1488,  5312,  448,   13824, 5268,
	31599, 29843, 29671, 31143, 18925, 31183, 31689, 18735, 31727, 18927, 1040,  5136,  17492, 3640,  5393,  8359,
	25450, 23530, 31467, 25166, 15211, 29391, 4815,  27470, 23533, 29847, 15142, 23277, 29257, 23421, 23403, 11114,
	4843,  26474, 23279, 14798, 9367,  27501, 12141, 24429, 23213, 14829, 29351, 25750, 17553, 13459, 9402,  28672,
	34,    23530, 31467, 25166, 15211, 29391, 4815,  27470, 23533, 29847, 15142, 23277, 29257, 23421, 23403, 11114,
	4843,  26474, 23279, 14798, 9367,  27501, 12141, 24429, 23213, 14829, 29351, 25686, 9362,  13587, 42,    21845
];

function createDefaultCharset() {
	var canvas = createCanvas(64, 36);
	var ctx = canvas.getContext('2d');
	ctx.fillStyle = '#000';
	ctx.fillRect(0, 0, 64, 36);

	ctx.fillStyle = '#FFF';
	for (var c = 0; c < MINITEXT.length; c++) {
		var code = MINITEXT[c];

		var i = c % 16;
		var j = ~~(c / 16);

		for (var bit = 0; bit < 15; bit++) {
			var x = bit % 3;
			var y = ~~(bit / 3);
			var pixel = (code >> bit) & 1;
			if (pixel !== 1) continue;
			ctx.fillRect(i * CHAR_WIDTH + x, j * CHAR_HEIGHT + y, 1, 1);
		}
	}
	return canvas;
}

var DEFAULT_CHARSET = createDefaultCharset();
var CHARSET_IMG = DEFAULT_CHARSET;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var _resize = Texture.prototype.resize;
Texture.prototype.resize = function (width, height) {
	this._textColumn   = ~~(width  / CHAR_WIDTH);
	this._textLine     = ~~(height / CHAR_HEIGHT);
	this._textPadding  = height - this._textLine * CHAR_HEIGHT;

	this._cursor.i = 0;
	this._cursor.j = 0;

	_resize.call(this, width, height);
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var _cls = Texture.prototype.cls;
Texture.prototype.cls = function () {
	_cls.call(this);
	this.locate(0, 0);
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.locate = function (i, j) {
	this._cursor.i = ~~i;
	this._cursor.j = ~~j;
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.print = function (str, x, y) {
	// string transformation
	if (typeof str === 'object') {
		try {
			str = JSON.stringify(str);
		} catch (error) {
			str = "[Object]";
		}
	} else if (typeof str !== 'string') {
		str = str.toString();
	}

	// prepare renderer
	var renderer = batcher.prepare(renderers.colorSprite, CHARSET_IMG, this);

	var color = this.palette[this._pen];
	var r = color.r;
	var g = color.g;
	var b = color.b;

	// print at cooordinate
	if (x !== undefined) {
		x = ~~Math.round(x - this.camera.x);
		y = ~~Math.round(y - this.camera.y);
		for (var i = 0; i < str.length; i++) {
			// TODO
			x += CHAR_WIDTH;
		}
		return this;
	}

	// print at cursor
	var i = this._cursor.i;
	var j = this._cursor.j;

	for (var c = 0; c < str.length; c++) {
		if (i > this._textColumn) {
			j += 1;
			i = 0;
		}

		if (j > this._textLine) {
			this.textScroll();
			j -= 1;
			// don't forget to switch back the renderer
			renderer = batcher.prepare(renderers.colorSprite, CHARSET_IMG, this);
		}

		var chr = str.charCodeAt(c);
		if (chr === 10 || chr === 13) {
			j += 1;
			i = 0;
			continue;
		}
		chr -= 32;
		if (chr < 0 || chr > 255) {
			i += 1;
			continue;
		}

		var sx = CHAR_WIDTH  *   (chr % CHAR_PER_LINE) + OX;
		var sy = CHAR_HEIGHT * ~~(chr / CHAR_PER_LINE) + OY;
		renderer.pushSprite(i * CHAR_WIDTH, j * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT, sx, sy, r, g, b);
		i += 1;
	}

	this._cursor.i = i;
	this._cursor.j = j;

	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.println = function (str) {
	this.print(str);
	this.print('\n');
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.textScroll = function (n) {
	if (n === undefined) n = 1;
	this._cursor.j -= n;
	n *= CHAR_HEIGHT;

	// NOTE: for optimization purpose, this code is duplicated from `draw` method
	if (!this._copyTexture) {
		this._copyTexture = new Texture(this.width, this.height);
	}

	this._copyTexture.draw(this, 0, 0);

	this.cls();
	this.draw(this._copyTexture, 0, -n);

	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.setCharset = function (img) {
	CHARSET_IMG = img || DEFAULT_CHARSET;
	CHAR_WIDTH  = ~~(CHARSET_IMG.width / 16);
	CHAR_HEIGHT = ~~(CHARSET_IMG.height / 6);

	OX = 0;
	OY = 0;

	// if charset is a sprite in an atlas
	if (CHARSET_IMG._isSprite) {
		OX = CHARSET_IMG.x;
		OY = CHARSET_IMG.y;
		CHARSET_IMG = CHARSET_IMG.img;
	}

	this._textColumn   = ~~(this.width  / CHAR_WIDTH)  - 1;
	this._textLine     = ~~(this.height / CHAR_HEIGHT) - 1;
	this._textPadding  = this.height - this._textLine * CHAR_HEIGHT;
};
<commit_msg>Implement webGL version of "print at coordinate"<commit_after>var Texture      = require('./index.js');
var createCanvas = require('../domUtils/createCanvas');
var batcher      = require('../webGL/batcher');
var renderers    = batcher.renderers;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var CHAR_WIDTH    = 4;
var CHAR_HEIGHT   = 6;
var CHAR_PER_LINE = 16;
var OX            = 0;
var OY            = 0;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var MINITEXT = [
	// 219,   438,   511,   14016, 14043, 14326, 14335, 28032, 28123, 28086, 28159, 32704, 32731, 32758, 32767, 128,
	// 146,   384,   402,   9344,  9362,  9600,  9618,  192,   210,   448,   466,   9408,  9426,  9664,  9682,  32767,
	0,     8338,  45,    11962, 5588,  21157, 29354, 10,    17556, 5265,  21973, 1488,  5312,  448,   13824, 5268,
	31599, 29843, 29671, 31143, 18925, 31183, 31689, 18735, 31727, 18927, 1040,  5136,  17492, 3640,  5393,  8359,
	25450, 23530, 31467, 25166, 15211, 29391, 4815,  27470, 23533, 29847, 15142, 23277, 29257, 23421, 23403, 11114,
	4843,  26474, 23279, 14798, 9367,  27501, 12141, 24429, 23213, 14829, 29351, 25750, 17553, 13459, 9402,  28672,
	34,    23530, 31467, 25166, 15211, 29391, 4815,  27470, 23533, 29847, 15142, 23277, 29257, 23421, 23403, 11114,
	4843,  26474, 23279, 14798, 9367,  27501, 12141, 24429, 23213, 14829, 29351, 25686, 9362,  13587, 42,    21845
];

function createDefaultCharset() {
	var canvas = createCanvas(64, 36);
	var ctx = canvas.getContext('2d');
	ctx.fillStyle = '#000';
	ctx.fillRect(0, 0, 64, 36);

	ctx.fillStyle = '#FFF';
	for (var c = 0; c < MINITEXT.length; c++) {
		var code = MINITEXT[c];

		var i = c % 16;
		var j = ~~(c / 16);

		for (var bit = 0; bit < 15; bit++) {
			var x = bit % 3;
			var y = ~~(bit / 3);
			var pixel = (code >> bit) & 1;
			if (pixel !== 1) continue;
			ctx.fillRect(i * CHAR_WIDTH + x, j * CHAR_HEIGHT + y, 1, 1);
		}
	}
	return canvas;
}

var DEFAULT_CHARSET = createDefaultCharset();
var CHARSET_IMG = DEFAULT_CHARSET;

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var _resize = Texture.prototype.resize;
Texture.prototype.resize = function (width, height) {
	this._textColumn   = ~~(width  / CHAR_WIDTH);
	this._textLine     = ~~(height / CHAR_HEIGHT);
	this._textPadding  = height - this._textLine * CHAR_HEIGHT;

	this._cursor.i = 0;
	this._cursor.j = 0;

	_resize.call(this, width, height);
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var _cls = Texture.prototype.cls;
Texture.prototype.cls = function () {
	_cls.call(this);
	this.locate(0, 0);
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.locate = function (i, j) {
	this._cursor.i = ~~i;
	this._cursor.j = ~~j;
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.print = function (str, x, y) {
	// string transformation
	if (typeof str === 'object') {
		try {
			str = JSON.stringify(str);
		} catch (error) {
			str = "[Object]";
		}
	} else if (typeof str !== 'string') {
		str = str.toString();
	}

	// prepare renderer
	var renderer = batcher.prepare(renderers.colorSprite, CHARSET_IMG, this);

	var color = this.palette[this._pen];
	var r = color.r;
	var g = color.g;
	var b = color.b;

	// print at cooordinate
	if (x !== undefined) {
		x = ~~Math.round(x - this.camera.x);
		y = ~~Math.round(y - this.camera.y);
		var originX = x;
		for (var i = 0; i < str.length; i++) {
			var chr = str.charCodeAt(i);
			if (chr === 10 || chr === 13) {
				y += CHAR_HEIGHT;
				x = originX;
				continue;
			}
			chr -= 32;
			if (chr < 0 || chr > 255) {
				x += CHAR_WIDTH;
				continue;
			}
			var sx = CHAR_WIDTH  *   (chr % CHAR_PER_LINE) + OX;
			var sy = CHAR_HEIGHT * ~~(chr / CHAR_PER_LINE) + OY;
			renderer.pushSprite(x, y, CHAR_WIDTH, CHAR_HEIGHT, sx, sy, r, g, b);
			x += CHAR_WIDTH;
		}
		return this;
	}

	// print at cursor
	var i = this._cursor.i;
	var j = this._cursor.j;

	for (var c = 0; c < str.length; c++) {
		if (i > this._textColumn) {
			j += 1;
			i = 0;
		}

		if (j > this._textLine) {
			this.textScroll();
			j -= 1;
			// don't forget to switch back the renderer
			renderer = batcher.prepare(renderers.colorSprite, CHARSET_IMG, this);
		}

		var chr = str.charCodeAt(c);
		if (chr === 10 || chr === 13) {
			j += 1;
			i = 0;
			continue;
		}
		chr -= 32;
		if (chr < 0 || chr > 255) {
			i += 1;
			continue;
		}

		var sx = CHAR_WIDTH  *   (chr % CHAR_PER_LINE) + OX;
		var sy = CHAR_HEIGHT * ~~(chr / CHAR_PER_LINE) + OY;
		renderer.pushSprite(i * CHAR_WIDTH, j * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT, sx, sy, r, g, b);
		i += 1;
	}

	this._cursor.i = i;
	this._cursor.j = j;

	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.println = function (str) {
	this.print(str);
	this.print('\n');
	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.textScroll = function (n) {
	if (n === undefined) n = 1;
	this._cursor.j -= n;
	n *= CHAR_HEIGHT;

	// NOTE: for optimization purpose, this code is duplicated from `draw` method
	if (!this._copyTexture) {
		this._copyTexture = new Texture(this.width, this.height);
	}

	this._copyTexture.draw(this, 0, 0);

	this.cls();
	this.draw(this._copyTexture, 0, -n);

	return this;
};

//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.setCharset = function (img) {
	CHARSET_IMG = img || DEFAULT_CHARSET;
	CHAR_WIDTH  = ~~(CHARSET_IMG.width / 16);
	CHAR_HEIGHT = ~~(CHARSET_IMG.height / 6);

	OX = 0;
	OY = 0;

	// if charset is a sprite in an atlas
	if (CHARSET_IMG._isSprite) {
		OX = CHARSET_IMG.x;
		OY = CHARSET_IMG.y;
		CHARSET_IMG = CHARSET_IMG.img;
	}

	this._textColumn   = ~~(this.width  / CHAR_WIDTH)  - 1;
	this._textLine     = ~~(this.height / CHAR_HEIGHT) - 1;
	this._textPadding  = this.height - this._textLine * CHAR_HEIGHT;
};
<|endoftext|>


<commit_before>/* eslint-disable no-console */
const url = require('url');
const crypto = require('crypto');
const Request = require('request-promise');
const ipaddr = require('ipaddr.js');
const queryString = require('query-string');

const SOCKET_POOL_SIZE = 32;
const MAX_BATCH_SIZE = 10;
const MAX_QUEUE_TIME = 60 * 60 * 1000;
const FALLBACK_HOST = 'https://schul-cloud.org';
const BATCH_HITS_ENDPOINT = 'https://www.google-analytics.com/batch';

let queue = [];
let flushTimer = [];
const request = Request.defaults({
  forever: true,
  pool: {maxSockets: SOCKET_POOL_SIZE}
});

const emit = (hit) => {
  queue.push(hit);
  if(queue.length === MAX_BATCH_SIZE) {
    flush();
  } else {
    // Schedules flush if no event is triggered within MAX_QUEUE_TIME
    flushTimer = setTimeout(() => flush(), MAX_QUEUE_TIME);
  }
};

const flush = () => {
  if(flushTimer) {
    clearTimeout(flushTimer);
    flushTimer = null;
  }
  if(queue.length === 0) {
    return;
  }

  let now = Date.now();
  let body = queue.map(hit => {
    // Adapts queue time parameter to the moment the hit is emitted
    hit.qt = Math.round((now - hit.qt) / 1000);
    return queryString.stringify(hit);
  }).join('\r\n');
  queue = [];

  request({
    method: 'POST',
    uri: BATCH_HITS_ENDPOINT,
    encoding: 'utf-8',
    body,
  }).catch(err => console.error(`Error while emitting Google Analytics hits (${err.message})`));
};

const anonymizeIp = (ip) => {
  ip = ipaddr.parse(ip);
  let bytes;
  if(ip.kind() === 'ipv4') {
    // Sets last 8bit to 0
    bytes = [].concat(ip.toByteArray().slice(0, 3), [0]);
  } else {
    // Sets last 88bit to 0
    bytes = [].concat(ip.toByteArray().slice(0, 5), Array(11).fill(0));
  }

  return ipaddr.fromByteArray(bytes).toNormalizedString();
};

const middleware = (req, res, next) => {
  if(req.app.get('env') === 'production' && process.env.GOOGLE_ANALYTICS_TRACKING_ID) {
    res.on('finish', () => {
      // Only track page views, not API calls
      let contentType = res.getHeader('content-type');
      let isHtmlContent = contentType && contentType.includes('text/html');
      if(!isHtmlContent && res.statusCode !== 304) {
        return;
      }

      let hit = {
        v: 1, // Protocol version
        tid: process.env.GOOGLE_ANALYTICS_TRACKING_ID, // Tracking ID
        qt: Date.now(), // Queue time
        t: 'pageview', // Hit type
        ds: 'web', // Data source
        dh: req.headers.origin || process.env.HOST || FALLBACK_HOST, // Document Host
        dl: url.parse(req.originalUrl).pathname, // Document location,
        cid: crypto.createHash('sha256').update(req.sessionID).digest('base64'), // User ID
        ua: req.headers['user-agent'], // User agent override
        uip: anonymizeIp(req.ip), // IP override
      };
      if(res.locals.currentSchoolData) {
        hit.cd1 = res.locals.currentSchoolData.name;
      }
      if(res.locals.currentUser) {
        hit.cd2 = res.locals.currentUser.gender;
        hit.cd4 = res.locals.currentUser.roles[0].name;
        hit.cd5 = res.locals.currentRole == 'Demo' ? 1 : 0;
      }

      emit(hit);
    });
  }

  next();
};

// Flush hit queue on exit
process.on('exit', () => {
  console.log('Sending queued Google Analytics hits...');
  flush();
});

module.exports = {
  middleware: middleware,
};
<commit_msg>Send exception hits for error pages.<commit_after>/* eslint-disable no-console */
const url = require('url');
const crypto = require('crypto');
const Request = require('request-promise');
const ipaddr = require('ipaddr.js');
const queryString = require('query-string');

const SOCKET_POOL_SIZE = 32;
const MAX_BATCH_SIZE = 10;
const MAX_QUEUE_TIME = 60 * 60 * 1000;
const FALLBACK_HOST = 'https://schul-cloud.org';
const BATCH_HITS_ENDPOINT = 'https://www.google-analytics.com/batch';

let queue = [];
let flushTimer = [];
const request = Request.defaults({
  forever: true,
  pool: {maxSockets: SOCKET_POOL_SIZE}
});

const emit = (hit) => {
  queue.push(hit);
  if(queue.length === MAX_BATCH_SIZE) {
    flush();
  } else {
    // Schedules flush if no event is triggered within MAX_QUEUE_TIME
    flushTimer = setTimeout(() => flush(), MAX_QUEUE_TIME);
  }
};

const flush = () => {
  if(flushTimer) {
    clearTimeout(flushTimer);
    flushTimer = null;
  }
  if(queue.length === 0) {
    return;
  }

  let now = Date.now();
  let body = queue.map(hit => {
    // Adapts queue time parameter to the moment the hit is emitted
    hit.qt = Math.round((now - hit.qt) / 1000);
    return queryString.stringify(hit);
  }).join('\r\n');
  queue = [];

  request({
    method: 'POST',
    uri: BATCH_HITS_ENDPOINT,
    encoding: 'utf-8',
    body,
  }).catch(err => console.error(`Error while emitting Google Analytics hits (${err.message})`));
};

const anonymizeIp = (ip) => {
  ip = ipaddr.parse(ip);
  let bytes;
  if(ip.kind() === 'ipv4') {
    // Sets last 8bit to 0
    bytes = [].concat(ip.toByteArray().slice(0, 3), [0]);
  } else {
    // Sets last 88bit to 0
    bytes = [].concat(ip.toByteArray().slice(0, 5), Array(11).fill(0));
  }

  return ipaddr.fromByteArray(bytes).toNormalizedString();
};

const middleware = (req, res, next) => {
  if(req.app.get('env') === 'production' && process.env.GOOGLE_ANALYTICS_TRACKING_ID) {
    res.on('finish', () => {
      // Only track page views, not API calls
      let contentType = res.getHeader('content-type');
      let isHtmlContent = contentType && contentType.includes('text/html');
      if(!isHtmlContent && res.statusCode !== 304) {
        return;
      }

      let hit = {
        v: 1, // Protocol version
        tid: process.env.GOOGLE_ANALYTICS_TRACKING_ID, // Tracking ID
        qt: Date.now(), // Queue time
        t: 'pageview', // Hit type
        ds: 'web', // Data source
        dh: req.headers.origin || process.env.HOST || FALLBACK_HOST, // Document Host
        dl: url.parse(req.originalUrl).pathname, // Document location,
        cid: crypto.createHash('sha256').update(req.sessionID).digest('base64'), // User ID
        ua: req.headers['user-agent'], // User agent override
        uip: anonymizeIp(req.ip), // IP override
      };

      // If page is error page, send exception hit instead of pageview hit
      if(res.locals.error) {
        hit.t = 'exception';
        hit.exd = res.locals.error.message;
      }

      // Custom dimensions
      if(res.locals.currentSchoolData) {
        hit.cd1 = res.locals.currentSchoolData.name;
      }
      if(res.locals.currentUser) {
        hit.cd2 = res.locals.currentUser.gender;
        hit.cd4 = res.locals.currentUser.roles[0].name;
        hit.cd5 = res.locals.currentRole === 'Demo' ? 1 : 0;
      }

      emit(hit);
    });
  }

  next();
};

// Flush hit queue on exit
process.on('exit', () => {
  console.log('Sending queued Google Analytics hits...');
  flush();
});

module.exports = {
  middleware: middleware,
};
<|endoftext|>


<commit_before>var LED = require("./led");
var Button = require("./button");
var leds=[
  new LED(0x27,{red:1}),
  new LED(0x27,{red:2}),
  new LED(0x27,{red:3}),
  new LED(0x27,{red:4}),
  new LED(0x27,{red:5}),
  new LED(0x27,{red:6}),
  new LED(0x27,{red:7}),
  new LED(0x27,{red:8}),
  new LED(0x27,{red:9}),
  new LED(0x27,{red:10}),
  new LED(0x27,{red:11}),
  new LED(0x27,{red:12}),
  new LED(0x27,{red:13}),
  new LED(0x27,{red:14}),
  new LED(0x27,{red:15}),
  new LED(0x27,{red:16}),
];
process.on('SIGINT', function(){
  for(let x in leds){
    leds[x].close();
  }
  process.exit();
}.bind(null, {exit:true}));
var at=-1;
var previous=at;
var next=function(){
  leds[previous].close();
  leds[at].open();
  previous=at;
  at++;
  if(at>=leds.length){
    at=0;
  }
};
next();
var button=new Button(0x21,1,next);
<commit_msg>bug fix<commit_after>var LED = require("./led");
var Button = require("./button");
var leds=[
  new LED(0x27,{red:1}),
  new LED(0x27,{red:2}),
  new LED(0x27,{red:3}),
  new LED(0x27,{red:4}),
  new LED(0x27,{red:5}),
  new LED(0x27,{red:6}),
  new LED(0x27,{red:7}),
  new LED(0x27,{red:8}),
  new LED(0x27,{red:9}),
  new LED(0x27,{red:10}),
  new LED(0x27,{red:11}),
  new LED(0x27,{red:12}),
  new LED(0x27,{red:13}),
  new LED(0x27,{red:14}),
  new LED(0x27,{red:15}),
  new LED(0x27,{red:16}),
];
process.on('SIGINT', function(){
  for(let x in leds){
    leds[x].close();
  }
  process.exit();
}.bind(null, {exit:true}));
var at=-1;
var previous=0;
var next=function(){
  leds[previous].close();
  leds[at].open();
  previous=at;
  at++;
  if(at>=leds.length){
    at=0;
  }
};
next();
var button=new Button(0x21,1,next);
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var removeCharAt = function(s,n) {
    return s.substr(0,n) + s.substr(n+1);
};

var anagrams = function(s) {
    var n = s.length;

    if (n === 0) return [];

    if (n === 1) return [s];

    var r = [];

    for (var i=0;i<n;++i) {
	var remaining = removeCharAt(s,i);

	var rest = anagrams(remaining);
	var jn = rest.length;
	for (var j=0;j<jn;++j) {
	    r.push(s[i] + rest[j]);
	}
    }

    return r;
};

describe('anagrams', function() {

    it('anagrams of an empty string are empty', function() {
	assert.deepEqual([], anagrams(''));
    });

    it('anagrams of a single character', function() {
	assert.deepEqual(['a'], anagrams('a'));
	assert.deepEqual(['z'], anagrams('z'));
    });

    it('anagrams of multiple characters', function() {
	assert.deepEqual(['aa','aa'], anagrams('aa'));
	assert.deepEqual(['bb','bb'], anagrams('bb'));
    });

    it('anagrams of different characters', function() {
	assert.deepEqual(['ab','ba'], anagrams('ab'));
    });

    it('anagrams have expected count', function() {
	assert.equal(5*4*3*2*1, anagrams('abcde').length);
    });
});
<commit_msg>refactor<commit_after>"use strict";

var assert = require('assert');

var removeCharAt = function(s,n) {
    return s.substr(0,n) + s.substr(n+1);
};

var anagrams = function(s) {
    var n = s.length;

    if (n === 0) return [];

    if (n === 1) return [s];

    var r = [];

    for (var i=0;i<n;++i) {
	var rest = anagrams(removeCharAt(s,i));
	var jn = rest.length;
	for (var j=0;j<jn;++j) {
	    r.push(s[i] + rest[j]);
	}
    }

    return r;
};

describe('anagrams', function() {

    it('anagrams of an empty string are empty', function() {
	assert.deepEqual([], anagrams(''));
    });

    it('anagrams of a single character', function() {
	assert.deepEqual(['a'], anagrams('a'));
	assert.deepEqual(['z'], anagrams('z'));
    });

    it('anagrams of multiple characters', function() {
	assert.deepEqual(['aa','aa'], anagrams('aa'));
	assert.deepEqual(['bb','bb'], anagrams('bb'));
    });

    it('anagrams of different characters', function() {
	assert.deepEqual(['ab','ba'], anagrams('ab'));
    });

    it('anagrams have expected count', function() {
	assert.equal(5*4*3*2*1, anagrams('abcde').length);
    });
});
<|endoftext|>


<commit_before>module('Data adapters - Array');

var ArrayData = require('select2/data/array');
var $ = require('jquery');
var Options = require('select2/options');

var arrayOptions = new Options({
  data: [
    {
      id: 'default',
      text: 'Default'
    },
    {
      id: '1',
      text: 'One'
    },
    {
      id: '2',
      text: '2'
    }
  ]
});

var extraOptions = new Options ({
  data: [
    {
      id: 'default',
      text: 'Default',
      extra: true
    },
    {
      id: 'One',
      text: 'One',
      extra: true
    }
  ]
});

var nestedOptions = new Options({
  data: [
    {
      text: 'Default',
      children: [
        {
          text: 'Next',
          children: [
            {
              id: 'a',
              text: 'Option'
            }
          ]
        }
      ]
    }
  ]
});

test('current gets default for single', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should always be a selected item for array data.'
    );

    var item = val[0];

    assert.equal(
      item.id,
      'default',
      'The first item should be selected'
    );
  });
});

test('current gets default for multiple', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  data.current(function (val) {
    assert.equal(
      val.length,
      0,
      'There should be no default selection.'
    );
  });
});

test('current works with existing selections', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  $select.val(['One']);

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should only be one existing selection.'
    );

    var option = val[0];

    assert.equal(
      option.id,
      'One',
      'The id should be equal to the value of the option tag.'
    );

    assert.equal(
      option.text,
      'One',
      'The text should be equal to the text of the option tag.'
    );
  });
});

test('current works with selected data', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  data.select({
    id: '2',
    text: '2'
  });

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should only be one option selected.'
    );

    var option = val[0];

    assert.equal(
      option.id,
      '2',
      'The id should match the original id from the array.'
    );

    assert.equal(
      option.text,
      '2',
      'The text should match the original text from the array.'
    );
  });
});

test('select works for single', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  assert.equal(
    $select.val(),
    'default',
    'There should already be a selection'
  );

  data.select({
    id: '1',
    text: 'One'
  });

  assert.equal(
    $select.val(),
    '1',
    'The selected value should be the same as the selected id'
  );
});

test('multiple sets the value', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  assert.equal($select.val(), null);

  data.select({
    id: 'default',
    text: 'Default'
  });

  assert.deepEqual($select.val(), ['default']);
});

test('multiple adds to the old value', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  $select.val(['One']);

  assert.deepEqual($select.val(), ['One']);

  data.select({
    id: 'default',
    text: 'Default'
  });

  assert.deepEqual($select.val(), ['One', 'default']);
});

test('option tags are automatically generated', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  assert.equal(
    $select.find('option').length,
    3,
    'An <option> element should be created for each object'
  );
});

test('option tags can receive new data', function(assert) {
  var $select = $('#qunit-fixture .single');

  var data = new ArrayData($select, extraOptions);

  assert.equal(
    $select.find('option').length,
    2,
    'Only one more <option> element should be created'
  );

  data.select({
    id: 'default'
  });

  assert.ok(
    $select.find(':selected').data('data').extra,
    '<option> default should have new data'
  );

  data.select({
    id: 'One'
  });

  assert.ok(
    $select.find(':selected').data('data').extra,
    '<option> One should have new data'
  );
});

test('optgroup tags can also be generated', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, nestedOptions);

  assert.equal(
    $select.find('option').length,
    1,
    'An <option> element should be created for the one selectable object'
  );

  assert.equal(
    $select.find('optgroup').length,
    2,
    'An <optgroup> element should be created for the two with children'
  );
});

test('optgroup tags have the right properties', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, nestedOptions);

  var $group = $select.children('optgroup');

  assert.equal(
    $group.prop('label'),
    'Default',
    'An `<optgroup>` label should match the text property'
  );

  assert.equal(
    $group.children().length,
    1,
    'The <optgroup> should have one child under it'
  );
});
<commit_msg>Add failing test for existing array selections<commit_after>module('Data adapters - Array');

var ArrayData = require('select2/data/array');
var $ = require('jquery');
var Options = require('select2/options');

var arrayOptions = new Options({
  data: [
    {
      id: 'default',
      text: 'Default'
    },
    {
      id: '1',
      text: 'One'
    },
    {
      id: '2',
      text: '2'
    }
  ]
});

var extraOptions = new Options ({
  data: [
    {
      id: 'default',
      text: 'Default',
      extra: true
    },
    {
      id: 'One',
      text: 'One',
      extra: true
    }
  ]
});

var nestedOptions = new Options({
  data: [
    {
      text: 'Default',
      children: [
        {
          text: 'Next',
          children: [
            {
              id: 'a',
              text: 'Option'
            }
          ]
        }
      ]
    }
  ]
});

test('current gets default for single', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should always be a selected item for array data.'
    );

    var item = val[0];

    assert.equal(
      item.id,
      'default',
      'The first item should be selected'
    );
  });
});

test('current gets default for multiple', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  data.current(function (val) {
    assert.equal(
      val.length,
      0,
      'There should be no default selection.'
    );
  });
});

test('current works with existing selections', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  $select.val(['One']);

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should only be one existing selection.'
    );

    var option = val[0];

    assert.equal(
      option.id,
      'One',
      'The id should be equal to the value of the option tag.'
    );

    assert.equal(
      option.text,
      'One',
      'The text should be equal to the text of the option tag.'
    );
  });
});

test('current works with selected data', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  data.select({
    id: '2',
    text: '2'
  });

  data.current(function (val) {
    assert.equal(
      val.length,
      1,
      'There should only be one option selected.'
    );

    var option = val[0];

    assert.equal(
      option.id,
      '2',
      'The id should match the original id from the array.'
    );

    assert.equal(
      option.text,
      '2',
      'The text should match the original text from the array.'
    );
  });
});

test('select works for single', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  assert.equal(
    $select.val(),
    'default',
    'There should already be a selection'
  );

  data.select({
    id: '1',
    text: 'One'
  });

  assert.equal(
    $select.val(),
    '1',
    'The selected value should be the same as the selected id'
  );
});

test('multiple sets the value', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  assert.equal($select.val(), null);

  data.select({
    id: 'default',
    text: 'Default'
  });

  assert.deepEqual($select.val(), ['default']);
});

test('multiple adds to the old value', function (assert) {
  var $select = $('#qunit-fixture .multiple');

  var data = new ArrayData($select, arrayOptions);

  $select.val(['One']);

  assert.deepEqual($select.val(), ['One']);

  data.select({
    id: 'default',
    text: 'Default'
  });

  assert.deepEqual($select.val(), ['One', 'default']);
});

test('option tags are automatically generated', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, arrayOptions);

  assert.equal(
    $select.find('option').length,
    3,
    'An <option> element should be created for each object'
  );
});

test('option tags can receive new data', function(assert) {
  var $select = $('#qunit-fixture .single');

  var data = new ArrayData($select, extraOptions);

  assert.equal(
    $select.find('option').length,
    2,
    'Only one more <option> element should be created'
  );

  data.select({
    id: 'default'
  });

  assert.ok(
    $select.find(':selected').data('data').extra,
    '<option> default should have new data'
  );

  data.select({
    id: 'One'
  });

  assert.ok(
    $select.find(':selected').data('data').extra,
    '<option> One should have new data'
  );
});

test('optgroup tags can also be generated', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, nestedOptions);

  assert.equal(
    $select.find('option').length,
    1,
    'An <option> element should be created for the one selectable object'
  );

  assert.equal(
    $select.find('optgroup').length,
    2,
    'An <optgroup> element should be created for the two with children'
  );
});

test('optgroup tags have the right properties', function (assert) {
  var $select = $('#qunit-fixture .single-empty');

  var data = new ArrayData($select, nestedOptions);

  var $group = $select.children('optgroup');

  assert.equal(
    $group.prop('label'),
    'Default',
    'An `<optgroup>` label should match the text property'
  );

  assert.equal(
    $group.children().length,
    1,
    'The <optgroup> should have one child under it'
  );
});

test('existing selections are respected on initialization', function (assert) {
   var $select = $(
     '<select>' +
        '<option>First</option>' +
        '<option selected>Second</option>' +
      '</select>'
    );

    var options = new Options({
      data: [
        {
          id: 'Second',
          text: 'Second'
        },
        {
          id: 'Third',
          text: 'Third'
        }
      ]
    });

    assert.equal($select.val(), 'Second');

    var data = new ArrayData($select, options);

    assert.equal($select.val(), 'Second');
});<|endoftext|>


<commit_before>/**
 * Created by desver_f on 23/01/17.
 */

import axios from 'axios';
import uiState from '../stores/uiState';
import moment from 'moment';

const client = axios.create({
    baseURL: 'https://intra.epitech.eu',
    timeout: 20000,
    validateStatus: (status) => status < 500,
});

function request(config) {
    if (uiState.isConnected) {
        return client.request(config)
            .then(({ data }) => data)
            .catch((error) => {
                if (error.response) {
                    // The request was made and the server responded with a status code
                    // that falls out of the range of 2xx
                    console.log(error.response.data);
                    console.log(error.response.status);
                    console.log(error.response.headers);
                } else if (error.request) {
                    // The request was made but no response was received
                    console.log(error.request);
                } else {
                    // Something happened in setting up the request that triggered an Error
                    console.log('Error', error.message);
                }
                console.log(error.config);
                uiState.errorState();
            });
    } else {
        return Promise.resolve(false);
    }
}

export function loginFromRedirectUri(redirectUri) {
    return request({
        url: redirectUri,
        baseURL: '',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchAutoLogin() {
    return request({
        url: `/admin/autolog?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
    });
}

export function autoLog(autoLoginLink) {
    return request({
        url: autoLoginLink,
        baseURL: '',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchStudent(student) {
    return request({
        url: `/user/` + student + '/?format=json',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchLoggedInStudent() {
    return request({
        url: `/user?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchNetsoul(student) {
    return request({
        url: `/user/` + student + '/netsoul?format=json',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchCalendar(start, end) {
    const calendarUrl = start && end
        ? `/planning/load?format=json&start=${start}&end=${end}`
        : `/planning/load?format=json`;

    return request({
        url: calendarUrl,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchActivity({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function registerActivity({ year, module, instance, activity, event }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/${event}/register?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function unregisterActivity({ year, module, instance, activity, event }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/${event}/unregister?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function registerActivitySlot(slotId, teamId, { year, module, instance, activity }) {
    const formData = new FormData();
    formData.append('id_creneau', slotId);
    teamId && formData.append('id_team', teamId);

    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/register?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData
    });
}

export function unregisterActivitySlot(slotId, idTeam, masterLogin, { year, module, instance, activity }) {
    const formData = new FormData();
    formData.append('value[0][id_creneau]', slotId);
    idTeam && formData.append('value[0][id_team]', idTeam);
    masterLogin && formData.append('value[0][login]', masterLogin);

    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/unregister?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData
    });
}

export function logout() {
    return request({
        url: `/logout?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

function fetchPromotionPage(page) {
    return request({
        url: `${page}`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export async function fetchPromotion(location, year, promo) {
    try {
        const PAGE_SIZE = 48;
        const { items: promotion, total } = await fetchPromotionPage(`/user/filter/user?format=json&location=${location}&year=${year}&promo=${promo}&offset=0`);
        const fullPromotion = [...promotion];

        for (let i = promotion.length; i < total; i += PAGE_SIZE) {
            const { items: newPage } = await fetchPromotionPage(`/user/filter/user?format=json&location=${location}&year=${year}&promo=${promo}&offset=${i}`);

            fullPromotion.push(...newPage);
        }

        return fullPromotion;
    } catch (e) {
        console.error(e.message)
    }
}

export function fetchProjects() {
    const start = moment().startOf('year').format('YYYY-MM-DD');
    const end = moment().add(1, 'year').format('YYYY-MM-DD');

    return request({
        url: `/module/board?format=json&start=${start}&end=${end}`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectDetails({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/project/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectFiles({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/project/file/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
    });
}

export function fetchMarks(user) {
    return request({
        url: `/user/${user}/notes?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectMarks(year, module, instance, activity) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/note?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchDocuments(user) {
    return request({
        url: `/user/${user}/document/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function validateToken(tokenLink, tokenValue) {
    const formData = new FormData();
    formData.append('token', tokenValue);
    formData.append('rate', '0');
    formData.append('comment', '');

    return request({
        url: `${tokenLink}?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData,
    });
}

export function fetchOffice365Link() {
    return request({
        url: '/',
        method: 'GET',
    });
}
<commit_msg>Increased requests' timeout to 50 seconds<commit_after>/**
 * Created by desver_f on 23/01/17.
 */

import axios from 'axios';
import uiState from '../stores/uiState';
import moment from 'moment';

const client = axios.create({
    baseURL: 'https://intra.epitech.eu',
    timeout: 50 * 1000, //50 seconds timeout limit
    validateStatus: (status) => status < 500,
});

function request(config) {
    if (uiState.isConnected) {
        return client.request(config)
            .then(({ data }) => data)
            .catch((error) => {
                if (error.response) {
                    // The request was made and the server responded with a status code
                    // that falls out of the range of 2xx
                    console.log(error.response.data);
                    console.log(error.response.status);
                    console.log(error.response.headers);
                } else if (error.request) {
                    // The request was made but no response was received
                    console.log(error.request);
                } else {
                    // Something happened in setting up the request that triggered an Error
                    console.log('Error', error.message);
                }
                console.log(error.config);
                uiState.errorState();
            });
    } else {
        return Promise.resolve(false);
    }
}

export function loginFromRedirectUri(redirectUri) {
    return request({
        url: redirectUri,
        baseURL: '',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchAutoLogin() {
    return request({
        url: `/admin/autolog?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
    });
}

export function autoLog(autoLoginLink) {
    return request({
        url: autoLoginLink,
        baseURL: '',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchStudent(student) {
    return request({
        url: `/user/` + student + '/?format=json',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchLoggedInStudent() {
    return request({
        url: `/user?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchNetsoul(student) {
    return request({
        url: `/user/` + student + '/netsoul?format=json',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchCalendar(start, end) {
    const calendarUrl = start && end
        ? `/planning/load?format=json&start=${start}&end=${end}`
        : `/planning/load?format=json`;

    return request({
        url: calendarUrl,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchActivity({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function registerActivity({ year, module, instance, activity, event }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/${event}/register?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function unregisterActivity({ year, module, instance, activity, event }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/${event}/unregister?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function registerActivitySlot(slotId, teamId, { year, module, instance, activity }) {
    const formData = new FormData();
    formData.append('id_creneau', slotId);
    teamId && formData.append('id_team', teamId);

    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/register?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData
    });
}

export function unregisterActivitySlot(slotId, idTeam, masterLogin, { year, module, instance, activity }) {
    const formData = new FormData();
    formData.append('value[0][id_creneau]', slotId);
    idTeam && formData.append('value[0][id_team]', idTeam);
    masterLogin && formData.append('value[0][login]', masterLogin);

    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/rdv/unregister?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData
    });
}

export function logout() {
    return request({
        url: `/logout?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

function fetchPromotionPage(page) {
    return request({
        url: `${page}`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export async function fetchPromotion(location, year, promo) {
    try {
        const PAGE_SIZE = 48;
        const { items: promotion, total } = await fetchPromotionPage(`/user/filter/user?format=json&location=${location}&year=${year}&promo=${promo}&offset=0`);
        const fullPromotion = [...promotion];

        for (let i = promotion.length; i < total; i += PAGE_SIZE) {
            const { items: newPage } = await fetchPromotionPage(`/user/filter/user?format=json&location=${location}&year=${year}&promo=${promo}&offset=${i}`);

            fullPromotion.push(...newPage);
        }

        return fullPromotion;
    } catch (e) {
        console.error(e.message)
    }
}

export function fetchProjects() {
    const start = moment().startOf('year').format('YYYY-MM-DD');
    const end = moment().add(1, 'year').format('YYYY-MM-DD');

    return request({
        url: `/module/board?format=json&start=${start}&end=${end}`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectDetails({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/project/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectFiles({ year, module, instance, activity }) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/project/file/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
    });
}

export function fetchMarks(user) {
    return request({
        url: `/user/${user}/notes?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchProjectMarks(year, module, instance, activity) {
    return request({
        url: `/module/${year}/${module}/${instance}/${activity}/note?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function fetchDocuments(user) {
    return request({
        url: `/user/${user}/document/?format=json`,
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
    });
}

export function validateToken(tokenLink, tokenValue) {
    const formData = new FormData();
    formData.append('token', tokenValue);
    formData.append('rate', '0');
    formData.append('comment', '');

    return request({
        url: `${tokenLink}?format=json`,
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
        },
        data: formData,
    });
}

export function fetchOffice365Link() {
    return request({
        url: '/',
        method: 'GET',
    });
}
<|endoftext|>