<commit_before>'use strict';

var unserializeValue = require('dbjs/_setup/unserialize/value')
  , serializeValue   = require('dbjs/_setup/serialize/value')
  , normalizeOptions = require('es5-ext/object/normalize-options')
  , getDbSet         = require('eregistrations/server/utils/get-db-set')
  , deferred         = require('deferred')
  , aFrom            = require('es5-ext/array/from')
  , debug            = require('debug-ext')('auto-assign');

module.exports = function (businessProcessStorage, counterStorage, officials, step/*, options*/) {
	var options = normalizeOptions(arguments[4])
	  , id      = step.shortPath
	  , path    = step.__id__.slice(step.master.__id__.length + 1)
	  , customFilterPath = options.customFilterPath
	  , customFilterValue = options.customFilterValue
	  , officialsArray, lastIndex;

	officialsArray = officials.toArray();

	var addAssignee = function (businessProcessId) {
		var recordId = businessProcessId + '/' + path + '/assignee', customFilterCheck;
		if (customFilterPath) {
			var key = businessProcessId + '/' + customFilterPath;
			customFilterCheck = businessProcessStorage.getComputed(key)(function (data) {
				if (!data || !data.value || (data.value.slice(1) !== customFilterValue)) return;
				return true;
			});
		} else {
			customFilterCheck = deferred(true);
		}

		return businessProcessStorage.get(recordId)(function (data) {
			if (data && data.value[0] === '7') return;

			return customFilterCheck.then(function (isOK) {
				var officialId;
				if (!isOK) return;

				lastIndex = officialsArray[lastIndex + 1] ? lastIndex + 1 : 0;
				officialId = officialsArray[lastIndex];
				debug('for %s assigned official: %s, to process: %s', id, officialId, businessProcessId);
				return deferred(
					businessProcessStorage.store(businessProcessId + '/' + path + '/assignee',
							'7' + officialId),
					counterStorage.store('processingStepAutoAssignLastIndex/' + id +
						(customFilterValue ? '/' + customFilterValue : ''), serializeValue(lastIndex))
				)(function () {
					if (options.onAssign) return options.onAssign(officialId);
				});
			});
		});
	};

	return counterStorage.get('processingStepAutoAssignLastIndex/' + id +
		(customFilterValue ? '/' + customFilterValue : ''))(function (data) {
		return (data && unserializeValue(data.value)) || 0;
	}).then(function (index) {
		lastIndex = index;
		return getDbSet(
			businessProcessStorage,
			'computed',
			path + '/status',
			serializeValue('pending')
		)(function (businessProcesses) {
			var listener = function (ev) {
				if (ev.type === 'delete') return;
				if (ev.type === 'add') {
					addAssignee(ev.value).done();
					return;
				}
				if (ev.type === 'batch') {
					if (!ev.added) return;
					deferred.map(aFrom(ev.added), addAssignee).done();
					return;
				}

				throw new Error("Unsupported event: " + ev.type);
			};
			var activate = function () {
				businessProcesses.on('change', listener);
				return deferred.map(aFrom(businessProcesses), addAssignee);
			};
			var deactivate = function () {
				businessProcesses.off('change', listener);
			};
			if (officials.size) return activate();
			officials._size.on('change', function (event) {
				if (event.newValue) {
					if (!event.oldValue) activate().done();
				} else if (event.oldValue) {
					deactivate();
				}
			});
		});
	});
};
<commit_msg>#1387 better options<commit_after>'use strict';

var unserializeValue = require('dbjs/_setup/unserialize/value')
  , serializeValue   = require('dbjs/_setup/serialize/value')
  , normalizeOptions = require('es5-ext/object/normalize-options')
  , getDbSet         = require('eregistrations/server/utils/get-db-set')
  , deferred         = require('deferred')
  , aFrom            = require('es5-ext/array/from')
  , debug            = require('debug-ext')('auto-assign');

module.exports = function (businessProcessStorage, counterStorage, officials, step/*, options*/) {
	var options = normalizeOptions(arguments[4])
	  , id      = step.shortPath
	  , path    = step.__id__.slice(step.master.__id__.length + 1)
	  , customFilter    = options.customFilter && deferred(options.customFilter)
	  , customIndexPath = options.customIndexPath
	  , officialsArray, lastIndex;

	officialsArray = officials.toArray();

	var addAssignee = function (businessProcessId) {
		var recordId = businessProcessId + '/' + path + '/assignee';

		if (!customFilter) customFilter = deferred(true);

		return businessProcessStorage.get(recordId)(function (data) {
			if (data && data.value[0] === '7') return;

			return customFilter.then(function (isOK) {
				var officialId;
				if (!isOK) return;

				lastIndex = officialsArray[lastIndex + 1] ? lastIndex + 1 : 0;
				officialId = officialsArray[lastIndex];
				debug('for %s assigned official: %s, to process: %s', id, officialId, businessProcessId);
				return deferred(
					businessProcessStorage.store(businessProcessId + '/' + path + '/assignee',
							'7' + officialId),
					counterStorage.store('processingStepAutoAssignLastIndex/' +
						(customIndexPath || id), serializeValue(lastIndex))
				)(function () {
					if (options.onAssign) return options.onAssign(officialId);
				});
			});
		});
	};

	return counterStorage.get('processingStepAutoAssignLastIndex/' + (customIndexPath || id)
		)(function (data) {
		return (data && unserializeValue(data.value)) || 0;
	}).then(function (index) {
		lastIndex = index;
		return getDbSet(
			businessProcessStorage,
			'computed',
			path + '/status',
			serializeValue('pending')
		)(function (businessProcesses) {
			var listener = function (ev) {
				if (ev.type === 'delete') return;
				if (ev.type === 'add') {
					addAssignee(ev.value).done();
					return;
				}
				if (ev.type === 'batch') {
					if (!ev.added) return;
					deferred.map(aFrom(ev.added), addAssignee).done();
					return;
				}

				throw new Error("Unsupported event: " + ev.type);
			};
			var activate = function () {
				businessProcesses.on('change', listener);
				return deferred.map(aFrom(businessProcesses), addAssignee);
			};
			var deactivate = function () {
				businessProcesses.off('change', listener);
			};
			if (officials.size) return activate();
			officials._size.on('change', function (event) {
				if (event.newValue) {
					if (!event.oldValue) activate().done();
				} else if (event.oldValue) {
					deactivate();
				}
			});
		});
	});
};
<|endoftext|>


<commit_before>/* globals sinon */
import Ember from 'ember';
import { AutosaveProxy } from 'ember-autosave';
import { module, test } from 'qunit';
var run = Ember.run;

var model;
var autoSaveObject;
var clock;

// TODO: Test experience of having data pushed while typing in field
// TODO: Evaluate using isDirty property to trigger saving

module('AutosaveProxy', {
  beforeEach: function() {
    model = Ember.Object.create({ save: sinon.spy() });
    autoSaveObject = AutosaveProxy.create({ content: model });
    clock = sinon.useFakeTimers();
  },

  afterEach: function() {
    clock.restore();
  }
});

test('setting a property eventually saves the model with the property', function(assert) {
  autoSaveObject.set('name', 'Millie');
  assert.equal(model.get('name'), 'Millie', "the model's property was set");
  assert.ok(!model.save.called, 'save was not called immediately');

  clock.tick(1000);
  assert.ok(model.save.called, 'save was called after ellapsed time');
});

test('properties on the model can be retrieved via the proxy', function(assert) {
  model.set('name', 'Tina');
  assert.equal(autoSaveObject.get('name'), 'Tina', "returned model's name attribute");
});

test('a pending save is flushed before the object is destroyed', function(assert) {
  autoSaveObject.set('name', 'Millie');

  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.ok(model.save.called, 'save was called before the object was destroyed');
});

test('destroying an object with no pending save is ok', function(assert) {
  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.ok(!model.save.called, 'save was not called');
});

test('destroying an object after a save was flushed is ok', function(assert) {
  autoSaveObject.set('name', 'Millie');
  clock.tick(1000);

  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.equal(model.save.callCount, 1, 'save was only called once');
});

test('changing the content flushes a pending save', function(assert) {
  autoSaveObject.set('name', 'Millie');
  autoSaveObject.set('content', {});
  assert.ok(model.save.called, 'save was called before the content changed');
});
<commit_msg>Remove old todos<commit_after>/* globals sinon */
import Ember from 'ember';
import { AutosaveProxy } from 'ember-autosave';
import { module, test } from 'qunit';
var run = Ember.run;

var model;
var autoSaveObject;
var clock;

module('AutosaveProxy', {
  beforeEach: function() {
    model = Ember.Object.create({ save: sinon.spy() });
    autoSaveObject = AutosaveProxy.create({ content: model });
    clock = sinon.useFakeTimers();
  },

  afterEach: function() {
    clock.restore();
  }
});

test('setting a property eventually saves the model with the property', function(assert) {
  autoSaveObject.set('name', 'Millie');
  assert.equal(model.get('name'), 'Millie', "the model's property was set");
  assert.ok(!model.save.called, 'save was not called immediately');

  clock.tick(1000);
  assert.ok(model.save.called, 'save was called after ellapsed time');
});

test('properties on the model can be retrieved via the proxy', function(assert) {
  model.set('name', 'Tina');
  assert.equal(autoSaveObject.get('name'), 'Tina', "returned model's name attribute");
});

test('a pending save is flushed before the object is destroyed', function(assert) {
  autoSaveObject.set('name', 'Millie');

  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.ok(model.save.called, 'save was called before the object was destroyed');
});

test('destroying an object with no pending save is ok', function(assert) {
  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.ok(!model.save.called, 'save was not called');
});

test('destroying an object after a save was flushed is ok', function(assert) {
  autoSaveObject.set('name', 'Millie');
  clock.tick(1000);

  Ember.run(function() {
    autoSaveObject.destroy();
  });

  assert.equal(model.save.callCount, 1, 'save was only called once');
});

test('changing the content flushes a pending save', function(assert) {
  autoSaveObject.set('name', 'Millie');
  autoSaveObject.set('content', {});
  assert.ok(model.save.called, 'save was called before the content changed');
});
<|endoftext|>


<commit_before>describe('Toggle Switch', function() {
  var $scope, $compile;

  var baseTemplate = '<toggle-switch ng-model="switchState">\n</toggle-switch>';
  var onLabelTemplate = '<toggle-switch ng-model="switchState" on-label="CUSTOM-ON">\n</toggle-switch>';
  var offLabelTemplate = '<toggle-switch ng-model="switchState" off-label="CUSTOM-OFF">\n</toggle-switch>';
  var knobLabelTemplate = '<toggle-switch ng-model="switchState" knob-label="CUSTOM">\n</toggle-switch>';
  var htmlLabelsTemplate = '<toggle-switch ng-model="switchState" on-label="" off-label="">\n</toggle-switch>';
  var disabledTemplate = '<toggle-switch ng-model="switchState" is-disabled="isDisabled">\n</toggle-switch>';

  // Load up just our module
  beforeEach(module('toggle-switch'));

  beforeEach(inject(function($rootScope, _$compile_) {
    // Get an isolated scope
    $scope = $rootScope.$new();
    $compile = _$compile_;
  }));

  function compileDirective(template, scope) {
    // Compile Directive
    var elm = angular.element(template);
    $compile(elm)(scope);
    scope.$apply();
    return elm;
  }

  describe('default labels', function() {
    var elm;

    beforeEach(function() {
      elm = compileDirective(baseTemplate, $scope);
    });

    it('onLabel', function() {
      expect(elm.text()).toContain('On');
    });

    it('offLabel', function() {
      expect(elm.text()).toContain('Off');
    });
  });

  describe('when state is null', function() {
    it('changes model to true when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });
  });

  describe('when state is true', function() {
    // Change state to true
    beforeEach(function() {
      $scope.$apply(function() {
        $scope.switchState = true;
      });
    });

    it('changes model to false when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(false);
    });
  });

  describe('when state is false', function() {
    // Change state to true
    beforeEach(function() {
      $scope.$apply(function() {
        $scope.switchState = false;
      });
    });

    it('changes model to true when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });
  });

  describe('when there is a custom `on-label`', function () {
    // @TODO: figure out how to deal with html in Angular 1.2
    //describe('is html', function() {
    //  it('sets the on label', function() {
    //    var elm = compileDirective(htmlLabelsTemplate, $scope);
    //    expect(elm.html()).toContain('');
    //  });
    //});

    describe('is string', function() {
      it('sets the on label', function() {
        var elm = compileDirective(onLabelTemplate, $scope);
        expect(elm.text()).toContain('CUSTOM-ON');
      });
    });
  });

  describe('when there is a custom `off-label`', function () {
    // @TODO: figure out how to deal with html in Angular 1.2
    //describe('is html', function() {
    //  it('sets the on label', function() {
    //    var elm = compileDirective(htmlLabelsTemplate, $scope);
    //    expect(elm.html()).toContain('');
    //  });
    //});

    describe('is string', function() {
      it('sets the on label', function() {
        var elm = compileDirective(offLabelTemplate, $scope);
        expect(elm.text()).toContain('CUSTOM-OFF');
      });
    });
  });

  describe('when there is a custom `knob-label`', function () {
    it('sets the on label', function() {
      var elm = compileDirective(knobLabelTemplate, $scope);
      expect(elm.text()).toContain('CUSTOM');
    });
  });

  describe('when toggle is disabled', function() {
    it('ngModel does not change on click', function() {
      $scope.switchState = true;
      $scope.isDisabled = true;
      var elm = compileDirective(disabledTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });
  });

});
<commit_msg>tests for ng-change<commit_after>describe('Toggle Switch', function() {
  var $scope, $compile;

  var baseTemplate = '<toggle-switch ng-model="switchState">\n</toggle-switch>';
  var onLabelTemplate = '<toggle-switch ng-model="switchState" on-label="CUSTOM-ON">\n</toggle-switch>';
  var offLabelTemplate = '<toggle-switch ng-model="switchState" off-label="CUSTOM-OFF">\n</toggle-switch>';
  var knobLabelTemplate = '<toggle-switch ng-model="switchState" knob-label="CUSTOM">\n</toggle-switch>';
  var htmlLabelsTemplate = '<toggle-switch ng-model="switchState" on-label="" off-label="">\n</toggle-switch>';
  var disabledTemplate = '<toggle-switch ng-model="switchState" is-disabled="isDisabled">\n</toggle-switch>';
  var changeTemplate = '<toggle-switch ng-model="switchState" ng-change="changedState()">\n</toggle-switch>';

  // Load up just our module
  beforeEach(module('toggle-switch'));

  beforeEach(inject(function($rootScope, _$compile_) {
    // Get an isolated scope
    $scope = $rootScope.$new();
    $compile = _$compile_;
  }));

  function compileDirective(template, scope) {
    // Compile Directive
    var elm = angular.element(template);
    $compile(elm)(scope);
    scope.$apply();
    return elm;
  }

  describe('default labels', function() {
    var elm;

    beforeEach(function() {
      elm = compileDirective(baseTemplate, $scope);
    });

    it('onLabel', function() {
      expect(elm.text()).toContain('On');
    });

    it('offLabel', function() {
      expect(elm.text()).toContain('Off');
    });
  });

  describe('when state is null', function() {
    it('changes model to true when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });
  });

  describe('when state is true', function() {
    // Change state to true
    beforeEach(function() {
      $scope.$apply(function() {
        $scope.switchState = true;
      });
    });
    $scope.changedState = function(){};
    spyOn($scope, 'changedState');

    it('changes model to false when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(false);
    });

    it('change handler is called when clicked', function() {
      var elm = compileDirective(changeTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.changedState).toHaveBeenCalled();
    });
  });

  describe('when state is false', function() {
    // Change state to true
    beforeEach(function() {
      $scope.$apply(function() {
        $scope.switchState = false;
      });
      $scope.changedState = function(){};
      spyOn($scope, 'changedState');
    });

    it('changes model to true when clicked', function() {
      var elm = compileDirective(baseTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });

    it('change handler is called when clicked', function() {
      var elm = compileDirective(changeTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.changedState).toHaveBeenCalled();
    });
  });

  describe('when there is a custom `on-label`', function () {
    // @TODO: figure out how to deal with html in Angular 1.2
    //describe('is html', function() {
    //  it('sets the on label', function() {
    //    var elm = compileDirective(htmlLabelsTemplate, $scope);
    //    expect(elm.html()).toContain('');
    //  });
    //});

    describe('is string', function() {
      it('sets the on label', function() {
        var elm = compileDirective(onLabelTemplate, $scope);
        expect(elm.text()).toContain('CUSTOM-ON');
      });
    });
  });

  describe('when there is a custom `off-label`', function () {
    // @TODO: figure out how to deal with html in Angular 1.2
    //describe('is html', function() {
    //  it('sets the on label', function() {
    //    var elm = compileDirective(htmlLabelsTemplate, $scope);
    //    expect(elm.html()).toContain('');
    //  });
    //});

    describe('is string', function() {
      it('sets the on label', function() {
        var elm = compileDirective(offLabelTemplate, $scope);
        expect(elm.text()).toContain('CUSTOM-OFF');
      });
    });
  });

  describe('when there is a custom `knob-label`', function () {
    it('sets the on label', function() {
      var elm = compileDirective(knobLabelTemplate, $scope);
      expect(elm.text()).toContain('CUSTOM');
    });
  });

  describe('when toggle is disabled', function() {
    it('ngModel does not change on click', function() {
      $scope.switchState = true;
      $scope.isDisabled = true;
      var elm = compileDirective(disabledTemplate, $scope);
      elm.triggerHandler('click');
      expect($scope.switchState).toEqual(true);
    });
  });

});
<|endoftext|>


<commit_before>var request = require('request')
  , async = require('async')
  , mkdirp = require('mkdirp')
  , fs = require('fs')
  , lockerUrl
  , auth
  ;

exports.sync = function(processInfo, cb) {
    auth = processInfo.auth;
    auth.headers = {"Authorization":"token "+auth.accessToken, "Connection":"keep-alive"};
    var cached = {};
    if (processInfo.config && processInfo.config.cached) cached = processInfo.config.cached;
    lockerUrl = processInfo.lockerUrl;
    exports.syncRepos(cached, function(err, repos) {
        if (err) console.error(err);
        var responseObj = {data : {}, config: { cached: cached }};
        responseObj.data.repo = repos;
        cb(err, responseObj);
    });
};

exports.syncRepos = function(cached, callback) {
    request.get({url:"https://api.github.com/user/repos", json:true, headers:auth.headers}, function(err, resp, repos) {
        if(err || !repos || !repos.length) return callback(err, []);
        // process each one to get richer data
        async.forEachSeries(repos, function(repo, cb) {
            repo.idorig = repo.id;
            repo.id = getIDFromUrl(repo.url); // LEGACY UGH, we're overwriting the numeric id, not simple to change now
            // nothing changed
            var ckey = repo.pushed_at + repo.watchers;
            if(cached[repo.id] == ckey) return cb(); // if it hasn't changed!
            cached[repo.id] = ckey;
            // get the watchers, is nice
            getWatchers(repo, function(err, resp, watchers) {
                repo.watchers = watchers;
                // now see if it's an app special on isle 9
                getPackage(repo, cb, function(pkg) {
                    // ok, we've got an app, make sure settings are valid
                    getTree(repo, function(err, resp, tree) {
                        if(err || !tree || !tree.tree) return cb();
                        syncRepo(repo, tree.tree, function(err) {
                            if(err) {
                                delete cached[repo.id]; // invalidate the cache, it never sync'd
                                console.error(err);
                                return cb();
                            }
                            // make sure package.json is safe, set some defaults!
                            pkg.repository.handle = repo.id.replace("/", "-").toLowerCase();
                            pkg.name = pkg.repository.handle;
                            pkg.repository.url = 'https://github.com/' + repo.id;
                            if(!pkg.author && auth.profile.name) {
                                pkg.author = {name:auth.profile.name};
                                if(auth.profile.email) pkg.author.email = auth.profile.email;
                            }
                            if(!pkg.version) pkg.version = "0.0.0";
                            if(!pkg.repository.title) pkg.repository.title = repo.name;
                            // XXX: hmmm, these next two if statements should never be true since we only sync the repo
                            // if static == true and type == "app" per line 46 (smurthas)
                            if(!pkg.repository.hasOwnProperty('static')) pkg.repository.static = true;
                            if(!pkg.repository.hasOwnProperty('type')) pkg.repository.type = "app";
                            if(!pkg.repository.hasOwnProperty('update')) pkg.repository.update = "auto";
                            fs.writeFileSync(repo.id+"/package.json", JSON.stringify(pkg)); // overwriting :/
                            request.post({url:lockerUrl+'/map/upsert?manifest=Me/github/'+repo.id+'/package.json'}, function(err, resp) {
                                if(err) console.error(err);
                                // XXX: shouldn't we handle this error!!?! (smurthas)
                                return cb();
                            });
                        });
                    });
                });
            });
        }, function(err){
            callback(err, repos);
        });
    });
}

function getIDFromUrl(url) {
    if(typeof url !== 'string') return url;
    return url.substring(url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1);
}

function getWatchers(repo, callback) {
    request.get({url:"https://api.github.com/repos/"+repo.id+"/watchers",
                 headers:auth.headers, json:true}, callback);
}

function getPackage(repo, notAppCallback, isAppCallback) {
    request.get({uri: 'https://raw.github.com/'+repo.id+'/HEAD/package.json',
                 headers:auth.headers},  function(err, resp, pkg) {
        if(resp.statusCode === 404) return notAppCallback();
        try {
            pkg = JSON.parse(pkg);
            if(!isApp(pkg)) return notAppCallback();
        } catch(E) {
            console.error('for repo ' + repo.id + ', found a package.json, but couldn\'t parse it, or it wasn\'t an app', pkg);
            return notAppCallback();
        } // this is going to be really hard to catch, but what can we do from here to let someone know?
        return isAppCallback(pkg);
    });
}

function getTree(repo, callback) {
    request.get({uri:"https://api.github.com/repos/" + repo.id + "/git/trees/HEAD?recursive=1",
                 headers:auth.headers, json:true}, callback);
}

function isApp(pkg) {
    var repository = pkg.repository;
    return repository && (repository.static == "true" || repository.static == true) && repository.type == "app";
}

function syncRepo(repo, tree, callback) {
    var existing = {};
    try {
        var oldtree = JSON.parse(fs.readFileSync(repo.id+".tree.json"));
        for(var i in oldtree) existing[oldtree[i].path] = oldtree[i].sha;
    } catch(e) {
        if(e.code !== 'ENOENT') return callback(e);
    }
    // make sure there's at least one tree entry for the repo dir itself
    tree.push({path:".",sha:"na",type:"tree"});
    async.forEachSeries(tree, function(t, cbTree) {
        if(t.type !== "tree") return cbTree();
        if(existing[t.path] === t.sha) return cbTree(); // no changes
        mkdirp(repo.id + "/" + t.path, 0777, function(err) {
            if(err && err.code !== 'EEXIST') return cbTree(err);
            return cbTree();
        });
    }, function(err) {
        // then the blobs
        var errors = [];
        async.forEachSeries(tree, function(treeItem, cb) {
            return saveBlob(treeItem, repo, existing, cb);
        }, function(err) {
            fs.writeFile(repo.id+".tree.json", JSON.stringify(tree), function(err) {
                if(err) errors.push(err);
                return callback(errors.length > 0 ? errors : null);
            });
        });
    });
}

function saveBlob(treeItem, repo, existing, cb) {
    if(treeItem.type !== "blob") return cb();
    if(existing[treeItem.path] === treeItem.sha) return cb(); // no changes
    request.get({uri:'https://raw.github.com/'+repo.id+'/HEAD/'+treeItem.path,
                 encoding: 'binary',
                 headers:auth.headers},
        function(err, resp, body) {
        if(err || !resp || resp.statusCode !== 200 || body === undefined || body === null) {
            // don't save the sha so it gets retried again
            errors.push(repo.id+": error fetching "+repo.id + "/" + treeItem.path+" status code "+(resp ? resp.statusCode : 0)+" body "+body);
            treeItem.sha = "";
            return cb();
        }
        fs.writeFile(repo.id + "/" + treeItem.path, body, 'binary', cb);
    });
}
<commit_msg>restored my fixes that someone merge conflict voided :)<commit_after>var request = require('request')
  , async = require('async')
  , mkdirp = require('mkdirp')
  , fs = require('fs')
  , lockerUrl
  , auth
  ;

exports.sync = function(processInfo, cb) {
    auth = processInfo.auth;
    auth.headers = {"Authorization":"token "+auth.accessToken, "Connection":"keep-alive"};
    var cached = {};
    if (processInfo.config && processInfo.config.cached) cached = processInfo.config.cached;
    lockerUrl = processInfo.lockerUrl;
    exports.syncRepos(cached, function(err, repos) {
        if (err) console.error(err);
        var responseObj = {data : {}, config: { cached: cached }};
        responseObj.data.repo = repos;
        cb(err, responseObj);
    });
};

exports.syncRepos = function(cached, callback) {
    request.get({url:"https://api.github.com/user/repos", json:true, headers:auth.headers}, function(err, resp, repos) {
        if(err || !repos || !repos.length) return callback(err, []);
        // process each one to get richer data
        async.forEachSeries(repos, function(repo, cb) {
            repo.idorig = repo.id;
            repo.id = getIDFromUrl(repo.url); // LEGACY UGH, we're overwriting the numeric id, not simple to change now
            // nothing changed
            var ckey = repo.pushed_at + repo.watchers;
            if(cached[repo.id] == ckey) return cb(); // if it hasn't changed!
            cached[repo.id] = ckey;
            // get the watchers, is nice
            getWatchers(repo, function(err, resp, watchers) {
                repo.watchers = watchers;
                // now see if it's an app special on isle 9
                getPackage(repo, cb, function(pkg) {
                    // ok, we've got an app, make sure settings are valid
                    getTree(repo, function(err, resp, tree) {
                        if(err || !tree || !tree.tree) return cb();
                        syncRepo(repo, tree.tree, function(err) {
                            if(err) {
                                delete cached[repo.id]; // invalidate the cache, it never sync'd
                                console.error(err);
                                return cb();
                            }
                            // make sure package.json is safe, set some defaults!
                            pkg.repository.handle = repo.id.replace("/", "-").toLowerCase();
                            pkg.name = pkg.repository.handle;
                            pkg.repository.url = 'https://github.com/' + repo.id;
                            if(!pkg.author && auth.profile.name) {
                                pkg.author = {name:auth.profile.name};
                                if(auth.profile.email) pkg.author.email = auth.profile.email;
                            }
                            if(!pkg.version) pkg.version = "0.0.0";
                            if(!pkg.repository.title) pkg.repository.title = repo.name;
                            if(!pkg.repository.hasOwnProperty('static')) pkg.repository.static = true;
                            if(!pkg.repository.hasOwnProperty('type')) pkg.repository.type = "app";
                            if(!pkg.repository.hasOwnProperty('update')) pkg.repository.update = "auto";
                            fs.writeFileSync(repo.id+"/package.json", JSON.stringify(pkg)); // overwriting :/
                            request.post({url:lockerUrl+'/map/upsert?manifest=Me/github/'+repo.id+'/package.json'}, function(err, resp) {
                                if(err) console.error(err);
                                // XXX: shouldn't we handle this error!!?! (smurthas)
                                return cb();
                            });
                        });
                    });
                });
            });
        }, function(err){
            callback(err, repos);
        });
    });
}

function getIDFromUrl(url) {
    if(typeof url !== 'string') return url;
    return url.substring(url.lastIndexOf('/', url.lastIndexOf('/') - 1) + 1);
}

function getWatchers(repo, callback) {
    request.get({url:"https://api.github.com/repos/"+repo.id+"/watchers",
                 headers:auth.headers, json:true}, callback);
}

function getPackage(repo, notCallback, isCallback) {
    request.get({uri: 'https://raw.github.com/'+repo.id+'/HEAD/package.json',
                 headers:auth.headers},  function(err, resp, pkg) {
        if(resp.statusCode === 404) return notCallback();
        try {
            pkg = JSON.parse(pkg);
            if(!pkg.repository || !pkg.repository.title) return notCallback();
        } catch(E) {
            console.error('for repo ' + repo.id + ', found a package.json, but couldn\'t parse it, or it wasn\'t an app', pkg);
            return notCallback();
        } // this is going to be really hard to catch, but what can we do from here to let someone know?
        return isCallback(pkg);
    });
}

function getTree(repo, callback) {
    request.get({uri:"https://api.github.com/repos/" + repo.id + "/git/trees/HEAD?recursive=1",
                 headers:auth.headers, json:true}, callback);
}

function syncRepo(repo, tree, callback) {
    var existing = {};
    try {
        var oldtree = JSON.parse(fs.readFileSync(repo.id+".tree.json"));
        for(var i in oldtree) existing[oldtree[i].path] = oldtree[i].sha;
    } catch(e) {
        if(e.code !== 'ENOENT') return callback(e);
    }
    // make sure there's at least one tree entry for the repo dir itself
    tree.push({path:".",sha:"na",type:"tree"});
    async.forEachSeries(tree, function(t, cbTree) {
        if(t.type !== "tree") return cbTree();
        if(existing[t.path] === t.sha) return cbTree(); // no changes
        mkdirp(repo.id + "/" + t.path, 0777, function(err) {
            if(err && err.code !== 'EEXIST') return cbTree(err);
            return cbTree();
        });
    }, function(err) {
        // then the blobs
        var errors = [];
        async.forEachSeries(tree, function(treeItem, cb) {
            return saveBlob(treeItem, repo, existing, cb);
        }, function(err) {
            fs.writeFile(repo.id+".tree.json", JSON.stringify(tree), function(err) {
                if(err) errors.push(err);
                return callback(errors.length > 0 ? errors : null);
            });
        });
    });
}

function saveBlob(treeItem, repo, existing, cb) {
    if(treeItem.type !== "blob") return cb();
    if(existing[treeItem.path] === treeItem.sha) return cb(); // no changes
    request.get({uri:'https://raw.github.com/'+repo.id+'/HEAD/'+treeItem.path,
                 encoding: 'binary',
                 headers:auth.headers},
        function(err, resp, body) {
        if(err || !resp || resp.statusCode !== 200 || body === undefined || body === null) {
            // don't save the sha so it gets retried again
            errors.push(repo.id+": error fetching "+repo.id + "/" + treeItem.path+" status code "+(resp ? resp.statusCode : 0)+" body "+body);
            treeItem.sha = "";
            return cb();
        }
        fs.writeFile(repo.id + "/" + treeItem.path, body, 'binary', cb);
    });
}
<|endoftext|>


<commit_before>var WebsocketServer = require('ws').Server;
var config = require('../../config/config');
var redis = require('redis');
var redisClient = redis.createClient(config['REDIS_PORT'], config['FOXRIVER_IP']);
var computeOrderKey = require('./redisKeyCompute').computeOrderKey;
var computeCustomerKey = require('./redisKeyCompute').computeCustomerKey;
var TTL = require('./redisKeyCompute').TTL;
var NO_INFLIGHT_ORDER = require('./redisKeyCompute').NO_INFLIGHT_ORDER;
var INFLIGHT_ORDERS = require('./redisKeyCompute').INFLIGHT_ORDERS;
var writeWS = require('./writeWS');
var async = require('async');

module.exports = function(winston) {
	return function handeSetDT(params, ws) {
		if (!params.customer) {
			writeWS(ws, 400, 'no customer in params', {});
		}
		if (!params.dt) {
			writeWS(ws, 400, 'no dt in params', {});
		}

		async.waterfall([

		],
			function(err, result) {
				if (err) {
					winston.error(err);
					return writeWS(ws, 500, err, {});
				}

				//intentionally don't write result back to client.
			}
		);
	};
}<commit_msg>stash<commit_after>var WebsocketServer = require('ws').Server;
var config = require('../../config/config');
var redis = require('redis');
var redisClient = redis.createClient(config['REDIS_PORT'], config['FOXRIVER_IP']);
var computeOrderKey = require('./redisKeyCompute').computeOrderKey;
var computeCustomerKey = require('./redisKeyCompute').computeCustomerKey;
var TTL = require('./redisKeyCompute').TTL;
var NO_INFLIGHT_ORDER = require('./redisKeyCompute').NO_INFLIGHT_ORDER;
var INFLIGHT_ORDERS = require('./redisKeyCompute').INFLIGHT_ORDERS;
var writeWS = require('./writeWS');
var async = require('async');
var handlerHelperInitFunc = require('./handlerHelper');

module.exports = function(winston) {
	var handlerHelper = handlerHelperInitFunc(winston, redisClient);

	return function handeSetDT(params, ws) {
		if (!params.customer) {
			writeWS(ws, 400, 'no customer in params', {});
		}
		if (!params.dt) {
			writeWS(ws, 400, 'no dt in params', {});
		}

		async.waterfall([

		],
			function(err, result) {
				if (err) {
					winston.error(err);
					return writeWS(ws, 500, err, {});
				}

				//intentionally don't write result back to client.
			}
		);
	};
}<|endoftext|>


<commit_before>import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';

export class BoardgameComponent extends PolymerElement {
  static get template() {
    return html`

`;
  }

  static get is() {
    return "boardgame-component"
  }

  static get properties() {
    return {
      //Index is the index of this card in the stack. Included in
      //component-tapped events.
      index: Number,
      //If item is set, it is assumed to be a Component from a stack in a
      //state. faceUp, coContent, spacer, and id will all be set based on
      //its value. A convenient way to stamp out cards with minimal fuss
      //in the default case.
      item: {
        type: Object,
        observer: "_itemChanged"
      },
      //If true, no animations will be played.
      noAnimate: {
        type: Boolean,
        value: false
      },
      //id  should be set to the component's Id from the framework.
      //boardgame-component-animator will look for this id to figure out
      //which cards are logically the same and thus should be animated
      //from one place to another.
      id: String,
      //if true, the card will be rendered differently, no component-
      //tapped events will be triggered.
      disabled: {
        type: Boolean,
        value: false
      },
      //True if the card is neither disabled nor a spacer.
      interactive: {
        type: Boolean,
        readOnly: true,
        computed: "_computeInteractive(spacer, disabled)"
      },
      //If true, the card is "empty" and should take up space but not
      //render a card. Useful for sizedStacks that have an empty slot.
      spacer: {
        type: Boolean,
        reflectToAttribute: true,
        value: false
      },
      //if true, no drop-shadow will be rendered. boardgame-card- stack
      //will set this in some cases, like in a stack, so that all of the
      //shadows for 10's of cards don't multiply together.
      noShadow: Boolean,

      //If true, will use a different shadow rendering using drop-shadow
      //instead of box-shadow. This allows the alpha of the image to be
      //used, but with a lower fidelity rendering.
      altShadow: Boolean,

      //boardgameComponent reflects the attribute boardgame-component to
      //all subclasses, allowing htem to be selected via CSS.
      boardgameComponent: {
        type: Boolean,
        reflectToAttribute: true,
        value: true
      },

      //set _classes to change the classes that are set. call
      //_updateClasses() and it will call _computedClasses. When you
      //override _computedClasses, just add a new item in the observers
      //array that lists all the dependencies. Idelaly this would just be
      //a computed property thta you'd override how it was computed in
      //subclasses, but Polymer doesn't let you re-define a computed
      //property in a subclass.
      _classes: {
        type: String,
      },

      //Bound into outer's style property. Extension point for subclasses.
      _outerStyle: {
        type: String,
        value: "",
      }
    }
  }

  static get observers() {
    //Your subclass should add an entry like _updateClasses(...allPropNames that could change), where all props includes noAnimate, spacer, noshadow,interactive,and disabled. We don't return one here because if we did it would be called twice--one for yours and one for this one.
    return []
  }

  //Returns a template for the subclass. The subtemplate's style will be
  //inserted right after the super template's style, and then the node
  //iwth id of import (if it exists) will have all of its children
  //imported in order and parented under #inner.
  static combinedTemplate(subTemplate) {
    let content = BoardgameComponent.template

    let result = document.importNode(content, true);

    let styleEle = subTemplate.content.querySelector("style");

    let injectionSite = result.content.children[1];

    result.content.insertBefore(styleEle,injectionSite);

    let innerEle = result.content.querySelector("#inner")

    let eleToImport = subTemplate.content.querySelector("#import")

    if (eleToImport) {
      while (eleToImport.children.length > 0) {
        innerEle.appendChild(eleToImport.children[0]);
      }
    }
    return result;
  }

  //obj.properties, smooshed down all the way to the upper.
  get _composedPropertyDefinition() {
    if (!this._memoizedComposedPropertyDefinition) {
      let result = {};
      let obj = this;
      while (obj) {
        let props = obj.constructor.properties;
        if (!props) break;
        for (let key of Object.keys(props)) {
          result[key] = props[key];
        }
        obj = obj.__proto__;
      }
      this._memoizedComposedPropertyDefinition = result;
    }
    return this._memoizedComposedPropertyDefinition;
  }

  //animatingProperties should return an array of strings of property
  //names that change during animations. animatingPropValues() and
  //animatingPropDefaults() will use this.
  get animatingProperties() {
    return [];
  }

  //Returns the bundle of properties, as configured by
  //animatingProperties(), at their current value.
  animatingPropValues() {
    let result = {};
    for (let propName of this.animatingProperties) {
      result[propName] = this[propName];
    }
    return result;
  }

  //Returns the bundle of animating properties, as defined by
  //animatingProperties(), set to the defaults for the given stack. Used
  //when there isn't an element analog before or after the animation to
  //compare to.
  animatingPropDefaults(stack) {
    let result = {};
    for (let propName of this.animatingProperties) {
      result[propName] = stack.stackDefault(propName);
    }
    return result;
  }

  //prepareAnimation is called after the new state is databound but just
  //before animation starts. Default is this.setProperties(beforeProps),
  //but some things have more complicated logic for animation performance.
  //beforeProps is what this element--or one like it--returned from
  //animatingPropValues() before the databinding happened.
  prepareAnimation(beforeProps) {
    this.setProperties(beforeProps);
  }

  //startAnimation is called after the new state is databound and after
  //prepareAnimatino. Default is this.setProperties(beforeProps), but some
  //things have more complicated logic for animation performance.
  //afterProps is what this element--or one like it--returned from
  //animatingPropValues() after the databinding happened.
  startAnimation(afterProps) {
    this.setProperties(afterProps);
  }

  //prepareForBeingAnimatingComponent is called if the component is going
  //to be an animating component; that is it was created within
  //stack.newAnimatingComponent().
  prepareForBeingAnimatingComponent(stack) {
    //Do nothing; subclasses might do something.
  }

  //cloneContent returns whether we should clone the content of this
  //element during animating. Defaults to false; subclasses might
  //override.
  get cloneContent() {
    return false;
  }

  //animationRotates should return true if the before and after have a
  //different rotated property.
  animationRotates(beforeProps, afterProps) {
    return false;
  }

  _updateClasses() {
    //We can ignore the arguments, all that's necessary is that we're
    //called whenever the value could have changed.
    this._classes = this._computeClasses();
  }

  handleTap(e) {
    if (!this.interactive) {
      return;
    }
    this.dispatchEvent(new CustomEvent('component-tapped', {composed: true, detail: {index: this.index}}));
  }

  _itemChanged(newValue) {
    if (newValue === undefined) return;
    if (newValue === null) {
      this.spacer = true;
      return;
    }
    this.id = newValue.Id || "";
  }

  _computeInteractive(spacer, disabled) {
    return !spacer && !disabled;
  }

  _computeClasses() {
    let result = [];
    if (this.spacer) {
      result.push("spacer");
    }
    if (!this.noShadow) {
      result.push(this.altShadow ? "alt-shadow" : "shadow");
    }
    if (this.interactive) {
      result.push("interactive");
    }
    if (this.disabled) {
      result.push("disabled");
    }
    if (this.noAnimate) {
      result.push("no-animate")
    }
    return result.join(" ");
  }
}

customElements.define(BoardgameComponent.is, BoardgameComponent);
<commit_msg>Added a quick TODO for cleanup. Part of #396.<commit_after>import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';

export class BoardgameComponent extends PolymerElement {
  static get template() {
    return html`

`;
  }

  static get is() {
    return "boardgame-component"
  }

  static get properties() {
    return {
      //Index is the index of this card in the stack. Included in
      //component-tapped events.
      index: Number,
      //If item is set, it is assumed to be a Component from a stack in a
      //state. faceUp, coContent, spacer, and id will all be set based on
      //its value. A convenient way to stamp out cards with minimal fuss
      //in the default case.
      item: {
        type: Object,
        observer: "_itemChanged"
      },
      //If true, no animations will be played.
      noAnimate: {
        type: Boolean,
        value: false
      },
      //id  should be set to the component's Id from the framework.
      //boardgame-component-animator will look for this id to figure out
      //which cards are logically the same and thus should be animated
      //from one place to another.
      id: String,
      //if true, the card will be rendered differently, no component-
      //tapped events will be triggered.
      disabled: {
        type: Boolean,
        value: false
      },
      //True if the card is neither disabled nor a spacer.
      interactive: {
        type: Boolean,
        readOnly: true,
        computed: "_computeInteractive(spacer, disabled)"
      },
      //If true, the card is "empty" and should take up space but not
      //render a card. Useful for sizedStacks that have an empty slot.
      spacer: {
        type: Boolean,
        reflectToAttribute: true,
        value: false
      },
      //if true, no drop-shadow will be rendered. boardgame-card- stack
      //will set this in some cases, like in a stack, so that all of the
      //shadows for 10's of cards don't multiply together.
      noShadow: Boolean,

      //If true, will use a different shadow rendering using drop-shadow
      //instead of box-shadow. This allows the alpha of the image to be
      //used, but with a lower fidelity rendering.
      altShadow: Boolean,

      //boardgameComponent reflects the attribute boardgame-component to
      //all subclasses, allowing htem to be selected via CSS.
      boardgameComponent: {
        type: Boolean,
        reflectToAttribute: true,
        value: true
      },

      //set _classes to change the classes that are set. call
      //_updateClasses() and it will call _computedClasses. When you
      //override _computedClasses, just add a new item in the observers
      //array that lists all the dependencies. Idelaly this would just be
      //a computed property thta you'd override how it was computed in
      //subclasses, but Polymer doesn't let you re-define a computed
      //property in a subclass.
      _classes: {
        type: String,
      },

      //Bound into outer's style property. Extension point for subclasses.
      _outerStyle: {
        type: String,
        value: "",
      }
    }
  }

  static get observers() {
    //Your subclass should add an entry like _updateClasses(...allPropNames that could change), where all props includes noAnimate, spacer, noshadow,interactive,and disabled. We don't return one here because if we did it would be called twice--one for yours and one for this one.
    return []
  }

  //Returns a template for the subclass. The subtemplate's style will be
  //inserted right after the super template's style, and then the node
  //iwth id of import (if it exists) will have all of its children
  //imported in order and parented under #inner.
  static combinedTemplate(subTemplate) {
    let content = BoardgameComponent.template

    let result = document.importNode(content, true);

    let styleEle = subTemplate.content.querySelector("style");

    let injectionSite = result.content.children[1];

    result.content.insertBefore(styleEle,injectionSite);

    let innerEle = result.content.querySelector("#inner")

    let eleToImport = subTemplate.content.querySelector("#import")

    if (eleToImport) {
      while (eleToImport.children.length > 0) {
        innerEle.appendChild(eleToImport.children[0]);
      }
    }
    return result;
  }

  //obj.properties, smooshed down all the way to the upper.
  get _composedPropertyDefinition() {
    //TODO: can we get rid of this? Doesn't seem to be used, and I believe
    //Polymer does this for us now.
    if (!this._memoizedComposedPropertyDefinition) {
      let result = {};
      let obj = this;
      while (obj) {
        let props = obj.constructor.properties;
        if (!props) break;
        for (let key of Object.keys(props)) {
          result[key] = props[key];
        }
        obj = obj.__proto__;
      }
      this._memoizedComposedPropertyDefinition = result;
    }
    return this._memoizedComposedPropertyDefinition;
  }

  //animatingProperties should return an array of strings of property
  //names that change during animations. animatingPropValues() and
  //animatingPropDefaults() will use this.
  get animatingProperties() {
    return [];
  }

  //Returns the bundle of properties, as configured by
  //animatingProperties(), at their current value.
  animatingPropValues() {
    let result = {};
    for (let propName of this.animatingProperties) {
      result[propName] = this[propName];
    }
    return result;
  }

  //Returns the bundle of animating properties, as defined by
  //animatingProperties(), set to the defaults for the given stack. Used
  //when there isn't an element analog before or after the animation to
  //compare to.
  animatingPropDefaults(stack) {
    let result = {};
    for (let propName of this.animatingProperties) {
      result[propName] = stack.stackDefault(propName);
    }
    return result;
  }

  //prepareAnimation is called after the new state is databound but just
  //before animation starts. Default is this.setProperties(beforeProps),
  //but some things have more complicated logic for animation performance.
  //beforeProps is what this element--or one like it--returned from
  //animatingPropValues() before the databinding happened.
  prepareAnimation(beforeProps) {
    this.setProperties(beforeProps);
  }

  //startAnimation is called after the new state is databound and after
  //prepareAnimatino. Default is this.setProperties(beforeProps), but some
  //things have more complicated logic for animation performance.
  //afterProps is what this element--or one like it--returned from
  //animatingPropValues() after the databinding happened.
  startAnimation(afterProps) {
    this.setProperties(afterProps);
  }

  //prepareForBeingAnimatingComponent is called if the component is going
  //to be an animating component; that is it was created within
  //stack.newAnimatingComponent().
  prepareForBeingAnimatingComponent(stack) {
    //Do nothing; subclasses might do something.
  }

  //cloneContent returns whether we should clone the content of this
  //element during animating. Defaults to false; subclasses might
  //override.
  get cloneContent() {
    return false;
  }

  //animationRotates should return true if the before and after have a
  //different rotated property.
  animationRotates(beforeProps, afterProps) {
    return false;
  }

  _updateClasses() {
    //We can ignore the arguments, all that's necessary is that we're
    //called whenever the value could have changed.
    this._classes = this._computeClasses();
  }

  handleTap(e) {
    if (!this.interactive) {
      return;
    }
    this.dispatchEvent(new CustomEvent('component-tapped', {composed: true, detail: {index: this.index}}));
  }

  _itemChanged(newValue) {
    if (newValue === undefined) return;
    if (newValue === null) {
      this.spacer = true;
      return;
    }
    this.id = newValue.Id || "";
  }

  _computeInteractive(spacer, disabled) {
    return !spacer && !disabled;
  }

  _computeClasses() {
    let result = [];
    if (this.spacer) {
      result.push("spacer");
    }
    if (!this.noShadow) {
      result.push(this.altShadow ? "alt-shadow" : "shadow");
    }
    if (this.interactive) {
      result.push("interactive");
    }
    if (this.disabled) {
      result.push("disabled");
    }
    if (this.noAnimate) {
      result.push("no-animate")
    }
    return result.join(" ");
  }
}

customElements.define(BoardgameComponent.is, BoardgameComponent);
<|endoftext|>


<commit_before>

(function () {
    var tokenise = function (str) {
        var tokens = []
        ,   re = {
                "float":        /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
            ,   "integer":      /^-?(0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*)/
            ,   "identifier":   /^[A-Z_a-z][0-9A-Z_a-z]*/
            ,   "string":       /^"[^"]*"/
            ,   "whitespace":   /^[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n)*?\*\/)[\t\n\r ]*)+/
            ,   "other":        /^[^\t\n\r 0-9A-Z_a-z]/
            }
        ,   types = []
        ;
        for (var k in re) types.push(k);
        while (str.length > 0) {
            var matched = false;
            for (var i = 0, n = types.length; i < n; i++) {
                var type = types[i];
                str = str.replace(re[type], function (tok) {
                    tokens.push({ type: type, value: tok });
                    matched = true;
                    return "";
                });
                if (matched) break;
            }
            if (matched) continue;
            throw new Error("Token stream not progressing.");
        }
        return tokens;
    };

    var parse = function (tokens) {
        var line = 0;
        tokens = tokens.slice();

        var error = function (str) {
            throw str + ", line " + line;
        };

        var consume = function (type, value) {
            if (!tokens.length || tokens[0].type !== type) return;
            if (typeof value === "undefined" || tokens[0].value === value) return tokens.shift();
        };

        var ws = function () {
            if (!tokens.length) return;
            if (tokens[0].type === "whitespace") {
                var t = tokens.shift();
                t.value.replace(/\n/g, function (m) { line++; return m; });
                return t;
            }
        };

        var all_ws = function () {
            var t = { type: "whitespace", value: "" };
            while (true) {
                var w = ws();
                if (!w) break;
                t.value += w.value;
            }
            if (t.value.length > 0) return t;
        };

        var integer_type = function () {
            var ret = "";
            all_ws();
            if (consume("identifier", "unsigned")) ret = "unsigned ";
            all_ws();
            if (consume("identifier", "short")) return ret + "short";
            if (consume("identifier", "long")) {
                ret += "long";
                all_ws();
                if (consume("identifier", "long")) return ret + " long";
                return ret;
            }
            if (ret) error("Failed to parse integer type");
        };

        var float_type = function () {
            var ret = "";
            all_ws();
            if (consume("identifier", "unrestricted")) ret = "unrestricted ";
            all_ws();
            if (consume("identifier", "float")) return ret + "float";
            if (consume("identifier", "double")) return ret + "double";
            if (ret) error("Failed to parse float type");
        };

        var primitive_type = function () {
            var num_type = integer_type() || float_type();
            if (num_type) return num_type;
            all_ws();
            if (consume("identifier", "boolean")) return "boolean";
            if (consume("identifier", "byte")) return "byte";
            if (consume("identifier", "octet")) return "octet";
        };

        var const_value = function () {
            if (consume("identifier", "true")) return true;
            if (consume("identifier", "false")) return false;
            if (consume("identifier", "null")) return null;
            if (consume("identifier", "Infinity")) return Infinity;
            if (consume("identifier", "NaN")) return NaN;
            var ret = consume("float") || consume("integer");
            if (ret) return 1 * ret.value;
            var tok = consume("other", "-");
            if (tok) {
                if (consume("identifier", "Infinity")) return -Infinity;
                else tokens.unshift(tok);
            }
        };

        var type_suffix = function (obj) {
            while (true) {
                all_ws();
                if (consume("other", "?")) obj.nullable = true;
                else if (consume("other", "[")) {
                    all_ws();
                    consume("other", "]") || error("Unterminated array type");
                    if (!obj.array) obj.array = 1;
                    else obj.array++;
                }
                else return;
            }
        };

        var single_type = function () {
            var prim = primitive_type()
            ,   ret = { sequence: false, nullable: false, array: false, union: false }
            ;
            if (prim) {
                ret.idlType = prim;
            }
            else if (consume("identifier", "sequence")) {
                all_ws();
                if (!consume("other", "<")) {
                    ret.idlType = "sequence";
                }
                else {
                    ret.sequence = true;
                    ret.idlType = type() || error("Error parsing sequence type");
                    all_ws();
                    if (!consume("other", ">")) error("Unterminated sequence");
                    all_ws();
                    if (consume("other", "?")) ret.nullable = true;
                    return ret;
                }
            }
            else {
                var name = consume("identifier");
                if (!name) return;
                ret.idlType = name.value;
            }
            type_suffix(ret);
            return ret;
        };

        var union_type = function () {
            all_ws();
            if (!consume("other", "(")) return;
            var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
            var fst = type() || error("Union type with no content");
            ret.idlType.push(fst);
            while (true) {
                all_ws();
                if (!consume("identifier", "or")) break;
                var typ = type() || error("No type after 'or' in union type");
                ret.idlType.push(typ);
            }
            if (!consume("other", ")")) error("Unterminated union type");
            type_suffix(ret);
            return ret;
        };

        var type = function () {
            return single_type() || union_type();
        };

        var argument = function () {
            var ret = { optional: false, variadic: false };
            ret.extAttrs = extended_attrs();
            all_ws();
            if (consume("identifier", "optional")) {
                ret.optional = true;
                all_ws();
            }
            ret.type = type();
            if (!ret.optional) {
                all_ws();
                if (tokens.length >= 3 &&
                    tokens[0].type === "other" && tokens[0].value === "." &&
                    tokens[1].type === "other" && tokens[1].value === "." &&
                    tokens[2].type === "other" && tokens[2].value === "."
                    ) {
                    tokens.shift();
                    tokens.shift();
                    tokens.shift();
                    ret.variadic = true;
                }
            }
            all_ws();
            var name = consume("identifier") || error("No name in argument");
            ret.name = name.value;
            if (ret.optional) {
                all_ws();
                if (consume("other", "=")) {
                    all_ws();
                    var def = const_value();
                    if (typeof def !== "undefined") {
                        ret["default"] = def;
                    }
                    else {
                        ret["default"] = consume("string") || error("No value for argument default");
                    }
                }
            }
            return ret;
        };

        var argument_list = function () {
            var arg = argument(), ret = [];
            if (!arg) return ret;
            ret.push(arg);
            while (true) {
                all_ws();
                if (!consume("other", ",")) return ret;
                all_ws();
                var nxt = argument() || error("Trailing comma in arguments list");
                ret.push(nxt);
            }
        };

        var simple_extended_attr = function () {
            all_ws();
            var name = consume("identifier");
            if (!name) return;
            var ret = {
                name: name.value
            ,   "arguments": null
            };
            all_ws();
            var eq = consume("other", "=");
            if (eq) {
                all_ws();
                ret.rhs = consume("identifier");
                if (!ret.rhs) return error("No right hand side to extended attribute assignment");
            }
            all_ws();
            if (consume("other", "(")) {
                ret["arguments"] = argument_list();
                all_ws();
                consume("other", ")") || error("Unclosed argument in extended attribute");
            }
            return ret;
        };

        // Note: we parse something simpler than the official syntax. It's all that ever
        // seems to be used
        var extended_attrs = function () {
            var eas = [];
            all_ws();
            if (!consume("other", "[")) return eas;
            eas[0] = simple_extended_attr() || error("Extended attribute with not content");
            all_ws();
            while (consume("other", ",")) {
                all_ws();
                eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
                all_ws();
            }
            consume("other", "]") || error("No end of extended attribute");
            return eas;
        };

        var const_ = function () {
            all_ws();
            if (!consume("identifier", "const")) return;
            // XXX
        };

        var callback = function () {

        };

        var interface_ = function () {

        };

        var partial = function () {

        };

        var dictionary = function () {

        };

        var exception = function () {
            all_ws();
            if (!consume("identifier", "exception")) return;
            all_ws();
            var name = consume("identifier") || error("No name for exception");
            var ret = {
                type:   "exception"
            ,   name:   name.value
            ,   inheritance: null
            ,   members:    []
            };
            all_ws();
            if (consume("other", ":")) {
                all_ws();
                ret.inheritance = consume("identifier") || error ("No type in exception inheritance");
                all_ws();
            }
            consume("other", "{") || error("Bodyless exception");
            while (true) {
                all_ws();
                if (consume("other", "}")) {
                    all_ws();
                    consume("other", ";") || error("Missing semicolon after exception");
                    return ret;
                }
                var ea = extended_attrs();
                all_ws();
                var cnt = const_();
                if (cnt) {
                    cnt.extAttrs = ea;
                    ret.members.push(cnt);
                }
                else {
                    var typ = type();
                    all_ws();
                    var name = consume("identifier");
                    all_ws();
                    if (!typ || !name || !consume("other", ";")) error("Unknown member in exception body");
                    ret.members.push({
                        type:       "field"
                    ,   name:       name.value
                    ,   idlType:    typ
                    ,   extAttrs:   ea
                    });
                }
            }
        };
        // {
        //   "type": "exception",
        //   "name": "E",
        //   "members": [
        //     {
        //       "type": "field",
        //       "extAttrs": "",
        //       "idlType": {
        //         "sequence": false,
        //         "array": false,
        //         "idlType": "unsigned short",
        //         "nullable": false
        //       },
        //       "name": "code"
        //     }
        //   ],
        //   "extAttrs": ""
        // },

        var enum_ = function () {
            all_ws();
            if (!consume("identifier", "enum")) return;
            all_ws();
            var name = consume("identifier") || error("No name for enum");
            var ret = {
                type:   "enum"
            ,   name:   name.value
            ,   values: []
            };
            all_ws();
            consume("other", "{") || error("No curly for enum");
            var saw_comma = false;
            while (true) {
                all_ws();
                if (consume("other", "}")) {
                    all_ws();
                    if (saw_comma) error("Trailing comma in enum");
                    consume("other", ";") || error("No semicolon after enum");
                    return ret;
                }
                var val = consume("string") || error("Unexpected value in enum");
                ret.values.push(val.value.replace(/"/g, ""));
                all_ws();
                if (consume("other", ",")) {
                    all_ws();
                    saw_comma = true;
                }
                else {
                    saw_comma = false;
                }
            }
        };

        var typedef = function () {
            all_ws();
            if (!consume("identifier", "typedef")) return;
            var ret = {
                type:   "typedef"
            };
            all_ws();
            ret.extAttrs = extended_attrs();
            all_ws();
            ret.idlType = type() || error("No type in typedef");
            all_ws();
            var name = consume("identifier") || error("No name in typedef");
            ret.name = name.value;
            all_ws();
            consume("other", ";") || error("Unterminated typedef");
            return ret;
        };

        var implements_ = function () {
            all_ws();
            var target = consume("identifier");
            if (!target) return;
            var w = all_ws();
            if (consume("identifier", "implements")) {
                var ret = {
                    type:   "implements"
                ,   target: target.value
                };
                all_ws();
                var imp = consume("identifier") || error("Incomplete implements statement");
                ret["implements"] = imp.value;
                all_ws();
                consume("other", ";") || error("No terminating ; for implements statement");
                return ret;
            }
            else {
                // rollback
                tokens.unshift(w);
                tokens.unshift(target);
            }
        };

        var definition = function () {
            return  callback()      ||
                    interface_()    ||
                    partial()       ||
                    dictionary()    ||
                    exception()     ||
                    enum_()         ||
                    typedef()       ||
                    implements_()
                    ;
        };

        var definitions = function () {
            if (!tokens.length) return [];
            var defs = [];
            while (true) {
                var ea = extended_attrs()
                ,   def = definition();
                if (!def) {
                    if (ea) error("Stray extended attributes");
                    break;
                }
                def.extAttrs = ea;
                defs.push(def);
            }
            return defs;
        };

        return definitions();
    };

    var obj = {
        parse:  function (str) {
            var tokens = tokenise(str);
            console.log(tokens);
            return parse(tokens);
        }
    };
    if (typeof module !== "undefined" && module.exports) {
        module.exports = obj;
    }
    else {
        window.WebIDL2 = obj;
    }
}());
<commit_msg>exceptions<commit_after>

(function () {
    var tokenise = function (str) {
        var tokens = []
        ,   re = {
                "float":        /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
            ,   "integer":      /^-?(0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*)/
            ,   "identifier":   /^[A-Z_a-z][0-9A-Z_a-z]*/
            ,   "string":       /^"[^"]*"/
            ,   "whitespace":   /^[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n)*?\*\/)[\t\n\r ]*)+/
            ,   "other":        /^[^\t\n\r 0-9A-Z_a-z]/
            }
        ,   types = []
        ;
        for (var k in re) types.push(k);
        while (str.length > 0) {
            var matched = false;
            for (var i = 0, n = types.length; i < n; i++) {
                var type = types[i];
                str = str.replace(re[type], function (tok) {
                    tokens.push({ type: type, value: tok });
                    matched = true;
                    return "";
                });
                if (matched) break;
            }
            if (matched) continue;
            throw new Error("Token stream not progressing.");
        }
        return tokens;
    };

    var parse = function (tokens) {
        var line = 0;
        tokens = tokens.slice();

        var error = function (str) {
            var tok = "", numTokens = 0, maxTokens = 5;
            while (numTokens < maxTokens && tokens.length > numTokens) {
                tok += tokens[numTokens].value;
                numTokens++;
            }
            throw str + ", line " + line + " (tokens: '" + tok + "')";
        };

        var consume = function (type, value) {
            if (!tokens.length || tokens[0].type !== type) return;
            if (typeof value === "undefined" || tokens[0].value === value) return tokens.shift();
        };

        var ws = function () {
            if (!tokens.length) return;
            if (tokens[0].type === "whitespace") {
                var t = tokens.shift();
                t.value.replace(/\n/g, function (m) { line++; return m; });
                return t;
            }
        };

        var all_ws = function () {
            var t = { type: "whitespace", value: "" };
            while (true) {
                var w = ws();
                if (!w) break;
                t.value += w.value;
            }
            if (t.value.length > 0) return t;
        };

        var integer_type = function () {
            var ret = "";
            all_ws();
            if (consume("identifier", "unsigned")) ret = "unsigned ";
            all_ws();
            if (consume("identifier", "short")) return ret + "short";
            if (consume("identifier", "long")) {
                ret += "long";
                all_ws();
                if (consume("identifier", "long")) return ret + " long";
                return ret;
            }
            if (ret) error("Failed to parse integer type");
        };

        var float_type = function () {
            var ret = "";
            all_ws();
            if (consume("identifier", "unrestricted")) ret = "unrestricted ";
            all_ws();
            if (consume("identifier", "float")) return ret + "float";
            if (consume("identifier", "double")) return ret + "double";
            if (ret) error("Failed to parse float type");
        };

        var primitive_type = function () {
            var num_type = integer_type() || float_type();
            if (num_type) return num_type;
            all_ws();
            if (consume("identifier", "boolean")) return "boolean";
            if (consume("identifier", "byte")) return "byte";
            if (consume("identifier", "octet")) return "octet";
        };

        var const_value = function () {
            if (consume("identifier", "true")) return true;
            if (consume("identifier", "false")) return false;
            if (consume("identifier", "null")) return null;
            if (consume("identifier", "Infinity")) return Infinity;
            if (consume("identifier", "NaN")) return NaN;
            var ret = consume("float") || consume("integer");
            if (ret) return 1 * ret.value;
            var tok = consume("other", "-");
            if (tok) {
                if (consume("identifier", "Infinity")) return -Infinity;
                else tokens.unshift(tok);
            }
        };

        var type_suffix = function (obj) {
            while (true) {
                all_ws();
                if (consume("other", "?")) obj.nullable = true;
                else if (consume("other", "[")) {
                    all_ws();
                    consume("other", "]") || error("Unterminated array type");
                    if (!obj.array) obj.array = 1;
                    else obj.array++;
                }
                else return;
            }
        };

        var single_type = function () {
            var prim = primitive_type()
            ,   ret = { sequence: false, nullable: false, array: false, union: false }
            ;
            if (prim) {
                ret.idlType = prim;
            }
            else if (consume("identifier", "sequence")) {
                all_ws();
                if (!consume("other", "<")) {
                    ret.idlType = "sequence";
                }
                else {
                    ret.sequence = true;
                    ret.idlType = type() || error("Error parsing sequence type");
                    all_ws();
                    if (!consume("other", ">")) error("Unterminated sequence");
                    all_ws();
                    if (consume("other", "?")) ret.nullable = true;
                    return ret;
                }
            }
            else {
                var name = consume("identifier");
                if (!name) return;
                ret.idlType = name.value;
            }
            type_suffix(ret);
            return ret;
        };

        var union_type = function () {
            all_ws();
            if (!consume("other", "(")) return;
            var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
            var fst = type() || error("Union type with no content");
            ret.idlType.push(fst);
            while (true) {
                all_ws();
                if (!consume("identifier", "or")) break;
                var typ = type() || error("No type after 'or' in union type");
                ret.idlType.push(typ);
            }
            if (!consume("other", ")")) error("Unterminated union type");
            type_suffix(ret);
            return ret;
        };

        var type = function () {
            return single_type() || union_type();
        };

        var argument = function () {
            var ret = { optional: false, variadic: false };
            ret.extAttrs = extended_attrs();
            all_ws();
            if (consume("identifier", "optional")) {
                ret.optional = true;
                all_ws();
            }
            ret.type = type();
            if (!ret.optional) {
                all_ws();
                if (tokens.length >= 3 &&
                    tokens[0].type === "other" && tokens[0].value === "." &&
                    tokens[1].type === "other" && tokens[1].value === "." &&
                    tokens[2].type === "other" && tokens[2].value === "."
                    ) {
                    tokens.shift();
                    tokens.shift();
                    tokens.shift();
                    ret.variadic = true;
                }
            }
            all_ws();
            var name = consume("identifier") || error("No name in argument");
            ret.name = name.value;
            if (ret.optional) {
                all_ws();
                if (consume("other", "=")) {
                    all_ws();
                    var def = const_value();
                    if (typeof def !== "undefined") {
                        ret["default"] = def;
                    }
                    else {
                        ret["default"] = consume("string") || error("No value for argument default");
                    }
                }
            }
            return ret;
        };

        var argument_list = function () {
            var arg = argument(), ret = [];
            if (!arg) return ret;
            ret.push(arg);
            while (true) {
                all_ws();
                if (!consume("other", ",")) return ret;
                all_ws();
                var nxt = argument() || error("Trailing comma in arguments list");
                ret.push(nxt);
            }
        };

        var simple_extended_attr = function () {
            all_ws();
            var name = consume("identifier");
            if (!name) return;
            var ret = {
                name: name.value
            ,   "arguments": null
            };
            all_ws();
            var eq = consume("other", "=");
            if (eq) {
                all_ws();
                ret.rhs = consume("identifier");
                if (!ret.rhs) return error("No right hand side to extended attribute assignment");
            }
            all_ws();
            if (consume("other", "(")) {
                ret["arguments"] = argument_list();
                all_ws();
                consume("other", ")") || error("Unclosed argument in extended attribute");
            }
            return ret;
        };

        // Note: we parse something simpler than the official syntax. It's all that ever
        // seems to be used
        var extended_attrs = function () {
            var eas = [];
            all_ws();
            if (!consume("other", "[")) return eas;
            eas[0] = simple_extended_attr() || error("Extended attribute with not content");
            all_ws();
            while (consume("other", ",")) {
                all_ws();
                eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
                all_ws();
            }
            consume("other", "]") || error("No end of extended attribute");
            return eas;
        };

        var const_ = function () {
            all_ws();
            if (!consume("identifier", "const")) return;
            var ret = { type: "const", nullable: false };
            all_ws();
            var typ = primitive_type();
            if (!typ) {
                typ = consume("identifier") || error("No type for const");
                typ = typ.value;
            }
            ret.idlType = typ;
            all_ws();
            if (consume("other", "?")) {
                ret.nullable = true;
                all_ws();
            }
            var name = consume("identifier") || error("No name for const");
            ret.name = name.value;
            all_ws();
            consume("other", "=") || error("No value assignment for const");
            all_ws();
            ret.value = const_value() || error("No value for const");
            all_ws();
            consume("other", ";") || error("Unterminated const");
            return ret;
        };

        var callback = function () {

        };

        var interface_ = function () {

        };

        var partial = function () {

        };

        var dictionary = function () {

        };

        var exception = function () {
            all_ws();
            if (!consume("identifier", "exception")) return;
            all_ws();
            var name = consume("identifier") || error("No name for exception");
            var ret = {
                type:   "exception"
            ,   name:   name.value
            ,   inheritance: null
            ,   members:    []
            };
            all_ws();
            if (consume("other", ":")) {
                all_ws();
                var inh = consume("identifier") || error ("No type in exception inheritance");
                ret.inheritance = inh.value;
                all_ws();
            }
            consume("other", "{") || error("Bodyless exception");
            while (true) {
                all_ws();
                if (consume("other", "}")) {
                    all_ws();
                    consume("other", ";") || error("Missing semicolon after exception");
                    return ret;
                }
                var ea = extended_attrs();
                all_ws();
                var cnt = const_();
                if (cnt) {
                    cnt.extAttrs = ea;
                    ret.members.push(cnt);
                }
                else {
                    var typ = type();
                    all_ws();
                    var name = consume("identifier");
                    all_ws();
                    if (!typ || !name || !consume("other", ";")) error("Unknown member in exception body");
                    ret.members.push({
                        type:       "field"
                    ,   name:       name.value
                    ,   idlType:    typ
                    ,   extAttrs:   ea
                    });
                }
            }
        };
        // {
        //   "type": "exception",
        //   "name": "E",
        //   "members": [
        //     {
        //       "type": "field",
        //       "extAttrs": "",
        //       "idlType": {
        //         "sequence": false,
        //         "array": false,
        //         "idlType": "unsigned short",
        //         "nullable": false
        //       },
        //       "name": "code"
        //     }
        //   ],
        //   "extAttrs": ""
        // },

        var enum_ = function () {
            all_ws();
            if (!consume("identifier", "enum")) return;
            all_ws();
            var name = consume("identifier") || error("No name for enum");
            var ret = {
                type:   "enum"
            ,   name:   name.value
            ,   values: []
            };
            all_ws();
            consume("other", "{") || error("No curly for enum");
            var saw_comma = false;
            while (true) {
                all_ws();
                if (consume("other", "}")) {
                    all_ws();
                    if (saw_comma) error("Trailing comma in enum");
                    consume("other", ";") || error("No semicolon after enum");
                    return ret;
                }
                var val = consume("string") || error("Unexpected value in enum");
                ret.values.push(val.value.replace(/"/g, ""));
                all_ws();
                if (consume("other", ",")) {
                    all_ws();
                    saw_comma = true;
                }
                else {
                    saw_comma = false;
                }
            }
        };

        var typedef = function () {
            all_ws();
            if (!consume("identifier", "typedef")) return;
            var ret = {
                type:   "typedef"
            };
            all_ws();
            ret.extAttrs = extended_attrs();
            all_ws();
            ret.idlType = type() || error("No type in typedef");
            all_ws();
            var name = consume("identifier") || error("No name in typedef");
            ret.name = name.value;
            all_ws();
            consume("other", ";") || error("Unterminated typedef");
            return ret;
        };

        var implements_ = function () {
            all_ws();
            var target = consume("identifier");
            if (!target) return;
            var w = all_ws();
            if (consume("identifier", "implements")) {
                var ret = {
                    type:   "implements"
                ,   target: target.value
                };
                all_ws();
                var imp = consume("identifier") || error("Incomplete implements statement");
                ret["implements"] = imp.value;
                all_ws();
                consume("other", ";") || error("No terminating ; for implements statement");
                return ret;
            }
            else {
                // rollback
                tokens.unshift(w);
                tokens.unshift(target);
            }
        };

        var definition = function () {
            return  callback()      ||
                    interface_()    ||
                    partial()       ||
                    dictionary()    ||
                    exception()     ||
                    enum_()         ||
                    typedef()       ||
                    implements_()
                    ;
        };

        var definitions = function () {
            if (!tokens.length) return [];
            var defs = [];
            while (true) {
                var ea = extended_attrs()
                ,   def = definition();
                if (!def) {
                    if (ea.length) error("Stray extended attributes");
                    break;
                }
                def.extAttrs = ea;
                defs.push(def);
            }
            return defs;
        };

        return definitions();
    };

    var obj = {
        parse:  function (str) {
            var tokens = tokenise(str);
            console.log(tokens);
            return parse(tokens);
        }
    };
    if (typeof module !== "undefined" && module.exports) {
        module.exports = obj;
    }
    else {
        window.WebIDL2 = obj;
    }
}());
<|endoftext|>


<commit_before>import React from 'react'
import h from '../../helpers'

const BillEntry = ({
  bill
}) => (

          You owe

              {bill.whoIsOwed}

              {h.formatPrice(bill.amount * 100)}
              for

              {bill.billName}

          by {h.getPaymentDate(bill.due_date)}

        <button className="pay-button btn btn-default">Pay</button>

)

export default BillEntry<commit_msg>update bill entry styling and button alignment<commit_after>import React from 'react'
import h from '../../helpers'

const BillEntry = ({
  bill
}) => (

          You owe

              {bill.whoIsOwed}

              {h.formatPrice(bill.amount * 100)}
              for

              {bill.billName}

          by {h.getPaymentDate(bill.due_date)}

        <button className="pay-button btn btn-default">Pay</button>

)

export default BillEntry<|endoftext|>