'use strict';

var Platform = require('Platform');
var I18n = require('./vendor/i18n');
var { RNI18n } = require('react-native').NativeModules;

if (Platform.OS === 'android') {
  RNI18n.getCurrentLocale( function(locale) {
    I18n.locale = locale.replace(/_/, '-');
  });
} else {
  I18n.locale = RNI18n.locale.replace(/_/, '-');
}

module.exports = I18n;

'use strict';

let { Platform, NativeModules/RNI18N } = require('react-native')
let I18n = require('./vendor/i18n')

if (Platform.OS === 'android') {
  RNI18n.getCurrentLocale( function(locale) {
    I18n.locale = locale.replace(/_/, '-');
  });
} else {
  I18n.locale = RNI18n.locale.replace(/_/, '-');
}

module.exports = I18n;

Update requires to be compatible with ES6


'use strict';

const redis = require('redis'),
    co = require('co'),
    coRedis = require('co-redis'),
    Koa = require('koa');

const app = exports.app = new Koa(),
    client  = redis.createClient(
      process.env.REDIS_PORT,
      process.env.REDIS_HOST
    ),
    dbCo = coRedis(client);

if (process.env.REDIS_SECRET) {
  client.auth(process.env.REDIS_SECRET);
}

client.on('error', function (err) {
  console.log('Redis client error: ' + err);
});

app.use(co.wrap(function* (ctx) {

  var indexkey;

  if (ctx.request.query.index_key) {
    indexkey = process.env.APP_NAME +':'+ ctx.request.query.index_key;
  } else {
    indexkey = yield dbCo.get(process.env.APP_NAME +':current');
    indexkey = process.env.APP_NAME + ':' + indexkey;
  }
  var index = yield dbCo.get(indexkey);

  if (index) {
    ctx.body = index;
  } else {
    ctx.status = 404;
  }
}));

app.listen(process.env.PORT || 3000);

'use strict';

const redis = require('redis'),
    co = require('co'),
    coRedis = require('co-redis'),
    Koa = require('koa');

const app = exports.app = new Koa(),
    client  = redis.createClient(
      process.env.REDIS_PORT,
      process.env.REDIS_HOST
    ),
    dbCo = coRedis(client);

if (process.env.REDIS_SECRET) {
  client.auth(process.env.REDIS_SECRET);
}

client.on('error', function (err) {
  console.log('Redis client error: ' + err);
});

app.use(co.wrap(function* (ctx) {

  var indexkey;

  if (ctx.request.query.index_key) {
    indexkey = process.env.APP_NAME +':'+ ctx.request.query.index_key;
  } else {
    indexkey = process.env.APP_NAME +':current-content';
  }
  var index = yield dbCo.get(indexkey);

  if (index) {
    ctx.body = index;
  } else {
    ctx.status = 404;
  }
}));

app.listen(process.env.PORT || 3000);

Update to work with current-content key


;(function ( factory ) {
    "use strict";

    if ("function" === typeof define && define.amd) {
        define(["knockout"], factory);
    } else if ("undefined" !== typeof module) {
        factory(require("knockout"));
    } else {
        factory(window.ko);
    }

})(function ( ko ) {
    "use strict";

    return ko;
});

;(function ( factory ) {
    "use strict";

    if ("function" === typeof define && define.amd) {
        define(["knockout"], factory);
    } else if ("undefined" !== typeof module) {
        factory(require("knockout"));
    } else {
        factory(window.ko);
    }

})(function ( ko ) {
    "use strict";

    var PropertyModel, PropertyGridViewModel;

    PropertyModel = (function ( ) {
        var proto;

        function PropertyModel ( options ) {
            // TODO: implement
            this.options = options;

        }

        proto = PropertyModel.prototype;

        return PropertyModel;
    })();

    PropertyGridViewModel = (function (  ) {
        var proto;

        function PropertyGridViewModel ( params ) {
            // TODO: implement
            this.params = params;

        }

        proto = PropertyGridViewModel.prototype;

        return PropertyGridViewModel;
    })();

    ko.components.register("property-grid", {
        viewModel: PropertyGridViewModel
    ,   synchronous: true
    ,   template: "<!-- TODO -->"
    });

    ko.property_grid = {
        PropertyModel: PropertyModel
    };

    return ko;
});

Add model skeletons and setup ko component


var isString = require('./lib/isstring')

module.exports = function frameguard (options) {
  options = options || {}

  var domain = options.domain
  var action = options.action

  var directive
  if (action === undefined) {
    directive = 'SAMEORIGIN'
  } else if (isString(action)) {
    directive = action.toUpperCase()
  }

  if (directive === 'ALLOWFROM') {
    directive = 'ALLOW-FROM'
  } else if (directive === 'SAME-ORIGIN') {
    directive = 'SAMEORIGIN'
  }

  if (['DENY', 'ALLOW-FROM', 'SAMEORIGIN'].indexOf(directive) === -1) {
    throw new Error('X-Frame must be undefined, "DENY", "ALLOW-FROM", or "SAMEORIGIN"')
  }

  if (directive === 'ALLOW-FROM') {
    if (!isString(domain)) {
      throw new Error('X-Frame: ALLOW-FROM requires a second parameter')
    }
    if (!domain.length) {
      throw new Error('X-Frame: domain must not be empty')
    }
    directive = 'ALLOW-FROM ' + domain
  }

  return function frameguard (req, res, next) {
    res.setHeader('X-Frame-Options', directive)
    next()
  }
}

var isString = require('./lib/isstring')

module.exports = function frameguard (options) {
  options = options || {}

  var domain = options.domain
  var action = options.action

  var directive
  if (action === undefined) {
    directive = 'SAMEORIGIN'
  } else if (isString(action)) {
    directive = action.toUpperCase()
  }

  if (directive === 'ALLOWFROM') {
    directive = 'ALLOW-FROM'
  } else if (directive === 'SAME-ORIGIN') {
    directive = 'SAMEORIGIN'
  }

  if (['DENY', 'ALLOW-FROM', 'SAMEORIGIN'].indexOf(directive) === -1) {
    throw new Error('action must be undefined, "DENY", "ALLOW-FROM", or "SAMEORIGIN".')
  }

  if (directive === 'ALLOW-FROM') {
    if (!isString(domain)) {
      throw new Error('ALLOW-FROM action requires a domain parameter.')
    }
    if (!domain.length) {
      throw new Error('domain parameter must not be empty.')
    }
    directive = 'ALLOW-FROM ' + domain
  }

  return function frameguard (req, res, next) {
    res.setHeader('X-Frame-Options', directive)
    next()
  }
}

Update error messages to be more descriptive and correct


'use strict';
var execFile = require('child_process').execFile;
var wifiName = require('wifi-name');

function getPassword(ssid, cb) {
	var cmd = 'security';
	var args = ['find-generic-password', '-D', 'AirPort network password', '-ga', ssid];
	var ret;

	execFile(cmd, args, function (err, stdout, stderr) {
		if (err && /The specified item could not be found in the keychain/.test(err.message)) {
			err.message = 'Your network doesn\'t have a password';
		}

		if (err) {
			cb(err);
			return;
		}

		ret = /^\s*password: "(.+)"\s*$/gm.exec(stderr);
		ret = ret && ret.length ? ret[1] : null;

		if (!ret) {
			cb(new Error('Could not get password'));
			return;
		}

		cb(null, ret);
	});
}

module.exports = function (ssid, cb) {
	if (process.platform !== 'darwin') {
		throw new Error('Only OS X systems are supported');
	}

	if (ssid && typeof ssid !== 'function') {
		getPassword(ssid, cb);
		return;
	} else if (ssid && !cb) {
		cb = ssid;
	}

	wifiName(function (err, name) {
		if (err) {
			cb(err);
			return;
		}

		getPassword(name, cb);
	});
};

'use strict';
var execFile = require('child_process').execFile;
var wifiName = require('wifi-name');

function getPassword(ssid, cb) {
	var cmd = 'security';
	var args = ['find-generic-password', '-D', 'AirPort network password', '-wa', ssid];

	execFile(cmd, args, function (err, stdout) {
		stdout = stdout.trim();

		if (err && /The specified item could not be found in the keychain/.test(err.message)) {
			err.message = 'Your network doesn\'t have a password';
		}

		if (err) {
			cb(err);
			return;
		}

		if (!stdout) {
			cb(new Error('Could not get password'));
			return;
		}

		cb(null, stdout);
	});
}

module.exports = function (ssid, cb) {
	if (process.platform !== 'darwin') {
		throw new Error('Only OS X systems are supported');
	}

	if (ssid && typeof ssid !== 'function') {
		getPassword(ssid, cb);
		return;
	} else if (ssid && !cb) {
		cb = ssid;
	}

	wifiName(function (err, name) {
		if (err) {
			cb(err);
			return;
		}

		getPassword(name, cb);
	});
};

Use `-w` flag to display password directly


'use strict';

var gutil = require('gulp-util');
var through = require('through2');
var juice = require('juice');

module.exports = function(opt){
    return through.obj(function (file, enc, cb) {
        if (!opt) {
            opt = {};
        }

        // 'url' option is required
        // set it automatically if not provided
        if(!opt.url) {
            opt.url = 'file://' + file.path;
        }

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError('gulp-inline-css', 'Streaming not supported'));
            return cb();
        }

        juice.juiceContent(file.contents, opt, function(err, html) {
            if (err) {
                this.emit('error', new gutil.PluginError('gulp-inline-css', err));
            }

            file.contents = new Buffer(String(html));

            this.push(file);

            return cb();
        }.bind(this));
    });
};

var gutil = require('gulp-util');
var through = require('through2');
var juice = require('juice');

module.exports = function(opt){
    return through.obj(function (file, enc, cb) {
        'use strict';

        opt = opt || {};

        // 'url' option is required
        // set it automatically if not provided
        opt.url = opt.url || 'file://' + file.path;

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError('gulp-inline-css', 'Streaming not supported'));
            return cb();
        }

        juice.juiceContent(file.contents, opt, function(err, html) {
            if (err) {
                this.emit('error', new gutil.PluginError('gulp-inline-css', err));
            }

            file.contents = new Buffer(String(html));

            this.push(file);

            return cb();
        }.bind(this));
    });
};

Use the function form of "use strict".


'use strict';

var path = require('path');
var isglob = require('is-glob');

module.exports = function globParent(str) {
	while (isglob(str)) str = path.dirname(str);
	return str;
};

'use strict';

var path = require('path');
var isglob = require('is-glob');

module.exports = function globParent(str) {
	while (isglob(path.basename(str))) str = path.dirname(str);
	return str;
};

Make regex test strings smaller


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

module.exports = {
  name: 'ember-g-map',

  included: function(app, parentAddon) {
    var target = (parentAddon || app);
    target.import('vendor/addons.css');
  },

  contentFor: function(type, config) {
    var content = '';

    if (type === 'head') {
      var src = '//maps.googleapis.com/maps/api/js';
      var gMapConfig = config['g-map'] || {};
      var params = [];

      var key = gMapConfig.key;
      if (key) {
        params.push('key=' + encodeURIComponent(key));
      }

      var libraries = gMapConfig.libraries;
      if (libraries && libraries.length) {
        params.push('libraries=' + encodeURIComponent(libraries.join(',')));
      }

      var language = gMapConfig.language;
      if (language) {
        params.push('language=' + encodeURIComponent(language));
      }

      var protocol = gMapConfig.protocol;
      if (protocol) {
        src = protocol + ':' + src;
      }

      src += '?' + params.join('&');
      content = '<script type="text/javascript" src="' + src + '"></script>';
    }

    return content;
  }
};

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

module.exports = {
  name: 'ember-g-map',

  included: function(app, parentAddon) {
    var target = (parentAddon || app);
    target.import('vendor/addons.css');
  },

  contentFor: function(type, config) {
    var content = '';

    if (type === 'head') {
      var src = '//maps.googleapis.com/maps/api/js';
      var gMapConfig = config['g-map'] || {};
      var params = [];

      var key = gMapConfig.key;
      if (key) {
        params.push('key=' + encodeURIComponent(key));
      }

      var version = gMapConfig.version;
      if (version) {
        params.push('v=' + encodeURIComponent(version));
      }

      var client = gMapConfig.client;
      if (client) {
        params.push('client=' + encodeURIComponent(client));
        if (!version) {
          params.push('v=3');
        }
      }

      var libraries = gMapConfig.libraries;
      if (libraries && libraries.length) {
        params.push('libraries=' + encodeURIComponent(libraries.join(',')));
      }

      var language = gMapConfig.language;
      if (language) {
        params.push('language=' + encodeURIComponent(language));
      }

      var protocol = gMapConfig.protocol;
      if (protocol) {
        src = protocol + ':' + src;
      }

      src += '?' + params.join('&');
      content = '<script type="text/javascript" src="' + src + '"></script>';

      var exclude = gMapConfig.exclude;
      if (exclude) {
        content = ''
      }
    }

    return content;
  }
};

Add config for exclude, client ID, version


var express = require('express');
var app = express();

var npm = new require('npm-api')();
var repo = npm.repo('gulp')

app.get('/', function (req, res) {
    repo.package()
    .then(function(pkg) {
        res.send('https://'+pkg.repository.url.split('//')[1].replace(/\.git$/, ''));
    })
    .catch(function(err) {
        res.status(500).send(err);
    });
});

app.listen(process.env.PORT, function () {
    console.log('Example app listening on port ' + process.env.PORT);
});

var express = require('express');
var app = express();

var npm = new require('npm-api')();

app.get('/', function (req, res) {
    res.send('
Npm Api Service

Usage

GET /:package e.g. GET /gulp retreives github url')
});

app.get('/:package', function (req, res) {
    var repo = npm.repo(req.params.package);
    repo.package()
    .then(function(pkg) {
        res.send('https://'+pkg.repository.url.split('//')[1].replace(/\.git$/, ''));
    })
    .catch(function(err) {
        res.status(500).send(err);
    });
});

app.listen(process.env.PORT, function () {
    console.log('Example app listening on port ' + process.env.PORT);
});

Add help at / and /:package endpoint


'use strict';

module.exports = [
    'testproxy.php', // Proxy finder
    'tmUnblock.cgi',
    'getheaders9.php',
    'getheaders2.php',
    'cgi-bin/common/attr',
    'scripts/setup.php', // phpMyAdmin setup path
    'muieblackcat' // Muieblackcat http://eromang.zataz.com/2011/08/14/suc027-muieblackcat-setup-php-web-scanner-robot/
];

'use strict';

module.exports = [
    'testproxy.php', // open proxy test
    'tmUnblock.cgi',
    'getheaders9.php',
    'getheaders2.php',
    'cgi-bin/common/attr', // open proxy test
    'scripts/setup.php', // phpMyAdmin setup path
    'muieblackcat' // Muieblackcat http://eromang.zataz.com/2011/08/14/suc027-muieblackcat-setup-php-web-scanner-robot/
];

Add better explanation to some paths


'use strict';

const VersionChecker = require('ember-cli-version-checker');

module.exports = {
  name: require('./package.json').name,

  emberResolverFeatureFlags() {
    const resolverConfig = {}; //TODO: load from ember-cli-build.js

    return Object.assign({
      /* Add default feature flags here, for now there is none */
    }, resolverConfig.features);
  },

  included() {
    this._super.included.apply(this, arguments);

    let checker = new VersionChecker(this);
    let dep = checker.for('ember-cli', 'npm');

    if (dep.lt('2.0.0')) {
      this.monkeyPatchVendorFiles();
    }
  },

  monkeyPatchVendorFiles() {
    let filesToAppend = this.app.legacyFilesToAppend;
    let legacyResolverIndex = filesToAppend.indexOf(this.app.bowerDirectory + '/ember-resolver/dist/modules/ember-resolver.js');

    if (legacyResolverIndex > -1) {
      filesToAppend.splice(legacyResolverIndex, 1);
    }
  }
};

'use strict';

module.exports = {
  name: require('./package.json').name,
};

Remove support for ember-cli < 2.0.0


var yaml = require('js-yaml');

module.exports = function (source) {
  var loader = this;

  loader.cacheable && loader.cacheable();
  var value = yaml.safeLoad(source, {
    filename: loader.resourcePath,
    onWarning: function (error) {
      loader.emitWarning(error.toString());
    }
  });

  return 'module.exports = ' + JSON.stringify(value, null, '\t') + ';';
};

var yaml = require('js-yaml');
var loaderUtils = require('loader-utils');

function jsonValueReplacer(blacklist, key, value){
  return blacklist.indexOf(key) > -1 ? undefined : value;
}

module.exports = function ymlLoader(source, map){
  var loader = this;
  loader.cacheable && loader.cacheable();

  var query = loaderUtils.parseQuery(loader.query);
  var keysToRemove = query.keysToRemove;

  var value = yaml.safeLoad(source, {
    filename: loader.resourcePath,
    onWarning: function emitLoaderWarning(error){
      loader.emitWarning(error.toString());
    }
  });

  var anyKeysToReplace = Array.isArray(keysToRemove) && keysToRemove.length;
  var replacer = anyKeysToReplace ? jsonValueReplacer.bind(keysToRemove) : undefined;

  return [
    'module.exports = ',
    JSON.stringify(value, replacer , '\t'),
    ';'
  ].join('');
};

Add option to remove keys.


"use strict";

var TypeDecorator = require( "./lib/type/decorator" );
var TypeHelper    = require( "./lib/type/helper" );
var TypeInfo      = require( "./lib/type/info" );

module.exports = {
	DecoratorError : require( "./lib/error/decorator" ),
	HelperError    : require( "./lib/error/helper" ),

	ChangeSet : require( "./lib/history/changeset" ),

	Conductor  : require( "./lib/transaction/conductor" ),
	WebSockets : require( "./lib/transaction/websockets" ),

	TypeDecorator : TypeDecorator,
	Factory       : require( "./lib/type/factory" ),
	TypeHelper    : TypeHelper,
	TypeInfo      : TypeInfo,
	Type          : require( "./lib/type/type" ),

	decorate : decorate,
	info     : info,
	helper   : helper
};

function decorate( type ) {
	return new TypeDecorator( type );
}

function info( typeName, type ) {
	return new TypeInfo( typeName, type );
}

function helper( typeInfo ) {
	return new TypeHelper( typeInfo );
}

module.exports.USERCLASS_ADMIN = TypeInfo.USERCLASS_ADMIN;
module.exports.USERCLASS_USER  = TypeInfo.USERCLASS_USER;

module.exports.CONCEALED = TypeInfo.CONCEALED;
module.exports.HIDDEN    = TypeInfo.HIDDEN;
module.exports.READ_ONLY = TypeInfo.READ_ONLY;

Put commonly used constants on main export


/**
 * @file Blizzard.js
 * @description A Node.js wrapper for the Blizzard Battle.net Community Platform API
 * @copyright Copyright(c) 2016 Ben Weier <ben.weier@gmail.com>
 * @license MIT
 * @version 1.0.0
 * @module index
 * @requires lib/blizzard
 */
'use strict';

const Blizzard = require('./lib/blizzard');

/**
 * Initialize the Blizzard.js instance.
 *
 * @param  {Object} args Blizzard.js configuration options
 * @return {Object}      An instance of Blizzard.js
 * @example
 * const blizzard = require('blizzard.js').initialize({api_key: process.env.BATTLENET_API_KEY});
 */
const initialize = function initialize (args) {
  // TODO Filter args by whitelisted configuration keys
  const config = args;

  return new Blizzard(config);
};

exports.initialize = initialize;

/**
 * @file Blizzard.js
 * @description A Node.js wrapper for the Blizzard Battle.net Community Platform API
 * @copyright Copyright(c) 2016 Ben Weier <ben.weier@gmail.com>
 * @license MIT
 * @version 1.0.0
 * @module index
 * @requires lib/blizzard
 */
'use strict';

/**
 * @typedef {Object} Blizzard
 * @prop    {Object} account  Account API methods
 * @prop    {Object} d3       D3 API methods
 * @prop    {Object} sc2      Sc2 API methods
 * @prop    {Object} wow      WoW API methods
 * @prop    {Function} params Filter an objects keys from an array of keys
 * @prop    {Function} get    Perform a single request to the Blizzard API
 * @prop    {Function} all    Perform multiple requests to the Blizzard API
 */
const Blizzard = require('./lib/blizzard');

/**
 * Initialize the Blizzard.js instance.
 *
 * @param  {Object} args  Blizzard.js configuration options
 * @param  {Object} axios An instance config object compatible with [axios]{@link https://github.com/mzabriskie/axios}
 * @return {Blizzard}     An instance of Blizzard.js
 * @example
 * const blizzard = require('blizzard.js').initialize({apikey: process.env.BATTLENET_API_KEY});
 */
const initialize = function initialize (args, axios) {
  const config = Object.assign({}, args);
  const instance = Object.assign({}, axios);

  return new Blizzard(config, instance);
};

exports.initialize = initialize;

Update Blizzard instance function signature


'use strict';

const characters = require('./characters');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json()); //support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true})); //support encoded bodies.
//must say {extended: true} because compressed bodies will be inflated and the default value of the extended option has been deprecated

const server = app.listen(3000, () => {
  console.log('Express server is listening on part %d in %s mode', server.address().port, app.settings.env);
});

//HTTP POST route method
app.post('/', (req, res) => {
  let text = req.body.text;

  if(! /[A-z]/g.test(text)) { //if an Adventure Time Character's name contains integers
    res.send("Please don't enter numbers, but rather please enter a character from Adventure Time, like Lumpy Space Princess.");
  return;
  }

  let name = text.replace(/\s/g,'').toLowerCase();
  if (!(name in characters)) {
    res.send(`Sorry about that, "${name}" is not an Adventure Time character I recognize ⭐ `);
    return;
  }

  let data = {
    response_type: 'in_channel', //public to channel
    text: `${name} Found`,
    attachments:[ {
      image_url: characters[name]
    } ]};

    res.json(data);
  }
)

'use strict';

const characters = require('./characters');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json()); //support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true})); //support encoded bodies.
//must say {extended: true} because compressed bodies will be inflated and the default value of the extended option has been deprecated

const server = app.listen(3000, () => {
  console.log('Express server is listening on part %d in %s mode', server.address().port, app.settings.env);
});

//HTTP POST route method
app.post('/', (req, res) => {
  let text = req.body.text;

  if(! /[A-z]/g.test(text)) { //if an Adventure Time Character's name contains integers
    res.send("Please don't enter numbers, but rather please enter a character from Adventure Time, like Lumpy Space Princess.");
  return;
  }

  let name = text.replace(/\s/g,'').toLowerCase();
  if (!(name in characters)) {
    res.send(`Sorry about that, "${name}" is not an Adventure Time character I recognize ⭐ `);
    return;
  }

  let data = {
    response_type: 'in_channel', //public to channel
    text: `${name} found ✨`,
    attachments:[ {
      image_url: characters[name]
    } ]};

    res.json(data);
  }
)

Edit error message to include ✨ emoji. V important


var _ = require('lodash');
var through = require('through2');
var Parker = require('parker');

module.exports = function(options) {
  options = _.defaults(options || {}, {
    metrics: 'all',
    format: 'json',
  });

  var metrics = options.metrics;
  var includedMetrics = [];

  // @TODO allow for custom metrics (anonymous functions)
  if (_.isArray(metrics)) {

    _.forEach(metrics, function(metric) {
      if(_.isString(metric)) {
        includedMetrics.push(require('./node_modules/parker/metrics/' + metric + '.js'));
      } else {
        includedMetrics.push(metric);
      }
    });
  } else {
    includedMetrics = require('./node_modules/parker/metrics/All.js');
  }

  var parker = new Parker(includedMetrics);

  var parsedMetrics = {};
  _.forEach(includedMetrics, function(parsedMetric) {
    parsedMetrics[parsedMetric.id] = parsedMetric;
  });

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

  var stream = through.obj(function(file, enc, cb) {
    var results = parker.run(file.contents.toString());
    var obj = {};

    cb(null, formats[options.format](results, file, enc));
  });

  return stream;
};

var _ = require('lodash');
var gutil = require('gulp-util');
var through = require('through2');
var Parker = require('parker');

module.exports = function(options) {
  options = _.defaults(options || {}, {
    metrics: 'all',
    format: 'json',
  });

  var metrics = options.metrics;
  var includedMetrics = [];

  // @TODO allow for custom metrics (anonymous functions)
  if (_.isArray(metrics)) {

    _.forEach(metrics, function(metric) {
      if(_.isString(metric)) {
        includedMetrics.push(require('./node_modules/parker/metrics/' + metric + '.js'));
      } else {
        includedMetrics.push(metric);
      }
    });
  } else {
    includedMetrics = require('./node_modules/parker/metrics/All.js');
  }

  var parker = new Parker(includedMetrics);

  var parsedMetrics = {};
  _.forEach(includedMetrics, function(parsedMetric) {
    parsedMetrics[parsedMetric.id] = parsedMetric;
  });

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

  return through.obj(function(file, enc, cb) {

    if (file.isNull()) {
      cb(null, file);
      return;
    }

    if (file.isStream()) {
      cb(new gutil.PluginError('gulp-parker', 'Streaming not supported'));
      return;
    }

    var results;
    try {
      results = parker.run(file.contents.toString());
      this.push(formats[options.format](results, file, enc));
    } catch (err) {
      this.emit('error', new gutil.PluginError('gulp-parker', err, { fileName: file.path }));
    }

    cb();
  });
};

Rewrite plugin to be buffer-only and with error handling


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

module.exports = {
  extends: ['airbnb-base'],
  rules: {
    "indent": [ 2, 2, { "SwitchCase": 1} ],
    "space-before-blocks": [2, "never"],
    "max-len": [1, 120, 4]
  },
};

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

module.exports = {
  extends: ['airbnb-base'],
  rules: {
    "indent": [ 2, 2, { "SwitchCase": 1} ],
    "space-before-blocks": [2, "never"],
    "max-len": [1, 120, 4],
    "comma-dangle": [2, 'always-multiline'],
  },
};

Add override for comma-dangle in functions


var path = require('path'),
    config = require('nconf'),
    SnapshotCleaner = require('./lib/cleaner');

config.env();
config.add('config', {
    type: 'file',
    file: path.resolve(__dirname, './config/_config.json')
});

var sc = new SnapshotCleaner();
sc.start();

var fsExtra = require('fs-extra'),
    SnapshotCleaner = require('./lib/cleaner');

fsExtra.readJSON('./config/config.json', function (error, config) {
    if (error) {
        throw error;
    }
    var sc = new SnapshotCleaner(config);
    sc.start();
});

Replace complex nconf usage to simple open and parse json configuration file via fsExtra module


import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { deprecated } from 'prop-types-extra';
import exportToCsv from './exportToCsv';
import Button from '../Button';

class ExportCsv extends React.Component {
  static propTypes = {
    data: PropTypes.arrayOf(PropTypes.object),
    excludeKeys: deprecated(PropTypes.arrayOf(PropTypes.string), 'Use onlyFields to pass in the necessary fields'),
    onlyFields: PropTypes.oneOfType([
      PropTypes.arrayOf(PropTypes.string),
      PropTypes.arrayOf(PropTypes.object),
    ]),
  }

  render() {
    const { data, excludeKeys, onlyFields } = this.props;
    const options = { excludeKeys, onlyFields };
    return (
      <Button onClick={() => { exportToCsv(data, options); }}>
        <FormattedMessage id="stripes-components.exportToCsv" />
      </Button>
    );
  }
}

export default ExportCsv;

import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import exportToCsv from './exportToCsv';
import Button from '../Button';

class ExportCsv extends React.Component {
  static propTypes = {
    data: PropTypes.arrayOf(PropTypes.object),
    onlyFields: PropTypes.oneOfType([
      PropTypes.arrayOf(PropTypes.string),
      PropTypes.arrayOf(PropTypes.object),
    ]),
  }

  render() {
    const { data, onlyFields } = this.props;
    const options = { onlyFields };
    return (
      <Button onClick={() => { exportToCsv(data, options); }}>
        <FormattedMessage id="stripes-components.exportToCsv" />
      </Button>
    );
  }
}

export default ExportCsv;

Remove deprecated excludeKeys prop for exportCsv


/**
 *  Compass task config
 */

exports.task = function () {
  return {
    dist: {
      options: {
        importPath: [
          'tmp/sass/cartoassets'
        ],
        sassDir: 'tmp/sass/editor',
        cssDir: '<%= assets_dir %>/stylesheets',
        fontsDir: '<%= assets_dir %>/fonts',
        httpFontsPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/fonts',

        imagesDir: 'app/assets/images/',
        generatedImagesDir: '<%= assets_dir %>/images/',
        httpImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/',
        httpGeneratedImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/',

        environment: 'production',
        outputStyle: 'compressed',
        noLineComments: true,
        force: false,
        time: true
      }
    }
  };
};

/**
 *  Compass task config
 */

exports.task = function () {
  return {
    dist: {
      options: {
        importPath: [
          'tmp/sass/cartoassets'
        ],
        sassDir: 'tmp/sass/editor',
        cssDir: '<%= assets_dir %>/stylesheets',
        fontsDir: '<%= assets_dir %>/fonts',
        httpFontsPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/fonts',

        imagesDir: 'app/assets/images/',
        generatedImagesDir: '<%= assets_dir %>/images/',
        httpImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/',
        httpGeneratedImagesPath: '<%= env.http_path_prefix %>/assets/<%= pkg.version %>/images/',

        environment: 'production',
        outputStyle: 'compressed',
        noLineComments: true,
        force: false,
        time: true,
        raw: 'Encoding.default_external = \'utf-8\'\n'
      }
    }
  };
};

Fix issue with Sass encoding


var util = require('util');
var Socket = require('net').Socket;

var bindings = require(__dirname + '/../build/Debug/unix_stream.node');

var SOCK_STREAM  = bindings.SOCK_STREAM;
var AF_UNIX     = bindings.AF_UNIX;

var bind = bindings.bind;
var socket = bindings.socket;

function errnoException(errorno, syscall) {

    var e = new Error(syscall + ' ' + errorno);
    e.errno = e.code = errorno;
    e.syscall = syscall;
    return e;
}

exports.createSocket = function(local_path) {

    var fd;
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) === -1)
        throw errnoException(errno, 'socket');

    var s = new Socket(fd);
    if (local_path) {
        if (bind(fd, local_path) == -1) {
            process.nextTick(function() {
                s.emit('error', errnoException(errno, 'bind'));
            });
        }

        s.local_path = local_path;
    }

    return s;
};

var util = require('util');
var Socket = require('net').Socket;

/* Make sure we choose the correct build directory */
var directory = process.config.default_configuration === 'Debug' ? 'Debug' : 'Release';
var bindings = require(__dirname + '/../build/' + directory + '/unix_stream.node');

var SOCK_STREAM  = bindings.SOCK_STREAM;
var AF_UNIX     = bindings.AF_UNIX;

var bind = bindings.bind;
var socket = bindings.socket;

function errnoException(errorno, syscall) {

    var e = new Error(syscall + ' ' + errorno);
    e.errno = e.code = errorno;
    e.syscall = syscall;
    return e;
}

exports.createSocket = function(local_path) {

    var fd;
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) === -1)
        throw errnoException(errno, 'socket');

    var s = new Socket(fd);
    if (local_path) {
        if (bind(fd, local_path) == -1) {
            process.nextTick(function() {
                s.emit('error', errnoException(errno, 'bind'));
            });
        }

        s.local_path = local_path;
    }

    return s;
};

Choose the correct build directory


var set = Em.set;
var get = Em.get;

module("Em.View extensions", {
  setup: function() {
    Em.Gestures.register('viewTestGesture',Em.Object.extend());
  },

  teardown: function() {
    Em.Gestures.unregister('viewTestGesture');
  }
});

test("should detect gesture", function() {

  var view = Em.View.create({
    viewTestGestureStart: function() {

    },
    viewTestGestureChange: function() {

    },
    viewTestGestureEnd: function() {

    },
    viewTestGestureCancel: function() {

    }
  });

  var eventManager = get(view, 'eventManager');
  ok(eventManager,'view has an eventManager');

  var gestures = get(eventManager, 'gestures');
  equal(gestures.length,1,'gesture exists');
});

test("should apply options", function() {

  var view = Em.View.create({
    viewTestGestureOptions: {
      numberOfRequiredTouches: 4
    },

    viewTestGestureStart: function() {

    }
  });

  var eventManager = get(view, 'eventManager');
  ok(eventManager,'view has an eventManager');

  var gestures = get(eventManager, 'gestures');
  equal(gestures.length,1,'gesture exists');

  equal(gestures[0].numberOfRequiredTouches,4, "should apply options hash");
});

test("A view without gestures have assigned a GestureManager at its eventManager property", function() {

  var view = Em.View.create({

  });

  var eventManager = get(view, 'eventManager');
  ok(eventManager,'view has an eventManager');

  var gestures = get(eventManager, 'gestures');
  equal(gestures.length,0,' has not gestures');

});

Add test to ensure all the views have assigned an eventManager This test ensure that current eventManager bubbling logic performs correctly


if (Meteor.isServer) {

  Meteor.startup(function () {
    // Validate that the config file contains the data we need.
    validateSettings();

    // Create our DNS zone for PowerDNS, if necessary.
    mysqlQuery = createWrappedQuery();
    createDomainIfNeeded(mysqlQuery);

    Router.map(function() {
      this.route('register', {
        path: '/register',
        where: 'server',
        action: function() {
          doRegister(this.request, this.response);
        }
      });

      var clientCertificateAuth = Meteor.npmRequire('client-certificate-auth');
      function checkAuth(cert) {
        console.log(cert.fingerprint);
      }

      WebApp.connectHandlers.use(clientCertificateAuth(checkAuth));

      this.route('update', {
        path: '/update',
        where: 'server',
        action: function() {
          doUpdate(this.request, this.response);
        }
      });
    });
  });
}

if (Meteor.isServer) {

  Meteor.startup(function () {
    // Validate that the config file contains the data we need.
    validateSettings();

    // Create our DNS zone for PowerDNS, if necessary.
    mysqlQuery = createWrappedQuery();
    createDomainIfNeeded(mysqlQuery);

    Router.map(function() {
      this.route('register', {
        path: '/register',
        where: 'server',
        action: function() {
          doRegister(this.request, this.response);
        }
      });

      this.route('update', {
        path: '/update',
        where: 'server',
        action: function() {
          doUpdate(this.request, this.response);
        }
      });
    });
  });
}

Remove client cert stuff that never mattered


'use strict';

const { makeImmutable } = require('../utilities');

const CONTENT_TYPES = [
  {
    name: 'model',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'collection',
    test({ content }) {
      return content instanceof Array && isJSON(content);
    },
  },

  {
    name: 'error',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'object',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'html',
    test({ content }) {
      return typeof content === 'string';
    },
  },

  {
    name: 'text',
    test({ content }) {
      return typeof content === 'string';
    },
  },

  {
    name: 'failure',
    test({ content }) {
      return content === undefined;
    },
  },
];
makeImmutable(CONTENT_TYPES);

const isJSON = function (val) {
  try {
    JSON.stringify(val);
  } catch (error) { return false; }
  return true;
};

module.exports = {
  CONTENT_TYPES,
};

'use strict';

const { makeImmutable } = require('../utilities');

const CONTENT_TYPES = [
  {
    name: 'model',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'collection',
    test({ content }) {
      return content && typeof content === 'object' && isJSON(content);
    },
  },

  {
    name: 'error',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'object',
    test({ content }) {
      return content && content.constructor === Object && isJSON(content);
    },
  },

  {
    name: 'html',
    test({ content }) {
      return typeof content === 'string';
    },
  },

  {
    name: 'text',
    test({ content }) {
      return typeof content === 'string';
    },
  },

  {
    name: 'failure',
    test({ content }) {
      return content === undefined;
    },
  },
];
makeImmutable(CONTENT_TYPES);

const isJSON = function (val) {
  try {
    JSON.stringify(val);
  } catch (error) { return false; }
  return true;
};

module.exports = {
  CONTENT_TYPES,
};

Fix content type detection for GraphQL *many actions


(function (load, morgen, __morgen) {
    'use strict';

    morgen.uid = function () {
        return __morgen.uid++;
    };

    morgen.run = function (options) {
        load('__morgen_broadcast');
        load('__morgen_history');
        load('__morgen_data_events');

        if (options.watch)
            load('__morgen_watchdog');
    };

    morgen.broadcast = function (msg) {
        __morgen.ws.send(JSON.stringify(msg));
    };

}) (window.morgen.load,
    window.morgen,
    window.__morgen);

(function (load, morgen, __morgen) {
    'use strict';

    function encodeData (data) {
        var query = [];
        for (var key in data)
            query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
        return query.join('&');
    }

    morgen.uid = function () {
        return __morgen.uid++;
    };

    morgen.run = function (options) {
        load('__morgen_broadcast');
        load('__morgen_history');
        load('__morgen_data_events');

        if (options.watch)
            load('__morgen_watchdog');
    };

    morgen.broadcast = function (msg) {
        __morgen.ws.send(JSON.stringify(msg));
    };

    morgen.httpSend = function (method, url, data, success, error) {
        var xhr = new XMLHttpRequest(),
            contentType = method == 'get' ? 'text/plain' : 'application/x-www-form-urlencoded'

        xhr.open(method, url, true);
        xhr.setRequestHeader('Content-Type', contentType + ';charset=UTF-8');
        xhr.onreadystatechange = function() {
            if (xhr.readyState == XMLHttpRequest.DONE) {
                if (xhr.status == 200 || xhr.status === 0)
                    success && success(xhr.responseText);
                else
                    error && error();
            }
        };

        xhr.onerror = error;
        xhr.ontimeout = error;

        try {
            xhr.send(data);
        } catch (e) {
            error && error();
        }

        return xhr;
    };

    morgen.httpGet = function (url, data, success, error) {
        return morgen.httpSend('get', url + '?' + encodeData(data), null, success, error);
    }

    morgen.httpPost = function (url, data, success, error) {
        return morgen.httpSend('post', url, data instanceof FormData ? data : encodeData(data), success, error);
    }

}) (window.morgen.load,
    window.morgen,
    window.__morgen);

Add simple support for ajax


import {getVectorConfig} from '../getconfig';

function onBackToRiotClick() {
    document.cookie = 'mobile_redirect_to_guide=false;path=/';
    window.location.href = '../';
}

async function initPage() {
    document.getElementById('back_to_riot_button').onclick = onBackToRiotClick;

    const config = await getVectorConfig('..');
    let hsUrl;
    if (config && config['default_hs_url']) {
        hsUrl = config['default_hs_url'];
    }
    if (hsUrl && !hsUrl.endsWith('/')) hsUrl += '/';
    if (hsUrl && hsUrl !== 'https://matrix.org/') {
        document.getElementById('step2_container').style.display = 'block';
        document.getElementById('hs_url').innerHTML = hsUrl;
        document.getElementById('step_login_header').innerHTML= 'Step 3: Register or Log in';
    }
}

initPage();

import {getVectorConfig} from '../getconfig';

function onBackToRiotClick() {
    // Cookie should expire in 24 hours
    document.cookie = 'mobile_redirect_to_guide=false;path=/;max-age=86400';
    window.location.href = '../';
}

async function initPage() {
    document.getElementById('back_to_riot_button').onclick = onBackToRiotClick;

    const config = await getVectorConfig('..');
    let hsUrl;
    if (config && config['default_hs_url']) {
        hsUrl = config['default_hs_url'];
    }
    if (hsUrl && !hsUrl.endsWith('/')) hsUrl += '/';
    if (hsUrl && hsUrl !== 'https://matrix.org/') {
        document.getElementById('step2_container').style.display = 'block';
        document.getElementById('hs_url').innerHTML = hsUrl;
        document.getElementById('step_login_header').innerHTML= 'Step 3: Register or Log in';
    }
}

initPage();

Expire mobile guide cookie after 24 hours


import ExecutorAbstract from '../ExecutorAbstract';
import {sleep} from '../util/function';

class SleepExecutor extends ExecutorAbstract {
  //-----------------------------------
  // Constructor
  //-----------------------------------
  /**
   * Constructor for a new SleepExecutor, which will wait `milliseconds` before calling `this.done()`
   *
   * @param thread The parent thread that owns this executor.
   * @param milliseconds The number of milliseconds to sleep
   */
  constructor(thread, milliseconds) {
    super(thread);

    this.milliseconds = milliseconds;
  }

  //-----------------------------------
  // Methods
  //-----------------------------------
  /**
   * Internal execution method called by Thread only.
   *
   * @param doneHandler The handler to call when done() is called.
   * @param failHandler The handler to call when fail() is called;
   * @private
   */
  _execute(doneHandler, failHandler) {
    super._execute(doneHandler, failHandler);

    sleep(this.milliseconds).then(() => this.done());
  }

  toString() {
    return this.id + ': Sleep for ' + this.milliseconds + 'milliseconds';
  }
}

export default SleepExecutor;

import ExecutorAbstract from '../ExecutorAbstract';
import {sleep} from '../util/function';

class SleepExecutor extends ExecutorAbstract {
  //-----------------------------------
  // Constructor
  //-----------------------------------
  /**
   * Constructor for a new SleepExecutor, which will wait `milliseconds` before calling `this.done()`
   *
   * @param thread The parent thread that owns this executor.
   * @param milliseconds The number of milliseconds to sleep
   */
  constructor(thread, milliseconds) {
    super(thread);

    this.milliseconds = milliseconds;
  }

  //-----------------------------------
  // Methods
  //-----------------------------------
  /**
   * Internal execution method called by Thread only.
   *
   * @param doneHandler The handler to call when done() is called.
   * @param failHandler The handler to call when fail() is called;
   * @private
   */
  _execute(doneHandler, failHandler) {
    super._execute(doneHandler, failHandler);

    sleep(this.milliseconds).then(this.done);
  }

  toString() {
    return this.id + ': Sleep for ' + this.milliseconds + 'milliseconds';
  }
}

export default SleepExecutor;

Simplify done call as per PR review


import http from 'http';

let pkgUrl = {
  host: 'rawgit.com',
  path: '/videojs/video.js/master/package.json'
};

function getPackage(cb) {
  http.get({host: pkgUrl.host, path: pkgUrl.path, withCredentials: false}, function(res) {
    var body = '';
    res.on('data', function(d) {
      body += d;
    });

    res.on('end', function(e) {
      body = JSON.parse(body);
      cb(null, body);
    });

  }).on('error', function(e) {
    cb(e);
  });
}

export { getPackage };

import http from 'http';

let pkgUrl = {
  host: 'crossorigin.me',
  path: '/https://registry.npmjs.org/video.js/latest'
};

function getPackage(cb) {
  http.get({host: pkgUrl.host, path: pkgUrl.path, withCredentials: false}, function(res) {
    var body = '';
    res.on('data', function(d) {
      body += d;
    });

    res.on('end', function(e) {
      body = JSON.parse(body);
      cb(null, body);
    });

  }).on('error', function(e) {
    cb(e);
  });
}

export { getPackage };

Use crossorigin.me and npm to get latest version


import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ActionCreators } from './devTools';

export default function connectMonitor(Monitor) {
  const ConnectedMonitor = connect(state => state, ActionCreators)(Monitor);

  class DevTools extends Component {
    static propTypes = {
      store(props, propName, componentName) {
        if (!props.store) {
          return new Error('Required prop `store` was not specified in `' + componentName + '`.');
        }
        if (!props.store.devToolsStore) {
          return new Error(
            'Could not find the DevTools store inside the `store` prop passed to `' +
            componentName +
            '`. Have you applied the devTools() store enhancer?'
          );
        }
      }
    };

    render() {
      const { store } = this.props;
      if (!store) {
        return null;
      }

      const { devToolsStore } = store;
      if (!devToolsStore) {
        return null;
      }

      return <ConnectedMonitor {...this.props} store={devToolsStore} />;
    }
  }

  return DevTools;
}

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ActionCreators } from './devTools';

export default function connectMonitor(Monitor) {
  const ConnectedMonitor = connect(state => state, ActionCreators)(Monitor);

  class DevTools extends Component {
    static Monitor = Monitor;

    static propTypes = {
      store(props, propName, componentName) {
        if (!props.store) {
          return new Error('Required prop `store` was not specified in `' + componentName + '`.');
        }
        if (!props.store.devToolsStore) {
          return new Error(
            'Could not find the DevTools store inside the `store` prop passed to `' +
            componentName +
            '`. Have you applied the devTools() store enhancer?'
          );
        }
      }
    };

    render() {
      const { store } = this.props;
      if (!store) {
        return null;
      }

      const { devToolsStore } = store;
      if (!devToolsStore) {
        return null;
      }

      return <ConnectedMonitor {...this.props} store={devToolsStore} />;
    }
  }

  return DevTools;
}

Put the original Monitor on statics


import React, { PropTypes, Component } from 'react'
import { connect } from 'react-redux'

import { fetchTodos, completeTodo } from '../../actions/todos'

import TodoList from '../../components/TodoList'

import {} from './Todos.scss'

class Todos extends Component {
  constructor () {
    super()
    this.onTodoClick = this.onTodoClick.bind(this)
  }

  componentDidMount () {
    this.props.dispatch(fetchTodos())
  }

  onTodoClick (id) {
    this.props.dispatch(completeTodo(id))
  }

  render () {
    return (

Todos

        <TodoList todos={this.props.todos} isFetching={this.props.isFetching} onTodoClick={this.onTodoClick} />

    )
  }
}

Todos.propTypes = {
  todos: PropTypes.array.isRequired,
  isFetching: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired
}

const mapStateToProps = (state) => {
  return {
    todos: state.todos.items,
    isFetching: state.todos.isFetching
  }
}

export default connect(mapStateToProps)(Todos)

import React, { PropTypes, Component } from 'react'
import { connect } from 'react-redux'

import { fetchTodos, completeTodo, removeCompletedTodos } from '../../actions/todos'

import TodoList from '../../components/TodoList'

import {} from './Todos.scss'

class Todos extends Component {
  constructor () {
    super()
    this.onTodoClick = this.onTodoClick.bind(this)
    this.onRemoveTodosClick = this.onRemoveTodosClick.bind(this)
  }

  componentDidMount () {
    this.props.dispatch(fetchTodos())
  }

  onTodoClick (id) {
    this.props.dispatch(completeTodo(id))
  }

  onRemoveTodosClick (e) {
    e.preventDefault()

    this.props.dispatch(removeCompletedTodos())
  }

  render () {
    return (

Todos

        <TodoList todos={this.props.todos} isFetching={this.props.isFetching} onTodoClick={this.onTodoClick} />
        Remove Completed Todos

    )
  }
}

Todos.propTypes = {
  todos: PropTypes.array.isRequired,
  isFetching: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired
}

const mapStateToProps = (state) => {
  return {
    todos: state.todos.items,
    isFetching: state.todos.isFetching
  }
}

export default connect(mapStateToProps)(Todos)

Add button to remove completed todos


/**
*	Underscore Mixins
*	@author Patricio Ferreira <3dimentionar@gmail.com>
**/
define(['underscore', 'jquery'], function() {

	_.mixin({

		/**
		*	Returns true if a given object is not null and not undefined, otherwise returns false
		*	@public
		*	@method def
		*	@param o {Object} object to evaluate
		*	@return Boolean
		**/
		def: function(o) {
			return (!_.isNull(o) && !_.isUndefined(o));
		},

		/**
		*	Decorate a given object with jquery event system
		*	@public
		*	@method events
		*	@param o {Object} object to decorate
		*	@return Object
		**/
		events: function(o) {
			return _.def(o) ? _.extend(o, _.pick($.prototype,
				'on', 'once', 'off', 'trigger', 'each')) : o;
		}

	});

});

/**
*	Underscore Mixins
*	@author Patricio Ferreira <3dimentionar@gmail.com>
**/
define(['underscore', 'jquery'], function() {

	_.mixin({

		/**
		*	Returns true if a given object is not null and not undefined, otherwise returns false
		*	@public
		*	@method def
		*	@param o {Object} object to evaluate
		*	@return Boolean
		**/
		def: function(o) {
			return (!_.isNull(o) && !_.isUndefined(o));
		}

	});

});

Clean up unused underscore mixin function.


import { Component } from 'react';
import { instanceOf, node } from 'prop-types';
import Cookies from 'universal-cookie';
import { isNode } from 'universal-cookie/lib/utils';

export default class CookiesProvider extends Component {
  static propTypes = {
    children: node
  };

  static childContextTypes = {
    cookies: instanceOf(Cookies).isRequired
  };

  constructor(props) {
    super(props);

    if (isNode()) {
      this.cookies = props.cookies;
    } else {
      this.cookies = new Cookies();
    }
  }

  getChildContext() {
    return {
      cookies: this.cookies
    };
  }

  render() {
    return this.props.children;
  }
}

if (isNode()) {
  CookiesProvider.propTypes.cookies = instanceOf(Cookies).isRequired;
}

import { Component } from 'react';
import { instanceOf, node } from 'prop-types';
import Cookies from 'universal-cookie';
import { isNode } from 'universal-cookie/lib/utils';

export default class CookiesProvider extends Component {
  static propTypes = {
    children: node,
    cookies: instanceOf(Cookies)
  };

  static childContextTypes = {
    cookies: instanceOf(Cookies).isRequired
  };

  constructor(props) {
    super(props);

    if (props.cookies) {
      this.cookies = props.cookies;
    } else {
      this.cookies = new Cookies();
    }
  }

  getChildContext() {
    return {
      cookies: this.cookies
    };
  }

  render() {
    return this.props.children;
  }
}

if (isNode()) {
  CookiesProvider.propTypes.cookies = instanceOf(Cookies).isRequired;
}

Allow passing cookies instance in client


import Vue from 'vue';

Vue.config.productionTip = false;

// require all test files (files that ends with .spec.js)
const testsContext = require.context('./specs', true, /\.spec$/);
testsContext.keys().forEach(testsContext);

// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/);
srcContext.keys().forEach(srcContext);

import Vue from 'vue';

Vue.config.productionTip = false;

// require all test files (files that ends with .spec.js)
const testsContext = require.context('./specs', true, /\.spec$/);
testsContext.keys().forEach(testsContext);

// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../src/components', true, /^\.\/(?!main(\.js)?$)/);
srcContext.keys().forEach(srcContext);

Update coverage of unit testing


const initialState = {
  readMore: false,
  jwToken: null,
  myEmail: null,
  myName: null,
  myFetch: null
}

export default function update (state = initialState, action) {
  switch (action.type) {
    case 'TOGGLE_READ_MORE':
      return Object.assign({}, state, {
        readMore: action.readMore
      })
    case 'RECEIVE_TOKEN_FROM_SERVER':
      return Object.assign({}, state, {
        jwToken: action.jwToken,
        myEmail: action.email,
        myName: action.name
      })
    case 'RECEIVE_TOKEN_FROM_LOCAL_STORAGE': {
      return Object.assign({}, state, {
        jwToken: action.jwToken,
        myEmail: action.email,
        myName: action.name
      })
    }
    case '@@me/DELETE_TOKEN':
      return Object.assign({}, state, {
        jwToken: null
      })
    case '@@login/DELETE_TOKEN':
      return Object.assign({}, state, {
        jwToken: null
      })
    default:
      return state
  }
}

const initialState = {
  readMore: false,
  jwToken: null,
  myEmail: null,
  myName: null,
  myFetch: null
}

export default function update (state = initialState, action) {
  switch (action.type) {
    case 'TOGGLE_READ_MORE':
      return Object.assign({}, state, {
        readMore: action.readMore
      })
    case 'RECEIVE_TOKEN_FROM_SERVER':
      return Object.assign({}, state, {
        jwToken: action.jwToken,
        myEmail: action.email,
        myName: action.name
      })
    case 'RECEIVE_TOKEN_FROM_LOCAL_STORAGE': {
      return Object.assign({}, state, {
        jwToken: action.jwToken,
        myEmail: action.email,
        myName: action.name
      })
    }
    case '@@me/DELETE_TOKEN':
      return Object.assign({}, state, {
        jwToken: null,
        myEmail: null,
        myName: null
      })
    case '@@login/DELETE_TOKEN':
      return Object.assign({}, state, {
        jwToken: null,
        myEmail: null,
        myName: null
      })
    default:
      return state
  }
}

Delete name nad email also


import nanoid from 'nanoid';

const GA_KEY = 'GA:clientID';
let clientId = localStorage.getItem(GA_KEY);

export default class Ga {
  constructor (db) {
    this.db = db;

    this.load();
  }

  load () {
    if (!clientId) {
      clientId = nanoid();
      localStorage.setItem(GA_KEY, clientId);
    }
  }

  report (event, service) {
    this.db.get('sendUsageStatistics')
      .then((sendUsageStatistics) => {
        if (sendUsageStatistics) {
          const request = new XMLHttpRequest();
          const message =
            'v=1&tid=' +
            process.env.GA_TRACKING_ID +
            '&cid=' +
            this.clientId +
            '&aip=1' +
            '&ds=extension&t=event&ec=' +
            event +
            '&ea=' +
            service;

          request.open('POST', 'https://www.google-analytics.com/collect', true);
          request.send(message);
        }
      });
  }

  reportEvent (event, service) {
    this.report(event, event + '-' + service);
  }

  reportOs () {
    chrome.runtime.getPlatformInfo((info) => {
      this.report('os', 'os-' + info.os);
    });
  }

  reportSettings (event, service) {

  }
}

import nanoid from 'nanoid';

const GA_KEY = 'GA:clientID';
let clientId = localStorage.getItem(GA_KEY);

export default class Ga {
  constructor (db) {
    this.db = db;

    this.load();
  }

  load () {
    if (!clientId) {
      clientId = nanoid();
      localStorage.setItem(GA_KEY, clientId);
    }
  }

  report (event, service) {
    this.db.get('sendUsageStatistics')
      .then((sendUsageStatistics) => {
        if (sendUsageStatistics) {
          const request = new XMLHttpRequest();
          const message =
            'v=1&tid=' +
            process.env.GA_TRACKING_ID +
            '&cid=' +
            clientId +
            '&aip=1' +
            '&ds=extension&t=event&ec=' +
            event +
            '&ea=' +
            service;

          request.open('POST', 'https://www.google-analytics.com/collect', true);
          request.send(message);
        }
      });
  }

  reportEvent (event, service) {
    this.report(event, event + '-' + service);
  }

  reportOs () {
    chrome.runtime.getPlatformInfo((info) => {
      this.report('os', 'os-' + info.os);
    });
  }

  reportSettings (event, service) {

  }
}

Fix client ID not being sent to GA


//
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
  if(details.url.indexOf("youtube.com") !== -1 && details.url.indexOf("disable_polymer=true") === -1){
    if(details.url.indexOf("?") !== -1){
      chrome.tabs.update(details.tabId, {
        url: details.url + "&disable_polymer=true"
      });
    }else{
      chrome.tabs.update(details.tabId, {
        url: details.url + "?disable_polymer=true"
      });
    }
  }
});

//
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
  console.log(details);

  if(detectYTURL(details.url) && details.url.indexOf("disable_polymer=true") === -1){
    if(details.url.indexOf("?") !== -1){
      chrome.tabs.update(details.tabId, {
        url: details.url + "&disable_polymer=true"
      });
    }else{
      chrome.tabs.update(details.tabId, {
        url: details.url + "?disable_polymer=true"
      });
    }
  }
});

function detectYTURL(url){
  return (url.indexOf("www.youtube.com") !== -1 && url.indexOf("/js") === -1);
}

Fix bug with async YT script load & any subdomains


(function(stopwatch){
    if (!stopwatch) { throw new Error('stopwatch namespace not found'); }
    console.log('Ready to play!');

    var game = new stopwatch.Game(5);
    new stopwatch.GameView(game, document.getElementById('stopwatch'));

    window.game = game;
})(stopwatch);

(function(stopwatch){
    if (!stopwatch) { throw new Error('stopwatch namespace not found'); }
    console.log('Ready to play!');

    var game = new stopwatch.Game(5);
    var view = new stopwatch.GameView(game, document.getElementById('stopwatch'));

    function loop(){
        view.update();
        requestAnimationFrame(loop);
    }
    loop();

    window.game = game;
})(stopwatch);

Update Game view on each animation frame


const { Command } = require('discord-akairo');
const { RichEmbed } = require('discord.js');

function exec(message, args){
    args.content = isNaN(args.content) ? args.content : parseInt(args.content);
    const embed = new RichEmbed().setColor(args.content);

    let hex = isNaN(embed.color) ? '0' : embed.color.toString(16);
    hex = '#' + '0'.repeat(6 - hex.length) + hex;
    embed.setDescription(hex.toUpperCase());

    return message.edit('', { embed });
}

module.exports = new Command('color', exec, {
    aliases: ['color', 'colour'],
    args: [
        {
            id: 'content',
            match: 'content',
            defaultValue: () => (1 << 24) * Math.random() | 0
        }
    ]
});

const { Command } = require('discord-akairo');
const { RichEmbed } = require('discord.js');

function exec(message, args){
    args.content = isNaN(args.content) ? args.content : parseInt(args.content);
    const embed = new RichEmbed().setColor(args.content);

    let hex = isNaN(embed.color) ? '0' : embed.color.toString(16);
    hex = '#' + '0'.repeat(6 - hex.length) + hex;
    embed.setTitle(hex.toUpperCase());

    return message.edit('', { embed });
}

module.exports = new Command('color', exec, {
    aliases: ['color', 'colour'],
    args: [
        {
            id: 'content',
            match: 'content',
            defaultValue: () => (1 << 24) * Math.random() | 0
        }
    ]
});

Set title instead of desc.


// [START get_service_account_tokens]
var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json");
var credential = admin.credential.cert(serviceAccount);

var accessTokenInfo = credential.getAccessToken();
var accessToken = accessTokenInfo.access_token;
var expirationTime = accessTokenInfo.expires_in;
// Attach accessToken to HTTPS request in the "Authorization: Bearer" header
// After expirationTime, you must generate a new access token
// [END get_service_account_tokens]

console.log("The token " + accessToken + " expires in " + expirationTime);

// [START get_service_account_tokens]
var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json");
var credential = admin.credential.cert(serviceAccount);

var accessTokenInfo = credential.getAccessToken().then(function(accessTokenInfo) {
  var accessToken = accessTokenInfo.access_token;
  var expirationTime = accessTokenInfo.expires_in;

  // Attach accessToken to HTTPS request in the "Authorization: Bearer" header
  // After expirationTime, you must generate a new access token
  // [START_EXCLUDE]
  console.log("The token " + accessToken + " expires in " + expirationTime);
  // [END_EXCLUDE]
});
// [END get_service_account_tokens]

Fix Node.js Admin SDK snippet


define([
  'summernote/base/core/func',
  'summernote/base/core/list',
  'summernote/base/core/dom'
], function (func, list, dom) {
  var ImagePopover = function (context) {
    var ui = $.summernote.ui;

    var options = context.options;

    this.shouldInitialize = function () {
      return !list.isEmpty(options.popover.image);
    };

    this.initialize = function () {
      this.$popover = ui.popover({
        className: 'note-image-popover'
      }).render().appendTo('body');
      var $content = this.$popover.find('.popover-content');

      context.invoke('buttons.build', $content, options.popover.image);
    };

    this.destroy = function () {
      this.$popover.remove();
    };

    this.update = function (target) {
      if (dom.isImg(target)) {
        var pos = dom.posFromPlaceholder(target);
        this.$popover.css({
          display: 'block',
          left: pos.left,
          top: pos.top
        });
      } else {
        this.hide();
      }
    };

    this.hide = function () {
      this.$popover.hide();
    };
  };

  return ImagePopover;
});

define([
  'summernote/base/core/func',
  'summernote/base/core/list',
  'summernote/base/core/dom',
  'summernote/base/core/key'
], function (func, list, dom, key) {
  var ImagePopover = function (context) {
    var self = this;
    var ui = $.summernote.ui;

    var options = context.options;

    this.shouldInitialize = function () {
      return !list.isEmpty(options.popover.image);
    };

    this.events = {
      'summernote.keydown': function (we, e) {
        self.handleKeydown(e);
      }
    };

    this.initialize = function () {
      this.$popover = ui.popover({
        className: 'note-image-popover'
      }).render().appendTo('body');
      var $content = this.$popover.find('.popover-content');

      context.invoke('buttons.build', $content, options.popover.image);
    };

    this.destroy = function () {
      this.$popover.remove();
    };

    this.isVisible = function () {
      return this.$popover.is(':visible');
    };

    this.update = function (target) {
      if (dom.isImg(target)) {
        var pos = dom.posFromPlaceholder(target);
        this.$popover.css({
          display: 'block',
          left: pos.left,
          top: pos.top
        });

        //be sure editor has focus to handle key events
        context.invoke('editor.focus');
      } else {
        this.hide();
      }
    };

    this.hide = function () {
      this.$popover.hide();
    };

    this.handleKeydown = function (e) {
      if (list.contains([key.code.BACKSPACE], e.keyCode)) {
        if (this.isVisible()) {
          context.invoke('editor.removeMedia');
        }
      }
    };
  };

  return ImagePopover;
});

Remove image on backspace key event


window.GobiertoAdmin.GobiertoCommonCustomFieldRecordsProgressPluginController = (function() {

  function GobiertoCommonCustomFieldRecordsProgressPluginController() {}

  GobiertoCommonCustomFieldRecordsProgressPluginController.prototype.form = function(opts = {}) {
    _handlePluginData(opts.uid);
  };

  function _handlePluginData(uid) {
    let element = $(`[data-uid=${uid}]`)
    let id = element.attr('id')
    let data = JSON.parse($(`#${id}`).find("input[name$='[value]'").val())

    _insertProgressPlugin(id, data)
  }

  function _insertProgressPlugin(id, data) {
    let element = $(`#${id}`)
    element.remove();
    let progress_select = $("select[id$='_progress'")
    if(progress_select.length) {
      progress_select.replaceWith(
        $('<input type="text" disabled="disabled">').val(
          data.toLocaleString(I18n.locale, { style: 'percent', maximumFractionDigits: 1 })
        )
      );
    }
  }

  return GobiertoCommonCustomFieldRecordsProgressPluginController;
})();

window.GobiertoAdmin.gobierto_common_custom_field_records_progress_plugin_controller = new GobiertoAdmin.GobiertoCommonCustomFieldRecordsProgressPluginController;

window.GobiertoAdmin.GobiertoCommonCustomFieldRecordsProgressPluginController = (function() {

  function GobiertoCommonCustomFieldRecordsProgressPluginController() {}

  GobiertoCommonCustomFieldRecordsProgressPluginController.prototype.form = function(opts = {}) {
    _handlePluginData(opts.uid);
  };

  function _handlePluginData(uid) {
    let element = $(`[data-uid=${uid}]`)
    let id = element.attr('id')
    let data = JSON.parse($(`#${id}`).find("input[name$='[value]'").val())

    _insertProgressPlugin(id, data)
  }

  function _insertProgressPlugin(id, data) {
    let element = $(`#${id}`)
    element.remove();
    let progress_select = $("select[id$='_progress'")
    if(progress_select.length) {
      progress_select.replaceWith(
        $('<input type="text" disabled="disabled" data-plugin-type="progress">').val(
          data
            ? data.toLocaleString(I18n.locale, { style: 'percent', maximumFractionDigits: 1 })
            : "-"
        )
      );
    }
  }

  return GobiertoCommonCustomFieldRecordsProgressPluginController;
})();

window.GobiertoAdmin.gobierto_common_custom_field_records_progress_plugin_controller = new GobiertoAdmin.GobiertoCommonCustomFieldRecordsProgressPluginController;

Add data attribute and set value when nil to input replacing of progress


const http = require('http');
const chokidar = require('chokidar');

const constructHierarchy = require('./roblox-tree.js');

module.exports = function startServer(dir, port) {
  console.log(`Server started on http://localhost:${port}`)
  console.log(`Using: ${dir}`)

  let objects = constructHierarchy(dir);

  // Rebuilds the hierarchy each time a file is changed.
  // Previously we would build the hierarchy each GET request.
  chokidar.watch(dir).on('change', (path) => {
    objects = constructHierarchy(dir);
  });

  http.createServer((req, res) => {
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify(objects, null, 1));
  }).listen(port);
}

const http = require('http');
const chokidar = require('chokidar');

const constructHierarchy = require('./roblox-tree.js');

module.exports = function startServer(dir, port) {
  console.log(`Server started on http://localhost:${port}`)
  console.log(`Using: ${dir}`)

  let objects = constructHierarchy(dir);

  // Rebuilds the hierarchy each time a file is changed.
  // Previously we would build the hierarchy each GET request.
  chokidar.watch(dir).on('all', (evt, path) => {
    // 'change' for when a file is modified.
    // 'unlink' for when a file is removed or renamed.
    if (evt === 'change' || evt === 'unlink') {
      objects = constructHierarchy(dir);
    }
  });

  http.createServer((req, res) => {
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify(objects, null, 1));
  }).listen(port);
}

Rebuild for file changes, along with removing and renaming files


/* @flow */

import { bus, cache } from '../../core-client';

cache.onChange(changes => bus.emit('postchange', changes));

bus.on('change', changes => cache.put(changes));
bus.on('error', changes => cache.put(changes));

bus.emit('state:init', {}, (err, state) => {
	if (err) {
		return;
	}

	bus.emit('change', { state });
});

/* @flow */

import { bus, cache } from '../../core-client';

cache.onChange(changes => bus.emit('postchange', changes));

bus.on('change', changes => cache.put(changes));
bus.on('error', changes => {
	if (changes.state) {
		const { resource, session, ...state } = changes.state; // eslint-disable-line no-use-before-define

		cache.put({ state });
	}
});

bus.emit('state:init', {}, (err, state) => {
	if (err) {
		return;
	}

	bus.emit('change', { state });
});

Handle error in state properly


#!/usr/bin/env node

var n2kMapper = require('../n2kMapper.js');
var msg = {
  "timestamp": "2013-10-08-15:47:28.263",
  "prio": "2",
  "src": "204",
  "dst": "255",
  "pgn": "127251",
  "description": "Rate of Turn",
  "fields": {
    "Rate": "0.0066071"
  }
};

console.log(JSON.stringify(n2kMapper.toFlat(msg)));
console.log(JSON.stringify(n2kMapper.toNested(msg)));

#!/usr/bin/env node

var n2kMapper = require('../n2kMapper.js');
var msg = {
  "timestamp": "2013-10-08T15:47:28.263Z",
  "prio": "2",
  "src": "204",
  "dst": "255",
  "pgn": "127251",
  "description": "Rate of Turn",
  "fields": {
    "Rate": "0.0066071"
  }
};

console.log(".toDelta()\n----------");
console.log(JSON.stringify(n2kMapper.toDelta(msg), null, 2));
console.log("\n.toNested()\n-----------");
console.log(JSON.stringify(n2kMapper.toNested(msg), null, 2));

Correct timestamp & pretty print JSON


import React, { PropTypes } from 'react';
import { TransitionView, Calendar } from 'react-date-picker';

import Modal from '../Modal';

const propTypes = {
  open: PropTypes.bool.isRequired,
  defaultDate: PropTypes.string,
  onClose: PropTypes.func.isRequired,
  onChange: PropTypes.func.isRequired
};

const now = new Date();
const defaultProps = {
  defaultDate: now.toISOString().replace('T', ' ')
    .slice(0, -5)
};

const TimePickerModal = props => props.open &&
  <Modal onClose={props.onClose} className="time-picker">
    <div className="flex-col">
      <TransitionView>
        <Calendar
          dateFormat="YYYY-MM-DD HH:mm:ss"
          defaultDate={props.defaultDate}
          onChange={(dateString) =>
              props.onChange(dateString)}
        />
      </TransitionView>
      <button
        className="btn btn-primary btn--absolute"
        onClick={props.onClose}
      >
        Apply
      </button>
    </div>
  </Modal>;

TimePickerModal.propTypes = propTypes;
TimePickerModal.defaultProps = defaultProps;

export default TimePickerModal;

import React, { PropTypes } from 'react';
import { TransitionView, Calendar } from 'react-date-picker';

import Modal from '../Modal';

const propTypes = {
  open: PropTypes.bool.isRequired,
  defaultDate: PropTypes.string,
  onClose: PropTypes.func.isRequired,
  onChange: PropTypes.func.isRequired
};

const TimePickerModal = props => props.open &&
  <Modal onClose={props.onClose} className="time-picker">
    <div className="flex-col">
      <TransitionView>
        <Calendar
          dateFormat="YYYY-MM-DD HH:mm:ss"
          defaultDate={props.defaultDate || ''}
          onChange={(dateString) =>
              props.onChange(dateString)}
        />
      </TransitionView>
      <button
        className="btn btn-primary btn--absolute"
        onClick={props.onClose}
      >
        Apply
      </button>
    </div>
  </Modal>;

TimePickerModal.propTypes = propTypes;

export default TimePickerModal;

Fix calendar modal default value


// Web page retrieval using server-side proxy support to get around CORS restrictions

requirejs(["sanitizeHTML", "vendor/purify"], function(sanitizeHTML, DOMPurify) {

    let content

    const url = "http://kurtz-fernhout.com/"
    // const url = "http://pdfernhout.net/"

    const crossOriginService = "/api/proxy"

    m.request({
        method: "POST",
        url: crossOriginService,
        data: { url },
    }).then((result) => {
        console.log("result", result)
        content = result.content
        m.redraw()
    }).catch((error) => {
        console.log("An error occured:", error)
        content = JSON.stringify(error)
        m.redraw()
    })

    Twirlip7.show(() => {
        return m("div", [
            content ?
                // Two examples of ways to sanitize web content for display locally -- but always a bit of a risk to display remote content
                sanitizeHTML.generateSanitizedHTMLForMithrilWithAttributes(m, DOMParser, content, {allowLinks: true, allowImages: true, baseURL: url})
                // m.trust(DOMPurify.sanitize(content))
                :
                "loading..."
        ])
    })
})

// Web page retrieval using server-side proxy support to get around CORS restrictions

requirejs(["sanitizeHTML", "vendor/purify"], function(sanitizeHTML, DOMPurify) {

    let content

    // const url = "http://kurtz-fernhout.com/"
    const url = "http://pdfernhout.net/"

    const crossOriginService = "/api/proxy"

    m.request({
        method: "POST",
        url: crossOriginService,
        data: { url },
    }).then((result) => {
        console.log("result", result)
        content = result.content
        m.redraw()
    }).catch((error) => {
        console.log("An error occured:", error)
        content = JSON.stringify(error)
        m.redraw()
    })

    Twirlip7.show(() => {
        return m("div", [
            content ?
                // Two examples of ways to sanitize web content for display locally -- but always a bit of a risk to display remote content
                sanitizeHTML.generateSanitizedHTMLForMithrilWithAttributes(m, DOMParser, content, {allowLinks: true, allowImages: true, baseURL: url})
                // m.trust(DOMPurify.sanitize(content))
                :
                "loading..."
        ])
    }, { title: url })
})

Improve proxy web page example


angular
  .module('app')
  .directive('projectQuickView', function() {
    return {
      restrict: 'E',
      replace: false,
      templateUrl: 'app/templates/partials/projectQuickView.html',
      link: function(scope, element, attrs) {
        scope.completed = attrs.completed;
      }
    };
  });

angular
  .module('app')
  .directive('projectQuickView', function() {
    return {
      restrict: 'E',
      replace: false,
      templateUrl: 'app/templates/partials/projectQuickView.html',
      link: function(scope, element, attrs) {
        scope.completed = attrs.completed;
        scope.isContentFromOldSite = function(item) {
          return item.end_date == "2012-10-20T04:00:00.000Z";
        };

        scope.completedStamp = function(item) {
          return scope.completed && !scope.isContentFromOldSite(item);
        };

      }
    };
  });

Update Project Quick View directive


const sanitizer = require('../../../sanitizer');
/**
 * Loop and sanitize fields of an item to create a new one
 *
 * @param  {Array<string>} fields    List of fields to look inside item
 * @param  {Object}        item      The item to be looped and sanitized
 * @param  {Object}        services  Sanitization methods and sanitize list services
 * @return {Object}                  A new object representing the sanitized item
 */
const loop = (fields, item) => {
  const data = {};

  for (let f = fields.length - 1; f >= 0; f--) {
    let key = fields[f];
    let value = item[key];

    // sanitize keys and values
    // TODO: Document keys and values sanitization better
    value = sanitizer.value(key, value, sanitizer);
    key = sanitizer.key(key, sanitizer);

    // update the field
    if (value) {
      Object.assign(data, {[key]: value});
    }
  }

  // Return the new object
  return data;
};

module.exports = loop;

const sanitizer = require('../../../sanitizer');
/**
 * Loop and sanitize fields of an item to create a new one
 *
 * @param  {Array<string>} fields    List of fields to look inside item
 * @param  {Object}        item      The item to be looped and sanitized
 * @param  {Object}        services  Sanitization methods and sanitize list services
 * @return {Object}                  A new object representing the sanitized item
 */
const loop = (fields, item) => {
  const data = {};

  for (let f = fields.length - 1; f >= 0; f--) {
    let key = fields[f];
    let value = item[key];

    // sanitize keys and values
    // TODO: Document keys and values sanitization better
    value = sanitizer.value(key, value, sanitizer);
    key = sanitizer.key(key, item);

    // update the field
    if (value && value.constructor === Array) {
      // if it's not an invalid array
      if (value[0]) {
        Object.assign(data, {[key]: value});
      }
    } else if (value) {
      // or if it's a valid single value
      Object.assign(data, {[key]: value});
    }
  }

  // Return the new object
  return data;
};

module.exports = loop;

Fix some fields being overwritten by undefined


class NavbarDirective {
  constructor () {
    'ngInject';

    let directive = {
      restrict: 'E',
      templateUrl: 'app/layout/navbar/navbar.html',
      scope: {
          creationDate: '='
      },
      controller: NavbarController,
      controllerAs: 'vm',
      bindToController: true
    };

    return directive;
  }
}

class NavbarController {
  constructor (moment) {
    'ngInject';

    // "this.creation" is avaible by directive option "bindToController: true"
    this.relativeDate = moment(this.creationDate).fromNow();
  }
}

export default NavbarDirective;

class NavbarDirective {
  constructor () {
    'ngInject';

    let directive = {
      restrict: 'E',
      templateUrl: 'app/layout/navbar/navbar.html',
      scope: {
          creationDate: '&'
      },
      controller: NavbarController,
      controllerAs: 'vm',
      bindToController: true
    };

    return directive;
  }
}

class NavbarController {
  constructor (moment) {
    'ngInject';

    // "this.creation" is avaible by directive option "bindToController: true"
    this.relativeDate = moment(this.creationDate()).fromNow();
  }
}

export default NavbarDirective;

Change navbar to use scope: '&' to prevent a unnecessary watch


var fab = require('fabulous');
var rest = require('fabulous/rest');
var Document = require('fabulous/Document');

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

exports.main = fab.run(document.body, todosApp);

function todosApp(node, context) {
	context.controller = new TodosController([]);

	var todosClient = rest.at('/todos');
	var patchClient = rest.at('/sync/todos');

	Document.sync([
		Document.fromPatchRemote(function(patch) {
			return patchClient.patch({ entity: patch });
		}, todosClient.get()),
		Document.fromProperty('todos', context.controller)
	]);
}

var fab = require('fabulous');
var rest = require('fabulous/rest');
var Document = require('fabulous/Document');

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

exports.main = fab.run(document.body, todosApp);

function todosApp(node, context) {
	context.controller = new TodosController([]);

	var todosClient = rest.at('/todos');
	var patchClient = rest.at('/sync/todos');

	Document.sync([
		Document.fromPatchSyncRemote(function(patch) {
			return patchClient.patch({ entity: patch });
		}, todosClient.get()),
		Document.fromProperty('todos', context.controller)
	]);
}

Update to latest sync api


import { Component } from 'substance'

export default class CaptionComponent extends Component {

  render($$) {
    const node = this.props.node

    let el = $$('div')
      .addClass('sc-caption')
      .attr('data-id', node.id)
    let contentEl = $$(this.getComponent('container'), {
      node: node,
      disabled: this.props.disabled
    })
    el.append(contentEl)
    return el
  }
}

import { Component } from 'substance'

export default class CaptionComponent extends Component {

  render($$) {
    const node = this.props.node

    let el = $$('div')
      .addClass('sc-caption')
      .attr('data-id', node.id)
    let contentEl = $$(this.getComponent('container'), {
      placeholder: 'Enter Caption',
      node: node,
      disabled: this.props.disabled
    })
    el.append(contentEl)
    return el
  }
}

Use placeholder for empty captions.


import React from 'react';
import {
    ApolloClient,
    ApolloProvider,
    createNetworkInterface,
} from 'react-apollo';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import initStore from '../shared/store';
import Routes from './routes';

require('offline-plugin/runtime').install();

const store = initStore(window.APOLLO_STATE);

const webClient = new ApolloClient({
  initialState: { apollo: window.APOLLO_STATE.apollo },
  networkInterface: createNetworkInterface({
    uri: '/graphql',
  }),
});

const render = (Component) => {
  ReactDOM.render(
    (
      <ApolloProvider client={webClient} store={store}>
        <BrowserRouter>
          <Component />
        </BrowserRouter>
      </ApolloProvider>
    ),
    document.querySelector('#app'),
  );
};

render(Routes);

if (module.hot) {
  // eslint-disable-next-line global-require
  module.hot.accept('./routes', () => render(require('./routes').default));
}

import React from 'react';
import {
    ApolloClient,
    ApolloProvider,
    createNetworkInterface,
} from 'react-apollo';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

import initStore from '../shared/store';
import Routes from './routes';

require('offline-plugin/runtime').install();

const store = initStore(window.APOLLO_STATE);

const webClient = new ApolloClient({
  initialState: { apollo: window.APOLLO_STATE.apollo },
  networkInterface: createNetworkInterface({
    uri: '/graphql',
  }),
});

const render = (Component) => {
  ReactDOM.hydrate(
    (
      <ApolloProvider client={webClient} store={store}>
        <BrowserRouter>
          <Component />
        </BrowserRouter>
      </ApolloProvider>
    ),
    document.querySelector('#app'),
  );
};

render(Routes);

if (module.hot) {
  // eslint-disable-next-line global-require
  module.hot.accept('./routes', () => render(require('./routes').default));
}

Switch from render to hydrate on the client side


enyo.kind({
	name: "MyApp.MainView",
	kind: "moon.Panel",
	classes: "moon main-view",
	controller: ".app.$.messageController",
	bindings: [{
		from: ".controller.message",
		to: ".title"
	}],
	components: [
		{content: "Your content here"}
	],
	headerComponents: [
		{kind: "moon.IconButton", src: "assets/icon-like.png"}
	]
});

enyo.kind({
	name: "MyApp.MainView",
	kind: "moon.Panel",
	classes: "moon main-view",
	bindings: [{
		from: ".app.$.messageController.message",
		to: ".title"
	}],
	components: [
		{content: "Your content here"}
	],
	headerComponents: [
		{kind: "moon.IconButton", src: "assets/icon-like.png"}
	]
});

Fix warning about deprecated controllers support


'use strict';

import gulp from 'gulp';
import shell from 'gulp-shell';

// import config from './_config.babel.js';

gulp.task('bower-install', shell.task([
  'bower install'
]));

'use strict';

import gulp from 'gulp';
import shell from 'gulp-shell';

// import config from './_config.babel.js';

gulp.task('bower-install', shell.task([
  'bower install --loglevel=warn'
]));

Reduce verbosity of Bower install log to warnings only


import { ACTIVE_IN_INPUT } from '../../application/keyboardShortcuts.js';
import { Class } from '../../core/Core.js';
import { ButtonView } from './ButtonView.js';

const ClearSearchButtonView = Class({
    Name: 'ClearSearchButtonView',

    Extends: ButtonView,

    positioning: 'absolute',

    baseClassName: 'v-ClearSearchButton',

    shortcut: 'Ctrl-/',
    shortcutWhenInputFocused: ACTIVE_IN_INPUT,
});

export { ClearSearchButtonView };

import { ACTIVE_IN_INPUT } from '../../application/keyboardShortcuts.js';
import { Class } from '../../core/Core.js';
import { ButtonView } from './ButtonView.js';
import { formatKeyForPlatform } from '../../application/formatKeyForPlatform.js';
import { loc } from '../../localisation/i18n.js';

const ClearSearchButtonView = Class({
    Name: 'ClearSearchButtonView',

    Extends: ButtonView,

    positioning: 'absolute',

    baseClassName: 'v-ClearSearchButton',

    tooltip: loc('Shortcut: {value1}', formatKeyForPlatform('Ctrl-/')),

    // Alternatives are for AZERTY keyboard
    shortcut: 'Ctrl-/ Ctrl-Shift-/ Ctrl-Shift-:',
    shortcutWhenInputFocused: ACTIVE_IN_INPUT,
});

export { ClearSearchButtonView };

Fix Ctrl-/ shortcut on AZERTY keyboards


(function () {
  'use strict';

  var React = require('react');
  var $ = require('jquery');

  var RatingForm = React.createClass({
    handleSubmit: function (event) {
      event.preventDefault();

      var $form = $(event.target);
      var rating = {
        text: $form.find('#text').val(),
        stars:  $form.find('#stars').val()
      };

      $.ajax({
        url: this.props.url,
        dataType: 'json',
        type: 'POST',
        data: rating,
        success: function(data) {
          this.props.ratingSent();
          // notify container
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(xhr, status, err.toString());
        }.bind(this)
      });
    },
    render: function () {
      return(
        <form onSubmit={ this.handleSubmit }>
          Stars <select id="stars" name="stars">
            <option value="1">1</option>
            <option value="1">2</option>
            <option value="1">3</option>
            <option value="1">4</option>
            <option value="1">5</option>
          </select>

          Text <input id="text" type="text" name="text" />

          <input type="submit" value="Send comment" />
        </form>
      );
    }
  });

  module.exports = RatingForm;
})();

(function () {
  'use strict';

  var React = require('react');
  var $ = require('jquery');

  var RatingForm = React.createClass({
    handleSubmit: function (event) {
      event.preventDefault();

      var $form = $(event.target);
      var rating = {
        text: $form.find('#text').val(),
        stars:  $form.find('#stars').val()
      };

      $.ajax({
        url: this.props.url,
        dataType: 'json',
        type: 'POST',
        data: rating,
        success: function(data) {
          this.props.ratingSent();
          // notify container
        }.bind(this),
        error: function(xhr, status, err) {
          console.error(xhr, status, err.toString());
        }.bind(this)
      });
    },
    render: function () {
      return(
        <form onSubmit={ this.handleSubmit }>
          Stars <select id="stars" name="stars">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
            <option value="5">5</option>
          </select>

          Text <input id="text" type="text" name="text" />

          <input type="submit" value="Send comment" />
        </form>
      );
    }
  });

  module.exports = RatingForm;
})();

Fix select values for stars


$(document).ready(function() {
  // fix for printing bug in Windows Safari
  var windowsSafari = (window.navigator.userAgent.match(/(\(Windows[\s\w\.]+\))[\/\(\s\w\.\,\)]+(Version\/[\d\.]+)\s(Safari\/[\d\.]+)/) !== null),
      style;

  if (windowsSafari) {
    // set the New Transport font to Arial for printing
    style = document.createElement('style');
    style.setAttribute('type', 'text/css');
    style.setAttribute('media', 'print');
    style.innerHTML = '@font-face { font-family: nta !important; src: local("Arial") !important; }';
    document.getElementsByTagName('head')[0].appendChild(style);
  }

  if (window.GOVUK && GOVUK.addCookieMessage) {
    GOVUK.addCookieMessage();
  }
});

(function() {
  // fix for printing bug in Windows Safari
  var windowsSafari = (window.navigator.userAgent.match(/(\(Windows[\s\w\.]+\))[\/\(\s\w\.\,\)]+(Version\/[\d\.]+)\s(Safari\/[\d\.]+)/) !== null),
      style;

  if (windowsSafari) {
    // set the New Transport font to Arial for printing
    style = document.createElement('style');
    style.setAttribute('type', 'text/css');
    style.setAttribute('media', 'print');
    style.innerHTML = '@font-face { font-family: nta !important; src: local("Arial") !important; }';
    document.getElementsByTagName('head')[0].appendChild(style);
  }

  if (window.GOVUK && GOVUK.addCookieMessage) {
    GOVUK.addCookieMessage();
  }
}).call(this);

Remove last usage of jQuery


/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';
import type {SchemaType} from '../../CodegenSchema.js';

const FlowParser = require('../../parsers/flow');
const fs = require('fs');

function combineSchemas(files: Array<string>): SchemaType {
  return files.reduce(
    (merged, filename) => {
      const contents = fs.readFileSync(filename, 'utf8');
      if (
        contents &&
        (/export\s+default\s+codegenNativeComponent</.test(contents) ||
          /extends TurboModule/.test(contents))
      ) {
        const schema = FlowParser.parseFile(filename);

        if (schema && schema.modules) {
          merged.modules = {...merged.modules, ...schema.modules};
        }
      }
      return merged;
    },
    {modules: {}},
  );
}

module.exports = combineSchemas;

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';
import type {SchemaType} from '../../CodegenSchema.js';

const FlowParser = require('../../parsers/flow');
const fs = require('fs');

function combineSchemas(files: Array<string>): SchemaType {
  return files.reduce(
    (merged, filename) => {
      const contents = fs.readFileSync(filename, 'utf8');
      if (
        contents &&
        (/export\s+default\s+\(?codegenNativeComponent</.test(contents) ||
          /extends TurboModule/.test(contents))
      ) {
        const schema = FlowParser.parseFile(filename);

        if (schema && schema.modules) {
          merged.modules = {...merged.modules, ...schema.modules};
        }
      }
      return merged;
    },
    {modules: {}},
  );
}

module.exports = combineSchemas;

Update regex used to check for codegenNativeComponent


const chai = require('chai');
const lookup = require('../../app/lib/postcodes').lookup;

const expect = chai.expect;

describe('Postcode to lat long', () => {
  it('Should return lat long for valid England postcode', (done) => {
    const res = { locals: { location: 'LS11 5RU' } };
    lookup(res, () => {
      /* eslint-disable no-unused-expressions */
      expect(res.locals.coordinates).to.exist;
      expect(res.locals.coordinates.latitude).to.exist;
      expect(res.locals.coordinates.longitude).to.exist;
      done();
      /* eslint-enable no-unused-expressions */
    });
  });
});

const chai = require('chai');
const lookup = require('../../app/lib/postcodes').lookup;

const expect = chai.expect;

describe('Postcode to lat long', () => {
  it('Should return lat long for valid England postcode', (done) => {
    const res = { locals: { location: 'BD24 9PT' } };
    lookup(res, () => {
      /* eslint-disable no-unused-expressions */
      expect(res.locals.coordinates).to.exist;
      expect(res.locals.coordinates.latitude).to.exist;
      expect(res.locals.coordinates.longitude).to.exist;
      done();
      /* eslint-enable no-unused-expressions */
    });
  });
});

Replace inactive postcode with active one


const express = require('express'),
      app = express(),
      bodyParser = require('body-parser'),
      methodOverride = require('method-override'),
      mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/mojibox');
app.set('view engine', 'pug');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride('_method'));

const userRoutes = require('./routes/users');
const emoticonRoutes = require('./routes/emoticons');

app.use('/user', userRoutes);
// app.use('/user/:user_id/', emoticonRoutes);

app.get('/', function(req, res, next) {
  res.redirect('/user');
});

if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

app.listen(3000, function() {
  console.log('Server is listening on port 3000');
});

const express = require('express'),
      app = express(),
      bodyParser = require('body-parser'),
      methodOverride = require('method-override'),
      mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/mojibox');
app.set('view engine', 'pug');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride('_method'));

const userRoutes = require('./routes/users');
const emoticonRoutes = require('./routes/emoticons');

app.use('/user', userRoutes);
app.use('/user/:username/collection', emoticonRoutes);

app.get('/', function(req, res, next) {
  res.redirect('/user');
});

if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

app.listen(3000, function() {
  console.log('Server is listening on port 3000');
});

Move /collection to path prepended to emoticonRoutes


'use strict'
const {expect} = require('chai')

describe('meta data mapper', () => {
  const {metaData} = require('../lib/mapper')

  it('should treat ../../ as design name', () => {
    const file = 'test/views/sampleView.js'
    expect(metaData(file)).to.have.property('design', 'test')
  })

  it('should treat current file name as view name', () => {
    const file = 'test/views/sampleView.js'
    expect(metaData(file)).to.have.property('view', 'sampleView')
  })
})

'use strict'
const {expect} = require('chai')

describe('meta data mapper', () => {
  const {metaData} = require('../lib/mapper')

  it('should treat ../../ as design name', () => {
    const file = 'test/views/sampleView.js'
    expect(metaData(file)).to.have.property('design', 'test')
  })

  it('should treat current file name as view name', () => {
    const file = 'test/views/sampleView.js'
    expect(metaData(file)).to.have.property('view', 'sampleView')
  })

  it('should require the file and import the map function', () => {
    const file = 'test/views/sampleView.js'
    const expectedMapFunc = require('./views/sampleView').map
    expect(metaData(file)).to.have.property('map', expectedMapFunc)
  })

  it('should require the file and import the reduce function', () => {
    const file = 'test/views/sampleView.js'
    const expectedReduceFunc = require('./views/sampleView').reduce
    expect(metaData(file)).to.have.property('reduce', expectedReduceFunc)
  })
})

Add test for metaData mapper