import DS from 'ember-data';
import ProjectedModel from './projected-model';
import Proj from '../utils/projection-attributes';

var Model = ProjectedModel.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  birthDate: DS.attr('date'),
  employee1: DS.belongsTo('employee', { inverse: null, async: false }),
  orders: DS.hasMany('order', { inverse: null, async: false }),

  // Validation rules.
  validations: {
    firstName: {
      presence: true,
      length: { minimum: 5 }
    },
    lastName: {
      presence: true,
      length: { minimum: 5 }
    }
  }
});

Model.defineProjection('EmployeeE', 'employee', {
  firstName: Proj.attr('First Name'),
  lastName: Proj.attr('Last Name'),
  birthDate: Proj.attr('Birth Date'),
  employee1: Proj.belongsTo('employee', {
    firstName: Proj.attr('Reports To - First Name'),
    lastName: Proj.attr('Reports To - Last Name', { hidden: true })
  }),
  orders: Proj.hasMany('order', {
    shipName: Proj.attr('Ship Name'),
    ShipCountry: Proj.attr('Ship Country'),
    OrderDate: Proj.attr('Order Date')
  })
});

Model.defineProjection('EmployeeL', 'employee', {
  firstName: Proj.attr('First Name'),
  lastName: Proj.attr('Last Name')
});

export default Model;

import DS from 'ember-data';
import ProjectedModel from './projected-model';
import Proj from '../utils/projection-attributes';

var Model = ProjectedModel.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  birthDate: DS.attr('date'),
  employee1: DS.belongsTo('employee', { inverse: null, async: false }),
  orders: DS.hasMany('order', { inverse: null, async: false }),

  // Validation rules.
  validations: {
    firstName: {
      presence: true,
      length: { minimum: 5 }
    },
    lastName: {
      presence: true,
      length: { minimum: 5 }
    }
  }
});

Model.defineProjection('EmployeeE', 'employee', {
  firstName: Proj.attr('First Name'),
  lastName: Proj.attr('Last Name'),
  birthDate: Proj.attr('Birth Date'),
  employee1: Proj.belongsTo('employee', {
    firstName: Proj.attr('Reports To - First Name'),
    lastName: Proj.attr('Reports To - Last Name', { hidden: true })
  }),
  orders: Proj.hasMany('order', {
    shipName: Proj.attr('Ship Name'),
    shipCountry: Proj.attr('Ship Country'),
    orderDate: Proj.attr('Order Date')
  })
});

Model.defineProjection('EmployeeL', 'employee', {
  firstName: Proj.attr('First Name'),
  lastName: Proj.attr('Last Name')
});

export default Model;

Fix typos in EmployeeE projection


var numericString = function (number) {
  var string = ('' + number).split('.'),
      length = string[0].length,
      places = 0;

  while (--length) {
    places += 1;
    // At every third position we want to insert a comma
    if (places % 3 === 0) {
      string[0] = string[0].substr(0, length) + ',' + string[0].substr(length);
    }
  }

  return string.join('.');
};

var numericString = function (number) {
  var parts  = ('' + number).split('.'),
      length = parts[0].length,
      places = 0;

  while (--length) {
    places += 1;
    // At every third position we want to insert a comma
    if (places % 3 === 0) {
      parts[0] = parts[0].substr(0, length) + ',' + parts[0].substr(length);
    }
  }

  return parts.join('.');
};

// Example using regular expression - let me know if I can skip the split step
// some how
var numericString = function (number) {
  var parts = ('' + number).split('.');

  parts[0] = parts[0].replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');

  return parts.join('.');
};

Implement regex numeric string solution.


import { GET_TONE } from '../actions/index';

export default function (state = [], action) {
  switch (action.type) {
    case GET_TONE:
      return action.payload.data;

    default:
      return state;
  }
}

import { GET_TONE } from '../actions/index';

export default function (state = [], action) {
  switch (action.type) {
    case GET_TONE:
      if (action.payload.status === 200) {
        return action.payload.data;
      }
      return state;
    default:
      return state;
  }
}

Add conditional to tone reducer


class MCWorkflowProcessTemplatesComponentController {
    /*@ngInit*/
    constructor(templates) {
        this.templates = templates.get();
        this.templateTypes = [
            {
                title: 'CREATE SAMPLES',
                cssClass: 'mc-create-samples-color',
                icon: 'fa-cubes',
                margin: true,
                templates: this.templates.filter(t => t.process_type === 'create' && t.name === 'Create Samples')
            },
            {
                title: 'TRANSFORMATION',
                cssClass: 'mc-transform-color',
                icon: 'fa-exclamation-triangle',
                templates: this.templates.filter(t => t.process_type === 'transform' && t.name !== 'Create Samples')
            },
            {
                title: 'MEASUREMENT',
                cssClass: 'mc-measurement-color',
                icon: 'fa-circle',
                templates: this.templates.filter(t => t.process_type === 'measurement')
            },
            {
                title: 'ANALYSIS',
                cssClass: 'mc-analysis-color',
                icon: 'fa-square',
                templates: this.templates.filter(t => t.process_type === 'analysis')
            }
        ];
    }

    chooseTemplate(t) {
        if (this.onSelected) {
            this.onSelected({templateId: t.name, processId: ''});
        }
    }
}

angular.module('materialscommons').component('mcWorkflowProcessTemplates', {
    templateUrl: 'app/project/experiments/experiment/components/processes/mc-workflow-process-templates.html',
    controller: MCWorkflowProcessTemplatesComponentController,
    bindings: {
        onSelected: '&'
    }
});

class MCWorkflowProcessTemplatesComponentController {
    /*@ngInit*/
    constructor(templates) {
        this.templates = templates.get();
        this.templateTypes = [
            {
                title: 'CREATE SAMPLES',
                cssClass: 'mc-create-samples-color',
                icon: 'fa-cubes',
                margin: true,
                templates: this.templates.filter(t => t.process_type === 'create')
            },
            {
                title: 'TRANSFORMATION',
                cssClass: 'mc-transform-color',
                icon: 'fa-exclamation-triangle',
                templates: this.templates.filter(t => t.process_type === 'transform')
            },
            {
                title: 'MEASUREMENT',
                cssClass: 'mc-measurement-color',
                icon: 'fa-circle',
                templates: this.templates.filter(t => t.process_type === 'measurement')
            },
            {
                title: 'ANALYSIS',
                cssClass: 'mc-analysis-color',
                icon: 'fa-square',
                templates: this.templates.filter(t => t.process_type === 'analysis')
            }
        ];
    }

    chooseTemplate(t) {
        if (this.onSelected) {
            this.onSelected({templateId: t.name, processId: ''});
        }
    }
}

angular.module('materialscommons').component('mcWorkflowProcessTemplates', {
    templateUrl: 'app/project/experiments/experiment/components/processes/mc-workflow-process-templates.html',
    controller: MCWorkflowProcessTemplatesComponentController,
    bindings: {
        onSelected: '&'
    }
});

Modify filter to show new computational sample templates.


//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// ButtonWidget
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widget"], function(widget_manager){

    var ButtonWidgetModel = IPython.WidgetModel.extend({});
    widget_manager.register_widget_model('ButtonWidgetModel', ButtonWidgetModel);

    var ButtonView = IPython.WidgetView.extend({

        // Called when view is rendered.
        render : function(){
            var that = this;
            this.$el = $("<button />")
                .addClass('btn')
                .click(function() {
                    that.model.set('clicks', that.model.get('clicks') + 1);
                    that.model.update_other_views(that);
                });
            this._ensureElement();

            this.update(); // Set defaults.
        },

        // Handles: Backend -> Frontend Sync
        //          Frontent -> Frontend Sync
        update : function(){
            var description = this.model.get('description');
            description = description.replace(/ /g, ' ', 'm');
            description = description.replace(/\n/g, '<br>\n', 'm');
            if (description.length == 0) {
                this.$el.html(' '); // Preserve button height
            } else {
                this.$el.html(description);
            }

            return IPython.WidgetView.prototype.update.call(this);
        },

    });

    widget_manager.register_widget_view('ButtonView', ButtonView);

});

//----------------------------------------------------------------------------
//  Copyright (C) 2013 The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// ButtonWidget
//============================================================================

/**
 * @module IPython
 * @namespace IPython
 **/

define(["notebook/js/widget"], function(widget_manager){

    var ButtonWidgetModel = IPython.WidgetModel.extend({});
    widget_manager.register_widget_model('ButtonWidgetModel', ButtonWidgetModel);

    var ButtonView = IPython.WidgetView.extend({

        // Called when view is rendered.
        render : function(){
            var that = this;
            this.setElement($("<button />")
                .addClass('btn')
                .click(function() {
                    that.model.set('clicks', that.model.get('clicks') + 1);
                    that.model.update_other_views(that);
                }));

            this.update(); // Set defaults.
        },

        // Handles: Backend -> Frontend Sync
        //          Frontent -> Frontend Sync
        update : function(){
            var description = this.model.get('description');
            description = description.replace(/ /g, ' ', 'm');
            description = description.replace(/\n/g, '<br>\n', 'm');
            if (description.length == 0) {
                this.$el.html(' '); // Preserve button height
            } else {
                this.$el.html(description);
            }

            return IPython.WidgetView.prototype.update.call(this);
        },

    });

    widget_manager.register_widget_view('ButtonView', ButtonView);

});

Use setElement to set the view's element properly.


import Vue from 'vue';
import app from './App';<% if (extraConfig.isUseVueRouter) { %>
import VueRouter from 'vue-router';
import { configRouter } from './route';<% } %>
<% if (extraConfig.isUseVueRouter) { %>
Vue.use(VueRouter);

const App = Vue.extend(app);
const router = new VueRouter();

configRouter(router);
router.start(App, 'app');
<% } else { %>
const App = new Vue({
  el: 'body',
  components: {
    app
  }
});
<% } %>
Vue.config.debug = process.env.NODE_ENV !== 'production';

import Vue from 'vue';
import app from './App';<% if (extraConfig.isUseVueRouter) { %>
import VueRouter from 'vue-router';
import { configRouter } from './route';<% } %>
<% if (extraConfig.isUseVueRouter) { %>
Vue.use(VueRouter);

const App = Vue.extend(app);
const router = new VueRouter();

configRouter(router);
router.start(App, 'app');
<% } else { %>
const App = new Vue({
  el: 'body',
  components: {
    app,
  },
});
<% } %>
Vue.config.debug = process.env.NODE_ENV !== 'production';

Fix code style error in main.js


import { Application } from "@hotwired/stimulus"

// Stimulus controllers. This should ultimately be moved to koi/admin.js
import "@hotwired/turbo-rails"
import "trix";
import "@rails/actiontext";

const application = Application.start()

window.Stimulus = application

export { application }

import { Application } from "@hotwired/stimulus"

// Stimulus controllers. This should ultimately be moved to koi/admin.js
import "@hotwired/turbo-rails"
import "@rails/actiontext";

const application = Application.start()

window.Stimulus = application

export { application }

Move trix dependency to content gem


$(document).ready(function () {
    attachResizeVideo();
    switchAd();
});

function attachResizeVideo() {

    $(window).resize(function () {
        resizeVideo();
    });

    resizeVideo();
}

function resizeVideo() {

    console.log("fired");

    var $ytplayer = $('#ytplayer');

    var $parent = $ytplayer.parent();

    var newWidth = $parent.width() - 40;

    var aspectRatio = $ytplayer.get(0).height / $ytplayer.get(0).width;

    console.log(aspectRatio);

    $ytplayer
       .width(newWidth)
       .height(newWidth * aspectRatio);
}

function switchAd() {
    setInterval(function () {

        console.log('update');
        var $ads = $('.advertisement-wrapper');
        var len = $ads.length;

        var $curAd = $ads.filter('.active');
        $curAd.removeClass('active');

        console.log($ads.index($curAd));

        if ($ads.index($curAd) < len - 1) {
            $curAd.next().addClass('active');
        } else {
            $ads.first().addClass('active');
        }

    }, 10000);
}

$(document).ready(function () {
    attachResizeVideo();
    switchAd();
});

function attachResizeVideo() {

    $(window).resize(function () {
        resizeVideo();
    });

    resizeVideo();
}

function resizeVideo() {

    console.log("fired");

    var $ytplayer = $('#ytplayer');

    var $parent = $ytplayer.parent();

    var newHeight = $parent.height();

    var aspectRatio = $ytplayer.get(0).height / $ytplayer.get(0).width;

    console.log(aspectRatio);

    $ytplayer
       .height(newHeight)
       .width(newHeight / aspectRatio);

    if ($ytplayer.width() > $parent.width()) {
        console.log('correction');

        var newWidth = $parent.width();

        $ytplayer
       .height(newWidth * aspectRatio)
       .width(newWidth);
    }
}

function switchAd() {
    setInterval(function () {

        console.log('update');
        var $ads = $('.advertisement-wrapper');
        var len = $ads.length;

        var $curAd = $ads.filter('.active');
        $curAd.removeClass('active');

        console.log($ads.index($curAd));

        if ($ads.index($curAd) < len - 1) {
            $curAd.next().addClass('active');
        } else {
            $ads.first().addClass('active');
        }

    }, 10000);
}

Add width checking to video javascript.


'use strict';

var path = require('path');

var expect = require('thehelp-test').expect;

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

describe('bunyan', function() {
  var child;

  before(function(done) {
    this.timeout(10000);

    util.setupScenario('bunyan', function(err) {
      if (err) {
        throw err;
      }

      child = util.startProcess(
          path.join(__dirname, '../../scenarios/bunyan/go.js'));

      child.on('close', function() {
        done();
      });
    });
  });

  after(function(done) {
    util.cleanupScenario('bunyan', done);
  });

  it('does not log verbose by default', function() {
    expect(child.stdoutResult).not.to.match(/default verbose text/);
  });

  it('should have logged info', function() {
    expect(child.stdoutResult).to.match(/info text { data: { yes:/);
  });

  it('should have logged warn and printed out the supplied callback', function() {
    expect(child.stdoutResult).to.match(/warn text/);
    expect(child.stdoutResult).to.match(/jshint unused/);
  });

  it('should have logged error', function() {
    expect(child.stdoutResult).to.match(/error interpolation/);
  });

  it('logs custom verbose level', function() {
    expect(child.stdoutResult).to.match(/custom verbose text/);
  });

});

'use strict';

var path = require('path');

var expect = require('thehelp-test').expect;

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

describe('bunyan', function() {
  var child;

  before(function(done) {
    this.timeout(30000);

    util.setupScenario('bunyan', function(err) {
      if (err) {
        throw err;
      }

      child = util.startProcess(
          path.join(__dirname, '../../scenarios/bunyan/go.js'));

      child.on('close', function() {
        done();
      });
    });
  });

  after(function(done) {
    util.cleanupScenario('bunyan', done);
  });

  it('does not log verbose by default', function() {
    expect(child.stdoutResult).not.to.match(/default verbose text/);
  });

  it('should have logged info', function() {
    expect(child.stdoutResult).to.match(/info text { data: { yes:/);
  });

  it('should have logged warn and printed out the supplied callback', function() {
    expect(child.stdoutResult).to.match(/warn text/);
    expect(child.stdoutResult).to.match(/jshint unused/);
  });

  it('should have logged error', function() {
    expect(child.stdoutResult).to.match(/error interpolation/);
  });

  it('logs custom verbose level', function() {
    expect(child.stdoutResult).to.match(/custom verbose text/);
  });

});

Increase timeout for bunyan test


'use strict';

var should = require('should');
var utils = require('./utilsCtrl.js');

describe('utilsCtrl tests', function() {

  it('should find list with some id', function(done) {
    var lists = {
      listContainer1: [ {id: 'idlist1'}, {id: 'idlist2'}, {id: 'idlist3'} ],
      listContainer2: [ {id: 'idlist4'}, {id: 'idlist5'}, {id: 'idlist6'} ],
      listContainer3: [ {id: 'idlist7'}, {id: 'idlist8'}, {id: 'idlist9'} ]
    };

    var successSearch = utils.searchList('idlist8', lists);
    var falseSearch = utils.searchList('idlist10', lists);

    should(successSearch).be.ok();
    should(falseSearch).not.be.ok();

    done();
  });

  it('should get diff of dates', function(done) {
    var dateDiff = utils.getDateDiff('2016-01-08', '2016-01-10');
    should(dateDiff).be.eql(172800000);
    done();
  });
});

'use strict';

var should = require('should');
var utils = require('./utilsCtrl.js');

describe('utils module tests', function() {

  it('should find list with some id', function(done) {
    var lists = {
      listContainer1: [ {id: 'idlist1'}, {id: 'idlist2'}, {id: 'idlist3'} ],
      listContainer2: [ {id: 'idlist4'}, {id: 'idlist5'}, {id: 'idlist6'} ],
      listContainer3: [ {id: 'idlist7'}, {id: 'idlist8'}, {id: 'idlist9'} ]
    };

    var successSearch = utils.searchList('idlist8', lists);
    var falseSearch = utils.searchList('idlist10', lists);

    should(successSearch).be.ok();
    should(falseSearch).not.be.ok();

    done();
  });

  it('should get diff of dates', function(done) {
    var dateDiff = utils.getDateDiff('2016-01-08', '2016-01-10');
    should(dateDiff).be.eql(172800000);
    done();
  });

  it('should convert unix date to human readable date', function(done) {
    var unixDate = 172800000;

    var humanReadable = utils.getHumanReadableTime(unixDate);

    should(humanReadable).have.property('time');
    should(humanReadable).have.property('format');
    should(humanReadable.time).be.eql(2);
    should(humanReadable.format).be.eql('days');

    done();
  });
});

Add automatic test for utils


'use strict';

angular.module('myApp.operation', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/operation', {
    templateUrl: 'operation/operation.html',
    controller: 'OperationController'
  });
}])

.controller('OperationController', ['$scope', '$rootScope', function($scope, $rootScope) {
    $rootScope.menu = {
        home: false,
        operation: true,
        contact: false
    };
}]);

'use strict';

angular.module('myApp.operation', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/operation', {
    templateUrl: 'operation/operation.html',
    controller: 'OperationController'
  });
}])

.controller('OperationController', ['$scope', '$rootScope', function($scope, $rootScope) {
    $rootScope.menu = {
        home: false,
        operation: true,
        contact: false
    };

    $scope.typeOperation = 'rent';
}]);

Set typeOperation for default to select option


'use strict';

var Settings = require('./settings.js');
var ChromeStorage = require('./chrome-storage.js');

function Request() {};

Request._send = function _send(options, accessToken) {
  var xhr = new XMLHttpRequest();

  xhr.open(options.method, options.url, true);
  xhr.setRequestHeader('Content-type', 'application/json');
  xhr.setRequestHeader('trakt-api-key', Settings.clientId);
  xhr.setRequestHeader('trakt-api-version', Settings.apiVersion);
  xhr.timeout = 4000; // increase the timeout for trakt.tv calls

  if (accessToken) {
    xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
  }

  xhr.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      options.success.call(this, this.response);
    } else {
      options.error.call(this, this.status, this.responseText);
    }
  };
  xhr.onerror = options.error;

  xhr.send(JSON.stringify(options.params));
};

Request.send = function send(options) {
  ChromeStorage.get('access_token', function(data) {
    Request._send(options, data.access_token);
  }.bind(this));
};

module.exports = Request;

'use strict';

var Settings = require('./settings.js');
var ChromeStorage = require('./chrome-storage.js');

function Request() {};

Request._send = function _send(options, accessToken) {
  var xhr = new XMLHttpRequest();

  xhr.open(options.method, options.url, true);
  xhr.setRequestHeader('Content-type', 'application/json');
  xhr.setRequestHeader('trakt-api-key', Settings.clientId);
  xhr.setRequestHeader('trakt-api-version', Settings.apiVersion);
  xhr.timeout = 10000; // increase the timeout for trakt.tv calls

  if (accessToken) {
    xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
  }

  xhr.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      options.success.call(this, this.response);
    } else {
      options.error.call(this, this.status, this.responseText);
    }
  };
  xhr.onerror = options.error;

  xhr.send(JSON.stringify(options.params));
};

Request.send = function send(options) {
  ChromeStorage.get('access_token', function(data) {
    Request._send(options, data.access_token);
  }.bind(this));
};

module.exports = Request;

Increase timeout for trakt.tv API calls


import hbs from 'htmlbars-inline-precompile';
import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('my-component', {
  integration: true
});

test('precompile enabled flags', function(assert) {
  this.render(hbs`
    {{#if-flag-ENABLE_FOO}}Foo{{/if-flag-ENABLE_FOO}}
  `);

  assert.equal(this.$().text().trim(), 'Foo');
});

test('precompile disabled flags', function(assert) {
  this.render(hbs`
    {{#if-flag-ENABLE_BAR}}Bar{{/if-flag-ENABLE_BAR}}
  `);

  assert.equal(this.$().text().trim(), '');
});

test('precompile else block', function(assert) {
  this.render(hbs`
    {{#if-flag-ENABLE_BAR}}Bar{{else}}Baz{{/if-flag-ENABLE_BAR}}
  `);

  assert.equal(this.$().text().trim(), 'Baz');
});

Add test for {{else}} block


(function (env) {
    "use strict";
    env.ddg_spice_<: $lia_name :> = function(api_result){

        // Validate the response (customize for your Spice)
        if (api_result.error) {
            return Spice.failed('<: $lia_name :>');
        }

        // Render the response
        Spice.add({
            id: "<: $lia_name :>",

            // Customize these properties
            name: "AnswerBar title",
            data: api_result,
            meta: {
                sourceName: "Example.com",
                sourceUrl: 'http://example.com/url/to/details/' + api_result.name
            },
            templates: {
                group: 'your-template-group',
                options: {
                    content: Spice.<: $lia_name :>.content,
                    moreAt: true
                }
            }
        });
    };
}(this));

(function (env) {
    "use strict";
    env.ddg_spice_<: $lia_name :> = function(api_result){

        // Validate the response (customize for your Spice)
        if (api_result.error) {
            return Spice.failed('<: $lia_name :>');
        }

        // Render the response
        Spice.add({
            id: "<: $lia_name :>",

            // Customize these properties
            name: "AnswerBar title",
            data: api_result,
            meta: {
                sourceName: "Example.com",
                sourceUrl: 'http://<: $ia_domain :>/details/' + api_result.name
            },
            templates: {
                group: '<: $ia_group :>',
                options: {
                    content: Spice.<: $lia_name :>.content,
                    moreAt: true
                }
            <: $ia_rel :>
        });
    };
}(this));

Add new placeholders to Example.js


const package = require('./package.json')

module.exports = {
  parser: 'babel-eslint',
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    jest: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:import/errors',
    'plugin:react/recommended',
    'plugin:flowtype/recommended',

    // suppress conflicted rules
    'plugin:prettier/recommended',
    'prettier/flowtype',
    'prettier/react'
  ],
  parserOptions: {
    ecmaFeatures: {
      experimentalObjectRestSpread: true,
      jsx: true
    },
    sourceType: 'module'
  },
  plugins: ['flowtype'],
  settings: {
    react: {
      version: package.dependencies.react
    }
  },
  rules: {
    'no-console': 'warn',
    'flowtype/require-valid-file-annotation': ['warn', 'always'],
    'react/no-deprecated': 'warn',
    'react/prefer-stateless-function': 'error'
  }
}

const package = require('./package.json')

module.exports = {
  parser: 'babel-eslint',
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    jest: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:import/errors',
    'plugin:react/recommended',
    'plugin:flowtype/recommended',

    // suppress conflicted rules
    'plugin:prettier/recommended',
    'prettier/flowtype',
    'prettier/react'
  ],
  parserOptions: {
    ecmaFeatures: {
      experimentalObjectRestSpread: true,
      jsx: true
    },
    sourceType: 'module'
  },
  plugins: [],
  settings: {
    react: {
      version: package.dependencies.react
    }
  },
  rules: {
    'no-console': 'warn',
    'flowtype/require-valid-file-annotation': ['warn', 'always'],
    'react/no-deprecated': 'warn',
    'react/prefer-stateless-function': 'error'
  }
}

Remove unnecessary plugin using declaration


module.exports = {
    "env": {
        "browser": true,
        "node": true
    },

    "globals": {
        "app": true,
        "angular": true,
        "_": true
    },

    "extends": "eslint:recommended",
    "rules": {
        "indent": [
            "error",
            2
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
};

module.exports = {
    "env": {
        "browser": true,
        "node": true
    },

    "globals": {
        "app": true,
        "angular": true,
        "_": true
    },

    "extends": "eslint:recommended",
    "rules": {

        "no-console": [
            "error", { allow: ["log", "warn", "error"] }
        ],
        "indent": [
            "error",
            2
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
};

Allow console log warn error


module.exports = {
    "env": {
        "browser": true
    },
    "extends": "eslint:recommended",
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ]
    },
    "globals": {
        "$": true,
        "it": true,
        "describe": true,
        "beforeEach": true,
        "chai": true,
        "jQuery": true,
        "chrome": true,
        "modal": true,
        "modalTimeout": true,
        "modalHTML": true,
        "Modal": true,
        "flashInterval": true,
        "currentScript": true
    }
};

module.exports = {
    "env": {
        "browser": true,
        "es6": true,
        "node": true
    },
    "extends": "eslint:recommended",
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ]
    },
    "globals": {
        "$": true,
        "it": true,
        "describe": true,
        "beforeEach": true,
        "chai": true,
        "jQuery": true,
        "chrome": true,
        "modal": true,
        "modalTimeout": true,
        "modalHTML": true,
        "Modal": true,
        "flashInterval": true,
        "currentScript": true
    }
};

Allow eslint to use node and es6


/**
 * Logs Screen
 *
 * Shows all logs
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
'use strict';

const Log = require('./logs/log');

const LogsScreen = {
  init: function() {
    this.view = document.getElementById('logs-view');
    this.logsContainer = this.view.querySelector('.logs');
    this.logs = [
      new Log('virtual-things-2', 'level'),
      new Log('weather-8b8f279cfcc42b05f2b3cdfd4b0c7f9c5eac5b18',
              'temperature'),
      new Log('philips-hue-001788fffe4f2113-sensors-2',
              'temperature'),
    ];
    this.onWindowResize = this.onWindowResize.bind(this);
    window.addEventListener('resize', this.onWindowResize);
    this.onWindowResize();
  },

  show: function() {
    this.logsContainer.innerHTML = '';
    this.logs.forEach((log) => {
      log.reload();
      this.logsContainer.appendChild(log.elt);
    });
  },

  onWindowResize: function() {
  },
};

module.exports = LogsScreen;

/**
 * Logs Screen
 *
 * Shows all logs
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
'use strict';

const Log = require('./logs/log');

const LogsScreen = {
  init: function() {
    this.view = document.getElementById('logs-view');
    this.logsContainer = this.view.querySelector('.logs');
    this.logs = [
      new Log('virtual-things-2', 'level'),
      new Log('virtual-things-2', 'on'),
      new Log('weather-8b8f279cfcc42b05f2b3cdfd4b0c7f9c5eac5b18',
              'temperature'),
      new Log('philips-hue-001788fffe4f2113-sensors-2',
              'temperature'),
    ];
    this.onWindowResize = this.onWindowResize.bind(this);
    window.addEventListener('resize', this.onWindowResize);
    this.onWindowResize();
  },

  show: function() {
    this.logsContainer.innerHTML = '';
    this.logs.forEach((log) => {
      log.reload();
      this.logsContainer.appendChild(log.elt);
    });
  },

  onWindowResize: function() {
  },
};

module.exports = LogsScreen;

Add graph of boolean property


'use strict';
// socket.io-client requires the window object, and navigator.userAgent to be present.

var window = {},
  navigator = {userAgent: 'tvos'};

var io = require('socket.io-client');

/*
 import * as router from 'lib/router';
 */

function resume(lastLocation) {
  if (!lastLocation) {
    return;
  }

  //router.goTo(lastLocation);
}

function logDebug(msg) {
  if (console && console.debug) {
    console.debug(msg);
  }
}

module.exports = {
  connect: function (connectURL, app, launchOptions) {
    var socket = io(connectURL);

    socket.on('connect', function () {
      logDebug('Live reload: connected');
    });
    socket.on('compile', function () {
      logDebug('Live reload: compiling, prepare for reload');
    });

    // reload app on reload event
    socket.on('live-reload', function () {
      app.reload()//{when: 'now'}, {
      //lastLocation: router.getLocation()
      //});
    });

    if (launchOptions.reloadData) {
      resume(launchOptions.reloadData || {});
    }

  }
};

'use strict';
// socket.io-client requires the window object, and navigator.userAgent to be present.

var window = global;
window.navigator = {userAgent: 'tvos'};

var io = require('socket.io-client');

function resume(lastLocation) {
  if (!lastLocation) {
    return;
  }
}

function logDebug(msg) {
  if (console && console.debug) {
    console.debug(msg);
  }
}

module.exports = {
  connect: function (connectURL, app, launchOptions) {
    var socket = io(connectURL);

    socket.on('connect', function () {
      logDebug('Live reload: connected');
    });
    socket.on('compile', function () {
      logDebug('Live reload: compiling, prepare for reload');
    });

    // reload app on reload event
    socket.on('live-reload', function () {
      app.reload();
    });

    if (launchOptions && launchOptions.reloadData) {
      resume(launchOptions.reloadData || {});
    }

  }
};

Fix reload errors with latest tvOS


var LogStream = require('./LogStream');
var lodash = require('lodash');

/**
 * The LogFactory constructor.
 * @constructor
 */
var LogFactory = function () {
	var self = this;
	self._streams = {};
	self._filters = [];
};

/**
 * Create a new LogStream.
 * @param name The name of the component originating the logs.
 * @returns {LogStream}
 */
LogFactory.prototype.create = function (name) {
	var self = this;
	self._streams[name] = new LogStream(name);
	self._streams[name].on('error', function (error) {
		self.streams['BeagleDrone.server.LogFactory'].error(error);
	});
	return self._streams[name];
};

/**
 * Connect all loggers to an output stream.
 *
 * Example:
 *   logFactory.pipe(process.stdout)
 *
 * @param outputStream A node stream.Writable that will accept all log events.
 */
LogFactory.prototype.pipe = function (outputStream) {
	var self = this;
	lodash.forEach(self._streams, function (stream) {
		stream.pipe(outputStream, {
			end: false
		});
	});
};

// It's a single instance.
module.exports = new LogFactory();

var LogStream = require('./LogStream');
var lodash = require('lodash');

/**
 * The LogFactory constructor.
 * @constructor
 */
var LogFactory = function () {
	var self = this;
	self._inputs = {};
	self._outputs = [];
};

/**
 * Create a new LogStream.
 * @param name The name of the component originating the logs.
 * @returns {LogStream}
 */
LogFactory.prototype.create = function (name) {
	var self = this;
	self._inputs[name] = new LogStream(name);
	lodash.forEach(self._outputs, function (outputStream) {
		self._inputs[name].pipe(outputStream);
	});
	return self._inputs[name];
};

/**
 * Connect all loggers to an output stream.
 *
 * Example:
 *   logFactory.pipe(process.stdout)
 *
 * @param outputStream A node stream.Writable that will accept all log events.
 */
LogFactory.prototype.pipe = function (outputStream) {
	var self = this;
	self._outputs.push(outputStream);
	lodash.forEach(self._inputs, function (stream) {
		stream.pipe(outputStream, {
			end: false
		});
	});
};

// It's a single instance.
module.exports = new LogFactory();

Fix a race condition when adding output streams before all input streams are added.


// Brunch automatically concatenates all files in your
// watched paths. Those paths can be configured at
// config.paths.watched in "brunch-config.js".
//
// However, those files will only be executed if
// explicitly imported. The only exception are files
// in vendor, which are never wrapped in imports and
// therefore are always executed.

// Import dependencies
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"

// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".

// import socket from "./socket"

// Brunch automatically concatenates all files in your
// watched paths. Those paths can be configured at
// config.paths.watched in "brunch-config.js".
//
// However, those files will only be executed if
// explicitly imported. The only exception are files
// in vendor, which are never wrapped in imports and
// therefore are always executed.

// Import dependencies
//
// If you no longer want to use a dependency, remember
// to also remove its path from "config.paths.watched".
import "phoenix_html"

// Import local files
//
// Local files can be imported directly using relative
// paths "./socket" or full ones "web/static/js/socket".

import socket from "./socket"

Enable the web socket for channel transports


import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import ContentSave from 'material-ui/svg-icons/content/save';

const SaveButton = () => <RaisedButton
    type="submit"
    label="Save"
    icon={<ContentSave />}
    primary
    style={{
        margin: '10px 24px',
        position: 'relative',
    }}
/>;

export default SaveButton;

import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import ContentSave from 'material-ui/svg-icons/content/save';
import CircularProgress from 'material-ui/CircularProgress';

class SaveButton extends Component {
    constructor(props) {
        super(props);
        this.state = {
            submitting: false,
        };
    }

    handleClick = (e) => {
        if (this.state.submitting) {
            // prevent double submission
            e.preventDefault();
        }
        this.setState({ submitting: true });
    }

    render() {
        return <RaisedButton
            type="submit"
            label="Save"
            icon={this.state.submitting ? <CircularProgress size={25} thickness={2} /> : <ContentSave />}
            onClick={this.handleClick}
            primary={!this.state.submitting}
            style={{
                margin: '10px 24px',
                position: 'relative',
            }}
        />;
    }
}

export default SaveButton;

Add double submission protection in Save button


'use strict';

var localforage = require('localforage');

var $body = $('body');
['theme', 'mode'].forEach(function (property) {
  localforage.getItem(property, function (value) {
    if (!value) {
      value = 'default';
      localforage.setItem(property, value);
    } else {
      // For non-first time users, we want to see what modes/themes they are using.
      if (property === 'theme') {
        ga('send', 'event', 'Theme', 'Load page with theme', value);
      }
      if (property === 'mode') {
        ga('send', 'event', 'Mode', 'Load page with mode', value);
      }
    }
    $body.addClass(property + '-' + value);
    $body.attr('data-' + property, value);
    if (property === 'mode' && value !== 'default') {
      $('#mode').attr('href', '/styles/' + value + '.min.css');
    }
  });
});

var App = require('./app');
App.start();

'use strict';

var localforage = require('localforage');

var $body = $('body');
['theme', 'mode'].forEach(function (property) {
  localforage.getItem(property, function (value) {
    if (!value) {
      value = 'default';
      localforage.setItem(property, value);
    } else {
      // For non-first time users, we want to see what modes/themes they are using.
      if (property === 'theme') {
        // Set theme custom dimension
        ga('set', 'dimension3', value);
      }
      if (property === 'mode') {
        // Set mode custom dimension
        ga('set', 'dimension4', value);
      }
    }
    $body.addClass(property + '-' + value);
    $body.attr('data-' + property, value);
    if (property === 'mode' && value !== 'default') {
      $('#mode').attr('href', '/styles/' + value + '.min.css');
    }
  });
});

var App = require('./app');
App.start();

Set mode and theme custom dimensions instead of sending event


/* eslint-env node */

const path = require('path');

module.exports = {
  description: 'Initialize files needed for typescript compilation',

  files() {
    return [
      path.join(this.path, 'files', 'tsconfig.json'),
      path.join(this.path, 'files', 'app', 'config', 'environment.d.ts'),
    ];
  },

  mapFile() {
    const result = this._super.mapFile.apply(this, arguments);

    const tsconfigPattern = `${path.sep}tsconfig.json`;
    const appPattern = `${path.sep}app${path.sep}`;

    if (result.indexOf(tsconfigPattern) > -1) {
      return 'tsconfig.json';
    } else if (result.indexOf(appPattern) > -1) {
      var pos = result.indexOf(appPattern);
      return result.substring(pos + 1);
    }
  },

  locals() {
    return {
      inRepoAddons: (this.project.pkg['ember-addon'] || {}).paths || []
    };
  },

  normalizeEntityName() {
    // Entity name is optional right now, creating this hook avoids an error.
  },

  afterInstall() {
    return this.addPackagesToProject([
      { name: 'typescript', target: '^2.4.2' },
      { name: '@types/ember', target: '^2.7.43' },
      { name: '@types/rsvp', target: '^3.3.0' },
      { name: '@types/ember-testing-helpers' },
    ]);
  },
};

/* eslint-env node */

const path = require('path');

module.exports = {
  description: 'Initialize files needed for typescript compilation',

  files() {
    return [
      path.join(this.path, 'files', 'tsconfig.json'),
      path.join(this.path, 'files', 'app', 'config', 'environment.d.ts'),
    ];
  },

  mapFile() {
    const result = this._super.mapFile.apply(this, arguments);

    const tsconfigPattern = `${path.sep}tsconfig.json`;
    const appPattern = `${path.sep}app${path.sep}`;

    if (result.indexOf(tsconfigPattern) > -1) {
      return 'tsconfig.json';
    } else if (result.indexOf(appPattern) > -1) {
      var pos = result.indexOf(appPattern);
      return result.substring(pos + 1);
    }
  },

  locals() {
    return {
      inRepoAddons: (this.project.pkg['ember-addon'] || {}).paths || [],
    };
  },

  normalizeEntityName() {
    // Entity name is optional right now, creating this hook avoids an error.
  },

  afterInstall() {
    return this.addPackagesToProject([
      { name: 'typescript', target: 'latest' },
      { name: '@types/ember', target: 'latest' },
      { name: '@types/rsvp', target: 'latest' },
      { name: '@types/ember-testing-helpers', target: 'latest' },
    ]);
  },
};

Use 'latest' for typing versions.


/**
 * Step 5
 * Where you write the generator specific files (routes, controllers, etc)
 */

const SOURCE_CONFIG = name => `${name}Config.js`;

const DESTINATION_CONFIG = `config/log.js`;

export default function () {
  let logger = this['logger-name'];

  this.template(SOURCE_CONFIG(logger), DESTINATION_CONFIG, {logger});
};

/**
 * Step 5
 * Where you write the generator specific files (routes, controllers, etc)
 */

const SOURCE_CONFIG = name => `${name}Config.js`;

const DESTINATION_CONFIG = `config/log.js`;

export default function () {
  let logger = this['logger-name'].toLowerCase();

  this.template(SOURCE_CONFIG(logger), DESTINATION_CONFIG, {logger});
};

Fix issue with lower-cased form of logger name


var data = '';
process.stdin.on('data', function(chunk) { data += chunk; });
process.stdin.on('end', function() {
  var regExp = /[0-9]+([.][0-9]+)?%/g;
  var match;
  do {
    match = regExp.exec(data);
    if (match) {
      var percent = parseFloat(match[0].substring(0, match[0].length - 1));
      if (percent < 80) {
        console.error('Insufficient code coverage');
        process.exit(1);
      }
    }
  } while (match);
  console.log('Code coverage OK');
  process.exit(0);
});

var MIN_METRIC_COVERAGE = 90.0;

var data = '';
process.stdin.on('data', function(chunk) { data += chunk; });
process.stdin.on('end', function() {
  var regExp = /[0-9]+([.][0-9]+)?%/g;
  var match;
  do {
    match = regExp.exec(data);
    if (match) {
      var percent = parseFloat(match[0].substring(0, match[0].length - 1));
      if (percent < MIN_METRIC_COVERAGE) {
        console.error('Insufficient code coverage');
        process.exit(1);
      }
    }
  } while (match);
  console.log('Code coverage OK');
  process.exit(0);
});

Raise the bar on test coverage from 80% to 90%


import React from "react"
import { Nav, NavItem, Collapse } from "react-bootstrap"

var threadNavStyle = {
    backgroundColor: 'rgb(220, 220, 220)',
    color: 'black'
};

export default React.createClass({
    getInitialState: function() {
        return {}
    },

    render: function() {
        var { title, ...other } = this.props;
        return (
            <NavItem {...other}>

                    { this.props.title }

                <Collapse in={this.props.active}>

                            <NavItem eventKey={1}>
                                Thread 1 - src/main.c:1234
                            </NavItem>
                            <NavItem eventKey={2}>
                                Thread 2 - src/events.c:120
                            </NavItem>

                </Collapse>
            </NavItem>
        );
    }
});

import React from "react"
import { Button, ButtonGroup, NavItem, Collapse } from "react-bootstrap"

export default React.createClass({
    getInitialState: function() {
        return {}
    },

    render: function() {
        var { title, ...other } = this.props;
        return (
            <NavItem {...other}>

                    { this.props.title }

                <Collapse in={this.props.active}>

                        <ButtonGroup vertical block>
                            <Button bsStyle="info">
                                Thread 1 - src/main.c:1234
                            </Button>
                            <Button>
                                Thread 2 - src/events.c:120
                            </Button>
                        </ButtonGroup>

                </Collapse>
            </NavItem>
        );
    }
});

Switch to using a button group for the context selector.


"use strict";

describe('lang-specific directive', function() {
  var element;
  var documentStore;
  function createDocumentStore() {
    return {
      store: {
        treebank: {
          json: {
            treebank: {}
          }
        }
      }
    };
  }

  beforeEach(module('arethusa.core'));

  var createElement = function() {
    inject(function ($compile, $rootScope) {
      var $scope = $rootScope.$new();
      element = angular.element("<span lang-specific />");
      $compile(element)($scope);
    });
  };

  describe('Arabic', function() {
    beforeEach(module(function($provide) {
      documentStore = createDocumentStore();
      documentStore.store.treebank.json.treebank["_xml:lang"] = "ara";
      $provide.value('documentStore', documentStore);
    }));

    beforeEach(function() {
      createElement();
    });

    it('sets the language on the html element', function() {
      expect(element.attr('lang')).toEqual('ar');
    });

    it('sets the text direction on the html element', function() {
      expect(element.attr('dir')).toEqual('rtl');
    });
  });

  describe('unspecified language', function() {
    beforeEach(module(function($provide) {
      documentStore = createDocumentStore();
      $provide.value('documentStore', documentStore);
    }));

    beforeEach(function() {
      createElement();
    });

    it('does not set any language on the html element', function() {
      expect(element.attr('lang')).toBeUndefined();
    });

    it('does not set dir on the html element', function() {
      expect(element.attr('dir')).toBeUndefined();
    });
  });
});

"use strict";

describe('lang-specific directive', function() {
  var element;
  var documentStore;
  function createLanguageSettingsWith(settings) {
    return {
      getFor: function(doc) {
        return settings;
      }
    };
  }

  beforeEach(module('arethusa.core'));

  var createElement = function() {
    inject(function ($compile, $rootScope) {
      var $scope = $rootScope.$new();
      element = angular.element("<span lang-specific />");
      $compile(element)($scope);
    });
  };

  describe('Arabic', function() {
    var arabicSettings = {
      lang: 'ar',
      leftToRight: false
    };
    beforeEach(module(function($provide) {
      $provide.value('languageSettings',
        createLanguageSettingsWith(arabicSettings));
    }));

    beforeEach(function() {
      createElement();
    });

    it('sets the language on the html element', function() {
      expect(element.attr('lang')).toEqual('ar');
    });

    it('sets the text direction on the html element', function() {
      expect(element.attr('dir')).toEqual('rtl');
    });
  });

  describe('unspecified language', function() {
    beforeEach(module(function($provide) {
      $provide.value('languageSettings', createLanguageSettingsWith(undefined));
    }));

    beforeEach(function() {
      createElement();
    });

    it('does not set any language on the html element', function() {
      expect(element.attr('lang')).toBeUndefined();
    });

    it('does not set dir on the html element', function() {
      expect(element.attr('dir')).toBeUndefined();
    });
  });
});

Update lang-specific specs to stub languageSettings


const config = require("config");
let basePath;
if (config.basePath) {
  if (typeof config.basePath === "string") {
    basePath = {
      test: config.basePath
    };
  }
  basePath = config.basePath;
}
config.assetPrefix = config.assetPrefix || basePath || "";
config.configOrigin = config.configOrigin || "default";
config.distDir = config.distDir || ".next";
config.useFileSystemPublicRoutes = config.useFileSystemPublicRoutes || true;
config.server = config.server || {};
config.server.host = config.server.host || "0.0.0.0";
config.server.port = config.server.port || 3000;

const config = require("config");
let basePath;
if (config.basePath) {
  if (typeof config.basePath === "string") {
    basePath = config.basePath;
  } else {
    basePath = config.basePath && config.basePath.test;
  }
}
config.assetPrefix = config.assetPrefix || basePath || "";
config.configOrigin = config.configOrigin || "default";
config.distDir = config.distDir || ".next";
config.useFileSystemPublicRoutes = config.useFileSystemPublicRoutes || true;
config.server = config.server || {};
config.server.host = config.server.host || "0.0.0.0";
config.server.port = config.server.port || 3000;

Fix wrong assetPrefix when using basePath as object


function NavController($scope, Auth) {
  $scope.signedIn = Auth.isAuthenticated;
  $scope.logout = Auth.logout;

  Auth.currentUser().then(function (user) {
    $scope.user = user;
  });

  $scope.$on('devise:new-registration', function (e, user) {
    $scope.user = user;
  });

  $scope.$on('devise:login', function (e, user) {
    $scope.user = user;
  });

  $scope.$on()('devise:logout', function (e, user) {
    $scope.user = {};
  });
}

NavController.$inject = ['$scope', 'Auth'];
angular
  .module('app')
  .controller('NavController', NavController);

function NavController($scope, $state, Auth) {
  $scope.signedIn = Auth.isAuthenticated;
  $scope.logout = Auth.logout;

  Auth.currentUser().then(function (user) {
    $scope.user = user;
  });

  $scope.$on('devise:new-registration', function (e, user) {
    $scope.user = user;
  });

  $scope.$on('devise:login', function (e, user) {
    $scope.user = user;
  });

  $scope.$on('devise:logout', function (e, user) {
    $scope.user = {};
    $state.go('home');
  });
}

NavController.$inject = ['$scope', '$state', 'Auth'];
angular
  .module('app')
  .controller('NavController', NavController);

Load home page on logout


import React, { Component } from 'react';
import { Card } from 'native-base';

import CardImageHeader from '../../common/card-image-header/cardImageHeader';
import SimpleCardBody from '../../common/simple-card-body/simpleCardBody';

export default class JobTypeCardPreview extends Component {
  static propTypes = {
    title: React.PropTypes.string.isRequired,
    cover: React.PropTypes.string.isRequired,
    icon: React.PropTypes.string.isRequired,
    subtitle: React.PropTypes.string.isRequired,
  };

  render() {
    return (
      <Card>
        <CardImageHeader cover={this.props.cover} icon={this.props.icon} />
        <SimpleCardBody title={this.props.title} subtitle={this.props.subtitle} />
      </Card>
    );
  }
}

import React, { Component } from 'react';
import { Card } from 'native-base';

import CardImageHeader from '../../common/card-image-header/cardImageHeader';
import SimpleCardBody from '../../common/simple-card-body/simpleCardBody';
import { imageProp } from '../../common/propTypes';

export default class JobTypeCardPreview extends Component {
  static propTypes = {
    title: React.PropTypes.string.isRequired,
    cover: imageProp.isRequired,
    icon: imageProp.isRequired,
    subtitle: React.PropTypes.string.isRequired,
  };

  render() {
    return (
      <Card>
        <CardImageHeader cover={this.props.cover} icon={this.props.icon} />
        <SimpleCardBody title={this.props.title} subtitle={this.props.subtitle} />
      </Card>
    );
  }
}

Make JobTypeCardPreview use imageProp from propTypes


var test = require('tape');
var tm = require('../lib/tm');
var style = require('../lib/style');
var source = require('../lib/source');
var mockOauth = require('../lib/mapbox-mock')(require('express')());
var tmppath = tm.join(require('os').tmpdir(), 'Examples ШЖФ -' + (+new Date));
var server;

test('setup', function(t) {
    tm.config({
        log: false,
        db: tm.join(tmppath, 'app.db'),
        cache: tm.join(tmppath, 'cache'),
        fonts: tm.join(tmppath, 'fonts'),
        mapboxauth: 'http://localhost:3001'
    }, t.end);
});

test('setup: mockserver', function(t) {
    tm.db.set('oauth', {
        account: 'test',
        accesstoken: 'testaccesstoken'
    });
    tm._config.mapboxauth = 'http://localhost:3001',
    tm._config.mapboxtile = 'http://localhost:3001/v4';
    server = mockOauth.listen(3001, t.end);
});

for (var key in style.examples) (function(key) {
    test(key, function(t) {
        style(style.examples[key], function(err, s) {
            t.ifError(err);
            t.end();
        });
    });
})(key);

test('cleanup', function(t) {
    server.close(function() { t.end(); });
});

var test = require('tape');
var tm = require('../lib/tm');
var style = require('../lib/style');
var source = require('../lib/source');
var mockOauth = require('../lib/mapbox-mock')(require('express')());
var tmppath = tm.join(require('os').tmpdir(), 'Examples ШЖФ -' + (+new Date));
var server;

test('setup', function(t) {
    tm.config({
        log: false,
        db: tm.join(tmppath, 'app.db'),
        cache: tm.join(tmppath, 'cache'),
        fonts: tm.join(tmppath, 'fonts'),
        mapboxauth: 'http://localhost:3001'
    }, t.end);
});

test('setup: mockserver', function(t) {
    tm.db.set('oauth', {
        account: 'test',
        accesstoken: 'testaccesstoken'
    });
    tm._config.mapboxauth = 'http://localhost:3001',
    tm._config.mapboxtile = 'http://localhost:3001/v4';
    server = mockOauth.listen(3001, t.end);
});

for (var key in style.examples) (function(key) {
    test(key, function(t) {
        style(style.examples[key], function(err, s) {
            t.ifError(err);
            t.ok(!s.data._prefs.mapid, 'no mapid set');
            t.end();
        });
    });
})(key);

test('cleanup', function(t) {
    server.close(function() { t.end(); });
});

Test that no mapid is set on example styles.


import React from 'react'
import {Route, RouteHandler, Link, Button} from 'react-router'

class Profile extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      teacherData: {
        name: 'Teachy McTeacherton',
        email: 'teachy@teach.er'
      }
    };
  }

  render(){
    return(

Settings

            Name: {this.state.teacherData.name}

            Email: {this.state.teacherData.email}

{ auth.logout();
          }}>

        <button style={{marginLeft: '20%'}}>Logout</button>

    )
  }
}

module.exports = Profile;

import React from 'react'
import {Route, RouteHandler, Link, Button} from 'react-router'
var auth = require('./../utils/auth');

class Profile extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      teacherData: {
        name: 'Teachy McTeacherton',
        email: 'teachy@teach.er'
      }
    };
  }

  handleLogout() {
    auth.logout();
    this.context.router.push({
      pathname: '/login'
    });
  }

  render(){
    return(

Settings

            Name: {this.state.teacherData.name}

            Email: {this.state.teacherData.email}

        <button style={{marginLeft: '20%'}}>Logout</button>

    )
  }
}

Profile.contextTypes = {
  router: React.PropTypes.any.isRequired
};

module.exports = Profile;

Add functionality to logout button


var assert = require("assert");
var Q = require("q");

var api = require("../../lib/dummy/api");
var DummyApi = api.DummyApi;

var resources = require("../../lib/dummy/resources");
var DummyResource = resources.DummyResource;

var ToyResource = DummyResource.extend(function(self) {
    DummyResource.call(self);

    self.name = 'toy';

    self.handlers.foo = function(cmd) {
        return {
            handler: 'foo',
            cmd: cmd
        };
    };
});

describe("DummyApi", function () {
    var api;

    function api_request(name, data) {
        var d = Q.defer();

        api.request(name, data, function(reply) {
            d.resolve(reply);
        });

        return d.promise;
    }

    beforeEach(function () {
        api = new DummyApi();
    });

    // TODO remove when pending_calls_complete is removed
    it.skip("should dispatch commands asynchronously", function() {
        var has_reply = false;

        api_request("kv.get", {key: "foo"}).then(function (reply) {
            has_reply = true;
            assert(reply.success);
            assert.equal(reply.value, null);
        });

        // check reply wasn't called immediately
        assert(!has_reply);
        return api.pending_calls_complete().then(function () {
            assert(has_reply);
        });
    });

    it("should dispatch commands using its resource controller when possible",
    function() {
        api.resources.add(new ToyResource());
        return api_request('toy.foo', {}).then(function(result) {
            assert.deepEqual(result, {
                handler: 'foo',
                cmd: {cmd: 'toy.foo'}
            });
        });
    });

    describe(".find_contact", function() {
        it("should fail for unknown address types", function() {
            assert.throws(
                function () { api.find_contact("unknown", "+12334"); },
                "/Unsupported delivery class " +
                "(got: unknown with address +12334)/");
        });
    });
});

var assert = require("assert");

var vumigo = require("../../lib");
var test_utils = vumigo.test_utils;
var DummyApi = vumigo.dummy.api.DummyApi;
var DummyResource = vumigo.dummy.resources.DummyResource;

var ToyResource = DummyResource.extend(function(self) {
    DummyResource.call(self);

    self.name = 'toy';

    self.handlers.foo = function(cmd) {
        return {
            handler: 'foo',
            cmd: cmd
        };
    };
});

describe("DummyApi", function () {
    var api;
    var request;

    beforeEach(function () {
        api = new DummyApi();
        request = test_utils.requester(api);
    });

    it("should dispatch commands using its resource controller", function() {
        api.resources.add(new ToyResource());
        return request('toy.foo', {}).then(function(result) {
            assert.deepEqual(result, {
                handler: 'foo',
                cmd: {cmd: 'toy.foo'}
            });
        });
    });
});

Remove old parts of DummyApi tests


import { createLoader } from 'react-hijack';

const componentLoader = createLoader((module) => import('' + module));

export default componentLoader;

import { createLoader } from 'react-hijack';

const componentLoader = createLoader((module) => import('./components/' + module));

export default componentLoader;

Add component path at module level


const path = require('path');

const webpackConfig = {
  entry: path.join(__dirname, 'index.js'),
  output: {
    path: path.join(__dirname, '../server/static'),
    filename: 'bundle.js'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel',
        exclude: /node_modules/,
        query: {
          presets: ['react', 'es2015', 'stage-0']
        }
      },
      {
        test: /\.(png|jpg|gif|svg|woff|woff2|eot|ttf)$/,
        loader: 'url-loader'
      },
      {
        test: /\.scss$/,
        loader: 'style-loader!css-loader!sass-loader'
      }
    ]
  }
};

module.exports = webpackConfig;

const path = require('path');

const webpackConfig = {
  entry: path.join(__dirname, 'index.js'),
  output: {
    path: path.join(__dirname, '../server/static'),
    filename: 'bundle.js'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel',
        exclude: /node_modules/,
        query: {
          presets: ['react', 'es2015', 'stage-0']
        }
      },
      {
        test: /\.(png|jpg|gif|svg|woff|woff2|eot|ttf)$/,
        loader: 'url-loader'
      },
      {
        test: /\.css$/,
        loader: 'style-loader!css-loader'
      },
      {
        test: /\.scss$/,
        loader: 'style-loader!css-loader!sass-loader'
      }
    ]
  }
};

module.exports = webpackConfig;

Bring back regular css loading


'use strict';

var sepItems        = require('es5-ext/array/#/sep-items')
  , d               = require('d/d')
  , DOMInput        = require('./_observable')
  , resolveTriggers = require('dbjs/_setup/utils/resolve-static-triggers')

  , Input;

module.exports = Input = function (document, ns/*, options*/) {
	DOMInput.apply(this, arguments);
};

Input.prototype = Object.create(DOMInput.prototype, {
	_render: d(function (options) {
		var el = this.make, desc = options.dbOptions
		  , triggers = resolveTriggers(desc._value_)
		  , object = options.observable.object;

		this.dom = el('div', sepItems.call(triggers.map(function (name) {
			return this._get(name);
		}, object).map(function (observable) {
			var desc = observable.descriptor, opts = this.getOptions(desc);
			if ((opts.placeholder == null) && desc.label) {
				opts.placeholder = desc.label;
			}
			return this.addItem(observable.toDOMInput(this.document, opts),
				observable.dbId);
		}, this), (options.sep != null) ? options.sep : ' '));
	})
});

'use strict';

var sepItems     = require('es5-ext/array/#/sep-items')
  , d            = require('d/d')
  , memoize      = require('memoizee/lib/primitive')
  , DOMInput     = require('./_observable')
  , resolveProps = require('esniff/accessed-properties')('this')

  , re = new RegExp('^\\s*function\\s*(?:[\\0-\'\\)-\\uffff]+)*\\s*\\(\\s*' +
	'(_observe[\\/*\\s]*)?\\)\\s*\\{([\\0-\\uffff]*)\\}\\s*$')
  , Input, resolve;

resolve = memoize(function (fn) {
	return resolveProps(String(fn).match(re)[2]).map(function (data) {
		return data.name;
	});
});

module.exports = Input = function (document, ns/*, options*/) {
	DOMInput.apply(this, arguments);
};

Input.prototype = Object.create(DOMInput.prototype, {
	_render: d(function (options) {
		var el = this.make, desc = options.dbOptions
		  , triggers = resolve(desc._value_)
		  , object = options.observable.object;

		this.dom = el('div', sepItems.call(triggers.map(function (name) {
			return this._get(name);
		}, object).map(function (observable) {
			var desc = observable.descriptor, opts = this.getOptions(desc);
			if ((opts.placeholder == null) && desc.label) {
				opts.placeholder = desc.label;
			}
			return this.addItem(observable.toDOMInput(this.document, opts),
				observable.dbId);
		}, this), (options.sep != null) ? options.sep : ' '));
	})
});

Update up to changes in dbjs


import React, { Component } from 'react';

export default class SearchBar extends Component {
  render() {
    return (
      <form className='input-group'>
        <input />
        <span className='input-group-btn'>
          <button type='submit' className='btn btn-secondary'>Submit</button>
        </span>
      </form>
    );
  }
}

import React, { Component } from 'react';

export default class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = {term: ''}
    this.onInputChange = this.onInputChange.bind(this);
  }

  onInputChange(event) {
    this.setState({term: event.target.value});
  }

  onFormSubmit(event) {
    event.preventDefault();

    // Go and fetch weather data from API
  }

  render() {
    return (
      <form onSubmit={this.onFormSubmit} className='input-group'>
        <input
          placeholder='Get a 5 day forecast in your favourite cities'
          className='form-control'
          value={this.state.term}
          onChange={this.onInputChange} />
        <span className='input-group-btn'>
          <button type='submit' className='btn btn-secondary'>Submit</button>
        </span>
      </form>
    );
  }
}

Add Form submit handler to search bar


var scroll_lock = false;
var last_page = false;
var current_page = 1;

function checkScroll()
{
  if(!last_page && !scroll_lock && nearBottomOfPage())
  {
    scroll_lock = true;

    current_page++;

    var qs = $H(location.search.toQueryParams());
    qs.each(function(pair)
    {
      qs.set(pair.key, pair.value.replace("+", " "));
    });

    var url = location.pathname + "?" + qs.merge({ 'page' : current_page }).toQueryString();

    new Ajax.Request(url, { asynchronous: true, evalScripts: true, method: 'get', onSuccess: function(req)
    {
      setTimeout(function()
      {
        scroll_lock = false;
      }, 500);
    } });
  }
}

function nearBottomOfPage() {
  return scrollDistanceFromBottom() < 500;
}

function scrollDistanceFromBottom() {
  return pageHeight() - (window.pageYOffset + self.innerHeight);
}

function pageHeight() {
  return $$("body")[0].getHeight();
}

document.observe('dom:loaded', function()
{
  setInterval("checkScroll()", 250);
});

var scroll_lock = false;
var last_page = false;
var current_page = 1;

function checkScroll()
{
  if(!last_page && !scroll_lock && nearBottomOfPage())
  {
    scroll_lock = true;

    current_page++;

    var qs = $H(location.search.toQueryParams());
    qs.each(function(pair)
    {
      qs.set(pair.key, pair.value.replace("+", " "));
    });

    var url = location.pathname + "?" + qs.merge({ 'page' : current_page }).toQueryString();

    new Ajax.Request(url, { asynchronous: true, evalScripts: true, method: 'get', onSuccess: function(req)
    {
      scroll_lock = false;
      myLightWindow._setupLinks();
    } });
  }
}

function nearBottomOfPage() {
  return scrollDistanceFromBottom() < 500;
}

function scrollDistanceFromBottom() {
  return pageHeight() - (window.pageYOffset + self.innerHeight);
}

function pageHeight() {
  return $$("body")[0].getHeight();
}

document.observe('dom:loaded', function()
{
  setInterval("checkScroll()", 250);
});

Make lightwindow work after ajax load


'use strict';

var ChartsController = function($scope, $rootScope) {
  $scope.drawChart = function() {
    if ($scope.geoAggData && $scope.geoAggData.length > 0) {
      console.log('$scope.geoAggData', $scope.geoAggData, 'length', $scope.geoAggData);
      // instantiate d3plus
      d3plus.viz()
        .container('#viz')              // container DIV to hold the visualization
        .data($scope.geoAggData)        // data to use with the visualization
        .type('bubbles')                // visualization type
        .id(['group', 'name'])          // nesting keys
        .depth(1)                       // 0-based depth
        .size('value')                  // key name to size bubbles
        .color('name')                  // color by each group
        .legend({value: false})
        .margin('-40px 10px 0px 0px')
        .width({value: 370})
        .height({value: 320})
        .draw()                         // finally, draw the visualization!

      if (window._gaq) {
        _gaq.push(['_trackEvent', 'police-uk', 'chart']);
      }
    }
  }
};

'use strict';

var ChartsController = function($scope, $rootScope) {
  $scope.drawChart = function() {
    if ($scope.geoAggData && $scope.geoAggData.length > 0) {
      var length = $scope.geoAggData.length;
      var height = 320;
      var width = 370;
      var margin = '-40px 10px 0px 0px';

      if (length == 1) {
        height = 300;
        width = 350;
        margin = '40px 0px 0px 30px';
      }

      // instantiate d3plus
      d3plus.viz()
        .container('#viz')              // container DIV to hold the visualization
        .data($scope.geoAggData)        // data to use with the visualization
        .type('bubbles')                // visualization type
        .id(['group', 'name'])          // nesting keys
        .depth(1)                       // 0-based depth
        .size('value')                  // key name to size bubbles
        .color('name')                  // color by each group
        .legend({value: false})
        .margin(margin)
        .width({value: width})
        .height({value: height})
        .draw()                         // finally, draw the visualization!

      if (window._gaq) {
        _gaq.push(['_trackEvent', 'police-uk', 'chart']);
      }
    }
  }
};

Reduce size of chart with query


/**
 * Widgets layout constants.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Internal dependencies
 */
import { WIDGET_WIDTHS } from '../datastore/constants';
import ReportZero from '../../../components/ReportZero';
import CompleteModuleActivationCTA from '../../../components/CompleteModuleActivationCTA';
import ActivateModuleCTA from '../../../components/ActivateModuleCTA';

export const WIDTH_GRID_COUNTER_MAP = {
	[ WIDGET_WIDTHS.QUARTER ]: 3,
	[ WIDGET_WIDTHS.HALF ]: 6,
	[ WIDGET_WIDTHS.FULL ]: 12,
};

export const HIDDEN_CLASS = 'googlesitekit-hidden';
export const SPECIAL_WIDGET_STATES = [
	ActivateModuleCTA,
	CompleteModuleActivationCTA,
	ReportZero,
];

/**
 * Widgets layout constants.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Internal dependencies
 */
import { WIDGET_WIDTHS } from '../datastore/constants';
import ReportZero from '../../../components/ReportZero';
import RecoverableModules from '../../../components/RecoverableModules';
import CompleteModuleActivationCTA from '../../../components/CompleteModuleActivationCTA';
import ActivateModuleCTA from '../../../components/ActivateModuleCTA';

export const WIDTH_GRID_COUNTER_MAP = {
	[ WIDGET_WIDTHS.QUARTER ]: 3,
	[ WIDGET_WIDTHS.HALF ]: 6,
	[ WIDGET_WIDTHS.FULL ]: 12,
};

export const HIDDEN_CLASS = 'googlesitekit-hidden';
export const SPECIAL_WIDGET_STATES = [
	ActivateModuleCTA,
	CompleteModuleActivationCTA,
	ReportZero,
	RecoverableModules,
];

Add RecoverableModules as special widget state.


/*
 * @flow
 */

import type {RawStats, Module} from '../types/Stats';

import type {Child} from './getEntryHeirarchy';

import getModulesByChunk from './getModulesByChunk';

export default function getChunkModules(
  stats: RawStats,
  parentChunks: ?Array<Child>,
): ?Array<Module> {
  if (!parentChunks) {
    return null;
  }

  const modulesByChunk = getModulesByChunk(
    stats,
    parentChunks.map((chunk) => chunk.id),
  );

  return parentChunks.reduce(
    (modules, chunk) => modules.concat(modulesByChunk[chunk.id].modules),
    [],
  );
}

/*
 * @flow
 */

import type {RawStats, Module} from '../types/Stats';

import type {Child} from './getEntryHeirarchy';

import getModulesByChunk from './getModulesByChunk';

export default function getChunkModules(
  stats: RawStats,
  parentChunks: ?Array<Child>,
): ?Array<Module> {
  if (!parentChunks) {
    return null;
  }

  const modulesByChunk = getModulesByChunk(
    stats,
    parentChunks.map((chunk) => chunk.id),
  );

  return parentChunks.reduce(
    (modules, chunk) => {
      const chunkWithModules = modulesByChunk[chunk.id] || {modules: []};
      return modules.concat(chunkWithModules.modules);
    },
    [],
  );
}

Check for undefined when fetching a chunkWithModules by chunk.id


/**
 * In development we assume that the code generated is going to be consumed by
 * webpack dev server and we are bundling into a single js file.
 */

import webpack from 'webpack';
import webpackConfigBase from './webpack.base.babel';
import HtmlWebpackPlugin from 'html-webpack-plugin';

const plugins = [
  new HtmlWebpackPlugin({
    template: `index.html`
  }),
  new webpack.EnvironmentPlugin([
    `CISCOSPARK_ACCESS_TOKEN`,
    `MESSAGE_DEMO_CLIENT_ID`,
    `MESSAGE_DEMO_CLIENT_SECRET`,
    `SPACE_ID`,
    `TO_PERSON_EMAIL`,
    `TO_PERSON_ID`
  ])
];

export default webpackConfigBase({
  entry: `./index.js`,
  plugins,
  devtool: `source-map`,
  devServer: {
    port: 8000,
    stats: {
      colors: true,
      hash: false,
      version: false,
      timings: false,
      assets: true,
      chunks: false,
      modules: false,
      reasons: false,
      children: false,
      source: false,
      errors: true,
      errorDetails: true,
      warnings: true,
      publicPath: false
    }
  }
});

/**
 * In development we assume that the code generated is going to be consumed by
 * webpack dev server and we are bundling into a single js file.
 */

import webpack from 'webpack';
import webpackConfigBase from './webpack.base.babel';
import HtmlWebpackPlugin from 'html-webpack-plugin';

const plugins = [
  new HtmlWebpackPlugin({
    template: `index.html`
  }),
  new webpack.EnvironmentPlugin([
    `CISCOSPARK_ACCESS_TOKEN`,
    `MESSAGE_DEMO_CLIENT_ID`,
    `MESSAGE_DEMO_CLIENT_SECRET`,
    `SPACE_ID`,
    `TO_PERSON_EMAIL`,
    `TO_PERSON_ID`
  ])
];

export default webpackConfigBase({
  entry: `./index.js`,
  plugins,
  devtool: `source-map`,
  devServer: {
    host: `0.0.0.0`,
    port: 8000,
    stats: {
      colors: true,
      hash: false,
      version: false,
      timings: false,
      assets: true,
      chunks: false,
      modules: false,
      reasons: false,
      children: false,
      source: false,
      errors: true,
      errorDetails: true,
      warnings: true,
      publicPath: false
    }
  }
});

Allow any host connection to local dev


chorus.views.Sidebar = chorus.views.Base.extend({
    constructorName: "SidebarView",
    preRender:function () {
        this._super("preRender", arguments);

        // We don't want to deal with having multiple declarations of `events`,
        // so we unbind click in preRender and bind it in postRender.
        $("#sidebar_wrapper").find(".jump_to_top").unbind("click");
    },

    template:function () {
        var result = this._super('template', arguments);
        return "<div class='spacer'/>" + result;
    },

    postRender:function () {
        this._super('postRender');

        var sidebar = $(this.el).closest("#sidebar");
        this.setupScrolling(sidebar, {
            contentWidth: sidebar.width()
        });

        $("#sidebar_wrapper .jump_to_top").bind("click", function (e) {
            var api = $("#sidebar").data("jsp")
            if (api) {
                api.scrollTo(0, 0);
                $(this).removeClass("clickable");
            }
        });
    },

    onMouseWheel:function (event, d) {
        var api = $("#sidebar").data("jsp")
        $("#sidebar_wrapper .jump_to_top").toggleClass("clickable", api.getContentPositionY() > 10);
        event.preventDefault();
        return true;
    },

    recalculateScrolling : function() {
        this._super("recalculateScrolling", [$(this.el).closest(".custom_scroll")])
    }
});

chorus.views.Sidebar = chorus.views.Base.extend({
    constructorName: "SidebarView",
    preRender:function () {
        this._super("preRender", arguments);

        // We don't want to deal with having multiple declarations of `events`,
        // so we unbind click in preRender and bind it in postRender.
        $("#sidebar_wrapper").find(".jump_to_top").unbind("click");
    },

    template:function () {
        var result = this._super('template', arguments);
        return "<div class='spacer'/>" + result;
    },

    postRender:function () {
        this._super('postRender');

        var sidebar = $(this.el).closest("#sidebar");
        this.setupScrolling(sidebar, {
            contentWidth: sidebar.width()
        });

        $("#sidebar_wrapper .jump_to_top").bind("click", function (e) {
            var api = $("#sidebar").data("jsp")
            if (api) {
                api.scrollTo(0, 0);
                $(this).removeClass("clickable");
            }
        });

        if (chorus.isDevMode) {
            $("#sidebar_wrapper").attr("data-sidebar-template", this.className);
        }
    },

    onMouseWheel:function (event, d) {
        var api = $("#sidebar").data("jsp")
        $("#sidebar_wrapper .jump_to_top").toggleClass("clickable", api.getContentPositionY() > 10);
        event.preventDefault();
        return true;
    },

    recalculateScrolling : function() {
        this._super("recalculateScrolling", [$(this.el).closest(".custom_scroll")])
    }
});

Add data attr for sidebar template higher up in DOM


'use babel'

export default {
  eslintrc: {
    config: {
      description: '.eslint',
      icons: [ 'ESLint' ],
      title: 'ESLint',
    },
    match: [ '.eslint' ],
  },
  git: {
    config: {
      description: '.git',
      icons: [ 'Git', 'GitHub', 'GitHubAlt' ],
      title: 'Git',
    },
    match: [ '.git' ],
  },
  license: {
    config: {
      description: 'license',
      icons: [ 'OSI', 'MIT' ],
      title: 'License',
    },
    match: [ 'license' ],
  },
}

'use babel'

export default {
  eslintrc: {
    config: {
      description: '.eslint',
      icons: [ 'ESLint' ],
      title: 'ESLint',
    },
    match: [ '.eslint' ],
  },
  git: {
    config: {
      description: '.git',
      icons: [ 'Git', 'GitHub', 'GitHubAlt' ],
      title: 'Git',
    },
    match: [ '.git' ],
  },
  license: {
    config: {
      description: 'license',
      icons: [ 'OSI', 'MIT' ],
      title: 'License',
    },
    match: [ 'license' ],
  },
  npm: {
    config: {
      description: '.npm',
      icons: [ 'npm', 'npmAlt' ],
      title: 'npm',
    },
    match: [ '.npm' ],
  },
}

Add icon for .npm files


import React, { Component, PropTypes } from 'react';

export default class BoardgameListItem extends Component {
  static propTypes = {
    name: PropTypes.string.isRequired,
    year: PropTypes.string
  }

  render() {
    const { name, year } = this.props;
    return (

{name}

{year}

    );
  }
}

import React, { Component, PropTypes } from 'react';

export default class BoardgameListItem extends Component {
  static propTypes = {
    name: PropTypes.string.isRequired,
    year: PropTypes.number,
    thumbnail: PropTypes.string,
    score: PropTypes.number
  }

  render() {
    const { name, year, thumbnail, score } = this.props;
    return (

          {thumbnail ?
           :
           }

{name}

{year}, {score}

    );
  }
}

Update BoardgameListComponent to accept more input


/* @flow */

import React, {Component} from 'react'
import {connect} from 'react-redux'
import HiddenString from '../../../util/hidden-string'
import {sawPaperKey} from '../../../actions/signup'

import Render from './index.render'

class Success extends Component {
  render () {
    return (
      <Render
        title={this.props.title}
        paperkey={this.props.paperkey}
        onFinish={this.props.onFinish}
        onBack={this.props.onBack}
        />
    )
  }
}

Success.propTypes = {
  paperkey: React.PropTypes.instanceOf(HiddenString).isRequired,
  onFinish: React.PropTypes.func.isRequired
}

export default connect(
  state => ({paperkey: state.signup.paperkey}),
  dispatch => ({
    onFinish: dispatch(sawPaperKey()),
    onBack: () => {}
  })
)(Success)

/* @flow */

import React, {Component} from 'react'
import {connect} from 'react-redux'
import HiddenString from '../../../util/hidden-string'
import {sawPaperKey} from '../../../actions/signup'

import Render from './index.render'

class Success extends Component {
  render () {
    return (
      <Render
        title={this.props.title}
        paperkey={this.props.paperkey}
        onFinish={this.props.onFinish}
        onBack={this.props.onBack}
        />
    )
  }
}

Success.propTypes = {
  paperkey: React.PropTypes.instanceOf(HiddenString).isRequired,
  onFinish: React.PropTypes.func.isRequired
}

export default connect(
  state => ({paperkey: state.signup.paperkey}),
  dispatch => ({
    onFinish: () => dispatch(sawPaperKey()),
    onBack: () => {}
  })
)(Success)

Fix not waiting for paper key approval


'use strict';

module.exports = isAngularProject => ({
  test: /\.tsx?$/,
  exclude: /(node_modules)/,
  loaders: [...isAngularProject ? ['ng-annotate'] : [], 'ts'],
  query: {
    logInfoToStdOut: true
  }
});

'use strict';

module.exports = isAngularProject => ({
  test: /\.tsx?$/,
  exclude: /(node_modules)/,
  loaders: [...isAngularProject ? ['ng-annotate'] : [], 'ts']
});

Revert Lod ts-loader info to stdout


basePath = '../';

files = [
  JASMINE,
  JASMINE_ADAPTER,
  'app/js/jquery-1.9.1.min.js',
  'app/js/bootstrap.js',
  'app/lib/angular/angular.js',
  'app/lib/angular/angular-*.js',
  'test/lib/angular/angular-mocks.js',
  'app/js/leaflet.js',
  'app/js/**/*.js',
  'test/unit/**/*.js'
];

autoWatch = true;

browsers = ['Chrome'];

junitReporter = {
  outputFile: 'test_out/unit.xml',
  suite: 'unit'
};

basePath = '../';

files = [
  JASMINE,
  JASMINE_ADAPTER,
  'app/lib/jquery/jquery-1.10.1.js',
  'app/lib/jquery/jquery-ui/jquery-ui-1.10.3.min.js',
  'app/lib/bootstrap/bootstrap.js',
  'app/lib/angular/angular.js',
  'app/lib/angular/angular-*.js',
  'test/lib/angular/angular-mocks.js',
  'app/lib/jquery/jquery-ui/datepicker-wrapper/date.js',
  'app/lib/leaflet/leaflet-0.5.js',
  'app/js/app.js',
  'app/js/controllers/*.js',
  'app/js/directives/*.js',
  'app/js/services/*.js',
  'test/unit/**/*.js'
];

autoWatch = true;

browsers = ['Chrome'];

junitReporter = {
  outputFile: 'test_out/unit.xml',
  suite: 'unit'
};

Resolve CLIMATE-112 - Unit tests don't run after refactoring


import Command from './Command'

class EditInlineNodeCommand extends Command {
  constructor(...args) {
    super(...args)
    if (!this.config.nodeType) {
      throw new Error('Every AnnotationCommand must have a nodeType')
    }
  }

  getCommandState(params) {
    let sel = params.selection
    let newState = {
      disabled: true,
      active: false
    }
    let annos = this._getAnnotationsForSelection(params)
    if (annos.length === 1 && annos[0].getSelection().equals(sel)) {
      newState.disabled = false
      newState.node = annos[0]
    }
    return newState
  }

  execute(params) { // eslint-disable-line

  }

  _getAnnotationsForSelection(params) {
    return params.selectionState.getAnnotationsForType(this.config.nodeType)
  }

}

export default EditInlineNodeCommand

import Command from './Command'

class EditInlineNodeCommand extends Command {
  constructor(...args) {
    super(...args)
    if (!this.config.nodeType) {
      throw new Error('Every AnnotationCommand must have a nodeType')
    }
  }

  getCommandState(params) {
    let sel = params.selection
    let newState = {
      disabled: true,
      active: false
    }
    let annos = this._getAnnotationsForSelection(params)
    if (annos.length === 1 && annos[0].getSelection().equals(sel)) {
      newState.disabled = false
      newState.nodeId = annos[0].id
    }
    return newState
  }

  execute(params) { // eslint-disable-line

  }

  _getAnnotationsForSelection(params) {
    return params.selectionState.getAnnotationsForType(this.config.nodeType)
  }

}

export default EditInlineNodeCommand

Use primitive command state for EditInlineCommand.


import debug from 'debug';

const log = debug('app:enhance');

// ServiceWorker is a progressive technology. Ignore unsupported browsers
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistrations().then((registrations) => {
    registrations.forEach(registration => registration.unregister());
  });
} else {
  log('service worker is not supported.');
}

window.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('.top-bar-menu-button').forEach(el => {
    el.addEventListener('click', (event) => {
      event.stopPropagation();
      toggleClass(document.querySelector('.top-bar-section'), 'hidden');
      el.querySelectorAll('.icon').forEach(ic => toggleClass(ic, 'hidden'));
    })
  });
});

function toggleClass(el, className) {
  if (el.classList.contains(className)) {
    el.classList.remove(className)
  } else {
    el.classList.add(className)
  }
}

import debug from 'debug'

const log = debug('app:enhance')

// ServiceWorker is a progressive technology. Ignore unsupported browsers
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistrations().then(registrations => {
    registrations.forEach(registration => registration.unregister())
  })
} else {
  log('service worker is not supported.')
}

window.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('.top-bar-menu-button').forEach(el => {
    el.addEventListener('click', event => {
      event.stopPropagation()
      toggleClass(document.querySelector('.top-bar-section'), 'hidden')
      el.querySelectorAll('.icon').forEach(ic => toggleClass(ic, 'hidden'))
    })
  })

  const slideInForm = document.querySelector('.formkit-slide-in')
  if (slideInForm) {
    log('slide in form detected')
  } else {
    log('slide in form NOT detected')
  }
})

function toggleClass(el, className) {
  if (el.classList.contains(className)) {
    el.classList.remove(className)
  } else {
    el.classList.add(className)
  }
}

const targetNode = document.querySelector('body')

// Options for the observer (which mutations to observe)
const config = { childList: true }

// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
  for (let mutation of mutationsList) {
    if (mutation.type === 'childList') {
      mutation.addedNodes.forEach((node) => {
        if (node.classList && node.classList.contains('formkit-slide-in')) {
          log('formkit slide in', node)
          const input = node.querySelector('input[name=\'email_address\']')
          input.type = 'email'
          log('input', input)
        }
      })
    }
  }
}

// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback)

// Start observing the target node for configured mutations
observer.observe(targetNode, config)

// Later, you can stop observing
// observer.disconnect()

Change async form input type


const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, '../dist/devpanel.tmpl.html');
const distPath = path.join(__dirname, '../dist/devpanel.html');
const startRemoteDev = require('remotedev-server');

const html = fs.readFileSync(filePath, 'utf-8');

module.exports = function(argv) {
  if (argv.runserver) {
    argv.port = argv.port || 8000;
    startRemoteDev(argv);
  }
  if (argv.hostname || argv.port) {
    fs.writeFileSync(
      distPath,
      html.replace(
        '// __remotedevOptionsSet__',
        'window.remotedevOptions = ' + JSON.stringify({
          hostname: argv.hostname,
          port: argv.port || 8000,
          autoReconnect: true
        })
      )
    );
  } else {
    fs.writeFileSync(distPath, html);
  }
};

const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, '../dist/devpanel.tmpl.html');
const distPath = path.join(__dirname, '../dist/devpanel.html');
const startRemoteDev = require('remotedev-server');

const html = fs.readFileSync(filePath, 'utf-8');

module.exports = argv => {
  if (argv.hostname || argv.port) {
    fs.writeFileSync(
      distPath,
      html.replace(
        '// __remotedevOptionsSet__',
        'window.remotedevOptions = ' + JSON.stringify({
          hostname: argv.hostname,
          port: argv.port || 8000,
          autoReconnect: true
        })
      )
    );
  } else {
    fs.writeFileSync(distPath, html);
  }
  if (argv.runserver) {
    argv.port = argv.port || 8000;
    return startRemoteDev(argv);
  }
  return { on: (status, cb) => cb() };
};

Apply runserver option will return socketcluster server


#!/usr/bin/env node

var program = require('commander'),
	cjs = require(__dirname + '/../lib/');

program
	.version(require(__dirname + '/../package.json').version)
	.option('-i, --input <file>', 'input file (required)')
	.option('-o, --output <file>', 'output file (defaults to <input>.out.js)')
	.option('-x, --extension <ext>', 'default extension for requires (defaults to "js")')
	.option('-m, --map [file]', 'file to store source map to (optional)')
	.option('-c, --comments', 'preserve comments in output')
	.option('-e, --exports <id>', 'top module exports destination (optional)')
	.option('-s, --external [hash]', 'external modules (names or JSON hashes)', function (value, obj) {
		try {
			var add = JSON.parse(value);
			for (var name in add) {
				obj[name] = add[name];
			}
		} catch (e) {
			obj[value] = true;
		}
		return obj;
	}, {})
	.parse(process.argv);

if (!program.input) {
	program.help();
}

console.log('Building...');

cjs.transform(program).then(function (result) {
	console.log('Built to:', result.options.output);
}, function (error) {
	console.error(error.stack);
});

#!/usr/bin/env node

var program = require('commander'),
	cjs = require(__dirname + '/../lib/');

program
	.version(require(__dirname + '/../package.json').version)
	.option('-i, --input <file>', 'input file (required)')
	.option('-o, --output <file>', 'output file (defaults to <input>.out.js)')
	.option('-x, --extension <ext>', 'default extension for requires (defaults to "js")')
	.option('-m, --map [file]', 'file to store source map to (optional)')
	.option('-c, --comments', 'preserve comments in output')
	.option('-e, --exports <id>', 'top module exports destination (optional)')
	.option('-d, --module-dir <dir>', 'top level location to search for external modules (optional)')
	.option('-s, --external [hash]', 'external modules (names or JSON hashes)', function (value, obj) {
		try {
			var add = JSON.parse(value);
			for (var name in add) {
				obj[name] = add[name];
			}
		} catch (e) {
			obj[value] = true;
		}
		return obj;
	}, {})
	.parse(process.argv);

if (!program.input) {
	program.help();
}

console.log('Building...');

cjs.transform(program).then(function (result) {
	console.log('Built to:', result.options.output);
}, function (error) {
	console.error(error.stack);
});

Fix support for specifying module-dir


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({"skip-all": true})
            .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({"skip-all": true})
            .on('end', done);
    });

    // TODO: write normal tests for generator

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

Add todo in generator tests


const crypto = require('crypto')
const fs = require('fs')
const path = require('path')

const CONFIG = require('../config')
const FINGERPRINT_PATH = path.join(CONFIG.repositoryRootPath, 'node_modules', '.dependencies-fingerprint')

module.exports = {
  write: function () {
    const fingerprint = this.compute()
    fs.writeFileSync(FINGERPRINT_PATH, fingerprint)
    console.log('Wrote Dependencies Fingerprint:', FINGERPRINT_PATH, fingerprint)
  },
  read: function () {
    return fs.existsSync(FINGERPRINT_PATH) ? fs.readFileSync(FINGERPRINT_PATH, 'utf8') : null
  },
  isOutdated: function () {
    const fingerprint = this.read()
    return fingerprint ? fingerprint !== this.compute() : false
  },
  compute: function () {
    // Include the electron minor version in the fingerprint since that changing requires a re-install
    const electronVersion = CONFIG.appMetadata.electronVersion.replace(/\.\d+$/, '')
    const apmVersion = CONFIG.apmMetadata.dependencies['atom-package-manager']
    const body = electronVersion + apmVersion + process.platform + process.version
    return crypto.createHash('sha1').update(body).digest('hex')
  }
}

const crypto = require('crypto')
const fs = require('fs')
const path = require('path')

const CONFIG = require('../config')
const FINGERPRINT_PATH = path.join(CONFIG.repositoryRootPath, 'node_modules', '.dependencies-fingerprint')

module.exports = {
  write: function () {
    const fingerprint = this.compute()
    fs.writeFileSync(FINGERPRINT_PATH, fingerprint)
    console.log('Wrote Dependencies Fingerprint:', FINGERPRINT_PATH, fingerprint)
  },
  read: function () {
    return fs.existsSync(FINGERPRINT_PATH) ? fs.readFileSync(FINGERPRINT_PATH, 'utf8') : null
  },
  isOutdated: function () {
    const fingerprint = this.read()
    return fingerprint ? fingerprint !== this.compute() : false
  },
  compute: function () {
    // Include the electron minor version in the fingerprint since that changing requires a re-install
    const electronVersion = CONFIG.appMetadata.electronVersion.replace(/\.\d+$/, '')
    const apmVersion = CONFIG.apmMetadata.dependencies['atom-package-manager']
    const body = electronVersion + apmVersion + process.platform + process.version + process.arch
    return crypto.createHash('sha1').update(body).digest('hex')
  }
}

Include arch in dependency cache key