<commit_before>import React, {
	render,
	createClass,
	createElement,
	cloneElement,
	findDOMNode,
	Component,
	PropTypes,
	// eslint-disable-next-line camelcase
	unstable_renderSubtreeIntoContainer,
	__spread
} from '../../../src/compat';
import { setupScratch, teardown } from '../../_util/helpers';

let ce = type => document.createElement(type);
let text = text => document.createTextNode(text);

describe('preact-compat', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	describe('render()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('render')
				.that.is.a('function')
				.that.equals(render);
		});

		it('should replace isomorphic content', () => {
			let root = ce('div');
			let initialChild = ce('div');
			initialChild.appendChild(text('initial content'));
			root.appendChild(initialChild);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should remove extra elements', () => {
			let root = ce('div');

			let c1 = ce('div');
			c1.appendChild(text('isomorphic content'));
			root.appendChild(c1);

			let c2 = ce('div');
			c2.appendChild(text('extra content'));
			root.appendChild(c2);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should remove text nodes', () => {
			let root = ce('div');

			root.appendChild(text('Text Content in the root'));
			root.appendChild(text('More Text Content'));

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should support defaultValue', () => {
			render(<input defaultValue="foo"></input>, scratch);
			expect(scratch.firstElementChild).to.have.property('value', 'foo');
		});
	});

	describe('createClass()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('createClass')
				.that.is.a('function')
				.that.equals(createClass);
		});

		it('should create a Component', () => {
			let specState = { something: 1 };
			let spec = {
				foo: 'bar',
				getInitialState() {
					return specState;
				},
				method: sinon.spy()
			};
			const C = createClass(spec);
			let inst = new C();
			expect(inst).to.have.property('foo', 'bar');
			expect(inst).to.have.property('state', specState);
			expect(inst).to.have.property('method').that.is.a('function');
			expect(inst).to.be.an.instanceof(Component);
			inst.method('a','b');
			expect(spec.method)
				.to.have.been.calledOnce
				.and.calledOn(inst)
				.and.calledWithExactly('a', 'b');
		});

		it('should not bind blacklisted methods', () => {
			let constructor = () => {};
			let render = () => null;
			const C = createClass({
				constructor,
				render
			});
			let c = new C();
			expect(c).to.have.property('constructor').that.equals(constructor);
			expect(c).to.have.property('render').not.with.property('__bound');
		});

		it('should copy statics', () => {
			let def = {
				statics: {
					foo: 'bar',
					baz() {}
				}
			};
			let c = createClass(def);
			expect(c).to.have.property('foo', def.statics.foo);
			expect(c).to.have.property('baz', def.statics.baz);
		});

		it('should support mixins', () => {
			let def = {
				mixins: [
					{
						foo: sinon.spy(),
						bar: sinon.spy()
					},
					{
						bar: sinon.spy(),
						componentWillMount: sinon.spy(),
						render: 'nothing here'
					},
					{
						componentWillMount: sinon.spy()
					}
				],
				foo: sinon.spy(),
				componentWillMount: sinon.spy(),
				render: sinon.stub().returns(null)
			};
			let C = createClass(def);
			let inst = new C();

			inst.foo();
			expect(def.foo).to.have.been.calledOnce;
			expect(def.mixins[0].foo).to.have.been.calledOnce.and.calledBefore(def.foo);

			inst.bar();
			expect(def.mixins[0].bar).to.have.been.calledOnce;
			expect(def.mixins[1].bar).to.have.been.calledOnce.and.calledAfter(def.mixins[0].bar);

			let props = {},
				state = {};
			inst.componentWillMount(props, state);
			expect(def.mixins[1].componentWillMount)
				.to.have.been.calledOnce
				.and.calledWithExactly(props, state);
			expect(def.mixins[2].componentWillMount)
				.to.have.been.calledOnce
				.and.calledWithExactly(props, state)
				.and.calledAfter(def.mixins[1].componentWillMount);

			expect(inst.render(props, state)).to.equal(null);
		});
	});

	describe('createElement()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('createElement')
				.that.is.a('function')
				.that.equals(createElement);
		});

		it('should normalize vnodes', () => {
			let vnode =
t
;
			let $$typeof = 0xeac7;
			try {
				// eslint-disable-next-line
				if (Function.prototype.toString.call(eval('Sym'+'bol.for')).match(/\[native code\]/)) {
					// eslint-disable-next-line
					$$typeof = eval('Sym'+'bol.for("react.element")');
				}
			}
			catch (e) {}
			expect(vnode).to.have.property('$$typeof', $$typeof);
			expect(vnode).to.have.property('type', 'div');
			expect(vnode).to.have.property('props').that.is.an('object');
			expect(vnode.props).to.have.property('children');
			expect(vnode.props.children).to.have.property('$$typeof', $$typeof);
			expect(vnode.props.children).to.have.property('type', 'a');
			expect(vnode.props.children).to.have.property('props').that.is.an('object');
			expect(vnode.props.children.props).to.eql({ children: 't' });
		});

		it('should normalize onChange', () => {
			let props = { onChange(){} };

			function expectToBeNormalized(vnode, desc) {
				expect(vnode, desc).to.have.property('props').with.all.keys(['oninput'].concat(vnode.props.type ? 'type' : [])).and.property('oninput').that.is.a('function');
			}

			function expectToBeUnmodified(vnode, desc) {
				expect(vnode, desc).to.have.property('props').eql({ ...props, ...(vnode.props.type ? { type: vnode.props.type } : {}) });
			}

			expectToBeUnmodified(
, '
');
			expectToBeUnmodified(<input {...props} type="radio" />, '<input type="radio">');
			expectToBeUnmodified(<input {...props} type="checkbox" />, '<input type="checkbox">');
			expectToBeUnmodified(<input {...props} type="file" />, '<input type="file">');

			expectToBeNormalized(<textarea {...props} />, '<textarea>');
			expectToBeNormalized(<input {...props} />, '<input>');
			expectToBeNormalized(<input {...props} type="text" />, '<input type="text">');

		});
	});

	describe('Component', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('Component')
				.that.is.a('function')
				.that.equals(Component);
		});
	});

	describe('PropTypes', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('PropTypes')
				.that.is.an('object')
				.that.equals(PropTypes);
		});
	});

	describe('cloneElement', () => {
		it('should clone elements', () => {
			let element = <foo a="b" c="d">ab</foo>;
			expect(cloneElement(element)).to.eql(element);
		});

		it('should support props.children', () => {
			let element = <foo children={b}></foo>;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(cloneElement(clone).props.children).to.eql(element.props.children);
		});

		it('children take precedence over props.children', () => {
			let element = <foo children={c}>
b
</foo>;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(clone.props.children.type).to.eql('div');
		});

		it('should support children in prop argument', () => {
			let element = <foo></foo>;
			let children = [b];
			let clone = cloneElement(element, { children });
			expect(clone.props.children).to.eql(children);
		});

		it('children argument takes precedence over props.children', () => {
			let element = <foo></foo>;
			let childrenA = [b];
			let childrenB = [
c
];
			let clone = cloneElement(element, { children: childrenA }, ...childrenB);
			expect(clone.props.children).to.eql(childrenB);
		});

		it('children argument takes precedence over props.children even if falsey', () => {
			let element = <foo></foo>;
			let childrenA = [b];
			let clone = cloneElement(element, { children: childrenA }, undefined);
			expect(clone.children).to.eql(undefined);
		});
	});

	describe('findDOMNode()', () => {
		class Helper extends React.Component {
			render({ something }) {
				if (something == null) return null;
				if (something === false) return null;
				return
;
			}
		}

		it('should return DOM Node if render is not false nor null', () => {
			let scratch = document.createElement('div');
			(document.body || document.documentElement).appendChild(scratch);
			const helper = React.render(<Helper />, scratch);
			expect(findDOMNode(helper)).to.be.instanceof(Node);
		});

		it('should return null if given null', () => {
			expect(findDOMNode(null)).to.be.null;
		}),

		it('should return a regular DOM Element if given a regular DOM Element', () => {
			let scratch = document.createElement('div');
			expect(findDOMNode(scratch)).to.equal(scratch);
		}),

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		xit('should return null if render returns false', () => {
			const helper = React.render(<Helper something={false} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		xit('should return null if render returns null', () => {
			const helper = React.render(<Helper something={null} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});
	});

	describe('unstable_renderSubtreeIntoContainer', () => {
		class Inner extends Component {
			render() {
				return null;
			}
			getNode() {
				return 'inner';
			}
		}

		it('should export instance', () => {
			class App extends Component {
				render() {
					return null;
				}
				componentDidMount() {
					this.renderInner();
				}
				renderInner() {
					const wrapper = document.createElement('div');
					this.inner = unstable_renderSubtreeIntoContainer(this, <Inner />, wrapper);
				}
			}
			const app = render(<App />, scratch);
			expect(typeof app.inner.getNode === 'function').to.equal(true);
		});

		it('should there must be a context in callback', () => {
			class App extends Component {
				render() {
					return null;
				}
				componentDidMount() {
					this.renderInner();
				}
				renderInner() {
					const wrapper = document.createElement('div');
					const self = this;
					unstable_renderSubtreeIntoContainer(this, <Inner />, wrapper, function() {
						self.inner = this;
					});
				}
			}
			const app = render(<App />, scratch);
			expect(typeof app.inner.getNode === 'function').to.equal(true);
		});
	});

	describe('Unsupported hidden internal __spread API', () => {
		it('should work with multiple objects', () => {
			const start = {};
			const result = React.__spread(start, { one: 1, two: 3 }, { two: 2 });
			expect(result).to.equal(start);
			expect(start).to.deep.equal({ one: 1, two: 2 });
		});

		it('should be exported on default and as __spread', () => {
			expect(__spread).to.equal(React.__spread);
		});
	});
});
<commit_msg>Skip invalid findDOMNode test<commit_after>import React, {
	render,
	createClass,
	createElement,
	cloneElement,
	findDOMNode,
	Component,
	PropTypes,
	// eslint-disable-next-line camelcase
	unstable_renderSubtreeIntoContainer,
	__spread
} from '../../../src/compat';
import { setupScratch, teardown } from '../../_util/helpers';

let ce = type => document.createElement(type);
let text = text => document.createTextNode(text);

describe('preact-compat', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	describe('render()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('render')
				.that.is.a('function')
				.that.equals(render);
		});

		it('should replace isomorphic content', () => {
			let root = ce('div');
			let initialChild = ce('div');
			initialChild.appendChild(text('initial content'));
			root.appendChild(initialChild);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should remove extra elements', () => {
			let root = ce('div');

			let c1 = ce('div');
			c1.appendChild(text('isomorphic content'));
			root.appendChild(c1);

			let c2 = ce('div');
			c2.appendChild(text('extra content'));
			root.appendChild(c2);

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should remove text nodes', () => {
			let root = ce('div');

			root.appendChild(text('Text Content in the root'));
			root.appendChild(text('More Text Content'));

			render(
dynamic content
, root);
			expect(root)
				.to.have.property('textContent')
				.that.is.a('string')
				.that.equals('dynamic content');
		});

		it('should support defaultValue', () => {
			render(<input defaultValue="foo"></input>, scratch);
			expect(scratch.firstElementChild).to.have.property('value', 'foo');
		});
	});

	describe('createClass()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('createClass')
				.that.is.a('function')
				.that.equals(createClass);
		});

		it('should create a Component', () => {
			let specState = { something: 1 };
			let spec = {
				foo: 'bar',
				getInitialState() {
					return specState;
				},
				method: sinon.spy()
			};
			const C = createClass(spec);
			let inst = new C();
			expect(inst).to.have.property('foo', 'bar');
			expect(inst).to.have.property('state', specState);
			expect(inst).to.have.property('method').that.is.a('function');
			expect(inst).to.be.an.instanceof(Component);
			inst.method('a','b');
			expect(spec.method)
				.to.have.been.calledOnce
				.and.calledOn(inst)
				.and.calledWithExactly('a', 'b');
		});

		it('should not bind blacklisted methods', () => {
			let constructor = () => {};
			let render = () => null;
			const C = createClass({
				constructor,
				render
			});
			let c = new C();
			expect(c).to.have.property('constructor').that.equals(constructor);
			expect(c).to.have.property('render').not.with.property('__bound');
		});

		it('should copy statics', () => {
			let def = {
				statics: {
					foo: 'bar',
					baz() {}
				}
			};
			let c = createClass(def);
			expect(c).to.have.property('foo', def.statics.foo);
			expect(c).to.have.property('baz', def.statics.baz);
		});

		it('should support mixins', () => {
			let def = {
				mixins: [
					{
						foo: sinon.spy(),
						bar: sinon.spy()
					},
					{
						bar: sinon.spy(),
						componentWillMount: sinon.spy(),
						render: 'nothing here'
					},
					{
						componentWillMount: sinon.spy()
					}
				],
				foo: sinon.spy(),
				componentWillMount: sinon.spy(),
				render: sinon.stub().returns(null)
			};
			let C = createClass(def);
			let inst = new C();

			inst.foo();
			expect(def.foo).to.have.been.calledOnce;
			expect(def.mixins[0].foo).to.have.been.calledOnce.and.calledBefore(def.foo);

			inst.bar();
			expect(def.mixins[0].bar).to.have.been.calledOnce;
			expect(def.mixins[1].bar).to.have.been.calledOnce.and.calledAfter(def.mixins[0].bar);

			let props = {},
				state = {};
			inst.componentWillMount(props, state);
			expect(def.mixins[1].componentWillMount)
				.to.have.been.calledOnce
				.and.calledWithExactly(props, state);
			expect(def.mixins[2].componentWillMount)
				.to.have.been.calledOnce
				.and.calledWithExactly(props, state)
				.and.calledAfter(def.mixins[1].componentWillMount);

			expect(inst.render(props, state)).to.equal(null);
		});
	});

	describe('createElement()', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('createElement')
				.that.is.a('function')
				.that.equals(createElement);
		});

		it('should normalize vnodes', () => {
			let vnode =
t
;
			let $$typeof = 0xeac7;
			try {
				// eslint-disable-next-line
				if (Function.prototype.toString.call(eval('Sym'+'bol.for')).match(/\[native code\]/)) {
					// eslint-disable-next-line
					$$typeof = eval('Sym'+'bol.for("react.element")');
				}
			}
			catch (e) {}
			expect(vnode).to.have.property('$$typeof', $$typeof);
			expect(vnode).to.have.property('type', 'div');
			expect(vnode).to.have.property('props').that.is.an('object');
			expect(vnode.props).to.have.property('children');
			expect(vnode.props.children).to.have.property('$$typeof', $$typeof);
			expect(vnode.props.children).to.have.property('type', 'a');
			expect(vnode.props.children).to.have.property('props').that.is.an('object');
			expect(vnode.props.children.props).to.eql({ children: 't' });
		});

		it('should normalize onChange', () => {
			let props = { onChange(){} };

			function expectToBeNormalized(vnode, desc) {
				expect(vnode, desc).to.have.property('props').with.all.keys(['oninput'].concat(vnode.props.type ? 'type' : [])).and.property('oninput').that.is.a('function');
			}

			function expectToBeUnmodified(vnode, desc) {
				expect(vnode, desc).to.have.property('props').eql({ ...props, ...(vnode.props.type ? { type: vnode.props.type } : {}) });
			}

			expectToBeUnmodified(
, '
');
			expectToBeUnmodified(<input {...props} type="radio" />, '<input type="radio">');
			expectToBeUnmodified(<input {...props} type="checkbox" />, '<input type="checkbox">');
			expectToBeUnmodified(<input {...props} type="file" />, '<input type="file">');

			expectToBeNormalized(<textarea {...props} />, '<textarea>');
			expectToBeNormalized(<input {...props} />, '<input>');
			expectToBeNormalized(<input {...props} type="text" />, '<input type="text">');

		});
	});

	describe('Component', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('Component')
				.that.is.a('function')
				.that.equals(Component);
		});
	});

	describe('PropTypes', () => {
		it('should be exported', () => {
			expect(React)
				.to.have.property('PropTypes')
				.that.is.an('object')
				.that.equals(PropTypes);
		});
	});

	describe('cloneElement', () => {
		it('should clone elements', () => {
			let element = <foo a="b" c="d">ab</foo>;
			expect(cloneElement(element)).to.eql(element);
		});

		it('should support props.children', () => {
			let element = <foo children={b}></foo>;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(cloneElement(clone).props.children).to.eql(element.props.children);
		});

		it('children take precedence over props.children', () => {
			let element = <foo children={c}>
b
</foo>;
			let clone = cloneElement(element);
			expect(clone).to.eql(element);
			expect(clone.props.children.type).to.eql('div');
		});

		it('should support children in prop argument', () => {
			let element = <foo></foo>;
			let children = [b];
			let clone = cloneElement(element, { children });
			expect(clone.props.children).to.eql(children);
		});

		it('children argument takes precedence over props.children', () => {
			let element = <foo></foo>;
			let childrenA = [b];
			let childrenB = [
c
];
			let clone = cloneElement(element, { children: childrenA }, ...childrenB);
			expect(clone.props.children).to.eql(childrenB);
		});

		it('children argument takes precedence over props.children even if falsey', () => {
			let element = <foo></foo>;
			let childrenA = [b];
			let clone = cloneElement(element, { children: childrenA }, undefined);
			expect(clone.children).to.eql(undefined);
		});
	});

	describe('findDOMNode()', () => {
		class Helper extends React.Component {
			render({ something }) {
				if (something == null) return null;
				if (something === false) return null;
				return
;
			}
		}

		it.skip('should return DOM Node if render is not false nor null', () => {
			const helper = React.render(<Helper />, scratch);
			expect(findDOMNode(helper)).to.be.instanceof(Node);
		});

		it('should return null if given null', () => {
			expect(findDOMNode(null)).to.be.null;
		}),

		it('should return a regular DOM Element if given a regular DOM Element', () => {
			let scratch = document.createElement('div');
			expect(findDOMNode(scratch)).to.equal(scratch);
		}),

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		xit('should return null if render returns false', () => {
			const helper = React.render(<Helper something={false} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});

		// NOTE: React.render() returning false or null has the component pointing
		// 			to no DOM Node, in contrast, Preact always render an empty Text DOM Node.
		xit('should return null if render returns null', () => {
			const helper = React.render(<Helper something={null} />, scratch);
			expect(findDOMNode(helper)).to.be.null;
		});
	});

	describe('unstable_renderSubtreeIntoContainer', () => {
		class Inner extends Component {
			render() {
				return null;
			}
			getNode() {
				return 'inner';
			}
		}

		it('should export instance', () => {
			class App extends Component {
				render() {
					return null;
				}
				componentDidMount() {
					this.renderInner();
				}
				renderInner() {
					const wrapper = document.createElement('div');
					this.inner = unstable_renderSubtreeIntoContainer(this, <Inner />, wrapper);
				}
			}
			const app = render(<App />, scratch);
			expect(typeof app.inner.getNode === 'function').to.equal(true);
		});

		it('should there must be a context in callback', () => {
			class App extends Component {
				render() {
					return null;
				}
				componentDidMount() {
					this.renderInner();
				}
				renderInner() {
					const wrapper = document.createElement('div');
					const self = this;
					unstable_renderSubtreeIntoContainer(this, <Inner />, wrapper, function() {
						self.inner = this;
					});
				}
			}
			const app = render(<App />, scratch);
			expect(typeof app.inner.getNode === 'function').to.equal(true);
		});
	});

	describe('Unsupported hidden internal __spread API', () => {
		it('should work with multiple objects', () => {
			const start = {};
			const result = React.__spread(start, { one: 1, two: 3 }, { two: 2 });
			expect(result).to.equal(start);
			expect(start).to.deep.equal({ one: 1, two: 2 });
		});

		it('should be exported on default and as __spread', () => {
			expect(__spread).to.equal(React.__spread);
		});
	});
});
<|endoftext|>


<commit_before>import React from 'react';
import Dimensions from 'react-dimensions';
import Viewer from './viewer';

class ViewerResponsive extends React.Component {
  render() {
    let {containerWidth, containerHeight, children, ...props} = this.props;
    return (
      <Viewer {...props} width={containerWidth} height={containerHeight}>
        {children}
      </Viewer>
    )
  }
}

export default Dimensions()(ViewerResponsive)
<commit_msg>adds custom width and height on responsive component<commit_after>import React from 'react';
import Dimensions from 'react-dimensions';
import Viewer from './viewer';

class ViewerResponsive extends React.Component {
  render() {
    let {containerWidth, containerHeight, width, height, children, ...props} = this.props;
    width = width || containerWidth; height = height || containerHeight;
    return (
      <Viewer {...props} width={width} height={height}>
        {children}
      </Viewer>
    )
  }
}

export default Dimensions()(ViewerResponsive)
<|endoftext|>


<commit_before>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
});

function initializePage() {

}

/*
Name: class Class

Description: Class is the parent class of section. When we parse JSON,
	we will be creating sections instead.
	Why?  There could be two sections for the same course, such as CSE 3.
	The combination of their LE time and DI time should not overlapped
	with each other.

*/

class Class{

	/*
		crsNum(int)		: course number
		crsName(string)	: course name
		LE = Lecture	DI = Discussion
		LA = Lab		FI = Final
		LEtime, DItime, LAtime, FItime: array of instances of Timeinfo
	*/
	constructor(crsNum, crsName, LEtime, DItime,
		LAtime, FItime){
		this.crsNum = crsNum;
		this.crsName = crsName;
		this.LEtime = LEtime;
		this.DItime = DItime;
		this.LAtime = LAtime;
		this.FItime = FItime;
	}

	/*
	Function Name: addTime

	Description: Add the instance of time to the correct array.

	Input:
		category(string): 	the identifier of the time as LE, DI,
							LA, or FI.
		time(instance of Timeinfo): read Timeinfo class

	Ouput: nothing
	*/
	addTime(category, time){
		switch (category){
			case "LE":
				if(this.LEtime[0] == "")
					this.LEtime[0] = time;
				else
					this.LEtime.push(time);
				break;
			case "DI":
				if(this.DItime[0] == "")
					this.DItime[0] = time;
				else
					this.DItime.push(time);
				break;
			case "LA":
				if(this.LAtime[0] == "")
					this.LAtime[0] = time;
				else
					this.LAtime.push(time);
				break;
			case "FI":
				if(this.FItime[0] == "")
					this.FItime[0] = time;
				else
					this.FItime.push(time);
				break;
			default:
				console.log("Unidentified category.");
				break;
		}
	}

	/*
	Function Name: 	possibleTime

	Description: 	Get combinations of the time.
					Doesn't check for time conflict as there shouldnt be any

	Output: 		Returns array of possible time given the LE, DI, and LA.

					EX: if there are 2 LE, 4 DI
					it returns array of 8 different combinations
					of LE + DI.

	*/
	possibleTime(){
		let timeList = [];

		for( let le of this.LEtime ){
			for( let di of this.DItime ){
				for( let la of this.LAtime){
					timeList.push([le, di, la]);
				}
			}
		}
		return timeList;
	}
}

/*
Name: class Section

Description: 	child class of Class.
				When parsing json, we want to instantiate
				with this class.  Can call functcions of
				parent class.

				Two extra params are added:
				section and instructor.

*/
class Section extends Class{
	constructor(crsNum, crsName, LEtime, DItime,
		LAtime, FItime, section, instructor){

		super(crsNum, crsName, LEtime, DItime,
		LAtime, FItime);
		this.section = section;
		this.instructor = instructor;
	}
}

/*
Name: Timeinfo

Description: 	Keep track of the time for a LE, DI, LA, or FI.

*/
class Timeinfo{
	/*
		days(string[]): the weekdays, ["Tu", "TH"]
		startTime(int): the start time
		endTime(int): 	the end time
			note:start time and end time should be converted
			into int.  0 - 2400
			ex: 10:30am = 1030
		room(string): 	ex: CENTER
		roomNum(int): 	ex: 212

	*/
	constructor(days, startTime, endTime, room, roomNum){
		this.days = days;
		this.startTime = startTime;
		this.endTime = endTime;
		this.room = room;
		this.roomNum = roomNum;
	}
}

/*
	Check if the new time would have conflict with
	the existing schedule

	Input: 	current(array of array of Timeinfo):
				the current schedule.

			add(array of array of Timeinfo):
				the time to be added.

	need to check the emptiness of the LE, DI, LA

	Output: True if conflict
			False if not
*/
function checkConflicts( current, add ){
	/* 	add only has one element, and that is the array
		of timeinfo */

	let addTimeInfo = add[0];

	console.log("Checking Conflicts");

	for( let time of addTimeInfo ){ // the timeinfo of class we are checking
		for( let curr of current ){ // the timeinfo array
			for( let currTime of curr ){ // the timeinfo of curr schedule
				if( currTime != "" && time != "" ){ // only can access element if they are not ""
					/*
					console.log("Printing currTime and time");
					console.log(currTime);
					console.log(time);
					console.log("Printing start and end time");
					console.log(time.startTime);
					console.log(time.endTime);
					console.log("Printing Days");
					console.log(time.days);
					*/

					if ( time.startTime < currTime.endTime
						&& time.startTime > currTime.startTime){
						for( let addDay of time.days ){
							for( let currDay of currTime.days ){
									if( addDay == currDay ){
										return true;
								}
							}
						}
					}
				}
			}
		}
	}
	return false;
}

/*
Function Name: 	allSchedule

Description: 	checks for class conflict, create a combination
				of non-conflict schedule

Input: 			classes: an array of class
				class: array of non-conflict time from all the sections
					EX: if cse 8 has 2 sections, then class cse8
						would be cse8firstsection.allpossibletime()
						append with cse8secondsection.allpossibletime()

Output: 		array of possible time,

				EX:
				LE1, DI1, LE2, DI2.1 are instances of Timeinfo
				array[0] would be an array of all possible courses
					without time conflict.
				[ [ [LE1, DI1], [LE2, DI2.1] ],
				  [ [LE1, DI1], [LE2, DI2.2] ] ]

*/
function allSchedule( classes ){
	let schedules = [""];
	let tempSched = [];

	/* 	c = array of non-conflict time
		from all the sections
	*/

	for( let c of classes ){
		for( let time of c ){
			if( schedules[0] == "" )
				tempSched.push([time]);
			else{
				for( let s of schedules ){
					/* check for time conflict */
					if( checkConflicts(s, [time]) == false ){
						let timeArray = s.concat([time]);
						tempSched.push(timeArray);
					}
				}
			}
		}
		schedules = tempSched;
		tempSched = [];
	}

	if( schedules.length == 0){
		console.log("schedule conflict");
	}

	return schedules;
}

/* TEST DATA */
let time1 = new Timeinfo(["T", "TH"], 1000, 1200, "CENTER", "105");
/* conflict*/
let time2 = new Timeinfo(["T", "TH"], 1100, 1300, "CENTER", "105");
let class1 = new Class("100", "CSE", [""], [""], [""], [""]);
let class2 = new Class("100", "CSE", [""], [""], [""], [""]);
class1.addTime("LE", time1);
class2.addTime("DI", time2);
class1.possibleTime();
let classes = [];
classes.push(class1.possibleTime());
classes.push(class2.possibleTime());
let schedules = allSchedule(classes);
<commit_msg>resolving merge<commit_after>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
});

function initializePage() {

}

/*
Name: class Class

Description: Class is the parent class of section. When we parse JSON,
	we will be creating sections instead.
	Why?  There could be two sections for the same course, such as CSE 3.
	The combination of their LE time and DI time should not overlapped
	with each other.

*/

class Class{

	/*
		crsNum(int)		: course number
		crsName(string)	: course name
		LE = Lecture	DI = Discussion
		LA = Lab		FI = Final
		LEtime, DItime, LAtime, FItime: array of instances of Timeinfo
	*/
	constructor(subject, crsNum, crsName, LEtime, DItime,
		LAtime, FItime){
		this.subject = subject;
		this.crsNum = crsNum;
		this.crsName = crsName;
		this.LEtime = LEtime;
		this.DItime = DItime;
		this.LAtime = LAtime;
		this.FItime = FItime;
	}

	/*
	Function Name: addTime

	Description: Add the instance of time to the correct array.

	Input:
		category(string): 	the identifier of the time as LE, DI,
							LA, or FI.
		time(instance of Timeinfo): read Timeinfo class

	Ouput: nothing
	*/
	addTime(category, time){
		switch (category){
			case "LE":
				if(this.LEtime[0] == "")
					this.LEtime[0] = time;
				else
					this.LEtime.push(time);
				break;
			case "DI":
				if(this.DItime[0] == "")
					this.DItime[0] = time;
				else
					this.DItime.push(time);
				break;
			case "LA":
				if(this.LAtime[0] == "")
					this.LAtime[0] = time;
				else
					this.LAtime.push(time);
				break;
			case "FI":
				if(this.FItime[0] == "")
					this.FItime[0] = time;
				else
					this.FItime.push(time);
				break;
			default:
				console.log("Unidentified category.");
				break;
		}
	}

	/*
	Function Name: 	possibleTime

	Description: 	Get combinations of the time.
					Doesn't check for time conflict as there shouldnt be any

	Output: 		Returns array of possible time given the LE, DI, and LA.

					EX: if there are 2 LE, 4 DI
					it returns array of 8 different combinations
					of LE + DI.

	*/
	possibleTime(){
		let timeList = [];

		for( let le of this.LEtime ){
			for( let di of this.DItime ){
				for( let la of this.LAtime){
					timeList.push([le, di, la]);
				}
			}
		}
		return timeList;
	}
}

/*
Name: class Section

Description: 	child class of Class.
				When parsing json, we want to instantiate
				with this class.  Can call functcions of
				parent class.

				Two extra params are added:
				section and instructor.

*/
class Section extends Class{
	constructor(crsNum, crsName, LEtime, DItime,
		LAtime, FItime, section, instructor){

		super(crsNum, crsName, LEtime, DItime,
		LAtime, FItime);
		this.section = section;
		this.instructor = instructor;
	}
}

/*
Name: Timeinfo

Description: 	Keep track of the time for a LE, DI, LA, or FI.

*/
class Timeinfo{
	/*
		days(string[]): the weekdays, ["Tu", "TH"]
		startTime(int): the start time
		endTime(int): 	the end time
			note:start time and end time should be converted
			into int.  0 - 2400
			ex: 10:30am = 1030
		room(string): 	ex: CENTER
		roomNum(int): 	ex: 212

	*/
	constructor(days, startTime, endTime, room, roomNum){
		this.days = days;
		this.startTime = startTime;
		this.endTime = endTime;
		this.room = room;
		this.roomNum = roomNum;
	}
}

/*
	Check if the new time would have conflict with
	the existing schedule

	Input: 	current(array of array of Timeinfo):
				the current schedule.

			add(array of array of Timeinfo):
				the time to be added.

	need to check the emptiness of the LE, DI, LA

	Output: True if conflict
			False if not
*/
function checkConflicts( current, add ){
	/* 	add only has one element, and that is the array
		of timeinfo */

	let addTimeInfo = add[0];

	console.log("Checking Conflicts");

	for( let time of addTimeInfo ){ // the timeinfo of class we are checking
		for( let curr of current ){ // the timeinfo array
			for( let currTime of curr ){ // the timeinfo of curr schedule
				if( currTime != "" && time != "" ){ // only can access element if they are not ""
					/*
					console.log("Printing currTime and time");
					console.log(currTime);
					console.log(time);
					console.log("Printing start and end time");
					console.log(time.startTime);
					console.log(time.endTime);
					console.log("Printing Days");
					console.log(time.days);
					*/

					if ( time.startTime < currTime.endTime
						&& time.startTime > currTime.startTime){
						for( let addDay of time.days ){
							for( let currDay of currTime.days ){
									if( addDay == currDay ){
										return true;
								}
							}
						}
					}
				}
			}
		}
	}
	return false;
}

/*
Function Name: 	allSchedule

Description: 	checks for class conflict, create a combination
				of non-conflict schedule

Input: 			classes: an array of class
				class: array of non-conflict time from all the sections
					EX: if cse 8 has 2 sections, then class cse8
						would be cse8firstsection.allpossibletime()
						append with cse8secondsection.allpossibletime()

Output: 		array of possible time,

				EX:
				LE1, DI1, LE2, DI2.1 are instances of Timeinfo
				array[0] would be an array of all possible courses
					without time conflict.
				[ [ [LE1, DI1], [LE2, DI2.1] ],
				  [ [LE1, DI1], [LE2, DI2.2] ] ]

*/
function allSchedule( classes ){
	let schedules = [""];
	let tempSched = [];

	/* 	c = array of non-conflict time
		from all the sections
	*/

	for( let c of classes ){
		for( let time of c ){
			if( schedules[0] == "" )
				tempSched.push([time]);
			else{
				for( let s of schedules ){
					/* check for time conflict */
					if( checkConflicts(s, [time]) == false ){
						let timeArray = s.concat([time]);
						tempSched.push(timeArray);
					}
				}
			}
		}
		schedules = tempSched;
		tempSched = [];
	}

	if( schedules.length == 0){
		console.log("schedule conflict");
	}

	return schedules;
}

/* TEST DATA */
let time1 = new Timeinfo(["T", "TH"], 1000, 1200, "CENTER", "105");
/* conflict*/
let time2 = new Timeinfo(["T", "TH"], 1100, 1300, "CENTER", "105");
let class1 = new Class("100", "CSE", [""], [""], [""], [""]);
let class2 = new Class("100", "CSE", [""], [""], [""], [""]);
class1.addTime("LE", time1);
class2.addTime("DI", time2);
class1.possibleTime();
let classes = [];
classes.push(class1.possibleTime());
classes.push(class2.possibleTime());
let schedules = allSchedule(classes);

/*
sample json:
{
	subject: "CSE"
	courseNum: 3
	courseName: "Fluency/Information Technolog"
	LE:
	crsNum, crsName, LEtime, DItime,
		LAtime, FItime

	days: ["M", "W", "F"]
	startTime: 1000
	endTime: 1050
	room: "CENTR"
	roomNum: "105"
}*/<|endoftext|>


<commit_before>/* @flow */

import { Cozy } from 'cozy-client-js'
import uuid from 'node-uuid'

import { FILES_DOCTYPE, ROOT_DIR_ID } from '../../../src/remote/constants'
import timestamp from '../../../src/timestamp'

import type { RemoteDoc } from '../../../src/remote/document'

const ROOT_DIR_PROPERTIES = {
  _id: ROOT_DIR_ID,
  path: '/'
}

export default class RemoteBaseBuilder {
  cozy: Cozy
  options: {
    contentType?: string,
    dir: {_id: string, path: string},
    name: string,
    lastModifiedDate: Date
  }

  constructor (cozy: Cozy) {
    this.cozy = cozy
    this.options = {
      dir: ROOT_DIR_PROPERTIES,
      name: '',
      lastModifiedDate: timestamp.current()
    }
  }

  inDir (dir: RemoteDoc): this {
    this.options.dir = dir
    return this
  }

  inRootDir (): this {
    this.options.dir = ROOT_DIR_PROPERTIES
    return this
  }

  timestamp (...args: number[]): this {
    this.options.lastModifiedDate = timestamp.build(...args)
    return this
  }

  named (name: string): this {
    this.options.name = name
    return this
  }

  build (): Object {
    return {
      _id: uuid.v4().replace(/-/g, ''),
      _rev: '1-' + uuid.v4().replace(/-/g, ''),
      _type: FILES_DOCTYPE,
      created_at: this.options.lastModifiedDate,
      dir_id: this.options.dir._id,
      name: this.options.name,
      path: `${this.options.dir.path}/${this.options.name}`,
      tags: [],
      updated_at: this.options.lastModifiedDate
    }
  }
}
<commit_msg>test/builders: Introduce remoteDir().trashed() and remoteFile().trashed()<commit_after>/* @flow */

import { Cozy } from 'cozy-client-js'
import uuid from 'node-uuid'

import { FILES_DOCTYPE, ROOT_DIR_ID, TRASH_DIR_ID, TRASH_DIR_NAME } from '../../../src/remote/constants'
import timestamp from '../../../src/timestamp'

import type { RemoteDoc } from '../../../src/remote/document'

const ROOT_DIR_PROPERTIES = {
  _id: ROOT_DIR_ID,
  path: '/'
}

const TRASH_DIR_PROPERTIES = {
  _id: TRASH_DIR_ID,
  path: `/${TRASH_DIR_NAME}`
}

export default class RemoteBaseBuilder {
  cozy: Cozy
  options: {
    contentType?: string,
    dir: {_id: string, path: string},
    name: string,
    lastModifiedDate: Date
  }

  constructor (cozy: Cozy) {
    this.cozy = cozy
    this.options = {
      dir: ROOT_DIR_PROPERTIES,
      name: '',
      lastModifiedDate: timestamp.current()
    }
  }

  inDir (dir: RemoteDoc): this {
    this.options.dir = dir
    return this
  }

  inRootDir (): this {
    this.options.dir = ROOT_DIR_PROPERTIES
    return this
  }

  trashed (): this {
    this.options.dir = TRASH_DIR_PROPERTIES
    return this
  }

  timestamp (...args: number[]): this {
    this.options.lastModifiedDate = timestamp.build(...args)
    return this
  }

  named (name: string): this {
    this.options.name = name
    return this
  }

  build (): Object {
    return {
      _id: uuid.v4().replace(/-/g, ''),
      _rev: '1-' + uuid.v4().replace(/-/g, ''),
      _type: FILES_DOCTYPE,
      created_at: this.options.lastModifiedDate,
      dir_id: this.options.dir._id,
      name: this.options.name,
      path: `${this.options.dir.path}/${this.options.name}`,
      tags: [],
      updated_at: this.options.lastModifiedDate
    }
  }
}
<|endoftext|>


<commit_before>import { captureException } from 'common/errorLogger';

import Module from '../Module';
import { SELECTED_PLAYER, SELECTED_PLAYER_PET } from '../Analyzer';
import EventFilter from '../EventFilter';

const CATCH_ALL_EVENT = 'event';

/**
 * This (core) module takes care of:
 * - tracking attached event listeners
 * - filtering event listeners (as configured in EventFilter)
 * - firing event listeners
 */
class EventEmitter extends Module {
  static __dangerousInvalidUsage = false;

  _eventListenersByEventType = {};
  addEventListener(eventFilter, listener, module) {
    const eventType = eventFilter instanceof EventFilter ? eventFilter.eventType : eventFilter;
    const options = {};
    if (eventFilter instanceof EventFilter) {
      const by = eventFilter.by();
      if (by === SELECTED_PLAYER) {
        options.byPlayer = true;
      }
      if (by === SELECTED_PLAYER_PET) {
        options.byPlayerPet = true;
      }
      const to = eventFilter.to();
      if (to === SELECTED_PLAYER) {
        options.toPlayer = true;
      }
      if (to === SELECTED_PLAYER_PET) {
        options.toPlayerPet = true;
      }
    }
    this._eventListenersByEventType[eventType] = this._eventListenersByEventType[eventType] || [];
    this._eventListenersByEventType[eventType].push({
      ...options,
      eventFilter,
      listener: this._compileListener(eventFilter, listener, module),
    });
  }
  _compileListener(eventFilter, listener, module) {
    if (eventFilter instanceof EventFilter) {
      listener = this._prependFilters(eventFilter, listener);
    }
    // We need to do this last so it's at the top level
    listener = this._prependActiveCheck(listener, module);
    return listener;
  }
  _prependActiveCheck(listener, module) {
    return function (event) {
      if (!module.active) return;
      listener(event);
    };
  }
  _prependFilters(eventFilter, listener) {
    // const by = eventFilter.by();
    // if (by) {
    //   listener = this._prependByCheck(listener, by);
    // }
    return listener;
  }
  // _prependByCheck(listener, by) {
  //   const requiresSelectedPlayer = by & SELECTED_PLAYER;
  //   const requiresSelectedPlayerPet = by & SELECTED_PLAYER_PET;
  //
  //
  // }

  triggerEvent(event) {
    if (process.env.NODE_ENV === 'development') {
      if (!event.type) {
        console.log(event);
        throw new Error('Events should have a type. No type received. See the console for the event.');
      }
    }

    // When benchmarking the event triggering make sure to disable the event batching and turn the listener into a dummy so you get the performance of just this piece of code. At the time of writing the event triggering code only has about 12ms overhead for a full log.

    if (event.timestamp) {
      this._timestamp = event.timestamp;
    }
    this._event = event;

    const isByPlayer = this.owner.byPlayer(event);
    const isToPlayer = this.owner.toPlayer(event);
    const isByPlayerPet = this.owner.byPlayerPet(event);
    const isToPlayerPet = this.owner.toPlayerPet(event);

    // TODO: Refactor this to compose a function with only the necessary if-statements in the addEventListener method so this doesn't become an endless list of if-statements
    const run = options => {
      if (!isByPlayer && options.byPlayer) {
        return;
      }
      if (!isByPlayerPet && options.byPlayerPet) {
        return;
      }
      if (!isToPlayer && options.toPlayer) {
        return;
      }
      if (!isToPlayerPet && options.toPlayerPet) {
        return;
      }

      try {
        options.listener(event);
      } catch (err) {
        if (process.env.NODE_ENV === 'development') {
          throw err;
        }
        console.error('Disabling', options.module.constructor.name, 'and child dependencies because an error occured', err);
        // Disable this module and all active modules that have this as a dependency
        this.owner.deepDisable(options.module);
        captureException(err);
      }
    };

    {
      // Handle on_event (listeners of all events)
      const listeners = this._eventListenersByEventType[CATCH_ALL_EVENT];
      if (listeners) {
        listeners.forEach(run);
      }
    }
    {
      const listeners = this._eventListenersByEventType[event.type];
      if (listeners) {
        listeners.forEach(run);
      }
    }

    this.owner.eventHistory.push(event);
    // Some modules need to have a primitive value to cause re-renders
    // TODO: This can probably be removed since we only render upon completion now
    this.owner.eventCount += 1;
  }
}

export default EventEmitter;
<commit_msg>Fix a bug in event listening<commit_after>import { captureException } from 'common/errorLogger';

import Module from '../Module';
import { SELECTED_PLAYER, SELECTED_PLAYER_PET } from '../Analyzer';
import EventFilter from '../EventFilter';

const CATCH_ALL_EVENT = 'event';

/**
 * This (core) module takes care of:
 * - tracking attached event listeners
 * - filtering event listeners (as configured in EventFilter)
 * - firing event listeners
 */
class EventEmitter extends Module {
  static __dangerousInvalidUsage = false;

  _eventListenersByEventType = {};
  addEventListener(eventFilter, listener, module) {
    const eventType = eventFilter instanceof EventFilter ? eventFilter.eventType : eventFilter;
    const options = {};
    if (eventFilter instanceof EventFilter) {
      const by = eventFilter.by();
      if (by === SELECTED_PLAYER) {
        options.byPlayer = true;
      }
      if (by === SELECTED_PLAYER_PET) {
        options.byPlayerPet = true;
      }
      const to = eventFilter.to();
      if (to === SELECTED_PLAYER) {
        options.toPlayer = true;
      }
      if (to === SELECTED_PLAYER_PET) {
        options.toPlayerPet = true;
      }
    }
    this._eventListenersByEventType[eventType] = this._eventListenersByEventType[eventType] || [];
    this._eventListenersByEventType[eventType].push({
      ...options,
      eventFilter,
      listener: this._compileListener(eventFilter, listener, module),
    });
  }
  _compileListener(eventFilter, listener, module) {
    if (eventFilter instanceof EventFilter) {
      listener = this._prependFilters(eventFilter, listener);
    }
    // We need to do this last so it's at the top level
    listener = this._prependActiveCheck(listener, module);
    return listener;
  }
  _prependActiveCheck(listener, module) {
    return function (event) {
      if (!module.active) return;
      listener(event);
    };
  }
  _prependFilters(eventFilter, listener) {
    // const by = eventFilter.by();
    // if (by) {
    //   listener = this._prependByCheck(listener, by);
    // }
    return listener;
  }
  // _prependByCheck(listener, by) {
  //   const requiresSelectedPlayer = by & SELECTED_PLAYER;
  //   const requiresSelectedPlayerPet = by & SELECTED_PLAYER_PET;
  //
  //
  // }

  triggerEvent(event) {
    if (process.env.NODE_ENV === 'development') {
      if (!event.type) {
        console.log(event);
        throw new Error('Events should have a type. No type received. See the console for the event.');
      }
    }

    // When benchmarking the event triggering make sure to disable the event batching and turn the listener into a dummy so you get the performance of just this piece of code. At the time of writing the event triggering code only has about 12ms overhead for a full log.

    if (event.timestamp) {
      this.owner._timestamp = event.timestamp;
    }
    this.owner._event = event;

    const isByPlayer = this.owner.byPlayer(event);
    const isToPlayer = this.owner.toPlayer(event);
    const isByPlayerPet = this.owner.byPlayerPet(event);
    const isToPlayerPet = this.owner.toPlayerPet(event);

    // TODO: Refactor this to compose a function with only the necessary if-statements in the addEventListener method so this doesn't become an endless list of if-statements
    const run = options => {
      if (!isByPlayer && options.byPlayer) {
        return;
      }
      if (!isByPlayerPet && options.byPlayerPet) {
        return;
      }
      if (!isToPlayer && options.toPlayer) {
        return;
      }
      if (!isToPlayerPet && options.toPlayerPet) {
        return;
      }

      try {
        options.listener(event);
      } catch (err) {
        if (process.env.NODE_ENV === 'development') {
          throw err;
        }
        console.error('Disabling', options.module.constructor.name, 'and child dependencies because an error occured', err);
        // Disable this module and all active modules that have this as a dependency
        this.owner.deepDisable(options.module);
        captureException(err);
      }
    };

    {
      // Handle on_event (listeners of all events)
      const listeners = this._eventListenersByEventType[CATCH_ALL_EVENT];
      if (listeners) {
        listeners.forEach(run);
      }
    }
    {
      const listeners = this._eventListenersByEventType[event.type];
      if (listeners) {
        listeners.forEach(run);
      }
    }

    this.owner.eventHistory.push(event);
    // Some modules need to have a primitive value to cause re-renders
    // TODO: This can probably be removed since we only render upon completion now
    this.owner.eventCount += 1;
  }
}

export default EventEmitter;
<|endoftext|>


<commit_before>import { h, ref, computed, Transition, onBeforeUnmount, withDirectives, getCurrentInstance } from 'vue'

import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'

import Ripple from '../../directives/Ripple.js'

import useBtn, { useBtnProps } from './use-btn.js'

import { createComponent } from '../../utils/private/create.js'
import { hMergeSlot } from '../../utils/private/render.js'
import { stop, prevent, stopAndPrevent, listenOpts } from '../../utils/event.js'
import { isKeyCode } from '../../utils/private/key-composition.js'

const { passiveCapture } = listenOpts

let
  touchTarget = null,
  keyboardTarget = null,
  mouseTarget = null

export default createComponent({
  name: 'QBtn',

  props: {
    ...useBtnProps,

    percentage: Number,
    darkPercentage: Boolean
  },

  emits: [ 'click', 'keydown', 'touchstart', 'mousedown', 'keyup' ],

  setup (props, { slots, emit }) {
    const { proxy } = getCurrentInstance()

    const {
      classes, style, innerClasses,
      attributes,
      hasLink, isLink, navigateToLink,
      isActionable
    } = useBtn(props)

    const rootRef = ref(null)
    const blurTargetRef = ref(null)

    let localTouchTargetEl = null, avoidMouseRipple, mouseTimer

    const hasLabel = computed(() =>
      props.label !== void 0 && props.label !== null && props.label !== ''
    )

    const ripple = computed(() => (
      props.disable === true || props.ripple === false
        ? false
        : {
            keyCodes: isLink.value === true ? [ 13, 32 ] : [ 13 ],
            ...(props.ripple === true ? {} : props.ripple)
          }
    ))

    const rippleProps = computed(() => ({ center: props.round }))

    const percentageStyle = computed(() => {
      const val = Math.max(0, Math.min(100, props.percentage))
      return val > 0
        ? { transition: 'transform 0.6s', transform: `translateX(${ val - 100 }%)` }
        : {}
    })

    const onEvents = computed(() => {
      if (props.loading === true) {
        return {
          onMousedown: onLoadingEvt,
          onTouchstartPassive: onLoadingEvt,
          onClick: onLoadingEvt,
          onKeydown: onLoadingEvt,
          onKeyup: onLoadingEvt
        }
      }

      if (isActionable.value === true) {
        return {
          onClick,
          onKeydown,
          onMousedown,
          onTouchstartPassive
        }
      }

      return {
        // needed; especially for disabled <a> tags
        onClick: stopAndPrevent
      }
    })

    const nodeProps = computed(() => ({
      ref: rootRef,
      class: 'q-btn q-btn-item non-selectable no-outline ' + classes.value,
      style: style.value,
      ...attributes.value,
      ...onEvents.value
    }))

    function onClick (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (e !== void 0) {
        if (e.defaultPrevented === true) {
          return
        }

        const el = document.activeElement
        // focus button if it came from ENTER on form
        // prevent the new submit (already done)
        if (
          props.type === 'submit'
          && el !== document.body
          && rootRef.value.contains(el) === false
          // required for iOS and desktop Safari
          && el.contains(rootRef.value) === false
        ) {
          rootRef.value.focus()

          const onClickCleanup = () => {
            document.removeEventListener('keydown', stopAndPrevent, true)
            document.removeEventListener('keyup', onClickCleanup, passiveCapture)
            rootRef.value !== null && rootRef.value.removeEventListener('blur', onClickCleanup, passiveCapture)
          }

          document.addEventListener('keydown', stopAndPrevent, true)
          document.addEventListener('keyup', onClickCleanup, passiveCapture)
          rootRef.value.addEventListener('blur', onClickCleanup, passiveCapture)
        }
      }

      if (hasLink.value === true) {
        const go = () => {
          e.__qNavigate = true
          navigateToLink(e)
        }

        emit('click', e, go)
        e.defaultPrevented !== true && go()
      }
      else {
        emit('click', e)
      }
    }

    function onKeydown (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (isKeyCode(e, [ 13, 32 ]) === true) {
        stopAndPrevent(e)

        if (keyboardTarget !== rootRef.value) {
          keyboardTarget !== null && cleanup()

          // focus external button if the focus helper was focused before
          rootRef.value.focus()

          keyboardTarget = rootRef.value
          rootRef.value.classList.add('q-btn--active')
          document.addEventListener('keyup', onPressEnd, true)
          rootRef.value.addEventListener('blur', onPressEnd, passiveCapture)
        }
      }

      emit('keydown', e)
    }

    function onTouchstartPassive (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (touchTarget !== rootRef.value) {
        touchTarget !== null && cleanup()
        touchTarget = rootRef.value

        localTouchTargetEl = e.target
        localTouchTargetEl.addEventListener('touchcancel', onPressEnd, passiveCapture)
        localTouchTargetEl.addEventListener('touchend', onPressEnd, passiveCapture)
      }

      // avoid duplicated mousedown event
      // triggering another early ripple
      avoidMouseRipple = true
      clearTimeout(mouseTimer)
      mouseTimer = setTimeout(() => {
        avoidMouseRipple = false
      }, 200)

      emit('touchstart', e)
    }

    function onMousedown (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (mouseTarget !== rootRef.value) {
        mouseTarget !== null && cleanup()
        mouseTarget = rootRef.value
        rootRef.value.classList.add('q-btn--active')
        document.addEventListener('mouseup', onPressEnd, passiveCapture)
      }

      e.qSkipRipple = avoidMouseRipple === true
      emit('mousedown', e)
    }

    function onPressEnd (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      // needed for IE (because it emits blur when focusing button from focus helper)
      if (e !== void 0 && e.type === 'blur' && document.activeElement === rootRef.value) {
        return
      }

      if (e !== void 0 && e.type === 'keyup') {
        if (keyboardTarget === rootRef.value && isKeyCode(e, [ 13, 32 ]) === true) {
          // for click trigger
          const evt = new MouseEvent('click', e)
          evt.qKeyEvent = true
          e.defaultPrevented === true && prevent(evt)
          e.cancelBubble === true && stop(evt)
          rootRef.value.dispatchEvent(evt)

          stopAndPrevent(e)

          // for ripple
          e.qKeyEvent = true
        }

        emit('keyup', e)
      }

      cleanup()
    }

    function cleanup (destroying) {
      const blurTarget = blurTargetRef.value

      if (
        destroying !== true
        && (touchTarget === rootRef.value || mouseTarget === rootRef.value)
        && blurTarget !== null
        && blurTarget !== document.activeElement
      ) {
        blurTarget.setAttribute('tabindex', -1)
        blurTarget.focus()
      }

      if (touchTarget === rootRef.value) {
        if (localTouchTargetEl !== null) {
          localTouchTargetEl.removeEventListener('touchcancel', onPressEnd, passiveCapture)
          localTouchTargetEl.removeEventListener('touchend', onPressEnd, passiveCapture)
        }
        touchTarget = localTouchTargetEl = null
      }

      if (mouseTarget === rootRef.value) {
        document.removeEventListener('mouseup', onPressEnd, passiveCapture)
        mouseTarget = null
      }

      if (keyboardTarget === rootRef.value) {
        document.removeEventListener('keyup', onPressEnd, true)
        rootRef.value !== null && rootRef.value.removeEventListener('blur', onPressEnd, passiveCapture)
        keyboardTarget = null
      }

      rootRef.value !== null && rootRef.value.classList.remove('q-btn--active')
    }

    function onLoadingEvt (evt) {
      evt.qSkipRipple = true
    }

    onBeforeUnmount(() => {
      cleanup(true)
    })

    // expose public methods
    Object.assign(proxy, { click: onClick })

    return () => {
      let inner = []

      props.icon !== void 0 && inner.push(
        h(QIcon, {
          name: props.icon,
          left: props.stack === false && hasLabel.value === true,
          role: 'img',
          'aria-hidden': 'true'
        })
      )

      hasLabel.value === true && inner.push(
        h('span', { class: 'block' }, [ props.label ])
      )

      inner = hMergeSlot(slots.default, inner)

      if (props.iconRight !== void 0 && props.round === false) {
        inner.push(
          h(QIcon, {
            name: props.iconRight,
            right: props.stack === false && hasLabel.value === true,
            role: 'img',
            'aria-hidden': 'true'
          })
        )
      }

      const child = [
        h('span', {
          class: 'q-focus-helper',
          ref: blurTargetRef
        })
      ]

      if (props.loading === true && props.percentage !== void 0) {
        child.push(
          h('span', {
            class: 'q-btn__progress absolute-full overflow-hidden'
          }, [
            h('span', {
              class: 'q-btn__progress-indicator fit block' + (props.darkPercentage === true ? ' q-btn__progress--dark' : ''),
              style: percentageStyle.value
            })
          ])
        )
      }

      child.push(
        h('span', {
          class: 'q-btn__content text-center col items-center q-anchor--skip ' + innerClasses.value
        }, inner)
      )

      props.loading !== null && child.push(
        h(Transition, {
          name: 'q-transition--fade'
        }, () => (
          props.loading === true
            ? [
                h('span', {
                  key: 'loading',
                  class: 'absolute-full flex flex-center'
                }, slots.loading !== void 0 ? slots.loading() : [ h(QSpinner) ])
              ]
            : null
        ))
      )

      return withDirectives(
        h(
          isLink.value === true ? 'a' : 'button',
          nodeProps.value,
          child
        ),
        [ [
          Ripple,
          ripple.value,
          void 0,
          rippleProps.value
        ] ]
      )
    }
  }
})
<commit_msg>fix(QBtn): QBtn with loading=true does not hinder form submission #11392<commit_after>import { h, ref, computed, Transition, onBeforeUnmount, withDirectives, getCurrentInstance } from 'vue'

import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'

import Ripple from '../../directives/Ripple.js'

import useBtn, { useBtnProps } from './use-btn.js'

import { createComponent } from '../../utils/private/create.js'
import { hMergeSlot } from '../../utils/private/render.js'
import { stop, prevent, stopAndPrevent, listenOpts } from '../../utils/event.js'
import { isKeyCode } from '../../utils/private/key-composition.js'

const { passiveCapture } = listenOpts

let
  touchTarget = null,
  keyboardTarget = null,
  mouseTarget = null

export default createComponent({
  name: 'QBtn',

  props: {
    ...useBtnProps,

    percentage: Number,
    darkPercentage: Boolean
  },

  emits: [ 'click', 'keydown', 'touchstart', 'mousedown', 'keyup' ],

  setup (props, { slots, emit }) {
    const { proxy } = getCurrentInstance()

    const {
      classes, style, innerClasses,
      attributes,
      hasLink, isLink, navigateToLink,
      isActionable
    } = useBtn(props)

    const rootRef = ref(null)
    const blurTargetRef = ref(null)

    let localTouchTargetEl = null, avoidMouseRipple, mouseTimer

    const hasLabel = computed(() =>
      props.label !== void 0 && props.label !== null && props.label !== ''
    )

    const ripple = computed(() => (
      props.disable === true || props.ripple === false
        ? false
        : {
            keyCodes: isLink.value === true ? [ 13, 32 ] : [ 13 ],
            ...(props.ripple === true ? {} : props.ripple)
          }
    ))

    const rippleProps = computed(() => ({ center: props.round }))

    const percentageStyle = computed(() => {
      const val = Math.max(0, Math.min(100, props.percentage))
      return val > 0
        ? { transition: 'transform 0.6s', transform: `translateX(${ val - 100 }%)` }
        : {}
    })

    const onEvents = computed(() => {
      if (props.loading === true) {
        return {
          onMousedown: onLoadingEvt,
          onTouchstartPassive: onLoadingEvt,
          onClick: onLoadingEvt,
          onKeydown: onLoadingEvt,
          onKeyup: onLoadingEvt
        }
      }

      if (isActionable.value === true) {
        return {
          onClick,
          onKeydown,
          onMousedown,
          onTouchstartPassive
        }
      }

      return {
        // needed; especially for disabled <a> tags
        onClick: stopAndPrevent
      }
    })

    const nodeProps = computed(() => ({
      ref: rootRef,
      class: 'q-btn q-btn-item non-selectable no-outline ' + classes.value,
      style: style.value,
      ...attributes.value,
      ...onEvents.value
    }))

    function onClick (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (e !== void 0) {
        if (e.defaultPrevented === true) {
          return
        }

        const el = document.activeElement
        // focus button if it came from ENTER on form
        // prevent the new submit (already done)
        if (
          props.type === 'submit'
          && el !== document.body
          && rootRef.value.contains(el) === false
          // required for iOS and desktop Safari
          && el.contains(rootRef.value) === false
        ) {
          rootRef.value.focus()

          const onClickCleanup = () => {
            document.removeEventListener('keydown', stopAndPrevent, true)
            document.removeEventListener('keyup', onClickCleanup, passiveCapture)
            rootRef.value !== null && rootRef.value.removeEventListener('blur', onClickCleanup, passiveCapture)
          }

          document.addEventListener('keydown', stopAndPrevent, true)
          document.addEventListener('keyup', onClickCleanup, passiveCapture)
          rootRef.value.addEventListener('blur', onClickCleanup, passiveCapture)
        }
      }

      if (hasLink.value === true) {
        const go = () => {
          e.__qNavigate = true
          navigateToLink(e)
        }

        emit('click', e, go)
        e.defaultPrevented !== true && go()
      }
      else {
        emit('click', e)
      }
    }

    function onKeydown (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (isKeyCode(e, [ 13, 32 ]) === true) {
        stopAndPrevent(e)

        if (keyboardTarget !== rootRef.value) {
          keyboardTarget !== null && cleanup()

          // focus external button if the focus helper was focused before
          rootRef.value.focus()

          keyboardTarget = rootRef.value
          rootRef.value.classList.add('q-btn--active')
          document.addEventListener('keyup', onPressEnd, true)
          rootRef.value.addEventListener('blur', onPressEnd, passiveCapture)
        }
      }

      emit('keydown', e)
    }

    function onTouchstartPassive (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (touchTarget !== rootRef.value) {
        touchTarget !== null && cleanup()
        touchTarget = rootRef.value

        localTouchTargetEl = e.target
        localTouchTargetEl.addEventListener('touchcancel', onPressEnd, passiveCapture)
        localTouchTargetEl.addEventListener('touchend', onPressEnd, passiveCapture)
      }

      // avoid duplicated mousedown event
      // triggering another early ripple
      avoidMouseRipple = true
      clearTimeout(mouseTimer)
      mouseTimer = setTimeout(() => {
        avoidMouseRipple = false
      }, 200)

      emit('touchstart', e)
    }

    function onMousedown (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      if (mouseTarget !== rootRef.value) {
        mouseTarget !== null && cleanup()
        mouseTarget = rootRef.value
        rootRef.value.classList.add('q-btn--active')
        document.addEventListener('mouseup', onPressEnd, passiveCapture)
      }

      e.qSkipRipple = avoidMouseRipple === true
      emit('mousedown', e)
    }

    function onPressEnd (e) {
      // is it already destroyed?
      if (rootRef.value === null) { return }

      // needed for IE (because it emits blur when focusing button from focus helper)
      if (e !== void 0 && e.type === 'blur' && document.activeElement === rootRef.value) {
        return
      }

      if (e !== void 0 && e.type === 'keyup') {
        if (keyboardTarget === rootRef.value && isKeyCode(e, [ 13, 32 ]) === true) {
          // for click trigger
          const evt = new MouseEvent('click', e)
          evt.qKeyEvent = true
          e.defaultPrevented === true && prevent(evt)
          e.cancelBubble === true && stop(evt)
          rootRef.value.dispatchEvent(evt)

          stopAndPrevent(e)

          // for ripple
          e.qKeyEvent = true
        }

        emit('keyup', e)
      }

      cleanup()
    }

    function cleanup (destroying) {
      const blurTarget = blurTargetRef.value

      if (
        destroying !== true
        && (touchTarget === rootRef.value || mouseTarget === rootRef.value)
        && blurTarget !== null
        && blurTarget !== document.activeElement
      ) {
        blurTarget.setAttribute('tabindex', -1)
        blurTarget.focus()
      }

      if (touchTarget === rootRef.value) {
        if (localTouchTargetEl !== null) {
          localTouchTargetEl.removeEventListener('touchcancel', onPressEnd, passiveCapture)
          localTouchTargetEl.removeEventListener('touchend', onPressEnd, passiveCapture)
        }
        touchTarget = localTouchTargetEl = null
      }

      if (mouseTarget === rootRef.value) {
        document.removeEventListener('mouseup', onPressEnd, passiveCapture)
        mouseTarget = null
      }

      if (keyboardTarget === rootRef.value) {
        document.removeEventListener('keyup', onPressEnd, true)
        rootRef.value !== null && rootRef.value.removeEventListener('blur', onPressEnd, passiveCapture)
        keyboardTarget = null
      }

      rootRef.value !== null && rootRef.value.classList.remove('q-btn--active')
    }

    function onLoadingEvt (evt) {
      stopAndPrevent(evt)
      evt.qSkipRipple = true
    }

    onBeforeUnmount(() => {
      cleanup(true)
    })

    // expose public methods
    Object.assign(proxy, { click: onClick })

    return () => {
      let inner = []

      props.icon !== void 0 && inner.push(
        h(QIcon, {
          name: props.icon,
          left: props.stack === false && hasLabel.value === true,
          role: 'img',
          'aria-hidden': 'true'
        })
      )

      hasLabel.value === true && inner.push(
        h('span', { class: 'block' }, [ props.label ])
      )

      inner = hMergeSlot(slots.default, inner)

      if (props.iconRight !== void 0 && props.round === false) {
        inner.push(
          h(QIcon, {
            name: props.iconRight,
            right: props.stack === false && hasLabel.value === true,
            role: 'img',
            'aria-hidden': 'true'
          })
        )
      }

      const child = [
        h('span', {
          class: 'q-focus-helper',
          ref: blurTargetRef
        })
      ]

      if (props.loading === true && props.percentage !== void 0) {
        child.push(
          h('span', {
            class: 'q-btn__progress absolute-full overflow-hidden'
          }, [
            h('span', {
              class: 'q-btn__progress-indicator fit block' + (props.darkPercentage === true ? ' q-btn__progress--dark' : ''),
              style: percentageStyle.value
            })
          ])
        )
      }

      child.push(
        h('span', {
          class: 'q-btn__content text-center col items-center q-anchor--skip ' + innerClasses.value
        }, inner)
      )

      props.loading !== null && child.push(
        h(Transition, {
          name: 'q-transition--fade'
        }, () => (
          props.loading === true
            ? [
                h('span', {
                  key: 'loading',
                  class: 'absolute-full flex flex-center'
                }, slots.loading !== void 0 ? slots.loading() : [ h(QSpinner) ])
              ]
            : null
        ))
      )

      return withDirectives(
        h(
          isLink.value === true ? 'a' : 'button',
          nodeProps.value,
          child
        ),
        [ [
          Ripple,
          ripple.value,
          void 0,
          rippleProps.value
        ] ]
      )
    }
  }
})
<|endoftext|>


<commit_before>import React from 'react';

import { Abelito75, emallson, Zerotorescue, Dambroda, Zeboot } from 'CONTRIBUTORS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 10, 24), <>Added checklist item and timeline annotations for bad <SpellLink id={SPELLS.TIGER_PALM.id} /> casts.</>, emallson),
  change(date(2020, 10, 18), 'Converted legacy listeners to new event filters', Zeboot),
  change(date(2020, 10, 17), 'Added some missing abilities and updated ability cooldowns.', Dambroda),
  change(date(2020, 10, 16), <>Removed "Inefficient <SpellLink id={SPELLS.PURIFYING_BREW.id} /> Casts" statistic.</>, emallson),
  change(date(2020, 10, 12), 'Updated checklist to reflect the Shadowlands changes.', emallson),
  change(date(2020, 10, 6), <>Added Fallen Order statistic.</>, Abelito75),
  change(date(2020, 9, 5), <>Updated Brewmaster spells for Shadowlands.</>, emallson),
  change(date(2020, 5, 4), <>Fixed bug in <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> handling of absorbs.</>, emallson),
  change(date(2020, 3, 10), <>Updated hit tracking for Ny'alotha and bumped compatability to 8.3.</>, emallson),
  change(date(2019, 12, 22), <>Warning threshold for <SpellLink id={SPELLS.HEAVY_STAGGER_DEBUFF.id} /> / <SpellLink id={SPELLS.PURIFYING_BREW.id} /> is now calculated based on time spent in red stagger.</>, emallson),
  change(date(2019, 10, 28), <>Added support for items in the <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> tab.</>, Abelito75),
  change(date(2019, 10, 16), <>Removed <SpellLink id={SPELLS.ARCANE_TORRENT_ENERGY.id} /> from Brewmaster suggestions.</>, emallson),
  change(date(2019, 9, 27), <>Update to date for 8.2.5 </>,Abelito75),
  change(date(2019, 7, 20), 'Updated hit-tracking blacklist and internal constants for Eternal Palace.', emallson),
  change(date(2019, 6, 28), <>Added a <SpellLink id={SPELLS.HOT_TRUB.id} /> damage estimator. Nerf sucks :(</>, [emallson]),
  change(date(2019, 4, 11), <>Fixed a bug in the Mitigation Values tab that caused Mastery's base 8% to be counted towards contribution by Mastery Rating.</>, [emallson]),
  change(date(2019, 3, 10), <>Fixed a bug in the Ironskin Brew normalizer that led to a crash in the new timeline.</>, [Zerotorescue]),
  change(date(2019, 2, 16), <>Removed <SpellLink id={SPELLS.BREATH_OF_FIRE.id} /> from the checklist.</>, [emallson]),
  change(date(2019, 1, 31), <>Added Mitigation Values tab showing estimated stat values for damage mitigation.</>, [emallson]),
  change(date(2018, 12, 30), <>Added <SpellLink id={SPELLS.STRAIGHT_NO_CHASER.id} /> module.</>, [emallson]),
  change(date(2018, 12, 29), <>Added <SpellLink id={SPELLS.GIFT_OF_THE_OX_1.id} /> healing statistic.</>, [emallson]),
  change(date(2018, 10, 26), <>Added <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> healing statistic and table.</>, [emallson]),
  change(date(2018, 10, 22), <>The <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module now loads on-demand, improving load times for lower-end devices.</>, [emallson]),
  change(date(2018, 10, 15), <>Added <SpellLink id={SPELLS.PURIFYING_BREW.id} /> checklist item.</>, [emallson]),
  change(date(2018, 10, 2), <>Re-enabled the <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module and added additional distribution information to it.</>, [emallson]),
  change(date(2018, 9, 27), 'Updated Stagger plot to show very quick purifies more accurately.', [emallson]),
  change(date(2018, 9, 22), <>Updated Ironskin Brew and <SpellLink id={SPELLS.BREATH_OF_FIRE.id} /> suggestions to use hits mitigated instead of uptime.</>, [emallson]),
  change(date(2018, 9, 22), <>Added support for <SpellLink id={SPELLS.FIT_TO_BURST.id} />.</>, [emallson]),
  change(date(2018, 9, 13), <>Added support for <SpellLink id={SPELLS.ELUSIVE_FOOTWORK.id} />.</>, [emallson]),
  change(date(2018, 8, 11), <>Added support for <SpellLink id={SPELLS.STAGGERING_STRIKES.id} />.</>, [emallson]),
  change(date(2018, 7, 22), <>Updated support for Soul of the Grandmaster legendary and temporarily disabled the <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module pending new formula coefficients.</>, [emallson]),
  change(date(2018, 7, 18), <>Added support for <SpellLink id={SPELLS.LIGHT_BREWING_TALENT.id} />.</>, [emallson]),
  change(date(2018, 7, 15), <>Added <SpellLink id={SPELLS.TRAINING_OF_NIUZAO.id} /> support.</>, [emallson]),
  change(date(2018, 6, 16), <>Updated Ironskin Brew cooldown and duration and added Guard, along with other changes in beta build 26812.</>, [emallson]),
];
<commit_msg>changelog entry<commit_after>import React from 'react';

import { Abelito75, emallson, Zerotorescue, Dambroda, Zeboot } from 'CONTRIBUTORS';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import { change, date } from 'common/changelog';

export default [
  change(date(2020, 11, 22), <>Added stats for <SpellLink id={SPELLS.SCALDING_BREW.id} /> and <SpellLink id={SPELLS.EVASIVE_STRIDE.id} />.</>, emallson),
  change(date(2020, 10, 24), <>Added checklist item and timeline annotations for bad <SpellLink id={SPELLS.TIGER_PALM.id} /> casts.</>, emallson),
  change(date(2020, 10, 18), 'Converted legacy listeners to new event filters', Zeboot),
  change(date(2020, 10, 17), 'Added some missing abilities and updated ability cooldowns.', Dambroda),
  change(date(2020, 10, 16), <>Removed "Inefficient <SpellLink id={SPELLS.PURIFYING_BREW.id} /> Casts" statistic.</>, emallson),
  change(date(2020, 10, 12), 'Updated checklist to reflect the Shadowlands changes.', emallson),
  change(date(2020, 10, 6), <>Added Fallen Order statistic.</>, Abelito75),
  change(date(2020, 9, 5), <>Updated Brewmaster spells for Shadowlands.</>, emallson),
  change(date(2020, 5, 4), <>Fixed bug in <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> handling of absorbs.</>, emallson),
  change(date(2020, 3, 10), <>Updated hit tracking for Ny'alotha and bumped compatability to 8.3.</>, emallson),
  change(date(2019, 12, 22), <>Warning threshold for <SpellLink id={SPELLS.HEAVY_STAGGER_DEBUFF.id} /> / <SpellLink id={SPELLS.PURIFYING_BREW.id} /> is now calculated based on time spent in red stagger.</>, emallson),
  change(date(2019, 10, 28), <>Added support for items in the <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> tab.</>, Abelito75),
  change(date(2019, 10, 16), <>Removed <SpellLink id={SPELLS.ARCANE_TORRENT_ENERGY.id} /> from Brewmaster suggestions.</>, emallson),
  change(date(2019, 9, 27), <>Update to date for 8.2.5 </>,Abelito75),
  change(date(2019, 7, 20), 'Updated hit-tracking blacklist and internal constants for Eternal Palace.', emallson),
  change(date(2019, 6, 28), <>Added a <SpellLink id={SPELLS.HOT_TRUB.id} /> damage estimator. Nerf sucks :(</>, [emallson]),
  change(date(2019, 4, 11), <>Fixed a bug in the Mitigation Values tab that caused Mastery's base 8% to be counted towards contribution by Mastery Rating.</>, [emallson]),
  change(date(2019, 3, 10), <>Fixed a bug in the Ironskin Brew normalizer that led to a crash in the new timeline.</>, [Zerotorescue]),
  change(date(2019, 2, 16), <>Removed <SpellLink id={SPELLS.BREATH_OF_FIRE.id} /> from the checklist.</>, [emallson]),
  change(date(2019, 1, 31), <>Added Mitigation Values tab showing estimated stat values for damage mitigation.</>, [emallson]),
  change(date(2018, 12, 30), <>Added <SpellLink id={SPELLS.STRAIGHT_NO_CHASER.id} /> module.</>, [emallson]),
  change(date(2018, 12, 29), <>Added <SpellLink id={SPELLS.GIFT_OF_THE_OX_1.id} /> healing statistic.</>, [emallson]),
  change(date(2018, 10, 26), <>Added <SpellLink id={SPELLS.CELESTIAL_FORTUNE_HEAL.id} /> healing statistic and table.</>, [emallson]),
  change(date(2018, 10, 22), <>The <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module now loads on-demand, improving load times for lower-end devices.</>, [emallson]),
  change(date(2018, 10, 15), <>Added <SpellLink id={SPELLS.PURIFYING_BREW.id} /> checklist item.</>, [emallson]),
  change(date(2018, 10, 2), <>Re-enabled the <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module and added additional distribution information to it.</>, [emallson]),
  change(date(2018, 9, 27), 'Updated Stagger plot to show very quick purifies more accurately.', [emallson]),
  change(date(2018, 9, 22), <>Updated Ironskin Brew and <SpellLink id={SPELLS.BREATH_OF_FIRE.id} /> suggestions to use hits mitigated instead of uptime.</>, [emallson]),
  change(date(2018, 9, 22), <>Added support for <SpellLink id={SPELLS.FIT_TO_BURST.id} />.</>, [emallson]),
  change(date(2018, 9, 13), <>Added support for <SpellLink id={SPELLS.ELUSIVE_FOOTWORK.id} />.</>, [emallson]),
  change(date(2018, 8, 11), <>Added support for <SpellLink id={SPELLS.STAGGERING_STRIKES.id} />.</>, [emallson]),
  change(date(2018, 7, 22), <>Updated support for Soul of the Grandmaster legendary and temporarily disabled the <SpellLink id={SPELLS.MASTERY_ELUSIVE_BRAWLER.id} /> module pending new formula coefficients.</>, [emallson]),
  change(date(2018, 7, 18), <>Added support for <SpellLink id={SPELLS.LIGHT_BREWING_TALENT.id} />.</>, [emallson]),
  change(date(2018, 7, 15), <>Added <SpellLink id={SPELLS.TRAINING_OF_NIUZAO.id} /> support.</>, [emallson]),
  change(date(2018, 6, 16), <>Updated Ironskin Brew cooldown and duration and added Guard, along with other changes in beta build 26812.</>, [emallson]),
];
<|endoftext|>


<commit_before>/**
 * Demo Website of FOXEL SA, Geneva Switzerland.
 *
 * Copyright (c) 2014 FOXEL SA - http://foxel.ch
 * Please read <http://foxel.ch/license> for more information.
 *
 *
 * Author(s):
 *
 *      Alexandre Kraft <a.kraft@foxel.ch>
 *
 *
 * This file is part of the FOXEL project <http://foxel.ch>.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Additional Terms:
 *
 *      You are required to preserve legal notices and author attributions in
 *      that material or in the Appropriate Legal Notices displayed by works
 *      containing it.
 *
 *      You are required to attribute the work as explained in the "Usage and
 *      Attribution" section of <http://foxel.ch/license>.
 */

$(document).ready(function() {

    var panorama;

    /**
     * layout
     */
    var _layout = function() {

        $('#wrapper').width($(window).width());
        $('#wrapper').height($(window).height());

        if ($(window).width() <= 700)
            $('footer').addClass('smalldevice');
        else
            $('footer').removeClass('smalldevice');
    };

    // layout
    _layout();
    $(window).on('resize', function(e) {
        _layout();
    });

    /**
     * init nav scrollbar
     */
    var _scrollbar = function() {

        var _w = 0;
        $.each($('#nav .dataset'),function(index,el) {
            _w += $(el).outerWidth(true);
        });
        $('#nav .area').width(_w+10);

        $('#nav .scrollable').mCustomScrollbar({
            axis: 'x',
            theme: 'light-thin',
            advanced: {
                updateOnContentResize: true
            }
        });
    };

    /**
     * panorama currently moving
     */
    var _isPanoMoving = function() {
        if (panorama == null)
            return false;
        return !(panorama.mode.mousedown=='undefined' || !panorama.mode.mousedown);
    };

    // nav scrollbar
    _scrollbar();

    // nav
    var navStartHeight = 0;

    /**
     * nav mouseenter
     */
    $('#nav').on('mouseenter',function() {

        if (_isPanoMoving())
            return;

        var navTargetHeight = $('#nav .main').outerHeight(true);

        $('#nav .shade').stop(true,false).animate({
            height: navTargetHeight,
            opacity: 0.85
        },400,'easeOutQuart');

        $('#nav .tab').stop(true,false).animate({
            top: navTargetHeight
        },400,'easeOutQuart');

        $('#nav .main').stop(true,false).animate({
            bottom: -navTargetHeight
        },400,'easeOutQuart');

        $('#nav .tab .lay').stop(true,false).fadeTo(400,0.85);

    });

    /**
     * nav mouseleave
     */
    $('#nav').on('mouseleave',function() {

        if (_isPanoMoving())
            return;

        $('#nav .shade').stop(true,false).animate({
            height: navStartHeight,
            opacity: 0.5
        },400,'easeOutQuart');

        $('#nav .tab').stop(true,false).animate({
            top: navStartHeight
        },400,'easeOutQuart');

        $('#nav .main').stop(true,false).animate({
            bottom: navStartHeight
        },400,'easeOutQuart');

        $('#nav .tab .lay').stop(true,false).fadeTo(400,0.5);

    });

    // footer
    var footerStartHeight = $('footer .shade').height();

    /**
     * footer mouseenter
     */
    $('footer').on('mouseenter',function() {

        if ($(this).hasClass('inactive') || $(this).hasClass('smalldevice') || _isPanoMoving())
            return;

        var footerTargetHeight = $('footer .more').outerHeight(true) + $('footer .more').position().top;

        $('footer .shade').stop(true,false).animate({
            height: footerTargetHeight
        },400,'easeOutQuart');

        $('footer .main').stop(true,false).animate({
            height: footerTargetHeight
        },400,'easeOutQuart');

        $('footer .scroll').stop(true,false).fadeOut(250);
        $('footer .logo.attribution').stop(true,false).fadeOut(250);
        $('footer .caption').stop(true,false).fadeOut(250);

    });

    /**
     * footer mouseleave
     */
    $('footer').on('mouseleave',function() {

        if ($(this).hasClass('inactive') || $(this).hasClass('smalldevice') || _isPanoMoving())
            return;

        $('footer .shade').stop(true,false).animate({
            height: footerStartHeight
        },400,'easeOutQuart');

        $('footer .main').stop(true,false).animate({
            height: footerStartHeight
        },400,'easeOutQuart');

        $('footer .scroll').stop(true,false).delay(500).fadeIn(250);
        $('footer .logo.attribution').stop(true,false).delay(250).fadeIn(250);
        $('footer .caption').stop(true,false).delay(250).fadeIn(250);

    });

    /**
     * arrow move
     */
    var _arrow = function() {

        $('footer .scroll > img')
            .delay(1000)
            .animate({
                    marginTop: '+=5',
                },250,'swing')
            .animate({
                    marginTop: '-=5',
                    opacity: 0.75
                }, {
                    duration: 250,
                    complete: function() {
                        _arrow();
                    },
                    easing: 'swing'
                });

    };

    // arrow
    _arrow();

    // options
    var options = {

        lon: 180,

        camera: {
            zoom: {
                max: 1.5
            }
        },

        fov: {
            max: 140
        },

        controls: {
            keyboard: {
                move: {
                    active: true,
                    step: 2
                },
                zoom: {
                    active: true
                }
            },
            devicemotion: {
                move: {
                    active: true
                }
            }
        },

        renderer: {
            precision: 'lowp',
            antialias: false,
            alpha: false
        },

        sphere: {
            texture: {
                dirName: 'tiles/'+cfg.path,
                baseName: cfg.src,
                columns: 16,
                rows: 8
            }
        }

    };

    // override options
    $.extend(options,cfg.override);

    // freepano
    $('#pano').panorama(options);
    panorama = $('#pano').data('pano');

});
<commit_msg>Dezoom a little by default<commit_after>/**
 * Demo Website of FOXEL SA, Geneva Switzerland.
 *
 * Copyright (c) 2014 FOXEL SA - http://foxel.ch
 * Please read <http://foxel.ch/license> for more information.
 *
 *
 * Author(s):
 *
 *      Alexandre Kraft <a.kraft@foxel.ch>
 *
 *
 * This file is part of the FOXEL project <http://foxel.ch>.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Additional Terms:
 *
 *      You are required to preserve legal notices and author attributions in
 *      that material or in the Appropriate Legal Notices displayed by works
 *      containing it.
 *
 *      You are required to attribute the work as explained in the "Usage and
 *      Attribution" section of <http://foxel.ch/license>.
 */

$(document).ready(function() {

    var panorama;

    /**
     * layout
     */
    var _layout = function() {

        $('#wrapper').width($(window).width());
        $('#wrapper').height($(window).height());

        if ($(window).width() <= 700)
            $('footer').addClass('smalldevice');
        else
            $('footer').removeClass('smalldevice');
    };

    // layout
    _layout();
    $(window).on('resize', function(e) {
        _layout();
    });

    /**
     * init nav scrollbar
     */
    var _scrollbar = function() {

        var _w = 0;
        $.each($('#nav .dataset'),function(index,el) {
            _w += $(el).outerWidth(true);
        });
        $('#nav .area').width(_w+10);

        $('#nav .scrollable').mCustomScrollbar({
            axis: 'x',
            theme: 'light-thin',
            advanced: {
                updateOnContentResize: true
            }
        });
    };

    /**
     * panorama currently moving
     */
    var _isPanoMoving = function() {
        if (panorama == null)
            return false;
        return !(panorama.mode.mousedown=='undefined' || !panorama.mode.mousedown);
    };

    // nav scrollbar
    _scrollbar();

    // nav
    var navStartHeight = 0;

    /**
     * nav mouseenter
     */
    $('#nav').on('mouseenter',function() {

        if (_isPanoMoving())
            return;

        var navTargetHeight = $('#nav .main').outerHeight(true);

        $('#nav .shade').stop(true,false).animate({
            height: navTargetHeight,
            opacity: 0.85
        },400,'easeOutQuart');

        $('#nav .tab').stop(true,false).animate({
            top: navTargetHeight
        },400,'easeOutQuart');

        $('#nav .main').stop(true,false).animate({
            bottom: -navTargetHeight
        },400,'easeOutQuart');

        $('#nav .tab .lay').stop(true,false).fadeTo(400,0.85);

    });

    /**
     * nav mouseleave
     */
    $('#nav').on('mouseleave',function() {

        if (_isPanoMoving())
            return;

        $('#nav .shade').stop(true,false).animate({
            height: navStartHeight,
            opacity: 0.5
        },400,'easeOutQuart');

        $('#nav .tab').stop(true,false).animate({
            top: navStartHeight
        },400,'easeOutQuart');

        $('#nav .main').stop(true,false).animate({
            bottom: navStartHeight
        },400,'easeOutQuart');

        $('#nav .tab .lay').stop(true,false).fadeTo(400,0.5);

    });

    // footer
    var footerStartHeight = $('footer .shade').height();

    /**
     * footer mouseenter
     */
    $('footer').on('mouseenter',function() {

        if ($(this).hasClass('inactive') || $(this).hasClass('smalldevice') || _isPanoMoving())
            return;

        var footerTargetHeight = $('footer .more').outerHeight(true) + $('footer .more').position().top;

        $('footer .shade').stop(true,false).animate({
            height: footerTargetHeight
        },400,'easeOutQuart');

        $('footer .main').stop(true,false).animate({
            height: footerTargetHeight
        },400,'easeOutQuart');

        $('footer .scroll').stop(true,false).fadeOut(250);
        $('footer .logo.attribution').stop(true,false).fadeOut(250);
        $('footer .caption').stop(true,false).fadeOut(250);

    });

    /**
     * footer mouseleave
     */
    $('footer').on('mouseleave',function() {

        if ($(this).hasClass('inactive') || $(this).hasClass('smalldevice') || _isPanoMoving())
            return;

        $('footer .shade').stop(true,false).animate({
            height: footerStartHeight
        },400,'easeOutQuart');

        $('footer .main').stop(true,false).animate({
            height: footerStartHeight
        },400,'easeOutQuart');

        $('footer .scroll').stop(true,false).delay(500).fadeIn(250);
        $('footer .logo.attribution').stop(true,false).delay(250).fadeIn(250);
        $('footer .caption').stop(true,false).delay(250).fadeIn(250);

    });

    /**
     * arrow move
     */
    var _arrow = function() {

        $('footer .scroll > img')
            .delay(1000)
            .animate({
                    marginTop: '+=5',
                },250,'swing')
            .animate({
                    marginTop: '-=5',
                    opacity: 0.75
                }, {
                    duration: 250,
                    complete: function() {
                        _arrow();
                    },
                    easing: 'swing'
                });

    };

    // arrow
    _arrow();

    // options
    var options = {

        lon: 180,

        camera: {
            zoom: {
                max: 1.5,
                current: 1.25
            }
        },

        fov: {
            max: 140
        },

        controls: {
            keyboard: {
                move: {
                    active: true,
                    step: 2
                },
                zoom: {
                    active: true
                }
            },
            devicemotion: {
                move: {
                    active: true
                }
            }
        },

        renderer: {
            precision: 'lowp',
            antialias: false,
            alpha: false
        },

        sphere: {
            texture: {
                dirName: 'tiles/'+cfg.path,
                baseName: cfg.src,
                columns: 16,
                rows: 8
            }
        }

    };

    // override options
    $.extend(options,cfg.override);

    // freepano
    $('#pano').panorama(options);
    panorama = $('#pano').data('pano');

});
<|endoftext|>