<commit_before>/*
Mag.JS AddOns v0.21
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/
!function(e,t,n,a){e.noop=function(){},e.copy=e.utils.copy,e.merge=e.utils.merge,e.show=function(t,n){var a=typeof t;return"object"!==a?(a={_config:function(e){e.style.display=t?"block":"none"}},"object"==typeof n&&e.merge(n,a),a):void("object"==a?t.willload=function(e,t){t.style.display="block"}:t.willupdate=function(e,t,a){t.style.display=a[n]?"block":"none"})},e.deferred=function(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e},e.when=function(e,t){Promise.all(e).then(t)},e.request=function(t){var n=e.deferred();t.initialValue&&(n.promise.initialValue=t.initialValue);var a=JSON.stringify(t);if(t.cache){var i=e.cache(a);if(i)return n.resolve(i),n.promise}if(e.request.queue[a])return e.request.queue[a];e.request.queue[a]=n.promise;var r=new XMLHttpRequest,i=(t.method||"GET").toUpperCase(),u="GET"!==i&&t.data?t.data:"";return r.onload=function(i){var u=r.getResponseHeader("content-type")||"";i=i.target.responseText,-1<u.indexOf("json")&&(i=JSON.parse(i)),t.cache&&e.cache(a,i,t.cacheTime,t.cacheType),delete e.request.queue[a],n.resolve(i)},r.onerror=function(e){n.reject(e)},r.open(i,t.url),r.send(u),n.promise},e.request.queue={},e.cache=function(n,a,i,r){var u=t[r||"sessionStorage"];if(1==arguments.length){if(e.cache.data[n])return e.cache.data[n].data;var c=u.getItem(n);return c?(e.cache.data[n]=JSON.parse(c),e.cache.data[n].data):0}e.cache.data[n]&&e.cache.data[n].id&&clearTimeout(e.cache.data[n].id),c=setTimeout(function(t){u.removeItem(t),delete e.cache.data[t]}.bind({},n),i||6e5),e.cache.data[n]={id:c,data:a},u.setItem(n,JSON.stringify(e.cache.data[n]))},e.cache.data={},e.requestWithFeedback=function(t){var a=JSON.stringify(t);if(!e.requestWithFeedback.cache[a]){for(var i,r=n.querySelectorAll(".loader"),u=0;i=r[u];u++)i.style.display="block";var c=function(t){if(delete e.requestWithFeedback.cache[a],0==Object.keys(e.requestWithFeedback.cache).length)for(var n,i=0;n=r[i];i++)n.style.display="none";return t};e.requestWithFeedback.cache[a]=e.request(t),e.requestWithFeedback.cache[a].then(c,function(e){throw c(e),e})}return e.requestWithFeedback.cache[a]},e.requestWithFeedback.cache={},e.disable=function(e,t){"object"!=typeof this[e]&&(this[e]={_value:this[e]});var n=this[e]._config?this[e]._config:function(){};this[e]._config=function(e){n.call(this,arguments),t()?e.setAttribute("disabled","disabled"):e.removeAttribute("disabled")}},e.find=function(t,a){var i=n.getElementById(t);return e.fill.find(i,a)},e.toMenu=function(e,t){return e.map(function(e,n){return{_text:e,_value:n,_selected:t===n||t===e?!0:null}})},e.namespace=function(t,n){var a=t.split("."),i=a.shift();return n=n||this,n[i]=n[i]||{},a.length?e.namespace(a.join("."),n[i]):n[i]},e.route=function(e){function t(){for(var t=e.location.hash.substr(1).split("/"),i=t.length,r=0;r<n.length;r++){var u=n[r];if(u.parts.length===i){for(var c=[],o=0;i>o;o++)if(":"===u.parts[o].substr(0,1))c.push(t[o]);else if(u.parts[o]!==t[o])break;if(o===i)return u.handler.apply(a,c),a}}}var n=[];return e.onhashchange=t,{addRoute:function(e,t){n.push({parts:e.split("/"),handler:t})},load:function(t){e.location.hash=t},start:t}}(t),e.hookin("attributes","key",function(){}),e.hookin("attributes","className",function(e){e.key="class";var t=e.value;if("string"==typeof e.value)t=e.value.split(" ");else if("object"==typeof e.value){for(var n in t)e.node.classList.toggle(n,t[n]);return e.value=e.node.classList+"",a}e.value=e.node.classList+"";for(n in t){var i=t[n];i.trim()&&0<e.node.classList.length&&!e.node.classList.contains(i.trim())?e.value=e.node.classList+" "+i:i.trim()&&0==e.node.classList.length&&(e.value+=i+" ")}e.value=e.value.trim()}),e.hookin("values","*",function(t){var n=typeof t.value;if(null!=t.value)if("function"===n&&t.value.toJSON)t.change=1,t.value=t.value();else if("object"==n&&"function"==typeof t.value.then){var a=e.utils.items.getItem(e.fill.id);t.value.then(function(n){e.mod.getState(a)[t.key]=n}),t.value.initialValue&&(t.change=1,t.value=t.value.initialValue)}}),e.prop=function(e){var t=function(){return arguments.length&&(e=arguments[0]),e};return t.toJSON=function(){return e},t.type="fun",t},e.withProp=function(e,t){return function(n){n=n||event,n=n.currentTarget||this,t(e in n?n[e]:n.getAttribute(e))}}}(mag,window,document);
<commit_msg>Update mag.addons.0.21.min.js<commit_after>/*
Mag.JS AddOns v0.21
http://github.com/magnumjs/mag.js
(c) Michael Glazer
License: MIT
*/
!function(e,t,a,n){e.noop=function(){},e.copy=e.utils.copy,e.merge=e.utils.merge,e.show=function(t,a){var n=typeof t;return"object"!==n?(n={_config:function(e){e.style.display=t?"block":"none"}},"object"==typeof a&&e.merge(a,n),n):void("object"==n?t.willload=function(e,t){t.style.display="block"}:t.willupdate=function(e,t,n){t.style.display=n[a]?"block":"none"})},e.deferred=function(){var e={};return e.promise=new Promise(function(t,a){e.resolve=t,e.reject=a}),e},e.when=function(e,t){Promise.all(e).then(t)},e.request=function(t){var a=e.deferred();t.initialValue&&(a.promise.initialValue=t.initialValue);var n=JSON.stringify(t);if(t.cache){var r=e.cache(n);if(r)return a.resolve(r),a.promise}if(e.request.queue[n])return e.request.queue[n];e.request.queue[n]=a.promise;var i=new XMLHttpRequest,c=(t.method||"GET").toUpperCase(),r="GET"!==c&&t.data?t.data:"";if(i.onload=function(r){var c=i.getResponseHeader("content-type")||"";r=r.target.responseText,-1<c.indexOf("json")&&(r=JSON.parse(r)),t.cache&&e.cache(n,r,t.cacheTime,t.cacheType),delete e.request.queue[n],a.resolve(r)},i.onerror=function(e){a.reject(e)},i.open(c,t.url),0<t.headers.length)for(c=0;c<t.headers.length;c++){var u,s=t.headers[c];for(u in s)i.setRequestHeader(u,s[u])}return i.send(r),a.promise},e.request.queue={},e.cache=function(a,n,r,i){var c=t[i||"sessionStorage"];if(1==arguments.length){if(e.cache.data[a])return e.cache.data[a].data;var u=c.getItem(a);return u?(e.cache.data[a]=JSON.parse(u),e.cache.data[a].data):0}e.cache.data[a]&&e.cache.data[a].id&&clearTimeout(e.cache.data[a].id),u=setTimeout(function(t){c.removeItem(t),delete e.cache.data[t]}.bind({},a),r||6e5),e.cache.data[a]={id:u,data:n},c.setItem(a,JSON.stringify(e.cache.data[a]))},e.cache.data={},e.requestWithFeedback=function(t){var n=JSON.stringify(t);if(!e.requestWithFeedback.cache[n]){for(var r,i=a.querySelectorAll(".loader"),c=0;r=i[c];c++)r.style.display="block";var u=function(t){if(delete e.requestWithFeedback.cache[n],0==Object.keys(e.requestWithFeedback.cache).length)for(var a,r=0;a=i[r];r++)a.style.display="none";return t};e.requestWithFeedback.cache[n]=e.request(t),e.requestWithFeedback.cache[n].then(u,function(e){throw u(e),e})}return e.requestWithFeedback.cache[n]},e.requestWithFeedback.cache={},e.disable=function(e,t){"object"!=typeof this[e]&&(this[e]={_value:this[e]});var a=this[e]._config?this[e]._config:function(){};this[e]._config=function(e){a.call(this,arguments),t()?e.setAttribute("disabled","disabled"):e.removeAttribute("disabled")}},e.find=function(t,n){var r=a.getElementById(t);return e.fill.find(r,n)},e.toMenu=function(e,t){return e.map(function(e,a){return{_text:e,_value:a,_selected:t===a||t===e?!0:null}})},e.namespace=function(t,a){var n=t.split("."),r=n.shift();return a=a||this,a[r]=a[r]||{},n.length?e.namespace(n.join("."),a[r]):a[r]},e.route=function(e){function t(){for(var t=e.location.hash.substr(1).split("/"),r=t.length,i=0;i<a.length;i++){var c=a[i];if(c.parts.length===r){for(var u=[],s=0;r>s;s++)if(":"===c.parts[s].substr(0,1))u.push(t[s]);else if(c.parts[s]!==t[s])break;if(s===r)return c.handler.apply(n,u),n}}}var a=[];return e.onhashchange=t,{addRoute:function(e,t){a.push({parts:e.split("/"),handler:t})},load:function(t){e.location.hash=t},start:t}}(t),e.hookin("attributes","key",function(){}),e.hookin("attributes","className",function(e){e.key="class";var t=e.value;if("string"==typeof e.value)t=e.value.split(" ");else if("object"==typeof e.value){for(var a in t)e.node.classList.toggle(a,t[a]);return e.value=e.node.classList+"",n}e.value=e.node.classList+"";for(a in t){var r=t[a];r.trim()&&0<e.node.classList.length&&!e.node.classList.contains(r.trim())?e.value=e.node.classList+" "+r:r.trim()&&0==e.node.classList.length&&(e.value+=r+" ")}e.value=e.value.trim()}),e.hookin("values","*",function(t){var a=typeof t.value;if(null!=t.value)if("function"===a&&t.value.toJSON)t.change=1,t.value=t.value();else if("object"==a&&"function"==typeof t.value.then){var n=e.utils.items.getItem(e.fill.id);t.value.then(function(a){e.mod.getState(n)[t.key]=a}),t.value.initialValue&&(t.change=1,t.value=t.value.initialValue)}}),e.prop=function(e){var t=function(){return arguments.length&&(e=arguments[0]),e};return t.toJSON=function(){return e},t.type="fun",t},e.withProp=function(e,t){return function(a){a=a||event,a=a.currentTarget||this,t(e in a?a[e]:a.getAttribute(e))}}}(mag,window,document);
<|endoftext|>


<commit_before>/*global BugMagnet, describe, window, it, afterEach, beforeEach, jasmine, expect, chrome, spyOn*/
describe('BugMagnet.initChromeMenu', function () {
	'use strict';
	var oldHttpRequest, fakeHttpRequest, fakeRoot;
	beforeEach(function () {
		oldHttpRequest = window.XMLHttpRequest;
		chrome.extension.getURL.and.returnValue('http://some-url');
		spyOn(BugMagnet, 'processConfigText');
		window.XMLHttpRequest = function () {
			var self = this;
			self.open = jasmine.createSpy('open');
			self.send = jasmine.createSpy('send');
			fakeHttpRequest = this;
		};
		fakeRoot = {fake: 'root'};
		chrome.contextMenus.create.calls.reset();
		chrome.contextMenus.create.and.returnValue(fakeRoot);
		chrome.storage.sync.get.calls.reset();
		chrome.storage.onChanged.addListener.calls.reset();
	});
	afterEach(function () {
		window.XMLHttpRequest = oldHttpRequest;
	});
	it('creates a root menu and the menu builder', function () {
		BugMagnet.initChromeMenu();
		expect(chrome.contextMenus.create.calls.count()).toBe(1);
		expect(BugMagnet.rootMenu).toBe(fakeRoot);
	});
	describe('standard config', function () {
		it('loads the standard config using xhr', function () {
			BugMagnet.initChromeMenu();

			expect(chrome.extension.getURL).toHaveBeenCalledWith('config.json');
			expect(fakeHttpRequest.open).toHaveBeenCalledWith('GET', 'http://some-url');
			expect(fakeHttpRequest.send).toHaveBeenCalled();
		});
		it('does not load the config text immediately', function () {
			BugMagnet.initChromeMenu();
			expect(BugMagnet.processConfigText).not.toHaveBeenCalled();
		});
		it('registers an onload handler that creates the menu', function () {
			BugMagnet.initChromeMenu();
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
			expect(BugMagnet.processConfigText).toHaveBeenCalledWith('some-text', jasmine.any(BugMagnet.ChromeMenuBuilder), fakeRoot);
		});
	});
	describe('additional config', function () {
		beforeEach(function () {
			spyOn(BugMagnet, 'processMenuObject');
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
		});
		it('asks the chrome.storage.sync api for additional config items', function () {
			BugMagnet.initChromeMenu();
			expect(chrome.storage.sync.get).toHaveBeenCalledWith({additionalMenus: []}, jasmine.any(Function));
		});
		it('loads additional items from the config and name property of each array element', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback({additionalMenus: [{name: 'additional 1', config: {first: 'yes'}}, {name: 'additional 2', config: {second: 'yes'}}]});
			expect(BugMagnet.processMenuObject.calls.count()).toBe(2);
			expect(BugMagnet.processMenuObject.calls.argsFor(0)[0]).toEqual({'additional 1': {first: 'yes'}});
			expect(BugMagnet.processMenuObject.calls.argsFor(1)[0]).toEqual({'additional 2': {second: 'yes'}});
		});
		it('does nothing if chrome.storage.sync api does not have any additional config', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback(false);
			expect(BugMagnet.processMenuObject).not.toHaveBeenCalled();
		});
		it('does nothing if chrome.storage.sync api has an empty array', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback([]);
			expect(BugMagnet.processMenuObject).not.toHaveBeenCalled();
		});
	});
	describe('sync storage listener', function () {
		var newFakeRoot = {newRoot: 'root'},
				underTest;
		beforeEach(function () {
			spyOn(BugMagnet, 'processMenuObject');
			BugMagnet.initChromeMenu();
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
			chrome.contextMenus.create.calls.reset();
			chrome.contextMenus.removeAll.calls.reset();
			chrome.contextMenus.create.and.returnValue(newFakeRoot);
			fakeHttpRequest.send.calls.reset();
			underTest = chrome.storage.onChanged.addListener.calls.argsFor(0)[0];

		});
		it('clears all menu items', function () {
			underTest({additionalMenus: [{name: 'a', config:{'b': 'c'}}]}, 'sync');
			expect(chrome.contextMenus.removeAll).toHaveBeenCalled();
		});
		describe('after menu was cleared', function () {
			beforeEach(function () {
				underTest({additionalMenus: {newValue: [{name: 'a', config:{'b': 'c'}}]}}, 'sync');
				chrome.contextMenus.removeAll.calls.argsFor(0)[0]();
			});
			it('creates a new root menu', function () {
				expect(BugMagnet.rootMenu).toBe(newFakeRoot);
			});
			it('reloads menus from cached standard config response', function () {
				expect(fakeHttpRequest.send).not.toHaveBeenCalled();
				expect(BugMagnet.processConfigText).toHaveBeenCalledWith('some-text', jasmine.any(BugMagnet.ChromeMenuBuilder), newFakeRoot);
			});
			it('appends any elements from the additionalMenus.newValue object', function () {
				expect(BugMagnet.processMenuObject.calls.count()).toBe(1);
				expect(BugMagnet.processMenuObject.calls.first().args[0]).toEqual({'a': {'b':'c'}});
			});
		});
		it('ignores changes that are not for the sync storage area', function () {
			underTest({additionalMenus: [{name: 'a', config:{'b': 'c'}}]}, 'local');
			expect(chrome.contextMenus.removeAll).not.toHaveBeenCalled();
			expect(BugMagnet.rootMenu).toBe(fakeRoot);
		});
		it('ignores changes that are not for the additionalMenus object', function () {
			underTest({version: 2}, 'sync');
			expect(chrome.contextMenus.removeAll).not.toHaveBeenCalled();
			expect(BugMagnet.rootMenu).toBe(fakeRoot);
		});
	});
});
<commit_msg>linting<commit_after>/*global BugMagnet, describe, window, it, afterEach, beforeEach, jasmine, expect, chrome, spyOn*/
describe('BugMagnet.initChromeMenu', function () {
	'use strict';
	var oldHttpRequest, fakeHttpRequest, fakeRoot;
	beforeEach(function () {
		oldHttpRequest = window.XMLHttpRequest;
		chrome.extension.getURL.and.returnValue('http://some-url');
		spyOn(BugMagnet, 'processConfigText');
		window.XMLHttpRequest = function () {
			var self = this;
			self.open = jasmine.createSpy('open');
			self.send = jasmine.createSpy('send');
			fakeHttpRequest = this;
		};
		fakeRoot = {fake: 'root'};
		chrome.contextMenus.create.calls.reset();
		chrome.contextMenus.create.and.returnValue(fakeRoot);
		chrome.storage.sync.get.calls.reset();
		chrome.storage.onChanged.addListener.calls.reset();
	});
	afterEach(function () {
		window.XMLHttpRequest = oldHttpRequest;
	});
	it('creates a root menu and the menu builder', function () {
		BugMagnet.initChromeMenu();
		expect(chrome.contextMenus.create.calls.count()).toBe(1);
		expect(BugMagnet.rootMenu).toBe(fakeRoot);
	});
	describe('standard config', function () {
		it('loads the standard config using xhr', function () {
			BugMagnet.initChromeMenu();

			expect(chrome.extension.getURL).toHaveBeenCalledWith('config.json');
			expect(fakeHttpRequest.open).toHaveBeenCalledWith('GET', 'http://some-url');
			expect(fakeHttpRequest.send).toHaveBeenCalled();
		});
		it('does not load the config text immediately', function () {
			BugMagnet.initChromeMenu();
			expect(BugMagnet.processConfigText).not.toHaveBeenCalled();
		});
		it('registers an onload handler that creates the menu', function () {
			BugMagnet.initChromeMenu();
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
			expect(BugMagnet.processConfigText).toHaveBeenCalledWith('some-text', jasmine.any(BugMagnet.ChromeMenuBuilder), fakeRoot);
		});
	});
	describe('additional config', function () {
		beforeEach(function () {
			spyOn(BugMagnet, 'processMenuObject');
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
		});
		it('asks the chrome.storage.sync api for additional config items', function () {
			BugMagnet.initChromeMenu();
			expect(chrome.storage.sync.get).toHaveBeenCalledWith({additionalMenus: []}, jasmine.any(Function));
		});
		it('loads additional items from the config and name property of each array element', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback({additionalMenus: [{name: 'additional 1', config: {first: 'yes'}}, {name: 'additional 2', config: {second: 'yes'}}]});
			expect(BugMagnet.processMenuObject.calls.count()).toBe(2);
			expect(BugMagnet.processMenuObject.calls.argsFor(0)[0]).toEqual({'additional 1': {first: 'yes'}});
			expect(BugMagnet.processMenuObject.calls.argsFor(1)[0]).toEqual({'additional 2': {second: 'yes'}});
		});
		it('does nothing if chrome.storage.sync api does not have any additional config', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback(false);
			expect(BugMagnet.processMenuObject).not.toHaveBeenCalled();
		});
		it('does nothing if chrome.storage.sync api has an empty array', function () {
			BugMagnet.initChromeMenu();
			var callback = chrome.storage.sync.get.calls.argsFor(0)[1];
			callback([]);
			expect(BugMagnet.processMenuObject).not.toHaveBeenCalled();
		});
	});
	describe('sync storage listener', function () {
		var newFakeRoot = {newRoot: 'root'},
				underTest;
		beforeEach(function () {
			spyOn(BugMagnet, 'processMenuObject');
			BugMagnet.initChromeMenu();
			fakeHttpRequest.onload.apply({responseText: 'some-text'});
			chrome.contextMenus.create.calls.reset();
			chrome.contextMenus.removeAll.calls.reset();
			chrome.contextMenus.create.and.returnValue(newFakeRoot);
			fakeHttpRequest.send.calls.reset();
			underTest = chrome.storage.onChanged.addListener.calls.argsFor(0)[0];

		});
		it('clears all menu items', function () {
			underTest({additionalMenus: [{name: 'a', config: {'b': 'c'}}]}, 'sync');
			expect(chrome.contextMenus.removeAll).toHaveBeenCalled();
		});
		describe('after menu was cleared', function () {
			beforeEach(function () {
				underTest({additionalMenus: {newValue: [{name: 'a', config: {'b': 'c'}}]}}, 'sync');
				chrome.contextMenus.removeAll.calls.argsFor(0)[0]();
			});
			it('creates a new root menu', function () {
				expect(BugMagnet.rootMenu).toBe(newFakeRoot);
			});
			it('reloads menus from cached standard config response', function () {
				expect(fakeHttpRequest.send).not.toHaveBeenCalled();
				expect(BugMagnet.processConfigText).toHaveBeenCalledWith('some-text', jasmine.any(BugMagnet.ChromeMenuBuilder), newFakeRoot);
			});
			it('appends any elements from the additionalMenus.newValue object', function () {
				expect(BugMagnet.processMenuObject.calls.count()).toBe(1);
				expect(BugMagnet.processMenuObject.calls.first().args[0]).toEqual({'a': {'b': 'c'}});
			});
		});
		it('ignores changes that are not for the sync storage area', function () {
			underTest({additionalMenus: [{name: 'a', config: {'b': 'c'}}]}, 'local');
			expect(chrome.contextMenus.removeAll).not.toHaveBeenCalled();
			expect(BugMagnet.rootMenu).toBe(fakeRoot);
		});
		it('ignores changes that are not for the additionalMenus object', function () {
			underTest({version: 2}, 'sync');
			expect(chrome.contextMenus.removeAll).not.toHaveBeenCalled();
			expect(BugMagnet.rootMenu).toBe(fakeRoot);
		});
	});
});
<|endoftext|>


<commit_before>/**
 * @author alteredq / http://alteredqualia.com/
 */

function Clock( autoStart ) {

	this.autoStart = ( autoStart !== undefined ) ? autoStart : true;

	this.startTime = 0;
	this.oldTime = 0;
	this.elapsedTime = 0;

	this.running = false;

}

Clock.prototype = {

	constructor: Clock,

	start: function () {

		this.startTime = ( performance || Date ).now();

		this.oldTime = this.startTime;
		this.running = true;

	},

	stop: function () {

		this.getElapsedTime();
		this.running = false;

	},

	getElapsedTime: function () {

		this.getDelta();
		return this.elapsedTime;

	},

	getDelta: function () {

		var diff = 0;

		if ( this.autoStart && ! this.running ) {

			this.start();

		}

		if ( this.running ) {

			var newTime = ( performance || Date ).now();

			diff = ( newTime - this.oldTime ) / 1000;
			this.oldTime = newTime;

			this.elapsedTime += diff;

		}

		return diff;

	}

};

export { Clock };
<commit_msg>getElapsedTime() in TREE.Clock returns whole running time and not elapsed time from previous start. (#9692)<commit_after>/**
 * @author alteredq / http://alteredqualia.com/
 */

function Clock( autoStart ) {

	this.autoStart = ( autoStart !== undefined ) ? autoStart : true;

	this.startTime = 0;
	this.oldTime = 0;
	this.elapsedTime = 0;

	this.running = false;

}

Clock.prototype = {

	constructor: Clock,

	start: function () {

		this.startTime = ( performance || Date ).now();

		this.oldTime = this.startTime;
		this.elapsedTime = 0;
		this.running = true;

	},

	stop: function () {

		this.getElapsedTime();
		this.running = false;

	},

	getElapsedTime: function () {

		this.getDelta();
		return this.elapsedTime;

	},

	getDelta: function () {

		var diff = 0;

		if ( this.autoStart && ! this.running ) {

			this.start();

		}

		if ( this.running ) {

			var newTime = ( performance || Date ).now();

			diff = ( newTime - this.oldTime ) / 1000;
			this.oldTime = newTime;

			this.elapsedTime += diff;

		}

		return diff;

	}

};

export { Clock };
<|endoftext|>


<commit_before>const { render } = require('@bolt/twig-renderer');
const { readYamlFileSync } = require('@bolt/build-tools/utils/yaml');
const { join } = require('path');
const schema = readYamlFileSync(join(__dirname, '../image.schema.yml'));
const { tag } = schema.properties;

describe('<bolt-image> Component', async () => {
  test('<bolt-image> with ratio object compiles', async () => {
    const results = await render('@bolt-components-image/image.twig', {
      src: '/images/placeholders/tout-4x3-climber.jpg',
      alt: 'A Rock Climber',
    });
    expect(results.ok).toBe(true);
    expect(results.html).toMatchSnapshot();
  });

  test('<bolt-image> with ratio object and lazyloading disabled compiles', async () => {
    const results = await render('@bolt-components-image/image.twig', {
      src: '/images/placeholders/tout-4x3-climber.jpg',
      alt: 'A Rock Climber',
      lazyload: false,
    });
    expect(results.ok).toBe(true);
    expect(results.html).toMatchSnapshot();
  });
});
<commit_msg>Update image.js<commit_after>const { render } = require('@bolt/twig-renderer');
const { readYamlFileSync } = require('@bolt/build-tools/utils/yaml');
const { join } = require('path');
const schema = readYamlFileSync(join(__dirname, '../image.schema.yml'));
const { tag } = schema.properties;

describe('<bolt-image> Component', async () => {
  test('<bolt-image> with ratio object compiles', async () => {
    const results = await render('@bolt-components-image/image.twig', {
      src: '/images/placeholders/tout-4x3-climber.jpg',
      alt: 'A Rock Climber',
      lazyload: true,
    });
    expect(results.ok).toBe(true);
    expect(results.html).toMatchSnapshot();
  });

  test('<bolt-image> with ratio object and lazyloading disabled compiles', async () => {
    const results = await render('@bolt-components-image/image.twig', {
      src: '/images/placeholders/tout-4x3-climber.jpg',
      alt: 'A Rock Climber',
      lazyload: false,
    });
    expect(results.ok).toBe(true);
    expect(results.html).toMatchSnapshot();
  });
});
<|endoftext|>


<commit_before>var common = require('../common');
var helpers = require('./helpers');
var assert = common.assert;

var stormpath = require('../../');

var Account = require('../../lib/resource/Account');
var AccessToken = require('../../lib/resource/AccessToken');

describe('OAuthClientCredentialsAuthenticator', function() {
  var newAccount;
  var application;

  before(function(done) {
    newAccount = helpers.fakeAccount();

    helpers.createApplication(function(err, app) {
      application = app;
      application.createAccount(newAccount, done);
    });
  });

  after(function(done) {
    helpers.cleanupApplicationAndStores(application, done);
  });

  describe('object construction', function() {
    describe('with new operator', function() {
      var auth;

      before(function() {
        auth = new stormpath.OAuthClientCredentialsAuthenticator(application);
      });

      it('should return an instance of OAuthClientCredentialsAuthenticator', function() {
        assert.instanceOf(auth, stormpath.OAuthClientCredentialsAuthenticator);
      });

      it('should return a new instance', function() {
        var auth2 = new stormpath.OAuthClientCredentialsAuthenticator(application);

        assert.notEqual(auth, auth2);
      });

      it('should construct an instance with the `application` property', function() {
        assert.property(auth, 'application');
        assert.equal(auth.application, application);
      });
    });

    describe('without new operator', function() {
      var auth;

      before(function() {
        auth = stormpath.OAuthClientCredentialsAuthenticator(application);
      });

      it('should return an instance of OAuthClientCredentialsAuthenticator', function() {
        assert.instanceOf(auth, stormpath.OAuthClientCredentialsAuthenticator);
      });

      it('should return a new instance', function() {
        var auth2 = stormpath.OAuthClientCredentialsAuthenticator(application);

        assert.notEqual(auth, auth2);
      });

      it('should construct an instance with the `application` property', function() {
        assert.property(auth, 'application');
        assert.equal(auth.application, application);
      });
    });
  });

  describe('calling the #authenticate(data, callback) method', function() {
    var auth;
    var apiKey;
    var account;
    var badApiKey = {
      id: 'id',
      secret: 'secret'
    };

    before(function(done) {
      auth = new stormpath.OAuthClientCredentialsAuthenticator(application);

      application.createAccount(helpers.fakeAccount(), function(err, accountResponse) {
        if (err) {
          return done(err);
        }

        account = accountResponse;

        account.createApiKey(function(err, apiKeyResponse) {
          if (err) {
            return done(err);
          }

          apiKey = {
            id: apiKeyResponse.id,
            secret: apiKeyResponse.secret
          };

          done();
        });
      });
    });

    after(function() {
      account.delete();
    });

    describe('parameter validation', function() {
      it('should throw an error if the `authenticationRequest` param is not provided, or not an object', function() {
        assert.throws(auth.authenticate.bind(auth), Error, 'The \'authenticationRequest\' parameter must be an object.');
        assert.throws(auth.authenticate.bind(auth, 'boom!'), Error, 'The \'authenticationRequest\' parameter must be an object.');
        assert.doesNotThrow(auth.authenticate.bind(auth, {}), /^The 'authenticationRequest' parameter must be an object\.$/);
      });

      it('should throw an error if the `callback` param is not provided, or not a function', function() {
        assert.throws(auth.authenticate.bind(auth, {}), Error, 'The \'callback\' parameter must be a function.');
        assert.throws(auth.authenticate.bind(auth, {}, {}), Error, 'The \'callback\' parameter must be a function.');
        assert.doesNotThrow(auth.authenticate.bind(auth, {}, function() {}), /^The 'callback' parameter must be a function\.$/);
      });

      it('should throw an error if the `authenticationRequest` param does not have an `apiKey` field', function() {
        assert.throws(auth.authenticate.bind(auth, {}, function() {}), Error, 'apiKey object within request is required');
        assert.doesNotThrow(auth.authenticate.bind(auth, {apiKey: {}}, function() {}), /^apiKey object within request is required$/);
      });

      it('should throw an error if the `authenticationRequest.apiKey` object does not contain the `id` and `secret` fields', function() {
        assert.throws(auth.authenticate.bind(auth, {apiKey: {}}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.throws(auth.authenticate.bind(auth, {apiKey: {
          id: 'id'
        }}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.throws(auth.authenticate.bind(auth, {apiKey: {
          secret: 'secret'
        }}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.doesNotThrow(auth.authenticate.bind(auth, {apiKey: {
          id: 'id',
          secret: 'secret'
        }}, function() {}), /^apiKey object must contain 'id' and 'secret' fields$/);
      });
    });

    describe('calling the method with correct number and type of parameters', function() {
      describe('and the api key is valid', function() {
        it('should succeed with an OAuthClientCredentialsAuthenticationResult', function(done) {
          auth.authenticate({apiKey: apiKey}, function(err, result) {
            if (err) {
              return done(err);
            }

            assert.notOk(err);
            assert.ok(result);
            assert.instanceOf(result, stormpath.OAuthClientCredentialsAuthenticationResult);
            done();
          });
        });
      });

      describe('and the api key is invalid', function() {
        it('should fail with a ResourceError', function() {
          auth.authenticate({apiKey: badApiKey}, function(err, result) {
            assert.ok(err);
            assert.equal(err.name, 'ResourceError');
            assert.equal(err.status, 400);
            assert.equal(err.code, 10019);
            assert.equal(err.developerMessage, 'API Key Authentication failed because the API key or secret submitted is invalid.');
            assert.notOk(result);
          });
        });
      });
    });

    describe('returned OAuthClientCredentialsAuthenticationResult', function() {
      var authResult;

      beforeEach(function(done) {
        auth.authenticate({apiKey: apiKey}, function(err, result) {
          if (err) {
            return done(err);
          }

          authResult = result;
          done();
        });
      });

      it('should store the token response', function() {
        assert.property(authResult, 'accessTokenResponse');
      });

      it('should fetch the correct Account via #getAccount', function() {
        var getAccountCallback = function(err, accountResponse) {
          assert.notOk(err);
          assert.ok(accountResponse);
          assert.instanceOf(accountResponse, Account);
          assert.deepEqual(accountResponse, account);
        };

        assert.property(authResult, 'getAccount');
        authResult.getAccount(getAccountCallback);
      });

      it('should fetch an AccessToken via #getAccessToken', function() {
        var getAccessTokenCallback = function(err, accessToken) {
          assert.notOk(err);
          assert.ok(accessToken);
          assert.instanceOf(accessToken, AccessToken);
        };

        assert.property(authResult, 'getAccessToken');
        authResult.getAccessToken(getAccessTokenCallback);
      });
    });

  });
});
<commit_msg>Fix async issues in client credentials oauth it<commit_after>var common = require('../common');
var helpers = require('./helpers');
var assert = common.assert;

var stormpath = require('../../');

var Account = require('../../lib/resource/Account');
var AccessToken = require('../../lib/resource/AccessToken');

describe('OAuthClientCredentialsAuthenticator', function() {
  var newAccount;
  var application;

  before(function(done) {
    newAccount = helpers.fakeAccount();

    helpers.createApplication(function(err, app) {
      application = app;
      application.createAccount(newAccount, done);
    });
  });

  after(function(done) {
    helpers.cleanupApplicationAndStores(application, done);
  });

  describe('object construction', function() {
    describe('with new operator', function() {
      var auth;

      before(function() {
        auth = new stormpath.OAuthClientCredentialsAuthenticator(application);
      });

      it('should return an instance of OAuthClientCredentialsAuthenticator', function() {
        assert.instanceOf(auth, stormpath.OAuthClientCredentialsAuthenticator);
      });

      it('should return a new instance', function() {
        var auth2 = new stormpath.OAuthClientCredentialsAuthenticator(application);

        assert.notEqual(auth, auth2);
      });

      it('should construct an instance with the `application` property', function() {
        assert.property(auth, 'application');
        assert.equal(auth.application, application);
      });
    });

    describe('without new operator', function() {
      var auth;

      before(function() {
        auth = stormpath.OAuthClientCredentialsAuthenticator(application);
      });

      it('should return an instance of OAuthClientCredentialsAuthenticator', function() {
        assert.instanceOf(auth, stormpath.OAuthClientCredentialsAuthenticator);
      });

      it('should return a new instance', function() {
        var auth2 = stormpath.OAuthClientCredentialsAuthenticator(application);

        assert.notEqual(auth, auth2);
      });

      it('should construct an instance with the `application` property', function() {
        assert.property(auth, 'application');
        assert.equal(auth.application, application);
      });
    });
  });

  describe('calling the #authenticate(data, callback) method', function() {
    var auth;
    var apiKey;
    var account;
    var badApiKey = {
      id: 'id',
      secret: 'secret'
    };

    before(function(done) {
      auth = new stormpath.OAuthClientCredentialsAuthenticator(application);

      application.createAccount(helpers.fakeAccount(), function(err, accountResponse) {
        if (err) {
          return done(err);
        }

        account = accountResponse;

        account.createApiKey(function(err, apiKeyResponse) {
          if (err) {
            return done(err);
          }

          apiKey = {
            id: apiKeyResponse.id,
            secret: apiKeyResponse.secret
          };

          done();
        });
      });
    });

    after(function() {
      account.delete();
    });

    describe('parameter validation', function() {
      it('should throw an error if the `authenticationRequest` param is not provided, or not an object', function() {
        assert.throws(auth.authenticate.bind(auth), Error, 'The \'authenticationRequest\' parameter must be an object.');
        assert.throws(auth.authenticate.bind(auth, 'boom!'), Error, 'The \'authenticationRequest\' parameter must be an object.');
        assert.doesNotThrow(auth.authenticate.bind(auth, {}), /^The 'authenticationRequest' parameter must be an object\.$/);
      });

      it('should throw an error if the `callback` param is not provided, or not a function', function() {
        assert.throws(auth.authenticate.bind(auth, {}), Error, 'The \'callback\' parameter must be a function.');
        assert.throws(auth.authenticate.bind(auth, {}, {}), Error, 'The \'callback\' parameter must be a function.');
        assert.doesNotThrow(auth.authenticate.bind(auth, {}, function() {}), /^The 'callback' parameter must be a function\.$/);
      });

      it('should throw an error if the `authenticationRequest` param does not have an `apiKey` field', function() {
        assert.throws(auth.authenticate.bind(auth, {}, function() {}), Error, 'apiKey object within request is required');
        assert.doesNotThrow(auth.authenticate.bind(auth, {apiKey: {}}, function() {}), /^apiKey object within request is required$/);
      });

      it('should throw an error if the `authenticationRequest.apiKey` object does not contain the `id` and `secret` fields', function() {
        assert.throws(auth.authenticate.bind(auth, {apiKey: {}}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.throws(auth.authenticate.bind(auth, {apiKey: {
          id: 'id'
        }}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.throws(auth.authenticate.bind(auth, {apiKey: {
          secret: 'secret'
        }}, function() {}), Error, 'apiKey object must contain \'id\' and \'secret\' fields');
        assert.doesNotThrow(auth.authenticate.bind(auth, {apiKey: {
          id: 'id',
          secret: 'secret'
        }}, function() {}), /^apiKey object must contain 'id' and 'secret' fields$/);
      });
    });

    describe('calling the method with correct number and type of parameters', function() {
      describe('and the api key is valid', function() {
        it('should succeed with an OAuthClientCredentialsAuthenticationResult', function(done) {
          auth.authenticate({apiKey: apiKey}, function(err, result) {
            if (err) {
              return done(err);
            }

            assert.notOk(err);
            assert.ok(result);
            assert.instanceOf(result, stormpath.OAuthClientCredentialsAuthenticationResult);
            done();
          });
        });
      });

      describe('and the api key is invalid', function() {
        it('should fail with a ResourceError', function() {
          auth.authenticate({apiKey: badApiKey}, function(err, result) {
            assert.ok(err);
            assert.equal(err.name, 'ResourceError');
            assert.equal(err.status, 400);
            assert.equal(err.code, 10019);
            assert.equal(err.developerMessage, 'API Key Authentication failed because the API key or secret submitted is invalid.');
            assert.notOk(result);
          });
        });
      });
    });

    describe('returned OAuthClientCredentialsAuthenticationResult', function() {
      var authResult;

      beforeEach(function(done) {
        auth.authenticate({apiKey: apiKey}, function(err, result) {
          if (err) {
            return done(err);
          }

          authResult = result;
          done();
        });
      });

      it('should store the token response', function() {
        assert.property(authResult, 'accessTokenResponse');
      });

      it('should fetch the correct Account via #getAccount', function(done) {
        var getAccountCallback = function(err, accountResponse) {
          if (err) {
            return done(err);
          }

          assert.notOk(err);
          assert.ok(accountResponse);
          assert.instanceOf(accountResponse, Account);
          assert.deepEqual(accountResponse, account);
          done();
        };

        assert.property(authResult, 'getAccount');
        authResult.getAccount(getAccountCallback);
      });

      it('should fetch an AccessToken via #getAccessToken', function(done) {
        var getAccessTokenCallback = function(err, accessToken) {
          if (err) {
            return done(err);
          }

          assert.notOk(err);
          assert.ok(accessToken);
          assert.instanceOf(accessToken, AccessToken);
          done();
        };

        assert.property(authResult, 'getAccessToken');
        authResult.getAccessToken(getAccessTokenCallback);
      });
    });

  });
});
<|endoftext|>


<commit_before>import AbstractModel from 'hospitalrun/models/abstract';
import DateFormat from 'hospitalrun/mixins/date-format';
import DS from 'ember-data';
import Ember from 'ember';
import NumberFormat from 'hospitalrun/mixins/number-format';
import PatientValidation from 'hospitalrun/utils/patient-validation';

export default AbstractModel.extend(DateFormat, NumberFormat, {
  externalInvoiceNumber: DS.attr('string'),
  patient: DS.belongsTo('patient', {
    async: false
  }),
  patientInfo: DS.attr('string'), // Needed for searching
  visit: DS.belongsTo('visit', {
    async: false
  }),
  status: DS.attr('string'),
  remarks: DS.attr('string'),
  billDate: DS.attr('date'),
  paidTotal: DS.attr('number'),
  paymentProfile: DS.belongsTo('price-profile', {
    async: false
  }),
  /*payments track the number of payment events attached to an invoice.*/
  payments: DS.hasMany('payment', {
    async: false
  }),
  /*the individual line items of the invoice*/
  lineItems: DS.hasMany('billing-line-item', {
    async: false
  }),

  addPayment: function(payment) {
    var payments = this.get('payments');
    payments.addObject(payment);
    this.paymentAmountChanged();
  },

  billDateAsTime: function() {
    return this.dateToTime(this.get('billDate'));
  }.property('billDate'),

  discountTotals: Ember.computed.mapBy('lineItemsByCategory', 'discount'),
  discount: Ember.computed.sum('discountTotals'),

  nationalInsuranceTotals: Ember.computed.mapBy('lineItemsByCategory', 'nationalInsurance'),
  nationalInsurance: Ember.computed.sum('nationalInsuranceTotals'),

  paidFlag: function() {
    return (this.get('status') === 'Paid');
  }.property('status'),

  remainingBalance: function() {
    var patientResponsibility = this.get('patientResponsibility'),
      paidTotal = this.get('paidTotal');
    return this._numberFormat((patientResponsibility - paidTotal), true);
  }.property('patientResponsibility', 'paidTotal'),

  privateInsuranceTotals: Ember.computed.mapBy('lineItemsByCategory', 'privateInsurance'),
  privateInsurance: Ember.computed.sum('privateInsuranceTotals'),

  lineTotals: Ember.computed.mapBy('lineItems', 'total'),
  total: Ember.computed.sum('lineTotals'),

  displayInvoiceNumber: function() {
    var externalInvoiceNumber = this.get('externalInvoiceNumber'),
      id = this.get('id');
    if (Ember.isEmpty(externalInvoiceNumber)) {
      return id;
    } else {
      return externalInvoiceNumber;
    }
  }.property('externalInvoiceNumber', 'id'),

  lineItemsByCategory: function() {
    var lineItems = this.get('lineItems'),
      byCategory = [];
    lineItems.forEach(function(lineItem) {
      var category = lineItem.get('category'),
        categoryList = byCategory.findBy('category', category);
      if (Ember.isEmpty(categoryList)) {
        categoryList = {
          category: category,
          items: []
        };
        byCategory.push(categoryList);
      }
      categoryList.items.push(lineItem);
    }.bind(this));
    byCategory.forEach(function(categoryList) {
      categoryList.amountOwed = this._calculateTotal(categoryList.items, 'amountOwed');
      categoryList.discount = this._calculateTotal(categoryList.items, 'discount');
      categoryList.nationalInsurance = this._calculateTotal(categoryList.items, 'nationalInsurance');
      categoryList.privateInsurance = this._calculateTotal(categoryList.items, 'privateInsurance');
      categoryList.total = this._calculateTotal(categoryList.items, 'total');
    }.bind(this));
    return byCategory;
  }.property('lineItems.@each.amountOwed'),
  patientIdChanged: function() {
    if (!Ember.isEmpty(this.get('patient'))) {
      var patientDisplayName = this.get('patient.displayName'),
        patientDisplayId = this.get('patient.displayPatientId');
      this.set('patientInfo', `${patientDisplayName} - ${patientDisplayId}`);
    }
  }.observes('patient.displayName', 'patient.id', 'patient.displayPatientId'),

  patientResponsibilityTotals: Ember.computed.mapBy('lineItems', 'amountOwed'),
  patientResponsibility: Ember.computed.sum('patientResponsibilityTotals'),

  paymentAmountChanged: function() {
    var payments = this.get('payments'),
      paidTotal = payments.reduce(function(previousValue, payment) {
        return previousValue += this._getValidNumber(payment.get('amount'));
      }.bind(this), 0);
    this.set('paidTotal', this._numberFormat(paidTotal, true));
    var remainingBalance = this.get('remainingBalance');
    if (remainingBalance <= 0) {
      this.set('status', 'Paid');
    }
  }.observes('payments.[]', 'payments.@each.amount'),

  validations: {
    patientTypeAhead: PatientValidation.patientTypeAhead,

    patient: {
      presence: true
    },

    visit: {
      presence: true
    }
  }
});
<commit_msg>Mark an invoice as paid only when the payments have been saved<commit_after>import AbstractModel from 'hospitalrun/models/abstract';
import DateFormat from 'hospitalrun/mixins/date-format';
import DS from 'ember-data';
import Ember from 'ember';
import NumberFormat from 'hospitalrun/mixins/number-format';
import PatientValidation from 'hospitalrun/utils/patient-validation';

export default AbstractModel.extend(DateFormat, NumberFormat, {
  externalInvoiceNumber: DS.attr('string'),
  patient: DS.belongsTo('patient', {
    async: false
  }),
  patientInfo: DS.attr('string'), // Needed for searching
  visit: DS.belongsTo('visit', {
    async: false
  }),
  status: DS.attr('string'),
  remarks: DS.attr('string'),
  billDate: DS.attr('date'),
  paidTotal: DS.attr('number'),
  paymentProfile: DS.belongsTo('price-profile', {
    async: false
  }),
  /*payments track the number of payment events attached to an invoice.*/
  payments: DS.hasMany('payment', {
    async: false
  }),
  /*the individual line items of the invoice*/
  lineItems: DS.hasMany('billing-line-item', {
    async: false
  }),

  addPayment: function(payment) {
    var payments = this.get('payments');
    payments.addObject(payment);
    this.paymentAmountChanged();
  },

  billDateAsTime: function() {
    return this.dateToTime(this.get('billDate'));
  }.property('billDate'),

  discountTotals: Ember.computed.mapBy('lineItemsByCategory', 'discount'),
  discount: Ember.computed.sum('discountTotals'),

  nationalInsuranceTotals: Ember.computed.mapBy('lineItemsByCategory', 'nationalInsurance'),
  nationalInsurance: Ember.computed.sum('nationalInsuranceTotals'),

  paidFlag: function() {
    return (this.get('status') === 'Paid');
  }.property('status'),

  remainingBalance: function() {
    var patientResponsibility = this.get('patientResponsibility'),
      paidTotal = this.get('paidTotal');
    return this._numberFormat((patientResponsibility - paidTotal), true);
  }.property('patientResponsibility', 'paidTotal'),

  privateInsuranceTotals: Ember.computed.mapBy('lineItemsByCategory', 'privateInsurance'),
  privateInsurance: Ember.computed.sum('privateInsuranceTotals'),

  lineTotals: Ember.computed.mapBy('lineItems', 'total'),
  total: Ember.computed.sum('lineTotals'),

  displayInvoiceNumber: function() {
    var externalInvoiceNumber = this.get('externalInvoiceNumber'),
      id = this.get('id');
    if (Ember.isEmpty(externalInvoiceNumber)) {
      return id;
    } else {
      return externalInvoiceNumber;
    }
  }.property('externalInvoiceNumber', 'id'),

  lineItemsByCategory: function() {
    var lineItems = this.get('lineItems'),
      byCategory = [];
    lineItems.forEach(function(lineItem) {
      var category = lineItem.get('category'),
        categoryList = byCategory.findBy('category', category);
      if (Ember.isEmpty(categoryList)) {
        categoryList = {
          category: category,
          items: []
        };
        byCategory.push(categoryList);
      }
      categoryList.items.push(lineItem);
    }.bind(this));
    byCategory.forEach(function(categoryList) {
      categoryList.amountOwed = this._calculateTotal(categoryList.items, 'amountOwed');
      categoryList.discount = this._calculateTotal(categoryList.items, 'discount');
      categoryList.nationalInsurance = this._calculateTotal(categoryList.items, 'nationalInsurance');
      categoryList.privateInsurance = this._calculateTotal(categoryList.items, 'privateInsurance');
      categoryList.total = this._calculateTotal(categoryList.items, 'total');
    }.bind(this));
    return byCategory;
  }.property('lineItems.@each.amountOwed'),
  patientIdChanged: function() {
    if (!Ember.isEmpty(this.get('patient'))) {
      var patientDisplayName = this.get('patient.displayName'),
        patientDisplayId = this.get('patient.displayPatientId');
      this.set('patientInfo', `${patientDisplayName} - ${patientDisplayId}`);
    }
  }.observes('patient.displayName', 'patient.id', 'patient.displayPatientId'),

  patientResponsibilityTotals: Ember.computed.mapBy('lineItems', 'amountOwed'),
  patientResponsibility: Ember.computed.sum('patientResponsibilityTotals'),

  paymentAmountChanged: function() {
    var payments = this.get('payments').filter(function(payment) {
      return !payment.get('isNew');
    });
    if (payments.length === 0) {
      return;
    }
    var paidTotal = payments.reduce(function(previousValue, payment) {
        return previousValue += this._getValidNumber(payment.get('amount'));
      }.bind(this), 0);
    this.set('paidTotal', this._numberFormat(paidTotal, true));
    var remainingBalance = this.get('remainingBalance');
    if (remainingBalance <= 0) {
      this.set('status', 'Paid');
    }
  }.observes('payments.[]', 'payments.@each.amount'),

  validations: {
    patientTypeAhead: PatientValidation.patientTypeAhead,

    patient: {
      presence: true
    },

    visit: {
      presence: true
    }
  }
});
<|endoftext|>


<commit_before>import { isObject } from '../utils'

export default function(target, value, transformFn) {
  function set(context) {
    const { store, props, resolve } = context

    if (!resolve.isTag(target, 'state', 'props', 'moduleState')) {
      throw new Error(
        'Cerebral factory.set: You have to use the STATE, PROPS or MODULESTATE tag as first argument'
      )
    }

    let resolvedValue = resolve.value(value)

    if (!resolve.isResolveValue(value) && isObject(value)) {
      resolvedValue = Object.assign({}, resolvedValue)
    } else if (!resolve.isResolveValue(value) && Array.isArray(value)) {
      resolvedValue = resolvedValue.slice()
    }

    if (transformFn) {
      resolvedValue = transformFn(resolvedValue, context)
    }

    if (target.type === 'state' || target.type === 'moduleState') {
      store.set(target, resolvedValue)
    } else {
      const result = Object.assign({}, props)
      const parts = resolve.path(target).split('.')
      const key = parts.pop()
      const targetObj = parts.reduce((target, key) => {
        return (target[key] = Object.assign({}, target[key] || {}))
      }, result)
      targetObj[key] = resolvedValue

      return result
    }
  }

  set.displayName = `factory.set(${String(target)}, ${String(value)})`

  return set
}
<commit_msg>fix(cerebral): fix issue with computed and props tag<commit_after>import { isObject } from '../utils'
import { Compute } from '../Compute'

export default function(target, value, transformFn) {
  function set(context) {
    const { store, props, resolve } = context

    if (!resolve.isTag(target, 'state', 'props', 'moduleState')) {
      throw new Error(
        'Cerebral factory.set: You have to use the STATE, PROPS or MODULESTATE tag as first argument'
      )
    }

    let resolvedValue = resolve.value(value)

    if (resolvedValue && resolvedValue instanceof Compute) {
      resolvedValue = resolvedValue.getValue(props)
    }

    if (!resolve.isResolveValue(value) && isObject(value)) {
      resolvedValue = Object.assign({}, resolvedValue)
    } else if (!resolve.isResolveValue(value) && Array.isArray(value)) {
      resolvedValue = resolvedValue.slice()
    }

    if (transformFn) {
      resolvedValue = transformFn(resolvedValue, context)
    }

    if (target.type === 'state' || target.type === 'moduleState') {
      store.set(target, resolvedValue)
    } else {
      const result = Object.assign({}, props)
      const parts = resolve.path(target).split('.')
      const key = parts.pop()
      const targetObj = parts.reduce((target, key) => {
        return (target[key] = Object.assign({}, target[key] || {}))
      }, result)
      targetObj[key] = resolvedValue

      return result
    }
  }

  set.displayName = `factory.set(${String(target)}, ${String(value)})`

  return set
}
<|endoftext|>


<commit_before>var path = require("path");
var webpack = require("webpack");
let npmCommand = process.env.npm_lifecycle_event;

let browsers = ["ChromeHeadless"];

if (npmCommand.includes(":watch")) {
    browsers = ["Chrome"];
}

module.exports = function (config) {
    config.set({
        frameworks: ["jasmine"],
        browsers: browsers,
        port: 6400,
        files: ["./test/entry.ts"],
        preprocessors: {
            "./test/entry.ts": ["webpack"],
        },
        webpack: {
            devtool: "inline-source-map",
            mode: "development",
            resolve: {
                extensions: [".ts", ".tsx", ".js"],
                alias: {
                    "@elements": path.resolve("./src/elements"),
                    "@metadata": path.resolve("./src/metadata"),
                    "@sandbox": path.resolve("./src/sandbox"),
                    "@sample-types": path.resolve("./src/playground/types")
                }
            },
            module: {
                rules: [
                    {
                        test: /\.ts$/,
                        loader: "ts-loader",
                        options: {
                            configFile: "tsconfig-test.json"
                        }
                    },
                    {
                        enforce: "post",
                        test: /\.ts$/,
                        loader: "istanbul-instrumenter-loader",
                        include: /src/,
                        exclude: /\.spec\.ts$/
                    }
                ]
            },
            plugins: [
                // allows source inspection @ browser
                new webpack.SourceMapDevToolPlugin({
                    filename: null,
                    test: /\.(ts|js)($|\?)/i
                })
            ]
        },
        reporters: ["mocha", "coverage-istanbul"],
        mochaReporter: {
            ignoreSkipped: true
        },
        coverageIstanbulReporter: {
            reports: ["text-summary", "html"]
        },
        webpackMiddleware: {
            noInfo: true
        },
        mime: {
            "text/x-typescript": ["ts", "tsx"]
        } // so chrome doesn"t refuse execution
    });
}<commit_msg>[meta] source maps while testing<commit_after>var path = require("path");
var webpack = require("webpack");
let npmCommand = process.env.npm_lifecycle_event;

let browsers = ["ChromeHeadless"];

if (npmCommand.includes(":watch")) {
    browsers = ["Chrome"];
}

module.exports = function (config) {
    config.set({
        frameworks: ["jasmine"],
        browsers: browsers,
        port: 6400,
        files: ["./test/entry.ts"],
        preprocessors: {
            "./test/entry.ts": ["webpack"],
        },
        webpack: {
            devtool: "inline-source-map",
            mode: "development",
            resolve: {
                extensions: [".ts", ".tsx", ".js"],
                alias: {
                    "@elements": path.resolve("./src/elements"),
                    "@metadata": path.resolve("./src/metadata"),
                    "@sandbox": path.resolve("./src/sandbox"),
                    "@sample-types": path.resolve("./src/playground/types")
                }
            },
            module: {
                rules: [
                    {
                        test: /\.ts$/,
                        loader: "ts-loader",
                        options: {
                            configFile: "tsconfig-test.json"
                        }
                    },
                    {
                        enforce: "post",
                        test: /\.ts$/,
                        loader: "istanbul-instrumenter-loader",
                        include: /src/,
                        exclude: /\.spec\.ts$/
                    }
                ]
            }
        },
        reporters: ["mocha", "coverage-istanbul"],
        mochaReporter: {
            ignoreSkipped: true
        },
        coverageIstanbulReporter: {
            reports: ["text-summary", "html"]
        },
        webpackMiddleware: {
            noInfo: true
        },
        mime: {
            "text/x-typescript": ["ts", "tsx"]
        } // so chrome doesn"t refuse execution
    });
}<|endoftext|>


<commit_before>import peopleApi from '../api/people'
import colors from '../../lib/colors'
import { populateTask } from '../../lib/helpers'
import { sortTasks } from '../../lib/sorting'
import { indexSearch, buildTaskIndex } from '../../lib/indexing'
import taskStatusStore from './taskstatus'
import {
  LOAD_PEOPLE_START,
  LOAD_PEOPLE_ERROR,
  LOAD_PEOPLE_END,

  SHOW_IMPORT_PEOPLE_MODAL,
  HIDE_IMPORT_PEOPLE_MODAL,
  PERSON_CSV_FILE_SELECTED,
  IMPORT_PEOPLE_START,
  IMPORT_PEOPLE_ERROR,
  IMPORT_PEOPLE_END,

  EDIT_PEOPLE_START,
  EDIT_PEOPLE_ERROR,
  EDIT_PEOPLE_END,
  SHOW_EDIT_PEOPLE_MODAL,
  HIDE_EDIT_PEOPLE_MODAL,

  NEW_PEOPLE_END,

  DELETE_PEOPLE_START,
  DELETE_PEOPLE_ERROR,
  DELETE_PEOPLE_END,
  SHOW_DELETE_PEOPLE_MODAL,
  HIDE_DELETE_PEOPLE_MODAL,

  UPLOAD_AVATAR_END,

  LOAD_PERSON_TASKS_END,
  SET_PERSON_TASKS_SEARCH,
  NEW_TASK_COMMENT_END,

  RESET_ALL
} from '../mutation-types'

const helpers = {
  addAdditionalInformation (person) {
    if (person.first_name && person.last_name) {
      person.name = `${person.first_name} ${person.last_name}`
      person.initials = `${person.first_name[0]}${person.last_name[0]}`
    } else if (person.first_name) {
      person.name = person.first_name
      person.initials = person.first_name[0]
    } else if (person.last_name) {
      person.name = person.last_name
      person.initials = person.last_name[0]
    } else {
      person.name = person.email
      person.initials = person.email[0]
    }
    person.initials = person.initials.toUpperCase()
    person.color = colors.fromString(person.name)
    if (person.has_avatar) {
      const randomHash = Math.random().toString(36).substring(7)
      person.avatarPath =
        `/api/pictures/thumbnails/persons/${person.id}` +
        `.png?unique=${randomHash}`
    }
    return person
  },

  getTaskStatus (taskStatusId) {
    return taskStatusStore.state.taskStatusMap[taskStatusId]
  }
}

const state = {
  people: [],
  personMap: {},
  isPeopleLoading: false,
  isPeopleLoadingError: true,

  isImportPeopleModalShown: false,
  isImportPeopleLoading: false,
  isImportPeopleLoadingError: false,

  isEditModalShown: false,
  isEditLoading: false,
  isEditLoadingError: false,
  personToEdit: {},

  isDeleteModalShown: false,
  isDeleteLoading: false,
  isDeleteLoadingError: false,
  personToDelete: undefined,

  personCsvFormData: undefined,

  personTasks: [],
  displayedPersonTasks: [],
  personTasksIndex: {},
  personTasksSearchText: ''
}

const getters = {
  people: state => state.people,
  personMap: state => state.personMap,
  isPeopleLoading: state => state.isPeopleLoading,
  isPeopleLoadingError: state => state.isPeopleLoadingError,

  isImportPeopleModalShown: state => state.isImportPeopleModalShown,
  isImportPeopleLoading: state => state.isImportPeopleLoading,
  isImportPeopleLoadingError: state => state.isImportPeopleLoadingError,

  isDeleteModalShown: state => state.isDeleteModalShown,
  isDeleteLoading: state => state.isDeleteLoading,
  isDeleteLoadingError: state => state.isDeleteLoadingError,
  personToDelete: state => state.personToDelete,

  isEditModalShown: state => state.isEditModalShown,
  isEditLoading: state => state.isEditLoading,
  isEditLoadingError: state => state.isEditLoadingError,
  personToEdit: state => state.personToEdit,

  personCsvFormData: state => state.personCsvFormData,

  displayedPersonTasks: state => state.displayedPersonTasks,
  personTasksSearchText: state => state.personTasksSearchText,

  getPerson: (state, getters) => (id) => state.personMap[id],
  getPersonOptions: state => state.people.map(
    (person) => {
      return {
        label: `${person.first_name} ${person.last_name}`,
        value: person.id
      }
    }
  )
}

const sortPeople = (people) => {
  return people.sort((a, b) => {
    if (a.first_name !== a.last_name) {
      return a.first_name.localeCompare(b.first_name)
    } else {
      return a.last_name.localeCompare(b.last_name)
    }
  })
}

const actions = {

  loadPeople ({ commit, state }, callback) {
    commit(LOAD_PEOPLE_START)
    peopleApi.getPeople((err, people) => {
      if (err) {
        commit(LOAD_PEOPLE_ERROR)
      } else {
        commit(LOAD_PEOPLE_END, people.map((person) => {
          person.departments = person.departments || ''
          return person
        }))
      }
      if (callback) callback(err)
    })
  },

  newPeople ({ commit, state }, payload) {
    commit(EDIT_PEOPLE_START, payload.data)
    peopleApi.newPerson(state.personToEdit, (err, person) => {
      if (err) {
        commit(EDIT_PEOPLE_ERROR)
      } else {
        commit(NEW_PEOPLE_END, person.id)
        commit(EDIT_PEOPLE_END)
        commit(HIDE_EDIT_PEOPLE_MODAL)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  editPeople ({ commit, state }, payload) {
    commit(EDIT_PEOPLE_START, payload.data)
    peopleApi.updatePerson(state.personToEdit, (err, people) => {
      if (err) {
        commit(EDIT_PEOPLE_ERROR)
      } else {
        commit(EDIT_PEOPLE_END)
        commit(HIDE_EDIT_PEOPLE_MODAL)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  deletePeople ({ commit, state }, callback) {
    commit(DELETE_PEOPLE_START)
    peopleApi.deletePerson(state.personToDelete.id, (err, people) => {
      if (err) {
        commit(DELETE_PEOPLE_ERROR)
      } else {
        commit(DELETE_PEOPLE_END)
        commit(HIDE_DELETE_PEOPLE_MODAL)
      }
      if (callback) callback(err)
    })
  },

  uploadPersonFile ({ commit, state }, callback) {
    commit(IMPORT_PEOPLE_START)
    peopleApi.postCsv(state.personCsvFormData, (err) => {
      if (err) {
        commit(IMPORT_PEOPLE_ERROR)
      }
      commit(IMPORT_PEOPLE_END)
      if (callback) callback(err)
    })
  },

  loadPersonTasks ({ commit, state }, { personId, forced, callback }) {
    if (state.personTasks.length === 0 || forced) {
      commit(LOAD_PERSON_TASKS_END, [])
      peopleApi.getPersonTasks(personId, (err, tasks) => {
        if (err) tasks = []
        commit(LOAD_PERSON_TASKS_END, tasks)
        if (callback) callback(err)
      })
    } else {
      if (callback) callback()
    }
  },

  showPersonImportModal ({ commit, state }, personId) {
    commit(SHOW_IMPORT_PEOPLE_MODAL)
  },

  hidePersonImportModal ({ commit, state }, personId) {
    commit(HIDE_IMPORT_PEOPLE_MODAL)
  },

  showPersonEditModal ({ commit, state }, personId) {
    commit(SHOW_EDIT_PEOPLE_MODAL, personId)
  },

  hidePersonEditModal ({ commit, state }, personId) {
    commit(HIDE_EDIT_PEOPLE_MODAL, personId)
  },

  showPersonDeleteModal ({ commit, state }, personId) {
    commit(SHOW_DELETE_PEOPLE_MODAL, personId)
  },

  hidePersonDeleteModal ({ commit, state }, personId) {
    commit(HIDE_DELETE_PEOPLE_MODAL, personId)
  },

  setPersonTasksSearch ({ commit, state }, searchText) {
    commit(SET_PERSON_TASKS_SEARCH, searchText)
  }
}

const mutations = {
  [LOAD_PEOPLE_START] (state) {
    state.isPeopleLoading = true
    state.isPeopleLoadingError = false
    state.personMap = {}
  },

  [LOAD_PEOPLE_ERROR] (state) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = true
  },

  [LOAD_PEOPLE_END] (state, people) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = false
    state.people = sortPeople(people)
    state.people.forEach((person) => {
      person = helpers.addAdditionalInformation(person)
      state.personMap[person.id] = person
    })
  },

  [DELETE_PEOPLE_START] (state) {
    state.isDeleteLoading = true
    state.isDeleteLoadingError = false
  },

  [DELETE_PEOPLE_END] (state) {
    state.isDeleteLoading = false
    const personToDeleteIndex = state.people.findIndex(
      (person) => person.id === state.personToDelete.id
    )
    state.people.splice(personToDeleteIndex, 1)
    delete state.personMap[state.personToDelete.id]
    state.personToDelete = undefined
  },

  [DELETE_PEOPLE_ERROR] (state) {
    state.isDeleteLoading = false
    state.isDeleteLoadingError = true
  },

  [SHOW_DELETE_PEOPLE_MODAL] (state, personId) {
    state.isDeleteModalShown = true
    state.isDeleteLoadingError = false
    state.isDeleteLoading = false
    state.personToDelete = state.people.find(
      (person) => person.id === personId
    )
  },

  [HIDE_DELETE_PEOPLE_MODAL] (state, personId) {
    state.isDeleteModalShown = false
    state.personToDelete = undefined
  },

  [EDIT_PEOPLE_START] (state, data) {
    state.isEditLoading = true
    state.isEditLoadingError = false
    state.personToEdit = Object.assign(state.personToEdit, data)
  },

  [NEW_PEOPLE_END] (state, personId) {
    state.personToEdit.id = personId
  },

  [EDIT_PEOPLE_END] (state) {
    state.isEditLoading = false
    state.isEditLoadingError = false
    const personToEditIndex = state.people.findIndex(
      (person) => person.id === state.personToEdit.id
    )
    state.personToEdit = helpers.addAdditionalInformation(state.personToEdit)
    if (personToEditIndex >= 0) {
      state.people[personToEditIndex] = state.personToEdit
    } else {
      state.people.push(state.personToEdit)
      state.personMap[state.personToEdit.id] = state.personToEdit
      sortPeople(state.people)
    }
    state.personToEdit = {}
  },

  [EDIT_PEOPLE_ERROR] (state) {
    state.isEditLoading = false
    state.isEditLoadingError = true
  },

  [IMPORT_PEOPLE_START] (state, data) {
    state.isImportPeopleLoading = true
    state.isImportPeopleLoadingError = false
  },

  [IMPORT_PEOPLE_END] (state, personId) {
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
  },

  [IMPORT_PEOPLE_ERROR] (state) {
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = true
  },

  [SHOW_EDIT_PEOPLE_MODAL] (state, personId) {
    state.isEditModalShown = true
    state.isEditLoadingError = false
    state.isEditLoading = false
    if (personId !== undefined) {
      state.personToEdit = state.people.find(
        (person) => person.id === personId
      )
    } else {
      state.personToEdit = {
        first_name: '',
        last_name: '',
        phone: '',
        email: ''
      }
    }
  },

  [HIDE_EDIT_PEOPLE_MODAL] (state) {
    state.isEditModalShown = false
    state.personToEdit = {}
  },

  [SHOW_IMPORT_PEOPLE_MODAL] (state) {
    state.isImportPeopleModalShown = true
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
  },

  [HIDE_IMPORT_PEOPLE_MODAL] (state) {
    state.isImportPeopleModalShown = false
  },

  [PERSON_CSV_FILE_SELECTED] (state, formData) {
    state.personCsvFormData = formData
  },

  [UPLOAD_AVATAR_END] (state, personId) {
    const randomHash = Math.random().toString(36).substring(7)
    const person = state.personMap[personId]
    person.has_avatar = true
    person.avatarPath =
      `/api/pictures/thumbnails/persons/${person.id}` +
      `.png?unique=${randomHash}`
  },

  [LOAD_PERSON_TASKS_END] (state, tasks) {
    tasks.forEach(populateTask)
    state.personTasks = sortTasks(tasks)

    state.personTasksIndex = buildTaskIndex(tasks)
    const searchResult = indexSearch(
      state.personTasksIndex,
      state.personTasksSearchText
    )
    state.displayedPersonTasks = searchResult || state.personTasks
  },

  [SET_PERSON_TASKS_SEARCH] (state, searchText) {
    const searchResult = indexSearch(state.personTasksIndex, searchText)
    state.personTasksSearchText = searchText
    state.displayedPersonTasks = searchResult || state.personTasks
  },

  [NEW_TASK_COMMENT_END] (state, {comment, taskId}) {
    const task = state.personTasks.find((task) => task.id === taskId)

    if (task) {
      const taskStatus = helpers.getTaskStatus(comment.task_status_id)

      Object.assign(task, {
        task_status_id: taskStatus.id,
        task_status_name: taskStatus.name,
        task_status_short_name: taskStatus.short_name,
        task_status_color: taskStatus.color
      })
    }
  },

  [RESET_ALL] (state, people) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = false

    state.isDeleteModalShown = false
    state.isDeleteLoading = false
    state.isDeleteLoadingError = false
    state.personToDelete = {}

    state.isEditModalShown = false
    state.isEditLoading = false
    state.isEditLoadingError = false
    state.personToEdit = {}

    state.isImportModalShown = false
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
    state.personCsvFormData = null

    state.people = []
    state.personMap = {}
  }
}

export default {
  state,
  getters,
  actions,
  mutations,
  helpers
}
<commit_msg>Fix wrong loading on person task list<commit_after>import peopleApi from '../api/people'
import colors from '../../lib/colors'
import { populateTask } from '../../lib/helpers'
import { sortTasks } from '../../lib/sorting'
import { indexSearch, buildTaskIndex } from '../../lib/indexing'
import taskStatusStore from './taskstatus'
import {
  LOAD_PEOPLE_START,
  LOAD_PEOPLE_ERROR,
  LOAD_PEOPLE_END,

  SHOW_IMPORT_PEOPLE_MODAL,
  HIDE_IMPORT_PEOPLE_MODAL,
  PERSON_CSV_FILE_SELECTED,
  IMPORT_PEOPLE_START,
  IMPORT_PEOPLE_ERROR,
  IMPORT_PEOPLE_END,

  EDIT_PEOPLE_START,
  EDIT_PEOPLE_ERROR,
  EDIT_PEOPLE_END,
  SHOW_EDIT_PEOPLE_MODAL,
  HIDE_EDIT_PEOPLE_MODAL,

  NEW_PEOPLE_END,

  DELETE_PEOPLE_START,
  DELETE_PEOPLE_ERROR,
  DELETE_PEOPLE_END,
  SHOW_DELETE_PEOPLE_MODAL,
  HIDE_DELETE_PEOPLE_MODAL,

  UPLOAD_AVATAR_END,

  LOAD_PERSON_TASKS_END,
  SET_PERSON_TASKS_SEARCH,
  NEW_TASK_COMMENT_END,

  RESET_ALL
} from '../mutation-types'

const helpers = {
  addAdditionalInformation (person) {
    if (person.first_name && person.last_name) {
      person.name = `${person.first_name} ${person.last_name}`
      person.initials = `${person.first_name[0]}${person.last_name[0]}`
    } else if (person.first_name) {
      person.name = person.first_name
      person.initials = person.first_name[0]
    } else if (person.last_name) {
      person.name = person.last_name
      person.initials = person.last_name[0]
    } else {
      person.name = person.email
      person.initials = person.email[0]
    }
    person.initials = person.initials.toUpperCase()
    person.color = colors.fromString(person.name)
    if (person.has_avatar) {
      const randomHash = Math.random().toString(36).substring(7)
      person.avatarPath =
        `/api/pictures/thumbnails/persons/${person.id}` +
        `.png?unique=${randomHash}`
    }
    return person
  },

  getTaskStatus (taskStatusId) {
    return taskStatusStore.state.taskStatusMap[taskStatusId]
  }
}

const state = {
  people: [],
  personMap: {},
  isPeopleLoading: false,
  isPeopleLoadingError: true,

  isImportPeopleModalShown: false,
  isImportPeopleLoading: false,
  isImportPeopleLoadingError: false,

  isEditModalShown: false,
  isEditLoading: false,
  isEditLoadingError: false,
  personToEdit: {},

  isDeleteModalShown: false,
  isDeleteLoading: false,
  isDeleteLoadingError: false,
  personToDelete: undefined,

  personCsvFormData: undefined,

  personTasks: [],
  displayedPersonTasks: [],
  personTasksIndex: {},
  personTasksSearchText: ''
}

const getters = {
  people: state => state.people,
  personMap: state => state.personMap,
  isPeopleLoading: state => state.isPeopleLoading,
  isPeopleLoadingError: state => state.isPeopleLoadingError,

  isImportPeopleModalShown: state => state.isImportPeopleModalShown,
  isImportPeopleLoading: state => state.isImportPeopleLoading,
  isImportPeopleLoadingError: state => state.isImportPeopleLoadingError,

  isDeleteModalShown: state => state.isDeleteModalShown,
  isDeleteLoading: state => state.isDeleteLoading,
  isDeleteLoadingError: state => state.isDeleteLoadingError,
  personToDelete: state => state.personToDelete,

  isEditModalShown: state => state.isEditModalShown,
  isEditLoading: state => state.isEditLoading,
  isEditLoadingError: state => state.isEditLoadingError,
  personToEdit: state => state.personToEdit,

  personCsvFormData: state => state.personCsvFormData,

  displayedPersonTasks: state => state.displayedPersonTasks,
  personTasksSearchText: state => state.personTasksSearchText,

  getPerson: (state, getters) => (id) => state.personMap[id],
  getPersonOptions: state => state.people.map(
    (person) => {
      return {
        label: `${person.first_name} ${person.last_name}`,
        value: person.id
      }
    }
  )
}

const sortPeople = (people) => {
  return people.sort((a, b) => {
    if (a.first_name !== a.last_name) {
      return a.first_name.localeCompare(b.first_name)
    } else {
      return a.last_name.localeCompare(b.last_name)
    }
  })
}

const actions = {

  loadPeople ({ commit, state }, callback) {
    commit(LOAD_PEOPLE_START)
    peopleApi.getPeople((err, people) => {
      if (err) {
        commit(LOAD_PEOPLE_ERROR)
      } else {
        commit(LOAD_PEOPLE_END, people.map((person) => {
          person.departments = person.departments || ''
          return person
        }))
      }
      if (callback) callback(err)
    })
  },

  newPeople ({ commit, state }, payload) {
    commit(EDIT_PEOPLE_START, payload.data)
    peopleApi.newPerson(state.personToEdit, (err, person) => {
      if (err) {
        commit(EDIT_PEOPLE_ERROR)
      } else {
        commit(NEW_PEOPLE_END, person.id)
        commit(EDIT_PEOPLE_END)
        commit(HIDE_EDIT_PEOPLE_MODAL)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  editPeople ({ commit, state }, payload) {
    commit(EDIT_PEOPLE_START, payload.data)
    peopleApi.updatePerson(state.personToEdit, (err, people) => {
      if (err) {
        commit(EDIT_PEOPLE_ERROR)
      } else {
        commit(EDIT_PEOPLE_END)
        commit(HIDE_EDIT_PEOPLE_MODAL)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  deletePeople ({ commit, state }, callback) {
    commit(DELETE_PEOPLE_START)
    peopleApi.deletePerson(state.personToDelete.id, (err, people) => {
      if (err) {
        commit(DELETE_PEOPLE_ERROR)
      } else {
        commit(DELETE_PEOPLE_END)
        commit(HIDE_DELETE_PEOPLE_MODAL)
      }
      if (callback) callback(err)
    })
  },

  uploadPersonFile ({ commit, state }, callback) {
    commit(IMPORT_PEOPLE_START)
    peopleApi.postCsv(state.personCsvFormData, (err) => {
      if (err) {
        commit(IMPORT_PEOPLE_ERROR)
      }
      commit(IMPORT_PEOPLE_END)
      if (callback) callback(err)
    })
  },

  loadPersonTasks ({ commit, state }, { personId, forced, callback }) {
    commit(LOAD_PERSON_TASKS_END, [])
    peopleApi.getPersonTasks(personId, (err, tasks) => {
      if (err) tasks = []
      commit(LOAD_PERSON_TASKS_END, tasks)
      if (callback) callback(err)
    })
  },

  showPersonImportModal ({ commit, state }, personId) {
    commit(SHOW_IMPORT_PEOPLE_MODAL)
  },

  hidePersonImportModal ({ commit, state }, personId) {
    commit(HIDE_IMPORT_PEOPLE_MODAL)
  },

  showPersonEditModal ({ commit, state }, personId) {
    commit(SHOW_EDIT_PEOPLE_MODAL, personId)
  },

  hidePersonEditModal ({ commit, state }, personId) {
    commit(HIDE_EDIT_PEOPLE_MODAL, personId)
  },

  showPersonDeleteModal ({ commit, state }, personId) {
    commit(SHOW_DELETE_PEOPLE_MODAL, personId)
  },

  hidePersonDeleteModal ({ commit, state }, personId) {
    commit(HIDE_DELETE_PEOPLE_MODAL, personId)
  },

  setPersonTasksSearch ({ commit, state }, searchText) {
    commit(SET_PERSON_TASKS_SEARCH, searchText)
  }
}

const mutations = {
  [LOAD_PEOPLE_START] (state) {
    state.isPeopleLoading = true
    state.isPeopleLoadingError = false
    state.personMap = {}
  },

  [LOAD_PEOPLE_ERROR] (state) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = true
  },

  [LOAD_PEOPLE_END] (state, people) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = false
    state.people = sortPeople(people)
    state.people.forEach((person) => {
      person = helpers.addAdditionalInformation(person)
      state.personMap[person.id] = person
    })
  },

  [DELETE_PEOPLE_START] (state) {
    state.isDeleteLoading = true
    state.isDeleteLoadingError = false
  },

  [DELETE_PEOPLE_END] (state) {
    state.isDeleteLoading = false
    const personToDeleteIndex = state.people.findIndex(
      (person) => person.id === state.personToDelete.id
    )
    state.people.splice(personToDeleteIndex, 1)
    delete state.personMap[state.personToDelete.id]
    state.personToDelete = undefined
  },

  [DELETE_PEOPLE_ERROR] (state) {
    state.isDeleteLoading = false
    state.isDeleteLoadingError = true
  },

  [SHOW_DELETE_PEOPLE_MODAL] (state, personId) {
    state.isDeleteModalShown = true
    state.isDeleteLoadingError = false
    state.isDeleteLoading = false
    state.personToDelete = state.people.find(
      (person) => person.id === personId
    )
  },

  [HIDE_DELETE_PEOPLE_MODAL] (state, personId) {
    state.isDeleteModalShown = false
    state.personToDelete = undefined
  },

  [EDIT_PEOPLE_START] (state, data) {
    state.isEditLoading = true
    state.isEditLoadingError = false
    state.personToEdit = Object.assign(state.personToEdit, data)
  },

  [NEW_PEOPLE_END] (state, personId) {
    state.personToEdit.id = personId
  },

  [EDIT_PEOPLE_END] (state) {
    state.isEditLoading = false
    state.isEditLoadingError = false
    const personToEditIndex = state.people.findIndex(
      (person) => person.id === state.personToEdit.id
    )
    state.personToEdit = helpers.addAdditionalInformation(state.personToEdit)
    if (personToEditIndex >= 0) {
      state.people[personToEditIndex] = state.personToEdit
    } else {
      state.people.push(state.personToEdit)
      state.personMap[state.personToEdit.id] = state.personToEdit
      sortPeople(state.people)
    }
    state.personToEdit = {}
  },

  [EDIT_PEOPLE_ERROR] (state) {
    state.isEditLoading = false
    state.isEditLoadingError = true
  },

  [IMPORT_PEOPLE_START] (state, data) {
    state.isImportPeopleLoading = true
    state.isImportPeopleLoadingError = false
  },

  [IMPORT_PEOPLE_END] (state, personId) {
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
  },

  [IMPORT_PEOPLE_ERROR] (state) {
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = true
  },

  [SHOW_EDIT_PEOPLE_MODAL] (state, personId) {
    state.isEditModalShown = true
    state.isEditLoadingError = false
    state.isEditLoading = false
    if (personId !== undefined) {
      state.personToEdit = state.people.find(
        (person) => person.id === personId
      )
    } else {
      state.personToEdit = {
        first_name: '',
        last_name: '',
        phone: '',
        email: ''
      }
    }
  },

  [HIDE_EDIT_PEOPLE_MODAL] (state) {
    state.isEditModalShown = false
    state.personToEdit = {}
  },

  [SHOW_IMPORT_PEOPLE_MODAL] (state) {
    state.isImportPeopleModalShown = true
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
  },

  [HIDE_IMPORT_PEOPLE_MODAL] (state) {
    state.isImportPeopleModalShown = false
  },

  [PERSON_CSV_FILE_SELECTED] (state, formData) {
    state.personCsvFormData = formData
  },

  [UPLOAD_AVATAR_END] (state, personId) {
    const randomHash = Math.random().toString(36).substring(7)
    const person = state.personMap[personId]
    person.has_avatar = true
    person.avatarPath =
      `/api/pictures/thumbnails/persons/${person.id}` +
      `.png?unique=${randomHash}`
  },

  [LOAD_PERSON_TASKS_END] (state, tasks) {
    tasks.forEach(populateTask)
    state.personTasks = sortTasks(tasks)

    state.personTasksIndex = buildTaskIndex(tasks)
    const searchResult = indexSearch(
      state.personTasksIndex,
      state.personTasksSearchText
    )
    state.displayedPersonTasks = searchResult || state.personTasks
  },

  [SET_PERSON_TASKS_SEARCH] (state, searchText) {
    const searchResult = indexSearch(state.personTasksIndex, searchText)
    state.personTasksSearchText = searchText
    state.displayedPersonTasks = searchResult || state.personTasks
  },

  [NEW_TASK_COMMENT_END] (state, {comment, taskId}) {
    const task = state.personTasks.find((task) => task.id === taskId)

    if (task) {
      const taskStatus = helpers.getTaskStatus(comment.task_status_id)

      Object.assign(task, {
        task_status_id: taskStatus.id,
        task_status_name: taskStatus.name,
        task_status_short_name: taskStatus.short_name,
        task_status_color: taskStatus.color
      })
    }
  },

  [RESET_ALL] (state, people) {
    state.isPeopleLoading = false
    state.isPeopleLoadingError = false

    state.isDeleteModalShown = false
    state.isDeleteLoading = false
    state.isDeleteLoadingError = false
    state.personToDelete = {}

    state.isEditModalShown = false
    state.isEditLoading = false
    state.isEditLoadingError = false
    state.personToEdit = {}

    state.isImportModalShown = false
    state.isImportPeopleLoading = false
    state.isImportPeopleLoadingError = false
    state.personCsvFormData = null

    state.people = []
    state.personMap = {}
  }
}

export default {
  state,
  getters,
  actions,
  mutations,
  helpers
}
<|endoftext|>


<commit_before>import Class from 'lowclass'
import Transformable from './Transformable'
import {isInstanceof} from './Utility'

const Motor = Class('Motor', ({ Public, Private }) => ({

    constructor() {
        Private(this).allRenderTasks = []
        Private(this).nodesToBeRendered = []
        Private(this).modifiedScenes = []
        Private(this).treesToUpdate = []
    },

    /**
     * When a render tasks is added a new requestAnimationFrame loop will be started if there
     * isn't one currently.
     *
     * A render task is simply a function that will be called over and over
     * again, in the Motor's animation loop. That's all, nothing special.
     * However, if a Node setter is used inside of a render task, then the Node
     * will tell Motor that it needs to be re-rendered, which will happen at
     * the end of the current frame. If a Node setter is used outside of a
     * render task (i.e. outside of the Motor's animation loop), then the Node
     * tells Motor to re-render the Node on the next animation loop tick.
     * Basically, regardless of where the Node's setters are used (inside or
     * outside of the Motor's animation loop), rendering always happens inside
     * the loop.
     *
     * @param {Function} fn The render task to add.
     * @return {Function} A reference to the render task. Useful for saving to
     * a variable so that it can later be passed to Motor.removeRenderTask().
     */
    addRenderTask(fn) {
        if (typeof fn != 'function')
            throw new Error('Render task must be a function.')

        const self = Private(this)

        if (self.allRenderTasks.includes(fn)) return

        self.allRenderTasks.push(fn)
        self.numberOfTasks += 1

        // If the render loop isn't started, start it.
        if (!self.animationLoopStarted)
            self.startAnimationLoop()

        return fn
    },

    removeRenderTask(fn) {
        const self = Private(this)

        const taskIndex = self.allRenderTasks.indexOf(fn)

        if (taskIndex == -1) return

        self.allRenderTasks.splice(taskIndex, 1)
        self.numberOfTasks -= 1

        if ( taskIndex <= self.taskIterationIndex )
            self.taskIterationIndex -= 1
    },

    once(fn) {
        this.addRenderTask(time => (fn(time), false))
    },

    // A Node calls this any time its properties have been modified (f.e. by the end user).
    setNodeToBeRendered(node) {
        const self = Private(this)
        if (self.nodesToBeRendered.includes(node)) return
        self.nodesToBeRendered.push(node)

        // noop if the loop's already started
        self.startAnimationLoop()
    },

    setFrameRequester( requester ) {
        Private( this ).requestFrame = requester
    },

    private: {

        animationLoopStarted: false,
        taskIterationIndex: null,
        numberOfTasks: 0,

        allRenderTasks: [],
        nodesToBeRendered: [],
        modifiedScenes: [],

        // A set of nodes that are the root nodes of subtrees where all nodes
        // in each subtree need to have their world matrices updated.
        treesToUpdate: [],

        // default to requestAnimationFrame for regular non-VR/AR scenes.
        requestFrame: window.requestAnimationFrame.bind( window ),

        /**
         * Starts a requestAnimationFrame loop and runs the render tasks in the allRenderTasks stack.
         * As long as there are tasks in the stack, the loop continues. When the
         * stack becomes empty due to removal of tasks, the
         * requestAnimationFrame loop stops and the app sits there doing nothing
         * -- silence, crickets.
         */
        async startAnimationLoop() {
            if (this.animationLoopStarted) return

            this.animationLoopStarted = true

            let timestamp = null

            while (this.animationLoopStarted) {
                timestamp = await this.animationFrame()

                this.runRenderTasks(timestamp)

                // wait for the next microtask before continuing so that SkateJS
                // updated methods (or any other microtask handlers) have a
                // chance to handle changes before the next renderNodes call.
                //
                // TODO add test to make sure behavior size change doesn't
                // happen after render
                await Promise.resolve()

                this.renderNodes(timestamp)

                // If no tasks are left, stop the animation loop.
                if (!this.allRenderTasks.length)
                    this.animationLoopStarted = false
            }
        },

        animationFrame() {
            return new Promise(r => this.requestFrame(r))
        },

        runRenderTasks(timestamp) {
            for (this.taskIterationIndex = 0; this.taskIterationIndex < this.numberOfTasks; this.taskIterationIndex += 1) {
                const task = this.allRenderTasks[this.taskIterationIndex]

                if (task(timestamp) === false)
                    Public(this).removeRenderTask(task)
            }
        },

        renderNodes(timestamp) {
            if (!this.nodesToBeRendered.length) return

            for (let i=0, l=this.nodesToBeRendered.length; i<l; i+=1) {
                const node = this.nodesToBeRendered[i]

                node._render(timestamp)

                // if there is no ancestor of the current node that should be
                // rendered, then the current node is a root node of a subtree
                // that needs to be updated
                if (
                    !node._getNearestAncestorThatShouldBeRendered() &&
                    !this.treesToUpdate.includes(node)
                ) {
                    this.treesToUpdate.push(node)
                }

                // keep track of which scenes are modified so we can render webgl
                // only for those scenes.
                if (!this.modifiedScenes.includes(node.scene))
                    this.modifiedScenes.push(node.scene)
            }

            // Update world matrices of the subtrees.
            const treesToUpdate = this.treesToUpdate
            for (let i=0, l=treesToUpdate.length; i<l; i+=1) {
                treesToUpdate[i]._calculateWorldMatricesInSubtree()
            }
            treesToUpdate.length = 0

            // render webgl of modified scenes.
            const modifiedScenes = this.modifiedScenes
            for (let i=0, l=modifiedScenes.length; i<l; i+=1) {
                modifiedScenes[i].drawScene()
            }
            modifiedScenes.length = 0

            const nodesToBeRendered = this.nodesToBeRendered
            for (let i=0, l=nodesToBeRendered.length; i<l; i+=1) {
                nodesToBeRendered[i]._willBeRendered = false
            }
            nodesToBeRendered.length = 0
        },
    },

}))

// export a singleton instance rather than the class directly.
export default new Motor
<commit_msg>remove unused imports in Motor.js<commit_after>import Class from 'lowclass'

const Motor = Class('Motor', ({ Public, Private }) => ({

    constructor() {
        Private(this).allRenderTasks = []
        Private(this).nodesToBeRendered = []
        Private(this).modifiedScenes = []
        Private(this).treesToUpdate = []
    },

    /**
     * When a render tasks is added a new requestAnimationFrame loop will be started if there
     * isn't one currently.
     *
     * A render task is simply a function that will be called over and over
     * again, in the Motor's animation loop. That's all, nothing special.
     * However, if a Node setter is used inside of a render task, then the Node
     * will tell Motor that it needs to be re-rendered, which will happen at
     * the end of the current frame. If a Node setter is used outside of a
     * render task (i.e. outside of the Motor's animation loop), then the Node
     * tells Motor to re-render the Node on the next animation loop tick.
     * Basically, regardless of where the Node's setters are used (inside or
     * outside of the Motor's animation loop), rendering always happens inside
     * the loop.
     *
     * @param {Function} fn The render task to add.
     * @return {Function} A reference to the render task. Useful for saving to
     * a variable so that it can later be passed to Motor.removeRenderTask().
     */
    addRenderTask(fn) {
        if (typeof fn != 'function')
            throw new Error('Render task must be a function.')

        const self = Private(this)

        if (self.allRenderTasks.includes(fn)) return

        self.allRenderTasks.push(fn)
        self.numberOfTasks += 1

        // If the render loop isn't started, start it.
        if (!self.animationLoopStarted)
            self.startAnimationLoop()

        return fn
    },

    removeRenderTask(fn) {
        const self = Private(this)

        const taskIndex = self.allRenderTasks.indexOf(fn)

        if (taskIndex == -1) return

        self.allRenderTasks.splice(taskIndex, 1)
        self.numberOfTasks -= 1

        if ( taskIndex <= self.taskIterationIndex )
            self.taskIterationIndex -= 1
    },

    once(fn) {
        this.addRenderTask(time => (fn(time), false))
    },

    // A Node calls this any time its properties have been modified (f.e. by the end user).
    setNodeToBeRendered(node) {
        const self = Private(this)
        if (self.nodesToBeRendered.includes(node)) return
        self.nodesToBeRendered.push(node)

        // noop if the loop's already started
        self.startAnimationLoop()
    },

    setFrameRequester( requester ) {
        Private( this ).requestFrame = requester
    },

    private: {

        animationLoopStarted: false,
        taskIterationIndex: null,
        numberOfTasks: 0,

        allRenderTasks: [],
        nodesToBeRendered: [],
        modifiedScenes: [],

        // A set of nodes that are the root nodes of subtrees where all nodes
        // in each subtree need to have their world matrices updated.
        treesToUpdate: [],

        // default to requestAnimationFrame for regular non-VR/AR scenes.
        requestFrame: window.requestAnimationFrame.bind( window ),

        /**
         * Starts a requestAnimationFrame loop and runs the render tasks in the allRenderTasks stack.
         * As long as there are tasks in the stack, the loop continues. When the
         * stack becomes empty due to removal of tasks, the
         * requestAnimationFrame loop stops and the app sits there doing nothing
         * -- silence, crickets.
         */
        async startAnimationLoop() {
            if (this.animationLoopStarted) return

            this.animationLoopStarted = true

            let timestamp = null

            while (this.animationLoopStarted) {
                timestamp = await this.animationFrame()

                this.runRenderTasks(timestamp)

                // wait for the next microtask before continuing so that SkateJS
                // updated methods (or any other microtask handlers) have a
                // chance to handle changes before the next renderNodes call.
                //
                // TODO add test to make sure behavior size change doesn't
                // happen after render
                await Promise.resolve()

                this.renderNodes(timestamp)

                // If no tasks are left, stop the animation loop.
                if (!this.allRenderTasks.length)
                    this.animationLoopStarted = false
            }
        },

        animationFrame() {
            return new Promise(r => this.requestFrame(r))
        },

        runRenderTasks(timestamp) {
            for (this.taskIterationIndex = 0; this.taskIterationIndex < this.numberOfTasks; this.taskIterationIndex += 1) {
                const task = this.allRenderTasks[this.taskIterationIndex]

                if (task(timestamp) === false)
                    Public(this).removeRenderTask(task)
            }
        },

        renderNodes(timestamp) {
            if (!this.nodesToBeRendered.length) return

            for (let i=0, l=this.nodesToBeRendered.length; i<l; i+=1) {
                const node = this.nodesToBeRendered[i]

                node._render(timestamp)

                // if there is no ancestor of the current node that should be
                // rendered, then the current node is a root node of a subtree
                // that needs to be updated
                if (
                    !node._getNearestAncestorThatShouldBeRendered() &&
                    !this.treesToUpdate.includes(node)
                ) {
                    this.treesToUpdate.push(node)
                }

                // keep track of which scenes are modified so we can render webgl
                // only for those scenes.
                if (!this.modifiedScenes.includes(node.scene))
                    this.modifiedScenes.push(node.scene)
            }

            // Update world matrices of the subtrees.
            const treesToUpdate = this.treesToUpdate
            for (let i=0, l=treesToUpdate.length; i<l; i+=1) {
                treesToUpdate[i]._calculateWorldMatricesInSubtree()
            }
            treesToUpdate.length = 0

            // render webgl of modified scenes.
            const modifiedScenes = this.modifiedScenes
            for (let i=0, l=modifiedScenes.length; i<l; i+=1) {
                modifiedScenes[i].drawScene()
            }
            modifiedScenes.length = 0

            const nodesToBeRendered = this.nodesToBeRendered
            for (let i=0, l=nodesToBeRendered.length; i<l; i+=1) {
                nodesToBeRendered[i]._willBeRendered = false
            }
            nodesToBeRendered.length = 0
        },
    },

}))

// export a singleton instance rather than the class directly.
export default new Motor
<|endoftext|>