'use strict';

const handelbars = require('handlebars');
const fs = require('fs');
const path = require('path');

const nodemailer = require('nodemailer');
require('env2')(`${__dirname}/../.env`);

// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: process.env.EMAIL,
    pass: process.env.PASS
  }
});

function sendMail(emailAddress, emailContent, cb){

  const emailTemplate = fs.readFileSync(path.join(__dirname, '..', 'public', 'views', 'email.hbs'), 'utf8');
  const template = handelbars.compile(emailTemplate);
  const emailBody = template(emailContent);

  const mailOptions = {
    from: '"CAMHS 😀" <welcome.to.cahms@hotmail.co.uk>',
    subject: 'Getting to know you Questionnaire',
    text: 'Questionnaire',
    html: emailBody,
    to: emailAddress
  };

  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      return cb(error)
    }
    return cb(null, `Message ${info.messageId} sent: ${info.response}`)
  });
}

module.exports = sendMail;

'use strict';

const handelbars = require('handlebars');
const fs = require('fs');
const path = require('path');

const nodemailer = require('nodemailer');
require('env2')(`${__dirname}/../.env`);

// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: process.env.EMAIL,
    pass: process.env.PASS
  }
});

function sendMail(emailAddress, emailContent, cb){

  const emailTemplate = fs.readFileSync(path.join(__dirname, '..', 'src', 'templates' ,'views', 'email.hbs'), 'utf8');
  const template = handelbars.compile(emailTemplate);
  const emailBody = template(emailContent);

  const mailOptions = {
    from: '"CAMHS 😀" <welcome.to.cahms@hotmail.co.uk>',
    subject: 'Getting to know you Questionnaire',
    text: 'Questionnaire',
    html: emailBody,
    to: emailAddress
  };

  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      return cb(error)
    }
    return cb(null, `Message ${info.messageId} sent: ${info.response}`)
  });
}

module.exports = sendMail;

Fix path to emial.hbs in second-emial.js


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

moduleForComponent('jobs-list', 'JobsListComponent', {
  needs: ['helper:format-duration', 'component:jobs-item']
});

test('it renders a list of jobs', function (assert) {
  var component, jobs;
  jobs = [
    Ember.Object.create({
      id: 1,
      state: 'passed'
    }), Ember.Object.create({
      id: 1,
      state: 'failed'
    })
  ];
  component = this.subject({
    jobs: jobs,
    required: true
  });
  this.render();
  assert.equal(component.$('.section-title').text().trim(), 'Build Jobs');
  assert.equal(component.$('.jobs-item').length, 2, 'there should be 2 job items');
  assert.ok(component.$('.jobs-item:nth(0)').hasClass('passed'), 'passed class should be applied to a job');
  assert.ok(component.$('.jobs-item:nth(1)').hasClass('failed'), 'failed class should be applied to a job');
});

test('it renders "Allowed Failures" version without a `required` property', function (assert) {
  const jobs = [
    Ember.Object.create({
      id: 1
    })
  ];
  const component = this.subject({
    jobs: jobs
  });
  this.render();
  assert.ok(component.$('.section-title').text().match(/Allowed Failures/));
});

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

moduleForComponent('jobs-list', 'JobsListComponent', {
  needs: ['helper:format-duration', 'component:jobs-item']
});

test('it renders a list of jobs', function (assert) {
  const jobs = [
    Ember.Object.create({
      id: 1,
      state: 'passed'
    }), Ember.Object.create({
      id: 1,
      state: 'failed'
    })
  ];
  const component = this.subject({
    jobs: jobs,
    required: true
  });
  this.render();
  assert.equal(component.$('.section-title').text().trim(), 'Build Jobs');
  assert.equal(component.$('.jobs-item').length, 2, 'there should be 2 job items');
  assert.ok(component.$('.jobs-item:nth(0)').hasClass('passed'), 'passed class should be applied to a job');
  assert.ok(component.$('.jobs-item:nth(1)').hasClass('failed'), 'failed class should be applied to a job');
});

test('it renders "Allowed Failures" version without a `required` property', function (assert) {
  const jobs = [
    Ember.Object.create({
      id: 1
    })
  ];
  const component = this.subject({
    jobs: jobs
  });
  this.render();
  assert.ok(component.$('.section-title').text().match(/Allowed Failures/));
});

Declare with const in unit test


var _ = require("../lodash");

module.exports = dfs;

/*
 * A helper that preforms a pre- or post-order traversal on the input graph
 * and returns the nodes in the order they were visited. This algorithm treats
 * the input as undirected.
 *
 * Order must be one of "pre" or "post".
 */
function dfs(g, vs, order) {
  if (!_.isArray(vs)) {
    vs = [vs];
  }

  var acc = [],
      visited = {};
  _.each(vs, function(v) {
    if (!g.hasNode(v)) {
      throw new Error("Graph does not have node: " + v);
    }

    doDfs(g, v, order === "post", visited, acc);
  });
  return acc;
}

function doDfs(g, v, postorder, visited, acc) {
  if (!_.has(visited, v)) {
    visited[v] = true;

    if (!postorder) { acc.push(v); }
    _.each(g.successors(v), function(w) {
      doDfs(g, w, postorder, visited, acc);
    });
    if (postorder) { acc.push(v); }
  }
}

var _ = require("../lodash");

module.exports = dfs;

/*
 * A helper that preforms a pre- or post-order traversal on the input graph
 * and returns the nodes in the order they were visited. If the graph is
 * undirected then this algorithm will navigate using neighbors. If the graph
 * is directed then this algorithm will navigate using successors.
 *
 * Order must be one of "pre" or "post".
 */
function dfs(g, vs, order) {
  if (!_.isArray(vs)) {
    vs = [vs];
  }

  var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g);

  var acc = [],
      visited = {};
  _.each(vs, function(v) {
    if (!g.hasNode(v)) {
      throw new Error("Graph does not have node: " + v);
    }

    doDfs(g, v, order === "post", visited, navigation, acc);
  });
  return acc;
}

function doDfs(g, v, postorder, visited, navigation, acc) {
  if (!_.has(visited, v)) {
    visited[v] = true;

    if (!postorder) { acc.push(v); }
    _.each(navigation(v), function(w) {
      doDfs(g, w, postorder, visited, navigation, acc);
    });
    if (postorder) { acc.push(v); }
  }
}

Use neighbors to perform DFS on undirected graphs


import React, { Component } from 'react'
import albums from './all-albums'
import Thumbnail from './Thumbnail'
// import AlbumsHeader from './AlbumsHeader'

class Album extends Component {
  render() {
    const {albumNum = 0} = this.props.params
    const album = albums[albumNum]
    return (

        { /* <AlbumsHeader /> */ }

          { album.pictures.map((picture, pictureNum) => <Thumbnail albumNum={albumNum} pictureNum={pictureNum} key={pictureNum} rowHeight={128} />) }

    )
  }
}

export default Album

import React, { Component } from 'react'
import albums from './all-albums'
import Thumbnail from './Thumbnail'
// import AlbumsHeader from './AlbumsHeader'

const rowHeight = 180

class Album extends Component {
  render() {
    const {albumNum = 0} = this.props.params
    const album = albums[albumNum]
    return (

        { /* <AlbumsHeader /> */ }

          { album.pictures.map((picture, pictureNum) => <Thumbnail albumNum={albumNum} pictureNum={pictureNum} key={pictureNum} rowHeight={rowHeight} />) }

    )
  }
}

export default Album

Make the rows a little higher to have larger thumbnails to press


import { test } from 'ember-qunit';
import startApp from '../helpers/start-app';

var App;

module('Acceptances - Login page', {
  setup: function(){
    App = startApp();
  },
  teardown: function() {
    Ember.run(App, 'destroy');
  }
});

test('login page renders properly', function () {
  visit('/login').then(function () {
    var container = find('div.account-container > div.content');
    var form = container.find('form');
    var loginFields = container.find('form > div.login-fields');
    var loginActions = container.find('form > div.login-actions');

    equal(form.find('h1').text(), 'Sign In', 'Form header exists');
    ok(exists(loginFields.find('input#username')), 'username input field exists');
    ok(exists(loginFields.find('input#password')), 'password input field exists');
  });
});

import { test } from 'ember-qunit';
import startApp from '../helpers/start-app';

var App;

module('Acceptances - Login page', {
  setup: function(){
    App = startApp();
  },
  teardown: function() {
    Ember.run(App, 'destroy');
  }
});

test('login page renders properly', function () {
  visit('/login').then(function () {
    var container = find('div.account-container > div.content');
    var form = container.find('form');

    equal(form.find('h1').text(), 'Sign In', 'Form header exists');
  });
});

Remove exists() test in login tests


import {computed} from '@ember/object';
import Service from '@ember/service';

export default class Fw extends Service {

  @computed()
  get isBs3() {
    return window.location.href.includes('/v.3/bs3/') || window.location.href.includes('localhost');
  }

  @computed()
  get isBs4() {
    return window.location.href.includes('/v.3/bs4/');
  }

  @computed()
  get isSemanticUI() {
    return window.location.href.includes('/v.3/semantic/');
  }

}

import {computed} from '@ember/object';
import Service from '@ember/service';

export default class Fw extends Service {

  @computed()
  get isBs3() {
    return window.location.href.includes('/v.3/bs3/');
  }

  @computed()
  get isBs4() {
    return window.location.href.includes('/v.3/bs4/');
  }

  @computed()
  get isSemanticUI() {
    return window.location.href.includes('/v.3/semantic/') || window.location.href.includes('localhost');
  }

}

Use semantic for localhost (needed for tests)


/**
  This view handles the rendering of the top lists

  @class DiscoveryTopView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.DiscoveryTopView = Discourse.View.extend({

  didInsertElement: function() {
    this._super();
    Em.run.schedule('afterRender', function() {
      $('document').scrollTop(0);
    });
  },

});

/**
  This view handles the rendering of the top lists

  @class DiscoveryTopView
  @extends Discourse.View
  @namespace Discourse
  @module Discourse
**/
Discourse.DiscoveryTopView = Discourse.View.extend(Discourse.ScrollTop);

Use the `ScrollTop` mixin instead of custom scrolling code.


import React, {Component, PropTypes} from 'react'
import BoardContainer from './BoardContainer'
import {Provider} from 'react-redux'
import {createStore} from 'redux'
import boardReducer from '../reducers/BoardReducer'

let store = createStore(boardReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())

export default class Board extends Component {

  render () {
    return <Provider store={store}>
      <BoardContainer {...this.props} />
    </Provider>
  }
}

Board.propTypes = {
  data: PropTypes.object.isRequired,
  onLaneScroll: PropTypes.func,
  onCardClick: PropTypes.func,
  eventBusHandle: PropTypes.func,
  laneSortFunction: PropTypes.func,
  draggable: PropTypes.bool,
  handleDragStart: PropTypes.func,
  handleDragEnd: PropTypes.func,
  onDataChange: PropTypes.func
}

import React, {Component, PropTypes} from 'react'
import BoardContainer from './BoardContainer'
import {Provider} from 'react-redux'
import {createStore} from 'redux'
import boardReducer from '../reducers/BoardReducer'

let store = createStore(boardReducer, window && window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())

export default class Board extends Component {

  render () {
    return <Provider store={store}>
      <BoardContainer {...this.props} />
    </Provider>
  }
}

Board.propTypes = {
  data: PropTypes.object.isRequired,
  onLaneScroll: PropTypes.func,
  onCardClick: PropTypes.func,
  eventBusHandle: PropTypes.func,
  laneSortFunction: PropTypes.func,
  draggable: PropTypes.bool,
  handleDragStart: PropTypes.func,
  handleDragEnd: PropTypes.func,
  onDataChange: PropTypes.func
}

Check for existence of window object


var pyshell = require('python-shell');

module.exports = function(RED) {
    function LowerCaseNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;

        this.on('input', function(msg) {
            msg.payload = msg.payload.toLowerCase();
            node.send(msg);
        });

        pyshell.run('spi_read.py', function (err) {
            if (err) throw err;
            console.log('finished');
        });
        pyshell.on('message', function (message) {
            // received a message sent from the Python script (a simple "print" statement)
            console.log(message);
        });

        // end the input stream and allow the process to exit
        pyshell.end(function (err) {
            if (err) throw err;
            console.log('finished');
        });
    }
    RED.nodes.registerType("adafruit-max31855",LowerCaseNode);
}

var pyshell = require('python-shell');

module.exports = function(RED) {
    function LowerCaseNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;

        this.on('input', function(msg) {
            msg.payload = msg.payload.toLowerCase();
            node.send(msg);
        });

        pyshell.run('/home/pi/node-red-contrib-adafruit-max31855/thermocouple/spi_read.py', function (err) {
            if (err) throw err;
            console.log('finished');
        });
        pyshell.on('message', function (message) {
            // received a message sent from the Python script (a simple "print" statement)
            console.log(message);
            node.send(message);
        });

        // end the input stream and allow the process to exit
        pyshell.end(function (err) {
            if (err) throw err;
            console.log('finished');
            node.send('finished');
        });
    }
    RED.nodes.registerType("adafruit-max31855",LowerCaseNode);
}

Update location of python file and send messages.


'use strict';

const net = require('net');
const process = require('process');
const ProtoDef = require('protodef').ProtoDef;

if (process.argv.length < 4) {
  console.log('Usage: node mcping16.js <host> <port>');
  process.exit(1);
}

const host = process.argv[2];
const port = parseInt(process.argv[3]);

const proto = new ProtoDef();

const socket = net.connect(port, host);
socket.on('connect', () => {
  console.log('connected');

  // request challenge token
  // TODO: encode/decode using protodef
  socket.write(new Buffer('fefd090000000000000000', 'hex'));
});
socket.on('end', () => {
  console.log('ended');
});
socket.on('data', (raw) => {
  console.log('data',raw);
  console.log(raw.toString());
});

'use strict';

const net = require('net');
const process = require('process');
const ProtoDef = require('protodef').ProtoDef;

if (process.argv.length < 4) {
  console.log('Usage: node mcping16.js <host> <port>');
  process.exit(1);
}

const host = process.argv[2];
const port = parseInt(process.argv[3]);

const proto = new ProtoDef();

const socket = net.connect(port, host);
socket.on('connect', () => {
  console.log('connected');

  // request challenge token
  // TODO: encode/decode using protodef
  socket.write(new Buffer('fefd090000000000000000', 'hex'));
});
socket.on('end', () => {
  console.log('ended');
});
socket.on('data', (raw) => {
  console.log('data',raw);
  const packetID = raw.readUInt8(0);
  if (packetID !== 0xff) {
    throw new Error('unexpected packet id');
  }
  const length = raw.slice(1).readUInt16BE(); // in UCS-2/UTF-16 characters

  const string = raw.slice(4).toString('ucs2');
  console.log('response string',string);
  const array = string.split('\xa7');
  console.log('array',array);
});

Read the UTF-16BE/UCS-2 response string


import {Server} from 'hapi'
import inert from 'inert'
import getProjects from './app/get-projects'

/**
 * @param {string} configFilename The config filename
 * @param {number} port The port number
 */
export function serve(configFilename, port) {

  const server = new Server()
  let projects

  server.connection({port})

  server.register(inert, err => {

    server.route({
      method: 'GET',
      path: '/api/projects',
      handler: (request, reply) => {
        reply(projects)
      }
    })

    server.route({
      method: 'GET',
      path: '/site/{param*}',
      handler: { directory: { path: 'site', listing: true } }
    })

    server.start(err => {
      if (err) {
        console.log(err.stack)
        return
      }

      try {

        projects = getProjects(configFilename)

      } catch (e) {

        console.log(e.stack)

        process.exit(1)
      }

      console.log('Server running at:', server.info.uri + '/site')
    })

  })

}

import {Server} from 'hapi'
import inert from 'inert'
import getProjects from './app/get-projects'

/**
 * @param {string} configFilename The config filename
 * @param {number} port The port number
 */
export function serve(configFilename, port) {

  const server = new Server()
  let projects

  server.connection({port})

  server.register(inert, err => {

    server.route({
      method: 'GET',
      path: '/api/projects',
      handler: (request, reply) => {
        reply(getProjects(configFilename))
      }
    })

    server.route({
      method: 'GET',
      path: '/site/{param*}',
      handler: { directory: { path: 'site', listing: true } }
    })

    server.start(err => {
      if (err) {
        console.log(err.stack)
        return
      }

      try {

        projects = getProjects(configFilename)

      } catch (e) {

        console.log(e.stack)

        process.exit(1)
      }

      console.log('Server running at:', server.info.uri + '/site')
    })

  })

}

Load fresh data for each request


/**
 * This is the entrypoint for our application on the server.
 * When the server is started, this code will run.
 */

// Use Babel to provide support for JSX
require('babel-core/register');

const http = require('http');
const ws = require('ws');

const createApp = require('./createApp').default;
const WebSocketServer = require('./webSocketServer').default;
const sessionParser = require('./config/sessionParser').default;

createApp().then((app) => {
  const server = http.createServer(app);
  server.listen(3000, () => {
    console.log('Server started.');
    global.wss = new WebSocketServer(new ws.Server({
      verifyClient: (info, done) => {
        sessionParser(info.req, {}, () => {
          done(info.req.session.passport.user);
        });
      },
      server
    }));
  });
});

/**
 * This is the entrypoint for our application on the server.
 * When the server is started, this code will run.
 */

// Use Babel to provide support for JSX
require('babel-core/register');

const http = require('http');
const ws = require('ws');

const createApp = require('./createApp').default;
const WebSocketServer = require('./webSocketServer').default;
const sessionParser = require('./config/sessionParser').default;

createApp().then((app) => {
  const server = http.createServer(app);
  server.listen(3000, () => {
    console.log('Server started.');
    global.wss = new WebSocketServer(new ws.Server({
      verifyClient: (info, done) => {
        sessionParser(info.req, {}, () => {
          if(!info.req.session.passport) {
            done(null);
            return;
          }
          done(info.req.session.passport.user);
        });
      },
      server
    }));
  });
});

Fix crash when passport is not on the session


const Hapi = require('hapi');
const Inert = require('inert');
const Vision = require('vision');
const Path = require('path');

const routes = require('./routes');

const server = new Hapi.Server();

server.connection({
  port: process.env.PORT || 8080,
  routes: {
    files: {
      relativeTo: Path.join(__dirname, '..', 'public')
    }
  }
});

server.register([Inert], err => {
  if(err) console.log(err);

  server.route(routes);
});

module.exports = server;

const Hapi = require('hapi');
const Inert = require('inert');
const Path = require('path');

const routes = require('./routes');

const server = new Hapi.Server();

server.connection({
  port: process.env.PORT || 8080,
  routes: {
    files: {
      relativeTo: Path.join(__dirname, '..', 'public')
    }
  }
});

server.register([Inert], err => {
  if(err) console.log(err);

  server.route(routes);
});

module.exports = server;

Remove Vision as it is not needed yet.


import React from 'react'
import { Provider } from 'react-redux'
import { applyMiddleware, combineReducers, createStore } from 'redux';
import { reducer as formReducer } from 'redux-form'
import { render } from 'react-dom'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'

import customers from './reducers/customers'
import App from './App'
import listUpdater from './middlewares/listUpdater'
import { OK } from './constants'

import 'bootstrap/dist/css/bootstrap.min.css'
import './styles.css'

const loggerMiddleware = createLogger()

const reducers = combineReducers({
	customers,
	form: formReducer.plugin({
		customer: (state, action) => {
			switch(action.type) {
				case OK:
					// reset form data.
					return undefined;
				default:
					return state;
			}
		}
	})
})

const store = createStore(
  reducers,
  applyMiddleware(
    thunkMiddleware,
    loggerMiddleware,
		listUpdater,
  )
);

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

import React from 'react'
import { Provider } from 'react-redux'
import { applyMiddleware, combineReducers, createStore } from 'redux';
import { reducer as formReducer } from 'redux-form'
import { render } from 'react-dom'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'

import customers from './reducers/customers'
import App from './App'
import listUpdater from './middlewares/listUpdater'
import { OK } from './constants'

import 'bootstrap/dist/css/bootstrap.min.css'
import './styles.css'

const loggerMiddleware = createLogger()

const reducers = combineReducers({
	customers,
	form: formReducer.plugin({
		customer: (state, action) => {
			switch(action.type) {
				case OK:
					// reset form data.
					return undefined;
				default:
					return state;
			}
		}
	})
})

const store = createStore(
  reducers,
  applyMiddleware(
    thunkMiddleware,
    loggerMiddleware,
		listUpdater,
  )
);

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

Fix small problem in import


import FontFace from './style/FontFace';
import Keyframes from './style/Keyframes';
import prop from './style/prop';
import sel from './style/sel';
import media from './style/media';
import style from './style/style';
import createInsertStyle from './createInsertStyle/index';
import createStyleSheet from './createStyleSheet/index';
import Prefixer from './prefixer/index';

export {
  FontFace,
  Keyframes,
  prop,
  sel,
  media,
  style,
  createInsertStyle,
  createStyleSheet,
  Prefixer
};

import Keyframes from './style/Keyframes';
import prop from './style/prop';
import sel from './style/sel';
import media from './style/media';
import style from './style/style';
import createInsertStyle from './createInsertStyle/index';
import createStyleSheet from './createStyleSheet/index';
import Prefixer from './prefixer/index';

export {
  Keyframes,
  prop,
  sel,
  media,
  style,
  createInsertStyle,
  createStyleSheet,
  Prefixer
};

Remove FontFace from main bundle


import React from 'react';
import ReactDOM from 'react-dom';

import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom';

import { Provider } from 'react-redux';
import {
  createStore,
  combineReducers,
  applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';

import reducers from 'reducers';

import AppComponent from 'components/App';
import NotFoundComponent from 'components/NotFound';

let store = createStore(
  combineReducers(reducers),
  applyMiddleware(thunk)
);

ReactDOM.render(
  <Provider store={store}>
    <Router>
      <Switch>
        <Route exact path="/" component={AppComponent} />
        <Route component={NotFoundComponent} />
      </Switch>
    </Router>
  </Provider>,
  document.getElementById('root')
);

import React from 'react';
import ReactDOM from 'react-dom';

import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom';

import { Provider } from 'react-redux';
import {
  createStore,
  combineReducers,
  applyMiddleware
} from 'redux';
import thunk from 'redux-thunk';

import reducers from 'reducers';

import AppComponent from 'components/App';
import NotFoundComponent from 'components/NotFound';

let store = createStore(
  combineReducers(reducers),
  applyMiddleware(thunk),
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

ReactDOM.render(
  <Provider store={store}>
    <Router>
      <Switch>
        <Route exact path="/" component={AppComponent} />
        <Route component={NotFoundComponent} />
      </Switch>
    </Router>
  </Provider>,
  document.getElementById('root')
);

Add support for Redux devtools


/**
 * Redux middleware for calling an API
 * @module redux-api-middleware
 * @requires lodash.isplainobject
 * @exports {string} RSAA
 * @exports {function} isRSAA
 * @exports {function} validateRSAA
 * @exports {function} isValidRSAA
 * @exports {error} InvalidRSAA
 * @exports {error} InternalError
 * @exports {error} RequestError
 * @exports {error} ApiError
 * @exports {function} getJSON
 * @exports {ReduxMiddleWare} apiMiddleware
 */

/**
 * @typedef {function} ReduxMiddleware
 * @param {object} store
 * @returns {ReduxNextHandler}
 *
 * @typedef {function} ReduxNextHandler
 * @param {function} next
 * @returns {ReduxActionHandler}
 *
 * @typedef {function} ReduxActionHandler
 * @param {object} action
 * @returns undefined
 */

import RSAA from './RSAA';
import { isRSAA, validateRSAA, isValidRSAA } from './validation';
import { InvalidRSAA, InternalError, RequestError, ApiError } from './errors';
import { getJSON } from './util';
import { apiMiddleware, createMiddleware } from './middleware';

export {
  RSAA,
  isRSAA,
  validateRSAA,
  isValidRSAA,
  InvalidRSAA,
  InternalError,
  RequestError,
  ApiError,
  getJSON,
  createMiddleware,
  apiMiddleware
};

/**
 * Redux middleware for calling an API
 * @module redux-api-middleware
 * @requires lodash.isplainobject
 * @exports {string} RSAA
 * @exports {function} isRSAA
 * @exports {function} validateRSAA
 * @exports {function} isValidRSAA
 * @exports {error} InvalidRSAA
 * @exports {error} InternalError
 * @exports {error} RequestError
 * @exports {error} ApiError
 * @exports {function} getJSON
 * @exports {function} createMiddleware
 * @exports {ReduxMiddleWare} apiMiddleware
 */

/**
 * @typedef {function} ReduxMiddleware
 * @param {object} store
 * @returns {ReduxNextHandler}
 *
 * @typedef {function} ReduxNextHandler
 * @param {function} next
 * @returns {ReduxActionHandler}
 *
 * @typedef {function} ReduxActionHandler
 * @param {object} action
 * @returns undefined
 */

import RSAA from './RSAA';
import { isRSAA, validateRSAA, isValidRSAA } from './validation';
import { InvalidRSAA, InternalError, RequestError, ApiError } from './errors';
import { getJSON } from './util';
import { apiMiddleware, createMiddleware } from './middleware';

export {
  RSAA,
  isRSAA,
  validateRSAA,
  isValidRSAA,
  InvalidRSAA,
  InternalError,
  RequestError,
  ApiError,
  getJSON,
  createMiddleware,
  apiMiddleware
};

Add createMiddlware to top level jsdoc


import SocketIO from 'socket.io';
import { createClient } from 'redis';

import { redisUrl } from './config';

const io = SocketIO();

io.use((socket, next) => {
  return next();
});

io.of('/live-chatroom')
  .use((socket, next) => {
    return next();
  })
  .on('connection', (socket) => {
    const redis = createClient(redisUrl);
    let currentRoom = '';

    socket.on('subscribe', (id) => {
      currentRoom = `live:${id}:comments`;
      socket.join(currentRoom);

      redis.subscribe(`${currentRoom}:latest`);

      io.of('/live-chatroom').in(currentRoom).clients((err, clients) => {
        if (clients.length === 1) {
          redis.lpush('live', id);
        }
      });

      socket.emit('subscribed');
    });

    redis.on('message', (channel, message) => {
      socket.emit('comment', message);
    });

    socket.on('unsubscribe', (id) => {
      socket.leave(id);
      socket.emit('unsubscribed');
    });

    socket.on('disconnect', () => {
      redis.unsubscribe(`${currentRoom}:latest`);
      redis.quit();
    })
  });

export default io;

import SocketIO from 'socket.io';
import { createClient } from 'redis';

import { redisUrl } from './config';

const io = SocketIO();

io.use((socket, next) => {
  return next();
});

io.of('/live-chatroom')
  .use((socket, next) => {
    return next();
  })
  .on('connection', (socket) => {
    const redis = createClient(redisUrl);
    let currentRoom = '';

    socket.on('subscribe', (id) => {
      currentRoom = `live:${id}:comments`;
      socket.join(currentRoom);

      redis.subscribe(`${currentRoom}:latest`);

      redis.exists('live', id, (err, result) => {
        if (result < 2) {
          redis.lpush('live', id);
        }
      });

      socket.emit('subscribed');
    });

    redis.on('message', (channel, message) => {
      socket.emit('comment', message);
    });

    socket.on('unsubscribe', (id) => {
      socket.leave(id);
      socket.emit('unsubscribed');
    });

    socket.on('disconnect', () => {
      redis.unsubscribe(`${currentRoom}:latest`);
      redis.quit();
    })
  });

export default io;

Fix add live room condition.


import SocketIO from 'socket.io';
import { createClient } from 'redis';

import { redisUrl } from './config';

const io = SocketIO();

io.use((socket, next) => {
  return next();
});

io.of('/live-chatroom')
  .use((socket, next) => {
    return next();
  })
  .on('connection', (socket) => {
    const redisSubscriber = createClient(redisUrl);
    let currentRoom = '';

    socket.on('subscribe', (id) => {
      currentRoom = `live:${id}:comments`;
      socket.join(currentRoom);

      redisSubscriber.subscribe(`${currentRoom}:latest`);

      const redis = createClient(redisUrl);
      redis.exists('live', id, (err, result) => {
        if (result < 2) {
          redis.lpush('live', id);
        }

        redis.quit();
      });

      socket.emit('subscribed');
    });

    redisSubscriber.on('message', (channel, message) => {
      socket.emit('comment', message);
    });

    socket.on('unsubscribe', (id) => {
      socket.leave(id);
      socket.emit('unsubscribed');
    });

    socket.on('disconnect', () => {
      redisSubscriber.unsubscribe(`${currentRoom}:latest`);
      redisSubscriber.quit();
    })
  });

export default io;

import SocketIO from 'socket.io';
import { createClient } from 'redis';

import { redisUrl } from './config';

const io = SocketIO();

io.use((socket, next) => {
  return next();
});

io.of('/live-chatroom')
  .use((socket, next) => {
    return next();
  })
  .on('connection', (socket) => {
    const redisCommentSubscriber = createClient(redisUrl);
    const redisProductSubscriber = createClient(redisUrl);
    let currentRoom = '';

    socket.on('subscribe', (id) => {
      currentRoom = `live:${id}`;
      socket.join(currentRoom);

      redisCommentSubscriber.subscribe(`${currentRoom}:comments:latest`);
      redisProductSubscriber.subscribe(`${currentRoom}:products:latest`);

      const redis = createClient(redisUrl);
      redis.exists('live', id, (err, result) => {
        if (result < 2) {
          redis.lpush('live', id);
        }

        redis.quit();
      });

      socket.emit('subscribed');
    });

    redisCommentSubscriber.on('message', (channel, message) => {
      socket.emit('comment', message);
    });

    redisProductSubscriber.on('message', (channel, message) => {
      socket.emit('product', JSON.parse(message));
    })

    socket.on('unsubscribe', (id) => {
      socket.leave(id);
      socket.emit('unsubscribed');
    });

    socket.on('disconnect', () => {
      redisCommentSubscriber.unsubscribe(`${currentRoom}:comments:latest`);
      redisCommentSubscriber.quit();

      redisProductSubscriber.unsubscrive(`${currentRoom}:products:latest`);
      redisProductSubscriber.quit();
    })
  });

export default io;

Rename redisSubscriber to redisCommentSubscriber for individual subscribers.


import thunk from 'redux-thunk'
import createLogger from 'redux-logger'
import { browserHistory } from 'react-router'
import { syncHistory } from 'redux-simple-router'
import { combineReducers, compose, createStore, applyMiddleware } from 'redux'
import { autoRehydrate } from 'redux-persist'
import { analytics, requester, uploader } from './middleware'
import * as reducers from './reducers'

const reducer = combineReducers(reducers)
const reduxRouterMiddleware = syncHistory(browserHistory)

let store = null
if (typeof window !== 'undefined') {
  const logger = createLogger({ collapsed: true, predicate: () => ENV.APP_DEBUG })
  store = compose(
    autoRehydrate(),
    applyMiddleware(thunk, reduxRouterMiddleware, uploader, requester, analytics, logger),
  )(createStore)(reducer, window.__INITIAL_STATE__ || {})
} else {
  store = compose(
    applyMiddleware(thunk, reduxRouterMiddleware, uploader, requester, analytics),
  )(createStore)(reducer, {})
}

export default store

import thunk from 'redux-thunk'
import createLogger from 'redux-logger'
import { browserHistory } from 'react-router'
import { syncHistory } from 'redux-simple-router'
import { combineReducers, compose, createStore, applyMiddleware } from 'redux'
import { autoRehydrate } from 'redux-persist'
import { analytics, requester, uploader } from './middleware'
import * as reducers from './reducers'

const reducer = combineReducers(reducers)
const reduxRouterMiddleware = syncHistory(browserHistory)

let store = null
if (typeof window !== 'undefined') {
  const logger = createLogger({ collapsed: true, predicate: () => ENV.APP_DEBUG })
  store = compose(
    autoRehydrate(),
    applyMiddleware(thunk, reduxRouterMiddleware, uploader, requester, analytics, logger),
  )(createStore)(reducer, window.__INITIAL_STATE__ || {})
} else {
  store = compose(
    applyMiddleware(thunk, uploader, requester, analytics),
  )(createStore)(reducer, {})
}

export default store

Fix rainbow and staging :boom:


import { useContext } from 'react';
import { getI18n, getHasUsedI18nextProvider, I18nContext } from './context';

export function useSSR(initialI18nStore, initialLanguage, props = {}) {
  const { i18n: i18nFromProps } = props;
  const { i18n: i18nFromContext } = getHasUsedI18nextProvider() ? useContext(I18nContext) : {};
  const i18n = i18nFromProps || i18nFromContext || getI18n();

  // opt out if is a cloned instance, eg. created by i18next-express-middleware on request
  // -> do not set initial stuff on server side
  if (i18n.options && i18n.options.isClone) return;

  // nextjs / SSR: getting data from next.js or other ssr stack
  if (initialI18nStore && !i18n.initializedStoreOnce) {
    i18n.services.resourceStore.data = initialI18nStore;
    i18n.initializedStoreOnce = true;
  }

  if (initialLanguage && !i18n.initializedLanguageOnce) {
    i18n.changeLanguage(initialLanguage);
    i18n.initializedLanguageOnce = true;
  }
}

import { useContext } from 'react';
import { getI18n, getHasUsedI18nextProvider, I18nContext } from './context';

export function useSSR(initialI18nStore, initialLanguage, props = {}) {
  const { i18n: i18nFromProps } = props;
  const ReactI18nContext = useContext(I18nContext);
  const { i18n: i18nFromContext } = getHasUsedI18nextProvider() ? ReactI18nContext : {};
  const i18n = i18nFromProps || i18nFromContext || getI18n();

  // opt out if is a cloned instance, eg. created by i18next-express-middleware on request
  // -> do not set initial stuff on server side
  if (i18n.options && i18n.options.isClone) return;

  // nextjs / SSR: getting data from next.js or other ssr stack
  if (initialI18nStore && !i18n.initializedStoreOnce) {
    i18n.services.resourceStore.data = initialI18nStore;
    i18n.initializedStoreOnce = true;
  }

  if (initialLanguage && !i18n.initializedLanguageOnce) {
    i18n.changeLanguage(initialLanguage);
    i18n.initializedLanguageOnce = true;
  }
}

Fix conditional usage of hooks error


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require_tree .

var update = React.addons.update

$(function() {
  var timezone = jstz.determine()
  cookies({timezone: timezone.name()})

  $('.ratingCircle').click(function(e) {
    console.log($(this).find('.ratingNumber').text())
    $('.ratingCircle').removeClass('active')
    $(this).addClass('active')

    $('#ratingInput').val($(this).find('.ratingNumber').text())
  })

  var removeFlash = function() {
    $('.flash').fadeOut()
  }

  setTimeout(removeFlash, 5000)
})

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require_tree .

var update = React.addons.update

$(document).on('turbolinks:load', function() {
  var timezone = jstz.determine()
  cookies({timezone: timezone.name()})

  $('.ratingCircle').click(function(e) {
    console.log($(this).find('.ratingNumber').text())
    $('.ratingCircle').removeClass('active')
    $(this).addClass('active')

    $('#ratingInput').val($(this).find('.ratingNumber').text())
  })

  var removeFlash = function() {
    $('.flash').fadeOut()
  }

  setTimeout(removeFlash, 5000)
})

Use turbolinks event handler instead of document ready.


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
$(document).ready(function() {
  if ($("#message_list").length) {
    setInterval( function() {
      $.getScript("/messages/message_table.js");
    }, 60000 );
  }
});

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
$(document).on("page:change", function() {
  if ($("#message_list").length) {
    setInterval( function() {
      $.getScript("/messages/message_table.js");
    }, 60000 );
  }
});

Modify to work with Turbolinks


'use strict';

function FakeDatabaseService() {

}

module.exports = FakeDatabaseService;

FakeDatabaseService.prototype = {
    run: function(query, callback) {
        return callback(null, {});
    },

    createCacheTable: function(node, callback) {
        return callback(null, true);
    },

    registerAnalysisInCatalog: function(analysis, callback) {
        return callback(null);
    },

    getMetadataFromAffectedTables: function(node, skip, callback) {
        return callback(null, 0);
    },

    queueAnalysisOperations: function(analysis, callback) {
        return callback(null);
    },

    trackAnalysis: function(analysis, callback) {
        return callback(null);
    },

    getColumnNames: function(query, callback) {
        return callback(null, []);
    },

    getColumns: function(query, callback) {
        return callback(null, []);
    },

    enqueue: function(query, callback) {
        return callback(null, {status: 'ok'});
    }
};

'use strict';

function FakeDatabaseService() {

}

module.exports = FakeDatabaseService;

FakeDatabaseService.prototype = {
    run: function(query, callback) {
        return callback(null, {});
    },

    createCacheTable: function(node, callback) {
        return callback(null, true);
    },

    registerAnalysisInCatalog: function(analysis, callback) {
        return callback(null);
    },

    getMetadataFromAffectedTables: function(node, skip, callback) {
        return callback(null, {
            last_update: 0,
            affected_tables: []
        });
    },

    queueAnalysisOperations: function(analysis, callback) {
        return callback(null);
    },

    trackAnalysis: function(analysis, callback) {
        return callback(null);
    },

    getColumnNames: function(query, callback) {
        return callback(null, []);
    },

    getColumns: function(query, callback) {
        return callback(null, []);
    },

    enqueue: function(query, callback) {
        return callback(null, {status: 'ok'});
    }
};

Fix fake db service API


import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: [
    { file: 'lib/index.cjs.js', format: 'cjs', exports: 'default' },
    { file: 'lib/index.esm.js', format: 'esm' },
  ],
  external: [/@babel\/runtime/],
  plugins: [
    babel({
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime'],
    }),
  ],
};

import babel from '@rollup/plugin-babel';

export default {
  input: 'src/index.js',
  output: [
    {
      exports: 'default',
      file: 'lib/index.cjs.js',
      format: 'cjs',
      sourcemap: true,
    },
    {
      file: 'lib/index.esm.js',
      format: 'esm',
      sourcemap: true,
    },
  ],
  external: [/@babel\/runtime/],
  plugins: [
    babel({
      babelHelpers: 'runtime',
      plugins: ['@babel/plugin-transform-runtime'],
    }),
  ],
};

Build source maps with rollup


let React = require("react")
let cx = require("classnames")
let {Focusable} = require("frig").HigherOrderComponents
let popup = React.createFactory(require("./timepicker_popup"))
let {errorList, sizeClassNames, formGroupCx, label} = require("../util.js")
let {savedNotification} = require("../util.js")
let {div, input} = React.DOM

@Focusable
export default class extends React.Component {

  displayName = "Frig.friggingBootstrap.TimePicker"

  static defaultProps = Object.assign(require("../default_props.js"))

  _inputCx() {
    return cx(
      this.props.inputHtml.className,
      "frigb-timepicker-input",
      "form-control",
    )
  }

  _input() {
    return input(Object.assign({}, this.props.inputHtml, {
        valueLink: this.props.valueLink,
        className: this._inputCx(),
      })
    )
  }

  _timePopup() {
    if(this.props.focused === false) return

    return popup({
      valueLink: this.props.valueLink,
    })
  }

  render() {
    return div({className: cx(sizeClassNames(this.props))},
      div({className: formGroupCx(this.props)},
        div({}, label(this.props)),
        this._input(),
        savedNotification({parentProps: this.props}),
        errorList(this.props.errors),
      ),
      this._timePopup(),
    )
  }

}

let React = require("react")
let cx = require("classnames")
let {Focusable} = require("frig").HigherOrderComponents
let popup = React.createFactory(require("./timepicker_popup"))
let {errorList, sizeClassNames, formGroupCx, label} = require("../util.js")
let {savedNotification} = require("../util.js")
let {div, input} = React.DOM

@Focusable
export default class extends React.Component {

  displayName = "Frig.friggingBootstrap.TimePicker"

  static defaultProps = Object.assign(require("../default_props.js"))

  _inputCx() {
    return cx(
      this.props.inputHtml.className,
      "frigb-timepicker-input",
      "form-control",
    )
  }

  _input() {
    return input(Object.assign({}, this.props.inputHtml, {
        valueLink: this.props.valueLink,
        className: this._inputCx(),
        onFocus: () => {
          return this.props.valueLink.requestChange(
            this.props.valueLink.value || "12:00pm"
          )
        },
      })
    )
  }

  _timePopup() {
    if(this.props.focused === false) return

    return popup({
      valueLink: this.props.valueLink,
    })
  }

  render() {
    return div({className: cx(sizeClassNames(this.props))},
      div({className: formGroupCx(this.props)},
        div({}, label(this.props)),
        this._input(),
        savedNotification({parentProps: this.props}),
        errorList(this.props.errors),
      ),
      this._timePopup(),
    )
  }

}

Add Default Value For Timepicker When Focus


var assertionModifiers = require('../src/modifiers/assertions');
var assert = require('assert');
var fs = require('fs');
var noOfData = 1;

var oneToTenArray = [];
for(var i = 1; i <= noOfData; i++) {
	oneToTenArray.push(i);
}

var testData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/actual.js').toString();
})

var expectedData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/expected.js').toString();
})

describe('Assertions modules', function () {
	it('should have a working assertion modifier', function () {
		for(var i = 0; i < noOfData; i++) {
			var result = assertionModifiers(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

var assertionsModifier = require('../src/modifiers/assertions');
var testsModifier = require('../src/modifiers/tests');
var assert = require('assert');
var fs = require('fs');
var noOfData = 2;

var oneToTenArray = [];
for(var i = 1; i <= noOfData; i++) {
	oneToTenArray.push(i);
}

var testData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/actual.js').toString();
})

var expectedData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/expected.js').toString();
})

describe('Assertions modules', function () {
	it('should have a working assertion modifier', function () {
		for(var i = 0; i < 1; i++) {
			var result = assertionsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

describe('Tests working', function () {
	it('should have a workign tests modifier', function () {
		for(var i = 1; i < 2; i++) {
			var result = testsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

Add tests for test modifier


import DBus from 'dbus';

try {
  const dbus = new DBus();
  const session = dbus.getBus('session');

  session.getInterface('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys',
  'org.gnome.SettingsDaemon.MediaKeys', (err, iface) => {
    if (!err) {
      iface.on('MediaPlayerKeyPressed', (n, keyName) => {
        switch (keyName) {
          case 'Next': Emitter.sendToGooglePlayMusic('playback:nextTrack'); return;
          case 'Previous': Emitter.sendToGooglePlayMusic('playback:previousTrack'); return;
          case 'Play': Emitter.sendToGooglePlayMusic('playback:playPause'); return;
          case 'Stop': Emitter.sendToGooglePlayMusic('playback:stop'); return;
          default: return;
        }
        iface.GrabMediaPlayerKeys(0, 'org.gnome.SettingsDaemon.MediaKeys'); // eslint-disable-line
      });
    }
  });
} catch (e) {
  // do nothing.
}

import DBus from 'dbus';

try {
  const dbus = new DBus();
  const session = dbus.getBus('session');

  session.getInterface('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys',
  'org.gnome.SettingsDaemon.MediaKeys', (err, iface) => {
    if (!err) {
      iface.on('MediaPlayerKeyPressed', (n, keyName) => {
        switch (keyName) {
          case 'Next': Emitter.sendToGooglePlayMusic('playback:nextTrack'); return;
          case 'Previous': Emitter.sendToGooglePlayMusic('playback:previousTrack'); return;
          case 'Play': Emitter.sendToGooglePlayMusic('playback:playPause'); return;
          case 'Stop': Emitter.sendToGooglePlayMusic('playback:stop'); return;
          default: return;
        }
      });
      iface.GrabMediaPlayerKeys(0, 'org.gnome.SettingsDaemon.MediaKeys'); // eslint-disable-line
    }
  });
} catch (e) {
  // do nothing.
}

Fix bug in media player keys ubuntu


// MODULES //

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import fs from 'fs';
import path, { join } from 'path';
import httpServer from '@stdlib/_tools/docs/www/server';
import App from './../src/server.jsx';

// VARIABLES //

let APP_SHELL_TEMPLATE = fs.readFileSync( path.resolve('./build/index.html' ), 'utf8' );
APP_SHELL_TEMPLATE.replace( '

', `<div id="root">${ReactDOMServer.renderToString( <App /> )}</div>` );

// MAIN //

const opts = {
	'port': 7331,
	'address': '0.0.0.0',
	'template': APP_SHELL_TEMPLATE,
	'root': join( __dirname, '..', 'build', 'assets', 'v0.0.87' ),
	'static': join( __dirname, '..', 'build' ),
	'logger': true
};
const createServer = httpServer( opts );

function done( error, fastify ) {
	if ( error ) {
		throw error;
	}
	console.log( 'Server is running...' );
}

createServer( done );

// MODULES //

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import fs from 'fs';
import path, { join } from 'path';
import httpServer from '@stdlib/_tools/docs/www/server';
import App from './../src/server.jsx';

// VARIABLES //

let APP_SHELL_TEMPLATE = fs.readFileSync( path.resolve('./build/index.html' ), 'utf8' );
APP_SHELL_TEMPLATE = APP_SHELL_TEMPLATE.replace( '

', `<div id="root">${ReactDOMServer.renderToString( <App /> )}</div>` );

// MAIN //

const opts = {
	'port': 7331,
	'address': '0.0.0.0',
	'template': APP_SHELL_TEMPLATE,
	'root': join( __dirname, '..', 'build', 'assets', 'v0.0.87' ),
	'static': join( __dirname, '..', 'build' ),
	'logger': true
};
const createServer = httpServer( opts );

function done( error, fastify ) {
	if ( error ) {
		throw error;
	}
	console.log( 'Server is running...' );
}

createServer( done );

Fix bug where APP_SHELL_TEMPLATE was not overwritten


import cuid from 'cuid';
import {storage} from '@/config/firebase';

export default function Image(img = {}) {
  this.file = img.file || null;
  this.downloadURL = img.downloadURL || null;
  this.id = img.id || cuid();

  // Methods
  this.url = function () {
    return this.file
      ? window.URL.createObjectURL(this.file)
      : this.downloadURL || null;
  };
  this.put = function () {
    if (!this.file) throw new Error('Can\'t upload image, no file specified');
    if (this.downloadURL) return new Promise((resolve, reject) => resolve({downloadURL: this.downloadURL}));

    return storage
      .ref(`/gallery/${this.id}`)
      .put(this.file);
  };
}

import cuid from 'cuid';
import { storage, database } from '@/config/firebase';

const getFileExtension = str => str.substring(str.lastIndexOf('.'), str.length);

export default function Image(img = {}) {
  this.file = img.file || null;
  this.downloadURL = img.downloadURL || null;
  this.id = img.id || cuid();

  const imagesRef = database.ref(`/images/gallery/${this.id}`);

  // Methods
  this.url = () => {
    return this.file
      ? window.URL.createObjectURL(this.file)
      : this.downloadURL || null;
  };

  this.put = () => {
    if (!this.file) throw new Error('Can\'t upload image, no file specified');
    if (this.downloadURL) return Promise.resolve({ downloadURL: this.downloadURL });

    return storage
      .ref(`/gallery/${this.id}${getFileExtension(this.file.name)}`)
      .put(this.file, { customMetadata: { id: this.id } });
  };

  this.getThumbnail = (width) => {
    return new Promise((resolve) => {
      imagesRef.child(width).once('value', snapshot => resolve(snapshot.val()));
    });
  }
}

Update image model to match new image database layout


/**
* Show model
* @module models/Show
*/

/**
* Show model - create and export the database model for shows
* including all assosiations and classmethods assiciated with this model.
* @memberof  module:models/Post
* @param  {Object} sequelize description
* @param  {Object} DataTypes description
*/
export default function (sequelize, DataTypes) {
    const Show = sequelize.define('show', {
        title: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        rssFeed: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        logoImage: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        lead: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        explicitContent: {
            type: DataTypes.BOOL,
            allowNull: false,
            defaultValue: false
        },
        language: {
            type: DataTypes.STRING(5),
            allowNull: false,
            defaultValue: 'no'
        }
    }, {
        classMethods: {
            associate(models) {
                Show.hasMany(models.Post, {
                    onDelete: 'cascade'
                });
                Show.hasMany(models.Episode, {
                    onDelete: 'cascade'
                });
            }
        }
    }
    );
    return Show;
}

/**
* Show model
* @module models/Show
*/

/**
* Show model - create and export the database model for shows
* including all assosiations and classmethods assiciated with this model.
* @memberof  module:models/Post
* @param  {Object} sequelize description
* @param  {Object} DataTypes description
*/
export default function (sequelize, DataTypes) {
    const Show = sequelize.define('show', {
        title: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        description: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        rssFeed: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        logoImage: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        lead: {
            type: DataTypes.STRING,
            allowNull: false,
            defaultValue: ''
        },
        explicitContent: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },
        archived: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },
        language: {
            type: DataTypes.STRING(5),
            allowNull: false,
            defaultValue: 'no'
        }
    }, {
        classMethods: {
            associate(models) {
                Show.hasMany(models.Post, {
                    onDelete: 'cascade'
                });
                Show.hasMany(models.Episode, {
                    onDelete: 'cascade'
                });
            }
        }
    }
    );
    return Show;
}

Add fields `archived` and `description`


/* eslint-env browser, mocha */
/* global expect, sinon, units */
/* eslint no-unused-expressions: 0 */

'use strict';

describe('units', function() {
  it('should be defined as an object', function() {
    expect(units).to.be.an('object');
  });
});

/* eslint-env browser, mocha */
/* global expect, sinon, units */
/* eslint no-unused-expressions: 0 */

'use strict';

describe('units', function() {
  var lengthUnits = ['%', 'ch', 'cm', 'em', 'ex', 'in', 'mm', 'pc', 'pt', 'px', 'rem', 'vh', 'vmax', 'vmin', 'vw'];
  var parseValid = function(value, unit) {
    expect(units.parse(value + unit)).to.deep.equal({'value': value, 'unit': unit});
  };

  it('should be defined as an object', function() {
    expect(units).to.be.an('object');
  });

  it('should parse valid length values', function() {
    for (var i = 0; i < lengthUnits.length; i++) {
      parseValid(0, lengthUnits[i]);
      parseValid(1, lengthUnits[i]);
      parseValid(-1, lengthUnits[i]);
      parseValid(9007199254740991, lengthUnits[i]);
      parseValid(-9007199254740991, lengthUnits[i]);
      parseValid(0.000001, lengthUnits[i]);
      parseValid(-0.000001, lengthUnits[i]);
    }
  });
});

Add valid length unit tests


var database = Ti.Database.install('twitter.sqlite', 'twitter');

/* PUBLIC */

exports.loadStoredTweets = function () {

	var tweets = [];

	// LOAD TWEETS FROM DATABASE HERE

	return tweets;

}

exports.loadNewTweets = function (callback) {

	var httpClient = Ti.Network.createHTTPClient();

	httpClient.open('GET', 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=craigmarvelley');

	httpClient.onload = function() {
		var tweets = JSON.parse(this.responseText);

		// SAVE TWEETS TO DATABASE HERE

		callback.call(this, tweets);
	}

	httpClient.send();

}

var database = Ti.Database.install('twitter.sqlite', 'twitter');

/* PUBLIC */

exports.loadStoredTweets = function () {

	var tweets = [];

	// LOAD STORED TWEETS HERE

	return tweets;

}

exports.loadNewTweets = function (callback) {

	var httpClient = Ti.Network.createHTTPClient();

	httpClient.open('GET', 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=craigmarvelley');

	httpClient.onload = function() {
		var tweets = JSON.parse(this.responseText);

		var sql = "INSERT OR REPLACE INTO tweets (id, tweet, created_at)"
			        + " VALUES (?, ?, ?)";

		for(var i=0, n=tweets.length; i<n; i++) {

			var tweet = tweets[i];

			database.execute(sql, tweet.id, JSON.stringify(tweet), tweet.created_at);

		}

		callback.call(this, tweets);
	}

	httpClient.send();

}

Save tweets to local database


const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpackFiles = require('../lib/build/files/webpack_files');
const Package = require('./../package.json');

const VERSION = Package.version;

module.exports = {
  entry: './lib/assets/javascripts/cartodb/static.js',
  output: {
    filename: `${VERSION}/javascripts/[name].js`,
    path: path.resolve(__dirname, '../public/assets'),
    publicPath: '/assets/'
  },
  devtool: 'source-map',
  plugins: Object.keys(webpackFiles).map((entryName) => {
    return new HtmlWebpackPlugin({
      inject: false,
      cache: false,
      filename: path.resolve(__dirname, `../public/static/${entryName}/index.html`),
      template: path.resolve(__dirname, '../lib/assets/javascripts/cartodb/static/index.jst.ejs'),
      config: webpackFiles[entryName]
    });
  })
};

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpackFiles = require('../lib/build/files/webpack_files');
const Package = require('./../package.json');

const VERSION = Package.version;

module.exports = {
  entry: './lib/assets/javascripts/dashboard/statics/static.js',
  output: {
    filename: `${VERSION}/javascripts/[name].js`,
    path: path.resolve(__dirname, '../public/assets'),
    publicPath: '/assets/'
  },
  devtool: 'source-map',
  plugins: Object.keys(webpackFiles).map((entryName) => {
    return new HtmlWebpackPlugin({
      inject: false,
      cache: false,
      filename: path.resolve(__dirname, `../public/static/${entryName}/index.html`),
      template: path.resolve(__dirname, '../lib/assets/javascripts/dashboard/statics/index.jst.ejs'),
      config: webpackFiles[entryName]
    });
  })
};

Fix production webpack for static pages


function reverseArray(arr) {
    var revArr = [];
    for (var i = 0; i < arr.length; ++i)
        revArr.unshift(arr[i]);
    return revArr;
}

function reverseArrayInPlace(arr) {
    for (var i = 0; i < arr.length/2; ++i) {
        var tmp = arr[i];
        arr[i] = arr[arr.length - 1 -i];
        arr[arr.length - 1 -i] = tmp;
    }
}

function reverseArray(array) {
    let newArray = [];
    for (let el of array) {
        newArray.unshift(el);
    }
    return newArray;
}

function reverseArrayInPlace(array) {
    for (let i = 0; i < Math.floor(array.length/2); ++i) {
        let temp = array[i];
        array[i] = array[array.length - 1 - i];
        array[array.length - 1 - i] = temp;
    }
}

Add Chapter 04, exercise 2


const path = require('path');
const expect = require('chai').expect;
const findPlugins = require('../src/findPlugins');
const mock = require('mock-require');

const pjsonPath = path.join(process.cwd(), 'package.json');

describe('findPlugins', () => {

  it('list of the plugins should be an array', () => {
    mock(pjsonPath, {
      dependencies: { 'rnpm-plugin-test': '*' },
    });
    expect(findPlugins(process.cwd())).to.be.an('array');
  });

  it('should return an empty array if there\'re no plugins in this folder', () => {
    mock(pjsonPath, {});
    expect(findPlugins(process.cwd())).to.be.empty;
  });

  it('should return plugins from both dependencies and dev dependencies', () => {
    mock(pjsonPath, {
      dependencies: { 'rnpm-plugin-test': '*' },
      devDependencies: { 'rnpm-plugin-test-2': '*' },
    });
    expect(findPlugins(process.cwd()).length).to.equals(2);
  });

  afterEach(mock.stopAll);

});

const path = require('path');
const expect = require('chai').expect;
const findPlugins = require('../src/findPlugins');
const mock = require('mock-require');

const pjsonPath = path.join(process.cwd(), 'package.json');

describe('findPlugins', () => {

  it('list of the plugins should be an array', () => {
    mock(pjsonPath, {
      dependencies: { 'rnpm-plugin-test': '*' },
    });
    expect(findPlugins(process.cwd())).to.be.an('array');
  });

  it('should return an empty array if there\'re no plugins in this folder', () => {
    mock(pjsonPath, {});
    expect(findPlugins(process.cwd())).to.be.empty;
  });

  it('should return plugins from both dependencies and dev dependencies', () => {
    mock(pjsonPath, {
      dependencies: { 'rnpm-plugin-test': '*' },
      devDependencies: { 'rnpm-plugin-test-2': '*' },
    });
    expect(findPlugins(process.cwd()).length).to.equals(2);
  });

  it('should return unique list of plugins', () => {
    mock(pjsonPath, {
      dependencies: { 'rnpm-plugin-test': '*' },
      devDependencies: { 'rnpm-plugin-test': '*' },
    });
    expect(findPlugins(process.cwd()).length).to.equals(1);
  });

  afterEach(mock.stopAll);

});

Add in one more test case


var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
  warn: function() {
    console.warn('The default generator for react-generator is currently empty. Check out the subgenerators.');
    this.config.save();
  }
});

var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
  warn: function() {
    this.log('The default generator for react-generator is currently empty. Check out the subgenerators.');
    this.config.save();
  }
});

Use this.log instead of console.warn in default generator


var assert = require("assert");
var ll = require('../algos/bubblesort');
var BubbleSorter = ll.BubbleSorter;

describe('BubbleSort', function() {
  describe('sort standard array', function() {
    it('should return an empty array after initialized', function() {
      var b = new BubbleSorter();
      assert.equal(JSON.stringify([1, 2, 3]), JSON.stringify(b.sort([3, 2, 1])));
    });
  });
});

var assert = require("assert");
var ll = require('../algos/bubblesort');
var BubbleSorter = ll.BubbleSorter;

describe('BubbleSort', function() {
  describe('sort standard array', function() {
    it('should sort a standard set of numbers', function() {
      var b = new BubbleSorter();
      assert.equal(JSON.stringify([1, 2, 3]), JSON.stringify(b.sort([3, 2, 1])));
    });
    it('should return [] for sorting []', function() {
      var b = new BubbleSorter();
      assert.equal(JSON.stringify([]), JSON.stringify(b.sort([])));
    });
    it('should return [1, 1, 2, 3] for sorting [1, 3, 2, 1]', function() {
      var b = new BubbleSorter();
      assert.equal(JSON.stringify([1, 1, 2, 3]), JSON.stringify(b.sort([1, 3, 2, 1])));
    });
  });
});

Add more bubble sort tests


import 'whatwg-fetch';

const apiBaseURL = 'http://localhost:1337';

export const bookshelfApi = {
  getBooks() {
    return fetch(`${apiBaseURL}/books`)
      .then(response => response.json())
      .catch(error =>
        console.error(
          'Something went awry trying to fetch books from bookshelf-server.',
          error
        )
      );
  }
};

import 'whatwg-fetch';

const apiBaseURL = 'http://localhost:1337';

function parseJSONFromResponse(response) {
  return response.json();
}

export const bookshelfApi = {
  getBooks() {
    return fetch(`${apiBaseURL}/books`)
      .then(parseJSONFromResponse)
      .catch(error =>
        console.error(
`Something went wrong trying to fetch books from bookshelf-server.
Make sure the bookshelf-server is started.`, error));
  }
};

Refactor services to use a function with a clear name instead of arrow function for parsing the response to JSON.


angular.module("proBebe.controllers")
.controller("AppCtrl", function($scope, storage, messageHandler, Profile, ScrollPositions) {

  $scope.$on('$ionicView.enter', init);

  var profile;

  function init(){
    reloadProfile();
  }

  function getChildren(){
    profile = storage.get("profile");
    $scope.children = profile.children;
  }

  function reloadProfile () {
    Profile.reloadChild()
    .then(function(success) {
      if(success) getChildren();
    }).catch(function(error) {
      messageHandler.show("Impossível recarregar o perfil");
    });
  }

  $scope.clearScroll = function(){
    ScrollPositions['maintain_scroll'] = false;
  }
});

angular.module("proBebe.controllers")
.controller("AppCtrl", function($scope, $rootScope, storage, messageHandler, Profile, ScrollPositions) {

  $scope.$on('$ionicView.enter', init);

  var profile;

  function init(){
    reloadProfile();
  }

  function getChildren(){
    profile = storage.get("profile");
    $scope.children = profile.children;
  }

  function reloadProfile () {
    Profile.reloadChild()
    .then(function(success) {
      if(success) getChildren();
    }).catch(function(error) {
      messageHandler.show("Impossível recarregar o perfil");
    });
  }

  $scope.clearScroll = function(){
    ScrollPositions['maintain_scroll'] = false;
  }

  $scope.openFilter = function(){
    console.log("calll")
    $rootScope.$emit("openFilter");
  }
});

Define emit event to open filter


'use strict';

var path = require('path');

var po2json = require('po2json');

module.exports = function (router) {

  router.get('/data', function (req, res) {
    var locale = (res.locals && res.locals.context && res.locals.context.locality) || 'en_US';
    var format = req.query['format'] || 'jed';

    po2json.parseFile(path.join(__dirname, '../../locale/', locale, 'messages.po'), {
      format: format,
      domain: 'coder-dojo-platform'
    }, function(err, data) {
      if (err) { throw err; }
      res.send(data);
    });

  });

};

'use strict';

var path = require('path');
var po2json = require('po2json');
var languages = require('../languages.js');

module.exports = function (router) {

  router.get('/data', function (req, res) {
    var locale = (res.locals && res.locals.context && res.locals.context.locality) || 'en_US';
    var format = req.query['format'] || 'jed';

    po2json.parseFile(path.join(__dirname, '../../locale/', locale, 'messages.po'), {
      format: format,
      domain: 'coder-dojo-platform'
    }, function(err, data) {
      if (err) { throw err; }
      res.send(data);
    });

  });

  router.get('/languages', function(req, res){
    res.send(languages);
  });

};

Add route for retrieving languages


define(['chai',
        'mocha',
        'projecteuler/001/multiples_of_three_and_five'],
       function(chai,
                unusedMocha,
                multiplesOfThreeAndFive) {
  'use strict';
  var expect = chai.expect;
  describe('multiplesOfThreeAndFive', function() {
    describe('bruteforce', function() {
      it('should solve small input', function() {
        expect(multiplesOfThreeAndFive.bruteforce(10)).to.equal(23);
      });
    });
  });
});

define(['chai',
        'mocha',
        'projecteuler/001/multiples_of_three_and_five'],
       function(chai,
                unusedMocha,
                multiplesOfThreeAndFive) {
  'use strict';
  var expect = chai.expect;
  describe('multiplesOfThreeAndFive', function() {
    describe('bruteforce', function() {
      it('should solve small input', function() {
        expect(multiplesOfThreeAndFive.bruteforce(10)).to.equal(23);
      });
      it('should solve large input', function() {
        expect(multiplesOfThreeAndFive.bruteforce(1000)).to.equal(233168);
      });
    });
  });
});

Add the large input test case.


import path from 'path';
import serverRequire from '../utils/server-require';

export default function loadCustomPresets({ configDir }) {
  const presets = serverRequire(path.resolve(configDir, 'presets'));
  const main = serverRequire(path.resolve(configDir, 'main'));

  return []
    .concat(presets || [])
    .concat(main ? main.presets || [] : [])
    .concat(main || []);
}

import path from 'path';
import dedent from 'ts-dedent';
import serverRequire from '../utils/server-require';

const toArray = a => (a ? [].concat(a) : a);

export default function loadCustomPresets({ configDir }) {
  const presets = serverRequire(path.resolve(configDir, 'presets'));
  const main = serverRequire(path.resolve(configDir, 'main'));

  if (presets && main) {
    throw new Error(dedent`
      You have both a "main.js" and a "presets.js", remove the "presets.js" file from your configDir (${path.resolve(
        configDir,
        'presets'
      )})`);
  }

  return [...(toArray(main ? main.presets : presets) || [])];
}

ADD fail condition when users migrate partially to triconfig also for presets.js


'use strict';

angular.module('classBrowserUHApp.offeredclass', ['ngRoute'])

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

.controller('OfferedClassCtrl', ['$scope', '$http', '$q', '$rootScope', function ($scope, $http, $q, $rootScope) {

    $rootScope.httpService.getData('resources/format.json').then(function(result) {
        $scope.format = result;
    });

    $rootScope.httpService.getData('resources/terms.json').then(function(result) {
        $scope.terms = result;
    });

    $scope.status = {
        availableOptions: [
            {
                status: "Open"
            },
            {
                status: "Closed"
            }
        ]
    };

    $scope.session = {
        availableOptions: [
            {
                session: 1,
                sessionTitle: "Regular Academic Session"
            },
            {
                session: 2,
                sessionTitle: "Session 2"
            },
            {
                session: 3,
                sessionTitle: "Session 3"
            },
            {
                session: 4,
                sessionTitle: "Session 4"
            },
            {
                session: 5,
                sessionTitle: "Session 5"
            },
            {
                session: 6,
                sessionTitle: "Session 6"
            },
            {
                session: "MIN",
                sessionTitle: "Mini Session"
            }
        ]
    }
}]);

'use strict';

angular.module('classBrowserUHApp.offeredclass', ['ngRoute'])

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

.controller('OfferedClassCtrl', ['$scope', '$http', '$q', '$rootScope', function ($scope, $http, $q, $rootScope) {

    $rootScope.httpService.getData('resources/format.json').then(function(result) {
        $scope.format = result;
    });

    $rootScope.httpService.getData('resources/terms.json').then(function(result) {
        $scope.terms = result;
    });

    $rootScope.httpService.getData('resources/status.json').then(function(result) {
        $scope.status = result;
    });

    $rootScope.httpService.getData('resources/session.json').then(function(result) {
        $scope.session = result;
    });
}]);

Set session and status scope variables through JSON data.


$(document).ready(function() {
    $("#searchfield").typeahead({
        minLength: 3,
        source: function(query, process) {
            $.post('/quicksearch', { q: query, limit: 8 }, function(data) {
                process(JSON.parse(data));
            });
        },
        updater: function (item) {
            document.location = "/" +item;
            return item;
        }
    });
});

$(document).ready(function() {
    $("#searchfield").typeahead({
        minLength: 2,
        source: function(query, process) {
            $.post('/quicksearch', { q: query, limit: 8 }, function(data) {
                process(JSON.parse(data));
            });
        },
        updater: function (item) {
            document.location = "/" +item;
            return item;
        },
        matcher: function (item) {
            return true;
        }
    });
});

Fix to make autocomplete field trust server results


import OsfSerializer from './osf-serializer';

export default OsfSerializer.extend({
    serialize(snapshot, options) {  // jshint ignore:line
        // Add relationships field to identify comment target
        let serialized = this._super(...arguments);

        // For POST requests specifically, two additional fields are needed
        let targetID = snapshot.record.get('targetID');
        let targetType = snapshot.record.get('targetType');
        // TODO: Find a better way to detect request type, so this can enforce fields that are needed for creation (but not for editing)
        //Ember.assert('Must provide target ID', targetID);
        //Ember.assert('Must provide target type', targetType);

        if (targetID && targetType) {
            serialized.data.relationships = {
                target: {
                    data: {
                        id: targetID,
                        type: targetType
                    }
                }
            };
        }
        return serialized;
    }
});

import Ember from 'ember';
import OsfSerializer from './osf-serializer';

export default OsfSerializer.extend({
    serialize(snapshot, options) {  // jshint ignore:line
        // Add relationships field to identify comment target
        let serialized = this._super(...arguments);

        // For POST requests specifically, two additional fields are needed
        let targetID = snapshot.record.get('targetID');
        let targetType = snapshot.record.get('targetType');
        // TODO: Find a better way to detect request type, so this can enforce fields that are needed for creation (but not for editing)
        //Ember.assert('Must provide target ID', targetID);
        //Ember.assert('Must provide target type', targetType);

        if (targetID && targetType) {
            serialized.data.relationships = {
                target: {
                    data: {
                        id: targetID,
                        type: targetType
                    }
                }
            };
        }
        return serialized;
    },
    extractRelationships(modelClass, resourceHash) {
	// TODO: remove when https://openscience.atlassian.net/browse/OSF-6646 is done
	resourceHash = this._super(modelClass, resourceHash);
	resourceHash.replies.links.related = Ember.copy(resourceHash.replies.links.self);
	return resourceHash;
    }
});

Add temporary fix for Comment.replies serialization


app.dependencies = ['store-common'];
app.server = function(ctx) {
    return {
        endpoints: {
            pages: [{
                title: 'Publisher | Servicex Page',
                url: 'servicex_global',
                path: 'servicex_global.jag'
            }, {
                title: 'Publisher | Servicex Splash page',
                url: 'splash',
                path: 'servicex_splash.jag'
            }]
        },
        configs:{
            landingPage:'/assets/process/list',
            disabledAssets:['gadget','site','ebook', 'api', 'wsdl', 'service','policy','proxy','schema','sequence','servicex','uri','wadl','endpoint','swagger','restservice','comments','soapservice']
        }
    }
};

app.dependencies = ['store-common'];
app.server = function(ctx) {
    return {
        endpoints: {
            pages: [{
                title: 'Publisher | Servicex Page',
                url: 'servicex_global',
                path: 'servicex_global.jag'
            }, {
                title: 'Publisher | Servicex Splash page',
                url: 'splash',
                path: 'servicex_splash.jag'
            }]
        },
        configs:{
            landingPage:'/assets/process/list',
            disabledAssets:['package','gadget','site','ebook', 'api', 'wsdl', 'service','policy','proxy','schema','sequence','servicex','uri','wadl','endpoint','swagger','restservice','comments','soapservice']
        }
    }
};

Disable package asset from store


var assert = require('chai').assert;

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

describe('Dummy worker', function() {
  describe('#hello()', function() {
    it('should return "hello"', function() {
      assert.equal(worker.hello(), 'hello');
    });
  });
});

var assert = require('chai').assert;

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

describe('LMLayerWorker', function() {
  describe('#constructor()', function() {
    it('should construct with zero arguments', function() {
      assert.isOk(new worker.LMLayerWorker);
    });
  });
});

Test whether the LMLayerWorker can be instantiated.


export const flagOrgs = {
  ENUMERATORS: new Set([43, 42]),
  NUFFIC: new Set([4531, 5255, 5722]),
  AKVO_USERS: new Set([42])
}

export const shouldShowFlag = (orgs, orgSet) => {
  return orgs && orgs.findIndex(it => orgSet.has(it.id) || orgSet.has(it.contentOwner)) !== -1
}

export const isRSRTeamMember = (userRdr) => {
  return userRdr.isSuperuser
}

export const isRSRAdmin = (userRdr) => {
  return userRdr?.isAdmin || userRdr?.isSuperuser
}

export const flagOrgs = {
  ENUMERATORS: new Set([43, 42, 6002]),
  NUFFIC: new Set([4531, 5255, 5722]),
  AKVO_USERS: new Set([42])
}

export const shouldShowFlag = (orgs, orgSet) => {
  return orgs && orgs.findIndex(it => orgSet.has(it.id) || orgSet.has(it.contentOwner)) !== -1
}

export const isRSRTeamMember = (userRdr) => {
  return userRdr.isSuperuser
}

export const isRSRAdmin = (userRdr) => {
  return userRdr?.isAdmin || userRdr?.isSuperuser
}

Enable enumrator for Wemos (Make way programme)


/**
 * Created by v0n000p on 2/19/17.
 */
const path = require('path');
const webpack = require('webpack');
const isProduction = (process.env.NODE_ENV === 'production');

console.log('isProduction', isProduction);

const plugins = [];
const loaders = [
    { test: /\.tsx?$/, loader: 'ts-loader' }
];
if(isProduction){
    plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    )
}else {

}
module.exports = {
    entry: path.resolve(__dirname, '../src/containers/index.ts'),
    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: '[name].js'
    },
    resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: ['.ts', '.tsx', '.js'] // note if using webpack 1 you'd also need a '' in the array as well
    },
    module:{
        loaders:loaders
    },
    plugins:plugins
};

/**
 * Created by v0n000p on 2/19/17.
 */
const path = require('path');
const webpack = require('webpack');
const isProduction = (process.env.NODE_ENV === 'production');

console.log('isProduction', isProduction);

const plugins = [];
const loaders = [
    { test: /\.tsx?$/, loader: 'ts-loader' }
];
if(isProduction){
    plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    )
}else {

}
module.exports = {
    entry: path.resolve(__dirname, './src/containers/index.ts'),
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: '[name].js'
    },
    resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: ['.ts', '.tsx', '.js'] // note if using webpack 1 you'd also need a '' in the array as well
    },
    module:{
        loaders:loaders
    },
    plugins:plugins
};

Change dist folder to match after move


var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

var nodeModules = {};

// This is to filter out node_modules as we don't want them
// to be made part of any bundles.
fs.readdirSync('node_modules')
  .filter(function(x) {
    return ['.bin'].indexOf(x) === -1;
  })
  .forEach(function(mod) {
    // jscs:disable requireTemplateStrings
    nodeModules[mod] = 'commonjs ' + mod;
    // jscs:enable requireTemplateStrings
  });

module.exports = {
  entry: './src/main.js',
  target: 'node',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'dispensary.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        exclude: /(node_modules|bower_components)/,
        test: /\.js$/,
        // babel options are in .babelrc
        use: [
          {loader: 'babel-loader'}
        ],
      },
      {
        test: /\.txt$/,
        use: [
          {loader: 'raw-loader'}
        ],
      },
    ],
  },
  externals: nodeModules,
  plugins: [
    new webpack.BannerPlugin({
      banner: 'require("source-map-support").install();',
      raw: true,
      entryOnly: false,
    }),
  ],
  resolve: {
    extensions: ['.js', '.json'],
    modules: [
      'node_modules',
    ],
  },
  devtool: 'sourcemap',
};

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

var nodeModules = {};

// This is to filter out node_modules as we don't want them
// to be made part of any bundles.
fs.readdirSync('node_modules')
  .filter(function(x) {
    return ['.bin'].indexOf(x) === -1;
  })
  .forEach(function(mod) {
    // jscs:disable requireTemplateStrings
    nodeModules[mod] = 'commonjs ' + mod;
    // jscs:enable requireTemplateStrings
  });

module.exports = {
  mode: 'production',
  entry: './src/main.js',
  target: 'node',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'dispensary.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        exclude: /(node_modules|bower_components)/,
        test: /\.js$/,
        // babel options are in .babelrc
        use: [
          {loader: 'babel-loader'}
        ],
      },
      {
        test: /\.txt$/,
        use: [
          {loader: 'raw-loader'}
        ],
      },
    ],
  },
  externals: nodeModules,
  plugins: [
    new webpack.BannerPlugin({
      banner: 'require("source-map-support").install();',
      raw: true,
      entryOnly: false,
    }),
  ],
  resolve: {
    extensions: ['.js', '.json'],
    modules: [
      'node_modules',
    ],
  },
  devtool: 'sourcemap',
};

Set webpack mode to silence warning


var webpack = require('webpack');

var config = {
  context: __dirname + '/src',

  entry: {
    'rails-ranger':     './rails-ranger.js',
    'rails-ranger.min': './rails-ranger.js'
  },

  devtool: 'source-map',

  output: {
    path: __dirname + '/dist',
    filename: '[name].js'
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: { presets: ['babel-preset-es2015'].map(require.resolve) }
      }
    ]
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      include: /\.min\.js$/,
      minimize: true
    })
  ]
}

module.exports = config;

var webpack = require('webpack');

var config = {
  context: __dirname + '/src',

  entry: {
    'rails-ranger':     './rails-ranger.js',
    'rails-ranger.min': './rails-ranger.js'
  },

  devtool: 'source-map',

  output: {
    path:          __dirname + '/dist',
    filename:      '[name].js',
    library:       '',
    libraryTarget: 'commonjs-module'
  },

  module: {
    rules: [
      {
        test:   /\.js$/,
        loader: 'babel-loader',
        query: { presets: ['babel-preset-es2015'].map(require.resolve) }
      }
    ]
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      include:  /\.min\.js$/,
      minimize: true
    })
  ]
}

module.exports = config;

Fix library import on webpack


import Ember from 'ember';
// import $ from 'jquery';

export default Ember.Component.extend({
  expense: {
    sum: null,
    category: '',
    name: ''
  },
  currency: '£',
  expenseCategories: [
    'Charity',
    'Clothing',
    'Education',
    'Events',
    'Food',
    'Gifts',
    'Healthcare',
    'Household',
    'Leisure',
    'Hobbies',
    'Trasportation',
    'Utilities',
    'Vacation'
  ],

  errorMessagesSum: {
    emRequired: 'This field can\'t be blank',
    emPattern: 'Must be a number!'
  },

  errorMessagesCategory: {
    emRequired: 'Must select a category'
  },
  didInsertElement () {
    componentHandler.upgradeAllRegistered();
  },

  actions: {
    clearInputs () {
      this.$('.mdl-textfield input[type=text]').val('');
      this.$('.mdl-textfield').removeClass('is-dirty');
    },
    addExpense () {
      this.sendAction('action', this.get('expense'));
      this.send('clearInputs');
    }
  }
});

import Ember from 'ember';
// import $ from 'jquery';

export default Ember.Component.extend({
  expense: {
    sum: '',
    category: '',
    name: ''
  },
  currency: '£',
  expenseCategories: [
    'Charity',
    'Clothing',
    'Education',
    'Events',
    'Food',
    'Gifts',
    'Healthcare',
    'Household',
    'Leisure',
    'Hobbies',
    'Trasportation',
    'Utilities',
    'Vacation'
  ],

  errorMessagesSum: {
    emRequired: 'This field can\'t be blank',
    emPattern: 'Must be a number!'
  },

  errorMessagesCategory: {
    emRequired: 'Must select a category'
  },
  didInsertElement () {
    componentHandler.upgradeAllRegistered();
  },

  actions: {
    clearInputs () {
      this.set('expense', {
        sum: '',
        category: '',
        name: ''
      });
      this.$('.mdl-textfield').removeClass('is-dirty is-invalid is-touched');
    },
    addExpense () {
      this.sendAction('action', this.get('expense'));
      this.send('clearInputs');
    }
  }
});

Reset input values in controller when clear button clicked


const path = require('path');
const webpack = require('webpack');
const BabiliPlugin = require('babili-webpack-plugin');

module.exports = {
    entry: path.resolve(__dirname, 'src/index.ts'),
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'react-layout-transition.js',
        library: 'ReactLayoutTransition',
        libraryTarget: 'umd',
    },
    externals: {
        'react': {
            commonjs: 'react',
            commonjs2: 'react',
            root: 'React',
        },
    },
    module: {
        rules: [
            {
                test: /\.tsx|ts?$/,
                loader: 'awesome-typescript-loader',
                exclude: path.resolve(__dirname, 'node_modules'),
            },
        ],
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production'),
        }),
        new BabiliPlugin({}, {
            removeComments: true,
        }),
    ],
};

const path = require('path');
const webpack = require('webpack');
const BabiliPlugin = require('babili-webpack-plugin');

const isProd = process.env.NODE_ENV === 'PRODUCTION';

const outputFilename = isProd ? 'react-layout-transition.min.js' : 'react-layout-transition.js';

module.exports = {
    devtool: !isProd ? 'source-map' : '',
    entry: path.resolve(__dirname, 'src/index.ts'),
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: outputFilename,
        library: 'ReactLayoutTransition',
        libraryTarget: 'umd',
    },
    externals: {
        'react': {
            commonjs: 'react',
            commonjs2: 'react',
            root: 'React',
        },
    },
    module: {
        rules: [
            {
                test: /\.tsx|ts?$/,
                loader: 'awesome-typescript-loader',
                exclude: path.resolve(__dirname, 'node_modules'),
            },
        ],
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
    },
    plugins: isProd ? [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production'),
        }),
        new BabiliPlugin({}, {
            removeComments: true,
        }),
    ] : [],
};

Add minified and non-minified umd build


/**
*
* DragHandle
*
*/

import React from 'react';

import IconButton from 'material-ui/IconButton';
import DragHandleIcon from 'material-ui/svg-icons/editor/drag-handle';

const style = {
  position: 'absolute',
  top: -10,
  left: -10,
  zIndex: 9999,
  opacity: 0.4,
};

const handleMouseEnter = e => e.target.style.opacity = 0.9; // eslint-disable-line
const handleMouseLeave = e => e.target.style.opacity = 0.4; // eslint-disable-line

const DragHandle = () => (
  <IconButton
    tooltip="Drag Me!"
    tooltipPosition="top-right"
    className="drag handle"
    style={style}
    disableTouchRipple
    onMouseEnter={handleMouseEnter}
    onMouseLeave={handleMouseLeave}
  >
    <DragHandleIcon />
  </IconButton>
);

export default DragHandle;

/**
*
* DragHandle
*
*/

import React from 'react';

import IconButton from 'material-ui/IconButton';
import DragHandleIcon from 'material-ui/svg-icons/editor/drag-handle';

const style = {
  cursor: 'move',
  position: 'absolute',
  top: -10,
  left: -10,
  zIndex: 9999,
  opacity: 0.4,
};

const handleMouseEnter = e => e.target.style.opacity = 0.9; // eslint-disable-line
const handleMouseLeave = e => e.target.style.opacity = 0.4; // eslint-disable-line

const DragHandle = () => (
  <IconButton
    tooltip="Drag Me!"
    tooltipPosition="top-right"
    className="drag handle"
    style={style}
    disableTouchRipple
    onMouseEnter={handleMouseEnter}
    onMouseLeave={handleMouseLeave}
  >
    <DragHandleIcon />
  </IconButton>
);

export default DragHandle;

Fix webrtc drag handle style


(function(Modules) {
  "use strict";

  Modules.SelectAll = function() {
    var that = this;

    that.start = function(element) {
      addEventListener();

      function addEventListener() {
        var selectAll = element.find('#select_all');
        if (selectAll != undefined) {
          selectAll.on('change', function(e) {
            var checked = selectAll.prop('checked');
            var checkboxes = $('.select-content-item');
            checkboxes.prop('checked', checked);
          });
        }
      }
    }
  };
})(window.GOVUKAdmin.Modules);

(function(Modules) {
  "use strict";

  Modules.SelectAll = function() {
    var that = this;

    that.start = function(element) {
      function addEventListener() {
        var selectAll = element.find('#select_all');
        if (selectAll != undefined) {
          selectAll.on('change', function(e) {
            var checked = selectAll.prop('checked');
            var checkboxes = $('.select-content-item');
            checkboxes.prop('checked', checked);
          });
        }
      }

      addEventListener();
    }
  };
})(window.GOVUKAdmin.Modules);

Fix 'addEventListener' used before it was defined


import { callController } from '../../util/apiConnection';

export const getAvailableRoles = () => {
    const route = '/roles/available';
    const prefix = 'AVAILABLEROLES_GET_ALL_';
    return callController(route, prefix);
}

export const saveRole = (role) => {
    const route = '/roles';
    const prefix = 'ROLE_SAVE_ONE_';
    const method = 'post';
    return callController(route, prefix, role, method);
}

export const deleteRole = (role) => {
    const route = `/roles/${role.personRoleId}`;
    const prefix = 'ROLE_DELETE_ONE_';
    const method = 'delete';
    return callController(route, prefix, role, method);
}

export const updateRole = (role) => {
    const route = '/roles';
    const prefix = 'ROLE_UPDATE_ONE_';
    const method = 'put';
    return callController(route, prefix, role, method);
}

export const updateVisitorRoles = (role) => {
    const route = '/roles/visitor';
    const prefix = 'ROLE_VISITOR_UPDATE_';
    const method = 'put';
    return callController(route, prefix, role, method);
}

Remove unused visitor role handing from code.


"use strict";

angular.module('arethusa.core').factory('StateChange', [
  '$parse',
  function($parse) {
    function getToken(state, tokenOrId) {
      if (angular.isObject(tokenOrId)) {
        return tokenOrId;
      } else {
        return state.getToken(tokenOrId);
      }
    }

    return function(state, tokenOrId, property, newVal, undoFn, preExecFn) {
      var self = this;

      this.token = getToken(state, tokenOrId);
      this.property = property;
      this.get = $parse(property);
      this.set = self.get.assign;
      this.newVal = newVal;
      this.oldVal = self.get(self.token);
      this.time = new Date();

      function inverse() {
        state.change(self.token, property, self.oldVal);
      }

      this.undo   = function() {
        return angular.isFunction(undoFn) ? undoFn : inverse;
      };

      this.exec   = function() {
        if (angular.isFunction(preExecFn)) preExecFn();

        self.set(self.token, self.newVal);
        return self;
      };
    };
  }
]);

"use strict";

angular.module('arethusa.core').factory('StateChange', [
  '$parse',
  function($parse) {
    function getToken(state, tokenOrId) {
      if (angular.isObject(tokenOrId)) {
        return tokenOrId;
      } else {
        return state.getToken(tokenOrId);
      }
    }

    function buildMessage(token, property, newVal, oldVal) {
      // We should check here if newVal and oldVal are objects, otherwise
      // the user won't see anything helpful. Ideally these objects come
      // with a toString function we could check for?
      return "Changed " + property +" of "+
        token.string + " at " + token.id +
        " from " +oldVal +" to " + newVal;
    }

    return function(state, tokenOrId, property, newVal, undoFn, preExecFn) {
      var self = this;

      this.token = getToken(state, tokenOrId);
      this.property = property;
      this.get = $parse(property);
      this.set = self.get.assign;
      this.newVal = newVal;
      this.oldVal = self.get(self.token);
      this.time = new Date();

      this.message = buildMessage(self.token, self.property, self.newVal, self.oldVal);

      function inverse() {
        state.change(self.token, property, self.oldVal);
      }

      this.undo   = function() {
        return angular.isFunction(undoFn) ? undoFn : inverse;
      };

      this.exec   = function() {
        if (angular.isFunction(preExecFn)) preExecFn();

        self.set(self.token, self.newVal);
        return self;
      };
    };
  }
]);

Add a human-readable message to StateChange


/*
 *
 */

var cordova = require('cordova');
var execProxy = require('cordova/exec/proxy');

module.exports = function (success, fail, service, action, args) {
  if ((service === 'Device') && (action === 'getDeviceInfo')) {
    success({
      platform: 'PhantomJS',
      version: '1.9.7',
      uuid: 'BD905752-CCBB-4C35-BF7D-178E2F7930B8',
      model: ''
    });
  } else if (success) {
    success();
  }
};

/*
 *
 */

var cordova = require('cordova');
var execProxy = require('cordova/exec/proxy');

module.exports = function (success, fail, service, action, args) {
  if ((service === 'Device') && (action === 'getDeviceInfo')) {
    success({
      platform: 'PhantomJS',
      version: '1.9.7',
      uuid: 'BD905752-CCBB-4C35-BF7D-178E2F7930B8',
      model: ''
    });
  } else if ((service === 'NetworkStatus') && (action === 'getConnectionInfo')) {
    success('wifi');
  } else if (success) {
    success();
  }
};

Return wifi as network status.


'use strict';

var atan2 = require( './../lib' );

var x;
var y;
var i;

for ( i = 0; i < 100; i++ ) {
	x = Math.random() * 100;
	y = Math.random() * 100;
	console.log( 'x: %d, \t y: %d, \t atan2(y,x): %d',
		x.toFixed( 4 ),
		y.toFixed( 4 ),
		atan2( x, y ).toFixed( 4 )
	);
}

'use strict';

var atan2 = require( './../lib' );

var x;
var y;
var i;

for ( i = 0; i < 100; i++ ) {
	y = Math.random() * 100;
	x = Math.random() * 100;
	console.log( 'y: %d, \t x: %d, \t atan2(y,x): %d',
		y.toFixed( 4 ),
		x.toFixed( 4 ),
		atan2( y, x ).toFixed( 4 )
	);
}

Fix argument order in example code