const types = {
  USERNAME: 'USERNAME',
  GET_EVENTS: 'GET_EVENTS',
  GET_PUSH_EVENTS: 'GET_PUSH_EVENTS',
  GET_OPENED_PULL_REQUESTS: 'GET_OPENED_PULL_REQUESTS',
  GET_ISSUES_CREATED: 'GET_ISSUES_CREATED',
  GET_ISSUES_CLOSED: 'GET_ISSUES_CLOSED',
  GET_COMMITS: 'GET_COMMITS',
};

export const submitUserName = (inputVal) => {
  return {
    type: types.USERNAME,
    username: inputVal
  };
};

export const getEvents = (data) => {
    return {
      type: types.GET_EVENTS,
      data: data
    };
  };

export const getPushEvents = (data) => {
    return {
      type: types.GET_PUSH_EVENTS,
      data: data
    };
  };

export const getOpenedPullRequests = (data) => {
    return {
      type: types.GET_OPENED_PULL_REQUESTS,
      data: data
    };
  };

export const getIssuesCreated = (data) => {
    return {
      type: types.GET_ISSUES_CREATED,
      data: data
    };
  };

export const getIssuesClosed = (data) => {
    return {
      type: types.GET_ISSUES_CLOSED,
      data: data
    };
  };

export const getCommits = (data) => {
    return {
      type: types.GET_COMMITS,
      data: data
    };
  };

const types = {
  USERNAME: 'USERNAME',
  GET_EVENTS: 'GET_EVENTS'
};

export const submitUserName = (inputVal) => {
  return {
    type: types.USERNAME,
    username: inputVal
  };
};

export const getEvents = (data) => {
    return {
      type: types.GET_EVENTS,
      data: data
    };
  };

Remove actions that we are no longer using post-refactor


'use strict';

var React = require('react'),
	GenericViewer = require('../../generic/viewer.js');

var NativeViewer = React.createClass({
	propTypes: {
		resizeCallback: React.PropTypes.func,
		progressCallback: React.PropTypes.func,
		srcdownload: React.PropTypes.string,
		locale: React.PropTypes.string,
		src: React.PropTypes.src
	},
	componentWillMount: function() {
		this.updateProgress(0);
		if (this.props.resizeCallback) {
			this.props.resizeCallback('100%', false);
		}
	},
	componentDidMount: function() {
		this.updateProgress(100);
	},
	updateProgress: function(progress) {
		if (this.props.progressCallback) {
			this.props.progressCallback(progress, 'none');
		}
	},
	render: function() {
		return <object
			data={this.props.src}
			type="application/pdf"
			className="vui-fileviewer-pdf-native">
			<GenericViewer srcdownload={this.props.srcdownload} locale={this.props.locale} />
		</object>;
	}
});

module.exports = NativeViewer;

'use strict';

var React = require('react'),
	GenericViewer = require('../../generic/viewer.js');

var NativeViewer = React.createClass({
	propTypes: {
		resizeCallback: React.PropTypes.func,
		progressCallback: React.PropTypes.func,
		srcdownload: React.PropTypes.string,
		locale: React.PropTypes.string,
		src: React.PropTypes.string
	},
	componentWillMount: function() {
		this.updateProgress(0);
		if (this.props.resizeCallback) {
			this.props.resizeCallback('100%', false);
		}
	},
	componentDidMount: function() {
		this.updateProgress(100);
	},
	updateProgress: function(progress) {
		if (this.props.progressCallback) {
			this.props.progressCallback(progress, 'none');
		}
	},
	render: function() {
		return <object
			data={this.props.src}
			type="application/pdf"
			className="vui-fileviewer-pdf-native">
			<GenericViewer srcdownload={this.props.srcdownload} locale={this.props.locale} />
		</object>;
	}
});

module.exports = NativeViewer;

Fix incorrect proptype in native pdf viewer


import { moduleFor, test } from 'ember-qunit';

moduleFor('controller:submit', 'Unit | Controller | submit', {
  // Specify the other units that are required for this test.
  // needs: ['controller:foo']
    needs: [
        'validator:presence',
        'validator:length',
        'validator:format',
        'service:metrics'
    ],
});

test('editLicense sets basicsLicense and licenseValid', function(assert) {
    const ctrl = this.subject();
    assert.equal(ctrl.get('basicsLicense.copyrightHolders'), null);
    assert.equal(ctrl.get('basicsLicense.licenseType'), null);
    assert.equal(ctrl.get('basicsLicense.year'), null);
    assert.equal(ctrl.get('licenseValid'), false);
    // Trigger controller action
    ctrl.send('editLicense', 'license', true);

    assert.equal(ctrl.get('basicsLicense'), 'license');
    assert.equal(ctrl.get('licenseValid'), true);
});

test('applyLicenseToggle toggles applyLicense', function(assert) {
    const ctrl = this.subject();
    assert.equal(ctrl.get('applyLicense'), false);
    ctrl.send('applyLicenseToggle', true);
    assert.equal(ctrl.get('applyLicense'), true);
});

import { moduleFor, test } from 'ember-qunit';
import Ember from 'ember';

//Stub panelActions service
const panelActionsStub = Ember.Service.extend({
    open() {
        return;
    }
});

moduleFor('controller:submit', 'Unit | Controller | submit', {
  // Specify the other units that are required for this test.
  // needs: ['controller:foo']
    needs: [
        'validator:presence',
        'validator:length',
        'validator:format',
        'service:metrics',
        'service:panel-actions'
    ],
    beforeEach: function () {
       this.register('service:panel-actions', panelActionsStub);
       this.inject.service('panel-actions', { as: 'panelActions' });
   }

});

test('editLicense sets basicsLicense and licenseValid', function(assert) {
    const ctrl = this.subject();
    assert.equal(ctrl.get('basicsLicense.copyrightHolders'), null);
    assert.equal(ctrl.get('basicsLicense.licenseType'), null);
    assert.equal(ctrl.get('basicsLicense.year'), null);
    assert.equal(ctrl.get('licenseValid'), false);
    // Trigger controller action
    ctrl.send('editLicense', 'license', true);

    assert.equal(ctrl.get('basicsLicense'), 'license');
    assert.equal(ctrl.get('licenseValid'), true);
});

test('applyLicenseToggle toggles applyLicense', function(assert) {
    const ctrl = this.subject();
    assert.equal(ctrl.get('applyLicense'), false);
    ctrl.send('applyLicenseToggle', true);
    assert.equal(ctrl.get('applyLicense'), true);
});

test('next opens next panel and flashes changes saved', function(assert) {
    const ctrl = this.subject();
    const currentPanelName = 'Discipline';
    assert.equal('Basics', ctrl.get(`_names.${ctrl.get('_names').indexOf(currentPanelName) + 1}`));
    // Test breaking down before Ember.run.later complete
    // ctrl.send('next', currentPanelName);

});

Add partial test for submit controller > next.


import m from 'mithril';
import AppComponent from '../../app/scripts/components/app.js';
import AIPlayer from '../../app/scripts/models/ai-player.js';
import { qs } from './utils.js';

export function _before() {
  // Minimize the transition duration to speed up tests (interestingly, a
  // duration of 0ms will prevent transitionEnd from firing)
  const style = document.createElement('style');
  style.innerHTML = '* {transition-duration: 50ms !important;}';
  document.head.appendChild(style);
  // Also zero out the AI Player's delay between each swift movement
  AIPlayer.waitDelay = 0;
}

export function _beforeEach() {
  document.body.appendChild(document.createElement('main'));
  m.mount(qs('main'), AppComponent);
}

export function _afterEach() {
  m.mount(qs('main'), null);
}

import m from 'mithril';
import AppComponent from '../../app/scripts/components/app.js';
import AIPlayer from '../../app/scripts/models/ai-player.js';
import { qs } from './utils.js';

export function _before() {
  // Minimize the transition duration to speed up tests (interestingly, a
  // duration of 0ms will prevent transitionEnd from firing)
  const style = document.createElement('style');
  style.innerHTML = '* {transition-duration: 50ms !important;}';
  document.head.appendChild(style);
  // Also zero out the AI Player's delay between each swift movement
  AIPlayer.prototype.waitDelay = 0;
}

export function _beforeEach() {
  document.body.appendChild(document.createElement('main'));
  m.mount(qs('main'), AppComponent);
}

export function _afterEach() {
  m.mount(qs('main'), null);
}

Fix broken test as the result of b8c15c2


var util = require('src/util').default;

beforeEach(function() {
	spyOn(util, 'warn');
});

var testsContext = require.context(".", true, /_spec$/);
testsContext.keys().forEach(testsContext);

Add detection for util.warn in every test spec


// These are mostly a simplification of
// augment.js (https://github.com/javascript/augment, MIT Licensed) and
// Douglas Crockford's protoypical inheritance pattern.
/**
 * Usage:
 *
 *  var Animal = defclass({
 *      constructor: function(name) {
 *          this.name = name || 'unnamed';
 *          this.sleeping = false;
 *      },
 *      sayHi: function() {
 *          console.log('Hi, my name is ' + this.name);
 *      }
 *  });
 */
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

/**
 * Usage:
 *
 *     var Person = extend(Animal, {
 *         constructor: function(name) {
 *             this.super.constructor(name);
 *             this.name = name || 'Steve';
 *         }
 *     });
 */
function extend(constructor, sub) {
    var prototype = Object.create(constructor.prototype);
    for (var key in sub) { prototype[key] = sub[key]; }
    prototype.super = constructor.prototype;
    return defclass(prototype);
}

module.exports = {
    defclass: defclass,
    extend: extend
};

// These are mostly a simplification of
// augment.js (https://github.com/javascript/augment, MIT Licensed) and
// Douglas Crockford's protoypical inheritance pattern.

var noop = function() {};
/**
 * Usage:
 *
var Animal = defclass({
    constructor: function(name) {
        this.name = name || 'unnamed';
        this.sleeping = false;
    },
    sayHi: function() {
        console.log('Hi, my name is ' + this.name);
    }
});
 */
function defclass(prototype) {
    var constructor = prototype.hasOwnProperty('constructor') ? prototype.constructor : noop;
    constructor.prototype = prototype;
    return constructor;
}

/**
 * Usage:
 *
 *     var Person = extend(Animal, {
 *         constructor: function(name) {
 *             this.super.constructor(name);
 *             this.name = name || 'Steve';
 *         }
 *     });
 */
function extend(constructor, sub) {
    var prototype = Object.create(constructor.prototype);
    for (var key in sub) { prototype[key] = sub[key]; }
    prototype.super = constructor.prototype;
    return defclass(prototype);
}

module.exports = {
    defclass: defclass,
    extend: extend
};

Handle classes that don't have a constructor


const DEFAULT_VIEWS = Object.freeze({
  NEW: 'new',
  EDIT: 'edit',
  VIEW: 'view'
})

function getCurrentUri () {
  const params = new URLSearchParams(window.location.search)
  const uri = params.get('uri')
  return uri
}

function getCurrentView () {
  const params = new URLSearchParams(window.location.search)
  const view = params.get('view')
  return view
}

function getViewUrl(uri, view) {
  return `${ROOT_URL}about?view=${view}&uri=${uri}`
}

function goToView(uri, view, args) {
  if(args){
    return window[args](getViewUrl(uri, view), '_blank')
  }
  return window.location = getViewUrl(uri, view)
}

export { DEFAULT_VIEWS, getCurrentUri, getCurrentView, getViewUrl, goToView }

const DEFAULT_VIEWS = Object.freeze({
  NEW: 'new',
  EDIT: 'edit',
  VIEW: 'view'
})

function getCurrentUri () {
  return NODE_URI
}

function getCurrentView () {
  const params = new URLSearchParams(window.location.search)
  const view = params.get('view')
  return view
}

function getViewUrl(uri, view) {
  return `${ROOT_URL}about?view=${view}&uri=${uri}`
}

function goToView(uri, view, args) {
  if(args){
    return window[args](getViewUrl(uri, view), '_blank')
  }
  return window.location = getViewUrl(uri, view)
}

export { DEFAULT_VIEWS, getCurrentUri, getCurrentView, getViewUrl, goToView }

Use NODE_URI instead of parsing uri from url


import React from 'react'
import { Box, Grid, Typography } from '@material-ui/core'

export default function Flight({ data }) {
    return(
        <Grid item>
            <Typography variant="h6" gutterBottom>Flight from { data.departureAirport } to { data.arrivalAirport }</Typography>
            <Typography variant="subtitle2" gutterBottom>Departure: { data.departureDate }</Typography>
            <Typography variant="subtitle2" gutterBottom>Arrival: { data.arrivalDate }</Typography>
            <Typography variant="body2" gutterBottom>{ data.description }</Typography>
        </Grid>
    )
}

import React from 'react'
import { Box, Grid, Typography } from '@material-ui/core'

/*
Takes data in this form:
        {
          finalized: true,
          type: "flight",
          departureAirport: "BOS",
          arrivalAirport: "SFO",
          departureDate: "4:00pm EST",
          arrivalDate: "7:00pm PST",
          description: "Additional notes"
        }

*/
export default function Flight({ data }) {
    return(
        <Grid item>
            <Typography variant="h6" gutterBottom>Flight from { data.departureAirport } to { data.arrivalAirport }</Typography>
            <Typography variant="subtitle2" gutterBottom>Departure: { data.departureDate }</Typography>
            <Typography variant="subtitle2" gutterBottom>Arrival: { data.arrivalDate }</Typography>
            <Typography variant="body2" gutterBottom>{ data.description }</Typography>
        </Grid>
    )
}

Add comment to clarify object type passed into props


angular.module("mirmindr")
.controller("subjectsCtrl", function($rootScope, $scope){
  $scope.newSub = {};
  $scope.addSub = function(form) {
    if(form.$valid) {
      $scope.userRef.child("subjects").push($scope.newSub);
      $scope.newSub = {};
    } else {
      $scope.showActionToast("Missing Something?");
    }
  }

  $scope.deleteSubject = function(sub) {
    var deleteConfirm = confirm("Are you sure you want to delete the subject? This will delete all tasks under this subject.");
    if (deleteConfirm) {
      $scope.subjects.$remove(sub);
    }
  };
});

angular.module("mirmindr")
.controller("subjectsCtrl", function($rootScope, $scope, $mdDialog){
  $scope.newSub = {};
  $scope.addSub = function(form) {
    if(form.$valid) {
      $scope.userRef.child("subjects").push($scope.newSub);
      $scope.newSub = {};
    } else {
      $scope.showActionToast("Missing Something?");
    }
  }

//   $scope.deleteSubject = function(sub) {
//     var deleteConfirm = confirm("Are you sure you want to delete the subject? This will delete all tasks under this subject.");
//     if (deleteConfirm) {
//       $scope.subjects.$remove(sub);
//     }
//   };
// });

$scope.deleteSubject = function(sub) {
    var confirm = $mdDialog.confirm()
          .title('Are you sure you want to delete the subject?')
          .textContent('This will also delete all tasks under this subject.\nThis cannot be undone.')
          .ok('Yes')
          .cancel('No');
    $mdDialog.show(confirm).then(function() {
      $scope.subjects.$remove(sub);
    }, function() {
    });
  };
});

Delete subject dialog is now an mdDialog


$('#title').addClass('redtitle');

var json = {
   "room":{
      "name":"FangDev",
      "members":"Salgat:1452401384430,Fangfang2:1452401457085",
      "messages":[
         "Salgat: I love you more xiaobei",
         "Fangfang: I love you Laobei"
      ]
   }
};

function removeMessages(){
    $('li').remove();
}

function showMessages(messages){
    var arrayLength = messages.length;
    for (var i = 0; i < arrayLength; i++) {
        $('#messages').find('ul').append('<li>' + messages[i] + '</li>');
    }
}

window.setInterval(function(){
  $.post( "http://singleendpointchatserver.herokuapp.com/api/v1/chatroom/FangDev?username=Fangfang&expireafter=60", function( json ) {
    var messages = json.room.messages;
    removeMessages();
    showMessages(messages);
  });
}, 1000);

$('#messageSubmit').on('click', function(event) {
  var message = $('#messageInput').val();
  $('#messageInput').val('');
  console.log("Button pressed with value: " + message);
  var encodedMessage = encodeURIComponent(message);
  $.post( "http://singleendpointchatserver.herokuapp.com/api/v1/chatroom/FangDev?username=Fangfang&message="+encodedMessage+"&expireafter=60");
});

$('#title').addClass('redtitle');

var json = {
   "room":{
      "name":"FangDev",
      "members":"Salgat:1452401384430,Fangfang2:1452401457085",
      "messages":[
         "Salgat: I love you more xiaobei",
         "Fangfang: I love you Laobei"
      ]
   }
};

function removeMessages(){
    $('#messages').find('li').remove();
}

function showMessages(messages){
    var arrayLength = messages.length;
    for (var i = 0; i < arrayLength; i++) {
        $('#messages').find('ul').append('<li>' + messages[i] + '</li>');
    }
}

window.setInterval(function(){
  $.post( "http://singleendpointchatserver.herokuapp.com/api/v1/chatroom/FangDev?username=Fangfang&expireafter=60", function( json ) {
    var messages = json.room.messages;
    removeMessages();
    showMessages(messages);
  });
}, 1000);

$('#messageSubmit').on('click', function(event) {
  var message = $('#messageInput').val();
  $('#messageInput').val('');
  var encodedMessage = encodeURIComponent(message);
  $.post( "http://singleendpointchatserver.herokuapp.com/api/v1/chatroom/FangDev?username=Fangfang&message="+encodedMessage+"&expireafter=60");
});

Update selector for removing li


module.exports = (config) => {
  config.set({
    frameworks: ['mocha', 'karma-typescript'],
    browsers: ['chromeHeadless'],
    singleRun: true,
    concurrency: Infinity,
    reporters: ['mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_WARN,

    files: [
      'src/*.ts',
    ],

    preprocessors: {
      '**/*.ts': ['karma-typescript'],
    },

    karmaTypescriptConfig: {
      compilerOptions: {
        target: 'es6',
      },
    },

    customLaunchers: {
      chromeHeadless: {
        base: 'Chrome',
        flags: [
          '--headless',
          '--disable-gpu',
          '--remote-debugging-port=9222',
        ],
      },
    },

  })
};

module.exports = (config) => {
  config.set({
    frameworks: ['mocha', 'karma-typescript'],
    browsers: ['ChromeHeadless'],
    singleRun: true,
    concurrency: Infinity,
    reporters: ['mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_WARN,

    files: [
      'src/*.ts',
    ],

    preprocessors: {
      '**/*.ts': ['karma-typescript'],
    },

    karmaTypescriptConfig: {
      compilerOptions: {
        target: 'es6',
      },
    },
  })
};

Use karma's built-in chrome headless mode


'use strict'

var browsers = ['Chrome', 'Firefox', 'PhantomJS']

if (process.env.TRAVIS) {
  browsers = ['Firefox', 'PhantomJS']
}

module.exports = function(karma) {
  karma.set({
    browsers: browsers,
    phantomjsLauncher: {
      exitOnResourceError: true,
    },
    frameworks: [
      'browserify',
      'tap',
      'sinon',
    ],
    files: [
      'test/**/*.js',
    ],
    preprocessors: {
      'test/**/*.js': ['browserify'],
    },
    reporters: [
      process.env.TRAVIS ? 'dots' : 'progress',
    ],
  })
}

'use strict'

var browsers = ['Chrome', 'FirefoxDeveloper', 'PhantomJS']

if (process.env.TRAVIS) {
  browsers = ['PhantomJS']
}

module.exports = function(karma) {
  karma.set({
    browsers: browsers,
    phantomjsLauncher: {
      exitOnResourceError: true,
    },
    frameworks: [
      'browserify',
      'tap',
      'sinon',
    ],
    files: [
      'test/**/*.js',
    ],
    preprocessors: {
      'test/**/*.js': ['browserify'],
    },
    reporters: [
      process.env.TRAVIS ? 'dots' : 'progress',
    ],
  })
}

Disable Firefox tests on TravisCI


module.exports = function(config) {
    config.set({

        client: {
            captureConsole: false
        },

        plugins: [
            'karma-chai',
            'karma-sinon',
            'karma-mocha',
            'karma-chrome-launcher'
        ],

        frameworks: ['mocha', 'sinon', 'chai'],

        files: [
            //- lib
            "lib/three.js/build/three.js",
            "lib/ColladaLoader/index.js",
            "lib/isMobile/isMobile.min.js",
            "lib/detector/index.js",

            //- app
            "src/javascript/helpers/*.js",
            "src/javascript/control/*.js",
            "src/javascript/models/*.js",
            "src/javascript/scene/*.js",
            "src/javascript/simulator/*.js",

            //- test
            'test/utils/**/*.js',
            'test/unit/**/*.js'
        ],

        reporters: ['progress'],
        browsers: ['Chrome'],

        singleRun: false
    });
}

module.exports = function(config) {
    var configuration = {

        client: {
            captureConsole: false
        },

        plugins: [
            'karma-chai',
            'karma-sinon',
            'karma-mocha',
            'karma-chrome-launcher'
        ],

        frameworks: ['mocha', 'sinon', 'chai'],

        files: [
            //- lib
            "lib/three.js/build/three.js",
            "lib/ColladaLoader/index.js",
            "lib/isMobile/isMobile.min.js",
            "lib/detector/index.js",

            //- app
            "src/javascript/helpers/*.js",
            "src/javascript/control/*.js",
            "src/javascript/models/*.js",
            "src/javascript/scene/*.js",
            "src/javascript/simulator/*.js",

            //- test
            'test/utils/**/*.js',
            'test/unit/**/*.js'
        ],

        reporters: ['progress'],
        browsers: ['Chrome'],

        singleRun: false
    }

    if(process.env.TRAVIS){
        configuration.browsers = ['Chrome_travis_ci'];
    }

    config.set(configuration);
}

Add GUI support for Travis


'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        }
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')

    // Configure tasks
    grunt.registerTask('default', ['test'])
    grunt.registerTask('test', ['mochacli', 'jshint'])
}

'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 97,
                        statements: 98
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

Update grunt file to generate coverage reports


'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 97,
                        statements: 98
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 98,
                        statements: 99
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

Update coverage values to match existing


import io from 'socket.io-client';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { App } from './components/app';
import { GameContainer } from './components/game';
import createStore from './store';

import './index.scss';

const socket = io('http://localhost:3000');
const store = createStore(socket);

render(
  <Provider store={store}>
    <App>
      <GameContainer socket={socket} store={store}/>
    </App>
  </Provider>,
  document.getElementById('root')
);

import io from 'socket.io-client';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { App } from './components/app';
import { GameContainer } from './components/game';
import createStore from './store';

import './index.scss';

const socket = io();
const store = createStore(socket);

render(
  <Provider store={store}>
    <App>
      <GameContainer socket={socket} store={store}/>
    </App>
  </Provider>,
  document.getElementById('root')
);

Change SocketIO client to use a dynamic host


export function _fetch(url, method, data) {
  const options = {
    headers: {
      'Accept': 'application/json',
    },
    mode: 'cors',
    method: method || 'GET',
  }

  if (url.includes('https://inspire.data.gouv.fr/dgv/api')) {
    options.credentials = 'include'
  }

  if (data) options.body = JSON.stringify(data)

  return fetch(url, options)
    .then(response => {
      if (response.status === 500) throw new Error('Internal Server Error')
      if (response.status === 401) throw new Error('Unauthorized')
      if (response.status === 404) throw new Error('Not found')
      if (response.status === 202) return
      if (response.status === 204) return
      return response.json()
    })
    .catch((err) => {
      throw err
    });
}

export function _get(url) {
  return _fetch(url)
}

export function _put(url, data) {
  return _fetch(url, 'PUT', data)
}

export function _post(url, data) {
  return _fetch(url, 'POST', data)
}

export function _delete(url) {
  return _fetch(url, 'DELETE')
}

export function _fetch(url, method, data) {
  const options = {
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    mode: 'cors',
    method: method || 'GET',
  }

  if (url.includes('https://inspire.data.gouv.fr/dgv/api')) {
    options.credentials = 'include'
  }

  if (data) options.body = JSON.stringify(data)

  return fetch(url, options)
    .then(response => {
      if (response.status === 500) throw new Error('Internal Server Error')
      if (response.status === 401) throw new Error('Unauthorized')
      if (response.status === 404) throw new Error('Not found')
      if (response.status === 202) return
      if (response.status === 204) return
      return response.json()
    })
    .catch((err) => {
      throw err
    });
}

export function _get(url) {
  return _fetch(url)
}

export function _put(url, data) {
  return _fetch(url, 'PUT', data)
}

export function _post(url, data) {
  return _fetch(url, 'POST', data)
}

export function _delete(url) {
  return _fetch(url, 'DELETE')
}

Fix headers sent with fetch


var fs, path, mkdirp

function toFileName(delimiter, name) {
  if (delimiter === "") {
    return name
  }

  return name
    .replace(/[a-z][A-Z]/g, function(str) {
      return str[0] + delimiter + str[1]
    })
    .toLowerCase()
}

if (typeof IN_BROWSER === "undefined") {
  fs = require("fs")
  path = require("path")
  mkdirp = require("mkdirp")

  function writeToFS(components, options) {
    options = options || {}

    var outPath = path.resolve(options.path || "components")
    var delimiter = options.moduleFileNameDelimiter || ""
    var ext = options.fileExtension || "js"

    mkdirp.sync(outPath)

    Object.keys(components).forEach(function(name) {
      fs.writeFileSync(
        path.join(outPath, toFileName(delimiter, name)) + "." + ext,
        components[name],
        "utf8"
      )
    })
  }

  module.exports = {
    toFileName: toFileName,
    writeToFS: writeToFS
  }
} else {
  module.exports = {
    toFileName: toFileName
  }
}

var fs, path, mkdirp

function toFileName(delimiter, name) {
  if (delimiter === "") {
    return name
  }

  return name
    .replace(/[a-z][A-Z]/g, function(str) {
      return str[0] + delimiter + str[1]
    })
    .toLowerCase()
}

if (typeof IN_BROWSER === "undefined") {
  fs = require("fs")
  path = require("path")
  mkdirp = require("mkdirp")

  function writeToFS(components, options) {
    options = options || {}

    var outPath = path.resolve(options.output && options.output.path ? options.output.path : "components")
    var delimiter = options.moduleFileNameDelimiter || ""
    var ext = options.output && options.output.fileExtension ? options.output.fileExtension : "js"

    mkdirp.sync(outPath)

    Object.keys(components).forEach(function(name) {
      fs.writeFileSync(
        path.join(outPath, toFileName(delimiter, name)) + "." + ext,
        components[name],
        "utf8"
      )
    })
  }

  module.exports = {
    toFileName: toFileName,
    writeToFS: writeToFS
  }
} else {
  module.exports = {
    toFileName: toFileName
  }
}

Fix for ignoring passed in output directory and file extension


require('@babel/polyfill')
require('@babel/register')

const chokidar = require('chokidar')
const fs = require('fs')
const http = require('http')

const config = require('../config').env[process.env.NODE_ENV]
const PORT = process.env.PORT || config.PORT
const REPO_ROOT = config.REPO_ROOT

// disable cache when file changed
if (process.env.NODE_ENV === 'development') {
  const watcher = chokidar.watch('.')
  watcher.on('ready', () =>
    watcher.on('all', (event, filepath) => {
      console.log(`File ${filepath} changed. Clear module cache...`)
      Object.keys(require.cache).forEach(id => {
        if (!id.includes('node_modules')) delete require.cache[id]
      })
    })
  )
}

// requiring app on every request to enable module reloading
const server = http.createServer(function() {
  require('./app').default(...arguments)
})

server.listen(PORT, () => {
  // create repos directory
  if (!fs.existsSync(REPO_ROOT)) {
    fs.mkdirSync(REPO_ROOT)
  }
  console.log(`start on port ${PORT}`)
})

require('@babel/polyfill')
require('@babel/register')

const chokidar = require('chokidar')
const fs = require('fs')
const http = require('http')

const config = require('../config').env[process.env.NODE_ENV]
const PORT = process.env.PORT || config.PORT
const REPO_ROOT = config.REPO_ROOT

// disable cache when file changed
if (process.env.NODE_ENV === 'development') {
  const watcher = chokidar.watch('{app,models,shared}/**')
  watcher.on('ready', () =>
    watcher.on('all', (event, filepath) => {
      console.log(`File ${filepath} changed. Clear module cache...`)
      ;['app/app.js', filepath].forEach(
        f =>
          delete require.cache[require.resolve(f, { paths: [process.cwd()] })]
      )
    })
  )
}

// requiring app on every request to enable module reloading
const server = http.createServer(function() {
  require('./app').default(...arguments)
})

server.listen(PORT, () => {
  // create repos directory
  if (!fs.existsSync(REPO_ROOT)) {
    fs.mkdirSync(REPO_ROOT)
  }
  console.log(`start on port ${PORT}`)
})

Delete module caches only changed files


import iframeBuilder from 'src/helpers/iframeBuilder';

describe('iframe Builder', () => {
  describe('getDocument', () => {
    it('should get the document of an iframe', () => {
      // Initialise data
      const iframeTestObj = document.createElement('iframe');

      // Insert iframe to DOM tree, this will construct the iframe
      document.body.appendChild(iframeTestObj);
      const expectedResult = iframeTestObj.contentWindow.document;
      const outputResult = iframeBuilder.getDocument(iframeTestObj);
      expect(outputResult).to.equal(expectedResult);

      // Clean up
      document.body.removeChild(iframeTestObj);
    });

    it('should get null if the iframe is not in the DOM tree', () => {
      const iframeTestObj = document.createElement('iframe');
      expect(() => {
        iframeBuilder.getDocument(iframeTestObj);
      }).to.throw(Error);
    });
  });
});

import iframeBuilder from 'src/helpers/iframeBuilder';

describe('iframe Builder', () => {
  describe('getDocument', () => {
    it('should get the document of an iframe', () => {
      // Initialise data
      const iframeTestObj = document.createElement('iframe');

      // Insert iframe to DOM tree, this will construct the iframe
      document.body.appendChild(iframeTestObj);
      const expectedResult = iframeTestObj.contentWindow.document;
      const outputResult = iframeBuilder.getDocument(iframeTestObj);
      expect(outputResult).to.equal(expectedResult);

      // Clean up
      document.body.removeChild(iframeTestObj);
    });

    it('should get null if the iframe is not in the DOM tree', () => {
      const iframeTestObj = document.createElement('iframe');
      expect(() => {
        iframeBuilder.getDocument(iframeTestObj);
      }).to.throw(Error);
    });
  });
  describe('rebuild', () => {
    it('should return a document with proper doctype', () => {
      // Initialise data
      const iframeTestObj = document.createElement('iframe');
      document.body.appendChild(iframeTestObj);
      iframeBuilder.rebuild(iframeTestObj);

      const frameDoc = iframeTestObj.contentWindow.document;

      // .doctype is a null if the document does not have a doctype
      expect(frameDoc.doctype).to.not.be.a.null;
      if (frameDoc.doctype) {
        expect(frameDoc.doctype.name).to.equal('html');
      }

      document.body.removeChild(iframeTestObj);
    });

    it('should return a document of HTML with head and body', () => {
      // Initialise data
      const iframeTestObj = document.createElement('iframe');
      document.body.appendChild(iframeTestObj);
      iframeBuilder.rebuild(iframeTestObj);

      const frameDoc = iframeTestObj.contentWindow.document;

      expect(frameDoc.head).to.not.be.a.null;
      expect(frameDoc.body).to.not.be.a.null;

      document.body.removeChild(iframeTestObj);
    });
  });

  // TODO Write test for addStyle
    // describe('addStyle', () => {
    // describe('addStyles', () => {
});

Add test for iframebuilder rebuild


(function() {
  "use strict";
  GOVUK.Tracker.load();
  var cookieDomain = (document.domain === 'www.beta.digitalmarketplace.service.gov.uk') ? '.digitalmarketplace.service.gov.uk' : document.domain;
  GOVUK.analytics = new GOVUK.Tracker({
    universalId: 'UA-49258698-3',
    cookieDomain: cookieDomain
  });
  GOVUK.analytics.trackPageview();
})();

(function() {
  "use strict";
  GOVUK.Tracker.load();
  var cookieDomain = (document.domain === 'www.beta.digitalmarketplace.service.gov.uk') ? '.digitalmarketplace.service.gov.uk' : document.domain;
  var property = (document.domain === 'www.digitalmarketplace.service.gov.uk') ? 'UA-49258698-1' : 'UA-49258698-3';
  GOVUK.analytics = new GOVUK.Tracker({
    universalId: property,
    cookieDomain: cookieDomain
  });
  GOVUK.analytics.trackPageview();
})();

Use correct analytics properties for live/other


Meteor.methods({
  updateCount: function(count) {
    AlertsCollection.insert({
      'clicked' : count
    });
  }
});

Meteor.methods({
  feedContentResponse: function(feedId, reponse) {
    console.log(feedId, reponse);
    AlertsCollection.update({_id: feedId}, {$set:{"status": reponse}});
    console.log("Done");
  }
});

Create feed content response method to update alerts collection


module.exports = {
  render: require('./render'),
  builtInStore: require('./built-in-store'),
  actionBuilder: require('./action-builder'),
  clear: require('./clear'),
  mountedComponents: require('./mounted-components')
};

var dispatcher = require('../dispatcher');
module.exports = {
  render: require('./render'),
  builtInStore: require('./built-in-store'),
  actionBuilder: require('./action-builder'),
  clear: require('./clear'),
  mountedComponents: require('./mounted-components'),
  changeMode(newMode, previousMode){
    var mode = {newMode: newMode, previousMode: previousMode};
    dispatcher.handleViewAction({data: {mode: mode}, type: 'update'});
  },
  changeRoute(newRoute){
    dispatcher.handleViewAction({data: {route: newRoute}, type: 'update'});
  }
};

Add helper method to change mode and route on the application level.


module.exports = bindArgs

function bindArgs (func) {
  var boundArgs = [].slice.call(arguments, 1)
  return function () {
    func.apply(this, boundArgs.concat(arguments))
  }
}

module.exports = bindArgs

function bindArgs (func) {
  var slice = [].slice
  var boundArgs = slice.call(arguments, 1)
  return function () {
    var args = slice.call(arguments)
    func.apply(this, boundArgs.concat(args))
  }
}

Fix bug: `arguments` can not be concerted as array


Template.postsList.helpers({
  posts: function() {
    return Posts.find();
  }
});

Template.postsList.helpers({
  posts: function() {
    return Posts.find({}, { sort: { submitted: -1 }});
  }
});

Sort posts by submission date


var tape = require("tape")

var evalScript = require("../../lib/eval-script")

tape("test evalScript method", function(t) {
  document.body.className = ""

  var script = document.createElement("script")
  script.innerHTML = "document.body.className = 'executed'"

  t.equal(document.body.className, "", "script hasn't been executed yet")

  evalScript(script)
  t.equal(document.body.className, "executed", "script has been properly executed")

  // script.innerHTML = "document.write('failure')"
  // var bodyText = document.body.text
  // evalScript(script)
  // t.equal(document.body.text, bodyText, "document.write hasn't been executed")

  t.end()
})

var tape = require("tape")

var evalScript = require("../../lib/eval-script")

tape("test evalScript method", function(t) {
  document.body.className = ""

  var script = document.createElement("script")
  script.innerHTML = "document.body.className = 'executed'"

  t.equal(document.body.className, "", "script hasn't been executed yet")

  evalScript(script)
  t.equal(document.body.className, "executed", "script has been properly executed")

  script.innerHTML = "document.write('failure')"
  document.body.text = "document.write hasn't been executed"
  var bodyText = document.body.text
  evalScript(script)
  t.equal(document.body.text, bodyText, "document.write hasn't been executed")

  t.end()
})

Enable test for `document.write` in eval'd scripts


function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Monad
Id.of = function(a) {
    return new Id(a);
};

if(typeof module == 'object') module.exports = Id;

function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Foldable
Id.prototype.reduce = function(f, acc) {
    return f(acc, this.value);
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Traversable
Id.prototype.traverse = function(f, of) {
    return f(this.value).map(function(y){ return new Id(y); });
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Monad
Id.of = function(a) {
    return new Id(a);
};

if(typeof module == 'object') module.exports = Id;

Add traversable and foldable to Id


Zepto(function($) {
  prettyPrint();

  var $frameContainer = $('.frames-container');
  var $container      = $('.details-container');
  var $activeLine     = $frameContainer.find('.frame.active');
  var $activeFrame    = $container.find('.frame-code.active');
  var headerHeight    = $('header').height();

  var highlightCurrentLine = function() {
    // Highlight the active and neighboring lines for this frame:
    var activeLineNumber = +($activeLine.find('.frame-line').text());
    var $lines           = $activeFrame.find('.linenums li');
    var firstLine        = +($lines.first().val());

    $($lines[activeLineNumber - firstLine - 1]).addClass('current');
    $($lines[activeLineNumber - firstLine]).addClass('current active');
    $($lines[activeLineNumber - firstLine + 1]).addClass('current');
  }

  // Highlight the active for the first frame:
  highlightCurrentLine();

  $frameContainer.delegate('.frame', 'click', function() {
    var $this  = $(this);
    var id     = /frame\-line\-([\d]*)/.exec($this.attr('id'))[1];
    var $codeFrame = $('#frame-code-' + id);

    if ($codeFrame) {
      $activeLine.removeClass('active');
      $activeFrame.removeClass('active');

      $this.addClass('active');
      $codeFrame.addClass('active');

      $activeLine  = $this;
      $activeFrame = $codeFrame;

      highlightCurrentLine();

      $container.scrollTop(headerHeight);
    }
  });
});

Zepto(function($) {
  prettyPrint();

  var $frameContainer = $('.frames-container');
  var $container      = $('.details-container');
  var $activeLine     = $frameContainer.find('.frame.active');
  var $activeFrame    = $container.find('.frame-code.active');
  var headerHeight    = $('header').height();

  var highlightCurrentLine = function() {
    // Highlight the active and neighboring lines for this frame:
    var activeLineNumber = +($activeLine.find('.frame-line').text());
    var $lines           = $activeFrame.find('.linenums li');
    var firstLine        = +($lines.first().val());

    $($lines[activeLineNumber - firstLine - 1]).addClass('current');
    $($lines[activeLineNumber - firstLine]).addClass('current active');
    $($lines[activeLineNumber - firstLine + 1]).addClass('current');
  }

  // Highlight the active for the first frame:
  highlightCurrentLine();

  $frameContainer.on('click', '.frame', function() {
    var $this  = $(this);
    var id     = /frame\-line\-([\d]*)/.exec($this.attr('id'))[1];
    var $codeFrame = $('#frame-code-' + id);

    if ($codeFrame) {
      $activeLine.removeClass('active');
      $activeFrame.removeClass('active');

      $this.addClass('active');
      $codeFrame.addClass('active');

      $activeLine  = $this;
      $activeFrame = $codeFrame;

      highlightCurrentLine();

      $container.scrollTop(headerHeight);
    }
  });
});

Use on() instead of delegate().


;(function(lander){
    console.log('Moon Lander Visualisation');

    var display = document.getElementById('display');
    var horizon = new Array(display.width);
    horizon[0] = 50;
    for (var index = 1; index < display.width; index++){
        horizon[index] = horizon[index - 1] + 2 * (Math.random() - 0.5);
    }

    var model = {
        "lander": {
            "x": 37, "y": 251,
            "vx": 0, "vy": 0,
            "orientation": Math.PI/4, "angular-velocity": 0,
            "radius": 10,
            "fuel": 1
        },
        "horizon": horizon
    };

    new lander.View(model, display);
})(lander);

;(function(lander){
    console.log('Moon Lander Visualisation');

    var display = document.getElementById('display');
    var horizon = new Array(display.width);
    horizon[0] = 50;
    for (var index = 1; index < display.width; index++){
        horizon[index] = horizon[index - 1] + 2 * (Math.random() - 0.5);
    }

    var model = {
        "lander": {
            "x": 37, "y": 251,
            "vx": 0, "vy": 0,
            "orientation": Math.PI/4, "angular-velocity": 0,
            "radius": 10,
            "fuel": 1
        },
        "horizon": horizon
    };

    var view = new lander.View(model, display);
    function tick(){
        view.update();
        requestAnimationFrame(tick);
    };
    tick();
})(lander);

Update view on each animation frame


Update compiled JS with latest


CRM.$(function ($) {
  $('.fast-action-link').click(function () {

    link = $(this)[0];
    // Show a confirmation dialog if appropriate
    if ($(this).hasClass('fast-action-link-confirm')) {
    CRM.confirm("Are you sure you want to take this action?")
      .on('crmConfirm:yes', function () {
          execute(link);
        });
    } else {
      execute(link);
    }
    // Suppress the link-following behavior
    return false;
  });
});

/*
 * Make an API call to FastActionLink.execute and update search results with
 * notifications and/or dimming the search result.
 */
function execute(link) {
  var falId = link.getAttribute('falid');
  var entityId = link.getAttribute('entityid');
  var params = {"id": falId, "entity_id": entityId};
  CRM.api3('FastActionLink', 'execute', params)
    .done(function (result) {
      if (result.is_error) {
        CRM.alert(result.error_message, "", "error");
      } else {
        CRM.alert(result.success_message, "", "success");
        if (result.dim_on_use == 1) {
          CRM.$(link).parents('tr').addClass('disabled');
        }
      }
    });
}

CRM.$(function ($) {
  $('.fast-action-link').click(function () {

    link = $(this)[0];
    // Show a confirmation dialog if appropriate
    if ($(this).hasClass('fast-action-link-confirm')) {
    CRM.confirm("Are you sure you want to take this action?")
      .on('crmConfirm:yes', function () {
          execute(link);
        });
    } else {
      execute(link);
    }
    // Suppress the link-following behavior
    return false;
  });
});

/*
 * Make an API call to FastActionLink.execute and update search results with
 * notifications and/or dimming the search result.
 */
function execute(link) {
  var falId = link.getAttribute('falid');
  var entityId = link.getAttribute('entityid');
  var params = {"id": falId, "entity_id": entityId};
  CRM.api3('FastActionLink', 'execute', params)
    .done(function (result) {
      if (result.is_error) {
        CRM.alert(result.error_message, "", "error");
      } else {
        if (!result.success_message) {
          result.success_message = "Action was successful";
        }
        CRM.alert(result.success_message, "", "success");
        if (result.dim_on_use == 1) {
          CRM.$(link).parents('tr').addClass('disabled');
        }
      }
    });
}

Handle error on blank hovertext


/* eslint func-names:0 */

const path = require('path');
const webpackConfig = require('../webpack');

module.exports = function (config) {
  config.set({
    browsers: ['PhantomJS'],

    singleRun: true,

    frameworks: ['mocha'],

    files: ['./test-bundler.js'],

    preprocessors: {
      './test-bundler.js': ['webpack', 'sourcemap'],
    },

    reporters: ['mocha', 'coverage'],

    webpack: webpackConfig,

    // Make Webpack bundle generation quiet
    webpackMiddleware: {
      noInfo: true,
      stats: 'errors-only',
    },

    // Set the format of reporter
    coverageReporter: {
      dir: path.join(process.cwd(), 'coverage'),
      reporters: [
        { type: 'html', subdir: 'html' },
        { type: 'lcov', subdir: 'lcov' },
        { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
      ],
    },
  });
};

/* eslint-disable */

var path = require('path');
var webpackConfig = require('../webpack');

module.exports = function (config) {
  config.set({
    browsers: ['PhantomJS'],

    singleRun: true,

    frameworks: ['mocha'],

    files: ['./test-bundler.js'],

    preprocessors: {
      './test-bundler.js': ['webpack', 'sourcemap'],
    },

    reporters: ['mocha', 'coverage'],

    webpack: webpackConfig,

    // Make Webpack bundle generation quiet
    webpackMiddleware: {
      noInfo: true,
      stats: 'errors-only',
    },

    // Set the format of reporter
    coverageReporter: {
      dir: path.join(process.cwd(), 'coverage'),
      reporters: [
        { type: 'html', subdir: 'html' },
        { type: 'lcov', subdir: 'lcov' },
        { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
      ],
    },
  });
};

Fix es6 error on node 5


var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;

selftest.define("report-stats", function () {
  var s = new Sandbox;

  run = s.run("create", "foo");
  run.expectExit(0);
  s.cd("foo");

  // verify that identifier file exists for new apps
  var identifier = s.read(".meteor/identifier");
  selftest.expectEqual(!! identifier, true);
  selftest.expectEqual(identifier.length > 0, true);

  // verify that identifier file when running 'meteor bundle' on old
  // apps
  s.unlink(".meteor/identifier");
  s.run("bundle", "foo.tar.gz");
  identifier = s.read(".meteor/identifier");
  selftest.expectEqual(!! identifier, true);
  selftest.expectEqual(identifier.length > 0, true);

  // TODO:
  // - test that we actually send stats
  // - opt out
  // - test both the logged in state and the logged out state
});

var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;

selftest.define("report-stats", function () {
  var s = new Sandbox;

  var run = s.run("create", "foo");
  run.expectExit(0);
  s.cd("foo");

  // verify that identifier file exists for new apps
  var identifier = s.read(".meteor/identifier");
  selftest.expectEqual(!! identifier, true);
  selftest.expectEqual(identifier.length > 0, true);

  // verify that identifier file when running 'meteor bundle' on old
  // apps
  s.unlink(".meteor/identifier");
  run = s.run("bundle", "foo.tar.gz");
  run.waitSecs(30);
  run.expectExit(0);
  identifier = s.read(".meteor/identifier");
  selftest.expectEqual(!! identifier, true);
  selftest.expectEqual(identifier.length > 0, true);

  // TODO:
  // - test that we actually send stats
  // - opt out
  // - test both the logged in state and the logged out state
});

Make our failing partial stats test pass


/* eslint-env amd */
(function (name, definition) {
  if (typeof define === 'function') {
    // AMD
    define(definition)
  } else if (typeof module !== 'undefined' && module.exports) {
    // Node.js
    module.exports = definition()
  } else {
    // Browser
    window[name] = definition()
  }
})('nullPrune', function () {
  function isObject (input) {
    return input && (typeof input === 'object')
  }

  function ownKeys (obj) {
    var key
    var ownKeys = []
    var hasOwnProperty = Object.prototype.hasOwnProperty

    for (key in obj) {
      if (hasOwnProperty.call(obj, key)) {
        ownKeys.push(key)
      }
    }

    return ownKeys
  }

  function nullPrune (inputObject, context) {
    var objectKey = context.objectKey
    var parentObject = context.parentObject
    var keys = ownKeys(inputObject)
    var k
    var key
    var node

    for (k in keys) {
      key = keys[k]
      node = inputObject[key]

      if (isObject(node)) {
        nullPrune(node, {
          objectKey: key,
          parentObject: inputObject
        })
      } else if (node == null) {
        delete inputObject[key]
      }
    }

    if (parentObject && ownKeys(inputObject).length === 0) {
      delete parentObject[objectKey]
    }
  }

  return function (inputObject) {
    if (!isObject(inputObject)) {
      return inputObject
    }

    nullPrune(inputObject, {})
    return inputObject
  }
})

(function (name, definition) {
  if (typeof define === 'function') {
    // AMD
    /* eslint-env amd */
    define(definition)
  } else if (typeof module !== 'undefined' && module.exports) {
    // Node.js
    module.exports = definition()
  } else {
    // Browser
    window[name] = definition()
  }
})('nullPrune', function () {
  function isObject (input) {
    return input && (typeof input === 'object')
  }

  function ownKeys (obj) {
    var key
    var ownKeys = []
    var hasOwnProperty = Object.prototype.hasOwnProperty

    for (key in obj) {
      if (hasOwnProperty.call(obj, key)) {
        ownKeys.push(key)
      }
    }

    return ownKeys
  }

  function nullPrune (inputObject, context) {
    var objectKey = context.objectKey
    var parentObject = context.parentObject
    var keys = ownKeys(inputObject)
    var k
    var key
    var node

    for (k in keys) {
      key = keys[k]
      node = inputObject[key]

      if (isObject(node)) {
        nullPrune(node, {
          objectKey: key,
          parentObject: inputObject
        })
      } else if (node == null) {
        delete inputObject[key]
      }
    }

    if (parentObject && ownKeys(inputObject).length === 0) {
      delete parentObject[objectKey]
    }
  }

  return function (inputObject) {
    if (!isObject(inputObject)) {
      return inputObject
    }

    nullPrune(inputObject, {})
    return inputObject
  }
})

Add the eslint-pragma for AMD to be more in context


const path = require("path");

function shouldIncludePath(filePath) {
  return path.extname(filePath) === ".sol";
}

module.exports = { shouldIncludePath };

const path = require("path");

function shouldIncludePath(filePath) {
  const validExtensions = [".sol", ".abi.json", ".json"];
  return validExtensions.some(extension => path.extname(filePath) === extension);
}

module.exports = { shouldIncludePath };

Add .abi.json and .json to whitelist for profiler


'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 100,
                        statements: 99
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            allFiles: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js', 'index.js'],
            options: {
                jshintrc: '.jshintrc',
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        'mocha_istanbul': {
            coveralls: {
                src: [ 'test/lib', 'test/lib/utils' ],
                options: {
                    coverage: true,
                    legend: true,
                    check: {
                        lines: 99,
                        statements: 99
                    },
                    root: './lib',
                    reportFormats: ['lcov']
                }
            }
        }
    })

    grunt.event.on('coverage', function(lcov, done){
        require('coveralls').handleInput(lcov, function(error) {
            if (error) {
                console.log(error)
                return done(error)
            }
            done()
        })
    })

    // Load the plugins
    grunt.loadNpmTasks('grunt-contrib-jshint')
    grunt.loadNpmTasks('grunt-mocha-cli')
    grunt.loadNpmTasks('grunt-mocha-istanbul')

    // Configure tasks
    grunt.registerTask('coveralls', ['mocha_istanbul:coveralls'])
    grunt.registerTask('default', ['test'])

    grunt.registerTask('test', ['mochacli', 'jshint', 'coveralls'])
}

Update coverage values to match current


//= require spree/frontend

SpreePaypalExpress = {
  hidePaymentSaveAndContinueButton: function(paymentMethod) {
    if (SpreePaypalExpress.paymentMethodID && paymentMethod.val() == SpreePaypalExpress.paymentMethodID) {
      $('.continue').hide();
    } else {
      $('.continue').show();
    }
  }
}

$(document).ready(function() {
  checkedPaymentMethod = $('div[data-hook="checkout_payment_step"] input[type="radio"]:checked');
  SpreePaypalExpress.hidePaymentSaveAndContinueButton(checkedPaymentMethod);
  paymentMethods = $('div[data-hook="checkout_payment_step"] input[type="radio"]').click(function (e) {
    SpreePaypalExpress.hidePaymentSaveAndContinueButton($(e.target));
  });
})

//= require spree/frontend

SpreePaypalExpress = {
  hidePaymentSaveAndContinueButton: function(paymentMethod) {
    if (!$('#use_existing_card_yes:checked').length && SpreePaypalExpress.paymentMethodID && paymentMethod.val() == SpreePaypalExpress.paymentMethodID) {
      $('.continue').hide();
    } else {
      $('.continue').show();
    }
  },
  checkedPaymentMethod: function() {
    return $('div[data-hook="checkout_payment_step"] input[type="radio"][name="order[payments_attributes][][payment_method_id]"]:checked');
  }
}

$(document).ready(function() {
  SpreePaypalExpress.hidePaymentSaveAndContinueButton(SpreePaypalExpress.checkedPaymentMethod());
  paymentMethods = $('div[data-hook="checkout_payment_step"] input[type="radio"]').click(function (e) {
    SpreePaypalExpress.hidePaymentSaveAndContinueButton(SpreePaypalExpress.checkedPaymentMethod());
  });
})

Fix "Save & Continue" + Payment Profiles.


(function($, window, _, undefined){

	$(function() {
		jQuery.extend(jQuery.validator.messages, {
			required : "Това поле е задължително",
			minlength : jQuery.validator.format("Моля въведете поне {0} символа")
		});
	});

	var appConfig = {
		projectName : "Diaphanum",
		textAreaValidationReq : {
			required: true,
			minlength: 140
		},
		nameSearchUrl : "http://localhost:8000/search/"
	};

	window.Diaphanum = {};
	window.Diaphanum.appConfig = appConfig;
	window.Diaphanum.validationRequirementsFromAttributes=function(inputNameValue) {
		var inputObject=$("input[name="+inputNameValue+"]");
		var resultObject = {};

		if ( inputObject.is('[maxlength]') ) {
			resultObject.maxlength=inputObject.attr('maxlength');
		}

		if ( inputObject.is('[minlength]') ) {
			resultObject.minlength=inputObject.attr('minlength');
		}

		if ( inputObject.is('[required]') ) {
			resultObject.required=true;
		}

		if ( inputObject.is("input[type=email]") ) {
			resultObject.email=true;
		}

		return resultObject;
	};
})($, window, _);

(function($, window, _, undefined){

	$(function() {
		jQuery.extend(jQuery.validator.messages, {
			required : "Това поле е задължително",
			minlength : jQuery.validator.format("Моля въведете поне {0} символа")
		});
	});

	var getHostLocation = function() {
		var loc = window.location;

		return loc.protocol + "//" + loc.hostname + ":" + loc.port;
	};

	var appConfig = {
		projectName : "Diaphanum",
		textAreaValidationReq : {
			required: true,
			minlength: 140
		},
		nameSearchUrl : getHostLocation() + "/search/"
	};

	window.Diaphanum = {};
	window.Diaphanum.appConfig = appConfig;
	window.Diaphanum.validationRequirementsFromAttributes=function(inputNameValue) {
		var inputObject=$("input[name="+inputNameValue+"]");
		var resultObject = {};

		if ( inputObject.is('[maxlength]') ) {
			resultObject.maxlength=inputObject.attr('maxlength');
		}

		if ( inputObject.is('[minlength]') ) {
			resultObject.minlength=inputObject.attr('minlength');
		}

		if ( inputObject.is('[required]') ) {
			resultObject.required=true;
		}

		if ( inputObject.is("input[type=email]") ) {
			resultObject.email=true;
		}

		return resultObject;
	};
})($, window, _);

Fix a bug with the hardcoded location for the name search API


#!/usr/bin/node
"use strict";
var child_process = require("child_process");
process.chdir(__dirname + "/src")
child_process.execFile("find", ["-type", "f"], function(err, stdout, stderr){
	var files = stdout.split("\n");
	files.pop();
	files.forEach(function(path, i){
		files[i] = path.slice(2);
	});
	files.splice(0, 0, "../pixi.js");
	child_process.execFile("mkcjs", files, function(err, stdout, stderr){
		if (stdout || stderr) console.log(stdout, stderr);
		child_process.execFile("uglifyjs", ["../pixi.js", "--screw-ie8", "-mco", "../pixi.min.js"], function(err, stdout, stderr){
			if (stdout || stderr)  console.log(stdout, stderr);
			process.exit();
		});
	});
});

#!/usr/bin/env node
"use strict";
var child_process = require("child_process");
process.chdir(__dirname + "/src")
child_process.execFile("find", ["-type", "f"], function(err, stdout, stderr){
	var files = stdout.split("\n");
	files.pop();
	files.forEach(function(path, i){
		files[i] = path.slice(2);
	});
	files.splice(0, 0, "../pixi.js");
	child_process.execFile("mkcjs", files, function(err, stdout, stderr){
		if (stdout || stderr) console.log(stdout, stderr);
		child_process.execFile("uglifyjs", ["../pixi.js", "--screw-ie8", "-mco", "../pixi.min.js"], function(err, stdout, stderr){
			if (stdout || stderr)  console.log(stdout, stderr);
			process.exit();
		});
	});
});

Use node binary defined by user environment


/* global $ */
import Ember from 'ember';
import { translationMacro as t } from 'ember-i18n';

export default Ember.Controller.extend({
    i18n: Ember.inject.service(),

    actions: {
        navigationClick(fieldGroupId) {
            $('html, body').animate({
                scrollTop: $('#field-group-' + fieldGroupId).offset().top - 10
            }, 300);
        }
    },

    entityType: t('definitions.zoological'),

    fieldGroups: [
        {
            title: 'field_group_basic_data',
            fields: [{
                type: 'autocomplete',
                title: 'definitions.name'
            }]
        }, {
            title: 'field_group_taxonomy',
            fields: []
        }, {
            title: 'field_group_site',
            fields: []
        }, {
            title: 'field_group_preparation',
            fields: []
        }, {
            title: 'field_group_other',
            fields: []
        }
    ]
});

/* global $ */
import Ember from 'ember';
import { translationMacro as t } from 'ember-i18n';

export default Ember.Controller.extend({
    i18n: Ember.inject.service(),

    actions: {
        navigationClick(fieldGroupId) {
            $('html, body').animate({
                scrollTop: $('#field-group-' + fieldGroupId).offset().top - 10
            }, 300);
        }
    },

    entityType: t('definitions.zoological'),

    // TODO: Collect these fieldGroups from a Service based on the
    // department the user belongs to.
    fieldGroups: [
        {
            title: 'field_group_basic_data',
            fields: [{
                type: 'autocomplete',
                title: 'definitions.name'
            }]
        }, {
            title: 'field_group_taxonomy',
            fields: []
        }, {
            title: 'field_group_site',
            fields: []
        }, {
            title: 'field_group_preparation',
            fields: []
        }, {
            title: 'field_group_other',
            fields: []
        }
    ]
});

Add todo about field groups


"use strict";

module.exports = function (grunt) {
    grunt.initConfig({
        eslint: {
            options: {
                config: ".eslintrc"
            },
            src: [
                "Gruntfile.js",
                "lib/**/*.js",
                "test/**/*.js"
            ]
        },

        mochaTest: {
            all: {
                src: "test/*.js",
                options: {
                    reporter: "spec"
                }
            }
        }
    });

    grunt.loadNpmTasks("grunt-eslint");
    grunt.loadNpmTasks("grunt-mocha-test");

    grunt.registerTask("default", [
        "eslint",
        "mochaTest"
    ]);
};

"use strict";

module.exports = function (grunt) {
    grunt.initConfig({
        eslint: {
            options: {
                config: ".eslintrc"
            },
            src: [
                "Gruntfile.js",
                "lib/**/*.js",
                "test/**/*.js"
            ]
        },

        mochaTest: {
            all: {
                src: "test/*-test.js",
                options: {
                    reporter: "spec"
                }
            }
        }
    });

    grunt.loadNpmTasks("grunt-eslint");
    grunt.loadNpmTasks("grunt-mocha-test");

    grunt.registerTask("default", [
        "eslint",
        "mochaTest"
    ]);
};

Set test files with "*-test.js" pattern.


const express = require('express')
const guildFeedsAPI = express.Router({ mergeParams: true })
const validate = require('../../../../middleware/validator.js')
const guildHasFeed = require('../../../../middleware/guildHasFeed.js')
const guildHasChannel = require('../../../../middleware/guildHasChannel.js')
const {
  isMongoID
} = require('../../../../util/validators/index.js')
const {
  param,
  body
} = require('express-validator')

guildFeedsAPI.use('/:feedID', validate([
  param('feedID')
    .custom(isMongoID).withMessage('Invalid resource ID')
]))

guildFeedsAPI.post('/', [
  validate([
    body(['url', 'channel'])
      .exists({ checkFalsy: true }).withMessage('Must exist')
      .isString().withMessage('Must be a string'),
  ]),
  guildHasChannel
], require('../../../../controllers/api/guilds/feeds/createFeed.js'))

guildFeedsAPI.use('/:feedID', guildHasFeed)

module.exports = guildsAPI

const express = require('express')
const guildFeedsAPI = express.Router({ mergeParams: true })
const validate = require('../../../../middleware/validator.js')
const guildHasFeed = require('../../../../middleware/guildHasFeed.js')
const guildHasChannel = require('../../../../middleware/guildHasChannel.js')
const guildHasChannelOptional = require('../../../../middleware/guildHasChannelOptional.js')
const {
  isMongoID
} = require('../../../../util/validators/index.js')
const {
  param,
  body
} = require('express-validator')

guildFeedsAPI.post('/', [
  validate([
    body(['url', 'channel'])
      .isString().withMessage('Must be a string')
      .notEmpty().withMessage('Cannot be empty'),
  ]),
  guildHasChannel
], require('../../../../controllers/api/guilds/feeds/createFeed.js'))

// Make sure feedID exists before proceeding
guildFeedsAPI.use('/:feedID', validate([
  param('feedID')
    .custom(isMongoID).withMessage('Invalid resource ID')
]))

// Make sure the guild has this feed
guildFeedsAPI.use('/:feedID', guildHasFeed)

// Edit the feed
guildFeedsAPI.patch('/:feedID', [
  validate([
    body('channel')
      .if(val => !!val)
      .isString().withMessage('Must be a string')
      .notEmpty().withMessage('Cannot be empty'),
    body('title')
      .if(val => !!val)
      .isString().withMessage('Must be a string')
      .notEmpty().withMessage('Cannot be empty')
  ]),
  guildHasChannelOptional
], require('../../../../controllers/api/guilds/feeds/editFeed.js'))

module.exports = guildsAPI

Add PATCH guilds/:guildID/feeds/:feedID route Also fix feedID validation order


import Ember from 'ember';
import layout from '../templates/components/freestyle-collection';

const { computed, inject } = Ember;

export default Ember.Component.extend({
  layout,
  classNames: ['FreestyleCollection'],
  classNameBindings: ['inline:FreestyleCollection--inline'],

  emberFreestyle: inject.service(),
  showLabels: computed.and('emberFreestyle.notFocused', 'emberFreestyle.showLabels'),
  hasLabels: computed.and('showLabels', 'title'),
  showVariantList: computed.not('emberFreestyle.focus'),

  defaultKey: 'all',
  activeKey: computed('defaultKey', 'key', 'emberFreestyle.focus', function() {
    if (this.get('emberFreestyle.focus')) {
      return 'all';
    }
    return this.get('key') || this.get('defaultKey');
  }),

  registerVariant(variantKey) {
    Ember.run.next(() => {
      let variants = this.get('variants') || Ember.A(['all']);
      if (!variants.includes(variantKey)) {
        variants.pushObject(variantKey);
      }
      this.set('variants', variants);
    });
  },

  actions: {
    setKey(key) {
      this.set('key', key);
    }
  }
});

import Ember from 'ember';
import layout from '../templates/components/freestyle-collection';

const { computed, inject } = Ember;

export default Ember.Component.extend({
  layout,
  classNames: ['FreestyleCollection'],
  classNameBindings: ['inline:FreestyleCollection--inline'],

  emberFreestyle: inject.service(),
  showLabels: computed.and('emberFreestyle.notFocused', 'emberFreestyle.showLabels'),
  hasLabels: computed.and('showLabels', 'title'),
  showVariantList: computed.not('emberFreestyle.focus'),

  defaultKey: 'all',
  activeKey: computed('defaultKey', 'key', 'emberFreestyle.focus', function() {
    if (this.get('emberFreestyle.focus')) {
      return 'all';
    }
    return this.get('key') || this.get('defaultKey');
  }),

  registerVariant(variantKey) {
    Ember.run.next(() => {
      if (this.isDestroyed) { return; }
      let variants = this.get('variants') || Ember.A(['all']);
      if (!variants.includes(variantKey)) {
        variants.pushObject(variantKey);
      }
      this.set('variants', variants);
    });
  },

  actions: {
    setKey(key) {
      this.set('key', key);
    }
  }
});

Fix 'calling set on destroyed object' error


import $ from 'jquery';
import _ from 'lodash/fp';
import { match } from 'wildstring';
import beautify from './common/beautify';

function isMatched(needle, haystack) {
  return _.some(rule => match(rule, needle))(haystack);
}

function onLoad() {
  const href = _.getOr('')('location.href')(document);

  chrome.storage.sync.get({ whitelist: [] }, ({ whitelist }) => {
    if (!isMatched(href, whitelist)) {
      return;
    }

    beautify();
  });
}

$(document).ready(onLoad);

import $ from 'jquery';
import some from 'lodash/fp/some';
import getOr from 'lodash/fp/getOr';
import { match } from 'wildstring';
import beautify from './common/beautify';

function isMatched(needle, haystack) {
  return some(rule => match(rule, needle))(haystack);
}

function onLoad() {
  const href = getOr('')('location.href')(document);

  chrome.storage.sync.get({ whitelist: [] }, ({ whitelist }) => {
    if (!isMatched(href, whitelist)) {
      return;
    }

    beautify();
  });
}

$(document).ready(onLoad);

Use partially module of lodash instead of full package


const selectors = require('../../../../selectors')

describe('ompany Collections', () => {
  before(() => {
    cy.visit(`/companies`)
  })

  it('should return the results summary for a company collection', () => {
    cy.get(selectors.collection.headerCount).invoke('text').then((headerCount) => {
      cy.get(selectors.collection.items).should((collectionItems) => {
        expect(headerCount).to.eq(collectionItems.length.toString())
      })
    })
  })
})

const selectors = require('../../../../selectors')

describe('company Collections', () => {
  before(() => {
    cy.visit(`/companies`)
  })

  it('should return the results summary for a company collection', () => {
    cy.get(selectors.collection.headerCount).invoke('text').then((headerCount) => {
      cy.get(selectors.collection.items).should((collectionItems) => {
        expect(headerCount).to.eq(collectionItems.length.toString())
      })
    })
  })
})

Fix typo on test name


import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  currentUser: Ember.inject.service('current-user'),
  model: function() {
    let user_id = this.get('currentUser.user.id');
    return this.get('store').query('convention', {
      'assignments__person__user': user_id,
    });
  },
});

import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Ember.Route.extend(AuthenticatedRouteMixin, {
  currentUser: Ember.inject.service('current-user'),
  model: function() {
    let user_id = this.get('currentUser.user.id');
    return this.get('store').query('convention', {
      'assignments__person__user': user_id,
      'assignments__kind': 5 //Hard-coded
    });
  },
});

Add filter to the route


$( document ).ready(function() {
    var url = $.url();
    var query = url.param("q");

    if (typeof query != 'undefined')
    {
        $("#left").attr("src", "https://www.bing.com/search?q=" + encodeURIComponent(query));
        $("#right").attr("src", "https://www.google.com/search?q=" + encodeURIComponent(query));
        document.title = query.replace(/</g, "<").replace(/>/g, ">") + " - Double Shot Search";
    }
});

const shopPrefix = "shop ";
$( document ).ready(function() {
    let left = "https://www.bing.com/search?q=";
    let right = "https://www.google.com/search?q=";
    var url = $.url();
    var query = url.param("q");

    if (typeof query != 'undefined')
    {
        if (query.indexOf(shopPrefix) === 0) {
            query = query.substr(shopPrefix.length);
            left = left.replace("search?", "shop?");
            right = right.replace("search?", "search?psb=1&tbm=shop&");
        }
        $("#left").attr("src", left + encodeURIComponent(query));
        $("#right").attr("src", right + encodeURIComponent(query));
        document.title = query.replace(/</g, "<").replace(/>/g, ">") + " - Double Shot Search";
    }
});

Add back change from Sarvesh


const TwitterPostBase = require('./TwitterPostBase');
const { captureGearScreenshot } = require('@/app/screenshots');
const { readData, getTopOfCurrentHour } = require('@/common/utilities');

class GearTweet extends TwitterPostBase {
    getKey() { return 'gear'; }
    getName() { return 'Gear'; }

    getMerchandises() {
        return readData('merchandises.json').merchandises;
    }

    getDataTime() {
        // We only have end_times for merchandise items, so we need to track the latest end_time
        let endTimes = this.getMerchandises().map(m => m.end_time);
        let lastEndTime = Math.max(...endTimes);
        return lastEndTime;
    }

    getData() {
        return this.getMerchandises().find(m => m.end_time == this.getDataTime());
    }

    getTestData() {
        let merchandises = this.getMerchandises();
        return merchandises[merchandises.length - 1];
    }

    getImage(data) {
        let now = getTopOfCurrentHour();
        return captureGearScreenshot(now);
    }

    getText(data) {
        return `Up now on SplatNet: ${data.gear.name} with ${data.skill.name} #splatnet2`;
    }
}

module.exports = GearTweet;

const TwitterPostBase = require('./TwitterPostBase');
const { captureGearScreenshot } = require('@/app/screenshots');
const { readData, getTopOfCurrentHour } = require('@/common/utilities');

class GearTweet extends TwitterPostBase {
    getKey() { return 'gear'; }
    getName() { return 'Gear'; }

    getMerchandises() {
        return readData('merchandises.json').merchandises;
    }

    getDataTime() {
        // We only have end_times for merchandise items, so we need to track the latest end_time
        let endTimes = this.getMerchandises().map(m => m.end_time);
        let lastEndTime = Math.max(...endTimes);
        return lastEndTime;
    }

    getData() {
        return this.getMerchandises().find(m => m.end_time == this.getDataTime());
    }

    getTestData() {
        let merchandises = this.getMerchandises();
        return merchandises[merchandises.length - 1];
    }

    getImage() {
        return captureGearScreenshot(this.getDataTime());
    }

    getText(data) {
        return `Up now on SplatNet: ${data.gear.name} with ${data.skill.name} #splatnet2`;
    }
}

module.exports = GearTweet;

Fix a mistake, this should use the specified tweet time


const path = require('path')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const proxy = require('proxy-middleware')
const url = require('url')
const express = require('express')

const config = require('./webpack.config.js')

const app = express()

app.use(express.static(path.join(__dirname, 'public')))

if (process.env.NODE_ENV !== 'production') {
  const compiler = webpack(config)
  const devServer = new WebpackDevServer(compiler, {

  })

  devServer.listen(8081)

  app.use('/assets', proxy(url.parse('http://localhost:8081/assets')))
}

module.exports = app

const path = require('path')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const proxy = require('proxy-middleware')
const url = require('url')
const express = require('express')

const config = require('./webpack.config.js')

const app = express()

app.use(express.static(path.join(__dirname, 'public')))

app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, '/index.html'))
})

if (process.env.NODE_ENV !== 'production') {
  const compiler = webpack(config)
  const devServer = new WebpackDevServer(compiler, {
    contentBase: __dirname,
    hot: true,
    quiet: false,
    noInfo: false,
    publicPath: '/assets/',

    stats: { colors: true }
  })

  devServer.listen(8081)

  app.use('/assets', proxy(url.parse('http://localhost:8081/assets')))
}

module.exports = app

Send index.html on all requests


'use strict';

if (process.env.NEW_RELIC_LICENSE_KEY) require('newrelic');

const express = require('express');
const logger = require('./modules').logger;
const mongooseHelper = require('./modules').mongooseHelper;
const rollbarHelper = require('./modules').rollbarHelper;

const server = express();

require('./routes').config(server);

function start() {
  return rollbarHelper.init()
    .then(() => mongooseHelper.connect())
    .then(() => {
      const listener = server.listen(server.get('port'), () => {
        logger.info({ port: listener.address().port, environment: process.env.NODE_ENV }, '[SERVER] Server started');
      });
    })
    .catch(err => shutdown(err));
}

function shutdown(err) {
  if (err) {
    logger.error(err, '[SERVER] Uncaught error');
    rollbarHelper.rollbar.handleError(err, '[SERVER] Uncaught exception');
  }
  process.exit(err ? 1 : 0);
}

if (!module.parent) {
  start();
} else {
  module.exports = {
    server,
    start,
    shutdown
  };
}

'use strict';

if (process.env.NEW_RELIC_LICENSE_KEY) require('newrelic');

const config = require('config');
const express = require('express');
const http = require('http');
const logger = require('./modules').logger;
const mongooseHelper = require('./modules').mongooseHelper;
const rollbarHelper = require('./modules').rollbarHelper;

const app = express();
const server = http.createServer(app);

require('./routes').config(app);

function start() {
  return rollbarHelper.init()
    .then(() => mongooseHelper.connect())
    .then(() => {
      server.listen(config.port, () => {
        logger.info({ port: config.port, environment: process.env.NODE_ENV }, '[SERVER] Server started');
      });
    })
    .catch(err => shutdown(err));
}

function shutdown(err, callback) {
  if (err) {
    logger.error(err, '[SERVER] Uncaught error');
    rollbarHelper.rollbar.handleError(err, '[SERVER] Uncaught exception');
  }

  server.close(closeErr => {
    if (closeErr) {
      logger.error(closeErr, '[SERVER] Error closing the server');
      rollbarHelper.rollbar.handleError(closeErr, '[SERVER] Error closing the server');
    } else {
      logger.info('[SERVER] Server closed');
    }
    process.exit(err ? 1 : 0);
    // XXX: callback used for tests, process.exit should be stubed in the case
    if (callback) callback(closeErr);
  });
}

if (!module.parent) {
  start();
} else {
  module.exports = {
    server,
    start,
    shutdown
  };
}

Use http instead of express wrapper to listen


var express = require('express');
var app = express();
var http = require('http').Server(app);

// TODO: Use socket.io, which requires swift on the ios side, but would make all of this code
// not necessary.
var io = require('websocket.io').attach(http);

app.use(express.static('public'));

app.get('/', function(req, res){
  res.sendFile(__dirname + 'public/index.html');
});

var clients = [];
function broadcast(msg, socket) {
  clients.forEach(function(client) {
    if (client === socket) {
      return;
    }

    client.send(msg);
  })
}

function addClient(socket) {
  for (client in clients) {
    if (client === socket) {
      return; // Already in the list
    }
  }

  clients.push(socket);
}

function removeClient(socket) {
  clients.filter(function(client) {
    return (client !== socket);
  });
}

io.on('connection', function(socket) {
  addClient(socket);
  broadcast(JSON.stringify({'clientConnections': clients.length}));

  socket.on('message', function(msg) {
    broadcast(msg, socket);
  });

  socket.on('close', function () {
    removeClient(socket);
    broadcast(JSON.stringify({'clientConnections': clients.length}));
  });
});

http.listen(6565, function() {
  console.log('listening on *:6565');
});

var express = require('express');
var app = express();
var http = require('http').Server(app);

// TODO: Use socket.io, which requires swift on the ios side, but would make all of this code
// not necessary.
var io = require('websocket.io').attach(http);

app.use(express.static('public'));

app.get('/', function(req, res){
  res.sendFile(__dirname + 'public/index.html');
});

var clients = [];
function broadcast(msg, socket) {
  clients.forEach(function(client) {
    if (client === socket) {
      return;
    }

    client.send(msg);
  })
}

function addClient(socket) {
  for (client in clients) {
    if (client === socket) {
      return; // Already in the list
    }
  }

  clients.push(socket);
}

function removeClient(socket) {
  clients = clients.filter(function(client) {
    return (client !== socket);
  });
}

io.on('connection', function(socket) {
  addClient(socket);
  broadcast(JSON.stringify({'clientConnections': clients.length}));

  socket.on('message', function(msg) {
    broadcast(msg, socket);
  });

  socket.on('close', function() {
    removeClient(socket);
    broadcast(JSON.stringify({'clientConnections': clients.length}));
  });

  socket.on('error', function(error) {
    // Remove the socket in an error case
    removeClient(socket);
    broadcast(JSON.stringify({'clientConnections': clients.length}));
  })
});

http.listen(6565, function() {
  console.log('listening on *:6565');
});

Fix up error case. This would be so much easier with socket.io :fire:


$(document).ready(function() {
  var $body = $('body');
  var senateData;

  var scan = function() {
    $.each(senateData, function(i, senator) {
      var firstLastRegEx = new RegExp(senator.firstName + ' ' + senator.lastName);

      $body.highlightRegex(firstLastRegEx, {
        className: 'dial-congress',
        attrs: {
          'data-phone': senator.phone
        }
      });
    });
  }

  $.when(
    $.get(chrome.extension.getURL('js/senate.json'), function(data) {
      senateData = JSON.parse(data);
    })
  ).then(function() {
    scan();
  })
});

$(document).ready(function() {
  var $body = $('body');
  var senateData;

  var scan = function() {
    $.each(senateData, function(i, senator) {
      var firstLast = senator.firstName + '\\s+' + senator.lastName;
      var lastFirst = senator.lastName + ',\\s*' + senator.firstName;
      var withTitle = '(?:Senator\\s+|Sen.\\s+|Congressman\\s+|Congresswoman\\s+)(' + firstLast + '|' + senator.lastName + ')';
      var regExp = new RegExp(firstLast + '|' + lastFirst + '|' + withTitle, 'ig');

      $body.highlightRegex(regExp, {
        className: 'dial-congress',
        attrs: {
          'data-phone': senator.phone
        }
      });
    });
  }

  $.when(
    $.get(chrome.extension.getURL('js/senate.json'), function(data) {
      senateData = JSON.parse(data);
    })
  ).then(function() {
    scan();
  })
});

Improve regExp logic with name variations


define(function(require, exports, module) {

exports.showCli = true;
exports.aceKeyboardHandler = require("ace/keyboard/vim").handler;

exports.aceKeyboardHandler.defaultKeymap.unshift(

    { keys: ':', type: 'action', action: 'aceCommand', actionArgs: { exec: function(ace) {
        ace.showCommandLine(":");
    } } }

);

});

define(function(require, exports, module) {

exports.showCli = true;
exports.aceKeyboardHandler = require("ace/keyboard/vim").handler;

});

Revert "Merge remote-tracking branch 'origin/ace' into fix-release"


// JavaScript Document

// Scripts written by YOURNAME @ YOURCOMPANY

var overlays = document.querySelectorAll("[data-overlay]"),
    overlay_closer = document.querySelector(".overlay-closer");

// listen for clicks on the overlay closer
overlay_closer.addEventListener("click", function (e) {
    e.preventDefault();
    close_overlay_closer();
});

// listen for esc key press
document.onkeyup = function(e) {
    if (e.keyCode === 27) {
        close_overlay_closer();
    }
};

// function to close the overlay closer
function close_overlay_closer() {
    // make sure the overlay closer exists
    if ((typeof overlay_closer === "object")) {
        overlay_closer.classList.remove("is-active");
        overlay_closer.setAttribute("aria-hidden", "true");

        // make sure overlays exist
        if ((typeof overlays === "object")) {
            for (var i = 0; i < overlays.length; i++) {
                if (overlays[i].hasAttribute("aria-hidden")) {
                    // mark the overlay as hidden
                    overlays[i].setAttribute("aria-hidden", "true");
                } else {
                    // focus the overlay
                    overlays[i].focus();
                }

                // mark the overlay as inactive
                overlays[i].classList.remove("is-active");
            }
        }
    }
}

// JavaScript Document

// Scripts written by YOURNAME @ YOURCOMPANY

var overlays = document.querySelectorAll("[data-overlay]"),
    overlay_closer = document.querySelector(".overlay-closer");

// listen for clicks on the overlay closer
overlay_closer.addEventListener("click", function (e) {
    e.preventDefault();
    close_overlay_closer();
});

// listen for esc key press
document.addEventListener("keyup", function(e) {
    if (e.keyCode === 27)
        close_overlay_closer();
});

// function to close the overlay closer
function close_overlay_closer() {
    // make sure the overlay closer exists
    if ((typeof overlay_closer === "object")) {
        overlay_closer.classList.remove("is-active");
        overlay_closer.setAttribute("aria-hidden", "true");

        // make sure overlays exist
        if ((typeof overlays === "object")) {
            for (var i = 0; i < overlays.length; i++) {
                if (overlays[i].hasAttribute("aria-hidden")) {
                    // mark the overlay as hidden
                    overlays[i].setAttribute("aria-hidden", "true");
                } else {
                    // focus the overlay
                    overlays[i].focus();
                }

                // mark the overlay as inactive
                overlays[i].classList.remove("is-active");
            }
        }
    }
}

Make keyup function a bit more consistant


import React, { Component } from 'react';
import { connect } from 'react-redux';
import { colorClick } from '../../actions/assets';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ assets, dispatch }) => {
	return (

      {map(assets.data[assets.current].colors, color => {
       const className = classNames('color', color.id, { active: assets.current === color.id });
       return  dispatch(colorClick(color.id))}>;
      })}

  );
}

export default connect(store => ({
	assets: store.assets
}))(ColorPicker);

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { colorClick } from '../../actions/assets';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ assets, dispatch }) => {
	return (

      {map(assets.data[assets.current].colors, color => {
       const className = classNames('color', color.id, { active: assets.currentColor === color.id });
       return  dispatch(colorClick(color.id))}>;
      })}

  );
}

export default connect(store => ({
	assets: store.assets
}))(ColorPicker);

Fix current color active style


const { spawn } = require('child_process')

module.exports = {
  onPreBuild({ utils: { status: { show } } }) {
    spawn('sleep', ['60'], { detached: true })
    show({ summary: 'Test' })
  }
}

module.exports = {
  onPreBuild({ utils: { status: { show } } }) {
    show({ summary: 'Test' })
  }
}

Revert "Revert "Revert "Revert "Revert "Try child process"""""


#!/usr/bin/env node

var oust = require('../index');
var pkg = require('../package.json');
var fs = require('fs');

var argv = require('minimist')((process.argv.slice(2)))

var printHelp = function() {
    console.log('oust');
    console.log(pkg.description);
    console.log('');
    console.log('Usage:');
    console.log('  $ oust <filename> <selector>');
};

if(argv.h || argv.help) {
    printHelp();
    return;
}

if(argv.v || argv.version) {
    console.log(pkg.version);
    return;
}

var file = argv._[0];
var selector = argv._[1];

fs.readFile(file, function(err, data) {
    if(err) {
        console.error('Error opening file:', err.message);
        process.exit(1);
    }
    var res = oust(data, type);
    console.log(res.join("\n"));
});

#!/usr/bin/env node

var oust = require('../index');
var pkg = require('../package.json');
var fs = require('fs');

var argv = require('minimist')(process.argv.slice(2));

var printHelp = function() {
    console.log('oust');
    console.log(pkg.description);
    console.log('');
    console.log('Usage:');
    console.log('  $ oust <filename> <selector>');
};

if(argv.h || argv.help) {
    printHelp();
    return;
}

if(argv.v || argv.version) {
    console.log(pkg.version);
    return;
}

var file = argv._[0];
var selector = argv._[1];

fs.readFile(file, function(err, data) {
    if(err) {
        console.error('Error opening file:', err.message);
        process.exit(1);
    }
    var res = oust(data, selector);
    console.log(res.join("\n"));
});

Fix syntax and merge differences


// package metadata file for Meteor.js

/* jshint strict:false */
/* global Package:true */

Package.describe({
  name: 'twbs:bootstrap',  // http://atmospherejs.com/twbs/bootstrap
  summary: 'The most popular front-end framework for developing responsive, mobile first projects on the web.',
  version: '3.3.5',
  git: 'https://github.com/twbs/bootstrap.git'
});

Package.onUse(function (api) {
  api.versionsFrom('METEOR@1.0');
  api.use('jquery', 'client');
  api.addFiles([
    'dist/fonts/glyphicons-halflings-regular.eot',
    'dist/fonts/glyphicons-halflings-regular.svg',
    'dist/fonts/glyphicons-halflings-regular.ttf',
    'dist/fonts/glyphicons-halflings-regular.woff',
    'dist/fonts/glyphicons-halflings-regular.woff2',
    'dist/css/bootstrap.css',
    'dist/js/bootstrap.js'
  ], 'client');
});

// package metadata file for Meteor.js

/* jshint strict:false */
/* global Package:true */

Package.describe({
  name: 'twbs:bootstrap',  // http://atmospherejs.com/twbs/bootstrap
  summary: 'The most popular front-end framework for developing responsive, mobile first projects on the web.',
  version: '3.3.5',
  git: 'https://github.com/twbs/bootstrap.git'
});

Package.onUse(function (api) {
  api.versionsFrom('METEOR@1.0');
  api.use('jquery', 'client');
  api.addFiles([
    'dist/fonts/glyphicons-halflings-regular.eot',
    'dist/fonts/glyphicons-halflings-regular.svg',
    'dist/fonts/glyphicons-halflings-regular.ttf',
    'dist/fonts/glyphicons-halflings-regular.woff',
    'dist/fonts/glyphicons-halflings-regular.woff2'
  ], 'client', { isAsset: true });
  api.addFiles([
    'dist/css/bootstrap.css',
    'dist/js/bootstrap.js'
  ], 'client');
});

Add support for Meteor 1.2


Package.describe({
    name: 'nitrolabs:accounts-dropbox',
    summary: "Accounts service for Dropbox",
    version: '1.3.0',
    git: 'https://github.com/NitroLabs/meteor-accounts-dropbox'
});

Package.onUse(function (api) {
    api.versionsFrom('1.2');
    api.use('accounts-base', ['client', 'server']);

    api.imply('accounts-base', ['client', 'server']);
    api.use('accounts-oauth', ['client', 'server']);
    api.use('nitrolabs:dropbox-oauth@1.3.0', ['client', 'server']);

    api.addFiles('css/dropbox_login_button.css', 'client');
    api.addFiles('lib/dropbox.js', ['client', 'server']);
});

Package.describe({
    name: 'nitrolabs:accounts-dropbox',
    summary: "Accounts service for Dropbox",
    version: '1.4.0',
    git: 'https://github.com/Nitrolabs/meteor-accounts-dropbox'
});

Package.onUse(function (api) {
    api.versionsFrom('1.2');
    api.use('accounts-base', ['client', 'server']);

    api.imply('accounts-base', ['client', 'server']);
    api.use('accounts-oauth', ['client', 'server']);
    api.use('nitrolabs:dropbox-oauth@1.4.0', ['client', 'server']);

    api.addFiles('css/dropbox_login_button.css', 'client');
    api.addFiles('lib/dropbox.js', ['client', 'server']);
});

Update dropbox-auth version for avatar field


Package.describe({
    summary: "Network visualization library",
    version: "1.0.6",
    name: "futurescaper:futuregrapher",
    git: "https://github.com/Futurescaper/futuregrapher.git"
});

Package.onUse(function(api, where) {
    api.versionsFrom('METEOR@0.9.0');
    api.use(['d3', 'jquery'], 'client');
    api.addFiles(['dist/futuregrapher.js'], ['client']);
    api.export('futuregrapher');
});

Package.on_test(function (api) {
    api.use(["d3", "tinytest", "test-helpers"]);
    api.addFiles(['dist/futuregrapher.js'], ["client"]);
    api.addFiles([
        "test/jquery-simulate/jquery.simulate.js",
        "test/stubs.js", "test/helpers.js",
        "test/graphvis-tests.js"
    ], ["client"]);
});

Package.describe({
    summary: 'Graph / network visualization library',
    version: '1.0.7',
    name: 'futurescaper:futuregrapher',
    git: 'https://github.com/Futurescaper/futuregrapher.git'
});

Package.onUse(function(api, where) {
    api.versionsFrom('METEOR@0.9.0');
    api.use(['d3', 'jquery'], 'client');
    api.addFiles(['dist/futuregrapher.js'], 'client');
    api.export('futuregrapher');
});

Package.onTest(function (api) {
    api.use(['d3', 'tinytest', 'test-helpers']);
    api.addFiles(['dist/futuregrapher.js'], 'client');
    api.addFiles([
        'test/jquery-simulate/jquery.simulate.js',
        'test/stubs.js', 'test/helpers.js',
        'test/graphvis-tests.js'
    ], 'client');
});

Improve SEO for Atmosphere. Uniform quoting.