/**
 * @fileOverview Communities CRUD controller.
 */

// var log = require('logg').getLogger('app.ctrl.city.Dashboard');
var ControllerBase = require('nodeon-base').ControllerBase;
var CrudeEntity = require('crude-entity');

var CommunityEnt = require('../../entities/community.ent');

/**
 * Communities CRUD controller.
 *
 * @contructor
 * @extends {app.ControllerBase}
 */
var Dashboard = module.exports = ControllerBase.extendSingleton(function(){
  this.crude = new CrudeEntity('/community', CommunityEnt);
  this.crude.config({
    idField: '_id',
    pagination: false,
    entityCreate: 'createApi',
    entityReadOne: 'readOneApi',
    entityReadLimit: 'readLimitApi',
    entityUpdate: 'updateApi',
  });
});

/**
 * @fileOverview Communities CRUD controller.
 */

// var log = require('logg').getLogger('app.ctrl.city.Dashboard');
var ControllerBase = require('nodeon-base').ControllerBase;
var CrudeEntity = require('crude-entity');

var CommunityEnt = require('../../entities/community.ent');
var CommunityMidd = require('../../middleware/communities.midd');

/**
 * Communities CRUD controller.
 *
 * @contructor
 * @extends {app.ControllerBase}
 */
var Community = module.exports = ControllerBase.extendSingleton(function () {
  this.crude = new CrudeEntity('/community', CommunityEnt);
  this.crude.config({
    idField: '_id',
    pagination: false,
    entityCreate: 'createApi',
    entityReadOne: 'readOneApi',
    entityReadLimit: 'readLimitApi',
    entityUpdate: 'updateApi',
  });

  var communityMidd = CommunityMidd.getInstance();

  this.add = [
    this._add.bind(this),
  ];
  this.del = [
    communityMidd.populateSingleFromParam.bind(communityMidd),
    this._del.bind(this),
  ];
  this.edit = [
    communityMidd.populateSingleFromParam.bind(communityMidd),
    this._edit.bind(this),
  ];
});

/**
 * The add an item form.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 */
Community.prototype._add = function(req, res) {
  res.render('/city/community/add');
};

/**
 * The del an item form.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 */
Community.prototype._del = function(req, res) {
  res.render('/city/community/del');
};

/**
 * The edit an item form.
 *
 * @param {Object} req The request Object.
 * @param {Object} res The response Object.
 */
Community.prototype._edit = function(req, res) {
  res.render('/city/community/edit');
};

Add CRUD forms on community controller


var http = require('http');
var url = require('url');
var port = 8000;

var server = http.createServer(function (request, response){

    try {
		//go to http://127.0.0.1:8000/?req=Monty
		console.log('Server Pinged');	//Ping the server (will show in the cmd prompt)
		//The below will be written to the Web Browser
		response.writeHead(200, {"Content-Type": "text/plain"});
		var req = checkURL(request); 	//Passes request into the checkURL function and stores output into the req variable
		var resp;
		if(req === "Aaron"){
			resp = "Gavendo";
		}
		else if(req === "Monty"){
			resp = "python";
		}
		else if(req === "Hello"){
			resp = "World";
		}
		else{
			resp = "Please Enter: Aaron, Monty or Hello";
		}
		response.write(resp);			//Write the response on the web page.
		response.end();
    }
    catch (e) {
        response.writeHead(500, { 'content-type': 'text/plain' });
        response.write('ERROR:' + e);
        response.end('\n');
    }

});
server.listen(port);

console.log('The server has run');		//This is for the cmd prompt. Runs once at the start.

function checkURL(request){
	var phrase = url.parse(request.url, true).query;
	// this checks for a query with a property called 'req' and returns its value.
	return phrase.req;
}

var http = require('http');
var url = require('url');
var port = 8000;

var server = http.createServer(function (request, response){
 try {
  //go to http://127.0.0.1:8000/?req=Hello
  console.log('Server Pinged');
  response.writeHead(200, {"Content-Type": "text/plain"});
  var req = checkURL(request);
  var resp;
  if(req === "Hello"){
   resp = "World";
  }
  else{
   resp = "Please Enter: Aaron, Monty or Hello";
  }
  response.write(resp);
  response.end();
 }
 catch(e) {
  response.writeHead(500, { 'content-type': 'text/plain' });
  response.write('ERROR:' + e);
  response.end('\n');
 }
});
server.listen(port);

console.log('The server has run');

function checkURL(request){
 var phrase = url.parse(request.url, true).query;
 // this checks for a query with a property called 'req' and returns its value.
 return phrase.req;
}

Fix Space and comment issues.


/*
 * @flow
 */

import React from 'react';

export default function Navbar() {
  return (

            Bonsai - Trim your dependency trees

              Docs

              Github

  );
}

/*
 * @flow
 */

import React from 'react';

export default function Navbar() {
  return (

           {
            event.preventDefault();
            window.location.reload();
          }}>
            Bonsai - Trim your dependency trees

              Docs

              Github

  );
}

Set the navbar to reload, no matter the url.


( function() {

	"use strict";

	angular.module('ACBank')
		.controller('AccountsController', AccountsController);

	AccountsController.$inject = [ '$scope', '$ionicModal' ];

	function AccountsController ($scope, $ionicModal) {
		var vm = this;

		vm.options = [
			{
				iconClass: "ion-plus-round",
				imageSource: "",
				clicked: function () { $scope.addAccountModal.show(); }
			},
			{
				iconClass: "",
				imageSource: "../img/withdraw.png",
				clicked: function () { console.log("Withdraw"); }
			},
			{
				iconClass: "",
				imageSource: "../img/deposit.png",
				clicked: function () { $scope.depositModal.show(); }
			}
		];

		$scope.accounts = [];

		$ionicModal.fromTemplateUrl('../templates/NL-Modals/Add Account/add-account-modal.html', {
    		scope: $scope,
    		animation: 'slide-in-up'
  		}).then(function(modal) {
    		$scope.addAccountModal = modal;
  		});

  		$ionicModal.fromTemplateUrl('../templates/NL-Modals/Deposit/deposit-modal.html', {
    		scope: $scope,
    		animation: 'slide-in-up'
  		}).then(function(modal) {
    		$scope.depositModal = modal;
  		});
	}

})();

( function() {

	"use strict";

	angular.module('ACBank')
		.controller('AccountsController', AccountsController);

	AccountsController.$inject = [ '$scope', '$ionicModal' ];

	function AccountsController ($scope, $ionicModal) {
		var vm = this;

		vm.options = [
			{
				iconClass: "ion-plus-round",
				imageSource: "",
				clicked: function () { $scope.addAccountModal.show(); }
			},
			{
				iconClass: "",
				imageSource: "../img/withdraw.png",
				clicked: function () { $scope.withdrawModal.show(); }
			},
			{
				iconClass: "",
				imageSource: "../img/deposit.png",
				clicked: function () { $scope.depositModal.show(); }
			}
		];

		$scope.accounts = [];

		$ionicModal.fromTemplateUrl('../templates/NL-Modals/Add Account/add-account-modal.html', {
    		scope: $scope,
    		animation: 'slide-in-up'
  		}).then(function(modal) {
    		$scope.addAccountModal = modal;
  		});

  		$ionicModal.fromTemplateUrl('../templates/NL-Modals/Deposit/deposit-modal.html', {
    		scope: $scope,
    		animation: 'slide-in-up'
  		}).then(function(modal) {
    		$scope.depositModal = modal;
  		});

  		$ionicModal.fromTemplateUrl('../templates/NL-Modals/Withdraw/withdraw-modal.html', {
    		scope: $scope,
    		animation: 'slide-in-up'
  		}).then(function(modal) {
    		$scope.withdrawModal = modal;
  		});
	}

})();

Create modal for withdraw form


var SolidityUtils = {

  getCharacterOffsetToLineAndColumnMapping: function(source) {
    var mapping = [];

    source = source.split("");

    var line = 0;
    var column = 0;

    source.forEach(function(character) {
      if (character == "\n") {
        line += 1;
        column = -1;

        mapping.push({
          line: line,
          column: 0
        });
      } else {
        mapping.push({
          line: line,
          column: column
        });
      }

      column += 1;
    });

    return mapping;
  },

  getHumanReadableSourceMap: function(sourceMap) {
    var map = sourceMap.split(';');

    var last = {};

    return map.map(function(current) {
      var ret = {
        start: last.start,
        length: last.length,
        file: last.file,
        jump: last.jump
      };

      current = current.split(':');

      if (current[0] && current[0] !== '-1' && current[0].length) {
        ret.start = parseInt(current[0]);
      }
      if (current[1] && current[1] !== '-1' && current[1].length) {
        ret.length = parseInt(current[1]);
      }
      if (current[2] /*&& current[2] !== '-1'*/ && current[2].length) {
        ret.file = parseInt(current[2]);
      }
      if (current[3] && current[3].length) {
        ret.jump = current[3];
      }

      last = ret;

      return ret;
    });
  }
};

module.exports = SolidityUtils;

var SolidityUtils = {

  getCharacterOffsetToLineAndColumnMapping: function(source) {
    var mapping = [];

    source = source.split("");

    var line = 0;
    var column = 0;

    source.forEach(function(character) {
      if (character === "\n") {
        line += 1;
        column = -1;

        mapping.push({
          line: line,
          column: 0
        });
      } else {
        mapping.push({
          line: line,
          column: column
        });
      }

      column += 1;
    });

    return mapping;
  },

  getHumanReadableSourceMap: function(sourceMap) {
    var map = sourceMap.split(';');

    var last = {};

    return map.map(function(current) {
      var ret = {
        start: last.start,
        length: last.length,
        file: last.file,
        jump: last.jump
      };

      current = current.split(':');

      if (current[0] && current[0] !== '-1' && current[0].length) {
        ret.start = parseInt(current[0]);
      }
      if (current[1] && current[1] !== '-1' && current[1].length) {
        ret.length = parseInt(current[1]);
      }
      if (current[2] /*&& current[2] !== '-1'*/ && current[2].length) {
        ret.file = parseInt(current[2]);
      }
      if (current[3] && current[3].length) {
        ret.jump = current[3];
      }

      last = ret;

      return ret;
    });
  }
};

module.exports = SolidityUtils;

Convert Non-strict to strict equality checking Convert non-strict equality checking, using `==`, to the strict version, using `===`.


import gulpLoadPlugins from 'gulp-load-plugins';
import runSequence from 'run-sequence';

import * as paths from './paths';
import gulp from './_gulp';
import jspmBuild from './utils';

const $ = gulpLoadPlugins();

gulp.task('dist:jspm', ['compile:styles'], () =>
  jspmBuild({
    minify: true,
    mangle: true,
    sourceMaps: false
  })
);

gulp.task('dist:js', (callback) =>
  runSequence('dist:jspm', 'js:replace_paths', callback)
);

gulp.task('dist:html', () =>
  gulp.src(paths.SRC_INDEX)
  .pipe($.htmlReplace({'js': 'scripts/main.js'}))
  .pipe($.minifyHtml({empty: true}))
  .pipe(gulp.dest(paths.BUILD_DIR))
);

gulp.task('dist:copy', () => {
  const htmlFilter = $.filter("**/*.!(html)", {restore: true});

  return gulp.src(paths.BUILD_ALL)
  .pipe(htmlFilter)
  .pipe($.rev())
  .pipe(htmlFilter.restore)
  .pipe($.revReplace())
  .pipe(gulp.dest(paths.DIST_DIR));
});

gulp.task('dist', (callback) =>
  runSequence(
    ['clean:build', 'clean:dist', 'utils:copy_to_tmp'],
    ['dist:js', 'dist:html', 'build:images', 'build:fonts'],
    'dist:copy',
    callback
  )
);

import gulpLoadPlugins from 'gulp-load-plugins';
import runSequence from 'run-sequence';

import * as paths from './paths';
import gulp from './_gulp';
import jspmBuild from './utils';

const $ = gulpLoadPlugins();

gulp.task('dist:jspm', ['compile:styles'], () =>
  jspmBuild({
    minify: true,
    mangle: true,
    sourceMaps: false
  })
);

gulp.task('dist:js', (callback) =>
  runSequence('dist:jspm', 'js:replace_paths', callback)
);

gulp.task('dist:html', () =>
  gulp.src(paths.SRC_INDEX)
  .pipe($.htmlReplace({'js': 'scripts/main.js'}))
  .pipe($.minifyHtml({empty: true}))
  .pipe(gulp.dest(paths.BUILD_DIR))
);

gulp.task('dist:copy', () => {
  const htmlFilter = $.filter('**/*.!(html)', {restore: true});

  return gulp.src(paths.BUILD_ALL)
  .pipe(htmlFilter)
  .pipe($.rev())
  .pipe(htmlFilter.restore)
  .pipe($.revReplace())
  .pipe(gulp.dest(paths.DIST_DIR))
  .pipe($.gzip())
  .pipe(gulp.dest(paths.DIST_DIR));
});

gulp.task('dist', (callback) =>
  runSequence(
    ['clean:build', 'clean:dist', 'utils:copy_to_tmp'],
    ['dist:js', 'dist:html', 'build:images', 'build:fonts'],
    'dist:copy',
    callback
  )
);

Make sure we gzip our assets for deployment


'use strict';

const
    RELOAD_KEY = 'safari-reload-193289123',
    WAIT_TIME = 100; // Does it matter how long to wait?

/**
 * Safari desktop audio is being dumb and won't play until a second page loads an audio file within a session.
 * Uses browser detection: http://stackoverflow.com/a/23522755
 */
exports.check = function () {
    let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
    if (isSafari) {
        if (navigator.cookieEnabled === false) {
            showError(1);
        } else {
            if (sessionStorage !== undefined && sessionStorage.getItem(RELOAD_KEY) !== 'y') {
                sessionStorage.setItem(RELOAD_KEY, 'y');
                if (sessionStorage.getItem(RELOAD_KEY) === 'y') {
                    setTimeout(() => { document.location.reload(false); }, WAIT_TIME);
                } else {
                    showError(2);
                }
            } else {
                showError(3);
            }
        }
    }
};

function showError(code) {
    console.error('Cannot set Safari reload key - sound might not work until reloaded manually: ' + code);
}

'use strict';

const
    RELOAD_KEY = 'safari-reload-193289123',
    WAIT_TIME = 100; // Does it matter how long to wait?

/**
 * Safari desktop audio is being dumb and won't play until a second page loads an audio file within a session.
 * Uses browser detection: http://stackoverflow.com/a/23522755
 *
 * This still, doesn't always work.
 */
exports.check = function () {
    let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
    if (isSafari) {
        if (navigator.cookieEnabled === false) {
            showError(1);
        } else {
            if (sessionStorage !== undefined) {
                if (sessionStorage.getItem(RELOAD_KEY) !== 'y') {
                    sessionStorage.setItem(RELOAD_KEY, 'y');
                    if (sessionStorage.getItem(RELOAD_KEY) === 'y') {
                        setTimeout(() => { document.location.reload(false); }, WAIT_TIME);
                    } else {
                        showError(2);
                    }
                } else {
                    // Was already set, so do nothing.
                }
            } else {
                showError(3);
            }
        }
    }
};

function showError(code) {
    console.error('Cannot set Safari reload key - sound might not work until reloaded manually: ' + code);
}

Fix error message logging in Safari bandaid


'use strict';

/**
 *  client
 **/

/**
 *  client.common
 **/

/**
 *  client.common.auth
 **/

/**
 *  client.common.auth.register
 **/

/*global $, _, nodeca, window*/

/**
 *  client.common.auth.register.exec($form, event)
 *
 *  send registration data on server
 **/
module.exports = function ($form, event) {
  var params = nodeca.client.common.form.getData($form);

  nodeca.server.users.auth.register.exec(params, function(err){
    var message;

    if (err) {
      if (err.statusCode === 409) {
        // clear pass
        params.pass = '';
        // add errors
        params.errors = err.message;
        $form.replaceWith(
          nodeca.client.common.render('users.auth.register.view', params)
        ).fadeIn();
        return;
      }
      message = nodeca.runtime.t('common.error.server_internal');
      nodeca.client.common.notice('error', message);
      return;
    }

    $form.replaceWith(
      nodeca.client.common.render('users.auth.register.success')
    ).fadeIn();
  });

  // Disable regular click
  return false;
};

'use strict';

/**
 *  client
 **/

/**
 *  client.common
 **/

/**
 *  client.common.auth
 **/

/**
 *  client.common.auth.register
 **/

/*global $, _, nodeca, window*/

/**
 *  client.common.auth.register.exec($form, event)
 *
 *  send registration data on server
 **/
module.exports = function ($form, event) {
  var params = nodeca.client.common.form.getData($form);

  nodeca.server.users.auth.register.exec(params, function(err){
    var message;

    if (err) {
      if (err.statusCode === 409) {
        // clear pass
        params.pass = '';
        // add errors
        params.errors = err.message;
        nodeca.client.common.render.page('users.auth.register.view', params);
        return;
      }
      message = nodeca.runtime.t('common.error.server_internal');
      nodeca.client.common.notice('error', message);
      return;
    }

    nodeca.client.common.render.page('users.auth.register.success');
  });

  // Disable regular click
  return false;
};

Use render.page for full-page reload


import "ol/ol.css";
import "./style.css";
import listMap from "./listMap";
import itemMap from "./itemMap";

export default {
  listMap,
  itemMap,
};

import "ol/ol.css";
import "./style.css";

export async function listMap() {
  const listMapModule = (await import("./listMap")).default;

  listMapModule.apply(null, arguments);
}

export async function itemMap() {
  const itemMapModule = (await import("./itemMap")).default;

  itemMapModule.apply(null, arguments);
}

Revert "fix: chunk load errors on legacy map"


import loop from 'loop';
import StatusBar from 'statusBar';
import BattleMap from 'map';
import state from 'state';
import { subtractSet } from 'pura/vector/tuple';
import { canvasOffsetLeft, canvasOffsetTop, scaleX, scaleY } from 'dom';
import { render as renderUI, uiElements, clearUi } from 'ui';
import { calcWorldPosition } from 'camera';
import { clear } from 'pura/canvas/tuple';

let stopLoop;

const calcMousePosition =
  ({ clientX, clientY, touches }) =>
    touches
      ? ([touches[0].clientX, touches[0].clientY])
      : ([clientX, clientY]);

export default {
  init() {
    uiElements.push(BattleMap, StatusBar);
    stopLoop = loop(dt => {
      clear();
      renderUI();
    });
    window.addEventListener('click', (evt) => {
      const position = subtractSet(calcMousePosition(evt), [canvasOffsetLeft, canvasOffsetTop]);
      position[0] *= scaleX;
      position[1] *= scaleY;
      state.target = calcWorldPosition(position);
    });
  },
  dismiss() {
    stopLoop();
  }
}

import loop from 'loop';
import StatusBar from 'statusBar';
import BattleMap from 'map';
import state from 'state';
import { inputs } from 'controls';
import { subtractSet } from 'pura/vector/tuple';
import { canvasOffsetLeft, canvasOffsetTop, scaleX, scaleY } from 'dom';
import { render as renderUI, uiElements, clearUi } from 'ui';
import { calcWorldPosition } from 'camera';
import { clear } from 'pura/canvas/tuple';

export default {
  init() {
    uiElements.push(BattleMap, StatusBar);
    state.logic = () => {
      if (inputs.mouse === 1) {
        state.target = calcWorldPosition(inputs.mousePosition);
      }
    };
  },
  dismiss() { }
}

Update battle map with input handling


'use strict';

var request = require('request');

var opts = {
    uri: 'https://api.github.com' + '/users/jm-janzen/events',
    json: true,
    method: 'GET',
    headers: {
        'User-Agent': 'nodejs script'
    }
}

request(opts, function (err, res, body) {
    if (err) throw new Error(err);

    var format = '[%s]: %s %s %s %s';

    for (var i = 0; i < body.length; i++) {
        console.log(format
            , body[i].created_at
            , body[i].type
            , body[i].actor.login
            , body[i].repo.name);
    }
});

'use strict';

var request = require('request');

var opts = {
    uri: 'https://api.github.com' + '/users/' + process.argv[2] + '/events',
    json: true,
    method: 'GET',
    headers: {
        'User-Agent': 'nodejs script'
    }
}

request(opts, function (err, res, body) {
    if (err) throw new Error(err);

    var format = '[%s]: %s %s %s %s';

    for (var i = 0; i < body.length; i++) {
        console.log(format
            , body[i].created_at
            , body[i].type
            , body[i].actor.login
            , body[i].repo.name);
    }
});

Add user-name as parameter, rather than hardcoding


Mixer = function () {
	this.mix = []
}

//Adds a track to the mix. Takes args = {url: sound.aws.com, divId: 1}
Mixer.prototype.addTrack = function (args) {
	var newTrack = new Howl({
		url : [args['url']]
	});
	this.mix[args['divId']] = newTrack
}

Mixer.prototype.removeTrack = function (arrayPosition) {
	this.mix[arrayPosition] = null
}

Mixer = function () {
	this.mix = []
}

//Adds a track to the mix. Takes args = {url: sound.aws.com, divId: 1}
Mixer.prototype.addTrack = function (args) {
	var newTrack = new Howl({
		url : [args['url']]
	});
	this.mix[args['divId']] = newTrack;
}

Mixer.prototype.removeTrack = function (arrayPosition) {
	this.mix[arrayPosition] = null;
}

Mixer.prototype.globalPlay = function () {
	for (var i = 0; i< this.mix.length; i++) {
		this.mix[i].play()
	}
}

Mixer.prototype.globalPause = function () {
	for (var i = 0; i< this.mix.length; i++) {
		this.mix[i].pause()
	}
}

Write globalPause and globalPlay methods for Mixer model


'use strict';

angular.module('arachne.controllers')

/**
 * Set new password.
 *
 * @author: Daniel M. de Oliveira
 */
    .controller('PwdActivationController',
        ['$scope', '$stateParams', '$filter', '$location', 'PwdActivation', 'messageService',
            function ($scope, $stateParams, $filter, $location, PwdActivation, messages) {

                /**
                 * Copy the user so that the shown passwords
                 * of user object in $scope do not get modified
                 *
                 * @param user
                 */
                var copyUser = function (user) {

                    var newUser = JSON.parse(JSON.stringify(user));

                    if (user.password)
                        newUser.password = $filter('md5')(user.password);
                    if (user.passwordConfirm)
                        newUser.passwordConfirm = $filter('md5')(user.passwordConfirm);

                    return newUser;
                };

                var handleActivationSuccess = function () {

                    messages.dontClearOnNextLocationChange();
                    messages.add('ui.passwordactivation.success', 'success');
                    $location.path("/");
                };

                var handleActivationError = function (data) {
                    console.log(data);
                    messages.add('default', 'danger');
                };

                $scope.submit = function () {
                    PwdActivation.save({token: $stateParams.token},
                        copyUser($scope.user),
                        handleActivationSuccess,
                        handleActivationError
                    );
                }
            }]);

'use strict';

angular.module('arachne.controllers')

/**
 * Set new password.
 *
 * @author: Daniel M. de Oliveira
 */
    .controller('PwdActivationController',
        ['$scope', '$stateParams', '$filter', '$location', 'PwdActivation', 'messageService',
            function ($scope, $stateParams, $filter, $location, PwdActivation, messages) {

                /**
                 * Copy the user so that the shown passwords
                 * of user object in $scope do not get modified
                 *
                 * @param user
                 */
                var copyUser = function (user) {

                    var newUser = JSON.parse(JSON.stringify(user));

                    if (user.password)
                        newUser.password = $filter('md5')(user.password);
                    if (user.passwordConfirm)
                        newUser.passwordConfirm = $filter('md5')(user.passwordConfirm);

                    return newUser;
                };

                var handleActivationSuccess = function () {

                    messages.dontClearOnNextLocationChange();
                    messages.add('ui.passwordactivation.success', 'success');
                    $location.path("/");
                };

                var handleActivationError = function (data) {
                    console.log(data);
                    messages.add('ui.register.passwordsDontMatch', 'danger');
                };

                $scope.submit = function () {
                    PwdActivation.save({token: $stateParams.token},
                        copyUser($scope.user),
                        handleActivationSuccess,
                        handleActivationError
                    );
                }
            }]);

Change password reset error message


'use strict';

import React, { Component } from 'react';
import ReactCSS from 'reactcss';

import colors from '../styles/variables/colors';
import 'normalize.css';
import '../fonts/work-sans/WorkSans.css!';
import '../styles/felony.css!';

import Header from './header/Header';
import EncryptKeyList from './encrypt/EncryptKeyList';
import FloatingButtonToggle from './floating-button/FloatingButtonToggle';

export class Felony extends Component {
  classes() {
    return {
      'default': {
        app: {
          Absolute: '0 0 0 0',
          background: colors.bgLight,
        },
        header: {
          position: 'fixed',
          top: 0,
          left: 0,
          right: 0,
        },
        encryptKeyList: {
          position: 'fixed',
          top: 78,
          left: 0,
          right: 0,
          bottom: 0,
          overflowY: 'scroll', // TODO: elastic scroll
        },
      },
    };
  }

  render() {
    return (

          <EncryptKeyList />

        <FloatingButtonToggle />

    );
  }
}

export default ReactCSS(Felony);

'use strict'

import 'normalize.css'
import React, { Component } from 'react'
import ReactCSS  from 'reactcss'

import '../fonts/work-sans/WorkSans.css!'
import '../styles/felony.css!'
import colors  from '../styles/variables/colors'

import EncryptKeyListContainer  from '../containers/EncryptKeyListContainer'
import FloatingButtonToggle  from './floating-button/FloatingButtonToggle'
import Header  from './header/Header'

export class Felony extends Component {
  classes() {
    return {
      'default': {
        app: {
          absolute: '0 0 0 0',
          background: colors.bgLight,
        },
        header: {
          position: 'fixed',
          top: 0,
          left: 0,
          right: 0,
        },
        encryptKeyList: {
          position: 'fixed',
          top: 78,
          left: 0,
          right: 0,
          bottom: 0,
          overflowY: 'scroll', // TODO: elastic scroll
        },
      },
    }
  }

  render() {
    return (

          <EncryptKeyListContainer />

        <FloatingButtonToggle />

    )
  }
}

export default ReactCSS(Felony)

Use react key list container


module.exports = function() {

    return this
            .title('Benchmarks')
            .helpful()
            .arg()
                .name('treeish-list')
                .title('List of revisions to compare (git treeish)')
                .arr()
                .end()
            .opt()
                .name('benchmarks')
                .short('b').long('benchmark')
                .arr()
                .title('List of benchmarks to run')
                .end()
            .opt()
                .name('no-wc')
                .short('w').long('no-wc')
                .flag()
                .title('Run benchmarks without using working copy, use only specified revisions')
                .end()
            .opt()
                .name('rerun')
                .long('rerun')
                .flag()
                .title('Reuse previously checked out and made revisions, run benchmarks only')
                .end()
            .opt()
                .name('techs')
                .long('techs')
                .short('t')
                .title('Tech to run testing')
                .arr()
                .end()
            .opt()
                .name('rme')
                .short('r').long('rme')
                .title('Delta for RME')
                .end()
            .opt()
                .name('delay')
                .short('d').long('delay')
                .title('Delay between benchmarks')
                .end()
            .act(function(opts,args) {
                return require('../bench')(opts, args).start();
            })
        .end();
};

module.exports = function() {

    return this
            .title('Benchmarks')
            .helpful()
            .arg()
                .name('treeish-list')
                .title('List of revisions to compare (git treeish)')
                .arr()
                .end()
            .opt()
                .name('benchmarks')
                .short('b').long('benchmark')
                .arr()
                .title('List of benchmarks to run')
                .end()
            .opt()
                .name('no-wc')
                .short('w').long('no-wc')
                .flag()
                .title('Run benchmarks without using working copy, use only specified revisions')
                .end()
            .opt()
                .name('rerun')
                .long('rerun')
                .flag()
                .title('Reuse previously checked out and made revisions, run benchmarks only')
                .end()
            .opt()
                .name('techs')
                .long('techs')
                .short('t')
                .title('Techs to run tests for')
                .arr()
                .end()
            .opt()
                .name('rme')
                .short('r').long('rme')
                .title('Delta for RME')
                .end()
            .opt()
                .name('delay')
                .short('d').long('delay')
                .title('Delay between benchmarks')
                .end()
            .act(function(opts,args) {
                return require('../bench')(opts, args).start();
            })
        .end();
};

Change a title of the option


// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: config.angularCli && config.angularCli.codeCoverage
      ? ['progress', 'coverage-istanbul']
      : ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html

module.exports = function (config) {
  configuration = {
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: config.angularCli && config.angularCli.codeCoverage
      ? ['progress', 'coverage-istanbul']
      : ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    customLaunchers: {
        Chrome_travis_ci: {
            base: 'Chrome',
            flags: ['--no-sandbox']
        }
    },
  };

  // Update browser configuration for Travis CI
  if (process.env.TRAVIS) {
      configuration.browsers = ['Chrome_travis_ci'];
  }

  config.set(configuration);
};

Configure custom karma browser launcher for travis


Meteor.startup(function () {

  console.log('Running server startup code...');

  Accounts.onCreateUser(function (options, user) {
    Roles.addRolesToUserObj(user, ['admin','view-secrets']);

    if (options.profile) {
      // include the user profile
      user.profile = options.profile
    }

    // other user object changes...
    // ...

    return user;
  });

});

Meteor.startup(function () {

  console.log('Running server startup code...');

  Accounts.onCreateUser(function (options, user) {
    Roles.setRolesOnUserObj(user, ['admin','view-secrets']);

    if (options.profile) {
      // include the user profile
      user.profile = options.profile
    }

    // other user object changes...
    // ...

    return user;
  });

});

Change example method to properly reflect action taken


// Simple, slimline template based on https://github.com/rackt/react-router/blob/master/CHANGES.md

import Default from './Default'

export default class Compact extends Default {
  mergesTitle = null
  fixesTitle = null
  commitsTitle = null

  fixPrefix = 'Fixed '
  mergePrefix = 'Merged '

  listSpacing = '\n'

  renderReleaseHeading = (release, previousRelease) => {
    const title = this.renderReleaseTitle(release, previousRelease)
    const date = release.tag ? `\n> ${formatDate(release.date)}` : ''
    return `### ${title}${date}\n`
  }
}

function formatDate (string) {
  const date = new Date(string)
  const day = date.getDate()
  const month = date.toLocaleString('en', { month: 'long' })
  const year = date.getFullYear()
  return `${day} ${month} ${year}`
}

// Simple, slimline template based on https://github.com/rackt/react-router/blob/master/CHANGES.md

import Default from './Default'

const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

export default class Compact extends Default {
  mergesTitle = null
  fixesTitle = null
  commitsTitle = null

  fixPrefix = 'Fixed '
  mergePrefix = 'Merged '

  listSpacing = '\n'

  renderReleaseHeading = (release, previousRelease) => {
    const title = this.renderReleaseTitle(release, previousRelease)
    const date = release.tag ? `\n> ${formatDate(release.date)}` : ''
    return `### ${title}${date}\n`
  }
}

function formatDate (string) {
  const date = new Date(string)
  const day = date.getDate()
  const month = months[date.getMonth()]
  const year = date.getFullYear()
  return `${day} ${month} ${year}`
}

Replace toLocaleString usage with months array


// Requires
var wireFriendly = require('../utils').wireFriendly;

function setup(options, imports, register) {
    // Import
    var events = imports.events;
    var io = imports.socket_io.io;

    // Send events to user
    io.of('/events').on('connection', function(socket) {
        // Send to client
        var handler = function(data) {
            socket.emit(this.event, wireFriendly(data));
        };

        // Clean up on disconnect
        var cleanup = function() {
            events.offAny(handler);
        };

        // Construct
        events.onAny(handler);

        // Disconnect cleanly
        socket.on('disconnect', cleanup);

    });

    // Register
    register(null, {});
}

// Exports
module.exports = setup;

// Requires
var wireFriendly = require('../utils').wireFriendly;

function setup(options, imports, register) {
    // Import
    var events = imports.events;
    var io = imports.socket_io.io;

    // Send events to user
    io.of('/events').on('connection', function(socket) {
        // Send to client
        var handler = function(data) {
            socket.emit("event", {
                "event": this.event,
                "data": wireFriendly(data)
            });
        };

        // Clean up on disconnect
        var cleanup = function() {
            events.offAny(handler);
        };

        // Construct
        events.onAny(handler);

        // Disconnect cleanly
        socket.on('disconnect', cleanup);

    });

    // Register
    register(null, {});
}

// Exports
module.exports = setup;

Change event propagation in socket.io


Hubzero.submitbutton = function(task) {
	$(document).trigger('editorSave');

	var frm = document.getElementById('item-form');

	if (frm) {
		if (task == 'cancel' || document.formvalidator.isValid(frm)) {
			Hubzero.submitform(task, frm);
		} else {
			alert(frm.getAttribute('data-invalid-msg'));
		}
	}
}

jQuery(document).ready(function ($) {
	$('#btn-batch-submit')
		.on('click', function (e){
			Hubzero.submitbutton('article.batch');
		});

	$('#btn-batch-clear')
		.on('click', function (e){
			e.preventDefault();
			$('#batch-category-id').val('');
			$('#batch-access').val('');
			$('#batch-language-id').val('');
		});
});

Hubzero.submitbutton = function(task) {
	var frm = document.getElementById('adminForm');

	if (frm) {
		return Hubzero.submitform(task, frm);
	}

	$(document).trigger('editorSave');

	var frm = document.getElementById('item-form');

	if (frm) {
		if (task == 'cancel' || document.formvalidator.isValid(frm)) {
			Hubzero.submitform(task, frm);
		} else {
			alert(frm.getAttribute('data-invalid-msg'));
		}
	}
}

jQuery(document).ready(function ($) {
	$('#btn-batch-submit')
		.on('click', function (e){
			Hubzero.submitbutton('article.batch');
		});

	$('#btn-batch-clear')
		.on('click', function (e){
			e.preventDefault();
			$('#batch-category-id').val('');
			$('#batch-access').val('');
			$('#batch-language-id').val('');
		});
});

Add support for submitbutton on listing page


// @flow
import React, { Component, PropTypes } from 'react';
import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
import Settings from '../../components/settings/Settings';
import ProfileSettings from '../../components/settings/categories/ProfileSettings';

@observer(['state', 'controller'])
export default class ProfileSettingsPage extends Component {

  static propTypes = {
    state: PropTypes.shape({
      account: PropTypes.shape({
        userAccount: PropTypes.shape({
          profile: MobxPropTypes.observableObject.isRequired
        }).isRequired,
        isLoading: PropTypes.bool.isRequired
      }).isRequired
    }).isRequired,
    controller: PropTypes.shape({
      account: PropTypes.shape({
        updateName: PropTypes.func.isRequired
      }).isRequired
    }).isRequired
  };

  render() {
    const { profile } = this.props.state.account.userAccount;
    const { isLoading } = this.props.state.account;
    const { controller } = this.props;
    if (isLoading) return
Loading
;
    return (
      <Settings>
        <ProfileSettings
          profile={profile}
          onFieldValueChange={(field, name) => controller.account.updateField(field, name)}
        />
      </Settings>
    );
  }

}

// @flow
import React, { Component, PropTypes } from 'react';
import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
import Settings from '../../components/settings/Settings';
import ProfileSettings from '../../components/settings/categories/ProfileSettings';

@observer(['state', 'controller'])
export default class ProfileSettingsPage extends Component {

  static propTypes = {
    state: PropTypes.shape({
      account: PropTypes.shape({
        userAccount: PropTypes.shape({
          profile: MobxPropTypes.observableObject.isRequired
        }).isRequired,
        isLoading: PropTypes.bool.isRequired
      }).isRequired
    }).isRequired,
    controller: PropTypes.shape({
      account: PropTypes.shape({
        updateField: PropTypes.func.isRequired
      }).isRequired
    }).isRequired
  };

  render() {
    const { profile } = this.props.state.account.userAccount;
    const { isLoading } = this.props.state.account;
    const { controller } = this.props;
    if (isLoading) return
Loading
;
    return (
      <Settings>
        <ProfileSettings
          profile={profile}
          onFieldValueChange={(field, name) => controller.account.updateField(field, name)}
        />
      </Settings>
    );
  }

}

Fix profile settings page props type checking


IRG.constants = (function(){

    var approvedDomains = function(){
        return ["imgur.com", "flickr.com", "cdn.diycozyhome.com", "pbs.twimg.com", "msnbc.com",
            "fbcdn-sphotos-f-a.akamaihd.net", "flic.kr", "instagram.com", "deviantart.com",
            "s-media-cache-ak0.pinimg.com", "gfycat.com", "gifbeam.com", "staticflickr.com", "imgflip.com",
            "upload.wikimedia.org", "livememe.com", "tumblr.com", "wikipedia.org", "archive.is", "imgrush.com",
            "deviantart.net", "awwni.me", "i."];
    };

    var messages = {
        error: {
            aProblem : "There's been a problem!",
            noImagesFound: "No images were found."
        },
        info: {
            enterSubreddits: "Please enter your subreddits."
        }
    };

    return{
        approvedDomains: approvedDomains(),
        messages: messages
    };
})();

IRG.config = (function(){
    var redditLocation = "https://www.reddit.com";

    return{
        redditLocation: redditLocation
    };
})();

IRG.constants = (function(){

    var approvedDomains = function(){
        return ["imgur.com", "flickr.com", "cdn.diycozyhome.com", "pbs.twimg.com", "msnbc.com",
            "fbcdn-sphotos-f-a.akamaihd.net", "flic.kr", "instagram.com", "deviantart.com",
            "s-media-cache-ak0.pinimg.com", "gfycat.com", "gifbeam.com", "staticflickr.com", "imgflip.com",
            "upload.wikimedia.org", "livememe.com", "tumblr.com", "wikipedia.org", "archive.is", "imgrush.com",
            "deviantart.net", "awwni.me", "i.", "fbcdn.net"];
    };

    var messages = {
        error: {
            aProblem : "There's been a problem!",
            noImagesFound: "No images were found."
        },
        info: {
            enterSubreddits: "Please enter your subreddits."
        }
    };

    return{
        approvedDomains: approvedDomains(),
        messages: messages
    };
})();

IRG.config = (function(){
    var redditLocation = "https://www.reddit.com";

    return{
        redditLocation: redditLocation
    };
})();

Add a URL to the whitelist


IRG.constants = (function(){

    var approvedDomains = function(){
        return ["imgur.com", "flickr.com", "cdn.diycozyhome.com", "pbs.twimg.com", "msnbc.com",
            "fbcdn-sphotos-f-a.akamaihd.net", "flic.kr", "instagram.com", "deviantart.com",
            "s-media-cache-ak0.pinimg.com", "gfycat.com", "gifbeam.com", "staticflickr.com", "imgflip.com",
            "upload.wikimedia.org", "livememe.com", "tumblr.com", "wikipedia.org", "archive.is", "imgrush.com"];
    };

    var messages = {
        error: {
            aProblem : "There's been a problem!",
            noImagesFound: "No images were found."
        },
        info: {
            enterSubreddits: "Please enter your subreddits."
        }
    };

    return{
        approvedDomains: approvedDomains(),
        messages: messages
    };
})();

IRG.config = (function(){

})();

IRG.constants = (function(){

    var approvedDomains = function(){
        return ["imgur.com", "flickr.com", "cdn.diycozyhome.com", "pbs.twimg.com", "msnbc.com",
            "fbcdn-sphotos-f-a.akamaihd.net", "flic.kr", "instagram.com", "deviantart.com",
            "s-media-cache-ak0.pinimg.com", "gfycat.com", "gifbeam.com", "staticflickr.com", "imgflip.com",
            "upload.wikimedia.org", "livememe.com", "tumblr.com", "wikipedia.org", "archive.is", "imgrush.com",
            "deviantart.net"];
    };

    var messages = {
        error: {
            aProblem : "There's been a problem!",
            noImagesFound: "No images were found."
        },
        info: {
            enterSubreddits: "Please enter your subreddits."
        }
    };

    return{
        approvedDomains: approvedDomains(),
        messages: messages
    };
})();

IRG.config = (function(){

})();

Add a new domain to the whitelist


setupFlorence();

//forms field styling markup injection
//$('select:not(.small)').wrap('');
//$('select.small').wrap('');
//$('.selectbg--small:eq(1)').addClass('selectbg--small--margin');
//$('.selectbg--small:eq(3)').addClass('float-right');

Remove commented code not used.


Server = require('./src/server');
Mockingjay = require('./src/mockingjay');
DefaultOptions = require('./src/default_options');

var Mockingjays = function() {}

/**
 * Start creates a Mockingjays Server and starts proxying
 * or mocking responses based on previous training.
 *
 * @param options - An {Object} with options
 *          :cacheDir - Path to cache storage.
 *          :port - Port that Mockingjays should bind to.
 *          :serverBaseUrl - Base URL for the source server.
 */
Mockingjays.prototype.start = function(options) {
  var finalOptions = DefaultOptions.merge(options);
  var mockingjay = new Mockingjay();
  Server.listen(finalOptions, function(req, res){ mockingjay.onRequest(req, res)});
}

module.exports = Mockingjays

Server = require('./src/server');
Mockingjay = require('./src/mockingjay');
DefaultOptions = require('./src/default_options');

var Mockingjays = function() {}

/**
 * Start creates a Mockingjays Server and starts proxying
 * or mocking responses based on previous training.
 *
 * @param options - An {Object} with options
 *          :cacheDir - Path to cache storage.
 *          :port - Port that Mockingjays should bind to.
 *          :serverBaseUrl - Base URL for the source server.
 */
Mockingjays.prototype.start = function(options) {
  var defaultOptions = new DefaultOptions();
  var finalOptions = defaultOptions.merge(options);
  var mockingjay = new Mockingjay(finalOptions);
  Server.listen(finalOptions, function(req, res){ mockingjay.onRequest(req, res)});
}

module.exports = Mockingjays

Update Option Parsing for Mockingjays


var pointTable = {};

module.exports = function(match, response) {
  var points = Number(match[1]),
    multiplier = ('to' === match[2]) - ('from' === match[2]),
    target = match[3];

  pointTable[target] = (pointTable[target] || 0) + (points * multiplier);
  response.end(target + ': ' + pointTable[target] + ' points');
}

var math = require('mathjs');

var pointTable = {};

var renderPoints = function renderPoints(points) {
  if (typeof points !== 'number' || points !== points) {
    return 'wat';
  }

  if (points === Infinity) {
    return 'way too many points';
  }

  return points.toString + ' points';
};

var points = function points(match, response) {
  var matchExpr = match[1],
    points = Math.round(math.eval(matchExpr)),
    multiplier = ('to' === match[3]) - ('from' === match[3]),
    target = match[4];

  pointTable[target] = (pointTable[target] || 0) + (points * multiplier);
  response.end(target + ': ' + renderPoints(pointTable[target]));
};

module.exports = points;

Make point parsing and rendering less crappy


var follow = require('follow');
var request = require('request');

module.exports = function(db_url, handler_url) {

  function isSuccessCode(code) {
    return code >= 200 && code < 300;
  }

  function forwardHook(doc) {
    request({
      url: handler_url,
      method: doc.req.method,
      body: doc.req.body,
      headers: {
        'Content-type': doc.req.headers["Content-Type"]
      }
    }, function (error, response, body) {
      if (error) {
        console.log("Error forwarding hook: " + error);
      } else if (!isSuccessCode(response.statusCode)) {
        console.log("Error forwarding hook: server returned " + response.statusCode);
      } else {
        console.log("Forwarded webhook id: " + doc._id);
      }
    });
  }

  follow({db: db_url, include_docs:true, since: 'now'}, function(error, change) {
    if (error) {
      console.log("Error following changes: " + error);
    } else {
      var doc = change.doc;

      if (doc.type === 'webhook') {
        forwardHook(doc);
      }
    }
  });

  console.log("Hookforward is listening for changes...");
}

var follow = require('follow');
var request = require('request');

module.exports = function(db_url, handler_url) {

  function isSuccessCode(code) {
    return code >= 200 && code < 300;
  }

  function forwardHook(doc) {
    var url = doc.req.query.handler_url || handler_url;

    if (!url) {
      console.log("No handler_url defined for webhook id: " + doc._id);
      return;
    }

    request({
      url: url,
      method: doc.req.method,
      body: doc.req.body,
      headers: {
        'Content-type': doc.req.headers["Content-Type"]
      }
    }, function (error, response, body) {
      if (error) {
        console.log("Error forwarding hook: " + error);
      } else if (!isSuccessCode(response.statusCode)) {
        console.log("Error forwarding hook: server returned " + response.statusCode);
      } else {
        console.log("Forwarded webhook id: " + doc._id);
      }
    });
  }

  follow({db: db_url, include_docs:true, since: 'now'}, function(error, change) {
    if (error) {
      console.log("Error following changes: " + error);
    } else {
      var doc = change.doc;

      if (doc.type === 'webhook') {
        forwardHook(doc);
      }
    }
  });

  console.log("Hookforward is listening for changes...");
}

Enable passing in handler_url via capture url querystring variable


'use strict';
// TODO: use traditional way or ?
let Framework = {
    inject: function (coreName) {
        console.log(`framework inject ${coreName}`);
        // checkCore();
        this.coreName = coreName;
        return this;
    },

    start: function () { console.log(`server start with ${this.coreName}`); },

    // internal
    coreName: '',
    checkCore: (coreName) => {

    }
};

module.exports = Framework;

'use strict';
const net = require('net');
const socket = net.Socket;

// TODO: use traditional way or ?
let Framework = {
    inject: function (coreName) {
        this.core = getCore(coreName);
        console.log(`framework inject ${coreName}`);
        return this;
    },

    start: function () {
        const server = net.createServer(onConnect);
        server.listen(this.core.system.port, () => {
            console.log(`server start with ${this.core.name}`);
        });
    },

    // internal
    core: {}
};

function onConnect() {

}

function getCore(coreName) {
    return {
        name: coreName,
        system: {
            port: 9922
        }
    };
}

module.exports = Framework;

Add draft for core structure.


'use strict';

/* lib/client.js
 * The Client object.
 * * */

module.exports = Client;

function Client(socket){
  if (typeof socket !== 'object')
    throw new Error('Clients can only bind to Sockets');

  this._socket = socket;
  this.state = 'start';
}

Client.prototype = require('events').EventEmitter;

Client.prototype.send = function(data){
  if (typeof data === 'object') data = JSON.stringify(data);
  this._socket.write(data);
};

'use strict';

const EventEmitter = require('events');

/* lib/client.js
 * The Client object.
 * * */

module.exports = Client;

function Client(socket){
  if (typeof socket !== 'object')
    throw new Error('Clients can only bind to Sockets');

  this._socket = socket;
  this.state = 'start';
}

Client.prototype = new EventEmitter();

Client.prototype.send = function(data){
  if (typeof data === 'object') data = JSON.stringify(data);
  this._socket.write(data);
};

Fix events on Client objects


module.exports = {
    WS_URL : 'http://www.workshape.io/'
};

module.exports = {
    WS_URL : process.env.WS_URL || 'http://www.workshape.io/'
};

Read WS_URL from environment variables


var FeatureModel = function(attrs){
  this.id                  = (attrs && attrs.id) || null;
  this.title               = (attrs && attrs.title) || null;
  this.description         = (attrs && attrs.description) || null;
  this.master_switch_state = (attrs && attrs.master_switch_state) || null;
  this.definition          = (attrs && attrs.definition) || null;
};

FeatureModel.prototype.toggle = function(toggleState) {
  if (toggleState) {
    this.master_switch_state = this.definition === null ? "on" : "dynamic";
  } else {
    this.master_switch_state = "off";
  }
}

FeatureModel.prototype.isNew = function() {
  return this.id === null;
}

module.exports = FeatureModel;

var FeatureModel = function(attrs){
  this.id            = (attrs && attrs.id) || null;
  this.title         = (attrs && attrs.title) || null;
  this.description   = (attrs && attrs.description) || null;
  this.master_state  = (attrs && attrs.master_state) || null;
  this.dynamic_state = (attrs && attrs.dynamic_state) || null;
  this.definition    = (attrs && attrs.definition) || null;
};

FeatureModel.prototype.toggle = function(toggleState) {
  if (toggleState) {
    this.master_state = true;
  } else {
    this.master_state = false;
  }
}

FeatureModel.prototype.isNew = function() {
  return this.id === null;
}

module.exports = FeatureModel;

Update the feature model with master_state and dynamic_state props


const bcrypt = require('bcrypt')

module.exports = function (sequelize, DataTypes) {
  const User = sequelize.define('User', {
    id: {
      primaryKey: true,
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4
    },
    name: {
      unique: true,
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        notEmpty: true
      }
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        isEmail: true
      },
      roles: false
    },
    hashedPassword: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        notEmpty: true
      },
      roles: false
    }
  }, {
    classMethods: {
      associate (models) {
        User.hasMany(models.Bikeshed)
        User.hasMany(models.Vote)
      },

      hashPassword (password) {
        return new Promise((resolve, reject) => {
          bcrypt.hash(password, 8, (err, hash) => {
            err ? reject(err) : resolve(hash)
          })
        })
      },

      comparePassword (password, hash) {
        return new Promise((resolve, reject) => {
          bcrypt.compare(password, hash, (err, res) => {
            err ? reject(err) : resolve(res)
          })
        })
      }
    }
  })

  return User
}

const bcrypt = require('bcrypt')

module.exports = function (sequelize, DataTypes) {
  const User = sequelize.define('User', {
    id: {
      primaryKey: true,
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        max: 255,
        notEmpty: true
      }
    },
    email: {
      unique: true,
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        isEmail: true
      },
      roles: false
    },
    hashedPassword: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        notEmpty: true
      },
      roles: false
    }
  }, {
    classMethods: {
      associate (models) {
        User.hasMany(models.Bikeshed)
        User.hasMany(models.Vote)
      },

      hashPassword (password) {
        return new Promise((resolve, reject) => {
          bcrypt.hash(password, 8, (err, hash) => {
            err ? reject(err) : resolve(hash)
          })
        })
      },

      comparePassword (password, hash) {
        return new Promise((resolve, reject) => {
          bcrypt.compare(password, hash, (err, res) => {
            err ? reject(err) : resolve(res)
          })
        })
      }
    }
  })

  return User
}

Remove name unique constraint, move to email


function ddg_spice_xkcd_display(api_result) {
  if (!api_result.img || !api_result.alt) return;

  //calls our endpoint to get the number of the latest comic
  $.getJSON('/js/spice/xkcd/latest/', function(data){

    //if we are looking at the latest comic, don't display the 'next' link
    api_result.has_next = parseInt(data.num) > parseInt(api_result.num);

    Spice.render({
      data             : api_result,
      header1          : api_result.safe_title + " (xkcd)",
      source_url       : 'http://xkcd.com/' + api_result.num,
      source_name      : 'xkcd',
      template_normal  : 'xkcd',
      force_big_header : true,
      force_no_fold    : true,
    });
  });
}

//gets the number for the previous comic
Handlebars.registerHelper("previousNum", function(num, options) {
    if(num > 1) {
        return options.fn({num: num - 1});
    }
});

//gets the number for the next comic
Handlebars.registerHelper("nextNum", function(num, options) {
    return options.fn({num: num + 1});
});

function ddg_spice_xkcd_display(api_result) {
  if (!api_result.img || !api_result.alt) return;

  //calls our endpoint to get the number of the latest comic
  $.getJSON('/js/spice/xkcd/latest/', function(data){

    //if we are looking at the latest comic, don't display the 'next' link
    api_result.has_next = parseInt(data.num) > parseInt(api_result.num);

    Spice.render({
      data             : api_result,
      header1          : api_result.safe_title + " (xkcd)",
      source_url       : 'http://xkcd.com/' + api_result.num,
      source_name      : 'xkcd',
      template_normal  : 'xkcd_display',
      force_big_header : true,
      force_no_fold    : true
    });
  });
}

//gets the number for the previous comic
Handlebars.registerHelper("previousNum", function(num, options) {
    if(num > 1) {
        return options.fn({num: num - 1});
    }
});

//gets the number for the next comic
Handlebars.registerHelper("nextNum", function(num, options) {
    return options.fn({num: num + 1});
});

Change name of the template.


// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html

module.exports = function (options = {}) {
  // eslint-disable-line no-unused-vars
  return function (hook) {
    const { facebook, facebookId } = hook.data;
    return this.find({ facebookId }).then(user => {
      if (user.data.length < 1) {
        const { profile } = facebook;
        const { name, gender, emails } = profile;
        const email = emails && emails.length && emails[0].value || undefined;
        hook.data = Object.assign(
          {},
          {
            firstName: name && name.givenName || undefined,
            lastName: name && name.familyName || undefined,
            facebookId,
            email,
            gender,
          }
        );
      }
      return hook;
    });
  };
};

// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html

module.exports = function (options = {}) {
  // eslint-disable-line no-unused-vars
  return function (hook) {
    const { facebook, facebookId } = hook.data;
    return this.find({ query: { facebookId } }).then(user => {
      if (user.data.length < 1) {
        const { profile } = facebook;
        const { name, gender, emails } = profile;
        const email = emails && emails.length && emails[0].value || undefined;
        hook.data = Object.assign(
          {},
          {
            firstName: name && name.givenName || undefined,
            lastName: name && name.familyName || undefined,
            facebookId,
            email,
            gender,
          }
        );
      }
      return hook;
    });
  };
};

Fix the params object in the find


/*global System:false, exports:false */

var serverRelativeRegexp = /^\w+:\/\/[^\/]*/;

function santiseSource( source ) {
  return source.replace( /(['\\])/g, '\\$1' )
    .replace( /[\f]/g, '\\f' )
    .replace( /[\b]/g, '\\b' )
    .replace( /[\n]/g, '\\n' )
    .replace( /[\t]/g, '\\t' )
    .replace( /[\r]/g, '\\r' )
    .replace( /[\u2028]/g, '\\u2028' )
    .replace( /[\u2029]/g, '\\u2029' );
}

function resolveUrl( address, baseUrl, useServerRelative ) {
  if ( useServerRelative ) {
    return address.replace( serverRelativeRegexp, '' );

  } else if ( baseUrl && address.indexOf( baseUrl ) === 0 ) {
    return address.substr( baseUrl.length );
  }
  return address;
}

exports.translate = function( load ) {
  var baseUrl = typeof System.baseURL === 'string' ? System.baseURL : '',
    options = System.ngTemplatePlugin || {},
    url = resolveUrl( load.address, baseUrl, !!options.serverRelative );

  return "require('angular').module('ng')" +
    ".run(['$templateCache', function(c) { c.put('" + url + "', '" + santiseSource( load.source ) + "') }]);" +
    "module.exports = { templateUrl: '" + url + "' };";
};

/*global System:false, exports:false */

function santiseSource( source ) {
  return source.replace( /(['\\])/g, '\\$1' )
    .replace( /[\f]/g, '\\f' )
    .replace( /[\b]/g, '\\b' )
    .replace( /[\n]/g, '\\n' )
    .replace( /[\t]/g, '\\t' )
    .replace( /[\r]/g, '\\r' )
    .replace( /[\u2028]/g, '\\u2028' )
    .replace( /[\u2029]/g, '\\u2029' );
}

function resolveUrl( address, baseUrl ) {
  if ( baseUrl && address.indexOf( baseUrl ) === 0 ) {
    return address.substr( baseUrl.length );
  }
  return address;
}

exports.translate = function translate( load ) {
  var baseUrl = typeof System.baseURL === 'string' ? System.baseURL : '',
    options = System.ngTemplatePlugin || {},
    url = resolveUrl( load.address, baseUrl );

  return 'var url = ' +
    ( options.serverRelative ? "System.baseURL.replace(/^\\w+:\\/\\/[^\\/]*/,'')+" : '' ) +
    "'" + url + "';" +
    "require('angular').module('ng')" +
    ".run(['$templateCache', function(c) { c.put(" +
      "url, '" +
      santiseSource( load.source ) + "'); }]);" +
    'module.exports = { templateUrl: url };';
};

Fix issues with resolving server relative URLs when bundling with JSPM


console.log("Courses+ background page!");
cpal.request.addBeforeSendHeaders(["*://courses2015.dalton.org/*"], function(details) {
	if (details.url.indexOf("user/icon/dalton/") > -1) {
		details.requestHeaders.push({name: 'X-CoursesPlus-RefererSpoof-Yay', value: "https://courses2015.dalton.org/user/profile.php"});
	}
	for (var i = 0; i < details.requestHeaders.length; ++i) {
		if (details.requestHeaders[i].name === 'X-CoursesPlus-RefererSpoof-Yay') {
			var newVal = details.requestHeaders[i].value;
			//details.requestHeaders.splice(i, 1);
			for (var j = 0; j < details.requestHeaders.length; ++j) {
				if (details.requestHeaders[i].name === 'Referer') {
					break;
				}
			}
			if (details.requestHeaders[j] !== 'Referer') {
				details.requestHeaders.push({name: 'Referer', value: newVal});
			} else {
				details.requestHeaders[j].value = newVal;
			}
			break;
		}
	}
	return {requestHeaders: details.requestHeaders};
});

console.log("Courses+ background page!");
cpal.request.addBeforeSendHeaders(["*://courses2015.dalton.org/*"], function(details) {
	if (details.url.indexOf("user/icon/dalton/") > -1) {
		details.requestHeaders.push({name: 'X-CoursesPlus-RefererSpoof-Yay', value: "https://courses2015.dalton.org/user/profile.php"});
	}
	if (details.url.indexOf("theme/image.php/dalton/core") > -1 && details.url.indexOf("/u/f1") > -1) {
		details.requestHeaders.push({name: 'X-CoursesPlus-RefererSpoof-Yay', value: "https://courses2015.dalton.org/user/profile.php"});
	}
	for (var i = 0; i < details.requestHeaders.length; ++i) {
		if (details.requestHeaders[i].name === 'X-CoursesPlus-RefererSpoof-Yay') {
			var newVal = details.requestHeaders[i].value;
			//details.requestHeaders.splice(i, 1);
			for (var j = 0; j < details.requestHeaders.length; ++j) {
				if (details.requestHeaders[i].name === 'Referer') {
					break;
				}
			}
			if (details.requestHeaders[j] !== 'Referer') {
				details.requestHeaders.push({name: 'Referer', value: newVal});
			} else {
				details.requestHeaders[j].value = newVal;
			}
			break;
		}
	}
	return {requestHeaders: details.requestHeaders};
});

Fix coursesnew with no avatar


var assert = require('assert');
var request = require('supertest');
var exec = require('child_process').exec;

var application = require(__dirname + '/server.js');

describe('server', function () {
    before(function () {
        application.listen();
    });

    after(function () {
        application.close();
    });

    describe('Array', function () {
        describe('#indexOf()', function () {
            it('should return -1 when the value is not present', function () {
                assert.equal(-1, [1, 2, 3].indexOf(5));
                assert.equal(-1, [1, 2, 3].indexOf(0));
            });
        });

        describe('#indexOf()', function () {
            it('should return -1 when the value is not present', function () {
                assert.equal(-1, [1, 2, 3].indexOf(5));
            });
        });

        describe('GET /', function() {
            it('respond with json', function(done) {
                request(application.app)
                    .get('/')
                    .set('Accept', 'application/json')
                    .expect('Content-Type', /html/)
                    .expect(200, done);
            });
        });

        describe('Run Abao', function() {
            it('respond with json', function(done) {
                this.timeout(15000);
                exec('abao ./raml/api.raml --server http://localhost:3000/api', function (err) {
                    done();
                });
            });
        });
    });
});

var assert = require('assert');
var request = require('supertest');
var exec = require('child_process').exec;

var application = require(__dirname + '/server.js');

describe('server', function () {
    before(function () {
        application.listen();
    });

    after(function () {
        application.close();
    });

    describe('Array', function () {
        describe('#indexOf()', function () {
            it('should return -1 when the value is not present', function () {
                assert.equal(-1, [1, 2, 3].indexOf(5));
                assert.equal(-1, [1, 2, 3].indexOf(0));
            });
        });

        describe('#indexOf()', function () {
            it('should return -1 when the value is not present', function () {
                assert.equal(-1, [1, 2, 3].indexOf(5));
            });
        });

        describe('GET /', function() {
            it('respond with json', function(done) {
                request(application.app)
                    .get('/')
                    .set('Accept', 'application/json')
                    .expect('Content-Type', /html/)
                    .expect(200, done);
            });
        });

        describe('Run Abao', function() {
            it('respond with json', function(done) {
                this.timeout(15000);
                exec('node node_modules/.bin/abao ./raml/api.raml --server http://localhost:3000/api', function (err, stdout) {
                    console.log(stdout);
                    done();
                });
            });
        });
    });
});

Change abao test to use the local installation instead of the global.


import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as pubsActions from 'redux/modules/pubs';

@connect(
  state => ({
    availablePubs: state.pubs.availablePubs,
    selectedPub: state.pubs.selectedPub
  }),
  pubsActions
)
export default class Pubs extends Component {
  static propTypes = {
    availablePubs: PropTypes.array,
    selectedPub: PropTypes.object,
    getPubs: PropTypes.func
  }

  render() {
    return (

        Pubs Component
        {this.props.availablePubs.map((pub) => {
          return
{pub.name}
;
        })}
        <button className="btn" onClick={::this.props.getPubs}>Dawaj</button>

    );
  }
}

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as pubsActions from 'redux/modules/pubs';

@connect(
  state => ({
    availablePubs: state.pubs.availablePubs,
    selectedPub: state.pubs.selectedPub
  }),
  pubsActions
)
export default class Pubs extends Component {
  static propTypes = {
    availablePubs: PropTypes.array,
    selectedPub: PropTypes.object,
    getPubs: PropTypes.func
  }

  componentDidMount() {
    if (this.props.availablePubs.length === 0) this.props.getPubs();
  }

  render() {
    return (

        Pubs Component
        {this.props.availablePubs.map((pub) => {
          return
{pub.name}
;
        })}
        <button className="btn" onClick={::this.props.getPubs}>Dawaj</button>

    );
  }
}

Add initial available pubs load function if the route is accessed without transition


var tap = require("../")

tap.test("trap result", function (t0) {
  t0.plan(3)

  var t1 = new(tap.Harness)(tap.Test).test()

console.log('here')
  t1.plan(1)

  t1.on("result", function (res) {
    if (res.wanted === 4) {
      t0.equal(res.found, 3)
      t0.equal(res.wanted, 4)

      t0.end()
      t1.end()
    }
  })

  t1.equal(1 + 2, 4)
})

var tap = require("../")

tap.test("trap result #TODO", function (t0) {

  console.log("not ok 1 result event trapping #TODO")
  return t0.end()

  t0.plan(3)

  var t1 = new(tap.Harness)(tap.Test).test()

  t1.plan(1)

  t1.on("result", function (res) {
    if (res.wanted === 4) {
      t0.equal(res.found, 3)
      t0.equal(res.wanted, 4)

      t0.end()
      t1.end()
    }
  })

  t1.equal(1 + 2, 4)
})

Mark this test as todo


import React from 'react'
import { rhythm } from 'utils/typography'

const ImagePage = (props) => {
  console.log(props)
  const {regular, retina} = props.data.image
  return (

{props.data.image.FileName}

      <img
        src={regular.src}
        srcSet={`${retina.src} 2x`}
        style={{
          display: 'inline',
          marginRight: rhythm(1/2),
          marginBottom: rhythm(1/2),
          verticalAlign: 'middle',
        }}
      />

  )
}

export default ImagePage

export const pageQuery = `
  query ImagePage($path: String!) {
    image(path: $path) {
      FileName
      regular: image(width: 1000) {
        src
      }
      retina: image(width: 2000) {
        src
      }
    }
  }
`

import React from 'react'
import { rhythm } from 'utils/typography'

class ImagePage extends React.Component {
  constructor () {
    super()
    this.state = {
      loaded: false,
    }
  }

  render () {
    console.log(this.props)
    const {regular, retina, micro} = this.props.data.image
    return (

{this.props.data.image.FileName}

        <img
          src={`data:image/jpeg;base64,${micro.src}`}
          style={{
            width: regular.width,
            height: regular.height,
            position: 'absolute',
            transition: 'opacity 0.5s',
            opacity: this.state.loaded ? 0 : 1,
          }}
        />
         this.setState({ loaded: true })}
          srcSet={`${retina.src} 2x`}
        />

    )
  }
}

export default ImagePage

export const pageQuery = `
  query ImagePage($path: String!) {
    image(path: $path) {
      FileName
      micro: image(width: 20, base64: true) {
        src
      }
      regular: image(width: 1000) {
        src
        width
        height
      }
      retina: image(width: 2000) {
        src
      }
    }
  }
`

Add progressive image loading example


/**
 * Created by Wayne on 16/2/22.
 */

var config = require('./config/config');
var setup = require('./config/setup')();
var express = require('./config/express');

new express().listen(config.port);
console.log('process.env.NODE_ENV', process.env.NODE_ENV);

exports.index = function () {

}

exports.index1 = function () {

}

exports.index3 = function () {

}
exports.index4 = function () {
  var j = 0;
}

exports.index5 = function () {
  var j = 5;
}
exports.index6 = function () {
  var j = 6;
}

/**
 * Created by Wayne on 16/2/22.
 */

var config = require('./config/config');
var setup = require('./config/setup')();
var express = require('./config/express');

new express().listen(config.port);
console.log('process.env.NODE_ENV', process.env.NODE_ENV);

exports.index = function () {

}

exports.index1 = function () {

}

exports.index3 = function () {

}
exports.index4 = function () {
  var j = 0;
}

exports.index5 = function () {
  var j = 55;
}
exports.index6 = function () {
  var j = 66;
}

Fix bug about index 6


import React, { Component } from 'react';
import {observer} from 'mobx-react';
import Services from '../components/Services';
import Tasks from '../components/Tasks';
import FlatButton from 'material-ui/FlatButton';
import { Link } from 'react-router';

const styles = {
  button: {
    margin: 0.6 + 'rem'
  }
};

const Dashboard = observer(
  class Dashboard extends Component {
    constructor(props) {
      super(props);
      this.state = {
        selectedServices: []
      };
    }

    onServiceSelection(selectedServices) {
      this.setState({
        selectedServices: selectedServices
      });
    }

    render() {
      const {nodeStore} = this.props.route;

      return (

          <FlatButton
            label="Create Service"
            containerElement={<Link to="/services/create" />}
            primary={true}
            style={styles.button}
            icon={add}
          />
          <Services
           services={nodeStore.services}
           onSelection={this.onServiceSelection.bind(this)}
           selectedServices={this.state.selectedServices}/>
          <Tasks
           tasks={nodeStore.tasks}
           selectedServices={this.state.selectedServices}/>

      );
    }
  }
);

export default Dashboard;

import React, { Component } from 'react';
import {observer} from 'mobx-react';
import Services from '../components/Services';
import Tasks from '../components/Tasks';
import FlatButton from 'material-ui/FlatButton';
import { Link } from 'react-router';

const styles = {
  button: {
    margin: 0.6 + 'rem'
  }
};

const Dashboard = observer(
  class Dashboard extends Component {
    constructor(props) {
      super(props);
      this.state = {
        selectedServices: []
      };
    }

    onServiceSelection(selectedServices) {
      this.setState({
        selectedServices: selectedServices
      });
    }

    render() {
      const {nodeStore} = this.props.route;
      /*
          <FlatButton
            label="Create Service"
            containerElement={<Link to="/services/create" />}
            primary={true}
            style={styles.button}
            icon={add}
          />
      */

      return (

          <Services
           services={nodeStore.services}
           onSelection={this.onServiceSelection.bind(this)}
           selectedServices={this.state.selectedServices}/>
          <Tasks
           tasks={nodeStore.tasks}
           selectedServices={this.state.selectedServices}/>

      );
    }
  }
);

export default Dashboard;

Disable non-functional create service button


import request from 'request';

function call(endpoint, method, qs, json) {
    let options = {
        uri: `http://localhost/api/${endpoint}`,
        method
    };
    if (typeof qs !== 'undefined') {
        options.qs = qs;
    }
    if (typeof json !== 'undefined') {
        options.json = json;
    }
    return new Promise((resolve, reject) => {
        request(options, (error, response, body) => {
            if (typeof body === 'string') {
                body = JSON.parse(body);
            }
            if (body.status === 'success') {
                resolve(body.data);
            } else {
                reject(body.message);
            }
        });
    });
}

export function del(endpoint, data) {
    return call(endpoint, 'DELETE', data);
}

export function get(endpoint, data) {
    return call(endpoint, 'GET', data);
}

export function post(endpoint, data) {
    return call(endpoint, 'POST', undefined, data);
}

import request from 'request';

const PROTOCOL = 'http';
const HOST = 'localhost';
const PORT = 8080;

function call(endpoint, method, qs, json) {
    let options = {
        uri: `${PROTOCOL}://${HOST}:${PORT}/api/${endpoint}`,
        method
    };
    if (typeof qs !== 'undefined') {
        options.qs = qs;
    }
    if (typeof json !== 'undefined') {
        options.json = json;
    }
    return new Promise((resolve, reject) => {
        request(options, (error, response, body) => {
            if (typeof body === 'string') {
                body = JSON.parse(body);
            }
            if (body.status === 'success') {
                resolve(body.data);
            } else {
                reject(body.message);
            }
        });
    });
}

export function del(endpoint, data) {
    return call(endpoint, 'DELETE', data);
}

export function get(endpoint, data) {
    return call(endpoint, 'GET', data);
}

export function post(endpoint, data) {
    return call(endpoint, 'POST', undefined, data);
}

Use port 8080 for testing


import { fromJS } from 'immutable';
import {
    SERVER_DATA_AUTHORIZE,
    SERVER_DATA_BALANCE,
    SERVER_DATA_PAYOUT_CURRENCIES,
    SERVER_DATA_BUY,
    UPDATE_TOKEN,
} from '../_constants/ActionTypes';

const initialState = fromJS({
    loginid: '',
    fullname: '',
    currency: 'USD',
    balance: 0,
    token: '',
    currencies: ['USD'],
});

export default (state = initialState, action) => {
    switch (action.type) {
        case SERVER_DATA_AUTHORIZE: {
            const authorize = fromJS(action.serverResponse.authorize);
            return state.merge(authorize);
        }
        case SERVER_DATA_BALANCE: {
            return state.set('balance', action.serverResponse.balance.balance);
        }
        case SERVER_DATA_BUY: {
            return state.setIn(['account', 'balance'], action.serverResponse.balance_after);
        }
        case SERVER_DATA_PAYOUT_CURRENCIES: {
            return state.set('currencies', action.serverResponse.payout_currencies);
        }
        case UPDATE_TOKEN: {
            return state.set('token', action.token);
        }
        default:
            return state;
    }
};

import { fromJS } from 'immutable';
import {
    SERVER_DATA_AUTHORIZE,
    SERVER_DATA_BALANCE,
    SERVER_DATA_PAYOUT_CURRENCIES,
    SERVER_DATA_BUY,
    UPDATE_TOKEN,
} from '../_constants/ActionTypes';

const initialState = fromJS({
    loginid: '',
    fullname: '',
    currency: 'USD',
    balance: 0,
    token: '',
    currencies: ['USD'],
});

export default (state = initialState, action) => {
    switch (action.type) {
        case SERVER_DATA_AUTHORIZE: {
            const authorize = fromJS(action.serverResponse.authorize);
            if (!authorize.currency) {
                return state.merge(authorize).set('currency', 'USD');
            }
            return state.merge(authorize);
        }
        case SERVER_DATA_BALANCE: {
            return state.set('balance', action.serverResponse.balance.balance);
        }
        case SERVER_DATA_BUY: {
            return state.setIn(['account', 'balance'], action.serverResponse.balance_after);
        }
        case SERVER_DATA_PAYOUT_CURRENCIES: {
            return state.set('currencies', action.serverResponse.payout_currencies);
        }
        case UPDATE_TOKEN: {
            return state.set('token', action.token);
        }
        default:
            return state;
    }
};

Set default currency to USD if none


var subscriptions = new SubsManager();

Router.configure({
    layoutTemplate: 'layout',
    loadingTemplate: 'loading',
    notFoundTemplate: 'notFound'
});

Router.route('/', {
    name: 'home',
    waitOn: function() {
        return subscriptions.subscribe('allMangasCover');
    }
});

Router.route('/ownedMangas', {
    name: 'ownedMangas',
    waitOn: function() {
        return subscriptions.subscribe('allOwnedMangas', Meteor.userId());
    }
});

Router.route('/missingMangas', {
    name: 'missingMangas',
    waitOn: function() {
        return subscriptions.subscribe('allMissingMangas', Meteor.userId());
    }
});

Router.route('/:mangasName/tome/:tomeNumber/:_id', {
    name: 'tomeDetails',
    waitOn: function() {
        return [subscriptions.subscribe('tomeDetails', this.params._id), subscriptions.subscribe('allTomes', Meteor.userId(), this.params.mangasName)];
    },
    data: function() {
        return Mangas.findOne(this.params._id);
    }
});

Router.route('/:author', {
    name: 'mangaka',
    waitOn: function() {
        return subscriptions.subscribe('mangaka', Meteor.userId(), this.params.author);
    }
});

var subscriptions = new SubsManager();

Router.configure({
    layoutTemplate: 'layout',
    loadingTemplate: 'loading',
    notFoundTemplate: 'notFound'
});

Router.route('/', {
    name: 'home',
    waitOn: function() {
        return subscriptions.subscribe('allMangasCover');
    },
    fastRender: true
});

Router.route('/ownedMangas', {
    name: 'ownedMangas',
    waitOn: function() {
        return subscriptions.subscribe('allOwnedMangas', Meteor.userId());
    }
});

Router.route('/missingMangas', {
    name: 'missingMangas',
    waitOn: function() {
        return subscriptions.subscribe('allMissingMangas', Meteor.userId());
    }
});

Router.route('/:mangasName/tome/:tomeNumber/:_id', {
    name: 'tomeDetails',
    waitOn: function() {
        return [subscriptions.subscribe('tomeDetails', this.params._id), subscriptions.subscribe('allTomes', Meteor.userId(), this.params.mangasName)];
    },
    data: function() {
        return Mangas.findOne(this.params._id);
    }
});

Router.route('/:author', {
    name: 'mangaka',
    waitOn: function() {
        return subscriptions.subscribe('mangaka', Meteor.userId(), this.params.author);
    }
});

Add fastRender for the home


define([
  'angular',
  'app',
  'lodash'
],
function (angular, app, _) {
  'use strict';

  angular
    .module('grafana.directives')
    .directive('bodyClass', function() {
      return {
        link: function($scope, elem) {

          var lastPulldownVal;
          var lastHideControlsVal;

          $scope.$watch('dashboard.pulldowns', function() {
            if (!$scope.dashboard) {
              return;
            }

            var panel = _.find($scope.dashboard.pulldowns, function(pulldown) { return pulldown.enable; });
            var panelEnabled = panel ? panel.enable : false;
            if (lastPulldownVal !== panelEnabled) {
              elem.toggleClass('submenu-controls-visible', panelEnabled);
              lastPulldownVal = panelEnabled;
            }
          }, true);

          $scope.$watch('dashboard.hideControls', function() {
            if (!$scope.dashboard) {
              return;
            }

            var hideControls = $scope.dashboard.hideControls || $scope.playlist_active;

            if (lastHideControlsVal !== hideControls) {
              elem.toggleClass('hide-controls', hideControls);
              lastHideControlsVal = hideControls;
            }
          });

          $scope.$watch('playlist_active', function() {
            elem.toggleClass('hide-controls', $scope.playlist_active === true);
            elem.toggleClass('playlist-active', $scope.playlist_active === true);
          });
        }
      };
    });

});

define([
  'angular',
  'app',
  'lodash'
],
function (angular, app, _) {
  'use strict';

  angular
    .module('grafana.directives')
    .directive('bodyClass', function() {
      return {
        link: function($scope, elem) {

          var lastPulldownVal;
          var lastHideControlsVal;

          $scope.$watchCollection('dashboard.pulldowns', function() {
            if (!$scope.dashboard) {
              return;
            }

            var panel = _.find($scope.dashboard.pulldowns, function(pulldown) { return pulldown.enable; });
            var panelEnabled = panel ? panel.enable : false;
            if (lastPulldownVal !== panelEnabled) {
              elem.toggleClass('submenu-controls-visible', panelEnabled);
              lastPulldownVal = panelEnabled;
            }
          });

          $scope.$watch('dashboard.hideControls', function() {
            if (!$scope.dashboard) {
              return;
            }

            var hideControls = $scope.dashboard.hideControls || $scope.playlist_active;

            if (lastHideControlsVal !== hideControls) {
              elem.toggleClass('hide-controls', hideControls);
              lastHideControlsVal = hideControls;
            }
          });

          $scope.$watch('playlist_active', function() {
            elem.toggleClass('hide-controls', $scope.playlist_active === true);
            elem.toggleClass('playlist-active', $scope.playlist_active === true);
          });
        }
      };
    });

});

Switch from watch to watchCollection for bodyclass directive


var WorldConstructor = function() {
  return function(callback) { callback(this) };
};
module.exports = WorldConstructor;

var WorldConstructor = function() {
  return function World(callback) { callback() };
};
module.exports = WorldConstructor;

Simplify default World constructor callback


function RecaptchaV3() {
    $(".recaptcha-v3").each(function () {
        var el = $(this);
        var clientId = grecaptcha.render($(el).attr("id"), {
            'sitekey': $(el).attr('data-sitekey'),
            'badge': $(el).attr('data-badge'),
            'theme': $(el).attr('data-theme'),
            'size': 'invisible',
        });
        grecaptcha.ready(function () {
            grecaptcha.execute(clientId, {
                action: 'submit'
            })
        });
    });
}

/* jshint unused:vars, undef:true, browser:true, jquery:true */
/* global grecaptcha */

;(function(global, $) {
'use strict';

function render(element) {
    var $element = $(element),
        clientId = grecaptcha.render(
            $element.attr('id'),
            {
                sitekey: $element.data('sitekey'),
                badge: $element.data('badge'),
                theme: $element.data('theme'),
                size: 'invisible'
            }
        );
    grecaptcha.ready(function () {
        grecaptcha.execute(
            clientId,
            {
                action: 'submit'
            }
        );
    });
}

global.RecaptchaV3 = function() {
    $('.recaptcha-v3').each(function () {
        render(this);
    });
};

})(window, jQuery);

Configure jshint, simplify javascript structure


$(function () {
  var locationList = $("#location-list");
  function populateLocations(features) {
    locationList.empty();
    $.each(features, function(i, v) {
      $("<li/>").appendTo(locationList)
        .toggleClass("lead", i == 0)
        .html(v.properties.details);
    });
  }
  if (locationList.length > 0) {
    $.getJSON("sites.geojson", function(data) {
      populateLocations(data.features);
      var d = $.Deferred();
      navigator.geolocation.getCurrentPosition(
        function(position) {
          var distanceTo = function(feature) {
            return geolib.getDistance({
              'longitude': position.coords.longitude,
              'latitude': position.coords.latitude
            },
            {
              'longitude': feature.geometry.coordinates[0],
              'latitude': feature.geometry.coordinates[1]
            });
          };
          // sort features by distance
          data.features.sort(function(a, b) {
            return distanceTo(a) - distanceTo(b);
          });
          d.resolve(data.features);
        },
        function() {
          // use original order
          d.resolve(data.features);
        },
        {
          enableHighAccuracy: false
        }
      );
      d.promise().done(populateLocations);
    });
  }
});

$(function () {
  var locationList = $("#location-list");
  function populateLocations(data) {
    locationList.empty();
    $.each(data.features, function(i, v) {
      $("<li/>").appendTo(locationList)
        .toggleClass("lead", i == 0 && data.locationKnown)
        .html(v.properties.details);
    });
  }
  if (locationList.length > 0) {
    $.getJSON("sites.geojson", function(data) {
      populateLocations({
        locationKnown: false,
        features: data.features
      });
      var d = $.Deferred();
      navigator.geolocation.getCurrentPosition(
        function(position) {
          var distanceTo = function(feature) {
            return geolib.getDistance({
              'longitude': position.coords.longitude,
              'latitude': position.coords.latitude
            },
            {
              'longitude': feature.geometry.coordinates[0],
              'latitude': feature.geometry.coordinates[1]
            });
          };
          // sort features by distance
          data.features.sort(function(a, b) {
            return distanceTo(a) - distanceTo(b);
          });
          d.resolve({
            locationKnown: true,
            features: data.features
          });
        },
        function() {
          function name(feature) {
            var details = feature.properties.details;
            return $('<div>'+details+'</div>').text();
          }
          // sort features by distance
          data.features.sort(function(a, b) {
            return name(a).localeCompare(name(b));
          });
          d.resolve({
            locationKnown: false,
            features: data.features
          });
        },
        {
          enableHighAccuracy: false
        }
      );
      d.promise().done(populateLocations);
    });
  }
});

Sort by name if location not available


/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Method using a factory pattern variant to get a reducer
 * for a particular page or page style.
 *
 * For a new page - create a constant object with the
 * methods from reducerMethods which are composed to create
 * a reducer. Add to PAGE_REDUCERS.
 *
 * Each key value pair in a reducer object should be in the form
 * action type: reducer function, returning a new state object
 *
 */

import {
  filterData,
  editTotalQuantity,
  focusNextCell,
  selectRow,
  deselectRow,
  deselectAll,
  focusCell,
  sortData,
} from './reducerMethods';

/**
 * Used for actions that should be in all pages using a data table.
 */
const BASE_TABLE_PAGE_REDUCER = {
  focusNextCell,
  focusCell,
  sortData,
};

const customerInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  editTotalQuantity,
  selectRow,
  deselectRow,
  deselectAll,
};

const PAGE_REDUCERS = {
  customerInvoice,
};

const getReducer = page => {
  const reducer = PAGE_REDUCERS[page];
  return (state, action) => {
    const { type } = action;
    if (!reducer[type]) return state;
    return reducer[type](state, action);
  };
};

export default getReducer;

/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Method using a factory pattern variant to get a reducer
 * for a particular page or page style.
 *
 * For a new page - create a constant object with the
 * methods from reducerMethods which are composed to create
 * a reducer. Add to PAGE_REDUCERS.
 *
 * Each key value pair in a reducer object should be in the form
 * action type: reducer function, returning a new state object
 *
 */

import {
  filterData,
  editTotalQuantity,
  focusNextCell,
  selectRow,
  deselectRow,
  deselectAll,
  focusCell,
  sortData,
  openBasicModal,
} from './reducerMethods';

/**
 * Used for actions that should be in all pages using a data table.
 */
const BASE_TABLE_PAGE_REDUCER = {
  focusNextCell,
  focusCell,
  sortData,
};

const customerInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  editTotalQuantity,
  selectRow,
  deselectRow,
  deselectAll,
  openBasicModal,
};

const PAGE_REDUCERS = {
  customerInvoice,
};

const getReducer = page => {
  const reducer = PAGE_REDUCERS[page];
  return (state, action) => {
    const { type } = action;
    if (!reducer[type]) return state;
    return reducer[type](state, action);
  };
};

export default getReducer;

Add openBasicModal reducer method to customer invoice


import React, { Component } from 'react';
import Polyglot from 'node-polyglot';

// Provider root component
export default class I18n extends Component {
  constructor(props) {
    super(props);

    this._polyglot = new Polyglot({
      locale: props.locale,
      phrases: props.messages,
    });
  }

  getChildContext() {
    return { t: this._polyglot.t.bind(this._polyglot) };
  }

  componentWillReceiveProps(newProps) {
    if (newProps.locale !== this.props.locale) {
      this._polyglot = new Polyglot({
        locale: newProps.locale,
        phrases: newProps.messages
      })
    }
 }

 shouldComponentUpdate(nextProps) {
   return this.props.locale !== nextProps.locale
 }

  render() {
    const children = this.props.children;
    return React.Children.only(children);
  }
}

I18n.propTypes = {
  locale: React.PropTypes.string.isRequired,
  messages: React.PropTypes.object.isRequired,
  children: React.PropTypes.element.isRequired,
};

I18n.childContextTypes = {
  t: React.PropTypes.func.isRequired,
};

import React, { Component } from 'react';
import Polyglot from 'node-polyglot';

// Provider root component
export default class I18n extends Component {
  constructor(props) {
    super(props);

    this._polyglot = new Polyglot({
      locale: props.locale,
      phrases: props.messages,
    });
  }

  getChildContext() {
    return { t: this._polyglot.t.bind(this._polyglot) };
  }

  componentWillReceiveProps(newProps) {
    if (newProps.locale !== this.props.locale) {
      this._polyglot = new Polyglot({
        locale: newProps.locale,
        phrases: newProps.messages
      })
    }
 }

  render() {
    const children = this.props.children;
    return React.Children.only(children);
  }
}

I18n.propTypes = {
  locale: React.PropTypes.string.isRequired,
  messages: React.PropTypes.object.isRequired,
  children: React.PropTypes.element.isRequired,
};

I18n.childContextTypes = {
  t: React.PropTypes.func.isRequired,
};

Fix update behavior removing shouldComponentUpdate


// These tests are for locale independent features
// locale dependent tests would be in locale test folder
import { module, test } from '../qunit';
import moment from '../../moment';

module('calendar');

test('passing a function', function (assert) {
    var a  = moment().hours(2).minutes(0).seconds(0);
    assert.equal(moment(a).calendar(null, {
        'sameDay': function () {
            return 'h:mmA';
        }
    }), '2:00AM', 'should equate');
});

// These tests are for locale independent features
// locale dependent tests would be in locale test folder
import { module, test } from '../qunit';
import moment from '../../moment';

module('calendar');

test('passing a function', function (assert) {
    var a  = moment().hours(13).minutes(0).seconds(0);
    assert.equal(moment(a).calendar(null, {
        'sameDay': function () {
            return 'h:mmA';
        }
    }), '1:00PM', 'should equate');
});

Test mid-day to avoid DST issues


var util = require('util');

var levels = [
    'silly',
    'input',
    'verbose',
    'prompt',
    'debug',
    'http',
    'info',
    'data',
    'help',
    'warn',
    'error'
];

levels.forEach(function(level) {
    exports[level] = function(msg) {
        console.log(
            level + ':',
            util.format.apply(
                this,
                [msg].concat([].slice.call(arguments, 1))));
    };
});

exports.wrap = function(getPrefix, func) {
    if (typeof getPrefix != 'function') {
        var msg = getPrefix;
        getPrefix = function() {
            return msg;
        };
    }

    return function() {
        var args    = arguments,
            that    = this,
            prefix  = getPrefix.apply(that, args);
            wrapper = {};

        levels.forEach(function(level) {
            wrapper[level] = function(msg) {
                msg = (prefix ? prefix + ': ' : '') + msg;
                exports[level].apply(
                    this,
                    [msg].concat([].slice.call(arguments, 1)));
            };
        });

        return func.apply(that, [wrapper].concat([].slice.call(args)));
    };
};

var util = require('util');

var levels = [
    'silly',
    'input',
    'verbose',
    'prompt',
    'debug',
    'http',
    'info',
    'data',
    'help',
    'warn',
    'error'
];

levels.forEach(function(level) {
    exports[level] = function(msg) {
        if (!exports.disabled) {
            console.log(
                level + ':',
                util.format.apply(
                    this,
                    [msg].concat([].slice.call(arguments, 1))));
        }
    };
});

exports.disabled = false;

exports.wrap = function(getPrefix, func) {
    if (typeof getPrefix != 'function') {
        var msg = getPrefix;
        getPrefix = function() {
            return msg;
        };
    }

    return function() {
        var args    = arguments,
            that    = this,
            prefix  = getPrefix.apply(that, args);
            wrapper = {};

        levels.forEach(function(level) {
            wrapper[level] = function(msg) {
                msg = (prefix ? prefix + ': ' : '') + msg;
                exports[level].apply(
                    this,
                    [msg].concat([].slice.call(arguments, 1)));
            };
        });

        return func.apply(that, [wrapper].concat([].slice.call(args)));
    };
};

Allow disabling logging when Winston is not being used