<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, 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 */

/*

        Cycles

                <input type="checkbox" value="1">
                1

                <input type="checkbox" value="2">
                2

*/

/* *****************************************************************************
a Form component containing a CheckboxGroup
    should include a <Form.CheckboxGroup> as a child
    should contain a div.form-group
    should contain a label if the label prop is set
    should not contain a label if the label prop is not set
    should contain a div.checkbox for each option
    should contain a label for each option
    should contain an input[type="checkbox"] for each option
    should contain a span with the option name for each option
    should have the correct options checked when multiple values are provided
    should have the correct options checked when a single value is provided
    should have the correct options checked when null value is provided
    should have the correct options checked when no value is provided
    should have the correct options checked when empty array value is provided
*/
describe('a Form component containing a CheckboxGroup', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

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

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

    it('should contain a div.form-group', () => {
        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.form-group')).to.have.length(1);
    });

    it('should contain a label if the label prop is set', () => {

        const label = 'humpty dumpty';

        const component = mount(
            <Form>
                <Form.CheckboxGroup label={label} options={options} />
            </Form>
        );

        expect(component.find('label.checkboxgroup')).to.have.length(1);
        expect(component.find('label.checkboxgroup').text()).to.equal(label);
    });

    it('should not contain a label if the label prop is not set', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('label.checkboxgroup')).to.have.length(0);
    });

    it('should contain a div.checkbox for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox')).to.have.length(options.length);
    });

    it('should contain a label for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox label')).to.have.length(options.length);
    });

    it('should contain an input[type="checkbox"] for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().value)
            .to.equal(options[0].value);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().value)
            .to.equal(options[1].value);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().value)
            .to.equal(options[2].value);
    });

    it('should contain a span with the option name for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox span')).to.have.length(options.length);
        expect(component.find('div.checkbox span').at(0).text()).to.equal(options[0].name);
        expect(component.find('div.checkbox span').at(1).text()).to.equal(options[1].name);
        expect(component.find('div.checkbox span').at(2).text()).to.equal(options[2].name);

    });

    it('should have the correct options checked when multiple values are provided', () => {

        const value = ['1', '2'];

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when a single value is provided', () => {

        const value = '2';

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when null value is provided', () => {

        const value = null;

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when no value is provided', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

});

/* *****************************************************************************
the CheckboxGroup component
    should call onChange with the correct value(s) when a new item is toggled on
    should call onChange with the correct value(s) when a new item is toggled off
*/
describe('the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    it('should call onChange with the correct value(s) when a new item is toggled on', () => {

        const onChange = sinon.spy();

        const value = '2';

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} onChange={onChange} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        // click the first item
        //
        component.find('div.checkbox input[type="checkbox"]').at(0).simulate('click');

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        expect(onChange.callCount).to.equal(1, 'callCount');
        expect(onChange.calledWith(['2', '1'])).to.equal(true, 'args');

    });

    it('should call onChange with the correct value(s) when a new item is toggled off', () => {

        const onChange = sinon.spy();

        const value = ['1', '2'];

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} onChange={onChange} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        // click the first item
        //
        component.find('div.checkbox input[type="checkbox"]').at(0).simulate('click');

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        expect(onChange.callCount).to.equal(1, 'callCount');
        expect(onChange.calledWith(['2'])).to.equal(true, 'args');

    });

});

// @TODO write these specs
/* *****************************************************************************
on initialization, the CheckboxGroup component
    should not show the validation message when required=false and some items are checked
    should not show the validation message when required=false and no items are checked
    should not show the validation message when required=true and some items are checked
    should not show the validation message when required=true and no items are checked
*/
describe('on initialization, the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    // it('should not show the validation message when required=false and some items are checked', () => {});

    // it('should not show the validation message when required=false and no items are checked', () => {});

    // it('should not show the validation message when required=true and some items are checked', () => {});

    // it('should not show the validation message when required=true and no items are checked', () => {});

});

// @TODO write these specs
/* *****************************************************************************
after the user clicks something, the CheckboxGroup component
    should not show the validation message when required=false and some items are checked
    should not show the validation message when required=false and no items are checked
    should not show the validation message when required=true and some items are checked
    should show the validation message when required=true and no items are checked
*/
describe('after the user clicks something, the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    // it('should not show the validation message when required=false and some items are checked', () => {});

    // it('should not show the validation message when required=false and no items are checked', () => {});

    // it('should not show the validation message when required=true and some items are checked', () => {});

    // it('should show the validation message when required=true and no items are checked', () => {});

});
<commit_msg>added spec for empty array value on init<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, 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 */

/*

        Cycles

                <input type="checkbox" value="1">
                1

                <input type="checkbox" value="2">
                2

*/

/* *****************************************************************************
a Form component containing a CheckboxGroup
    should include a <Form.CheckboxGroup> as a child
    should contain a div.form-group
    should contain a label if the label prop is set
    should not contain a label if the label prop is not set
    should contain a div.checkbox for each option
    should contain a label for each option
    should contain an input[type="checkbox"] for each option
    should contain a span with the option name for each option
    should have the correct options checked when multiple values are provided
    should have the correct options checked when a single value is provided
    should have the correct options checked when null value is provided
    should have the correct options checked when no value is provided
    should have the correct options checked when empty array value is provided
*/
describe('a Form component containing a CheckboxGroup', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

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

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

    it('should contain a div.form-group', () => {
        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.form-group')).to.have.length(1);
    });

    it('should contain a label if the label prop is set', () => {

        const label = 'humpty dumpty';

        const component = mount(
            <Form>
                <Form.CheckboxGroup label={label} options={options} />
            </Form>
        );

        expect(component.find('label.checkboxgroup')).to.have.length(1);
        expect(component.find('label.checkboxgroup').text()).to.equal(label);
    });

    it('should not contain a label if the label prop is not set', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('label.checkboxgroup')).to.have.length(0);
    });

    it('should contain a div.checkbox for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox')).to.have.length(options.length);
    });

    it('should contain a label for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox label')).to.have.length(options.length);
    });

    it('should contain an input[type="checkbox"] for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().value)
            .to.equal(options[0].value);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().value)
            .to.equal(options[1].value);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().value)
            .to.equal(options[2].value);
    });

    it('should contain a span with the option name for each option', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox span')).to.have.length(options.length);
        expect(component.find('div.checkbox span').at(0).text()).to.equal(options[0].name);
        expect(component.find('div.checkbox span').at(1).text()).to.equal(options[1].name);
        expect(component.find('div.checkbox span').at(2).text()).to.equal(options[2].name);

    });

    it('should have the correct options checked when multiple values are provided', () => {

        const value = ['1', '2'];

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when a single value is provided', () => {

        const value = '2';

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when null value is provided', () => {

        const value = null;

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when no value is provided', () => {

        const component = mount(
            <Form>
                <Form.CheckboxGroup options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

    it('should have the correct options checked when empty array value is provided', () => {

        const value = [];

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]'))
            .to.have.length(options.length);
        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);
    });

});

/* *****************************************************************************
the CheckboxGroup component
    should call onChange with the correct value(s) when a new item is toggled on
    should call onChange with the correct value(s) when a new item is toggled off
*/
describe('the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    it('should call onChange with the correct value(s) when a new item is toggled on', () => {

        const onChange = sinon.spy();

        const value = '2';

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} onChange={onChange} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        // click the first item
        //
        component.find('div.checkbox input[type="checkbox"]').at(0).simulate('click');

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        expect(onChange.callCount).to.equal(1, 'callCount');
        expect(onChange.calledWith(['2', '1'])).to.equal(true, 'args');

    });

    it('should call onChange with the correct value(s) when a new item is toggled off', () => {

        const onChange = sinon.spy();

        const value = ['1', '2'];

        const component = mount(
            <Form>
                <Form.CheckboxGroup value={value} options={options} onChange={onChange} />
            </Form>
        );

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        // click the first item
        //
        component.find('div.checkbox input[type="checkbox"]').at(0).simulate('click');

        expect(component.find('div.checkbox input[type="checkbox"]').at(0).props().checked)
            .to.equal(false);
        expect(component.find('div.checkbox input[type="checkbox"]').at(1).props().checked)
            .to.equal(true);
        expect(component.find('div.checkbox input[type="checkbox"]').at(2).props().checked)
            .to.equal(false);

        expect(onChange.callCount).to.equal(1, 'callCount');
        expect(onChange.calledWith(['2'])).to.equal(true, 'args');

    });

});

// @TODO write these specs
/* *****************************************************************************
on initialization, the CheckboxGroup component
    should not show the validation message when required=false and some items are checked
    should not show the validation message when required=false and no items are checked
    should not show the validation message when required=true and some items are checked
    should not show the validation message when required=true and no items are checked
*/
describe('on initialization, the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    // it('should not show the validation message when required=false and some items are checked', () => {});

    // it('should not show the validation message when required=false and no items are checked', () => {});

    // it('should not show the validation message when required=true and some items are checked', () => {});

    // it('should not show the validation message when required=true and no items are checked', () => {});

});

// @TODO write these specs
/* *****************************************************************************
after the user clicks something, the CheckboxGroup component
    should not show the validation message when required=false and some items are checked
    should not show the validation message when required=false and no items are checked
    should not show the validation message when required=true and some items are checked
    should show the validation message when required=true and no items are checked
*/
describe('after the user clicks something, the CheckboxGroup component', () => {

    const options = [
        { value: '1', name: 'One' },
        { value: '2', name: 'Two' },
        { value: '3', name: 'Three' },
    ];

    // it('should not show the validation message when required=false and some items are checked', () => {});

    // it('should not show the validation message when required=false and no items are checked', () => {});

    // it('should not show the validation message when required=true and some items are checked', () => {});

    // it('should show the validation message when required=true and no items are checked', () => {});

});
<|endoftext|>


<commit_before>"use strict";
var fs = require('fs');
var path = require('path');
var env = require('./env');
var Spec = require('./Spec');
var Storage = require('./Storage');
var SecondPreciseClock = require('./SecondPreciseClock');

/**
 * An improvised filesystem-based storage implementation.
 * Objects are saved into separate files in a hashed directory
 * tree. Ongoing operations are streamed into a log file.
 * One can go surprisingly far with this kind of an approach.
 * https://news.ycombinator.com/item?id=7872239 */
function FileStorage (dir) {
    Storage.call(this);
    this._host = null; //will be set during Host creation
    this.dir = path.resolve(dir);
    if (!fs.existsSync(this.dir)) {
        fs.mkdirSync(this.dir);
    }
    this._id = 'file';
    this.tail = {};

    var clock_fn = env.clock || SecondPreciseClock;
    this.clock = new clock_fn(this._id);

    this.loadLog();
    this.rotateLog();
}
FileStorage.prototype = new Storage();
module.exports = FileStorage;

FileStorage.prototype.stateFileName = function (spec) {
    var base = path.resolve(this.dir, spec.type());
    var file = path.resolve(base, spec.id());
    return file; // TODO hashing?
};

FileStorage.prototype.logFileName = function () {
    return path.resolve(this.dir, "_log");
};

FileStorage.prototype.writeState = function (spec, state, cb) {
    var self = this;
    var ti = spec.filter('/#');
    var fileName = this.stateFileName(ti);
    var dir = path.dirname(fileName);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
    }
    var save = JSON.stringify(state, undefined, 2);
    // dump JSON to the tmp file
    delete self.tails[ti]; // TODO save 'em in case write fails
    fs.writeFile(fileName, save, function onSave(err) {
        if (err) {
            console.error("failed to flush state; can't trim the log", err);
        }
        cb(err);
    });
};

FileStorage.prototype.writeOp = function (spec, value, cb) {
    var self = this;
    var ti = spec.filter('/#');
    var vm = spec.filter('!.');
    var tail = this.tails[ti] || (this.tails[ti] = {});
    if (vm in tail) {
        console.error('op replay @storage',vm,new Error().stack);
        return;
    }
    var clone = JSON.parse(JSON.stringify(value)); // FIXME performance please
    tail[vm] = clone;
    var record = ',\n"'+spec+'":\t'+JSON.stringify(clone);
    this.logFile.write (record, function onFail(err) {
        if (err) { self.close(null,err); }
        cb(err);
    });
};

FileStorage.prototype.readState = function (ti, callback) {
    var statefn = this.stateFileName(ti);
    // read in the state
    fs.readFile(statefn, function onRead(err, data) { // FIXME fascism
        var state = err ? {_version: '!0'} : JSON.parse(data.toString());
        callback(null,state||null); // important: no state is "null"
    });
};

FileStorage.prototype.readOps = function (ti, callback) {
    var tail = this.tails[ti];
    if (tail) {
        var unjsoned = {};
        for(var key in tail) {
            unjsoned[key] = tail[key];
        }
        tail = unjsoned;
    }
    callback(null, tail||null);
};

FileStorage.prototype.close = function (callback,error) {
    if (error) {
        console.log("fatal IO error", error);
    }
    if (this.logFile) {
        this.rotateLog(true, callback);
    }
};

FileStorage.prototype.rotateLog = function (noOpen, callback) {
    var self = this;
    if (this.logFile) {
        this.logFile.end('}', callback);
        this.logFile = null;
        callback = undefined;
    }

    if (!noOpen) {

        if (fs.existsSync(this.logFileName())) {
            fs.rename(this.logFileName(),this.logFileName()+'.bak');
        }
        this.logFile = fs.createWriteStream(this.logFileName()); // TODO file swap
        this.logFile.on('error', function (err) {
            self.close(null,err);
        });

        var json = JSON.stringify(this.tails, undefined, 2);
        json = '{"":\n' + json; // open-ended JSON

        this.logFile.write (json, function onFail(err) {
            if (err) { self.close(null,err); }
        });

    }

    if (callback) {
        callback();
    }

};

FileStorage.prototype.loadLog = function () {
    if ( !fs.existsSync(this.logFileName()) ) {
        return;
    }
    var json = fs.readFileSync(this.logFileName(), {encoding:"utf8"});
    if (!json) { return; }
    var log;
    try {
        log = JSON.parse(json);
    } catch (ex) {
        // open-ended JSON
        log = JSON.parse(json + '}');
    }
    this.tails = log[''];
    delete log[''];
    for(var s in log) {
        var spec = new Spec(s);
        if (spec.pattern()==='/#!.') {
            var ti = spec.filter('/#');
            var vm = spec.filter('!.');
            var tail = this.tails[ti] || (this.tails[ti] = {});
            tail[vm]  = log[spec];
        }
    }
};

process.on('uncaughtException', function(err) {
      console.log('uncaught:', err, err.stack);
});
<commit_msg>"unhandledException" handler should be in app main script, not here<commit_after>"use strict";
var fs = require('fs');
var path = require('path');
var env = require('./env');
var Spec = require('./Spec');
var Storage = require('./Storage');
var SecondPreciseClock = require('./SecondPreciseClock');

/**
 * An improvised filesystem-based storage implementation.
 * Objects are saved into separate files in a hashed directory
 * tree. Ongoing operations are streamed into a log file.
 * One can go surprisingly far with this kind of an approach.
 * https://news.ycombinator.com/item?id=7872239 */
function FileStorage (dir) {
    Storage.call(this);
    this._host = null; //will be set during Host creation
    this.dir = path.resolve(dir);
    if (!fs.existsSync(this.dir)) {
        fs.mkdirSync(this.dir);
    }
    this._id = 'file';
    this.tail = {};

    var clock_fn = env.clock || SecondPreciseClock;
    this.clock = new clock_fn(this._id);

    this.loadLog();
    this.rotateLog();
}
FileStorage.prototype = new Storage();
module.exports = FileStorage;

FileStorage.prototype.stateFileName = function (spec) {
    var base = path.resolve(this.dir, spec.type());
    var file = path.resolve(base, spec.id());
    return file; // TODO hashing?
};

FileStorage.prototype.logFileName = function () {
    return path.resolve(this.dir, "_log");
};

FileStorage.prototype.writeState = function (spec, state, cb) {
    var self = this;
    var ti = spec.filter('/#');
    var fileName = this.stateFileName(ti);
    var dir = path.dirname(fileName);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
    }
    var save = JSON.stringify(state, undefined, 2);
    // dump JSON to the tmp file
    delete self.tails[ti]; // TODO save 'em in case write fails
    fs.writeFile(fileName, save, function onSave(err) {
        if (err) {
            console.error("failed to flush state; can't trim the log", err);
        }
        cb(err);
    });
};

FileStorage.prototype.writeOp = function (spec, value, cb) {
    var self = this;
    var ti = spec.filter('/#');
    var vm = spec.filter('!.');
    var tail = this.tails[ti] || (this.tails[ti] = {});
    if (vm in tail) {
        console.error('op replay @storage',vm,new Error().stack);
        return;
    }
    var clone = JSON.parse(JSON.stringify(value)); // FIXME performance please
    tail[vm] = clone;
    var record = ',\n"'+spec+'":\t'+JSON.stringify(clone);
    this.logFile.write (record, function onFail(err) {
        if (err) { self.close(null,err); }
        cb(err);
    });
};

FileStorage.prototype.readState = function (ti, callback) {
    var statefn = this.stateFileName(ti);
    // read in the state
    fs.readFile(statefn, function onRead(err, data) { // FIXME fascism
        var state = err ? {_version: '!0'} : JSON.parse(data.toString());
        callback(null,state||null); // important: no state is "null"
    });
};

FileStorage.prototype.readOps = function (ti, callback) {
    var tail = this.tails[ti];
    if (tail) {
        var unjsoned = {};
        for(var key in tail) {
            unjsoned[key] = tail[key];
        }
        tail = unjsoned;
    }
    callback(null, tail||null);
};

FileStorage.prototype.close = function (callback,error) {
    if (error) {
        console.log("fatal IO error", error);
    }
    if (this.logFile) {
        this.rotateLog(true, callback);
    }
};

FileStorage.prototype.rotateLog = function (noOpen, callback) {
    var self = this;
    if (this.logFile) {
        this.logFile.end('}', callback);
        this.logFile = null;
        callback = undefined;
    }

    if (!noOpen) {

        if (fs.existsSync(this.logFileName())) {
            fs.rename(this.logFileName(),this.logFileName()+'.bak');
        }
        this.logFile = fs.createWriteStream(this.logFileName()); // TODO file swap
        this.logFile.on('error', function (err) {
            self.close(null,err);
        });

        var json = JSON.stringify(this.tails, undefined, 2);
        json = '{"":\n' + json; // open-ended JSON

        this.logFile.write (json, function onFail(err) {
            if (err) { self.close(null,err); }
        });

    }

    if (callback) {
        callback();
    }

};

FileStorage.prototype.loadLog = function () {
    if ( !fs.existsSync(this.logFileName()) ) {
        return;
    }
    var json = fs.readFileSync(this.logFileName(), {encoding:"utf8"});
    if (!json) { return; }
    var log;
    try {
        log = JSON.parse(json);
    } catch (ex) {
        // open-ended JSON
        log = JSON.parse(json + '}');
    }
    this.tails = log[''];
    delete log[''];
    for(var s in log) {
        var spec = new Spec(s);
        if (spec.pattern()==='/#!.') {
            var ti = spec.filter('/#');
            var vm = spec.filter('!.');
            var tail = this.tails[ti] || (this.tails[ti] = {});
            tail[vm]  = log[spec];
        }
    }
};
<|endoftext|>


<commit_before>import NextErrorComponent from 'next/error'
import * as Sentry from '@sentry/node'

const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => {
  if (!hasGetInitialPropsRun && err) {
    // getInitialProps is not called in case of
    // https://github.com/zeit/next.js/issues/8592. As a workaround, we pass
    // err via _app.js so it can be captured
    Sentry.captureException(err)
  }

  return <NextErrorComponent statusCode={statusCode} />
}

MyError.getInitialProps = async ({ res, err, asPath }) => {
  const errorInitialProps = await Error.getInitialProps({ res, err })

  // Workaround for https://github.com/zeit/next.js/issues/8592, mark when
  // getInitialProps has run
  errorInitialProps.hasGetInitialPropsRun = true

  // Running on the server, the response object (`res`) is available.
  //
  // Next.js will pass an err on the server if a page's data fetching methods
  // threw or returned a Promise that rejected
  //
  // Running on the client (browser), Next.js will provide an err if:
  //
  //  - a page's `getInitialProps` threw or returned a Promise that rejected
  //  - an exception was thrown somewhere in the React lifecycle (render,
  //    componentDidMount, etc) that was caught by Next.js's React Error
  //    Boundary. Read more about what types of exceptions are caught by Error
  //    Boundaries: https://reactjs.org/docs/error-boundaries.html

  if (res?.statusCode === 404) {
    // Opinionated: do not record an exception in Sentry for 404
    return { statusCode: 404 }
  }
  if (err) {
    Sentry.captureException(err)
    return errorInitialProps
  }

  // If this point is reached, getInitialProps was called without any
  // information about what the error might be. This is unexpected and may
  // indicate a bug introduced in Next.js, so record it in Sentry
  Sentry.captureException(
    new Error(`_error.js getInitialProps missing data at path: ${asPath}`)
  )

  return errorInitialProps
}

export default MyError
<commit_msg>Fix `with-sentry-simple` example (#13404)<commit_after>import NextErrorComponent from 'next/error'
import * as Sentry from '@sentry/node'

const MyError = ({ statusCode, hasGetInitialPropsRun, err }) => {
  if (!hasGetInitialPropsRun && err) {
    // getInitialProps is not called in case of
    // https://github.com/zeit/next.js/issues/8592. As a workaround, we pass
    // err via _app.js so it can be captured
    Sentry.captureException(err)
  }

  return <NextErrorComponent statusCode={statusCode} />
}

MyError.getInitialProps = async ({ res, err, asPath }) => {
  const errorInitialProps = await NextErrorComponent.getInitialProps({
    res,
    err,
  })

  // Workaround for https://github.com/zeit/next.js/issues/8592, mark when
  // getInitialProps has run
  errorInitialProps.hasGetInitialPropsRun = true

  // Running on the server, the response object (`res`) is available.
  //
  // Next.js will pass an err on the server if a page's data fetching methods
  // threw or returned a Promise that rejected
  //
  // Running on the client (browser), Next.js will provide an err if:
  //
  //  - a page's `getInitialProps` threw or returned a Promise that rejected
  //  - an exception was thrown somewhere in the React lifecycle (render,
  //    componentDidMount, etc) that was caught by Next.js's React Error
  //    Boundary. Read more about what types of exceptions are caught by Error
  //    Boundaries: https://reactjs.org/docs/error-boundaries.html

  if (res?.statusCode === 404) {
    // Opinionated: do not record an exception in Sentry for 404
    return { statusCode: 404 }
  }
  if (err) {
    Sentry.captureException(err)
    return errorInitialProps
  }

  // If this point is reached, getInitialProps was called without any
  // information about what the error might be. This is unexpected and may
  // indicate a bug introduced in Next.js, so record it in Sentry
  Sentry.captureException(
    new Error(`_error.js getInitialProps missing data at path: ${asPath}`)
  )

  return errorInitialProps
}

export default MyError
<|endoftext|>


<commit_before>function Game()
{
	this.snake = new Snake();
	this.lose = false;
	this.apple = new Apple();
	this.board = new Board(50);
	this.board.generateBoard();
	this.apple.generateApple();
}

Game.prototype.Turn = function () {
	var lastRowCoord = this.snake.snakeArray[0][0];
	var lastColCoord = this.snake.snakeArray[0][1];
	this.board.renderSnakeCoords(this.snake.snakeArray);
	this.snake.GetKeyStroke();
	this.snake.Move(this.apple);
	this.board.disActivateCell(lastRowCoord, lastColCoord);
	console.log(this.snake.snakeArray);
	this.CheckLose();
}

Game.prototype.CheckLose = function ()
{
	var snakeHeadRow = this.snake.snakeArray[this.snake.snakeArray.length-1][0];
	var snakeHeadCol = this.snake.snakeArray[this.snake.snakeArray.length-1][1];
	if (snakeHeadRow < 0 || snakeHeadRow > 49 || snakeHeadCol < 0 || snakeHeadCol > 49)
	{
		this.lose = true;
		console.log("you lose!");
	}
}

Game.prototype.Start = function () {
	var thisGame = this;
	var startGame = setInterval(function (){
		thisGame.Turn();
		if (thisGame.lose === true)
		{
			clearInterval(startGame);
		}
	}, 200);
}

function Snake ()
{
	this.snakeArray = [[0,0], [0,1], [0,2]];
	this.direction = 2;
	this.score = 0;
}

Snake.prototype.GetKeyStroke = function ()
{
	var thisSnake = this;
		$(document).keydown(function (e)
		{
			switch(e.which)
			{
				case 38:
				thisSnake.direction = 1;
				break;
				case 39:
				thisSnake.direction = 2;
				break;
				case 40:
				thisSnake.direction = 3;
				break;
				case 37:
				thisSnake.direction = 4;
				break;
			}

		});
}
	// move
Snake.prototype.Move = function (apple)
{
	var headRow = this.snakeArray[this.snakeArray.length-1][0];
	var headCol = this.snakeArray[this.snakeArray.length-1][1];
	console.log(headRow + "," + headCol);
		switch (this.direction)
		{
			// up
			case 1:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow -1;
					var newHeadCol = headCol + 0;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				var testCell = this.snakeArray.shift();
				console.log(testCell);
				var newHeadRow = headRow -1;
				var newHeadCol = headCol + 0;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// down
			case 3:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 1;
					var newHeadCol = headCol + 0;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 1;
				var newHeadCol = headCol + 0;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// right
			case 2:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 0;
					var newHeadCol = headCol + 1;
					console.log(headRow + "," + headCol);
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 0;
				var newHeadCol = headCol + 1;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// left
			case 4:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 0;
					var newHeadCol = headCol -1;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 0;
				var newHeadCol = headCol -1;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
		}
}

Snake.prototype.EatApple = function (apple)
{
	var appleRow = apple.coordinates[0];
	var appleCol = apple.coordinates[1];

	var headRow = this.snakeArray[this.snakeArray.length-1][0];
	var headCol = this.snakeArray[this.snakeArray.length-1][1];

	if (appleRow === headRow && appleCol === headCol)
	{
		console.log("eaten apple");
		this.score += 10;
		console.log("your score is now " + this.score);
		return true;
	}
	return false;
}

	// driver code
$(document).ready(function() {

var game = new Game();
game.Start();

});

<commit_msg>snake eats apples and grows! -MVP<commit_after>function Game()
{
	this.snake = new Snake();
	this.lose = false;
	this.apple = new Apple();
	this.board = new Board(50);
	this.board.generateBoard();
	this.apple.generateApple();
}

Game.prototype.Turn = function () {
	var lastRowCoord = this.snake.snakeArray[0][0];
	var lastColCoord = this.snake.snakeArray[0][1];
	this.board.renderSnakeCoords(this.snake.snakeArray);
	this.snake.GetKeyStroke();
	this.snake.Move(this.apple);
	if (this.snake.eatenApple)
	{
		this.board.disActivateApple(this.apple.coordinates[0],this.apple.coordinates[1]);
		this.apple.generateApple();
		this.snake.eatenApple = false;
	}
	this.board.disActivateCell(lastRowCoord, lastColCoord);
	console.log(this.snake.snakeArray);
	this.CheckLose();
}

Game.prototype.CheckLose = function ()
{
	var snakeHeadRow = this.snake.snakeArray[this.snake.snakeArray.length-1][0];
	var snakeHeadCol = this.snake.snakeArray[this.snake.snakeArray.length-1][1];
	if (snakeHeadRow < 0 || snakeHeadRow > 49 || snakeHeadCol < 0 || snakeHeadCol > 49)
	{
		this.lose = true;
		console.log("you lose!");
	}
}

Game.prototype.Start = function () {
	var thisGame = this;
	var startGame = setInterval(function (){
		thisGame.Turn();
		if (thisGame.lose === true)
		{
			clearInterval(startGame);
		}
	}, 50);
}

function Snake ()
{
	this.snakeArray = [[0,0], [0,1], [0,2]];
	this.direction = 2;
	this.score = 0;
	this.eatenApple = false;
}

Snake.prototype.GetKeyStroke = function ()
{
	var thisSnake = this;
		$(document).keydown(function (e)
		{
			switch(e.which)
			{
				case 38:
				thisSnake.direction = 1;
				break;
				case 39:
				thisSnake.direction = 2;
				break;
				case 40:
				thisSnake.direction = 3;
				break;
				case 37:
				thisSnake.direction = 4;
				break;
			}

		});
}
	// move
Snake.prototype.Move = function (apple)
{
	var headRow = this.snakeArray[this.snakeArray.length-1][0];
	var headCol = this.snakeArray[this.snakeArray.length-1][1];
	console.log(headRow + "," + headCol);
		switch (this.direction)
		{
			// up
			case 1:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow -1;
					var newHeadCol = headCol + 0;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				var testCell = this.snakeArray.shift();
				console.log(testCell);
				var newHeadRow = headRow -1;
				var newHeadCol = headCol + 0;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// down
			case 3:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 1;
					var newHeadCol = headCol + 0;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 1;
				var newHeadCol = headCol + 0;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// right
			case 2:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 0;
					var newHeadCol = headCol + 1;
					console.log(headRow + "," + headCol);
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 0;
				var newHeadCol = headCol + 1;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
			// left
			case 4:
				if (this.EatApple(apple))
				{
					var newHeadRow = headRow + 0;
					var newHeadCol = headCol -1;
					var newHead = [newHeadRow, newHeadCol];
					this.snakeArray.push(newHead);
				}
				this.snakeArray.shift();
				var newHeadRow = headRow + 0;
				var newHeadCol = headCol -1;
				var newHead = [newHeadRow, newHeadCol];
				this.snakeArray.push(newHead);
			break;
		}
}

Snake.prototype.EatApple = function (apple)
{
	var appleRow = apple.coordinates[0];
	var appleCol = apple.coordinates[1];

	var headRow = this.snakeArray[this.snakeArray.length-1][0];
	var headCol = this.snakeArray[this.snakeArray.length-1][1];

	if (appleRow === headRow && appleCol === headCol)
	{
		console.log("eaten apple");
		this.score += 10;
		console.log("your score is now " + this.score);
		this.eatenApple = true;
		return true;

	}
	return false;
}

	// driver code
$(document).ready(function() {

var game = new Game();
game.Start();

});

<|endoftext|>


<commit_before>app.constant("MAIL_ENDPOINT", "php/apis/mail/");
app.service("mailService", function($http, MAIL_ENDPOINT) {

	function getUrl() {
		return(MAIL_ENDPOINT);
	}

	function getUrlForMailSenderId(mailSenderId) {
		return(getUrl() + mailSenderId);
	}

	function getUrlForMailReceiverId(mailReceiverId) {
		return(getUrl() + MailReceiverId);
	}

	this.fetchMail = function() {
	 return($http.get(getUrl()));
	 };

	this.fetchMailByMailSenderId = function(mailSenderId) {
		return($http.get(getUrl() + "?mailSenderId=" + mailSenderId));
	};

	this.fetchMailByMailReceiverId = function(mailReceiverId) {
		return($http.get(getUrl() + "?mailReceiverId=" + mailReceiverId));
	};

	this.create = function(mailSenderId, mailReceiverId) {
		return($http.post(getUrlforId(mailSenderId, mailReceiverId)));
	};

	this.destroy = function(mailSenderId, mailReceiverId) {
		return($http.delete(getUrlforId(mailSenderId, mailReceiverId)));
	};

});<commit_msg>camelCased mail  service. https://app.asana.com/0/161555263951762/170520114054348<commit_after>app.constant("MAIL_ENDPOINT", "php/apis/mail/");
app.service("mailService", function($http, MAIL_ENDPOINT) {

	function getUrl() {
		return(MAIL_ENDPOINT);
	}

	function getUrlForMailSenderId(mailSenderId) {
		return(getUrl() + mailSenderId);
	}

	function getUrlForMailReceiverId(mailReceiverId) {
		return(getUrl() + mailReceiverId);
	}

	this.fetchMail = function() {
	 return($http.get(getUrl()));
	 };

	this.fetchMailByMailSenderId = function(mailSenderId) {
		return($http.get(getUrl() + "?mailSenderId=" + mailSenderId));
	};

	this.fetchMailByMailReceiverId = function(mailReceiverId) {
		return($http.get(getUrl() + "?mailReceiverId=" + mailReceiverId));
	};

	this.create = function(mailSenderId, mailReceiverId) {
		return($http.post(getUrlforId(mailSenderId, mailReceiverId)));
	};

	this.destroy = function(mailSenderId, mailReceiverId) {
		return($http.delete(getUrlforId(mailSenderId, mailReceiverId)));
	};

});<|endoftext|>


<commit_before>// manages limiting the characters of the description
// on the details page and providing a more/less link to
// toggle showing or hiding excess text
define(['util/web-storage-proxy'],function(storage) {
  'use strict';

		// PRIVATE PROPERTIES
		var _searchBox;
		var _locationBox;

		var _storageID = "util-web-storage-proxy";

		// PUBLIC METHODS
		function init()
		{
			_searchBox = document.getElementById('keyword-search-box');
			_locationBox = document.getElementById('location-search-box');

			var locationSearch = document.querySelector('#location-search-btn a');
			var keywordSearch = document.querySelector('#keyword-search-btn a');

			// the switcher anchors are hidden by default so they don't show up when JS is disabled
			locationSearch.classList.remove('hide');
			keywordSearch.classList.remove('hide');

			locationSearch.addEventListener('click', _locationClicked, false);
			keywordSearch.addEventListener('click', _keywordClicked, false);

			if (storage.getItem(_storageID) != 'KEYWORD')
			{
				_showKeywordSearch();
			}
			else
			{
				_showLocationSearch();
			}
		}

		// PRIVATE METHODS
		function _locationClicked(evt)
		{
			_showLocationSearch();
			evt.preventDefault();
			return false;
		}

		function _keywordClicked(evt)
		{
			_showKeywordSearch();
			evt.preventDefault();
			return false;
		}

		function _showKeywordSearch()
		{
			_searchBox.classList.remove('hide');
			_locationBox.classList.add('hide');
			document.getElementById('location').value = "";

			storage.setItem(_storageID, 'KEYWORD');
		}

		function _showLocationSearch()
		{
			_searchBox.classList.add('hide');
			_locationBox.classList.remove('hide');
			document.getElementById('keyword').value = "";

			storage.setItem(_storageID, 'LOCATION');
		}

	return {
		init:init
	};
});<commit_msg>Removes search switcher<commit_after><|endoftext|>


<commit_before>/* Copyright (c) 2016 Dmitri Kourennyi
   See the file COPYING for copying permission.
*/

var darkColor;
var lightColor;
var allColors;

const kInputElems = ["INPUT", "TEXTAREA", "SELECT", "BUTTON", "TOOLBARBUTTON"];

function isInputNode(node) {
    return kInputElems.indexOf(node.nodeName) > -1;
}

self.port.on("colors", function (colors) {
    'use strict';

    darkColor = colors[0];
    lightColor = colors[1];
    allColors = colors[2];

    checkInputs();
    if (allColors === true) {
        checkDoc();
    }

    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            for (var newNode of mutation.addedNodes) {
                // Get all input-like elements
                var nodeIterator = document.createNodeIterator(
                    newNode, NodeFilter.SHOW_ELEMENT, {
                        acceptNode: isInputNode
                    });
                var node;
                while ((node = nodeIterator.nextNode())) {
                    checkElementContrast(node);
                }

                if (allColors === true) {
                    // Recurse though new node
                    checkElementContrast(newNode);
                }
            }
        });
    });
    var config = {
        childList: true,
        subtree: true,
    };
    observer.observe(document, config);
});

function checkDoc() {
    checkElementContrast(document.all[0]);

    // Other checks required when browser is in quirks mode
    if (document.compatMode == "BackCompat") {
        // tables don't inherit color
        var tables = document.getElementsByTagName("table");
        for (var i = 0; i < tables.length; i++) {
            if (getComputedStyle(tables[i]).color ==
                getDefaultComputedStyle(tables[i]).color) {
                //if color has not been set explicitely, then force inherit
                tables[i].style.color = "inherit";
            }
        }
    }
}

function checkInputs() {
    'use strict';
    kInputElems.forEach(
        function (val) {
            var elements = document.getElementsByTagName(val),
                i;
            for (i = 0; i < elements.length; i += 1) {
                checkElementContrast(elements[i]);
            }
        });
}

function is_fg_defined(element) {
    return getComputedStyle(element).color !=
        getDefaultComputedStyle(element).color;
}

function is_bg_defined(element) {
    return (getComputedStyle(element).backgroundColor !=
        getDefaultComputedStyle(element).backgroundColor);
}

function is_bg_img_defined(element) {
    return (getComputedStyle(element).backgroundImage != 'none');
}

function checkElementContrast(element) {
    'use strict';

    var fg_color_defined = is_fg_defined(element);
    var bg_color_defined = is_bg_defined(element);
    var bg_img_defined = is_bg_img_defined(element);

    if (fg_color_defined && bg_color_defined) {
        //Both colors explicitely defined, nothing to do
        return;
    }

    if (!fg_color_defined && bg_color_defined) {
        // always set fg if only bg color defined
        element.style.color = darkColor;
        return;
    }

    if (fg_color_defined && !bg_color_defined) {
        //since bg images might be transparent, set the bg color no
        //matter what
        element.style.backgroundColor = lightColor;
        return;
    }

    if (bg_img_defined) {
        //No FG or BG color, but possibly transparent image, so need
        //to set both
        element.style.color = darkColor;
        element.style.backgroundColor = lightColor;
        return;
    }

    //Nothing defined, recurse through children
    var children = element.children;
    for (var i = 0; i < children.length; i++) {
        // Don't look at non-renderable elements
        switch (element.children[i].nodeName) {
        case "HEAD":
        case "TITLE":
        case "META":
        case "SCRIPT":
        case "IMG":
        case "STYLE":
            break;
        default:
            checkElementContrast(element.children[i]);
        }
    }
    return;
}
<commit_msg>Don't recolor added elements if any parents' already define colors<commit_after>/* Copyright (c) 2016 Dmitri Kourennyi
   See the file COPYING for copying permission.
*/

var darkColor;
var lightColor;
var allColors;

const kInputElems = ["INPUT", "TEXTAREA", "SELECT", "BUTTON", "TOOLBARBUTTON"];

function isInputNode(node) {
    return kInputElems.indexOf(node.nodeName) > -1;
}

self.port.on("colors", function (colors) {
    'use strict';

    darkColor = colors[0];
    lightColor = colors[1];
    allColors = colors[2];

    checkInputs();
    if (allColors === true) {
        checkDoc();
    }

    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            for (var newNode of mutation.addedNodes) {
                // Get all input-like elements
                var nodeIterator = document.createNodeIterator(
                    newNode, NodeFilter.SHOW_ELEMENT, {
                        acceptNode: isInputNode
                    });
                var node;
                while ((node = nodeIterator.nextNode())) {
                    checkElementContrast(node);
                }

                if (allColors === true) {
                    var parent = newNode.parentElement;
                    var defined = false;
                    while (parent !== null) {
                        if (is_fg_defined(parent) ||
                            is_bg_defined(parent) ||
                            is_bg_img_defined(parent)) {
                            // If any parents' color property is defined,
                            // new elements don't need recolor.
                            defined = true;
                            break;
                        }
                        parent = parent.parentElement;
                    }
                    if (!defined) {
                        checkElementContrast(newNode);
                    }
                }
            }
        });
    });
    var config = {
        childList: true,
        subtree: true,
    };
    observer.observe(document, config);
});

function checkDoc() {
    checkElementContrast(document.all[0]);

    // Other checks required when browser is in quirks mode
    if (document.compatMode == "BackCompat") {
        // tables don't inherit color
        var tables = document.getElementsByTagName("table");
        for (var i = 0; i < tables.length; i++) {
            if (getComputedStyle(tables[i]).color ==
                getDefaultComputedStyle(tables[i]).color) {
                //if color has not been set explicitely, then force inherit
                tables[i].style.color = "inherit";
            }
        }
    }
}

function checkInputs() {
    'use strict';
    kInputElems.forEach(
        function (val) {
            var elements = document.getElementsByTagName(val),
                i;
            for (i = 0; i < elements.length; i += 1) {
                checkElementContrast(elements[i]);
            }
        });
}

function is_fg_defined(element) {
    return getComputedStyle(element).color !=
        getDefaultComputedStyle(element).color;
}

function is_bg_defined(element) {
    return (getComputedStyle(element).backgroundColor !=
        getDefaultComputedStyle(element).backgroundColor);
}

function is_bg_img_defined(element) {
    return (getComputedStyle(element).backgroundImage != 'none');
}

function checkElementContrast(element) {
    'use strict';

    var fg_color_defined = is_fg_defined(element);
    var bg_color_defined = is_bg_defined(element);
    var bg_img_defined = is_bg_img_defined(element);

    if (fg_color_defined && bg_color_defined) {
        //Both colors explicitely defined, nothing to do
        return;
    }

    if (!fg_color_defined && bg_color_defined) {
        // always set fg if only bg color defined
        element.style.color = darkColor;
        return;
    }

    if (fg_color_defined && !bg_color_defined) {
        //since bg images might be transparent, set the bg color no
        //matter what
        element.style.backgroundColor = lightColor;
        return;
    }

    if (bg_img_defined) {
        //No FG or BG color, but possibly transparent image, so need
        //to set both
        element.style.color = darkColor;
        element.style.backgroundColor = lightColor;
        return;
    }

    //Nothing defined, recurse through children
    var children = element.children;
    for (var i = 0; i < children.length; i++) {
        // Don't look at non-renderable elements
        switch (element.children[i].nodeName) {
        case "HEAD":
        case "TITLE":
        case "META":
        case "SCRIPT":
        case "IMG":
        case "STYLE":
            break;
        default:
            checkElementContrast(element.children[i]);
        }
    }
    return;
}
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var computeCis = require( './cis.js' );

// MAIN //

/**
* Computes the cis function of a complex number.
*
* @param {(Array|TypedArray|Object)} [out] - output array
* @param {number} re - real component
* @param {number} im - imaginary component
* @returns {(Array|TypedArray|Object)} output array
*
* @example
* var v = cis( 0.0, 0.0 );
* // returns [ 1.0, 0.0 ]
*
* @example
* var v = cis( 1.0, 0.0 );
* // returns [ ~0.540, ~0.841 ]
*
* @example
* var out = new Array( 2 );
*
* var v = cis( out, 1.0, 0.0 );
* // returns [ ~0.540, ~0.841 ]
*
* var bool = ( v === out );
* // returns true
*/
function cis( out, re, im ) {
	if ( arguments.length === 2 ) {
		return computeCis( new Array( 2 ), out, re );
	}
	return computeCis( out, re, im );
} // end FUNCTION cis()

// EXPORTS //

module.exports = cis;
<commit_msg>Provide an initialized array<commit_after>'use strict';

// MODULES //

var computeCis = require( './cis.js' );

// MAIN //

/**
* Computes the cis function of a complex number.
*
* @param {(Array|TypedArray|Object)} [out] - output array
* @param {number} re - real component
* @param {number} im - imaginary component
* @returns {(Array|TypedArray|Object)} output array
*
* @example
* var v = cis( 0.0, 0.0 );
* // returns [ 1.0, 0.0 ]
*
* @example
* var v = cis( 1.0, 0.0 );
* // returns [ ~0.540, ~0.841 ]
*
* @example
* var out = new Array( 2 );
*
* var v = cis( out, 1.0, 0.0 );
* // returns [ ~0.540, ~0.841 ]
*
* var bool = ( v === out );
* // returns true
*/
function cis( out, re, im ) {
	if ( arguments.length === 2 ) {
		return computeCis( [ 0.0, 0.0 ], out, re );
	}
	return computeCis( out, re, im );
} // end FUNCTION cis()

// EXPORTS //

module.exports = cis;
<|endoftext|>


<commit_before>if(!Draw.image) {
  Draw.image = function(image, x, y, w, h, sx, sy, sw, sh) {
    if(!Canvas.configured) {
      console.warn("KAPhy Warning - You must use Canvas.configure(); before you can draw!");
      return;
    }

    var realX = x,
        realY = y,
        realW = w,
        realH = h;

    switch (Draw.currentImageMode) {
      case CORNERS:
        realW = w - x;
        realH = h - y;
        break;
      case RADIUS:
        realX = x + w / 2;
        realY = y + h / 2;
        realW = w * 2;
        realH = h * 2;
        break;
      case CENTER:
        realX = x - w / 2;
        realY = y - h / 2;
        break;
    }

    realX = Canvas.getX(realX);
    realY = Canvas.getY(realY);
    realW = Canvas.getX(realW);
    realH = Canvas.getY(realH);

    switch(arguments.length) {
      case 3: Canvas.context.drawImage(image, realX, realY); break;
      case 5: Canvas.context.drawImage(image, realX, realY, realW, realH); break;
      case 9: Canvas.context.drawImage(image, realX, realY, realW, realH, sx, sy, sw, sh); break;
      default: console.warn("KAPhy Warning - Draw.image() takes 3, 5, or 9 arguments."); break;
    }
  };
}
<commit_msg>Update image.js<commit_after>if(!Draw.image) {
  Draw.image = function(image, x, y, w, h, sx, sy, sw, sh) {
    if(!Canvas.configured) {
      console.warn("KAPhy Warning - You must use Canvas.configure(); before you can draw!");
      return;
    }

    if(Draw.shapeOn) {
      console.warn("KAPhy Warning - You can't use other drawing commands when in shape mode.");
      return;
    }

    var realX = x,
        realY = y,
        realW = w,
        realH = h;

    switch (Draw.currentImageMode) {
      case CORNERS:
        realW = w - x;
        realH = h - y;
        break;
      case RADIUS:
        realX = x + w / 2;
        realY = y + h / 2;
        realW = w * 2;
        realH = h * 2;
        break;
      case CENTER:
        realX = x - w / 2;
        realY = y - h / 2;
        break;
    }

    realX = Canvas.getX(realX);
    realY = Canvas.getY(realY);
    realW = Canvas.getX(realW);
    realH = Canvas.getY(realH);

    switch(arguments.length) {
      case 3: Canvas.context.drawImage(image, realX, realY); break;
      case 5: Canvas.context.drawImage(image, realX, realY, realW, realH); break;
      case 9: Canvas.context.drawImage(image, realX, realY, realW, realH, sx, sy, sw, sh); break;
      default: console.warn("KAPhy Warning - Draw.image() takes 3, 5, or 9 arguments."); break;
    }
  };
}
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'cheap-module-eval-source-map',
  entry: [
    'eventsource-polyfill', // necessary for hot reloading with IE
    'webpack-hot-middleware/client',
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }]
  }
};
<commit_msg>Add some notes<commit_after>var path = require('path');
var webpack = require('webpack');

module.exports = {
  // or devtool: 'eval' to debug issues with compiled output:
  devtool: 'cheap-module-eval-source-map',
  entry: [
    // necessary for hot reloading with IE:
    'eventsource-polyfill',
    // listen to code updates emitted by hot middleware:
    'webpack-hot-middleware/client',
    // your code:
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: '/dist/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }]
  }
};
<|endoftext|>


<commit_before>(function () {
  Polymer({
    is: 'uql-mega-menu',

    subMenuWidth: 480,

    properties: {

      /** menu structure
       * @type {{ "heading": String, "items": Array }} */
      menu: {
        type: Object,
        observer: '_menuChanged'
      },

      /** debugging flag */
      verbose: {
        type: Boolean,
        value: true
      },

      /** currently selected top level menu item */
      selectedMenuItemIndex: {
        type: Number,
        observer: '_selectedMenuChanged'

      }
    },

    _selectedMenuChanged: function(newValue, oldValue) {
      var that = this;

      //temp fix for FFox/Safari, menu item is selected before all elements are drawn on the screen
      //making selection underline misplaced
      window.setTimeout(function() {
        that.$.topMenu.select(newValue);
        if(that.$.topMenu.focusedItem) {
          that.$.topMenu.focusedItem.blur();
        }
      }, 1000);
    },

		/**
     * Called when a sub menu item is clicked
     * @param event
     * @private
     */
    _closeDropdown: function(event) {
      var menuItem = event.target;
      while(!menuItem.getAttribute('data-item-index')) {
        menuItem = menuItem.parentElement;
      }

      this._gaLinkClicked(menuItem.href);

      var tabIndex = Number(menuItem.getAttribute('data-item-index'));
      var subMenu = this.querySelector('#subMenu' + tabIndex);
      subMenu.close();
    },

    _topMenuSelected: function(event) {
      var menuItem = event.target;
      while(!menuItem.getAttribute('data-item-index')) {
        menuItem = menuItem.parentElement;
      }

      var tabIndex = Number(menuItem.getAttribute('data-item-index'));
      var currentItem = this.menu.items[tabIndex];

      //close any other opened sub-menus
      var selectedTabs = this.querySelectorAll('.sub-menu-opened');
      for(var index=0; index < selectedTabs.length; index++) {
        var selectedTab = selectedTabs[index];
        var openedTabIndex = Number(selectedTab.getAttribute('data-item-index'));
        var openedSubMenu = this.querySelector('#subMenu' + openedTabIndex);
        openedSubMenu.close();
      }

      if (currentItem.items) {

        var subMenu = this.querySelector('#subMenu' + tabIndex);

        if(menuItem.className.indexOf("sub-menu-opened") >= 0) {
          //if menu is already opened, close it
          menuItem.toggleClass("sub-menu-opened");
          subMenu.close();
        } else {
          //open sub menu for top level menu item
          subMenu.positionTarget = menuItem;
          menuItem.toggleClass("sub-menu-opened");

          //adjust sub-menu display for narrow screens
          var screenWidth = window.innerWidth || document.getElementsByTagName('body')[0].clientWidth;
          var tabCoords = menuItem.getBoundingClientRect();
          if (tabCoords.left + this.subMenuWidth > screenWidth) {
            subMenu.horizontalOffset = screenWidth - tabCoords.left - this.subMenuWidth;
          }

          subMenu.open();

          this._gaTopMenuClicked(currentItem.label);
        }
      } else {
        //follow the top level link
        this._gaLinkClicked(currentItem.href);
        window.location.href = currentItem.href;
      }
    },
		/**
     * Called when a menu item is clicked that has a dropdown menu
     * @param label
     * @private
     */
    _gaTopMenuClicked: function (label) {
      this.$.ga.addEvent('Navigate', "Top menu " + label);
    },
		/**
     * Called when a link is clicked in the menu
     * @param link
     * @private
     */
    _gaLinkClicked: function (link) {
      this.$.ga.addEvent('Click', link);
    },

    _subMenuClosed: function(event) {
      //reset styles
      var menuIndex = Number(event.target.getAttribute('data-item-index'));
      var deselectedTab = this.querySelectorAll('paper-tab')[menuIndex];

      if(deselectedTab && deselectedTab.className.indexOf("sub-menu-opened") >= 0)
        deselectedTab.toggleClass("sub-menu-opened");
    },

    ready: function() {
      var that = this;

      setTimeout(function() {
        //reveal elements with easing in effect
        var content = that.querySelectorAll(".loading");

        for(var i=0; i< content.length; i++){
          var element = content[i];
          element.removeAttribute('unresolved');
        }
      }, 0);
    },

    _menuChanged: function(newValue, oldValue) {
      console.log('menuChanged');

      if (newValue !== null && newValue.items) {
        for(var itemIndex = 0; itemIndex < newValue.items.length; itemIndex++) {
          var element = newValue.items[itemIndex];
          element.hasRight = false;

          if (element.items) {
            for(var subIndex = 0; subIndex < element.items.length; subIndex++) {
              var subItem = element.items[subIndex];
              if (subItem.right) {
                element.hasRight = true;
              }
            }
          }
        }
      }
    }

  })
  ;
})();<commit_msg>removing debugging info<commit_after>(function () {
  Polymer({
    is: 'uql-mega-menu',

    subMenuWidth: 480,

    properties: {

      /** menu structure
       * @type {{ "heading": String, "items": Array }} */
      menu: {
        type: Object,
        observer: '_menuChanged'
      },

      /** debugging flag */
      verbose: {
        type: Boolean,
        value: true
      },

      /** currently selected top level menu item */
      selectedMenuItemIndex: {
        type: Number,
        observer: '_selectedMenuChanged'

      }
    },

    _selectedMenuChanged: function(newValue, oldValue) {
      var that = this;

      //temp fix for FFox/Safari, menu item is selected before all elements are drawn on the screen
      //making selection underline misplaced
      window.setTimeout(function() {
        that.$.topMenu.select(newValue);
        if(that.$.topMenu.focusedItem) {
          that.$.topMenu.focusedItem.blur();
        }
      }, 1000);
    },

		/**
     * Called when a sub menu item is clicked
     * @param event
     * @private
     */
    _closeDropdown: function(event) {
      var menuItem = event.target;
      while(!menuItem.getAttribute('data-item-index')) {
        menuItem = menuItem.parentElement;
      }

      this._gaLinkClicked(menuItem.href);

      var tabIndex = Number(menuItem.getAttribute('data-item-index'));
      var subMenu = this.querySelector('#subMenu' + tabIndex);
      subMenu.close();
    },

    _topMenuSelected: function(event) {
      var menuItem = event.target;
      while(!menuItem.getAttribute('data-item-index')) {
        menuItem = menuItem.parentElement;
      }

      var tabIndex = Number(menuItem.getAttribute('data-item-index'));
      var currentItem = this.menu.items[tabIndex];

      //close any other opened sub-menus
      var selectedTabs = this.querySelectorAll('.sub-menu-opened');
      for(var index=0; index < selectedTabs.length; index++) {
        var selectedTab = selectedTabs[index];
        var openedTabIndex = Number(selectedTab.getAttribute('data-item-index'));
        var openedSubMenu = this.querySelector('#subMenu' + openedTabIndex);
        openedSubMenu.close();
      }

      if (currentItem.items) {

        var subMenu = this.querySelector('#subMenu' + tabIndex);

        if(menuItem.className.indexOf("sub-menu-opened") >= 0) {
          //if menu is already opened, close it
          menuItem.toggleClass("sub-menu-opened");
          subMenu.close();
        } else {
          //open sub menu for top level menu item
          subMenu.positionTarget = menuItem;
          menuItem.toggleClass("sub-menu-opened");

          //adjust sub-menu display for narrow screens
          var screenWidth = window.innerWidth || document.getElementsByTagName('body')[0].clientWidth;
          var tabCoords = menuItem.getBoundingClientRect();
          if (tabCoords.left + this.subMenuWidth > screenWidth) {
            subMenu.horizontalOffset = screenWidth - tabCoords.left - this.subMenuWidth;
          }

          subMenu.open();

          this._gaTopMenuClicked(currentItem.label);
        }
      } else {
        //follow the top level link
        this._gaLinkClicked(currentItem.href);
        window.location.href = currentItem.href;
      }
    },
		/**
     * Called when a menu item is clicked that has a dropdown menu
     * @param label
     * @private
     */
    _gaTopMenuClicked: function (label) {
      this.$.ga.addEvent('Navigate', "Top menu " + label);
    },
		/**
     * Called when a link is clicked in the menu
     * @param link
     * @private
     */
    _gaLinkClicked: function (link) {
      this.$.ga.addEvent('Click', link);
    },

    _subMenuClosed: function(event) {
      //reset styles
      var menuIndex = Number(event.target.getAttribute('data-item-index'));
      var deselectedTab = this.querySelectorAll('paper-tab')[menuIndex];

      if(deselectedTab && deselectedTab.className.indexOf("sub-menu-opened") >= 0)
        deselectedTab.toggleClass("sub-menu-opened");
    },

    ready: function() {
      var that = this;

      setTimeout(function() {
        //reveal elements with easing in effect
        var content = that.querySelectorAll(".loading");

        for(var i=0; i< content.length; i++){
          var element = content[i];
          element.removeAttribute('unresolved');
        }
      }, 0);
    },

    _menuChanged: function(newValue, oldValue) {

      if (newValue !== null && newValue.items) {
        for(var itemIndex = 0; itemIndex < newValue.items.length; itemIndex++) {
          var element = newValue.items[itemIndex];
          element.hasRight = false;

          if (element.items) {
            for(var subIndex = 0; subIndex < element.items.length; subIndex++) {
              var subItem = element.items[subIndex];
              if (subItem.right) {
                element.hasRight = true;
              }
            }
          }
        }
      }
    }

  })
  ;
})();
<|endoftext|>


<commit_before>"use strict";

const express = require("express");
const app = express();

// install request
const request = require('request');

const PORT = process.env.PORT || 3000;
const rovi = process.env.ROVI_APIKEY || '';

// API page with CORS header
app.get('/api/:artist/:sig', (req, res) => {
  const artist = req.params.artist;
  const sig = req.params.sig;

  console.log(artist);
  console.log(sig);
  console.log(rovi);
  console.log(PORT);
  console.log(process.env);

  const url = "http://api.rovicorp.com/search/v2.1/music/search?apikey=" + rovi + "&sig=" + sig + "&query=" + artist + "&entitytype=artist&size=1&include=images,musicbio,discography,videos&formatid=16";

  console.log(url);

  request.get(url, (err, response, body) => {
    if (err) throw err;

    res.header('Access-Control-Allow-Origin', '*');
    res.send(JSON.parse(body));
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
<commit_msg>More search result filters: image size, only main albums<commit_after>"use strict";

const express = require("express");
const app = express();

// install request
const request = require('request');

const PORT = process.env.PORT || 3000;
const rovi = process.env.ROVI_APIKEY || '';

// API page with CORS header
app.get('/api/:artist/:sig', (req, res) => {
  const artist = req.params.artist;
  const sig = req.params.sig;

  console.log(artist);
  console.log(sig);
  console.log(rovi);
  console.log(PORT);
  console.log(process.env);

  const url = "http://api.rovicorp.com/search/v2.1/music/search?apikey=" + rovi + "&sig=" + sig + "&query=" + artist + "&entitytype=artist&size=1&include=images,musicbio,discography,videos&formatid=62&type=main";

  console.log(url);

  request.get(url, (err, response, body) => {
    if (err) throw err;

    res.header('Access-Control-Allow-Origin', '*');
    res.send(JSON.parse(body));
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
<|endoftext|>