<commit_before>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

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

import Form from '../Form';

const expect = chai.expect;

/* eslint-disable no-unused-vars */
const debug = (component) => {
    console.info('------------------------------------------------------------');
    console.info(component.debug());
    console.info('------------------------------------------------------------');
};
/* eslint-enable no-unused-vars */

/* *****************************************************************************
a Form component containing a SubmitButton
    should include a <Form.SubmitButton> as a child
    should be a button.btn
    should have the text specified
    should have the child elements specified
    should be button.btn.btn-default when no style is provided
    should be button.btn.btn-danger when style=danger
    should be button.btn.btn-danger when style=error
    should be button.btn.btn-warning when style=warning
    should be button.btn.btn-warning when style=warn
    should be button.btn.btn-info when style=info
    should be button.btn.btn-success when style=success
    should be button.btn.btn-success when style=ok
*/
describe('a Form component containing a SubmitButton', () => {

    it('should include a <Form.SubmitButton> as a child', () => {
        const component = shallow(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find(Form.SubmitButton)).to.have.length(1);
    });

    it('should be a button.btn', () => {
        const component = render(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn')).to.have.length(1, 'button.btn.btn-default');
    });

    it('should have the text specified', () => {

        const buttonText = 'this is my button text';

        const component = render(
            <Form>
                <Form.SubmitButton>{buttonText}</Form.SubmitButton>
            </Form>
        );

        expect(component.find('button.btn').text()).to.equal(buttonText);
    });

    it('should have the child elements specified', () => {

        const icon = <i className="fa fa-some-icon" />;

        const component = render(
            <Form>
                <Form.SubmitButton>{icon}</Form.SubmitButton>
            </Form>
        );

        expect(component.find('button.btn i.fa.fa-some-icon')).to.have.length(1);
    });

    it('should be button.btn.btn-default when no style is provided', () => {
        const component = render(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-default'))
            .to.have.length(1, 'button.btn.btn-default');
    });

    it('should be button.btn.btn-danger when style=danger', () => {

        const style = 'danger';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-danger')).to.have.length(1, 'button.btn.btn-danger');
    });

    it('should be button.btn.btn-danger when style=error', () => {

        const style = 'error';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-danger')).to.have.length(1, 'button.btn.btn-danger');
    });

    it('should be button.btn.btn-warning when style=warning', () => {

        const style = 'warning';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-warning'))
            .to.have.length(1, 'button.btn.btn-warning');
    });

    it('should be button.btn.btn-warning when style=warn', () => {

        const style = 'warn';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-warning'))
            .to.have.length(1, 'button.btn.btn-warning');
    });

    it('should be button.btn.btn-info when style=info', () => {

        const style = 'info';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-info')).to.have.length(1, 'button.btn.btn-info');
    });

    it('should be button.btn.btn-success when style=success', () => {

        const style = 'success';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-success'))
            .to.have.length(1, 'button.btn.btn-success');
    });

    it('should be button.btn.btn-success when style=ok', () => {

        const style = 'ok';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-success'))
            .to.have.length(1, 'button.btn.btn-success');
    });
});

/* *****************************************************************************
given a Form containing a required TextInput and a SubmitButton
    after initialization with a valid value, the SubmitButton should be enabled
    after initialization with several valid values, the SubmitButton should be enabled
    after initialization with multiple invalid values, the SubmitButton should be disabled
    after initialization with a mix of valid & invalid values, the SubmitButton should be disabled
    after changing from invalid to valid, the SubmitButton should be enabled
    after changing from valid to invalid, the SubmitButton should be disabled
*/
describe('given a Form containing a required TextInput and a SubmitButton', () => {

    it('after initialization with a valid value, the SubmitButton should be enabled', () => {
        const value = 'a valid value';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after initialization with several valid values, the SubmitButton should be enabled', () => {
        const value = 'a valid value';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after initialization with multiple invalid values, the SubmitButton ' +
        'should be disabled', () => {
        const value = '';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

    it('after initialization with a mix of valid & invalid values, the ' +
        'SubmitButton should be disabled', () => {
        const invalid = '';

        const valid = 'triceratops';

        const component = mount(
            <Form>
                <Form.TextInput required value={invalid} />
                <Form.TextInput required value={invalid} />
                <Form.TextInput required value={valid} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

    it('after changing from invalid to valid, the SubmitButton should be enabled', () => {
        const initialValue = '';

        const finalValue = 'I wish I were an Oscar Meyer weiner';

        const component = mount(
            <Form>
                <Form.TextInput required value={initialValue} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        component.find('input').simulate('blur');

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after changing from valid to invalid, the SubmitButton should be disabled', () => {
        const initialValue = 'I wish I were an Oscar Meyer weiner';

        const finalValue = '';

        const component = mount(
            <Form>
                <Form.TextInput required value={initialValue} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        component.find('input').simulate('blur');

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

});

/* *****************************************************************************
a SubmitButton
    should call onClick when the button in a form is clicked
*/
describe('a SubmitButton', () => {

    it('should call onClick when the button in a form is clicked', () => {

        const onClick = sinon.spy();

        const component = mount(
            <Form>
                <Form.SubmitButton onClick={onClick} />
            </Form>
        );

        component.find('button').simulate('click');

        expect(onClick.callCount).to.equal(1);
    });
});
<commit_msg>updated notes<commit_after>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

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

import Form from '../Form';

const expect = chai.expect;

/* eslint-disable no-unused-vars */
const debug = (component) => {
    console.info('------------------------------------------------------------');
    console.info(component.debug());
    console.info('------------------------------------------------------------');
};
/* eslint-enable no-unused-vars */

/* *****************************************************************************
a Form component containing a SubmitButton
    should include a <Form.SubmitButton> as a child
    should be a button.btn
    should have the text specified
    should have the child elements specified
    should be button.btn.btn-default when no style is provided
    should be button.btn.btn-danger when style=danger
    should be button.btn.btn-danger when style=error
    should be button.btn.btn-warning when style=warning
    should be button.btn.btn-warning when style=warn
    should be button.btn.btn-info when style=info
    should be button.btn.btn-success when style=success
    should be button.btn.btn-success when style=ok
    should not have a name if the name prop is not set
    should have the appropriate name if the name prop is set
*/
describe('a Form component containing a SubmitButton', () => {

    it('should include a <Form.SubmitButton> as a child', () => {
        const component = shallow(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find(Form.SubmitButton)).to.have.length(1);
    });

    it('should be a button.btn', () => {
        const component = render(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn')).to.have.length(1, 'button.btn.btn-default');
    });

    it('should have the text specified', () => {

        const buttonText = 'this is my button text';

        const component = render(
            <Form>
                <Form.SubmitButton>{buttonText}</Form.SubmitButton>
            </Form>
        );

        expect(component.find('button.btn').text()).to.equal(buttonText);
    });

    it('should have the child elements specified', () => {

        const icon = <i className="fa fa-some-icon" />;

        const component = render(
            <Form>
                <Form.SubmitButton>{icon}</Form.SubmitButton>
            </Form>
        );

        expect(component.find('button.btn i.fa.fa-some-icon')).to.have.length(1);
    });

    it('should be button.btn.btn-default when no style is provided', () => {
        const component = render(
            <Form>
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-default'))
            .to.have.length(1, 'button.btn.btn-default');
    });

    it('should be button.btn.btn-danger when style=danger', () => {

        const style = 'danger';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-danger')).to.have.length(1, 'button.btn.btn-danger');
    });

    it('should be button.btn.btn-danger when style=error', () => {

        const style = 'error';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-danger')).to.have.length(1, 'button.btn.btn-danger');
    });

    it('should be button.btn.btn-warning when style=warning', () => {

        const style = 'warning';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-warning'))
            .to.have.length(1, 'button.btn.btn-warning');
    });

    it('should be button.btn.btn-warning when style=warn', () => {

        const style = 'warn';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-warning'))
            .to.have.length(1, 'button.btn.btn-warning');
    });

    it('should be button.btn.btn-info when style=info', () => {

        const style = 'info';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-info')).to.have.length(1, 'button.btn.btn-info');
    });

    it('should be button.btn.btn-success when style=success', () => {

        const style = 'success';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-success'))
            .to.have.length(1, 'button.btn.btn-success');
    });

    it('should be button.btn.btn-success when style=ok', () => {

        const style = 'ok';

        const component = render(
            <Form>
                <Form.SubmitButton style={style} />
            </Form>
        );

        expect(component.find('button')).to.have.length(1, 'button');
        expect(component.find('button.btn.btn-success'))
            .to.have.length(1, 'button.btn.btn-success');
    });
});

/* *****************************************************************************
given a Form containing a required TextInput and a SubmitButton
    after initialization with a valid value, the SubmitButton should be enabled
    after initialization with several valid values, the SubmitButton should be enabled
    after initialization with multiple invalid values, the SubmitButton should be disabled
    after initialization with a mix of valid & invalid values, the SubmitButton should be disabled
    after changing from invalid to valid, the SubmitButton should be enabled
    after changing from valid to invalid, the SubmitButton should be disabled
*/
describe('given a Form containing a required TextInput and a SubmitButton', () => {

    it('after initialization with a valid value, the SubmitButton should be enabled', () => {
        const value = 'a valid value';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after initialization with several valid values, the SubmitButton should be enabled', () => {
        const value = 'a valid value';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after initialization with multiple invalid values, the SubmitButton ' +
        'should be disabled', () => {
        const value = '';

        const component = mount(
            <Form>
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.TextInput required value={value} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

    it('after initialization with a mix of valid & invalid values, the ' +
        'SubmitButton should be disabled', () => {
        const invalid = '';

        const valid = 'triceratops';

        const component = mount(
            <Form>
                <Form.TextInput required value={invalid} />
                <Form.TextInput required value={invalid} />
                <Form.TextInput required value={valid} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

    it('after changing from invalid to valid, the SubmitButton should be enabled', () => {
        const initialValue = '';

        const finalValue = 'I wish I were an Oscar Meyer weiner';

        const component = mount(
            <Form>
                <Form.TextInput required value={initialValue} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(true);

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        component.find('input').simulate('blur');

        expect(component.find('button.btn').props().disabled).to.equal(false);
    });

    it('after changing from valid to invalid, the SubmitButton should be disabled', () => {
        const initialValue = 'I wish I were an Oscar Meyer weiner';

        const finalValue = '';

        const component = mount(
            <Form>
                <Form.TextInput required value={initialValue} />
                <Form.SubmitButton />
            </Form>
        );

        expect(component.find('button.btn').props().disabled).to.equal(false);

        component.find('input').simulate('change', {
            target: { value: finalValue }
        });

        component.find('input').simulate('blur');

        expect(component.find('button.btn').props().disabled).to.equal(true);
    });

});

/* *****************************************************************************
a SubmitButton
    should call onClick when the button in a form is clicked
*/
describe('a SubmitButton', () => {

    it('should call onClick when the button in a form is clicked', () => {

        const onClick = sinon.spy();

        const component = mount(
            <Form>
                <Form.SubmitButton onClick={onClick} />
            </Form>
        );

        component.find('button').simulate('click');

        expect(onClick.callCount).to.equal(1);
    });
});
<|endoftext|>


<commit_before>import { LitVirtualizer } from '../lit-virtualizer.js'

describe('<lit-virtualizer>', function () {
  it('is running the tests', function () {
    assert.equal(1 + 1, 2);
  });

  it('registers lit-virtualizer as a custom element', function () {
    const lvs = document.createElement('lit-virtualizer');
    assert.instanceOf(lvs, LitVirtualizer);
  });
});<commit_msg>[lit-virtualizer] add useShadowDOM tests<commit_after>import { LitVirtualizer, scroll } from '../lit-virtualizer.js'
import { html, render } from 'lit-html'

describe('<lit-virtualizer>', function () {
  it('is running the tests', function () {
    assert.equal(1 + 1, 2);
  });

  it('registers lit-virtualizer as a custom element', function () {
    const lvs = document.createElement('lit-virtualizer');
    assert.instanceOf(lvs, LitVirtualizer);
  });
});

describe('scroll', function () {
  let container;

  beforeEach(function() {
    container = document.createElement('div');
    document.body.appendChild(container);
  });

  afterEach(function() {
    document.body.removeChild(container);
  })

  it('uses the template to render the items', async function () {
    const dir = scroll({
      items: ['foo', 'bar', 'baz'],
      template: (item) => html`<p>${item}</p>`,
      useShadowDOM: false
    });
    const templateResult = html`<div>
      ${dir}
    </div>`

    render(templateResult, container);

    // must wait an animation frame to let render finish.
    await (new Promise(resolve => requestAnimationFrame(resolve)));
    assert.include(container.innerHTML, 'foo');
    assert.include(container.innerHTML, 'bar');
    assert.include(container.innerHTML, 'baz');
    // must return in async test per Mocha specs.
    return
  });

  describe('useShadowDOM', function() {
    it('renders to shadow DOM when useShadowDOM is true', async function() {
      const dir = scroll({
        items: ['foo', 'bar', 'baz'],
        template: (item) => html`<p>${item}</p>`,
        useShadowDOM: true,
      });
      const templateResult = html`${dir}`

      render(templateResult, container);

      await (new Promise(resolve => requestAnimationFrame(resolve)));
      assert.exists(container.shadowRoot);
      return
    });

    it('does not render to shadow DOM when useShadowDOM is false', async function() {
      const dir = scroll({
        items: ['foo', 'bar', 'baz'],
        template: (item) => html`<p>${item}</p>`,
        useShadowDOM: false,
      });
      const templateResult = html`${dir}`

      render(templateResult, container);

      await (new Promise(resolve => requestAnimationFrame(resolve)));
      assert.notExists(container.shadowRoot);
      return
    });
  })
});<|endoftext|>


<commit_before>const fs = require('fs')

const layout = require('dw-layout')
const Database = require('dw-database')
const logger = require('dw-log')

const Songs = new Database.Group('songs', {
    load: true,
    parse: true
})

const closeButton = new layout.Button(
    {
        text: 'Cancel',

        onClick: () => layout.window.close()
    },
    {
        margin: '4px'
    }
)
const fileSelect = new layout.FileInput(
    {
        save: true,

        text: 'Export',
        button: 'Export',

        disabled: true,

        filters: [
            {
                name: 'JSON',
                extensions: ['json']
            }
        ]
    },
    {
        margin: '4px'
    }
)
const output = new layout.Text(
    {
        text: 'Loading Songs...'
    },
    {
        margin: '4px',
        align: 'center'
    }
)

layout.change(layout.body, {
    direction: 'vertical',
    padding: '8px',

    justify: 'space-between'
})

layout.body.add(output)
layout.body.add(
    new layout.Block(
        {
            items: [closeButton, fileSelect]
        },
        {
            direction: 'horizontal',
            grow: 0,
            shrink: 0,

            align: 'end'
        }
    )
)

function save(filename) {
    let data = ''

    try {
        data = JSON.stringify(Songs.list)
    } catch (error) {
        output.text =
            'Could not export songs: ' + (error.message || error.toString())

        logger.error('Unable to stringify songs', error)

        closeButton.disabled = false
        fileSelect.disabled = false

        return false
    }

    fs.writeFile(filename, data, 'utf8', error => {
        closeButton.disabled = false
        fileSelect.disabled = false

        if (error) {
            output.text =
                'Error exporting songs: ' + (error.message || error.toString())

            logger.error('Unable to export songs', error)

            return false
        }

        if (process.platform === 'darwin') {
            layout.window.close()
        } else {
            output.text = 'Exported'
        }
    })
}

fileSelect.onEvent('save', event => {
    if (event.filename) {
        closeButton.disabled = true
        fileSelect.disabled = true

        output.text = 'Exporting...'

        if (Songs.updating) {
            Songs.onceEvent('update', () => {
                save(event.filename)
            })
        } else {
            save(event.filename)
        }
    } else {
        layout.showNotification({
            type: 'error',

            message: 'No file selected!'
        })
    }
})

Songs.onEvent('update', () => {
    if (Songs.files.length > 0) {
        fileSelect.disabled = false

        output.text =
            Songs.files.length.toString() +
            ' song' +
            (Songs.files.length > 1 ? 's' : '') +
            ' in library'
    } else {
        fileSelect.disabled = true
        output.text = 'No songs in library'
    }
})

Songs.onEvent('error', error => {
    layout.dialog.showNotification({
        type: 'error',
        autoHide: false,

        message: 'There is an error with the Song database!\n' + error.message
    })
})
<commit_msg>Changed button text<commit_after>const fs = require('fs')

const layout = require('dw-layout')
const Database = require('dw-database')
const logger = require('dw-log')

const Songs = new Database.Group('songs', {
    load: true,
    parse: true
})

const closeButton = new layout.Button(
    {
        text: 'Close',

        onClick: () => layout.window.close()
    },
    {
        margin: '4px'
    }
)
const fileSelect = new layout.FileInput(
    {
        save: true,

        text: 'Export',
        button: 'Export',

        disabled: true,

        filters: [
            {
                name: 'JSON',
                extensions: ['json']
            }
        ]
    },
    {
        margin: '4px'
    }
)
const output = new layout.Text(
    {
        text: 'Loading Songs...'
    },
    {
        margin: '4px',
        align: 'center'
    }
)

layout.change(layout.body, {
    direction: 'vertical',
    padding: '8px',

    justify: 'space-between'
})

layout.body.add(output)
layout.body.add(
    new layout.Block(
        {
            items: [closeButton, fileSelect]
        },
        {
            direction: 'horizontal',
            grow: 0,
            shrink: 0,

            align: 'end'
        }
    )
)

function save(filename) {
    let data = ''

    try {
        data = JSON.stringify(Songs.list)
    } catch (error) {
        output.text =
            'Could not export songs: ' + (error.message || error.toString())

        logger.error('Unable to stringify songs', error)

        closeButton.disabled = false
        fileSelect.disabled = false

        return false
    }

    fs.writeFile(filename, data, 'utf8', error => {
        closeButton.disabled = false
        fileSelect.disabled = false

        if (error) {
            output.text =
                'Error exporting songs: ' + (error.message || error.toString())

            logger.error('Unable to export songs', error)

            return false
        }

        if (process.platform === 'darwin') {
            layout.window.close()
        } else {
            output.text = 'Exported'
        }
    })
}

fileSelect.onEvent('save', event => {
    if (event.filename) {
        closeButton.disabled = true
        fileSelect.disabled = true

        output.text = 'Exporting...'

        if (Songs.updating) {
            Songs.onceEvent('update', () => {
                save(event.filename)
            })
        } else {
            save(event.filename)
        }
    } else {
        layout.showNotification({
            type: 'error',

            message: 'No file selected!'
        })
    }
})

Songs.onEvent('update', () => {
    if (Songs.files.length > 0) {
        fileSelect.disabled = false

        output.text =
            Songs.files.length.toString() +
            ' song' +
            (Songs.files.length > 1 ? 's' : '') +
            ' in library'
    } else {
        fileSelect.disabled = true
        output.text = 'No songs in library'
    }
})

Songs.onEvent('error', error => {
    layout.dialog.showNotification({
        type: 'error',
        autoHide: false,

        message: 'There is an error with the Song database!\n' + error.message
    })
})
<|endoftext|>


<commit_before>var test = require('tape')
var walkdir = require('../walkdir.js')
var basename = require('path').basename

test('follow symlinks',function(t){

  var links = [],paths = [],failures = [],errors = [], files = [];

  var emitter = walkdir(__dirname+'/dir/nested-symlink',{follow_symlinks:true});

  emitter.on('path',function(path,stat){
    paths.push(path);
  });

  emitter.on('file',function(path,stat){
    files.push(path);
  });

  emitter.on('link',function(path,stat){
    links.push(path);
  });

  emitter.on('error',function(path,err){
    console.log('error!!', arguments);
    errors.push(arguments);
  });

  emitter.on('fail',function(path,err){
    failures.push(path);
  });

  emitter.on('end',function(){

    t.equal(files.length,1)
    t.equal(basename(files[0]),'found-me','found the nested symlink')
    t.equal(paths.length,3,'should find 3 things')

    t.ok(!failues.length,'no failures')

    t.end();

  });
})
<commit_msg>Fix typo in nested-symlink.js spec.<commit_after>var test = require('tape')
var walkdir = require('../walkdir.js')
var basename = require('path').basename

test('follow symlinks',function(t){

  var links = [],paths = [],failures = [],errors = [], files = [];

  var emitter = walkdir(__dirname+'/dir/nested-symlink',{follow_symlinks:true});

  emitter.on('path',function(path,stat){
    paths.push(path);
  });

  emitter.on('file',function(path,stat){
    files.push(path);
  });

  emitter.on('link',function(path,stat){
    links.push(path);
  });

  emitter.on('error',function(path,err){
    console.log('error!!', arguments);
    errors.push(arguments);
  });

  emitter.on('fail',function(path,err){
    failures.push(path);
  });

  emitter.on('end',function(){

    t.equal(files.length,1)
    t.equal(basename(files[0]),'found-me','found the nested symlink')
    t.equal(paths.length,3,'should find 3 things')

    t.ok(!failures.length,'no failures')

    t.end();

  });
})
<|endoftext|>


<commit_before>var m = require('mithril');
var contextMenu = require('../contextMenu');
var raf = require('chessground').util.requestAnimationFrame;
var util = require('../util');
var defined = util.defined;
var empty = util.empty;
var game = require('game').game;
var treePath = require('./path');

var makeAutoScroll = function(ctrl) {
  return util.throttle(300, false, function(el) {
    raf(function() {
      var target = el.querySelector('.active');
      var cont = el.parentNode;
      if (!target) {
        cont.scrollTop = ctrl.vm.path === ctrl.vm.initialPath ? 0 : 99999;
        return;
      }
      cont.scrollTop = target.offsetTop - cont.offsetHeight / 2 + target.offsetHeight;
    });
  });
}

function pathContains(ctx, path) {
  return treePath.contains(ctx.ctrl.vm.path, path);
}

function plyToTurn(ply) {
  return Math.floor((ply - 1) / 2) + 1;
}

function renderIndex(ply, withDots) {
  return {
    tag: 'index',
    children: [
      plyToTurn(ply) + (withDots ? (ply % 2 === 1 ? '.' : '...') : '')
    ]
  };
}

function renderChildrenOf(ctx, node, opts) {
  var cs = node.children;
  var main = cs[0];
  if (!main) return;
  var conceal = opts.noConceal ? null : (opts.conceal || ctx.concealOf(true)(opts.parentPath + main.id, main));
  if (conceal === 'hide') return;
  if (opts.isMainline) {
    var isWhite = main.ply % 2 === 1;
    var commentTags = renderMainlineCommentsOf(ctx, main, {
      conceal: conceal,
      withColor: true
    });
    if (!cs[1] && empty(commentTags)) return [
      isWhite ? renderIndex(main.ply, false) : null,
      renderMoveAndChildrenOf(ctx, main, {
        parentPath: opts.parentPath,
        isMainline: true,
        conceal: conceal
      })
    ];
    var mainChildren = renderChildrenOf(ctx, main, {
      parentPath: opts.parentPath + main.id,
      isMainline: true,
      conceal: conceal
    });
    var passOpts = {
      parentPath: opts.parentPath,
      isMainline: true,
      conceal: conceal
    };
    return [
      isWhite ? renderIndex(main.ply, false) : null,
      renderMoveOf(ctx, main, passOpts),
      isWhite ? emptyMove(passOpts) : null,
      m('interrupt', [
        commentTags,
        renderLines(ctx, cs.slice(1), {
          parentPath: opts.parentPath,
          isMainline: true,
          conceal: conceal,
          noConceal: !conceal
        })
      ]),
      isWhite && mainChildren ? [
        renderIndex(main.ply, false),
        emptyMove(passOpts)
      ] : null,
      mainChildren
    ];
  }
  if (!cs[1]) return renderMoveAndChildrenOf(ctx, main, opts);
  return renderLines(ctx, cs, opts);
}

function renderLines(ctx, nodes, opts) {
  return {
    tag: 'lines',
    attrs: {
      class: (nodes[1] ? '' : 'single') // + (opts.conceal ? ' ' + opts.conceal : '')
    },
    children: nodes.map(function(n) {
      return lineTag(renderMoveAndChildrenOf(ctx, n, {
        parentPath: opts.parentPath,
        isMainline: false,
        withIndex: true,
        noConceal: opts.noConceal
      }));
    })
  };
}

function lineTag(content) {
  return {
    tag: 'line',
    children: content
  };
}

function renderMoveOf(ctx, node, opts) {
  return opts.isMainline ? renderMainlineMoveOf(ctx, node, opts) : renderVariationMoveOf(ctx, node, opts);
}

function renderMainlineMoveOf(ctx, node, opts) {
  var path = opts.parentPath + node.id;
  var attrs = {
    p: path
  };
  var eval = node.eval || node.ceval || {};
  var classes = [];
  if (path === ctx.ctrl.vm.path) classes.push('active');
  if (path === ctx.ctrl.vm.contextMenuPath) classes.push('context_menu');
  if (path === ctx.ctrl.vm.initialPath && game.playable(ctx.ctrl.data)) classes.push('current');
  if (opts.conceal) classes.push(opts.conceal);
  if (classes.length) attrs.class = classes.join(' ');
  return moveTag(attrs, [
    util.fixCrazySan(node.san),
    node.glyphs ? renderGlyphs(node.glyphs) : null,
    defined(eval.cp) ? renderEval(util.renderEval(eval.cp)) : (
      defined(eval.mate) ? renderEval('#' + eval.mate) : null
    ),
  ]);
}

function renderVariationMoveOf(ctx, node, opts) {
  var withIndex = opts.withIndex || node.ply % 2 === 1;
  var path = opts.parentPath + node.id;
  var attrs = {
    p: path
  };
  var classes = [];
  if (path === ctx.ctrl.vm.path) classes.push('active');
  else if (pathContains(ctx, path)) classes.push('parent');
  if (path === ctx.ctrl.vm.contextMenuPath) classes.push('context_menu');
  if (opts.conceal) classes.push(ctx.conceal);
  if (classes.length) attrs.class = classes.join(' ');
  return moveTag(attrs, [
    withIndex ? renderIndex(node.ply, true) : null,
    util.fixCrazySan(node.san),
    node.glyphs ? renderGlyphs(node.glyphs) : null
  ]);
}

function renderMoveAndChildrenOf(ctx, node, opts) {
  return [
    renderMoveOf(ctx, node, opts),
    renderVariationCommentsOf(ctx, node),
    renderChildrenOf(ctx, node, {
      parentPath: opts.parentPath + node.id,
      isMainline: opts.isMainline,
      noConceal: opts.noConceal
    })
  ];
}

function moveTag(attrs, content) {
  return {
    tag: 'move',
    attrs: attrs,
    children: content
  };
}

function emptyMove(opts) {
  return moveTag({
    class: 'empty' + (opts.conceal ? ' ' + opts.conceal : '')
  }, '...');
}

function renderGlyphs(glyphs) {
  return glyphs.map(function(glyph) {
    return {
      tag: 'glyph',
      attrs: {
        title: glyph.name
      },
      children: [glyph.symbol]
    };
  });
}

function renderEval(e) {
  return {
    tag: 'eval',
    children: [e]
  };
}

function renderMainlineCommentsOf(ctx, node, opts) {
  if (!ctx.ctrl.vm.comments || empty(node.comments)) return null;
  var colorClass = opts.withColor ? (node.ply % 2 === 0 ? 'black ' : 'white ') : '';
  var klass;
  return node.comments.map(function(comment) {
    if (comment.text.indexOf('Inaccuracy.') === 0) klass = 'inaccuracy';
    else if (comment.text.indexOf('Mistake.') === 0) klass = 'mistake';
    else if (comment.text.indexOf('Blunder.') === 0) klass = 'blunder';
    if (opts.conceal) klass += ' ' + opts.conceal;
    return renderMainlineComment(comment, colorClass, klass);
  });
}

function renderMainlineComment(comment, colorClass, commentClass) {
  return {
    tag: 'comment',
    attrs: {
      class: colorClass + commentClass
    },
    children: [truncateComment(comment.text, 400)]
  };
}

function renderVariationCommentsOf(ctx, node) {
  if (!ctx.ctrl.vm.comments || empty(node.comments)) return null;
  return node.comments.map(function(comment) {
    return renderVariationComment(comment);
  });
}

function renderVariationComment(comment) {
  return {
    tag: 'comment',
    children: [truncateComment(comment.text, 300)]
  };
}

function truncateComment(text, len) {
  if (text.length <= len) return text;
  return text.slice(0, len - 10) + ' [...]';
}

function eventPath(e, ctrl) {
  return e.target.getAttribute('p') || e.target.parentNode.getAttribute('p');
}

module.exports = function(ctrl, conceal) {
  var root = ctrl.tree.root;
  var ctx = {
    ctrl: ctrl,
    concealOf: function(isMainline) {
      return function(path, node) {
        if (!conceal || (isMainline && conceal.ply >= node.ply)) return null;
        if (treePath.contains(ctrl.vm.path, path)) return null;
        return conceal.owner ? 'conceal' : 'hide'
      };
    }
  };
  var commentTags = renderMainlineCommentsOf(ctx, root, {
    withColor: false,
    conceal: false
  });
  var autoScroll = makeAutoScroll(ctrl);
  return m('div.tview2', {
    onmousedown: function(e) {
      if (e.button !== undefined && e.button !== 0) return; // only touch or left click
      var path = eventPath(e, ctrl);
      if (path) ctrl.userJump(path);
    },
    config: function(el, isUpdate) {
      if (ctrl.vm.autoScrollRequested || !isUpdate) {
        autoScroll(el);
        ctrl.vm.autoScrollRequested = false;
      }
    },
    oncontextmenu: function(e) {
      var path = eventPath(e, ctrl);
      contextMenu.open(e, {
        path: path,
        root: ctrl
      });
      return false;
    },
  }, [
    commentTags ? m('interrupt', commentTags) : null,
    root.ply % 2 === 1 ? [
      renderIndex(root.ply, false),
      emptyMove({})
    ] : null,
    renderChildrenOf(ctx, root, {
      parentPath: '',
      isMainline: true
    })
  ]);
};
<commit_msg>tree view autoscroll performance<commit_after>var m = require('mithril');
var contextMenu = require('../contextMenu');
var raf = require('chessground').util.requestAnimationFrame;
var util = require('../util');
var defined = util.defined;
var empty = util.empty;
var game = require('game').game;
var treePath = require('./path');

var autoScroll = util.throttle(300, false, function(ctrl, el) {
  raf(function() {
    var target = el.querySelector('.active');
    var cont = el.parentNode;
    if (!target) {
      cont.scrollTop = ctrl.vm.path === ctrl.vm.initialPath ? 0 : 99999;
      return;
    }
    cont.scrollTop = target.offsetTop - cont.offsetHeight / 2 + target.offsetHeight;
  });
});

function pathContains(ctx, path) {
  return treePath.contains(ctx.ctrl.vm.path, path);
}

function plyToTurn(ply) {
  return Math.floor((ply - 1) / 2) + 1;
}

function renderIndex(ply, withDots) {
  return {
    tag: 'index',
    children: [
      plyToTurn(ply) + (withDots ? (ply % 2 === 1 ? '.' : '...') : '')
    ]
  };
}

function renderChildrenOf(ctx, node, opts) {
  var cs = node.children;
  var main = cs[0];
  if (!main) return;
  var conceal = opts.noConceal ? null : (opts.conceal || ctx.concealOf(true)(opts.parentPath + main.id, main));
  if (conceal === 'hide') return;
  if (opts.isMainline) {
    var isWhite = main.ply % 2 === 1;
    var commentTags = renderMainlineCommentsOf(ctx, main, {
      conceal: conceal,
      withColor: true
    });
    if (!cs[1] && empty(commentTags)) return [
      isWhite ? renderIndex(main.ply, false) : null,
      renderMoveAndChildrenOf(ctx, main, {
        parentPath: opts.parentPath,
        isMainline: true,
        conceal: conceal
      })
    ];
    var mainChildren = renderChildrenOf(ctx, main, {
      parentPath: opts.parentPath + main.id,
      isMainline: true,
      conceal: conceal
    });
    var passOpts = {
      parentPath: opts.parentPath,
      isMainline: true,
      conceal: conceal
    };
    return [
      isWhite ? renderIndex(main.ply, false) : null,
      renderMoveOf(ctx, main, passOpts),
      isWhite ? emptyMove(passOpts) : null,
      m('interrupt', [
        commentTags,
        renderLines(ctx, cs.slice(1), {
          parentPath: opts.parentPath,
          isMainline: true,
          conceal: conceal,
          noConceal: !conceal
        })
      ]),
      isWhite && mainChildren ? [
        renderIndex(main.ply, false),
        emptyMove(passOpts)
      ] : null,
      mainChildren
    ];
  }
  if (!cs[1]) return renderMoveAndChildrenOf(ctx, main, opts);
  return renderLines(ctx, cs, opts);
}

function renderLines(ctx, nodes, opts) {
  return {
    tag: 'lines',
    attrs: {
      class: (nodes[1] ? '' : 'single') // + (opts.conceal ? ' ' + opts.conceal : '')
    },
    children: nodes.map(function(n) {
      return lineTag(renderMoveAndChildrenOf(ctx, n, {
        parentPath: opts.parentPath,
        isMainline: false,
        withIndex: true,
        noConceal: opts.noConceal
      }));
    })
  };
}

function lineTag(content) {
  return {
    tag: 'line',
    children: content
  };
}

function renderMoveOf(ctx, node, opts) {
  return opts.isMainline ? renderMainlineMoveOf(ctx, node, opts) : renderVariationMoveOf(ctx, node, opts);
}

function renderMainlineMoveOf(ctx, node, opts) {
  var path = opts.parentPath + node.id;
  var attrs = {
    p: path
  };
  var eval = node.eval || node.ceval || {};
  var classes = [];
  if (path === ctx.ctrl.vm.path) classes.push('active');
  if (path === ctx.ctrl.vm.contextMenuPath) classes.push('context_menu');
  if (path === ctx.ctrl.vm.initialPath && game.playable(ctx.ctrl.data)) classes.push('current');
  if (opts.conceal) classes.push(opts.conceal);
  if (classes.length) attrs.class = classes.join(' ');
  return moveTag(attrs, [
    util.fixCrazySan(node.san),
    node.glyphs ? renderGlyphs(node.glyphs) : null,
    defined(eval.cp) ? renderEval(util.renderEval(eval.cp)) : (
      defined(eval.mate) ? renderEval('#' + eval.mate) : null
    ),
  ]);
}

function renderVariationMoveOf(ctx, node, opts) {
  var withIndex = opts.withIndex || node.ply % 2 === 1;
  var path = opts.parentPath + node.id;
  var attrs = {
    p: path
  };
  var classes = [];
  if (path === ctx.ctrl.vm.path) classes.push('active');
  else if (pathContains(ctx, path)) classes.push('parent');
  if (path === ctx.ctrl.vm.contextMenuPath) classes.push('context_menu');
  if (opts.conceal) classes.push(ctx.conceal);
  if (classes.length) attrs.class = classes.join(' ');
  return moveTag(attrs, [
    withIndex ? renderIndex(node.ply, true) : null,
    util.fixCrazySan(node.san),
    node.glyphs ? renderGlyphs(node.glyphs) : null
  ]);
}

function renderMoveAndChildrenOf(ctx, node, opts) {
  return [
    renderMoveOf(ctx, node, opts),
    renderVariationCommentsOf(ctx, node),
    renderChildrenOf(ctx, node, {
      parentPath: opts.parentPath + node.id,
      isMainline: opts.isMainline,
      noConceal: opts.noConceal
    })
  ];
}

function moveTag(attrs, content) {
  return {
    tag: 'move',
    attrs: attrs,
    children: content
  };
}

function emptyMove(opts) {
  return moveTag({
    class: 'empty' + (opts.conceal ? ' ' + opts.conceal : '')
  }, '...');
}

function renderGlyphs(glyphs) {
  return glyphs.map(function(glyph) {
    return {
      tag: 'glyph',
      attrs: {
        title: glyph.name
      },
      children: [glyph.symbol]
    };
  });
}

function renderEval(e) {
  return {
    tag: 'eval',
    children: [e]
  };
}

function renderMainlineCommentsOf(ctx, node, opts) {
  if (!ctx.ctrl.vm.comments || empty(node.comments)) return null;
  var colorClass = opts.withColor ? (node.ply % 2 === 0 ? 'black ' : 'white ') : '';
  var klass;
  return node.comments.map(function(comment) {
    if (comment.text.indexOf('Inaccuracy.') === 0) klass = 'inaccuracy';
    else if (comment.text.indexOf('Mistake.') === 0) klass = 'mistake';
    else if (comment.text.indexOf('Blunder.') === 0) klass = 'blunder';
    if (opts.conceal) klass += ' ' + opts.conceal;
    return renderMainlineComment(comment, colorClass, klass);
  });
}

function renderMainlineComment(comment, colorClass, commentClass) {
  return {
    tag: 'comment',
    attrs: {
      class: colorClass + commentClass
    },
    children: [truncateComment(comment.text, 400)]
  };
}

function renderVariationCommentsOf(ctx, node) {
  if (!ctx.ctrl.vm.comments || empty(node.comments)) return null;
  return node.comments.map(function(comment) {
    return renderVariationComment(comment);
  });
}

function renderVariationComment(comment) {
  return {
    tag: 'comment',
    children: [truncateComment(comment.text, 300)]
  };
}

function truncateComment(text, len) {
  if (text.length <= len) return text;
  return text.slice(0, len - 10) + ' [...]';
}

function eventPath(e, ctrl) {
  return e.target.getAttribute('p') || e.target.parentNode.getAttribute('p');
}

module.exports = function(ctrl, conceal) {
  var root = ctrl.tree.root;
  var ctx = {
    ctrl: ctrl,
    concealOf: function(isMainline) {
      return function(path, node) {
        if (!conceal || (isMainline && conceal.ply >= node.ply)) return null;
        if (treePath.contains(ctrl.vm.path, path)) return null;
        return conceal.owner ? 'conceal' : 'hide'
      };
    }
  };
  var commentTags = renderMainlineCommentsOf(ctx, root, {
    withColor: false,
    conceal: false
  });
  return m('div.tview2', {
    onmousedown: function(e) {
      if (e.button !== undefined && e.button !== 0) return; // only touch or left click
      var path = eventPath(e, ctrl);
      if (path) ctrl.userJump(path);
    },
    config: function(el, isUpdate) {
      if (ctrl.vm.autoScrollRequested || !isUpdate) {
        autoScroll(ctrl, el);
        ctrl.vm.autoScrollRequested = false;
      }
    },
    oncontextmenu: function(e) {
      var path = eventPath(e, ctrl);
      contextMenu.open(e, {
        path: path,
        root: ctrl
      });
      return false;
    },
  }, [
    commentTags ? m('interrupt', commentTags) : null,
    root.ply % 2 === 1 ? [
      renderIndex(root.ply, false),
      emptyMove({})
    ] : null,
    renderChildrenOf(ctx, root, {
      parentPath: '',
      isMainline: true
    })
  ]);
};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
    'use strict';

    // show elapsed time at the end
    require('time-grunt')(grunt);

    // load all grunt tasks
    require('load-grunt-tasks')(grunt);

    grunt.initConfig({
        clean: {
            components: ['_build'],
        },

        connect: {
            server: {
                options: {
                    port: 4000,
                    base: '_build/html'
                }
            }
        },

        shell: {
            make: {
                options: {
                    stdout: true
                },

                command: 'make html'
            }
        },

        watch: {
            options: {
                spawn: false,
                livereload: true,
            },

            rst: {
                files: ['*.rst'],
                tasks: ['shell'],
            }
        }
    });

    grunt.registerTask('default', [
        'clean',
        'shell',
        'connect',
        'watch'
    ]);
};
<commit_msg>ensuring docs are cleared at every build<commit_after>module.exports = function (grunt) {
    'use strict';

    // show elapsed time at the end
    require('time-grunt')(grunt);

    // load all grunt tasks
    require('load-grunt-tasks')(grunt);

    grunt.initConfig({
        clean: {
            components: ['_build'],
        },

        connect: {
            server: {
                options: {
                    port: 4000,
                    base: '_build/html'
                }
            }
        },

        shell: {
            make: {
                options: {
                    stdout: true
                },

                command: 'make html'
            }
        },

        watch: {
            options: {
                spawn: false,
                livereload: true,
            },

            rst: {
                files: ['*.rst'],
                tasks: ['clean', 'shell'],
            }
        }
    });

    grunt.registerTask('default', [
        'clean',
        'shell',
        'connect',
        'watch'
    ]);
};
<|endoftext|>


<commit_before>/*
INSTRUCTIONS
Copy/paste this whole section into console & hit enter. It should track gather rates (%) for Gathering, Fishing,
Mining and Woodcutting. It will print the results after every gather; please note that this will never give
exact values, and you should probably wait for 300-500+ attempts on a tile to draw conclusions.

To reset the values (necessary if you move tiles) you can copy/paste the whole script again or just enter
this into the console:  "clearTSResults();".

This *should* be compatible with Ryalane's script if Ryalane's script loads first.
TODO:
	total amount of resources gained
	taxes
//*/
// preferences; data is still tracked, this only affects output
var outputItems = true;
var outputTaxes = true;
var outputTiers = true;
var outputQuests = true;
var outputScouting = true;
var printItemDrops = true;
var outputToConsole = false;
var saveLogText = false;

var logText = [];
var questItemRegex = /(\d+) \/ (\d+)/;
var resourceListId = 'resourceLogList';
// results aren't stored under the resource because we aren't tracking tile changes/types
var tsResults = {
	actions: 0,
	taxedActions: 0,
	items: 0,
	regularItems: 0,
	scoutingItems: 0,
	questActive: false,
	questActions: 0,
	questItems: 0,
	1: 0,
	2: 0,
	3: 0,
	4: 0,
	5: 0,
	lumber:{
		items: ['Pine', 'Oak', 'Maple', 'Ironwood', 'Yggdrasil']
	},
	ore: {
		items: ['Iron', 'Silver', 'Obsidian', 'Mythril', 'Ethernium']
	},
	plant: {
		items: ['Plant Stem', 'Cotton', 'Living Leather', 'Silver Vine', 'Nimbus Fruit']
	},
	fish: {
		items: ['Tuna', 'Salmon', 'Flyfish', 'Marlin', 'Dragonfish']
	},
	itemInfo: {
		equipDrop: 0,
		resourceBagDrop: 0,
		keyDrop: 0,
		magicElementsDrop: 0,
		magicElementsTotal: 0,
		goldDrop: 0,
		goldTotal: 0,
		gemDrop: 0,
		relicDrop: 0,
		relicTotal: 0
	}
};
function clearTSResults() {
	tsResults.actions = 0;
	tsResults.items = 0;
	tsResults.regularItems = 0;
	tsResults.scoutingItems = 0;
	tsResults.questActive = false;
	tsResults.questActions = 0;
	tsResults.questItems = 0;
	tsResults.taxedActions = 0;
	tsResults.itemInfo = {
		equipDrop: 0,
		resourceBagDrop: 0,
		keyDrop: 0,
		magicElementsDrop: 0,
		magicElementsTotal: 0,
		goldDrop: 0,
		goldTotal: 0,
		gemDrop: 0,
		relicDrop: 0,
		relicTotal: 0
	};

	for(var i=1, iLen=6; i<iLen;i++) {
		tsResults[i] = 0;
	}
}
function handleQuestItem(msg) {
	var matches = msg.match(questItemRegex);
	var current = parseInt(matches[1]);
	var total = parseInt(matches[2]);

	if(current == total) {
		// this message is sent after the channel2 message, so we won't lose an action here
		tsResults.questActive = false;
	} else {
		tsResults.questActive = true;
		tsResults.questItems += 1;
	}
}
function handleItemDrop(msg) {
	if(printItemDrops) console.info(Date(), msg);

	// TODO better method than this?
	if(msg.indexOf('a resource bag') >= 0) tsResults.itemInfo.resourceBagDrop += 1;
	else if(msg.indexOf('a key') >= 0) tsResults.itemInfo.keyDrop += 1;
	else if(msg.indexOf('a gem') >= 0) tsResults.itemInfo.gemDrop += 1;

	// these will eventually be broken out by tier as well
	else if(msg.indexOf('Found Broken') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Basic') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Fine') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Elite') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Master') >= 0) tsResults.itemInfo.equipDrop += 1;
	// equips else if(msg.indexOf('a key') >= 0) tsResults.itemInfo.keyDrop += 1;
	else if(msg.indexOf('a relic') >= 0) {
		tsResults.itemInfo.relicDrop += 1;
		tsResults.itemInfo.relicTotal += 1;
	} else if(msg.indexOf('relics') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.relicDrop += 1;
		tsResults.itemInfo.relicTotal += count;
	} else if(msg.indexOf('gold coins') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.goldDrop += 1;
		tsResults.itemInfo.goldTotal += count;
	} else if(msg.indexOf('magic elements') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.magicElementsDrop += 1;
		tsResults.itemInfo.magicElementsTotal += count;
	}
}
function parseTSLog(datum) {
	var arr = datum.split('|');
	if (arr[0] != 'NLOG') {return;}

	var channel = arr[1];
	var msg = arr[2];

	// save all lines of text if requested
	if(saveLogText) {
		logText.push(msg);
	}

	// track relic, item, gem, gold drops
	if(channel == 3) {
		// track quest drops separately
		if(msg.indexOf('quest') > 0) return handleQuestItem(msg);
		// scouting vs normal
		if(msg.indexOf('*') > -1) tsResults.scoutingItems += 1;
		else tsResults.regularItems += 1;

		// track the total in addition to ts/scout
		tsResults.items += 1;
		return handleItemDrop(msg);
	} else if(channel == 2) {
		// skip level up message before counting the action
		if(msg.indexOf('gained a new tradeskill level') > -1) return;

		tsResults.actions += 1;
		if(tsResults.questActive) tsResults.questActions += 1;
		if(msg.indexOf('to taxes') > -1) tsResults.taxedActions += 1;

		// which array should be used?
		var itemTypes = null;
		if(msg.indexOf('You cut') >= 0) {
			itemTypes = tsResults.lumber.items;
		} else if(msg.indexOf('You mined') >= 0) {
			itemTypes = tsResults.ore.items;
		} else if(msg.indexOf('You gathered') >= 0) {
			itemTypes = tsResults.plant.items;
		} else if(msg.indexOf('You caught') >= 0) {
			itemTypes = tsResults.fish.items;
		} else {
			itemTypes = [];
		}

		for(var i=0,iLen=itemTypes.length;i<iLen;i++) {
			if(msg.indexOf(itemTypes[i]) >= 0) {
				tsResults[i+1] += 1;
			}
		}

		updateOutput(tsResults, msg);
	}
}
function addTierInfo(tsResults, outputArgs) {
	return outputArgs.concat([
		't1%:', (100*tsResults[1]/tsResults.actions).toFixed(2),
		't2%:', (100*tsResults[2]/tsResults.actions).toFixed(2),
		't3%:', (100*tsResults[3]/tsResults.actions).toFixed(2),
		't4%:', (100*tsResults[4]/tsResults.actions).toFixed(2),
		't5%:', (100*tsResults[5]/tsResults.actions).toFixed(2)],
		' ', ' ');
}
function addScoutingInfo(tsResults, outputArgs) {
	return outputArgs.concat([
		'regularItem%:', (100*tsResults.regularItems/tsResults.actions).toFixed(2),
		'scoutItem%:', (100*tsResults.scoutingItems/tsResults.actions).toFixed(2)]);
}
function addItemOutput(tsResults, outputArgs) {
	return outputArgs.concat([
		' ', ' ',
		'equip%:', (100*tsResults.itemInfo.equipDrop/tsResults.actions).toFixed(4),
		'res. bag%:', (100*tsResults.itemInfo.resourceBagDrop/tsResults.actions).toFixed(4),
		'key%:', (100*tsResults.itemInfo.keyDrop/tsResults.actions).toFixed(4),
		'gem%:', (100*tsResults.itemInfo.gemDrop/tsResults.actions).toFixed(4),
		'ME%:', (100*tsResults.itemInfo.magicElementsDrop/tsResults.actions).toFixed(4),
		'gold%:', (100*tsResults.itemInfo.goldDrop/tsResults.actions).toFixed(4),
		'relic%:', (100*tsResults.itemInfo.relicDrop/tsResults.actions).toFixed(4),

		' ', ' ',
		'avg ME:', (tsResults.itemInfo.magicElementsTotal/tsResults.itemInfo.magicElementsDrop),
		'avg Gold:', (tsResults.itemInfo.goldTotal/tsResults.itemInfo.goldDrop),
		'avg Relics:', (tsResults.itemInfo.relicTotal/tsResults.itemInfo.relicDrop),

		' ', ' ',
		'Total ME:', (tsResults.itemInfo.magicElementsTotal/tsResults.itemInfo.magicElementsDrop).toFixed(2),
		'Total Gold:', (tsResults.itemInfo.goldTotal/tsResults.itemInfo.goldDrop).toFixed(2),
		'Total Relics:', (tsResults.itemInfo.relicTotal/tsResults.itemInfo.relicDrop).toFixed(2)],
		' ', ' ');
}
function updateOutput(results, msg) {
	var outputArgs = ['actions:', tsResults.actions, ' ', ' '];
	if(outputTiers) outputArgs = addTierInfo(tsResults, outputArgs);
	outputArgs = outputArgs.concat(['item%:', (100*tsResults.items/tsResults.actions).toFixed(2)]);
	if(outputScouting) outputArgs = addScoutingInfo(tsResults, outputArgs);
	if(outputItems) outputArgs = addItemOutput(tsResults, outputArgs);
	if(outputQuests)
		outputArgs = outputArgs.concat(['quest%:', (100*tsResults.questItems/tsResults.questActions).toFixed(2)]);
	if(outputTaxes)
		outputArgs = outputArgs.concat(['tax%:', (100*tsResults.taxedActions/tsResults.actions).toFixed(2)]);

	outputArgs = outputArgs.concat(['\tmsg:', msg]);

	if(outputToConsole) console.info.apply(console, outputArgs);

	// skip posting the message to the UI
	outputArgs.pop();
	outputArgs.pop();
	updateUI(outputArgs);
}
function formatResource(label, value) {
	return '

' + label + ' ' + value + '

';
}
function updateUI(outputArgs) {
	$('#'+resourceListId).empty();
	for(var i=0,iLen=outputArgs.length;i<iLen;i+=2) {
		$('#'+resourceListId).append(formatResource(outputArgs[i], outputArgs[i+1]));
	}
}
function initializeUI() {
	jQuery('
', {
		id: 'resourceLogContainer',
		text: 'Resource Log',
		style: 'float: right;'
	}).prependTo('body');
	jQuery('
', {
		id: resourceListId,
	}).appendTo('div#resourceLogContainer');
}
function track_resources_onmsg(evt) {
	track_resources_original_msg(evt);
	parseTSLog(evt.data);
}
// set up handler & hook original game handler in
if(typeof track_resources_original_msg === 'undefined') {
	console.info('resource tracker not yet loaded, loading...');
	var track_resources_original_msg = ws.onmessage;
	ws.onmessage=track_resources_onmsg;
	initializeUI();
} else {
	ws.onmessage=track_resources_onmsg;
}
<commit_msg>added userscript metadata. fixed decimals of averages and made totals actually totals.<commit_after>// ==UserScript==
// @name MQO Resource Tracker
// @namespace https://github.com/trigunshin/miden_quest
// @description MQO resource tracker; need to run clearTSResults() to reset tile% after moving
// @homepage https://trigunshin.github.com/miden_quest
// @version 1
// @downloadURL http://trigunshin.github.io/miden_quest/track_resources.js
// @updateURL http://trigunshin.github.io/miden_quest/track_resources.js
// @include http://www.midenquest.com/Game.aspx
// @include https://www.midenquest.com/Game.aspx
// ==/UserScript==

/*
INSTRUCTIONS
Copy/paste this whole section into console & hit enter. It should track gather rates (%) for Gathering, Fishing,
Mining and Woodcutting. It will print the results after every gather; please note that this will never give
exact values, and you should probably wait for 300-500+ attempts on a tile to draw conclusions.

To reset the values (necessary if you move tiles) you can copy/paste the whole script again or just enter
this into the console:  "clearTSResults();".

This *should* be compatible with Ryalane's script if Ryalane's script loads first.
TODO:
	total amount of resources gained
	taxes
//*/
// preferences; data is still tracked, this only affects output
var outputItems = true;
var outputTaxes = true;
var outputTiers = true;
var outputQuests = true;
var outputScouting = true;
var printItemDrops = true;
var outputToConsole = false;
var saveLogText = false;

var logText = [];
var questItemRegex = /(\d+) \/ (\d+)/;
var resourceListId = 'resourceLogList';
// results aren't stored under the resource because we aren't tracking tile changes/types
var tsResults = {
	actions: 0,
	taxedActions: 0,
	items: 0,
	regularItems: 0,
	scoutingItems: 0,
	questActive: false,
	questActions: 0,
	questItems: 0,
	1: 0,
	2: 0,
	3: 0,
	4: 0,
	5: 0,
	lumber:{
		items: ['Pine', 'Oak', 'Maple', 'Ironwood', 'Yggdrasil']
	},
	ore: {
		items: ['Iron', 'Silver', 'Obsidian', 'Mythril', 'Ethernium']
	},
	plant: {
		items: ['Plant Stem', 'Cotton', 'Living Leather', 'Silver Vine', 'Nimbus Fruit']
	},
	fish: {
		items: ['Tuna', 'Salmon', 'Flyfish', 'Marlin', 'Dragonfish']
	},
	itemInfo: {
		equipDrop: 0,
		resourceBagDrop: 0,
		keyDrop: 0,
		magicElementsDrop: 0,
		magicElementsTotal: 0,
		goldDrop: 0,
		goldTotal: 0,
		gemDrop: 0,
		relicDrop: 0,
		relicTotal: 0
	}
};
function clearTSResults() {
	tsResults.actions = 0;
	tsResults.items = 0;
	tsResults.regularItems = 0;
	tsResults.scoutingItems = 0;
	tsResults.questActive = false;
	tsResults.questActions = 0;
	tsResults.questItems = 0;
	tsResults.taxedActions = 0;
	tsResults.itemInfo = {
		equipDrop: 0,
		resourceBagDrop: 0,
		keyDrop: 0,
		magicElementsDrop: 0,
		magicElementsTotal: 0,
		goldDrop: 0,
		goldTotal: 0,
		gemDrop: 0,
		relicDrop: 0,
		relicTotal: 0
	};

	for(var i=1, iLen=6; i<iLen;i++) {
		tsResults[i] = 0;
	}
}
function handleQuestItem(msg) {
	var matches = msg.match(questItemRegex);
	var current = parseInt(matches[1]);
	var total = parseInt(matches[2]);

	if(current == total) {
		// this message is sent after the channel2 message, so we won't lose an action here
		tsResults.questActive = false;
	} else {
		tsResults.questActive = true;
		tsResults.questItems += 1;
	}
}
function handleItemDrop(msg) {
	if(printItemDrops) console.info(Date(), msg);

	// TODO better method than this?
	if(msg.indexOf('a resource bag') >= 0) tsResults.itemInfo.resourceBagDrop += 1;
	else if(msg.indexOf('a key') >= 0) tsResults.itemInfo.keyDrop += 1;
	else if(msg.indexOf('a gem') >= 0) tsResults.itemInfo.gemDrop += 1;

	// these will eventually be broken out by tier as well
	else if(msg.indexOf('Found Broken') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Basic') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Fine') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Elite') >= 0) tsResults.itemInfo.equipDrop += 1;
	else if(msg.indexOf('Found Master') >= 0) tsResults.itemInfo.equipDrop += 1;
	// equips else if(msg.indexOf('a key') >= 0) tsResults.itemInfo.keyDrop += 1;
	else if(msg.indexOf('a relic') >= 0) {
		tsResults.itemInfo.relicDrop += 1;
		tsResults.itemInfo.relicTotal += 1;
	} else if(msg.indexOf('relics') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.relicDrop += 1;
		tsResults.itemInfo.relicTotal += count;
	} else if(msg.indexOf('gold coins') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.goldDrop += 1;
		tsResults.itemInfo.goldTotal += count;
	} else if(msg.indexOf('magic elements') >= 0) {
		var count = parseInt(msg.match(/(\d)+/)[0]);
		tsResults.itemInfo.magicElementsDrop += 1;
		tsResults.itemInfo.magicElementsTotal += count;
	}
}
function parseTSLog(datum) {
	var arr = datum.split('|');
	if (arr[0] != 'NLOG') {return;}

	var channel = arr[1];
	var msg = arr[2];

	// save all lines of text if requested
	if(saveLogText) {
		logText.push(msg);
	}

	// track relic, item, gem, gold drops
	if(channel == 3) {
		// track quest drops separately
		if(msg.indexOf('quest') > 0) return handleQuestItem(msg);
		// scouting vs normal
		if(msg.indexOf('*') > -1) tsResults.scoutingItems += 1;
		else tsResults.regularItems += 1;

		// track the total in addition to ts/scout
		tsResults.items += 1;
		return handleItemDrop(msg);
	} else if(channel == 2) {
		// skip level up message before counting the action
		if(msg.indexOf('gained a new tradeskill level') > -1) return;

		tsResults.actions += 1;
		if(tsResults.questActive) tsResults.questActions += 1;
		if(msg.indexOf('to taxes') > -1) tsResults.taxedActions += 1;

		// which array should be used?
		var itemTypes = null;
		if(msg.indexOf('You cut') >= 0) {
			itemTypes = tsResults.lumber.items;
		} else if(msg.indexOf('You mined') >= 0) {
			itemTypes = tsResults.ore.items;
		} else if(msg.indexOf('You gathered') >= 0) {
			itemTypes = tsResults.plant.items;
		} else if(msg.indexOf('You caught') >= 0) {
			itemTypes = tsResults.fish.items;
		} else {
			itemTypes = [];
		}

		for(var i=0,iLen=itemTypes.length;i<iLen;i++) {
			if(msg.indexOf(itemTypes[i]) >= 0) {
				tsResults[i+1] += 1;
			}
		}

		updateOutput(tsResults, msg);
	}
}
function addTierInfo(tsResults, outputArgs) {
	return outputArgs.concat([
		't1%:', (100*tsResults[1]/tsResults.actions).toFixed(2),
		't2%:', (100*tsResults[2]/tsResults.actions).toFixed(2),
		't3%:', (100*tsResults[3]/tsResults.actions).toFixed(2),
		't4%:', (100*tsResults[4]/tsResults.actions).toFixed(2),
		't5%:', (100*tsResults[5]/tsResults.actions).toFixed(2)],
		' ', ' ');
}
function addScoutingInfo(tsResults, outputArgs) {
	return outputArgs.concat([
		'regularItem%:', (100*tsResults.regularItems/tsResults.actions).toFixed(2),
		'scoutItem%:', (100*tsResults.scoutingItems/tsResults.actions).toFixed(2)]);
}
function addItemOutput(tsResults, outputArgs) {
	return outputArgs.concat([
		' ', ' ',
		'equip%:', (100*tsResults.itemInfo.equipDrop/tsResults.actions).toFixed(4),
		'res. bag%:', (100*tsResults.itemInfo.resourceBagDrop/tsResults.actions).toFixed(4),
		'key%:', (100*tsResults.itemInfo.keyDrop/tsResults.actions).toFixed(4),
		'gem%:', (100*tsResults.itemInfo.gemDrop/tsResults.actions).toFixed(4),
		'ME%:', (100*tsResults.itemInfo.magicElementsDrop/tsResults.actions).toFixed(4),
		'gold%:', (100*tsResults.itemInfo.goldDrop/tsResults.actions).toFixed(4),
		'relic%:', (100*tsResults.itemInfo.relicDrop/tsResults.actions).toFixed(4),

		' ', ' ',
		'avg ME:', (tsResults.itemInfo.magicElementsTotal/tsResults.itemInfo.magicElementsDrop).toFixed(2),
		'avg Gold:', (tsResults.itemInfo.goldTotal/tsResults.itemInfo.goldDrop).toFixed(2),
		'avg Relics:', (tsResults.itemInfo.relicTotal/tsResults.itemInfo.relicDrop).toFixed(2),

		' ', ' ',
		'Total ME:', tsResults.itemInfo.magicElementsTotal,
		'Total Gold:', tsResults.itemInfo.goldTotal,
		'Total Relics:', tsResults.itemInfo.relicTotal,
		' ', ' ']);
}
function updateOutput(results, msg) {
	var outputArgs = ['actions:', tsResults.actions, ' ', ' '];
	if(outputTiers) outputArgs = addTierInfo(tsResults, outputArgs);
	outputArgs = outputArgs.concat(['item%:', (100*tsResults.items/tsResults.actions).toFixed(2)]);
	if(outputScouting) outputArgs = addScoutingInfo(tsResults, outputArgs);
	if(outputItems) outputArgs = addItemOutput(tsResults, outputArgs);
	if(outputQuests)
		outputArgs = outputArgs.concat(['quest%:', (100*tsResults.questItems/tsResults.questActions).toFixed(2)]);
	if(outputTaxes)
		outputArgs = outputArgs.concat(['tax%:', (100*tsResults.taxedActions/tsResults.actions).toFixed(2)]);

	outputArgs = outputArgs.concat(['\tmsg:', msg]);

	if(outputToConsole) console.info.apply(console, outputArgs);

	// skip posting the message to the UI
	outputArgs.pop();
	outputArgs.pop();
	updateUI(outputArgs);
}
function formatResource(label, value) {
	return '

' + label + ' ' + value + '

';
}
function updateUI(outputArgs) {
	$('#'+resourceListId).empty();
	for(var i=0,iLen=outputArgs.length;i<iLen;i+=2) {
		$('#'+resourceListId).append(formatResource(outputArgs[i], outputArgs[i+1]));
	}
}
function initializeUI() {
	jQuery('
', {
		id: 'resourceLogContainer',
		text: 'Resource Log',
		style: 'float: right;'
	}).prependTo('body');
	jQuery('
', {
		id: resourceListId,
	}).appendTo('div#resourceLogContainer');
}
function track_resources_onmsg(evt) {
	track_resources_original_msg(evt);
	parseTSLog(evt.data);
}
// set up handler & hook original game handler in
if(typeof track_resources_original_msg === 'undefined') {
	console.info('resource tracker not yet loaded, loading...');
	var track_resources_original_msg = ws.onmessage;
	ws.onmessage=track_resources_onmsg;
	initializeUI();
} else {
	ws.onmessage=track_resources_onmsg;
}
<|endoftext|>


<commit_before>// Hot loading HRM Patch
import "react-hot-loader/patch"

// fetch polyfill
import "whatwg-fetch"

import metadata from "../src/metadata.js"
import routes from "../src/routes.js"
import store from "../src/store.js"
import phenomicClient from "phenomic/lib/client"
phenomicClient({ metadata, routes, store })

// md files processed via phenomic-loader to JSON && generate collection
const mdContext = require.context("../content", true, /\.md$/)
mdContext.keys().forEach(mdContext)

// hot loading
if (module.hot) {

  // hot load md
  const mdHotUpdater = require("phenomic/lib/client/hot-md").default
  module.hot.accept(mdContext.id, () => {
    // mdContext = require.context("../content", true, /\.md$/)
    const requireUpdate = mdHotUpdater(mdContext, window.__COLLECTION__, store)
    mdContext.keys().forEach(requireUpdate)
  })

  module.hot.accept(
    [ "../src/metadata.js", "../src/routes.js", "../src/store.js" ],
    () => phenomicClient({ metadata, routes, store })
  )
}
<commit_msg>Fix hot loading for base theme<commit_after>// Hot loading HRM Patch
import "react-hot-loader/patch"

// fetch polyfill
import "whatwg-fetch"

import metadata from "../src/metadata.js"
import routes from "../src/routes.js"
import store from "../src/store.js"
import phenomicClient from "phenomic/lib/client"
phenomicClient({ metadata, routes, store })

// md files processed via phenomic-loader to JSON & generate collection
let mdContext = require.context("../content", true, /\.md$/)
mdContext.keys().forEach(mdContext)

// hot loading
if (module.hot) {

  // hot load md
  module.hot.accept(mdContext.id, () => {
    mdContext = require.context("../content", true, /\.md$/)
    const mdHotUpdater = require("phenomic/lib/client/hot-md").default
    const requireUpdate = mdHotUpdater(mdContext, window.__COLLECTION__, store)
    mdContext.keys().forEach(requireUpdate)
  })

  // hot load app
  module.hot.accept(
    [ "../src/metadata.js", "../src/routes.js", "../src/store.js" ],
    () => phenomicClient({ metadata, routes, store })
  )
}
<|endoftext|>


<commit_before>// @flow
import type {JobType} from './types'

import {
  fastGetTrimmedText,
  removeHtmlWithRegex,
} from '../../../lib/html'

export function cleanJob(job: JobType): JobType {
  const title = fastGetTrimmedText(job.title)
  const office = fastGetTrimmedText(job.office)
  const type = fastGetTrimmedText(job.type)

  // these all need to retain their newlines
  const description = removeHtmlWithRegex(job.description).replace('\t', ' ')
  const comments = removeHtmlWithRegex(job.comments).replace('\t', ' ')
  const skills = removeHtmlWithRegex(job.skills).replace('\t', ' ')

  const hoursPerWeek = job.hoursPerWeek.trim()
  const lastModified = job.lastModified.trim()
  const contactEmail = fixupEmailFormat(job.contactEmail.trim())

  const contactFirstName = job.contactFirstName.trim()
  const contactLastName = job.contactLastName.trim()

  return {
    ...job,
    title,
    description,
    office,
    type,
    comments,
    skills,
    hoursPerWeek,
    lastModified,
    contactEmail,
    contactFirstName,
    contactLastName,
  }
}

export function getContactName(job: JobType) {
  return `${job.contactFirstName} ${job.contactLastName}`
}

export function getLinksFromJob(job: JobType) {
  // Clean up returns, newlines, tabs, and misc symbols...
  // ...and search for application links in the text
  return [
    ...parseLinks(job.description),
    ...parseLinks(job.comments),
    ...parseLinks(job.skills),
  ]
}

function parseLinks(data: string) {
  const allLinks = data.split(' ')
  if (!allLinks.length) {
    return []
  }
  return allLinks.filter(w => /^https?:\/\//.test(w))
}

function fixupEmailFormat(email: string) {
  if (!/@/.test(email)) {
    // No @ in address ... e.g. smith
    return `${email}@stolaf.edu`
  } else if (/@$/.test(email)) {
    // @ at end ... e.g. smith@
    return `${email}stolaf.edu`
  } else {
    // Defined address ... e.g. smith@stolaf.edu
    return email
  }
}
<commit_msg>replace all tab chars<commit_after>// @flow
import type {JobType} from './types'

import {
  fastGetTrimmedText,
  removeHtmlWithRegex,
} from '../../../lib/html'

export function cleanJob(job: JobType): JobType {
  const title = fastGetTrimmedText(job.title)
  const office = fastGetTrimmedText(job.office)
  const type = fastGetTrimmedText(job.type)

  // these all need to retain their newlines
  const description = removeHtmlWithRegex(job.description).replace(/\t/g, ' ')
  const comments = removeHtmlWithRegex(job.comments).replace(/\t/g, ' ')
  const skills = removeHtmlWithRegex(job.skills).replace(/\t/g, ' ')

  const hoursPerWeek = job.hoursPerWeek.trim()
  const lastModified = job.lastModified.trim()
  const contactEmail = fixupEmailFormat(job.contactEmail.trim())

  const contactFirstName = job.contactFirstName.trim()
  const contactLastName = job.contactLastName.trim()

  return {
    ...job,
    title,
    description,
    office,
    type,
    comments,
    skills,
    hoursPerWeek,
    lastModified,
    contactEmail,
    contactFirstName,
    contactLastName,
  }
}

export function getContactName(job: JobType) {
  return `${job.contactFirstName} ${job.contactLastName}`
}

export function getLinksFromJob(job: JobType) {
  // Clean up returns, newlines, tabs, and misc symbols...
  // ...and search for application links in the text
  return [
    ...parseLinks(job.description),
    ...parseLinks(job.comments),
    ...parseLinks(job.skills),
  ]
}

function parseLinks(data: string) {
  const allLinks = data.split(' ')
  if (!allLinks.length) {
    return []
  }
  return allLinks.filter(w => /^https?:\/\//.test(w))
}

function fixupEmailFormat(email: string) {
  if (!/@/.test(email)) {
    // No @ in address ... e.g. smith
    return `${email}@stolaf.edu`
  } else if (/@$/.test(email)) {
    // @ at end ... e.g. smith@
    return `${email}stolaf.edu`
  } else {
    // Defined address ... e.g. smith@stolaf.edu
    return email
  }
}
<|endoftext|>


<commit_before>// @client-side

import EventBus from './EventBus';
import GenericError from '../error/GenericError';
import Window from '../window/Window';

/**
 * Global name of IMA.js custom event.
 *
 * @const
 * @type {string}
 */
export const IMA_EVENT = '$IMA.CustomEvent';

/**
 * Helper for custom events.
 *
 * It offers public methods for firing custom events and two methods for
 * catching events (e.g. inside view components).
 */
export default class EventBusImpl extends EventBus {
  static get $dependencies() {
    return [Window];
  }

  /**
   * Initializes the custom event helper.
   *
   * @param {Window} window The IMA window helper.
   */
  constructor(window) {
    super();

    /**
     * The IMA window helper.
     *
     * @type {Window}
     */
    this._window = window;

    /**
     * Map of listeners provided to the public API of this event bus to a
     * map of event targets to a map of event names to actual listeners
     * bound to the native API.
     *
     * The "listen all" event listeners are not registered in this map.
     *
     * @type {WeakMap<
     *         function(Event),
     *         WeakMap<EventTarget, Map<string, function(Event)>>
     *       >}
     */
    this._listeners = new WeakMap();

    /**
     * Map of event targets to listeners executed on all IMA.js event bus
     * events.
     *
     * @type {WeakMap<EventTarget, WeakSet<function(Event)>>}
     */
    this._allListenersTargets = new WeakMap();
  }

  /**
   * @inheritdoc
   */
  fire(eventTarget, eventName, data, options = {}) {
    var eventInitialization = {};
    var params = { detail: { eventName, data } };
    var defaultOptions = { bubbles: true, cancelable: true };
    Object.assign(eventInitialization, defaultOptions, options, params);

    var event = this._window.createCustomEvent(IMA_EVENT, eventInitialization);

    if (eventTarget && typeof eventTarget.dispatchEvent !== 'undefined') {
      eventTarget.dispatchEvent(event);
    } else {
      throw new GenericError(
        `ima.event.EventBusImpl.fire: The EventSource ` +
          `${eventTarget} is not defined or can not dispatch event ` +
          `'${eventName}' (data: ${data}).`,
        { eventTarget, eventName, data, eventInitialization }
      );
    }

    return this;
  }

  /**
   * @inheritdoc
   */
  listenAll(eventTarget, listener) {
    if (!this._allListenersTargets.has(eventTarget)) {
      this._allListenersTargets.set(eventTarget, new WeakMap());
    }

    var nativeListener = event => {
      if (event.detail.eventName && event.type === IMA_EVENT) {
        listener(event);
      }
    };
    this._allListenersTargets.get(eventTarget).set(listener, nativeListener);

    this._window.bindEventListener(eventTarget, IMA_EVENT, nativeListener);

    return this;
  }

  /**
   * @inheritdoc
   */
  listen(eventTarget, eventName, listener) {
    if (!this._listeners.has(listener)) {
      this._listeners.set(listener, new WeakMap());
    }

    var targetToEventName = this._listeners.get(listener);
    if (!targetToEventName.has(eventTarget)) {
      targetToEventName.set(eventTarget, new Map());
    }

    var eventNameToNativeListener = targetToEventName.get(eventTarget);
    var nativeListener = event => {
      if (event.detail.eventName === eventName && event.type === IMA_EVENT) {
        listener(event);
      }
    };
    eventNameToNativeListener.set(eventName, nativeListener);

    this._window.bindEventListener(eventTarget, IMA_EVENT, nativeListener);

    return this;
  }

  /**
   * @inheritdoc
   */
  unlistenAll(eventTarget, listener) {
    if (!this._allListenersTargets.has(eventTarget)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not registered on the ' +
            'specified event target'
        );
      }

      return this;
    }

    var listeners = this._allListenersTargets.get(eventTarget);
    if (!listeners.has(listener)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not registered on the ' +
            'specified event target'
        );
      }

      return this;
    }

    var nativeListener = listeners.get(listener);
    this._window.unbindEventListener(eventTarget, IMA_EVENT, nativeListener);

    listeners.delete(listener);
    if (listeners.size) {
      return this;
    }

    this._allListenersTargets.delete(eventTarget);

    return this;
  }

  /**
   * @inheritdoc
   */
  unlisten(eventTarget, eventName, listener) {
    if (!this._listeners.has(listener)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var targets = this._listeners.get(listener);
    if (!targets.has(eventTarget)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var eventNameToNativeListener = targets.get(eventTarget);
    if (!eventNameToNativeListener.has(eventName)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var nativeListener = eventNameToNativeListener.get(eventName);
    this._window.unbindEventListener(eventTarget, IMA_EVENT, nativeListener);

    eventNameToNativeListener.delete(eventName);
    if (eventNameToNativeListener.size) {
      return this;
    }

    targets.delete(eventTarget);

    return this;
  }
}
<commit_msg>Fix error when event.detail is not defined for native listeners<commit_after>// @client-side

import EventBus from './EventBus';
import GenericError from '../error/GenericError';
import Window from '../window/Window';

/**
 * Global name of IMA.js custom event.
 *
 * @const
 * @type {string}
 */
export const IMA_EVENT = '$IMA.CustomEvent';

/**
 * Helper for custom events.
 *
 * It offers public methods for firing custom events and two methods for
 * catching events (e.g. inside view components).
 */
export default class EventBusImpl extends EventBus {
  static get $dependencies() {
    return [Window];
  }

  /**
   * Initializes the custom event helper.
   *
   * @param {Window} window The IMA window helper.
   */
  constructor(window) {
    super();

    /**
     * The IMA window helper.
     *
     * @type {Window}
     */
    this._window = window;

    /**
     * Map of listeners provided to the public API of this event bus to a
     * map of event targets to a map of event names to actual listeners
     * bound to the native API.
     *
     * The "listen all" event listeners are not registered in this map.
     *
     * @type {WeakMap<
     *         function(Event),
     *         WeakMap<EventTarget, Map<string, function(Event)>>
     *       >}
     */
    this._listeners = new WeakMap();

    /**
     * Map of event targets to listeners executed on all IMA.js event bus
     * events.
     *
     * @type {WeakMap<EventTarget, WeakSet<function(Event)>>}
     */
    this._allListenersTargets = new WeakMap();
  }

  /**
   * @inheritdoc
   */
  fire(eventTarget, eventName, data, options = {}) {
    var eventInitialization = {};
    var params = { detail: { eventName, data } };
    var defaultOptions = { bubbles: true, cancelable: true };
    Object.assign(eventInitialization, defaultOptions, options, params);

    var event = this._window.createCustomEvent(IMA_EVENT, eventInitialization);

    if (eventTarget && typeof eventTarget.dispatchEvent !== 'undefined') {
      eventTarget.dispatchEvent(event);
    } else {
      throw new GenericError(
        `ima.event.EventBusImpl.fire: The EventSource ` +
          `${eventTarget} is not defined or can not dispatch event ` +
          `'${eventName}' (data: ${data}).`,
        { eventTarget, eventName, data, eventInitialization }
      );
    }

    return this;
  }

  /**
   * @inheritdoc
   */
  listenAll(eventTarget, listener) {
    if (!this._allListenersTargets.has(eventTarget)) {
      this._allListenersTargets.set(eventTarget, new WeakMap());
    }

    var nativeListener = event => {
      if (event.type === IMA_EVENT && event.detail && event.detail.eventName) {
        listener(event);
      }
    };
    this._allListenersTargets.get(eventTarget).set(listener, nativeListener);

    this._window.bindEventListener(eventTarget, IMA_EVENT, nativeListener);

    return this;
  }

  /**
   * @inheritdoc
   */
  listen(eventTarget, eventName, listener) {
    if (!this._listeners.has(listener)) {
      this._listeners.set(listener, new WeakMap());
    }

    var targetToEventName = this._listeners.get(listener);
    if (!targetToEventName.has(eventTarget)) {
      targetToEventName.set(eventTarget, new Map());
    }

    var eventNameToNativeListener = targetToEventName.get(eventTarget);
    var nativeListener = event => {
      if (event.type === IMA_EVENT && event.detail && event.detail.eventName === eventName) {
        listener(event);
      }
    };
    eventNameToNativeListener.set(eventName, nativeListener);

    this._window.bindEventListener(eventTarget, IMA_EVENT, nativeListener);

    return this;
  }

  /**
   * @inheritdoc
   */
  unlistenAll(eventTarget, listener) {
    if (!this._allListenersTargets.has(eventTarget)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not registered on the ' +
            'specified event target'
        );
      }

      return this;
    }

    var listeners = this._allListenersTargets.get(eventTarget);
    if (!listeners.has(listener)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not registered on the ' +
            'specified event target'
        );
      }

      return this;
    }

    var nativeListener = listeners.get(listener);
    this._window.unbindEventListener(eventTarget, IMA_EVENT, nativeListener);

    listeners.delete(listener);
    if (listeners.size) {
      return this;
    }

    this._allListenersTargets.delete(eventTarget);

    return this;
  }

  /**
   * @inheritdoc
   */
  unlisten(eventTarget, eventName, listener) {
    if (!this._listeners.has(listener)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var targets = this._listeners.get(listener);
    if (!targets.has(eventTarget)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var eventNameToNativeListener = targets.get(eventTarget);
    if (!eventNameToNativeListener.has(eventName)) {
      if ($Debug) {
        console.warn(
          'The provided listener is not bound to listen for the ' +
            'specified event on the specified event target.'
        );
      }

      return this;
    }

    var nativeListener = eventNameToNativeListener.get(eventName);
    this._window.unbindEventListener(eventTarget, IMA_EVENT, nativeListener);

    eventNameToNativeListener.delete(eventName);
    if (eventNameToNativeListener.size) {
      return this;
    }

    targets.delete(eventTarget);

    return this;
  }
}
<|endoftext|>


<commit_before>Core.Object.extend('Core.HttpRequest',
/**@lends Core.HttpRequest#*/
{
	url: undefined,
	method: 'GET',
	binary: false,
	request: undefined,
	arguments: undefined,
	content: undefined,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		this.addEvents('error', 'done');

		this.request = new XMLHttpRequest();

		var wrapper = function() {
			var httprequest = arguments.callee.httprequest;
			if(httprequest.request.readyState == 4) {
				if(httprequest.request.status == 200)
					httprequest.fireEvent('done', httprequest);
				else
					httprequest.fireEvent('error', httprequest, httprequest.request.status);
			}
		}
		wrapper.httprequest = this;
		this.request.onreadystatechange = wrapper;
	},

	setUrl: function(url) {
		this.url = url;
	},

	setBinary: function(binary) {
		this.binary = binary;
	},

	setMethod: function(method) {
		this.method = method;
	},

	setRequestHeader: function(header, value) {
		this.request.setRequestHeader(header, value);
	},

	setArguments: function(arguments) {
		this.arguments = arguments;
	},

	addArgument: function(argName, argValue) {
		if(this.arguments == undefined)
			this.arguments = {};
		this.arguments[argName] = argValue;
	},

	setContent: function(content) {
		this.content = content;
	},

	abort: function() {
		this.request.abort();
	},

	send: function() {
		if(this.url == undefined)
			throw('url MUST be given for an HttpRequest');
		// encode arguments
		var args = '';
		if(this.arguments != undefined) {
			args = Core.Util.encodeURIQuery(this.arguments);
		}
		var url = this.url;

		if(((this.method == 'GET') || (this.method == 'DELETE') || (this.content != undefined)) && (args != '')) {
			if(this.url.indexOf('?') == -1)
				url += '?'+args;
			else
				url += '&'+args;
		}
		this.request.open(this.method, url, true);
		if(this.binary)
			this.request.overrideMimeType('text/plain; charset=x-user-defined');
		this.request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if((this.method == 'POST') || (this.method == 'PUT')) {
			if(this.content == undefined) {
				this.request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
				this.request.send(args);
			}
			else {
				this.request.setRequestHeader('Content-type', 'text/plain; charset=utf-8');
				this.request.send(this.content);
			}
		}
		// GET, DELETE... method
		else {
			this.request.send();
		}
	},

	getResponseText: function() {
		return this.request.responseText;
	},

	getResponseBase64: function() {
		var value = this.request.responseText;
		var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var res = '';
		var i = 0;
		while(i + 2 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var val3 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[((val2 & 15) << 2) | (val3 >> 6)];
			var enc4 = code[val3 & 63];
			res += enc1+enc2+enc3+enc4;
		}
		// 2 bytes
		if(i + 1 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[(val2 & 15) << 2];
			res += enc1+enc2+enc3+'=';
		}
		// 1 byte
		else if(i < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[(val1 & 3) << 4];
			res += enc1+enc2+'==';
		}
		return res;
	},

	getResponseJSON: function() {
		var res;
		try {
			res = JSON.parse(this.getResponseText());
		}
		catch(err) {
			res = undefined;
		}
		return res;
	},

	getResponseXML: function() {
		var parser=new DOMParser();
		try {
			var xmlDoc = parser.parseFromString(this.getResponseText(), 'text/xml');
			return xmlDoc;
		} catch(e) {}
		return undefined;
	}
});

<commit_msg>add getStatus to Core.HttpRequest and fire "done" for all HTTP 2xx codes<commit_after>Core.Object.extend('Core.HttpRequest',
/**@lends Core.HttpRequest#*/
{
	url: undefined,
	method: 'GET',
	binary: false,
	request: undefined,
	arguments: undefined,
	content: undefined,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		this.addEvents('error', 'done');

		this.request = new XMLHttpRequest();

		var wrapper = function() {
			var httprequest = arguments.callee.httprequest;
			if(httprequest.request.readyState == 4) {
				if((httprequest.request.status >= 200) && (httprequest.request.status < 300))
					httprequest.fireEvent('done', httprequest);
				else
					httprequest.fireEvent('error', httprequest, httprequest.request.status);
			}
		}
		wrapper.httprequest = this;
		this.request.onreadystatechange = wrapper;
	},

	setUrl: function(url) {
		this.url = url;
	},

	setBinary: function(binary) {
		this.binary = binary;
	},

	setMethod: function(method) {
		this.method = method;
	},

	setRequestHeader: function(header, value) {
		this.request.setRequestHeader(header, value);
	},

	setArguments: function(arguments) {
		this.arguments = arguments;
	},

	addArgument: function(argName, argValue) {
		if(this.arguments == undefined)
			this.arguments = {};
		this.arguments[argName] = argValue;
	},

	setContent: function(content) {
		this.content = content;
	},

	abort: function() {
		this.request.abort();
	},

	send: function() {
		if(this.url == undefined)
			throw('url MUST be given for an HttpRequest');
		// encode arguments
		var args = '';
		if(this.arguments != undefined) {
			args = Core.Util.encodeURIQuery(this.arguments);
		}
		var url = this.url;

		if(((this.method == 'GET') || (this.method == 'DELETE') || (this.content != undefined)) && (args != '')) {
			if(this.url.indexOf('?') == -1)
				url += '?'+args;
			else
				url += '&'+args;
		}
		this.request.open(this.method, url, true);
		if(this.binary)
			this.request.overrideMimeType('text/plain; charset=x-user-defined');
		this.request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if((this.method == 'POST') || (this.method == 'PUT')) {
			if(this.content == undefined) {
				this.request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
				this.request.send(args);
			}
			else {
				this.request.setRequestHeader('Content-type', 'text/plain; charset=utf-8');
				this.request.send(this.content);
			}
		}
		// GET, DELETE... method
		else {
			this.request.send();
		}
	},

	getResponseText: function() {
		return this.request.responseText;
	},

	getResponseBase64: function() {
		var value = this.request.responseText;
		var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var res = '';
		var i = 0;
		while(i + 2 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var val3 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[((val2 & 15) << 2) | (val3 >> 6)];
			var enc4 = code[val3 & 63];
			res += enc1+enc2+enc3+enc4;
		}
		// 2 bytes
		if(i + 1 < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var val2 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[((val1 & 3) << 4) | (val2 >> 4)];
			var enc3 = code[(val2 & 15) << 2];
			res += enc1+enc2+enc3+'=';
		}
		// 1 byte
		else if(i < value.length) {
			var val1 = value.charCodeAt(i++) & 0xff;
			var enc1 = code[val1 >> 2];
			var enc2 = code[(val1 & 3) << 4];
			res += enc1+enc2+'==';
		}
		return res;
	},

	getResponseJSON: function() {
		var res;
		try {
			res = JSON.parse(this.getResponseText());
		}
		catch(err) {
			res = undefined;
		}
		return res;
	},

	getResponseXML: function() {
		var parser=new DOMParser();
		try {
			var xmlDoc = parser.parseFromString(this.getResponseText(), 'text/xml');
			return xmlDoc;
		} catch(e) {}
		return undefined;
	},

	getStatus: function() {
		return httprequest.request.status;
	}
});

<|endoftext|>