<commit_before>Ext.define('TrackAnnot.store.Track', {
    extend: 'Ext.data.AbstractStore',
	data: [],
	requires: ['Ext.data.StoreManager'],
	model : 'TrackAnnot.model.Annotation', // TODO dummy model
	config: {
	    storeId: 'track',
	    /**
	     * @cfg {Number} trackerId Identifier of tracker
	     */
	    trackerId: null,
	    /**
	     * @cfg {Date} start Start of tracker data
	     */
	    start: null,
        /**
         * @cfg {Date} end End of tracker data
         */
	    end: null,
	    /**
	     * @cfg {Array}
	     */
	    format: null,
	    urlTemplate: '/aws/tracker/{trackerId}/{start}/{end}'
	},
	constructor: function(config) {
	   this.callParent(arguments);

       /**
        * @event load
        * Fires whener the store reads data from a remote data source
        * @param {TrackAnnot.store.Track} this
        * @param {Array} data
        * @param {Boolean} successful True if the operation was successful.
        */
       this.addEvents('load');
       this.initConfig(config);
	},
	load: function() {
	    var me = this;
	    Ext.Ajax.request({
	        url: this.getUrl(),
	        success: this.success,
	        failure: this.failure,
	        scope: me
	    });
	},
	getUrl: function() {
	    var tpl = new Ext.Template(this.urlTemplate);
	    return tpl.apply({
	        trackerId: this.trackerId,
	        start: this.start.toISOString(),
	        end: this.end.toISOString()
	    });
	},
	success: function(response) {
	    this.data = Ext.decode(response.responseText);
	    this.data.forEach(function(d) {
	        d.date_time = new Date(d.date_time);
	    });
	    var isLoaded = true;
	    if (this.data.length == 0) {
	        isLoaded = false;
	    }
	    this.fireEvent('load', this, this.data, isLoaded);
	},
	failure: function(response) {
	    // Reading local files returns status 0
	    if (response.status == 0) {
	        return this.success(response);
	    }
        var isLoaded = false;
	    this.fireEvent('load', this, this.data, isLoaded);
	},
	getTimeExtent: function() {
	    return [this.getStart(), this.getEnd()];
	},
	/**
	 * Generate new axis.
	 * Uses configured format.
	 */
	getAxis: function() {
	    return d3.svg.axis().tickFormat(this.getFormat()).tickSubdivide(2);
	},
	/**
	 *
	 * @param {Date} current
	 * @param {Number} before
	 * @param {Number} after
	 */
	getSurrounding: function(current, before, after) {
	    return [];
	},
	get: function(index) {
	  return this.data[index];
	},
	closestIndex: function(newdate) {
        // lookup index of timepoint closest to current
        var bisectDate = d3.bisector(function(d) { return d.date_time;}).left;
        var index = bisectDate(this.data, newdate, 1);
        return index;
	},
	eachRange: function(start, end, callback, scope) {
	    if (!scope) {
	        scope = this;
	    }
	    var startId = this.closestIndex(start);
	    var endId = this.closestIndex(end);
	    for (var i = startId, len = endId; i < len; i++) {
	        callback.call(scope, this.data[i], i);
	    }
	},
	isEmpty: function() {
	    return this.data.length == 0;
	}
});<commit_msg>Annotation export was missing single timepoint annotations. When start and end where the same then no csv line was generated. Corrected by calling csv line generated also for single timepoints.<commit_after>Ext.define('TrackAnnot.store.Track', {
    extend: 'Ext.data.AbstractStore',
	data: [],
	requires: ['Ext.data.StoreManager'],
	model : 'TrackAnnot.model.Annotation', // TODO dummy model
	config: {
	    storeId: 'track',
	    /**
	     * @cfg {Number} trackerId Identifier of tracker
	     */
	    trackerId: null,
	    /**
	     * @cfg {Date} start Start of tracker data
	     */
	    start: null,
        /**
         * @cfg {Date} end End of tracker data
         */
	    end: null,
	    /**
	     * @cfg {Array}
	     */
	    format: null,
	    urlTemplate: '/aws/tracker/{trackerId}/{start}/{end}'
	},
	constructor: function(config) {
	   this.callParent(arguments);

       /**
        * @event load
        * Fires whener the store reads data from a remote data source
        * @param {TrackAnnot.store.Track} this
        * @param {Array} data
        * @param {Boolean} successful True if the operation was successful.
        */
       this.addEvents('load');
       this.initConfig(config);
	},
	load: function() {
	    var me = this;
	    Ext.Ajax.request({
	        url: this.getUrl(),
	        success: this.success,
	        failure: this.failure,
	        scope: me
	    });
	},
	getUrl: function() {
	    var tpl = new Ext.Template(this.urlTemplate);
	    return tpl.apply({
	        trackerId: this.trackerId,
	        start: this.start.toISOString(),
	        end: this.end.toISOString()
	    });
	},
	success: function(response) {
	    this.data = Ext.decode(response.responseText);
	    this.data.forEach(function(d) {
	        d.date_time = new Date(d.date_time);
	    });
	    var isLoaded = true;
	    if (this.data.length == 0) {
	        isLoaded = false;
	    }
	    this.fireEvent('load', this, this.data, isLoaded);
	},
	failure: function(response) {
	    // Reading local files returns status 0
	    if (response.status == 0) {
	        return this.success(response);
	    }
        var isLoaded = false;
	    this.fireEvent('load', this, this.data, isLoaded);
	},
	getTimeExtent: function() {
	    return [this.getStart(), this.getEnd()];
	},
	/**
	 * Generate new axis.
	 * Uses configured format.
	 */
	getAxis: function() {
	    return d3.svg.axis().tickFormat(this.getFormat()).tickSubdivide(2);
	},
	/**
	 *
	 * @param {Date} current
	 * @param {Number} before
	 * @param {Number} after
	 */
	getSurrounding: function(current, before, after) {
	    return [];
	},
	get: function(index) {
	  return this.data[index];
	},
	closestIndex: function(newdate) {
        // lookup index of timepoint closest to current
        var bisectDate = d3.bisector(function(d) { return d.date_time;}).left;
        var index = bisectDate(this.data, newdate, 1);
        return index;
	},
	eachRange: function(start, end, callback, scope) {
	    if (!scope) {
	        scope = this;
	    }
	    var startId = this.closestIndex(start);
	    var endId = this.closestIndex(end);
	    for (var i = startId, len = endId; i <= len; i++) {
	        callback.call(scope, this.data[i], i);
	    }
	},
	isEmpty: function() {
	    return this.data.length == 0;
	}
});<|endoftext|>


<commit_before>const moment = require('moment');

function getTimesFromString(timesString) {
  const splitTimes = timesString.split('-');
  const opens = splitTimes[0];
  const closes = splitTimes[1];
  return { closes, opens };
}

function getOpeningTimes(asOpeningTimes) {
  const weekdays = moment.weekdays();
  const openingTimes = {
    alterations: {},
    general: {},
  };

  // Get opening times for each day of week
  weekdays.forEach((weekday) => {
    const sessions = asOpeningTimes
      .filter(ot => ot.OpeningTimeType === 'General' && ot.Weekday === weekday)
      .map(wot => getTimesFromString(wot.Times));
    openingTimes.general[weekday.toLowerCase()] = sessions;
  });

  // Get opening times for alterations/additional days
  asOpeningTimes
    .filter(ot => ot.OpeningTimeType === 'General' && ot.AdditionalOpeningDate)
    .forEach((aot) => {
      const aMoment = moment(aot.AdditionalOpeningDate, 'MMM DD YYYY');
      if (!openingTimes.alterations[aMoment.format('YYYY-MM-DD')]) {
        // initialise alterations for date
        openingTimes.alterations[aMoment.format('YYYY-MM-DD')] = [];
      }
      if (aot.IsOpen) {
        openingTimes.alterations[aMoment.format('YYYY-MM-DD')].push(getTimesFromString(aot.Times));
      }
    });

  return openingTimes;
}

module.exports = getOpeningTimes;
<commit_msg>:pill: Extract to method for legibility and SRP<commit_after>const moment = require('moment');

function getTimesFromString(timesString) {
  const splitTimes = timesString.split('-');
  const opens = splitTimes[0];
  const closes = splitTimes[1];
  return { closes, opens };
}

function getGeneralOpeningTimes(asOpeningTimes) {
  const weekdays = moment.weekdays();
  const general = {};
  weekdays.forEach((weekday) => {
    const sessions = asOpeningTimes
      .filter(ot => ot.OpeningTimeType === 'General' && ot.Weekday === weekday)
      .map(wot => getTimesFromString(wot.Times));
    general[weekday.toLowerCase()] = sessions;
  });
  return general;
}

function getAlterationsOpeningTimes(asOpeningTimes) {
  const alterations = {};
  asOpeningTimes
    .filter(ot => ot.OpeningTimeType === 'General' && ot.AdditionalOpeningDate)
    .forEach((aot) => {
      const aMoment = moment(aot.AdditionalOpeningDate, 'MMM DD YYYY');
      if (!alterations[aMoment.format('YYYY-MM-DD')]) {
        // initialise alterations for date
        alterations[aMoment.format('YYYY-MM-DD')] = [];
      }
      if (aot.IsOpen) {
        alterations[aMoment.format('YYYY-MM-DD')].push(getTimesFromString(aot.Times));
      }
    });
  return alterations;
}

function getOpeningTimes(asOpeningTimes) {
  return {
    alterations: getAlterationsOpeningTimes(asOpeningTimes),
    general: getGeneralOpeningTimes(asOpeningTimes),
  };
}

module.exports = getOpeningTimes;
<|endoftext|>


<commit_before>define(['class', 'etc/event-emitter', 'etc/tag', 'game/game-object', 'game/game-object-array', 'utils', 'collections/bag'], function (Class, EventEmitter, Tag, GameObject, GameObjectArray, Utils, Bag) {
    var GameObjectId = GameObject.id;

    function addWithOrWithoutName(target, name, object) {
        if (object === undefined) { //no name
            object = name;
            target.push(object);
        } else {
            if (target[name]) {
                throw 'Element "' + name + '" already added.';
            }
            target[name] = object;
        }
        return object;
    }

    function remove(target, name) {
        if (typeof  name === 'string') { //by name
            if (!target[name]) {
                throw 'Element "' + name + '" does not exist.';
            }
            delete target[name];
        } else { //by object
            if (!Utils.removeFromArray(target, name)) {
                throw 'Element does not exist.';
            }
        }
    }

    return Class('Layer', [EventEmitter, Tag.TagContainer], {
        init: function (opts) {
            opts = opts || {};
            this.width = opts.widht || 400;
            this.height = opts.height || 300;
            this.background = opts.background || null;
            this.backgroundColor = opts.backgroundColor || null;

            this._classes = {};
            this._activeClasses = {};

            this.instanceNumber = 0;
            this._layers = [];
            this._systems = [];

            this._parentLayer = null;
        },
        add: function (instance) {
            var i, classData, parentId, clazz = instance.getClass(), classId = clazz.id, allParent;
            if (!clazz.allParentId[GameObjectId]) { //TODO remove if no check
                throw 'The instance must be a descendant of Grape.GameObject.'; //TODO .is() function
            }
            instance.addToTagContainer(this);
            instance._layer = this;

            this.emit('instanceAdded', instance);

            if (!(classData = this._classes[classId])) { //instance class is not registered yet
                this._activeClasses[classId] = this._classes[classId] = classData = {
                    id: classId,
                    clazz: clazz,
                    instances: new Bag(),
                    instanceNumber: 1,
                    descendants: []
                };
                allParent = clazz.allParent;
                for (i = allParent.length; --i >= 0;) {
                    parentId = allParent[i].id;
                    if (!this._classes[parentId]) { //parent type is not registered yet
                        this._classes[parentId] = {
                            id: parentId,
                            clazz: allParent[i],
                            instances: new Bag(),
                            instanceNumber: 0,
                            descendants: [classData]
                        };
                    } else {
                        this._classes[parentId].descendants.push(classData);
                    }
                }
            } else {
                this._activeClasses[classId] = classData; //set class active
                classData.instanceNumber++;
            }
            this.instanceNumber++;
            instance._index = classData.instances.add(instance) - 1; //store the index in the bag for efficient remove
            instance.emit('add', this);
            return instance;
        },
        remove: function (instance) {
            var clazz = instance.getClass(), classId = clazz.id, typeData = this._classes[classId], instances = this._activeClasses[classId].instances, idx = instance._index, moved = instances.remove(idx);
            if (moved) {
                moved._index = idx; //update the index of the item moved to the position of the removed item
            }
            typeData.instanceNumber -= 1;
            if (typeData.instanceNumber === 0) {
                delete this._activeClasses[classId];
            }
            this.instanceNumber--;
            instance.removeFromTagContainer();
            instance.emit('remove');
        },
        getByTag: function (/*tag1, tag2, ...*/) {
            return this.parent(Tag.TagContainer, 'get').apply(this, arguments);
        },
        getClasses: function (parent) { //TODO private?
            var result = {}, classData = this._classes[parent.id], i, desc;
            if (classData) {
                if (this._activeClasses[classData.id]) {
                    result[classData.id] = classData;
                }
                for (i = 0; i < classData.descendants.length; i++) {
                    desc = classData.descendants[i];
                    if (this._activeClasses[desc.id]) {
                        result[desc.id] = desc;
                    }
                }
            }
            return result;
        },
        get: function (classes, descendants) {
            var i, j, instances,
                classData, classDataArr = [], addedClasses = {}, result = new GameObjectArray();

            function addIfNotAdded(cd) {
                if (addedClasses[cd.id]) {
                    return;
                }
                addedClasses[cd.id] = true;
                instances = cd.instances;
                for (i = instances.length; i-- > 0;) { //todo use this loop if possible
                    result.push(instances[i]);
                }
            }

            if (classes) {
                if (!Utils.isArray(classes)) {
                    classes = [classes];
                }
                for (i = 0; i < classes.length; i++) {
                    classData = this._classes[classes[i].id];
                    if (classData) {
                        classDataArr.push(classData);
                    }
                }
            } else {
                /*jshint -W088 */ //due an issue: https://github.com/jshint/jshint/issues/1016
                for (i in this._activeClasses) {
                    classDataArr.push(this._activeClasses[i]);
                }
            }
            for (i = 0; i < classDataArr.length; i++) {
                classData = classDataArr[i];
                addIfNotAdded(classData);
                if (descendants) {
                    descendants = classData.descendants;
                    for (j = 0; j < descendants.length; j++) {
                        addIfNotAdded(descendants[j]);
                    }
                }
            }
            return result;
        },
        addLayer: function (name, layer) {
            layer = addWithOrWithoutName(this._layers, name, layer);
            layer._parentLayer = this;
            /*TODO needed? if (this._started) {
             layer.emit('start');
             }*/
        },
        addSystem: function (name, system) { //todo add without name
            system = addWithOrWithoutName(this._systems, name, system);
            system._layer = this;
            if (this._started) {
                system.emit('start');
            }
        },
        addView: function (name, view) { //todo create with view class if config object is given
            this.addSystem(name, view);
        },
        removeLayer: function (name) { //todo stop event
            remove(this._layers, name);
        },
        removeSystem: function (system) {
            system = remove(this._systems, system);
            if (this._started) {
                system.emit('stop');
            }
        },
        removeView: function (name) {
            this.removeSystem(name);
        },
        getSystem: function (name) {
            return this._systems[name];
        },
        getScene: function () {
            if (this._parentLayer) {
                return this._parentLayer.getScene();
            }
            return this;
        },
        getGame: function () {
            return this.getScene()._game;
        },
        'event start': function () {
            this._started = true;
        },
        'event render': function (ctx) {
            if (this.backgroundColor) { //TODO create function
                ctx.fillStyle = this.backgroundColor;
                ctx.fillRect(0, 0, this.width, this.height);
            }
            if (this.background) {
                var pattern = ctx.createPattern(this.background.img, 'repeat'); //TODO create function
                ctx.rect(0, 0, this.width, this.height);
                ctx.fillStyle = pattern;
                ctx.fill();
                ctx.fillStyle = '';
            }
        },
        'event any': function (event, payload) {
            var i;
            for (i in this._layers) {
                this._layers[i].emit(event, payload);
            }
            for (i in this._views) {
                this._views[i].emit(event, payload);
            }
            for (i in this._systems) {
                this._systems[i].emit(event, payload);
            }
        }
    });
});<commit_msg>layer getByTag fix<commit_after>define(['class', 'etc/event-emitter', 'etc/tag', 'game/game-object', 'game/game-object-array', 'utils', 'collections/bag'], function (Class, EventEmitter, Tag, GameObject, GameObjectArray, Utils, Bag) {
    var GameObjectId = GameObject.id;

    function addWithOrWithoutName(target, name, object) {
        if (object === undefined) { //no name
            object = name;
            target.push(object);
        } else {
            if (target[name]) {
                throw 'Element "' + name + '" already added.';
            }
            target[name] = object;
        }
        return object;
    }

    function remove(target, name) {
        if (typeof  name === 'string') { //by name
            if (!target[name]) {
                throw 'Element "' + name + '" does not exist.';
            }
            delete target[name];
        } else { //by object
            if (!Utils.removeFromArray(target, name)) {
                throw 'Element does not exist.';
            }
        }
    }

    return Class('Layer', [EventEmitter, Tag.TagContainer], {
        init: function (opts) {
            opts = opts || {};
            this.width = opts.widht || 400;
            this.height = opts.height || 300;
            this.background = opts.background || null;
            this.backgroundColor = opts.backgroundColor || null;

            this._classes = {};
            this._activeClasses = {};

            this.instanceNumber = 0;
            this._layers = [];
            this._systems = [];

            this._parentLayer = null;
        },
        add: function (instance) {
            var i, classData, parentId, clazz = instance.getClass(), classId = clazz.id, allParent;
            if (!clazz.allParentId[GameObjectId]) { //TODO remove if no check
                throw 'The instance must be a descendant of Grape.GameObject.'; //TODO .is() function
            }
            instance.addToTagContainer(this);
            instance._layer = this;

            this.emit('instanceAdded', instance);

            if (!(classData = this._classes[classId])) { //instance class is not registered yet
                this._activeClasses[classId] = this._classes[classId] = classData = {
                    id: classId,
                    clazz: clazz,
                    instances: new Bag(),
                    instanceNumber: 1,
                    descendants: []
                };
                allParent = clazz.allParent;
                for (i = allParent.length; --i >= 0;) {
                    parentId = allParent[i].id;
                    if (!this._classes[parentId]) { //parent type is not registered yet
                        this._classes[parentId] = {
                            id: parentId,
                            clazz: allParent[i],
                            instances: new Bag(),
                            instanceNumber: 0,
                            descendants: [classData]
                        };
                    } else {
                        this._classes[parentId].descendants.push(classData);
                    }
                }
            } else {
                this._activeClasses[classId] = classData; //set class active
                classData.instanceNumber++;
            }
            this.instanceNumber++;
            instance._index = classData.instances.add(instance) - 1; //store the index in the bag for efficient remove
            instance.emit('add', this);
            return instance;
        },
        remove: function (instance) {
            var clazz = instance.getClass(), classId = clazz.id, typeData = this._classes[classId], instances = this._activeClasses[classId].instances, idx = instance._index, moved = instances.remove(idx);
            if (moved) {
                moved._index = idx; //update the index of the item moved to the position of the removed item
            }
            typeData.instanceNumber -= 1;
            if (typeData.instanceNumber === 0) {
                delete this._activeClasses[classId];
            }
            this.instanceNumber--;
            instance.removeFromTagContainer();
            instance.emit('remove');
        },
        getByTag: function (/*tag1, tag2, ...*/) {
            return this.parent(Tag.TagContainer, 'get').apply(this, arguments);
        },
        'override createResultContainer': function () {
            return new GameObjectArray();
        },
        getClasses: function (parent) { //TODO private?
            var result = {}, classData = this._classes[parent.id], i, desc;
            if (classData) {
                if (this._activeClasses[classData.id]) {
                    result[classData.id] = classData;
                }
                for (i = 0; i < classData.descendants.length; i++) {
                    desc = classData.descendants[i];
                    if (this._activeClasses[desc.id]) {
                        result[desc.id] = desc;
                    }
                }
            }
            return result;
        },
        get: function (classes, descendants) {
            var i, j, instances,
                classData, classDataArr = [], addedClasses = {}, result = new GameObjectArray();

            function addIfNotAdded(cd) {
                if (addedClasses[cd.id]) {
                    return;
                }
                addedClasses[cd.id] = true;
                instances = cd.instances;
                for (i = instances.length; i-- > 0;) { //todo use this loop if possible
                    result.push(instances[i]);
                }
            }

            if (classes) {
                if (!Utils.isArray(classes)) {
                    classes = [classes];
                }
                for (i = 0; i < classes.length; i++) {
                    classData = this._classes[classes[i].id];
                    if (classData) {
                        classDataArr.push(classData);
                    }
                }
            } else {
                /*jshint -W088 */ //due an issue: https://github.com/jshint/jshint/issues/1016
                for (i in this._activeClasses) {
                    classDataArr.push(this._activeClasses[i]);
                }
            }
            for (i = 0; i < classDataArr.length; i++) {
                classData = classDataArr[i];
                addIfNotAdded(classData);
                if (descendants) {
                    descendants = classData.descendants;
                    for (j = 0; j < descendants.length; j++) {
                        addIfNotAdded(descendants[j]);
                    }
                }
            }
            return result;
        },
        addLayer: function (name, layer) {
            layer = addWithOrWithoutName(this._layers, name, layer);
            layer._parentLayer = this;
            /*TODO needed? if (this._started) {
             layer.emit('start');
             }*/
        },
        addSystem: function (name, system) { //todo add without name
            system = addWithOrWithoutName(this._systems, name, system);
            system._layer = this;
            if (this._started) {
                system.emit('start');
            }
        },
        addView: function (name, view) { //todo create with view class if config object is given
            this.addSystem(name, view);
        },
        removeLayer: function (name) { //todo stop event
            remove(this._layers, name);
        },
        removeSystem: function (system) {
            system = remove(this._systems, system);
            if (this._started) {
                system.emit('stop');
            }
        },
        removeView: function (name) {
            this.removeSystem(name);
        },
        getSystem: function (name) {
            return this._systems[name];
        },
        getScene: function () {
            if (this._parentLayer) {
                return this._parentLayer.getScene();
            }
            return this;
        },
        getGame: function () {
            return this.getScene()._game;
        },
        'event start': function () {
            this._started = true;
        },
        'event render': function (ctx) {
            if (this.backgroundColor) { //TODO create function
                ctx.fillStyle = this.backgroundColor;
                ctx.fillRect(0, 0, this.width, this.height);
            }
            if (this.background) {
                var pattern = ctx.createPattern(this.background.img, 'repeat'); //TODO create function
                ctx.rect(0, 0, this.width, this.height);
                ctx.fillStyle = pattern;
                ctx.fill();
                ctx.fillStyle = '';
            }
        },
        'event any': function (event, payload) {
            var i;
            for (i in this._layers) {
                this._layers[i].emit(event, payload);
            }
            for (i in this._views) {
                this._views[i].emit(event, payload);
            }
            for (i in this._systems) {
                this._systems[i].emit(event, payload);
            }
        }
    });
});<|endoftext|>


<commit_before>const hapi = require('hapi');
const { graphqlHapi, graphiqlHapi } = require('graphql-server-hapi');

const rootSchema = require('./graphql/schema');
const dataLoaders = require('./graphql/data');
const config = require('../../config');

function configureConnection(server) {
  server.connection({
    host: config.HOST,
    port: config.PORT
  });
}

function registerGraphql(server) {
  server.register({
    register: graphqlHapi,
    options: {
      path: '/graphql/',
      graphqlOptions: {
        schema: rootSchema,
        context: { dataLoaders }
      },
      route: {
        cors: true
      }
    }
  });
}

function registerGraphiql(server) {
  server.register({
    register: graphiqlHapi,
    options: {
      path: '/graphiql/',
      graphiqlOptions: {
        endpointURL: '/graphql/'
      }
    }
  });
}

function start() {
  const server = new hapi.Server();
  configureConnection(server);
  registerGraphql(server);
  registerGraphiql(server);

  server.start(err => {
    if (err) {
      throw err;
    }
    console.log(`Server running at: ${server.info.uri}`);
  });
}

module.exports = { start };
<commit_msg>Only configure port and host in development environment<commit_after>const hapi = require('hapi');
const { graphqlHapi, graphiqlHapi } = require('graphql-server-hapi');

const rootSchema = require('./graphql/schema');
const dataLoaders = require('./graphql/data');
const config = require('../../config');

function configureConnection(server) {
  if (config.IS_DEV) {
    server.connection({
      host: config.HOST,
      port: config.PORT
    });
  }
}

function registerGraphql(server) {
  server.register({
    register: graphqlHapi,
    options: {
      path: '/graphql/',
      graphqlOptions: {
        schema: rootSchema,
        context: { dataLoaders }
      },
      route: {
        cors: true
      }
    }
  });
}

function registerGraphiql(server) {
  server.register({
    register: graphiqlHapi,
    options: {
      path: '/graphiql/',
      graphiqlOptions: {
        endpointURL: '/graphql/'
      }
    }
  });
}

function start() {
  const server = new hapi.Server();
  configureConnection(server);
  registerGraphql(server);
  registerGraphiql(server);

  server.start(err => {
    if (err) {
      throw err;
    }
    console.log(`Server running at: ${server.info.uri}`);
  });
}

module.exports = { start };
<|endoftext|>


<commit_before>import { createStore, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import _debounce from 'lodash/debounce';

import reducers from '../reducers';
import AsanaEventPoller from './AsanaEventPoller';

const middlewares = [thunk];

if (process.env.NODE_ENV === 'development') {
  const loggerMiddleware = createLogger({
    level: 'info',
    collapsed: true
  });
  middlewares.push(loggerMiddleware);
}

export default function configureStore() {
  const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
  const store = createStoreWithMiddleware(reducers);

  store.subscribe(() => {
    /**
     * Persist state.
     * There are fancy ways to store, like https://www.npmjs.com/package/redux-storage
     * but tbh, just a store listener on one end, and an initialState lookup
     * is all we need :)
     *
     * This can get pretty thrashy here as we're subscribing to every state
     * update, hence we use _debounce.
     */
    function persistBoardsState (currentState) {
      localStorage.setItem('boards', JSON.stringify(currentState.boards));
    }

    let currentState = store.getState();
    if (currentState.auth.isAsanaAuthed) {
      _debounce(persistBoardsState, 300);
      persistBoardsState(currentState);
    }

  })

  /**
   * Asana Events Polling
   */
  // Reference copy of state before we update it.
  let stateTracker = store.getState();

  const poller = AsanaEventPoller(store);
  poller.init(stateTracker.entities.projects.conditions.currentId);

  if (stateTracker.entities.projects.conditions.currentId !== null) {
    poller.start();
  }

  store.subscribe(() => {
    // Any time there's a state update, we want to see if the projectId has changed.
    // So refer check against reference state.
    const currentState = store.getState();
    const currentProjectId = currentState.entities.projects.conditions.currentId;

    if (currentProjectId !== stateTracker.entities.projects.conditions.currentId) {
      poller.changeProject(currentProjectId);

      // Update the reference state for next time!
      stateTracker = currentState;
    }
  });

  return store;
}
<commit_msg>Stopped unnecessary polling (#150)<commit_after>import { createStore, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
import _debounce from 'lodash/debounce';

import reducers from '../reducers';
import AsanaEventPoller from './AsanaEventPoller';

const middlewares = [thunk];

if (process.env.NODE_ENV === 'development') {
  const loggerMiddleware = createLogger({
    level: 'info',
    collapsed: true
  });
  middlewares.push(loggerMiddleware);
}

export default function configureStore() {
  const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
  const store = createStoreWithMiddleware(reducers);

  store.subscribe(() => {
    /**
     * Persist state.
     * There are fancy ways to store, like https://www.npmjs.com/package/redux-storage
     * but tbh, just a store listener on one end, and an initialState lookup
     * is all we need :)
     *
     * This can get pretty thrashy here as we're subscribing to every state
     * update, hence we use _debounce.
     */
    function persistBoardsState (currentState) {
      localStorage.setItem('boards', JSON.stringify(currentState.boards));
    }

    let currentState = store.getState();
    if (currentState.auth.isAsanaAuthed) {
      _debounce(persistBoardsState, 300);
      persistBoardsState(currentState);
    }

  })

  /**
   * Asana Events Polling
   */
  // Reference copy of state before we update it.
  let stateTracker = store.getState();

  const poller = AsanaEventPoller(store);
  poller.init(stateTracker.entities.projects.conditions.currentId);

  if (typeof stateTracker.entities.projects.conditions.currentId !== 'undefined') {
    poller.start();
  }

  store.subscribe(() => {
    // Any time there's a state update, we want to see if the projectId has changed.
    // So refer check against reference state.
    const currentState = store.getState();
    const currentProjectId = currentState.entities.projects.conditions.currentId;

    if (currentProjectId !== stateTracker.entities.projects.conditions.currentId) {
      poller.changeProject(currentProjectId);

      // Update the reference state for next time!
      stateTracker = currentState;
    }
  });

  return store;
}
<|endoftext|>


<commit_before>var GAME_WIDTH = 640;
var GAME_HEIGHT = 360;

var enemies = [
  {
    x: 100, //x coordinate
    y: 100, //y coordinate
    speedY: 1, //speed in Y
    speedX: 2,
    w: 40, //width
    h: 40 //heght
  },
  {
    x: 260,
    y: 100,
    speedY: 2,
    speedX: 1,
    w: 40,
    h: 40
  },
  {
    x: 380,
    y: 100,
    speedY: 2,
    speedX: 4,
    w: 40,
    h: 40
  }
];

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

var update = function() {

  var i = 0;
  var n = enemies.length;

  enemies.forEach(function(element, index){
    element.y += element.speedY;
    element.x += element.speedX;

    if(element.y <= 10) {
      element.y = 10;
      element.speedY *= -1;
    }
    else if(element.y >= GAME_HEIGHT - 50) {
      element.y = GAME_HEIGHT - 50;
      element.speedY *= -1;
    }

    if(element.x <= 10) {
      element.x = 10;
      element.speedX *= -1;
    }
    else if(element.x >= GAME_WIDTH - 50) {
      element.x = GAME_WIDTH - 50;
      element.speedX *= -1;
    }
  });
};

var draw = function() {
  ctx.clearRect(0,0,GAME_WIDTH,GAME_HEIGHT);
  ctx.fillStyle = "orange";

  enemies.forEach(function(element, index){
    ctx.fillRect(element.x, element.y, element.w, element.h);
  });
};

var step = function() {

  update();
  draw();

  window.requestAnimationFrame(step);
};

step();<commit_msg>add event listener for load event<commit_after>window.addEventListener('load', function(){

  var GAME_WIDTH = 640;
  var GAME_HEIGHT = 360;

  var enemies = [
    {
      x: 100, //x coordinate
      y: 100, //y coordinate
      speedY: 1, //speed in Y
      speedX: 2,
      w: 40, //width
      h: 40 //heght
    },
    {
      x: 260,
      y: 100,
      speedY: 2,
      speedX: 1,
      w: 40,
      h: 40
    },
    {
      x: 380,
      y: 100,
      speedY: 2,
      speedX: 4,
      w: 40,
      h: 40
    }
  ];

  var canvas = document.getElementById("myCanvas");
  var ctx = canvas.getContext("2d");

  var update = function() {

    var i = 0;
    var n = enemies.length;

    enemies.forEach(function(element, index){
      element.y += element.speedY;
      element.x += element.speedX;

      if(element.y <= 10) {
        element.y = 10;
        element.speedY *= -1;
      }
      else if(element.y >= GAME_HEIGHT - 50) {
        element.y = GAME_HEIGHT - 50;
        element.speedY *= -1;
      }

      if(element.x <= 10) {
        element.x = 10;
        element.speedX *= -1;
      }
      else if(element.x >= GAME_WIDTH - 50) {
        element.x = GAME_WIDTH - 50;
        element.speedX *= -1;
      }
    });
  };

  var draw = function() {
    ctx.clearRect(0,0,GAME_WIDTH,GAME_HEIGHT);
    ctx.fillStyle = "orange";

    enemies.forEach(function(element, index){
      ctx.fillRect(element.x, element.y, element.w, element.h);
    });
  };

  var step = function() {

    update();
    draw();

    window.requestAnimationFrame(step);
  };

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


<commit_before>var RestDataProvider = function(){

    this.requestParams = {
        'get': {
            method: 'get',
            origin: null, // http://abs.com
            path: '',
            data: {}
        },

        'set':{
            method: 'post',
            origin: null,
            path: '',
            data: {}
        },

        'delete':{
            method: 'delete',
            origin: null,
            path: '',
            data: {}
        }
    };

};

_.extend(RestDataProvider.prototype, {

    getOrigin: function(type){
        return this.requestParams[type].origin;
    },

    setOrigin: function(type, newOrigin){
        this.requestParams[type].origin = newOrigin;
    },

    getPath: function(type){
        return this.requestParams[type].path;
    },

    setPath: function(type, path){
        this.requestParams[type].path = path;
    },

    getData: function(type){
        return this.requestParams[type].data;
    },

    setData: function(type, data){
        this.requestParams[type].data = data;
    },

    getMethod: function(type){
        return this.requestParams[type].method;
    },

    setMethod: function(type, queryMethod){
        this.requestParams[type].method = queryMethod;
    },

    send: function(type, successHandler, errorHandler){
        var params = this.requestParams[type];

        var urlString = params.origin + params.path;
        var queryString;
        var requestId = Math.round((Math.random() * 100000));
        var requestParams;

        var filesInData = this.extractFilesFromData(params.data);

        if( _.size(filesInData.files) == 0){

            requestParams = {
                type: params.method,
                xhrFields: {
                    withCredentials: true
                },
                url: urlString,
                success: function(data){
                    successHandler({
                        requestId: requestId,
                        data: data
                    });
                },
                error: function(data){
                    if (typeof errorHandler !== 'function') {
                        //Unhandled error
                        InfinniUI.global.logger.error(data);
                        return;
                    }
                    errorHandler({
                        requestId: requestId,
                        data: data
                    });
                }
            };

            if(params.method.toLowerCase() != 'get'){
                requestParams.contentType = 'application/json';
                requestParams.data = JSON.stringify( params.data );
            }else{
                if(_.size(params.data) > 0){
                    requestParams.url = requestParams.url + '?' + stringUtils.joinDataForQuery(params.data);
                }
            }

        }else{

            var formData = new FormData();
            formData.append('document', JSON.stringify( filesInData.dataWithoutFiles ));

            for(var k in filesInData.files){
                formData.append(k, filesInData.files[k]);
            }

            requestParams = {
                type: params.method,
                url: urlString,
                xhrFields: {
                    withCredentials: true
                },
                data: formData,
                cache: false,
                contentType: false,
                processData: false,
                success: function(data){
                    successHandler({
                        requestId: requestId,
                        data: data
                    });
                },
                error: function(data){
                    errorHandler({
                        requestId: requestId,
                        data: data
                    });
                }
            };
        }

        $.ajax(requestParams);

        return requestId;
    },

    getItems: function(successHandler, errorHandler){
        return this.send('get', successHandler, errorHandler);
    },

    saveItem: function(item, successHandler, errorHandler){
        this.requestParams['set'].data = item;
        return this.send('set', successHandler, errorHandler);
    },

    deleteItem: function(item, successHandler, errorHandler){
        return this.send('delete', successHandler, errorHandler);
    },

    createItem: function (resultCallback, idProperty) {
       var that = this;
        setTimeout( function(){
            resultCallback(that.createLocalItem(idProperty));
        }, 10);
    },

    createLocalItem: function (idProperty) {
        var result = {};

        result[idProperty] = this._generateLocalId();

        return result;
    },

    _generateLocalId: function(){
        return guid();
    },

    extractFilesFromData: function (data) {

        var files = Object.create(null);
        var dataWithoutFiles = extractFilesFromNode(data, []);

        return {
            dataWithoutFiles: dataWithoutFiles,
            files: files
        };

        function extractFilesFromNode (node, path) {
            var value, result = Array.isArray(node) ? [] : {}, currentPath;
            for (var i in node) {
                if (!node.hasOwnProperty(i)) {
                    continue;
                }

                currentPath = path.slice();
                currentPath.push(i);
                value = node[i];
                if (value !== null && typeof (value) === 'object') {
                    if (value.constructor === Date) {
                        result[i] = value
                    } else if (value.constructor === Object || value.constructor === Array)  {
                        //Plain object
                        result[i] = extractFilesFromNode(value, currentPath);
                    } else {
                        //Object instance
                        files[currentPath.join('.')] = value;
                        continue;
                    }
                } else {
                    result[i] = value;
                }

            }

            return result;
        }
    }

});

window.InfinniUI.Providers.RestDataProvider = RestDataProvider;<commit_msg>fix sending file array in restDataProvider<commit_after>var RestDataProvider = function(){

    this.requestParams = {
        'get': {
            method: 'get',
            origin: null, // http://abs.com
            path: '',
            data: {}
        },

        'set':{
            method: 'post',
            origin: null,
            path: '',
            data: {}
        },

        'delete':{
            method: 'delete',
            origin: null,
            path: '',
            data: {}
        }
    };

};

_.extend(RestDataProvider.prototype, {

    getOrigin: function(type){
        return this.requestParams[type].origin;
    },

    setOrigin: function(type, newOrigin){
        this.requestParams[type].origin = newOrigin;
    },

    getPath: function(type){
        return this.requestParams[type].path;
    },

    setPath: function(type, path){
        this.requestParams[type].path = path;
    },

    getData: function(type){
        return this.requestParams[type].data;
    },

    setData: function(type, data){
        this.requestParams[type].data = data;
    },

    getMethod: function(type){
        return this.requestParams[type].method;
    },

    setMethod: function(type, queryMethod){
        this.requestParams[type].method = queryMethod;
    },

    send: function(type, successHandler, errorHandler){
        var params = this.requestParams[type];

        var urlString = params.origin + params.path;
        var queryString;
        var requestId = Math.round((Math.random() * 100000));
        var requestParams;

        var filesInData = this.extractFilesFromData(params.data);

        if( _.size(filesInData.files) == 0){

            requestParams = {
                type: params.method,
                xhrFields: {
                    withCredentials: true
                },
                url: urlString,
                success: function(data){
                    successHandler({
                        requestId: requestId,
                        data: data
                    });
                },
                error: function(data){
                    if (typeof errorHandler !== 'function') {
                        //Unhandled error
                        InfinniUI.global.logger.error(data);
                        return;
                    }
                    errorHandler({
                        requestId: requestId,
                        data: data
                    });
                }
            };

            if(params.method.toLowerCase() != 'get'){
                requestParams.contentType = 'application/json';
                requestParams.data = JSON.stringify( params.data );
            }else{
                if(_.size(params.data) > 0){
                    requestParams.url = requestParams.url + '?' + stringUtils.joinDataForQuery(params.data);
                }
            }

        }else{

            var formData = new FormData();
            formData.append('document', JSON.stringify( filesInData.dataWithoutFiles ));

            for(var k in filesInData.files){
                formData.append(k, filesInData.files[k]);
            }

            requestParams = {
                type: params.method,
                url: urlString,
                xhrFields: {
                    withCredentials: true
                },
                data: formData,
                cache: false,
                contentType: false,
                processData: false,
                success: function(data){
                    successHandler({
                        requestId: requestId,
                        data: data
                    });
                },
                error: function(data){
                    errorHandler({
                        requestId: requestId,
                        data: data
                    });
                }
            };
        }

        $.ajax(requestParams);

        return requestId;
    },

    getItems: function(successHandler, errorHandler){
        return this.send('get', successHandler, errorHandler);
    },

    saveItem: function(item, successHandler, errorHandler){
        this.requestParams['set'].data = item;
        return this.send('set', successHandler, errorHandler);
    },

    deleteItem: function(item, successHandler, errorHandler){
        return this.send('delete', successHandler, errorHandler);
    },

    createItem: function (resultCallback, idProperty) {
       var that = this;
        setTimeout( function(){
            resultCallback(that.createLocalItem(idProperty));
        }, 10);
    },

    createLocalItem: function (idProperty) {
        var result = {};

        result[idProperty] = this._generateLocalId();

        return result;
    },

    _generateLocalId: function(){
        return guid();
    },

    extractFilesFromData: function (data) {

        var files = Object.create(null);
        var dataWithoutFiles = extractFilesFromNode(data, []);

        return {
            dataWithoutFiles: dataWithoutFiles,
            files: files
        };

        function extractFilesFromNode (node, path) {
            var value, result = Array.isArray(node) ? [] : {}, currentPath;
            for (var i in node) {
                if (!node.hasOwnProperty(i)) {
                    continue;
                }

                currentPath = path.slice();
                currentPath.push(i);
                value = node[i];
                if (value !== null && typeof (value) === 'object') {
                    if (value.constructor === Date) {
                        result[i] = value
                    } else if (value.constructor === Object || value.constructor === Array)  {
                        //Plain object
                        result[i] = extractFilesFromNode(value, currentPath);
                    } else {
                        //Object instance
                        result[i] = null;
                        files[currentPath.join('.')] = value;
                        continue;
                    }
                } else {
                    result[i] = value;
                }

            }

            return result;
        }
    }

});

window.InfinniUI.Providers.RestDataProvider = RestDataProvider;<|endoftext|>


<commit_before>var e = module.exports = {};
const moment = require('moment');
e.init = () => {
    e.category = bu.CommandType.GENERAL;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'poll <question> [duration] [emoji]...';
e.info = 'Creates a poll for the given question and duration. If no duration is given, defaults to 60 seconds. If emojis are given, they will be used as options for the poll.';
e.longinfo = `<p>Creates a poll for the given question and duration. If emojis are given, they will be used as options for the poll.`;

e.execute = async function (msg, words) {
    let choices = ['👍', '👎'];
    if (words.length >= 2) {
        if (!words[2]) words[2] = 60;
        if (words.length > 3) {
            choices = words.slice(3);
        }
        let time = parseInt(words[2]);
        if (isNaN(time)) {
            bu.send(msg, 'Invalid duration!');
            return;
        }
        time *= 1000;
        let message = `**__${words[1]}__**\n\nThe poll will expire ${moment.duration(time).humanize(true)}.\n\nVote here:`;

        let msg2 = await bu.send(msg, message);
        for (let choice of choices) {
            try {
                await bot.addMessageReaction(msg2.channel.id, msg2.id, encodeURIComponent(choice));
            } catch (err) {
                //NO-OP
                logger.error(err);
            }
        }
        setTimeout(async function () {
            let msg3 = await bot.getMessage(msg2.channel.id, msg2.id);
            let reactions = [];
            for (let key in msg3.reactions) {
                msg3.reactions[key].emoji = key;
                reactions.push(msg3.reactions[key]);
            }
            reactions.sort((a, b) => {
                return b.count - a.count;
            });
            let totalVotes = 0;
            for (let reaction of reactions) {
                totalVotes += reaction.count - 1;
            }
            let max = reactions[0].count;
            let winners = reactions.filter(r => r.count == max);
            if (winners.length > 1) {
                bu.send(msg, `**__${words[1]}__**\nThe results are in! It was a tie between these choices, at **${max - 1}** vote${max == 1 ? '' : 's'} each:
${winners.map(r => r.emoji).join('')}

A total of **${totalVotes}** were collected!`);
            } else {
                bu.send(msg, `**__${words[1]}__**\nThe results are in! At **${max - 1}** vote${max == 1 ? '' : 's'}, the winner is:
${winners[0].emoji}

A total of **${totalVotes}** were collected!`);
            }
        }, time);
    }
};<commit_msg>more fixes<commit_after>var e = module.exports = {};
const moment = require('moment');
e.init = () => {
    e.category = bu.CommandType.GENERAL;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'poll <question> [duration] [emoji]...';
e.info = 'Creates a poll for the given question and duration. If no duration is given, defaults to 60 seconds. If emojis are given, they will be used as options for the poll.';
e.longinfo = `<p>Creates a poll for the given question and duration. If emojis are given, they will be used as options for the poll.`;

e.execute = async function (msg, words) {
    let choices = ['👍', '👎'];
    if (words.length >= 2) {
        if (!words[2]) words[2] = 60;
        if (words.length > 3) {
            choices = words.slice(3);
        }
        let time = parseInt(words[2]);
        if (isNaN(time)) {
            bu.send(msg, 'Invalid duration!');
            return;
        }
        time *= 1000;
        let message = `**__${words[1]}__**\n\nThe poll will expire ${moment.duration(time).humanize(true)}.\n\nVote here:`;

        let msg2 = await bu.send(msg, message);
        for (let choice of choices) {
            choice = choice.replace(/>/g, '').replace(/</g, '');
            try {
                await bot.addMessageReaction(msg2.channel.id, msg2.id, encodeURIComponent(choice));
            } catch (err) {
                //NO-OP
                //   logger.error(err);
            }
        }
        setTimeout(async function () {
            let msg3 = await bot.getMessage(msg2.channel.id, msg2.id);
            let reactions = [];
            for (let key in msg3.reactions) {
                msg3.reactions[key].emoji = key;
                reactions.push(msg3.reactions[key]);
            }
            if (reactions.length == 0) {
                bu.send(msg, 'No results were collected!');
                return;
            }
            let totalVotes = 0;
            for (let key in reactions) {
                if (/\d/.test(reactions[key].emoji))
                    reactions[key].emoji = `<:${reactions[key].emoji}>`;
                if (choices.indexOf(reactions[key].emoji) > -1)
                    reactions[key].count--;
                totalVotes += reactions[key].count;
            }
            reactions.sort((a, b) => {
                return b.count - a.count;
            });
            let max = reactions[0].count;
            let winners = reactions.filter(r => r.count == max);
            let winnerString = winners.map(r => r.emoji).join(' ');
            if (winners.length > 1) {
                bu.send(msg, `**__${words[1]}__**\nThe results are in! It was a tie between these choices, at **${max}** vote${max == 1 ? '' : 's'} each:
${winnerString}

A total of **${totalVotes}** were collected!`);
            } else {
                bu.send(msg, `**__${words[1]}__**\nThe results are in! At **${max}** vote${max == 1 ? '' : 's'}, the winner is:
${winnerString}

A total of **${totalVotes}** were collected!`);
            }
        }, time);
    }
};<|endoftext|>


<commit_before>// Home controller for TypeItQuick app.
angular.module('TypeItQuick').
    controller('HomeCtrl', ['$scope', '$http', '$location', '$window', function($scope, $http, $location, $window) {
        // Render captcha.
        $($window).on('recaptchaLoaded', function() {
            console.log('CAPTCHA LOADED!!!');
            console.log(grecaptcha.render);
            grecaptcha.render('g-recaptcha', {
                'sitekey': $window.captchaKey
            });
        });
    }]);<commit_msg>Tracing..<commit_after>// Home controller for TypeItQuick app.
angular.module('TypeItQuick').
    controller('HomeCtrl', ['$scope', '$http', '$location', '$window', function($scope, $http, $location, $window) {
        console.log('CHAKA!!!');
        // Render captcha.
        $($window).on('recaptchaLoaded', function() {
            console.log('CAPTCHA LOADED!!!');
            console.log(grecaptcha.render);
            grecaptcha.render('g-recaptcha', {
                'sitekey': $window.captchaKey
            });
        });
    }]);<|endoftext|>


<commit_before>import { Router } from 'express'
import axios from 'axios'
import bodyParser from 'body-parser'
import cors from 'cors'
import { getTokenOctokit } from '@argos-ci/github'
import { User } from '@argos-ci/database/models'
import config from '@argos-ci/config'
import { synchronizeFromUserId } from '@argos-ci/synchronize'
import { asyncHandler } from '../util'

const router = new Router()

export default router

const url = new URL(config.get('server.url'))
router.use(
  cors({
    origin: `${url.protocol}//${url.hostname}:${url.port}`,
    methods: ['POST'],
  }),
)

function getDataFromProfile(profile) {
  return {
    githubId: profile.id,
    login: profile.login,
    name: profile.name,
    email: profile.email,
  }
}

async function registerUserFromGitHub(accessToken) {
  const octokit = getTokenOctokit(accessToken)

  const profile = await octokit.users.getAuthenticated()
  const userData = { ...getDataFromProfile(profile.data), accessToken }

  let user = await User.query()
    .where({ githubId: userData.githubId })
    .limit(1)
    .first()

  if (user) {
    await user.$query().patch(userData)
  } else {
    user = await User.query().insertAndFetch(userData)
  }

  await synchronizeFromUserId(user.id)
}

router.post(
  '/auth/github',
  bodyParser.json(),
  asyncHandler(async (req, res) => {
    const result = await axios.post(
      'https://github.com/login/oauth/access_token',
      {
        client_id: config.get('github.clientId'),
        client_secret: config.get('github.clientSecret'),
        code: req.body.code,
      },
      {
        headers: {
          accept: 'application/json',
        },
      },
    )

    if (result.data.error) {
      res.status(400)
      res.send(result.data)
      return
    }

    await registerUserFromGitHub(result.data.access_token)
    res.send(result.data)
  }),
)
<commit_msg>fix: fix cors access to API<commit_after>import { Router } from 'express'
import axios from 'axios'
import bodyParser from 'body-parser'
import cors from 'cors'
import { getTokenOctokit } from '@argos-ci/github'
import { User } from '@argos-ci/database/models'
import config from '@argos-ci/config'
import { synchronizeFromUserId } from '@argos-ci/synchronize'
import { asyncHandler } from '../util'

const router = new Router()

export default router

// @TODO be more restrictive on cors
router.use(cors())

function getDataFromProfile(profile) {
  return {
    githubId: profile.id,
    login: profile.login,
    name: profile.name,
    email: profile.email,
  }
}

async function registerUserFromGitHub(accessToken) {
  const octokit = getTokenOctokit(accessToken)

  const profile = await octokit.users.getAuthenticated()
  const userData = { ...getDataFromProfile(profile.data), accessToken }

  let user = await User.query()
    .where({ githubId: userData.githubId })
    .limit(1)
    .first()

  if (user) {
    await user.$query().patch(userData)
  } else {
    user = await User.query().insertAndFetch(userData)
  }

  await synchronizeFromUserId(user.id)
}

router.post(
  '/auth/github',
  bodyParser.json(),
  asyncHandler(async (req, res) => {
    const result = await axios.post(
      'https://github.com/login/oauth/access_token',
      {
        client_id: config.get('github.clientId'),
        client_secret: config.get('github.clientSecret'),
        code: req.body.code,
      },
      {
        headers: {
          accept: 'application/json',
        },
      },
    )

    if (result.data.error) {
      res.status(400)
      res.send(result.data)
      return
    }

    await registerUserFromGitHub(result.data.access_token)
    res.send(result.data)
  }),
)
<|endoftext|>


<commit_before>var step = ''
            +'
Test Purpose:
'
            +'
Verifies the full screen mode and CSS full screen mode could be enabled and cancelled. The CSS full screen mode is filled with green color in background.
'
            +'
Expected Result:
'
            +'
Test passes if full screen mode and CSS full screen mode functions all work well.
'
          +'';
<commit_msg>[usecase] add clicking drop down list for fullscreen usecase<commit_after>var step = ''
            +'
Test Purpose:
'
            +'
Verifies the full screen mode and CSS full screen mode could be enabled and cancelled. The CSS full screen mode is filled with green color in background.
'
            +'
Expected Result:
'
            +'
Test passes if full screen mode and CSS full screen mode functions all work well whether the drop down list is clicked.
'
          +'';
<|endoftext|>


<commit_before>var outages = [
    {
	what: 'partial api outage',
	where: 'productiondr',
	why: 'scheduled replacement of our redis master',
	timeline: [
	    { date: 1466445780, what: 'icinga reports one of our load-balancer is returning with 503 errors, route53 did the job as StatusCake and UptimeRobot both show a 100% uptime on DR' },
	    { date: 1466470920, what: 'noticing the warning, turns out one of our websocket worker is neither dead nor alive' },
	    { date: 1466470980, what: 'AWS did warn us a while ago, that they would be replacing the master instance from our ElastiCache cluster. Reloading our NodeJS process fixed. Pending further investigations as per why that process did not crash as expected.' } ]
    }, {
	what: 'mysql node down',
	where: 'productiondr',
	why: 'probably some hardware failure on AWS part',
	timeline: [
	    { date: 1466037840, what: 'icinga2 reports one of our mysql node is down. No incidence on service whatsoever' },
	    { date: 1466038020, what: 'trying to stop our faulty VM from AWS console' },
	    { date: 1466038440, what: 'instance stopped. Starting it back' },
	    { date: 1466038560, what: 'node back up, cluster is back to 3 nodes.' } ]
    }, {
	what: 'general api outage',
	where: 'staging',
	why: 'some AWS health checks returned faulty, resulting in our ASG trying to replace the foreground worker instance from our staging setup',
	timeline: [
	    { date: 1464548106, what: 'uptimeRobot, statusCake & icinga2 report staging is down' },
	    { date: 1464549780, what: 'our faulty instance finally got replaced, ansible just started deploying the new one' },
	    { date: 1464551139, what: 'mail received from AWS, notifying us about a hardware failure on the host running our instance that just disappeared' },
	    { date: 1464551520, what: 'CodeDeploy failed pushing the last known-to-be-stable version of peerio-server to our new worker' },
	    { date: 1464551880, what: 'problem identified in our CodeDeploy init script, running on a blank instance' },
	    { date: 1464552720, what: 'dropped the foreground ASG from our staging deployment group, deployed a fix to the remaining instance of that group' },
	    { date: 1464553320, what: 'ansible finished configuring the new instance that will run our foreground worker on staging' },
	    { date: 1464553767, what: 'done running CodeDeploy, service is back up.' } ]
    }, {
	what: 'healthcheck flaps',
	where: 'labs',
	why: 'adding a constraint on redis memory usage, switching our healthcheck to faulty when there is not enough memory to handle further traffic',
	timeline: [
	    { date: 1464333288, what: 'health check returns unhealthy' },
	    { date: 1464341837, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464344092, what: 'health check returns unhealthy' },
	    { date: 1464349157, what: 'our labs setup has no active users, resulting in our redis database having a single record.
Add a high limit to redis average key size, since our check aims to ensure at least 10.000 keys can be created' },
	    { date: 1464354805, what: 'health check returns unhealthy' },
	    { date: 1464385903, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464387219, what: 'health check returns unhealthy' },
	    { date: 1464435398, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464437532, what: 'health check returns unhealthy' },
	    { date: 1464441933, what: 'problem fixed with a parseInt.' } ]
    }, {
	what: 'public site outage',
	where: 'aws_prod',
	why: 'unscheduled, un-notified and still unexplained maintenance on our site instance, hosted by digitalocean',
	timeline: [
	    { date: 1464161352, what: 'uptimeRobot & statusCake notifies us that our site is not responding, cloudflare is serving a cached version' },
	    { date: 1464164280, what: 'waking up, acknowledged, digitalocean shows our VM is down, I can not start, stop or restart it, no explanation regarding what is going on, no notifications in our inboxes nor in the dashboard' },
	    { date: 1464165060, what: 'booting a new instance on EC2, provisioning our website, status site, windows and mac packages' },
	    { date: 1464165420, what: 'our digitalocean instance came back to life, which I only relized half an hour later' },
	    { date: 1464166080, what: 'instance ready' },
	    { date: 1464166260, what: 'DNS updated, pointing to our new instance.' } ]
    }, {
	what: 'general api outage',
	where: 'productiondr',
	why: 'lost 2 riak nodes in a couple minutes, while production is under maintenance / DR is acting as production',
	timeline: [
	    { date: 1460803620, what: 'nagios reports plan pending commit in 3 nodes and riak_check down in 2' },
	    { date: 1460803680, what: 'acknowledged, I connect to our 5 nodes. Thankfully, they are still all up. Riak is stopped in two of them' },
	    { date: 1460803740, what: 'restarting riak on my first faulty node failed. Stopping then starting worked' },
	    { date: 1460803800, what: 'stopping then starting riak on my second node worked. Cluster shows healthy, leaving it alone' },
	    { date: 1460804580, what: 'checking the client, turns out my active session is frozen, while Vincent reports that he can not log in. Reloading the foreground workers fixed' },
	    { date: 1460804700, what: 'in doubt, reloading our schedule, background and filemgr workers as well.' } ]
    }, {
	what: 'logs forwarding failure',
	where: 'aws_prod',
	why: 'logstash service stopped responding',
	timeline: [
	    { date: 1455582780, what: 'nagios reports an unusual delay processing logs' },
	    { date: 1455614640, what: 'process killed, service restarted, looking at the logs, we are asked to check our AWS-ES credentials.
Configuration matches DR & staging, both restarted at the same time, both working' },
	    { date: 1455617280, what: 'I had to send production logs to a new index, keeping the rest of my AWS-ES backend configuration: no more error in our logs' },
	    { date: 1459695222, what: 'after a few flaps between logstash and AWS-ES, we finally installed our own elasticsearch cluster in EC2.' } ]
    }, {
	what: 'riak host failure',
	where: 'productiondr',
	why: 'on-disk file corruption',
	timeline: [
	    { date: 1455471660, what: 'nagios reports host unreachable, our riak init node is actually rebooting' },
	    { date: 1455492900, what: 'nagios reports host unreachable, no reboot this time, host checks failing from AWS console' },
	    { date: 1455527700, what: 'waking up, I notice nagios alerts, floh also warned me on slack' },
	    { date: 1455527760, what: 'stopping our instance on AWS' },
	    { date: 1455528780, what: 'issue opened with Basho' },
	    { date: 1455530820, what: 'instance shows as stopped on AWS, restarting it' },
	    { date: 1455531480, what: 'cluster back up and running, logs showing CRC errors occured, none that I could find after reboot.' } ]
    }, {
	what: 'riak host failure',
	where: 'productiondr',
	why: 'on-disk file corruption',
	timeline: [
	    { date: 1454870460, what: 'nagios reports host unreachable, our riak init node is actually rebooting' },
	    { date: 1454875080, what: 'nagios reports host unreachable, our riak init node is rebooting again' },
	    { date: 1454876760, what: 'nagios reports host unreachable, our riak init node is rebooting again' },
	    { date: 1454925060, what: 'waking up, I notice a lot of CRC errors in riak logs' },
	    { date: 1454943060, what: 'issued open with Basho' },
	    { date: 1454957400, what: 'reply from Basho, explaining how to manually repair faulty objects' },
	    { date: 1454957640, what: 'unable to apply Basho recommendations, as I could not find any corrupted object remaining.' } ]
    }, {
	what: 'logs forwarding failure',
	where: 'productiondr',
	why: 'logstash service stopped responding',
	timeline: [
	    { date: 1452810780, what: 'nagios reports an unusual delay processing logs' },
	    { date: 1452810900, what: 'service restarted, looking at the logs, no change' },
	    { date: 1452814800, what: 'problem identified, logstash init script does not stop logstash process' },
	    { date: 1452815040, what: 'logstash properly restarted, nagios confirms messages are being processed' },
	    { date: 1459695222, what: 'after a few flaps, we finally installed our own elasticsearch cluster in EC2.' } ]
    }, {
	what: 'codedeploy almost-gedon',
	where: 'staging',
	why: 'disks inode usage reaching 99%',
	timeline: [
	    { date: 1452032580, what: 'munin graphs showing inode usage reached 90% on our background staging worker' },
	    { date: 1452039120, what: 'munin graphs showing inode usage reached 90% on our foreground staging worker' },
	    { date: 1452118260, what: 'I finally notice munin reports 99% inode usage on both instances' },
	    { date: 1452118860, what: 'after autopurging packages with no real effect, I identified the source of our problem:
CodeDeploy backups piles up in /var/www/peerio-server/shared/backup.
Purging backups from december freed 72% inodes - and 14G' },
	    { date: 1452120360, what: 'quick fix to prevent this from happening again (1, 2).' } ]
    }, {
	what: 'general api outage',
	where: 'aws_prod',
	why: 'authentication service unavailable',
	timeline: [
	    { date: 1451556360, what: 'background worker restarting in a loop' },
	    { date: 1451566500, what: 'problem identified, ElastiCache is full, pretty much everything involving auth tokens is affected' },
	    { date: 1451566680, what: 'DR is not affected, route53 records updated, service back up' },
	    { date: 1451579160, what: 'production ElastiCache fixed, server code patched' },
	    { date: 1451580000, what: 'route53 records restored, new connections started moving back from DR to production' },
	    { date: 1451624100, what: 'last client disconnected from DR, everyone is back on production.' } ]
    } ];
<commit_msg>record our last failure<commit_after>var outages = [
    {
	what: 'logs collection outage',
	where: 'aws_prod',
	why: 'unscheduled termination of our logstash instance',
	timeline: [
	    { date: 1466699220, what: 'icinga2 reports our production logstash instance is down. No incidence on service whatsoever' },
	    { date: 1466699400, what: 'logging into aws console, we can confirm the corresponding EC2 instance is being terminated. No explanation available. The corresponding AutoScale Group already spawned a replacement instance, we just need to wait for Ansible to do its job' },
	    { date: 1466700360, what: 'situation is back to normal.' } ]
    }, {
	what: 'partial api outage',
	where: 'productiondr',
	why: 'scheduled replacement of our redis master',
	timeline: [
	    { date: 1466445780, what: 'icinga reports one of our load-balancer is returning with 503 errors, route53 did the job as StatusCake and UptimeRobot both show a 100% uptime on DR' },
	    { date: 1466470920, what: 'noticing the warning, turns out one of our websocket worker is neither dead nor alive' },
	    { date: 1466470980, what: 'AWS did warn us a while ago, that they would be replacing the master instance from our ElastiCache cluster. Reloading our NodeJS process fixed. Pending further investigations as per why that process did not crash as expected.' } ]
    }, {
	what: 'mysql node down',
	where: 'productiondr',
	why: 'probably some hardware failure on AWS part',
	timeline: [
	    { date: 1466037840, what: 'icinga2 reports one of our mysql node is down. No incidence on service whatsoever' },
	    { date: 1466038020, what: 'trying to stop our faulty VM from AWS console' },
	    { date: 1466038440, what: 'instance stopped. Starting it back' },
	    { date: 1466038560, what: 'node back up, cluster is back to 3 nodes.' } ]
    }, {
	what: 'general api outage',
	where: 'staging',
	why: 'some AWS health checks returned faulty, resulting in our ASG trying to replace the foreground worker instance from our staging setup',
	timeline: [
	    { date: 1464548106, what: 'uptimeRobot, statusCake & icinga2 report staging is down' },
	    { date: 1464549780, what: 'our faulty instance finally got replaced, ansible just started deploying the new one' },
	    { date: 1464551139, what: 'mail received from AWS, notifying us about a hardware failure on the host running our instance that just disappeared' },
	    { date: 1464551520, what: 'CodeDeploy failed pushing the last known-to-be-stable version of peerio-server to our new worker' },
	    { date: 1464551880, what: 'problem identified in our CodeDeploy init script, running on a blank instance' },
	    { date: 1464552720, what: 'dropped the foreground ASG from our staging deployment group, deployed a fix to the remaining instance of that group' },
	    { date: 1464553320, what: 'ansible finished configuring the new instance that will run our foreground worker on staging' },
	    { date: 1464553767, what: 'done running CodeDeploy, service is back up.' } ]
    }, {
	what: 'healthcheck flaps',
	where: 'labs',
	why: 'adding a constraint on redis memory usage, switching our healthcheck to faulty when there is not enough memory to handle further traffic',
	timeline: [
	    { date: 1464333288, what: 'health check returns unhealthy' },
	    { date: 1464341837, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464344092, what: 'health check returns unhealthy' },
	    { date: 1464349157, what: 'our labs setup has no active users, resulting in our redis database having a single record.
Add a high limit to redis average key size, since our check aims to ensure at least 10.000 keys can be created' },
	    { date: 1464354805, what: 'health check returns unhealthy' },
	    { date: 1464385903, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464387219, what: 'health check returns unhealthy' },
	    { date: 1464435398, what: 'adding debugs, restarting pm2 processes' },
	    { date: 1464437532, what: 'health check returns unhealthy' },
	    { date: 1464441933, what: 'problem fixed with a parseInt.' } ]
    }, {
	what: 'public site outage',
	where: 'aws_prod',
	why: 'unscheduled, un-notified and still unexplained maintenance on our site instance, hosted by digitalocean',
	timeline: [
	    { date: 1464161352, what: 'uptimeRobot & statusCake notifies us that our site is not responding, cloudflare is serving a cached version' },
	    { date: 1464164280, what: 'waking up, acknowledged, digitalocean shows our VM is down, I can not start, stop or restart it, no explanation regarding what is going on, no notifications in our inboxes nor in the dashboard' },
	    { date: 1464165060, what: 'booting a new instance on EC2, provisioning our website, status site, windows and mac packages' },
	    { date: 1464165420, what: 'our digitalocean instance came back to life, which I only relized half an hour later' },
	    { date: 1464166080, what: 'instance ready' },
	    { date: 1464166260, what: 'DNS updated, pointing to our new instance.' } ]
    }, {
	what: 'general api outage',
	where: 'productiondr',
	why: 'lost 2 riak nodes in a couple minutes, while production is under maintenance / DR is acting as production',
	timeline: [
	    { date: 1460803620, what: 'nagios reports plan pending commit in 3 nodes and riak_check down in 2' },
	    { date: 1460803680, what: 'acknowledged, I connect to our 5 nodes. Thankfully, they are still all up. Riak is stopped in two of them' },
	    { date: 1460803740, what: 'restarting riak on my first faulty node failed. Stopping then starting worked' },
	    { date: 1460803800, what: 'stopping then starting riak on my second node worked. Cluster shows healthy, leaving it alone' },
	    { date: 1460804580, what: 'checking the client, turns out my active session is frozen, while Vincent reports that he can not log in. Reloading the foreground workers fixed' },
	    { date: 1460804700, what: 'in doubt, reloading our schedule, background and filemgr workers as well.' } ]
    }, {
	what: 'logs forwarding failure',
	where: 'aws_prod',
	why: 'logstash service stopped responding',
	timeline: [
	    { date: 1455582780, what: 'nagios reports an unusual delay processing logs' },
	    { date: 1455614640, what: 'process killed, service restarted, looking at the logs, we are asked to check our AWS-ES credentials.
Configuration matches DR & staging, both restarted at the same time, both working' },
	    { date: 1455617280, what: 'I had to send production logs to a new index, keeping the rest of my AWS-ES backend configuration: no more error in our logs' },
	    { date: 1459695222, what: 'after a few flaps between logstash and AWS-ES, we finally installed our own elasticsearch cluster in EC2.' } ]
    }, {
	what: 'riak host failure',
	where: 'productiondr',
	why: 'on-disk file corruption',
	timeline: [
	    { date: 1455471660, what: 'nagios reports host unreachable, our riak init node is actually rebooting' },
	    { date: 1455492900, what: 'nagios reports host unreachable, no reboot this time, host checks failing from AWS console' },
	    { date: 1455527700, what: 'waking up, I notice nagios alerts, floh also warned me on slack' },
	    { date: 1455527760, what: 'stopping our instance on AWS' },
	    { date: 1455528780, what: 'issue opened with Basho' },
	    { date: 1455530820, what: 'instance shows as stopped on AWS, restarting it' },
	    { date: 1455531480, what: 'cluster back up and running, logs showing CRC errors occured, none that I could find after reboot.' } ]
    }, {
	what: 'riak host failure',
	where: 'productiondr',
	why: 'on-disk file corruption',
	timeline: [
	    { date: 1454870460, what: 'nagios reports host unreachable, our riak init node is actually rebooting' },
	    { date: 1454875080, what: 'nagios reports host unreachable, our riak init node is rebooting again' },
	    { date: 1454876760, what: 'nagios reports host unreachable, our riak init node is rebooting again' },
	    { date: 1454925060, what: 'waking up, I notice a lot of CRC errors in riak logs' },
	    { date: 1454943060, what: 'issued open with Basho' },
	    { date: 1454957400, what: 'reply from Basho, explaining how to manually repair faulty objects' },
	    { date: 1454957640, what: 'unable to apply Basho recommendations, as I could not find any corrupted object remaining.' } ]
    }, {
	what: 'logs forwarding failure',
	where: 'productiondr',
	why: 'logstash service stopped responding',
	timeline: [
	    { date: 1452810780, what: 'nagios reports an unusual delay processing logs' },
	    { date: 1452810900, what: 'service restarted, looking at the logs, no change' },
	    { date: 1452814800, what: 'problem identified, logstash init script does not stop logstash process' },
	    { date: 1452815040, what: 'logstash properly restarted, nagios confirms messages are being processed' },
	    { date: 1459695222, what: 'after a few flaps, we finally installed our own elasticsearch cluster in EC2.' } ]
    }, {
	what: 'codedeploy almost-gedon',
	where: 'staging',
	why: 'disks inode usage reaching 99%',
	timeline: [
	    { date: 1452032580, what: 'munin graphs showing inode usage reached 90% on our background staging worker' },
	    { date: 1452039120, what: 'munin graphs showing inode usage reached 90% on our foreground staging worker' },
	    { date: 1452118260, what: 'I finally notice munin reports 99% inode usage on both instances' },
	    { date: 1452118860, what: 'after autopurging packages with no real effect, I identified the source of our problem:
CodeDeploy backups piles up in /var/www/peerio-server/shared/backup.
Purging backups from december freed 72% inodes - and 14G' },
	    { date: 1452120360, what: 'quick fix to prevent this from happening again (1, 2).' } ]
    }, {
	what: 'general api outage',
	where: 'aws_prod',
	why: 'authentication service unavailable',
	timeline: [
	    { date: 1451556360, what: 'background worker restarting in a loop' },
	    { date: 1451566500, what: 'problem identified, ElastiCache is full, pretty much everything involving auth tokens is affected' },
	    { date: 1451566680, what: 'DR is not affected, route53 records updated, service back up' },
	    { date: 1451579160, what: 'production ElastiCache fixed, server code patched' },
	    { date: 1451580000, what: 'route53 records restored, new connections started moving back from DR to production' },
	    { date: 1451624100, what: 'last client disconnected from DR, everyone is back on production.' } ]
    } ];
<|endoftext|>


<commit_before>var _spaces = '                                       ';

function isNumber (n) {
  return 'number' === typeof n
}

function isObject (o) {
  return o && 'object' === typeof o
}

function isString (s) {
  return 'string' === typeof s
}

function isDecimal (e) {
  return isNumber(e) ? (e % 1 !== 0) : false
}

function isExponent (e) {
  return isNumber(e) && !isWeird(e) && (Math.abs(e) >= 1e21 || Math.abs(e) < 1e-6 && e !== 0)
}

function isWeird (n) {
  return isNumber(n) && (n === Infinity || n === -Infinity || isNaN(n))
}

function spaces (n) {
  return _spaces.substring(0, n)
}

function pad (value, length) {
  var string = value.toString()
  var total = Math.max(length.total, length.fraction + length.whole)

  if(isNumber(value) && !isWeird(value)) {
    var whole
    if(isExponent(value)) {
      whole = string.indexOf('e')
    } else
      whole = Math.round(value).toString().length
    string = spaces(total - whole - length.fraction) + value
    return  string + spaces(total - string.length)
  }
  var padding = spaces(total - string.length)
  return string + padding
}

module.exports = function (array) {
  function headerToString (e) {
    return e.name + (e.units ? ' (' + e.units + ')' : '')
  }

  function reduceLengths (l, item) {
    if(!l) l = {whole: 0, total: 0, fraction: 0}
    var whole, fraction, total
    if(isNumber(item)) {
      if(isExponent(item)) {
        var str = item.toString()
        whole = str.indexOf('e')
        fraction = str.length - whole
        total = str.length
      } else if(isDecimal(item)) {
        var string = item.toString()
        whole = string.indexOf('.')
        fraction = string.substring(whole).length
        console.log(string, whole, fraction)
        total = string.length
      } else {
        total = whole = item.toString().length
        fraction = 0
      }
    } else {
      total = item.toString().length
      fraction = whole = 0
    }

    l.total = Math.max(l.total, total)
    l.whole = Math.max(l.whole, whole)
    l.fraction = Math.max(l.fraction, fraction)

    return l
  }

  var lengths = []
  array[0] = array[0].map(headerToString)
  var lengths = array.reduce(function (a, line) {
      line.forEach(function (v, i) {
        a[i] = reduceLengths(a[i], v)
      })
      return a
    }, [])

  return array.map(function (e, i) {
    return e.map(function (e, i) {
      return pad(e, lengths[i])
    }).join(', ').trimRight()
  }).join('\n')

}
<commit_msg>remove logging<commit_after>var _spaces = '                                       ';

function isNumber (n) {
  return 'number' === typeof n
}

function isObject (o) {
  return o && 'object' === typeof o
}

function isString (s) {
  return 'string' === typeof s
}

function isDecimal (e) {
  return isNumber(e) ? (e % 1 !== 0) : false
}

function isExponent (e) {
  return isNumber(e) && !isWeird(e) && (Math.abs(e) >= 1e21 || Math.abs(e) < 1e-6 && e !== 0)
}

function isWeird (n) {
  return isNumber(n) && (n === Infinity || n === -Infinity || isNaN(n))
}

function spaces (n) {
  return _spaces.substring(0, n)
}

function pad (value, length) {
  var string = value.toString()
  var total = Math.max(length.total, length.fraction + length.whole)

  if(isNumber(value) && !isWeird(value)) {
    var whole
    if(isExponent(value)) {
      whole = string.indexOf('e')
    } else
      whole = Math.round(value).toString().length
    string = spaces(total - whole - length.fraction) + value
    return  string + spaces(total - string.length)
  }
  var padding = spaces(total - string.length)
  return string + padding
}

module.exports = function (array) {
  function headerToString (e) {
    return e.name + (e.units ? ' (' + e.units + ')' : '')
  }

  function reduceLengths (l, item) {
    if(!l) l = {whole: 0, total: 0, fraction: 0}
    var whole, fraction, total
    if(isNumber(item)) {
      if(isExponent(item)) {
        var str = item.toString()
        whole = str.indexOf('e')
        fraction = str.length - whole
        total = str.length
      } else if(isDecimal(item)) {
        var string = item.toString()
        whole = string.indexOf('.')
        fraction = string.substring(whole).length
        total = string.length
      } else {
        total = whole = item.toString().length
        fraction = 0
      }
    } else {
      total = item.toString().length
      fraction = whole = 0
    }

    l.total = Math.max(l.total, total)
    l.whole = Math.max(l.whole, whole)
    l.fraction = Math.max(l.fraction, fraction)

    return l
  }

  var lengths = []
  array[0] = array[0].map(headerToString)
  var lengths = array.reduce(function (a, line) {
      line.forEach(function (v, i) {
        a[i] = reduceLengths(a[i], v)
      })
      return a
    }, [])

  return array.map(function (e, i) {
    return e.map(function (e, i) {
      return pad(e, lengths[i])
    }).join(', ').trimRight()
  }).join('\n')

}
<|endoftext|>


<commit_before>var title = document.querySelector('#title')
var titleDisabled = document.querySelector('#title-disabled');

(function() {
var doc = window.editor;

function FileSystem(doc) {

  var dataString = localStorage.getItem('docsData');
  if(dataString) {
    this.docsData = JSON.parse(dataString);
    console.log(this.docsData)
  } else {
    this.docsData = [];
  }

  var fs = this;

  this.open = function(file) {
    title.value = file.title
    titleDisabled.value = file.title + file.extension;
    doc.setText(file.content);
    fs.openFile = file;
  }
  this.save = function(force) {
    if(force || fs.openFile.content != doc.getDocumentAsPlainText()
      || fs.openFile.title != title.value) {
      fs.openFile.content = doc.getDocumentAsPlainText();
      fs.openFile.title = title.value;
      localStorage.setItem('docsData', JSON.stringify(fs.docsData));
      console.log('Document saved to localStorage');
    } else {
      console.log('Document not saved to localStorage since nothing changed');
    }
    console.log(fs.docsData);
  }
  this.new = function(text) {
    var newfile = {
      'title': 'untitled',
      'extension': '.txt',
      'content': text || ''
    };
    this.docsData.push(newfile);
    this.open(newfile);
    this.save(true);
  }

  if(this.docsData[0]) {
    this.open(this.docsData[0]);
  } else {
    this.new('document.addEventListener(\'copy\', function(e) {\n    e.clipboardData.setData(\'text/plain\', \'Hello, world!\');\n    e.clipboardData.setData(\'text/html\', \'Hello, world!\');\n    e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard\n});');
  }

  this.saveTimer = {
    interval: 3000,
    timer: null,
    reset: function() {
      if(fs.saveTimer.timer) fs.saveTimer.timer = clearTimeout(fs.saveTimer.timer);
      fs.saveTimer.timer = setTimeout(fs.save, fs.saveTimer.interval);
      console.log(`Save timer reset to ${fs.saveTimer.interval}ms`);
    }
  }

  title.addEventListener('input', function(e) {
    titleDisabled.value = title.value + this.openFile.extension;
    fs.saveTimer.save();
  });

  var file = document.querySelector('#file');
  file.querySelector('.button').addEventListener('click', function(e) {
    if(file.classList.contains('open')) {
      file.classList.remove('open');
    } else {
      let menu = file.querySelector('.menu');
      while(menu.firstChild) {
        menu.removeChild(menu.firstChild);
      }
      for(let i = 0; i < fs.docsData.length; i++) {
        let li = document.createElement('li');
        let icon = document.createElement('img');
        icon.setAttribute('src', 'icons/file.svg')
        li.appendChild(icon);
        li.appendChild(document.createTextNode(fs.docsData[i].title + fs.docsData[i].extension));
        menu.appendChild(li);
        li.addEventListener('click', function(e) {
          fs.open(fs.docsData[i]);
          file.classList.remove('open');
        });
      }

      let li = document.createElement('li');
      let icon = document.createElement('img');
      icon.setAttribute('src', 'icons/plus.svg')
      li.appendChild(icon);
      li.appendChild(document.createTextNode('New'));
      menu.appendChild(li);
      li.addEventListener('click', function(e) {
        fs.new()
        file.classList.remove('open');
      });

      file.classList.add('open');
    }
  });
  document.querySelector('#delete').addEventListener('click', function(e) {
    var index = fs.docsData.indexOf(fs.openFile);
    fs.docsData.splice(index, 1);
    fs.save(true);
    if(fs.docsData.length) {
      fs.open(fs.docsData[0]);
    } else {
      fs.new();
    }
  });
}

doc.fileSystem = new FileSystem(doc);
})();
<commit_msg>bugfix<commit_after>var title = document.querySelector('#title')
var titleDisabled = document.querySelector('#title-disabled');

(function() {
var doc = window.editor;

function FileSystem(doc) {

  var dataString = localStorage.getItem('docsData');
  if(dataString) {
    this.docsData = JSON.parse(dataString);
    console.log(this.docsData)
  } else {
    this.docsData = [];
  }

  var fs = this;

  this.open = function(file) {
    title.value = file.title
    titleDisabled.value = file.title + file.extension;
    doc.setText(file.content);
    fs.openFile = file;
  }
  this.save = function(force) {
    if(force || fs.openFile.content != doc.getDocumentAsPlainText()
      || fs.openFile.title != title.value) {
      fs.openFile.content = doc.getDocumentAsPlainText();
      fs.openFile.title = title.value;
      localStorage.setItem('docsData', JSON.stringify(fs.docsData));
      console.log('Document saved to localStorage');
    } else {
      console.log('Document not saved to localStorage since nothing changed');
    }
    console.log(fs.docsData);
  }
  this.new = function(text) {
    var newfile = {
      'title': 'untitled',
      'extension': '.txt',
      'content': text || ''
    };
    this.docsData.push(newfile);
    this.open(newfile);
    this.save(true);
  }

  if(this.docsData[0]) {
    this.open(this.docsData[0]);
  } else {
    this.new('document.addEventListener(\'copy\', function(e) {\n    e.clipboardData.setData(\'text/plain\', \'Hello, world!\');\n    e.clipboardData.setData(\'text/html\', \'Hello, world!\');\n    e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard\n});');
  }

  this.saveTimer = {
    interval: 3000,
    timer: null,
    reset: function() {
      if(fs.saveTimer.timer) fs.saveTimer.timer = clearTimeout(fs.saveTimer.timer);
      fs.saveTimer.timer = setTimeout(fs.save, fs.saveTimer.interval);
      console.log(`Save timer reset to ${fs.saveTimer.interval}ms`);
    }
  }

  title.addEventListener('input', function(e) {
    titleDisabled.value = title.value + fs.openFile.extension;
    fs.save();
  });

  var file = document.querySelector('#file');
  file.querySelector('.button').addEventListener('click', function(e) {
    if(file.classList.contains('open')) {
      file.classList.remove('open');
    } else {
      let menu = file.querySelector('.menu');
      while(menu.firstChild) {
        menu.removeChild(menu.firstChild);
      }
      for(let i = 0; i < fs.docsData.length; i++) {
        let li = document.createElement('li');
        let icon = document.createElement('img');
        icon.setAttribute('src', 'icons/file.svg')
        li.appendChild(icon);
        li.appendChild(document.createTextNode(fs.docsData[i].title + fs.docsData[i].extension));
        menu.appendChild(li);
        li.addEventListener('click', function(e) {
          fs.open(fs.docsData[i]);
          file.classList.remove('open');
        });
      }

      let li = document.createElement('li');
      let icon = document.createElement('img');
      icon.setAttribute('src', 'icons/plus.svg')
      li.appendChild(icon);
      li.appendChild(document.createTextNode('New'));
      menu.appendChild(li);
      li.addEventListener('click', function(e) {
        fs.new()
        file.classList.remove('open');
      });

      file.classList.add('open');
    }
  });
  document.querySelector('#delete').addEventListener('click', function(e) {
    var index = fs.docsData.indexOf(fs.openFile);
    fs.docsData.splice(index, 1);
    fs.save(true);
    if(fs.docsData.length) {
      fs.open(fs.docsData[0]);
    } else {
      fs.new();
    }
  });
}

doc.fileSystem = new FileSystem(doc);
})();
<|endoftext|>