'use strict';
/* globals describe, it */

var assert = require('assert');
var get = require('../');
var isPlainObject = require('lodash.isplainobject');

describe('get', function(){
  var o = { a: 'a', b: 'b', c: { c1: 'c1', c2: 'c2' } };

  it('returns the original object if path lookup completely fails', function(){
    assert.equal(get('d', o), o);
  });

  it('returns the last successful value if path lookup partially succeeds and then fails', function(){
    assert.equal(get('c.c3', o), o.c);
  });

  it('returns an empty object if given object isn\'t actually an object', function(){
    assert(isPlainObject(get('foobar', 5)));
  });

  it('accesses a path given as string with keys separated by dots', function(){
    assert.equal(get('c.c2', o), 'c2');
  });

  it('is curried', function(){
    // Redo the above tests in curreid form.
    assert.equal(get('d')(o), o);
    assert.equal(get('c.c3')(o), o.c);
    assert(isPlainObject(get('foobar')(5)));
    assert.equal(get('c.c2')(o), 'c2');
  });

});

'use strict';
/* globals describe, it */

var assert = require('assert');
var get = require('../');
var isPlainObject = require('lodash.isplainobject');

describe('get', function(){
  var o = { a: 'a', b: 'b', c: { c1: 'c1', c2: 'c2' } };

  it('returns given object if path lookup totally fails', function(){
    assert.equal(get('d', o), o);
  });

  it('returns last found value if path lookup partially succeeds and then fails', function(){
    assert.equal(get('c.c3', o), o.c);
  });

  it('returns {} if given object isn\'t actually an object', function(){
    assert(isPlainObject(get('foobar', 5)));
  });

  it('path as String', function(){
    assert.equal(get('b', o), 'b');
  });

  it('path as String with keys separated by dots', function(){
    assert.equal(get('c.c2', o), 'c2');
  });

  it('path as [String]', function(){
    assert.equal(get(['c', 'c2'], o), 'c2');
  });

  it('is curried', function(){
    assert.equal(get('d')(o), o);
  });

});

Add tests using array for path


// travis doesn't support colors, so we have to force it
process.env.FORCE_COLOR = 1;

console.log(process.env);

global.chai = require('chai');
global.chai.use(require('chai-as-promised'));
global.chai.use(require('sinon-chai'));
global.expect = global.chai.expect;
global.sinon = require('sinon');

beforeEach(function () {
	this.sandbox = global.sinon.createSandbox();
	global.spy = this.sandbox.spy.bind(this.sandbox);
	global.stub = this.sandbox.stub.bind(this.sandbox);
});

afterEach(function () {
	delete global.spy;
	delete global.stub;
	this.sandbox.restore();
});

// travis doesn't support colors, so we have to force it
process.env.FORCE_COLOR = 1;
delete process.env.CI; // supports-colors really hates us

global.chai = require('chai');
global.chai.use(require('chai-as-promised'));
global.chai.use(require('sinon-chai'));
global.expect = global.chai.expect;
global.sinon = require('sinon');

beforeEach(function () {
	this.sandbox = global.sinon.createSandbox();
	global.spy = this.sandbox.spy.bind(this.sandbox);
	global.stub = this.sandbox.stub.bind(this.sandbox);
});

afterEach(function () {
	delete global.spy;
	delete global.stub;
	this.sandbox.restore();
});

Disable CI env var for tests.


const {Disposable} = require('event-kit')
const Point = require('./point')

const EMPTY = []

module.exports =
class NullLanguageMode {
  bufferDidChange () {}
  buildHighlightIterator () { return new NullHighlightIterator() }
  onDidChangeHighlighting () { return new Disposable(() => {}) }
}

class NullHighlightIterator {
  seek (position) { return EMPTY }
  moveToSuccessor () { return false }
  getPosition () { return Point.INFINITY }
  getCloseTags () { return EMPTY }
  getOpenTags () { return EMPTY }
}

const {Disposable} = require('event-kit')
const Point = require('./point')

const EMPTY = []

module.exports =
class NullLanguageMode {
  bufferDidChange () {}
  buildHighlightIterator () { return new NullHighlightIterator() }
  onDidChangeHighlighting () { return new Disposable(() => {}) }
  getLanguageId () { return null }
}

class NullHighlightIterator {
  seek (position) { return EMPTY }
  moveToSuccessor () { return false }
  getPosition () { return Point.INFINITY }
  getCloseTags () { return EMPTY }
  getOpenTags () { return EMPTY }
}

Add getLanguageId method to NullLanguageMode


goog.provide('ol.Coordinate');

goog.require('goog.math.Coordinate');

/**
 * @constructor
 * @extends {goog.math.Coordinate}
 * @param {number} x X.
 * @param {number} y Y.
 */
ol.Coordinate = function(x, y) {
  goog.base(this, x, y);
};
goog.inherits(ol.Coordinate, goog.math.Coordinate);

/**
 * @return {ol.Coordinate} Clone.
 */
ol.Coordinate.prototype.clone = function() {
  return new ol.Coordinate(this.x, this.y);
};

goog.provide('ol.Coordinate');

goog.require('goog.math.Vec2');

/**
 * @constructor
 * @extends {goog.math.Vec2}
 * @param {number} x X.
 * @param {number} y Y.
 */
ol.Coordinate = function(x, y) {
  goog.base(this, x, y);
};
goog.inherits(ol.Coordinate, goog.math.Vec2);

/**
 * @return {ol.Coordinate} Clone.
 */
ol.Coordinate.prototype.clone = function() {
  return new ol.Coordinate(this.x, this.y);
};

Make ol.Coordinate inherit from goog.math.Vec2


var path = require('path'),
    assert = require('yeoman-generator').assert,
    helpers = require('yeoman-generator').test,
    os = require('os');

describe('sails-rest-api:app', function () {
    before(function (done) {
        helpers.run(path.join(__dirname, '../app'))
            .inDir(path.join(os.tmpdir(), './temp-test'))
            .on('end', done);
    });

    it('Should properly create root files', function () {
        assert.file([
            '.editorconfig',
            '.gitignore',
            '.sailsrc',
            'app.js',
            'package.json'
        ]);
    });
});

var path = require('path'),
    assert = require('yeoman-generator').assert,
    helpers = require('yeoman-generator').test,
    os = require('os');

describe('sails-rest-api:app', function () {
    before(function (done) {
        helpers.run(path.join(__dirname, '../app'))
            .inDir(path.join(os.tmpdir(), './temp-test'))
            .withOptions({skipInstall: true})
            .on('end', done);
    });

    it('Should properly create root files', function () {
        assert.file([
            '.editorconfig',
            '.gitignore',
            '.sailsrc',
            'app.js',
            'package.json'
        ]);
    });
});

Add skip install flag to test


module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        concat: {
            options: {
                separator: '\n'
            },
            dist: {
                src: [
                    'vendor/**/*.js',
                    'template/_header.js',
                    'src/**/*.js',
                    'template/_footer.js',
                    'plugins/**/*.js'
                ],
                dest: 'build/<%= pkg.name %>.js'
            }
        },
        uglify: {
            options: {
                banner: '/*! Raven.js <%= pkg.version %> | github.com/getsentry/raven-js */\n'
            },
            dist: {
                files: {
                    'build/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');

    grunt.registerTask('default', ['concat', 'uglify']);

};

module.exports = function(grunt) {
    var _ = grunt.util._;

    var plugins = (grunt.option('plugins') || '').split(',');

    var gruntConfig = {
        pkg: grunt.file.readJSON('package.json'),
        concat: {
            options: {
                separator: '\n'
            },
            dist: {
                src: [
                    'vendor/**/*.js',
                    'template/_header.js',
                    'src/**/*.js',
                    'template/_footer.js'
                ],
                dest: 'build/<%= pkg.name %>.js'
            }
        },
        uglify: {
            options: {
                banner: '/*! Raven.js <%= pkg.version %> | github.com/getsentry/raven-js */\n'
            },
            dist: {
                files: {
                    'build/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
                }
            }
        }
    };

    // Create plugin paths and verify hey exist
    var plugins = _.map(plugins, function (plugin) {
        var path = 'plugins/' + plugin + '.js';

        if(!grunt.file.exists(path))
            throw new Error("Plugin '" + plugin + "' not found in plugins directory.");

        return path;
    });

    // Amend plugins to the concat source list
    gruntConfig.concat.dist.src = gruntConfig.concat.dist.src.concat(plugins);

    grunt.initConfig(gruntConfig);

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');

    grunt.registerTask('default', ['concat', 'uglify']);

};

Add optional plugin build options


/*
 * grunt-config-options
 * https://github.com/henvic/grunt-cli-config
 *
 * Copyright (c) 2014 Henrique Vicente
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function exports(grunt) {
    grunt.initConfig({
        jshint: {
            all: [
                'Gruntfile.js',
                'tasks/*.js',
                '<%= nodeunit.tests %>'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        nodeunit: {
            tests: ['test/*_test.js']
        },
        complexity: {
            generic: {
                src: ['Gruntfile.js', 'tasks/config_options.js'],
                options: {
                    breakOnErrors: true,
                    errorsOnly: false,
                    cyclomatic: 3,
                    halstead: 8,
                    maintainability: 70,
                    hideComplexFunctions: false
                }
            }
        }
    });

    grunt.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-nodeunit');
    grunt.loadNpmTasks('grunt-complexity');

    grunt.registerTask('test', ['nodeunit']);
    grunt.registerTask('default', ['jshint', 'test', 'complexity']);

};

/*
 * grunt-config-options
 * https://github.com/henvic/grunt-cli-config
 *
 * Copyright (c) 2014 Henrique Vicente
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function exports(grunt) {
    grunt.initConfig({
        jshint: {
            all: [
                'Gruntfile.js',
                'tasks/*.js',
                '<%= nodeunit.tests %>'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        nodeunit: {
            tests: ['test/*_test.js']
        },
        complexity: {
            generic: {
                src: ['Gruntfile.js', 'tasks/config_options.js'],
                options: {
                    breakOnErrors: true,
                    errorsOnly: false,
                    cyclomatic: 3,
                    halstead: 8,
                    maintainability: 70,
                    hideComplexFunctions: false
                }
            }
        }
    });

    grunt.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-nodeunit');
    grunt.loadNpmTasks('grunt-complexity');

    grunt.registerTask('test', ['jshint', 'nodeunit', 'complexity']);
    grunt.registerTask('default', ['test']);

};

Add jshint and grunt-complexity to the test task


'use strict';

import registry from '../../src/registry';
import skate from '../../src/skate';

describe('Registry', function () {
  afterEach(function () {
    registry.clear();
  });

  it('should set definitions', function () {
    registry.set('test', {});
    try {
      registry.set('test', {});
      assert(false);
    } catch (e) {}
  });

  it('should clear definitions', function () {
    registry.set('test', {});
    registry.clear();
    registry.set('test', {});
  });

  it('should return definitions for a given element', function () {
    var definition1 = { type: skate.type.ELEMENT };
    var definition2 = { type: skate.type.ATTRIBUTE };
    var definition3 = { type: skate.type.CLASSNAME };
    var definitions;
    var element = document.createElement('test1');

    element.setAttribute('test2', '');
    element.classList.add('test3');

    registry.set('test1', definition1);
    registry.set('test2', definition2);
    registry.set('test3', definition3);

    definitions = registry.getForElement(element);

    expect(definitions.length).to.equal(3);
    expect(definitions[0]).to.equal(definition1);
    expect(definitions[1]).to.equal(definition2);
    expect(definitions[2]).to.equal(definition3);
  });
});

'use strict';

import registry from '../../src/registry';
import skate from '../../src/skate';

describe('Registry', function () {
  afterEach(function () {
    registry.clear();
  });

  it('should set definitions', function () {
    registry.set('test', {});
    try {
      registry.set('test', {});
      assert(false);
    } catch (e) {}
  });

  it('should clear definitions', function () {
    registry.set('test', {});
    registry.clear();
    registry.set('test', {});
  });

  it('should return definitions for a given element', function () {
    var definition1 = { type: skate.type.ELEMENT };
    var definition2 = { type: skate.type.ATTRIBUTE };
    var definition3 = { type: skate.type.CLASSNAME };
    var definitions;
    var element = document.createElement('test1');

    element.setAttribute('test2', '');
    element.className += ' test3';

    registry.set('test1', definition1);
    registry.set('test2', definition2);
    registry.set('test3', definition3);

    definitions = registry.getForElement(element);

    expect(definitions.length).to.equal(3);
    expect(definitions[0]).to.equal(definition1);
    expect(definitions[1]).to.equal(definition2);
    expect(definitions[2]).to.equal(definition3);
  });
});

Fix test that uses classList (which is IE >= 10).


import {Locale} from 'Framework/Locale'
import {Flash} from 'Framework/Flash'
const locale = new Locale()

export class Ajax {
  initAjax () {
    /* set some defaults for AJAX-request */
    $.ajaxSetup(
      {
        cache: false,
        type: 'POST',
        dataType: 'json',
        timeout: 5000
      }
    )

    /* 403 means we aren't authenticated anymore, so reload the page */
    $(document).ajaxError((event, XMLHttpRequest, ajaxOptions) => {
      if (XMLHttpRequest.status === 403) {
        window.location.reload()
      }

      if (ajaxOptions !== null) {
        let textStatus = locale.err('GeneralError')

        if (XMLHttpRequest.responseText !== null) {
          let json = $.parseJSON(XMLHttpRequest.responseText)

          if (json.message !== null) {
            textStatus = json.message
          } else {
            textStatus = XMLHttpRequest.responseText
          }
        }

        new Flash().add(textStatus, 'danger')
      }

      return false
    })

    /* show spinners */
    $(document).ajaxStart(() => {
      $.event.trigger('ajax_start')
    })

    $(document).ajaxStop(() => {
      $.event.trigger('ajax_stop')
    })
  }
}

import {Locale} from 'Framework/Locale'
import {Flash} from 'Framework/Flash'
const locale = new Locale()

export class Ajax {
  initAjax () {
    /* set some defaults for AJAX-request */
    $.ajaxSetup(
      {
        cache: false,
        type: 'POST',
        dataType: 'json',
        timeout: 5000
      }
    )

    /* 403 means we aren't authenticated anymore, so reload the page */
    $(document).ajaxError((event, XMLHttpRequest, ajaxOptions) => {
      if (XMLHttpRequest.status === 403) {
        window.location.reload()
      }

      if (ajaxOptions !== null) {
        let textStatus = locale.err('GeneralError')

        if (XMLHttpRequest.responseText !== null) {
          let json = $.parseJSON(XMLHttpRequest.responseText)

          if (json.message !== null) {
            textStatus = json.message
          } else {
            textStatus = XMLHttpRequest.responseText
          }
        }

        new Flash().add(textStatus, 'danger')
      }

      return false
    })

    $(document).ajaxStart(() => {
      $.event.trigger('ajax_start')
    })

    $(document).ajaxStop(() => {
      $.event.trigger('ajax_stop')
    })
  }
}

Remove false comment about ajax_start and ajax_stop


import React, { Component } from 'react'
import { inject, observer } from 'mobx-react'

@inject('AppStore')
@observer
export default class NewTabLink extends Component {
  render () {
    const { saved, autoSaveHandle } = this.props.AppStore
    const text = (autoSaveHandle && 'Saving...') || (saved && 'Draft saved')
    return (

          {text}

    )
  }
}

import React, { Component } from 'react'
import { inject, observer } from 'mobx-react'
import { CircularProgress } from 'material-ui/Progress'
import { InputLabel } from 'material-ui/Input'

@inject('AppStore')
@observer
export default class NewTabLink extends Component {
  render () {
    const { saved, autoSaveHandle } = this.props.AppStore
    const content = (
      autoSaveHandle && <CircularProgress size={24} />
    ) || (
      saved && <InputLabel>Draft saved</InputLabel>
    )
    return (
      <div>
        {content}
      </div>
    )
  }
}

Use CircularProgress to indicate saving


/*!
 * Price Recommendation Service
 * ----------------------------
 * Gruntfile.
 */

module.exports = function(grunt) {

    grunt.initConfig({
        jshint: {
            options: {
                jshintrc: true
            },
            files: [
                'Gruntfile.js',
                'server/*.js',
                'server/lib/**/*.js',
                'server/app/controllers/**/*.js'
            ]
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');

    grunt.registerTask('build', ['jshint']);

    // Default task.
    grunt.registerTask('default', ['build']);

};

/*!
 * Price Recommendation Service
 * ----------------------------
 * Gruntfile.
 */

module.exports = function(grunt) {

    grunt.initConfig({
        jshint: {
            options: {
                jshintrc: true
            },
            files: [
                'Gruntfile.js',
                'server/*.js',
                'server/lib/**/*.js',
                'server/app/**/*.js',
                'server/test/**/*.js'
            ]
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');

    grunt.registerTask('build', ['jshint']);

    // Default task.
    grunt.registerTask('default', ['build']);

};

Expand list of files that are linted to include tests.


module.exports = function(grunt) {

	// Project configuration.
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		less: {
			options: {
				ieCompat: true,
				strictImports: false,
				syncImport: false,
				report: 'min'
			},
			css: {
				options: {
					compress: false,
					yuicompress: false
				},
				files: {
					'css/calendar.css': 'less/calendar.less',
				}
			},
			css_min: {
				options: {
					compress: true,
					yuicompress: true
				},
				files: {
					'css/calendar.min.css': 'css/calendar.css'
				}
			}
		},
		uglify: {
			options: {
				banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\nhttps://github.com/Serhioromano/bootstrap-calendar.git\n'
			},
			build: {
				src: 'js/calendar.js',
				dest: 'js/calendar.min.js'
			}
		}
	});

	// Load the plugin that provides the tasks.
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');

	// Default task(s).
	grunt.registerTask('default', ['less:css', 'less:css_min', 'uglify']);

};

module.exports = function(grunt) {

	// Project configuration.
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		less: {
			options: {
				ieCompat: true,
				strictImports: false,
				syncImport: false,
				report: 'min'
			},
			css: {
				options: {
					compress: false,
					yuicompress: false
				},
				files: {
					'css/calendar.css': 'less/calendar.less',
				}
			},
			css_min: {
				options: {
					compress: true,
					yuicompress: true
				},
				files: {
					'css/calendar.min.css': 'css/calendar.css'
				}
			}
		},
		uglify: {
			options: {
				banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
					'<%= grunt.template.today("yyyy-mm-dd") %> \n' +
					'https://github.com/Serhioromano/bootstrap-calendar */\n'
			},
			build: {
				src: 'js/calendar.js',
				dest: 'js/calendar.min.js'
			}
		}
	});

	// Load the plugin that provides the tasks.
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');

	// Default task(s).
	grunt.registerTask('default', ['less:css', 'less:css_min', 'uglify']);

};

Add version to head of minified version


/*
	High level presentation component for entire front-end applicaiton
	index > app > nav & view
*/
import React from 'react'
import Main from './main/main'

// a "location" state variable and then selectively display a Component based on the value
var location = 'MAIN_PAGE'

// TODO :  routing logic to determine which view to display.
export const App = () => (

Hello React!

    // if (location == 'MAIN_PAGE') {
    //
    // } else if (location == 'PROFILE_PAGE') {
    //     <Profile />
    // } else {
    //     <Landing />
    // }
)

// TODO : Additionally we might have compound views, including a navigation section

export default App

/*
	High level presentation component for entire front-end applicaiton
	index > app > nav & view
*/
import React from 'react'
import Main from './main/main'
import Profile from './profile/profile'
import Landing from './auth/landing'

// TODO :  routing logic to determine which view to display.
class App extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            // a route state variable and then selectively
            // display a Component based on the value
            route : window.location.hash.substr(1)
        }
    }

    componentDidMount() {
        window.addEventListener('hashchange', () => {
            this.setState({
                route: window.location.hash.substr(1)
            })
            console.log(this.state.route)
        })
    }

    render() {
        let Child
        switch (this.state.route) {
            case '/main': Child = Main; break;
            case '/profile': Child = Profile; break;
            default: Child = Landing;
        }
        return (

App

Landing

Main

Profile

                <Child/>

        )
    }
}

// TODO : Additionally we might have compound views, including a navigation section

export default App

Add routing logic for three views.


module.exports = function(grunt) {

  grunt.initConfig({
    watch: {
      jasmine: {
        files:    [ 'aviator.js', 'spec/*_spec.js' ],
        tasks:    [ 'jasmine:all' ],
        options:  { interrupt: true }
      },
      browserify: {
        files:    [ 'src/**/*.js' ],
        tasks:    [ 'browserify:aviator' ],
        options:  { interrupt: true }
      }
    },

    jasmine: {
      all: {
        src: 'aviator.js',
        options: {
          specs: 'spec/*_spec.js'
        }
      }
    },

    browserify: {
      aviator: {
        src:  'src/main.js',
        dest: 'aviator.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-contrib-watch');
};

module.exports = function(grunt) {

  grunt.initConfig({
    watch: {
      jasmine: {
        files:    [ 'aviator.js', 'spec/*_spec.js' ],
        tasks:    [ 'jasmine:all' ],
        options:  { interrupt: true }
      },
      browserify: {
        files:    [ 'src/**/*.js' ],
        tasks:    [ 'browserify:aviator' ],
        options:  { interrupt: true }
      }
    },

    jasmine: {
      all: {
        src: 'aviator.js',
        options: {
          specs: 'spec/*_spec.js'
        }
      }
    },

    browserify: {
      aviator: {
        src:  'src/main.js',
        dest: 'aviator.js'
      }
    }
  });

  grunt.registerTask('test', ['jasmine']);
  grunt.registerTask('build', ['browserify']);

  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-contrib-watch');
};

Add `grunt test` and `grunt build`


module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
  });

  grunt.loadNpmTasks('grunt-contrib-jasmine');

  grunt.initConfig({
    jasmine: {
      runSpecs: {
        src: 'public/javascripts/*.js',
        options: {
          specs: 'test/js/specs/**/*.spec.js',
          vendor: [
            'node_modules/angular/angular.js',
            'node_modules/angular-route/angular-route.js',
            'node_modules/angular-mocks/angular-mocks.js'
          ],
          summary: true
        }
      }
    }
  });

  grunt.registerTask('build', function() {});  // Empty for now
  grunt.registerTask('test', ['jasmine']);
};

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
  });

  grunt.loadNpmTasks('grunt-contrib-jasmine');

  grunt.initConfig({
    jasmine: {
      runSpecs: {
        src: 'public/javascripts/*.js',
        options: {
          specs: 'test/js/specs/**/*.spec.js',
          vendor: [
            'node_modules/angular/angular.js',
            'node_modules/angular-route/angular-route.js',
            'node_modules/angular-mocks/angular-mocks.js'
          ],
          summary: true
        }
      }
    }
  });

  grunt.registerTask('build', function() {});  // Empty for now
  grunt.registerTask('test', ['jasmine']);
  grunt.registerTask('default', ['test']);
};

Add default task to grunt


module.exports = function (grunt) {

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json'),

		clean: {
			build: {
				src: "build"
			}
		},

		copy: {
			build: {
				files: [
					{
						expand: true,
						cwd: 'source/',
						src: ['clever-style-switcher.js'],
						dest: 'build/'
					}
				]
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-copy');

	grunt.registerTask('build', ['clean', 'copy']);

	grunt.registerTask('default', ['build']);
};

module.exports = function (grunt) {

	grunt.initConfig({

		pkg: grunt.file.readJSON('package.json'),

		clean: {
			build: {
				src: "build"
			}
		},

		copy: {
			build: {
				files: [
					{
						expand: true,
						cwd: 'source/',
						src: ['clever-style-switcher.js'],
						dest: 'build/'
					}
				]
			}
		},

		uglify: {
			build: {
				files: {
					'build/css.min.js': ['source/clever-style-switcher.js']
				}
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-copy');
	grunt.loadNpmTasks('grunt-contrib-uglify');

	grunt.registerTask('build', ['clean', 'copy']);

	grunt.registerTask('default', ['build']);
};

Add an uglify task for Grunt.


import {createAction} from 'redux-actions';
import values from 'lodash/values';
import {SearchActions, MAX_SUGGESTION_COUNT} from './constants';
import {Action} from '../common/constants';
import {UnitServices} from '../unit/constants';

export const clearSearch = () =>
  createAction(SearchActions.CLEAR)();

export const searchUnits = (input: string, params: Object): Action => {
  const init = {
    input,
    service: `${values(UnitServices).join(',')}`
  };

  params = Object.assign({}, init, params);
  return createAction(SearchActions.FETCH_UNITS)({params});
};

export const receiveUnits = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_UNITS)(results);

export const fetchUnitSuggestions = (input: string): Action =>
  createAction(SearchActions.FETCH_UNIT_SUGGESTIONS)({params: {
    input,
    service: `${values(UnitServices).join(',')}`,
    page_size: MAX_SUGGESTION_COUNT
  }});

export const receiveUnitSuggestions = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_UNIT_SUGGESTIONS)(results);

export const receiveAddressSuggestions = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_ADDRESS_SUGGESTIONS)(results);

import {createAction} from 'redux-actions';
import values from 'lodash/values';
import {SearchActions, MAX_SUGGESTION_COUNT} from './constants';
import {Action} from '../common/constants';
import {UnitServices} from '../service/constants';

export const clearSearch = () =>
  createAction(SearchActions.CLEAR)();

export const searchUnits = (input: string, params: Object): Action => {
  const init = {
    input,
    service: `${values(UnitServices).join(',')}`
  };

  params = Object.assign({}, init, params);
  return createAction(SearchActions.FETCH_UNITS)({params});
};

export const receiveUnits = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_UNITS)(results);

export const fetchUnitSuggestions = (input: string): Action =>
  createAction(SearchActions.FETCH_UNIT_SUGGESTIONS)({params: {
    input,
    service: `${values(UnitServices).join(',')}`,
    page_size: MAX_SUGGESTION_COUNT
  }});

export const receiveUnitSuggestions = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_UNIT_SUGGESTIONS)(results);

export const receiveAddressSuggestions = (results: Array<Object>) =>
  createAction(SearchActions.RECEIVE_ADDRESS_SUGGESTIONS)(results);

Fix unit search suggestions bug


function something()
{
	var x = window.localStorage.getItem('aaa');

	window.localStorage.setItem('aaa', 555);

	alert(x);
}

function something()
{
	var x = window.localStorage.getItem('aaa');

	x = x*1 + 1;

	window.localStorage.setItem('aaa', x);

	alert(x);
}

Store x variable on client side


var Router = Ember.Router.extend(); // ensure we don't share routes between all Router instances

Router.map(function() {
  this.route('about');
  this.route('login');
  this.route('component-test');
  this.route('helper-test');
  this.resource('sensors', function() {
      this.resource('sensor', { path:'/:sensor_id' },
        function() {
          this.resource('sensordata', {
              path: '/data/:data_id'
            });
        });
  });
  this.resource('accesses', function() {
      this.resource('access', { path:'/:access_id' });
  });
});

export default Router;

var Router = Ember.Router.extend(); // ensure we don't share routes between all Router instances

Router.map(function() {
  this.route('about');
  this.route('login');
  this.resource('sensors', function() {
      this.resource('sensor', { path:'/:sensor_id' },
        function() {
          this.resource('sensordata', {
              path: '/data/:data_id'
            });
        });
  });
  this.resource('accesses', function() {
      this.resource('access', { path:'/:access_id' });
  });
});

export default Router;

Remove component and helper routes


console.warn('panel.js');

const messagesEl = document.getElementById('messages');

// chrome.devtools.network.onRequestFinished.addListener((req) => {
//     const url = req.request.url;
//     if (url.indexOf('twitter') !== -1) {
//         console.log(`req:${JSON.stringify(req)}`);
//         document.getElementById('message').innerText = 'このサイトから、Twitterの香りがするでゲソ';
//     }
// });

chrome.devtools.network.onNavigated.addListener((url) => {
    console.clear();
    messagesEl.textContent = '';
});

// b-5. dump message
function notifyBackgroundMessage(message) {
    console.warn('from devtools.js to panel.js', message);
    const messageEl = document.createElement('p');
    messageEl.textContent = JSON.stringify(message);
    messagesEl.appendChild(messageEl);
}

console.warn('panel.js');

const messagesEl = document.getElementById('messages');

chrome.devtools.network.onNavigated.addListener((url) => {
    console.clear();
    messagesEl.textContent = '';
});

// b-5. dump message
function notifyBackgroundMessage(message) {
    console.warn('from devtools.js to panel.js', message);
    const messageEl = document.createElement('p');
    messageEl.textContent = JSON.stringify(message);
    messagesEl.appendChild(messageEl);
}

Update sample of google chrome developer tools


import Enzyme, { shallow, render, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });
// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
Enzyme.configure({ adapter: new Adapter() });

Remove globally defined functions in tests


'use strict';

const electron = require('electron');
const {app, BrowserWindow} = electron;

let mainWindow;

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    title: 'Hello World',
    width: 266,
    height: 150,
    center: true,
  });

  mainWindow.loadURL('file://' + __dirname + '/index.html');

  mainWindow.on('closed', () => {
    mainWindow = null;
    app.quit();
  });
});

// Restore the window on Mac when the user click on the app icon in the dock
app.on('activate', () => {
  mainWindow.restore();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin')
      app.quit();
});

'use strict';

const {app, BrowserWindow} = require('electron');

let mainWindow;

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    title: 'Hello World',
    width: 266,
    height: 150,
    center: true,
  });

  mainWindow.loadURL('file://' + __dirname + '/index.html');

  mainWindow.on('closed', () => {
    mainWindow = null;
    app.quit();
  });
});

// Restore the window on Mac when the user click on the app icon in the dock
app.on('activate', () => {
  mainWindow.restore();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin')
      app.quit();
});

Update electron require to be called directly from 'const {app, BrowserWidow}' line


'use strict';

var gcd = require('./gcd.js');

/**
 * Algorithm to calculate Least Common Multiple based on Euclidean algorithm
 *
 * @param Number
 * @param Number
 *
 * @return Number
 */
var lcmDivisionBased = function (a, b) {
  if (a === 0 || b === 0) {
    return 0;
  }
  a = Math.abs(a);
  b = Math.abs(b);
  return a / gcd(a, b) * b;
};

/**
 * Algorithm to calculate Least Common Multiple based on Stein's Algorithm
 *
 * @param Number
 * @param Number
 *
 * @return Number
 */
var lcmBinaryIterative = function (a, b) {
  if (a === 0 || b === 0) {
    return 0;
  }
  a = Math.abs(a);
  b = Math.abs(b);
  return a / gcd.binary(a, b) * b;
};

lcmDivisionBased.binary = lcmBinaryIterative;
module.exports = lcmDivisionBased;

'use strict';

var gcd = require('./gcd.js');

/**
 * Calcule the Least Common Multiple with a given Greatest Common Denominator
 * function
 *
 * @param Number
 * @param Number
 * @param Function
 *
 * @return Number
 */
var genericLCM = function (gcdFunction, a, b) {
   if (a === 0 || b === 0) {
    return 0;
  }
  a = Math.abs(a);
  b = Math.abs(b);
  return a / gcdFunction(a, b) * b;
};

/**
 * Algorithm to calculate Least Common Multiple based on Euclidean algorithm
 * calls the generic LCM function passing the division based GCD calculator
 *
 * @param Number
 * @param Number
 *
 * @return Number
 */
var lcmDivisionBased = genericLCM.bind(null, gcd);

/**
 * Algorithm to calculate Least Common Multiple based on Stein's Algorithm
 * calls the generic LCM function passing the binary interative GCD calculator
 *
 * @param Number
 * @param Number
 *
 * @return Number
 */
var lcmBinaryIterative = genericLCM.bind(null, gcd.binary);

var lcm = lcmDivisionBased;
lcm.binary = lcmBinaryIterative;
module.exports = lcm;

Remove duplicated code from LCM


// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: validators.bookmarkID.allow(null),
  url: Joi.string()
    .required()
    .uri({
      scheme: [
        'http',
        'https',
      ]
    }),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.date().iso().allow(null),
  created_at: Joi.date().iso().allow(null),
  updated_at: Joi.date().iso().allow(null),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmarkID.required();

module.exports = validators;

// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: validators.bookmarkID.allow(null),
  url: Joi.string()
    .required()
    .uri({
      scheme: [
        'http',
        'https',
      ]
    }),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.date().iso().allow(null),
  created_at: Joi.date().iso().allow(null),
  updated_at: Joi.date().iso().allow(null),
  'private': Joi.boolean(),
  to_read: Joi.boolean(),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmarkID.required();

module.exports = validators;

Allow flags to pass through object validations.


/**
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 */

/* global app */
app.start();

/**
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 */

/* global app */
try {
  app.start();
}
catch (e) {
  var error = [
    'Could not start app,',
    'please check if source was compiled successfully',
    '(Browserify, React JSX Transform).'
  ].join(' ');
  throw new Error(error + ' Original error: ' + e.message);
}

Add better error message if app failed to compile


/* @flow */
import React, { PureComponent } from 'react';

import type { Actions, Stream, Topic } from '../types';
import connectWithActions from '../connectWithActions';
import { Screen } from '../common';
import { topicNarrow } from '../utils/narrow';
import { getTopicsInScreen } from '../selectors';
import { getStreamEditInitialValues } from '../subscriptions/subscriptionSelectors';
import TopicList from './TopicList';

type Props = {
  actions: Actions,
  stream: Stream,
  topics: Topic[],
};

class TopicListScreen extends PureComponent<Props> {
  props: Props;

  componentDidMount() {
    const { actions, stream } = this.props;
    actions.fetchTopics(stream.stream_id);
  }

  handlePress = (streamObj: string, topic: string) => {
    const { actions, stream } = this.props;
    actions.doNarrow(topicNarrow(stream.name, topic));
  };

  render() {
    const { topics } = this.props;

    return (
      <Screen title="Topics" padding>
        <TopicList topics={topics} onPress={this.handlePress} />
      </Screen>
    );
  }
}

export default connectWithActions(state => ({
  stream: getStreamEditInitialValues(state),
  topics: getTopicsInScreen(state),
}))(TopicListScreen);

/* @flow */
import React, { PureComponent } from 'react';

import type { Actions, Stream, Topic } from '../types';
import connectWithActions from '../connectWithActions';
import { Screen } from '../common';
import { topicNarrow } from '../utils/narrow';
import { getTopicsInScreen } from '../selectors';
import { getStreamEditInitialValues } from '../subscriptions/subscriptionSelectors';
import TopicList from './TopicList';

type Props = {
  actions: Actions,
  stream: Stream,
  topics: Topic[],
};

type State = {
  filter: string,
};

class TopicListScreen extends PureComponent<Props, State> {
  props: Props;

  state: State = {
    filter: '',
  };

  componentDidMount() {
    const { actions, stream } = this.props;
    actions.fetchTopics(stream.stream_id);
  }

  handlePress = (streamObj: string, topic: string) => {
    const { actions, stream } = this.props;
    actions.doNarrow(topicNarrow(stream.name, topic));
  };

  handleFilterChange = (filter: string) => this.setState({ filter });

  render() {
    const { topics } = this.props;
    const { filter } = this.state;
    const filteredTopics =
      topics && topics.filter(topic => topic.name.toLowerCase().includes(filter.toLowerCase()));

    return (
      <Screen title="Topics" padding search searchBarOnChange={this.handleFilterChange}>
        <TopicList topics={filteredTopics} onPress={this.handlePress} />
      </Screen>
    );
  }
}

export default connectWithActions(state => ({
  stream: getStreamEditInitialValues(state),
  topics: getTopicsInScreen(state),
}))(TopicListScreen);

Add search to Topic List screen


module.exports = {
  root: true,
  parserOptions: {
    ecmaVersion: 8,
    sourceType: 'module'
  },
  extends: 'eslint:recommended',
  env: {
    browser: true
  },
  rules: {
    /* Possible Errors http://eslint.org/docs/rules/#possible-errors */
    "comma-dangle": [2, "only-multiline"],

    /* Stylistic Issues http://eslint.org/docs/rules/#stylistic-issues */
    indent: [2, 2], /* two-space indentation */
    semi: 2, /* require semi-colons */
    'no-shadow': [2, {
      builtinGlobals: true,
      allow: ['event', 'i', 'name', 'parent', 'resolve', 'self', 'select', 'scrollTo', 'status']
    },], /* Prevent shadowing globals like Object*/
  }
};

module.exports = {
  root: true,
  parserOptions: {
    ecmaVersion: 8,
    sourceType: 'module'
  },
  extends: 'eslint:recommended',
  env: {
    browser: true
  },
  rules: {
    /* Possible Errors http://eslint.org/docs/rules/#possible-errors */
    "comma-dangle": [2, "only-multiline"],

    /* Stylistic Issues http://eslint.org/docs/rules/#stylistic-issues */
    indent: [2, 2], /* two-space indentation */
    semi: 2, /* require semi-colons */
    camelcase: 2, /* require camelcase variables */
    'no-shadow': [2, {
      builtinGlobals: true,
      allow: ['event', 'i', 'name', 'parent', 'resolve', 'self', 'select', 'scrollTo', 'status']
    },], /* Prevent shadowing globals like Object*/
  }
};

Enforce camelcase variables with eslint


module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
    ecmaFeatures: {
      legacyDecorators: true
    }
  },
  plugins: [
    'ember'
  ],
  extends: [
    'eslint:recommended',
    'plugin:ember/recommended'
  ],
  env: {
    browser: true
  },
  rules: {
    'ember/no-jquery': 'error',
    'ember/no-new-mixins': 'off'
  },
  overrides: [
    // node files
    {
      files: [
        '.eslintrc.js',
        '.template-lintrc.js',
        'ember-cli-build.js',
        'index.js',
        'testem.js',
        'blueprints/*/index.js',
        'config/**/*.js',
        'tests/dummy/config/**/*.js'
      ],
      excludedFiles: [
        'addon/**',
        'addon-test-support/**',
        'app/**',
        'tests/dummy/app/**'
      ],
      parserOptions: {
        sourceType: 'script'
      },
      env: {
        browser: false,
        node: true
      },
      plugins: ['node'],
      rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
        // add your custom rules and overrides for node files here
      })
    }
  ]
};

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
    ecmaFeatures: {
      legacyDecorators: true
    }
  },
  plugins: [
    'ember'
  ],
  extends: [
    'eslint:recommended',
    'plugin:ember/recommended'
  ],
  env: {
    browser: true
  },
  rules: {
    'ember/no-jquery': 'error'
  },
  overrides: [
    // node files
    {
      files: [
        '.eslintrc.js',
        '.template-lintrc.js',
        'ember-cli-build.js',
        'index.js',
        'testem.js',
        'blueprints/*/index.js',
        'config/**/*.js',
        'tests/dummy/config/**/*.js'
      ],
      excludedFiles: [
        'addon/**',
        'addon-test-support/**',
        'app/**',
        'tests/dummy/app/**'
      ],
      parserOptions: {
        sourceType: 'script'
      },
      env: {
        browser: false,
        node: true
      },
      plugins: ['node'],
      rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
        // add your custom rules and overrides for node files here
      })
    }
  ]
};

Remove exception for mixin linting now that they've all been removed.


window.lp = {};
window.lp.supports = {};
window.lp.isMobile = false;

require.config({
  paths: {
    jquery: "vendor/assets/javascripts/jquery/jquery",
    lib: "public/assets/javascripts/lib",
    jplugs: "vendor/assets/javascripts/jquery-plugins",
    pointer: "vendor/assets/javascripts/pointer",
    touchwipe: "vendor/assets/javascripts/jquery.touchwipe.1.1.1",
    s_code: "vendor/assets/javascripts/omniture/s_code",
    nouislider: "vendor/assets/javascripts/nouislider"
  }
});

window.lp = {};
window.lp.supports = {};
window.lp.isMobile = false;
window.lpUserStatusCallback = function() {};

require.config({
  paths: {
    jquery: "vendor/assets/javascripts/jquery/jquery",
    lib: "public/assets/javascripts/lib",
    jplugs: "vendor/assets/javascripts/jquery-plugins",
    pointer: "vendor/assets/javascripts/pointer",
    touchwipe: "vendor/assets/javascripts/jquery.touchwipe.1.1.1",
    s_code: "vendor/assets/javascripts/omniture/s_code",
    nouislider: "vendor/assets/javascripts/nouislider"
  }
});

Fix 'lpUserStatusCallback' error when running Grunt


module.exports = {
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "extends": "eslint:recommended",
  "parserOptions": {
    "ecmaFeatures": {
      "experimentalObjectRestSpread": true,
      "jsx": true
    },
    "sourceType": "module"
  },
  "plugins": [
    "react"
  ],
  "rules": {
    "indent": [
      "error",
      "tab"
    ],
    "linebreak-style": [
      "error",
      "unix"
    ],
    "quotes": [
      "error",
      "double"
    ],
    "semi": [
      "error",
      "never"
    ]
  }
}

module.exports = {
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "extends": "eslint:recommended",
  "parserOptions": {
    "ecmaFeatures": {
      "experimentalObjectRestSpread": true,
      "jsx": true
    },
    "sourceType": "module"
  },
  "plugins": [
    "react"
  ],
  "rules": {
    "indent": [
      "error",
      2
    ],
    "linebreak-style": [
      "error",
      "unix"
    ],
    "quotes": [
      "error",
      "double"
    ],
    "semi": [
      "error",
      "never"
    ]
  }
}

Update eslint config to allow 2 spaces


module.exports = {
  extends: 'eslint:recommended',
  parserOptions: {
    sourceType: 'script',
    modules: true,
  },
  env: {
    es6: true,
    node: true,
    jest: true,
    mocha: true,
  },
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': 'error',
  },
};

module.exports = {
  extends: 'eslint:recommended',
  parserOptions: {
    sourceType: 'script',
    modules: true,
  },
  env: {
    browser: true,
    es6: true,
    node: true,
    jest: true,
    mocha: true,
  },
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': 'error',
  },
};

Enable 'browser' environment for ESLint


module.exports = {
  plain: {
    color: '#f8f8f2',
    backgroundColor: '#272822'
  },
  styles: [
    {
      types: ['comment', 'prolog', 'doctype', 'cdata'],
      style: {
        color: '#778090'
      }
    },
    {
      types: ['punctuation'],
      style: {
        color: '#F8F8F2'
      }
    },
    {
      types: ['property', 'tag', 'constant', 'symbol', 'deleted'],
      style: {
        color: '#F92672'
      }
    },
    {
      types: ['boolean', 'number'],
      style: {
        color: '#AE81FF'
      }
    },
    {
      types: ['selector', 'attr-name', 'string', 'char', 'builtin', 'inserted'],
      style: {
        color: '#a6e22e'
      }
    },
    {
      types: ['operator', 'entity', 'url', 'variable'],
      style: {
        color: '#F8F8F2'
      }
    },
    {
      types: ['atrule', 'attr-value', 'function'],
      style: {
        color: '#E6D874'
      }
    },
    {
      types: ['keyword'],
      style: {
        color: '#F92672'
      }
    },
    {
      types: ['regex', 'important'],
      style: {
        color: '#FD971F'
      }
    }
  ]
}

module.exports = {
  plain: {
    color: '#f8f8f2',
    backgroundColor: '#272822'
  },
  styles: [
    {
      types: ['comment', 'prolog', 'doctype', 'cdata'],
      style: {
        color: '#c6cad2'
      }
    },
    {
      types: ['punctuation'],
      style: {
        color: '#F8F8F2'
      }
    },
    {
      types: ['property', 'tag', 'constant', 'symbol', 'deleted'],
      style: {
        color: '#F92672'
      }
    },
    {
      types: ['boolean', 'number'],
      style: {
        color: '#AE81FF'
      }
    },
    {
      types: ['selector', 'attr-name', 'string', 'char', 'builtin', 'inserted'],
      style: {
        color: '#a6e22e'
      }
    },
    {
      types: ['operator', 'entity', 'url', 'variable'],
      style: {
        color: '#F8F8F2'
      }
    },
    {
      types: ['atrule', 'attr-value', 'function'],
      style: {
        color: '#E6D874'
      }
    },
    {
      types: ['keyword'],
      style: {
        color: '#F92672'
      }
    },
    {
      types: ['regex', 'important'],
      style: {
        color: '#FD971F'
      }
    }
  ]
}

Change comment color to increase contrast


pageflow.ConfigurationEditorTabView.groups.define('general', function() {
  this.input('title', pageflow.TextInputView, {required: true});
  this.input('hide_title', pageflow.CheckBoxInputView);
  this.input('tagline', pageflow.TextInputView);
  this.input('subtitle', pageflow.TextInputView);
  this.input('text', pageflow.TextAreaInputView);
  this.input('text_position', pageflow.SelectInputView, {values: pageflow.Page.textPositions});
  this.input('gradient_opacity', pageflow.SliderInputView);
  this.input('invert', pageflow.CheckBoxInputView);
});

pageflow.ConfigurationEditorTabView.groups.define('general', function() {
  this.input('title', pageflow.TextInputView, {required: true, maxLength: 5000});
  this.input('hide_title', pageflow.CheckBoxInputView);
  this.input('tagline', pageflow.TextInputView, {maxLength: 5000});
  this.input('subtitle', pageflow.TextInputView, {maxLength: 5000});
  this.input('text', pageflow.TextAreaInputView);
  this.input('text_position', pageflow.SelectInputView, {values: pageflow.Page.textPositions});
  this.input('gradient_opacity', pageflow.SliderInputView);
  this.input('invert', pageflow.CheckBoxInputView);
});

Increase max length for page tagline, title and subtitles


const test = require('tape');
const User = require('../../../models/User');
const { connect, add } = require('../../database');

test('connection to database', async (assert) => {
  const res = await connect();
  assert.equal(res, 0, 'It should connect to the database without errors');
  assert.end();
});

test('adding to database', async (assert) => {
  const res = await add(User, 'sample', 'secret');
  assert.equal(res, 0, 'It should add documents to the database without errors');
  assert.end();
});

const test = require('tape');
const User = require('../../../models/User');
const { connect, add } = require('../../database');

test('connection to database', async (assert) => {
  const res = await connect();
  assert.equal(res, 0, 'It should connect to the database without errors');
  assert.end();
});

test('adding to database', async (assert) => {
  const res = await add(User, 'sample', 'secret');
  assert.equal(res, 0, 'It should add documents to the database without errors');
  // await remove(User, 'sample');
  assert.end();
});

test('adding duplicates to database', async (assert) => {
  await add(User, 'sample', 'secret');
  const res = await add(User, 'sample', 'secret');
  assert.equal(res, 1, 'It should not add duplicate documents to the database');
  // await remove(User, 'sample');
  assert.end();
});

Test for adding duplicates to database


import extractValuesFromDOMEvent from 'awareness/es/extractValuesFromDOMEvent'

export default function extractFromDOM(args) {
  if (args[0]) {
    const values = extractValuesFromDOMEvent(args[0])

    // Place extracted dataset values first, followed by original arguments
    args = [values].concat(args)
  }

  return args
}

import extractValuesFromDOMEvent from 'awareness/lib/extractValuesFromDOMEvent'

export default function extractFromDOM(args) {
  if (args[0]) {
    const values = extractValuesFromDOMEvent(args[0])

    // Place extracted dataset values first, followed by original arguments
    args = [values].concat(args)
  }

  return args
}

Use lib instead of es


chrome.contextMenus.create(
    {
        "title": "Define '%s'",
        "contexts":["selection"],
        "onclick": function(info, tab){
            chrome.tabs.create(
                {url: "http://www.google.com/search?q=define:" + info.selectionText
            });
        }
});

chrome.contextMenus.create(
    {
        "title": "Define \"%s\"",
        "contexts":["selection"],
        "onclick": function(info, tab){
            chrome.tabs.create(
                {url: "http://www.google.com/search?q=define:" + info.selectionText
            });
        }
});

Use double quotes in the context menu, for consistency with Chrome's "Search Google for ..."


/**
 * Turn arrow functions into normal functions.
 *
 * @example
 *
 * **In**
 *
 * ```javascript
 * arr.map(x => x * x);
 * ```
 *
 * **Out**
 *
 * ```javascript
 * arr.map(function (x) {
 *   return x * x;
 * });
 */

export var visitor = {

  /**
   * Look for arrow functions and turn them into normal functions.
   */

  ArrowFunctionExpression(node) {
    this.ensureBlock();
    node.expression = false;
    node.type = "FunctionExpression";
    node.shadow = true;
  }
};

/**
 * Turn arrow functions into normal functions.
 *
 * @example
 *
 * **In**
 *
 * ```javascript
 * arr.map(x => x * x);
 * ```
 *
 * **Out**
 *
 * ```javascript
 * arr.map(function (x) {
 *   return x * x;
 * });
 */

export var visitor = {

  /**
   * Look for arrow functions and mark them as "shadow functions".
   * @see /transformation/transformers/internal/shadow-functions.js
   */

  ArrowFunctionExpression(node) {
    this.ensureBlock();
    node.expression = false;
    node.type = "FunctionExpression";
    node.shadow = true;
  }
};

Clarify what the arrow function transformer does


import React from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';

import { trackAnalyticsEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    const { icon, human } = this.props.control;
    const className = classNames('tour-step-anchor node-control-button', icon, {
      // Old Agent / plugins don't include the 'fa ' prefix, so provide it if they don't.
      fa: icon.startsWith('fa-'),
      'node-control-button-pending': this.props.pending
    });
    return (
      <i className={className} title={human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackAnalyticsEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

import React from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';

import { trackAnalyticsEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    const { icon, id, human } = this.props.control;
    const className = classNames('tour-step-anchor node-control-button', icon, {
      // Old Agent / plugins don't include the 'fa ' prefix, so provide it if they don't.
      fa: icon.startsWith('fa-'),
      'node-control-button-pending': this.props.pending
    });
    return (
      <i className={className} data-id={id} title={human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackAnalyticsEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

Include the control's id as an attr on the button


(function(){
  'use strict';
  angular.module('starter.controllers')
  .controller('DashCtrl', function($scope) {
    console.log($scope);
  });
})();

(function(){
  'use strict';
  angular.module('starter.controllers', ['config'])
  .controller('DashCtrl', function($scope, $http, ENV) {
    var loadData = function(){
      $http.get(ENV.apiEndpoint + '/admin/neurons.json')
        .then(function(r){
          console.log(r);
          // we need work in backend. stand by
        });
    };

    loadData();
  });
})();

Add some logic to get neurons


function eventsourcetest() {
    var alInfo = $("#alInfo");
    var alSuccess = $("#alSuccess");
    var ta = document.getElementById('output');
    var source = new EventSource('php/gravity.sh.php');

    alInfo.show();
    alSuccess.hide();

    source.addEventListener('message', function(e) {
        if(e.data == "START"){
           alInfo.show();
        }
        else if(e.data == "SUCCESS"){
           alSuccess.show();
           alInfo.delay(5000).fadeOut(2000, function() { alInfo.hide(); });
        }
        else if (e.data !== '')
        {
            ta.innerHTML += e.data;
        }
    }, false);

    // Will be called when script has finished
    source.addEventListener('error', function(e) {
        source.close();
    }, false);
}
$(function(){
   eventsourcetest();
});

function eventsourcetest() {
    var alInfo = $("#alInfo");
    var alSuccess = $("#alSuccess");
    var ta = document.getElementById('output');
    var source = new EventSource('php/gravity.sh.php');

    alInfo.show();
    alSuccess.hide();

    source.addEventListener('message', function(e) {
        if(e.data == "START"){
           alInfo.show();
        }
        else if(e.data == "SUCCESS"){
           alSuccess.show();
           alInfo.delay(1000).fadeOut(2000, function() { alInfo.hide(); });
        }
        else if (e.data !== '')
        {
            ta.innerHTML += e.data;
        }
    }, false);

    // Will be called when script has finished
    source.addEventListener('error', function(e) {
        source.close();
    }, false);
}
$(function(){
   eventsourcetest();
});

Reduce timeout for hiding the info box


const chai = require('chai');
const expect = chai.expect;
const xcode = require('xcode');
const getGroup = require('../../src/ios/getGroup');

const project = xcode.project('test/fixtures/project.pbxproj');

describe('ios::getGroup', () => {
  beforeEach(() => {
    project.parseSync();
  });

  it('should return a top-level group', () => {
    const group = getGroup(project, 'Libraries');
    expect(group.children.length > 0).to.be.true;
    expect(group.name).to.equals('Libraries');
  });

  it('should return nested group when specified', () => {
    const group = getGroup(project, 'NestedGroup/Libraries');
    expect(group.children.length).to.equals(0);
    expect(group.name).to.equals('Libraries');
  });
});

const chai = require('chai');
const expect = chai.expect;
const xcode = require('xcode');
const getGroup = require('../../src/ios/getGroup');

const project = xcode.project('test/fixtures/project.pbxproj');

describe('ios::getGroup', () => {
  beforeEach(() => {
    project.parseSync();
  });

  it('should return a top-level group', () => {
    const group = getGroup(project, 'Libraries');
    expect(group.children.length > 0).to.be.true; // our test top-level Libraries has children
    expect(group.name).to.equals('Libraries');
  });

  it('should return nested group when specified', () => {
    const group = getGroup(project, 'NestedGroup/Libraries');
    expect(group.children.length).to.equals(0); // our test nested Libraries is empty
    expect(group.name).to.equals('Libraries');
  });

  it('should return null when no group found', () => {
    const group = getGroup(project, 'I-Dont-Exist');
    expect(group).to.be.null;
  });
});

Clarify assertions and add missing suite


var proxyMiddleware = require('http-proxy-middleware');

module.exports = {
  port: 3000,
  open: false,
  files: ["./dist/**/*.{html,css,js,less}"],
  server: {
    baseDir: './dist',
    routes: {
      '/output': '../output'
    },
    middleware: {
      1: proxyMiddleware('/api', {
        target: 'http://localhost:5566',
      changeOrigin: true   // for vhosted sites, changes host header to match to target's host
      }),
      2: proxyMiddleware('/go', {
        target: 'http://localhost:5566',
      changeOrigin: true   // for vhosted sites, changes host header to match to target's host
      }),

    }
  }
};

var proxyMiddleware = require('http-proxy-middleware');

var onProxyRes = function (proxyRes, res, req) {
  if (proxyRes.headers['location']) {
    proxyRes.headers['location'] = proxyRes.headers['location'].replace('http://localhost:5566', 'http://localhost:3000')
  }
}

module.exports = {
  port: 3000,
  open: false,
  files: ["./dist/**/*.{html,css,js,less}"],
  server: {
    baseDir: './dist',
    routes: {
      '/output': '../output'
    },
    middleware: {
      1: proxyMiddleware('/api', {
        target: 'http://localhost:5566',
        onProxyRes: onProxyRes,
        changeOrigin: true   // for vhosted sites, changes host header to match to target's host
      }),
      2: proxyMiddleware('/go', {
        target: 'http://localhost:5566',
        onProxyRes: onProxyRes,
        changeOrigin: true   // for vhosted sites, changes host header to match to target's host
      }),

    }
  }
};

Make the middleware do the right thing for Location headers


"use strict";

var GameState = function(game) {}

var publicMethods = function() {
  this.create = function() {};
};

var privateMethods = function() {};

privateMethods.call(GameState.prototype);
publicMethods.call(GameState.prototype);

module.exports = GameState;

"use strict";

var StateIds = require("./States");

var GameState = function(game) {}

var publicMethods = function() {
  this.create = function() {
    this.game.add.button(400, 400, 'button', function() {
        this.game.state.start(StateIds.MENU_STATE_ID);
      },
      this, 0, 1, 2);
  };

  this.resize = function() {
    Game.scale.setGameSize(window.innerWidth, window.innerHeight);
  };
};

var privateMethods = function() {};

privateMethods.call(GameState.prototype);
publicMethods.call(GameState.prototype);

module.exports = GameState;

Add return to menu button in game


var config = require('./protractor-shared.conf').config;

config.sauceUser = process.env.SAUCE_USERNAME;
config.sauceKey = process.env.SAUCE_ACCESS_KEY;

config.allScriptsTimeout = 60000;

config.multiCapabilities = [
    {
        browserName: 'chrome',
        version: '35',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Chrome'
    },
    {
        browserName: 'firefox',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Firefox'
    },
    {
        browserName: 'internet explorer',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with IE 11'
    },
    {
        browserName: 'safari',
        version: '7',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Safari'
    }
];

exports.config = config;

var config = require('./protractor-shared.conf').config;

config.sauceUser = process.env.SAUCE_USERNAME;
config.sauceKey = process.env.SAUCE_ACCESS_KEY;

config.allScriptsTimeout = 60000;

config.multiCapabilities = [
    {
        browserName: 'chrome',
        platform: 'Windows 8.1',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Chrome'
    },
    {
        browserName: 'firefox',
        platform: 'Windows 8.1',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Firefox'
    },
    {
        browserName: 'internet explorer',
        platform: 'Windows 8.1',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with IE 11'
    },
    {
        browserName: 'safari',
        version: '7',
        'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
        build: process.env.TRAVIS_BUILD_NUMBER,
        name: 'Catenaccio with Safari'
    }
];

exports.config = config;

Change platform from Linux to Windows 8.1


import React, { Component } from 'react';
import { View } from 'react-native';
import RepoSearchBar from './components/RepoSearchBar';
import RepoSearchButton from './components/RepoSearchButton';
import RepoList from './components/RepoList';

export default class SearchScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: '',
      repos: []
    };
    this.changeUsername = this.changeUsername.bind(this);
    this.updateRepos = this.updateRepos.bind(this);
  }

  render() {
    let buttonDisabled = this.state.username.length === 0;
    return (
      <View style={{
        flex: 1,
        flexDirection: 'column',
      }}>
        <RepoSearchBar
          onChangeText={this.changeUsername}/>
        <RepoSearchButton
          disabled={buttonDisabled}
          username={this.state.username}
          onPress={this.updateRepos} />
        <RepoList />
      </View>
    );
  }

  changeUsername(newUsername) {
    this.setState({
      username: newUsername
    });
  }

  updateRepos(newRepos) {
    this.setState({
      repos: newRepos
    });
  }
}

import React, { Component } from 'react';
import { View } from 'react-native';
import RepoSearchBar from './components/RepoSearchBar';
import RepoSearchButton from './components/RepoSearchButton';
import RepoList from './components/RepoList';

export default class SearchScreen extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: '',
      repos: []
    };
    this.changeUsername = this.changeUsername.bind(this);
    this.updateRepos = this.updateRepos.bind(this);
  }

  render() {
    let buttonDisabled = this.state.username.length === 0;
    return (
      <View style={{
        flex: 1,
        flexDirection: 'column',
      }}>
        <RepoSearchBar
          onChangeText={this.changeUsername}/>
        <RepoSearchButton
          disabled={buttonDisabled}
          username={this.state.username}
          onPress={this.updateRepos} />
        <RepoList
          list={this.state.repos}/>
      </View>
    );
  }

  changeUsername(newUsername) {
    this.setState({
      username: newUsername
    });
  }

  updateRepos(newRepos) {
    this.setState({
      repos: newRepos
    });
  }
}

Add repos to list component


import { run } from '@ember/runloop';
import { NAMESPACES, NAMESPACES_BY_ID } from '@ember/-internals/metal';

function NamespacesAssert(env) {
  this.env = env;
}

NamespacesAssert.prototype = {
  reset: function() {},
  inject: function() {},
  assert: function() {
    let { assert } = QUnit.config.current;

    if (NAMESPACES.length > 0) {
      assert.ok(false, 'Should not have any NAMESPACES after tests');
      run(() => {
        let namespaces = NAMESPACES.slice();
        for (let i = 0; i < namespaces.length; i++) {
          namespaces[i].destroy();
        }
      });
    }
    let keys = Object.keys(NAMESPACES_BY_ID);
    if (keys.length > 0) {
      assert.ok(false, 'Should not have any NAMESPACES_BY_ID after tests');
      for (let i = 0; i < keys.length; i++) {
        delete NAMESPACES_BY_ID[keys[i]];
      }
    }
  },
  restore: function() {},
};

export default NamespacesAssert;

import { run } from '@ember/runloop';
import { NAMESPACES, NAMESPACES_BY_ID } from '@ember/-internals/metal';

export default class NamespacesAssert {
  constructor(env) {
    this.env = env;
  }

  reset() {}

  inject() {}

  assert() {
    let { assert } = QUnit.config.current;

    if (NAMESPACES.length > 0) {
      assert.ok(false, 'Should not have any NAMESPACES after tests');
      run(() => {
        let namespaces = NAMESPACES.slice();
        for (let i = 0; i < namespaces.length; i++) {
          namespaces[i].destroy();
        }
      });
    }
    let keys = Object.keys(NAMESPACES_BY_ID);
    if (keys.length > 0) {
      assert.ok(false, 'Should not have any NAMESPACES_BY_ID after tests');
      for (let i = 0; i < keys.length; i++) {
        delete NAMESPACES_BY_ID[keys[i]];
      }
    }
  }

  restore() {}
}

Convert `NamespacesAssert` to ES6 class


'use strict';

angular.module('chemartApp')
  .factory('moleculeDrawer', function (centerAtoms, canvas) {
    return function (molecule) {
      var currentMolecule = canvas.getMolecule();
      var time = 200;

      centerAtoms(molecule);

      for (var i in molecule.atoms) {
        if (typeof currentMolecule.atoms[i] !== 'undefined') {
          new TWEEN.Tween(currentMolecule.atoms[i].position)
            .to(molecule.atoms[i].position, time)
            .easing(TWEEN.Easing.Circular.Out)
            .start();
        }
      }

      var endTime = Date.now() + time;

      animate();

      function animate() {
        if (Date.now() < endTime) {
          requestAnimationFrame(animate);
        }

        TWEEN.update();
        canvas.update();
      }

      setTimeout(function () {
        canvas.clear();
        canvas.attach(molecule);
      }, time);
    };
  });

'use strict';

angular.module('chemartApp')
  .factory('moleculeDrawer', function (centerAtoms, canvas) {
    return function (molecule) {
      var currentMolecule = canvas.getMolecule();
      var time = 400;

      centerAtoms(molecule);

      for (var i in molecule.atoms) {
        if (typeof currentMolecule.atoms[i] !== 'undefined') {
          new TWEEN.Tween(currentMolecule.atoms[i].position)
            .to(molecule.atoms[i].position, time)
            .easing(TWEEN.Easing.Circular.Out)
            .start();
        }
      }

      var endTime = Date.now() + time;

      function animate() {
        if (Date.now() < endTime) {
          requestAnimationFrame(animate);
        }

        TWEEN.update();
        canvas.update();
      }

      requestAnimationFrame(animate);

      setTimeout(function () {
        canvas.clear();
        canvas.attach(molecule);
      }, time);
    };
  });

Add transition time and take redraw function into requestAnimationFrame


export default ({ colors, borders }) => ({
  primary: {
    backgroundColor: colors.purpleMedium,
    border: borders.input,
    borderColor: colors.purpleMedium,
    color: colors.white,
    '&:hover,&:active,&:focus': {
      backgroundColor: colors.text.primary,
      borderColor: colors.text.primary,
    },
    '&:disabled': {
      backgroundColor: colors.lightestGrey,
      borderColor: colors.lightestGrey,
      color: colors.text.primaryDisabled,
    },
  },
  secondary: {
    bg: colors.white,
    color: colors.text.primary,
    border: borders.input,
    '&:hover,&:active,&:focus': {
      color: colors.white,
      backgroundColor: colors.text.primary,
      borderColor: colors.text.primary,
    },
    '&:disabled': {
      backgroundColor: colors.lightestGrey,
      borderColor: colors.lightestGrey,
      color: colors.text.primaryDisabled,
    },
  },
});

export default ({ colors, borders }) => ({
  primary: {
    backgroundColor: colors.purpleMedium,
    border: borders.input,
    borderColor: colors.purpleMedium,
    color: colors.white,
    '&:hover,&:active': {
      backgroundColor: colors.text.primary,
      borderColor: colors.text.primary,
    },
    '&:disabled': {
      backgroundColor: colors.lightestGrey,
      borderColor: colors.lightestGrey,
      color: colors.text.primaryDisabled,
    },
  },
  secondary: {
    bg: colors.white,
    color: colors.text.primary,
    border: borders.input,
    '&:hover,&:active': {
      color: colors.white,
      backgroundColor: colors.text.primary,
      borderColor: colors.text.primary,
    },
    '&:disabled': {
      backgroundColor: colors.lightestGrey,
      borderColor: colors.lightestGrey,
      color: colors.text.primaryDisabled,
    },
  },
});

Fix issue with button focus styles


{
  class UsersService {
    create(user) {
      console.log('CREATED!');
      console.log(user);
    }
  }

  angular
    .module('meganote.users')
    .service('UsersService', UsersService);
}

{
  angular
    .module('meganote.users')
    .service('UsersService', [
      '$http',
      'API_BASE',
      ($http, API_BASE) =>{

        class UsersService {
          create(user) {
            return $http.get(API_BASE)
              .then(res => {
                console.log(res.data);
              });
          }
        }

        return new UsersService();
      }
    ]);
}

Refactor sign-up directive to use UsersService


'use strict';
describe('controller specs', function() {
  var $scope;

  beforeEach(module('myApp.controllers'));

  beforeEach(inject(function($rootScope, $controller) {
    $scope = $rootScope.$new();
    $controller('helloWorldCtrl', {$scope: $scope});
  }));

  it('should create "name" model with first name "Jane"', function() {
    expect($scope.name.first).toBe('Jane');
  });
});

'use strict';
describe('controller specs', function() {
  var $scope;

  beforeEach(module('myApp.controllers'));

  beforeEach(inject(function($rootScope, $controller) {
    $scope = $rootScope.$new();
    $controller('helloWorldCtrl', {$scope: $scope});
  }));

  it('should create "person" model with first name "Jane"', function() {
    expect($scope.person.firstName).toBe('Jane');
  });

  it('should create "person" model with last name "Doe"', function() {
    expect($scope.person.firstName).toBe('Doe');
  });
});

Fix of broken test and addition of new test


#!/usr/bin/env node

const cli = require('commander');
const path = require('path');
const glob = require('glob');

const options = {
  realpath: true,
  cwd: path.resolve(__dirname, './lib'),
};

glob
  .sync('*/index.js', options)
  .map(require)
  .forEach(fn => fn(cli));

cli.version('0.0.5-alpha');
cli.parse(process.argv);

if (!cli.args.length) cli.help();

#!/usr/bin/env node

const cli = require('commander');
const path = require('path');
const glob = require('glob');

const options = {
  realpath: true,
  cwd: path.resolve(__dirname, './lib'),
};

glob
  .sync('*/index.js', options)
  .map(require)
  .forEach(fn => fn(cli));

cli.version('0.0.6-alpha');
cli.parse(process.argv);

if (!cli.args.length) cli.help();

Update CLI version to 0.0.6-alpha


"use strict";

var UserPages = require("./UserPages.js");
var MailParser = require("mailparser").MailParser;
var fs = require("fs");

var restUrl = "http://localhost:9080/";

var hasClass = function (element, cls) {
    return element.getAttribute("class").then(function (classes) {
        return classes.split(" ").indexOf(cls) !== -1;
    });
};

var parseEmail = function(filename, callback) {
    var mailparser = new MailParser();

    mailparser.on("end", callback);
    mailparser.write(fs.readFileSync(filename));
    mailparser.end();
};

module.exports = {
    restUrl: restUrl,
    register: UserPages.register,
    login: UserPages.login,
    logout: UserPages.logout,
    isLoggedIn: UserPages.isLoggedIn,
    loginParticipant: UserPages.loginParticipant,
    participantName: UserPages.participantName,
    loginOtherParticipant: UserPages.loginOtherParticipant,
    hasClass: hasClass,
    parseEmail: parseEmail
}

"use strict";

var UserPages = require("./UserPages.js");
var MailParser = require("mailparser").MailParser;
var fs = require("fs");
var EC = protractor.ExpectedConditions;

var restUrl = "http://localhost:9080/";

var hasClass = function (element, cls) {
    return element.getAttribute("class").then(function (classes) {
        return classes.split(" ").indexOf(cls) !== -1;
    });
};

var parseEmail = function(filename, callback) {
    var mailparser = new MailParser();

    mailparser.on("end", callback);
    mailparser.write(fs.readFileSync(filename));
    mailparser.end();
};

var waitAndClick = function(element) {
    var isClickable = EC.elementToBeClickable(element);
    browser.wait(isClickable, 5000);
    element.click();
}

module.exports = {
    restUrl: restUrl,
    register: UserPages.register,
    login: UserPages.login,
    logout: UserPages.logout,
    isLoggedIn: UserPages.isLoggedIn,
    loginParticipant: UserPages.loginParticipant,
    participantName: UserPages.participantName,
    loginOtherParticipant: UserPages.loginOtherParticipant,
    hasClass: hasClass,
    parseEmail: parseEmail,
    waitAndClick: waitAndClick
}

Add protractor test function to click after an element is loaded


#!/usr/bin/env node
/*
 * Copyright (c) 2015-2016 PointSource, LLC.
 * MIT Licensed
 */
var server = require('../');

server.init({
    appDir: process.cwd()
}, function(err) {
    if (err) {
        console.warn('Startup failed', err);
    } else {
        console.log('started');
    }

});

#!/usr/bin/env node
/*
 * Copyright (c) 2015-2016 PointSource, LLC.
 * MIT Licensed
 */
var server = require('../');

server.init({
    appDir: process.cwd()
}, function(err) {
    if (err) {
        console.warn('Startup failed', err);
    } else {
        var logger = this.services.get('logger');
        logger.info('Server started');
    }

});

Make server launcher use logger for server startup message


import Em from 'ember';
import { defaultValue } from 'llama-table/computed';
var computed = Em.computed;
var alias = computed.alias;
var reads = computed.reads;

/**
 * Adds some extra properties to row definitions.
 * @module controllers
 * @constructor
 * @class RowController
 * @extends Ember.ObjectProxy
 */
var RowController = Em.ObjectProxy.extend({
	content: alias('model'),
	contentIndex: computed('parentController.@each', function () {
		var array = this.get('parentController');
		var content = this;
		var index = array.indexOf(content);
		return index;
	}),
	isExpanded: false,
	height: defaultValue('model.height', 30),
	subcontentHeight: reads('height')
});

export default RowController;

import Em from 'ember';
import { defaultValue } from 'llama-table/computed';
var makeArray = Em.makeArray;
var computed = Em.computed;
var alias = computed.alias;
var reads = computed.reads;

/**
 * Adds some extra properties to row definitions.
 * @module controllers
 * @constructor
 * @class RowController
 * @extends Ember.ObjectProxy
 */
var RowController = Em.ObjectProxy.extend({
	content: alias('model'),
	contentIndex: computed('parentController.@each', function () {
		var array = this.get('parentController');
		var content = this;
		var index = makeArray(array).indexOf(content);
		return index;
	}),
	isExpanded: false,
	height: defaultValue('model.height', 30),
	subcontentHeight: reads('height')
});

export default RowController;

Make sure parent controller is an array


// satisfy ag-grid HTMLElement dependency
HTMLElement = typeof HTMLElement === 'undefined' ? function () {} : HTMLElement;
HTMLSelectElement = typeof HTMLSelectElement === 'undefined' ? function () {} : HTMLSelectElement;

var {AgGridNg2} = require('./dist/agGridNg2');
var {ComponentUtil} = require("ag-grid/main");

var missingProperties = [];
ComponentUtil.ALL_PROPERTIES.forEach((property) => {
    if (!AgGridNg2.propDecorators.hasOwnProperty(property)) {
        missingProperties.push(`Grid property ${property} does not exist on AgGridNg2`)
    }
});

var missingEvents = [];
ComponentUtil.EVENTS.forEach((event) => {
    if (!AgGridNg2.propDecorators.hasOwnProperty(event)) {
        missingEvents.push(`Grid event ${event} does not exist on AgGridNg2`)
    }
});

if(missingProperties.length || missingEvents.length) {
    console.error("*************************** BUILD FAILED ***************************");
    missingProperties.concat(missingEvents).forEach((message) => console.error(message));
    console.error("*************************** BUILD FAILED ***************************");

    throw("Build Properties Check Failed");
} else {
    console.info("*************************** BUILD OK ***************************");
}

// satisfy ag-grid HTMLElement dependency
HTMLElement = typeof HTMLElement === 'undefined' ? function () {} : HTMLElement;
HTMLSelectElement = typeof HTMLSelectElement === 'undefined' ? function () {} : HTMLSelectElement;
HTMLInputElement = typeof HTMLInputElement === 'undefined' ? function () {} : HTMLInputElement;

var {AgGridNg2} = require('./dist/agGridNg2');
var {ComponentUtil} = require("ag-grid/main");

var missingProperties = [];
ComponentUtil.ALL_PROPERTIES.forEach((property) => {
    if (!AgGridNg2.propDecorators.hasOwnProperty(property)) {
        missingProperties.push(`Grid property ${property} does not exist on AgGridNg2`)
    }
});

var missingEvents = [];
ComponentUtil.EVENTS.forEach((event) => {
    if (!AgGridNg2.propDecorators.hasOwnProperty(event)) {
        missingEvents.push(`Grid event ${event} does not exist on AgGridNg2`)
    }
});

if(missingProperties.length || missingEvents.length) {
    console.error("*************************** BUILD FAILED ***************************");
    missingProperties.concat(missingEvents).forEach((message) => console.error(message));
    console.error("*************************** BUILD FAILED ***************************");

    throw("Build Properties Check Failed");
} else {
    console.info("*************************** BUILD OK ***************************");
}

Add HTMLInputElement to satisfy ag-grid dependecy


var promisesAplusTests = require("promises-aplus-tests"),
    spromise = require("../dist/spromise-debug");

// Adapter so that promise tests can run
function adapter() {
  var promise1 = spromise();
  return {
    promise: promise1.promise,
    resolve: promise1.resolve,
    reject: promise1.reject
  };
}

promisesAplusTests({deferred:adapter}, function (err) {
  console.log("=====> Errors:");
  console.log(err);
});

var promisesAplusTests = require("promises-aplus-tests"),
    spromise = require("../dist/spromise-debug");

spromise.debug = false;

// Adapter so that promise tests can run
function adapter() {
  var promise1 = spromise();
  return {
    promise: promise1.promise,
    resolve: promise1.resolve,
    reject: promise1.reject
  };
}

promisesAplusTests({deferred:adapter}, function (err) {
  console.log("=====> Errors:");
  console.log(err);
});

Disable debug when running unit tests for compliance


horizon.metering = {
  init_create_usage_report_form: function() {
    horizon.datepickers.add('input[data-date-picker="True"]');
    horizon.metering.add_change_event_to_period_dropdown();
    horizon.metering.show_or_hide_date_fields();
  },
  init_stats_page: function() {
    if (typeof horizon.d3_line_chart !== 'undefined') {
      horizon.d3_line_chart.init("div[data-chart-type='line_chart']",
                                 {'auto_resize': true});
    }
    horizon.metering.add_change_event_to_period_dropdown();
    horizon.metering.show_or_hide_date_fields();
  },
  show_or_hide_date_fields: function() {
    $("#date_from .controls input, #date_to .controls input").val('');
    if ($("#id_period").find("option:selected").val() === "other"){
      $("#id_date_from, #id_date_to").parent().parent().show();
      return true;
    } else {
      $("#id_date_from, #id_date_to").parent().parent().hide();
      return false;
    }
  },
  add_change_event_to_period_dropdown: function() {
    $("#id_period").change(function(evt) {
        if (horizon.metering.show_or_hide_date_fields()) {
          evt.stopPropagation();
        }
    });
  }
};

horizon.metering = {
  init_create_usage_report_form: function() {
    horizon.datepickers.add('input[data-date-picker]');
    horizon.metering.add_change_event_to_period_dropdown();
    horizon.metering.show_or_hide_date_fields();
  },
  init_stats_page: function() {
    if (typeof horizon.d3_line_chart !== 'undefined') {
      horizon.d3_line_chart.init("div[data-chart-type='line_chart']",
                                 {'auto_resize': true});
    }
    horizon.metering.add_change_event_to_period_dropdown();
    horizon.metering.show_or_hide_date_fields();
  },
  show_or_hide_date_fields: function() {
    $("#date_from .controls input, #date_to .controls input").val('');
    if ($("#id_period").find("option:selected").val() === "other"){
      $("#id_date_from, #id_date_to").parent().parent().show();
      return true;
    } else {
      $("#id_date_from, #id_date_to").parent().parent().hide();
      return false;
    }
  },
  add_change_event_to_period_dropdown: function() {
    $("#id_period").change(function(evt) {
        if (horizon.metering.show_or_hide_date_fields()) {
          evt.stopPropagation();
        }
    });
  }
};

Change widget attribute to string


const Koa = require('koa')
const Routes = require('./routes')

module.exports = class {
  constructor(app, container, config, log) {
    this.app = app
    this.log = log
    this.container = container
    this.config = config

    this.koa = new Koa()
    container.register('koa', this.koa, { type: 'value' })

    this.port = config.get('quadro.http.port', 3000)
  }

  async initialize() {
    const koaBody = require('koa-body')
    this.koa.use(koaBody({
      multipart: true
    }))

    await this.app.emit('routes-will-load', { quadroHttpServer: this })

    let routes = await this.container.create(Routes)
    this.koa.use(await routes.build())

    await this.app.emit('routes-did-load', { quadroHttpServer: this })
  }

  async listen() {
    let port = this.port
    return new Promise((resolve) => {
      let http = this.koa.listen(port, () => {
        this.log.info({ port }, 'HTTP Server listening')
        resolve(this.configure(http))
      })
    })
  }

  configure(http) {
    let timeout = this.config.get('quadro.http.timeout')
    if (timeout) http.setTimeout(timeout)

    return http
  }
}

const Koa = require('koa')
const Routes = require('./routes')

module.exports = class {
  constructor(app, container, config, log) {
    this.app = app
    this.log = log
    this.container = container
    this.config = config

    this.koa = new Koa()
    container.register('koa', this.koa, { type: 'value' })

    this.port = config.get('quadro.http.port', 3000)
  }

  async initialize() {
    const enableKoaBodyParser = await this.config.get('quadro.middleware.koa-body', true)

    if (enableKoaBodyParser) {
      const koaBody = require('koa-body')
      this.koa.use(
        koaBody({
          multipart: true
        })
      )
    }

    await this.app.emit('routes-will-load', { quadroHttpServer: this })

    let routes = await this.container.create(Routes)
    this.koa.use(await routes.build())

    await this.app.emit('routes-did-load', { quadroHttpServer: this })
  }

  async listen() {
    let port = this.port
    return new Promise((resolve) => {
      let http = this.koa.listen(port, () => {
        this.log.info({ port }, 'HTTP Server listening')
        resolve(this.configure(http))
      })
    })
  }

  configure(http) {
    let timeout = this.config.get('quadro.http.timeout')
    if (timeout) http.setTimeout(timeout)

    return http
  }
}

Enable disabling of koa-body parser


var GOVUK = GOVUK || {};
GOVUK.Analytics = GOVUK.Analytics || {};
GOVUK.Analytics.Trackers = {};

/*
 * Available methods on control:
 * - trackTimeBasedSuccess(millisecondsUntilSuccess)
 * - trackLinks(linkSelector)
 */
GOVUK.Analytics.Trackers.guide = function (control) {
    control.trackTimeBasedSuccess(7000);
    control.trackLinks("#content a");
};

GOVUK.Analytics.Trackers.transaction = function (control) {
    control.trackLinks("#content a");
};

/* Benefit */
GOVUK.Analytics.Trackers.programme = function (control) {
    control.trackTimeBasedSuccess(7000);
    control.trackLinks("#content a");
};

/* Quick answer */
GOVUK.Analytics.Trackers.answer = function (control) {
    control.trackTimeBasedSuccess(7000);
    control.trackLinks("#content a");
};

GOVUK.Analytics.Trackers.local_transaction = function(control) {
    control.trackLinks("#get-started a");
    control.trackLinks("#content a[rel='external']");
};

Add success tracking for local transactions.


/**
 * Determines whether one string may be found within another string, returning true or false as appropriate.
 * @param {String} subjectString The string to process.
 * @param {String} searchString A string to be searched for within the original string.
 * @param {Number} [position=0] The position in the original string at which to begin searching for searchString; defaults to 0.
 * @returns {Boolean} True if searchString is found within subjectString, else false.
 */
function contains(subjectString, searchString, position) {
    'use strict';

    var subjectLength,
        searchLength;

    if (String.prototype.includes) {
        return subjectString.includes(searchString, position);
    }

    if (!searchString) {
        return false;
    }

    position = parseInt(position, 10) || 0;
    subjectLength = subjectString.length;
    searchLength = searchString.length;

    return position + searchLength <= subjectLength ?
        subjectString.indexOf(searchString, position) !== -1 :
        false;
}

/**
 * Determines whether one string may be found within another string, returning true or false as appropriate.
 * @param {String} subjectString The string to process.
 * @param {String} searchString A string to be searched for within the original string.
 * @param {Number} [position=0] The position in the original string at which to begin searching for searchString; defaults to 0.
 * @returns {Boolean} True if searchString is found within subjectString, else false.
 */
function contains(subjectString, searchString, position) {
    'use strict';

    var subjectLength,
        searchLength;

    if (!searchString) {
        return false;
    }

    if (String.prototype.includes) {
        return subjectString.includes(searchString, position);
    }

    position = parseInt(position, 10) || 0;
    subjectLength = subjectString.length;
    searchLength = searchString.length;

    return position + searchLength <= subjectLength ?
        subjectString.indexOf(searchString, position) !== -1 :
        false;
}

Return immediately if not search string provided


/* See license.txt for terms of usage */

define(function(require, exports, module) {

// Dependencies
const React = require("react");
const { PopupPanel } = require("popup-panel");
const { OverlayStore } = require("overlay-store");

// Get overlay data from persistent store.
var state = {
  overlays: [],
  selection: null
}

// Render panel content
var panel = React.render(PopupPanel(state), document.body);

// Handle refresh events sent from the chrome scope and refresh
// the panel content (panel component).
window.addEventListener("refresh", onRefresh);
function onRefresh(event) {
  var data = JSON.parse(event.data);
  panel.setState({overlays: data});
}

// Display the page content when it's ready to avoid flashing
// during the page load.
document.addEventListener("load", event => {
  document.body.removeAttribute("collapsed");
}, true);

// Panel is loaded, let the chrome content send the first
// 'refresh' message.
postChromeMessage("panel-ready");

// End of main.js
});

/* See license.txt for terms of usage */

define(function(require, exports, module) {

// Dependencies
const React = require("react");
const { PopupPanel } = require("popup-panel");
const { OverlayStore } = require("overlay-store");

// Get overlay data from persistent store.
var state = {
  overlays: [],
  selection: null
}

// Render panel content
var panel = React.render(PopupPanel(state), document.body);

// Handle refresh events sent from the chrome scope and refresh
// the panel content (panel component).
window.addEventListener("refresh", onRefresh);
function onRefresh(event) {
  var data = JSON.parse(event.data);
  // xxxHonza: properly pick the default selection
  panel.setState({overlays: data, selection: data[0]});
}

// Display the page content when it's ready to avoid flashing
// during the page load.
document.addEventListener("load", event => {
  document.body.removeAttribute("collapsed");
}, true);

// Panel is loaded, let the chrome content send the first
// 'refresh' message.
postChromeMessage("panel-ready");

// End of main.js
});

Select the first overlay by default


/*global angular*/
(function () {
    'use strict';

    angular.module('core').directive('qrDeviceInteraction', function () {
        var template = '';
        template += '';
        template += '    <qr text="qrConfig.slideUrl" type-number="qrConfig.typeNumber" correction-level="qrConfig.correctionLevel"';
        template += '        size="qrConfig.size" input-mode="qrConfig.inputMode" image="qrConfig.image">';
        template += '    </qr>';
        template += '';

        return {
            restrict: 'AE',
            template: template,
        };
    });
}());

/*global angular*/
(function () {
    'use strict';

    angular.module('core').directive('qrDeviceInteraction', function () {
        var template = '';
        template += '';
        template += '    <qr text="qrConfig.slideUrl" type-number="qrConfig.typeNumber" correction-level="qrConfig.correctionLevel"';
        template += '        size="qrConfig.size" input-mode="qrConfig.inputMode" image="qrConfig.image">';
        template += '    </qr>';
        template += '';

        return {
            restrict: 'AE',
            template: template
        };
    });
}());

Fix massive SPAM of errors from QR code in the browser console.


/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/* @flow */
import type { StackFrame } from './stack-frame';
import { parse } from './parser';
import { map } from './mapper';
import { unmap } from './unmapper';

function getStackFrames(
  error: Error,
  unhandledRejection: boolean = false,
  contextSize: number = 3
): Promise<StackFrame[] | null> {
  const parsedFrames = parse(error);
  let enhancedFramesPromise;
  if (error.__unmap_source) {
    enhancedFramesPromise = unmap(
      // $FlowFixMe
      error.__unmap_source,
      parsedFrames,
      contextSize
    );
  } else {
    enhancedFramesPromise = map(parsedFrames, contextSize);
  }
  return enhancedFramesPromise.then(enhancedFrames => {
    if (
      enhancedFrames
        .map(f => f._originalFileName)
        .filter(f => f != null && f.indexOf('node_modules') === -1).length === 0
    ) {
      return null;
    }
    return enhancedFrames.filter(
      ({ functionName }) =>
        functionName == null ||
        functionName.indexOf('__stack_frame_overlay_proxy_console__') === -1
    );
  });
}

export default getStackFrames;
export { getStackFrames };

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/* @flow */
import type { StackFrame } from './stack-frame';
import { parse } from './parser';
import { map } from './mapper';
import { unmap } from './unmapper';

function getStackFrames(
  error: Error,
  unhandledRejection: boolean = false,
  contextSize: number = 3
): Promise<StackFrame[] | null> {
  const parsedFrames = parse(error);
  let enhancedFramesPromise;
  if (error.__unmap_source) {
    enhancedFramesPromise = unmap(
      // $FlowFixMe
      error.__unmap_source,
      parsedFrames,
      contextSize
    );
  } else {
    enhancedFramesPromise = map(parsedFrames, contextSize);
  }
  return enhancedFramesPromise.then(enhancedFrames => {
    /*
    if (
      enhancedFrames
        .map(f => f._originalFileName)
        .filter(f => f != null && f.indexOf('node_modules') === -1).length === 0
    ) {
      return null;
    }
    */
    return enhancedFrames.filter(
      ({ functionName }) =>
        functionName == null ||
        functionName.indexOf('__stack_frame_overlay_proxy_console__') === -1
    );
  });
}

export default getStackFrames;
export { getStackFrames };

Fix throwing when no source maps on calling `forEach` of null