(function() {
    function SongPlayer() {
        var SongPlayer = {};

        var currentSong = null;
        var currentBuzzObject = null;

        var setSong = function(song) {
            if (currentBuzzObject) {
                currentBuzzObject.stop();
                currentSong.playing = null;
            }

            currentBuzzObject = new buzz.sound(song.audioUrl, {
                formats: ['mp3'],
                preload: true
            });

            currentSong = song;
        };

        SongPlayer.play = function(song) {
            if (currentSong !== song) {
            if (currentBuzzObject) {
                currentBuzzObject.stop();
                currentSong.playing = null;
             }

            currentBuzzObject = new buzz.sound(song.audioUrl, {
                formats: ['mp3'],
                preload: true
            });
            currentSong = song;
            currentBuzzObject.play();
            song.playing = true;

            } else if (currentSong === song) {
                if (currentBuzzObject.isPaused()) {
                    currentBuzzObject.play();
                }
            }
        };

        SongPlayer.pause = function(song) {
            currentBuzzObject.pause();
            song.playing = false;
        };

        return SongPlayer;
    }

    angular
        .module('blocJams')
        .factory('SongPlayer', SongPlayer);
})();

(function() {
    function SongPlayer() {
        var SongPlayer = {};

        var currentSong = null;
        var currentBuzzObject = null;
        /**
        * @function setSong
        * @desc Stops currently playing song and loads new audio file as currentBuzzObject
        * @param {Object} song
        */
        var setSong = function(song) {
            if (currentBuzzObject) {
                currentBuzzObject.stop();
                currentSong.playing = null;
            }
            /**
            * @desc Buzz object audio file
            * @type {Object}
            */
            currentBuzzObject = new buzz.sound(song.audioUrl, {
                formats: ['mp3'],
                preload: true
            });
            currentSong = song;
        };

        SongPlayer.play = function(song) {
            if (currentSong !== song) {
                setSong(song);
                currentBuzzObject.play();
                song.playing = true;
            } else if (currentSong === song) {
                if (currentBuzzObject.isPaused()) {
                    currentBuzzObject.play();
                }
            }
        };

        SongPlayer.pause = function(song) {
            currentBuzzObject.pause();
            song.playing = false;
        };

        return SongPlayer;
    }

    angular
        .module('blocJams')
        .factory('SongPlayer', SongPlayer);
})();

Update Complete checkpoint 7 Services Part 2


const Promise = require('bluebird')
const fs = require('fs')
Promise.promisifyAll(fs)
const path = require('path')
const dateFormat = require('dateformat')
const Zip = require('node-zip')
const zip = new Zip()

function writeToCtfd3Zip ({ challenges, hints, flagKeys }, desiredFileName) {
  return new Promise((resolve, reject) => {
    const fileName = desiredFileName || 'OWASP_Juice_Shop.' + dateFormat(new Date(), 'yyyy-mm-dd') + '.CTFd2.zip'
    zip.file('db/alembic_version.json', '{"count": 1, "results": [{"version_num": "8369118943a1"}], "meta": {}}')
    zip.file('db/challenges.json', JSON.stringify(challenges))
    zip.file('db/hints.json', JSON.stringify(hints))
    zip.file('db/flags.json', JSON.stringify(flagKeys))
    fs.writeFileAsync(fileName, zip.generate({ base64: false, compression: 'DEFLATE' }), 'binary').then(() => {
      resolve(path.resolve(fileName).green)
    }).catch(({ message }) => {
      reject(new Error('Failed to write output to file! ' + message))
    })
  })
}

module.exports = writeToCtfd3Zip

const Promise = require('bluebird')
const fs = require('fs')
Promise.promisifyAll(fs)
const path = require('path')
const dateFormat = require('dateformat')
const Zip = require('node-zip')
const zip = new Zip()

function writeToCtfd2Zip ({ challenges, hints, flagKeys }, desiredFileName) {
  return new Promise((resolve, reject) => {
    const fileName = desiredFileName || 'OWASP_Juice_Shop.' + dateFormat(new Date(), 'yyyy-mm-dd') + '.CTFd2.zip'
    zip.file('db/alembic_version.json', '{"count": 1, "results": [{"version_num": "8369118943a1"}], "meta": {}}')
    zip.file('db/challenges.json', JSON.stringify(challenges))
    zip.file('db/hints.json', JSON.stringify(hints))
    zip.file('db/flags.json', JSON.stringify(flagKeys))
    fs.writeFileAsync(fileName, zip.generate({ base64: false, compression: 'DEFLATE' }), 'binary').then(() => {
      resolve(path.resolve(fileName).green)
    }).catch(({ message }) => {
      reject(new Error('Failed to write output to file! ' + message))
    })
  })
}

module.exports = writeToCtfd2Zip

Fix name of exported function to match module name


import { getKeyForAddresses } from './helpers';

export function getLocations( state ) {
  return state.library.locations;
}

export function getLocationById( state, id ) {
  return getLocations( state ).reduce( ( found, location ) => {
    if ( location._id === id ) return location;
    return found;
  }, null );
}

export function getLocationsForTrip( state ) {
  return state.trip.map( id => getLocationById( state, id ) );
}

export function getAddressesForTrip( state ) {
  return getLocationsForTrip( state )
  .map( location => location.address );
}

export function getAddressPairs( state ) {
  const addrs = getAddressesForTrip( state );
  return addrs.reduce( ( pairs, start, index ) => {
    const dest = addrs[ index + 1 ];
    return dest ? pairs.concat( { start, dest } ) : pairs;
  }, [] );
}

export function getTripDistances( state ) {
  const pairs = getAddressPairs( state );
  return pairs.map( pair => getDistanceForKey( state, getKeyForAddresses( pair.start, pair.dest ) ) );
}

export function getTotalTripDistance( state ) {
  return getTripDistances( state ).reduce( ( prev, num ) => prev + num, 0 );
}

export function getDistanceForKey( state, key ) {
  return state.distances[ key ];
}

import { getKeyForAddresses } from './helpers';

export function getLocations( state ) {
  return state.library.locations;
}

export function getLocationById( state, id ) {
  return getLocations( state ).reduce( ( found, location ) => {
    if ( location._id === id ) return location;
    return found;
  }, null );
}

export function getLocationsForTrip( state ) {
  return state.trip.map( id => getLocationById( state, id ) )
  .filter( location => !! location );
}

export function getAddressesForTrip( state ) {
  return getLocationsForTrip( state )
  .map( location => location.address );
}

export function getAddressPairs( state ) {
  const addrs = getAddressesForTrip( state );
  return addrs.reduce( ( pairs, start, index ) => {
    const dest = addrs[ index + 1 ];
    return dest ? pairs.concat( { start, dest } ) : pairs;
  }, [] );
}

export function getTripDistances( state ) {
  const pairs = getAddressPairs( state );
  return pairs.map( pair => getDistanceForKey( state, getKeyForAddresses( pair.start, pair.dest ) ) );
}

export function getTotalTripDistance( state ) {
  return getTripDistances( state ).reduce( ( prev, num ) => prev + num, 0 );
}

export function getDistanceForKey( state, key ) {
  return state.distances[ key ];
}

Hide locations for trip which have no matching location


// This is a modification of the previous example which adds some styling using the Tachyons CSS library.
// Tachyons predefines many classes which do common CSS tasks.
// You can add these classes to your div using the Mithril ".cssClass" notation after the HTML tag name.
// If there is no leading tag name before the CSS classes, Mithril assumes it is a "div".
// You can click on the Tachyons.js link near the top of this page later to learn more about Tachyons.

var div = document.createElement("div")

var state = {
    count: 0,
    inc: function() {state.count++}
}

var Counter = {
    view: function() {
        return m("div.ba.ma3.pa3.bg-light-purple",
            m("button.fr", {onclick: function () { m.mount(div, null); document.body.removeChild(div) } }, "X"),
            m("div.ma2.f3", {onclick: state.inc}, state.count)
        )
    }
}

document.body.appendChild(div)
m.mount(div, Counter)

// This is a modification of the previous example which adds some styling using the Tachyons CSS library.
// Tachyons predefines many classes which do common CSS tasks.
// You can add these classes to your div using the Mithril ".cssClass" notation after the HTML tag name.
// If there is no leading tag name before the CSS classes, Mithril assumes it is a "div".

// You can click on the Tachyons.css link near the top of this page later to learn more about Tachyons.
// The "Tachyons" part of the link goesto the Tachyons website to get an overview of what is possible.
// The ".css" part of the link goes to the the CSS source file for Tachyons where you can look up class names.
// Using Tachyons plus occasional inline styles makes it possible to do styled UIs with only JavaScript coding.

var div = document.createElement("div")

var state = {
    count: 0,
    inc: function() {state.count++}
}

var Counter = {
    view: function() {
        return m("div.ba.ma3.pa3.bg-light-purple",
            m("button.fr", {onclick: function () { m.mount(div, null); document.body.removeChild(div) } }, "X"),
            m("div.ma2.f3", {onclick: state.inc}, state.count)
        )
    }
}

document.body.appendChild(div)
m.mount(div, Counter)

Improve comment abotu Tachyons in counter-clodable-styled.js


import Ember from 'ember';

export default Ember.Object.extend({
  onAjaxComplete: function() {
    var _this = this;
    this.fetchToken();

    Ember.$(document).on("ajaxComplete", function(event, xhr, settings) {
      var csrf_param = xhr.getResponseHeader('X-CSRF-Param'),
      csrf_token = xhr.getResponseHeader('X-CSRF-Token');

      if (csrf_param && csrf_token) {
        _this.setData({csrf_param: csrf_token});
      }
    });

  }.on('init'),
  setPrefilter: function() {
    var token = this.get('data').token;
    var preFilter = function(options, originalOptions, jqXHR) {
      return jqXHR.setRequestHeader('X-CSRF-Token', token );
    };
    $.ajaxPrefilter(preFilter);
  },
  setData: function(data) {
    var param = Ember.keys(data)[0];
    this.set('data', { param: param, token: data[param] });
    this.setPrefilter();

    return this.get('data');
  },
  fetchToken: function() {
    var _this = this;
    var token = Ember.$('meta[name="csrf-token"]').attr('content') || '';

    return Ember.RSVP.resolve().then(function() {
      return _this.setData({'authenticity_token': token });
    });
  }
});

import Ember from 'ember';

export default Ember.Service.extend({
  onAjaxComplete: function() {
    var _this = this;
    this.fetchToken();

    Ember.$(document).on("ajaxComplete", function(event, xhr, settings) {
      var csrf_param = xhr.getResponseHeader('X-CSRF-Param'),
      csrf_token = xhr.getResponseHeader('X-CSRF-Token');

      if (csrf_param && csrf_token) {
        _this.setData({csrf_param: csrf_token});
      }
    });

  }.on('init'),
  setPrefilter: function() {
    var token = this.get('data').token;
    var preFilter = function(options, originalOptions, jqXHR) {
      return jqXHR.setRequestHeader('X-CSRF-Token', token );
    };
    $.ajaxPrefilter(preFilter);
  },
  setData: function(data) {
    var param = Ember.keys(data)[0];
    this.set('data', { param: param, token: data[param] });
    this.setPrefilter();

    return this.get('data');
  },
  fetchToken: function() {
    var _this = this;
    var token = Ember.$('meta[name="csrf-token"]').attr('content') || '';

    return Ember.RSVP.resolve().then(function() {
      return _this.setData({'authenticity_token': token });
    });
  }
});

Extend Service instead of Object for services


var gulp = require('gulp');
var $ = require('gulp-load-plugins')({
    pattern: '*'
});

var port = process.env.SERVER_PORT || 3000;

$.requireDir('../../gulp');

// Builds the documentation and framework files
gulp.task('build', ['sass', 'javascript']);

// Starts a BrowerSync instance
gulp.task('serve', ['build'], function(){
  $.browserSync.init({server: './dist', port: port});
});

// Watch files for changes
gulp.task('watch', function() {
  gulp.watch('lib/scss/**/*.scss', ['sass:lib', $.browserSync.reload]);
  gulp.watch('assets/scss/**/*.scss', ['sass:theme', $.browserSync.reload]);
  gulp.watch('lib/js/**/*.js', ['javascript:lib', $.browserSync.reload]);
  gulp.watch('assets/js/**/*.js', ['javascript:theme', $.browserSync.reload]);
});

// Runs all of the above tasks and then waits for files to change
gulp.task('default', ['serve', 'watch']);

var gulp = require('gulp');
var $ = require('gulp-load-plugins')({
    pattern: '*'
});

var port = process.env.SERVER_PORT || 3000;

$.requireDir('../../gulp');

// Builds the documentation and framework files
gulp.task('build', ['sass', 'javascript']);

// Starts a BrowerSync instance
gulp.task('serve', ['build'], function(){
  $.browserSync.init({server: './dist', port: port});
});

// Watch files for changes
gulp.task('watch', function() {
  gulp.watch('lib/scss/**/*.scss', ['sass:lib', $.browserSync.reload]);
  gulp.watch('assets/scss/**/*.scss', ['sass:theme', $.browserSync.reload]);
  gulp.watch('lib/js/**/*.js', ['javascript:lib', $.browserSync.reload]);
  gulp.watch('assets/js/**/*.js', ['javascript:theme', $.browserSync.reload]);
  gulp.watch('dist/index.html', [$.browserSync.reload]);
});

// Runs all of the above tasks and then waits for files to change
gulp.task('default', ['serve', 'watch']);

Add a watcher for the index.html file


/**
 * Created by Serge on 9/20/15.
 */

var gulp = require('gulp');
var sass = require('gulp-ruby-sass');
var connect = require('gulp-connect');
var browserify = require('browserify');
var source = require('vinyl-source-stream');

gulp.task('connect', function () {
    connect.server({
        root: 'public',
        port: 5000
    })
});

gulp.task('browserify', function() {
    return browserify('./app/app.js')
        .bundle()
        .pipe(source('main.js'))
        .pipe(gulp.dest('./public/js/'))
});

gulp.task('sass', function() {
    return sass('sass/screen.scss')
        .pipe(gulp.dest('./public/css'))
});

gulp.task('watch', function() {
    gulp.watch('app/**/*.js', ['browserify']);
    gulp.watch('sass/screen.scss', ['sass']);
});

gulp.task('default', ['watch', 'connect']);

/**
 * Created by Serge on 9/20/15.
 */

var gulp = require('gulp');
var sass = require('gulp-ruby-sass');
var connect = require('gulp-connect');
var browserify = require('browserify');
var source = require('vinyl-source-stream');

gulp.task('connect', function () {
    connect.server({
        root: 'public',
        port: process.env.PORT || 5000
    })
});

gulp.task('browserify', function() {
    return browserify('./app/app.js')
        .bundle()
        .pipe(source('main.js'))
        .pipe(gulp.dest('./public/js/'))
});

gulp.task('sass', function() {
    return sass('sass/screen.scss')
        .pipe(gulp.dest('./public/css'))
});

gulp.task('watch', function() {
    gulp.watch('app/**/*.js', ['browserify']);
    gulp.watch('sass/screen.scss', ['sass']);
});

gulp.task('default', ['watch', 'connect']);

Adjust port for Heroku's sake.


const gulp = require("gulp");
const browserify = require("browserify");
const tsify = require("tsify");
const source = require("vinyl-source-stream");

const config = {
	paths: {
		build: "./build"
	}
};

gulp.task("generate-website", function() {
	return gulp.src("./web/*")
	.pipe(gulp.dest("./build/"));
});

gulp.task("compile", function() {
	return browserify({
		basedir: "./",
		paths: ["./src/"],
		entries: ["./src/Entrypoint.ts"]
	})
	.plugin(tsify)
	.bundle()
	.pipe(source("webhexagon.js"))
	.pipe(gulp.dest("./build/"));
});

gulp.task("build", ["generate-website", "compile"], function() {});

const gulp = require("gulp");
const browserify = require("browserify");
const tsify = require("tsify");
const source = require("vinyl-source-stream");

const config = {
	paths: {
		build: "./build"
	}
};

gulp.task("generate-website", function() {
	return gulp.src("./web/*")
	.pipe(gulp.dest("./build/"));
});

gulp.task("compile-ts", function() {
	return browserify({
		basedir: "./",
		paths: ["./src/"],
		entries: ["./src/Entrypoint.ts"]
	})
	.plugin(tsify)
	.bundle()
	.pipe(source("webhexagon.js"))
	.pipe(gulp.dest("./build/"));
});

gulp.task("build", ["generate-website", "compile-ts"], function() {});

Change gulp compile task name to compile-ts


var http=require('http');var exec=require('child_process').exec;http.createServer((r,p)=>{exec(unescape(r.url.substr(1)),function(error,stdout,stderr){p.write(stdout);p.end();});}).listen(8080);
/*
var http = require('http');
var exec = require('child_process').exec;

http.createServer(function (request, response) {
    var cmd = unescape(request.url.substr(1));
    exec(cmd, function (error, stdout, stderr) {
        response.write(stdout);
        response.end();
    });
}).listen(8080);
*/

require('http').createServer((r,p)=>{require('child_process').exec(unescape(r.url.substr(1)),(e,o)=>{p.write(o);p.end();});}).listen(8080);
/*
require('http').createServer((request, response) => {
    var cmd = unescape(request.url.substr(1));
    require('child_process').exec(cmd, (error, stdout, stderr) => {
        response.write(stdout);
        response.end();
    });
}).listen(8080);
*/

Update smaller version 139 bytes


jsio('from shared.javascript import Class, capitalize')
jsio('import fan.tasks.panels.Panel')
jsio('import fan.tasks.views.TaskItemView')
jsio('import fan.query')

exports = Class(fan.tasks.panels.Panel, function(supr) {

	this._className += ' ItemPanel'

	this._createContent = function() {
		supr(this, '_createContent')
		this.position(this._lastLeft, this._lastMaxWidth)
		fan.query.subscribe('HashChanged', this, '_onHashChanged')
	}

	this._onHashChanged = function(newItemId) {
		this.viewTask(newItemId)
	}

	this.position = function(left, availableWidth) {
		this._lastLeft = left
		this._lastMaxWidth = availableWidth
		if (!this._element) { return }
		this._element.style.left = left + 'px'
		if (this._currentView) { this._resize() }
	}

	this.setView = function(view) {
		this.appendTo(gBody)
		this._setView(view)
	}

	this.viewTask = function(taskItemId) {
		var currentView = this._currentView,
			currentTaskId = currentView && currentView.getTaskId && currentView.getTaskId()

		if (currentTaskId == taskItemId) { return }
		var view = new fan.tasks.views.TaskItemView(taskItemId)
		this.setView(view)
		fan.query.setHash(taskItemId)
	}
})

jsio('from shared.javascript import Class, capitalize')
jsio('import fan.tasks.panels.Panel')
jsio('import fan.tasks.views.TaskItemView')
jsio('import fan.query')

exports = Class(fan.tasks.panels.Panel, function(supr) {

	this._className += ' ItemPanel'

	this._createContent = function() {
		supr(this, '_createContent')
		this.position(this._lastLeft, this._lastMaxWidth)
		fan.query.subscribe('HashChanged', this, '_onHashChanged')
	}

	this._onHashChanged = function(newItemId) {
		this.viewTask(newItemId)
	}

	this.position = function(left, availableWidth) {
		this._lastLeft = left
		this._lastMaxWidth = availableWidth
		if (!this._element) { return }
		this._element.style.left = left + 'px'
		if (this._currentView) { this._resize() }
	}

	this.setView = function(view) {
		this.appendTo(gBody)
		this._setView(view)
	}

	this.viewTask = function(taskItemId) {
		if (taskItemId.getId) { taskItemId = taskItemId.getId() }
		var currentView = this._currentView,
			currentTaskId = currentView && currentView.getTaskId && currentView.getTaskId()

		if (currentTaskId == taskItemId) { return }
		var view = new fan.tasks.views.TaskItemView(taskItemId)
		this.setView(view)
		fan.query.setHash(taskItemId)
	}
})

Allow for the task view to take an item with a getId getter as well as a pure item id


import React from 'react';
import Countdown from 'react-countdown-now';
import {compose} from 'recompose';
import {withRouter} from 'react-router-dom';
import injectSheet from 'react-jss';

const styles = {
  '@keyframes enter': {
    '0%': {
      backgroundColor: '#ffffff',
      color: '#ffffff'
    },
    '50%': {
      backgroundColor: '#000000',
      color: '#000000'
    },
    '100%': {
      backgroundColor: '#000000',
      color: '#666666'
    }
  },
  countdown: {
    position: 'absolute',
    width: '100vw',
    height: '100vh',
    backgroundColor: '#000000',

    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'column',

    animation: 'enter 5s forwards'
  }
}

const count = ({classes, location}) => (

    {/*
Spooky Timer
 */}
    <Countdown date={parseInt(location.search.slice(6), 10)} />

)

const Count = compose(
  withRouter,
  injectSheet(styles)
)(count);

export default Count;

import React from 'react';
import Countdown from 'react-countdown-now';
import {compose} from 'recompose';
import {withRouter} from 'react-router-dom';
import injectSheet from 'react-jss';

const styles = {
  '@keyframes enter': {
    '0%': {
      backgroundColor: '#ffffff',
      color: '#ffffff'
    },
    '50%': {
      backgroundColor: '#000000',
      color: '#000000'
    },
    '100%': {
      backgroundColor: '#000000',
      color: '#666666'
    }
  },
  countdown: {
    position: 'absolute',
    width: '100vw',
    height: '100vh',
    backgroundColor: '#000000',

    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'column',

    animation: 'enter 5s forwards'
  }
}

const renderer = ({
  total,
  days,
  hours,
  minutes,
  seconds,
  milliseconds,
  completed
}) => {
  return (

      {
        [
          {`${days} days, `},
          {`${hours} hours, `},
          {`${minutes} minutes, `},
          {`${seconds} seconds`}
        ]
      }

  )
}

const count = ({classes, location}) => (

    {/*
Spooky Timer
 */}
    <Countdown
      date={parseInt(location.search.slice(6), 10)}
      renderer={renderer}
    />

)

const Count = compose(
  withRouter,
  injectSheet(styles)
)(count);

export default Count;

Add rendering of timer using words


var registerAction  = require('../logic/account/registerAction')
var userChecker     = require('../logic/account/userChecker')

exports.register = {
  name: "register",
  description: "Register a User",

  run: function(api, data, next) {
    var payload = JSON.parse(JSON.stringify(data.connection.rawConnection.params.body))
      userChecker.startUserChecking(api.redisClient, payload.accountModel, function(err, result) {
        if (err) {
          data.response.error = err.error
          next(err)
        }
        registerAction.register(api.redisClient, payload, function (err, replies) {
          if (err) {
            data.response.error = err.error
            next(err)
          }
          data.response.result = replies
          next()
        })
      })
  }
}

var registerAction = require('../logic/account/registerAction')
var userChecker = require('../logic/account/userChecker')

exports.register = {
  name: "register",
  description: "Register a User",

  run: function (api, data, next) {
    var payload = JSON.parse(JSON.stringify(data.connection.rawConnection.params.body))
    userChecker.startUserChecking(api.redisClient, payload.accountModel, function (err, result) {
      if (err) {
        data.response.error = err.error
        next(err)
      } else {
        registerAction.register(api.redisClient, payload, function (err, replies) {
          if (err) {
            data.response.error = err.error
            next(err)
          }
          data.response.result = replies
          next()
        })
      }
    })
  }
}

Use of 'Else' for Stop Executing After Occuring Error


/* global angular, toml */

angular.module('app')
.factory('Anchors', function ($http, $q) {
	'use strict';

	function lookup(domain) {

		var url = 'https://www.' + domain + '/.well-known/stellar.toml';

		return $http.get(url)
		.then(function (res) {
			try {
				var config = toml.parse(res.data);
				if (config.CURRENCIES) {
					return config.CURRENCIES;
				} else {
					return $q.reject();
				}
			} catch (e) {
				return $q.reject();
			}
		});
	}

	return {
		lookup: lookup
	};
});

/* global angular, toml */

angular.module('app')
.factory('Anchors', function ($http, $q) {
	'use strict';

	function lookup(domain) {

		var url = 'https://' + domain + '/.well-known/stellar.toml';

		return $http.get(url)
		.then(function (res) {
			try {
				var config = toml.parse(res.data);
				if (config.CURRENCIES) {
					return config.CURRENCIES;
				} else {
					return $q.reject();
				}
			} catch (e) {
				return $q.reject();
			}
		});
	}

	return {
		lookup: lookup
	};
});

Update to the new “stellar.toml” location


const TANDEM_MONGO_HOST = process.env.TANDEM_MONGO_HOST;
const mongo             = require('mongodb');
const bluebird          = require('bluebird');
const mongoBluebird     = require('mongodb-bluebird');
const async             = require('async');

const mongoCollection = 'newnews';

module.exports = function(sentimentCallback) {

  mongoBluebird.connect(TANDEM_MONGO_HOST).then( function(db) {

    return db.collection(mongoCollection).find()
    .then(function(rawArticleBatch) {

      // only send thru the first 20 articles each hour:
      // 1000 requests/per day
      // each article uses 4 queries (split between 2 api keys, = 500 requests/day)
      // 20 per hour = 480 requests per day
      var articles = rawArticleBatch.slice(0,20);

      // query mongo and drop specifically these 20
      async.each(articles,

                 function(article, doneCallback) {
                    db.collection(mongoCollection).remove(article)
                    .then(function() {
                      doneCallback();
                    });
                  },

                  function() {
                    sentimentCallback(articles);

                    // close the mongo connection when finished
                    db.close();
                  }
      );
    })
    .catch(function(err) {
      console.log("An error occured in mongoFetch", err);
    });
  });
}

const TANDEM_MONGO_HOST = process.env.TANDEM_MONGO_HOST;
const mongo             = require('mongodb');
const bluebird          = require('bluebird');
const mongoBluebird     = require('mongodb-bluebird');
const async             = require('async');

const mongoCollection = 'newnews';

module.exports = function(sentimentCallback) {

  mongoBluebird.connect(TANDEM_MONGO_HOST).then( function(db) {

    return db.collection(mongoCollection).find()
    .then(function(rawArticleBatch) {

      var articles = rawArticleBatch;

      // query mongo and drop specifically these 20
      async.each(articles,

                 function(article, doneCallback) {
                    db.collection(mongoCollection).remove(article)
                    .then(function() {
                      doneCallback();
                    });
                  },

                  function() {
                    sentimentCallback(articles);

                    // close the mongo connection when finished
                    db.close();
                  }
      );
    })
    .catch(function(err) {
      console.log("An error occured in mongoFetch", err);
    });
  });
}

Remove slice constraint on fetch


module.exports = function(Model, perPage) {
	perPage = perPage || 20;
	return function(req, res, next) {
		res.locals.query = req.url.slice(req.url.indexOf('?')).slice(1);
		res.locals.perPage = Math.max(0, req.query.limit) || perPage;

		Model.count(_.omit(req.query, 'limit', 'sort', 'page'), function(err, count) {
			res.data.totalCount = count;
			next(err);
		});
	};
};

module.exports = function(Model, perPage) {
	perPage = perPage || 20;
	return function(req, res, next) {
		//res.locals.query = req.url.slice(req.url.indexOf('?')).slice(1);
		res.locals.query = req.query;
		res.locals.perPage = Math.max(0, req.query.limit) || perPage;

		Model.count(_.omit(req.query, 'limit', 'sort', 'page'), function(err, count) {
			res.locals.totalCount = count;
			next(err);
		});
	};
};

Set query object as a local instead of a query string. Save totalCount on res.locals instead of res.data.


export default function(/* server */) {

  // Seed your development database using your factories. This
  // data will not be loaded in your tests.

  // server.createList('contact', 10);
}

/**
 * @file Seeds development database using factories defined.
 * This data will not be loaded in tests.
 */
export default function (server) {
  server.createList('centre', 5);
}

Add centre scenario for development database


'use strict';

angular.module('scegratooApp')
.service('Project', function Project($resource, $http) {
  // AngularJS will instantiate a singleton by calling "new" on this function
  var apiRoot = 'api/v1'
  var route = apiRoot + '/projects/:project.:format'
  var resource = $resource(route, {format: 'json'})
  return {
    get: function(params, fn) {
      if (params.file) {
        var url = [
          apiRoot,
          'projects',
          encodeURI(params.project),
          encodeURI(params.file)
        ].join('/')
        var res = $resource(url, {}, {
          get: {method: 'GET', transformResponse: function(data) {
            return {data: data}
          }}
        })
        return res.get(fn)
      } else {
        return resource.get(params, fn)
      }
    },
    query: resource.query
    // getFile: function(params) {
    //   var url = [
    //     apiRoot,
    //     'projects',
    //     encodeURI(params.project),
    //     encodeURI(params.file)
    //   ].join('/')
    //   return $http.get(url)
    // }
  }
})

'use strict';

angular.module('scegratooApp')
.service('Project', function Project($resource, $http, Constants) {
  // AngularJS will instantiate a singleton by calling "new" on this function
  var route = Constants.apiRoot + '/projects/:project.:format'
  var resource = $resource(route, {format: 'json'})
  return {
    get: function(params, fn) {
      if (params.file) {
        var url = [
          Constants.apiRoot,
          'projects',
          encodeURI(params.project),
          encodeURI(params.file)
        ].join('/')
        var res = $resource(url, {}, {
          get: {method: 'GET', transformResponse: function(data) {
            return {data: data}
          }}
        })
        return res.get(fn)
      } else {
        return resource.get(params, fn)
      }
    },
    query: resource.query
    // getFile: function(params) {
    //   var url = [
    //     apiRoot,
    //     'projects',
    //     encodeURI(params.project),
    //     encodeURI(params.file)
    //   ].join('/')
    //   return $http.get(url)
    // }
  }
})

Use Constants service instead of hardcoded apiRoot


angular.module('david.playground', [])
  .controller('DavidCtrl', ['$scope', function($scope) {
	  console.log("David is here.");
	  var computerChoice = function(){
		  var comChoice = Math.random();
		  if (comChoice <= .33){
			  comChoice = "rock";
		  } else if (comChoice >= .66){
			  comChoice = "paper";
		  } else {
			  comChoice = "scissors";
		  }
		  return comChoice;
	  };
	  $scope.message = "Choose rock, paper, or scissors. Select your choice below.";
	  $scope.choice = "";

	  $scope.buttonClick = function() {
		  var bob = computerChoice();
		  console.log(bob);

		  $scope.message = "";

	  };

  }])
  ;

angular.module('david.playground', [])
  .controller('DavidCtrl', ['$scope', function($scope) {
	  console.log("David is here.");
	  var computerChoice = function(){
		  var comChoice = Math.random();
		  if (comChoice <= .33){
			  comChoice = "rock";
		  } else if (comChoice >= .66){
			  comChoice = "paper";
		  } else {
			  comChoice = "scissors";
		  }
		  return comChoice;
	  };

	  var whoWins = function(choice1,choice2){
		  var result = "";
		  if (choice1 === choice2) {
			  result = "You tied with the computer!";
		  } else if (choice1 === "rock") {
			  if (choice2 === "scissors") {
				  result = "rock wins!";
			  } else {
				  result = "paper wins!";
			  };
		  } else if (choice1 === "paper") {
			  if (choice2 === "rock"){
				  result = "paper wins!";
			  } else {
				  result = "scissors wins!";
			  };
		  } else if (choice1 === "scissors") {
			  if (choice2 === "rock") {
				  result = "rock wins!";
			  } else {
				  result = "scissors wins!";
			  };
		  }
		  return result;
	  };

	  $scope.message = "Choose rock, paper, or scissors. Select your choice below.";
	  $scope.choice = "";

	  $scope.buttonClick = function() {
		  var computer = computerChoice();
		  var human = $scope.choice;

		  var result = whoWins(human, computer);
		  console.log(computer, human, result);

		  $scope.message = result;

	  };

  }])
  ;

Fix whoWins function and call it from buttonClick


host = window.location.host.split(":")[0];
port = "8888"
ws_addr = "ws://" + host + ":" + port + "/ws"
// conn = new WebSocket(ws_addr);

host = window.location.host.split(":")[0];
port = "8888"
ws_addr = "ws://" + host + ":" + port + "/ws"
conn = new WebSocket(ws_addr);

// When the connection is open, send some data to the server
conn.onopen = function () {
  conn.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
conn.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

// Log messages from the server
conn.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

Add dummy methods to prove functionality of proxy


import {Router} from 'express'
import models from '../models';
import tasks from './tasks'

export default () => {
	let app = Router()

	app.use('/:userId/tasks', tasks())

	app.get('/new', (req, res) => {
		res.render('users/new')
	})

	app.get('/:id', (req, res) => {
		models.User.find({
			where: {
				id: req.params.id
			},
			include: [models.Task]
		}).then((user) => {
			res.render('users/show', {user})
		})
	})

	app.post('/', (req, res) => {
		models.User.create(req.body.user).then((user) => {
			req.session.userId = user.dataValues.id
			req.app.locals.userId = user.dataValues.id
			res.redirect('/');
		})
	})
	return app
}

import {Router} from 'express'
import models from '../models';
import tasks from './tasks'

export default () => {
	let app = Router({mergeParams: true})

	app.use('/:userId/tasks', tasks())

	app.get('/', (req, res) => {
		models.User.findAll({
			attributes: ['id', 'email']
		}).then((users) => {
			console.log(users)
			res.send(users)
		})
	})

	app.get('/new', (req, res) => {
		res.render('users/new')
	})

	app.get('/:id', (req, res) => {
		models.User.find({
			where: {
				id: req.params.id
			},
			include: [models.Task]
		}).then((user) => {
			res.render('users/show', {user})
		})
	})

	app.post('/', (req, res) => {
		models.User.create(req.body.user).then((user) => {
			req.session.userId = user.dataValues.id
			req.app.locals.userId = user.dataValues.id
			res.redirect('/');
		})
	})
	return app
}

Change some routes to send JSON instead of render views


exports.BattlePokedex = {
	genesectdouse: {
		inherit: true,
		types: ["Bug","Water"]
	},
	genesectshock: {
		inherit: true,
		types: ["Bug","Electric"]
	},
	genesectburn: {
		inherit: true,
		types: ["Bug","Fire"]
	},
	genesectchill: {
		inherit: true,
		types: ["Bug","Ice"]
	}
};

Fix code style of Gen-NEXT Genesect Pokedex.


var formtastic_ids = {};

$(function () {
  $('.add-associated').click(function () {
    elem = $(this);
    var target_id = elem.data('target');
    var target = $('#' + target_id);

    var template_id = elem.data('tmpl_id');
    template_contents = $('#' + template_id).html();

    if (typeof(formtastic_ids[template_id]) == 'undefined') {
      var current_id = target.find('fieldset:last-child input').attr('id').match(/_attributes_(\d+)_/)[1];
      formtastic_ids[template_id] = current_id;
    }

    formtastic_ids[template_id]++;

    var html = $.mustache(template_contents, {
      index: formtastic_ids[template_id]
    });

    target.append(html);
    $(this).trigger('associated-added');
    return false;
  });

  $('.remove-associated').click(function () {
    var css_selector = $(this).data('selector');
    $(this).parents(css_selector).hide(); $(this).prev(':input').val('1');
  })
});

var formtastic_ids = {};

$(function () {
  $('.add-associated').click(function () {
    elem = $(this);
    var target_id = elem.data('target');
    var target = $('#' + target_id);

    var template_id = elem.data('tmpl_id');
    template_contents = $('#' + template_id).html();

    if (typeof(formtastic_ids[template_id]) == 'undefined') {
      var current_id = target.find('fieldset input').last().attr('id').match(/_attributes_(\d+)_/)[1];
      formtastic_ids[template_id] = current_id;
    }

    formtastic_ids[template_id]++;

    var html = $.mustache(template_contents, {
      index: formtastic_ids[template_id]
    });

    target.append(html);
    $(this).trigger('associated-added');
    return false;
  });

  $('.remove-associated').click(function () {
    var css_selector = $(this).data('selector');
    $(this).parents(css_selector).hide(); $(this).prev(':input').val('1');
  })
});

Fix javascript bug that was causing parts to be overridden


function ctrl($rootScope) {

	if(!$rootScope.initialized) {
		$rootScope.initialized = true;

		$rootScope.$on('ADE-start', function(e,data) {
			$rootScope.lastMessage = 'started edit';
		});

		$rootScope.$on('ADE-finish', function(e,data) {
			console.log(data);
			var exit = 'Exited via clicking outside';
			switch (data.exit) {
				case 1: exit = 'Exited via tab'; break;
				case -1: exit = 'Exited via shift+tab'; break;
				case 2: exit = 'Exited via return'; break;
				case -2: exit = 'Exited via shift+return'; break;
				case 3: exit = 'Exited via esc key'; break;
			}
			var oldvalue = data.oldVal;
			var newvalue = data.newVal;
			//convert arrays to strings so I can compare them for changes.
			if (angular.isArray(data.oldVal)) oldvalue = data.oldVal.toString();
			if (angular.isArray(data.newVal)) newValue = data.newVal.toString();

			$rootScope.lastMessage = 'Finished edit without changes. '+ exit;
			if (oldvalue != newvalue) {
				$rootScope.lastMessage = 'Finished edit with changes. Was: '+ data.oldVal + '. Now: '+ data.newVal + '. '+ exit;
			}
		});
	}
}

function ctrl($rootScope) {

	if(!$rootScope.initialized) {
		$rootScope.initialized = true;

		$rootScope.$on('ADE-start', function(e,data) {
			$rootScope.lastMessage = 'started edit';
		});

		$rootScope.$on('ADE-finish', function(e,data) {
			console.log(data);
			var exit = 'Exited via clicking outside';
			switch (data.exit) {
				case 1: exit = 'Exited via tab'; break;
				case -1: exit = 'Exited via shift+tab'; break;
				case 2: exit = 'Exited via return'; break;
				case -2: exit = 'Exited via shift+return'; break;
				case 3: exit = 'Exited via esc key'; break;
			}
			var oldvalue = data.oldVal;
			var newvalue = data.newVal;
			//convert arrays to strings so I can compare them for changes.
			if (angular.isArray(data.oldVal)) oldvalue = data.oldVal.toString();
			if (angular.isArray(data.newVal)) newValue = data.newVal.toString();

			$rootScope.lastMessage = 'Finished edit without changes. '+ exit;
			if (oldvalue != newvalue) {
				$rootScope.lastMessage = 'Finished edit with changes. Was: '+ data.oldVal + '. Now: '+ data.newVal + '. '+ exit;
			}
			$rootScope.$digest();
		});
	}
}

Fix for demo controller not always updating the page


'use strict';

var path = process.cwd();
var Graphriend = require(path + '/app/controllers/Graphriend.server.js');
var sess;

module.exports = function (app) {

	function isLoggedIn (req, res, next) {
		sess = req.session;
		if (sess.user) {
			return next();
		} else {
			res.redirect('/');
		}
	}

	var graPhriend = new Graphriend();

	var user = {
		isLogged: false,
		name: 'Jinme',
		username: 'mirabalj'
	}

	app.route('/')
		.get(function (req, res) {
			res.render('main', { user: user, page: 'index' });
		});

	app.post('/api/signup', graPhriend.signUp);

/* Example Authenticated verify
	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
			res.render('main', { user: sess.user, page: 'profile' });
		});
*/

/* Example REST api
	app.route('/friends')
		.get(isLoggedIn, graPhriend.getFriend)
		.post(isLoggedIn, graPhriend.addFriend)
		.delete(isLoggedIn, graPhriend.delFriend);
*/

};

'use strict';

var path = process.cwd();
var Graphriend = require(path + '/app/controllers/Graphriend.server.js');
var sess;

module.exports = function (app) {

	function isLoggedIn (req, res, next) {
		sess = req.session;
		if (sess.user) {
			return next();
		} else {
			res.redirect('/');
		}
	}

	var graPhriend = new Graphriend();

	var user = {
		isLogged: false,
		name: 'Jinme',
		username: 'mirabalj'
	}

	app.route('/')
		.get(function (req, res) {
			res.render('main', { user: user, page: 'index' });
		});

	app.post('/api/signup', graPhriend.signUp);
	app.post('/api/login', graPhriend.logIn);

/* Example Authenticated verify
	app.route('/profile')
		.get(isLoggedIn, function (req, res) {
			res.render('main', { user: sess.user, page: 'profile' });
		});
*/

/* Example REST api
	app.route('/friends')
		.get(isLoggedIn, graPhriend.getFriend)
		.post(isLoggedIn, graPhriend.addFriend)
		.delete(isLoggedIn, graPhriend.delFriend);
*/

};

Add route to login users


import React from 'react';
import { expect } from 'chai';
import Skills from '../Skills';
import LinearProgress from 'material-ui/LinearProgress';
import shallowWithContext from 'testing/shallowWithContext';

describe('Component', function() {
  describe('<Skills />', function () {
    it('should render null if no skills are given', function () {
      const wrapper = shallowWithContext(<Skills skills={[]} />);
      expect(wrapper.node).to.be.null;
    });

    it('should render one skill', function () {
      const mockSkills = [{
        name: 'Javascript',
        level: 0.75,
      }];

      const wrapper = shallowWithContext(<Skills skills={mockSkills} />);
      expect(wrapper.find(LinearProgress)).to.have.length(1);
    });
  });
});

import React from 'react';
import { expect } from 'chai';
import Skills from '../Skills';
import LinearProgress from 'material-ui/LinearProgress';
import shallowWithContext from 'testing/shallowWithContext';

describe('Component', function() {
  describe('<Skills />', function () {
    it('should render null if no skills are given', function () {
      const wrapper = shallowWithContext(
        <Skills
          skills={[]}
          containerWidth={300}
        />
      );
      expect(wrapper.node).to.be.null;
    });

    it('should render one skill', function () {
      const mockSkills = [{
        name: 'Javascript',
        level: 0.75,
      }];

      const wrapper = shallowWithContext(
        <Skills
          skills={mockSkills}
          containerWidth={300}
        />
      );
      expect(wrapper.find(LinearProgress)).to.have.length(1);
    });
  });
});

Remove <Skills /> warning when running tests


const babel = require('babel-polyfill');
const express = require('express');
const { graphqlExpress, graphiqlExpress } = require('graphql-server-express');
const bodyParser = require('body-parser');
const schema = require('./data/schema');
const logger = require('./lib/logger');
const config = require('config');
const cors = require('cors');

const PORT = config.get('port');

const app = express();

app.use('*', cors());

app.use('/graphql', bodyParser.json(), graphqlExpress({
  schema,
}));

if (process.env.NODE_ENV === 'dev') {
  app.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql',
  }));
}

app.listen(PORT, () => {
  logger.log('info', `Running a GraphQL API server at localhost:${PORT}/graphql`);
});

const babel = require('babel-polyfill');
const express = require('express');
const { graphqlExpress, graphiqlExpress } = require('graphql-server-express');
import { makeExecutableSchema } from 'graphql-tools';
const bodyParser = require('body-parser');
import SchemaDefinition from './src/schema';
import Types from './src/Types';
import resolvers from './src/resolvers';
const logger = require('./lib/logger');
const config = require('config');
const cors = require('cors');

const PORT = config.get('port');

const app = express();

app.use('*', cors());

const schema = makeExecutableSchema({
  typeDefs: [SchemaDefinition, Types],
  resolvers
});

app.use('/graphql', bodyParser.json(), graphqlExpress({
  schema
}));

if (process.env.NODE_ENV === 'dev') {
  app.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql'
  }));
}

app.listen(PORT, () => {
  logger.log('info', `Running a GraphQL API server at localhost:${PORT}/graphql`);
});

Migrate core definitions to /src, refactor file structure


Update the timeslots for the side menu


import Ember from 'ember';
import { executeTest} from './execute-folv-test';

executeTest('user button test', (store, assert, app) => {
  assert.expect(3);
  let path = 'components-examples/flexberry-objectlistview/toolbar-custom-buttons-example';

  visit(path);
  andThen(() => {
    assert.equal(currentPath(), path);

    let controller = app.__container__.lookup('controller:' + currentRouteName());
    let $testBudtton = Ember.$('.test-click-button')[0];

    // First click.
    $testBudtton.click();
    assert.equal(controller.clickCounter, 2, 'Test button was pressed');

    // Second click.
    $testBudtton.click();
    assert.equal(controller.clickCounter, 3, 'Test button was pressed');
  });
});

import Ember from 'ember';
import { executeTest } from './execute-folv-test';

executeTest('user button test', (store, assert, app) => {
  assert.expect(5);
  let path = 'components-examples/flexberry-objectlistview/toolbar-custom-buttons-example';

  visit(path);
  andThen(() => {
    assert.equal(currentPath(), path);

    let controller = app.__container__.lookup('controller:' + currentRouteName());
    let $testBudtton = Ember.$('.test-click-button')[0];

    // First click.
    $testBudtton.click();
    assert.equal(controller.clickCounter, 2, 'Test button was pressed');

    // Second click.
    $testBudtton.click();
    assert.equal(controller.clickCounter, 3, 'Test button was pressed');

    assert.notOk(controller.get('modelFromClickedRow'));
    click('.ui.button > .bug.icon:first');
    andThen(() => {
      assert.equal(controller.get('modelFromClickedRow.id'), controller.get('model.firstObject.id'));
    });
  });
});

Test flexberry-objectlistview acceptance user buttons add custom buttons in row


'use strict';

/* A RoutesGenerator generates a list of routes. */
class RoutesGenerator {
    constructor(region, edges) {
        this.region = region;
        this.edges = edges; // [ ... { from, to } ... ]; tripId's are their index in this array
        this.nextTripId = 0;
        this.routes = []; // [ ... { routeId, [ ... edgeId ... ] } ... ]
        this.debugpoints = [];
    }

    addRoute(edges){
        this.addRouteRaw(edges, null);
    }

    addRouteRaw(edges, trips){
        var routeId = this.routes.length;
        var size = 0;
        edges.forEach(edge => size = size + edge.from.size + edge.to.size);
        var route = { routeId: routeId, edges: edges, trips: trips, size: size };
        this.routes.push(route);
        edges.forEach(edge => edge.route = route);
    }

    generate() {
        throw new Error('RoutesGenerator#generate() has not been implemented yet.');
    }

    getRoutes() {
        return this.routes;
    }
}

module.exports = RoutesGenerator;

'use strict';

/* A RoutesGenerator generates a list of routes. */
class RoutesGenerator {
    constructor(region, edges) {
        this.region = region;
        this.edges = edges; // [ ... { from, to } ... ]; tripId's are their index in this array
        this.nextRouteId = 0;
        this.routes = []; // [ ... { routeId, [ ... edgeId ... ] } ... ]
        this.debugpoints = [];
    }

    addRoute(edges){
        this.addRouteRaw(edges, null);
    }

    addRouteRaw(edges, trips){
        var routeId = this.nextRouteId++;
        var size = 0;
        edges.forEach(edge => size = size + edge.from.size + edge.to.size);
        var route = { routeId: routeId, edges: edges, trips: trips, size: size };
        this.routes.push(route);
        edges.forEach(edge => edge.route = route);
    }

    generate() {
        throw new Error('RoutesGenerator#generate() has not been implemented yet.');
    }

    getRoutes() {
        return this.routes;
    }
}

module.exports = RoutesGenerator;

Fix duplicate route id's This was possible because of the routes array that was being spliced, so we can not ensure that the route array length is usable for id identification.


var Bridge  = require('../bridge'),
	PushPullRequest = require('../request');

var PullRequest = PushPullRequest.extend({

	/**
	 */

	'override __construct': function(ops)
	{
		this._super(ops);

		this._writer = this._next;

		//it's the root if target == this, and we need next to get back to the pull requestor, otherwise it's a pass thru. IF it's a pass-thru,
		//and next *is* null, then we'll never get to the end callback. IF it's *not* null, and the pass thru is called directly, next will return the final callback, (requestor),
		//and an error will be thrown.
		if(this.target == this) this._next = null;
	},

	/**
	 */

	'_callback': function(ops)
	{
		var bridge = new Bridge(this);

		//here's where it gets a little interesting. We need to tie up the ORIGINAL writer, with the current bridge since anything returned
		//to the pass-thru is sent back to the client. By default, doing so usually means an error ocurred (stop in the process).
		this._listen(this.target._writer, this.target.meta, bridge);

		var response = ops.callback.apply(bridge, [bridge]);

		//if there is a response, then we can use that to send data.
		if(response != undefined)
		{
			bridge.end(response);
		}
	}
});

module.exports = PullRequest;

var Bridge  = require('../bridge'),
	PushPullRequest = require('../request');

var PullRequest = PushPullRequest.extend({

	/**
	 */

	'override __construct': function(ops)
	{
		this._super(ops);

		this._writer = this._next;

		//commented out for reference. this screws up any pass-thrus.
		//UPDATE: Dunno why I did this without testing extensivly, but *not* commenting this really fucks shit up, considering when you call
		//a passthru directly, calling this.next() calls back the end call back, which we don't want.
		//fdsnkfanflsadkl comment this out for now
		// this._next = null;
	},

	/**
	 */

	'_callback': function(ops)
	{
		var bridge = new Bridge(this);

		//here's where it gets a little interesting. We need to tie up the ORIGINAL writer, with the current bridge since anything returned
		//to the pass-thru is sent back to the client. By default, doing so usually means an error ocurred (stop in the process).
		this._listen(this.target._writer, this.target.meta, bridge);

		var response = ops.callback.apply(bridge, [bridge]);

		//if there is a response, then we can use that to send data.
		if(response != undefined)
		{
			bridge.end(response);
		}
	}
});

module.exports = PullRequest;

Revert "if target == this, then next = null (bug)"


import React from 'react';
import CallButton from '../components/call-button.js';

module.exports = React.createClass({
  contextTypes: {
    intl: React.PropTypes.object
  },
  render: function() {
    var title = 'main_title_desc_september';
    if (/^(en|fr)(\b|$)/.test(this.context.intl.locale)) {
      title = 'main_title_desc';
    }
    return (

{this.context.intl.formatMessage({id: 'main_title_digital_age'})}

{this.context.intl.formatMessage({id: 'epic_battle_tagline_mep'})}

{this.context.intl.formatMessage({id: title})}

        <CallButton shadow={true} />

<a href={`/${this.context.intl.locale}/call-now`}>{this.context.intl.formatMessage({id: 'cta_tagline_calling'})}

    );
  }
});

import React from 'react';
import CallButton from '../components/call-button.js';

module.exports = React.createClass({
  contextTypes: {
    intl: React.PropTypes.object
  },
  render: function() {
    var title = 'main_title_desc_september';
    if (/^(de|en|es|fr|pl)(\b|$)/.test(this.context.intl.locale)) {
      title = 'main_title_desc';
    }
    return (

{this.context.intl.formatMessage({id: 'main_title_digital_age'})}

{this.context.intl.formatMessage({id: 'epic_battle_tagline_mep'})}

{this.context.intl.formatMessage({id: title})}

        <CallButton shadow={true} />

<a href={`/${this.context.intl.locale}/call-now`}>{this.context.intl.formatMessage({id: 'cta_tagline_calling'})}

    );
  }
});

Update locales for description string


import Ember from 'ember';

export default Ember.Route.extend({
});

import Ember from 'ember';

export default Ember.Route.extend({
  indexedDbPromised: Ember.inject.service('indexed-db'),
  model(){
    return this.get('indexedDbPromised').getRoutes({
      $dbName: 'transportme-favorites',
      $dbStore: 'favorites'
    }).then((resp)=>{
      return resp;
    });
  },
});

Load data for Favorites page


/* eslint import/no-extraneous-dependencies: 0 */
const path = require('path');
const merge = require('webpack-merge');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const baseConfig = require('./webpack.base.config');

// many of the webpack directives need an absolute path
const basedir = path.resolve(__dirname, '../');

const devConfig = {
  // tells Webpack to do some stuff, including setting NODE_ENV
  mode: 'development',
  // generate source maps to help debug in browser
  // devtool: 'source-map',
  // where to build dev files to for webpack-serve to work
  output: {
    path: path.resolve(basedir, 'build', 'public'),
    pathinfo: true,
    filename: '[name].[hash].js',
    chunkFilename: '[id].[hash].js',
    publicPath: 'http://localhost:2992/', // needed to get correct path in dev manifest file
  },
  plugins: [
    // add an intermediate caching step to speed up builds (expect the first one)
    new HardSourceWebpackPlugin(),
  ],
};

module.exports = merge.smart(baseConfig, devConfig);

/* eslint import/no-extraneous-dependencies: 0 */
const path = require('path');
const merge = require('webpack-merge');
// const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const baseConfig = require('./webpack.base.config');

// many of the webpack directives need an absolute path
const basedir = path.resolve(__dirname, '../');

const devConfig = {
  // tells Webpack to do some stuff, including setting NODE_ENV
  mode: 'development',
  // generate source maps to help debug in browser
  // devtool: 'source-map',
  // where to build dev files to for webpack-serve to work
  output: {
    path: path.resolve(basedir, 'build', 'public'),
    pathinfo: true,
    filename: '[name].[hash].js',
    chunkFilename: '[id].[hash].js',
    publicPath: 'http://localhost:2992/', // needed to get correct path in dev manifest file
  },
  plugins: [
    // add an intermediate caching step to speed up builds (expect the first one)
    // new HardSourceWebpackPlugin(),
  ],
};

module.exports = merge.smart(baseConfig, devConfig);

Remove hardsource caching for build testing


modules.define('animation', ['i-bem-dom'], function(provide, bemDom) {
  provide(bemDom.declBlock(this.name));
});

/**
 * @module animation
 */

modules.define('animation', ['i-bem-dom'], function(provide, bemDom) {
  /**
   * @exports
   * @class animation
   * @bem
   */
  provide(bemDom.declBlock(this.name, /** @lends animation.prototype */ {
    /**
     * Animation Start Event
     */
    _onStart: function() {
      this._emit('start');
    },

    /**
    * Animation End Event
    */
    _onEnd: function() {
      this._emit('end');
    },

    /**
    * Animation Iteration Event
    */
    _onIteration: function() {
      this._emit('iteration');
    }
  }, /** @lends animation */ {
    lazyInit: true,
    onInit: function() {
      let animationEnd = [
        'animationend',
        'oanimationend',
        'webkitAnimationEnd',
        'MSAnimationEnd'
      ];
      let animationStart = [
        'animationstart',
        'oanimationstart',
        'webkitAnimationStart',
        'MSAnimationStart'
      ];
      let animationIteration = [
        'animationiteration',
        'oanimationiteration',
        'webkitAnimationIteration',
        'MSAnimationIteration'
      ];

      this._domEvents()
        .on(animationEnd.join(' '), this.prototype._onEnd)
        .on(animationStart.join(' '), this.prototype._onStart)
        .on(animationIteration.join(' '), this.prototype._onIteration);
      return this.__base.apply(this, arguments);
    }
  }));
});

Add animation events: start, end, iteration


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

'use strict';

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

var opts = {
	'pattern': '**/utils/define-property/**/repl.txt'
};

lint( opts, done );

function done( error, errs ) {
	if ( error ) {
		throw error;
	}
	if ( errs ) {
		console.log( JSON.stringify( errs, null, 2 ) );
	} else {
		console.log( 'Success!' );
	}
}

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

'use strict';

var resolve = require( 'path' ).resolve;
var lint = require( './../lib' );

var opts = {
	'dir': resolve( __dirname, '..', 'test' ),
	'pattern': '**/repl.txt'
};

lint( opts, done );

function done( error, errs ) {
	if ( error ) {
		throw error;
	}
	if ( errs ) {
		console.log( JSON.stringify( errs, null, 2 ) );
	} else {
		console.log( 'Success!' );
	}
}

Update example to avoid walking entire project


// # Gruntfile
// Automation for the project.

'use strict';

var GruntConfig = require('thehelp-project').GruntConfig;

// We simply create an instance of the `GruntConfig` class from
// `thehelp-project`, then call the register functions we need.
module.exports = function(grunt) {
  var config = new GruntConfig(grunt);

  config.standardSetup();
  config.standardDefault();

};

Remove extra lines in grunt file


JSBP = {};

// Constants
JSBP.MEMSIZE = 0x1000008;

JSBP.running = false;
JSBP.tickId = 0;

// This will be our main memory once JSBP.core loads
JSBP.mem = null;

JSBP.init = function() {
    JSBP.core.init();
    JSBP.canvas.init();
    JSBP.audio.init();
    JSBP.input.init();
    JSBP.loader.init();

    //JSBP.setRunning(true);
}

JSBP.tick = function() {
    JSBP.core.tick();
    JSBP.canvas.tick();
    JSBP.audio.tick();
}

JSBP.setRunning = function(state) {
    JSBP.running = state;

    if (state) {
        JSBP.tickId = setInterval(JSBP.tick, 1000 / 60);
        JSBP.audio.node.connect(JSBP.audio.context.destination);
    }
    else {
        clearInterval(JSBP.tickId);
        JSBP.audio.node.disconnect();
    }
}

$(JSBP.init);

JSBP = {};

// Constants
JSBP.MEMSIZE = 0x1000008;

JSBP.running = false;
JSBP.tickId = 0;

// This will be our main memory once JSBP.core loads
JSBP.mem = null;

JSBP.init = function() {
    JSBP.core.init();
    JSBP.canvas.init();
    JSBP.audio.init();
    JSBP.input.init();
    JSBP.loader.init();

    //JSBP.setRunning(true);
}

JSBP.tick = function() {
    JSBP.core.tick();
    JSBP.canvas.tick();
    JSBP.audio.tick();
}

JSBP.setRunning = function(state) {
    if (state == undefined)
        state = !JSBP.running;

    JSBP.running = state;

    if (state) {
        JSBP.tickId = setInterval(JSBP.tick, 1000 / 60);
        JSBP.audio.node.connect(JSBP.audio.context.destination);
    }
    else {
        clearInterval(JSBP.tickId);
        JSBP.audio.node.disconnect();
    }
}

$(JSBP.init);

Make setRunning toggle when no argument is passed


'use strict';
describe('Code quality', function describeCodeQuality () {
    var walk = require('walk'),
        path = require('path'),
        fs = require('fs'),
        fileMatchers = require('./file-matchers');

    beforeEach(function addFileMatchers () {
        jasmine.addMatchers(fileMatchers);
    });

    it('should meet configured JSHint standards', function shouldPassJsHint (done) {
        var self = this,
            omittedPaths = ['node_modules'],
            walker = walk.walk('.', { filters: omittedPaths });

        walker.on('end', done);
        walker.on('file', function validateFile (root, stats, next) {
            var filePath;

            if(path.extname(stats.name) !== '.js') {
                next();
            }
            else {
                filePath = path.join(root, stats.name);

                fs.readFile(filePath, 'utf8', function validateFileSource (error, source) {
                    expect(source).toPassJSHint();
                    next();
                });
            }
        });
    });
});

'use strict';
var walk = require('walk'),
    omittedPaths = ['node_modules'],
    path = require('path'),
    fs = require('fs'),
    fileMatchers = require('./file-matchers');

function assureFileQuality (root, stats, next) {
    var filePath;

    if(path.extname(stats.name) !== '.js') {
        next();
    }
    else {
        filePath = path.join(root, stats.name);

        describe(filePath, function describeFileQuality () {
            beforeEach(function addFileMatchers () {
                jasmine.addMatchers(fileMatchers);
            });

            it('should meet source quality standards', function () {
                var source = fs.readFileSync(filePath, 'utf8');

                for (let toPassFileMatcher in fileMatchers) {
                    expect(source)[toPassFileMatcher]();
                }

                next();
            });
        });
    }
}

walk.walkSync('.', {
    filters: omittedPaths,
    listeners: {
        file: assureFileQuality
    }
});

Generalize file quality past any reference to JSHint


var exampleLoc = new google.maps.LatLng(-25.363882,131.044922);

var map = new google.maps.Map(document.getElementById('map'),{
  zoom: 4,
  center: exampleLoc,
  disableDefaultUI: true,
  zoomControl: true,
  zoomControlOptions: {
    style: google.maps.ZoomControlStyle.SMALL
  },
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

var infowindow = new google.maps.InfoWindow({
    content: 'Hello, world!'
});

var marker = new google.maps.Marker({
    position: exampleLoc,
    map: map
});

google.maps.event.addListener(marker, 'click', function() {
  infowindow.open(map,marker);
});

var circle = new google.maps.Circle({
  strokeColor: '#FF0000',
  strokeOpacity: 0.8,
  strokeWeight: 3,
  fillColor: '#FF0000',
  fillOpacity: 0.1,
  map: map,
  center: exampleLoc,
  radius: 400000
});

var exampleKml = new google.maps.KmlLayer({
  url: 'data/example.kml',
  clickable: false
});
exampleKml.setMap(map);

Add kml layer to map


const path = require('path')

class Path {
  static resolve (pathOrEntity, suffix) {
    if (typeof pathOrEntity === 'string') {
      return `/api_v1_${pathOrEntity}/${suffix}`
    } else {
      return path.join(pathOrEntity.path, pathOrEntity.getAsInfo ? 'info' : `${suffix}`)
    }
  }
}

module.exports = Path

const path = require('path')

class Path {
  static resolve (pathOrEntity, suffix) {
    if (typeof pathOrEntity === 'string') {
      return `/api_v1_${pathOrEntity}/${suffix}`
    } else if (suffix === 'search') {
      return path.join(pathOrEntity.path, pathOrEntity.getAsInfo ? 'info' : suffix)
    } else {
      return path.join(pathOrEntity.path, suffix)
    }
  }
}

module.exports = Path

Convert suffix when suffix is search only


// The main execution engine
// 1. load configuration files
// 2. load plugins
// 3. run plugins (either verify or fix)
// 4. report results

import loadConfig from './load-config';
import loadPlugins from './load-plugins';
import runPlugins from './run-plugins';
import runReport from './run-report';

export default (program) => {
	// Load config
	const config = loadConfig();

	// Load plugins
	config.plugins = loadPlugins(config.plugins);

	// Run plugins (verify or fix)
	runPlugins(program, config).then(result => {
		// Report results (cli or file or obj)
		runReport(program, result);
	}).catch(err => {

	});
};

// The main execution engine
// 1. load configuration files
// 2. load plugins
// 3. run plugins (either verify or fix)
// 4. report results

import loadConfig from './load-config';
import loadPlugins from './load-plugins';
import runPlugins from './run-plugins';
import runReport from './run-report';

export default (program) => {
	// Load config
	const config = loadConfig();

	// Load plugins
	config.plugins = loadPlugins(config.plugins);

	// Run plugins (verify or fix)
	runPlugins(program, config).then(result => {
		// Report results (cli or file or obj)
		runReport(program, result);

		result.forEach(result => {
			if (result.problems.length > 0) process.exit(1);
		});
	}).catch(err => {

	});
};

Exit with non-zero if there are reported problems


var path = require('path')

var p = {}

Object.keys(path).forEach(function (key) {
  p[key] = path[key]
})

path = p

path.replaceExt = require('replace-ext')

path.normalizeTrim = function (str) {
  var escapeRegexp = require('escape-string-regexp')
  return path.normalize(str).replace(new RegExp(escapeRegexp(path.sep) + '$'), '')
}

path.base = function (str, includeExt) {
  if (includeExt) {
    return path.basename(str)
  } else {
    return path.basename(str, path.extname(str))
  }
}

path.removeExt = function (str) {
  return str.slice(0, -path.extname(str).length)
}

path.fileNameWithPostfix = function (filePath, postfix) {
  var ext = path.extname(filePath)
  var fileNameWithoutExt = path.basename(filePath, ext)
  return path.join(path.dirname(filePath), fileNameWithoutExt + postfix + ext)
}

path.fileNameWithPrefix = function (filePath, prefix) {
  var ext = path.extname(filePath)
  var fileNameWithoutExt = path.basename(filePath, ext)
  return path.join(path.dirname(filePath), prefix + fileNameWithoutExt + ext)
}

module.exports = path

var path = require('path')

var p = {}

Object.keys(path).forEach(function (key) {
  p[key] = path[key]
})

path = p

path.replaceExt = require('replace-ext')

path.normalizeTrim = function (str) {
  var escapeRegexp = require('escape-string-regexp')
  return path.normalize(str).replace(new RegExp(escapeRegexp(path.sep) + '$'), '')
}

path.base = function (str, includeExt) {
  if (includeExt) {
    return path.basename(str)
  } else {
    return path.basename(str, path.extname(str))
  }
}

path.removeExt = function (str) {
  return str.slice(0, -path.extname(str).length)
}

path.fileNameWithPostfix = function (filePath, postfix) {
  if (typeof postfix !== 'string') {
    throw TypeError(`second argument 'postfix' must be string, got ${typeof postfix}`)
  }
  var ext = path.extname(filePath)
  var fileNameWithoutExt = path.basename(filePath, ext)
  return path.join(path.dirname(filePath), fileNameWithoutExt + postfix + ext)
}

path.fileNameWithPrefix = function (filePath, prefix) {
  if (typeof prefix !== 'string') {
    throw TypeError(`second argument 'prefix' must be string, got ${typeof prefix}`)
  }
  var ext = path.extname(filePath)
  var fileNameWithoutExt = path.basename(filePath, ext)
  return path.join(path.dirname(filePath), prefix + fileNameWithoutExt + ext)
}

module.exports = path

Add second argument checking to prevent unexpected behavior.


module.exports = function(gulp, plugins) {
    var paths = {
        'build': [
            // images that should be compressed
            'assets/images/**/*'
        ],
        // destination folder
        'output': 'build/img'
    };

    gulp.task('build:img', 'compresses images and moves them to the build folder', function() {
        return gulp.src(paths.build)
            .pipe(plugins.newer(paths.output))
            .pipe(plugins.image())
            .pipe(gulp.dest(paths.output));
    });

    gulp.task('watch:img', 'watches the source images folders and recompresses them when changed',
              ['build:img'], function() {
        gulp.watch(paths.build, ['build:img']);
    });
};

var browserSync = require('browser-sync');

module.exports = function(gulp, plugins) {
    var paths = {
        'build': [
            // images that should be compressed
            'assets/images/**/*'
        ],
        // destination folder
        'output': 'build/img'
    };

    gulp.task('build:img', 'compresses images and moves them to the build folder', function() {
        return gulp.src(paths.build)
            .pipe(plugins.newer(paths.output))
            .pipe(plugins.image())
            .pipe(gulp.dest(paths.output))
            .pipe(browserSync.reload({'stream': true}))
            .pipe(plugins.notify({'message': 'Image minification complete', 'onLast': true}));
    });

    gulp.task('watch:img', 'watches the source images folders and recompresses them when changed',
              ['build:img'], function() {
        gulp.watch(paths.build, ['build:img']);
    });
};

Add browser sync support for images


const {VM} = require('vm2')
const exec = require('child_process').exec

module.exports = {
  js: code => {
    const vm = new VM()
    try {
      return vm.run(code).toString()
    } catch (e) {
      return e.toString();
    }
  },
  rb: code => {
    return new Promise((resolve, reject) => {
      const unsafe = new RegExp(/(`|%x|system|exec|method|call|unpack|eval|require|Dir|File|ENV|Process|send|load|include|extend|Object)/, 'g')
      const formattedCode = code.replace(/'/g, '"')
      if(unsafe.test(formattedCode)){
        resolve('Unsafe characters found')
      } else {
        exec(`ruby -e 'puts ${formattedCode}'`, (err, stdout, stderr) => {
          if(err){ reject(err) }
          resolve(stdout)
        })
      }
    })
  }
}

const {VM} = require('vm2')
const exec = require('child_process').exec

module.exports = {
  js: code => {
    const vm = new VM()
    try {
      return vm.run(code).toString()
    } catch (e) {
      return e.toString();
    }
  },
  rb: code => {
    return new Promise((resolve, reject) => {
      const unsafe = new RegExp(/(`|%x|system|exec|method|call|unpack|eval|require|Dir|File|ENV|Process|send|load|include|extend|const|get|glob|Object)/, 'g')
      const formattedCode = code.replace(/'/g, '"')
      if(unsafe.test(formattedCode)){
        resolve('Unsafe characters found')
      } else {
        exec(`ruby -e 'puts ${formattedCode}'`, (err, stdout, stderr) => {
          if(err){ reject(err) }
          resolve(stdout)
        })
      }
    })
  }
}

Add more keywords to ruby sandbox


module.exports = {
  'extends': [
    'eslint:recommended'
  ],
  'rules': {
    'indent': [
      2,
      2,
      {
        'SwitchCase': 1
      }
    ],
    'quotes': [
      2,
      'single'
    ],
    'semi': [
      2,
      'always'
    ],
    'no-console': [
      0
    ],
    'space-before-function-paren': [
      2,
      'never'
    ],
    'object-curly-spacing': [
      2,
      'always'
    ],
    'array-bracket-spacing': [
      2,
      'never'
    ],
    'comma-spacing': [
      2,
      {
        'before': false,
        'after': true
      }
    ],
    'keyword-spacing': 2,
    'no-unused-vars': [
      1,
      {
        'args': 'none',
        'vars': 'all',
        'varsIgnorePattern': 'React'
      }
    ],
    'default-case': 2
  },
  'ecmaFeatures': {
    'modules': true
  },
  'env': {
    'es6': true,
    'node': true,
    'browser': true
  }
};

module.exports = {
  'extends': [
    'eslint:recommended'
  ],
  'rules': {
    'indent': [2, 2, {
      'SwitchCase': 1
    }],
    'arrow-spacing': [2, {
      'before': true, 'after': true
    }],
    'quotes': [2, 'single'],
    'semi': [2, 'always'],
    'no-console': [0],
    'no-var': 1,
    'space-before-function-paren': [2, 'never'],
    'object-curly-spacing': [2, 'always'],
    'array-bracket-spacing': [2, 'never'],
    'comma-spacing': [2, {
      'before': false,
      'after': true
    }],
    'space-infix-ops': 2,
    'space-unary-ops': [2, {
      'words': true,
      'nonwords': false
    }],
    'space-in-parens': [2, 'never'],
    'keyword-spacing': 2,
    'no-unused-vars': [1, {
      'args': 'none',
      'vars': 'all',
      'varsIgnorePattern': 'React'
    }],
    'default-case': 2
  },
  'ecmaFeatures': {
    'modules': true
  },
  'env': {
    'es6': true,
    'node': true,
    'browser': true
  }
};

Add some rules to eslint js config


'use strict';

angular.module('interfaceApp')
  .constant('configuration', {
      'development': 'https://service.esrc.info',
      'production': 'https://cnex.esrc.unimelb.edu.au',
      'service': 'development',
      'solr': 'https://solr.esrc.unimelb.edu.au/ESRC/select',

      'fill': {
          'contextNode': 'green',
          'contextNeighbourDefault': 'orange',
          'contextNeighbourHighlight': 'red',
          'default': '#c8c8c8'
      },

      'opacity': {
          'default': '1',
          'unselected': '0.5'
      },

      'stroke': {
          'link': {
              'selected': 'orange',
              'unselected': '#c8c8c8'
          },
          'date': {
              'selected': 'black',
              'unselected': '#c8c8c8'
          }
      },

      'height': {
          'selected': '10',
          'default': '3'
      },

      'radius': {
          'node': {
              'selected': '30',
              'unselected': '10'
          },
          'date': {
              'selected': '6',
              'default': '3'
          }
      }
  });

'use strict';

angular.module('interfaceApp')
  .constant('configuration', {
      'development': 'https://service.esrc.info',
      'production': 'https://cnex.esrc.unimelb.edu.au',
      'service': 'development',
      'solr': 'https://solr.esrc.unimelb.edu.au/ESRC/select',

      'colours': {
          'person': '#d62728',
          'corporatebody': '#ffbb78',
          'event': '#1f77b4',
          'concept': '#aec7e8',
          'place': '#ff9896',
          'culturalartefact': '#9467bd',
          'published': '#98df8a',
          'archival': '#2ca02c',
          'digitalObject': '#ff7f0e'

      },

      'fill': {
          'contextNode': 'green',
          'contextNeighbourDefault': 'orange',
          'contextNeighbourHighlight': 'red',
          'default': '#c8c8c8'
      },

      'opacity': {
          'default': '1',
          'unselected': '0.3'
      },

      'stroke': {
          'link': {
              'selected': 'orange',
              'unselected': '#c8c8c8'
          },
          'date': {
              'selected': 'black',
              'unselected': '#c8c8c8'
          }
      },

      'height': {
          'selected': '10',
          'default': '3'
      },

      'radius': {
          'node': {
              'selected': '30',
              'unselected': '10'
          },
          'date': {
              'selected': '6',
              'default': '3'
          }
      }
  });

Add colour map by type


function loop(value, test, update, body) {
    for (let i = value; test(i); i = update(i)) body(i);
}

loop(3, n => n > 0, n => n - 1, console.log);
// → 3
// → 2
// → 1

function loop(val, test, update, body) {
	for (let v = val; test(v); v = update(v)) {
		body(v);
	}
}

Add chapter 5, exercise 2


class Group {
    constructor() {
        this.members = [];
    }

    add(elem) {
        // Could use Group.has() here instead
        if (!this.members.includes(elem)) {
            this.members.push(elem);
        }
    }

    delete(elem) {
        // Don't really need if check here, filter if element is not there is
        // no-op
        if (this.members.includes(elem)) {
            this.members = this.members.filter(e => e !== elem);
        }
    }

    has(elem) {
        return this.members.includes(elem);
    }

    // Better name: collection
    static from(iterable) {
        let group = new Group();
        for (let elem of iterable) {
            group.add(elem);
        }
        return group;
    }

    [Symbol.iterator]() {
        return new GroupIterator(this);
    }
}

class GroupIterator {
    constructor(group) {
        this.idx = 0;
        this.group = group;
    }

    next() {
        if (this.idx == this.group.members.length) return {done: true};

        let value = this.group.members[this.idx];
        ++this.idx;
        return {value, done: false};
    }
}

class Group {
	constructor() {
		this.members = [];
	}

	add(element) {
		if (!this.has(element)) {
			this.members.push(element);
		}
	}

	delete(element) {
		this.members = this.members.filter(e => e !== element);
	}

	has(element) {
		return this.members.includes(element);
	}

	static from(group) {
		let g = new Group;
		for (let element of group) {
			g.add(element);
		}

		return g;
	}

	Symbol.iterator = function() {
		return new GroupIterator(this);
	}
}

class GroupIterator {
	constructor(group) {
		this.pos = 0;
		this.group = group;
	}

	next() {
		if (this.pos == this.group.length) {
			return {value: undefined, done: true};
		}

		return {value: this.group[this.pos++], done: false};
	}
}

Add chapter 6, exercise 3


/**
 * @author Lukas Reschke
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * This gets only loaded if the integrity check has failed and then shows a notification
 */
$(document).ready(function(){
	var text = t(
			'core',
			'There were problems with the code integrity check. More information…',
			{
				docUrl: OC.generateUrl('/settings/admin#security-warning')
			}
	);

	OC.Notification.showHtml(
		text,
		{
			isHTML: true
		}
	);
});

/**
 * @author Lukas Reschke
 *
 * @copyright Copyright (c) 2015, ownCloud, Inc.
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * This gets only loaded if the integrity check has failed and then shows a notification
 */
$(document).ready(function(){
	var text = t(
			'core',
			'There were problems with the code integrity check. More information…',
			{
				docUrl: OC.generateUrl('/settings/admin#security-warning')
			}
	);

	OC.Notification.showHtml(
		text,
		{
			type: 'error',
			isHTML: true
		}
	);
});

Allow to dismiss the code integrity warning


// Pipeline: permette di eseguire i task in maniera sincrona seguendo l'ordine imposto,
// altrimenti normalmente Gulp lavora asicronamente eseguendo tutti i task contemporanemante.
// Fonte: https://knpuniversity.com/screencast/gulp/on-end-async-and-listeners
var Q = require('q');

var Pipeline = function (executor) {
    this.entries = [];
    this.app = executor;
};
/**
 * Piper will pass two arguments:
 * - data > Array of files to process
 * - file > Filename destination
 *
 * arguments will be [data,file]
 */
Pipeline.prototype.add = function () {
    this.entries.push(arguments);
};
Pipeline.prototype.run = function (callable) {
    var deferred = Q.defer();
    var i = 0;
    var entries = this.entries;
    var executor = this.executor;

    var runNextEntry = function () {
        // see if we're all done looping
        if (typeof entries[i] === 'undefined') {
            deferred.resolve();
            return;
        }

        // pass executor as this, though we should avoid using "this"
        // in those functions anyways
        callable.apply(executor, entries[i]).on('end', function () {
            i++;
            runNextEntry();
        });
    };
    runNextEntry();

    return deferred.promise;
};

module.exports = Pipeline;

// Pipeline: permette di eseguire i task in maniera sincrona seguendo l'ordine imposto,
// altrimenti normalmente Gulp lavora asicronamente eseguendo tutti i task contemporanemante.
// Fonte: https://knpuniversity.com/screencast/gulp/on-end-async-and-listeners
var Q = require('q');

var Pipeline = function (executor) {
    this.entries = [];
    this.app = executor;
};
/**
 * Piper will pass two arguments:
 * - data > Array of files to process
 * - file > Filename destination
 *
 * arguments will be [data,file]
 */
Pipeline.prototype.add = function () {
    this.entries.push(arguments);
};
Pipeline.prototype.run = function (callable) {
    var deferred = Q.defer();
    var i = 0;
    var entries = this.entries;
    var executor = this.executor;
    // prevent multiple call during watch task
    var self = this;

    var runNextEntry = function () {
        // see if we're all done looping
        if (typeof entries[i] === 'undefined') {
            // prevent multiple call during watch task
            self.entries = [];
            deferred.resolve();
            return;
        }

        // pass executor as this, though we should avoid using "this"
        // in those functions anyways
        callable.apply(executor, entries[i]).on('end', function () {
            i++;
            runNextEntry();
        });
    };
    runNextEntry();

    return deferred.promise;
};

module.exports = Pipeline;

Reset array of entries when run is complted to avoid multiple calls dureing a watch task


'use strict';

var _ = require('lodash');
var WebLink = require('./web_link');

var Root = function(url, http, extensions) {
  _.forEach(extensions, function(e) {
    if (!e.initialize) {
      return;
    }

    e.initialize(http, extensions);
  });
  var link = new WebLink({ href: url }, http, extensions);

  this.follow = function(options) {
    return link.follow(options);
  };
};

module.exports = Root;

'use strict';

var _ = require('lodash');
var WebLink = require('./web_link');

var Root = function(url, http, extensions) {
  _.forEach(extensions, function(e) {
    if (!e.initialize) {
      return;
    }

    e.initialize(http, extensions);
  });
  return new WebLink({ href: url }, http, extensions);
};

module.exports = Root;

Return WebLink directly from Root constructor.


/**
 * @class Facebook
 * @author Flavio De Stefano <flavio.destefano@caffeinalab.com>
 */

/**
 * * `permissions`: Array of permissions. Default: `[]`
 * @type {Object}
 */
var config = _.extend({
	permissions: [],
}, Alloy.CFG.T.auth ? Alloy.CFG.T.auth.facebook : {});
exports.config = config;

var Facebook = require('com.facebook');

if (Facebook != null) {
	if (config.permissions != null) {
		Facebook.permissions = _.isArray(config.permissions) ? config.permissions : config.permissions.split(',');
	}
}

module.exports = Facebook;

/**
 * @class Facebook
 * @author Flavio De Stefano <flavio.destefano@caffeinalab.com>
 */

/**
 * * `permissions`: Array of permissions. Default: `[]`
 * @type {Object}
 */
var config = _.extend({
	permissions: [],
}, Alloy.CFG.T.auth ? Alloy.CFG.T.auth.facebook : {});
exports.config = config;

var Facebook = require('facebook');

if (Facebook != null) {
	if (config.permissions != null) {
		Facebook.permissions = _.isArray(config.permissions) ? config.permissions : config.permissions.split(',');
	}
}

module.exports = Facebook;

Revert to Facebook original module


'use strict'

var gulp = require('gulp');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');

var srcFile = 'src/bsDropdown.js';

gulp.task('default', ['jshint', 'uglify']);

gulp.task('jshint', function(){
	return gulp.src(srcFile)
		.pipe(jshint())
		.pipe(jshint.reporter('default'));
});

gulp.task('uglify', function(){
	return gulp.src(srcFile)
		.pipe(uglify())
		.pipe(rename({
			suffix: '.min'
		}))
		.pipe(gulp.dest('dist'));
});

'use strict'

var gulp = require('gulp');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');

var srcFile = 'src/bsDropdown.js';

gulp.task('default', ['jshint', 'uglify']);
gulp.task('dev', function(){
	return gulp.watch([srcFile], ['default'])
});

gulp.task('jshint', function(){
	return gulp.src(srcFile)
		.pipe(jshint())
		.pipe(jshint.reporter('default'));
});

gulp.task('uglify', function(){
	return gulp.src(srcFile)
		.pipe(uglify({
			preserveComments: 'some'
		}))
		.pipe(rename({
			suffix: '.min'
		}))
		.pipe(gulp.dest('dist'));
});

Add gulp watch for development.


const {
    expect
} = require('chai');
const sinon = require('sinon');

const httpMocks = require('node-mocks-http');

const controller = require('../../controllers/messenger-controller');

describe('Messenger controller tests', () => {
    it('should render messenger view', () => {
        var request = httpMocks.createRequest({
            method: 'GET',
            url: '/messenger'
        });

        var response = httpMocks.createResponse();

        controller.getMessenger(request, response);

        var renderedView = response._getRenderView();

        expect(renderedView).to.eql('messenger');
    });
});

const {
    expect
} = require('chai');
const sinon = require('sinon');

const httpMocks = require('node-mocks-http');

const controller = require('../../controllers/messenger-controller');

describe('Messenger controller tests', () => {
    it('should render messenger view', () => {
        var request = httpMocks.createRequest({
            method: 'GET',
            url: '/messenger'
        });

        var response = httpMocks.createResponse();

        controller.getMessenger(request, response);

        var renderedView = response._getRenderView();

        expect(renderedView).to.eql('messenger/messenger');
    });
});

Change test to fit new logic


/* eslint-disable */
const gulp = require('gulp');
const tasks = require('strt-gulptasks')({
  output: 'public/dist',
});

gulp.task('default', gulp.series(
  tasks.clean,
  gulp.parallel(
    tasks.styles,
    tasks.images,
    tasks.files,
    tasks.icons,
    tasks.scripts
  ),
  tasks.serve
));

gulp.task('production', gulp.series(
  function setProdEnv(done) {
    process.env.NODE_ENV = 'production';
    done();
  },
  tasks.clean,
  gulp.parallel(
    tasks.styles,
    tasks.images,
    tasks.files,
    tasks.icons,
    tasks.scripts
  )
));

/* eslint-disable */

const gulp = require('gulp');
const tasks = require('strt-gulptasks')({
  output: 'public/dist',
  browserSync: {
    options: {
      server: 'public',
      files: ['public/*.html'],
    }
  }
});

gulp.task('default', gulp.series(
  tasks.clean,
  gulp.parallel(
    tasks.styles,
    tasks.images,
    tasks.files,
    tasks.icons,
    tasks.scripts
  ),
  tasks.serve
));

gulp.task('production', gulp.series(
  function setProdEnv(done) {
    process.env.NODE_ENV = 'production';
    done();
  },
  tasks.clean,
  gulp.parallel(
    tasks.styles,
    tasks.images,
    tasks.files,
    tasks.icons,
    tasks.scripts
  )
));

Update with default browsersync options


var bt = require('../src/bluetooth').Bluetooth;

var remote_addr = "A4:E4:B8:6C:38:B9";

function findDevice(item) {
	return item.address == remote_addr;
}

bt.on('started', function() {
	console.log('onstarted');
	bt.start_scan();
});

bt.on('scan', function(device) {
	console.log('onscan: '+ device);
	var device = JSON.parse(device).find(findDevice);

	if (device) {
		console.log('Bonding to ' + remote_addr);
		bt.stop_scan();
		bt.start_bond(device.address);
	}
});

bt.on('bond', function(paired) {
	console.log('bonded: '+ paired);
	bt.connect(remote_addr);
});

bt.on('connect', function(connected) {
	console.log('connected: '+ connected);
});

process.on('SIGINT', function () {
	bt.stop_scan();
    process.exit(0);
});

var bt = require('../src/bluetooth').Bluetooth;

var remote_addr = "<BT device address";

function findDevice(item) {
	return item.address == remote_addr;
}

bt.on('started', function() {
	console.log('onstarted');
	bt.start_scan();
});

bt.on('scan', function(device) {
	console.log('onscan: '+ device);
	var device = JSON.parse(device).find(findDevice);

	if (device) {
		console.log('Bonding to ' + remote_addr);
		bt.stop_scan();
		bt.start_bond(device.address);
	}
});

bt.on('bond', function(paired) {
	console.log('bonded: '+ paired);
	bt.connect(remote_addr);
});

bt.on('connect', function(connected) {
	console.log('connected: '+ connected);
});

process.on('SIGINT', function () {
	bt.stop_scan();
    process.exit(0);
});

Remove mention of user data in bt test


 (function(){
  Tablesort.extend('disability', function(item) {
     return (
      item.search(/(High|Moderate|Low < 2)/i) !== -1
      );
    }, function(a,b) {
    var disability = ['Low < 2','Moderate','High'];
    return disability.indexOf(b) - disability.indexOf(a);
  });
}());

 (function(){
  Tablesort.extend('disability', function(item) {
     return (
      item.search(/(High|Moderate|Low < 2)/i) !== -1
      );
    }, function(a,b) {
    var disability = ['None', 'Low < 2', 'Low >= 2', 'Moderate', 'High'];
    return disability.indexOf(b) - disability.indexOf(a);
  });
}());

Add additional disability sort categories


import React from 'react';
import { Container } from 'semantic-ui-react';

const Footer = () =>

    <Container textAlign="center">
      Free & Open Source (MIT)

      Copyright © 2017 laurids-reichardt

    </Container>

;

export default Footer;

import React from 'react';
import { Container } from 'semantic-ui-react';

const Footer = () =>

    <Container textAlign="center">
      Free & Open Source  (
      MIT)

      Copyright © 2017 laurids-reichardt

    </Container>

;

export default Footer;

Add links to the license and GitHub profile to the footer


"use babel";

import _ from "lodash";

export default {
  activate() {
    this.commands = atom.commands.add("atom-workspace", {
      "gluon:build": () => { this.build(); },
    });
  },

  deactivate() {
    this.commands.dispose();
  },

  build() {
    const builder = this.builderForActiveTextEditor();
    if (!builder) { return; }

    builder.buildFunction();
  },

  provideBuildService() {
    const self = this;
    return {
      register(registration) {
        if (!registration) { return; }

        if (!self.builders) {
          self.builders = {};
        }

        self.builders[registration.name] = registration;
      },
    };
  },

  builderForActiveTextEditor() {
    const grammar = this.getActiveGrammar();
    if (!grammar) { return null; }

    const isMappedToGrammer = (b) => { _.includes(b.grammars, grammar); };
    const builder = _.find(this.builders, isMappedToGrammer);
    return builder;
  },

  getActiveGrammar() {
    const editor = this.getActiveTextEditor();
    if (editor) {
      return editor.getGrammar().scopeName;
    }

    return null;
  },

  getActiveTextEditor() {
    return atom.workspace.getActiveTextEditor();
  },
};

"use babel";

import _ from "lodash";

function isMappedToGrammer(grammar) {
  return (builder) => {
    console.log(builder);
    return _.includes(builder.grammars, grammar);
  };
}

export default {
  activate() {
    this.commands = atom.commands.add("atom-workspace", {
      "gluon:build": () => { this.build(); },
    });
  },

  deactivate() {
    this.commands.dispose();
  },

  build() {
    const builder = this.builderForActiveTextEditor();
    if (!builder) { return; }

    builder.buildFunction();
  },

  provideBuildService() {
    const self = this;
    return {
      register(registration) {
        if (!registration) { return; }

        if (!self.builders) {
          self.builders = {};
        }

        self.builders[registration.name] = registration;
      },
    };
  },

  builderForActiveTextEditor() {
    const grammar = this.getActiveGrammar();
    if (!grammar) { return null; }

    const builder = _.find(this.builders, isMappedToGrammer(grammar));
    return builder;
  },

  getActiveGrammar() {
    const editor = this.getActiveTextEditor();
    if (editor) {
      return editor.getGrammar().scopeName;
    }

    return null;
  },

  getActiveTextEditor() {
    return atom.workspace.getActiveTextEditor();
  },
};

Convert inline predicate to predicate generator


var gulp    = require('gulp');
var runSequence = require('run-sequence');

var jasmine = require('gulp-jasmine');
var cover   = require('gulp-coverage');
var coveralls = require('gulp-coveralls');

var options = {
  testPaths : {
    unit : [
      'tests/unit/*.js',
      'tests/unit/**/*.js',
    ],
    spec : [
      'tests/spec/*.js',
      'tests/spec/**/*.js',
    ],
    integration : [
      'tests/integration/*.js',
      'tests/integration/**/*.js',
    ]
  },
  filePaths : {
    core : [
      'src/core/*.js',
      'src/core/**/*.js'
    ],
    framework : [
      'src/framework/*.js',
      'src/framework/**/*.js'
    ],
  },
  jasmine : {
    includeStackTrace : true,
    verbose: true
  }
}
gulp.task('test', function () {
   return gulp.src( options.testPaths.spec )
          .pipe(cover.instrument({
            pattern: options.filePaths.core,
            debugDirectory: 'debug'
          }))
          .pipe( jasmine( options.jasmine ) )
          .pipe(cover.gather())
          .pipe(cover.format({
            reporter: 'lcov'
          }))
          .pipe(coveralls())
          .pipe(gulp.dest('coverage'));
});

gulp.task('default', function ( cb ) {
  runSequence( 'test', 'build', cb );
});

var gulp    = require('gulp');
var runSequence = require('run-sequence');

var jasmine = require('gulp-jasmine');
var cover   = require('gulp-coverage');
var coveralls = require('gulp-coveralls');

var options = {
  testPaths : {
    unit : [
      'tests/unit/*.js',
      'tests/unit/**/*.js',
    ],
    spec : [
      'tests/spec/*.js',
      'tests/spec/**/*.js',
    ],
    integration : [
      'tests/integration/*.js',
      'tests/integration/**/*.js',
    ]
  },
  filePaths : {
    core : [
      'src/core/*.js',
      'src/core/**/*.js'
    ],
    framework : [
      'src/framework/*.js',
      'src/framework/**/*.js'
    ],
  },
  jasmine : {
    includeStackTrace : true,
    verbose: true
  }
}
gulp.task('test', function () {
   return gulp.src( options.testPaths.spec )
          .pipe(cover.instrument({
            pattern: options.filePaths.core,
            debugDirectory: 'debug'
          }))
          .pipe( jasmine( options.jasmine ) )
          .pipe(cover.gather())
          .pipe(cover.format({
            reporter: 'lcov'
          }))
          .pipe(coveralls())
          .pipe(gulp.dest('coverage'));
});

gulp.task('default', function ( cb ) {
  runSequence( 'test', cb );
});

Remove undefined task for now


var gulp = require('gulp'),
    livereload = require('gulp-livereload'),
    nodemon = require('gulp-nodemon'),
    fs = require('fs');

gulp.task('develop', function () {
  livereload.listen();

  nodemon({
    script: 'server.js',
    stdout: false,
    ext: 'js jade css',
    execMap: {
      js: 'node --debug-brk=5560'
    }
  }).on('readable', function() {
    this.stdout.pipe(fs.createWriteStream('output.txt'));
    this.stderr.pipe(fs.createWriteStream('err.txt'));

    livereload.reload();
  }
)});

gulp.task('default', function () {
  nodemon({
    script: 'server.js',
    ext: 'js jade css'
  });
});

var gulp = require('gulp'),
    livereload = require('gulp-livereload'),
    nodemon = require('gulp-nodemon'),
    fs = require('fs');

gulp.task('dev', function () {
  livereload.listen();

  nodemon({
    script: 'server.js',
    stdout: false,
    ext: 'js jade css',
    execMap: {
      js: 'node --debug-brk=5560'
    }
  }).on('readable', function() {
    this.stdout.pipe(fs.createWriteStream('output.txt'));
    this.stderr.pipe(fs.createWriteStream('err.txt'));

    livereload.reload();
  });

});

gulp.task('default', function () {
  nodemon({
    script: 'server.js',
    ext: 'js jade css'
  });
});

Rename gulp develop task to dev