/*********************************************************************
 * NAN - Native Abstractions for Node.js
 *
 * Copyright (c) 2017 NAN contributors
 *
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 ********************************************************************/

const test     = require('tap').test
    , testRoot = require('path').resolve(__dirname, '..')
    , bindings = require('bindings')({ module_root: testRoot, bindings: 'weak' });

test('weak', function (t) {
  t.plan(3);

  var weak = bindings, count = 0;
  t.type(weak.hustle, 'function');

  weak.hustle(function () {}, function (val) {
    t.equal(val, 42);
    count++;
  });

  // run weak callback, should dispose
  gc();

  // do not run weak callback
  gc();

  t.equal(count, 1);
});

/*********************************************************************
 * NAN - Native Abstractions for Node.js
 *
 * Copyright (c) 2017 NAN contributors
 *
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 ********************************************************************/

const test     = require('tap').test
    , testRoot = require('path').resolve(__dirname, '..')
    , bindings = require('bindings')({ module_root: testRoot, bindings: 'weak' });

test('weak', function (t) {
  t.plan(3);

  var weak = bindings, count = 0;
  t.type(weak.hustle, 'function');

  weak.hustle(function () {}, function (val) {
    t.equal(val, 42);
    count++;
  });

  var timeout = setTimeout(function () {
    // run weak callback, should dispose
    gc();

    // do not run weak callback
    gc();

    if (count > 0) {
      clearTimeout(timeout);
      t.equal(count, 1);
    }
  }, 100);
});

Make garbage collection on the next tick to please TurboFan


var gulp         = require('gulp'),
    gulpif       = require('gulp-if'),
    imagemin     = require('gulp-imagemin'),
    livereload   = require('gulp-livereload'),
    newer        = require('gulp-newer'),
    svgSprite    = require('gulp-svg-sprite'),
    CONFIG       = require('../config.js')

gulp.task('svg-individual', function(cb) {
    return gulp.src('./source/svg/*.svg')
        .pipe(newer(CONFIG.dir.assets + 'svg'))
        .pipe(imagemin())
        .pipe(gulp.dest(CONFIG.dir.assets + 'svg/'))
        .pipe(gulpif(CONFIG.noEsBuild, livereload()))
})

gulp.task('svg-sprite', function(cb) {
    var options = {
        shape: {
            dimension: {
                maxWidth: 100,
                maxHeight: 100
            },
            id: {
                whitespace: '_',
            },
        },
        svg: {
            namespaceClassnames: false,
        },
        mode: {
            symbol: {
                dest: '',
                prefix: '',
                bust: false,
                sprite: 'sprite.svg'
            }
        }
    }

    return gulp.src('./source/svg/sprite/*.svg')
        .pipe(newer(CONFIG.dir.assets + 'svg/sprite.svg'))
        .pipe(svgSprite(options))
        .pipe(gulp.dest(CONFIG.dir.assets + 'svg/'))
        .pipe(gulpif(CONFIG.noEsBuild, livereload()))
})

gulp.task('svg', ['svg-individual', 'svg-sprite'])

var gulp         = require('gulp'),
    gulpif       = require('gulp-if'),
    imagemin     = require('gulp-imagemin'),
    livereload   = require('gulp-livereload'),
    newer        = require('gulp-newer'),
    svgSprite    = require('gulp-svg-sprite'),
    CONFIG       = require('../config.js')

gulp.task('svg-individual', function(cb) {
    return gulp.src('./source/svg/*.svg')
        .pipe(newer(CONFIG.dir.assets + 'svg'))
        .pipe(imagemin())
        .pipe(gulp.dest(CONFIG.dir.assets + 'svg/'))
        .pipe(gulpif(CONFIG.noEsBuild, livereload()))
})

gulp.task('svg-sprite', function(cb) {
    var options = {
        shape: {
            dimension: {
                maxWidth: 32,
                maxHeight: 32
            },
            id: {
                whitespace: '_',
            },
        },
        svg: {
            namespaceClassnames: false,
        },
        mode: {
            symbol: {
                dest: '',
                prefix: '',
                bust: false,
                sprite: 'sprite.svg'
            }
        }
    }

    return gulp.src('./source/svg/sprite/*.svg')
        .pipe(newer(CONFIG.dir.assets + 'svg/sprite.svg'))
        .pipe(svgSprite(options))
        .pipe(gulp.dest(CONFIG.dir.assets + 'svg/'))
        .pipe(gulpif(CONFIG.noEsBuild, livereload()))
})

gulp.task('svg', ['svg-individual', 'svg-sprite'])

Reduce los sprites SVG a 32x32


import React from 'react';
import {Platform} from 'react-native';
import {StateNavigator} from 'navigation';
import {NavigationStack} from 'navigation-react-native';
import Grid from './Grid';
import Detail from './Detail';

const colors = [
  'maroon', 'red', 'crimson', 'orange', 'brown', 'sienna', 'olive',
  'purple', 'fuchsia', 'indigo', 'green', 'navy', 'blue', 'teal', 'black'
];

export default () => {
  const stateNavigator = new StateNavigator([
    {key: 'grid', route: ''},
    {key: 'detail', route: '{color}', trackCrumbTrail: true},
  ]);

  const {grid, detail} = stateNavigator.states;
  grid.renderScene = () => <Grid colors={colors} />;
  detail.renderScene = ({color}) => <Detail colors={colors} color={color} />;

  detail.truncateCrumbTrail = (state, data, crumbs) => (
    crumbs.slice(-1)[0].state === detail ? crumbs.slice(0, -1) : crumbs
  );

  if (Platform.OS === 'web') {
    const buildStartUrl = url => {
      const {state, data} = stateNavigator.parseLink(url);
      return stateNavigator.fluent()
        .navigate('grid')
        .navigate(state.key, data).url;
    };
    stateNavigator.configure(stateNavigator, new NavigationStack.HistoryManager(buildStartUrl));
    stateNavigator.start()
  } else {
    stateNavigator.navigate('grid');
  }

  return stateNavigator;
}

import React from 'react';
import {Platform} from 'react-native';
import {StateNavigator} from 'navigation';
import {NavigationStack} from 'navigation-react-native';
import Grid from './Grid';
import Detail from './Detail';

const colors = [
  'maroon', 'red', 'crimson', 'orange', 'brown', 'sienna', 'olive',
  'purple', 'fuchsia', 'indigo', 'green', 'navy', 'blue', 'teal', 'black'
];

export default () => {
  const stateNavigator = new StateNavigator([
    {key: 'grid', route: ''},
    {key: 'detail', route: '{color}', trackCrumbTrail: true},
  ], NavigationStack.HistoryManager && new NavigationStack.HistoryManager(url => {
    const {state, data} = stateNavigator.parseLink(url);
    return stateNavigator.fluent()
      .navigate('grid')
      .navigate(state.key, data).url;
  }));

  const {grid, detail} = stateNavigator.states;
  grid.renderScene = () => <Grid colors={colors} />;
  detail.renderScene = ({color}) => <Detail colors={colors} color={color} />;

  detail.truncateCrumbTrail = (state, data, crumbs) => (
    crumbs.slice(-1)[0].state === detail ? crumbs.slice(0, -1) : crumbs
  );

  if (Platform.OS === 'web') stateNavigator.start()
  else stateNavigator.navigate('grid');

  return stateNavigator;
}

Create history manager in state navigator constructor


contract('Example', function(accounts) {
  it("should assert true", function(done) {
    var example = Example.at(Example.deployed_address);
    assert.isTrue(true);
    done();
  });
});

contract('Example', function(accounts) {
  it("should assert true", function(done) {
    var example = Example.deployed();
    assert.isTrue(true);
    done();
  });
});

Use Example.deployed() to retrieve deployed contract in tests.


import chai from 'chai';
import Library from '../lib/library.js';

chai.expect();

const expect = chai.expect;

var lib;

describe('Given an instance of my library', function () {
  before(function () {
    lib = new Library();
  });
  describe('when I need the name', function () {
    it('should return the name', () => {
      expect(lib.name).to.be.equal('Library');
    });
  });
});

import chai from 'chai';
import Library from '../lib/library.js';

chai.expect();

const expect = chai.expect;

let lib;

describe('Given an instance of my library', function () {
  before(function () {
    lib = new Library();
  });
  describe('when I need the name', function () {
    it('should return the name', () => {
      expect(lib.name).to.be.equal('Library');
    });
  });
});

Use 'let' instead of 'var' to better fit with other ES6 features


function AjaxNetwork() {
    function sendRequest(url, onComplete, data) {
        $.ajax({
            url: url,
            dataType: 'json',
            complete: onComplete,
            error: function(jqXHR, textStatus, errorThrown) {
                console.log("Error sending request to url(" + url + "): ",
                            errorThrown);
            }
        });
    }

    function sendGetAllCells(callback) {
        sendRequest("/info/cell/", callback);
    }

    function handleGetAllCells(response) {
        terrainTable = new Array(response.responseJSON.length);
        for (var i = response.responseJSON.length - 1; i >= 0; i--) {
            var cell = response.responseJSON[i].fields;
            terrainTable[cell.cType] = function(x, y) {
                return new Cell(x, y, cell.spriteName, cell.cType);
            }
            terrainTypes[cell.spriteName] = cell.cType;
        };
    }

    function sendGetViewWorldTemplate(callback) {
        sendRequest("/info/responses/viewWorld/", callback)
    }

    function handleGetViewWorldTemplate(response) {
        responseTemplates["viewWorld"] = response.responseJSON;
    }

    this.sendGetAllCells = sendGetAllCells;
    this.handleGetAllCells = handleGetAllCells;
    this.sendGetViewWorldTemplate = sendGetViewWorldTemplate;
    this.handleGetViewWorldTemplate = handleGetViewWorldTemplate;
}

function AjaxNetwork() {
    function sendRequest(url, onComplete, data, method) {
        var requestMethod = method == null ? "GET" : method;
        var headers = {};
        if (requestMethod == "POST") {
            headers["X-CSRFToken"] = $.cookie("csrftoken");
        }
        $.ajax({
            url: url,
            type: requestMethod,
            headers: headers,
            dataType: 'json',
            complete: onComplete,
            data: data,
            error: function(jqXHR, textStatus, errorThrown) {
                console.log("Error sending request to url(" + url + "): ",
                            errorThrown);
            }
        });
    }

    function sendGetAllCells(callback) {
        sendRequest("/info/cell/", callback);
    }

    function handleGetAllCells(response) {
        terrainTable = new Array(response.responseJSON.length);
        for (var i = response.responseJSON.length - 1; i >= 0; i--) {
            var cell = response.responseJSON[i].fields;
            terrainTable[cell.cType] = function(x, y) {
                return new Cell(x, y, cell.spriteName, cell.cType);
            }
            terrainTypes[cell.spriteName] = cell.cType;
        };
    }

    function sendGetViewWorldTemplate(callback) {
        sendRequest("/info/responses/viewWorld/", callback)
    }

    function handleGetViewWorldTemplate(response) {
        responseTemplates["viewWorld"] = response.responseJSON;
    }

    this.sendGetAllCells = sendGetAllCells;
    this.handleGetAllCells = handleGetAllCells;
    this.sendGetViewWorldTemplate = sendGetViewWorldTemplate;
    this.handleGetViewWorldTemplate = handleGetViewWorldTemplate;
}

Allow request type specification in ajax requests


Package.describe({
  name: 'meteor-base',
  version: '1.5.0',
  // Brief, one-line summary of the package.
  summary: 'Packages that every Meteor app needs',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.imply([
    // Super basic stuff about where your code is running and async utilities
    'meteor',

    // This package enables making client-server connections; currently Meteor
    // only supports building client/server web applications so this is not
    // removable
    'webapp',

    // The protocol and client/server libraries that Meteor uses to send data
    'ddp',

    // This package uses the user agent of each incoming HTTP request to
    // decide whether to inject <script> tags into the <head> of the
    // response document to polyfill ECMAScript 5 support.
    'es5-shim',

    // Push code changes to the client and automatically reload the page
    'hot-code-push'
  ]);
});

Package.describe({
  name: 'meteor-base',
  version: '1.5.1',
  // Brief, one-line summary of the package.
  summary: 'Packages that every Meteor app needs',
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.imply([
    // Super basic stuff about where your code is running and async utilities
    'meteor',

    // This package enables making client-server connections; currently Meteor
    // only supports building client/server web applications so this is not
    // removable
    'webapp',

    // The protocol and client/server libraries that Meteor uses to send data
    'ddp',

    // This package uses the user agent of each incoming HTTP request to
    // decide whether to inject <script> tags into the <head> of the
    // response document to polyfill ECMAScript 5 support.
    'es5-shim',

    // Push code changes to the client and automatically reload the page
    'hot-code-push'
  ]);
});

Remove posix from npm-shrinkwrap on webapp


const test = require('tape')
const BorrowState = require('../lib/module.js')

test(`strict-order`, (t) => {
  t.plan(2)
  let hasHappened = []
  let myState = new BorrowState()
  myState.block().then((state) => {
    hasHappened.push(1)
    state.foo = Math.PI
    state.unblock()
  })
  myState.block('r').then((state) => {
    hasHappened.push(2)
    state.foo = 5
    state.unblock()
  })
  // The following is not set to read-only so that it can occur *after* the previous
  myState.block().then((state) => {
    hasHappened.push(3)
    t.equal(state.foo, Math.PI, 'The state should still be set to pi')
    t.equal(hasHappened, [1, 2, 3], 'The operations happened in the right order')
    state.unblock()
  })
})

const test = require('tape')
const BorrowState = require('../lib/module.js')

test(`strict-order`, (t) => {
  t.plan(1)
  let hasHappened = []
  let myState = new BorrowState()
  myState.block().then((state) => {
    hasHappened.push(1)
    state.unblock()
  })
  myState.block('r').then((state) => {
    hasHappened.push(2)
    state.unblock()
  })
  myState.block().then((state) => {
    hasHappened.push(3)
    t.equal(hasHappened, [1, 2, 3], 'The operations happened in the right order')
    state.unblock()
  })
})

Make some adjustments to last commit


require('./common');

var
  DB_NAME = 'node-couchdb-test',

  callbacks = {
    A: false,
    B1: false,
    B2: false,
    C: false,
  },

  db = client.db(DB_NAME);

// Init fresh db
db.remove();
db
  .create(function(er) {
    if (er) throw er;

    var stream = db.changesStream();
    stream
      .addListener('change', function(change) {
        callbacks['B'+change.seq] = true;
        if (change.seq == 2) {
          stream.close();
        }
      })
      .addListener('end', function() {
        callbacks.C = true;
      });
  });

db.saveDoc({test: 1});
db.saveDoc({test: 2});

db.changes({since: 1}, function(er, r) {
  if (er) throw er;
  callbacks.A = true;
  assert.equal(2, r.results[0].seq);
  assert.equal(1, r.results.length);
});

process.addListener('exit', function() {
  checkCallbacks(callbacks);
});

require('./common');

var
  DB_NAME = 'node-couchdb-test',

  callbacks = {
    A: false,
    B1: false,
    B2: false,
    C: false,
  },

  db = client.db(DB_NAME);

// Init fresh db
db.remove();
db
  .create(function(er) {
    if (er) throw er;

    var stream = db.changesStream();
    stream
      .addListener('data', function(change) {
        callbacks['B'+change.seq] = true;
        if (change.seq == 2) {
          stream.close();
        }
      })
      .addListener('end', function() {
        callbacks.C = true;
      });
  });

db.saveDoc({test: 1});
db.saveDoc({test: 2});

db.changes({since: 1}, function(er, r) {
  if (er) throw er;
  callbacks.A = true;
  assert.equal(2, r.results[0].seq);
  assert.equal(1, r.results.length);
});

process.addListener('exit', function() {
  checkCallbacks(callbacks);
});

Update the event name in the test, too.


import { SplashEffect } from "./effect.js";
import { Linear } from "../interpolation.js";

export class DrawSVG extends SplashEffect {
	constructor(element, options) {
		super(element);

		options = options || {};

		this.interpolation = options.interpolation || new Linear();

		this.length = element.getTotalLength();
		this.setStyle("stroke-dasharray", this.length);
		this.setStyle("stroke-dashoffset", this.length);
	}

	in(value) {
		this.setStyle("stroke-dashoffset", (value * -1 + 1) * this.length);
	}

	out(value) {
		this.setStyle("stroke-dashoffset", value * this.length);
	}
}

import { SplashEffect } from "./effect.js";
import { Linear } from "../interpolation.js";

export class DrawSVG extends SplashEffect {
	constructor(element, options) {
		super(element);

		options = options || {};

		this.interpolation = options.interpolation || new Linear();

		this.fadeToFill = options.toFillFade;
		this.fadeToFillDelay = options.toFillDelay || 0;

		this.length = element.getTotalLength();
		this.setStyle("stroke-dasharray", this.length);
		this.setStyle("stroke-dashoffset", this.length);
	}

	in(value) {
		this.setStyle("stroke-dashoffset", (value * -1 + 1) * this.length);

		if (this.fadeToFill) {
			if (value > this.fadeToFillDelay) {
				let amount = (value - this.fadeToFillDelay) / (1 - this.fadeToFillDelay);
				this.setStyle("fill-opacity", amount);
				this.setStyle("stroke-opacity", amount * -1 + 1);
			}
		}
	}

	out(value) {
		this.setStyle("stroke-dashoffset", value * this.length);
	}
}

Add fade to fill function in draw SVG


// Just relay everything - could be useful for a spamtrap

exports.register = function() {
    this.register_hook('rcpt', 'confirm_all');
};

exports.confirm_all = function(next, connection, params) {
    var recipient = params.shift();
    connection.loginfo(this, "confirming recipient " + recipient);
    connection.relaying = 1;
    next(OK);
};

// Just relay everything - could be useful for a spamtrap

exports.register = function() {
    this.register_hook('rcpt', 'confirm_all');
};

exports.confirm_all = function(next, connection, params) {
    var recipient = params.shift();
    connection.loginfo(this, "confirming recipient " + recipient);
    connection.relaying = true;
    next(OK);
};

Use true not 1 for connection.relaying


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

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

var srcCSS = fs.readFileSync('./src/_type-scale.css', 'utf8')

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

fs.writeFileSync('./docs/typography/scale/index.html', html)

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

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

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

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

fs.writeFileSync('./docs/typography/scale/index.html', html)

Update with reference to global nav partial


/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

/* eslint-disable no-unused-vars */
/**
 * When using Closure Compiler, JSCompiler_renameProperty(property, object) is replaced by the munged name for object[property]
 * We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling.
 *
 * @param {string} prop
 * @param {?Object} obj
 * @return {string}
 */
window.JSCompiler_renameProperty = function(prop, obj) {
  return prop;
};
/* eslint-enable */

export {};

/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

/* eslint-disable no-unused-vars */
/**
 * When using Closure Compiler, JSCompiler_renameProperty(property, object) is replaced by the munged name for object[property]
 * We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling.
 *
 * @param {string} prop Property name
 * @param {?Object} obj Reference object
 * @return {string} Dereferenced value
 */
window.JSCompiler_renameProperty = function(prop, obj) {
  return prop;
};
/* eslint-enable */

export {};

Fix jsdoc to pass lint


#!/usr/bin/env node

// Modules
var connect = require('connect'),
    fs = require('fs'),
    http = require('http'),
    path = require('path'),
    serveStatic = require('serve-static'),
    serveIndex = require('serve-index');

// Variables
var app = connect(),
    hookFile = path.join(process.env.PWD, 'web-server-hook.js');

function main(port, rootPath, indexFile) {
    var hook = {
        config: function() {},
        postConfig: function() {}
    };

    rootPath = path.resolve(rootPath);

    if (fs.existsSync(hookFile)) {
        hook = require(hookFile);
    }

    if (undefined !== hook.config) {
        hook.config(app, port, rootPath);
    }

    app.use(serveStatic(rootPath, {'index': [indexFile]}));
    app.use(serveIndex(rootPath, {'icons': true, 'view': 'details'}));

    if (undefined !== hook.postConfig) {
        hook.postConfig(app, port, rootPath);
    }

    if (undefined != hook.createServer) {
        hook.createServer(app, port, rootPath);
    } else {
        http.createServer(app).listen(port, function() {
            console.log('Static server started at http://localhost:%d/', port);
        });
    }
}

module.exports = main;

#!/usr/bin/env node

// Modules
var connect = require('connect'),
    fs = require('fs'),
    http = require('http'),
    path = require('path'),
    serveStatic = require('serve-static'),
    serveIndex = require('serve-index');

function main(port, rootPath, indexFile) {

    rootPath = path.resolve(rootPath);

    var app = connect(),
    hookFile = path.join(rootPath, 'web-server-hook.js');
    hook = {
        config: function() {},
        postConfig: function() {}
    };

    if (fs.existsSync(hookFile)) {
        hook = require(hookFile);
    }

    if (undefined !== hook.config) {
        hook.config(app, port, rootPath);
    }

    app.use(serveStatic(rootPath, {'index': [indexFile]}));
    app.use(serveIndex(rootPath, {'icons': true, 'view': 'details'}));

    if (undefined !== hook.postConfig) {
        hook.postConfig(app, port, rootPath);
    }

    if (undefined != hook.createServer) {
        hook.createServer(app, port, rootPath);
    } else {
        http.createServer(app).listen(port, function() {
            console.log('Static server started at http://localhost:%d/', port);
        });
    }
}

module.exports = main;

Use same root path for server and hook file lookup


'use strict';

var toString = require('mdast-util-to-string');
var visit = require('unist-util-visit');
var slugs = require('github-slugger')();

module.exports = attacher;

function attacher() {
  return transformer;
}

/* Patch slugs on heading nodes. */
function transformer(ast) {
  slugs.reset();

  visit(ast, 'heading', function (node) {
    var id = slugs.slug(toString(node));
    var data = patch(node, 'data', {});

    /* Non-html */
    patch(data, 'id', id);
    /* Legacy remark-html */
    patch(data, 'htmlAttributes', {});
    /* Current remark-html */
    patch(data, 'hProperties', {});
    patch(data.htmlAttributes, 'id', id);
    patch(data.hProperties, 'id', id);
  });
}

function patch(context, key, value) {
  if (!context[key]) {
    context[key] = value;
  }

  return context[key];
}

'use strict';

var toString = require('mdast-util-to-string');
var visit = require('unist-util-visit');
var slugs = require('github-slugger')();

module.exports = slug;

function slug() {
  return transformer;
}

/* Patch slugs on heading nodes. */
function transformer(ast) {
  slugs.reset();

  visit(ast, 'heading', function (node) {
    var id = slugs.slug(toString(node));
    var data = patch(node, 'data', {});

    /* Non-html */
    patch(data, 'id', id);
    /* Legacy remark-html */
    patch(data, 'htmlAttributes', {});
    /* Current remark-html */
    patch(data, 'hProperties', {});
    patch(data.htmlAttributes, 'id', id);
    patch(data.hProperties, 'id', id);
  });
}

function patch(context, key, value) {
  if (!context[key]) {
    context[key] = value;
  }

  return context[key];
}

Use more meaningful plugin name


function send(hjelm) {
  return hjelm.program.model =
    hjelm.program.update(hjelm.message, hjelm.program.model)
}

class Hjelm {
  constructor(program) {
    this.program = program
  }

  run(message) {
    return this.program.model =
      send({ message, program: this.program })
  }
}

module.exports = program => {
  return new Hjelm(program)
}

const compose = require("compose-function")

/**
 * Allows an update function to transform the
 * model based on the message sent through Hjelm.
 */
function send(hjelm) {
  return hjelm.program.model =
    hjelm.program.update(hjelm.message, hjelm.program.model)
}

/**
 * Logs the Hjelm instance everytime a message is run.
 */
function log(hjelm) {
  console.log(hjelm)
  return hjelm
}

/** The Hjelm class */
class Hjelm {

  /**
   * Creates a Hjelm instance
   */
  constructor(program, middlewares) {
    this.program = program
    this.runMiddlewares = compose(...middlewares)
  }

  run(message) {
    return this.program.model =
      this.runMiddlewares({ message, program: this.program })
  }
}

module.exports = program => {
  return new Hjelm(program, [send, log])
}

Add log middleware and some jsdoc


var katex = require("katex");
var cheerio = require("cheerio");

module.exports = {
    book: {
        assets: "./book",
        js: []
    },
    hooks: {
        page: function(page) {
            for (var i in page.sections) {
                section = page.sections[i];
                if ( section.type != "normal" ) continue;

                var $ = cheerio.load(section.content);
                $("script").each(function() {
                    // Check is math
                    var type = $(this).attr("type") || "";
                    if (type.indexOf("math/tex") < 0) return;

                    var math = $(this).html().toString("utf8");
                    console.log("convert '"+math+"'");
                    $(this).replaceWith(katex.renderToString(math));
                });

                // Replace by transform
                section.content = $.html();
            }

            return page;
        }
    }
};

var katex = require("katex");
var cheerio = require("cheerio");

module.exports = {
    book: {
        assets: "./book",
        js: []
    },
    hooks: {
        page: function(page) {
            for (var i in page.sections) {
                section = page.sections[i];
                if ( section.type != "normal" ) continue;

                var $ = cheerio.load(section.content);
                $("script").each(function() {
                    // Check is math
                    var type = $(this).attr("type") || "";
                    if (type.indexOf("math/tex") < 0) return;

                    var math = $(this).html().replace(/\\/gi,"/");
                    $(this).replaceWith(katex.renderToString(math));
                });

                // Replace by transform
                section.content = $.html();
            }

            return page;
        }
    }
};

Fix backslashes before rendering math


// start webapck
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('./webpack.config');

// create express
const app = express();

// setup hot reload
config.plugins = [
  new webpack.HotModuleReplacementPlugin(),
  // setup no errors plugin
  new webpack.NoErrorsPlugin(),
];
  // override entry for hotload
config.entry = [
  'webpack-hot-middleware/client',
  config.entry,
];

// returns a Compiler instance
const compiler = webpack(config);
// stats outpu config
const statsConf = {
  colors: true,
  hash: false,
  timings: true,
  chunks: false,
  chunkModules: false,
  modules: false,
};
app.use(webpackMiddleware(compiler, {
  publicPath: config.output.publicPath,
  contentBase: 'src',
  stats: statsConf,
}));
app.use(webpackHotMiddleware(compiler));

// serve statics
app.use(express.static(__dirname));

// serve index
app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'index.html')));
// start server
app.listen(3000, (err) => {
  if (err) {
    console.log(err);
  }
  console.info('==> Listing on port 3000');
});

// start webapck
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('./webpack.config');

// create express
const app = express();

// setup hot reload
config.plugins = [
  // define plugin for node env
  new webpack.DefinePlugin({
    'process.env': {NODE_ENV: JSON.stringify(process.envNODE_ENV)},
  }),
  new webpack.HotModuleReplacementPlugin(),
  // setup no errors plugin
  new webpack.NoErrorsPlugin(),
];
  // override entry for hotload
config.entry = [
  'webpack-hot-middleware/client',
  config.entry,
];

// returns a Compiler instance
const compiler = webpack(config);
// stats outpu config
const statsConf = {
  colors: true,
  hash: false,
  timings: true,
  chunks: false,
  chunkModules: false,
  modules: false,
};
app.use(webpackMiddleware(compiler, {
  publicPath: config.output.publicPath,
  contentBase: 'src',
  stats: statsConf,
}));
app.use(webpackHotMiddleware(compiler));

// serve statics
app.use(express.static(__dirname));

// serve index
app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'index.html')));
// start server
app.listen(3000, (err) => {
  if (err) {
    console.log(err);
  }
  console.info('==> Listing on port 3000');
});

Add NODE_ENV to webpack using define plugin


'use strict';

const Database = require('nedb');
// let db = new Database({ filename: '.resource/db.sqlite', autoload: true });
let db = new Database();

class Bookmarker {

  create(path, callback) {
    this.select(path, function(doc) {
      if (doc == null) {
        var newDoc = {
          path: path,
          createdAt: Date()
        };
        db.insert(newDoc);
        callback(newDoc);
      } else {
        db.update({ path: path},
          { $set: { createdAt: Date() } },
          {},
          function(err, replaced) {
            callback(replaced);
          }
        );
      }
    });
  }

  select(path, callback) {
    db.findOne({ path: path }, function (err, doc) {
      callback(doc);
    });
  }

  selectAll(callback) {
    db.find({}).sort({ createdAt: -1 }).exec(function (err, docs) {
      callback(docs);
    });
  }

}

module.exports = new Bookmarker();

'use strict';

const Database = require('nedb');
let db = new Database({ filename: '.resource/db.sqlite', autoload: true });
// let db = new Database();

class Bookmarker {

  create(path, callback) {
    this.select(path, function(doc) {
      if (doc == null) {
        var newDoc = {
          path: path,
          createdAt: Date()
        };
        db.insert(newDoc);
        callback(newDoc);
      } else {
        db.update({ path: path},
          { $set: { createdAt: Date() } },
          {},
          function(err, replaced) {
            callback(replaced);
          }
        );
      }
    });
  }

  select(path, callback) {
    db.findOne({ path: path }, function (err, doc) {
      callback(doc);
    });
  }

  selectAll(callback) {
    db.find({}).sort({ createdAt: -1 }).exec(function (err, docs) {
      callback(docs);
    });
  }

}

module.exports = new Bookmarker();

Use local file for database


var os = require('os')
var stats = require('./lib/stats')

var wrapper = function(stat_type) {

  return function(pid, options, cb) {

    if(typeof options == 'function') {
      cb = options
      options = {}
    }

    return stats[stat_type](pid, options, cb)
  }
}

var pusage = {
  darwin: wrapper('ps'),
  sunos: wrapper('ps'),
  freebsd: wrapper('ps'),
  win: wrapper('win'),
  linux: wrapper('proc'),
  aix: wrapper('ps'),
  unsupported: function(pid, options, cb) {
    cb = typeof options == 'function' ? options : cb

    cb(new Error(os.platform() + ' is not supported yet, please fire an issue (https://github.com/soyuka/pidusage)'))
  }
}

var platform = os.platform();
platform = platform.match(/^win/) ? 'win' : platform; //nor is windows a winner...
platform = pusage[platform] ? platform : 'unsupported';

exports.stat = function() {
  pusage[platform].apply(stats, [].slice.call(arguments));
};

exports.unmonitor = function(pid) {
  delete stats.history[pid];
};

exports._history = stats.history;

var os = require('os')
var stats = require('./lib/stats')

var wrapper = function(stat_type) {

  return function(pid, options, cb) {

    if(typeof options == 'function') {
      cb = options
      options = {}
    }

    return stats[stat_type](pid, options, cb)
  }
}

var pusage = {
  darwin: wrapper('ps'),
  sunos: wrapper('ps'),
  freebsd: wrapper('ps'),
  netbsd: wrapper('proc'),
  win: wrapper('win'),
  linux: wrapper('proc'),
  aix: wrapper('ps'),
  unsupported: function(pid, options, cb) {
    cb = typeof options == 'function' ? options : cb

    cb(new Error(os.platform() + ' is not supported yet, please fire an issue (https://github.com/soyuka/pidusage)'))
  }
}

var platform = os.platform();
platform = platform.match(/^win/) ? 'win' : platform; //nor is windows a winner...
platform = pusage[platform] ? platform : 'unsupported';

exports.stat = function() {
  pusage[platform].apply(stats, [].slice.call(arguments));
};

exports.unmonitor = function(pid) {
  delete stats.history[pid];
};

exports._history = stats.history;

Support NetBSD via its Linux-compatible procfs.


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

module.exports = {
  name: 'ember-cli-toggle',

  included: function(app) {
    app.import('vendor/ember-cli-toggle/base.css');

    // Use configuration to decide which theme css files
    // to import, thus not populating the user's app
    this.importThemes(app);
  },

  importThemes: function(app) {
    var projectConfig = this.project.config(app.env);
    var config = projectConfig['ember-cli-toggle'];

    if (config) {
      var allThemes = ['light', 'ios', 'default', 'flat', 'skewed', 'flip'];
      var included = config.includedThemes;
      var excluded = config.excludedThemes;
      var themes = [];

      if (included && Array.isArray(included)) {
        themes = themes.concat(included);
      }
      else {
        themes = allThemes;
      }

      if (excluded && Array.isArray(excluded)) {
        themes = themes.filter(function (theme) {
          return excluded.indexOf(theme) === -1;
        });
      }

      themes = themes.filter(function (theme) {
        return theme && allThemes.indexOf(theme) !== -1;
      });

      themes.forEach(function (theme) {
        app.import('vendor/ember-cli-toggle/themes/' + theme + '.css');
      });
    }
  }
};

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

module.exports = {
  name: 'ember-cli-toggle',

  included: function(app, parentAddon) {
    var target = (parentAddon || app);

    target.import('vendor/ember-cli-toggle/base.css');

    // Use configuration to decide which theme css files
    // to import, thus not populating the user's app
    this.importThemes(target);
  },

  importThemes: function(app) {
    var projectConfig = this.project.config(app.env);
    var config = projectConfig['ember-cli-toggle'];
    var themes = [];

    if (config) {
      var allThemes = ['light', 'ios', 'default', 'flat', 'skewed', 'flip'];
      var included = config.includedThemes;
      var excluded = config.excludedThemes;

      if (included && Array.isArray(included)) {
        themes = themes.concat(included);
      }
      else {
        themes = allThemes;
      }

      if (excluded && Array.isArray(excluded)) {
        themes = themes.filter(function (theme) {
          return excluded.indexOf(theme) === -1;
        });
      }

      themes = themes.filter(function (theme) {
        return theme && allThemes.indexOf(theme) !== -1;
      });
    }

    themes = themes.length ? themes : ['default'];

    themes.forEach(function (theme) {
      app.import('vendor/ember-cli-toggle/themes/' + theme + '.css');
    });
  }
};

Fix default theme if none specified


var hoverZoomPlugins = hoverZoomPlugins || [];
hoverZoomPlugins.push({
    name:'GitHub',
    prepareImgLinks:function (callback) {
        var res = [];
        $('a > img[data-canonical-src]').each(function () {
            var img = $(this);
            img.data('hoverZoomSrc', [img.attr('data-canonical-src')]);
            img.data('hoverZoomCaption', [img.attr('alt')]);
            res.push(img);
        });
        hoverZoom.urlReplace(res,
            'a[href*="/blob/"]',
            /\/github.com\/(.*)\/blob\/(.*\.(jpg|png|gif))/,
            '/raw.githubusercontent.com/$1/$2'
        );
        callback($(res));
    }
});

var hoverZoomPlugins = hoverZoomPlugins || [];
hoverZoomPlugins.push({
    name:'GitHub',
    version:'0.3',
    prepareImgLinks:function (callback) {
        var res = [];

        $('a > img[data-canonical-src]').each(function () {
            var img = $(this);
            img.data('hoverZoomSrc', [img.attr('data-canonical-src')]);
            img.data('hoverZoomCaption', [img.attr('alt')]);
            res.push(img);
        });

        hoverZoom.urlReplace(res,
            'a[href*="/blob/"]',
            /\/github.com\/(.*)\/blob\/(.*\.(jpg|png|gif))/,
            '/raw.githubusercontent.com/$1/$2'
        );

        hoverZoom.urlReplace(res,
            'img[src]',
            /(.*)\?(.*)/,
            '$1'
        );

        callback($(res), this.name);
    }
});

Update for plug-in : GitHub


import { extend } from 'flarum/extend';
import PermissionGrid from 'flarum/components/PermissionGrid';

export default function() {
  extend(PermissionGrid.prototype, 'moderateItems', items => {
    items.add('tag', {
      label: 'Edit tags',
      permission: 'discussion.tag'
    });
  });
}

import { extend } from 'flarum/extend';
import PermissionGrid from 'flarum/components/PermissionGrid';

export default function() {
  extend(PermissionGrid.prototype, 'moderateItems', items => {
    items.add('tag', {
      label: 'Tag discussions',
      permission: 'discussion.tag'
    });
  });
}

Use more intuitive permission description


var page = require('webpage').create(),
  system = require('system'),
  address, outpng;

if (system.args.length !== 3) {
  console.log('Usage: phantomjs storepngsnapshot.js <some URL> <png output image path>');
  phantom.exit();
}

address = system.args[1];
outpng = system.args[2];
page.open(address, function(status) {
  if (status !== 'success') {
    console.log('FAIL to load the address');
  } else {
	page.render(outpng);
  }
  phantom.exit();
});

Store PNG snapshots phantomjs script adapted


$(document).ready(function(){
  $('.observable').on('change',
      function(evt) {
        var $field = $(evt.target);
        var url = $field.data('url');
        var target = $field.data('target');
        $.get(url, $field.serialize(),
            function(data) {
              $(target).html(data);
            });
      });
});

$(document).ready(function(){
  var timer = 0;

  var handle_field_change = function(evt) {
    var $field = $(evt.target);
    var url = $field.data('url');
    var target = $field.data('target');
    $.get(url, $field.serialize(),
        function(data) {
          $(target).html(data);
        });
  };

  var delay_callback = function(callback) {
    clearTimeout(timer);
    timer = setTimeout(callback, 200);
  }

  $('.observable').on('keyup keydown paste', function(evt) {
    delay_callback(function() { handle_field_change(evt) });
  });
});

Make live search respond more directly


// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// You can use CoffeeScript in this file: http://coffeescript.org/

ListenFor = (function() {
  var displaySection = function(section) {
    $(section).on('click', '.interest', function(event) {
      $('.main-content').hide('slow');
      $(this).siblings('.main-content').show('slow');
    });
  }

  var description = function() {
    $('.description').on('click', function(event) {
      event.preventDefault();
      $(this).parent().siblings().removeClass('hidden');
    })
  }

  var less = function() {
    $('.hidden').on('click', '.less', function(event) {
      event.preventDefault();
      $(this).parent().addClass('hidden');
    });
  }

  return {
    section: displaySection,
    description: description,
    less: less
  }
})();

$(document).ready(function() {
  ListenFor.section('#projects');
  ListenFor.section('#blog');
  ListenFor.section('#resume');
  ListenFor.section('#about');
  ListenFor.section('#contact');

  ListenFor.description();
  ListenFor.less();
});

// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// You can use CoffeeScript in this file: http://coffeescript.org/

ListenFor = (function() {
  var displaySection = function(section) {
    $(section).on('click', '.interest', function(event) {
      $('.main-content').hide('slow');
      $(this).siblings('.main-content').show('slow');
    });
  }

  var description = function() {
    $('.description').on('click', function(event) {
      event.preventDefault();
      $(this).parent().siblings('.hidden').show('slow');
    })
  }

  var less = function() {
    $('.hidden').on('click', '.less', function(event) {
      event.preventDefault();
      $(this).parent().hide('slow');
    });
  }

  return {
    section: displaySection,
    description: description,
    less: less
  }
})();

$(document).ready(function() {
  ListenFor.section('#projects');
  ListenFor.section('#blog');
  ListenFor.section('#resume');
  ListenFor.section('#about');
  ListenFor.section('#contact');

  ListenFor.description();
  ListenFor.less();
});

Make listeners to show & hide description more consistent


/*globals Polymer */
'use strict';

Polymer(
  {
    is: 'cordova-core',

    properties: {
      /**
       * Return if cordova deviceready event has been fired.
       */
      ready: {
        notify: true,
        readOnly: true,
        type: Boolean,
        value: false
      },

      /**
       * Return if cordova application is paused.
       */
      paused: {
        notify: true,
        readOnly: true,
        type: Boolean,
        value: false
      }
    },

    attached() {
      const cordovaEl = document.querySelector('#cordova');

      document.addEventListener(
        'deviceready',
        this._setReady.bind(this, true),
        false
      );
      document.addEventListener(
        'pause',
        this._setPaused.bind(this, true),
        false
      );
      document.addEventListener(
        'resume',
        this._setPaused.bind(this, false),
        false
      );

      if (!cordovaEl) {
        let script = document.createElement('script');

        script.id = 'cordova';
        script.src = 'cordova.js';

        document.body.appendChild(script);
      }
    },

    detached() {
      document.removeEventListener('deviceready', this._setReady);
      document.removeEventListener('pause', this._setPaused);
      document.removeEventListener('resume', this._setPaused);
    }
  }
);

/*globals Polymer */
'use strict';

Polymer(
  {
    is: 'cordova-core',

    properties: {
      /**
       * Return if cordova deviceready event has been fired.
       */
      ready: {
        notify: true,
        readOnly: true,
        type: Boolean,
        value: false
      },

      /**
       * Return if cordova application is paused.
       */
      paused: {
        notify: true,
        readOnly: true,
        type: Boolean,
        value: false
      }
    },

    attached() {
      const cordovaEl = document.querySelector('[src="cordova.js"]');

      document.addEventListener(
        'deviceready',
        this._setReady.bind(this, true),
        false
      );
      document.addEventListener(
        'pause',
        this._setPaused.bind(this, true),
        false
      );
      document.addEventListener(
        'resume',
        this._setPaused.bind(this, false),
        false
      );

      if (!cordovaEl) {
        let script = document.createElement('script');

        script.src = 'cordova.js';

        document.body.appendChild(script);
      }
    },

    detached() {
      document.removeEventListener('deviceready', this._setReady);
      document.removeEventListener('pause', this._setPaused);
      document.removeEventListener('resume', this._setPaused);
    }
  }
);

Change way to load cordova.js


'use strict';

// Third party dependencies
var moment = require('moment');

// Local dependencies
var command = require('../lib/command');
var fs      = require('../lib/fs');

/**
 * Returns the last modified timestamp of the remote file as a decimal number.
 * @param {!string} pathname
 * @param {!object} commandChannel
 * @param {!object} session
 */
command.add('MFMT', 'MFMT <sp> time <sp> pathname', function (args, commandChannel, session) {
  // Right number of arguments?
  if (args.split(' ').length < 2) {
    commandChannel.write(501, 'Invalid number of arguments');
    return;
  }

  // Parse the arguments
  var modificationTime = moment(args.split(' ', 2)[0], 'YYYYMMDDHHmmss');
  var pathname         = args.split(' ', 2)[1];
  var absolutePath     = fs.toAbsolute(pathname, session.cwd);

  // Valid modification time?
  if (!modificationTime.isValid()) {
    commandChannel.write(501, modificationTime + ': Invalid argument');
    return;
  }

  fs.utimes(absolutePath, modificationTime.unix(), modificationTime.unix(), function (err) {
    if (err) {
      commandChannel.write(550, fs.errorMessage(err, pathname));
    } else {
      commandChannel.write(213, 'Modify=' + modificationTime.format('YYYYMMDDHHmmss') + '; ' + pathname);
    }
  });
});

'use strict';

// Third party dependencies
var moment = require('moment');

// Local dependencies
var command = require('../lib/command');
var fs      = require('../lib/fs');

/**
 * Returns the last modified timestamp of the remote file as a decimal number.
 * @param {!string} pathname
 * @param {!object} commandChannel
 * @param {!object} session
 */
command.add('MFMT', 'MFMT <sp> time-val <sp> pathname', function (args, commandChannel, session) {
  // Right number of arguments?
  if (args.split(' ').length < 2) {
    commandChannel.write(501, 'Invalid number of arguments');
    return;
  }

  // Parse the arguments
  var modificationTime = moment(args.split(' ', 2)[0], 'YYYYMMDDHHmmss');
  var pathname         = args.split(' ', 2)[1];
  var absolutePath     = fs.toAbsolute(pathname, session.cwd);

  // Valid modification time?
  if (!modificationTime.isValid()) {
    commandChannel.write(501, modificationTime + ': Invalid argument');
    return;
  }

  fs.utimes(absolutePath, modificationTime.unix(), modificationTime.unix(), function (err) {
    if (err) {
      commandChannel.write(550, fs.errorMessage(err, pathname));
    } else {
      commandChannel.write(213, 'Modify=' + modificationTime.format('YYYYMMDDHHmmss') + '; ' + pathname);
    }
  });
});

Change help text for MFMT


var _ = require('lodash');
var vumigo = require('vumigo_v02');
var submit = require('./submit');
var Eventable = vumigo.events.Eventable;

var Ona = Eventable.extend(function(self, im) {
    self.submit = _.curry(submit, self);
});

module.exports = Ona;

var _ = require('lodash');
var vumigo = require('vumigo_v02');
var submit = require('./submit');
var Eventable = vumigo.events.Eventable;

var Ona = Eventable.extend(function(self, im) {
    self.submit = _.partial(submit, self);
});

module.exports = Ona;

Use _.partial instead of _.curry for Ona.submit


const TwitterPostBase = require('./TwitterPostBase');
const { captureSalmonRunGearScreenshot } = require('@/app/screenshots');
const { readData } = require('@/common/utilities');
const moment = require('moment-timezone');

class SalmonRunTweet extends TwitterPostBase {
    getKey() { return 'salmonrungear'; }
    getName() { return 'Salmon Run Gear'; }

    getRewardGear() {
        let timeline = readData('timeline.json');
        return timeline.coop && timeline.coop.reward_gear;
    }

    getData() {
        let rewardGear = this.getRewardGear();
        if (rewardGear.available_time == this.getDataTime())
            return rewardGear;
    }

    getTestData() {
        return this.getRewardGear();
    }

    getImage(data) {
        return captureSalmonRunGearScreenshot(data.available_time);
    }

    getText(data) {
        let monthName = moment.unix(data.available_time).tz('UTC').format('MMMM');
        return `Next month's Salmon Run reward gear has been posted! ${monthName}'s gear is the ${data.gear.name}. #salmonrun #splatoon2`;
    }
}

module.exports = SalmonRunTweet;

const TwitterPostBase = require('./TwitterPostBase');
const { captureSalmonRunGearScreenshot } = require('@/app/screenshots');
const { readData } = require('@/common/utilities');
const moment = require('moment-timezone');

class SalmonRunTweet extends TwitterPostBase {
    getKey() { return 'salmonrungear'; }
    getName() { return 'Salmon Run Gear'; }

    getRewardGear() {
        let timeline = readData('timeline.json');
        return timeline.coop && timeline.coop.reward_gear;
    }

    getData() {
        let rewardGear = this.getRewardGear();
        if (rewardGear.available_time == this.getDataTime())
            return rewardGear;
    }

    getTestData() {
        return this.getRewardGear();
    }

    getImage(data) {
        return captureSalmonRunGearScreenshot(data.available_time);
    }

    getText(data) {
        let monthName = moment.unix(data.available_time).tz('UTC').format('MMMM');
        return `New Salmon Run reward gear has been posted! ${monthName}'s gear is the ${data.gear.name}. #salmonrun #splatoon2`;
    }
}

module.exports = SalmonRunTweet;

Tweak wording in Salmon Run gear tweet


import 'process';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';

import 'codemirror/lib/codemirror.css';
import './react-docgen.less';

ReactDOM.render(<App />, document.getElementById('root'));

import 'process';
import React from 'react';
import * as ReactDOMClient from 'react-dom/client';
import App from './components/App';
import 'codemirror/lib/codemirror.css';
import './react-docgen.less';

const container = document.getElementById('root');
const root = ReactDOMClient.createRoot(container);

root.render(<App />);

Use new react 18 createRoot API


import ReactGA from 'react-ga'

ReactGA.initialize('UA-97498182-1')

export const logPageView = () => {
	ReactGA.set({ page: window.location.pathname })
	ReactGA.pageview(window.location.pathname)
}

import ReactGA from 'react-ga'

if (process.env.NODE_ENV === 'production') {
  ReactGA.initialize('UA-97498182-1')
}

export const logPageView = () => {
  if (process.env.NODE_ENV === 'production') {
    ReactGA.set({ page: window.location.pathname })
    ReactGA.pageview(window.location.pathname)
  }
}

Enable GA on prod only


Accounts.onCreateUser(function(options, user) {
  //send user email
  sendWelcomeEmail(options.email, "no-reply@cherish.com", options.email);

  if(user.services.facebook) {
    options.profile.avatarImg = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=large";
  } else {
    options.profile.avatarImg = "/images/placeholder-avatar.jpg";
  }
  user.profile = options.profile ? options.profile : {};
  return user;
});

Accounts.onCreateUser(function(options, user) {
  if(user.services.facebook) {
    options.profile.avatarImg = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=large";
  } else {
    options.profile.avatarImg = "/images/placeholder-avatar.jpg";
  }
  user.profile = options.profile ? options.profile : {};
  return user;
});

Remove email sending for now.


(function(app) {
  'use strict';

  var jCore = require('jcore');
  var helper = app.helper || require('../helper.js');
  var dom = app.dom || require('../dom.js');

  var ContentHeader = helper.inherits(function(props) {
    ContentHeader.super_.call(this);

    this.sidebarToggleType = this.prop(ContentHeader.SIDEBAR_TOGGLE_TYPE_COLLAPSE);
    this.element = this.prop(props.element);
    this.cache = this.prop({});
  }, jCore.Component);

  ContentHeader.prototype.sidebarToggleButtonElement = function() {
    return dom.child(this.element(), 0);
  };

  ContentHeader.prototype.redraw = function() {
    var sidebarToggleType = this.sidebarToggleType();
    var cache = this.cache();

    if (sidebarToggleType === cache.sidebarToggleType)
      return;

    dom.data(this.sidebarToggleButtonElement(), 'type', sidebarToggleType);
    cache.sidebarToggleType = sidebarToggleType;
  };

  ContentHeader.SIDEBAR_TOGGLE_TYPE_COLLAPSE = 'collapse';
  ContentHeader.SIDEBAR_TOGGLE_TYPE_EXPAND = 'expand';

  if (typeof module !== 'undefined' && module.exports)
    module.exports = ContentHeader;
  else
    app.ContentHeader = ContentHeader;
})(this.app || (this.app = {}));

(function(app) {
  'use strict';

  var jCore = require('jcore');
  var helper = app.helper || require('../helper.js');
  var dom = app.dom || require('../dom.js');

  var ContentHeader = helper.inherits(function(props) {
    ContentHeader.super_.call(this);

    this.sidebarToggleType = this.prop(ContentHeader.SIDEBAR_TOGGLE_TYPE_COLLAPSE);
    this.sidebarToggleDisabled = this.prop(false);
    this.element = this.prop(props.element);
    this.cache = this.prop({});
  }, jCore.Component);

  ContentHeader.prototype.sidebarToggleButtonElement = function() {
    return dom.child(this.element(), 0);
  };

  ContentHeader.prototype.redraw = function() {
    var cache = this.cache();

    var sidebarToggleType = this.sidebarToggleType();
    if (sidebarToggleType !== cache.sidebarToggleType) {
      dom.data(this.sidebarToggleButtonElement(), 'type', sidebarToggleType);
      cache.sidebarToggleType = sidebarToggleType;
    }

    var sidebarToggleDisabled = this.sidebarToggleDisabled();
    if (sidebarToggleDisabled !== cache.sidebarToggleDisabled) {
      dom.disabled(this.sidebarToggleButtonElement(), sidebarToggleDisabled);
      cache.sidebarToggleDisabled = sidebarToggleDisabled;
    }
  };

  ContentHeader.SIDEBAR_TOGGLE_TYPE_COLLAPSE = 'collapse';
  ContentHeader.SIDEBAR_TOGGLE_TYPE_EXPAND = 'expand';

  if (typeof module !== 'undefined' && module.exports)
    module.exports = ContentHeader;
  else
    app.ContentHeader = ContentHeader;
})(this.app || (this.app = {}));

Add property to disable sidebar-toggle-button


const Log = require('log')
const log = require('./log')

const test = require('./cli/test')
const config = require('./config')

const argv = require('minimist')(process.argv.slice(2))
const command = argv._[0]

if (argv.verbose) {
  log.level = Log.DEBUG
}

switch (command) {
  case 'test':
    config.validateFile(argv._[1])
      .then(config => test(config, argv))
      .catch(err => console.error(`Error: ${err.message}`))

    break

  case 'validate-config':
    config.validateFile(argv._[1])
      .then(() => console.log('Configuration is valid.'))
      .catch(err => console.error(err.message))

    break

  default:
    if (argv.version) {
      console.log(require('../package.json').version)
    } else {
      usage()
    }
}

function usage () {
  console.log(`
  Usage:

  whoopsie test <path>              Run visual regression tests using configuration at <path>
  whoopsie validate-config <path>   Validate configuration at <path>
  `)
}

const pkg = require('../package.json')
const Log = require('log')
const log = require('./log')

const test = require('./cli/test')
const config = require('./config')

const argv = require('minimist')(process.argv.slice(2))
const command = argv._[0]

if (argv.verbose) {
  log.level = Log.DEBUG
}

switch (command) {
  case 'test':
    config.validateFile(argv._[1])
      .then(config => test(config, argv))
      .catch(err => console.error(`Error: ${err.message}`))

    break

  case 'validate-config':
    config.validateFile(argv._[1])
      .then(() => console.log('Configuration is valid.'))
      .catch(err => console.error(err.message))

    break

  case 'version':
    console.log(pkg.version)
    break

  case 'help':
  default:
    usage()
}

function usage () {
  console.log(`
Whoopsie v${pkg.version}

Usage:

  whoopsie test <path>              Run visual regression tests using configuration at <path>
  whoopsie validate-config <path>   Validate configuration at <path>
  whoopsie version                  Show the program version
  whoopsie help                     Show this message
  `)
}

Add version and help commands


(function($) {
    $.fn.ellipsis = function(options) {

        // デフォルトオプション
        var defaults = {
            'row' : 1, // 省略行数
            'char' : '...' // 省略文字
        };

        options = $.extend(defaults, options);

        this.each(function() {
            // 現在のテキストを取得
            var $this = $(this);
            var text = $this.text();
            var origHeight = $this.height();

            // 1行分の高さを取得
            $this.text('a');
            var rowHeight = $this.height();
            var targetHeight = rowHeight * options.row;

            if (origHeight <= targetHeight) {
                $this.text(text);
                return;
            }

            // Binary search for max length
            var start = 1;
            var end = text.length;

            while (start < end) {
                var length = Math.floor((start + end) / 2);

                if (start === length) {
                    break;
                }

                $this.text(text.slice(0, length) + options['char']);

                if ($this.height () <= targetHeight) {
                    start = length;
                } else {
                    end = length - 1;
                }
            }

            $this.text(text.slice(0, start) + options['char']);
        });

        return this;
    };
}) (jQuery);

(function($) {
    $.fn.ellipsis = function(options) {

        // デフォルトオプション
        var defaults = {
            'row' : 1, // 省略行数
            'char' : '...' // 省略文字
        };

        options = $.extend(defaults, options);

        this.each(function() {
            // 現在のテキストを取得
            var $this = $(this);
            var text = $this.text();
            var origHeight = $this.height();

            // 1行分の高さを取得
            $this.text('a');
            var rowHeight = $this.height();
            var targetHeight = rowHeight * options.row;

            if (origHeight <= targetHeight) {
                $this.text(text);
                return;
            }

            // Binary search for max length
            var start = 1;
            var end = text.length;

            while (start < end) {
                var length = Math.ceil((start + end) / 2);

                $this.text(text.slice(0, length) + options['char']);

                if ($this.height () <= targetHeight) {
                    start = length;
                } else {
                    end = length - 1;
                }
            }

            $this.text(text.slice(0, start) + options['char']);
        });

        return this;
    };
}) (jQuery);

Use ceil() instead of floor() for midpoint calculation


var Autolinker = require('autolinker');

/**
 * parseUrls is a template filter to call Autolinker.
 * This is a library which automatically detects links, email addresses and
 * Twitter handles and converts them to clickable links.
 *
 * @param text {string}: Text to be converted
 *
 * @returns: string: Text containing clickable links.
 */
angular.module('app.filters').filter('parseUrls', parseUrls);

parseUrls.$inject = [];
function parseUrls() {
    return function(text) {
        return Autolinker.link(text, {
            replaceFn: function(match) {
                var email;
                switch (match.getType()) {
                    case 'email':
                        email = match.getEmail();
                        return '' + email + '';
                    default:
                        return true;
                }
            },
        });
    };
}

var Autolinker = require('autolinker');

/**
 * parseUrls is a template filter to call Autolinker.
 * This is a library which automatically detects links, email addresses and
 * Twitter handles and converts them to clickable links.
 *
 * @param text {string}: Text to be converted
 *
 * @returns: string: Text containing clickable links.
 */
angular.module('app.filters').filter('parseUrls', parseUrls);

parseUrls.$inject = [];
function parseUrls() {
    return function(text) {
        return Autolinker.link(text, {
            stripPrefix: false,
            stripTrailingSlash: false,
            replaceFn: function(match) {
                var email;
                switch (match.getType()) {
                    case 'email':
                        email = match.getEmail();
                        return '' + email + '';
                    default:
                        return true;
                }
            },
        });
    };
}

Set Autolinker option stripTrailingSlash to false so that trailing slashes are shown in urls.


var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var jsonParser = bodyParser.json();
var request = require('request');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', function (req, res) {
    res.send('Slack Commands!');
});

app.all('/github', jsonParser, function (req, res) {
    console.log(req.params);
    console.log(req.body);

    request({
        url: `http://api.github.com/users/${req.body.user_name || req.body.text}/events`,
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'User-Agent': 'SlackCmds',
        }
    }, function (error, response, body) {
        let data = JSON.parse(body).map((item)=>{
            return { 'type': item.type, 'date': item.created_at};
        });
        res.json(data);
    })
});

var port = process.env.PORT || 3000;
app.listen(port, function () {
    console.log(`Example app listening on port ${port}!`)
});

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var jsonParser = bodyParser.json();
var request = require('request');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', function (req, res) {
    res.send('Slack Commands!');
});

app.all('/github', jsonParser, function (req, res) {
    console.log(req.params);
    console.log(req.body);

    request({
        url: `http://api.github.com/users/${req.body.user_name || req.body.text}/events`,
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'User-Agent': 'SlackCmds',
        }
    }, function (error, response, body) {
        let data = JSON.parse(body).map((item)=>{
            return { 'type': item.type, 'date': item.created_at};
        });
        res.json(data.slice(0,3));
    })
});

var port = process.env.PORT || 3000;
app.listen(port, function () {
    console.log(`Example app listening on port ${port}!`)
});

Return latest 3 data items


import * as pdfjs from 'pdfjs-dist/build/pdf';
// eslint-disable-next-line import/no-unresolved
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?url';

import Document from './Document';
import Outline from './Outline';
import Page from './Page';

import { displayWorkerWarning } from './shared/utils';

displayWorkerWarning();

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

export { pdfjs, Document, Outline, Page };

import * as pdfjs from 'pdfjs-dist/build/pdf';
// eslint-disable-next-line import/no-unresolved
import pdfjsWorker from './pdf.worker.entry?url';

import Document from './Document';
import Outline from './Outline';
import Page from './Page';

import { displayWorkerWarning } from './shared/utils';

displayWorkerWarning();

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

export { pdfjs, Document, Outline, Page };

Fix Vite specific entry not working outside react-pdf workspace


#!/usr/bin/env node
"use strict";

var chokidar = require('chokidar')
var exec = require('child_process').exec

var command = process.argv[2]
var target = process.argv[3]

if (!command || !target) {
  usage()
  process.exit(1)
}

var watcher = chokidar.watch(target, {persistent: true})
var run = runner(command)

watcher
  .on('error', logError)
  .on('change', run)

console.log('Watching', target, 'and running command "' + command + '" on changes')

function runner(command) {
  var running = false

  return function() {
    if (running) return
    running = true

    var p = exec(command, function(err, stdout, stderr) {
      if (err !== null) logError(err)
      running = false
    })

    p.stdout.on('data', process.stdout.write)
    p.stderr.on('data', process.stderr.write)
  }
}

function logError(err) {
  console.error('chokidar-cmd error:', err.message)
}

function usage() {
  console.log('Usage: chokidar-watch \'command\' file-or-dir')
}

#!/usr/bin/env node
"use strict";

var chokidar = require('chokidar')
var child = require('child_process')

var command = process.argv[2]
var target = process.argv[3]

if (!command || !target) {
  usage()
  process.exit(1)
}

var watcher = chokidar.watch(target, {persistent: true})
var run = runner(command)

watcher
  .on('error', logError)
  .on('change', run)

console.log('Watching', target, 'and running command "' + command + '" on changes')

function runner(command) {
  var running = false

  return function() {
    if (running) return
    running = true

    execAsync(command, function(err, output) {
      if (err) logError(err, output)
      running = false
    })
  }
}

function logError() {
  console.error('chokidar-cmd error:', arguments)
}

function usage() {
  console.log('Usage: chokidar-watch \'command\' file-or-dir')
}

function execAsync(cmd, callback) {
  var output = ''

  var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
    callback(err ? err : null, output)
  })

  c.stdout.on('data', function(data) {
    output += data
  });

  c.stderr.on('data', function(data) {
    output += data
    process.stdout.write(data)
  });

  return c
}

Use a better exec from shelljs


'use babel'

/* @flow */

import {Intentions$Suggestion$Show} from '../types'

export function create(intention: Intentions$Suggestion$Show, length: number): HTMLElement {
  const element = document.createElement('intention-inline')
  let tries = 0
  element.textContent = '_'.repeat(length)
  requestAnimationFrame(function checkStyle() {
    if (++tries === 20) {
      // Ignore
      return
    }

    const styles = getComputedStyle(element)
    if (styles.lineHeight && styles.width !== 'auto') {
      element.style.width = styles.width
      element.style.height = styles.height
      element.style.top = '-' + styles.lineHeight
    } else requestAnimationFrame(checkStyle)
  })
  return element
}

'use babel'

/* @flow */

import {Intentions$Suggestion$Show} from '../types'

export function create(intention: Intentions$Suggestion$Show, length: number): HTMLElement {
  const element = document.createElement('intention-inline')
  let tries = 0
  element.style.opacity = '0'
  element.textContent = '_'.repeat(length)
  requestAnimationFrame(function checkStyle() {
    if (++tries === 20) { return }
    const styles = getComputedStyle(element)
    if (styles.lineHeight && styles.width !== 'auto') {
      element.style.opacity = '1'
      element.style.width = styles.width
      element.style.height = styles.height
      element.style.top = '-' + styles.lineHeight
    } else requestAnimationFrame(checkStyle)
  })
  return element
}

Add visual responsiveness of underlines


var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var busterPromise = require('buster-promise');
var checkedFile = require('./checked-file');

function checkFile(promise, fileName, err, file) {
  var errors = this.linter.check(file, this.options) ? [] : this.linter.check.errors;
  this.emit('fileChecked', checkedFile.create(fileName, errors));
  promise.resolve(errors);
}

function loadAndCheckFile(fileName) {
  var self = this;
  var promise = busterPromise.create();
  fs.readFile(fileName, 'utf-8', checkFile.bind(this, promise, fileName));
  return promise;
}

function create(options) {
  if (!options) { throw new TypeError('options is required (at least an empty object)'); }
  return Object.create(this, {
    options: { value: options },
    linter: { value: { check: require('../vendor/jslint').JSLINT } }
  });
}

module.exports = new EventEmitter();
module.exports.create = create;
module.exports.checkFile = loadAndCheckFile;

var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var busterPromise = require('buster-promise');
var checkedFile = require('./checked-file');

function check(file, fileName) {
  var errors = this.linter.check(file, this.options) ? [] : this.linter.check.errors;
  return checkedFile.create(fileName, errors);
}

function checkFile(promise, fileName, err, file) {
  var checked = check.call(this, file, fileName);
  this.emit('fileChecked', checked);
  promise.resolve(checked.errors);
}

function loadAndCheckFile(fileName) {
  var self = this;
  var promise = busterPromise.create();
  fs.readFile(fileName, 'utf-8', checkFile.bind(this, promise, fileName));
  return promise;
}

function create(options) {
  if (!options) { throw new TypeError('options is required (at least an empty object)'); }
  return Object.create(this, {
    options: { value: options },
    linter: { value: { check: require('../vendor/jslint').JSLINT } }
  });
}

module.exports = new EventEmitter();
module.exports.create = create;
module.exports.checkFile = loadAndCheckFile;
module.exports.check = check;

Add `check` method to linters, that just checks without loading a file.


function throw_error(s){
	console.log("ERROR: "+s);
	throw new Error(s);
}

module.exports.throw_error=throw_error;

function endsWith(subj,thing){
	return subj.indexOf(thing,subj.length-thing.length)!=-1;
}

module.exports.endsWith=endsWith;

Remove throw_error and add some useful function ;-)


export default function createDevToolsStore(onDispatch) {
  const initialState = {
    actionsById: {},
    computedStates: [],
    currentStateIndex: -1,
    monitorState: {},
    nextActionId: 0,
    skippedActionIds: [],
    stagedActionIds: []
  };
  let currentState = [];
  let listeners = [];
  let initiated = false;
  let instance;

  function dispatch(action) {
    if (action.type[0] !== '@') {
      if (action.type === 'JUMP_TO_STATE') {
        let state = getState();
        onDispatch(action, instance, state.computedStates[action.index]);
        setState({ ...state, currentStateIndex: action.index }, instance);
      } else onDispatch(action, instance);
    }
    return action;
  }

  function getState() {
    return (
      (instance ? currentState[instance] : initialState) || initialState
    );
  }

  function getInitialState() {
    return initialState;
  }

  function isSet() {
    return initiated;
  }

  function setState(state, id) {
    const isNew = !currentState[id];
    currentState[id] = state;
    listeners.forEach(listener => listener());
    initiated = true;
    return isNew;
  }

  function setInstance(id) {
    instance = id;
  }

  function subscribe(listener) {
    listeners.push(listener);

    return function unsubscribe() {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  return {
    dispatch,
    getState,
    subscribe,
    liftedStore: {
      dispatch,
      getInitialState,
      getState,
      setState,
      setInstance,
      subscribe,
      isSet
    }
  };
}

export default function createDevToolsStore(onDispatch) {
  const initialState = {
    actionsById: {},
    computedStates: [],
    currentStateIndex: -1,
    monitorState: {},
    nextActionId: 0,
    skippedActionIds: [],
    stagedActionIds: []
  };
  let currentState = [];
  let listeners = [];
  let initiated = false;
  let instance;

  function dispatch(action) {
    if (action.type[0] !== '@') {
      if (action.type === 'JUMP_TO_STATE') {
        let state = getState();
        onDispatch(action, instance, state.computedStates[action.index].state);
        setState({ ...state, currentStateIndex: action.index }, instance);
      } else onDispatch(action, instance);
    }
    return action;
  }

  function getState() {
    return (
      (instance ? currentState[instance] : initialState) || initialState
    );
  }

  function getInitialState() {
    return initialState;
  }

  function isSet() {
    return initiated;
  }

  function setState(state, id) {
    const isNew = !currentState[id];
    currentState[id] = state;
    listeners.forEach(listener => listener());
    initiated = true;
    return isNew;
  }

  function setInstance(id) {
    instance = id;
  }

  function subscribe(listener) {
    listeners.push(listener);

    return function unsubscribe() {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  return {
    dispatch,
    getState,
    subscribe,
    liftedStore: {
      dispatch,
      getInitialState,
      getState,
      setState,
      setInstance,
      subscribe,
      isSet
    }
  };
}

Send state when dispatched 'JUMP_TO_STATE'


import React from 'react';
import {Container} from 'semantic-ui-react';
import FacebookProvider, {Like} from 'react-facebook';
import Header from './Header';
import Footer from '../Footer';
import CalendarContainer from "../Calendar/CalendarContainer";

class Layout extends React.Component {

    render() {
        return (

                <FacebookProvider appId="1749300428641975" style={{float: 'right'}}>
                    <Like href={"https://www.facebook.com/inneklemtedager/"}/>
                </FacebookProvider>

                    <Container>
                        <CalendarContainer/>

                    </Container>

        );
    }
}

export default Layout;

import React from 'react';
import {Container} from 'semantic-ui-react';
import FacebookProvider, {Like} from 'react-facebook';
import Header from './Header';
import Footer from '../Footer';
import CalendarContainer from "../Calendar/CalendarContainer";

class Layout extends React.Component {

    render() {
        return (

                <FacebookProvider appId="1749300428641975">
                    <Like href={"https://www.facebook.com/inneklemtedager/"} width={document.documentElement.clientWidth}/>
                </FacebookProvider>

                    <Container>
                        <CalendarContainer/>

                    </Container>

        );
    }
}

export default Layout;

Make Facebook like button not take more width than available on mobile devices.


$(function(){
  console.log(JSON.stringify(scraperwiki.readSettings(), undefined, 2));
  console.log(6);

  var form_handler = new FormHandler();
  form_handler.setupLicenses();
  $('#apikey').on('input', function() {
    form_handler.setupOrganisations();
  });
  $('#submitBtn').click(function(){
    form_handler.resetErrors();
    if(form_handler.isValid()) {
      form_handler.submitDataset();
    }
    return false;
  });
});

$(function(){
  console.log(JSON.stringify(scraperwiki.readSettings(), undefined, 2));
  console.log(6);

  var form_handler = new FormHandler();
  form_handler.setupLicenses();

  $('#apikey').on('input', function() {
    form_handler.setupOrganisations();
  });

  $("form").submit(function() {
    $('#submitBtn').click();
    return false;
  });

  $('#submitBtn').click(function() {
    form_handler.resetErrors();
    if(form_handler.isValid()) {
      form_handler.submitDataset();
    }
    return false;
  });
});

Update so that form isn't submitted on enter.


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

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery/dist/jquery
//= require rails-ujs
//= require bootstrap-sass/assets/javascripts/bootstrap
//= require jquery-fileupload/basic
//= require_tree .

DELETE method is not working.


Package.describe({
  summary: 'Meetup OAuth flow',
  version: '1.0.1'
});

Package.onUse(function (api) {
  api.use('oauth2', ['client', 'server']);
  api.use('oauth', ['client', 'server']);
  api.use('http', ['server']);
  api.use('underscore', 'client');
  api.use('random', 'client');
  api.use('service-configuration', ['client', 'server']);

  api.addFiles('meetup_server.js', 'server');
  api.addFiles('meetup_client.js', 'client');

  api.export('Meetup');
});

Package.describe({
  summary: 'Meetup OAuth flow',
  version: '1.0.2'
});

Package.onUse(function (api) {
  api.use('oauth2', ['client', 'server']);
  api.use('oauth', ['client', 'server']);
  api.use('http', ['server']);
  api.use('underscore', ['client', 'server']);
  api.use('random', 'client');
  api.use('service-configuration', ['client', 'server']);

  api.addFiles('meetup_server.js', 'server');
  api.addFiles('meetup_client.js', 'client');

  api.export('Meetup');
});

Use underscore on server in meetup-oauth


import React, { Component } from 'react';
import TwitterLogin from 'react-twitter-auth/lib/react-twitter-auth-component.js';

class App extends Component {

  constructor() {
    super();

    this.onFailed = this.onFailed.bind(this);
    this.onSuccess = this.onSuccess.bind(this);
  }

  onSuccess(body) {
    alert(JSON.stringify(body));
  }

  onFailed(error) {
    alert(error);
  }

  render() {
    return (
      <div>
        <TwitterLogin loginUrl="http://localhost:4000/api/v1/auth/twitter" onFailure={this.onFailed} onSuccess={this.onSuccess} requestTokenUrl="http://localhost:4000/api/v1/auth/twitter/reverse"/>
      </div>
    );
  }
}

export default App;

import React, { Component } from 'react';
import TwitterLogin from 'react-twitter-auth/lib/react-twitter-auth-component.js';

class App extends Component {

  constructor() {
    super();

    this.onFailed = this.onFailed.bind(this);
    this.onSuccess = this.onSuccess.bind(this);
  }

  onSuccess(response) {
    response.json().then(body => {
      alert(JSON.stringify(body));
    });
  }

  onFailed(error) {
    alert(error);
  }

  render() {
    return (
      <div>
        <TwitterLogin loginUrl="http://localhost:4000/api/v1/auth/twitter" onFailure={this.onFailed} onSuccess={this.onSuccess} requestTokenUrl="http://localhost:4000/api/v1/auth/twitter/reverse"/>
      </div>
    );
  }
}

export default App;

Improve example so that it writes nicely received object on success


require('../test_helper');

mongoose.connect('mongodb://127.0.0.1:27017/api_umbrella_test');

require('../factories/api_users');

var redis = require('redis'),
    DatabaseCleaner = require('database-cleaner');

before(function(done) {
  var databaseCleaner = new DatabaseCleaner('mongodb');
  databaseCleaner.clean(mongoose.connection.db, function() {
    done();
  });
});

before(function(done) {
  var client = redis.createClient();

  var databaseCleaner = new DatabaseCleaner('redis');
  databaseCleaner.clean(client, function() {
    done();
  });
});

require('../test_helper');

mongoose.connect('mongodb://127.0.0.1:27017/api_umbrella_test');

require('../factories/api_users');

var redis = require('redis'),
    DatabaseCleaner = require('database-cleaner');

before(function(done) {
  mongoose.connection.on('connected', function() {
    var databaseCleaner = new DatabaseCleaner('mongodb');
    databaseCleaner.clean(mongoose.connection.db, function() {
      done();
    });
  });
});

before(function(done) {
  var client = redis.createClient();

  var databaseCleaner = new DatabaseCleaner('redis');
  databaseCleaner.clean(client, function() {
    done();
  });
});

Fix race condition. Ensure mongo is connected before cleaning the db.


export function router($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('login', {
      url: '/login',
      templateUrl: 'templates/login.html',
      controller: 'LoginCtrl'
    })
    .state('roomcontrol', {
      url: '/roomcontrol',
      templateUrl: 'templates/base.html',
      abstract: true
    })
    .state('roomcontrol.home', {
      url: '',
      templateUrl: 'templates/home.html'
    })
    .state('roomcontrol.music', {
      url: '/music',
      templateUrl: 'templates/music.html',
      controller: 'MusicCtrl'
    })
    .state('roomcontrol.light', {
      url: '/light',
      templateUrl: 'templates/light.html',
      controller: 'LightCtrl'
    })
    .state('roomcontrol.alarm', {
      url: '/alarm',
      templateUrl: 'templates/alarm.html',
      controller: 'AlarmCtrl'
    })
    .state('roomcontrol.settings', {
      url: '/settings',
      templateUrl: 'templates/settings.html',
      controller: 'SettingsCtrl'
    });
  $urlRouterProvider.otherwise('/login');
}

export function router($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('login', {
      url: '/login',
      templateUrl: 'templates/login.html',
      controller: 'LoginCtrl',
      onEnter: function($state, LocalStorage) {
        if (LocalStorage.getObjectProperty('device', 'activeSpotify')) {
          $state.go('roomcontrol.home');
        }
      },
      onExit: function($state, LocalStorage) {
        LocalStorage.setObjectProperty('device', 'activeSpotify', true);
      }
    })
    .state('roomcontrol', {
      url: '/roomcontrol',
      templateUrl: 'templates/base.html',
      abstract: true,
      onEnter: function($state, LocalStorage) {
        if (!LocalStorage.getObjectProperty('device', 'activeSpotify')) {
          $state.go('login');
        }
      }
    })
    .state('roomcontrol.home', {
      url: '',
      templateUrl: 'templates/home.html'
    })
    .state('roomcontrol.music', {
      url: '/music',
      templateUrl: 'templates/music.html',
      controller: 'MusicCtrl'
    })
    .state('roomcontrol.light', {
      url: '/light',
      templateUrl: 'templates/light.html',
      controller: 'LightCtrl'
    })
    .state('roomcontrol.alarm', {
      url: '/alarm',
      templateUrl: 'templates/alarm.html',
      controller: 'AlarmCtrl'
    })
    .state('roomcontrol.settings', {
      url: '/settings',
      templateUrl: 'templates/settings.html',
      controller: 'SettingsCtrl'
    });
  $urlRouterProvider.otherwise('/login');
}

Set activeSpotify to true when logging in and redirect to login if it's not set


var log4js = require('log4js');
var logger = log4js.getLogger('BOT-LOG');

function updateUserList(dyDB, table, userId, cb) {

	dyDB.putItem({
		"TableName": table,
		"Item": {
			"user_id": {"S": userId.toString()}
		}
	}, function(err, result) {

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

		result['userId'] = userId;

		cb(null, result);
	});
}

module.exports = {
	updateUserList: updateUserList
}

Remove logger as not use


const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        id: action.id,
        text: action.text,
        completed: false
      };
      break;
    case 'TOGGLE_TODO':
      if (state.id !== action.id) {
        return state
      }

      return {
        ...state,
        completed: !state.completed
      }
      break;
    default:
      return state;

  }
}

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, todo(undefined, action)]
      break;
    case 'TOGGLE_TODO':
      return state.map(t => {
        return todo(t, action)
      })
    default:
      return state;
  }
}

const cards = (state = [], action) => {
  switch (action.type) {
    case 'ADD_CARD':
      return [
        ...state,
        { id: action.id, todos: todos(undefined) }
      ];
    default:
      return state;
  }
}

export default todos;

const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        id: action.id,
        text: action.text,
        completed: false
      };
      break;
    case 'TOGGLE_TODO':
      if (state.id !== action.id) {
        return state
      }

      return {
        ...state,
        completed: !state.completed
      }
      break;
    default:
      return state;

  }
}

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, todo(undefined, action)]
      break;
    case 'TOGGLE_TODO':
      return state.map(t => {
        return todo(t, action)
      })
    default:
      return state;
  }
}

const cards = (state = [], action) => {
  switch (action.type) {
    case 'ADD_CARD':
      return [
        ...state,
        { cardid: action.cardid, todos: todos(undefined) }
      ];
    case 'ADD_TODO':
      return state.map((item) => {
        return todos(item.todos, action)
      });
    default:
      return state;
  }
}

export default todos;

Add reducer state handler for adding a todo in a card


var Player;

Player = function(options) {
  var paddle;
  this.canvasWidth = options.canvasWidth || 1280;
  this.canvasHeight = options.canvasHeight || 640;
  paddle = {
    x: options.paddle.x || 10,
    y: options.paddle.y || this.canvasHeight / 2 - (options.paddle.height / 2),
    width: options.paddle.width || 10,
    height: options.paddle.height || 100
  };
  this.paddle = new Paddle(paddle);
  this.keys = options.keys || {UP: 'I', DOWN: 'J'};
  this.score = 0;
  this.colour = options.colour || '#FFFFFF';
};

Player.prototype.render = function(context) {
  this.paddle.render(context);

  // Print score on screen
  context.font = 'bold 80px Helvetica, Verdana, san-serif';
  context.fillStyle = this.colour;
  context.fillText(this.score, (this.canvasWidth / 2) - 80, 80);
};

Player.prototype.update = function(inputHandler) {
  if (inputHandler.keysDown[inputHandler.KEY[this.keys.UP]]) {
    this.paddle.move(0, -5, this.canvasHeight);
  }
  else if(inputHandler.keysDown[inputHandler.KEY[this.keys.DOWN]]) {
    this.paddle.move(0, 5, this.canvasHeight);
  }
};

var Player;

Player = function(options) {
  var paddle;
  this.canvasWidth = options.canvasWidth || 1280;
  this.canvasHeight = options.canvasHeight || 640;
  paddle = {
    x: options.paddle.x || 10,
    y: options.paddle.y || this.canvasHeight / 2 - (options.paddle.height / 2),
    width: options.paddle.width || 10,
    height: options.paddle.height || 100
  };
  this.paddle = new Paddle(paddle);
  this.keys = options.keys || {UP: 'I', DOWN: 'J'};
  this.score = 0;
  this.scoreX = options.score.x || this.canvasWidth / 2;
  this.scoreY = options.score.y || this.canvasWidth / 10;
  this.colour = options.colour || '#FFFFFF';
};

Player.prototype.render = function(context) {
  this.paddle.render(context);

  // Print score on screen
  context.font = 'bold 80px Helvetica, Verdana, san-serif';
  context.fillStyle = this.colour;
  context.fillText(this.score, this.scoreX, this.scoreY);
};

Player.prototype.update = function(inputHandler) {
  if (inputHandler.keysDown[inputHandler.KEY[this.keys.UP]]) {
    this.paddle.move(0, -5, this.canvasHeight);
  }
  else if(inputHandler.keysDown[inputHandler.KEY[this.keys.DOWN]]) {
    this.paddle.move(0, 5, this.canvasHeight);
  }
};

Allow for game to set position of score drawings


/**
 * Native
 * ------
 *
 * Extension
 * ---------
 * .defer()             Defer the execution of a function
 * .curry()             Curry the function with a set of parameters
 **/
(function () {
	var Extensions = {};

	Extensions.defer = function (n) {
		if (!process || n > 0) {
			setTimeout(this, n);
		} else {
			process.nextTick(this);
		}
		return this;
	};
	Extensions.curry = function (scope) {
		var fun = this, args = Array.prototype.slice.apply(arguments, 1);

		return function () {
			fun.apply(scope || global || window, args.concat(Array.prototype.slice.apply(arguments)));
		};
	};

	function extendNative() {
		for (fun in Extensions) {
			if (!Function.prototype[fun]) {
				Object.defineProperty(Function.prototype, fun, {
					"value": Extensions[fun],
					"configurable": false,
					"enumerable": false,
					"writable": false
				});
			}
		}
	}

	if (typeof module != "undefined") {
		module.exports = { extendNative: extendNative };
	} else {
		extendNative();
	}
})();

/**
 * Native
 * ------
 *
 * Extension
 * ---------
 * .defer()             Defer the execution of a function
 * .curry()             Curry the function with a set of parameters
 **/
(function () {
	var Extensions = {};

	Extensions.defer = function (n) {
		if (!process || n > 0) {
			setTimeout(this, n);
		} else {
			process.nextTick(this);
		}
		return this;
	};
	Extensions.curry = function (scope) {
		var fun = this, args = Array.prototype.slice.apply(arguments, [ 1 ]);

		return function () {
			fun.apply(scope || global || window, args.concat(Array.prototype.slice.apply(arguments)));
		};
	};

	function extendNative() {
		for (fun in Extensions) {
			if (!Function.prototype[fun]) {
				Object.defineProperty(Function.prototype, fun, {
					"value": Extensions[fun],
					"configurable": false,
					"enumerable": false,
					"writable": false
				});
			}
		}
	}

	if (typeof module != "undefined") {
		module.exports = { extendNative: extendNative };
	} else {
		extendNative();
	}
})();

Fix bug created by previous commit - invalid fn.apply() arguments type


import test from 'ava';

import sidebarReducer, { INITIAL_STATE } from '../src/reducers/sidebar';
import { toggleSidebar } from '../src/actions/sidebar';

test('When you toggle the siebar, you invert the state', t => {
  t.is(
    sidebarReducer(INITIAL_STATE, toggleSidebar()),
    !INITIAL_STATE
  );
});

test('No unknown action will change the state', t => {
  t.is(
    sidebarReducer(INITIAL_STATE, { type: 'foo' }),
    INITIAL_STATE
  );
});

Test sidebar reaction to unknown actions


/* globals atom:false */

'use strict';

exports.provideBuilder = function () {
  return {
    niceName: 'Markdown',

    isEligable: function () {
      var textEditor = atom.workspace.getActiveTextEditor();
      if (!textEditor || !textEditor.getPath()) {
        return false;
      }
      var path = textEditor.getPath();
      return path.endsWith('.md') || path.endsWith('.mkd');
    },

    settings: function () {
      return [ {
        name: 'Markdown: view',
        sh: false,
        exec: 'open',
        args: [ '-a', 'Marked.app', '{FILE_ACTIVE}' ]
      }];
    }
  };
};

/* globals atom:false */

'use strict';

var spawnSync = require('child_process').spawnSync;

exports.provideBuilder = function () {
  return {
    niceName: 'Markdown',

    isEligable: function () {
      var textEditor = atom.workspace.getActiveTextEditor();
      if (!textEditor || !textEditor.getPath()) {
        return false;
      }
      var path = textEditor.getPath();
      return path.endsWith('.md') || path.endsWith('.mkd');
    },

    settings: function () {

      var app = spawnSync('mdfind', ['kMDItemCFBundleIdentifier == "com.brettterpstra.mark*"']);
      app = app.stdout.toString().split('\n')[0];

      return [ {
        name: 'Markdown: view',
        sh: false,
        exec: 'open',
        args: [ '-a', app || 'Marked 2', '{FILE_ACTIVE}' ]
      }];
    }
  };
};

Use mdfind to find the name of Marked or Marked 2.


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

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

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

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

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

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

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

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

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

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

Add site footer to each documentation generator


import React from 'react'
import ObservableView from './view'

const isEmission = (obj) => (
  typeof obj === 'object' &&
  typeof obj.x === 'number' &&
  obj.x === obj.x &&
  obj.x > 0 &&
  obj.d !== undefined &&
  obj.d !== null
)

const selectValue = obj => obj.x

function fromEmissions(arr, end) {
  if (!Array.isArray(arr) || !arr.every(isEmission)) {
    throw new Error([
      'Expected each value in `emissions` to be an emission',
      '({ x: [number], d: [string] })'
    ].join('. '))
  }

  const min = Math.min.apply(null, arr.map(selectValue))
  const max = typeof end === 'number' ? end : Math.max.apply(null, arr.map(selectValue))
  const range = max - min

  const minFactor = min / range

  const emissions = arr
    .filter(({ x }) => x <= max)
    .map(({ x, ...rest }) => ({
      ...rest,
      x: x / range - minFactor
    }))

  const completion = max / range - minFactor

  return props => (
    <ObservableView
      {...props}
      completion={completion}
      emissions={emissions}
    />
  )
}

export default fromEmissions

import React from 'react'
import ObservableView from './view'

const isEmission = (obj) => (
  typeof obj === 'object' &&
  typeof obj.x === 'number' &&
  obj.x === obj.x &&
  obj.x > 0 &&
  obj.d !== undefined &&
  obj.d !== null
)

const selectValue = obj => obj.x

function fromEmissions(arr, end) {
  if (!Array.isArray(arr) || !arr.every(isEmission)) {
    throw new Error([
      'Expected each value in `emissions` to be an emission',
      '({ x: [number], d: [string] })'
    ].join('. '))
  }

  const min = Math.min.apply(null, arr.map(selectValue))
  const max = typeof end === 'number' ? end : Math.max.apply(null, arr.map(selectValue))
  const range = max - min

  const minFactor = min / range

  const emissions = arr
    .filter(({ x }) => x <= max)
    .sort((a, b) => a.x - b.x)
    .map(({ x, ...rest }) => ({
      ...rest,
      x: x / range - minFactor
    }))

  const completion = max / range - minFactor

  return props => (
    <ObservableView
      {...props}
      completion={completion}
      emissions={emissions}
    />
  )
}

export default fromEmissions

Sort emissions in `froMEmissions` helper