const fs = require('fs')

const processors = fs.readdirSync(__dirname)
.filter((file) => file !== 'index.js')
.map((file) => {
  const {processMessage, order} = require(`./${file}`)
  return {
    order,
    processMessage: processMessage || ((msg) => msg),
  }
})
.sort(({order: o1}, {order: o2}) => o1 - o2)

module.exports = {processors}

const fs = require('fs')

const processors = fs.readdirSync(__dirname)
.filter((file) => file !== 'index.js')
.map((file) => {
  const {processMessage, order} = require(`./${file}`)
  return {
    order,
    processMessage: processMessage || ((msg) => msg),
  }
})
.sort(({order: o1}, {order: o2}) => o1 - o2)
.map(({processMessage}) => processMessage)

module.exports = {processors}

Fix message processor pipeline initialization


/**
 * game-loop.js
 *
 * Manages the creation and properties for our game loop object
 */

/**
 * Main game loop factory
 *
 * @returns {object}
 */
var createGameLoop = function() {
    var fps = 2;

    var lastCycleKeypress = '';

    var update = function() {
        if (typeof(game.keypress.keyCode) === 'undefined') {
            return;
        }

        console.log(game.keypress);

        switch (game.keypress.keyCode) {
            case LOWER_CASE_Q_KEY_CODE:
                this.end();
                break;
            case LOWER_CASE_D_KEY_CODE:
                ship.updateXPosRight();
                break;
        }

        lastCycleKeypress = game.keypress.keyCode;
    };

    var draw = function() {
        var svgSpec = 'http://www.w3.org/2000/svg';
        var shipEl = document.getElementById('ship');

        shipEl.setAttribute('x', ship.xPos);
    };

    var run = function() {
        update();
        draw();
    };

    var end = function() {
        clearInterval(gameLoop._intervalId);
    };

    return {
        fps: fps,
        lastCycleKeypress: lastCycleKeypress,
        update: update,
        draw: draw,
        end: end,
        run: run
    };
};

// Create game loop
game.loop = createGameLoop();

/**
 * game-loop.js
 *
 * Manages the creation and properties for our game loop object
 */

/**
 * Main game loop factory
 *
 * @returns {object}
 */
var createGameLoop = function() {
    var fps = 2;

    var lastCycleKeypress = '';

    var update = function() {
        if (typeof(game.keypress.keyCode) === 'undefined') {
            return;
        }

        console.log(game.keypress);

        switch (game.keypress.keyCode) {
            case LOWER_CASE_Q_KEY_CODE:
                this.end();
                break;
            case LOWER_CASE_A_KEY_CODE:
                ship.updateXPosLeft();
                break;
            case LOWER_CASE_D_KEY_CODE:
                ship.updateXPosRight();
                break;
        }

        lastCycleKeypress = game.keypress.keyCode;
    };

    var draw = function() {
        var svgSpec = 'http://www.w3.org/2000/svg';
        var shipEl = document.getElementById('ship');

        shipEl.setAttribute('x', ship.xPos);
    };

    var run = function() {
        update();
        draw();
    };

    var end = function() {
        clearInterval(gameLoop._intervalId);
    };

    return {
        fps: fps,
        lastCycleKeypress: lastCycleKeypress,
        update: update,
        draw: draw,
        end: end,
        run: run
    };
};

// Create game loop
game.loop = createGameLoop();

Add ship movement to the left in game loop


import Ember from 'ember';
import layout from '../../templates/components/bricks-grid/item';

const { Component, on } = Ember;

export default Component.extend({
  layout,

  sendRepackOnInit: on('didInsertElement', function() {
    const img = this.$('img');

    img.on('load', () => {
      if (!this.get('isDestroyed')) {
        this.sendAction('repack');
      }
    });

    this.sendAction('repack');
  }),

  sendRepackOnDestroy: on('willDestroyElement', function() {
    this.sendAction('repack');
  }),
});

import Ember from 'ember';
import layout from '../../templates/components/bricks-grid/item';

const { Component, on, run } = Ember;

export default Component.extend({
  layout,

  sendRepackOnInit: on('didInsertElement', function() {
    const img = this.$('img');

    img.on('load', () => {
      if (!this.get('isDestroyed')) {
        this.sendAction('repack');
      }
    });

    this.sendAction('repack');
  }),

  sendRepackOnDestroy: on('willDestroyElement', function() {
    run.next(() => this.sendAction('repack'))
  }),
});

Add repack action on destroy after all queue


"use strict";

const Shortlink = require("./shortlink");
const trim_slashes = require("./string_utility").trim_slashes;

module.exports = (server) => {

  server.get("/", (req, res) => {
    res.redirect(301, "http://jotaen.net");
  });

  server.get("*", (req, res) => {
    const path = trim_slashes(req.params[0]);
    Shortlink.findOne({
      path: path
    }).then((shortlink) => {
      if (shortlink) {
        res.redirect(301, shortlink.url);
      } else {
        res.sendStatus(404);
      }
    });
  });

  server.put("*", (req, res) => {
    const shortlink = new Shortlink({
      url: req.query.url,
      path: trim_slashes(req.params[0])
    });

    shortlink
      .save()
      .then(() => {
        res.sendStatus(200);
      })
      .catch((error) => {
        res.status(500).send(error);
      });
  });

};

"use strict";

const Shortlink = require("./shortlink");
const trim_slashes = require("./string_utility").trim_slashes;

module.exports = (server) => {

  server.get("/", (req, res) => {
    res.redirect(301, "http://jotaen.net");
  });

  server.get("/:token", (req, res) => {
    const path = trim_slashes(req.params.token);
    Shortlink.findOne({
      path: path
    }).then((shortlink) => {
      if (shortlink) {
        res.redirect(301, shortlink.url);
      } else {
        res.sendStatus(404);
      }
    });
  });

  server.put("/:token", (req, res) => {
    const shortlink = new Shortlink({
      url: req.query.url,
      path: trim_slashes(req.params.token)
    });

    shortlink
      .save()
      .then(() => {
        res.sendStatus(200);
      })
      .catch((error) => {
        res.status(500).send(error);
      });
  });

};

Use path parameters in http routes


#!/usr/bin/env node
var fs              = require('fs');
var Cucumber        = require('./lib/cucumber');
var supportCodePath = process.ARGV[3] ? process.cwd() + '/' + process.ARGV[3] : './features/step_definitions/cucumber_steps';
var supportCode     = require(supportCodePath);
var cucumber        = Cucumber(fs.readFileSync(process.ARGV[2]), supportCode);
var formatter       = Cucumber.Listener.ProgressFormatter();
cucumber.attachListener(formatter);
cucumber.start(function(succeeded) {
  var code = succeeded ? 0 : 1;
  var exitFunction = function() {
    process.exit(code);
  };

  // --- exit after waiting for all pending output ---
  var waitingIO = false;
  process.stdout.on('drain', function() {
    if (waitingIO) {
      // the kernel buffer is now empty
      exitFunction();
    }
  });
  if (process.stdout.write("")) {
    // no buffer left, exit now:
    exitFunction();
  } else {
    // write() returned false, kernel buffer is not empty yet...
    waitingIO = true;
  }
});

#!/usr/bin/env node
var fs              = require('fs');
var Cucumber        = require('./lib/cucumber');
var featurePath     = process.ARGV[2];
var supportCodePath = process.ARGV[3] ? process.cwd() + '/' + process.ARGV[3] : './features/step_definitions/cucumber_steps';

if (typeof(featurePath) == 'undefined') {
  throw("Please give me a feature, try something like `" + process.ARGV[1] + " features/cucumber-features/core.feature`.");
}

var supportCode     = require(supportCodePath);
var cucumber        = Cucumber(fs.readFileSync(featurePath), supportCode);
var formatter       = Cucumber.Listener.ProgressFormatter();
cucumber.attachListener(formatter);
cucumber.start(function(succeeded) {
  var code = succeeded ? 0 : 1;
  var exitFunction = function() {
    process.exit(code);
  };

  // --- exit after waiting for all pending output ---
  var waitingIO = false;
  process.stdout.on('drain', function() {
    if (waitingIO) {
      // the kernel buffer is now empty
      exitFunction();
    }
  });
  if (process.stdout.write("")) {
    // no buffer left, exit now:
    exitFunction();
  } else {
    // write() returned false, kernel buffer is not empty yet...
    waitingIO = true;
  }
});

Throw a more explicit exception on missing feature path CLI argument


'use strict';

var async = require('async');
var rarity = require('rarity');

var AnyFetch = require('../../lib/index.js');

/**
 * Reset the company and obtain the new Bearer token.
 * Automatically switch to Bearer auth.
 */
AnyFetch.prototype.resetToBearer = function(done) {
  var self = this;

  async.waterfall([
    function resetCompany(cb) {
      self.deleteCompanyReset(rarity.slice(1, cb));
    },
    function obtainNewToken(cb) {
      // Switch back to basic auth to obtain a new token
      self.authHeader = self.basicAuthHeader;
      self.getToken(cb);
    }
  ], function(err, res) {
    if(res && res.body && res.body.token) {
      // Switch to Bearer auth
      self.authHeader = 'Bearer ' + res.body.token;
    }
    done(err, res);
  });
};

'use strict';

var async = require('async');
var rarity = require('rarity');

var AnyFetch = require('../../lib/index.js');

/**
 * Reset the company and obtain the new Bearer token.
 * Automatically switch to Bearer auth.
 */
AnyFetch.prototype.resetToBearer = function(done) {
  var self = this;

  if(!self.basicAuthHeader) {
    return done(new Error('Reset canceled: `this` does not contain basic auth infos, we would not be able to obtain a new token after reset.'));
  }

  async.waterfall([
    function resetCompany(cb) {
      self.deleteCompanyReset(rarity.slice(1, cb));
    },
    function obtainNewToken(cb) {
      // Switch back to basic auth to obtain a new token
      self.authHeader = self.basicAuthHeader;
      self.getToken(cb);
    }
  ], function(err, res) {
    if(res && res.body && res.body.token) {
      // Switch to Bearer auth
      self.authHeader = 'Bearer ' + res.body.token;
    }
    done(err, res);
  });
};

Make sure we'll be able to obtain a new token before reset


export default class HttpApi {
  constructor (prefix = '') {
    this.prefix = prefix
    this.opts = {
      credentials: 'same-origin',
      headers: new Headers({
        'Content-Type': 'application/json',
      })
    }

    return this.callApi
  }

  callApi = (method, url, opts = {}) => {
    opts = Object.assign({}, this.opts, opts)
    opts.method = method

    if (typeof opts.body === 'object') {
      opts.body = JSON.stringify(opts.body)
    }

    return fetch(`/api/${this.prefix}${url}`, opts)
      .then(res => {
        if (res.ok) {
          const type = res.headers.get('Content-Type')
          return (type && type.includes('application/json')) ? res.json() : res
        }

        // error
        return res.text().then(txt => {
          return Promise.reject(new Error(txt))
        })
      })
  }
}

export default class HttpApi {
  constructor (prefix = '') {
    this.prefix = prefix
    this.options = {
      credentials: 'same-origin',
    }

    return this.callApi
  }

  callApi = (method, url, options = {}) => {
    const opts = {
      ...this.options,
      ...options,
      method,
    }

    if (typeof opts.body === 'object' && !(opts.body instanceof FormData)) {
      opts.body = JSON.stringify(opts.body)
    }

    return fetch(`/api/${this.prefix}${url}`, opts)
      .then(res => {
        if (res.ok) {
          const type = res.headers.get('Content-Type')
          return (type && type.includes('application/json')) ? res.json() : res
        }

        // error
        return res.text().then(txt => {
          return Promise.reject(new Error(txt))
        })
      })
  }
}

Support FormData in request body; minor refactor


export default function CustomersResource({apiHandler}) {
    return {
        async getAll({limit = null, offset = null, sort = null, expand = '', filter = '', q = '', criteria = ''}) {
            const params = {
                limit,
                offset,
                sort,
                expand,
                filter,
                q,
                criteria
            };
            return await apiHandler.getAll(`customers`, params);
        },

        async get({id}) {
            return await apiHandler.get(`customers/${id}`);
        },

        async create({id = '', data}) {
            return await apiHandler.create(`customers/${id}`, data);
        },

        async update({id, data}) {
            return await apiHandler.put(`customers/${id}`, data);
        },

        async getLeadSource({id}) {
            return await apiHandler.get(`customers/${id}/lead-source`);
        },

        async createLeadSource({id, data}) {
            return await apiHandler.put(`customers/${id}/lead-source`, data);
        },

        async deleteLeadSource({id}) {
            return await apiHandler.delete(`customers/${id}/lead-source`);
        }
    };
};

export default function CustomersResource({apiHandler}) {
    return {
        async getAll({limit = null, offset = null, sort = null, expand = '', filter = '', q = '', criteria = ''} = {}) {
            const params = {
                limit,
                offset,
                sort,
                expand,
                filter,
                q,
                criteria
            };
            return await apiHandler.getAll(`customers`, params);
        },

        async get({id}) {
            return await apiHandler.get(`customers/${id}`);
        },

        async create({id = '', data}) {
            return await apiHandler.create(`customers/${id}`, data);
        },

        async update({id, data}) {
            return await apiHandler.put(`customers/${id}`, data);
        },

        async getLeadSource({id}) {
            return await apiHandler.get(`customers/${id}/lead-source`);
        },

        async createLeadSource({id, data}) {
            return await apiHandler.put(`customers/${id}/lead-source`, data);
        },

        async deleteLeadSource({id}) {
            return await apiHandler.delete(`customers/${id}/lead-source`);
        }
    };
};

Add default value for customer getAll


/**
 * GET /
 * Home page.
 */
import Application from '../../application/HelloWorld';

exports.home = (req, res) => {
  res.render('home', {
    Application,
    applicationName: 'HelloWorld',
    preloadedState: Object.assign({}, Application.preloadedState, {
      otherState: 123,
    }),
    payload: {},
    cache: true,
  });
};

/**
 * GET /
 * Home page.
 */
import Application from '../../application/HelloWorld';

exports.home = (req, res) => {
  res.render('home', {
    Application,
    applicationName: 'HelloWorld',
    preloadedState: Object.assign({}, Application.preloadedState, {
      otherState: 123,
    }),
    payload: {
      title: 'My Application',
      description: 'This is an example.',
    },
    cache: true,
  });
};

Add payload 'title' and 'description' to render in 'Home' view.


/**
 * Return the track object for the track corresponding to this uid
 *
 * Null or undefined if none.
 */
const getTrackByUid = (tracks, uid) => {
  for (const trackType in tracks) {
    const theseTracks = tracks[trackType];

    // no tracks of this type specified, probably
    if (!theseTracks.filter) continue;

    const filteredTracks = theseTracks.filter(d => d.uid == uid);

    if (filteredTracks.length) { return filteredTracks[0]; }

    // check to see if this track is part of a combined track
    const combinedTracks = theseTracks.filter(d => d.type == 'combined');

    if (combinedTracks.length) {
      for (let i = 0; i < combinedTracks.length; i++) {
        const ct = combinedTracks[i];
        const filteredTracks = ct.contents.filter(d => d.uid == uid);

        if (filteredTracks.length) { return filteredTracks[0]; }
      }
    }
  }

  return null;
};

export default getTrackByUid;

/**
 * Return the track object for the track corresponding to this uid
 *
 * Null or undefined if none.
 */
const getTrackByUid = (tracks, uid) => {
  let found = null;

  const checkTrack = (track) => {
    if (track.uid === uid) found = track;
    if (track.type.substr(0, 8) === 'combined') track.contents.forEach(checkTrack);
    return null;
  };

  Object.keys(tracks)
    .map(trackType => tracks[trackType])
    .filter(tracksByPos => tracksByPos.filter)
    .reduce((a, b) => a.concat(b), [])
    .forEach(checkTrack);

  return found;
};

export default getTrackByUid;

Simplify and support recursively combined tracks.


angular.module("routeUrls", [])

.factory("urls", function($route) {

    // Cache the routing paths for any named routes.
    var pathsByName = {};
    angular.forEach($route.routes, function (route, path) {
        if (route.name) {
            pathsByName[route.name] = path;
        }
    });

    // Param name to replacement regex cache.
    var regexs = {};

    // Build a path for the named route from the route's URL and the given
    // params.
    var path = function (name, params) {
        var url = pathsByName[name] || "/";
        angular.forEach(params || {}, function (value, key) {
            var regex = regexs[key];
            if (regex === undefined) {
                regex = regexs[key] = new RegExp(":" + key + "(?=/|$)");
            }
            url = url.replace(regex, value);
        });
        return url;
    };

    return {
        path: path,
        href: function (name, params) {
            return "#" + path(name, params);
        }
    };
});

angular.module("routeUrls", [])

.provider("urls", function($locationProvider) {
    return {
        $get: function($route) {

            // Cache the routing paths for any named routes.
            var pathsByName = {};
            angular.forEach($route.routes, function (route, path) {
                if (route.name) {
                    pathsByName[route.name] = path;
                }
            });

            // Param name to replacement regex cache.
            var regexs = {};

            // Build a path for the named route from the route's URL and the given
            // params.
            var path = function (name, params) {
                var url = pathsByName[name] || "/";
                angular.forEach(params || {}, function (value, key) {
                    var regex = regexs[key];
                    if (regex === undefined) {
                        regex = regexs[key] = new RegExp(":" + key + "(?=/|$)");
                    }
                    url = url.replace(regex, value);
                });
                return url;
            };

            // Query $locationProvider for its configuration.
            var html5Mode = $locationProvider.html5Mode();
            var hashPrefix = $locationProvider.hashPrefix();

            return {
                path: path,
                href: function (name, params) {
                    if (html5Mode) {
                        return path(name, params);
                    }
                    return "#" + hashPrefix + path(name, params);
                }
            };
        }
    };
});

Make url construction sensitive to $locationProvider configuration.


import {t} from 'i18next-client';

const Translation = Component => class TranslatedComponent extends Component {
    constructor(props) {
        super(props);
        this.i18n = t;
    }
};

export default Translation;

import {translate} from 'focus-core/translation';

const Translation = Component => class TranslatedComponent extends Component {
    constructor(props) {
        super(props);
        this.i18n = translate;
    }
};

export default Translation;

Fix import from npm to focus-core


// File to handle processing the form code into JS events.

$(document).ready(function(){
  $('.form-group > input.form-control').each(function() {
    input = $(this)

    // Placeholder for all checkboxes is 1.0 or 0.0.
    //
    // Placeholder value is a string and not a float or int.
    //
    // The form should be setting the default value to "checked"
    // or "unchecked", instead of using a stringy placeholder, but I decided to
    // leave the form as-is to avoid dealing with the spaghetti form code.
    if (input.attr("type") == "checkbox"){
      value = input.prop("checked") ? "1.0" : "0.0"
    } else {
      value = input.val()
    }

    value_default = input.prop('placeholder');
    value_changed = (value != '') && (value != value_default);
    group = input.closest('.form-group')
    if (value_changed) {
      group.addClass('edited');
    } else {
      input.val('');
      group.removeClass('edited');
    }
  })

  $('div.inputs-block-content').each(function(){
    var that = this;
    is_edited = $(that).find('div.form-group').hasClass("edited");
    if(is_edited){
      $(that).find("div.collapse").addClass("in");
    }
  })
})

$(document).ready(function(){
  $('.form-group > input.form-control').each(function() {
    input = $(this)

    // Placeholder for all checkboxes is 1.0 or 0.0.
    //
    // Placeholder value is a string and not a float or int.
    //
    // The form should be setting the default value to "checked"
    // or "unchecked", instead of using a stringy placeholder, but I decided to
    // leave the form as-is to avoid dealing with the spaghetti form code.
    // if (input.attr("type") == "checkbox"){
    //   value = input.prop("checked") ? "1.0" : "0.0"
    // } else {
    //   value = input.val()
    // }
    value = input.val();
    value_default = input.prop('placeholder');
    value_changed = (value != '') && (value != value_default);
    group = input.closest('.form-group')
    if (value_changed) {
      group.addClass('edited');
    } else {
      input.val('');
      group.removeClass('edited');
    }
  })

  $('div.inputs-block-content').each(function(){
    var that = this;
    is_edited = $(that).find('div.form-group').hasClass("edited");
    if(is_edited){
      $(that).find("div.collapse").addClass("in");
    }
  })

  $("form").on("submit", function(e){
    var that = this;
    $(this).find("input[type=checkbox]").each(function(){
      if($(this).prop("checked")){
        $(this).val("True");
      } else {
        $(this).val("False");
      }
    })
  })
})

Make sure that checkboxes are given boolean values when checked.


/*
 * Cordova Device Name Plugin
 *
 * ZeroConf plugin for Cordova/Phonegap
 * by Sylvain Brejeon
 */

var argscheck = require('cordova/argscheck');
var channel = require('cordova/channel');
var utils = require('cordova/utils');
var exec = require('cordova/exec');
var cordova = require('cordova');

channel.createSticky('onDeviceNameReady');
// Tell cordova channel to wait on the DeviceNameReady event
channel.waitForInitialization('onDeviceNameReady');

function DeviceName() {
    this.name = null;
    var me = this;
    channel.onCordovaReady.subscribe(function() {
        me.get(function(info) {
            me.name = info.name;
            channel.onDeviceNameReady.fire();
        }, function(e) {
            utils.alert("[ERROR] Error initializing Cordova: " + e);
        });
    });
}

DeviceName.prototype.get = function(successCallback, errorCallback) {
    argscheck.checkArgs('fF', 'DeviceName.get', arguments);
    exec(successCallback, errorCallback, "DeviceName", "get", []);
};

module.exports = new DeviceName();

/*
 * Cordova Device Name Plugin
 *
 * ZeroConf plugin for Cordova/Phonegap
 * by Sylvain Brejeon
 */

var argscheck = require('cordova/argscheck');
var channel = require('cordova/channel');
var utils = require('cordova/utils');
var exec = require('cordova/exec');
var cordova = require('cordova');

channel.createSticky('onDeviceNameReady');
// Tell cordova channel to wait on the DeviceNameReady event
channel.waitForInitialization('onDeviceNameReady');

function DeviceName() {
    this.name = null;
    var me = this;
    channel.onCordovaReady.subscribe(function() {
        me.get(function(info) {
            me.name = info.name;
            channel.onDeviceNameReady.fire();
        }, function(e) {
            utils.alert("[DeviceName] Error initializing Cordova: " + e);
        });
    });
}

DeviceName.prototype.get = function(successCallback, errorCallback) {
    argscheck.checkArgs('fF', 'DeviceName.get', arguments);
    exec(successCallback, errorCallback, "DeviceName", "get", []);
};

module.exports = new DeviceName();

Make source of error clear in the message


module.exports = {
  up: function (migration, DataTypes) {
    return migration.createTable('Bikes', {
      id: {
        primaryKey: true,
        allowNull: false,
        type: DataTypes.UUID
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING
      },
      size: {
        allowNull: false,
        type: DataTypes.INTEGER
      },
      type: {
        allowNull: false,
        type: DataTypes.ENUM([
          'image/png',
          'image/jpeg'
        ])
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE
      },
      updatedAt: {
        allowNull: false,
        type: DataTypes.DATE
      },
      BikeshedId: {
        allowNull: false,
        type: DataTypes.UUID
      }
    })
  },
  down: function (migration) {
    return migration.dropTable('Bikes')
  }
}

module.exports = {
  up: function (migration, DataTypes) {
    return migration.createTable('Bikes', {
      id: {
        primaryKey: true,
        allowNull: false,
        type: DataTypes.UUID
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING
      },
      size: {
        allowNull: false,
        type: DataTypes.INTEGER
      },
      width: {
        allowNull: false,
        type: DataTypes.INTEGER
      },
      height: {
        allowNull: false,
        type: DataTypes.INTEGER
      },
      type: {
        allowNull: false,
        type: DataTypes.ENUM([
          'image/png',
          'image/jpeg'
        ])
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE
      },
      updatedAt: {
        allowNull: false,
        type: DataTypes.DATE
      },
      BikeshedId: {
        allowNull: false,
        type: DataTypes.UUID
      }
    })
  },
  down: function (migration) {
    return migration.dropTable('Bikes')
  }
}

Add width and height to bike model


export default class LabelOptions {

    constructor ({ offset, line_exceed, move_in_tile, keep_in_tile, buffer }) {
        this.buffer = buffer|| [0, 0];
        this.keep_in_tile = (keep_in_tile != null) ? keep_in_tile : true;
        this.move_in_tile = (move_in_tile != null) ? move_in_tile : true;
        this.offset = offset || [0, 0];
        this.line_exceed = (line_exceed != null) ? line_exceed : 30;
    }

}

export default class LabelOptions {

    constructor ({ offset, line_exceed, move_in_tile, keep_in_tile, buffer }) {
        this.buffer = buffer|| [0, 0];
        this.keep_in_tile = (keep_in_tile != null) ? keep_in_tile : true;
        this.move_in_tile = (move_in_tile != null) ? move_in_tile : true;
        this.offset = offset || [0, 0];
        this.line_exceed = (line_exceed != null) ? line_exceed : 80;
    }

}

Revert exceed default to 80


function windowPopup(url, width, height) {
  // Calculate the position of the popup so
  // it’s centered on the screen.
  var left = screen.width / 2 - width / 2,
    top = screen.height / 2 - height / 2;
  window.open(
    url,
    '',
    'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=' +
      width +
      ',height=' +
      height +
      ',top=' +
      top +
      ',left=' +
      left
  );
}

$('.js-social-share').on('click', function(e) {
  e.preventDefault();
  windowPopup($(this).attr('href'), 500, 300);
});

function windowPopup(url, width, height) {
  // Calculate the position of the popup so
  // it’s centered on the screen.
  var left = screen.width / 2 - width / 2,
    top = screen.height / 2 - height / 2;
  window.open(
    url,
    '',
    'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=' +
      width +
      ',height=' +
      height +
      ',top=' +
      top +
      ',left=' +
      left
  );
}

var socialLink = document.querySelector('.js-social-share');

if (socialLink) {
  socialLink.addEventListener('click', function(e) {
    e.preventDefault();
    windowPopup(this.getAttribute('href'), 500, 300);
  });
}

Move social links code to vanilla js


export function initialize(container, application) {
  application.register('google:main', window.google, { instantiate: false });
  application.inject('component', 'google', 'google:main');
}

export default {
  name: 'register-google',
  initialize: initialize
};

export function initialize() {
  let application = arguments[1] || arguments[0];

  application.register('google:main', window.google, { instantiate: false });
  application.inject('component', 'google', 'google:main');
}

export default {
  name: 'register-google',
  initialize: initialize
};

Fix initializer arity deprecation in ember >2.1


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

//= require sufia
//= require_tree .

$(document).ready(function(e) {
  $('#material-fields').nestedFields();
  $('#measurement-fields').nestedFields();
});

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

//= require sufia
//= require_tree .

var ready = function() {
  $('#material-fields').nestedFields();
  $('#measurement-fields').nestedFields();
};

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

Load measurements and materials JS after turbolinks page load


// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require_tree .

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

Return to original jquery requirements


// Module dependencies.
var express = require('express');
var ArticleProvider = require('./articleprovider-memory').ArticleProvider;

var app = express();
var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(bodyParser.json());
app.use(methodOverride());
app.use(require('stylus').middleware({ src: __dirname + '/public' }));

/*app.configure('production', function(){
  app.use(express.errorHandler());
});*/

var articleProvider= new ArticleProvider();

app.get('/', function(req, res){
  articleProvider.findAll(function(error, docs){
      res.send(docs);
  });
})

app.use(express.static(__dirname + '/public'));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));

app.listen(3000);

// Module dependencies.
var express = require('express');
var ArticleProvider = require('./articleprovider-memory').ArticleProvider;

var app = express();
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var errorHandler = require('errorhandler');

app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(bodyParser.json());
app.use(methodOverride());
app.use(require('stylus').middleware({ src: __dirname + '/public' }));

/*app.configure('production', function(){
  app.use(express.errorHandler());
});*/

var articleProvider= new ArticleProvider();

app.get('/', function(req, res){
  articleProvider.findAll(function(error, docs){
      res.send(docs);
  });
})

app.use(express.static(__dirname + '/public'));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));

app.listen(3000);

Fix errorHandler for express 4.0


#!/usr/bin/env node

const crypto = require("crypto");
const _sss = require("./build/Release/shamirsecretsharing");

exports.createShares = function createShares(data, n, k) {
  return new Promise((resolve) => {
    // Use node.js native random source for a key
    crypto.randomBytes(32, (err, random) => {
      if (err) throw err;
      _sss.createShares(data, n, k, random, resolve);
    });
  });
};

exports.combineShares = function combineShares(shares) {
  return new Promise((resolve) => {
    _sss.combineShares(shares, resolve);
  }).then((x) => {
    if (x === null) {
      throw "invalid or too few shares provided";
    } else {
      return x;
    }
  });
};

#!/usr/bin/env node

const crypto = require("crypto");
const _sss = require("./build/Release/shamirsecretsharing");

exports.createShares = function createShares(data, n, k) {
  return new Promise((resolve) => {
    // Use node.js native random source for a key
    crypto.randomBytes(32, (err, random) => {
      if (err) throw err;
      _sss.createShares(data, n, k, random, resolve);
    });
  });
};

exports.combineShares = function combineShares(shares) {
  return new Promise((resolve) => {
    _sss.combineShares(shares, resolve);
  }).then((x) => {
    if (x === null) {
      throw "InvalidAccessError: nvalid or too few shares provided";
    } else {
      return x;
    }
  });
};

Add error ident for easier catching


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

module.exports = function (dir1, dir2) {
  var expanded = path.resolve(dir1, dir2.replace(/\:/g, '/'))
  if (isDir(expanded)) {
    if (containsIndexFile(expanded)) {
      return expanded + '/index*'
    } else {
      return expanded + '/*'
    }
  } else {
    return expanded + '*'
  }
}

function isDir (f) {
  try {
    return fs.statSync(f).isDirectory()
  } catch (e) {}
}

function containsIndexFile (f) {
  try {
    return fs.statSync(f + '/index').isFile()
  } catch (e) {}
}

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

module.exports = function (dir1, dir2) {
  var expanded = path.resolve(dir1, path.join.apply(this, dir2.split(':')))
  if (isDir(expanded)) {
    if (containsIndexFile(expanded)) {
      return path.join(expanded, '/index') + '*'
    } else {
      return path.join(expanded, '/') + '*'
    }
  } else {
    return expanded + '*'
  }
}

function isDir (f) {
  try {
    return fs.statSync(f).isDirectory()
  } catch (e) {}
}

function containsIndexFile (f) {
  try {
    return fs.statSync(f + '/index').isFile()
  } catch (e) {}
}

Fix obvious forward-slashing that'll break windows


var CS = CS || {};

CS.BufferLength = 200;
CS.Console = new Array(10000);
CS.LastConsoleLength = 0;

CS.SetProgress = function (percent) {
	document.getElementById('bar').style.width = percent + '%';
	document.getElementById('percentage').innerHTML = percent + '%';
}

CS.SetStatus = function (status) {
	document.getElementById('status').innerHTML = status;
}

CS.WriteConsole = function (text) {
	CS.Console.push(text);
}

CS.ShowAll = function() {
	CS.BufferLength = Number.MAX_SAFE_INTEGER;
	CS.BatchComplete();
}

CS.BatchComplete = function () {
	if (CS.LastConsoleLength >= CS.Console.length) return;

	var bufferArray = CS.Console.slice(Math.max(0, CS.Console.length - CS.BufferLength), CS.Console.length);

	var contents = bufferArray.join('');

	if (bufferArray.length !== CS.Console.length) {
		contents = "<button onclick=\"CS.ShowAll()\" class=\"expand\">Expand complete log</button>" + contents;
	}

	var console = document.getElementById('console');
	console.innerHTML = contents;

	var scrollHeight = Math.max(console.scrollHeight, console.clientHeight);
	console.scrollTop = scrollHeight - console.clientHeight;
}

var CS = CS || {};

CS.BufferLength = 200;
CS.Console = new Array();
CS.LastConsoleLength = 0;

CS.SetProgress = function (percent) {
	document.getElementById('bar').style.width = percent + '%';
	document.getElementById('percentage').innerHTML = percent + '%';
}

CS.SetStatus = function (status) {
	document.getElementById('status').innerHTML = status;
}

CS.WriteConsole = function (text) {
	CS.Console.push(text);
}

CS.ShowAll = function() {
	CS.BufferLength = Number.MAX_SAFE_INTEGER;
	CS.BatchComplete();
}

CS.BatchComplete = function () {
	if (CS.LastConsoleLength >= CS.Console.length) return;

	var bufferArray = CS.Console.slice(Math.max(0, CS.Console.length - CS.BufferLength), CS.Console.length);

	var contents = bufferArray.join('');

	if (bufferArray.length != CS.Console.length) {
		contents = "<button onclick=\"CS.ShowAll()\" class=\"expand\">Expand complete log</button>" + contents;
	}

	var consoleElement = document.getElementById('console');
	consoleElement.innerHTML = contents;

	var scrollHeight = Math.max(consoleElement.scrollHeight, consoleElement.clientHeight);
	consoleElement.scrollTop = scrollHeight - consoleElement.clientHeight;
}

Fix error in showing expand button when it didn't need it


'use strict';
// to allow mongodb host and port injection thanks
// to the EZMASTER_MONGODB_HOST_PORT environment parameter
// (docker uses it)
var mongoHostPort = process.env.EZMASTER_MONGODB_HOST_PORT ?
                    process.env.EZMASTER_MONGODB_HOST_PORT :
                    'localhost:27017';

var publicDomain = process.env.EZMASTER_PUBLIC_DOMAIN ?
                  process.env.EZMASTER_PUBLIC_DOMAIN :
                  null;

var publicIP = process.env.EZMASTER_PUBLIC_IP ?
                    process.env.EZMASTER_PUBLIC_IP :
                    '127.0.0.1';

module.exports = {
  connectionURI: 'mongodb://' + mongoHostPort + '/ezmaster',
  collectionName: 'data',
  publicDomain: publicDomain,
  publicIP: publicIP,
  browserifyModules : [
    'vue'
    , 'vue-resource'
    , 'components/addInstance'
    , 'components/table'
    , 'vue-validator'
    , 'heartbeats'
  ],
  rootURL : '/',
  routes: [
    'route.js',
    'status.js'
  ],
  middlewares: {
    '/*': 'reverseproxy.js'
  },

  filters: ['jbj-parse']
};

module.exports.package = require('./package.json');

'use strict';
// to allow mongodb host and port injection thanks
// to the EZMASTER_MONGODB_HOST_PORT environment parameter
// (docker uses it)
var mongoHostPort = process.env.EZMASTER_MONGODB_HOST_PORT ?
                    process.env.EZMASTER_MONGODB_HOST_PORT :
                    'localhost:27017';

var publicDomain = process.env.EZMASTER_PUBLIC_DOMAIN ?
                  process.env.EZMASTER_PUBLIC_DOMAIN :
                  null;
/*
var publicIP = process.env.EZMASTER_PUBLIC_IP ?
                    process.env.EZMASTER_PUBLIC_IP :
                    '127.0.0.1';
*/
var publicIP = process.env.EZMASTER_PUBLIC_IP || '127.0.0.1';

module.exports = {
  connectionURI: 'mongodb://' + mongoHostPort + '/ezmaster',
  collectionName: 'data',
  publicDomain: publicDomain,
  publicIP: publicIP,
  browserifyModules : [
    'vue'
    , 'vue-resource'
    , 'components/addInstance'
    , 'components/table'
    , 'vue-validator'
    , 'heartbeats'
  ],
  rootURL : '/',
  routes: [
    'route.js',
    'status.js'
  ],
  middlewares: {
    '/*': 'reverseproxy.js'
  },

  filters: ['jbj-parse']
};

module.exports.package = require('./package.json');

Add 127.0.0.1 default if Public IP not specified.


/* global cloak */

var form = document.querySelector('#input-form');
var input = document.querySelector('#input');
var messages = document.querySelector('#messages');
var counter = document.querySelector('#counter');

cloak.configure({
  messages: {
    chat: function(msg) {
      var message = document.createElement('div');
      message.innerText = msg;
      message.className = 'msg';
      messages.appendChild(message);
      messages.scrollTop = messages.scrollHeight;
    },
    userCount: function(count) {
      counter.innerText = count;
    }
  },
});

cloak.run('http://localhost:8090');

form.addEventListener('submit', function(e) {
  e.preventDefault();
  var msg = input.value;
  if (msg.length < 1) {
    return;
  }
  cloak.message('chat', msg);
  input.value = '';
});

/* global cloak */

var form = document.querySelector('#input-form');
var input = document.querySelector('#input');
var messages = document.querySelector('#messages');
var counter = document.querySelector('#counter');

cloak.configure({
  messages: {
    chat: function(msg) {
      var message = document.createElement('div');
      message.textContent = msg;
      message.className = 'msg';
      messages.appendChild(message);
      messages.scrollTop = messages.scrollHeight;
    },
    userCount: function(count) {
      counter.textContent = count;
    }
  },
});

cloak.run('http://localhost:8090');

form.addEventListener('submit', function(e) {
  e.preventDefault();
  var msg = input.value;
  if (msg.length < 1) {
    return;
  }
  cloak.message('chat', msg);
  input.value = '';
});

Switch .innerText DOM output to .textContent


module.exports = {
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "globals": {
        "__BROWSER__": true,
        "__dirname": true,
        "__x": true,
        "global": true,
        "module": true,
        "process": true,
        "require": true,
        "Buffer": true,
        "DocumentTouch": true
    },
    "parserOptions": {
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        },
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "eol-last": [
            "error",
            "always"
        ],
        "indent": [
            "error",
            4,
            {
                "SwitchCase": 1
            }
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "no-case-declarations": [
            0
        ],
        "no-empty": [
            "error",
            {
                "allowEmptyCatch": true
            }
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ]
    }
};

module.exports = {
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "globals": {
        "__BROWSER__": true,
        "__dirname": true,
        "__x": true,
        "global": true,
        "module": true,
        "process": true,
        "require": true,
        "Buffer": true,
        "DocumentTouch": true
    },
    "parserOptions": {
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        },
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "eol-last": [
            "error",
            "always"
        ],
        "indent": [
            "error",
            4,
            {
                "SwitchCase": 1
            }
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "no-case-declarations": [
            0
        ],
        "no-empty": [
            "error",
            {
                "allowEmptyCatch": true
            }
        ],
        "no-unused-vars": [
            "error",
            {
                "vars": "all",
                "args": "after-used",
                "ignoreRestSiblings": true
            }
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ]
    }
};

Adjust linting rules: allow desctructured unused vars


describe('pingPong', function() {
  it("is true for a number that is divisible by 3", function() {
    expect(pingPong(6)).to.equal(true);
  });

  it("is true for a number that is divisible by 5", function() {
    expect(pingPong(10)).to.equal(True)
  });

});

describe('pingPong', function() {
  it("is true for a number that is divisible by 3", function() {
    expect(pingPong(6)).to.equal(true);
  });

  it("is true for a number that is divisible by 5", function() {
    expect(pingPong(10)).to.equal(true);
  });

});

Fix syntax capitalization error in line 7


'use strict';

let gulp = require('gulp');
let requireDir = require('require-dir');
let gulpLoadPlugin = require('gulp-load-plugins');
let $ = gulpLoadPlugin();
let runSequence = require('run-sequence');
let config = require('./gulp/config');
let exec = require('child_process').exec;

requireDir('./gulp/task', {recurse: true});

gulp.task('watch', () => {
  $.watch(config.style.src, () => {
    gulp.start(['style', 'lint:style', 'minify:style']);
  });
  $.watch(config.script.src, () => {
    gulp.start(['script', 'lint:script']);
  });
});

gulp.task('serve', (cb) => {
  exec('./node_modules/.bin/harp server test --port 5000', (err, stdout, stderr) => {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
});

gulp.task('develop', ['watch', 'serve']);

gulp.task('default', () => {
  return runSequence(
    'lint',
    'style',
    'minify',
    'watch'
  );
});

'use strict';

let gulp = require('gulp');
let requireDir = require('require-dir');
let gulpLoadPlugin = require('gulp-load-plugins');
let $ = gulpLoadPlugin();
let runSequence = require('run-sequence');
let config = require('./gulp/config');
let exec = require('child_process').exec;

requireDir('./gulp/task', {recurse: true});

gulp.task('watch', () => {
  $.watch(config.style.src, () => {
    gulp.start(['style', 'lint:style', 'minify:style']);
  });
  $.watch(config.script.src, () => {
    gulp.start(['script', 'lint:script']);
  });
});

gulp.task('serve', (cb) => {
  exec('./node_modules/.bin/harp server test --port 3000', (err, stdout, stderr) => {
    console.log(stdout);
    console.log(stderr);
    cb(err);
  });
});

gulp.task('develop', ['watch', 'serve']);

gulp.task('default', () => {
  return runSequence(
    'lint',
    'style',
    'minify',
    'watch'
  );
});

Change a port number of harp


'use strict';

require('dotenv').load();

var pkg = require('./package.json'),
    path = require('path');

var gulp = require('gulp'),
    gutil = require('gulp-util'),
    inject = require('gulp-inject'),
    plumber = require('gulp-plumber'),
    pgbuild = require('gulp-phonegap-build'),
    bowerFiles = require('main-bower-files');

var dir = {
    out: path.join(__dirname, './out'),
    dist: path.join(__dirname, './dist'),
    assets: path.join(__dirname, './public'),
};

gulp.task('default', ['build'], function () {
    gutil.log('Building... This might take a while.');
});

gulp.task('build', function () {
    gulp.src(dir.assets + '/**')
        .pipe(plumber())
        .pipe(pgbuild({
            appId: process.env.PG_BUILD_APP_ID,
            download: {
                android: dir.out + '/onesquare-' + pkg.version + '.apk'
            },
            user: {
                token: process.env.PG_BUILD_AUTH_TOKEN
            }
        }));
});

gulp.task('inject', function () {
    var sources = [
        dir.assets + '/lib/**/*.html'
    ];

    return gulp.src(dir.assets + '/index.html')
        .pipe(inject(gulp.src(sources, {read: false})))
        .pipe(inject(gulp.src(bowerFiles(), {read: false}), {name: 'bower'}))
        .pipe(gulp.dest(dir.dist));
});

'use strict';

require('dotenv').load();

var pkg = require('./package.json'),
    path = require('path');

var gulp = require('gulp'),
    gutil = require('gulp-util'),
    inject = require('gulp-inject'),
    plumber = require('gulp-plumber'),
    pgbuild = require('gulp-phonegap-build'),
    bowerFiles = require('main-bower-files');

var dir = {
    out: path.join(__dirname, './out'),
    dist: path.join(__dirname, './dist'),
    assets: path.join(__dirname, './public'),
};

gulp.task('default', ['inject', 'build'], function () {
    gutil.log('Building... This might take a while.');
});

gulp.task('build', function () {
    gulp.src(dir.assets + '/**')
        .pipe(plumber())
        .pipe(pgbuild({
            appId: process.env.PG_BUILD_APP_ID,
            download: {
                android: dir.out + '/onesquare-' + pkg.version + '.apk'
            },
            user: {
                token: process.env.PG_BUILD_AUTH_TOKEN
            }
        }));
});

gulp.task('inject', function () {
    var sources = [
        dir.assets + '/lib/**/*.html'
    ];

    return gulp.src(dir.assets + '/index.html')
        .pipe(inject(gulp.src(sources, {read: false})))
        .pipe(inject(gulp.src(bowerFiles(), {read: false}), {name: 'bower'}))
        .pipe(gulp.dest(dir.dist));
});

Add Inject to default task.


import { expect } from 'chai';

import {
  launchKernel,
  shutdownKernel,
  forceShutdownKernel,
} from '../../../src/notebook/api/kernel';

describe('the circle of life', () => {
  it('is available for creating and destroying kernels', () => {
    const kernelPromise = launchKernel('python2');

    return kernelPromise.then(kernel => {
      expect(kernel.channels).to.not.be.undefined;
      return shutdownKernel(kernel).then(() => {
        expect(kernel.channels).to.be.undefined;
      });
    })
  });
  it('is available for creating and force shutting down kernels', () => {
    const kernelPromise = launchKernel('python2');

    return kernelPromise.then(kernel => {
      expect(kernel.channels).to.not.be.undefined;
      forceShutdownKernel(kernel);
      expect(kernel.channels).to.be.undefined;
    })
  });
});

import { expect } from 'chai';

import {
  launchKernel,
  shutdownKernel,
  forceShutdownKernel,
} from '../../../src/notebook/api/kernel';

import reducers from '../../../src/notebook/reducers';
import * as constants from '../../../src/notebook/constants';
import { AppRecord } from '../../../src/notebook/records';

describe('the circle of life', () => {
  it('is available for creating and destroying kernels', () => {
    const kernelPromise = launchKernel('python2');

    return kernelPromise.then(kernel => {
      expect(kernel.channels).to.not.be.undefined;
      return shutdownKernel(kernel).then(() => {
        expect(kernel.channels).to.be.undefined;
      });
    })
  });
  it('is available for creating and force shutting down kernels', () => {
    const kernelPromise = launchKernel('python2');

    return kernelPromise.then(kernel => {
      expect(kernel.channels).to.not.be.undefined;
      forceShutdownKernel(kernel);
      expect(kernel.channels).to.be.undefined;
    })
  });
  it('can be interrupted', () => {
    const kernelPromise = launchKernel('python2');

    return kernelPromise.then(kernel => {
      const originalState = {
        app: new AppRecord(kernel),
      };

      const action = {
        type: constants.INTERRUPT_KERNEL,
      };

      const state = reducers(originalState, action);
      expect(state.app.spawn.connected).to.be.false;
      shutdownKernel(kernel);
    });
  });
});

Add tests for kernel interrupt


import React, { PureComponent, PropTypes } from 'react'
import { Link } from 'react-router'
import { Menu, Button, Header, Icon } from 'semantic-ui-react'

class Navbar extends PureComponent {
  static propTypes = {
    currentUser: PropTypes.object,
    signedIn: PropTypes.bool.isRequired,
    signOut: PropTypes.func.isRequired
  }

  render() {
    const { signedIn, signOut } = this.props
    return(
      <Menu stackable borderless>
        <Menu.Item as={Link} to='/'><Header color='grey'>SlimPoll</Header></Menu.Item>
        { signedIn &&
          <Menu.Item>
            <Button basic animated='vertical' color='orange' as={Link} to='/create-poll'>
              <Button.Content visible>Create a poll</Button.Content>
              <Button.Content hidden>Easy as 1. 2. 3.</Button.Content>
            </Button>
          </Menu.Item>
        }
        <Menu.Menu position='right'>
          { signedIn &&
            <Menu.Item onClick={signOut}>Sign out</Menu.Item>
          }
          { !signedIn &&
            <Menu.Item as={Link} to='sign-in'>Sign in</Menu.Item>
          }
        </Menu.Menu>
      </Menu>
    )
  }
}

export default Navbar

import React, { PureComponent, PropTypes } from 'react'
import { Link } from 'react-router'
import { Menu, Button, Header, Dropdown } from 'semantic-ui-react'

class Navbar extends PureComponent {
  static propTypes = {
    currentUser: PropTypes.object,
    signedIn: PropTypes.bool.isRequired,
    signOut: PropTypes.func.isRequired
  }

  render() {
    const { signedIn, signOut, currentUser } = this.props
    return(
      <Menu stackable borderless>
        <Menu.Item as={Link} to='/'><Header color='grey'>SlimPoll</Header></Menu.Item>
        { signedIn &&
          <Menu.Item>
            <Button basic animated='vertical' color='orange' as={Link} to='/create-poll'>
              <Button.Content visible>Create a poll</Button.Content>
              <Button.Content hidden>Easy as 1. 2. 3.</Button.Content>
            </Button>
          </Menu.Item>
        }
        <Menu.Menu position='right'>
          { signedIn &&
            <Dropdown item closeOnBlur text={ currentUser.name }>
              <Dropdown.Menu>
                <Dropdown.Item as={Link} to='/create-poll'>Create a poll</Dropdown.Item>
                <Dropdown.Item>Your polls</Dropdown.Item>
                <Dropdown.Item>Polls you voted on</Dropdown.Item>
                <Dropdown.Divider />
                <Dropdown.Item onClick={signOut}>Sign out</Dropdown.Item>
              </Dropdown.Menu>
            </Dropdown>
          }
          { !signedIn &&
            <Menu.Item as={Link} to='sign-in'>Sign in</Menu.Item>
          }
        </Menu.Menu>
      </Menu>
    )
  }
}

export default Navbar

Add dropdown menu to navbar


const path = require('path');

// This config file is used to create a webpack bundle that we use on
// spec/performance-test.html to profile the performance footprint of the
// component.
module.exports = {
  entry: path.join(__dirname, 'spec/performance-test.jsx'),
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'performance-test.js'
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  },
  module: {
    rules: [
      {
        test: /\.jsx?/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
};

const path = require('path');

// This config file is used to create a webpack bundle that we use on
// spec/performance-test.html to profile the performance footprint of the
// component.
module.exports = {
  entry: path.join(__dirname, 'spec/performance-test.jsx'),
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'performance-test.js'
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.jsx?/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
};

Add source-map to perf-test webpack bundle


'use strict';

const stringUtils = require('ember-cli-string-utils');
const validComponentName = require('ember-cli-valid-component-name');
const normalizeEntityName = require('ember-cli-normalize-entity-name');

module.exports = {
  description: 'Generates a component. Name must contain a hyphen.',

  normalizeEntityName(entityName) {
    return validComponentName(normalizeEntityName(entityName));
  },

  fileMapTokens() {
    return {
      __name__(options) {
        return options.dasherizedModuleName;
      },
    };
  },

  locals(options) {
    let className = stringUtils.classify(options.entity.name);
    return { className };
  },
};

'use strict';

const stringUtils = require('ember-cli-string-utils');
const validComponentName = require('ember-cli-valid-component-name');
const normalizeEntityName = require('ember-cli-normalize-entity-name');

module.exports = {
  description: 'Generates a component. Name must contain a hyphen.',

  normalizeEntityName(entityName) {
    return validComponentName(normalizeEntityName(entityName));
  },

  fileMapTokens() {
    return {
      __name__(options) {
        return options.dasherizedModuleName;
      },
    };
  },

  locals(options) {
    let className = `${stringUtils.classify(options.entity.name)}Component`;
    return { className };
  },
};

Add "Component" suffix to generated class name


var config = {
  server_port: 5000,
  server_opts: {}
};

var server = require('http').Server();
var serverio = require('socket.io').listen(server, config.server_opts);
server.listen(config.server_port);

var players = [];

var red_cards = require('../data/red.json'),
  green_cards = require('../data/green.json');

serverio.of('/game').on('connection', function(socket) {
  // TODO: add socket

  socket.on('disconnect', function() {
    // TODO: remove socket
  });

  socket.on('authme', function() {

  });
});

var config = {
  server_port: 5000,
  server_opts: {}
};

var server = require('http').Server();
var serverio = require('socket.io').listen(server, config.server_opts);
server.listen(config.server_port);

var players = [];

var data = require('../data/apples.json');
var red = data.red,
  green = data.green;

serverio.of('/game').on('connection', function(socket) {
  // TODO: add socket

  socket.on('disconnect', function() {
    // TODO: remove socket
  });

  socket.on('authme', function() {

  });
});

Use json file in reading


'use strict';

const express = require('express');
const cors = require('cors');
const MockCtrl = require('../controllers/MockCtrl');

module.exports = function({Configuration, Logger, app}) {
  // Cross-domain management
  app.use(cors());

  // Index route
  app.use(express.static('./public'));

  // Mocks route
  app.use(function(req, res) {
    new MockCtrl({Configuration, Logger}).buildResponse(req, res);
  });
};

'use strict';

const express = require('express');
const cors = require('cors');
const path = require('path');
const MockCtrl = require('../controllers/MockCtrl');

module.exports = function({Configuration, Logger, app}) {
  // Cross-domain management
  app.use(cors());

  // Index route
  app.use(express.static(path.join(__dirname, '../../public')));

  // Mocks route
  app.use(function(req, res) {
    new MockCtrl({Configuration, Logger}).buildResponse(req, res);
  });
};

Fix public route not working correctly in some cases


var console = require('better-console');
var bot = require('./bot');
var async = require('async');

console.info("Loading configuration...");
require('./config').init(function(config) {

    console.info("Loading plugins...");
    var plugins = require('./plugins').init(config.get("plugins"));

    var supervisor = new bot(config, "Dobby", 3);

    supervisor.send_channel_message("testing one", function() {
        supervisor.send_channel_message("testing two", function() {
            supervisor.change_username("AlsoDobby", function() {
                supervisor.send_channel_message("testing THREE", function() {
                    console.log("done");
                })
            })
        })
    })
})

var console = require('better-console');
var bot = require('./bot');
var async = require('async');

console.info("Loading configuration...");
require('./config').init(function(config) {

    console.info("Loading plugins...");
    var plugins = require('./plugins').init(config.get("plugins"));

    var supervisor = new bot(config, config.get("bot.name"), 3);

    supervisor.send_channel_message("testing one", function() {
        supervisor.send_channel_message("testing two", function() {
            supervisor.change_username("AlsoDobby", function() {
                supervisor.send_channel_message("testing THREE", function() {
                    console.log("done");
                })
            })
        })
    })
})

Use supervisor name from config


import PubSub from "./event/pubSub.js";
import Events from "./event/events.js";

class Exporter {
	constructor() {
		PubSub.subscribe(Events.SAVE_REQUESTED, type => {
			let parser = this[type];

			if (typeof(parser) != "undefined") {
				PubSub.publish(Events.PARSE_AND_SAVE, parser);
			} else {
				console.error("Parser " + type + " was not found!");
			}
		});
	}

	dTileJSON(map) {
		return JSON.stringify(map, function(key, value) {
			if (key.indexOf("_") == 0) {
				return undefined;
			}
			return value;
		});
	}
}

export default (new Exporter());

import PubSub from "./event/pubSub.js";
import Events from "./event/events.js";

class Exporter {
	constructor() {
		PubSub.subscribe(Events.SAVE_REQUESTED, type => {
			let parser = this[type];

			if (typeof(parser) != "undefined") {
				PubSub.publish(Events.PARSE_AND_SAVE, parser);
			} else {
				console.error("Parser " + type + " was not found!");
			}
		});
	}

	/*
	 * Currently, this doesn't support multiple tilesets although that is planned
	 * for the future.
	 *
	 * You can view the file spec here:
	 * https://docs.google.com/document/d/1Ir7rhUxNcbRCfkdZw7rNOJyFTcdWUOmjvnYylzpN8O0
	 */
	dTileJSON(map) {
		let image = (() => {
			let image = map.tilesets[0].getTexture().baseTexture.source;
			console.log(image);

			let canvas = document.createElement('canvas');
	        canvas.width = image.naturalWidth;
			canvas.height = image.naturalHeight;

	        canvas.getContext('2d').drawImage(image, 0, 0);

	        return canvas.toDataURL('image/png').substring(22);
		})();

		let width = map.width,
			height = map.height,
			tileWidth = map.tileWidth,
			tileHeight = map.tileHeight,
			name = map.name;

		let tileLayers = [];
		for (let layer of map.tileLayers) {
			let tiles = [];
			for (let x = 0; x < layer.tiles.length; x++) {
				for (let y = 0; y < layer.tiles.length; y++) {
					tiles[y * width + x] = {
						id: layer.tiles[x][y].id
					};
				}
			}

			tileLayers.push({
				name: layer.name,
				tiles
			});
		}

		return JSON.stringify({
			image,
			width,
			height,
			tileWidth,
			tileHeight,
			tileLayers,
			name
		});
	}
}

export default (new Exporter());

Make export follow DTile JSON file spec


var config = require('./config');
var http = require('http');
var bot = require('./modules/bot.js');
var tg = require('./modules/telegram/telegramAPI');

tg.setupWebhook();

http.createServer(function (req, res) {
    var body = '';

    req.on('data', function(chunk) {
        body += chunk;
    });

    req.on('end', function() {
        if (req.url === config.BOT_WEBHOOK_PATH) {
            res.writeHead(200);
            try {
                body = JSON.parse(body);
            } catch (e) {
                console.log(`Bad JSON: ${e.message}`);
                return;
            };
            bot.verifyRequest(body);
        } else {
            res.writeHead(401);
        }
        res.end();
    });

}).listen(config.SERVER_PORT);

var config = require('./config');
var http = require('http');
var bot = require('./modules/bot.js');
var tg = require('./modules/telegram/telegramAPI');

tg.setupWebhook();

http.createServer(function (req, res) {
    var body = '';

    req.on('data', function(chunk) {
        body += chunk;
    });

    req.on('end', function() {
        if (req.url === config.BOT_WEBHOOK_PATH) {
            res.writeHead(200);
            try {
                body = JSON.parse(body);
            } catch (e) {
                console.log(`Bad JSON: ${e.message}`);
                return;
            };
            if (process.env.DEBUG === '1') {
                console.log(body);
            };
            bot.verifyRequest(body);
        } else {
            res.writeHead(401);
        }
        res.end();
    });

}).listen(config.SERVER_PORT);

Add debug for incoming messages


import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  creatorEmail: DS.attr('string'),
  createdAt: DS.attr('date'),
  snippets: DS.hasMany('snippet')
});

import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  creatorEmail: DS.attr('string'),
  createdAt: DS.attr('date'),
  complete: DS.attr('boolean'),
  snippets: DS.hasMany('snippet')
});

Mark each Story as complete or incomplete.


var fs = require('fs');
var path = require('path');
var VersionChecker = require('ember-cli-version-checker');

module.exports = function(blueprint) {
  blueprint.supportsAddon = function() {
    return false;
  };

  blueprint.filesPath = function() {
    var type;

    var dependencies = this.project.dependencies();
    if ('ember-qunit' in dependencies || 'ember-cli-qunit' in dependencies) {
      type = 'qunit';

    } else if ('ember-mocha' in dependencies) {
      type = 'mocha-0.12';

    } else if ('ember-cli-mocha' in dependencies) {
      var checker = new VersionChecker({ root: this.project.root });
      if (fs.existsSync(this.path + '/mocha-0.12-files') && checker.for('ember-cli-mocha', 'npm').satisfies('>=0.12.0')) {
        type = 'mocha-0.12';
      } else {
        type = 'mocha';
      }

    } else {
      this.ui.writeLine('Couldn\'t determine test style - using QUnit');
      type = 'qunit';
    }

    return path.join(this.path, type + '-files');
  };

  return blueprint;
};

var fs = require('fs');
var path = require('path');
var VersionChecker = require('ember-cli-version-checker');

module.exports = function(blueprint) {
  blueprint.supportsAddon = function() {
    return false;
  };

  blueprint.filesPath = function() {
    var type;

    var dependencies = this.project.dependencies();
    if ('ember-qunit' in dependencies) {
      type = 'qunit-rfc-232';

    } else if ('ember-cli-qunit' in dependencies) {
      let checker = new VersionChecker(this.project);
      if (fs.existsSync(this.path + '/qunit-rfc-232-files') && checker.for('ember-cli-qunit', 'npm').gte('4.1.1')) {
        type = 'qunit-rfc-232';
      } else {
        type = 'qunit';
      }

    } else if ('ember-mocha' in dependencies) {
      type = 'mocha-0.12';

    } else if ('ember-cli-mocha' in dependencies) {
      let checker = new VersionChecker(this.project);
      if (fs.existsSync(this.path + '/mocha-0.12-files') && checker.for('ember-cli-mocha', 'npm').satisfies('>=0.12.0')) {
        type = 'mocha-0.12';
      } else {
        type = 'mocha';
      }

    } else {
      this.ui.writeLine('Couldn\'t determine test style - using QUnit');
      type = 'qunit';
    }

    return path.join(this.path, type + '-files');
  };

  return blueprint;
};

Adjust test framework detector for RFC232 blueprints


/* eslint-env mocha */
const fastexp = require('../../../src').algorithms.math.fastexp;

const assert = require('assert');

describe('Fast Exponentiation', () => {
  it('should check for both numbers as 0', () => {
    assert.equal(fastexp(0, 10), 0);
    assert.equal(fastexp(10, 0), 1);
    assert.equal(fastexp(0, 0), undefined);
  });

  it('should solve for positive powers', () => {
    assert.equal(fastexp(10, 2), 100);
    assert.equal(fastexp(10, 3), 1000);
    assert.equal(fastexp(10, 7), 10000000);
  });
});

/* eslint-env mocha */
const fastexp = require('../../../src').algorithms.math.fastexp;

const assert = require('assert');

describe('Fast Exponentiation', () => {
  it('should check for both numbers as 0', () => {
    assert.equal(fastexp(0, 10), 0);
    assert.equal(fastexp(10, 0), 1);
    assert.equal(fastexp(0, 0), undefined);
  });

  it('should solve for positive powers', () => {
    assert.equal(fastexp(10, 2), 100);
    assert.equal(fastexp(10, 3), 1000);
    assert.equal(fastexp(10, 7), 10000000);
  });

  it('should solve for modular exponentiation', () => {
    assert.equal(fastexp(10, 2, 10), 0);
    assert.equal(fastexp(10, 3, 9), 1);
    assert.equal(fastexp(10, 7, 29), 17);
  });
});

Test Fast Exponentiation: Check for modular exponentiation


import Ember from 'ember';

export default Ember.Service.extend({
  store: Ember.inject.service(),
  feedModel: null,
  createOrFindFeedModel: function() {
    if (this.get('feedModel') == null) {
      var newFeedModel = this.get('store').createRecord('feed', { });
      this.set('feedModel', newFeedModel);
    }
    return this.get('feedModel');
  },
  downloadGtfsArchiveAndParseIntoFeedModel: function(retries) {
    retries = (retries || 60);
    if (retries-- <= 0) {
      throw "Timeout"
    }
    var feedModel = this.createOrFindFeedModel();
    var url = feedModel.get('url');
    var adapter = this.get('store').adapterFor('feeds');
    var fetch_info_url = adapter.urlPrefix()+'/feeds/fetch_info';
    var controller = this;
    var promise = adapter.ajax(fetch_info_url, 'post', {data:{url:url}});
    promise.then(function(response) {
      if (response.status == 'complete') {
        feedModel.set('id', response.feed.onestop_id);
        return response.operators.map(function(operator){feedModel.addOperator(operator)});
      } else if (response.status == 'processing') {
        return Ember.run.later(controller, function(){this.downloadGtfsArchiveAndParseIntoFeedModel(retries)}, 1000);
      }
    });
    return promise
  },
	toChangeset: function() {
    var feedModel = this.createOrFindFeedModel();
    var changes = [];
    changes.push({action:'createUpdate', feed:feedModel.toChange()})
    feedModel.get('operators').map(function(operator){
      changes.push({action:'createUpdate', operator:operator.toChange()})
    });
		var changeset = this.get('store').createRecord('changeset', {
			notes: 'This is a test. TODO put a custom message here.',
			payload: {
				changes: changes
			}
		});
		return changeset;
	}
});

import Ember from 'ember';

export default Ember.Service.extend({
  store: Ember.inject.service(),
  feedModel: null,
  createOrFindFeedModel: function() {
    if (this.get('feedModel') == null) {
      var newFeedModel = this.get('store').createRecord('feed', { });
      this.set('feedModel', newFeedModel);
    }
    return this.get('feedModel');
  },
	toChangeset: function() {
    var feedModel = this.createOrFindFeedModel();
    var changes = [];
    changes.push({action:'createUpdate', feed:feedModel.toChange()})
    feedModel.get('operators').map(function(operator){
      changes.push({action:'createUpdate', operator:operator.toChange()})
    });
		var changeset = this.get('store').createRecord('changeset', {
			notes: 'This is a test. TODO put a custom message here.',
			payload: {
				changes: changes
			}
		});
		return changeset;
	}
});

Move to fetchinfo route for now...


const chai = require('chai');
const React = require('react');
chai.should();

const Button = require('../src/button');
describe('Button', () => {
  it('should pass tests', () => {
    var button = <Button />;
    button.should.exist;
  });
});

const React = require('react');
import {createRenderer} from 'react-addons-test-utils';
import expect, { createSpy, spyOn, isSpy } from 'expect';

const Button = require('../src/button');

describe('Button', () => {
  it('can render an empty button', () => {
    const renderer = createRenderer();
    renderer.render(<Button />);
    const component = renderer.getRenderOutput();

    expect(component.type).toEqual('button');
  });
});

Add actual test for button component


module.exports = {
  extends: [
    'simplabs/configs/ember-qunit',
    'simplabs/plugins/qunit',
  ],
};

module.exports = {
  parserOptions: {
    sourceType: 'module',
    ecmaVersion: 2017
  },
  extends: [
    'simplabs/configs/ember-qunit',
    'simplabs/plugins/qunit',
  ],
};

Update `parserOptions.ecmaVersion` to allow async/await


/* jshint node: true */

'use strict';

function getTaskConfig(projectConfig) {
	var taskConfig = {
		images: [
			projectConfig.paths.src.images + '**/*.{png,jpg}',
			'!' + projectConfig.paths.src.images + 'responsive/**/*.{png,jpg}'
		],
		svgs: [
			projectConfig.paths.src.images + '**/*.svg'
		],
		responsiveImages: {
			src: [
				projectConfig.paths.src.images + 'responsive/**/*.{png,jpg}',
			],
			config: {
				//
				//https://github.com/mahnunchik/gulp-responsive
				//
				// "path/to/images": [
				// 	{
				// 		"width":   1020,
				// 		"height":  1020,
				// 		"quality": 60,
				// 		"rename": {
				// 			"suffix":  "-large"
				// 		},
				// 		"crop":    "center"
				// 	},
				// ]
			}
		},
		watch: [
			projectConfig.paths.src.images + '**/*.{png,jpg}',
			projectConfig.paths.src.images + '**/*.svg'
		]
	};

	return taskConfig;
}

module.exports = getTaskConfig;

/* jshint node: true */

'use strict';

function getTaskConfig(projectConfig) {
	var taskConfig = {
		images: [
			projectConfig.paths.src.images + '**/*.{png,jpg}',
			'!' + projectConfig.paths.src.images + 'responsive/**/*.{png,jpg}'
		],
		svgs: [
			projectConfig.paths.src.images + '**/*.svg'
		],
		watch: [
			projectConfig.paths.src.images + '**/*.{png,jpg}',
			projectConfig.paths.src.images + '**/*.svg'
		]
	};

	return taskConfig;
}

module.exports = getTaskConfig;

Remove responsiveImages object key from config file


var assert = require('assert'),
  SandboxedModule = require('sandboxed-module'),
  SandboxedModuleBlanket = require('../'),
  moduleUnderTest = 'fixture',
  blanket = require('blanket')({
    pattern: moduleUnderTest
  });

var fixture = SandboxedModule.load('./' + moduleUnderTest, {
  sourceTransformers: SandboxedModuleBlanket
}).exports;

var instrumentedSource = fixture.answer.toString();

assert(instrumentedSource.indexOf(global._blanket.getCovVar()) !== -1);
assert(instrumentedSource.indexOf(moduleUnderTest) !== -1);

var assert = require('assert'),
  SandboxedModule = require('sandboxed-module'),
  SandboxedModuleBlanket = require('../'),
  moduleUnderTest = 'fixture',
  blanket = require('blanket')({
    pattern: moduleUnderTest
  });

// Verify sandboxed-module does not provide blanket code coverage instrumentation on its own
var fixtureWithoutBlanket = SandboxedModule.load('./' + moduleUnderTest).exports;
var instrumentedSourceWithoutBlanket = fixtureWithoutBlanket.answer.toString();

assert(instrumentedSourceWithoutBlanket.indexOf(global._blanket.getCovVar()) === -1);
assert(instrumentedSourceWithoutBlanket.indexOf(moduleUnderTest) === -1);

// Verify sandboxed-module + sandboxed-module-blanket provides blanket code coverage instrumentation
var fixtureWithBlanket = SandboxedModule.load('./' + moduleUnderTest, {
  sourceTransformers: SandboxedModuleBlanket
}).exports;
var instrumentedSourceWithBlanket = fixtureWithBlanket.answer.toString();

assert(instrumentedSourceWithBlanket.indexOf(global._blanket.getCovVar()) !== -1);
assert(instrumentedSourceWithBlanket.indexOf(moduleUnderTest) !== -1);

Add test to verify blanket.js instrumentation is missing without this module


var React = require('react');
var classNames = require('classnames');

var FilterItem = React.createClass({
    propTypes: {
        selected:  React.PropTypes.bool,
        href:      React.PropTypes.string,
        className: React.PropTypes.string,
        count:     React.PropTypes.number,
        onClick:   React.PropTypes.func
    },

    getDefaultProps: function() {
        return {
            selected: false,
            href:   '#'
        };
    },

    onClick: function(e) {
        if (!this.props.onClick) {
            return;
        }

        e.preventDefault();
        this.props.onClick();
    },

    render: function() {
        var className = classNames('filter-item', this.props.className, {
            selected: this.props.selected
        });
        var count = this.props.count;
        var inner = '';

        if (typeof count !== 'undefined') {
            inner = {count};
        }

        return (

                {inner}
                {this.props.children}

        );
    }
});

var FilterList = React.createClass({
    render: function() {
        return (

                {this.props.children}

        );
    }
});

module.exports = FilterList;
module.exports.Item = FilterItem;

Fix classname for filter list


'use strict'

const { before } = require('sdk/test/utils')
const { prefs } = require('sdk/simple-prefs')

const backup = new Map()

for (let key in prefs) {
  backup.set(key, prefs[key])
}

function restorePrefs () {
  // console.log('Restoring simple-prefs')
  for (let [key, data] of backup) {
    prefs[key] = data
  }
}

exports.restorePrefs = restorePrefs

exports.isolateTestCases = (testCases) => {
  before(testCases, (name, assert) => {
    restorePrefs()
  })
}

'use strict'

const { before } = require('sdk/test/utils')
const { prefs } = require('sdk/simple-prefs')

const backup = new Map()

for (let key in prefs) {
  backup.set(key, prefs[key])
}

// set fake ports so that local gw
// does not slow down test suite
backup.set('customApiPort', 55001)
backup.set('customGatewayPort', 58080)

function restorePrefs () {
  // console.log('Restoring simple-prefs')
  for (let [key, data] of backup) {
    prefs[key] = data
  }
}

exports.restorePrefs = restorePrefs

exports.isolateTestCases = (testCases) => {
  before(testCases, (name, assert) => {
    restorePrefs()
  })
}

Use fake ports so that local gw is not used by tests


// Common utilities

// call `enter`(pathname) when visiting `path` (string orregex)
// call `exit`(pathname) when leaving `path`
const routes = [];
$(document).on('turbolinks:load', () => {
  const { pathname } = location;
  for (const route of routes) {
    if (route.pathisRe ? pathname.match(route.path) : route.path === pathname) {
      route.enter.call(null, pathname);
      route.current = true;
    } else if (route.current) {
      route.current = false;
      route.exit.call(null, pathname);
    }
  }
});

export function route(path, enter, exit = () => {}) {
  if (!path || !enter) {
    throw new Error('Expecting at least two arguments to utils.route(), got: ' + JSON.stringify(arguments));
  }
  routes.push({
    path,
    pathisRe: path instanceof RegExp,
    enter,
    exit,
    current: false
  });
}

export function onLoad(cb) {
  $(document).on('turbolinks:load', cb);
}

import createDebug from 'debug';

const debug = createDebug('ms:util');
// Common utilities

// call `enter`(pathname) when visiting `path` (string orregex)
// call `exit`(pathname) when leaving `path`
const routes = [];
$(document).on('turbolinks:load', () => {
  const { pathname } = location;
  for (const route of routes) {
    if (route.pathisRe ? pathname.match(route.path) : route.path === pathname) {
      route.enter.call(null, pathname);
      route.current = true;
    } else if (route.current) {
      route.current = false;
      route.exit.call(null, pathname);
    }
  }
});

$(window).on('beforeunload', () => {
  debug('onbeforeunload');
  const route = routes.find(route => route.current) || { exit: () => {} };
  route.current = false;
  route.exit.call(null);
});

export function route(path, enter, exit = () => {}) {
  if (!path || !enter) {
    throw new Error('Expecting at least two arguments to utils.route(), got: ' + JSON.stringify(arguments));
  }
  routes.push({
    path,
    pathisRe: path instanceof RegExp,
    enter,
    exit,
    current: false
  });
}

export function onLoad(cb) {
  $(document).on('turbolinks:load', cb);
}

Call route exit code when leaving the page


define(function(require) {
	var registerSuite = require('intern!object'),
		assert = require('intern/chai!assert'),
		el;

	require('js/tests/support/exports.js');

	registerSuite({
		name: 'Screen',

		setup: function() {
			el = document.createElement('style');
			el.textContent = 'html {font-family: "5"}';

			document.head.appendChild(el);
		},

		teardown: function() {
			document.head.removeChild(el);
		},

		size: function() {
			assert.strictEqual(Wee.screen.size(), 5,
				'Screen size should return 5'
			);
		},

		map: function() {
			// TODO: Complete
			assert.strictEqual(Wee.screen.map({
					size: 1,
					callback: function() {}
				}), undefined,
				'Single event was not mapped successfully'
			);

			assert.strictEqual(Wee.screen.map([{
					size: 1,
					callback: function() {}
				}, {
					size: 2,
					callback: function() {}
				}]), undefined,
				'Multiple events were not mapped successfully'
			);
		}
	});
});

define(function(require) {
	var registerSuite = require('intern!object'),
		assert = require('intern/chai!assert'),
		el;

	require('js/tests/support/exports.js');

	registerSuite({
		name: 'Screen',

		setup: function() {
			el = document.createElement('style');
			el.textContent = 'html {font-family: "5"}';

			document.head.appendChild(el);
		},

		teardown: function() {
			document.head.removeChild(el);
		},

		size: function() {
			assert.strictEqual(Wee.screen.size(), 5,
				'Screen size should return 5'
			);
		},

		map: function() {
			// TODO: Complete
			assert.strictEqual(Wee.screen.map({
					size: 1,
					callback: function() {}
				}), undefined,
				'Single event was not mapped successfully'
			);

			assert.strictEqual(Wee.screen.map([{
					size: 1,
					callback: function() {}
				}, {
					size: 2,
					callback: function() {}
				}]), undefined,
				'Multiple events were not mapped successfully'
			);
		},

		run: function() {
			assert.isFunction(Wee.screen.run, true,
				'"run" did not evaluate as a function'
			);
		}
	});
});

Add test for ‘run’ method


// Wait for the first part of the page to load
$(document).ready(function () {

    // Wait for the rest of the fancy dynamic stuff to load.
    // 5 seconds should be enough.
    setTimeout(function () {

        // Get a handle on the calendar grid
        $('div[role="grid"]').on('mousewheel', function (e) {

            // Scrolling.... hahhahahaha I don't think so
            if (e.target.id == 'el') return;
            e.preventDefault();
            e.stopPropagation();
        });
    }, 5000);
});

var calendar_grid = document.querySelector('div[role="grid"]');
var body = document.querySelector('body');

var disable_scroll = function () {
// Get a handle on the calendar grid
    $('div[role="grid"]').on('mousewheel', function (e) {

        // Scrolling.... hahhahahaha I don't think so
        if (e.target.id == 'el') return;
        e.preventDefault();
        e.stopPropagation();
    });
};

var mutation_breaks_scroll_blocker = function (mutation) {
    if (mutation.attributeName && mutation.attributeName == 'data-viewfamily') {
        if ($('body').attr('data-viewfamily') == 'EVENT')
            return true;
    }
};

var calendar_observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if (mutation_breaks_scroll_blocker(mutation)) {
            disable_scroll();
        }
    });
});

// Wait for the first part of the page to load
$(document).ready(function () {
    if (!calendar_grid) {
        window.setTimeout(observe_if_calendar_available, 500);
        return;
    }

    // start observer
    calendar_observer.observe(body, {attributes: true});
});

Add use of MutationObserver to fix issues with new Google Calendar UI


var assert = require('assert');
var fs = require('fs');
var procrastinate = require('..');

describe('Procrastinate', function () {
  context('with an empty input', function () {
    it('returns an empty string', function () {
      assert.equal(procrastinate(''), '');
    });
  });

  context('with an input representing pending Mocha specs', function () {
    var fileOptions = { encoding: 'utf-8' };
    var input  = fs.readFileSync('test/data/input.txt', fileOptions);
    var output = fs.readFileSync('test/data/output.js', fileOptions);

    it('returns pending Mocha specs', function () {
      assert.equal(procrastinate(input), output);
    });
  });
});

var assert = require('assert');
var fs = require('fs');
var procrastinate = require('..');

xdescribe('Procrastinate', function () {
  context('with an empty input', function () {
    it('returns an empty string', function () {
      assert.equal(procrastinate(''), '');
    });
  });

  context('with an input representing pending Mocha specs', function () {
    var fileOptions = { encoding: 'utf-8' };
    var input  = fs.readFileSync('test/data/input.txt', fileOptions);
    var output = fs.readFileSync('test/data/output.js', fileOptions);

    it('returns pending Mocha specs', function () {
      assert.equal(procrastinate(input), output);
    });
  });
});

Mark all specs as pending


var hexagon = require('hexagon-js')

var version = require('../package.json').devDependencies['hexagon-js']

hexagon.light.build({
  dest: 'content/resources/hexagon/' + version,
  embedAssets: true
})

hexagon.light.build({
  dest: 'content/resources/hexagon/latest',
  embedAssets: true
})

hexagon.light.build({
  dest: 'content/resources/hexagon/docs',
  prefix: 'dx',
  embedAssets: true
})

var hexagon = require('hexagon-js')

var version = require('../package.json').devDependencies['hexagon-js']

hexagon.light.build({
  dest: 'resources/hexagon/' + version,
  embedAssets: true
})

hexagon.light.build({
  dest: 'resources/hexagon/latest',
  embedAssets: true
})

hexagon.light.build({
  dest: 'resources/hexagon/docs',
  prefix: 'dx',
  embedAssets: true
})

Update the hexagon builder script to reflect folder changes


import Ember from 'ember';

export default Ember.Route.extend({
  createFeedFromGtfsService: Ember.inject.service('create-feed-from-gtfs'),
  beforeModel: function(transition) {
    var controller = this;
    var feedModel = this.get('createFeedFromGtfsService').feedModel;
    var url = feedModel.get('url');
    var adapter = this.get('store').adapterFor('feeds');
    var fetch_info_url = adapter.urlPrefix()+'/feeds/fetch_info';
    var promise = adapter.ajax(fetch_info_url, 'post', {data:{url:url}});
    promise.then(function(response) {
      if (response.status == 'complete') {
        feedModel.set('id', response.feed.onestop_id);
        response.operators.map(function(operator){feedModel.addOperator(operator)});
        return feedModel;
      } else if (response.status == 'processing') {
        transition.abort();
        return Ember.run.later(controller, function(){
          transition.retry();
        }, 1000);
      }
    }).catch(function() {
      feedModel.get('errors').add('url','The feed URL is invalid. Please check the link and try again.');
    });
    return promise
  },
  model: function() {
    return this.get('createFeedFromGtfsService').feedModel;
  }
});

import Ember from 'ember';

export default Ember.Route.extend({
  createFeedFromGtfsService: Ember.inject.service('create-feed-from-gtfs'),
  beforeModel: function(transition) {
    var controller = this;
    var feedModel = this.get('createFeedFromGtfsService').feedModel;
    var url = feedModel.get('url');
    var adapter = this.get('store').adapterFor('feeds');
    var fetch_info_url = adapter.urlPrefix()+'/feeds/fetch_info';
    var promise = adapter.ajax(fetch_info_url, 'post', {data:{url:url}});
    promise.then(function(response) {
      if (response.status == 'complete') {
        feedModel.set('id', response.feed.onestop_id);
        feedModel.set('operators_in_feed', response.feed.operators_in_feed);
        response.operators.map(function(operator){feedModel.addOperator(operator)});
        return feedModel;
      } else if (response.status == 'processing') {
        transition.abort();
        return Ember.run.later(controller, function(){
          transition.retry();
        }, 1000);
      }
    }).catch(function() {
      feedModel.get('errors').add('url','The feed URL is invalid. Please check the link and try again.');
    });
    return promise
  },
  model: function() {
    return this.get('createFeedFromGtfsService').feedModel;
  }
});

Add back operators_in_feed from feedinfo response


'use strict';

import NearestNeighborIndexProcessor from './NearestNeighborIndexProcessor';

class FixationsDataProcessor {

    constructor() {
        this.fixationsData = [];
        this.nniProcessor = new NearestNeighborIndexProcessor();
    }

    getScreenResolution(fileContentsAsArray)
    {
        let firstLine = fileContentsAsArray[0];
        let resolution = firstLine.split(' ');

        return {
            width: parseInt(resolution[0]),
            height: parseInt(resolution[1])
        }
    }

    process(fileContentsAsArray)
    {
        fileContentsAsArray.splice(0, 1);

        for(let c in fileContentsAsArray) {
            let lineItems = fileContentsAsArray[c].split(' ');

            this.addItemToMinute([lineItems[1], lineItems[2]], this.getMinuteFromTimestamp(parseInt(lineItems[0])));
        }

        for(let i in this.fixationsData) {
            this.fixationsData[i]['nni'] = this.nniProcessor.calculate(this.fixationsData[i].points);
        }

        return this.fixationsData;
    }

    addItemToMinute(point, minute)
    {
        if(this.fixationsData[minute] === undefined) {
            this.fixationsData[minute] = {
                points: [point]
            }
        } else {
            this.fixationsData[minute].points.push(point);
        }
    }

    getMinuteFromTimestamp(timestamp) {
        let minute = Math.floor(timestamp * 1.67 * 0.00001);
        return minute;
    }

}

export default FixationsDataProcessor

'use strict';

import NearestNeighborIndexProcessor from './NearestNeighborIndexProcessor';

class FixationsDataProcessor {

    constructor() {
        this.fixationsData = [];
        this.nniProcessor = new NearestNeighborIndexProcessor();
    }

    getScreenResolution(fileContentsAsArray)
    {
        let firstLine = fileContentsAsArray[0];
        let resolution = firstLine.split(' ');

        return {
            width: parseInt(resolution[0]),
            height: parseInt(resolution[1])
        }
    }

    process(fileContentsAsArray)
    {
        fileContentsAsArray.splice(0, 1);

        for(let c in fileContentsAsArray) {
            let lineItems = fileContentsAsArray[c].split(' ');

            this.addItemToMinute([lineItems[1], lineItems[2]], this.getMinuteFromTimestamp(parseInt(lineItems[0])));
        }

        for(let i in this.fixationsData) {
            this.fixationsData[i]['nni'] = this.nniProcessor.calculate(this.fixationsData[i].points);
        }

        return this.fixationsData;
    }

    addItemToMinute(point, minute)
    {
        if(this.fixationsData[minute] === undefined) {
            this.fixationsData[minute] = {
                points: [point]
            }
        } else {
            this.fixationsData[minute].points.push(point);
        }
    }

    getMinuteFromTimestamp(timestamp) {
        let minute = Math.floor(timestamp / 1000 / 60);
        return minute;
    }

}

export default FixationsDataProcessor

Fix formula as requested in review


import AWS from 'aws-sdk'
import InputValidator from './InputValidator'
import EmailFormatter from './EmailFormatter'

AWS.config.region = 'us-east-1'

exports.handler = function(event, context) {
  // Validate Inputs
  let errors = InputValidator.validate(event)

  if (errors) {
    context.fail(errors)
    return
  }

  // TODO Add task to SQS and invoke ECS container

  // Send Email
  let {emailText, subject} = EmailFormatter.format(event),
    ses = new AWS.SES()

  ses.sendEmail({
    Source: 'contact@moocfetcher.com',
    Destination: {
      ToAddresses: [event.email]
    },
    Message: {
      Subject: {
        Data: subject,
        Charset: 'UTF-8'
      },
      Body: {
        Text: {
          Data: emailText,
          Charset: 'UTF-8'
        }
      }
    }
  }, function(err) {
    if (err) {
      context.fail(err)
      return
    }
    context.succeed()
  })
}

import AWS from 'aws-sdk'
import InputValidator from './InputValidator'
import EmailFormatter from './EmailFormatter'

AWS.config.region = 'us-east-1'

exports.handler = function(event, context) {
  // Validate Inputs
  let errors = InputValidator.validate(event)

  if (errors) {
    context.fail(errors)
    return
  }

  // TODO Add task to SQS and invoke ECS container

  // Send Email
  let {emailText, subject} = EmailFormatter.format(event),
    ses = new AWS.SES()

  ses.sendEmail({
    Source: 'MOOC Fetcher Support <contact@moocfetcher.com>',
    Destination: {
      ToAddresses: [event.email]
    },
    Message: {
      Subject: {
        Data: subject,
        Charset: 'UTF-8'
      },
      Body: {
        Text: {
          Data: emailText,
          Charset: 'UTF-8'
        }
      }
    }
  }, function(err) {
    if (err) {
      context.fail(err)
      return
    }
    context.succeed()
  })
}

Add friendly name to email-address


// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

$(document).on("ajax:success", "form", function(status, data, xhr){
  if (data.length > 1) {
    $('.message0').text("All your plants have been watered!");
    $('.message0').show().delay(1000).fadeOut();
  }
  else {
    $('.message' + data.plant_id).text("Plant Watered!");
    $('.message' + data.plant_id).show().delay(1000).fadeOut();
  }
});

// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// Handles the response from the server and creates confirmation message in DOM if success

$(document).on("ajax:success", "form", function(status, data, xhr){
  if (data.length > 1) {
    $('.message0').text("All your plants have been watered!");
    $('.message0').show().delay(1000).fadeOut();
  }
  else {
    $('.message' + data.plant_id).text("Plant Watered!");
    $('.message' + data.plant_id).show().delay(1000).fadeOut();
  }
});

Add comments to water events script


var _gutil = require('gulp-util');
var _gplumber = require('gulp-plumber');

var PrettyError = (function(customErrorFormat){

    // custom error format function provided ?
    if (typeof customErrorFormat != 'undefined'){

        // proxy
        return _gplumber(function(error){
            customErrorFormat.apply(this, [error, _gutil]);
        });

    }else{
        // default appearance
        return _gplumber(function(error){
            // add indentation
            var msg = error.codeFrame.replace(/\n/g, '\n    ');

            _gutil.log('|- ' + _gutil.colors.bgRed.bold('Build Error in ' + error.plugin));
            _gutil.log('|- ' + _gutil.colors.bgRed.bold(error.message));
            _gutil.log('|- ' + _gutil.colors.bgRed('>>>'));
            _gutil.log('|\n    ' + msg + '\n           |');
            _gutil.log('|- ' + _gutil.colors.bgRed('<<<'));
        });
    }
});

module.exports = PrettyError;

var _gutil = require('gulp-util');
var _gplumber = require('gulp-plumber');

var PrettyError = (function(customErrorFormat){

    // custom error format function provided ?
    if (typeof customErrorFormat != 'undefined'){

        // proxy
        return _gplumber(function(error){
            customErrorFormat.apply(this, [error, _gutil]);
        });

    }else{
        // default appearance
        return _gplumber(function(error){
            // log the error message
            _gutil.log('|- ' + _gutil.colors.bgRed.bold('Build Error in ' + error.plugin));
            _gutil.log('|- ' + _gutil.colors.bgRed.bold(error.message));

            // make sure there is codeFrame in the error object.
            // gulp-less does not have it, so it will throw an error
            if(error.codeFrame != 'undefined') {
                // add indentation
                var msg = error.codeFrame.replace(/\n/g, '\n    ');

                _gutil.log('|- ' + _gutil.colors.bgRed('>>>'));
                _gutil.log('|\n    ' + msg + '\n           |');
                _gutil.log('|- ' + _gutil.colors.bgRed('<<<'));
            }
        });
    }
});

module.exports = PrettyError;

Make sure it will not break with gulp-less


// alert.js

var striped_lines_box = document.createElement('div');
striped_lines_box.style.cssText = "background-image: url(https://i.imgur.com/m5Wjo7h.png); height: 100%;width: 100%;position: fixed; z-index: 99999; opacity: 0.7;";

var inner_text = document.createElement('span');
inner_text.textContent = "Този сайт разпространява подвеждаща или невярна информация!";
inner_text.style.cssText = "font-family: 'Lucida Console', Monaco, monospace; color: white; margin: auto; background: black; display: block; height: 120px; width: 500px; text-align: center; font-size: 24px;";

striped_lines_box.appendChild(inner_text);

document.body.insertBefore(striped_lines_box, document.body.firstChild);

// alert.js

var striped_lines_box = document.createElement('div');
striped_lines_box.style.cssText = "background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAwElEQVRYhcXYwQ3AIAwDwNBVuv9IzNJ+kKoiWkhiG74EnfjZKfWy8DlL/O2xRa1XFE6qFvtxXo3AENUNo1QfDFQdMFZdheHqEsxQ5zBJncA89Q+mqp8wWx3DAnUAa9QelqkvWKk+sFhtsF41s4SZUFNwMiUG4Xw2jcCQROyGUTncBwPTvwPGdo5VGN50lmBGv5rDpFY3gXld8u+S2mA/79m9eTwiaOuDKc2OoB+UbSZes8p9yDMu3sKULWqDt2ycbuQWUBgMCXBoAAAAAElFTkSuQmCC); height: 100%;width: 100%;position: fixed; z-index: 99999; opacity: 0.7;";

var inner_text = document.createElement('span');
inner_text.textContent = "Този сайт разпространява подвеждаща или невярна информация!";
inner_text.style.cssText = "font-family: 'Lucida Console', Monaco, monospace; color: white; margin: auto; background: black; display: block; height: 120px; width: 500px; text-align: center; font-size: 24px;";

striped_lines_box.appendChild(inner_text);

document.body.insertBefore(striped_lines_box, document.body.firstChild);

Include block image as base64 in the js.