var webpack = require('webpack');

module.exports = {
  entry: {
    app: './src/js/app.jsx',
    vendors: ['react', 'react-dom', 'react-router', 'redux', 'redux-thunk', 'es6-promise-polyfill']
  },
  output: {
    path: './public/js/',
    publicPath: '/js/',
    filename: 'app.js'
  },
  module: {
    preLoaders: [
      {
        test: /\.jsx?$/,
        loaders: ['eslint']
      }
    ],
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel',
        exclude: '/node_modules/',
        query: {
          cacheDirectory: true,
          presets: ['react', 'es2015']
        }
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  devServer: {
    historyApiFallback: true
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      minimize: true
    })
  ]
};

var webpack = require('webpack');

module.exports = {
  entry: {
    app: './src/js/app.jsx',
    vendors: ['react', 'react-dom', 'react-router', 'redux', 'redux-thunk', 'es6-promise-polyfill']
  },
  output: {
    path: './public/js/',
    publicPath: '/js/',
    filename: 'app.js'
  },
  module: {
    preLoaders: [
      {
        test: /\.jsx?$/,
        loaders: ['eslint']
      }
    ],
    loaders: [
      {
        test: /\.jsx?$/,
        loader: 'babel',
        exclude: '/node_modules/',
        query: {
          cacheDirectory: true,
          presets: ['react', 'es2015']
        }
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  devServer: {
    historyApiFallback: true,
    contentBase: './public'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      minimize: true
    })
  ]
};

Set public folder as the content base for webpack dev server


//= require tolk/prototype.js
//= require tolk/controls.js
//= require tolk/dragdrop.js
//= require tolk/effects.js

//= require tolk/prototype.js
//= require tolk/effects.js
//= require tolk/controls.js
//= require tolk/dragdrop.js

Reorder assets to remove a js warning


"use strict";

var program = require('commander');
var path    = require('path');
var os = require('os');
var appDir  = path.resolve(os.tmpDir(), 'cartridge');

var newCommand = require('./commands/new')(appDir);
var pkg        = require(path.resolve(__dirname, '..', 'package.json'));

module.exports = function() {
	setProgramBaseSettings();
	setNewCommand();
	initProgram();
}

function setNewCommand() {
	program
		.command('new')
		.description('Create a new project')
		.action(function() {
			newCommand.init(getProgramOptions());
		});
}

function initProgram() {
	program.parse(process.argv);

	if (!process.argv.slice(2).length) {
		program.outputHelp();
	}
}

function getProgramOptions() {
	return {
		silent: program.silent,
		verbose: program.verbose
	}
}

function setProgramBaseSettings() {
	program
		.version(pkg.version)
		.option('-s, --silent', 'Surpress all on-screen messages')
		.option('-v, --verbose', 'Show all on-screen messages');
}

"use strict";

var program = require('commander');
var path    = require('path');
var os = require('os');
var appDir  = path.resolve(os.tmpDir(), 'cartridge');

var newCommand = require('./commands/new')(appDir);
var pkg        = require(path.resolve(__dirname, '..', 'package.json'));

/**
 * Get the ball-rolling for the whole program
 */
module.exports = function() {
	setProgramBaseSettings();
	setNewCommand();
	initProgram();
}

/**
 * Setup the 'new' command
 */
function setNewCommand() {
	program
		.command('new')
		.description('Create a new project')
		.action(function() {
			newCommand.init(getProgramOptions());
		});
}

/**
 * Initialise program
 */
function initProgram() {
	program.parse(process.argv);

	if (!process.argv.slice(2).length) {
		program.outputHelp();
	}
}

/**
 * Get program option flags
 */
function getProgramOptions() {
	return {
		silent: program.silent,
		verbose: program.verbose
	}
}

/**
 * Set program base settings: version, option flags
 */
function setProgramBaseSettings() {
	program
		.version(pkg.version)
		.option('-s, --silent', 'Surpress all on-screen messages')
		.option('-v, --verbose', 'Show all on-screen messages');
}

Add doc comments for program builder


const animationPage = require('./animation');
const peoplePage = require('./people');
const trayPage = require('./tray');
const {Button, NavigationView, Page, ui} = require('tabris');

const MARGIN = 8;

let navigationView = new NavigationView({
  left: 0, top: 0, right: 0, bottom: 0
}).appendTo(ui.contentView);

let mainPage = new Page({
  title: 'Animation Examples'
}).appendTo(navigationView);

[animationPage, peoplePage, trayPage].forEach((page) => {
  new Button({
    left: MARGIN, top: ['prev()', MARGIN],
    text: page.title
  }).on('select', () => page.appendTo(navigationView))
    .appendTo(mainPage);
});

const animationPage = require('./animation');
const peoplePage = require('./people');
const trayPage = require('./tray');
const {Button, NavigationView, Page, ui} = require('tabris');

let navigationView = new NavigationView({
  left: 0, top: 0, right: 0, bottom: 0
}).appendTo(ui.contentView);

let mainPage = new Page({
  title: 'Animation Examples'
}).appendTo(navigationView);

[animationPage, peoplePage, trayPage].forEach((page) => {
  new Button({
    left: 8, top: 'prev() 8', right: 8,
    text: page.title
  }).on('select', () => page.appendTo(navigationView))
    .appendTo(mainPage);
});

Improve layout of animation example index page


import React from 'react';
import Banner from './Banner';
import { Link } from 'react-router';

export default () => (
  <Banner>
    <Banner.Element background='green'>BETA</Banner.Element>
    <Banner.Element background='blue'>
      <a
        style={{ color: '#fff' }}
        href={`mailto:${FEEDBACK_EMAIL}?subject=Tiedonohjaus-palaute`}
      >
        Anna palautetta
      </a>
    </Banner.Element>
    <Banner.Element background='#ddd'>
      <Link to='/info'>Tietoa palvelusta</Link>
    </Banner.Element>
  </Banner>
);

import React from 'react';
import Banner from './Banner';
import { Link } from 'react-router';

export default () => (
  <Banner>
    <Banner.Element background='green'>BETA</Banner.Element>
    <Banner.Element background='blue'>
      <a
        style={{ color: '#fff' }}
        href={`mailto:${FEEDBACK_EMAIL}?subject=Tiedonohjaus-palaute`}
      >
        Anna palautetta
      </a>
    </Banner.Element>
    <Banner.Element background='#ddd'>
      <Link to='/info' target='_blank' rel='noopener norefer'>
        Tietoa palvelusta
      </Link>
    </Banner.Element>
  </Banner>
);

Add target='_blank' to the info page link in the banner


'use strict';

var webpack = require('webpack');
var config = require('./webpack.config.base.js');

if (process.env.NODE_ENV !== 'test') {
  config.entry = [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/dev-server'
  ].concat(config.entry);
}

config.devtool = 'cheap-module-eval-source-map';

config.plugins = config.plugins.concat([
  new webpack.HotModuleReplacementPlugin()
]);

config.module.loaders = config.module.loaders.concat([
  {test: /\.jsx?$/, loaders: [ 'react-hot', 'babel'], exclude: /node_modules/}
]);

module.exports = config;

'use strict';

var webpack = require('webpack');
var config = require('./webpack.config.base.js');

if (process.env.NODE_ENV !== 'test') {
  config.entry = [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/dev-server'
  ].concat(config.entry);
}

config.devtool = '#cheap-module-eval-source-map';

config.plugins = config.plugins.concat([
  new webpack.HotModuleReplacementPlugin()
]);

config.module.loaders = config.module.loaders.concat([
  {test: /\.jsx?$/, loaders: [ 'react-hot', 'babel'], exclude: /node_modules/}
]);

module.exports = config;

Update webpack dev config to fix sourcemap warning


process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

const WebpackServe = require('webpack-serve');
const config = require('../config/webpack.config.base');

function createDevServer() {
  return WebpackServe({
    config,
  });
}

createDevServer();

process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

const WebpackServe = require('webpack-serve');
const config = require('../config/webpack.config.dev');

function createDevServer() {
  return WebpackServe({
    config,
    port: 3000,
  });
}

createDevServer();

Set port to dev server


document.addEventListener(
    'DOMContentLoaded',
    function () {
        var button = document.getElementById("submit_button");
        button.addEventListener(
            'click',
            function () {
                var translation = document.getElementById("processing_trans");
                this.disabled = true;
                this.innerHTML = translation.value;
            }
        );
    }
);

document.addEventListener(
    'DOMContentLoaded',
    function () {
        var button = document.getElementById("submit_button");
        button.addEventListener(
            'click',
            function () {
                var translation = document.getElementById("processing_trans");
                this.disabled = true;
                this.innerHTML = translation.value;
                return true;
            }
        );
    }
);

Fix form submit-button for Chrome


let backtrace = document.querySelector(".Flow-Debug-Exception-Backtrace-Code");
let localStorageItemIdentifier = 'Flow-Debug-Exception-Backtrace-Code-Open';

if (window.localStorage.getItem(localStorageItemIdentifier) === "true") {
    backtrace.open = true;
}

backtrace.addEventListener("click", (event) => {
    if (backtrace.open === true) {
        window.localStorage.removeItem(localStorageItemIdentifier);
    } else {
        window.localStorage.setItem(localStorageItemIdentifier, true);
    }
});

const backtrace = document.querySelector(".Flow-Debug-Exception-Backtrace-Code");
const localStorageItemIdentifier = 'Flow-Debug-Exception-Backtrace-Code-Open';

if (window.localStorage.getItem(localStorageItemIdentifier) === "true") {
    backtrace.open = true;
}

backtrace.addEventListener("click", (event) => {
    if (backtrace.open === true) {
        window.localStorage.removeItem(localStorageItemIdentifier);
    } else {
        window.localStorage.setItem(localStorageItemIdentifier, true);
    }
});

Change JS variables to constants


module.exports = {
  title: 'Form radio group',
  status: 'wip',
  context: {
    setup: {
      initScript: 'var $blockLabels = $(".gv-c-form-custom input[type=\'radio\'], .gv-c-form-custom input[type=\'checkbox\']"); new GOVUK.SelectionButtons($blockLabels);',
      openWrapper: '<form>',
      closeWrapper: '</form>'
    },
    id: 'contact',
    name: 'contact-group',
    legend: 'How do you want to be contacted?',
    hint: '',
    error: '',
    radioGroup: [{
      id: 'example-contact-by-email',
      value: 'contact-by-email',
      label: 'Email'
    }, {
      id: 'example-contact-by-phone',
      value: 'contact-by-phone',
      label: 'Phone'
    }, {
      id: 'example-contact-by-text',
      value: 'contact-by-text',
      label: 'Text'
    }]
  },
  variants: [{
    name: 'has hint',
    context: {
      hint: 'Hint text in here'
    }
  },
  {
    name: 'has error',
    context: {
      error: 'Error text in here'
    }
  },
  {
    name: 'has hint and error',
    context: {
      hint: 'Hint text in here',
      error: 'Error text in here'
    }
  }],
  arguments: ['id', 'name', 'legend', 'hint', 'error', 'radioGroup']
}

module.exports = {
  title: 'Form radio group',
  status: 'wip',
  context: {
    setup: {
      initScript: 'var $blockLabels = $(".gv-c-form-custom input[type=\'radio\']"); new GOVUK.SelectionButtons($blockLabels);',
      openWrapper: '<form>',
      closeWrapper: '</form>'
    },
    id: 'contact',
    name: 'contact-group',
    legend: 'How do you want to be contacted?',
    hint: '',
    error: '',
    radioGroup: [{
      id: 'example-contact-by-email',
      value: 'contact-by-email',
      label: 'Email'
    }, {
      id: 'example-contact-by-phone',
      value: 'contact-by-phone',
      label: 'Phone'
    }, {
      id: 'example-contact-by-text',
      value: 'contact-by-text',
      label: 'Text'
    }]
  },
  variants: [{
    name: 'has hint',
    context: {
      hint: 'Hint text in here'
    }
  },
  {
    name: 'has error',
    context: {
      error: 'Error text in here'
    }
  },
  {
    name: 'has hint and error',
    context: {
      hint: 'Hint text in here',
      error: 'Error text in here'
    }
  }],
  arguments: ['id', 'name', 'legend', 'hint', 'error', 'radioGroup']
}

Update the init script for the checkbox component


registerAction(function (node) {
    if (!settings["fixNames"]) return;
    node = node || document.body;

    toArray(node.querySelectorAll(".content > .l_profile, .lbody > .l_profile, .name > .l_profile, .foaf > .l_profile"))
        .forEach(function (node) {
            var login = node.getAttribute("href").substr(1);
            if (node.innerHTML != login) {
                node.dataset["displayName"] = node.innerHTML;
                node.innerHTML = login;
            }
        });

    // В попапе показываем оригинальное имя
    toArray(node.querySelectorAll("#popup .name > .l_profile"))
        .forEach(function (node) {
            node.innerHTML = node.dataset["displayName"];
        });
});

registerAction(function (node) {
    if (!settings["fixNames"]) return;
    node = node || document.body;

    toArray(node.querySelectorAll(".content > .l_profile, .lbody > .l_profile, .name > .l_profile, .foaf > .l_profile"))
        .forEach(function (node) {
            var login = node.getAttribute("href").substr(1);
            if (node.innerHTML != login) {
                node.dataset["displayName"] = node.innerHTML;
                node.innerHTML = login;
            }
        });

    // В попапе показываем оригинальное имя
    toArray(node.querySelectorAll("#popup .name > .l_profile"))
        .forEach(function (node) {
            var sn = node.dataset["displayName"];
            if (sn !== undefined) node.innerHTML = node.dataset["displayName"];
        });
});

FIX Неправильно обрабатывались юзеры без дисплейнейма


module.exports = function (shipit) {
  require('shipit-deploy')(shipit);

  shipit.initConfig({
    default: {
      workspace: 'tmp',
      deployTo: '/var/api/abcdef',
      deployTo: '/var/www/fussball-react',
      repositoryUrl: 'git@github.com:sping/abcd-electronic-fussball.git',
      keepReleases: 10,
      deleteOnRollback: false,
      shallowClone: false
    },
    staging: {
      branch: 'develop',
      servers: 'root@adleman.servers.sping.nl'
    },
    production: {
      branch: 'master',
      servers: 'root@adleman.servers.sping.nl'
    }
  });

  shipit.blTask('build', function() {
    return shipit.local('cd tmp/app/web && npm install && npm run build');
  });

  shipit.on('fetched', function () {
    return shipit.start('build');
  });
};

module.exports = function (shipit) {
  require('shipit-deploy')(shipit);

  shipit.initConfig({
    default: {
      workspace: 'tmp',
      dirToCopy: 'app/web/build',
      deployTo: '/var/www/abcdef-frontend',
      repositoryUrl: 'git@github.com:sping/abcd-electronic-fussball.git',
      keepReleases: 10,
      deleteOnRollback: false,
      shallowClone: false
    },
    staging: {
      branch: 'develop',
      servers: 'root@adleman.servers.sping.nl'
    },
    production: {
      branch: 'master',
      servers: 'root@adleman.servers.sping.nl'
    }
  });

  shipit.blTask('build', function() {
    return shipit.local('cd tmp/app/web && npm install && npm run build');
  });

  shipit.on('fetched', function () {
    return shipit.start('build');
  });
};

Add the correct folder for deploy.


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    environment: environment,
    baseURL: '/dashboard',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // LOG_MODULE_RESOLVER is needed for pre-1.6.0
    // ENV.LOG_MODULE_RESOLVER = true;

    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_MODULE_RESOLVER = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;

    ENV.WEBSITE_API_URL = "http://int-dev.withregard.io:3001";
    ENV.WEBSITE_DATASTORE_URL = "http://int-dev.withregard.io:3002";
  }

  if (environment === 'production') {
    ENV.WEBSITE_API_URL = "https://website-api.withregard.io";
    ENV.WEBSITE_DATASTORE_URL = "https://regard-website-datastore.azurewebsite.net";
  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    environment: environment,
    baseURL: '/dashboard',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // LOG_MODULE_RESOLVER is needed for pre-1.6.0
    // ENV.LOG_MODULE_RESOLVER = true;

    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_MODULE_RESOLVER = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;

    ENV.WEBSITE_API_URL = "http://int-dev.withregard.io:3001";
    ENV.WEBSITE_DATASTORE_URL = "http://int-dev.withregard.io:3002";
  }

  if (environment === 'production') {
    ENV.WEBSITE_API_URL = "https://website-api.withregard.io";
    ENV.WEBSITE_DATASTORE_URL = "https://website-datastore.withregard.io";
  }

  return ENV;
};

Use subdomain now it's setup


'use strict';

const service = require('feathers-mongoose');
const user-lesson-tokens = require('./user-lesson-tokens-model');
const hooks = require('./hooks');

module.exports = function() {
  const app = this;

  const options = {
    Model: user-lesson-tokens,
    paginate: {
      default: 5,
      max: 25
    }
  };

  // Initialize our service with any options it requires
  app.use('/user-lesson-tokens', service(options));

  // Get our initialize service to that we can bind hooks
  const user-lesson-tokensService = app.service('/user-lesson-tokens');

  // Set up our before hooks
  user-lesson-tokensService.before(hooks.before);

  // Set up our after hooks
  user-lesson-tokensService.after(hooks.after);
};

'use strict';

const service = require('feathers-mongoose');
const userLessonTokens = require('./user-lesson-tokens-model');
const hooks = require('./hooks');

module.exports = function() {
  const app = this;

  const options = {
    Model: user-lesson-tokens,
    paginate: {
      default: 5,
      max: 25
    }
  };

  // Initialize our service with any options it requires
  app.use('/user-lesson-tokens', service(options));

  // Get our initialize service to that we can bind hooks
  const userLessonTokensService = app.service('/user-lesson-tokens');

  // Set up our before hooks
  user-lesson-tokensService.before(hooks.before);

  // Set up our after hooks
  user-lesson-tokensService.after(hooks.after);
};

Correct syntax errors in user-lesson-tokens service


import Controller from 'ember-controller';
import $ from 'jquery';

export default Controller.extend({
  calculatePosition(trigger, content) {
    let { top, left, width, height } = trigger.getBoundingClientRect();
    let { height: contentHeight } = content.getBoundingClientRect();
    let $window = $(self.window);
    let style = {
      left: left + width,
      top: top + $window.scrollTop() + (height / 2) - (contentHeight / 2)
    };

    return { style };
  }
});

import Controller from 'ember-controller';
import $ from 'jquery';

export default Controller.extend({
  calculatePosition(trigger, content) {
    let { top, left, width, height } = trigger.getBoundingClientRect();
    let { height: contentHeight } = content.getBoundingClientRect();
    let style = {
      left: left + width,
      top: top + $(window).scrollTop() + (height / 2) - (contentHeight / 2)
    };

    return { style };
  }
});

Simplify javascript a little bit


JsHamcrest.Integration.JsUnit();
JsMockito.Integration.JsUnit();

var JsTestClass = new Class({
   Implements: [Events, Options],
   options: {
      testCase: null,
      testMethods: []
   },

   initialize: function( options ){
      this.setOptions( options );
      this.testCaseChain = new Chain();
      this.tracer;

      this.setUp();
   },

   afterAllTests: function(){
      //Abstract method, should be overwritten by test the test class
   },

   afterEachTest: function(){
      //Abstract method, should be overwritten by test the test class
   },

   beforeAllTests: function(){
      //Abstract method, should be overwritten by test the test class
   },

   beforeEachTest: function(){
      //Abstract method, should be overwritten by test the test class
   },

   //Properties
   getTestMethods: function() { return this.options.testMethods; },
   isJsTestClass: function() { return true; },

   //Protected, private helper methods
   debug : function( arguments ){
      this.tracer.debug( arguments );
   }.protect(),

   inform : function( arguments ){
      this.tracer.inform( arguments );
   }.protect(),

   setUp : function(){
      this.tracer = top.tracer;
   }.protect(),

   testMethodReady: function( error ){
      this.fireEvent( 'ready', error );
   }.protect(),

   warn : function( arguments ){
      this.tracer.warn( arguments );
   }.protect()

});

Remove JsHamCrest and JsMockito initialization.


var http = require("http");

function HttpServer(config) {
  this.port = config.port || 8181;
  this.address = config.address || "localhost";
  this.options = {
    data: null,
    resCode: 200
  };
}

HttpServer.prototype.start = function (fn) {
  this.server = http.createServer(function (req, res) {
    res.writeHead(this.options.resCode, {"Content-Type": "application/json"});
    res.end(JSON.stringify(this.options.data));
  }.bind(this)).listen(this.port, this.address, fn);
  return this;
};

HttpServer.prototype.setup = function (data, resCode) {
  this.options.data = data;
  this.options.resCode = resCode || 200;
  return this;
};

HttpServer.prototype.on = function (event, fn) {
  this.server.on(event, fn);
};

HttpServer.prototype.stop = function (fn) {
  this.server.close();
  if (fn) {
    fn();
  }
};

exports.HttpServer = HttpServer;

var http = require("http");

function HttpServer(config) {
  this.port = config.port || 8181;
  this.address = config.address || "localhost";
  this.options = {
    data: null,
    headers: {"Content-Type": "application/json"},
    returnPayload: false,
    resCode: 200
  };
}

HttpServer.prototype.start = function (fn) {

  this.server = http.createServer();

  this.server.on("request", (req, res) => {

    if (this.options.returnPayload === true) {
      this.options.data = {
        payload: "",
        method: req.method
      };

      req.addListener("data", chunk => {
        this.options.data.payload += chunk;
      });

      req.addListener("end", chunk => {
        if (chunk) {
          this.options.data.payload += chunk;
        }
        res.writeHead(this.options.resCode, this.options.headers);
        res.end(JSON.stringify(this.options.data));
      });
    } else {
      res.writeHead(this.options.resCode, this.options.headers);
      res.end(JSON.stringify(this.options.data));
    }
  }).listen(this.port, this.address, fn);

  return this;
};

HttpServer.prototype.on = function (event, fn) {
  this.server.on(event, fn);
};

HttpServer.prototype.setup = function (data, resCode, returnPayload = false) {
  this.options.data = data;
  this.options.resCode = resCode || 200;
  this.options.returnPayload = returnPayload;

  return this;
};

HttpServer.prototype.stop = function (fn) {
  this.server.close();
  if (fn) {
    fn();
  }
};

exports.HttpServer = HttpServer;

Add option to enable response with payload


/**
 * Add app view
 */

var _ = require('underscore');
var Backbone = require('backbone');

var templates = require('../../dist/');

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

module.exports = Backbone.View.extend({

  tagName : 'article',
  className : 'section',
  template : templates.addAppForm,

  $appName : null,

  events : {
    'click #addAppBtn' : 'addApp'
  },

  render : function () {
    var tmpl = _.template(this.template, { variable : 'data' });
    this.$el.html(tmpl());

    this.$appName = this.$el.find('#addAppName');

    return this;
  },

  addApp : function () {
    var name = this.$appName.val();

    var app = new App({
      name : name
    });

    if(!app.isValid()) return;

    app.save([], {
      success : function (model) {
        this.clean();
        this.trigger('new_app', model);
      }.bind(this),

      error : function (model, response) {
        alert('Error adding application');
        console.log(response.responseText);
      }.bind(this)
    });
  },

  clean : function () {
    this.$appName.val('');
  }
});

/**
 * Add app view
 */

var _ = require('underscore');
var Backbone = require('backbone');

var templates = require('../../dist/');

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

module.exports = Backbone.View.extend({

  tagName : 'article',
  className : 'section',
  template : templates.addAppForm,

  $appName : null,

  events : {
    'click #addAppBtn' : 'addApp'
  },

  render : function () {
    var tmpl = _.template(this.template, { variable : 'data' });
    this.$el.html(tmpl({ formModel : 'application' }));

    this.$appName = this.$el.find('#addAppName');

    return this;
  },

  addApp : function () {
    var name = this.$appName.val();

    var app = new App({
      name : name
    });

    if(!app.isValid()) return;

    app.save([], {
      success : function (model) {
        this.clean();
        this.trigger('new_app', model);
      }.bind(this),

      error : function (model, response) {
        alert('Error adding application');
        console.log(response.responseText);
      }.bind(this)
    });
  },

  clean : function () {
    this.$appName.val('');
  }
});

Fix new form template requirement


import axios from 'axios'

let links

export default async function AJAX({
  url,
  resource,
  id,
  method = 'GET',
  data = {},
  params = {},
  headers = {},
}) {
  try {
    const basepath = window.basepath || ''
    let response

    url = `${basepath}${url}`

    if (!links) {
      const linksRes = (response = await axios({
        url: `${basepath}/chronograf/v1`,
        method: 'GET',
      }))
      links = linksRes.data
    }

    if (resource) {
      url = id
        ? `${basepath}${links[resource]}/${id}`
        : `${basepath}${links[resource]}`
    }

    response = await axios({
      url,
      method,
      data,
      params,
      headers,
    })

    const {auth} = links

    return {
      ...response,
      auth: {links: auth},
      logoutLink: links.logout,
    }
  } catch (error) {
    const {response} = error

    const {auth} = links

    throw {...response, auth: {links: auth}, logoutLink: links.logout} // eslint-disable-line no-throw-literal
  }
}

export const get = async url => {
  try {
    return await AJAX({
      method: 'GET',
      url,
    })
  } catch (error) {
    console.error(error)
    throw error
  }
}

import axios from 'axios'

let links

const AJAX = async ({
  url,
  resource,
  id,
  method = 'GET',
  data = {},
  params = {},
  headers = {},
}) => {
  try {
    const basepath = window.basepath || ''
    let response

    url = `${basepath}${url}`

    if (!links) {
      const linksRes = (response = await axios({
        url: `${basepath}/chronograf/v1`,
        method: 'GET',
      }))
      links = linksRes.data
    }

    if (resource) {
      url = id
        ? `${basepath}${links[resource]}/${id}`
        : `${basepath}${links[resource]}`
    }

    response = await axios({
      url,
      method,
      data,
      params,
      headers,
    })

    const {auth} = links

    return {
      ...response,
      auth: {links: auth},
      logoutLink: links.logout,
    }
  } catch (error) {
    const {response} = error

    const {auth} = links

    throw {...response, auth: {links: auth}, logoutLink: links.logout} // eslint-disable-line no-throw-literal
  }
}

export const get = async url => {
  try {
    return await AJAX({
      method: 'GET',
      url,
    })
  } catch (error) {
    console.error(error)
    throw error
  }
}

export default AJAX

Update AJAX fn to ES6


var EOL = require('os').EOL;

module.exports = {
  name: 'ember-cli-mocha',

  normalizeEntityName: function() {
    // this prevents an error when the entityName is
    // not specified (since that doesn't actually matter
    // to us
  },

  afterInstall: function() {
    var addonContext = this;

    return this.addBowerPackagesToProject([
      { name: 'ember-mocha',           source: 'ember-mocha',                     target: '~0.8.6' },
      { name: 'ember-cli-test-loader', source: 'ember-cli/ember-cli-test-loader', target: '0.1.3'  },
      { name: 'ember-cli-shims',       source: 'ember-cli/ember-cli-shims',       target: '0.0.3'  }
    ]).then(function() {
      if ('removePackageFromProject' in addonContext) {
        return addonContext.removePackageFromProject('ember-cli-qunit');
      }
    });
  }
};

var EOL = require('os').EOL;

module.exports = {
  name: 'ember-cli-mocha',

  normalizeEntityName: function() {
    // this prevents an error when the entityName is
    // not specified (since that doesn't actually matter
    // to us
  },

  afterInstall: function() {
    var addonContext = this;

    return this.addBowerPackagesToProject([
      { name: 'ember-mocha',           source: 'ember-mocha',                     target: '~0.8.8' },
      { name: 'ember-cli-test-loader', source: 'ember-cli/ember-cli-test-loader', target: '0.2.2'  },
      { name: 'ember-cli-shims',       source: 'ember-cli/ember-cli-shims',       target: '0.0.6'  }
    ]).then(function() {
      if ('removePackageFromProject' in addonContext) {
        return addonContext.removePackageFromProject('ember-cli-qunit');
      }
    });
  }
};

Update blueprint to current versions.


export default from 'codemirror';

import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/searchcursor';
import 'codemirror/addon/search/jump-to-line';
import 'codemirror/addon/dialog/dialog.css';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/keymap/sublime';
import 'codemirror-graphql/results/mode';
import 'codemirror-graphql/hint';
import 'codemirror-graphql/lint';
import 'codemirror-graphql/mode';

export { default } from 'codemirror';

import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/searchcursor';
import 'codemirror/addon/search/jump-to-line';
import 'codemirror/addon/dialog/dialog.css';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/keymap/sublime';
import 'codemirror-graphql/results/mode';
import 'codemirror-graphql/hint';
import 'codemirror-graphql/lint';
import 'codemirror-graphql/mode';

Fix export syntax that is tripping compilation up for some reason


import Realm from 'realm';

export class InsurancePolicy extends Realm.Object {
  get policyNumber() {
    return `${this.policyNumberPerson} ${this.policyNumberFamily}`;
  }
}

InsurancePolicy.schema = {
  name: 'InsurancePolicy',
  primaryKey: 'id',
  properties: {
    id: 'string',
    policyNumberFamily: 'string',
    policyNumberPerson: 'string',
    type: 'string',
    discountRate: 'float',
    isActive: { type: 'bool', default: true },
    expiryDate: 'date',
    enteredBy: { type: 'User', optional: true },
    patient: { type: 'Name', optional: false },
    insuranceProvider: { type: 'InsuranceProvider', optional: false },
  },
};

export default InsurancePolicy;

import Realm from 'realm';

export class InsurancePolicy extends Realm.Object {
  get policyNumber() {
    return `${this.policyNumberPerson} ${this.policyNumberFamily}`;
  }
}

InsurancePolicy.schema = {
  name: 'InsurancePolicy',
  primaryKey: 'id',
  properties: {
    id: 'string',
    policyNumberFamily: 'string',
    policyNumberPerson: 'string',
    type: 'string',
    discountRate: 'double',
    isActive: { type: 'bool', default: true },
    expiryDate: 'date',
    enteredBy: { type: 'User', optional: true },
    patient: { type: 'Name', optional: false },
    insuranceProvider: { type: 'InsuranceProvider', optional: false },
  },
};

export default InsurancePolicy;

Add bump insurancePolicy discount rate from float to double for better precision


core.controller('RegistrationController', function ($controller, $location, $scope, $timeout, AlertService, RestApi) {

    angular.extend(this, $controller('AbstractController', {$scope: $scope}));

    $scope.registration = {
    	email: '',
    	token: ''
    };

	AlertService.create('auth/register');
    AlertService.create('auth');

	$scope.verifyEmail = function(email) {
		RestApi.anonymousGet({
			controller: 'auth',
			method: 'register?email=' + email
		}).then(function(data) {
			$scope.registration.email = '';
		});
	};

	if(typeof $location.search().token != 'undefined') {
		$scope.registration.token = $location.search().token;
	}

	$scope.register = function() {
		RestApi.anonymousGet({
			'controller': 'auth',
			'method': 'register',
			'data': $scope.registration
		}).then(function(data) {

			$scope.registration = {
		    	email: '',
		    	token: ''
		    };

		    $location.path("/");

		    $timeout(function() {
				AlertService.add(data.meta, 'auth/register');
			});

		});
	};

});

core.controller('RegistrationController', function ($controller, $location, $scope, $timeout, AlertService, RestApi) {

    angular.extend(this, $controller('AbstractController', {$scope: $scope}));

    $scope.registration = {
    	email: '',
    	token: ''
    };

	AlertService.create('auth/register');
    AlertService.create('auth');

	$scope.verifyEmail = function(email) {
		RestApi.anonymousGet({
			controller: 'auth',
			method: 'register?email=' + email
		}).then(function(data) {
			$scope.registration.email = '';

			$timeout(function() {
				AlertService.add(data.meta, 'auth/register');
			});
		});
	};

	if(typeof $location.search().token != 'undefined') {
		$scope.registration.token = $location.search().token;
	}

	$scope.register = function() {

		RestApi.anonymousGet({
			'controller': 'auth',
			'method': 'register',
			'data': $scope.registration
		}).then(function(data) {

			$scope.registration = {
		    	email: '',
		    	token: ''
		    };

		    $location.path("/");

		    $timeout(function() {
				AlertService.add(data.meta, 'auth/register');
			});

		});
	};

});

Add success alert indicating registration began and to verify email.


"use strict";

describe('Login component', () => {
    let element = undefined;
    let $rootScope = undefined;
    let $compile = undefined;
    let tpl = angular.element(`
<monad-login>

logged in

</monad-login>
    `);

    let mod = angular.module('tests.login', []).service('Authentication', function () {
        this.check = false;
        this.attempt = credentials => {
            if (credentials.username == 'test' && credentials.password == 'test') {
                this.check = true;
            }
        };
        this.status = () => this.check;
    });
    beforeEach(angular.mock.module(mod.name));

    beforeEach(inject((_$rootScope_, _$compile_) => {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
    }));

    describe('Not logged in', () => {
        it('should show the login form prior to authentication', () => {
            $rootScope.$apply(() => {
                $rootScope.credentials = {};
            });
            element = $compile(tpl)($rootScope);
            $rootScope.$digest();
            expect(element.find('form').length).toBe(1);
        });
    });
});

"use strict";

describe('Login component', () => {
    let element = undefined;
    let $rootScope = undefined;
    let $compile = undefined;

    let mod = angular.module('tests.login', []).service('Authentication', function () {
        this.check = false;
        this.attempt = credentials => {
            if (credentials.username == 'test' && credentials.password == 'test') {
                this.check = true;
            }
        };
        this.status = () => this.check;
    });
    beforeEach(angular.mock.module(mod.name));

    beforeEach(inject((_$rootScope_, _$compile_) => {
        $rootScope = _$rootScope_;
        $compile = _$compile_;
    }));

    describe('Not logged in', () => {
        it('should show the login form prior to authentication', () => {
            let tpl = angular.element(`
            <monad-login>

logged in

            </monad-login>
            `);
            element = $compile(tpl)($rootScope);
            $rootScope.$digest();
            expect(element.find('form').length).toBe(1);
        });
    });

    describe('Logged in', () => {
        it('should display a h1', () => {
            let tpl = angular.element(`
            <monad-login>

logged in

            </monad-login>
            `);
            element = $compile(tpl)($rootScope);
            $rootScope.$digest();
            element.find('input').eq(0).val('test').triggerHandler('input');
            element.find('input').eq(1).val('test').triggerHandler('input');
            element.find('form').triggerHandler('submit');
            expect(element.find('h1').length).toBe(1);
        });
    });
});

Test login with valid credentials


const paths = require('./paths');
const utils = require('../build/utils');
const vueLoaderConfig = require('../build/vue-loader.conf');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.json', '.scss'],
    alias: {
      vue$: 'vue/dist/vue.esm.js',
      '@': paths.src,
    },
  },
  externals: {
    firebase: 'firebase',
    vue: 'Vue',
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig,
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]'),
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
        },
      },
    ],
  },
  plugins: [
    new FriendlyErrorsPlugin(),
  ],
};

const paths = require('./paths');
const utils = require('../build/utils');
const vueLoaderConfig = require('../build/vue-loader.conf');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.scss'],
    alias: {
      '@': paths.src,
    },
  },
  externals: {
    firebase: 'firebase',
    vue: 'Vue',
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig,
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]'),
        },
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
        },
      },
    ],
  },
  plugins: [
    new FriendlyErrorsPlugin(),
  ],
};

Remove json and an unused alias


var React = require("react");
var Router = require('react-router');
var App = require("./components/App");
var WallView = require("./components/WallView/WallView");
var NotFoundView = require("./components/NotFoundView/NotFoundView");

var DefaultRoute = Router.DefaultRoute;
var Route = Router.Route;
var RouteHandler = Router.RouteHandler;
var NotFoundRoute = Router.NotFoundRoute;

var routes = (
  <Route name="app" path="/" handler={App}>
    <DefaultRoute handler={WallView} />
    <NotFoundRoute handler={NotFoundView} />
  </Route>
);

Router.run(routes, function (Handler) {
  React.render(<Handler/>, document.getElementById('main'));
});

var React = require("react");
var Router = require('react-router');
var App = require("./components/App");
var WallView = require("./components/WallView/WallView");
var NotFoundView = require("./components/NotFoundView/NotFoundView");

var DefaultRoute = Router.DefaultRoute;
var Route = Router.Route;
var RouteHandler = Router.RouteHandler;
var NotFoundRoute = Router.NotFoundRoute;

var injectTapEventPlugin = require("react-tap-event-plugin");
injectTapEventPlugin();

var routes = (
  <Route name="app" path="/" handler={App}>
    <DefaultRoute handler={WallView} />
    <NotFoundRoute handler={NotFoundView} />
  </Route>
);

Router.run(routes, function (Handler) {
  React.render(<Handler/>, document.getElementById('main'));
});

Add in module to use react materialize-ui


let {setExamSubmitting, setDirty, isUnsubmitted} =
    require('../examSubmission.js');

test('check set exam submitting', () => {
  expect(setExamSubmitting()).toBe(true);
});

test('check set dirty', () => {
  expect(setDirty()).toBe(true);
});

test('isUnsubmitted when exam has not been submitted but filled in', () =>{
  const reply = 'It looks like you have not submitted your exam';
  expect(isUnsubmitted('event', false, true)).toBe(reply);
})

test('isUnsubmitted when exam has been submitted and filled in', () =>{
  expect(isUnsubmitted('event',true, true )).toBe(undefined);
})

test('isUnsubmitted when exam has not been submitted and not filled in', () =>{
  expect(isUnsubmitted('event', false, false)).toBe(undefined);
})

test('isUnsubmitted when exam has been submitted and not filled in', () =>{
  expect(isUnsubmitted('event', true, false)).toBe(undefined);
})

const {setExamSubmitting, setDirty, isUnsubmitted} =
    require('../examSubmission.js');

test('check set exam submitting', () => {
  expect(setExamSubmitting()).toBe(true);
});

test('check set dirty', () => {
  expect(setDirty()).toBe(true);
});

test('isUnsubmitted when exam has not been submitted but filled in', () =>{
  const reply = 'It looks like you have not submitted your exam';
  expect(isUnsubmitted('event', false, true)).toBe(reply);
});

test('isUnsubmitted when exam has been submitted and filled in', () =>{
  expect(isUnsubmitted('event', true, true )).toBe(undefined);
});

test('isUnsubmitted when exam has not been submitted and not filled in', () =>{
  expect(isUnsubmitted('event', false, false)).toBe(undefined);
});

test('isUnsubmitted when exam has been submitted and not filled in', () =>{
  expect(isUnsubmitted('event', true, false)).toBe(undefined);
});

Update to comply with linter


// ==UserScript==
// @name           Turn off age gate on Amazon.co.jp
// @namespace      curipha
// @description    Click "I'm over 18" automatically
// @include        https://www.amazon.co.jp/gp/product/*
// @include        https://www.amazon.co.jp/dp/*
// @include        https://www.amazon.co.jp/*/dp/*
// @exclude        https://www.amazon.co.jp/ap/*
// @exclude        https://www.amazon.co.jp/mn/*
// @exclude        https://www.amazon.co.jp/clouddrive*
// @version        0.5.2
// @grant          none
// @noframes
// ==/UserScript==

(function() {
  'use strict';

  if (document.title === '警告：アダルトコンテンツ') {
    const a = document.body.querySelector('center a[href*="black-curtain-redirect.html"]');
    if (a) {
      location.href = a.href;
    }
  }
})();

// ==UserScript==
// @name           Turn off age gate on Amazon.co.jp
// @namespace      curipha
// @description    Click "I'm over 18" automatically
// @include        https://www.amazon.co.jp/gp/product/*
// @include        https://www.amazon.co.jp/gp/browse.html*
// @include        https://www.amazon.co.jp/dp/*
// @include        https://www.amazon.co.jp/*/dp/*
// @exclude        https://www.amazon.co.jp/ap/*
// @exclude        https://www.amazon.co.jp/mn/*
// @exclude        https://www.amazon.co.jp/clouddrive*
// @version        0.5.2
// @grant          none
// @noframes
// ==/UserScript==

(function() {
  'use strict';

  if (document.title === '警告：アダルトコンテンツ') {
    const a = document.body.querySelector('center a[href*="black-curtain-redirect.html"]');
    if (a) {
      location.href = a.href;
    }
  }
})();

Change @include settings so that the script is not run on all pages


'use strict';

// Like Lodash merge() but faster and does not mutate input
const deepMerge = function (objA, objB, ...objects) {
  if (objects.length > 0) {
    const newObjA = deepMerge(objA, objB);
    return deepMerge(newObjA, ...objects);
  }

  if (!isObjectTypes(objA, objB)) { return objB; }

  const rObjB = Object.entries(objB).map(([objBKey, objBVal]) => {
    const newObjBVal = deepMerge(objA[objBKey], objBVal);
    return { [objBKey]: newObjBVal };
  });
  return Object.assign({}, objA, ...rObjB);
};

const isObjectTypes = function (objA, objB) {
  return objA && objA.constructor === Object &&
    objB && objB.constructor === Object;
};

module.exports = {
  deepMerge,
};

'use strict';

// Like Lodash merge() but faster and does not mutate input
const deepMerge = function (objA, objB, ...objects) {
  if (objects.length > 0) {
    const newObjA = deepMerge(objA, objB);
    return deepMerge(newObjA, ...objects);
  }

  if (objB === undefined) { return objA; }

  if (!isObjectTypes(objA, objB)) { return objB; }

  const rObjB = Object.entries(objB).map(([objBKey, objBVal]) => {
    const newObjBVal = deepMerge(objA[objBKey], objBVal);
    return { [objBKey]: newObjBVal };
  });
  return Object.assign({}, objA, ...rObjB);
};

const isObjectTypes = function (objA, objB) {
  return objA && objA.constructor === Object &&
    objB && objB.constructor === Object;
};

module.exports = {
  deepMerge,
};

Fix deepMerge() with undefined arguments


/*jslint browser: true, undef: true *//*global Ext*/
Ext.define('SlateAdmin.store.people.UserClasses', {
    extend: 'Ext.data.Store',
    requires: [
        'SlateAdmin.proxy.API'
    ],

    idProperty: 'value',
    fields: [{
        name: 'value',
        type: 'string'
    }],

    proxy: {
        type: 'slateapi',
        url: '/people/*classes',
        extraParams: {
            interface: 'user'
        },
        reader: {
            type: 'json',
            transform: function(response) {
                var records = [];

                Ext.iterate(response.data, function(key, value) {
                    records.push({
                        value: value
                    });
                });

                return records;
            }
        }
    }
});

/*jslint browser: true, undef: true *//*global Ext*/
Ext.define('SlateAdmin.store.people.UserClasses', {
    extend: 'Ext.data.Store',
    requires: [
        'SlateAdmin.proxy.API'
    ],

    idProperty: 'value',
    fields: [{
        name: 'value',
        type: 'string'
    }],

    proxy: {
        type: 'slateapi',
        url: '/people/*classes',
        extraParams: {
            interface: 'user'
        },
        reader: {
            type: 'json',
            transform: function(response) {
                return Ext.Array.map(response.data, function (cls) {
                    return {
                        value: cls
                    }
                });
            }
        }
    }
});

Use Array.map to transform classes response


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.baseURL = '/liquid-fire-reveal';
  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.rootURL = '/liquid-fire-reveal/';
  }

  return ENV;
};

Update rootUrl for dummy app


/**
 * 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/.
 */

var name = 'cu-patcher-ui';

module.exports = {
  type: 'library',
  path: __dirname,
  name: name,
  bundle: {
    copy: [
      'src/**/!(*.js|*.jsx|*.ts|*.tsx|*.ui|*.styl|*.scss)',
      'src/include/**'
    ]
  }
};

/**
 * 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/.
 */

var name = 'cu-patcher-ui';

module.exports = {
  type: 'module',
  path: __dirname,
  name: name,
  bundle: {
    copy: [
      'src/**/!(*.js|*.jsx|*.ts|*.tsx|*.ui|*.styl|*.scss)',
      'src/include/**'
    ]
  }
};

Change build type to module


jest.mock('../../../models/meeting');
const { toggleRegistration } = require('../meeting');
const { getActiveGenfors, toggleRegistrationStatus } = require('../../../models/meeting');
const { generateSocket, generateGenfors } = require('../../../utils/generateTestData');

describe('toggleRegistration', () => {
  beforeEach(() => {
    getActiveGenfors.mockImplementation(async () => generateGenfors());
    toggleRegistrationStatus.mockImplementation(
      async (genfors, registrationOpen) =>
        generateGenfors({ ...genfors, registrationOpen: !registrationOpen }));
  });

  const generateData = data => (Object.assign({
    registrationOpen: true,
  }, data));

  it('emits a toggle action that closes registration', async () => {
    const socket = generateSocket();
    await toggleRegistration(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });

  it('emits a toggle action that opens registration', async () => {
    const socket = generateSocket();
    await toggleRegistration(socket, generateData({ registrationOpen: false }));

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });
});

jest.mock('../../../models/meeting');
const { toggleRegistration } = require('../meeting');
const { getActiveGenfors, updateGenfors } = require('../../../models/meeting');
const { generateSocket, generateGenfors } = require('../../../utils/generateTestData');

describe('toggleRegistration', () => {
  beforeEach(() => {
    getActiveGenfors.mockImplementation(async () => generateGenfors());
    updateGenfors.mockImplementation(async (genfors, data) =>
      ({ ...genfors, ...data, pin: genfors.pin }));
  });

  const generateData = data => (Object.assign({
    registrationOpen: true,
  }, data));

  it('emits a toggle action that closes registration', async () => {
    const socket = generateSocket();
    await toggleRegistration(socket, generateData());

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });

  it('emits a toggle action that opens registration', async () => {
    const socket = generateSocket();
    await toggleRegistration(socket, generateData({ registrationOpen: false }));

    expect(socket.emit.mock.calls).toMatchSnapshot();
    expect(socket.broadcast.emit.mock.calls).toEqual([]);
  });
});

Update tests to mock generic helper function


if (navigator.userAgent.indexOf('Android') >= 0) {
	log = console.log.bind(console)

	log("Android detected")
	exports.core.vendor = "google"
	exports.core.device = 2
	exports.core.os = "android"

	exports.core.keyCodes = {
		4: 'Back',
		13: 'Select',
		27: 'Back',
		37: 'Left',
		32: 'Space',
		38: 'Up',
		39: 'Right',
		40: 'Down',
		48: '0',
		49: '1',
		50: '2',
		51: '3',
		52: '4',
		53: '5',
		54: '6',
		55: '7',
		56: '8',
		57: '9',
		179: 'Pause',
		112: 'Red',
		113: 'Green',
		114: 'Yellow',
		115: 'Blue'
	}

	if (window.cordova) {
		document.addEventListener("backbutton", function(e) {
			var event = new KeyboardEvent("keydown", { bubbles : true });
			Object.defineProperty(event, 'keyCode', { get : function() { return 27; } })
			document.dispatchEvent(event);
		}, false);
	} else {
		log("'cordova' not defined. 'Back' button will be unhandable. It looks like you forget to include 'cordova.js'")
	}

	log("Android initialized")
}

if (navigator.userAgent.indexOf('Android') >= 0) {
	log = console.log.bind(console)

	log("Android detected")
	exports.core.vendor = "google"
	exports.core.device = 2
	exports.core.os = "android"

	exports.core.keyCodes = {
		4: 'Back',
		13: 'Select',
		37: 'Left',
		32: 'Space',
		38: 'Up',
		39: 'Right',
		40: 'Down',
		48: '0',
		49: '1',
		50: '2',
		51: '3',
		52: '4',
		53: '5',
		54: '6',
		55: '7',
		56: '8',
		57: '9',
		179: 'Pause',
		112: 'Red',
		113: 'Green',
		114: 'Yellow',
		115: 'Blue'
	}

	if (window.cordova) {
		document.addEventListener("backbutton", function(e) {
			var event = new KeyboardEvent("keydown", { bubbles : true });
			Object.defineProperty(event, 'keyCode', { get : function() { return 4; } })
			document.dispatchEvent(event);
		}, false);
	} else {
		log("'cordova' not defined. 'Back' button will be unhandable. It looks like you forget to include 'cordova.js'")
	}

	log("Android initialized")
}

Use keyCode '4' instead of '27' to prevent conflicts in platforms where '27' key code works


angular.module('myApp')
.controller('UserCtrl', ['userService', '$scope', '$rootScope', 'ctrlPanelService',
 function(userService, $scope, $rootScope, ctrlPanelService) {

 	// initialize data
 	$scope.users = ctrlPanelService.users;

	// listeners waiting for control panel changes
  $rootScope.$on('updateUsers', function() {
    $scope.users = ctrlPanelService.users;
  });

}]);

angular.module('myApp')
.controller('UserCtrl', ['userService', '$scope', '$rootScope', 'ctrlPanelService',
 function(userService, $scope, $rootScope, ctrlPanelService) {

 	// initialize data
 	$scope.users = userService.users;

	// listeners waiting for control panel changes
  $rootScope.$on('usersUpdated', function() {
    $scope.users = userService.users;
  });

}]);

Update UserCtrl to user userService values


const { Listener } = require('discord-akairo');

class MessageReactionAddListener extends Listener {
	constructor() {
		super('messageReactionAdd', {
			eventName: 'messageReactionAdd',
			emitter: 'client',
			category: 'client'
		});
	}

	async exec(reaction, user) {
		if (reaction.emoji.name === '⭐') {
			const starboard = this.client.starboards.get(reaction.message.guild.id);
			const error = await starboard.add(reaction.message, user);
			if (error) {
				reaction.message.channel.send(`${user}, ${error.message}`);
			}
		}
	}
}

module.exports = MessageReactionAddListener;

const { Listener } = require('discord-akairo');

class MessageReactionAddListener extends Listener {
	constructor() {
		super('messageReactionAdd', {
			eventName: 'messageReactionAdd',
			emitter: 'client',
			category: 'client'
		});
	}

	async exec(reaction, user) {
		if (reaction.emoji.name === '⭐') {
			const starboard = this.client.starboards.get(reaction.message.guild.id);
			const error = await starboard.add(reaction.message, user);
			if (error) {
				reaction.remove(user);
				reaction.message.channel.send(`${user}, ${error.message}`);
			}
		}
	}
}

module.exports = MessageReactionAddListener;

Remove reaction if there is some error


define([
    "jquery",
    "underscore",
    "parse",
    "backbone",
    "marionette"
], function( $, _, Parse, Backbone, Marionette ) {

    var userChannel = Backbone.Wreqr.radio.channel('user');

    Backbone.Wreqr.radio.reqres.setHandler("user", "testing", function(id) {
        return "deez" + id;
    });

    console.log(userChannel.reqres.request('testing', 'anid'));

    return userChannel;
} );

define([
    "jquery",
    "underscore",
    "parse",
    "backbone",
    "marionette"
], function( $, _, Parse, Backbone, Marionette ) {

    var userChannel = Backbone.Wreqr.radio.channel('user');

    return userChannel;
} );

Remove dumb logging stuff in initial radio channel


AFRAME.registerComponent('death-listener', {
 schema: {
    characterId: {default: undefined}
  },

  init: function () {
    const el = this.el;
    const data = this.data;
    window.addEventListener('characterDestroyed', function (e) {
      if(data.characterId === e.detail.characterId) {
        if(el.components.spawner) {
          el.components.spawner.pause();
          setTimeout(() => {
            el.components.spawner.play();
          }, 5000)
        }

      }
      // const characterId = e.detail.characterId;
      // console.log('character killed:', characterId);

      // const blackPlane = document.createElement('a-plane');
      // blackPlane.setAttribute('position', '0 0 -0.2');
      // blackPlane.setAttribute('material', 'shader', 'flat');
      // el.appendChild(blackPlane);

      // const opacityAnimation = document.createElement('a-animation');
      // opacityAnimation.setAttribute('attribute', 'material.opacity');
      // opacityAnimation.setAttribute('from', 0);
      // opacityAnimation.setAttribute('to', 1);
      // opacityAnimation.setAttribute('dur', 1500);
      // opacityAnimation.setAttribute('easing', 'ease-out');

      // blackPlane.appendChild(opacityAnimation);
      // el.appendChild(blackPlane);

      // setTimeout(() => {
      //   el.removeChild(blackPlane)
      // }, 3000);
    });
  }
});

AFRAME.registerComponent('death-listener', {
 schema: {
    characterId: {default: undefined}
  },

  init: function () {
    const el = this.el;
    const data = this.data;
    window.addEventListener('characterDestroyed', function (e) {
      if(data.characterId === e.detail.characterId) {

        if(el.components.spawner) {
          el.components.spawner.pause();
          setTimeout(() => {
            el.components.spawner.play();
          }, 5000)
        }

        if(el.components['data-emitter']) {
          el.components['data-emitter'].pause();
          setTimeout(() => {
            el.components['data-emitter'].play();
          }, 5000)
        }
      }
      // const characterId = e.detail.characterId;
      // console.log('character killed:', characterId);

      // const blackPlane = document.createElement('a-plane');
      // blackPlane.setAttribute('position', '0 0 -0.2');
      // blackPlane.setAttribute('material', 'shader', 'flat');
      // el.appendChild(blackPlane);

      // const opacityAnimation = document.createElement('a-animation');
      // opacityAnimation.setAttribute('attribute', 'material.opacity');
      // opacityAnimation.setAttribute('from', 0);
      // opacityAnimation.setAttribute('to', 1);
      // opacityAnimation.setAttribute('dur', 1500);
      // opacityAnimation.setAttribute('easing', 'ease-out');

      // blackPlane.appendChild(opacityAnimation);
      // el.appendChild(blackPlane);

      // setTimeout(() => {
      //   el.removeChild(blackPlane)
      // }, 3000);
    });
  }
});

Enable death listener to disable data-emitter


'use strict'

var shimmer = require('shimmer')
var asyncState = require('../../async-state')

var SERVER_FNS = ['on', 'addListener']

module.exports = function (http, client) {
  client.logger.trace('shimming http.Server.prototype functions:', SERVER_FNS)

  shimmer.massWrap(http.Server.prototype, SERVER_FNS, function (orig, name) {
    return function (event, listener) {
      if (event === 'request' && typeof listener === 'function') return orig.call(this, event, onRequest)
      else return orig.apply(this, arguments)

      function onRequest (req, res) {
        client.logger.trace('intercepted call to http.Server.prototype.%s', name)

        var trans = client.startTransaction(req.method + ' ' + req.url)
        trans.type = 'web.http'
        asyncState.req = req
        asyncState.trans = req.__opbeat_trans = trans

        res.once('finish', function () {
          trans.result = res.statusCode
          client.logger.trace('[%s] ending transaction', trans._uuid)
          trans.end()
        })

        listener.apply(this, arguments)
      }
    }
  })

  return http
}

'use strict'

var shimmer = require('shimmer')
var asyncState = require('../../async-state')

var SERVER_FNS = ['on', 'addListener']

module.exports = function (http, client) {
  client.logger.trace('shimming http.Server.prototype functions:', SERVER_FNS)

  shimmer.massWrap(http.Server.prototype, SERVER_FNS, function (orig, name) {
    return function (event, listener) {
      if (event === 'request' && typeof listener === 'function') return orig.call(this, event, onRequest)
      else return orig.apply(this, arguments)

      function onRequest (req, res) {
        client.logger.trace('intercepted call to http.Server.prototype.%s', name)

        var trans = client.startTransaction(req.method + ' ' + req.url, 'web.http')
        asyncState.req = req
        asyncState.trans = req.__opbeat_trans = trans

        res.once('finish', function () {
          trans.result = res.statusCode
          client.logger.trace('[%s] ending transaction', trans._uuid)
          trans.end()
        })

        listener.apply(this, arguments)
      }
    }
  })

  return http
}

Set HTTP transaction type during construction


PromiseAllSync = {
  extend: function(PromiseClass) {
    var emptyPromise = PromiseClass.resolve || PromiseClass.when;
    PromiseClass.allSync = function(collection, fn, unfn) {
      var stack = [];
      return collection.reduce(function(promise, item) {
        return promise.then(function() {
          var nextPromise = fn ? fn(item) : item;
          return nextPromise.then(function() {
            if(unfn) stack.push(item);
          });
        });
      }, emptyPromise.apply(PromiseClass))
        .catch(function(e) {
          if(unfn) while(stack.length) unfn(stack.pop());
          return PromiseClass.reject(e);
        });
    }
  }
};

PromiseAllSync = {
  extend: function(PromiseClass) {
    var emptyPromise = PromiseClass.resolve || PromiseClass.when;
    PromiseClass.allSync = function(collection, fn, unfn) {
      var stack = [];
      return collection.reduce(function(promise, item) {
        return promise.then(function() {
          var nextPromise = fn ? fn(item) : item;
          return nextPromise.then(function() {
            if(unfn) stack.push(item);
          });
        });
      }, emptyPromise.apply(PromiseClass))
        .catch(function(e) {
          if(unfn) {
            stack.reduceRight(function(promise, item) {
              return promise.then(function() {
                return unfn(item);
              });
            }, emptyPromise.apply(PromiseClass));
          }
          return PromiseClass.reject(e);
        });
    }
  }
};

Support undo functions that return a promise


import { createStore, applyMiddleware, compose } from 'redux';
import { reduxReactRouter } from 'redux-router';
import thunk from 'redux-thunk';
import createHistory from 'history/lib/createBrowserHistory';
import routes from '../routes';
import createLogger from 'redux-logger';
import rootReducer from '../reducers';

const finalCreateStore = compose(
  applyMiddleware(thunk),
  applyMiddleware(createLogger()),
  reduxReactRouter({ routes, createHistory })
)(createStore);

export default function configureStore(initialState) {
  const store = finalCreateStore(rootReducer, initialState);

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers');
      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
}

const finalCreateServerStore = compose(
  applyMiddleware(thunk),
  reduxReactRouter({ routes, createHistory })
)(createStore);

export default function configureServerStore(initialState) {
  return finalCreateServerStore(rootReducer, initialState);
}

Add function to configure store for server-side rendering


define(function (require) {

    return function (seriesType, ecModel) {
        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
            var data = seriesModel.getData();
            var coordSys = seriesModel.coordinateSystem;

            if (coordSys) {
                var dims = coordSys.dimensions;

                if (coordSys.type === 'singleAxis') {
                    data.each(dims[0], function (x, idx) {
                        // Also {Array.<number>}, not undefined to avoid if...else... statement
                        data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
                    });
                }
                else {
                    data.each(dims, function (x, y, idx) {
                        // Also {Array.<number>}, not undefined to avoid if...else... statement
                        data.setItemLayout(
                            idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
                        );
                    }, true);
                }
            }
        });
    };
});

define(function (require) {

    return function (seriesType, ecModel) {
        ecModel.eachSeriesByType(seriesType, function (seriesModel) {
            var data = seriesModel.getData();
            var coordSys = seriesModel.coordinateSystem;

            if (coordSys) {
                var dims = coordSys.dimensions;

                if (dims.length === 1) {
                    data.each(dims[0], function (x, idx) {
                        // Also {Array.<number>}, not undefined to avoid if...else... statement
                        data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
                    });
                }
                else if (dims.length === 2) {
                    data.each(dims, function (x, y, idx) {
                        // Also {Array.<number>}, not undefined to avoid if...else... statement
                        data.setItemLayout(
                            idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
                        );
                    }, true);
                }
            }
        });
    };
});

Use dimension shape instead of coordinate system type


'use strict';

angular.module('app.controllers')
    .controller('HomeController', ['$scope', '$state', 'fileModel', 'imageModel', function HomeController($scope, $state, fileModel, imageModel) {

        // private
        var _this = {
            onCreate: function() {
                if(imageModel.getImages().length === 0) {
                    $scope.loading(true);

                    imageModel.loadImages()
                        .then(function() {
                            $scope.loading(false);
                        });
                }
            }
        };

        // public
        $scope.model = imageModel;

        /**
         * Triggered when the user presses the upload button in the navigation bar.
         */
        $scope.upload = function(file) {
            if(file && file.length) {
                // Set the file
                fileModel.setFile(file[0]);

                // Navigate to the upload state
                $state.go('upload');
            }
        };

        $scope.loadMore = function() {
            if(!$scope._data.loading) {
                $scope.loading(true);

                imageModel.loadMore()
                    .then(function() {
                        $scope.loading(false);
                    });
            }
        };

        // Initialize the controller
        _this.onCreate();
    }]);

'use strict';

angular.module('app.controllers')
    .controller('HomeController', ['$scope', '$state', 'fileModel', 'imageModel', function HomeController($scope, $state, fileModel, imageModel) {

        // private
        var _this = {
            onCreate: function() {
                if(imageModel.getImages().length === 0) {
                    $scope.loading(true);

                    imageModel.loadImages()
                        .finally(function() {
                            $scope.loading(false);
                        });
                }
            }
        };

        // public
        $scope.model = imageModel;

        /**
         * Triggered when the user presses the upload button in the navigation bar.
         */
        $scope.upload = function(file) {
            if(file && file.length) {
                // Set the file
                fileModel.setFile(file[0]);

                // Navigate to the upload state
                $state.go('upload');
            }
        };

        $scope.loadMore = function() {
            if(!$scope._data.loading) {
                $scope.loading(true);

                imageModel.loadMore()
                    .finally(function() {
                        $scope.loading(false);
                    });
            }
        };

        // Initialize the controller
        _this.onCreate();
    }]);

Stop the loader in the finally to make sure it allways stops


module.exports.in  = function(_msg) {
  if (_msg.properties.contentType === 'application/json') {
    try {
      return JSON.parse(_msg.content.toString());
    } catch(e) {}
  }

  return _msg.content.toString();
};

module.exports.out = function(content, options) {
  if (content) {
    content = JSON.stringify(content);
    options.contentType = 'application/json';
  }

  return new Buffer(content);
};

module.exports.in  = function(_msg) {
  if (_msg.properties.contentType === 'application/json') {
    try {
      return JSON.parse(_msg.content.toString());
    } catch(e) {}
  }

  return _msg.content.toString();
};

module.exports.out = function(content, options) {
  if (content !== undefined) {
    content = JSON.stringify(content);
    options.contentType = 'application/json';
  }

  return new Buffer(content);
};

Check if content is different from undefined


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

import './SocialLink.css';

class SocialLink extends Component {

  static propTypes = {
    name: PropTypes.string.isRequired,
    url: PropTypes.string.isRequired,
    icon: PropTypes.string.isRequired,
    iconHover: PropTypes.string.isRequired,
  };

  state = {
    isHovering: false,
  };

  handleMouseOver() {
    this.setState({ isHovering: true });
  }

  handleMouseOut() {
    this.setState({ isHovering: false });
  }

  render() {
    const { isHovering } = this.state;
    const { name, url, icon, iconHover } = this.props;

    return (

           this.handleMouseOver()}
            onMouseOut={() => this.handleMouseOut()}
          />

    );
  }

}

export default SocialLink;

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

import './SocialLink.css';

class SocialLink extends Component {

  static propTypes = {
    name: PropTypes.string.isRequired,
    url: PropTypes.string.isRequired,
    icon: PropTypes.string.isRequired,
    iconHover: PropTypes.string.isRequired,
  };

  state = {
    isHovering: false,
  };

  handleMouseOver() {
    this.setState({ isHovering: true });
  }

  handleMouseOut() {
    this.setState({ isHovering: false });
  }

  render() {
    const { isHovering } = this.state;
    const { name, url, icon, iconHover } = this.props;

    return (
      <li className="social-link">
        <a
          className="social-link"
          href={url}
          onMouseOver={() => this.handleMouseOver()}
          onMouseOut={() => this.handleMouseOut()}
        >
          <img
            className="social-icon"
            src={isHovering ? iconHover : icon}
            alt={name}
          />
        </a>
      </li>
    );
  }

}

export default SocialLink;

Apply colored image when you hover the link


export default function bars (data, x, y) {
  x = x || 'x'
  y = y || 'y'

  return {
    type: 'vegalite',
    data: {
      values: data
    },
    mark: 'bar',
    encoding: {
      x: {
        field: x,
        type: 'qualitative'
      },
      y: {
        field: y,
        type: 'quantitative'
      }
    }
  }
}

import convertTableToArray from '../types/convertTableToArray'

export default function bars (data, x, y) {
  x = x || 'x'
  y = y || 'y'

  return {
    type: 'vegalite',
    data: {
      values: convertTableToArray(data)
    },
    mark: 'bar',
    encoding: {
      x: {
        field: x,
        type: 'qualitative'
      },
      y: {
        field: y,
        type: 'quantitative'
      }
    }
  }
}

Add conversion to bar plot


import { combineReducers } from "redux";

import { JOB_START, JOB_CREATED } from "./actions";

function views(state = {}, action) {
    switch (action.type) {

        case JOB_START:
            let views = {};

            // XXX need to change outrigger to return view_id instead of sink_id
            action.views.forEach(view => {
                views[view.sink_id] = view;
            });

            return views;
        default:
            return state;

    }
}

function job_id(state = null, action) {
    switch (action.type) {

        case JOB_CREATED:
            return action.job_id;
        default:
            return state;

    }
}

export default combineReducers({
    views,
    job_id
});

import { combineReducers } from "redux";

import { JOB_START, JOB_CREATED } from "./actions";

function views(state = {}, action) {
    switch (action.type) {
        // reset views on new job
        case JOB_CREATED:
            return {};

        case JOB_START:
            let views = {};

            // XXX need to change outrigger to return view_id instead of sink_id
            action.views.forEach(view => {
                views[view.sink_id] = view;
            });

            return views;

        default:
            return state;

    }
}

function job_id(state = null, action) {
    switch (action.type) {

        case JOB_CREATED:
            return action.job_id;
        default:
            return state;

    }
}

export default combineReducers({
    views,
    job_id
});

Clear views array on job_create event


// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

cur_frm.add_fetch("customer", "customer_name", "customer_name")
cur_frm.add_fetch("supplier", "supplier_name", "supplier_name")

cur_frm.add_fetch("item_code", "item_name", "item_name")
cur_frm.add_fetch("item_code", "description", "description")
cur_frm.add_fetch("item_code", "item_group", "item_group")
cur_frm.add_fetch("item_code", "brand", "brand")

cur_frm.cscript.onload = function() {
	cur_frm.set_query("item_code", function() {
		return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"})
	});
};

frappe.ui.form.on("Serial No", "refresh", function(frm) {
	frm.toggle_enable("item_code", frm.doc.__islocal);

	if(frm.doc.status == "Sales Returned" && frm.doc.warehouse)
		cur_frm.add_custom_button(__('Set Status as Available'), cur_frm.cscript.set_status_as_available);
});

cur_frm.cscript.set_status_as_available = function() {
	cur_frm.set_value("status", "Available");
	cur_frm.save()
}

// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

cur_frm.add_fetch("customer", "customer_name", "customer_name")
cur_frm.add_fetch("supplier", "supplier_name", "supplier_name")

cur_frm.add_fetch("item_code", "item_name", "item_name")
cur_frm.add_fetch("item_code", "description", "description")
cur_frm.add_fetch("item_code", "item_group", "item_group")
cur_frm.add_fetch("item_code", "brand", "brand")

cur_frm.cscript.onload = function() {
	cur_frm.set_query("item_code", function() {
		return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"})
	});
};

frappe.ui.form.on("Serial No", "refresh", function(frm) {
	frm.toggle_enable("item_code", frm.doc.__islocal);

	if(frm.doc.status == "Sales Returned" && frm.doc.warehouse)
		cur_frm.add_custom_button(__('Set Status as Available'), function() {
			cur_frm.set_value("status", "Available");
			cur_frm.save();
		});
});

Set status button in serial no


import Ember from 'ember';
import SelectPickerMixin from 'ember-cli-select-picker/mixins/select-picker';

var I18nProps = (Ember.I18n && Ember.I18n.TranslateableProperties) || {};

var SelectPickerComponent = Ember.Component.extend(
  SelectPickerMixin, I18nProps, {

  selectAllLabel:  'All',
  selectNoneLabel: 'None',

  nativeMobile: true,

  classNames: ['select-picker'],

  didInsertElement: function() {
    var eventName = 'click.' + this.get('elementId');
    var _this = this;
    $(document).on(eventName, function (e) {
      if (_this.get('keepDropdownOpen')) {
        _this.set('keepDropdownOpen', false);
        return;
      }
      if (_this.element && !$.contains(_this.element, e.target)) {
        _this.set('showDropdown', false);
      }
    });
  },

  willDestroyElement: function() {
    $(document).off('.' + this.get('elementId'));
  },

  actions: {
    showHide: function () {
      this.toggleProperty('showDropdown');
    }
  }
});

export default SelectPickerComponent;

import Ember from 'ember';
import SelectPickerMixin from 'ember-cli-select-picker/mixins/select-picker';

var I18nProps = (Ember.I18n && Ember.I18n.TranslateableProperties) || {};

var SelectPickerComponent = Ember.Component.extend(
  SelectPickerMixin, I18nProps, {

  selectAllLabel:  'All',
  selectNoneLabel: 'None',

  nativeMobile: true,

  classNames: ['select-picker'],

  setupDom: Ember.observer(
    'didInsertElement',
    function() {
      var eventName = 'click.' + this.get('elementId');
      var _this = this;
      $(document).on(eventName, function (e) {
        if (_this.get('keepDropdownOpen')) {
          _this.set('keepDropdownOpen', false);
          return;
        }
        if (_this.element && !$.contains(_this.element, e.target)) {
          _this.set('showDropdown', false);
        }
      });
    }
  ),

  teardownDom: Ember.observer(
    'willDestroyElement',
    function() {
      $(document).off('.' + this.get('elementId'));
    }
  ),

  actions: {
    showHide: function () {
      this.toggleProperty('showDropdown');
    }
  }
});

export default SelectPickerComponent;

Use Ember.observer over named properties


'use strict';

var Backbone = require('backbone');
var BusinessModel = Backbone.Model.extend({
	idAttribute: 'id',
	parse: function(data) {
        var hash = {};
        hash.name = data.name;
        hash.id = data.id;
        hash.address = data.location.display_address.join(' ');
        hash.rating = data.rating;

        if (data.location.coordinate && data.location.coordinate !== 'undefined') {
            hash.coordinates = {
                lat: data.location.coordinate.latitude,
                lng: data.location.coordinate.longitude
            };
        }

        if (data.categories && data.categories !== 'undefined') {
        	hash.specificCategory = data.categories[0][0];
        } else {
        	hash.specificCategory = '';
        }
        return hash;
	}
});

module.exports = BusinessModel;

'use strict';

var Backbone = require('backbone');
var BusinessModel = Backbone.Model.extend({
	idAttribute: 'id',
	parse: function(data) {
        var hash = {};
        hash.name = data.name;
        hash.id = data.id;
        hash.address = data.location.display_address.join(' ');
        hash.rating = data.rating;
        hash.url = data.url;

        if (data.location.coordinate && data.location.coordinate !== 'undefined') {
            hash.coordinates = {
                lat: data.location.coordinate.latitude,
                lng: data.location.coordinate.longitude
            };
        }

        if (data.categories && data.categories !== 'undefined') {
        	hash.specificCategory = data.categories[0][0];
        } else {
        	hash.specificCategory = '';
        }
        return hash;
	}
});

module.exports = BusinessModel;

Update parse() to grab the business URL


/**
 * Class for drawing elements in canvas
 * @param {object} canvasElem handler of canvas
*/
var CanvasScreen = function ( canvasElem ) {
    const SIZE = 40; //size of square
    let ctx = canvasElem.getContext( "2d" ),
        currentX = 0,
        currentY = 0;

    let = drawSingleSquare = ( obj ) => {
        ctx.beginPath();
        ctx.moveTo( currentX, currentY );
        ctx.rect( currentX,currentY,SIZE,SIZE );

        setBgSquare( obj );
        ctx.fill();
        ctx.stroke();//TODO delete
    }
    let setBgSquare = ( obj ) => {
        let bg;
        if ( obj.isFilled ) {
            bg = obj.bgColor;
        } else {
            bg = "white";
        }
        ctx.fillStyle = bg;
    }
    /**
        Public function, draw elements in canvas
        @param {array} array array of objects
    */
    this.draw = function ( array ) {
        currentX = 0,
        currentY = 0;
        array.forEach( (v) => {

            v.forEach( (sq) => {
                drawSingleSquare(sq);
                currentX += SIZE;
            });

            currentX = 0;
            currentY += SIZE;

        });
    }
}

/**
 * Class for drawing elements in canvas
 * @param {object} canvasElem handler of canvas
*/
var CanvasScreen = function ( canvasElem ) {
    const SIZE = 40; //size of square
    let ctx = canvasElem.getContext( "2d" ),
        currentX = 0,
        currentY = 0;

    let = drawSingleSquare = ( obj ) => {
        ctx.beginPath();
        ctx.moveTo( currentX, currentY );
        ctx.rect( currentX,currentY,SIZE,SIZE );

        setBgSquare( obj );
        ctx.fill();
        ctx.stroke();//TODO delete
    }
    let setBgSquare = ( obj ) => {
        let bg;
        if ( obj.isActived ) {
            bg = obj.bgColor;
        } else {
            bg = "white";
        }
        ctx.fillStyle = bg;
    }
    /**
        Public function, draw elements in canvas
        @param {array} array array of objects
    */
    this.draw = function ( array ) {
        currentX = 0,
        currentY = 0;
        array.forEach( (v) => {

            v.forEach( (sq) => {
                //console.log(sq);
                drawSingleSquare(sq);
                currentX += SIZE;
            });

            currentX = 0;
            currentY += SIZE;

        });
    }
}

Change property for painting element


const Assert = require('assert');
const Child = require('child_process');
const Fs = require('fs');
const Path = require('path');

var packagePath = Path.join(process.cwd(), 'package.json');
var out = Object.prototype, keys = [];

function execute(options, done) {
  var key = keys.shift();

  if (!key) return done(null, out);

  Child.exec(options[key], function (err, stdout) {
    out[key] = err ? err.toString() : stdout;
    return execute(options, done);
  });
}

module.exports = function (options, done) {
  if (typeof options === 'function') {
    done = options;
    options = {};
  }

  Assert.equal(typeof options, 'object', 'Options must be an object');
  Assert.equal(typeof done, 'function', 'Must pass in a callback function');

  options = Object.assign({ node: 'node -v', npm: 'npm -v' }, options);
  keys = Object.keys(options);

  Fs.readFile(packagePath, 'utf8', function (err, packageJson) {
    if (err) return done(err);

    packageJson = JSON.parse(packageJson);
    out = {
      name: packageJson.name,
      version: packageJson.version,
      env: process.env.NODE_ENV // eslint-disable-line no-process-env
    };

    return execute(options, done);
  });
};

const Assert = require('assert');
const Child = require('child_process');
const Fs = require('fs');
const Path = require('path');

var packagePath = Path.join(process.cwd(), 'package.json');
var out = Object.prototype, keys = [];

function execute(options, done) {
  var key = keys.shift();

  if (!key) return done(null, out);

  Child.exec(options[key], function (err, stdout) {
    out[key] = err ? err.toString() : stdout.replace(/\n/g, '');
    return execute(options, done);
  });
}

module.exports = function (options, done) {
  if (typeof options === 'function') {
    done = options;
    options = {};
  }

  Assert.equal(typeof options, 'object', 'Options must be an object');
  Assert.equal(typeof done, 'function', 'Must pass in a callback function');

  options = Object.assign({ node: 'node -v', npm: 'npm -v' }, options);
  keys = Object.keys(options);

  Fs.readFile(packagePath, 'utf8', function (err, packageJson) {
    if (err) return done(err);

    packageJson = JSON.parse(packageJson);
    out = {
      name: packageJson.name,
      version: packageJson.version,
      env: process.env.NODE_ENV // eslint-disable-line no-process-env
    };

    return execute(options, done);
  });
};

Remove newline characters at the end of each string


/* eslint camelcase: 0 */
const { get, merge, pickBy } = require('lodash')

const { transformInteractionResponseToForm } = require('../transformers')
const { transformDateStringToDateObject } = require('../../transformers')
const { interactionEditFormConfig } = require('../macros')
const { buildFormWithStateAndErrors } = require('../../builders')

function renderEditPage (req, res) {
  const interactionData = transformInteractionResponseToForm(res.locals.interaction)
  const interactionDefaults = {
    dit_adviser: req.session.user,
    date: transformDateStringToDateObject(new Date()),
  }
  const mergedInteractionData = pickBy(merge({}, interactionDefaults, interactionData, res.locals.requestBody))
  const interactionForm =
    buildFormWithStateAndErrors(
      interactionEditFormConfig(
        {
          returnLink: res.locals.returnLink,
          advisers: get(res.locals, 'advisers.results'),
          contacts: res.locals.contacts,
          services: res.locals.services,
        }),
      mergedInteractionData,
      get(res.locals, 'form.errors.messages'),
    )

  res
    .breadcrumb(`${interactionData ? 'Edit' : 'Add'} interaction`)
    .title(`${interactionData ? 'Edit' : 'Add'} interaction for ${res.locals.entityName}`)
    .render('interactions/views/edit', {
      interactionForm,
    })
}

module.exports = {
  renderEditPage,
}

/* eslint camelcase: 0 */
const { get, merge, pickBy } = require('lodash')

const { transformInteractionResponseToForm } = require('../transformers')
const { transformDateStringToDateObject } = require('../../transformers')
const { interactionEditFormConfig } = require('../macros')
const { buildFormWithStateAndErrors } = require('../../builders')

function renderEditPage (req, res) {
  const interactionData = transformInteractionResponseToForm(res.locals.interaction)
  const interactionDefaults = {
    dit_adviser: req.session.user,
    date: transformDateStringToDateObject(new Date()),
    contact: get(res.locals, 'contact.id'),
  }
  const mergedInteractionData = pickBy(merge({}, interactionDefaults, interactionData, res.locals.requestBody))
  const interactionForm =
    buildFormWithStateAndErrors(
      interactionEditFormConfig(
        {
          returnLink: res.locals.returnLink,
          advisers: get(res.locals, 'advisers.results'),
          contacts: res.locals.contacts,
          services: res.locals.services,
        }),
      mergedInteractionData,
      get(res.locals, 'form.errors.messages'),
    )

  res
    .breadcrumb(`${interactionData ? 'Edit' : 'Add'} interaction`)
    .title(`${interactionData ? 'Edit' : 'Add'} interaction for ${res.locals.entityName}`)
    .render('interactions/views/edit', {
      interactionForm,
    })
}

module.exports = {
  renderEditPage,
}

Set default contact if present


var path = require('path');
require('js-yaml');
module.exports = function(grunt, options) {
  var cwd = process.cwd();

  var defaults = {
    configPath: path.join(cwd, 'grunt'),
    init: true
  };

  options = grunt.util._.extend({}, defaults, options);

  var glob = require('glob');
  var object = {};
  var key;
  var aliases;

  glob.sync('*.{js,yml,yaml,coffee}', {cwd: options.configPath}).forEach(function(option) {
    key = option.replace(/\.(js|yml|yaml|coffee)$/,'');
    var fullPath = path.join(options.configPath, option);
    var settings = require(fullPath);
    if (key == 'aliases') {
      aliases = settings;
    } else {
      object[key] = grunt.util._.isFunction(settings) ?
        settings(grunt) : settings;
    }
  });

  object.package = grunt.file.readJSON(path.join(cwd, 'package.json'));

  if (options.config) {
    grunt.util._.merge(object, options.config);
  }

  if (options.loadGruntTasks !== false) {
    var loadTasksOptions = options.loadGruntTasks || {};
    require('load-grunt-tasks')(grunt, loadTasksOptions);
  }

  if (options.init) {
    grunt.initConfig(object);
  }

  if (aliases) {
    for (var taskName in aliases) {
      grunt.registerTask(taskName, aliases[taskName]);
    }
  }

  return object;

};

var path = require('path');
var _ = require('lodash-node/modern/objects');
require('js-yaml');

module.exports = function(grunt, options) {
  var cwd = process.cwd();

  var defaults = {
    configPath: path.join(cwd, 'grunt'),
    init: true
  };

  options = _.extend({}, defaults, options);

  var glob = require('glob');
  var object = {};
  var key;
  var aliases;

  glob.sync('*.{js,yml,yaml,coffee}', {cwd: options.configPath}).forEach(function(option) {
    key = option.replace(/\.(js|yml|yaml|coffee)$/,'');
    var fullPath = path.join(options.configPath, option);
    var settings = require(fullPath);
    if (key == 'aliases') {
      aliases = settings;
    } else {
      object[key] = _.isFunction(settings) ?
        settings(grunt) : settings;
    }
  });

  object.package = grunt.file.readJSON(path.join(cwd, 'package.json'));

  if (options.config) {
    _.merge(object, options.config);
  }

  if (options.loadGruntTasks !== false) {
    var loadTasksOptions = options.loadGruntTasks || {};
    require('load-grunt-tasks')(grunt, loadTasksOptions);
  }

  if (options.init) {
    grunt.initConfig(object);
  }

  if (aliases) {
    for (var taskName in aliases) {
      grunt.registerTask(taskName, aliases[taskName]);
    }
  }

  return object;

};

Use lodash-node instead of grunt.util._


import React from 'react';

import { IFrame } from './iframe';

export default {
  Component: IFrame,
  title: 'Components|Preview/Iframe',
};

const style = { width: '100%', height: '500px' };

export const workingStory = () => (
  <IFrame
    id="iframe"
    title="Missing"
    src="/iframe.html?id=ui-panel--default"
    allowFullScreen
    style={style}
  />
);

export const missingStory = () => (
  <IFrame id="iframe" title="Missing" src="/iframe.html?id=missing" allowFullScreen style={style} />
);

export const errorStory = () => (
  <IFrame
    id="iframe"
    title="Missing"
    src="/iframe.html?id=core-errors--story-throws-exception"
    allowFullScreen
    style={style}
  />
);
// We need to disable this one in Chromatic because the screenshot includes the uploaded URL sadly:
//   eg. https://www.chromaticqa.com/snapshot?appId=5a375b97f4b14f0020b0cda3&id=5c52edb4323f9000249aae72
errorStory.parameters = {
  chromatic: { disable: true },
};

Disable iframe error story in Chromatic :/


define(function(require) {

  var test = require('../../../test')

  test.assert(require('./missing/missing').bogus === null, 'return null when module file is 404')
  test.assert(require('./nested/b/c/d').foo() === 1, 'nested module identifier is allowed')
  test.assert(require('./relative/a').foo == require('./relative/b').foo, 'a and b share foo through a relative require')

  var a1 = require('./a.js?v=1.0')
  var a2 = require('./a.js?v=2.0')
  test.assert(a1.name === 'a', a1.name)
  test.assert(a2.name === 'a', a2.name)
  test.assert(a1.foo() === 'foo', a1.foo())
  test.assert(a2.foo() === 'foo', a2.foo())
  test.assert(a1.foo !== a2.foo, 'a1.foo !== a2.foo')

  test.next()

});

define(function(require) {

  var test = require('../../../test')

  test.assert(require('./missing/missing').bogus === null, 'return null when module file is 404')
  test.assert(require('./nested/b/c/d').name === 'd', 'nested module identifier is allowed')
  test.assert(require('./relative/a').foo == require('./relative/b').foo, 'a and b share foo through a relative require')

  var a1 = require('./version/a.js?v=1.0')
  var a2 = require('./version/a.js?v=2.0')
  test.assert(a1.name === 'a', a1.name)
  test.assert(a2.name === 'a', a2.name)
  test.assert(a1.foo() === 'foo', a1.foo())
  test.assert(a2.foo() === 'foo', a2.foo())
  test.assert(a1.foo !== a2.foo, 'a1.foo !== a2.foo')

  test.next()

});

Fix a bug for spec


// simple script to advertise a MDNS service for local API
// TODO(mc, 2017-10-31): remove this file once API can advertise for itself
const bonjour = require('bonjour')()

console.log('Publishing local MDNS service for API')

const service = bonjour.publish({
  name: 'ot-local-api',
  host: 'localhost',
  port: 31950,
  // TODO(mc, 2017-10-26): we're relying right now on the fact that resin
  // advertises an SSH service. Instead, we should be registering an HTTP
  // service on port 31950 and listening for that instead
  type: 'ssh'
})

service.on('error', (error) => {
  console.error('Error advertising MDNS service for local API', error)
})

// simple script to advertise a MDNS service for local API
// TODO(mc, 2017-10-31): remove this file once API can advertise for itself
const bonjour = require('bonjour')()

console.log('Publishing local MDNS service for API')

const service = bonjour.publish({
  name: 'opentrons-local-api',
  host: 'localhost',
  port: 31950,
  // TODO(mc, 2017-10-26): we're relying right now on the fact that resin
  // advertises an SSH service. Instead, we should be registering an HTTP
  // service on port 31950 and listening for that instead
  type: 'ssh'
})

service.on('error', (error) => {
  console.error('Error advertising MDNS service for local API', error)
})

Fix dev advertise script for mdns name change (b271a68)


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 JobTypeCard extends Component {
  static propTypes = {
    title: React.PropTypes.string.isRequired,
    cover: React.PropTypes.string.isRequired,
    icon: React.PropTypes.string.isRequired,
    subtitle: React.PropTypes.string.isRequired,
    toNextScreen: React.PropTypes.func.isRequired,
  };

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

import React, { Component, PropTypes } 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 JobTypeCard extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    cover: imageProp.isRequired,
    icon: imageProp.isRequired,
    subtitle: PropTypes.string.isRequired,
    toNextScreen: PropTypes.func.isRequired,
  };

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

Make JobTypeCard use imageProp from propTypes