/*!
 * global-prefix <https://github.com/jonschlinkert/global-prefix>
 *
 * Copyright (c) 2015 Jon Schlinkert.
 * Licensed under the MIT license.
 */

'use strict';

/**
 * This is the code used internally by npm to
 * resolve the global prefix.
 */

var isWindows = require('is-windows');
var path = require('path');
var prefix;

if (process.env.PREFIX) {
  prefix = process.env.PREFIX;
} else if (isWindows()) {
  // c:\node\node.exe --> prefix=c:\node\
  prefix = path.dirname(process.execPath);
} else {
  // /usr/local/bin/node --> prefix=/usr/local
  prefix = path.dirname(path.dirname(process.execPath));

  // destdir only is respected on Unix
  if (process.env.DESTDIR) {
    prefix = path.join(process.env.DESTDIR, prefix);
  }
}

module.exports = prefix;

/*!
 * global-prefix <https://github.com/jonschlinkert/global-prefix>
 *
 * Copyright (c) 2015 Jon Schlinkert.
 * Licensed under the MIT license.
 */

'use strict';

/**
 * This is the code used internally by npm to
 * resolve the global prefix.
 */

var isWindows = require('is-windows');
var path = require('path');
var prefix;

if (process.env.PREFIX) {
  prefix = process.env.PREFIX;
} else if (isWindows === true || isWindows()) {
  // c:\node\node.exe --> prefix=c:\node\
  prefix = path.dirname(process.execPath);
} else {
  // /usr/local/bin/node --> prefix=/usr/local
  prefix = path.dirname(path.dirname(process.execPath));

  // destdir only is respected on Unix
  if (process.env.DESTDIR) {
    prefix = path.join(process.env.DESTDIR, prefix);
  }
}

module.exports = prefix;

Fix "isWindows is not a function" on electron


/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-l10n',

  included: function(app) {
    // @see: https://github.com/ember-cli/ember-cli/issues/3718
    this._super.included.apply(this, arguments);
    if (typeof app.import!=='function' && app.app) {
      app = app.app;
    }

    app.import('bower_components/gettext.js/dist/gettext.min.js',{
      exports: {
        'get-text': [
          'default'
        ]
      }
    });
  }
};

/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-l10n',

  isDevelopingAddon: function() {
    // @see: https://ember-cli.com/extending/#link-to-addon-while-developing
    return true;
  },

  included: function(app) {
    // @see: https://github.com/ember-cli/ember-cli/issues/3718
    this._super.included.apply(this, arguments);
    if (typeof app.import!=='function' && app.app) {
      app = app.app;
    }

    app.import('bower_components/gettext.js/dist/gettext.min.js',{
      exports: {
        'get-text': [
          'default'
        ]
      }
    });
  }
};

Add development mode for live-reload on develop


/**
 * index.js
 * Client entry point.
 *
 * @author Francis Brito <fr.br94@gmail.com>
 * @license MIT
 */
'use strict';

/**
 * Client
 * Provides methods to access PrintHouse's API.
 *
 * @param {String} apiKey   API key identifying account owner.
 * @param {Object} opts     Contains options to be passed to the client
 */
var Client = function Client(apiKey, opts) {
    opts = opts || {};

    if (!apiKey) {
        throw new Error('No API key provided.');
    }

    this._apiUrl = opts.apiUrl || Client.DEFAULT_API_URL;
};

/**
 * Default API endpoint.
 * Used if no API endpoint is passed in opts parameter when constructing a client.
 *
 * @type {String}
 */
Client.DEFAULT_API_URL = 'http://printhouse.io/api';

module.exports = {
    Client: Client
};

/**
 * index.js
 * Client entry point.
 *
 * @author Francis Brito <fr.br94@gmail.com>
 * @license MIT
 */
'use strict';

/**
 * Client
 * Provides methods to access PrintHouse's API.
 *
 * @param {String} apiKey   API key identifying account owner.
 * @param {Object} opts     Contains options to be passed to the client
 */
var Client = function Client(apiKey, opts) {
    opts = opts || {};

    if (!apiKey) {
        throw new Error('No API key provided.');
    }

    this._apiUrl = opts.apiUrl || Client.DEFAULT_API_URL;
};

/**
 * Default API endpoint.
 * Used if no API endpoint is passed in opts parameter when constructing a client.
 *
 * @type {String}
 */
Client.DEFAULT_API_URL = 'http://printhouse.io/api';

/**
 * Default API version.
 * Used if no API version is passed in opts parameter when constructing a client.
 *
 * @type {String}
 */
Client.DEFAULT_API_VERSION = 1;

module.exports = {
    Client: Client
};

Add default API version to client.


/* @flow */

type GiftCategory =
  'Electronics' |
  'Games' |
  'Home Decoration';

type GiftWish = string | Array<GiftCategory>;

type User = {
  firstName: string,
  lastName?: string,
  giftWish?: GiftWish,
};

export type Selection = {
  giver: User,
  receiver: User,
};

const selectSecretSanta = (
  users: Array<User>,
  // TODO: Think of adding restrictions as a parameter, such as:
  // - User X cannot give to User Y
  // - User A must give to User B
): Array<Selection> => {
  const givers = [...users];

  return users.map((receiver) => {
    // Possible givers only for this receiver (receiver cannot be his own giver)
    const receiverGivers = givers.filter(g => g !== receiver);

    const giver = receiverGiver[
      Math.floor(Math.random() * receiverGiver.length)
    ];

    // Take the selected giver out of the givers array
    givers.splice(givers.findIndex(g => g === giver), 1);

    return {
      giver,
      receiver,
    };
  });
};

export default selectSecretSanta;

/* @flow */

type GiftCategory =
  'Electronics' |
  'Games' |
  'Home Decoration';

type GiftWish = string | Array<GiftCategory>;

type User = {
  firstName: string,
  lastName?: string,
  giftWish?: GiftWish,
};

export type Selection = {
  giver: User,
  receiver: User,
};

const selectSecretSanta = (
  users: Array<User>,
  // TODO: Think of adding restrictions as a parameter, such as:
  // - User X cannot give to User Y
  // - User A must give to User B
  // - User N only receives, doesn't give
): Array<Selection> => {
  const givers = [...users];

  return users.map((receiver) => {
    // Possible givers only for this receiver (receiver cannot be his own giver)
    const receiverGivers = givers.filter(g => g !== receiver);

    const giver = receiverGivers[
      Math.floor(Math.random() * receiverGivers.length)
    ];

    // Take the selected giver out of the givers array
    givers.splice(givers.findIndex(g => g === giver), 1);

    return {
      giver,
      receiver,
    };
  });
};

export default selectSecretSanta;

Fix variable name, add comment


var fs = require('fs');
module.exports = function (repl, file) {

  try {
    var stat = fs.statSync(file);
    repl.rli.history = fs.readFileSync(file, 'utf-8').split('\n').reverse();
    repl.rli.history.shift();
    repl.rli.historyIndex = 0;
  } catch (e) {}

  var fd = fs.openSync(file, 'a'), reval = repl.eval;

  repl.rli.addListener('line', function(code) {
    if (code && code !== '.history') {
      fs.write(fd, code + '\n');
    } else {
      repl.rli.historyIndex++;
      repl.rli.history.pop();
    }
  });

  process.on('exit', function() {
    fs.closeSync(fd);
  });

  repl.commands['.history'] = {
    help : 'Show the history',
    action : function() {
      var out = [];
      repl.rli.history.forEach(function(v, k) {
        out.push(v);
      });
      repl.outputStream.write(out.reverse().join('\n') + '\n');
      repl.displayPrompt();
    }
  };
};

var fs = require('fs');
module.exports = function (repl, file) {

  try {
    var stat = fs.statSync(file);
    repl.rli.history = fs.readFileSync(file, 'utf-8').split('\n').reverse();
    repl.rli.history.shift();
    repl.rli.historyIndex = -1; // will be incremented before pop
  } catch (e) {}

  var fd = fs.openSync(file, 'a'), reval = repl.eval;

  repl.rli.addListener('line', function(code) {
    if (code && code !== '.history') {
      fs.write(fd, code + '\n');
    } else {
      repl.rli.historyIndex++;
      repl.rli.history.pop();
    }
  });

  process.on('exit', function() {
    fs.closeSync(fd);
  });

  repl.commands['.history'] = {
    help : 'Show the history',
    action : function() {
      var out = [];
      repl.rli.history.forEach(function(v, k) {
        out.push(v);
      });
      repl.outputStream.write(out.reverse().join('\n') + '\n');
      repl.displayPrompt();
    }
  };
};

Fix historyIndex to actually get the last command on key-up


/* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */

define(

  [
    './lib/advice',
    './lib/component',
    './lib/compose',
    './lib/logger',
    './lib/registry',
    './lib/utils'
  ],

  function(advice, component, compose, logger, registry, utils) {
    'use strict';

    return {
      advice: advice,
      component: component,
      compose: compose,
      logger: logger,
      registry: registry,
      utils: utils
    };

  }
);

/* Copyright 2013 Twitter, Inc. Licensed under The MIT License. http://opensource.org/licenses/MIT */

define(

  [
    './lib/advice',
    './lib/component',
    './lib/compose',
    './lib/debug',
    './lib/logger',
    './lib/registry',
    './lib/utils'
  ],

  function(advice, component, compose, debug, logger, registry, utils) {
    'use strict';

    return {
      advice: advice,
      component: component,
      compose: compose,
      debug: debug,
      logger: logger,
      registry: registry,
      utils: utils
    };

  }
);

Add the debug module to the entry file


/**
 * Generates a new {@link Point} feature, given coordinates
 * and, optionally, properties.
 *
 * @module turf/point
 * @param {number} longitude - position west to east in decimal degrees
 * @param {number} latitude - position south to north in decimal degrees
 * @param {Object} properties
 * @return {Point} output
 * @example
 * var pt1 = turf.point(-75.343, 39.984)
 */
module.exports = function(x, y, properties){
  if(x instanceof Array) {
  	properties = y;
  	y = x[1];
  	x = x[0];
  } else if(isNaN(x) || isNaN(y)) throw new Error('Invalid coordinates')
  return {
    type: "Feature",
    geometry: {
      type: "Point",
      coordinates: [x, y]
    },
    properties: properties || {}
  };
}

/**
 * Generates a new {@link Point} feature, given coordinates
 * and, optionally, properties.
 *
 * @module turf/point
 * @param {number} longitude - position west to east in decimal degrees
 * @param {number} latitude - position south to north in decimal degrees
 * @param {Object} properties
 * @return {Point} output
 * @example
 * var pt1 = turf.point(-75.343, 39.984);
 * //=pt1
 */
module.exports = function(x, y, properties){
  if(x instanceof Array) {
  	properties = y;
  	y = x[1];
  	x = x[0];
  } else if(isNaN(x) || isNaN(y)) throw new Error('Invalid coordinates')
  return {
    type: "Feature",
    geometry: {
      type: "Point",
      coordinates: [x, y]
    },
    properties: properties || {}
  };
}

Make example work with rpl


var elixir = require('laravel-elixir');
var gulp = require('gulp');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
var notify = require('gulp-notify');
var _ = require('underscore');
var utilities = require('laravel-elixir/ingredients/commands/Utilities');

/*
 |----------------------------------------------------------------
 | ImageMin Processor
 |----------------------------------------------------------------
 |
 | This task will trigger your images to be processed using
 | imagemin processor.
 |
 | Minify PNG, JPEG, GIF and SVG images
 |
 */

elixir.extend('imagemin', function(src, output, options) {

    var config = this;

    var baseDir = config.assetsDir + 'img';

    src = utilities.buildGulpSrc(src, baseDir, '**/*');

    options = _.extend({
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant()]
    }, options);

    gulp.task('imagemin', function() {
        return gulp.src(src)
            .pipe(imagemin(options))
            .pipe(gulp.dest(output || 'public/img'))
            .pipe(notify({
                title: 'ImageMin Complete!',
                message: 'All images have be optimised.',
                icon: __dirname + '/../laravel-elixir/icons/pass.png'
            }));
    });

    this.registerWatcher('imagemin', [
        baseDir + '/**/*.png',
        baseDir + '/**/*.gif',
        baseDir + '/**/*.svg',
        baseDir + '/**/*.jpg',
        baseDir + '/**/*.jpeg'
    ]);

    return this.queueTask('imagemin');

});

var elixir = require('laravel-elixir');
var gulp = require('gulp');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
var notify = require('gulp-notify');
var _ = require('underscore');
var utilities = require('laravel-elixir/ingredients/commands/Utilities');

/*
 |----------------------------------------------------------------
 | ImageMin Processor
 |----------------------------------------------------------------
 |
 | This task will trigger your images to be processed using
 | imagemin processor.
 |
 | Minify PNG, JPEG, GIF and SVG images
 |
 */

elixir.extend('imagemin', function(src, output, options) {

    var config = this;

    var baseDir = config.assetsDir + 'img';

    src = utilities.buildGulpSrc(src, baseDir, '**/*');

    options = _.extend({
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant()]
    }, options);

    gulp.task('imagemin', function() {
        return gulp.src(src)
            .pipe(imagemin(options))
            .pipe(gulp.dest(output || 'public/img'))
            .on('error', notify.onError({
                title: 'ImageMin Failed!',
                message: 'Failed to optimise images.',
                icon: __dirname + '/../laravel-elixir/icons/fail.png'
            }));
    });

    this.registerWatcher('imagemin', [
        baseDir + '/**/*.png',
        baseDir + '/**/*.gif',
        baseDir + '/**/*.svg',
        baseDir + '/**/*.jpg',
        baseDir + '/**/*.jpeg'
    ]);

    return this.queueTask('imagemin');

});

Remove success message, only show error message.


var express = require('express');
var serveStatic = require('serve-static');
var bodyParser = require('body-parser');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);

function appCtor(cfg, pool) {
  var app = express();

  app.set('trust proxy', true);
  app.set('view engine', 'jade');
  app.set('views', __dirname + '/views');
  app.use(serveStatic(__dirname + '/static'));
  app.use(session({
    store: new RedisStore({
      host: cfg.redis.hostname,
      port: cfg.redis.port}),
    secret: cfg.env.SECRET_KEY_BASE,
    resave: false,
    saveUninitialized: false
  }));
  app.use(function (req, res, next) {
    if (!req.session) {
      return next(new Error("couldn't find sessions"));
    }
    else return next();
  });
  app.use(bodyParser.urlencoded({ extended: false }));

  app.get('/', function(req, res) {
    return res.render('index');
  });
  app.get('/m', function(req, res) {
    return res.render('messaging');
  });

  return app;
}

module.exports = appCtor;

Fix app constructor not being exported


jsio('from shared.javascript import Class')
jsio('import fan.ui.Button')
jsio('import fan.ui.RadioButtons')
jsio('import fan.tasks.views.View')

exports = Class(fan.tasks.views.View, function(supr) {

	this._className += ' TaskItemView'
	this._minWidth = 370
	this._maxWidth = 740

	this.init = function(itemId) {
		supr(this, 'init')
		this._itemId = itemId
	}

	this._buildHeader = function() {
		new fan.ui.RadioButtons()
			.addTextButton('Normal', 'normal')
			.addTextButton('Crucial', 'crucial')
			.addTextButton('Backlog', 'backlog')
			.addTextButton('Done', 'done')
			.subscribe('Click', bind(this, '_toggleTaskState'))
			.appendTo(this._header)
	}

	this._buildBody = function() {
		gUtil.withTemplate('task', 'panel', bind(this, function(template) {
			this._body.innerHTML = ''
			this._body.appendChild(fin.applyTemplate(template, this._itemId))
		}))
	}

	this._toggleTaskState = function(newState) {
		console.log("TOGGLE STATE", newState)
	}
})

jsio('from shared.javascript import Class')
jsio('import fan.ui.Button')
jsio('import fan.ui.RadioButtons')
jsio('import fan.tasks.views.View')

exports = Class(fan.tasks.views.View, function(supr) {

	this._className += ' TaskItemView'
	this._minWidth = 390
	this._maxWidth = 740

	this.init = function(itemId) {
		supr(this, 'init')
		this._itemId = itemId
	}

	this._buildHeader = function() {
		new fan.ui.RadioButtons()
			.addTextButton('Normal', 'normal')
			.addTextButton('Crucial', 'crucial')
			.addTextButton('Backlog', 'backlog')
			.addTextButton('Done', 'done')
			.subscribe('Click', bind(this, '_toggleTaskState'))
			.appendTo(this._header)
	}

	this._buildBody = function() {
		gUtil.withTemplate('task', 'panel', bind(this, function(template) {
			this._body.innerHTML = ''
			this._body.appendChild(fin.applyTemplate(template, this._itemId))
		}))
	}

	this._toggleTaskState = function(newState) {
		console.log("TOGGLE STATE", newState)
	}
})

Make the minimum width of a task item view 20 pixels wider to fit the discussion view in there comfortably and not have horizontal scrollbars appear


$(document).ready(function () {
    var showSlave = function ($master, showOn) {
        if ($master.is(':checkbox')) {
            return (+$master.is(':checked')).toString() === showOn;
        }

        var value = $master.val().toString();

        if ($master.is('select') && $master.prop('multiple')) {
            return value.indexOf(showOn) >= 0;
        }

        return value === showOn;
    };
    var toggleSlaveContainer = function ($slaveContainer, $master, showOn) {
        $master.val() && showSlave($master, showOn) ? $slaveContainer.show() : $slaveContainer.hide();
    };

    $('.slave_input').each(function () {
        var $slave = $(this);
        var $slaveContainer = $slave.closest('.table_row');
        var masterSelector = $slave.data('master');
        var showOn = $slave.data('show-on').toString();

        var $context = $slave.closest('[class*="_a2lix_translationsFields-"]');
        var $master = $context.find(masterSelector).first();

        if (!$master.length) {
            $context = $slave.closest('form');
            $master = $context.find(masterSelector).first();
        }

        toggleSlaveContainer($slaveContainer, $master, showOn);

        $context.on('change', masterSelector, function () {
            toggleSlaveContainer($slaveContainer, $(this), showOn);
        });
    });
});

$(document).ready(function () {
    var showSlave = function ($master, showOn) {
        if ($master.is(':checkbox')) {
            return (+$master.is(':checked')).toString() === showOn;
        }

        var value = $master.val().toString();

        if ($master.is('select') && $master.prop('multiple')) {
            return value.indexOf(showOn) >= 0;
        }

        return value === showOn;
    };
    var toggleSlaveContainer = function ($slaveContainer, $master, showOn) {
        $master.val() && showSlave($master, showOn) ? $slaveContainer.show() : $slaveContainer.hide();

        if ($slaveContainer.is('option')) {
            $slaveContainer.closest('select').trigger('chosen:updated');
        }
    };

    $('.slave_input').each(function () {
        var $slave = $(this);

        var $slaveContainer = $slave.is('option') ? $slave : $slave.closest('.table_row');

        var masterSelector = $slave.data('master');
        var showOn = $slave.data('show-on').toString();

        var $context = $slave.closest('[class*="_a2lix_translationsFields-"]');
        var $master = $context.find(masterSelector).first();

        if (!$master.length) {
            $context = $slave.closest('form');
            $master = $context.find(masterSelector).first();
        }

        toggleSlaveContainer($slaveContainer, $master, showOn);

        $context.on('change', masterSelector, function () {
            toggleSlaveContainer($slaveContainer, $(this), showOn);
        });
    });
});

Add slave option support to the master-slave inputs JS.


export default {
  data () {
    return {
      windowWidth: 0,
      currentBreakpoint: '',
      breakpoints: {
        small: 767, // MUST MATCH VARIABLES IN assets/stylesheets/_settings
        medium: 1024,
        large: 1200
      }
    }
  },

  created () {
    this.updateWindowSize()

    // allow for multiple functions to be called on window resize
    // window.addEventListener('resize', () => this.$eventHub.$emit('windowResized'))

    this.$eventHub.$on('windowResized', this.updateWindowSize)
  },

  methods: {
    updateWindowSize () {
      this.windowWidth = window.innerWidth

      if(this.isSmall()) { this.currentBreakpoint = 'small' }
      if(this.isMedium()) { this.currentBreakpoint = 'medium' }
      if(this.isLarge()) { this.currentBreakpoint = 'large' }
      if(this.isXLarge()) { this.currentBreakpoint = 'xlarge' }
    },

    isSmall () {
      return this.windowWidth <= this.breakpoints.small
    },

    isMedium () {
      return this.windowWidth > this.breakpoints.small && this.windowWidth <= this.breakpoints.medium
    },

    isLarge () {
      return this.windowWidth > this.breakpoints.medium && this.windowWidth <= this.breakpoints.large
    },

    isXLarge () {
      return this.windowWidth > this.breakpoints.large
    },

    getCurrentBreakpoint () {
      return this.currentBreakpoint
    }
  }
}

export default {
  data () {
    return {
      windowWidth: 0,
      currentBreakpoint: '',
      breakpoints: {
        small: 767, // MUST MATCH VARIABLES IN assets/stylesheets/_settings
        medium: 1024,
        large: 1200
      }
    }
  },

  created () {
    this.updateWindowSize()

    // allow for multiple functions to be called on window resize
    window.addEventListener('resize', () => this.$eventHub.$emit('windowResized'))

    this.$eventHub.$on('windowResized', this.updateWindowSize)
  },

  methods: {
    updateWindowSize () {
      this.windowWidth = window.innerWidth

      if(this.isSmall()) { this.currentBreakpoint = 'small' }
      if(this.isMedium()) { this.currentBreakpoint = 'medium' }
      if(this.isLarge()) { this.currentBreakpoint = 'large' }
      if(this.isXLarge()) { this.currentBreakpoint = 'xlarge' }
    },

    isSmall () {
      return this.windowWidth <= this.breakpoints.small
    },

    isMedium () {
      return this.windowWidth > this.breakpoints.small && this.windowWidth <= this.breakpoints.medium
    },

    isLarge () {
      return this.windowWidth > this.breakpoints.medium && this.windowWidth <= this.breakpoints.large
    },

    isXLarge () {
      return this.windowWidth > this.breakpoints.large
    },

    getCurrentBreakpoint () {
      return this.currentBreakpoint
    }
  }
}

Fix bug where stickybar height was not being adjusted on page resize


/*globals $, Morris, gettext*/
$(function () {
    $(".chart").css("height", "250px");
    new Morris.Area({
        element: 'obd_chart',
        data: JSON.parse($("#obd-data").html()),
        xkey: 'date',
        ykeys: ['ordered', 'paid'],
        labels: [gettext('Placed orders'), gettext('Paid orders')],
        lineColors: ['#000099', '#009900'],
        smooth: false,
        resize: true,
        fillOpacity: 0.3,
        behaveLikeLine: true
    });
    new Morris.Area({
        element: 'rev_chart',
        data: JSON.parse($("#rev-data").html()),
        xkey: 'date',
        ykeys: ['revenue'],
        labels: [gettext('Total revenue')],
        smooth: false,
        resize: true,
        fillOpacity: 0.3,
        preUnits: $.trim($("#currency").html()) + ' '
    });
    new Morris.Bar({
        element: 'obp_chart',
        data: JSON.parse($("#obp-data").html()),
        xkey: 'item',
        ykeys: ['ordered', 'paid'],
        labels: [gettext('Placed orders'), gettext('Paid orders')],
        barColors: ['#000099', '#009900'],
        resize: true,
        xLabelAngle: 60
    });
});

/*globals $, Morris, gettext*/
$(function () {
    $(".chart").css("height", "250px");
    new Morris.Area({
        element: 'obd_chart',
        data: JSON.parse($("#obd-data").html()),
        xkey: 'date',
        ykeys: ['ordered', 'paid'],
        labels: [gettext('Placed orders'), gettext('Paid orders')],
        lineColors: ['#000099', '#009900'],
        smooth: false,
        resize: true,
        fillOpacity: 0.3,
        behaveLikeLine: true
    });
    new Morris.Area({
        element: 'rev_chart',
        data: JSON.parse($("#rev-data").html()),
        xkey: 'date',
        ykeys: ['revenue'],
        labels: [gettext('Total revenue')],
        smooth: false,
        resize: true,
        fillOpacity: 0.3,
        preUnits: $.trim($("#currency").html()) + ' '
    });
    new Morris.Bar({
        element: 'obp_chart',
        data: JSON.parse($("#obp-data").html()),
        xkey: 'item',
        ykeys: ['ordered', 'paid'],
        labels: [gettext('Placed orders'), gettext('Paid orders')],
        barColors: ['#000099', '#009900'],
        resize: true,
        xLabelAngle: 30
    });
});

Adjust label angle to 30°


/** @jsx React.DOM */
'use strict';

var React = require('react'),
  ReactIntlMixin = require('react-intl');

var SendFeedbackButton = React.createClass({
  mixins: [ReactIntlMixin],
  propTypes: {
    testCase: React.PropTypes.object.isRequired,
  },
  render: function() {
    var sendFeedbackBody = `
Bonjour,

Je vous envoie mes remarques sur OpenFisca.

Les données ci-dessous vous aideront à résoudre mon problème.

Voici mon cas-type :

${JSON.stringify(this.props.testCase, null, 2)}
`;
    var sendFeedbackHref = `mailto:contact@openfisca.fr?subject=Retours sur OpenFisca&body=${encodeURIComponent(sendFeedbackBody)}`; // jshint ignore:line

    return (
      <a className='btn btn-default' href={sendFeedbackHref}>
        {this.getIntlMessage('sendFeedback')}
      </a>
    );
  },
});

module.exports = SendFeedbackButton;

/** @jsx React.DOM */
'use strict';

var React = require('react'),
  ReactIntlMixin = require('react-intl');

var SendFeedbackButton = React.createClass({
  mixins: [ReactIntlMixin],
  propTypes: {
    testCase: React.PropTypes.object.isRequired,
  },
  render: function() {
    var sendFeedbackBody = `
Bonjour,

Je vous envoie mes remarques sur OpenFisca.

Les données ci-dessous vous aideront à résoudre mon problème.

Voici mon cas-type :

${JSON.stringify(this.props.testCase, null, 2)}
`;
    var sendFeedbackHref = `mailto:contact@openfisca.fr?subject=Retours sur OpenFisca&body=${encodeURIComponent(sendFeedbackBody)}`; // jshint ignore:line

    return (
      <a className='btn btn-link' href={sendFeedbackHref}>
        {this.getIntlMessage('sendFeedback')}
      </a>
    );
  },
});

module.exports = SendFeedbackButton;

Use btn-link for send feedback


#!/usr/bin/env node

const flos = require('../lib/api');
const FlosRunner = flos.Runner;

// load config
const config = {};
// load linters
const linters = [];
// create runner
const runner = new FlosRunner(linters);
// configure runner
runner.configure(config);
// flos
runner.run();

#!/usr/bin/env node

// must do this initialization *before* other requires in order to work
if (process.argv.indexOf("--debug") > -1) {
  require("debug").enable("flos:*");
}

const flos = require('../lib/api');
const FlosRunner = flos.Runner;

// load config
const config = {};
// load linters
const linters = [];
// create runner
const runner = new FlosRunner(linters);
// configure runner
runner.configure(config);
// flos
runner.run();

Enable debug mode with CLI option


'use strict';

describe('pattyApp', function() {

  beforeEach(function() {
    browser.get('index.html');
  });

  it('should have patty title', function() {
    expect(browser.getTitle()).toMatch('Project Patty Visualisation');
  });

  describe('initial state', function() {
    it('should have zero search results', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(0);
    });

  });

  describe('searched on "py"', function() {
    beforeEach(function() {
      element(by.model('sp.query')).sendKeys('py');
    });

    it('should have one search result', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(1);
    });
  });

  describe('search on "bla"', function() {
    beforeEach(function() {
      element(by.model('sp.query')).sendKeys('bla');
    });

    it('should have zero search results', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(0);
    });
  });
});

'use strict';

describe('pattyApp', function() {

  beforeEach(function() {
    browser.get('index.html');
  });

  it('should have patty title', function() {
    expect(browser.getTitle()).toMatch('Project Patty Visualisation');
  });

  describe('initial state', function() {
    it('should have zero search results', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(0);
    });
    it('should not show settings panel', function() {
      var panel = element(by.css('.settings-panel'));
      expect(panel.isDisplayed()).toBe(false);
    });
  });

  describe('searched on "py"', function() {
    beforeEach(function() {
      element(by.model('sp.query')).sendKeys('py');
    });

    it('should have one search result', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(1);
    });
  });

  describe('search on "bla"', function() {
    beforeEach(function() {
      element(by.model('sp.query')).sendKeys('bla');
    });

    it('should have zero search results', function() {
      expect(element.all(by.css('.search-result')).count()).toBe(0);
    });
  });

  describe('click on settings gear', function() {
    beforeEach(function() {
      element(by.css('.glyphicon-cog')).click();
    });

    it('should show settings panel', function() {
      var panel = element(by.css('.settings-panel'));
      expect(panel.isDisplayed()).toBe(true);
    });
  });
});

Add e2e test for toggling settings panel.


module.exports = function(grunt){
  grunt.config('sass', {
    sass: {
      options: {
        sourceMap: 'true'
      }, //options
      dist: {
        files: {
          'dist/css/main.css': 'src/scss/main.scss',
        }
      }
    }
  }) //config
  grunt.loadNpmTasks('grunt-sass');
  grunt.registerTask('compileSass', 'sass');
}

module.exports = function(grunt){
  grunt.config('sass', {
    options: {
      sourceMap: true
    }, //options
    dist: {
      files: {
        'dist/css/main.css': 'src/scss/main.scss',
      }
    }
  }) //config
  grunt.loadNpmTasks('grunt-sass');
  grunt.registerTask('compileSass', 'sass');
}

Repair - Fixed up SASS compilation.


/*
 *    game.js
 *    All of the magic happens here
 */

YINS.Game = function(game) {
	this.music;
};

YINS.Game.prototype = {

	create: function() {

		/* TODO: transition smoothly from mainmenu music to this */
		this.music = YINS.game.add.audio('gameMusic');
		this.music.loopFull(0.5);

	},

	update: function() {

	}
};

/*
 *    game.js
 *    All of the magic happens here
 */

YINS.Game = function(game) {
	this.music;
	this.player;
};

YINS.Game.prototype = {

	create: function() {

		/* TODO: transition smoothly from mainmenu music to this */
		this.music = YINS.game.add.audio('gameMusic');
		this.music.loopFull(0.5);

		/* Add sprites to the game world */
		this.player = YINS.game.add.sprite(YINS.game.world.centerX, YINS.game.world.centerY, 'sprites', 19);

		/* Declare animations */
		this.player.animations.add('idle', [19]);
		this.player.animations.add('walk-right', [20, 21], 12);

		/* Enable ARCADE physics engine
		You can read more about this in the documentation: http://phaser.io/docs/2.3.0/Phaser.Physics.Arcade.html
		--> For documentation on the body property of physics enabled sprites,
		    see this article: http://phaser.io/docs/2.3.0/Phaser.Physics.Arcade.Body.html */
		YINS.game.physics.startSystem(Phaser.Physics.ARCADE);

		// Enable ARCADE physics on sprites
		YINS.game.physics.enable(this.player, Phaser.Physics.ARCADE);

		/* Set gravity of the whole game world
		This can be manually changed on a per sprite basis by setting
		SPRITE.body.gravity.y = GRAVITY */
		YINS.game.physics.arcade.gravity.y = 2000;

		/* Change properties of the player sprite */
		this.player.scale.setTo(YINS.sprite_scale);
		this.player.smoothed = false;
		this.player.body.collideWorldBounds = true;

	},

	update: function() {

	}
};

Add player and enable physics


import React from 'react';

let Trigger; // eslint-disable-line

if (process.env.REACT === '15') {
  const ActualTrigger = require.requireActual('rc-trigger');
  const { render } = ActualTrigger.prototype;

  ActualTrigger.prototype.render = () => {
    const { popupVisible } = this.state; // eslint-disable-line
    let component;

    if (popupVisible || this._component) { // eslint-disable-line
      component = this.getComponent(); // eslint-disable-line
    }

    return (
      <div id="TriggerContainer">
        {render.call(this)}
        {component}
      </div>
    );
  };
  Trigger = ActualTrigger;
} else {
  const TriggerMock = require('rc-trigger/lib/mock'); // eslint-disable-line
  Trigger = TriggerMock;
}

export default Trigger;

import React from 'react';

let Trigger; // eslint-disable-line

if (process.env.REACT === '15') {
  const ActualTrigger = require.requireActual('rc-trigger');
  // cannot use object destruction, cause react 15 test cases fail
  const render = ActualTrigger.prototype.render; // eslint-disable-line

  ActualTrigger.prototype.render = () => {
    const { popupVisible } = this.state; // eslint-disable-line
    let component;

    if (popupVisible || this._component) { // eslint-disable-line
      component = this.getComponent(); // eslint-disable-line
    }

    return (
      <div id="TriggerContainer">
        {render.call(this)}
        {component}
      </div>
    );
  };
  Trigger = ActualTrigger;
} else {
  const TriggerMock = require('rc-trigger/lib/mock'); // eslint-disable-line
  Trigger = TriggerMock;
}

export default Trigger;

Fix test case in react 15


function openCourse(Course) {
    var i, x;
    x = document.getElementsByClassName("schedule");
    for(i=0;i<x.length;i++){
        x[i].style.display = 'none';
    }
    document.getElementById(Course).style.display = "block";
}

function openCourse(Course) {
    var i, x, y;
    x = document.getElementsByClassName("schedule");
    y = document.getElementById(Course);
    for(i=0;i<x.length;i++){
        x[i].style.display = 'none';
    }
    y.style.display = "block";
    y.className = y.className + ' ' + Course.toLowerCase();
}

Add change of tab color on click to schedule


var exportGeoJson = require('osm-p2p-geojson')
var pump = require('pump')
var defork = require('osm-p2p-defork')
var collect = require('collect-transform-stream')

var userConfig = require('./user-config')
var presets = userConfig.getSettings('presets')

var matchPreset = require('./preset-matcher')(presets.presets)
var isPolygonFeature = require('./polygon-feature')(presets.presets)

var featureMap = function (f) {
  var newProps = {}
  Object.keys(f.properties).forEach(function (key) {
    var newKey = key.replace(':', '_')
    newProps[newKey] = f.properties[key]
  })
  f.properties = newProps
  var match = matchPreset(f)
  if (match) {
    f.properties.icon = match.icon
    f.properties.preset = match.id
  }
  return f
}

module.exports = function (osm, bbox) {
  var q = osm.queryStream(bbox)
  var d = collect(defork)
  var e = exportGeoJson(osm, {
    map: featureMap,
    polygonFeatures: isPolygonFeature
  })

  return pump(q, d, e)
}

var exportGeoJson = require('osm-p2p-geojson')
var pump = require('pump')
var defork = require('osm-p2p-defork')
var collect = require('collect-transform-stream')

var userConfig = require('./user-config')
var presets = userConfig.getSettings('presets') || {}

var matchPreset = require('./preset-matcher')(presets.presets)
var isPolygonFeature = require('./polygon-feature')(presets.presets)

var featureMap = function (f) {
  var newProps = {}
  Object.keys(f.properties).forEach(function (key) {
    var newKey = key.replace(':', '_')
    newProps[newKey] = f.properties[key]
  })
  f.properties = newProps
  var match = matchPreset(f)
  if (match) {
    f.properties.icon = match.icon
    f.properties.preset = match.id
  }
  return f
}

module.exports = function (osm, bbox) {
  var q = osm.queryStream(bbox)
  var d = collect(defork)
  var e = exportGeoJson(osm, {
    map: featureMap,
    polygonFeatures: isPolygonFeature
  })

  return pump(q, d, e)
}

Fix crash when user has no presets loaded.


/*
 * Author: Jerome Renaux
 * E-mail: jerome.renaux@gmail.com
 */

var Game = {};

Game.init = function(){
    game.stage.disableVisibilityChange = true;
};

Game.preload = function() {
    game.load.tilemap('map', 'assets/map/example_map.json', null, Phaser.Tilemap.TILED_JSON);
    game.load.spritesheet('tileset', 'assets/map/tilesheet.png',32,32);
    game.load.image('sprite','assets/sprites/sprite.png');
};

Game.create = function(){
    var testKey = game.input.keyboard.addKey(Phaser.Keyboard.ENTER);
    testKey.onDown.add(Client.sendTest, this);
    Client.getPyramidPlans(); // probably not needed

    // get this from the UI
    //Game.initPlayer('Me');
};

Game.initPlayer = function(name) {
    Client.newPlayer(name);
    // Player.init will be called after hearing back from server
}

// throwaway code?
Game.clickJoin = function() {
    var name = document.getElementById('input-name').value;
    Game.initPlayer(name);
}

/*
 * Author: Jerome Renaux
 * E-mail: jerome.renaux@gmail.com
 */

var Game = {};

Game.init = function(){
    game.stage.disableVisibilityChange = true;
};

Game.preload = function() {
    game.load.tilemap('map', 'assets/map/example_map.json', null, Phaser.Tilemap.TILED_JSON);
    game.load.spritesheet('tileset', 'assets/map/tilesheet.png',32,32);
    game.load.image('sprite','assets/sprites/sprite.png');
};

Game.create = function(){
    var testKey = game.input.keyboard.addKey(Phaser.Keyboard.ENTER);
    testKey.onDown.add(Client.sendTest, this);
    Client.getPyramidPlans(); // probably not needed

    // get this from the UI
    //Game.initPlayer('Me');
};

Game.initPlayer = function(name) {
    Client.newPlayer(name);
    // Player.init will be called after hearing back from server
}

// throwaway code?
Game.clickJoin = function() {
    var name = document.getElementById('input-name').value;
    Game.initPlayer(name);
    document.getElementById('name').remove();
}

Remove the name input after you use it


function parseQString(qString) {
    var d = /^\s*q=([01](?:\.\d+))\s*$/.exec(qString);
    if (!!d) {
        return 1;
    }
    return Number(d[1]);
}

function sortQArrayString(content) {
    var entries = content.split(','),
        sortData = [];
    entries.forEach(function(rec) {
                var s = rec.split(';');
                sortData.append({
                    key: s[0],
                    quality: parseQString(s[1])
                });
            });

    sortData.sort(function(a, b) {
                if (a.quality > b.quality) { return -1; }
                if (a.quality < b.quality) { return 1;  }
                return 0;
            });
    return sortData.map(function(rec) {
                return rec.key;
            });
}

module.exports = function negotiate(config) {
    config || config = {};

    // Connect-conneg will only handle Languages, Accept, and Charset
    // The gzip module handles the Accept-Encoding header
    // Accept-Range is outside the scope of this module
    return function(req, res, next) {
        if (req.headers['Accept-Language']) {
            req.languages = sortQArrayString(req.headers['Accept-Language']);
        }

        if (req.headers['Accept']) {
            req.acceptableTypes = sortQArrayString(req.headers['Accept']);
        }

        if (req.headers['Accept-Charset']) {
            req.charsets = sortQArrayString(req.headers['Accept-Charset']);
        }

        if (next) { next(); }
    };
}

function parseQString(qString) {
    var d = /^\s*q=([01](?:\.\d+))\s*$/.exec(qString);
    if (!!d) {
        return 1;
    }
    return Number(d[1]);
}

function sortQArrayString(content) {
    var entries = content.split(','),
        sortData = [];
    entries.forEach(function(rec) {
                var s = rec.split(';');
                sortData.append({
                    key: s[0],
                    quality: parseQString(s[1])
                });
            });

    sortData.sort(function(a, b) {
                if (a.quality > b.quality) { return -1; }
                if (a.quality < b.quality) { return 1;  }
                return 0;
            });
    return sortData.map(function(rec) {
                return rec.key;
            });
}

function buildScanner(header, property) {
    return function(req, res next) {
        var data = req.headers[header];
        if (data) {
            req[property] = sortQArrayString(data);
        }
        if (next) next();
    };
}

module.exports = {
    language: function() {
                  return buildScanner('Accept-Languages', 'languages');
              },
    acceptedTypes: function() {
                       return buildScanner('Accept', 'acceptableTypes');
                   },
    charsets: function() {
                  return buildScanner('Accept-Charset', 'charsets');
              },
    custom: buildScanner
}

Split out the different headers into their own methods for greater flexibility.


import React from 'react';
import {
  View,
  Text,
} from 'react-native';
// import { Actions } from 'react-native-router-flux';
import { RoomView } from './RoomView';

import { AppStyles, AppSizes } from '../../../theme/';
import Network from '../../../network';
import t from '../../../i18n';

export default class SupportGroup extends React.Component {
  constructor(props) {
    super(props);
    this.supportGroup = null;
  }

  componentWillMount() {
    const net = new Network();
    this.supportGroup = net.db.groups.findByName('support');
  }

  render() {
    return (
      <View
        style={[AppStyles.container, {
          marginTop: -(AppSizes.navbarHeight),
          paddingBottom: 5,
        }]}
      >
        {
          this.supportGroup &&
          <RoomView obj={this.supportGroup} />
        }
        {
          !this.supportGroup &&
          <View
            style={{
              flex: 1,
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <Text
              style={{
                fontSize: 16,
                fontFamily: 'OpenSans-Regular',
                fontWeight: '500',
              }}
            >{t('txt_CS')}</Text>
          </View>
        }
      </View>
    );
  }
}

SupportGroup.defaultProps = {
};

SupportGroup.propTypes = {
};

import React from 'react';
import {
  View,
  Text,
} from 'react-native';
// import { Actions } from 'react-native-router-flux';
import RoomView from './RoomView';

import { AppStyles, AppSizes } from '../../../theme/';
import Network from '../../../network';
import t from '../../../i18n';

export default class SupportGroup extends React.Component {
  constructor(props) {
    super(props);
    this.supportGroup = null;
  }

  componentWillMount() {
    const net = new Network();
    this.supportGroup = net.db.groups.findByName('support');
  }

  render() {
    return (
      <View
        style={[AppStyles.container, {
          marginTop: -(AppSizes.navbarHeight),
          paddingBottom: 5,
        }]}
      >
        {
          this.supportGroup &&
          <RoomView obj={this.supportGroup} />
        }
        {
          !this.supportGroup &&
          <View
            style={{
              flex: 1,
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <Text
              style={{
                fontSize: 16,
                fontFamily: 'OpenSans-Regular',
                fontWeight: '500',
              }}
            >{t('txt_CS')}</Text>
          </View>
        }
      </View>
    );
  }
}

SupportGroup.defaultProps = {
};

SupportGroup.propTypes = {
};

Support import was throwing exceptions


import { randomId } from './util';

const s = typeof Symbol === 'function';
export const vueFormConfig = s ? Symbol() : `VueFormProviderConfig${randomId}`;
export const vueFormState =  s ? Symbol() : `VueFormProviderState${randomId}`;

import { randomId } from './util';

export const vueFormConfig = `VueFormProviderConfig${randomId()}`;
export const vueFormState = `VueFormProviderState${randomId()}`;

Fix a bug when using symbols in iOS 8.1


import cx from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';

const Avatar = ({ className, user }) => (
  <div className={cx('Avatar', className)}>
    <img
      className="Avatar-image"
      src={user.avatar || `/a/${encodeURIComponent(user._id)}`}
      alt={user.username}
    />
  </div>
);

Avatar.propTypes = {
  className: PropTypes.string,
  user: PropTypes.object.isRequired
};

export default Avatar;

import cx from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';

const Avatar = ({ className, user }) => (
  <div className={cx('Avatar', className)}>
    <img
      className="Avatar-image"
      src={user.avatar || `https://sigil.cupcake.io/uwave-${encodeURIComponent(user._id)}`}
      alt={user.username}
    />
  </div>
);

Avatar.propTypes = {
  className: PropTypes.string,
  user: PropTypes.object.isRequired
};

export default Avatar;

Revert "[WLK-INSTANCE] use locally generated avatars as default instead of cupcake.io"


import React, { Component } from 'react';
import { faintBlack, cyan500 } from 'material-ui/styles/colors';
import MenuItem from 'material-ui/MenuItem';
import IconMenu from 'material-ui/IconMenu';
import IconButton from 'material-ui/IconButton/IconButton';
import ActionHelpOutline from 'material-ui/svg-icons/action/help-outline';

const TEST_CARD_NUMBERS = [
  '01-2167-30-92545'
];

class TestCardNumber extends Component {

  constructor() {
    super();

    this.state = {
      cardNumber: ''
    };
  }

  renredCardNumbers() {
    return TEST_CARD_NUMBERS.map((number, index) => <MenuItem key={index} value={number} primaryText={number} />);
  }

  render() {
    return (
        <IconMenu
          className='buka-cardnumber__help'
          onChange={this.props.onChange}
          iconButtonElement={<IconButton><ActionHelpOutline color={faintBlack} hoverColor={cyan500} /></IconButton>}
          anchorOrigin={{horizontal: 'left', vertical: 'top'}}
          targetOrigin={{horizontal: 'left', vertical: 'top'}}>
          {this.renredCardNumbers()}
        </IconMenu>
    );
  }
}

TestCardNumber.propTypes = {
  onChange: React.PropTypes.func
};

export default TestCardNumber;

import React, { Component } from 'react';
import { faintBlack, cyan500 } from 'material-ui/styles/colors';
import MenuItem from 'material-ui/MenuItem';
import IconMenu from 'material-ui/IconMenu';
import IconButton from 'material-ui/IconButton/IconButton';
import ActionHelpOutline from 'material-ui/svg-icons/action/help-outline';

const TEST_CARD_NUMBERS = [
  '01-2167-30-92545',
  '26-2167-19-35623',
  '29-2167-26-31433'
];

class TestCardNumber extends Component {

  constructor() {
    super();

    this.state = {
      cardNumber: ''
    };
  }

  renredCardNumbers() {
    return TEST_CARD_NUMBERS.map((number, index) => <MenuItem key={index} value={number} primaryText={number} />);
  }

  render() {
    return (
        <IconMenu
          className='buka-cardnumber__help'
          onChange={this.props.onChange}
          iconButtonElement={<IconButton><ActionHelpOutline color={faintBlack} hoverColor={cyan500} /></IconButton>}
          anchorOrigin={{horizontal: 'left', vertical: 'top'}}
          targetOrigin={{horizontal: 'left', vertical: 'top'}}>
          {this.renredCardNumbers()}
        </IconMenu>
    );
  }
}

TestCardNumber.propTypes = {
  onChange: React.PropTypes.func
};

export default TestCardNumber;

Add some test card numbers.


/**
 * Created by Tomasz Gabrysiak @ Infermedica on 08/02/2017.
 */

import View from '../../base/view';
import template from './template';

export default class GeoRisksView extends View {
  constructor (el, context) {
    context.locationRiskFactors = ['p_15', 'p_20', 'p_21', 'p_16', 'p_17', 'p_18', 'p_14', 'p_19', 'p_22', 'p_13'];

    const handleRisksChange = (e) => {
      let group = {};
      this.el.querySelectorAll('.input-risk').forEach((item) => {
        group[item.id] = item.checked;
      });
      this.context.patient.addSymptomsGroup(group);
    };

    const binds = {
      '.input-risk': {
        type: 'change',
        listener: handleRisksChange
      }
    };

    super(el, template, context, binds);
  }
}

/**
 * Created by Tomasz Gabrysiak @ Infermedica on 08/02/2017.
 */

import View from '../../base/view';
import template from './template';

export default class GeoRisksView extends View {
  constructor (el, context) {
    context.locationRiskFactors = ['p_15', 'p_20', 'p_21', 'p_16', 'p_17', 'p_18', 'p_14', 'p_19', 'p_22', 'p_13'];

    const handleRisksChange = (e) => {
      let group = {};
      this.el.querySelectorAll('.input-risk').forEach((item) => {
        group[item.id] = {reported: item.checked};
      });
      this.context.patient.addSymptomsGroup(group);
    };

    const binds = {
      '.input-risk': {
        type: 'change',
        listener: handleRisksChange
      }
    };

    super(el, template, context, binds);
  }
}

Update geo riskfators screen to use the new structure of reported symptoms


var React = require( "react" );
var MorningScreen = require( "./morning-screen" );
var PlayScreen = require( "./play-screen" );

module.exports = React.createClass({
	displayName: "RepubliaTimes",

	getInitialState() {
		return {
			day: 1,
			screen: "morning"
		};
	},

	changeToScreen( newScreen ) {
		this.setState({
			screen: newScreen
		});
	},

	renderMorningScreen() {
		return <MorningScreen
			day={this.state.day}
			onContinue={this.changeToScreen.bind( null, "play" )} />;
	},

	renderPlayScreen() {
		return <PlayScreen day={this.state.day} />;
	},

	render() {
		var screen = this.state.screen;
		var screenRenderFunction = {
			morning: this.renderMorningScreen,
			play: this.renderPlayScreen
		}[ screen ];

		if ( screenRenderFunction === undefined ) {
			throw new Error( "Invalid screen '" + screen +"'" );
		}

		return screenRenderFunction();
	}
});

var React = require( "react" );
var MorningScreen = require( "./morning-screen" );
var PlayScreen = require( "./play-screen" );

module.exports = React.createClass({
	displayName: "RepubliaTimes",

	getInitialState() {
		return {
			day: 1,
			screen: "morning"
		};
	},

	changeToScreen( newScreen ) {
		this.setState({
			screen: newScreen
		});
	},

	renderMorningScreen() {
		return <MorningScreen
			day={this.state.day}
			onContinue={() => this.changeToScreen( "play" )} />;
	},

	renderPlayScreen() {
		return <PlayScreen day={this.state.day} />;
	},

	render() {
		var screen = this.state.screen;
		var screenRenderFunction = {
			morning: this.renderMorningScreen,
			play: this.renderPlayScreen
		}[ screen ];

		if ( screenRenderFunction === undefined ) {
			throw new Error( "Invalid screen '" + screen +"'" );
		}

		return screenRenderFunction();
	}
});

Use an arrow function instead


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

/**
 * @author Vico @vicocotea
 * original shader : https://www.shadertoy.com/view/lssGDj by @movAX13h
 */

/**
 * An ASCII filter.
 *
 * @class
 * @extends AbstractFilter
 * @namespace PIXI.filters
 */
function AsciiFilter()
{
    core.AbstractFilter.call(this,
        // vertex shader
        null,
        // fragment shader
        require('fs').readFileSync(__dirname + '/ascii.frag', 'utf8'),
        // custom uniforms
        {
            dimensions: { type: '4fv', value: new Float32Array([0, 0, 0, 0]) },
            pixelSize:  { type: '1f', value: 8 }
        }
    );
}

AsciiFilter.prototype = Object.create(core.AbstractFilter.prototype);
AsciiFilter.prototype.constructor = AsciiFilter;
module.exports = AsciiFilter;

Object.defineProperties(AsciiFilter.prototype, {
    /**
     * The pixel size used by the filter.
     *
     * @member {number}
     * @memberof AsciiFilter#
     */
    size: {
        get: function ()
        {
            return this.uniforms.pixelSize.value;
        },
        set: function (value)
        {
            this.uniforms.pixelSize.value = value;
        }
    }
});

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

// TODO (cengler) - The Y is flipped in this shader for some reason.

/**
 * @author Vico @vicocotea
 * original shader : https://www.shadertoy.com/view/lssGDj by @movAX13h
 */

/**
 * An ASCII filter.
 *
 * @class
 * @extends AbstractFilter
 * @namespace PIXI.filters
 */
function AsciiFilter()
{
    core.AbstractFilter.call(this,
        // vertex shader
        null,
        // fragment shader
        require('fs').readFileSync(__dirname + '/ascii.frag', 'utf8'),
        // custom uniforms
        {
            dimensions: { type: '4fv', value: new Float32Array([0, 0, 0, 0]) },
            pixelSize:  { type: '1f', value: 8 }
        }
    );
}

AsciiFilter.prototype = Object.create(core.AbstractFilter.prototype);
AsciiFilter.prototype.constructor = AsciiFilter;
module.exports = AsciiFilter;

Object.defineProperties(AsciiFilter.prototype, {
    /**
     * The pixel size used by the filter.
     *
     * @member {number}
     * @memberof AsciiFilter#
     */
    size: {
        get: function ()
        {
            return this.uniforms.pixelSize.value;
        },
        set: function (value)
        {
            this.uniforms.pixelSize.value = value;
        }
    }
});

Add a TODO for ascii filter


/*
 * clean.js - Clean only the bots messages.
 *
 * Contributed by Ovyerus
 */

exports.commands = [
    'clean'
];

exports.clean = {
    desc: 'clean messages created by the bot itself',
    main: (bot, ctx) => {
        return new Promise((resolve,reject) => {
            ctx.msg.channel.getMessages(100).then(msgs => {
                let delet = [];
                msgs.forEach(m => delet.push(m.delete()));
                return Promise.all(delet);
            });
        });
    }
};

/*
 * clean.js - Clean only the bots messages.
 *
 * Contributed by Ovyerus
 */

exports.commands = [
    'clean'
];

exports.clean = {
    desc: 'clean messages created by the bot itself',
    main: (bot, ctx) => {
        return new Promise((resolve, reject) => {
            ctx.msg.channel.getMessages(100).then(msgs => {
                let delet = [];
                msgs = msgs.filter(m => m.author.id === bot.user.id);
                msgs.forEach(m => delet.push(m.delete()));
                return Promise.all(delet);
            }).then(amt => ctx.msg.channel.createMessage(amt)).then(resolve).catch(reject);
        });
    }
};

Clean additions and send message amount


require('find-java-home')(function(err, home){
  if(err){
    console.error("[node-java] "+err);
    process.exit(1);
  }
  process.stdout.write(home);
});

require('find-java-home')(function(err, home){
  if (err || !home) {
    if (!err) err = Error('Unable to determine Java home location');
    process.exit(1);
  }
  process.stdout.write(home);
});

Exit with error if Java home can't be found


/* global legacyConvert, module, project */

module.exports = function(grunt) {
	grunt.registerTask('convertLegacy', function(task) {
		var dest = legacyConvert[task],
			content = grunt.file.read(dest),
			rootSize = project.style.legacy.rootSize,
			rootValue = 10;

		// Determine root value for unit conversion
		if (rootSize.indexOf('%') !== -1) {
			rootValue = (rootSize.replace('%', '') / 100) * 16;
		} else if (rootSize.indexOf('px') !== -1) {
			rootValue = rootSize.replace('px', '');
		} else if (rootSize.indexOf('em') !== -1) {
			rootValue = rootSize.replace('em', '');
		} else if (rootSize.indexOf('pt') !== -1) {
			rootValue = rootSize.replace('pt', '');
		}

		content = content.replace(/(-?[.\d]+)rem/gi, function(str, match) {
			return (match * rootValue) + 'px';
		}).replace(/opacity:([.\d]+)/gi, function(str, match) {
			return 'filter:alpha(opacity=' + Math.round((match * 100) * 100 / 100) + ');';
		}).replace(/::/g, ':');

		grunt.file.write(dest, content);
	});
};

/* global legacyConvert, module, project */

module.exports = function(grunt) {
	grunt.registerTask('convertLegacy', function(task) {
		var dest = legacyConvert[task],
			content = grunt.file.read(dest),
			rootSize = project.style.legacy.rootSize,
			rootValue = 10;

		// Determine root value for unit conversion
		if (rootSize.indexOf('%') !== -1) {
			rootValue = (rootSize.replace('%', '') / 100) * 16;
		} else if (rootSize.indexOf('px') !== -1) {
			rootValue = rootSize.replace('px', '');
		} else if (rootSize.indexOf('em') !== -1) {
			rootValue = rootSize.replace('em', '');
		} else if (rootSize.indexOf('pt') !== -1) {
			rootValue = rootSize.replace('pt', '');
		}

		content = content.replace(/(-?[.\d]+)rem/gi, function(str, match) {
			return (match * rootValue) + 'px';
		}).replace(/opacity:([.\d]+)/gi, function(str, match) {
			return 'filter:alpha(opacity=' + Math.round((match * 100) * 100 / 100) + ')';
		}).replace(/::/g, ':');

		grunt.file.write(dest, content);
	});
};

Remove extra semicolon injection in legacy stylesheet


import {Entity}     from 'aframe-react';
import React        from 'react';
import Wsgamepad    from './Wsgamepad';

export default ({
  position=[0, 0, 5],
  id="camera",
  active="false",
  userHeight="4.8",
  rotation=[0, 0, 0],
  far="10000",
  looksControlsEnabeld="true",
  fov="80",
  near="0.005",
  velocity=[3, 3, 3],
  ...props
}) => (

    <Entity position={[0, 5, 0]} static-body="">
      <Entity
        id={id}
        active={active}
        camera={{
          'user-height': userHeight,
          active,
          far,
          fov,
          near
        }}
        rotation={rotation}
        mouse-controls=""
        touch-controls=""
        hmd-controls=""
        ws-gamepad={{
          endpoint: "ws://192.168.0.14:7878"
        }}
        look-controls=""
        universal-controls=""
        gamepad-controls=""

        kinematic-body=""
        {...props}/>
    </Entity>

);
/*
  add back `universal-controls=""` to `Entity` to enable universal control again
*/

import {Entity}     from 'aframe-react';
import React        from 'react';
import Wsgamepad    from './Wsgamepad';

export default ({
  position=[0, 0, 5],
  id="camera",
  active="false",
  userHeight="4.8",
  rotation=[0, 0, 0],
  far="10000",
  looksControlsEnabeld="true",
  fov="80",
  near="0.005",
  velocity=[3, 3, 3],
  ...props
}) => (

    <Entity position={[0, 5, 0]} static-body="">
      <Entity
        id={id}
        active={active}
        camera={{
          'user-height': userHeight,
          active,
          far,
          fov,
          near
        }}
        rotation={rotation}
        mouse-controls=""
        touch-controls=""
        hmd-controls=""
        ws-gamepad={{
          endpoint: "ws://127.0.0.1:7878"
        }}
        look-controls=""
        universal-controls=""
        gamepad-controls=""

        kinematic-body=""
        {...props}/>
    </Entity>

);
/*
  add back `universal-controls=""` to `Entity` to enable universal control again
*/

Update ws endpoint to localhost


'use strict';

/* eslint-env node */

let TEST_SELECTOR_PREFIX = /data-test-.*/;

function isTestSelector(attribute) {
  return TEST_SELECTOR_PREFIX.test(attribute);
}

function stripTestSelectors(node) {
  if ('sexpr' in node) {
    node = node.sexpr;
  }

  node.params = node.params.filter(function(param) {
    return !isTestSelector(param.original);
  });

  node.hash.pairs = node.hash.pairs.filter(function(pair) {
    return !isTestSelector(pair.key);
  });
}

function StripTestSelectorsTransform() {
  this.syntax = null;
}

StripTestSelectorsTransform.prototype.transform = function(ast) {
  let walker = new this.syntax.Walker();

  walker.visit(ast, function(node) {
    if (node.type === 'ElementNode') {
      node.attributes = node.attributes.filter(function(attribute) {
        return !isTestSelector(attribute.name);
      });
    } else if (node.type === 'MustacheStatement' || node.type === 'BlockStatement') {
      stripTestSelectors(node);
    }
  });

  return ast;
};

module.exports = StripTestSelectorsTransform;

'use strict';

/* eslint-env node */

let TEST_SELECTOR_PREFIX = /data-test-.*/;

function isTestSelector(attribute) {
  return TEST_SELECTOR_PREFIX.test(attribute);
}

function stripTestSelectors(node) {
  if ('sexpr' in node) {
    node = node.sexpr;
  }

  node.params = node.params.filter(function(param) {
    return !isTestSelector(param.original);
  });

  node.hash.pairs = node.hash.pairs.filter(function(pair) {
    return !isTestSelector(pair.key);
  });
}

function StripTestSelectorsTransform() {
  this.syntax = null;
}

StripTestSelectorsTransform.prototype.transform = function(ast) {
  let { traverse } = this.syntax;

  traverse(ast, {
    ElementNode(node) {
      node.attributes = node.attributes.filter(function(attribute) {
        return !isTestSelector(attribute.name);
      });
    },

    MustacheStatement(node) {
      stripTestSelectors(node);
    },

    BlockStatement(node) {
      stripTestSelectors(node);
    },
  });

  return ast;
};

module.exports = StripTestSelectorsTransform;

Use `traverse()` instead of the deprecated `Walker` class


 'use strict';

 L.Control.Angular = L.Control.extend({
    options: {
         position: 'bottomleft',
         template: ''
     },
     onAdd: function(map) {
         var that = this;
         var container = L.DomUtil.create('div', 'angular-control-leaflet');
         angular.element(document).ready(function() {
             // Grab the injector for the current angular app
             var $injector = angular.element(document).injector();

             var $rootScope = $injector.get('$rootScope'),
                 $compile = $injector.get('$compile'),
                 $controller = $injector.get('$controller');

             var scope = $rootScope.$new(true);

             var element = angular.element(container);
             element.html(that.options.template);

             var link = $compile(element);

             // Controller setup based on ui-router's code https://github.com/angular-ui/ui-router
             if (that.options.controller) {
                 var controller = $controller(that.options.controller, {
                     '$map': map,
                     '$scope': scope,
                     '$element': element
                 });

                 if (that.options.controllerAs) {
                     scope[that.options.controllerAs] = controller;
                 }

                 element.data('$ngControllerController', controller);
                 element.children().data('$ngControllerController', controller);
             }

             link(scope);
             scope.$apply();
         });
         return container;
     }
 });

 L.control.angular = function(options) {
     return new L.Control.Angular(options);
 };

 'use strict';

 L.Control.Angular = L.Control.extend({
    options: {
         position: 'bottomleft',
         template: ''
     },
     onAdd: function onAdd (map) {
         var that = this;
         var container = L.DomUtil.create('div', 'angular-control-leaflet');

         angular.element(document).ready(function() {
             // Grab the injector for the current angular app
             var $injector = angular.element(document).injector();

             var $rootScope = $injector.get('$rootScope'),
                 $compile = $injector.get('$compile'),
                 $controller = $injector.get('$controller');

             var scope = $rootScope.$new(true);

             var element = angular.element(container);
             element.html(that.options.template);

             // Controller setup based on ui-router's code https://github.com/angular-ui/ui-router
             if (that.options.controller) {
                 var controller = $controller(that.options.controller, {
                     '$map': map,
                     '$scope': scope,
                     '$element': element,
                     '$options': that.options
                 });

                 if (that.options.controllerAs) {
                     scope[that.options.controllerAs] = controller;
                 }

                 element.data('$ngControllerController', controller);
                 element.children().data('$ngControllerController', controller);
             }

             $compile(element)(scope);
             scope.$apply();
         });
         return container;
     }
 });

 L.control.angular = function(options) {
     return new L.Control.Angular(options);
 };

Add di to allow access to the control's options from its angular controller


/**
 * Message for errors when some method is not implemented
 * @type {String}
 * @private
 */
var NEED_IMPLEMENT_MESSAGE = "This method need to implement";

/**
 * BaseHash class
 * @constructor
 */
function BaseHash(options) {
    if (typeof options === 'string') {
        this.setData(options);
    } else if (typeof options === 'object' && options.data) {
        this.setData(options.data);
    } else {
        throw new Error('You need provide data');
    }
}

BaseHash.prototype = Object.create({
    constructor: BaseHash,

    /**
     * Get data from current hashing instance
     * @returns {*}
     */
    getData: function () {
        return this._data;
    },

    /**
     * Set data that need to hash
     * @param {*} data
     * @returns {BaseHash}
     */
    setData: function (data) {
        this._data = data;
        return this;
    },

    /**
     * Hash data
     */
    hash: function () {
        throw new Error(NEED_IMPLEMENT_MESSAGE);
    }
});

module.exports = BaseHash;

/**
 * Message for errors when some method is not implemented
 * @type {String}
 * @private
 */
var NEED_IMPLEMENT_MESSAGE = "This method need to implement";

/**
 * BaseHash class
 * @constructor
 */
function BaseHash(options) {
    if (!options) {
        throw new Error('You must provide data');
    }

    if (Object.prototype.toString.call(options) === '[object Object]') {
        this.setData(options.data);
    } else {
        this.setData(options);
    }
}

BaseHash.prototype = Object.create({
    constructor: BaseHash,

    /**
     * Get data from current hashing instance
     * @returns {*}
     */
    getData: function () {
        return this._data;
    },

    /**
     * Set data that need to hash
     * @param {*} data
     * @returns {BaseHash}
     */
    setData: function (data) {
        this._data = data;
        return this;
    },

    /**
     * Hash data
     */
    hash: function () {
        throw new Error(NEED_IMPLEMENT_MESSAGE);
    }
});

module.exports = BaseHash;

Fix bug with array and object data


module.exports = {
  content: [
    './app/**/*.{html,hbs,js}'
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {}
  },
  plugins: [
    require('@tailwindcss/forms')
  ]
}

module.exports = {
  content: [
    './app/**/*.{html,hbs,js}'
  ],
  darkMode: 'class', // or 'media' or 'class'
  theme: {
    extend: {}
  },
  plugins: [
    require('@tailwindcss/forms')
  ]
}

Update deprecated Tailwind config value


$(function() {
  var $tabs = $('#search-results-tabs'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getDefaultSearchTabIndex(), scrollOnload: true });
  }

  function getDefaultSearchTabIndex(){
    var tabIds = $('.search-navigation a').map(function(i, el){
          return $(el).attr('href').split('#').pop();
        }),
        $defaultTab = $('input[name=tab]'),
        selectedTab = $.inArray($defaultTab.val(), tabIds);

    return selectedTab > -1 ? selectedTab : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForm);
  }

  function updateSearchForm(e, hash){
    if(typeof hash !== 'undefined'){
      var $defaultTab = $('input[name=tab]');

      if($defaultTab.length === 0){
        $defaultTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($defaultTab);
      }

      $defaultTab.val(hash);
    }
  }
});

$(function() {
  var $tabs = $('#search-results-tabs'),
      $searchForm = $('.js-search-hash');

  if($tabs.length > 0){
    $tabs.tabs({ 'defaultTab' : getDefaultSearchTabIndex(), scrollOnload: true });
  }

  function getDefaultSearchTabIndex(){
    var tabIds = $('.search-navigation a').map(function(i, el){
      return $(el).attr('href').split('#').pop();
    });
    $defaultTab = $('input[name=tab]');
    selectedTab = $.inArray($defaultTab.val(), tabIds);

    return selectedTab > -1 ? selectedTab : 0;
  }

  if($searchForm.length){
    $tabs.on('tabChanged', updateSearchForm);
  }

  function updateSearchForm(e, hash){
    if(typeof hash !== 'undefined'){
      var $defaultTab = $('input[name=tab]');

      if($defaultTab.length === 0){
        $defaultTab = $('<input type="hidden" name="tab">');
        $searchForm.prepend($defaultTab);
      }

      $defaultTab.val(hash);
    }
  }
});

Tidy up whitespace and line-endings


import Ember from 'ember';
import EmberValidations from 'ember-validations';

export default Ember.Component.extend(EmberValidations, {
  classNames: ["edit-interface"],
  validations: {
    'changes': {
      changeset: true,
    }
  },
  /* This is only required because ember-validations doesn't correctly observe child errors */
  runValidations: function() {
    this.validate().then(function() {}, function() {});
  }.observes("hup.at"),
  actions: {
    addNewMeaning() {
      this.get("changes.meanings").pushObject(new Object({action: "add", def: "", example: "", labels: []}));
      this.validate();
    },
    addNewSeq() {
      this.get("changes.seqs").pushObject(new Object({action: "add", text: "", labels: []}));
      this.validate();
    },
  }
});

import Ember from 'ember';
import EmberValidations from 'ember-validations';

export default Ember.Component.extend(EmberValidations, {
  classNames: ["edit-interface"],
  validations: {
    'changes': {
      changeset: true,
    }
  },
  /* This is only required because ember-validations doesn't correctly observe child errors */
  runValidations: function() {
    this.validate().then(function() {}, function() {});
  }.observes("hup.at"),
  actions: {
    addNewMeaning() {
      this.get("changes.meanings").pushObject(new Object({action: "add", def: "", example: "", labels: [], synonyms: []}));
      this.validate();
    },
    addNewSeq() {
      this.get("changes.seqs").pushObject(new Object({action: "add", text: "", labels: []}));
      this.validate();
    },
  }
});

Add new meaning should have synonyms added too (empty array)


import React from 'react'
import MuiDrawer from 'material-ui/Drawer'
import MenuItem from 'material-ui/MenuItem'
import muiThemeable from 'material-ui/styles/muiThemeable';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as drawerActions from './dux'

class Drawer extends React.Component {
  render() {
    const theme = this.props.muiTheme
    const topBarStyle = {
     backgroundColor: theme.palette.primary1Color,
     color: theme.appBar.textColor

    }
    return (
      <div>
        <MuiDrawer open={this.props.drawer.isOpen}>
          <MenuItem
            primaryText='Apps'
            onTouchTap={this.props.actions.closeDrawer}
            innerDivStyle={topBarStyle}
          />
          <MenuItem primaryText='Counter' />
          <MenuItem primaryText='Todo List' />
        </MuiDrawer>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
    drawer: state.drawer
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(drawerActions, dispatch)
  }
}

export default muiThemeable()(connect(mapStateToProps, mapDispatchToProps)(Drawer))

import React from 'react'
import MuiDrawer from 'material-ui/Drawer'
import MenuItem from 'material-ui/MenuItem'
import { Link } from 'react-router-dom'
import muiThemeable from 'material-ui/styles/muiThemeable';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as drawerActions from './dux'

class Drawer extends React.Component {
  toggle(open, reason) {
    console.log('Toggling to ', open, ' because ', reason)
    if (open) {
      this.props.actions.openDrawer()
    } else {
      this.props.actions.closeDrawer()
    }
  }

  render() {
    const theme = this.props.muiTheme
    const topBarStyle = {
     backgroundColor: theme.palette.primary1Color,
     color: theme.appBar.textColor
    }
    return (
      <div>
        <MuiDrawer
          open={this.props.drawer.isOpen}
          docked={false}
          onRequestChange={this.toggle.bind(this)}
        >
          <MenuItem
            primaryText='Apps'
            onTouchTap={this.props.actions.closeDrawer}
            innerDivStyle={topBarStyle}
          />
          <MenuItem
            primaryText='Counter'
            containerElement={<Link to='/counter' />}
            onTouchTap={this.props.actions.closeDrawer}
          />
          <MenuItem
            primaryText='Todo List'
            containerElement={<Link to='/todo' />}
            onTouchTap={this.props.actions.closeDrawer}
          />
        </MuiDrawer>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
    drawer: state.drawer
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(drawerActions, dispatch)
  }
}

export default muiThemeable()(connect(mapStateToProps, mapDispatchToProps)(Drawer))

Make the drawer behave like a mobile drawer Close when you click away. Grey out the background when the menu is up.


const cheerio = require('cheerio');
const url = require('url');

function hrefIsIcon(href) {
    return /\w\.(png|jpg|ico)/.test(href);
}

function getIconLinks(rootUrl, dom) {
    var $ = cheerio.load(dom);
    const icons = [];
    $('link').each(function(i, elem) {
        const href = $(this).attr('href');
        const resolved = url.resolve(rootUrl, href);
        if (!hrefIsIcon(resolved)) {
            return;
        }
        icons.push(resolved);
    });
    return icons;
}

module.exports = getIconLinks;

const cheerio = require('cheerio');
const url = require('url');

function hrefIsIcon(href) {
    return /((icon.*\.(png|jpg))|(\w+\.ico))$/.test(href);
}

function getIconLinks(rootUrl, dom) {
    var $ = cheerio.load(dom);
    const icons = [];
    $('link').each(function(i, elem) {
        const href = $(this).attr('href');
        const resolved = url.resolve(rootUrl, href);
        if (!hrefIsIcon(resolved)) {
            return;
        }
        icons.push(resolved);
    });
    return icons;
}

module.exports = getIconLinks;

Check for `icon` in icon path


'use strict';

$(function() {
    function update() {
        var name = $('#boxes #name').val();
        var id = $('#boxes #id').val();
        if (name || id) {
            if (!name) { name = "{name}"; }
            if (!id) { id = "{id}"; }
            $('#links').show();
            $('.resource').each(function() {
                var link = $(this).attr('data-format').replace('{name}', name)
                                                      .replace('{id}', id);
                $(this).attr('href', link);
                $(this).children('span.name').text(name);
                $(this).children('span.id').text(id);
            });
        } else {
            $('#links').hide();
        }
    }

    $('#name').on('input propertychange paste', update);
    $('#id').on('input propertychange paste', update);
    $('#name').focus();
});

Add 'use strict' to javascript


import { Immutable } from 'nuclear-js';
import { getters as entityGetters } from '../entity';

const DEFAULT_VIEW_ENTITY_ID = 'group.default_view';

export const currentView = [
  'currentView',
];

export const views = [
  entityGetters.entityMap,
  entities => entities.filter(entity => entity.domain === 'group' &&
                                        entity.attributes.view &&
                                        entity.entityId !== DEFAULT_VIEW_ENTITY_ID),
];

function addToMap(map, entities, groupEntity) {
  groupEntity.attributes.entity_id.forEach(entityId => {
    if (map.has(entityId)) return;

    const entity = entities.get(entityId);

    if (!entity || entity.attributes.hidden) return;

    map.set(entityId, entity);

    if (entity.domain === 'group') {
      addToMap(map, entities, entity);
    }
  });
}

export const currentViewEntities = [
  entityGetters.entityMap,
  currentView,
  (entities, view) => {
    let viewEntity;

    if (view) {
      viewEntity = entities.get(view);
    } else {
      // will be undefined if entity does not exist
      viewEntity = entities.get(DEFAULT_VIEW_ENTITY_ID);
    }

    if (!viewEntity) {
      return entities.filter(entity => !entity.attributes.hidden);
    }

    return (new Immutable.Map()).withMutations(map => {
      addToMap(map, entities, viewEntity);
    });
  },
];

import { Immutable } from 'nuclear-js';
import { getters as entityGetters } from '../entity';

const DEFAULT_VIEW_ENTITY_ID = 'group.default_view';

export const currentView = [
  'currentView',
];

export const views = [
  entityGetters.entityMap,
  entities => entities.filter(entity => entity.domain === 'group' &&
                                        entity.attributes.view &&
                                        entity.entityId !== DEFAULT_VIEW_ENTITY_ID),
];

function addToMap(map, entities, groupEntity, recurse = true) {
  groupEntity.attributes.entity_id.forEach(entityId => {
    if (map.has(entityId)) return;

    const entity = entities.get(entityId);

    if (!entity || entity.attributes.hidden) return;

    map.set(entityId, entity);

    if (entity.domain === 'group' && recurse) {
      addToMap(map, entities, entity, false);
    }
  });
}

export const currentViewEntities = [
  entityGetters.entityMap,
  currentView,
  (entities, view) => {
    let viewEntity;

    if (view) {
      viewEntity = entities.get(view);
    } else {
      // will be undefined if entity does not exist
      viewEntity = entities.get(DEFAULT_VIEW_ENTITY_ID);
    }

    if (!viewEntity) {
      return entities.filter(entity => !entity.attributes.hidden);
    }

    return (new Immutable.Map()).withMutations(map => {
      addToMap(map, entities, viewEntity);
    });
  },
];

Allow embedding groups in groups


import columnAccessor from './columnAccessor';

describe('Util:columnAccessor', () => {
    it('should run this test', () => {
        const accessor = columnAccessor({ column: 'foo' });
        const d = { foo: 5 };
        expect(accessor(d)).toEqual(5);
    });
});

import columnAccessor from './columnAccessor';

describe('Util:columnAccessor', () => {
    it('should access the value of a column', () => {
        const accessor = columnAccessor({ column: 'foo' });
        const d = { foo: 5 };
        expect(accessor(d)).toEqual(5);
    });
    it('should apply a number formatter', () => {
        const accessor = columnAccessor({ column: 'foo', format: '.0s' });
        const d = { foo: 10000 };
        expect(accessor(d)).toEqual('10k');
    });
});

Add test for number formatter


import * as types from '../actions/action-types';

export default (state = [], action) => {
    switch (action.type) {
    case types.CREATE_COURSE:
        return [
            ...state,
            Object.assign({}, action.course),
        ];

    default:
        return state;
    }
};

import * as types from '../actions/action-types';

export default (state = [], action) => {
    switch (action.type) {
    case types.LOAD_COURSES_SUCCESS:
        return action.courses;

    default:
        return state;
    }
};

Load courses in the reducer


Add a few new Core Editors


chrome
  .runtime
  .sendMessage({ msg: 'getStatus' }, function (response) {
    if (response.status) {
      let storage = chrome.storage.local;
      storage.get(['type', 'freq', 'q', 'gain'], function (items) {

        type = items.type || 'highshelf';
        freq = items.freq || '17999';
        q = items.q || '0';
        gain = items.gain || '-70';

        let actualCode = `
          var st_type = '${type}',
            st_freq = '${freq}',
            st_q = '${q}',
            st_gain = '${gain}';
          console.log('Settings loaded...');
        `;

        let script = document.createElement('script');
        script.textContent = actualCode;
        (document.head || document.documentElement).appendChild(script);
        script.parentNode.removeChild(script);
      });

      let s = document.createElement('script');
      s.src = chrome.extension.getURL('intercept.js');
      s.onload = function () {
        this.parentNode.removeChild(this);
      };

      (document.head || document.documentElement).appendChild(s);
    }
  });

function createScript(property, source) {
  let scriptElement = document.createElement('script');
  scriptElement['property'] = source;
  return scriptElement;
}

function injectScript(script) {
  (document.head || document.documentElement).appendChild(script);
}

chrome
  .runtime
  .sendMessage({ msg: 'getStatus' }, function (response) {
    if (response.status) {
      let storage = chrome.storage.local;
      storage.get(['type', 'freq', 'q', 'gain'], function (items) {

        type = items.type || 'highshelf';
        freq = items.freq || '17999';
        q = items.q || '0';
        gain = items.gain || '-70';

        let actualCode = `
          var st_type = '${type}',
            st_freq = '${freq}',
            st_q = '${q}',
            st_gain = '${gain}';
          console.log('Settings loaded...');
        `;

        let script = createScript('textConetent', actualCode);
        injectScript(script);
        script.parentNode.removeChild(script);
      });

      let script = createScript('src', chrome.extension.getURL('intercept.js'));
      script.onload = function () {
        this.parentNode.removeChild(this);
      };
      injectScript(script);
    }
  });

Make script injection more abstract


module.exports = {
    loadPlugins: false
  , cms_methods: true
  , plugins: []
  , pluginConfigs: {}
  , metadata: {
        siteTitle: "No name"
      , description: "Foo"
    }
  , corePlugins: [
        "bloggify-router"
      , "bloggify-plugin-manager"
    ]
  , pluginConfigs: {
        "bloggify-plugin-manager": {
            plugins: []
        }
    }
  , server: {
        errorPages: false
      , public: "public"
      , port: process.env.PORT || 8080
      , host: null
      , csrf: true
    }
  , theme: {
        path: "theme"
      , viewsPath: "views"
      , public: "public"
    }
  , adapter: {
        paths: {
            articles: "content/articles"
          , pages: "content/pages"
        }
      , parse: {}
    }
  , paths:  {
        // The bloggify directory (by default the root)
        bloggify: "/"

        // The config file: `bloggify.js`
      , config: "/bloggify.js"

        // Where to store the plugins
      , plugins: "/node_modules"

      , publicMain: "/!/bloggify/public/"
      , publicCore: "/!/bloggify/core/"
      , publicTheme: "/!/bloggify/theme/"
      , cssUrls: "/!/bloggify/css-urls/"
    }
};

module.exports = {
    loadPlugins: false
  , cms_methods: true
  , plugins: []
  , pluginConfigs: {}
  , metadata: {
        siteTitle: "No name"
      , description: "Foo"
    }
  , corePlugins: [
        "bloggify-router"
      , "bloggify-plugin-manager"
    ]
  , pluginConfigs: {
        "bloggify-plugin-manager": {
            plugins: []
        }
    }
  , server: {
        errorPages: false
      , public: "public"
      , port: process.env.PORT || 8080
      , host: null
      , csrf: true
    }
  , theme: {
        path: "theme"
      , viewsPath: "views"
      , public: "public"
    }
  , adapter: {
        paths: {
            articles: "content/articles"
          , pages: "content/pages"
        }
      , parse: {}
    }
  , paths:  {
        // The bloggify directory (by default the root)
        bloggify: "/"

        // The config file: `bloggify.js`
      , config: "/bloggify.js"

        // Where to store the plugins
      , plugins: "/node_modules"

      , publicMain: "/!/bloggify/public/"
      , publicCore: "/!/bloggify/core/"
      , publicTheme: "/!/bloggify/theme/"
      , cssUrls: "/!/bloggify/css-urls/"
    }
  , bundler: {
        aliases: {
            "bloggify": `${__dirname}/client/index.js`
        }
    }
};

Add an alias for bloggify on the client


const babel = require('gulp-babel');
const gulp = require('gulp');
const pug = require('gulp-pug');
const uglify = require('gulp-uglify');

gulp.task('build', () => {
	gulp.src('app/src/pug/*')
		.pipe(pug())
		.pipe(gulp.dest('app/dist'));

	gulp.src('app/src/js/*')
		.pipe(babel({presets: ['es2015']}))
		.pipe(uglify())
		.pipe(gulp.dest('app/dist'));
});

const babel = require('gulp-babel');
const cssnano = require('cssnano');
const gulp = require('gulp');
const postcss = require('gulp-postcss');
const pug = require('gulp-pug');
const uglify = require('gulp-uglify');

gulp.task('build', () => {
	gulp.src('app/src/pug/*')
		.pipe(pug())
		.pipe(gulp.dest('app/dist'));

	gulp.src('app/src/css/*')
		.pipe(postcss([cssnano]))
		.pipe(gulp.dest('app/dist'));

	gulp.src('app/src/js/*')
		.pipe(babel({presets: ['es2015']}))
		.pipe(uglify())
		.pipe(gulp.dest('app/dist'));
});

Add css processing to `gulp build`


var gulp        = require('gulp'),
    browserSync = require('browser-sync'),
    sass        = require('gulp-sass'),
    bower       = require('gulp-bower'),
    notify      = require('gulp-notify'),
    reload      = browserSync.reload,
    bs          = require("browser-sync").create(),
    Hexo = require('hexo'),
    hexo = new Hexo(process.cwd(), {});

var src = {
    scss: './scss/',
    css:  './source/css',
    ejs: 'layout'
};

// Static Server + watching scss/html files
gulp.task('serve', ['sass'], function() {

    // init starts the server
    bs.init({
        baseDir: "../../public"
    });

    // hexo.call('generate', {}).then(function(){
    //   console.log('Generating Files');
    // });

    // Now call methods on bs instead of the
    // main browserSync module export
    bs.reload("*.html");
    bs.reload("*.css");
});

// Compile sass into CSS
gulp.task('sass', function() {
    // gulp.src(src.scss + "/*/*.scss")
    gulp.src(src.scss + "{,*}/*.scss")
        .pipe(sass({}))
        // .pipe(gulp.dest(src.css))
        .pipe(gulp.dest('./css/'))
        .pipe(reload({stream: true}));
});

gulp.task('sass:watch', function () {
  gulp.watch('./scss/**/*.scss', ['sass']);
});

gulp.task('bower', function() {
  return bower()
    .pipe(gulp.dest('source/lib'))
});

gulp.task('default', ['serve']);

var gulp        = require('gulp'),
    browserSync = require('browser-sync'),
    sass        = require('gulp-sass'),
    bower       = require('gulp-bower'),
    notify      = require('gulp-notify'),
    reload      = browserSync.reload,
    bs          = require("browser-sync").create(),
    Hexo = require('hexo'),
    hexo = new Hexo(process.cwd(), {});

var src = {
    scss: './scss/',
    css:  './source/css',
    ejs: 'layout'
},
watchFiles = [
    './scss/*.scss',
    '*/*.ejs'
];

// Static Server + watching scss/html files
gulp.task('serve', ['sass:watch'], function() {

    // init starts the server
    bs.init(watchFiles, {
        server: {
            baseDir: "../../public"
        },
        logLevel: "debug"
    });

    hexo.init();

});

// Compile sass into CSS
gulp.task('sass', function() {
    // gulp.src(src.scss + "/*/*.scss")
    gulp.src(src.scss + "{,*}/*.scss")
        .pipe(sass({}))
        // .pipe(gulp.dest(src.css))
        .pipe(gulp.dest('../../source/css/'))
        .pipe(reload({stream: true}));
});

gulp.task('sass:watch', function () {
  gulp.watch('./scss/**/*.scss', ['sass']);
});

gulp.task('bower', function() {
  return bower()
    .pipe(gulp.dest('source/lib'))
});

gulp.task('default', ['serve']);

Fix changes so it'll start tracking css watches


'use strict';

var gulp = require('gulp');
var sass = require('gulp-sass');
var sassLint = require('gulp-sass-lint');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
var babel = require('gulp-babel');

gulp.task('babel', function() {
    return gulp.src('app/Resources/client/jsx/project/details/*.jsx')
        .pipe(babel({
            presets: ['es2015', 'react']
        }))
        .pipe(concat('details.js'))
        .pipe(gulp.dest('web/assets/js/project'));
});

gulp.task('sass', function () {
  return gulp.src('web/assets/scss/*.scss')
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(sourcemaps.write('.', {includeContent: false}))
    .pipe(gulp.dest('web/assets/css'));
});

gulp.task('sassLint', function() {
  gulp.src('web/assets/scss/*.s+(a|c)ss')
    .pipe(sassLint())
    .pipe(sassLint.format())
    .pipe(sassLint.failOnError());
});

gulp.task('css', ['sassLint','sass'], function () {
});

gulp.task('default', ['css'], function() {
  // place code for your default task here
});

'use strict';

var gulp = require('gulp');
var sass = require('gulp-sass');
var sassLint = require('gulp-sass-lint');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
var babel = require('gulp-babel');
require('babel-core/register');
// testing
var mocha = require('gulp-mocha');

gulp.task('babel', function() {
    return gulp.src('app/Resources/client/jsx/project/details/*.jsx')
        .pipe(babel({
            presets: ['es2015', 'react']
        }))
        .pipe(concat('details.js'))
        .pipe(gulp.dest('web/assets/js/project'));
});

gulp.task('test', function() {
    return gulp.src('tests/jsx/**/*.js', {read: false})
        .pipe(mocha({reporter: 'spec', useColors: true}))
});

gulp.task('sass', function () {
  return gulp.src('web/assets/scss/*.scss')
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(sourcemaps.write('.', {includeContent: false}))
    .pipe(gulp.dest('web/assets/css'));
});

gulp.task('sassLint', function() {
  gulp.src('web/assets/scss/*.s+(a|c)ss')
    .pipe(sassLint())
    .pipe(sassLint.format())
    .pipe(sassLint.failOnError());
});

gulp.task('css', ['sassLint','sass'], function () {
});

gulp.task('default', ['css'], function() {
  // place code for your default task here
});

Add test task to gulp


/* global Promise */
var Reference = require('../models/reference');
var reference = new Reference();
// var References = require('../models/references');
// var references = new References();
var Promisify = require('../models/promisify');
var promiseCollection = new Promisify.Collection();
promiseCollection.url = reference.url;

function saveReference(model) {
	reference.clear({
		silent: true
	});

	new Promise(function(resolve, reject) {
		reference.save(model.toJSON(), {
			success: resolve,
			error: reject
		});
	}).then(function success(argument) {
		console.log("Success", argument);
	}, function(error) {
		console.log("error", error);
	});
}

function loadReferences(collection) {
	promiseCollection.reset(collection.toJSON());
	promiseCollection.fetch().then(function success(jsonResponse) {
		console.log('success promisify fetch', jsonResponse);
		collection.reset(jsonResponse);
	}, function error(err) {
		console.log("error promisify fetch", err);
	});
	// new Promise(function(resolve, reject) {
	// 	collection.fetch({
	// 		success: resolve,
	// 		error: reject
	// 	});
	// }).then(function success(argument) {
	// 	console.log('success promise fetch', argument);
	// 	collection.reset(argument.toJSON());
	// }, function error(err) {
	// 	console.log("error promise fetch", err);
	// });
}
module.exports = {
	save: saveReference,
	loadAll: loadReferences
};

/* global Promise */
var Reference = require('../models/reference');
var References = require('../models/references');
var Promisify = require('../models/promisify');
/*Instances of both model and collection of virtual machines.*/
var promiseReference = Promisify.Convert.Model(new Reference());
var promiseReferences = Promisify.Convert.Collection(new References());

function saveReference(model) {
	reference.clear({
		silent: true
	});

	new Promise(function(resolve, reject) {
		reference.save(model.toJSON(), {
			success: resolve,
			error: reject
		});
	}).then(function success(argument) {
		console.log("Success", argument);
	}, function(error) {
		console.log("error", error);
	});
}

//Load all the references of the application
function loadReferences() {
	promiseReferences.reset(null, {silent: true});
	return promiseReferences.fetch();
}
module.exports = {
	save: saveReference,
	loadAll: loadReferences
};

Add the promisification in the service in order to be treated by the view.


const API_URL = 'https://d07a5522.ngrok.io'

export const saveTokenForPush = subscription =>
    fetch(`${API_URL}/token`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        mode: 'no-cors',
        body: JSON.stringify(subscription)
    })

const API_URL = 'https://build-notification-api.herokuapp.com'

export const saveTokenForPush = subscription =>
    fetch(`${API_URL}/token`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        mode: 'no-cors',
        body: JSON.stringify(subscription)
    })

Update the url for the build-notification-api


importScripts("/app/bower_components/videogular-questions/questions-worker.js");

loadAnnotations({
	"first-question": {
		time: 4,
		questions: [
			{
				id: "first-question",
				type: "single",
				question: "What is the moon made of?",
				options: [
					{
						name: "cheese"
					},
					{
						name: "cheeese"
					},
					{
						name: "cheeeeeese"
					}
				]
			},
			{
				id: "check-question",
				type: "single",
				question: "Answer incorrect, do you want to review the video",
				options: [
					{
						name: "Yes",
						action: function(video) {
							video.setTime(0);
						}
					},
					{
						name: "No"
					}
				],
				condition: function(questions, result) {
					// show if the answer to the previous question is not "cheese"
					return result !== "cheese";
				}
			}
		]
	}
});

importScripts("/app/bower_components/videogular-questions/questions-worker.js");

loadAnnotations({
	"first-question": {
		time: 4,
		questions: [
			{
				id: "first-question",
				type: "single",
				question: "What is the moon made of?",
				options: [
					{
						name: "cheese"
					},
					{
						name: "cheeese"
					},
					{
						name: "cheeeeeese"
					}
				]
			},
			{
				id: "check-question",
				type: "single",
				question: "Answer incorrect, do you want to review the video",
				options: [
					{
						name: "Yes"
					},
					{
						name: "No"
					}
				],
				action: function(result, video) {
					if (result === "Yes") {
						video.setTime(0);
					}
				},
				condition: function(questions, result) {
					// show if the answer to the previous question is not "cheese"
					return result !== "cheese";
				}
			}
		]
	}
});

Move the action to the outer question object


var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-clears/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-clears/tachyons-clears.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_clearfix.css', 'utf8')
var navDocs = fs.readFileSync('./templates/nav_docs.html', 'utf8')

var template = fs.readFileSync('./templates/docs/clearfix/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS,
  navDocs: navDocs
})

fs.writeFileSync('./docs/layout/clearfix/index.html', html)

var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-clears/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-clears/tachyons-clears.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_clearfix.css', 'utf8')
var navDocs = fs.readFileSync('./templates/nav_docs.html', 'utf8')
var siteFooter = fs.readFileSync('./templates/footer.html', 'utf8')

var template = fs.readFileSync('./templates/docs/clearfix/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS,
  navDocs: navDocs,
  siteFooter: siteFooter
})

fs.writeFileSync('./docs/layout/clearfix/index.html', html)

Add site footer to each documentation generator


//Get the begin and end coordinates of selction
function getSelectionEnds() {
    var success = false, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    var sel = window.getSelection()
    if (sel.rangeCount > 0){
        var range_rects = sel.getRangeAt(0).getClientRects();
        if (range_rects.length > 0){ //fail if zero
            success = true;
            var rect_first = range_rects[0];
            x1 = rect_first.left;
            y1 = rect_first.top;
            var rect_last = range_rects[range_rects.length - 1];
            x2 = rect_last.right;
            y2 = rect_last.bottom;
        }
    }
    return {success: success, x1: x1, y1: y1, x2: x2, y2: y2};
}

document.addEventListener("mouseup", function(event) {
    chrome.runtime.sendMessage(getSelectionEnds(), function(response) {});
});

//Get the begin and end coordinates of selction
function getSelectionEnds() {
    var success = false, x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    var sel = window.getSelection()
    if (sel.rangeCount > 0){
        var range_rects = sel.getRangeAt(0).getClientRects();
        if (range_rects.length > 0){ //fail if zero
            success = true;
            var rect_first = range_rects[0];
            x1 = rect_first.left;
            y1 = rect_first.top;
            var rect_last = range_rects[range_rects.length - 1];
            x2 = rect_last.right;
            y2 = rect_last.bottom;
        }
    }
    return {success: success, x1: x1, y1: y1, x2: x2, y2: y2};
}

// A side note: use event listener, not inline event handlers (e.g., "onclick")
// new inline events could overwrite any existing inline event handlers

document.addEventListener("mouseup", function(event) {
    chrome.runtime.sendMessage(getSelectionEnds(), function(response) {});
});

window.addEventListener("resize", function(event) {
    chrome.runtime.sendMessage(getSelectionEnds(), function(response) {});
});

Add Event Listener to window.resize