const esbuild = require("esbuild");

const isProduction = false;

esbuild.buildSync({
  entryPoints: ['./src/platform/current/server/serverStartup.ts'],
  bundle: true,
  outfile: './build/server/js/bundle2.js',
  platform: "node",
  sourcemap: true,
  minify: false,
  define: {
    "process.env.NODE_ENV": isProduction ? "\"production\"" : "\"development\"",
    "webpackIsServer": true,
  },
  external: [
    "akismet-api", "mongodb", "canvas", "express", "mz", "pg", "pg-promise",
    "mathjax", "mathjax-node", "jsdom", "@sentry/node", "node-fetch", "later", "turndown",
    "apollo-server", "apollo-server-express", "graphql",
    "bcrypt", "node-pre-gyp", "@lesswrong",
  ],
})

esbuild.buildSync({
  entryPoints: ['./src/platform/current/client/clientStartup.ts'],
  bundle: true,
  target: "es6",
  sourcemap: true,
  outfile: "./build/client/js/bundle.js",
  minify: false,
  define: {
    "process.env.NODE_ENV": isProduction ? "\"production\"" : "\"development\"",
    "webpackIsServer": false,
    "global": "window",
  },
});

const esbuild = require("esbuild");

const isProduction = false;

esbuild.buildSync({
  entryPoints: ['./src/platform/current/server/serverStartup.ts'],
  bundle: true,
  outfile: './build/server/js/bundle2.js',
  platform: "node",
  sourcemap: true,
  minify: false,
  define: {
    "process.env.NODE_ENV": isProduction ? "\"production\"" : "\"development\"",
    "webpackIsServer": true,
  },
  external: [
    "akismet-api", "mongodb", "canvas", "express", "mz", "pg", "pg-promise",
    "mathjax", "mathjax-node", "mathjax-node-page", "jsdom", "@sentry/node", "node-fetch", "later", "turndown",
    "apollo-server", "apollo-server-express", "graphql",
    "bcrypt", "node-pre-gyp", "@lesswrong", "intercom-client",
  ],
})

esbuild.buildSync({
  entryPoints: ['./src/platform/current/client/clientStartup.ts'],
  bundle: true,
  target: "es6",
  sourcemap: true,
  outfile: "./build/client/js/bundle.js",
  minify: false,
  define: {
    "process.env.NODE_ENV": isProduction ? "\"production\"" : "\"development\"",
    "webpackIsServer": false,
    "global": "window",
  },
});

Fix crash in intercom on server navigation events


(function() {
    return {
		port: 8125,
		mgmt_port: 8126,

		percentThreshold: [ 50, 75, 90, 95, 98, 99, 99.9, 99.99, 99.999],

		graphitePort: parseInt(process.env.GRAPHITE_PORT) || 2003,
		graphiteHost: process.env.GRAPHITE_HOST || "127.0.0.1",
		flushInterval: 10000,

		prefixStats: "statsd",

		backends: ['./backends/graphite'],
		graphite: {
			legacyNamespace: false,
			prefixCounter: "",
			prefixTimer: "",
			prefixGauge: "",
			prefixSet: ""
		},

		deleteIdleStats: true,

		dumpMessages: true
    };
})()

(function() {
    return {
		port: 8125,
		mgmt_port: 8126,

		percentThreshold: [ 50, 75, 90, 95, 98, 99, 99.9, 99.99, 99.999],

		graphitePort: parseInt(process.env.GRAPHITE_PORT) || 2003,
		graphiteHost: process.env.GRAPHITE_HOST || "127.0.0.1",
		flushInterval: 60000,

		prefixStats: "statsd",

		backends: ['./backends/graphite'],
		graphite: {
			legacyNamespace: false,
			prefixCounter: "",
			prefixTimer: "",
			prefixGauge: "",
			prefixSet: ""
		},

		deleteIdleStats: true,

		dumpMessages: false
    };
})()

Set flush to 60s and disable dumpMessages by default


(function(){
'use strict';

angular
  .module('secondLead')

  .factory('ReviewModel',['Restangular', 'store', function(Restangular, store) {
    var currentUser = store.get('user');
    return {

      getAll: function(dramaID){
        return Restangular.one('dramas', dramaID).getList('reviews').$object
      },

      getOne: function(dramaID, reviewID) {
        return Restangular.one('dramas', dramaID).one('reviews', reviewID).get()
      }

    };

  }])

})();

(function(){
'use strict';

angular
  .module('secondLead')

  .factory('ReviewModel',['Restangular', 'store', function(Restangular, store) {
    var currentUser = store.get('user');
    return {

      getAll: function(dramaID){
        return Restangular.one('dramas', dramaID).getList('reviews').$object
      },

      getOne: function(dramaID, reviewID) {
        return Restangular.one('dramas', dramaID).one('reviews', reviewID).get()
      },

      create: function(dramaID, userID, review) {
        return Restangular.one('dramas', dramaID).getList('reviews').post({drama_id: dramaID, body: review, reviewer_id: userID})
      },

      update: function(dramaID, reviewID,review){
        return Restangular.one('dramas', dramaID).one('reviews', reviewID).put({body: review})
      }

    };

  }])

})();

Add create and update functions to ang review model


/*
  This is an example factory definition. Factories are
  used inside acceptance tests.

  Create more files in this directory to define additional factories.
*/
import Mirage from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  name: 'Pete',
  age: 20,

  email: (i) => `person${i}@test.com`,

  admin: function() {
    return this.age > 30;
  }
});

/*
  This is an example factory definition. Factories are
  used inside acceptance tests.

  Create more files in this directory to define additional factories.
*/
import Mirage from 'ember-cli-mirage';

export default Mirage.Factory.extend({
  // name: 'Pete',                         // strings
  // age: 20,                              // numbers
  // tall: true,                           // booleans

  // email: (i) => `person${i}@test.com`,  // and functions

  // admin: function() {
  //   return this.age > 30;
  // }
});

Update blueprint to use comments


import React from 'react'

class NewQuestion extends React.Component {
  render () {
    return

Create a New Question

  }
}

export default NewQuestion

import React from 'react'
import {
  Form, FormGroup, ControlLabel, FormControl, Col
} from 'react-bootstrap'
import Button from 'react-bootstrap-button-loader'

class NewQuestion extends React.Component {

  constructor (props) {
    super(props)
    this.state = { question: '', answer: '', isLoading: false }
    this.handleChange = this.handleChange.bind(this)
    this.onSubmit = this.onSubmit.bind(this)
  }

  render () {
    return

Create a New Question

      <Form horizontal>
        <FormGroup>
          <Col sm={2} componentClass={ControlLabel}>Question</Col>
          <Col sm={10}>
            <FormControl
              componentClass='textarea'
              name='question'
              value={this.state.question}
              placeholder='Enter your question here...'
              onChange={this.handleChange}
            />
          </Col>
        </FormGroup>
        <FormGroup>
          <Col sm={2} componentClass={ControlLabel}>Answer</Col>
          <Col sm={10}>
            <FormControl
              type='text'
              name='answer'
              value={this.state.answer}
              placeholder='Enter the correct answer here...'
              onChange={this.handleChange}
            />
          </Col>
        </FormGroup>
        <FormGroup>
          <Col smOffset={2} sm={10}>
            <Button
              bsStyle='primary'
              bsSize='large'
              loading={this.state.isLoading}
              disabled={!this.validFormInput()}
              onClick={this.onSubmit}
            >
              Create
            </Button>
          </Col>
        </FormGroup>
      </Form>

  }

  handleChange (event) {
    let target = event.target
    this.setState({ [target.name]: target.value })
  }

  onSubmit () {
    this.setState({ isLoading: true })
  }

  validFormInput () {
    return this.state.question !== '' && this.state.answer !== ''
  }

}

export default NewQuestion

Add form for for creating a new question.


import BaseProvider from "torii/providers/base";
import ajax from "../utils/ajax";
import config from "../config/environment";

export default BaseProvider.extend({
  open: function(credentials){
    return ajax(config.authBaseUri+'/tokens', {
      type: 'POST',
      data: credentials
    }).catch(function(jqXHR){
      if (jqXHR.responseJSON) {
        throw new Error(jqXHR.responseJSON.message);
      } else if (jqXHR.responseText) {
        throw new Error(jqXHR.responseText);
      } else {
        throw new Error("Unknown error from the server.");
      }
    });
  }

});

import BaseProvider from "torii/providers/base";
import ajax from "../utils/ajax";
import config from "../config/environment";

export default BaseProvider.extend({
  open: function(credentials){
    return ajax(config.authBaseUri+'/tokens', {
      type: 'POST',
      data: credentials,
      xhrFields: { withCredentials: true }
    }).catch(function(jqXHR){
      if (jqXHR.responseJSON) {
        throw new Error(jqXHR.responseJSON.message);
      } else if (jqXHR.responseText) {
        throw new Error(jqXHR.responseText);
      } else {
        throw new Error("Unknown error from the server.");
      }
    });
  }

});

Send the cookies with the request


const {
    spawn
} = require("child_process");

const gitCommand = "git";
const gitDiff = "diff";
const statOption = "--numstat";

function promisify(child_process) {
    return new Promise((resolve, reject) => {
        const result = "";

        child_process.stdout.on('data', data => {
            result.push(data.toString());
        });

        child_process.on('error', (err) => {
            reject(err);
        })

        child_process.on('close', () => {
            resolve(result);
        });
    });
}

module.exports = {
    getDiff: function (commit1, commit2, options) {

        const gitProcess = spawn(gitCommand, [gitDiff, statOption, commit1, commit2], {
            cwd: options.cwd
        });

        return promisify(gitProcess);
    }
}

const {
    spawn
} = require("child_process");

const Promise = require("bluebird").Promise;

const gitCommand = "git";
const gitDiff = "diff";
const statOption = "--numstat";

function promisify(child) {
    return new Promise((resolve, reject) => {
        let result = "";

        child.stdout.on('data', data => {
            console.log(data.toString());
            result += data;
        });

        child.stderr.on('data', data => {
            console.error(data.toString());
        });

        child.on('error', (err) => {
            reject(err);
        });

        child.on('exit', () => {
            resolve(result);
        });
    });
}

module.exports = {
    getDiff: function (commit1, commit2, options) {

        const gitProcess = spawn(gitCommand, [gitDiff, statOption, commit1, commit2], {
            // stdio: "inherit",
            cwd: options.cwd
        });

        return promisify(gitProcess);
    }
}

Return output from child process git diff


// Code goes here

/*
 * Time to display not just simple data, but data from an object.
 */
var MainController = function($scope, $http) {

	$scope.message = "Hello, Angular World!";

	var whenGetComplete = function(response) {
		$scope.person = response.data;
	}

	var ifErrorResponse = function(reason) {
		$scope.error = "Oops! Could not get person."
	}

	/**
	 * The function, `$http.get` is asynchronous. It returns a promise. A promise is an object with a `then` method. We
	 * supply a callback function invoked when the response is returned to our request.
	 *
	 * In our situation, the callback function simply sets the `$scope.person` to `response.data`.
	 *
	 * If an error occurs, for example because I incorrectly typed the resource, I invoke the error callback,
	 * `ifErrorResponse`. This function sets `$scope.error` to the error message.
	 */
	$http.get("https://api.github.com/users/odetocod").then(whenGetComplete, ifErrorResponse);
}

/*
 * Aha! I just figured out the purpose of the call to `inject`. AngularJS support dependency injection. Calling the
 * `inject` method injects objects (dependencies) into the controller.
 */
MainController.inject = ['$scope', '$http'];
angular.module('first-controller', []).controller('MainController', MainController);

/*
 * This controller now works by making simple text, `message`, and a JavaScript object, `person`, available to the
 * controller.
 *
 */

// Code goes here

/*
 * Time to display not just simple data, but data from an object.
 */
var MainController = function($scope, $http) {

	$scope.message = "Hello, Angular World!";

	var whenGetComplete = function(response) {
		$scope.person = response.data;
	}

	var ifErrorResponse = function(reason) {
		$scope.error = "Oops! Could not get person."
	}

	/**
	 * The function, `$http.get` is asynchronous. It returns a promise. A promise is an object with a `then` method. We
	 * supply a callback function invoked when the response is returned to our request.
	 *
	 * In our situation, the callback function simply sets the `$scope.person` to `response.data`.
	 *
	 * If an error occurs, for example because I incorrectly typed the resource, I invoke the error callback,
	 * `ifErrorResponse`. This function sets `$scope.error` to the error message.
	 */
	$http.get("https://api.github.com/users/odetocode").then(whenGetComplete, ifErrorResponse);
}

/*
 * Aha! I just figured out the purpose of the call to `inject`. AngularJS support dependency injection. Calling the
 * `inject` method injects objects (dependencies) into the controller.
 */
MainController.inject = ['$scope', '$http'];
angular.module('first-controller', []).controller('MainController', MainController);

/*
 * This controller now works by making simple text, `message`, and a JavaScript object, `person`, available to the
 * controller.
 *
 */

Correct the GitHub API call; everything works!


const chai = require('chai')
const exec = require('child_process').exec

var assert = chai.assert

console.log('Testing the app')

describe('Conversion Params', function () {
  // Further code for tests goes here
  it('If no params', function (done) {
    // Test implementation goes here
    exec('currencyconv',
      function (err, stdout, stderr) {
        assert.isNotNull(err)
        done()
      })
  })

  it('If no amount', function (done) {
    exec('currencyconv  --f=USD --to=EUR  --ouput=fulls',
      function (err, stdout, stderr) {
        assert.isNull(err)
        assert.strictEqual(stdout, 'Amount param should be defined\n')
        done()
      })
  })
})

describe('Conversion Logic', function () {
  it('If currency does not exist', function (done) {
    exec('currencyconv  --f=USDG --to=EURs  --ouput=fulls 1',
      function (err, stdout, stderr) {
        assert.isNull(err)
        assert.strictEqual(stdout, 'Currency no supported or bad spell\n')
        done()
      })
  })

  it('If conversion is good', function (done) {
    exec('currencyconv  --f=USD --to=EUR  --ouput=fulls 1',
      function (err, stdout, stderr) {
        assert.isNull(err)
        var result = stdout.split(' ')
        assert.isNotNull(result[0])
        assert.isNumber(parseInt(result[0]))
        done()
      })
  })
})

/*
 global describe
 global it
 */

const chai = require('chai')
const exec = require('child_process').exec
const path = require('path')

var assert = chai.assert

console.log('Testing the app')

var cmd = 'node ' + path.join(__dirname, '../index.js') + ' '

describe('Conversion Params', function () {
  // Further code for tests goes here
  it('If no params', function (done) {
    // Test implementation goes here

    exec(`${cmd}`,
      function (err, stdout, stderr) {
        assert.isNotNull(err)
        done()
      })
  })

  it('If no amount', function (done) {
    exec(`${cmd} --f=USD --to=EUR  --ouput=fulls`,
      function (err, stdout, stderr) {
        assert.isNull(err)
        assert.strictEqual(stdout, 'Amount param should be defined\n')
        done()
      })
  })
})

describe('Conversion Logic', function () {
  it('If currency does not exist', function (done) {
    exec(`${cmd}  --f=USDG --to=EURs  --ouput=fulls 1`,
      function (err, stdout, stderr) {
        assert.isNull(err)
        assert.strictEqual(stdout, 'Currency no supported or bad spell\n')
        done()
      })
  })

  it('If conversion is good', function (done) {
    exec(`${cmd}  --f=USD --to=EUR  --ouput=fulls 1`,
      function (err, stdout, stderr) {
        assert.isNull(err)
        var result = stdout.split(' ')
        assert.isNotNull(result[0])
        assert.isNumber(parseInt(result[0]))
        done()
      })
  })
})

Change the way the test works with the command


'use strict';

const assert = require('assert');
const Dashycode = require('./../../.lib-dist/dashycode');

describe('Dashycode', function () {
	// Technically we should be testing for values up to 0x10FFFF, but we will
	// never see any above 0xFFFF because of how SockJS works.
	const codepoints = Array.from({length: 0x10000}, (v, k) => k);
	const encoded = new Map();

	const encode = (codepoint, allowCaps) => {
		const character = String.fromCodePoint(codepoint);
		const dashycode = Dashycode.encode(character);
		assert.strictEqual(encoded.has(dashycode), false);
		encoded.set(dashycode, character);
	};

	const decode = (dashycode) => {
		const character = Dashycode.decode(dashycode);
		assert.strictEqual(encoded.get(dashycode), character);
	};

	it('should encode all codepoints uniquely', function () {
		return codepoints.reduce((p, codepoint) => (
			p.then(v => encode(codepoint))
		), Promise.resolve());
	});

	it('should decode all codepoints accurately', function () {
		return [...encoded.keys()].reduce((p, dashycode) => (
			p.then(v => decode(dashycode))
		), Promise.resolve());
	});

	after(function () {
		encoded.clear();
	});
});

'use strict';

const assert = require('assert');
const Dashycode = require('./../../.lib-dist/dashycode');

describe('Dashycode', function () {
	// Technically we should be testing for values up to 0x10FFFF, but we will
	// never see any above 0xFFFF because of how SockJS works.
	const codepoints = Array.from({length: 0x10000}, (v, k) => k);
	const encoded = new Map();

	const encode = (codepoint) => {
		const character = String.fromCodePoint(codepoint);
		const dashycode = Dashycode.encode(character);
		assert.strictEqual(encoded.has(dashycode), false);
		encoded.set(dashycode, character);
	};

	const decode = (dashycode) => {
		const character = Dashycode.decode(dashycode);
		assert.strictEqual(encoded.get(dashycode), character);
	};

	it('should encode all codepoints uniquely', function () {
		return codepoints.reduce((p, codepoint) => (
			p.then(v => encode(codepoint))
		), Promise.resolve());
	});

	it('should decode all codepoints accurately', function () {
		return [...encoded.keys()].reduce((p, dashycode) => (
			p.then(v => decode(dashycode))
		), Promise.resolve());
	});

	after(function () {
		encoded.clear();
	});
});

Remove unused parameter from function in Dashycode's unit tests


/*
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([], function () {
    "use strict";

    function displayPromotions() {
        return true;
    }

    function requestParams(queryModel, infiniteScroll) {
        return {
            text: queryModel.get('queryText'),
            auto_correct: infiniteScroll ? false : queryModel.get('autoCorrect')
        };
    }

    function validateQuery(queryModel) {
        return queryModel.get('queryText');
    }

    function waitForIndexes(queryModel) {
        return _.isEmpty(queryModel.get('indexes'));
    }

    return {
        colourboxGrouping: 'results',
        displayPromotions: displayPromotions,
        requestParams: requestParams,
        validateQuery: validateQuery,
        waitForIndexes: waitForIndexes
    }
});

/*
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([], function () {
    "use strict";

    function displayPromotions() {
        return true;
    }

    function requestParams(queryModel, infiniteScroll) {
        return {
            text: queryModel.get('queryText'),
            auto_correct: false
        };
    }

    function validateQuery(queryModel) {
        return queryModel.get('queryText');
    }

    function waitForIndexes(queryModel) {
        return _.isEmpty(queryModel.get('indexes'));
    }

    return {
        colourboxGrouping: 'results',
        displayPromotions: displayPromotions,
        requestParams: requestParams,
        validateQuery: validateQuery,
        waitForIndexes: waitForIndexes
    }
});

Disable spellcheck/auto correct as there are platform issues with it


class Middleware {
    constructor() {

    }

    before(event) {

    }

    after(event) {

    }
}

module.exports = Middleware

class Middleware {
    constructor() {

    }

    before(event) {
        return event
    }

    after(event) {
        return event
    }
}

module.exports = Middleware

Make sure stub methods return event.


// @flow

import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';

import { threadTypes, type ThreadType } from 'lib/types/thread-types';

type Props = {|
  +threadType: ThreadType,
  +color: string,
|};
function ThreadVisibility(props: Props) {
  const { threadType, color } = props;
  const visLabelStyle = [styles.visibilityLabel, { color }];
  if (threadType === threadTypes.CHAT_SECRET) {
    return (
      <View style={styles.container}>
        <Icon name="lock-outline" size={18} color={color} />
        <Text style={visLabelStyle}>Secret</Text>
      </View>
    );
  } else if (threadType === threadTypes.PRIVATE) {
    return (
      <View style={styles.container}>
        <Icon name="person" size={18} color={color} />
        <Text style={visLabelStyle}>Private</Text>
      </View>
    );
  } else {
    return (
      <View style={styles.container}>
        <Icon name="public" size={18} color={color} />
        <Text style={visLabelStyle}>Open</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    flexDirection: 'row',
  },
  visibilityLabel: {
    fontSize: 16,
    fontWeight: 'bold',
    paddingLeft: 4,
  },
});

export default ThreadVisibility;

// @flow

import * as React from 'react';
import { View, Text, StyleSheet } from 'react-native';

import { threadTypes, type ThreadType } from 'lib/types/thread-types';

import ThreadIcon from './thread-icon.react';

type Props = {|
  +threadType: ThreadType,
  +color: string,
|};
function ThreadVisibility(props: Props) {
  const { threadType, color } = props;
  const visLabelStyle = [styles.visibilityLabel, { color }];

  let label;
  if (threadType === threadTypes.CHAT_SECRET) {
    label = 'Secret';
  } else if (threadType === threadTypes.PRIVATE) {
    label = 'Private';
  } else {
    label = 'Open';
  }

  return (
    <View style={styles.container}>
      <ThreadIcon threadType={threadType} color={color} />
      <Text style={visLabelStyle}>{label}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    flexDirection: 'row',
  },
  visibilityLabel: {
    fontSize: 16,
    fontWeight: 'bold',
    paddingLeft: 4,
  },
});

export default ThreadVisibility;

Make thread-visiblity icon use thread-icon


import {
    connect
} from "react-redux";
import FormViewer from "./FormViewer";

const mapStateToProps = (state) => {
		const points = state.waterlines.filter((offset) => {
			return offset && offset.x != undefined && offset.y != undefined;
		});

        points.push(...state.buttocks.filter((offset) => {
            return offset && offset.x !== undefined && offset.y != undefined;
        }));

        return {
            points: points
        };
    },
    mapDispatchToProps = (dispatch) => {
        return {};
    };

export default connect(mapStateToProps, mapDispatchToProps)(FormViewer);

import {
    connect
} from "react-redux";
import FormViewer from "./FormViewer";

const mapStateToProps = (state) => {
		const points = state.waterlines.filter((offset) => {
			return offset && offset.x !== undefined && offset.y !== undefined;
		});

        points.push(...state.buttocks.filter((offset) => {
            return offset && offset.x !== undefined && offset.y !== undefined;
        }));

        points.sort((a, b) => {
            if (a.order < b.order) {
                return -1;
            }
            if (a.order > b.order) {
                return 1;
            }
            // a must be equal to b
            return 0;
        });

        return {
            points: points
        };
    },
    mapDispatchToProps = (dispatch) => {
        return {};
    };

export default connect(mapStateToProps, mapDispatchToProps)(FormViewer);

Sort points based on order, waterlines and buttocks fully integrated now


var context = require('../../src/exceptions/context')

describe("Minified code detection", function() {

  it("Empty source returns false", function() {
		expect(context.isSourceMinified('')).toBe(false)
  })

  it("Minified JS Bundle returns true", function() {
  	var source = require('./data/bundle.js')
		expect(context.isSourceMinified(source)).toBe(true)
  })

})

var context = require('../../src/exceptions/context')

describe("isSourceMinified", function() {

  it("Empty source returns false", function() {
		expect(context.isSourceMinified('')).toBe(false)
  })

  it("Minified JS Bundle returns true", function() {
  	var source = require('./data/bundle.js')
		expect(context.isSourceMinified(source)).toBe(true)
  })

})

Add tests of minified code detection logic


logMessage("Script Begins!");
var Galileo = require("galileo-io");
var board = new Galileo();
var previousData = 0;

logMessage("Setting Ready event for the Galileo Board");
board.on("ready", galileoReadyHandler);
logMessage("Root Execution Complete!");

/*
 * Contains the logic for when the Galileo Board is Ready
 * @returns {undefined}
 */
function galileoReadyHandler() {
    /***
     * This line sets a Digital Read event handler. Every time the Galileo Board completes a read operation
     * it will call the readHandler function to process it.
     **/
    logMessage("Galileo Ready Handler Begins! - Setting a Digital Read Handler...");
    board.digitalRead(10, readHandler);
    logMessage("Galileo Ready Handler Complete!");
}

/***
 * Receives DATA from a read event and uses it.
 * @param data
 * @returns {undefined}
 */
function readHandler(data) {
    logMessage("Read Handler Begins!");
    if (previousData !== data) {
        logMessage("Data Changed!");
        previousData = data;
        logMessage("New Data: " + data);
    }
}

/***
 * Logs messages into the console including a milliseconds timestamp
 * @param {type} message
 * @returns {undefined}
 */
function logMessage(message) {
    var d = new Date().getTime();
    console.log(d + " - " + message);
}

logMessage("Script Begins!");
var Galileo = require("galileo-io");
var board = new Galileo();
var previousData = 0;

logMessage("Setting Ready event for the Galileo Board");
board.on("ready", galileoReadyHandler);
logMessage("Root Execution Complete!");

/*
 * Contains the logic for when the Galileo Board is Ready
 * @returns {undefined}
 */
function galileoReadyHandler() {
    /***
     * This line sets a Digital Read event handler. Every time the Galileo Board completes a read operation
     * it will call the readHandler function to process it.
     **/
    logMessage("Galileo Ready Handler Begins! - Setting a Digital Read Handler...");
    board.digitalRead(10, readHandler);
    logMessage("Galileo Ready Handler Complete!");
}

/***
 * Receives DATA from a read event and uses it.
 * @param data
 * @returns {undefined}
 */
var readtimes = 0;
function readHandler(data) {

    if (readtimes<5) {
        readtimes++;
        logMessage("Read Handler Begins!");
    }
    if (previousData !== data) {
        logMessage("Data Changed!");
        previousData = data;
        logMessage("New Data: " + data);
    }
    if (readtimes<5) {
        logMessage("Read Handler END!");
    }
}

/***
 * Logs messages into the console including a milliseconds timestamp
 * @param {type} message
 * @returns {undefined}
 */
function logMessage(message) {
    var d = new Date().getTime();
    console.log(d + " - " + message);
}

Tidy code for basic examples on NodeJS


// eslint-disable-next-line import/prefer-default-export
export const createInputChanged = actionPrefix => (name, event) => ({
  type: `${actionPrefix}_INPUT_CHANGED`,
  name,
  id: event.target.id,
  startovne: event.target.nonce,
  value: event.target.type === 'checkbox' ? event.target.checked : event.target.value
});

// eslint-disable-next-line import/prefer-default-export
export const createInputChanged = actionPrefix => (name, event) => {
  const action = {
    type: `${actionPrefix}_INPUT_CHANGED`,
    name,
    id: event.target.id,
    value: event.target.type === 'checkbox' ? event.target.checked : event.target.value
  };

  if (event.target.nonce) {
    const startovne = parseInt(event.target.nonce, 10);
    if (!Number.isNaN(startovne)) {
      action.startovne = startovne;
    }
  }

  return action;
};

Convert startovne to a number.


import {createStore, compose, applyMiddleware} from 'redux';
import {devTools, persistState} from 'redux-devtools';
import thunkMiddleware from 'redux-thunk';
import travelApp from 'reducers/Reducers.js';

const enhancedCreateStore = compose(
  applyMiddleware(
    thunkMiddleware),
  devTools(),
  persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);

export default enhancedCreateStore(travelApp);

import {createStore, compose, applyMiddleware} from 'redux';
import {devTools, persistState} from 'redux-devtools';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import travelApp from 'reducers/Reducers.js';

const logger = createLogger();

const enhancedCreateStore = compose(
  applyMiddleware(
    logger,
    thunkMiddleware),
  devTools(),
  persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);

export default enhancedCreateStore(travelApp);

Apply the logger as middleware


'use strict';

angular.module('ticketbox.boxoffice.block', [
    'ngRoute',
    'ticketbox.config',
    'ticketbox.components.api',
    'ticketbox.common.seatplan.handlers',
    'ticketbox.components.seatplan',
    'ticketbox.components.reserver',
    'ticketbox.boxoffice.toolbar'])

    .config(function($routeProvider) {
        $routeProvider.when('/block/:blockId', {
            controller: 'BlockCtrl',
            templateUrl: 'boxoffice.block/boxoffice.block.html'
        });
    })

    .controller('BlockCtrl', function($scope, $routeParams, $location, Eventblock, reserver, maxNumberOfUnspecifiedSeats) {
        $scope.block = Eventblock.get({ 'id': $routeParams.blockId });

        $scope.selectableNumbersOfUnspecifiedSeats = _.range(1, maxNumberOfUnspecifiedSeats + 1);
        $scope.data = {
            numberOfSeats: 0
        };

        $scope.reserveMultiple = function(block, numberOfSeats) {
            reserver.reserveMultiple(block.id, numberOfSeats)
                .then(function() {
                    $scope.data.numberOfSeats = undefined;
                });
        }
    });

'use strict';

angular.module('ticketbox.boxoffice.block', [
    'ngRoute',
    'ticketbox.config',
    'ticketbox.components.api',
    'ticketbox.common.seatplan.handlers',
    'ticketbox.components.seatplan',
    'ticketbox.components.reserver',
    'ticketbox.boxoffice.toolbar'])

    .config(function($routeProvider) {
        $routeProvider.when('/block/:blockId', {
            controller: 'BlockCtrl',
            templateUrl: 'boxoffice.block/boxoffice.block.html'
        });
    })

    .controller('BlockCtrl', function($scope, $routeParams, $location, Eventblock, reserver, maxNumberOfUnspecifiedSeats) {
        $scope.block = Eventblock.get({ 'id': $routeParams.blockId });

        $scope.selectableNumbersOfUnspecifiedSeats = _.range(1, maxNumberOfUnspecifiedSeats + 1);
        $scope.data = {
            numberOfSeats: 0
        };

        $scope.reserveMultiple = function(block, numberOfSeats) {
            reserver.reserveMultiple(block.id, numberOfSeats)
                .then(function() {
                    $scope.data.numberOfSeats = 0;
                });
        }
    });

Set back number of seats when unspecified seats are reserved


var inspect = require("util").inspect
var path = require("path")

module.exports = function (callback) {
  try_in_order([
    function (callback) {
      if (process.env.RUN_SWF_VERBOSE) {
        console.warn("run-swf: looking for $FLASHPLAYER...")
      }

      if (process.env.FLASHPLAYER) {
        get_program(process.env.FLASHPLAYER, callback)
      } else {
        callback(null)
      }
    },
    function (callback) {
      get_osx_app_program("Flash Player Debugger", callback)
    },
    function (callback) {
      get_osx_app_program("Flash Player", callback)
    }
  ], callback)

  function get_osx_app_program(name, callback) {
    get_program(
      "/Applications/" + name + ".app/Contents/MacOS/" + name,
      callback
    )
  }

  function get_program(filename, callback) {
    if (process.env.RUN_SWF_VERBOSE) {
      console.warn("run-swf: looking for %s...", inspect(filename))
    }

    path.exists(filename, function (exists) {
      callback(exists ? filename : null)
    })
  }
}

function try_in_order(functions, callback) {
  functions = [].slice.call(functions)
  ;(function loop () {
    if (functions.length === 0) {
      callback(null)
    } else {
      functions.shift()(function (program) {
        if (program) {
          callback(program)
        } else {
          loop()
        }
      })
    }
  })()
}

var inspect = require("util").inspect
var fs = require("fs")

module.exports = function (callback) {
  try_in_order([
    function (callback) {
      if (process.env.RUN_SWF_VERBOSE) {
        console.warn("run-swf: looking for $FLASHPLAYER...")
      }

      if (process.env.FLASHPLAYER) {
        get_program(process.env.FLASHPLAYER, callback)
      } else {
        callback(null)
      }
    },
    function (callback) {
      get_osx_app_program("Flash Player Debugger", callback)
    },
    function (callback) {
      get_osx_app_program("Flash Player", callback)
    }
  ], callback)

  function get_osx_app_program(name, callback) {
    get_program(
      "/Applications/" + name + ".app/Contents/MacOS/" + name,
      callback
    )
  }

  function get_program(filename, callback) {
    if (process.env.RUN_SWF_VERBOSE) {
      console.warn("run-swf: looking for %s...", inspect(filename))
    }

    fs.exists(filename, function (exists) {
      callback(exists ? filename : null)
    })
  }
}

function try_in_order(functions, callback) {
  functions = [].slice.call(functions)
  ;(function loop () {
    if (functions.length === 0) {
      callback(null)
    } else {
      functions.shift()(function (program) {
        if (program) {
          callback(program)
        } else {
          loop()
        }
      })
    }
  })()
}

Use fs.exists instead of path.exists


import roundNumber from 'dummy/utils/round-number';
import { module, test } from 'qunit';

module('Unit | Utility | round number');

test('it rounds a number to two decimal places by default', function(assert) {
  let result = roundNumber(123.123);
  assert.equal(result, 123.12);
});

test('it rounds a number to a configurable number of decimal places', function(assert) {
  let result = roundNumber(123.123123, 1);
  assert.equal(result, 123.1);
});

test('it returns undefined when nothing provided', function(assert) {
  let result = roundNumber();
  assert.equal(result, undefined);
});

test('it parses a String number', function(assert) {
  let result = roundNumber('34.3333');
  assert.equal(result, 34.33);
});

test('it returns undefined when provided a string that is not a number', function(assert) {
  let result = roundNumber('boogers');
  assert.equal(result, undefined);
});

import roundNumber from 'dummy/utils/round-number';
import { module, test } from 'qunit';

module('Unit | Utility | round number');

test('it rounds a number to two decimal places by default', function(assert) {
  let result = roundNumber(123.123);
  assert.equal(result, 123.12);
});

test('it rounds a number to a configurable number of decimal places', function(assert) {
  let result = roundNumber(123.123123, 1);
  assert.equal(result, 123.1);
});

test('it rounds a number to a whole number if precision is 0', function(assert) {
  let result = roundNumber(123.123123, 0);
  assert.equal(result, 123);
});

test('it returns undefined when nothing provided', function(assert) {
  let result = roundNumber();
  assert.equal(result, undefined);
});

test('it parses a String number', function(assert) {
  let result = roundNumber('34.3333');
  assert.equal(result, 34.33);
});

test('it returns undefined when provided a string that is not a number', function(assert) {
  let result = roundNumber('boogers');
  assert.equal(result, undefined);
});

Add Unit Test For precision=0


var assert = require('chai').assert;
var StorageService = require('../');
var AmazonStorage = StorageService.AmazonStorage;

describe('Factory Method', function () {
  it('Should properly export', function () {
    assert.isObject(StorageService);
    assert.isFunction(StorageService.create);
    assert.isFunction(StorageService.AmazonStorage);
  });

  it('Should properly create storage instances', function () {
    assert.instanceOf(StorageService.create('amazon'), AmazonStorage);
  });
});

var assert = require('chai').assert;
var StorageService = require('../');
var AmazonStorage = StorageService.AmazonStorage;

describe('Factory Method', function () {
  it('Should properly export', function () {
    assert.isObject(StorageService);
    assert.isFunction(StorageService.create);
    assert.isFunction(StorageService.AmazonStorage);
  });

  it('Should properly create storage instances', function () {
    assert.instanceOf(StorageService.create('amazon'), AmazonStorage);
  });

  it('Should properly throw error on unrecognized type', function () {
    assert.throws(function () {
      StorageService.create('NOT_EXISTS');
    }, Error);
  });
});

Improve test coverage for StorageService


import { useEffect, useMemo } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import reducerRegistry from 'redux/registry';

import * as actions from './actions';
import { getLatestProps } from './selectors';
import reducers, { initialState } from './reducers';

const LatestProvider = ({ latestEndpoints, getLatest }) => {
  const endpoint = useMemo(() => {
    return latestEndpoints;
  }, [latestEndpoints]);

  useEffect(() => {
    getLatest(endpoint);
  }, [endpoint]);

  return null;
};

LatestProvider.propTypes = {
  getLatest: PropTypes.func.isRequired,
  latestEndpoints: PropTypes.array,
};

reducerRegistry.registerModule('latest', {
  actions,
  reducers,
  initialState,
});

export default connect(getLatestProps, actions)(LatestProvider);

import { PureComponent } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import reducerRegistry from 'redux/registry';
import isEqual from 'lodash/isEqual';

import * as actions from './actions';
import { getLatestProps } from './selectors';
import reducers, { initialState } from './reducers';

class LatestProvider extends PureComponent {
  componentDidUpdate(prevProps) {
    const { getLatest, latestEndpoints } = this.props;
    const { latestEndpoints: prevLatestEndpoints } = prevProps;

    if (!isEqual(latestEndpoints, prevLatestEndpoints)) {
      getLatest(latestEndpoints);
    }
  }

  render() {
    return null;
  }
}

LatestProvider.propTypes = {
  getLatest: PropTypes.func.isRequired,
  latestEndpoints: PropTypes.array,
};

reducerRegistry.registerModule('latest', {
  actions,
  reducers,
  initialState,
});

export default connect(getLatestProps, actions)(LatestProvider);

Fix loop if same provider endpoints


class MCProjectHomeRemindersComponentController {
    /*@ngInject*/
    constructor(projectsAPI) {
        this.projectsAPI = projectsAPI;
    }

    removeReminder(index) {
        this.project.reminders.splice(index, 1);
    }

    addReminder() {
        this.project.reminders.push({note: '', status: 'none'});
    }

    updateReminders() {
        this.projectsAPI.updateProject(this.project.id, {reminders: this.project.reminders});
    }
}

angular.module('materialscommons').component('mcProjectHomeReminders', {
    templateUrl: 'app/project/home/mc-project-home-reminders.html',
    controller: MCProjectHomeRemindersComponentController,
    bindings: {
        project: '<'
    }
});

class MCProjectHomeRemindersComponentController {
    /*@ngInject*/
    constructor(projectsAPI) {
        this.projectsAPI = projectsAPI;
    }

    removeReminder(index) {
        this.project.reminders.splice(index, 1);
        this.projectsAPI.updateProject(this.project.id, {reminders: this.project.reminders});
    }

    addReminder() {
        this.project.reminders.push({note: '', status: 'none'});
    }

    updateReminders() {
        this.projectsAPI.updateProject(this.project.id, {reminders: this.project.reminders});
    }
}

angular.module('materialscommons').component('mcProjectHomeReminders', {
    templateUrl: 'app/project/home/mc-project-home-reminders.html',
    controller: MCProjectHomeRemindersComponentController,
    bindings: {
        project: '<'
    }
});

Delete project reminder on backend


export default [
  {
    weight: 10,
    type: 'textfield',
    input: true,
    placeholder: 'Panel Title',
    label: 'Title',
    key: 'title',
    tooltip: 'The title text that appears in the header of this panel.'
  },
  {
    weight: 20,
    type: 'textarea',
    input: true,
    key: 'tooltip',
    label: 'Tooltip',
    placeholder: 'To add a tooltip to this field, enter text here.',
    tooltip: 'Adds a tooltip to the side of this field.'
  },
  {
    weight: 30,
    type: 'select',
    input: true,
    label: 'Theme',
    key: 'theme',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Default', value: 'default' },
        { label: 'Primary', value: 'primary' },
        { label: 'Info', value: 'info' },
        { label: 'Success', value: 'success' },
        { label: 'Danger', value: 'danger' },
        { label: 'Warning', value: 'warning' }
      ]
    }
  },
  {
    weight: 40,
    type: 'select',
    input: true,
    label: 'Show Breadcrumb',
    key: 'breadcrumb',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Yes', value: 'default' },
        { label: 'No', value: 'none' }
      ]
    }
  }
];

export default [
  {
    key: 'label',
    hidden: true,
    calculateValue: 'value = data.title'
  },
  {
    weight: 1,
    type: 'textfield',
    input: true,
    placeholder: 'Panel Title',
    label: 'Title',
    key: 'title',
    tooltip: 'The title text that appears in the header of this panel.'
  },
  {
    weight: 20,
    type: 'textarea',
    input: true,
    key: 'tooltip',
    label: 'Tooltip',
    placeholder: 'To add a tooltip to this field, enter text here.',
    tooltip: 'Adds a tooltip to the side of this field.'
  },
  {
    weight: 30,
    type: 'select',
    input: true,
    label: 'Theme',
    key: 'theme',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Default', value: 'default' },
        { label: 'Primary', value: 'primary' },
        { label: 'Info', value: 'info' },
        { label: 'Success', value: 'success' },
        { label: 'Danger', value: 'danger' },
        { label: 'Warning', value: 'warning' }
      ]
    }
  },
  {
    weight: 40,
    type: 'select',
    input: true,
    label: 'Show Breadcrumb',
    key: 'breadcrumb',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Yes', value: 'default' },
        { label: 'No', value: 'none' }
      ]
    }
  }
];

Hide the label settings for Panels since they have a title field.


// start the web server. (we'll soon write a dedicated script to do this.)
if (module.id === require.main) {
    require('ringo/httpserver').start();
}

// start the web server. (we'll soon write a dedicated script to do this.)
if (module == require.main) {
    require('ringo/webapp').start();
}

Make jsgi demo app run again


import React from 'react'
import RestaurantModel from './RestaurantModel'

class Restaurant extends React.Component {
  constructor(props) {
    super(props)
    this.displayName = "Restaurant"
  }

  render() {
    // helper for code simplicity.
    var model = this.props.model

    // Controller calculats food icon for HTML View presentation.
    var foodTypeImage = "";
    if (model.foodType === "Burger") {
      foodTypeImage = "assets/img/Burger.png"
    } else {
      foodTypeImage = "http://placeponi.es/48/48"
    }

    // Prepare rating JSX
    var ratings = [];
    for (var i=0; i < model.rating; i++) {
        ratings.push();
    }

    return (

{model.name}

            Rating: {ratings}

    )
  }
}

Restaurant.propTypes = {
  model: React.PropTypes.instanceOf(RestaurantModel)
}

Restaurant.defaultProps = {
  restaurant : new RestaurantModel("NameOfPlace", "Burger", 2, "Dubnov 7, Tel Aviv-Yafo, Israel")
}

export default Restaurant

Use better css selector naming.


(function () {
    'use strict';

    angular
        .module('app.core')
        .run(function($rootScope, $state) {
            return $rootScope.$on('$stateChangeStart', function() {
                return $rootScope.$state = $state;
            });
        })
        .config(function ($stateProvider, $urlRouterProvider) {

            $urlRouterProvider.otherwise('/home');

            $stateProvider
                .state('home', {
                    url: '/home',
                    controller: 'Home',
                    controllerAs: 'home',
                    templateUrl: 'app/home/home.html',
                    data: {
                        title: 'Home'
                    }
                })
                .state('color', {
                    url: '/color',
                    controller: 'Color',
                    controllerAs: 'color',
                    templateUrl: 'app/color/color.html',
                    data: {
                        title: 'Color'
                    }
                })
                .state('404', {
                    url: '/404',
                    templateUrl: 'app/core/404.html',
                    data: {
                        title: '404'
                    }
                });

        });

}());

(function () {
    'use strict';

    angular
        .module('app.core')
        .run(function($rootScope, $state) {
            return $rootScope.$on('$stateChangeStart', function() {
                $rootScope.$state = $state;
            });
        })
        .config(function ($stateProvider, $urlRouterProvider) {

            $urlRouterProvider.otherwise('/home');

            $stateProvider
                .state('home', {
                    url: '/home',
                    controller: 'Home',
                    controllerAs: 'home',
                    templateUrl: 'app/home/home.html',
                    data: {
                        title: 'Home'
                    }
                })
                .state('color', {
                    url: '/color',
                    controller: 'Color',
                    controllerAs: 'color',
                    templateUrl: 'app/color/color.html',
                    data: {
                        title: 'Color'
                    }
                })
                .state('404', {
                    url: '/404',
                    templateUrl: 'app/core/404.html',
                    data: {
                        title: '404'
                    }
                });

        });

}());

Correct code to remove JSHint error "Did you mean to return a conditional instead of an assignment?"


import { createStore, applyMiddleware } from 'redux'

import rootReducer from '../reducers'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'

const loggerMiddleware = createLogger()

export default function configureStore(initialState) {
  return createStore(
    rootReducer,
    applyMiddleware(
      thunkMiddleware, // lets us dispatch() functions
      loggerMiddleware // neat middleware that logs actions
    ),
    initialState
  )
}

import { createStore, applyMiddleware } from 'redux'

import rootReducer from '../reducers'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'

const loggerMiddleware = createLogger()

export default function configureStore(initialState) {
 	const store = createStore(
    rootReducer,
    applyMiddleware(
      thunkMiddleware, // lets us dispatch() functions
      loggerMiddleware // neat middleware that logs actions
    ),
    initialState
  )

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

	return store;
}

Enable store module hot loading


const ts = require('ts-node')

exports.config = {
  baseUrl: 'http://localhost:3000',
  specs: ['test-e2e/**/*.spec.ts'],
  directConnect: true,
  capabilities: {
    'browserName': 'chrome',
    'chromeOptions': {
      'args': [
        '--show-fps-counter',
        '--no-default-browser-check',
        '--no-first-run',
        '--disable-default-apps',
        '--disable-popup-blocking',
        '--disable-translate',
        '--disable-background-timer-throttling',
        '--disable-renderer-backgrounding',
        '--disable-device-discovery-notifications',
        '--no-gpu',
        '--headless'
      ]
    }
  },
  onPrepare: () => {
    ts.register({
      compilerOptions: { module: 'commonjs' },
      fast: true
    })
  },
  plugins: [{
    package: 'aurelia-protractor-plugin'
  }],
  SELENIUM_PROMISE_MANAGER: 0
}

const ts = require('ts-node')

exports.config = {
  baseUrl: 'http://localhost:3000',
  specs: ['test-e2e/**/*.spec.ts'],
  directConnect: true,
  capabilities: {
    'browserName': 'chrome',
    'chromeOptions': {
      'args': [
        '--show-fps-counter',
        '--no-default-browser-check',
        '--no-first-run',
        '--disable-default-apps',
        '--disable-popup-blocking',
        '--disable-translate',
        '--disable-background-timer-throttling',
        '--disable-renderer-backgrounding',
        '--disable-device-discovery-notifications',
        '--no-gpu',
        '--headless'
      ]
    }
  },
  beforeLaunch: () => {
    ts.register({
      compilerOptions: { module: 'commonjs' },
      fast: true
    })
  },
  plugins: [{
    package: 'aurelia-protractor-plugin'
  }],
  SELENIUM_PROMISE_MANAGER: 0
}

Use before-lauch as highlighted in the protractor docs.


(function ( $ ) {

    $.fn.onFocusLeave = function(cb) {

        return this.each(function() {

            var $this = $(this),
                timeout;

            $(this).on('focusout', function(e) {
                timeout = window.setTimeout(function(){cb($this);}, 50);
            });

            $(this).on('focusin', function(e) {
                window.clearTimeout(timeout);
            });
        });
    };

}( jQuery ));

(function ( $ ) {

    $.fn.onFocusLeave = function(cb) {

        return this.each(function() {

            var $this = $(this),
                timeout;

            $this.on('focusout', function(e) {
                timeout = window.setTimeout(function(){cb($this);}, 50);
            });

            $this.on('focusin', function(e) {
                window.clearTimeout(timeout);
            });
        });
    };

}( jQuery ));

Use cached version of $this


import PlayersStats from './PlayersStats'

test('should add player stat to collection', () => {
  // given
  let repository = new PlayersStats([])
  let playerStats = { id: 1, data: 'some-data' }

  // when
  let added = repository.add(playerStats)

  // then
  expect(added).toBeTruthy()
  expect(repository.all().length).toBe(1)
  expect(repository.all()[0]).toBe(playerStats)
})

test('should return players stats collection', () => {
  // given
  let playersStats = [
    { id: 1, data: 'some-data' },
    { id: 2, data: 'another-data' }
  ]
  let repository = new PlayersStats(playersStats)

  // when
  let collection = repository.all()

  // then
  expect(collection.length).toBe(2)
  expect(collection).toEqual(expect.arrayContaining(playersStats))
})

test('should export data with a given exporter service', () => {
  // given
  let playersStats = [
    { id: 1, data: 'some-data' },
    { id: 2, data: 'another-data' }
  ]
  let mockExport = jest.fn()
  let repository = new PlayersStats(playersStats, { export: mockExport })

  // when
  repository.export()

  // then
  expect(mockExport).toBeCalled()
  expect(mockExport.mock.calls.length).toBe(1)
  expect(mockExport).toBeCalledWith('players_stats', playersStats)
})

import PlayersStats from './PlayersStats'

test('should add player stat to collection', () => {
  // given
  let repository = new PlayersStats()
  let playerStats = { id: 1, data: 'some-data' }

  // when
  let added = repository.add(playerStats)

  // then
  expect(added).toBeTruthy()
  expect(repository.all().length).toBe(1)
  expect(repository.all()[0]).toBe(playerStats)
})

test('should return players stats collection', () => {
  // given
  let playersStats = [
    { id: 1, data: 'some-data' },
    { id: 2, data: 'another-data' }
  ]
  let repository = new PlayersStats(playersStats)

  // when
  let collection = repository.all()

  // then
  expect(collection.length).toBe(2)
  expect(collection).toEqual(expect.arrayContaining(playersStats))
})

test('should export data with a given exporter service', () => {
  // given
  let playersStats = [
    { id: 1, data: 'some-data' },
    { id: 2, data: 'another-data' }
  ]
  let mockExport = jest.fn()
  let repository = new PlayersStats(playersStats, { export: mockExport })

  // when
  repository.export()

  // then
  expect(mockExport).toBeCalled()
  expect(mockExport.mock.calls.length).toBe(1)
  expect(mockExport).toBeCalledWith('players_stats', playersStats)
})

Test playersstats without default empty array


foam.CLASS({
  package: 'foam.apploader',
  name: 'ModelRefines',
  refines: 'foam.core.Model',
  methods: [
    {
      name: 'getClassDeps',
      code: function() {
        var deps = this.requires ?
            this.requires.map(function(r) { return r.path }) :
            [];

        deps = deps.concat(this.implements ?
                           this.implements.map(function(i) { return i.path }) :
                           []);

        if ( this.extends ) deps.push(this.extends);
        return deps;
      }
    },
  ],
});

foam.CLASS({
  package: 'foam.apploader',
  name: 'ModelRefines',
  refines: 'foam.core.Model',
  methods: [
    {
      name: 'getClassDeps',
      code: function() {
        var deps = this.requires ?
            this.requires.map(function(r) { return r.path }) :
            [];

        deps = deps.concat(this.implements ?
                           this.implements.map(function(i) { return i.path }) :
                           []);

        if ( this.extends ) deps.push(this.extends);

        if ( this.refines ) deps.push(this.refines);

        return deps.map(function(d) {
          if ( d.indexOf('.') == -1 ) return 'foam.core.' + d;
          return d;
        });
        return deps;
      }
    },
  ],
});

Add refines to getClassDeps and prepend any deps with no package with 'foam.core.'


module.exports = {
    factcheck: (message, _, msg) => {
        const bool1 = (Math.random() > 0.5);
        const bool2 = (Math.random() > 0.5);
        let str;

        if (msg) {
            str = `${message.author}'s claim, "${msg}",`;
            str = bool1 ? `${str} is obviously ${bool2.toString()}.`
                        : `${str} can't possibly be ${bool2.toString()}.`;
        } else {
            str = bool1 ? `${message.author} is always ${bool2 ? 'right' : 'wrong'}.`
                        : `${message.author} is never ${bool2 ? 'right' : 'wrong'}.`;
        }
        message.channel.send(str);
    },

    coin: (message) => {
        const bool = (Math.random() > 0.5);
        message.channel.send(bool ? 'Heads.' : 'Tails.');
    },

    dice: (message, _, __, n) => {
        n = Math.max(Number(n), 0) || 6;
        message.channel.send(Math.floor(Math.random() * n) + 1)
            .catch(() => message.channel.send('Input a valid number!'));
    },
};

module.exports = {
    factcheck: (message, _, msg) => {
        const bool1 = (Math.random() > 0.5);
        const bool2 = (Math.random() > 0.5);
        let str;

        if (msg) {
            str = `${message.author}'s claim, "${msg}",`;
            str = bool1 ? `${str} is obviously ${bool2.toString()}.`
                        : `${str} can't possibly be ${bool2.toString()}.`;
        } else {
            str = bool1 ? `${message.author} is always ${bool2 ? 'right' : 'wrong'}.`
                        : `${message.author} is never ${bool2 ? 'right' : 'wrong'}.`;
        }
        message.channel.send(str);
    },

    coin: (message) => {
        const bool = (Math.random() > 0.5);
        const headsUrl = 'http://www.antheads.co.uk/_/rsrc/1467896244461/catguide/heads/Heads-21-30.jpg';
        const tailsUrl = 'https://upload.wikimedia.org/wikipedia/en/7/73/Sonicchannel_tails_cg.png';
        message.channel.send(bool ? `Heads.\n${headsUrl}`
                                  : `Tails.\n${tailsUrl}`);
    },

    dice: (message, _, __, n) => {
        n = Math.max(Number(n), 0) || 6;
        message.channel.send(Math.floor(Math.random() * n) + 1)
            .catch(() => message.channel.send('Input a valid number!'));
    },
};

Add images to coin command


import Ember from 'ember';
import layout from './template';

export default Ember.Component.extend({
  layout: layout,
  tagName: 'i',
  classNames: ['fa'],
  classNameBindings: ['nameComputed'],

  // Constructors
  classPrefix: 'fa',

  // Computed
  nameComputed: Ember.computed('style', function() {
    // Builds the icon class.
    // REVIEW: I wonder if there's an "easy" way to have this check if the icon
    //   name exists that doesn't rely on manually updating an array. It would
    //   be nice to give an Ember warning if the icon name doesn't exist.
    var iconName = this.get('name');
    return this.get('classPrefix') + '-' + iconName;
  })
});

import Ember from 'ember';
import layout from './template';

export default Ember.Component.extend({
  layout: layout,
  tagName: 'i',
  classNames: ['fa'],
  classNameBindings: ['nameComputed'],

  // Constructors
  classPrefix: 'fa',

  // Computed
  nameComputed: Ember.computed('name', function() {
    // Builds the icon class.
    // REVIEW: I wonder if there's an "easy" way to have this check if the icon
    //   name exists that doesn't rely on manually updating an array. It would
    //   be nice to give an Ember warning if the icon name doesn't exist.
    var iconName = this.get('name');
    return this.get('classPrefix') + '-' + iconName;
  })
});

Change computed to update on name change


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

describe('let', function() {
  it('variables declared with let are accessible within nested blocks', function() {
    'use strict';
    let foo = 'bar';

    if (true) {
      expect(foo).to.equal('bar');
    }
  });
});

'use strict';
var expect = require('chai').expect;

describe('let', function() {
  it('variables declared with let are accessible within nested blocks', function() {
    let foo = 'bar';

    if (true) {
      expect(foo).to.equal('bar');
    }
  });
});

Use string for all tests


/**
 * In order to support reload-and-profile functionality, the renderer needs to be injected before any other scripts.
 * Since it is a complex file (with imports) we can't just toString() it like we do with the hook itself,
 * So this entry point (one of the web_accessible_resources) provcides a way to eagerly inject it.
 * The hook will look for the presence of a global __REACT_DEVTOOLS_ATTACH__ and attach an injected renderer early.
 * The normal case (not a reload-and-profile) will not make use of this entry point though.
 *
 * @flow
 */

import { attach } from 'src/backend/renderer';

Object.defineProperty(
  window,
  '__REACT_DEVTOOLS_ATTACH__',
  ({
    enumerable: false,
    get() {
      return attach;
    },
  }: Object)
);

/**
 * In order to support reload-and-profile functionality, the renderer needs to be injected before any other scripts.
 * Since it is a complex file (with imports) we can't just toString() it like we do with the hook itself,
 * So this entry point (one of the web_accessible_resources) provcides a way to eagerly inject it.
 * The hook will look for the presence of a global __REACT_DEVTOOLS_ATTACH__ and attach an injected renderer early.
 * The normal case (not a reload-and-profile) will not make use of this entry point though.
 *
 * @flow
 */

import { attach } from 'src/backend/renderer';

Object.defineProperty(
  window,
  '__REACT_DEVTOOLS_ATTACH__',
  ({
    enumerable: false,
    configurable: true,
    get() {
      return attach;
    },
  }: Object)
);

Mark reload-and-profile attach as configurable


'use strict';
/* eslint-disable indent */

module.exports = function(tx) {
  var body = tx.get('body');

  tx.create({
    id: 'title',
    type: 'heading',
    level: 1,
    content: 'Input Element'
  });
  body.show('title');

  tx.create({
    id: 'intro',
    type: 'paragraph',
    content: [
      "You can use custom elements with an HTML input element"
    ].join('')
  });
  body.show('intro');

  tx.create({
    type: 'input',
    id: 'input',
    content: 'Lorem ipsum...'
  });
  body.show('input');

  tx.create({
    id: 'the-end',
    type: 'paragraph',
    content: [
     "That's it."
    ].join('')
  });
  body.show('the-end');

};

'use strict';
/* eslint-disable indent */

module.exports = function(tx) {
  var body = tx.get('body');

  tx.create({
    id: 'title',
    type: 'heading',
    level: 1,
    content: 'Input Element'
  });
  body.show('title');

  tx.create({
    id: 'intro',
    type: 'paragraph',
    content: [
      "You can use custom elements with an HTML input element"
    ].join('')
  });
  body.show('intro');

  tx.create({
    type: 'input',
    id: 'input',
    content: 'Lorem ipsum...'
  });
  body.show('input');

  tx.create({
    id: 'the-end',
    type: 'paragraph',
    content: [
     "That way you can implement editor functionality using class web development practices."
    ].join('')
  });
  body.show('the-end');

};

Add more description to input example.


// import { take, call, put, select } from 'redux-saga/effects';
import { REQUEST_TOPICS } from './constants';
import { takeLatest } from 'redux-saga';
import { call } from 'redux-saga/effects';

export function fetchTopicsFromServer() {
  return fetch('http://locahost:3000/api/topics')
    .then(response => response.json());
}

// Generator functions: do async logic without
// using callbacks.
function* fetchTopics() {
  const topics = yield call(fetchTopicsFromServer);
  console.log('Topics from server: ', topics);
}

export function* fetchTopicsSaga() {
  yield* takeLatest(REQUEST_TOPICS, fetchTopics);
}

// All sagas to be loaded
export default [
  fetchTopicsSaga,
];

// import { take, call, put, select } from 'redux-saga/effects';
import { REQUEST_TOPICS } from './constants';
import { takeLatest } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import { requestTopicsSucceeded, requestTopicsFailed } from './actions';

export function fetchTopicsFromServer() {
  return fetch('http://localhost:3000/api/topics')
    .then(response => response.json());
}

// Generator functions: do async logic without
// using callbacks.
function* fetchTopics() {
  try {
    const topics = yield call(fetchTopicsFromServer);
    yield put(requestTopicsSucceeded(topics));
  } catch (e) {
    yield put(requestTopicsFailed(e.message));
  }
}

export function* fetchTopicsSaga() {
  yield* takeLatest(REQUEST_TOPICS, fetchTopics);
}

// All sagas to be loaded
export default [
  fetchTopicsSaga,
];

Handle response from server in saga


/**
 * Array with fixed max length and displacing first element on oversize.
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 10.05.18
 */
Imcms.define('imcms-displacing-array', [], function () {

    var DisplacingArray = function (size) {
        if (typeof size !== 'number') throw new Error("Size should be integer!");
        if (size < 1) throw new Error("Size should be >= 1");

        this.size = size;
        this.elements = [];
    };

    DisplacingArray.prototype = {
        push: function (content) {
            if (this.elements.length > this.size) {
                this.elements.splice(0, 1); // removes first element
            }

            this.elements.push(content);
        },
        forEach: function (doForEach) {
            this.elements.forEach(doForEach);
        }
    };

    return DisplacingArray;
});

/**
 * Array with fixed max length and displacing first element on oversize.
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 10.05.18
 */
Imcms.define('imcms-displacing-array', [], function () {

    var DisplacingArray = function (size) {
        if (typeof size !== 'number') throw new Error("Size should be integer!");
        if (size < 1) throw new Error("Size should be >= 1");

        this.size = size;
        this.elements = [];
    };

    DisplacingArray.prototype = {
        push: function (content) {
            if (this.elements.length > this.size) {
                this.elements.splice(0, 1); // removes first element
            }

            this.elements.push(content);
            this.length = this.elements.length;
        },
        forEach: function (doForEach) {
            this.elements.forEach(doForEach);
        }
    };

    return DisplacingArray;
});

Add a "pin" icon to imCMS's panel: - Refreshing elements length for displacing array.


if (!shiv) {
  throw(new Error("Don't load this file directly! Use shiv.load()"));
}

shiv.test(
  "shiv.map",
  function() {
    return shiv.map([1,2,3], function(x) { return x * 2; });
  },
  function(res) {
    return (
      (res[0] == 1) &&
      (res[1] == 4) &&
      (res[2] == 6)
    );
  }
);

if (!shiv) {
  throw(new Error("Don't load this file directly! Use shiv.load()"));
}

shiv.test(
  "shiv.map",
  function() {
    return shiv.map([1,2,3], function(x) { return x * 2; });
  },
  function(res) {

    //
    // Array equality has to be tested element by element in JS
    //

    return (
      (res[0] == 1) &&
      (res[1] == 4) &&
      (res[2] == 6)
    );

  }
);

Comment added to shiv.map test


import { assert } from 'chai';
import getPetionSupportable from 'selectors/petitionSupportable';

describe('getPetionSupportable', () => {
  context('with a new petition', () => {
    const actual = getPetionSupportable({});
    const expected = false;

    it('returns false', () => assert.equal(actual, expected));
  });

  context('with a published petition', () => {
    const actual = getPetionSupportable({state: { name: 'pending', parent: 'supportable' }});
    const expected = true;

    it('returns true', () => assert.equal(actual, expected));
  });
});

import { assert } from 'chai';
import getPetitionSupportable from 'selectors/petitionSupportable';

describe('getPetitionSupportable', () => {
  context('with a new petition', () => {
    const actual = getPetitionSupportable({});
    const expected = false;

    it('returns false', () => assert.equal(actual, expected));
  });

  context('with a published petition', () => {
    const actual = getPetitionSupportable({state: { name: 'pending', parent: 'supportable' }});
    const expected = true;

    it('returns true', () => assert.equal(actual, expected));
  });
});

Fix a typo in selector test


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

module.exports = (
  userService,
  themeService,
  clientService
) => {
  return (templateName, getView, postHandler) => async (request, reply, source, error) => {
    if (error && error.output.statusCode === 404) {
      reply(error);
    }
    try {
      const client = await clientService.findById(request.query.client_id);
      let user = null;
      if (request.auth.isAuthenticated) {
        user = request.auth.strategy === 'email_token'
          ? request.auth.credentials.user
          : await userService.findById(request.auth.credentials.accountId());
      }

      const render = async e => {
        const viewContext = getView(user, client, request, e);
        const template = await themeService.renderThemedTemplate(request.query.client_id, templateName, viewContext);
        if (template) {
          reply(template);
        } else {
          reply.view(templateName, viewContext);
        }
      }

      if (!error && request.method === 'post') {
        error = await postHandler(request, reply, user, client, render);
      } else {
        await render(error);
      }
    } catch(e) {
      reply(e);
    }
  };
};

module.exports['@singleton'] = true;
module.exports['@require'] = [
  'user/user-service',
  'theme/theme-service',
  'client/client-service',
];

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

module.exports = (
  userService,
  themeService,
  clientService
) => {
  return (templateName, getView, postHandler) => async (request, reply, source, error) => {
    if (error && error.output.statusCode === 404) {
      return reply(error);
    }
    try {
      const client = await clientService.findById(request.query.client_id);
      let user = null;
      if (request.auth.isAuthenticated) {
        user = request.auth.strategy === 'email_token'
          ? request.auth.credentials.user
          : await userService.findById(request.auth.credentials.accountId());
      }

      const render = async e => {
        const viewContext = getView(user, client, request, e);
        const template = await themeService.renderThemedTemplate(request.query.client_id, templateName, viewContext);
        if (template) {
          return reply(template);
        } else {
          return reply.view(templateName, viewContext);
        }
      }

      if (!error && request.method === 'post') {
        error = await postHandler(request, reply, user, client, render);
      } else {
        await render(error);
      }
    } catch(e) {
      return reply(e);
    }
  };
};

module.exports['@singleton'] = true;
module.exports['@require'] = [
  'user/user-service',
  'theme/theme-service',
  'client/client-service',
];

Add return to replies so they don't get called multiple times


import * as types from './types';

export function updateField(key, value) {
  return {
    type: types.SETTING_UPDATE,
    payload: {
      key,
      value,
    },
  };
}

import {
  CHECKBOX_UPDATE,
  RANGE_UPDATE,
  FUZZY,
} from './types';

export function updateCheckbox(key, value) {
  return {
    type: CHECKBOX_UPDATE,
    payload: {
      key,
      value,
    },
  };
}

export function updateFuzzyCheckbox(key, value) {
  return {
    type: FUZZY + CHECKBOX_UPDATE,
    payload: {
      key,
      value,
    },
  };
}

export function updateFuzzyRange(key, value) {
  return {
    type: FUZZY + RANGE_UPDATE,
    payload: {
      key,
      value,
    },
  };
}

Add actions for handling setting state in the background


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

var isRequestPending = false;

module.exports = function requestForceUpdateAll(getRootInstances, React) {
  if (isRequestPending) {
    return;
  }

  /**
   * Forces deep re-render of all mounted React components.
   * Hat's off to Omar Skalli (@Chetane) for suggesting this approach:
   * https://gist.github.com/Chetane/9a230a9fdcdca21a4e29
   */
  function forceUpdateAll() {
    isRequestPending = false;

    var rootInstances = getRootInstances(),
        rootInstance;

    for (var key in rootInstances) {
      if (rootInstances.hasOwnProperty(key)) {
        deepForceUpdate(rootInstances[key], React);
      }
    }
  }

  setTimeout(forceUpdateAll);
};

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

var isRequestPending = false;

module.exports = function requestForceUpdateAll(getRootInstances, React) {
  if (isRequestPending) {
    return;
  }

  /**
   * Forces deep re-render of all mounted React components.
   * Hat's off to Omar Skalli (@Chetane) for suggesting this approach:
   * https://gist.github.com/Chetane/9a230a9fdcdca21a4e29
   */
  function forceUpdateAll() {
    isRequestPending = false;

    var rootInstances = getRootInstances(),
        rootInstance;

    for (var key in rootInstances) {
      if (rootInstances.hasOwnProperty(key)) {
        rootInstance = rootInstances[key];

        // `|| rootInstance` for React 0.12 and earlier
        rootInstance = rootInstance._reactInternalInstance || rootInstance;
        deepForceUpdate(rootInstance, React);
      }
    }
  }

  setTimeout(forceUpdateAll);
};

Fix usage with external React on 0.13+


require('native-promise-only');
var axios = require('axios');

var Axios = function(settings) {
  return new Promise(function(resolve, reject) {
    var options = {
      method: settings.type.toLowerCase(),
      url: settings.url,
      responseType: "text"
    };
    if (settings.headers) {
      options.headers = settings.headers;
    }
    if (settings.processData) {
      options.params = settings.data;
    } else {
      options.data = settings.data;
    }
    return axios(options).then(function(response) {
      settings.success(response.data);
      return resolve(response);
    }).catch(function(response) {
      var error = response.data || response;
      settings.error(error);
      return reject(response);
    });
  });
};

module.exports = Axios;

var extend = require('../utils').extend;
require('native-promise-only');
var axios = require('axios');

var Axios = function(settings) {
  return new Promise(function(resolve, reject) {
    var options = {
      method: settings.type.toLowerCase(),
      url: settings.url,
      responseType: "text",
      headers: {
        'Content-Type': 'application/json;charset=utf-8'
      }
    };
    if (settings.headers) {
      options.headers = extend(options.headers, settings.headers);
    }
    if (settings.processData) {
      options.params = settings.data;
    } else {
      options.data = settings.data;
    }
    return axios(options).then(function(response) {
      settings.success(response.data);
      return resolve(response);
    }).catch(function(response) {
      var error = response.data || response;
      settings.error(error);
      return reject(response);
    });
  });
};

module.exports = Axios;

Use application/json as default content-type with Axios


import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | froala-content', function(hooks) {
  setupRenderingTest(hooks);

  test('.fr-view class is applied', async function(assert) {

    await render(hbs`{{froala-content elementId="editor"}}`);

    assert.ok(this.$('#editor').hasClass('fr-view'));

  });

  test("'content' is output inside the block", async function(assert) {

    await render(hbs`{{froala-content content="foobar"}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

  test("positional param 'content' is output inside the block", async function(assert) {

    await render(hbs`{{froala-content "foobar"}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

  test("block content is properly yield'ed", async function(assert) {

    await render(hbs`{{#froala-content}}foobar{{/froala-content}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

});

import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import $ from 'jquery';

module('Integration | Component | froala-content', function(hooks) {
  setupRenderingTest(hooks);

  test('.fr-view class is applied', async function(assert) {

    await render(hbs`{{froala-content elementId="editor"}}`);

    assert.ok($('#editor', this.element).hasClass('fr-view'));

  });

  test("'content' is output inside the block", async function(assert) {

    await render(hbs`{{froala-content content="foobar"}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

  test("positional param 'content' is output inside the block", async function(assert) {

    await render(hbs`{{froala-content "foobar"}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

  test("block content is properly yield'ed", async function(assert) {

    await render(hbs`{{#froala-content}}foobar{{/froala-content}}`);

    assert.equal(this.element.textContent.trim(), 'foobar');

  });

});

Remove usage of ember jquery integration in tests


import ProjectConfigurationRepository from '../domain/project-configuration-repository'

import fs from 'fs'

const repository = new ProjectConfigurationRepository()

/**
 * @param {string} path The path to todo.conf.yml
 */
export default (path) => {

  const paths = [path, `${process.env.PWD}/${path}`, `${process.env.HOME}/${path}`]

  for (let i = 1; i < paths.length; i++) {
    let path = paths[i]

    if (fs.existsSync(path)) {
      return repository.getByPath(path).getProjects()
    }
  }

  throw new Error(`todo config file not found: ${JSON.stringify(paths)}`)

}

import ProjectConfigurationRepository from '../domain/project-configuration-repository'

import fs from 'fs'

const repository = new ProjectConfigurationRepository()

/**
 * @param {string} path The path to todo.conf.yml
 */
export default (path) => {

  const paths = [path, `${process.env.PWD}/${path}`, `${process.env.HOME}/.${path}`]

  for (let i = 1; i < paths.length; i++) {
    let path = paths[i]

    if (fs.existsSync(path)) {
      return repository.getByPath(path).getProjects()
    }
  }

  throw new Error(`todo config file not found: ${JSON.stringify(paths)}`)

}

Rename conf file in home dir


/* global malarkey:false, toastr:false, moment:false */
import config from './index.config';

import routerConfig from './index.route';

import runBlock from './index.run';
import MainController from './main/main.controller';
import GithubContributorService from '../app/components/githubContributor/githubContributor.service';
import WebDevTecService from '../app/components/webDevTec/webDevTec.service';
import NavbarDirective from '../app/components/navbar/navbar.directive';
import MalarkeyDirective from '../app/components/malarkey/malarkey.directive';
import './bookmarks/bookmarks.module';

angular.module('ngBookmarks', ['restangular', 'ngRoute', 'ui.bootstrap', 'bookmarks'])
  .constant('malarkey', malarkey)
  .constant('toastr', toastr)
  .constant('moment', moment)
  .config(config)

  .config(routerConfig)

  .run(runBlock)
  .service('githubContributor', GithubContributorService)
  .service('webDevTec', WebDevTecService)
  .controller('MainController', MainController)
  .directive('acmeNavbar', () => new NavbarDirective())
  .directive('acmeMalarkey', () => new MalarkeyDirective(malarkey));

/* global malarkey:false, toastr:false, moment:false */
import config from './index.config';

import routerConfig from './index.route';

import runBlock from './index.run';
import MainController from './main/main.controller';
import GithubContributorService from '../app/components/githubContributor/githubContributor.service';
import WebDevTecService from '../app/components/webDevTec/webDevTec.service';
import NavbarDirective from '../app/components/navbar/navbar.directive';
import MalarkeyDirective from '../app/components/malarkey/malarkey.directive';
import './bookmarks/bookmarks.module';

angular.module('ngBookmarks', ['ngRoute', 'ui.bootstrap', 'bookmarks'])
  .constant('malarkey', malarkey)
  .constant('toastr', toastr)
  .constant('moment', moment)
  .config(config)

  .config(routerConfig)

  .run(runBlock)
  .service('githubContributor', GithubContributorService)
  .service('webDevTec', WebDevTecService)
  .controller('MainController', MainController)
  .directive('acmeNavbar', () => new NavbarDirective())
  .directive('acmeMalarkey', () => new MalarkeyDirective(malarkey));

Remove 'restangular' as a direct dependency of the top level app


"use strict";

const Task = require('../Task'),
    gulp = require('gulp'),
    mocha = require('gulp-mocha'),
    fs = require('fs');

class DocTask extends Task {

    constructor(buildManager) {
        super(buildManager);
        this.command = "doc";
    }

    action() {
        //Dirty trick to capture Mocha output into a file
        //since gulp-mocha does not pipe the test output
        fs.writeFileSync('./doc.html', '');
        var originalWrite = process.stdout.write;
        process.stdout.write = function (chunk) {
            var textChunk = chunk.toString('utf8');
            if (!textChunk.match(/finished.+after/gmi)) {
                fs.appendFile('./doc.html', chunk);
            }
            originalWrite.apply(this, arguments);
        };

        return gulp.src(this._buildManager.options.test, {read: false})
            .pipe(mocha({reporter: 'doc'}));
    }
}

module.exports = DocTask;

"use strict";

const Task = require('../Task'),
    gulp = require('gulp'),
    mocha = require('gulp-mocha'),
    fs = require('fs');

class DocTask extends Task {

    constructor(buildManager) {
        super(buildManager);
        this.command = "doc";
    }

    action() {
        //Dirty trick to capture Mocha output into a file
        //since gulp-mocha does not pipe the test output
        fs.writeFileSync('./doc.html', '');
        var originalWrite = process.stdout.write;
        process.stdout.write = function (chunk) {
            var textChunk = chunk.toString('utf8');
            if (!textChunk.match(/finished.+after/gmi)) {
                fs.appendFile('./doc.html', chunk);
            }
            originalWrite.apply(this, arguments);
        };

        return gulp.src(this._buildManager.options.test, {read: false})
            .pipe(mocha({
                reporter: 'doc',
                compilers: {
                    ts: require('ts-node/register'),
                    js: require('babel-core/register')
                }
            }));
    }
}

module.exports = DocTask;

Add compilers to doc task


// Module for interacting with zoltar

const rp = require('request-promise-native')
const buildUrl = require('build-url')

async function get (url) {
  return rp(url, { json: true })
}

async function gets (url) {
  return rp(url, { json: false })
}

function proxifyObject (obj, root) {
  let handler = {
    get(target, propKey, receiver) {
      let value = target[propKey]

      if (typeof value === 'string' && (value.toString()).startsWith(root)) {
        return (async () => {
          let resp = await get(value)
          return proxifyObject(resp, root)
        })()
      } else if (typeof value === 'object') {
        return proxifyObject(value, root)
      } else  {
        return value
      }
    }
  }

  return new Proxy(obj, handler)
}

function zoltar (rootUrl) {
  let baseObject = {
    projects: `${rootUrl}/projects`
  }
  return proxifyObject(baseObject, rootUrl)
}

module.exports.zoltar = zoltar

// Module for interacting with zoltar

const rp = require('request-promise-native')
const buildUrl = require('build-url')

async function get (url) {
  return rp(url, { json: true })
}

async function gets (url) {
  return rp(url, { json: false })
}

function proxifyObject (obj, root) {
  let handler = {
    get(target, propKey, receiver) {
      let value = target[propKey]

      switch (propKey.toString()) {
      case 'url':
        return value
      case 'csv':
        {
          if ('forecast_data' in target) {
            return (async () => {
              let resp = await gets(`${target['forecast_data']}?format=csv`)
              return resp
            })()
          } else {
            throw new Error('This is not a forecast object')
          }
        }
      }

      if (typeof value === 'string' && (value.toString()).startsWith(root)) {
        return (async () => {
          let resp = await get(value)
          return proxifyObject(resp, root)
        })()
      } else if (typeof value === 'object') {
        return proxifyObject(value, root)
      } else  {
        return value
      }
    }
  }

  return new Proxy(obj, handler)
}

function zoltar (rootUrl) {
  let baseObject = {
    projects: `${rootUrl}/projects`
  }
  return proxifyObject(baseObject, rootUrl)
}

module.exports.zoltar = zoltar

Handle url and csv getters separately


(function(){

'use strict';

angular.module('secondLead.common')
  .factory('Auth', ['$http', 'store', function($http, store) {
	return {
	  isAuthenticated: function() {
	    return store.get('jwt');
	  },

	  login: function(credentials) {
	    var login = $http.post('/auth/login', credentials);
	    login.success(function(result) {
	      store.set('jwt', result.token);
          store.set('user', result.user);
	    });
	    return login;
	  },

	  logout: function() {
	    store.remove('jwt');
	    store.remove('user');
	  }

	};

  }])

})();

(function(){

'use strict';

angular.module('secondLead.common')
  .factory('Auth', ['$http', 'store', function($http, store) {
	return {
	  isAuthenticated: function() {
	    return store.get('jwt');
	  },

	  login: function(credentials) {
	    var login = $http.post('/auth/login', credentials);
	    login.success(function(result) {
	      store.set('jwt', result.token);
          store.set('user', result.user);
	    });
	    return login;
	  },

	  logout: function() {
	    var clearJWT = store.remove('jwt');
	    var clearUser = store.remove('user');
		return {
		  jwt: clearJWT,
		  user: clearUser
		};
	  }

	};

  }])

})();

Refactor logout in auth service


(function() {
  'use strict';

  angular.module('app.states')
    .run(appRun);

  /** @ngInject */
  function appRun(routerHelper) {
    routerHelper.configureStates(getStates());
  }

  function getStates() {
    return {
      'marketplace.details': {
        url: '/:serviceTemplateId',
        templateUrl: 'app/states/marketplace/details/details.html',
        controller: StateController,
        controllerAs: 'vm',
        title: 'Service Template Details',
        resolve: {
          serviceTemplate: resolveServiceTemplate
        }
      }
    };
  }

  /** @ngInject */
  function resolveServiceTemplate($stateParams, CollectionsApi) {
    return CollectionsApi.get('service_templates', $stateParams.serviceTemplateId);
  }

  /** @ngInject */
  function StateController(serviceTemplate) {
    var vm = this;

    vm.title = 'Service Template Details';
    vm.serviceTemplate = serviceTemplate;
  }
})();

(function() {
  'use strict';

  angular.module('app.states')
    .run(appRun);

  /** @ngInject */
  function appRun(routerHelper) {
    routerHelper.configureStates(getStates());
  }

  function getStates() {
    return {
      'marketplace.details': {
        url: '/:serviceTemplateId',
        templateUrl: 'app/states/marketplace/details/details.html',
        controller: StateController,
        controllerAs: 'vm',
        title: 'Service Template Details',
        resolve: {
          dialogs: resolveDialogs,
          serviceTemplate: resolveServiceTemplate
        }
      }
    };
  }

  /** @ngInject */
  function resolveServiceTemplate($stateParams, CollectionsApi) {
    return CollectionsApi.get('service_templates', $stateParams.serviceTemplateId);
  }

  /** @ngInject */
  function resolveDialogs($stateParams, CollectionsApi) {
    var options = {expand: true, attributes: 'content'};

    return CollectionsApi.query('service_templates/' + $stateParams.serviceTemplateId + '/service_dialogs', options);
  }

  /** @ngInject */
  function StateController(dialogs, serviceTemplate) {
    var vm = this;

    vm.title = 'Service Template Details';
    vm.dialogs = dialogs.resources[0].content;
    vm.serviceTemplate = serviceTemplate;
  }
})();

Add API call to return service dialogs


//This is a super-simple mock of a JavaScript Promise
//It only implement the 'then(success, failure)' function
//as that is the only function that the kanban calls
//in the modules that use the KanbanApi
class MockPromise {
  constructor(returnSuccess, result) {
        this.returnSuccess = returnSuccess;
        this.result = result || (returnSuccess ? 'my data': 'my error')
    }

    then(success, failure) {
            if (this.returnSuccess) {
                success(this.result);
            }
            else {
                failure(this.result);
            }
    }
}

export default MockPromise

Comment to say its a limited mock of a promise


window.ContactManager = {
  Models: {},
  Collections: {},
  Views: {},

  start: function(data) {
    var contacts = new ContactManager.Collections.Contacts(data.contacts),
        router = new ContactManager.Router();

    router.on('route:home', function() {
      router.navigate('contacts', {
        trigger: true,
        replace: true
      });
    });

    router.on('route:showContacts', function() {
      var contactsView = new ContactManager.Views.Contacts({
        collection: contacts
      });

      $('.main-container').html(contactsView.render().$el);
    });

    router.on('route:newContact', function() {
      var newContactForm = new ContactManager.Views.ContactForm({
        model: new ContactManager.Models.Contact()
      });

      newContactForm.on('form:submitted', function(attrs) {
        attrs.id = contacts.isEmpty() ? 1 : (_.max(contacts.pluck('id')) + 1);
        contacts.add(attrs);
        router.navigate('contacts', true);
      });

      $('.main-container').html(newContactForm.render().$el);
    });

    router.on('route:editContact', function(id) {
      var contact = contacts.get(id),
          editContactForm;

      if (contact) {
        editContactForm = new ContactManager.Views.ContactForm({
            model: contact
        });

        $('.main-container').html(editContactForm.render().$el);
      } else {
        router.navigate('contacts', true);
      }
    });

    Backbone.history.start();
  }
};

window.ContactManager = {
  Models: {},
  Collections: {},
  Views: {},

  start: function(data) {
    var contacts = new ContactManager.Collections.Contacts(data.contacts),
        router = new ContactManager.Router();

    router.on('route:home', function() {
      router.navigate('contacts', {
        trigger: true,
        replace: true
      });
    });

    router.on('route:showContacts', function() {
      var contactsView = new ContactManager.Views.Contacts({
        collection: contacts
      });

      $('.main-container').html(contactsView.render().$el);
    });

    router.on('route:newContact', function() {
      var newContactForm = new ContactManager.Views.ContactForm({
        model: new ContactManager.Models.Contact()
      });

      newContactForm.on('form:submitted', function(attrs) {
        attrs.id = contacts.isEmpty() ? 1 : (_.max(contacts.pluck('id')) + 1);
        contacts.add(attrs);
        router.navigate('contacts', true);
      });

      $('.main-container').html(newContactForm.render().$el);
    });

    router.on('route:editContact', function(id) {
      var contact = contacts.get(id),
          editContactForm;

      if (contact) {
        editContactForm = new ContactManager.Views.ContactForm({
            model: contact
        });

        editContactForm.on('form:submitted', function(attrs) {
          contact.set(attrs);
          router.navigate('contacts', true);
        });

        $('.main-container').html(editContactForm.render().$el);
      } else {
        router.navigate('contacts', true);
      }
    });

    Backbone.history.start();
  }
};

Update contact on form submitted


function scrollToBottom() {
  var $messages = $('#messages');

  if ($messages.length > 0) {
    $messages.scrollTop($messages[0].scrollHeight);
  }
}

$(document).ready(scrollToBottom);
$(document).on('turbolinks:load', scrollToBottom);

Fix scrollToBottom action on turbolinks:load


function adjustStyle() {
    var width = 0;
    // get the width.. more cross-browser issues
    if (window.innerHeight) {
        width = window.innerWidth;
    } else if (document.documentElement && document.documentElement.clientHeight) {
        width = document.documentElement.clientWidth;
    } else if (document.body) {
        width = document.body.clientWidth;
    }
    // now we should have it
    if (width < 600) {
        document.getElementById("myCSS").setAttribute("href", "_css/narrow.css");
    } else {
        document.getElementById("myCSS").setAttribute("href", "_css/main.css");
    }
}

// now call it when the window is resized.
window.onresize = function () {
    adjustStyle();
};

window.onload = function () {
    adjustStyle();
};

Add window.onload for style adjust


describe("About Promises", function () {

  describe("Asynchronous Flow", function () {

    it("should understand promise usage", function () {

      function isZero(number) {
        return new Promise(function(resolve, reject) {
          if(number === 0) {
            resolve();
          } else {
            reject(number + ' is not zero!');
          }
        })
      }

      expect(isZero(0) instanceof Promise).toEqual(FILL_ME_IN);
      expect(typeof isZero(0)).toEqual(FILL_ME_IN);
    });
  });
});

describe("About Promises", function () {

  describe("Asynchronous Flow", function () {

    it("should understand Promise type", function () {

      function promise(number) {
        return new Promise(function(resolve, reject) {
            resolve();
        })
      }

      // expect(promise() instanceof Promise).toEqual(FILL_ME_IN);
      // expect(typeof promise()).toEqual(FILL_ME_IN);
    });

    it('should understand a Promise can be fulfilled / resolved', function(done) {

      function promiseResolved() {
        return new Promise(function(resolve, reject) {
          resolve('promise me this');
        });
      }

      promiseResolved()
        .then(function(promiseValue) {
          expect(promiseValue).toEqual(promiseValue);
        })
        .then(done)
    })

  });
});

Add in extra promise koan


'use strict';

angular.module('<%=angularAppName%>')
    .directive('jhSocial', function($translatePartialLoader, $translate, $filter, SocialService) {
        return {
            restrict: 'E',
            scope: {
                provider: "@ngProvider"
            },
            templateUrl: 'scripts/app/account/social/directive/social.html',
            link: function(scope, element, attrs) {
                $translatePartialLoader.addPart('social');
                $translate.refresh();

                scope.label = $filter('capitalize')(scope.provider);
                scope.providerSetting = SocialService.getProviderSetting(scope.provider);
                scope.providerURL = SocialService.getProviderURL(scope.provider);
                scope.csrf = SocialService.getCSRF();
            }
        }
     });

'use strict';

angular.module('<%=angularAppName%>')
    .directive('jhSocial', function(<% if (enableTranslation){ %>$translatePartialLoader, $translate, <% } %>$filter, SocialService) {
        return {
            restrict: 'E',
            scope: {
                provider: "@ngProvider"
            },
            templateUrl: 'scripts/app/account/social/directive/social.html',
            link: function(scope, element, attrs) {<% if (enableTranslation){ %>
                $translatePartialLoader.addPart('social');
                $translate.refresh();
<% } %>
                scope.label = $filter('capitalize')(scope.provider);
                scope.providerSetting = SocialService.getProviderSetting(scope.provider);
                scope.providerURL = SocialService.getProviderURL(scope.provider);
                scope.csrf = SocialService.getCSRF();
            }
        }
     });

Fix translation issue in the social login


var
  fs         = require('fs'),
  Builder    = require('component-builder'),
  rimraf     = require('rimraf'),
  config     = require('../config'),
  utils      = require('./utils'),
  component  = require('./component'),
  options = {
    dest: config.componentInstallDir
  };

/**
 * Installs `model`s component from registry
 * into the install dir
 *
 * @param {Model} model
 * @param {Function} callback
 */

exports.install = function (model, callback) {
  var pkg = component.install(model.repo, '*', options);
  pkg.on('end', callback);
  pkg.install();
};

/**
 * Builds component from model's attributes,
 * calls callback with err and built js in UTF-8
 *
 * @param {Model} model
 * @param {Function} callback
 */

exports.build = function (model, callback) {
  var builder = new Builder(utils.getInstallDir(model.repo));
  builder.addLookup(config.componentInstallDir);
  builder.build(function (err, res) {
    if (err || (res && !res.js)) {
      return callback(err);
    }
    callback(err, res.js);
  });
};

/**
 * Deletes all components in the install dir
 *
 * @param {Function} callback
 */
exports.clearInstall = function (callback) {
  rimraf(config.componentInstallDir, function (err) {
    callback(err);
  });
};

var
  fs         = require('fs-extra'),
  Builder    = require('component-builder'),
  config     = require('../config'),
  utils      = require('./utils'),
  component  = require('./component'),
  options = {
    dest: config.componentInstallDir
  };

/**
 * Installs `model`s component from registry
 * into the install dir
 *
 * @param {Model} model
 * @param {Function} callback
 */

exports.install = function (model, callback) {
  var pkg = component.install(model.repo, '*', options);
  pkg.on('end', callback);
  pkg.install();
};

/**
 * Builds component from model's attributes,
 * calls callback with err and built js in UTF-8
 *
 * @param {Model} model
 * @param {Function} callback
 */

exports.build = function (model, callback) {
  var builder = new Builder(utils.getInstallDir(model.repo));
  builder.addLookup(config.componentInstallDir);
  builder.build(function (err, res) {
    if (err || (res && !res.js)) {
      return callback(err);
    }
    callback(err, res.js);
  });
};

/**
 * Deletes all components in the install dir
 *
 * @param {Function} callback
 */
exports.clearInstall = function (callback) {
  fs.remove(config.componentInstallDir, function (err) {
    console.log('fs-extra remove: ', err, err && err.stack);
    callback(err);
  });
};

Use fs-extra to remove recurisvely rather than rimraf