//Needed components
var Header = require('../header').component;
var Cartridge = require('../cartridge').component;
var ContentBar = require('../content-bar').component;
var Bar = require('../bar').component;
var ContentActions = require('../content-actions').component;

module.exports = React.createClass({
  displayName: 'AppHeader',
  render: function renderApplicationHeader() {
    return (
      <Header>
        <ContentBar>
          <Bar appName='FOCUS'/>
          <Cartridge />
        </ContentBar>
        <ContentActions />
      </Header>
    );
  }
});

//Needed components
var Header = require('../header').component;
var Cartridge = require('../cartridge').component;
var ContentBar = require('../content-bar').component;
var Bar = require('../bar').component;
var ContentActions = require('../content-actions').component;
var applicationStore = require('focus').application.builtInStore();
module.exports = React.createClass({

  displayName: 'AppHeader',
/** @inheriteddoc */
  getInitialState: function getCartridgeInitialState() {
    return this._getStateFromStore();
  },
  /** @inheriteddoc */
  componentWillMount: function cartridgeWillMount() {
    applicationStore.addModeChangeListener(this._handleModeChange);
  },
  /** @inheriteddoc */
  componentWillUnMount: function cartridgeWillUnMount(){
    applicationStore.removeModeChangeListener(this._handleModeChange);
  },
  _handleModeChange: function(){
    this.setState(this._getStateFromStore());
  },
  _getStateFromStore: function getCartridgeStateFromStore(){
    var processMode =   applicationStore.getMode();
    var mode = 'consult';
    if(processMode && processMode.edit && processMode.edit > 0){
      mode = 'edit';
    }
  return {mode: mode};
  },
  render: function renderApplicationHeader() {
    return (
      <Header data-focus-mode={this.state.mode}>
        <ContentBar>
          <Bar appName='FOCUS'/>
          <Cartridge />
        </ContentBar>
        <ContentActions />
      </Header>
    );
  }
});

Add mode to the application header.


const fs = require('fs'),
    path = require('path'),
    async = require('async'),

    IGNORE_FILES = ['index.js', '_servers.js', 'servers.json'],
    SERVERS = [],
    URLS = {};

fs.readdirSync(__dirname).forEach(function (file) {
    if (IGNORE_FILES.includes(file)) { return; }

    SERVERS.push({
        name: path.basename(file, '.js'),
        server: require(path.join(__dirname, file))
    });
});

module.exports = {
    start: function (callback) {
        async.each(SERVERS, function (s, next) {
            s.server.listen(function (err) {
                if (err) { return next(err); }

                URLS[s.name] = s.server.url;
                next();
            });
        }, function (err) {
            if (err) { return callback(err); }

            fs.writeFileSync(path.join(__dirname, 'servers.json'), JSON.stringify(URLS));

            callback();
        });
    },

    close: function (callback) {
        async.each(SERVERS, function (server, next) {
            server.server.destroy(next);
        }, callback);
    }
};

const fs = require('fs'),
    path = require('path'),
    async = require('async'),

    IGNORE_FILES = ['index.js', '_servers.js', 'servers.json', '.gitignore'],
    SERVERS = [],
    URLS = {};

fs.readdirSync(__dirname).forEach(function (file) {
    if (IGNORE_FILES.includes(file)) { return; }

    SERVERS.push({
        name: path.basename(file, '.js'),
        server: require(path.join(__dirname, file))
    });
});

module.exports = {
    start: function (callback) {
        async.each(SERVERS, function (s, next) {
            s.server.listen(function (err) {
                if (err) { return next(err); }

                URLS[s.name] = s.server.url;
                next();
            });
        }, function (err) {
            if (err) { return callback(err); }

            fs.writeFileSync(path.join(__dirname, 'servers.json'), JSON.stringify(URLS));

            callback();
        });
    },

    close: function (callback) {
        async.each(SERVERS, function (server, next) {
            server.server.destroy(next);
        }, callback);
    }
};

Update list of ignored files in servers bootstrap


const cwd = require('path').resolve();
const fs = require('fs');
const { COPYFILE_EXCL } = fs.constants;

fs.copyFile('.eslintrc', '../../.eslintrc', COPYFILE_EXCL, (err) => {
    if (err) console.log('Preserving .eslintrc file found at ' + cwd);
});

const fs = require('fs');
const path = require('path');

const cwd = process.cwd();

const eslintrcPath = path.join(cwd, '.eslintrc');

if (!fs.existsSync(eslintrcPath)) {
    fs.writeFileSync(
        eslintrcPath,
        fs.readFileSync(path.join(__dirname, '../.eslintrc'), {
            encoding: 'utf8'
        })
    );

    console.log('Created .eslintrc file at ' + eslintrcPath);
} else {
    console.log('Preserving .eslintrc file found at ' + eslintrcPath);
}

Make post install script compatible with older versions of Node


/**
 * Main application file
 */

'use strict';
var path = require('path');

// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';

var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');

var hardware = require('./config/config.json').hardware;

// Connect to database
mongoose.connect(config.mongo.uri, config.mongo.options);

// Populate DB with sample data
if(config.seedDB) { require('./config/seed'); }

// Setup server
var app = express();
var server = require('http').createServer(app);
require('./config/express')(app);

var sockets = require('./sockets')(server);

if (hardware) {
	require('./hardware')(sockets);
}

require('./routes')(app, sockets);

// Start server
server.listen(config.port, config.ip, function () {
  console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});

// Expose app
exports = module.exports = app;

/**
 * Main application file
 */

'use strict';
var path = require('path');

// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';

var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');

var hardware = require('./config/config.json').hardware;

// Connect to database
mongoose.connect(config.mongo.uri, config.mongo.options);

// Populate DB with sample data
if(config.seedDB) { require('./config/seed'); }

// Setup server
var app = express();
var server = require('http').createServer(app);
require('./config/express')(app);

var hardwareInterface;

if (hardware) {
	hardwareInterface = require('./hardware')(sockets);
}

var sockets = require('./sockets')(server, hardwareInterface);

require('./routes')(app, sockets);

// Start server
server.listen(config.port, config.ip, function () {
  console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});

// Expose app
exports = module.exports = app;

Add hardware interface in sockets constructor


var bogart = require('bogart')
   , router = bogart.router()
   , path   = require('path')
   , mysql  = require('mysql')
   , util   = require('./lib/util').Util
   , settings = require('./config/settings').Settings
   , fs = require('fs');

var dbSettings = require('./config/settings').Settings.db;
var connection = mysql.createConnection(dbSettings);

var viewEngine = bogart.viewEngine('mustache', path.join(__dirname, 'views'));

var app = bogart.app();

app.use(function (nextApp) {
  return function (req) {
    req.env = Object.create(req.env);
    return nextApp(req);
  }
});

require('./lib/controllers')(router, viewEngine, connection);
//require('./lib/accessors')  uncomment this line if you want to use accessors

app.use(bogart.batteries); //Life is better with batteries

app.use(router); // Our router

var server = app.start({ port:1337 });

var bogart = require('bogart')
   , router = bogart.router()
   , path   = require('path')
   , mysql  = require('mysql')
   , util   = require('./lib/util').Util
   , settings = require('./config/settings').Settings
   , fs = require('fs');

var dbSettings = require('./config/settings').Settings.db;
var connection = mysql.createConnection(dbSettings);

var viewEngine = bogart.viewEngine('mustache', path.join(__dirname, 'lib/views'));

var app = bogart.app();

app.use(function (nextApp) {
  return function (req) {
    req.env = Object.create(req.env);
    return nextApp(req);
  }
});

require('./lib/controllers')(router, viewEngine, connection);
//require('./lib/accessors')  uncomment this line if you want to use accessors

app.use(bogart.batteries); //Life is better with batteries

app.use(router); // Our router

var server = app.start({ port:1337 });

Correct location of the views


'use strict';

// TODO: clean-up

var abs = require( '@stdlib/math/base/special/abs' );
var divide = require( 'compute-divide' );
var mean = require( 'compute-mean' );
var subtract = require( 'compute-subtract' );
var asinh = require( './../lib' );

var data = require( './fixtures/julia/data.json' );

var x = data.x;
var yexpected = data.expected;
var ycustom = new Array( x.length );
var ynative = new Array( x.length );
for ( var i = 0; i < x.length; i++ ) {
	if ( yexpected[ i ] === 0.0 ) {
		yexpected[ i ] += 1e-16;
	}
	ycustom[ i ] = asinh( x[ i ] );
	ynative[ i ] = Math.asinh( x[ i ] );
}

var customErrs = abs( divide( subtract( ycustom, yexpected ), yexpected ) );
var nativeErrs = abs( divide( subtract( ynative, yexpected ), yexpected ) );

console.log( 'The mean relative error of Math.asinh compared to Julia is %d', mean( nativeErrs ) );
console.log( 'The mean relative error of this module compared to Julia is %d', mean( customErrs ) );

'use strict';

// TODO: clean-up

// MODULES //

var abs = require( '@stdlib/math/base/special/abs' );
var divide = require( 'compute-divide' );
var mean = require( 'compute-mean' );
var subtract = require( 'compute-subtract' );
var asinh = require( './../lib' );

// FIXTURES //

var data = require( './fixtures/julia/data.json' );

// MAIN //

var nativeErrs;
var customErrs;
var yexpected;
var ynative;
var ycustom;
var x;
var i;

x = data.x;
yexpected = data.expected;
ycustom = new Array( x.length );
ynative = new Array( x.length );
for ( i = 0; i < x.length; i++ ) {
	if ( yexpected[ i ] === 0.0 ) {
		yexpected[ i ] += 1e-16;
	}
	ycustom[ i ] = asinh( x[ i ] );
	ynative[ i ] = Math.asinh( x[ i ] );
}

customErrs = abs( divide( subtract( ycustom, yexpected ), yexpected ) );
nativeErrs = abs( divide( subtract( ynative, yexpected ), yexpected ) );

console.log( 'The mean relative error of Math.asinh compared to Julia is %d', mean( nativeErrs ) );
console.log( 'The mean relative error of this module compared to Julia is %d', mean( customErrs ) );

Move variable declarations and add section headers


import Mixin from '@ember/object/mixin';

import { computed, get } from '@ember/object';
import { isPresent } from '@ember/utils';

export default Mixin.create({

  /*
   * Default namespace for application
   * container keys.
   *
   * @type {String}
   */
  namespace: computed.alias('config.namespace'),

  /*
   * Prepend the namespace to the key
   * before using it as a storage
   * identifier.
   *
   * @method buildNamespace
   *
   * @param {String} key
   *   A string to prepend a namespace onto.
   *
   * @return {String}
   *   A namespaced key identifer.
   */
  buildNamespace(key) {
    const namespace = get(this, 'namespace');
    return isPresent(namespace) ? `${namespace}:${key}` : `${key}`;
  },

  /*
   * Determine whether the key has been
   * previously namespaced.
   *
   * @method isNamespacedKey
   *
   * @param {String} key
   *   A namespaced key
   *
   * @return {Boolean}
   *   True if the key was previously namespaced, otherwise false.
   */
  isNamespacedKey(key) {
    return `${key}`.indexOf(this.buildNamespace('')) === 0;
  }
});

import Mixin from '@ember/object/mixin';

import { get } from '@ember/object';
import { alias } from '@ember/object/computed';
import { isPresent } from '@ember/utils';

export default Mixin.create({

  /*
   * Default namespace for application
   * container keys.
   *
   * @type {String}
   */
  namespace: alias('config.namespace'),

  /*
   * Prepend the namespace to the key
   * before using it as a storage
   * identifier.
   *
   * @method buildNamespace
   *
   * @param {String} key
   *   A string to prepend a namespace onto.
   *
   * @return {String}
   *   A namespaced key identifer.
   */
  buildNamespace(key) {
    const namespace = get(this, 'namespace');
    return isPresent(namespace) ? `${namespace}:${key}` : `${key}`;
  },

  /*
   * Determine whether the key has been
   * previously namespaced.
   *
   * @method isNamespacedKey
   *
   * @param {String} key
   *   A namespaced key
   *
   * @return {Boolean}
   *   True if the key was previously namespaced, otherwise false.
   */
  isNamespacedKey(key) {
    return `${key}`.indexOf(this.buildNamespace('')) === 0;
  }
});

Use the `alias` function from the computed module


import {extractGithubUrl, renderError, cinvoke} from '../lib/utils'
import {all as qall} from 'q'

const cmp = (one, other) => 0 + (one < other) - (one > other)
const cmpKey = key => (one, other) => cmp(one[key], other[key])

export function index(req, res) {
  const path = extractGithubUrl(req.query.url)
  if (path) { return res.redirect(path) }

  const client = req.gh.client

  // client.me().info(function(err, me, headers) {  })

  if (!client) { return res.render('index') }

  cinvoke(client.me(), 'orgs')
  .then(orgs => orgs.map(org => client.org(org.login)).concat(client.me()))
  .then(users => qall(users.map(user => cinvoke(user, 'repos'))))
  .then(repos => [].concat.apply([], repos))
  .then(repos => repos.sort(cmpKey('pushed_at')))
  .then(repos => res.render('index', {repos}))
  .catch(renderError(res))
}

import {extractGithubUrl, renderError, cinvoke} from '../lib/utils'
import {all as qall} from 'q'

const cmp = (one, other) => 0 + (one < other) - (one > other)
const cmpKey = key => (one, other) => cmp(one[key], other[key])

export async function index(req, res) {
  const path = extractGithubUrl(req.query.url)
  if (path) { return res.redirect(path) }

  const client = req.gh.client

  if (!client) { return res.render('index') }

  try {
    var repos = await cinvoke(client.me(), 'repos')
    repos = repos.sort(cmpKey('pushed_at'))
    res.render('index', {repos})
  }
  catch (error) {
    renderError(res, error)
  }
}

Fix double rendering projects. First async function :tada:


const config = require('../../config.js')

exports.user = accessToken => {
  return {
    headers: { 'Authorization': `Bearer ${accessToken}` }
  }
}

exports.bot = { headers: { 'Authorization': `Bot ${config.bot.token}` } }

const config = require('../../config.js')
const package = require('../../../package.json')

exports.common = {
  headers: {
    'User-Agent': `DiscordBot (${package.name}, ${package.version}) Node.js/${process.version}`
  }
}

exports.user = accessToken => {
  return {
    headers: {
      ...exports.common.headers,
      'Authorization': `Bearer ${accessToken}`
    }
  }
}

exports.bot = {
  headers: {
    ...exports.common.headers,
    'Authorization': `Bot ${config.bot.token}`
  }
}

Add User-Agent headers to HTTP API requests


import m from 'mithril';
import Session from '../models/session.js';
import GameComponent from './game.js';
import UpdateNotificationComponent from './update-notification.js';
import SWUpdateManager from 'sw-update-manager';

class AppComponent {

  oninit({ attrs: { roomCode } = {} } = {}) {
    this.session = new Session({
      url: window.location.origin
    });
    if (navigator.serviceWorker && !window.__karma__ && window.location.port !== '8080') {
      let serviceWorker = navigator.serviceWorker.register('/service-worker.js');
      this.updateManager = new SWUpdateManager(serviceWorker);
      this.updateManager.on('updateAvailable', () => m.redraw());
      this.updateManager.checkForUpdates();
    }
  }

  view() {
    return m('div#app', [
      this.updateManager ? m(UpdateNotificationComponent, {
        updateManager: this.updateManager
      }) : null,
      m(GameComponent, { session: this.session })
    ]);
  }

}

export default AppComponent;

import m from 'mithril';
import Session from '../models/session.js';
import GameComponent from './game.js';
import UpdateNotificationComponent from './update-notification.js';
import SWUpdateManager from 'sw-update-manager';

class AppComponent {

  oninit() {
    this.session = new Session({
      url: window.location.origin
    });
    if (navigator.serviceWorker && !window.__karma__ && window.location.port !== '8080') {
      let serviceWorker = navigator.serviceWorker.register('/service-worker.js');
      this.updateManager = new SWUpdateManager(serviceWorker);
      this.updateManager.on('updateAvailable', () => m.redraw());
      this.updateManager.checkForUpdates();
    }
  }

  view() {
    return m('div#app', [
      this.updateManager ? m(UpdateNotificationComponent, {
        updateManager: this.updateManager
      }) : null,
      m(GameComponent, { session: this.session })
    ]);
  }

}

export default AppComponent;

Fix syntax error when running coverage


module.exports = {
  parser: "@typescript-eslint/parser", // Specifies the ESLint parser
  extends: [
    "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
    "plugin:@typescript-eslint/recommended", // Uses the recommended rules from @typescript-eslint/eslint-plugin
    "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    "plugin:prettier/recommended" // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions: {
    ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
    sourceType: "module", // Allows for the use of imports
    ecmaFeatures: {
      jsx: true // Allows for the parsing of JSX
    }
  },
  rules: {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
    "camelcase": "off",
    "@typescript-eslint/camelcase": "off",
    "@typescript-eslint/no-inferrable-types": "off"
  },
  settings: {
    react: {
      version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
    }
  }
};

module.exports = {
  parser: "@typescript-eslint/parser", // Specifies the ESLint parser
  extends: [
    "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
    "plugin:@typescript-eslint/recommended", // Uses the recommended rules from @typescript-eslint/eslint-plugin
    "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    "plugin:prettier/recommended" // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions: {
    ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
    sourceType: "module", // Allows for the use of imports
    ecmaFeatures: {
      jsx: true // Allows for the parsing of JSX
    }
  },
  rules: {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
    "camelcase": "off",
    "@typescript-eslint/camelcase": "off",
    "@typescript-eslint/no-inferrable-types": "off",
    "@typescript-eslint/no-explicit-any": "off"
  },
  settings: {
    react: {
      version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
    }
  }
};

Remove rule to warn on explicit any


PDRClient.directive('permissionsInfo', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {
      content: '@',
      placement: '@'
    },
    controller: ['$scope', '$rootScope', '$timeout', function($scope, $rootScope, $timeout) {
      $timeout(function(){
        $("[data-toggle=requestPopover]").popover({
          html : true,
          placement: $scope.placement,
          trigger: 'hover',
          content: "Facilitator
Facilitators share responsibility with the district facilitator to contact participants and view their individual responses, facilitate the consensus meeting, and view the final consensus report.
Participant
Participants responds to the individual readiness assessment survey and take part of the consensus meeting. They can view the final consensus report
Viewer
Viewers have read-only access to the final consensus report. They cannot view individual participant responses or data.
"
        });
      });
    }],
    templateUrl: 'client/views/directives/help_info_popover.html'
  }
});

PDRClient.directive('permissionsInfo', function() {
  return {
    restrict: 'E',
    transclude: true,
    scope: {
      content: '@',
      placement: '@'
    },
    controller: ['$scope', '$rootScope', '$timeout', function($scope, $rootScope, $timeout) {
      $timeout(function(){
        $("[data-toggle=requestPopover]").popover({
          html : true,
          placement: $scope.placement,
          trigger: 'hover',
          content: "Facilitator
Facilitators share responsibility with the district facilitator to contact participants and view their individual responses, facilitate the consensus meeting, and view the final consensus report.
Participant
Participants responds to the individual readiness assessment survey and take part of the consensus meeting. They can view the final consensus report
"
        });
      });
    }],
    templateUrl: 'client/views/directives/help_info_popover.html'
  }
});

Remove reference to "Viewer" in modal


const Route = require('../../structures/Route');
const Util = require('../../utils/Util');
const log = require('../../utils/Log');

class fileDELETE extends Route {
	constructor() {
		super('/file/:id', 'delete');
	}

	async run(req, res, db, user) {
		const { id } = req.params;
		if (!id) return res.status(400).json({ message: 'Invalid file ID supplied' });

		/*
			Make sure the file exists
		*/
		const file = await db.table('files').where({ id, userId: user.id }).first();
		if (!file) return res.status(400).json({ message: 'The file doesn\'t exist or doesn\'t belong to the user' });

		/*
			Delete the file
		*/
		try {
			await Util.deleteFile(file.name, true);
			return res.json({ message: 'The file was deleted successfully' });
		} catch (error) {
			log.error(error);
			return res.json({ message: 'There was a problem deleting the file' });
		}
	}
}

module.exports = fileDELETE;

const Route = require('../../structures/Route');
const Util = require('../../utils/Util');
const log = require('../../utils/Log');

class fileDELETE extends Route {
	constructor() {
		super('/file/:id', 'delete', { canApiKey: true });
	}

	async run(req, res, db, user) {
		const { id } = req.params;
		if (!id) return res.status(400).json({ message: 'Invalid file ID supplied' });

		/*
			Make sure the file exists
		*/
		const file = await db.table('files').where({ id, userId: user.id }).first();
		if (!file) return res.status(400).json({ message: 'The file doesn\'t exist or doesn\'t belong to the user' });

		/*
			Delete the file
		*/
		try {
			await Util.deleteFile(file.name, true);
			return res.json({ message: 'The file was deleted successfully' });
		} catch (error) {
			log.error(error);
			return res.json({ message: 'There was a problem deleting the file' });
		}
	}
}

module.exports = fileDELETE;

Enable deleting files with the API key


const KEY = 'flash-messages'

function getMessages() {
  const items = window.sessionStorage.getItem(KEY)
  if (items) {
    try {
      return JSON.parse(items)
    } catch (e) {
      // eslint-disable-next-line no-console
      console.log(e) // TODO: replace with more robust error logging when implemented
    }
  }
  return {}
}

function addMessage(messageType, message) {
  const messages = getMessages()
  messages[messageType] = messages[messageType] || []
  messages[messageType].push(message)
  try {
    window.sessionStorage.setItem(KEY, JSON.stringify(messages))
  } catch (e) {
    // eslint-disable-next-line no-console
    console.log(e) // TODO: replace with more robust error logging when implemented
  }
}

function addSuccessMessage(message) {
  addMessage('success', message)
}

function clearMessages() {
  window.sessionStorage.removeItem(KEY)
}

module.exports = {
  getMessages,
  addMessage,
  addSuccessMessage,
  clearMessages,
}

const KEY = 'flash-messages'

const getMessages = () => {
  const items = window.sessionStorage.getItem(KEY)
  if (items) {
    try {
      return JSON.parse(items)
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error('Cannot get messages from session storage', e)
    }
  }
  return {}
}

const addMessage = (messageType, message) => {
  const messages = getMessages()
  messages[messageType] = messages[messageType] || []
  messages[messageType].push(message)
  try {
    window.sessionStorage.setItem(KEY, JSON.stringify(messages))
  } catch (e) {
    // eslint-disable-next-line no-console
    console.error('Cannot set messages to session storage', e)
  }
}

const clearMessages = () => window.sessionStorage.removeItem(KEY)

module.exports = {
  getMessages,
  addMessage,
  clearMessages,
}

Simplify helper function for flash messages


// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

///////////////////////////////////////////////////////////////////////////////
// SystemOptions class:

/**
 * Encapsulated handling of ChromeOS system options page.
 * @constructor
 */
function SystemOptions(model) {
  OptionsPage.call(this, 'system', templateData.systemPage, 'systemPage');
}

SystemOptions.getInstance = function() {
  if (SystemOptions.instance_)
    return SystemOptions.instance_;
  SystemOptions.instance_ = new SystemOptions(null);
  return SystemOptions.instance_;
}

// Inherit SystemOptions from OptionsPage.
SystemOptions.prototype = {
  __proto__: OptionsPage.prototype,

  /**
   * Initializes SystemOptions page.
   * Calls base class implementation to starts preference initialization.
   */
  initializePage: function() {
    OptionsPage.prototype.initializePage.call(this);
    var timezone = $('timezone-select');
    if (timezone) {
      timezone.initializeValues(templateData.timezoneList);
    }

    $('language-button').onclick = function(event) {
      // TODO: Open ChromeOS language settings page.
    };
    $('sync-button').onclick = function(event) {
      OptionsPage.showPageByName('sync');
    }
    $('dummy-button').onclick = function(event) {
      OptionsPage.showOverlay('dummy');
    }
  },
};

// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

///////////////////////////////////////////////////////////////////////////////
// SystemOptions class:

/**
 * Encapsulated handling of ChromeOS system options page.
 * @constructor
 */
function SystemOptions(model) {
  OptionsPage.call(this, 'system', templateData.systemPage, 'systemPage');
}

SystemOptions.getInstance = function() {
  if (SystemOptions.instance_)
    return SystemOptions.instance_;
  SystemOptions.instance_ = new SystemOptions(null);
  return SystemOptions.instance_;
}

// Inherit SystemOptions from OptionsPage.
SystemOptions.prototype = {
  __proto__: OptionsPage.prototype,

  /**
   * Initializes SystemOptions page.
   * Calls base class implementation to starts preference initialization.
   */
  initializePage: function() {
    OptionsPage.prototype.initializePage.call(this);
    var timezone = $('timezone-select');
    if (timezone) {
      timezone.initializeValues(templateData.timezoneList);
    }

    $('language-button').onclick = function(event) {
      // TODO: Open ChromeOS language settings page.
    };
  },
};

Fix runtime JavaScript errors in chrome://options.


import DS from 'ember-data';

export default DS.RESTSerializer.extend({
    modelNameFromPayloadKey(payloadKey) {
        return this._super(payloadKey);
    },

    normalizeResponse(store, primaryModelClass, payload, id, requestType) {
        if (payload.response && payload.response.genres) {
            const genres = payload.response.genres.map(function (genre, index) {
                genre.id = index;
                return genre;
            });

            return this._super(store, primaryModelClass, { 'echonest-genre': genres }, id, requestType);
        }

        return this._super.apply(this, arguments);
    }
});

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
    normalizeResponse(store, primaryModelClass, payload, id, requestType) {
        if (payload.response && payload.response.genres) {
            const genres = payload.response.genres.map(function (genre, index) {
                genre.id = index;
                return genre;
            });

            return this._super(store, primaryModelClass, { 'echonest-genre': genres }, id, requestType);
        }

        return this._super.apply(this, arguments);
    }
});

Remove useless method added during debugging


module.exports = {
  "env": {
    "mocha": true
  },

  "plugins": [
    "mocha"
  ],

  "rules": {

    // Override to allow asserting that a function throws: (function() { fn(); }).should.throw();
    "no-wrap-func": 0,

    // Override to allow use of synchronous functions in tests. Usefull when loading fixtures for example.
    "no-sync": 0,

    "padded-blocks": 0,

    "no-extra-parens": 0,

    // Reminds you to remove .only from your tests.
    "mocha/no-exclusive-tests": 2

  }
};

module.exports = {
  "env": {
    "mocha": true
  },

  "plugins": [
    "mocha"
  ],

  "rules": {

    // Override to allow asserting that a function throws: (function() { fn(); }).should.throw();
    "no-wrap-func": 0,

    // Override to allow use of synchronous functions in tests. Usefull when loading fixtures for example.
    "no-sync": 0,

    // Increase the maximum depth callbacks can be nested.
    "max-nested-callbacks": [1, 8],

    "padded-blocks": 0,

    "no-extra-parens": 0,

    // Reminds you to remove .only from your tests.
    "mocha/no-exclusive-tests": 2

  }
};

Increase the max callback depth in tests


/* eslint-env mocha */
const Vue = require('vue-test');
const _ = require('lodash');
const assert = require('assert');
const LearnIndex = require('../../src/views/index.vue');

function makeVm(options) {
  const Ctor = Vue.extend(LearnIndex);
  // TODO not mounting the component, since I can't figure out how
  // to setup tests to make all of the dependent components (namely core-base) work
  // seems to be good enough for current tests
  return new Ctor(options);
}

describe('learn index', () => {
  const isExamTab = ({ title }) => title === 'Exams';

  it('the exam tab is available if user is logged in', () => {
    const vm = makeVm({
      vuex: {
        getters: {
          isUserLoggedIn: () => true,
        },
      },
    });
    const examTabObj = _.find(vm.learnTabs, isExamTab);
    assert(!_.isUndefined(examTabObj));
  });

  it('the exam tab is not available if user is not logged in', () => {
    const vm = makeVm({
      vuex: {
        getters: {
          isUserLoggedIn: () => false,
        },
      },
    });
    const examTabObj = _.find(vm.learnTabs, isExamTab);
    assert(_.isUndefined(examTabObj));
  });
});

/* eslint-env mocha */
const Vue = require('vue-test');
const _ = require('lodash');
const assert = require('assert');
const LearnIndex = require('../../src/views/index.vue');

function makeVm(options) {
  const Ctor = Vue.extend(LearnIndex);
  // TODO not mounting the component, since I can't figure out how
  // to setup tests to make all of the dependent components (namely core-base) work
  // seems to be good enough for current tests
  return new Ctor(options);
}

describe.only('learn index', () => {
  const isExamTab = ({ title }) => title === 'Exams';

  it('the exam tab is available if user is logged in', () => {
    const vm = makeVm({
      vuex: {
        getters: {
          isUserLoggedIn: () => true,
        },
      },
    });
    const examTabObj = _.find(vm.learnTabs, isExamTab);
    assert(!_.isUndefined(examTabObj));
  });

  it('the exam tab is not available if user is not logged in', () => {
    const vm = makeVm({
      vuex: {
        getters: {
          isUserLoggedIn: () => false,
        },
      },
    });
    const examTabObj = _.find(vm.learnTabs, isExamTab);
    assert(_.isUndefined(examTabObj));
  });

  it('the exam tab is not available if user has no memberships/classes', () => {
    const vm = makeVm({
      vuex: {
        getters: {
          isUserLoggedIn: () => true,
          userHasMemberships: () => false,
        },
      },
    });
    const examTabObj = _.find(vm.learnTabs, isExamTab);
    assert(_.isUndefined(examTabObj));
  });
});

Add failing test for view


class CardStackPanResponder {
}

class PagerPanResponder {
}

const NavigationCard = (props) => null;

NavigationCard.CardStackPanResponder = CardStackPanResponder;
NavigationCard.CardStackStyleInterpolator = {
  forHorizontal: () => ({}),
  forVertical: () => ({}),
};
NavigationCard.PagerPanResponder = PagerPanResponder;
NavigationCard.PagerStyleInterpolator = {
  forHorizontal: () => ({}),
};

module.exports = NavigationCard;

import React from 'react';

class CardStackPanResponder {
}

class PagerPanResponder {
}

class NavigationCard extends React.Component {
  static CardStackPanResponder = CardStackPanResponder;
  static CardStackStyleInterpolator = {
    forHorizontal: () => ({}),
    forVertical: () => ({}),
  };
  static PagerPanResponder = PagerPanResponder;
  static PagerStyleInterpolator = {
    forHorizontal: () => ({}),
  };
}

module.exports = NavigationCard;

Use static fields in the component class


var _ = require('lodash'),
    express = require('express'),
    STATIC_CACHE_AGE = 365 * 24 * 60 * 60 * 1000; // 1 year;

module.exports = function confgureServer(config) {
  var app = express();

  function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest,
      headers: {
          'Cache-Control': 'private'
        }
    });
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use(function checkForIndex(request, response, next) {
    var isIndex = request.url.match(/index\.html$/);

    if (isIndex) {
      serveIndex(request, response);
    } else {
      next();
    }
  });

  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge', STATIC_CACHE_AGE),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('*', serveIndex);

  return app;
};

var _ = require('lodash'),
    express = require('express'),
    STATIC_CACHE_AGE = 365 * 24 * 60 * 60 * 1000; // 1 year;

module.exports = function confgureServer(config, app) {
  var app = app || express();

  function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest,
      headers: {
          'Cache-Control': 'private'
        }
    });
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use(function checkForIndex(request, response, next) {
    var isIndex = request.url.match(/index\.html$/);

    if (isIndex) {
      serveIndex(request, response);
    } else {
      next();
    }
  });

  // Serve static assets
  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge', STATIC_CACHE_AGE),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('*', serveIndex);

  return app;
};

Allow passing existing express app to appFactory


exports.PLUGIN_NAME = 'eslint-plugin-disable';
exports.PLUGIN_NAME_SHORT = 'disable';
exports.PLUGIN_PROCESSOR_NAME = 'plugin-disable';

exports.PLUGIN_NAME = 'eslint-plugin-disable';
exports.PLUGIN_NAME_SHORT = 'disable';
exports.PLUGIN_PROCESSOR_NAME = 'disable';

Change name used in processor


App.RawTransform = DS.Transform.extend({
  deserialize: function(serialized) {
    return serialized;
  },
  serialize: function(deserialized) {
    return deserialized;
  }
});

App.ArrayTransform = App.RawTransform.extend();
App.ChoiceTransform = DS.StringTransform.extend();

App.TimestampTransform = DS.DateTransform.extend({
  serialize: function(){
    return serialized.getSeconds() / 1000;
  },
  deserialize: function(serialized){
    return new Date(serialized * 1000);
  }
});

App.TumblrStringTransform = DS.StringTransform.extend({
  deserialize: function(serialized){
    'use strict';
    var deserialized = this._super(serialized),
        self = this,
        router = this.container.lookup('router:main');

    // return if null
    if (Ember.isNone(deserialized))
      return null;

    // reformat tumblr urls appropriately
    return deserialized.replace(/href="http:\/\/([^\.]*).tumblr.com\/?([^"]*)"/g, function(orig, blog_name, path, idx, full){
      var split_path = Em.isEmpty(path) ? {} : /post\/(\d+)\/?(.*)?/.exec(path);

      var url = router.generate(Em.isEmpty(path) ? 'single_blog' : 'single_post', {
        blog_name: blog_name,
        post_ident: split_path[1] || '',
        post_slug: split_path[2] || ''
      });
      return 'href="%@"'.fmt(url);
    });
  }
});

App.RawTransform = DS.Transform.extend({
  deserialize: function(serialized) {
    return serialized;
  },
  serialize: function(deserialized) {
    return deserialized;
  }
});

App.ArrayTransform = App.RawTransform.extend();
App.ChoiceTransform = DS.StringTransform.extend();

App.TimestampTransform = DS.DateTransform.extend({
  serialize: function(){
    return serialized.getSeconds() / 1000;
  },
  deserialize: function(serialized){
    return new Date(serialized * 1000);
  }
});

App.TumblrStringTransform = DS.StringTransform.extend({
  full_url_re: /href="http:\/\/([^\.]*).tumblr.com\/?([^"]*)"/g,
  path_re: /post\/(\d+)\/?(.*)?/,

  deserialize: function(serialized){
    'use strict';
    var deserialized = this._super(serialized),
        self = this,
        router = this.container.lookup('router:main');

    // return if null
    if (Ember.isNone(deserialized))
      return null;

    // reformat tumblr urls appropriately
    return deserialized.replace(this.full_url_re, function(orig, blog_name, path){
      var split_path = self.path_re.exec(path) || {};

      var url = router.generate(Em.isEmpty(path) ? 'single_blog' : 'single_post', {
        blog_name: blog_name,
        post_ident: split_path[1] || '',
        post_slug: split_path[2] || ''
      });
      return 'href="%@"'.fmt(url);
    });
  }
});

Store the regex's on the object itself, cleanup


import React, { Component } from 'react';
import PropTypes from 'prop-types';

import '../css/Search.css';

class Search extends Component {

  render() {
    const { handleSubmit, handleChange, searchTerm } = this.props;
    return (
      <form onSubmit={handleSubmit} className="search-form">
        <input className="search-input" type="search" value={searchTerm} onChange={handleChange} />
        <button className="submit-button" type="submit">Search</button>
      </form>
    );
  }

}

Search.propTypes = {
  searchTerm: PropTypes.string.isRequired,
  handleChange: PropTypes.func.isRequired,
  handleSubmit: PropTypes.func.isRequired
}

export default Search;

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import '../css/Search.css';

class Search extends Component {

  render() {
    const { handleSubmit, handleChange, searchTerm } = this.props;
    return (
      <form onSubmit={handleSubmit} className="search-form">
        <input
          className="search-input"
          type="search"
          value={searchTerm}
          onChange={handleChange}
          placeholder="Enter keyword e.g 'Sports'"
        />
        <button className="submit-button" type="submit">Search</button>
      </form>
    );
  }

}

Search.propTypes = {
  searchTerm: PropTypes.string.isRequired,
  handleChange: PropTypes.func.isRequired,
  handleSubmit: PropTypes.func.isRequired
}

export default Search;

Add placeholder to search input


#! /usr/bin/env node

const program = require('commander')
const PipelineRunner = require('../pipeline/runner')

program
  .version('scry-css 0.2.1')
  .usage('[options] <type> <dir> <file...>')
  .option(
    '-r --reporter [reporter]',
    'reporter (json/console), defaults to console', /^(console|json)$/i
  )
  .parse(process.argv)

if (!program.args.length || program.args.length < 3) {
  program.help()
} else {
  PipelineRunner.run(program.reporter || 'console', ...program.args).then((summary) => {
    console.log(summary) // eslint-disable-line no-console
  })
}

#! /usr/bin/env node

const program = require('commander')
const PipelineRunner = require('../pipeline/runner')
const _ = require('lodash')

program
  .version('scry-css 0.2.1')
  .usage('[options] <type> <dir> <file...>')
  .option(
    '-r --reporter [reporter]',
    'reporter (json/console), defaults to console', /^(console|json)$/i
  )
  .parse(process.argv)

if (!program.args.length || program.args.length < 3) {
  program.help()
} else {
  PipelineRunner.run(
    !_.isEmpty(program.reporter) ? program.reporter.trim().toLowerCase() : 'console',
    ...program.args.map(arg => (
      !_.isEmpty(arg) ? arg.trim().toLowerCase() : arg
    ))
  ).then((summary) => {
    console.log(summary) // eslint-disable-line no-console
  })
}

Trim and apply lower case to all cli args.


import config from 'app-config';

export const links = {
  home: {
    to: '/',
    id: 'navigation_home',
    description: 'Go to Home / Landing Page',
    defaultMessage: config.name,
  },

  aboutUs: {
    to: '/',
    id: 'navigation_about_us',
    description: 'About Us page link',
    defaultMessage: 'About Us',
  },

  logIn: {
    id: 'navigation_log_in',
    description: 'Log in',
    defaultMessage: 'Log in',
  },

  logOut: {
    id: 'navigation_log_out',
    description: 'Log out',
    defaultMessage: 'Log out',
  },

  account: {
    to: '/account',
    id: 'navigation_account',
    description: 'Account',
    defaultMessage: 'Account',
  },

  faq: {
    to: '/pages/faq',
    id: 'navigation_faq',
    description: 'FAQ',
    defaultMessage: 'FAQ',
  },

  policies: {
    to: '/pages/policies',
    id: 'navigation_policies',
    description: 'Policies',
    defaultMessage: 'Policies',
  },

  terms: {
    to: '/pages/terms',
    id: 'navigation_terms',
    description: 'Terms & Privacy',
    defaultMessage: 'Terms & Privacy',
  },

  help: {
    to: '/pages/help',
    id: 'navigation_help',
    description: 'Help',
    defaultMessage: 'Help',
  },

};

import config from 'app-config';

export const links = {
  home: {
    to: '/',
    id: 'navigation_home',
    description: 'Go to Home / Landing Page',
    defaultMessage: config.name,
  },

  aboutUs: {
    to: '/pages/about-us',
    id: 'navigation_about_us',
    description: 'About Us page link',
    defaultMessage: 'About Us',
  },

  logIn: {
    id: 'navigation_log_in',
    description: 'Log in',
    defaultMessage: 'Log in',
  },

  logOut: {
    id: 'navigation_log_out',
    description: 'Log out',
    defaultMessage: 'Log out',
  },

  account: {
    to: '/account',
    id: 'navigation_account',
    description: 'Account',
    defaultMessage: 'Account',
  },

  faq: {
    to: '/pages/faq',
    id: 'navigation_faq',
    description: 'FAQ',
    defaultMessage: 'FAQ',
  },

  policies: {
    to: '/pages/policies',
    id: 'navigation_policies',
    description: 'Policies',
    defaultMessage: 'Policies',
  },

  terms: {
    to: '/pages/terms',
    id: 'navigation_terms',
    description: 'Terms & Privacy',
    defaultMessage: 'Terms & Privacy',
  },

  help: {
    to: '/pages/help',
    id: 'navigation_help',
    description: 'Help',
    defaultMessage: 'Help',
  },

};

Fix footer link to About Us page


( function( $ ) {
	'use strict';

	var api = wp.customize;

	// Nav bar text color.
	api( 'amp_navbar_color', function( value ) {
		value.bind( function( to ) {
			$( 'nav.title-bar a' ).css( 'color', to );
			$( 'nav.title-bar div' ).css( 'color', to );
		} );
	} );

	// Nav bar background color.
	api( 'amp_navbar_background', function( value ) {
		value.bind( function( to ) {
			$( 'nav.title-bar' ).css( 'background', to );
		} );
	} );

	// Nav bar site icon.
	api( 'site_icon', function( value ) {
		value.bind( function( to ) {

			var	ampSiteIcon = $( '.site-icon' ),
				siteIcon    = $( '.site-icon > img' );

			if ( '' === to ) {
				ampSiteIcon.addClass( 'hidden' );
			} else {
				var request = wp.ajax.post( 'get-attachment', {
					id: to
				} ).done( function( response ) {
					ampSiteIcon.removeClass( 'hidden' );
					ampSiteIcon.removeClass( '-amp-notbuilt' );

					ampSiteIcon.attr( 'src', response.url );
					siteIcon.attr( 'src', response.url );
				} );
			}
		} );
	} );

} )( jQuery );

( function( $ ) {
	'use strict';

	var api = wp.customize;

	// Nav bar text color.
	api( 'amp_navbar_color', function( value ) {
		value.bind( function( to ) {
			$( 'nav.amp-wp-title-bar a' ).css( 'color', to );
			$( 'nav.amp-wp-title-bar div' ).css( 'color', to );
		} );
	} );

	// Nav bar background color.
	api( 'amp_navbar_background', function( value ) {
		value.bind( function( to ) {
			$( 'nav.amp-wp-title-bar' ).css( 'background', to );
		} );
	} );

	// Nav bar site icon.
	api( 'site_icon', function( value ) {
		value.bind( function( to ) {

			var	ampSiteIcon = $( '.site-icon' ),
				siteIcon    = $( '.site-icon > img' );

			if ( '' === to ) {
				ampSiteIcon.addClass( 'hidden' );
			} else {
				var request = wp.ajax.post( 'get-attachment', {
					id: to
				} ).done( function( response ) {
					ampSiteIcon.removeClass( 'hidden' );
					ampSiteIcon.removeClass( '-amp-notbuilt' );

					ampSiteIcon.attr( 'src', response.url );
					siteIcon.attr( 'src', response.url );
				} );
			}
		} );
	} );

} )( jQuery );

Update selectors following update from master.


import React from 'react';
import DocumentTitle from 'react-document-title';
import { IssueContainer } from './Issue';
import Alternatives from './Alternatives';
import { ConcludedIssueListContainer } from '../ConcludedIssueList';
import IssueStatus from '../IssueStatus';
import css from '../../css/Home.css';

const AdminHome = () => (
  <DocumentTitle title="Generalforsamling adminpanel">

          <IssueContainer />
          <Alternatives disabled />

        <IssueStatus />

      <ConcludedIssueListContainer />

  </DocumentTitle>
);

export default AdminHome;

import React from 'react';
import DocumentTitle from 'react-document-title';
import { IssueContainer } from './Issue';
import Alternatives from './Alternatives';
import { ConcludedIssueListContainer } from '../ConcludedIssueList';
import NotificationToggle from '../NotificationToggle';
import IssueStatus from '../IssueStatus';
import css from '../../css/Home.css';

const AdminHome = () => (
  <DocumentTitle title="Generalforsamling adminpanel">

          <IssueContainer />
          <Alternatives disabled />

        <IssueStatus />

      <NotificationToggle />
      <ConcludedIssueListContainer />

  </DocumentTitle>
);

export default AdminHome;

Add notification toggle to admin page


// Print out campaign level counts

// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>

var cursor = db.campaigns.find({}, {slug: 1, levels: 1});
var allFree = 0;
var allPremium = 0;
while (cursor.hasNext()) {
    var doc = cursor.next();
    if (doc.slug === 'auditions') continue;
    var free = 0;
    var premium = 0;
    for (var levelID in doc.levels) {
        if (doc.levels[levelID].requiresSubscription) {
            premium++;
        }
        else {
            free++;
        }
    }
    print(free + " " + premium + " " + (free + premium) + " " + doc.slug);

    allFree += free;
    allPremium += premium;
}
print(allFree + " " + allPremium + " " + (allFree + allPremium) + " overall");

// Print out campaign level counts

// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>

print("free premium all campaign");
var cursor = db.campaigns.find({}, {slug: 1, levels: 1});
var allFree = 0;
var allPremium = 0;
while (cursor.hasNext()) {
    var doc = cursor.next();
    if (doc.slug === 'auditions') continue;
    var free = 0;
    var premium = 0;
    for (var levelID in doc.levels) {
        if (doc.levels[levelID].requiresSubscription) {
            premium++;
        }
        else {
            free++;
        }
    }
    print(free + " " + premium + " " + (free + premium) + " " + doc.slug);

    allFree += free;
    allPremium += premium;
}
print(allFree + " " + allPremium + " " + (allFree + allPremium) + " overall");

Add headers to campaign level counts script


// @flow

import * as React from 'react';
import { Platform } from 'react-native';
import { BottomTabBar } from 'react-navigation-tabs';

import {
  type KeyboardState,
  withKeyboardState,
} from '../keyboard/keyboard-state';

type Props = {|
  ...React.ElementConfig<typeof BottomTabBar>,
  // withKeyboardState
  keyboardState: ?KeyboardState,
|};
function TabBar(props: Props) {
  const { keyboardState, ...rest } = props;
  if (
    Platform.OS === 'android' &&
    keyboardState &&
    keyboardState.keyboardShowing
  ) {
    return null;
  }
  return <BottomTabBar {...rest} />;
}

export default withKeyboardState(TabBar);

// @flow

import * as React from 'react';
import { Platform } from 'react-native';
import { BottomTabBar } from '@react-navigation/bottom-tabs';

import {
  type KeyboardState,
  withKeyboardState,
} from '../keyboard/keyboard-state';

type Props = {|
  ...React.ElementConfig<typeof BottomTabBar>,
  // withKeyboardState
  keyboardState: ?KeyboardState,
|};
function TabBar(props: Props) {
  const { keyboardState, ...rest } = props;
  if (
    Platform.OS === 'android' &&
    keyboardState &&
    keyboardState.keyboardShowing
  ) {
    return null;
  }
  return <BottomTabBar {...rest} />;
}

const WrappedTabBar = withKeyboardState(TabBar);
export default props => <WrappedTabBar {...props} />;

Update TabBar for React Nav 5


import { commands, window, Position, Selection } from 'vscode'
import { extensionCommands, extensionCommandsWithInput } from './commands'
import { MSG_NO_ACTIVE_TEXT_EDITOR } from './constants'

export const activate = (context) => {
  extensionCommands.map(cmd => {
    context.subscriptions.push(
      commands.registerCommand(cmd.key, () => editorInsert(cmd.callback({})))
    )
  })

  extensionCommandsWithInput.map(cmd => {
    context.subscriptions.push(
      commands.registerCommand(cmd.key, () =>
        window.showInputBox({prompt: cmd.prompt})
        .then(inputValue => {
          if (cmd.validation(inputValue)) {
            editorInsert(cmd.callback({inputValue}))
          } else {
            window.showErrorMessage(cmd.errorMsg)
          }
        })
      )
    )
  })
}

const editorInsert = (text) => {
  const editor = window.activeTextEditor

  if (!editor) {
    window.showErrorMessage(MSG_NO_ACTIVE_TEXT_EDITOR)
    return
  }

  const newSelections = []
  editor.edit((builder) => {
    editor.selections.map(selection => {
      builder.replace(selection, text)
      newSelections.push(getEndPosition(selection, text))
    })
  }).then(() => {
    editor.selections = newSelections
  })
}

const getEndPosition = (selection, text) => {
  var endPosition = new Position(selection.start.line, selection.start.character + text.length)
  return new Selection(endPosition, endPosition)
}

import { commands, window, Position, Selection } from 'vscode'
import { extensionCommands, extensionCommandsWithInput } from './commands'
import { MSG_NO_ACTIVE_TEXT_EDITOR } from './constants'

export const activate = (context) => {
  extensionCommands.map(cmd => {
    context.subscriptions.push(
      commands.registerCommand(cmd.key, () => editorInsert(cmd.callback))
    )
  })

  extensionCommandsWithInput.map(cmd => {
    context.subscriptions.push(
      commands.registerCommand(cmd.key, () =>
        window.showInputBox({prompt: cmd.prompt})
        .then(inputValue => {
          if (cmd.validation(inputValue)) {
            editorInsert(cmd.callback, {inputValue})
          } else {
            window.showErrorMessage(cmd.errorMsg)
          }
        })
      )
    )
  })
}

const editorInsert = (generator, params = {}) => {
  const editor = window.activeTextEditor

  if (!editor) {
    window.showErrorMessage(MSG_NO_ACTIVE_TEXT_EDITOR)
    return
  }

  const newSelections = []
  editor.edit((builder) => {
    editor.selections.map(selection => {
      const text = generator(params)

      builder.replace(selection, text)
      newSelections.push(getEndPosition(selection, text))
    })
  }).then(() => {
    editor.selections = newSelections
  })
}

const getEndPosition = (selection, text) => {
  var endPosition = new Position(selection.start.line, selection.start.character + text.length)
  return new Selection(endPosition, endPosition)
}

Fix bug when generating random values with multiple editors


$(document).ready(function(){
  if($('*[data-scrollinity-path]').length > 0) {
    var page_num = 1
    load_path = $('*[data-scrollinity-path]').data('scrollinity-path');
    loading_pic = $('*[data-scrollinity-loading-pic]');
    data_container = $('#' + $('*[data-scrollinity-data-container]').data('scrollinity-data-container'));
    bottom_px_limit = $('*[data-scrollinity-bottom-px-limit]').data('scrollinity-bottom-px-limit');

    $(window).on('scroll', function(){
      if(loading_hidden() && near_bottom()) {
        if(page_num > 0) {
          loading_pic.show();
          load_new_items();
        }
      }
    });

    function load_new_items(){
      sign = load_path.indexOf('?') >= 0 ? '&' : '?'
      $.get(load_path + sign + 'page=' + (++page_num), function(data, e) {
        if(data.length < 5) {
          page_num = 0;
          return false;
        }
        data_container.append(data);
      }).complete(function() {
        loading_pic.hide();
      });
    }

    function loading_hidden(){
      return !loading_pic.is(':visible');
    }

    function near_bottom(){
      return $(window).scrollTop() > $(document).height() - $(window).height() - bottom_px_limit;
    }
  }
});

$(document).ready(function(){

  function load_new_items(){
    sign = load_path.indexOf('?') >= 0 ? '&' : '?'
    $.get(load_path + sign + 'page=' + (++page_num), function(data, e) {
      if(data.length < 5) {
        page_num = 0;
        return false;
      }
      data_container.append(data);
    }).complete(function() {
      loading_pic.hide();
    });
  }

  function loading_hidden(){
    return !loading_pic.is(':visible');
  }

  function near_bottom(){
    return $(window).scrollTop() > $(document).height() - $(window).height() - bottom_px_limit;
  }

  if($('*[data-scrollinity-path]').length > 0) {
    var page_num = 1
    load_path = $('*[data-scrollinity-path]').data('scrollinity-path');
    loading_pic = $('*[data-scrollinity-loading-pic]');
    data_container = $('#' + $('*[data-scrollinity-data-container]').data('scrollinity-data-container'));
    bottom_px_limit = $('*[data-scrollinity-bottom-px-limit]').data('scrollinity-bottom-px-limit');

    $(window).on('scroll', function(){
      if(loading_hidden() && near_bottom()) {
        if(page_num > 0) {
          loading_pic.show();
          load_new_items();
        }
      }
    });
  }
});

Fix JS for strict mode syntax error:


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

var meow = require('meow');
var w3counter = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ w3counter <type>',
		'',
		'Example',
		'  $ w3counter browser',
		'  $ w3counter country',
		'  $ w3counter os',
		'  $ w3counter res'
	].join('\n')
});

if (!cli.input.length) {
	console.error([
		'Provide a type',
		'',
		'Example',
		'  $ w3counter browser',
		'  $ w3counter country',
		'  $ w3counter os',
		'  $ w3counter res'
	].join('\n'));

	process.exit(1);
}

w3counter(cli.input, function (err, types) {
	if (err) {
		console.error(err.message);
		process.exit(1);
	}

	types.forEach(function (type, i) {
		i = i + 1;
		console.log(i + '. ' + type.item + ' (' + type.percent + ')');
	});
});

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

var meow = require('meow');
var w3counter = require('./');

var cli = meow({
	help: [
		'Usage',
		'  $ w3counter <type>',
		'',
		'Example',
		'  $ w3counter browser',
		'  $ w3counter country',
		'  $ w3counter os',
		'  $ w3counter res'
	].join('\n')
});

if (!cli.input.length) {
	console.error('Provide a type');
	process.exit(1);
}

w3counter(cli.input, function (err, types) {
	if (err) {
		console.error(err.message);
		process.exit(1);
	}

	types.forEach(function (type, i) {
		i = i + 1;
		console.log(i + '. ' + type.item + ' (' + type.percent + ')');
	});
});

Remove example usage from error in CLI


import NavigateConstants from '../constants/NavigateConstants'
import { ClientError } from '../lib/errors'

export default async function navigateAction (context, payload) {
  const { location, branch, components, params } = payload
  context.dispatch(NavigateConstants.START, { ...payload })

  if (!components || !branch) {
    const err = ClientError({
      title: `URL ${location.pathname} does not match any routes`,
      status: 404
    })
    context.dispatch(NavigateConstants.FAILURE, err)
    throw err
  }

  context.dispatch(NavigateConstants.SUCCESS, { ...payload })
}

import NavigateConstants from '../constants/NavigateConstants'
import { ClientError } from '../lib/errors'

export default async function navigateAction (context, payload) {
  // const { location, branch, components, params } = payload
  const { location, branch, components } = payload
  context.dispatch(NavigateConstants.START, { ...payload })

  if (!components || !branch) {
    const err = ClientError({
      title: `URL ${location.pathname} does not match any routes`,
      status: 404
    })
    context.dispatch(NavigateConstants.FAILURE, err)
    throw err
  }

  context.dispatch(NavigateConstants.SUCCESS, { ...payload })
}

Remove unused param for now


import {load, read} from 'vega-loader';

export function loadOptions(_) {
  return arguments.length ? (this._loadopt = _, this) : this._loadopt;
}

export function request(target, url, format) {
  var df = this;

  df._requests = 1 + (df._requests || 0);

  load(url, df._loadopt, function(err, data) {
    if (!err) {
      try {
        df.ingest(target, data, format);
      } catch (e) { err = e; }
    }

    if (err) {
      df.warn('Loading failed: ' + url, err);
    }

    if (--df._requests === 0) {
      df.runAfter(function() { df.run(); });
    }
  });
}

export function ingest(target, data, format) {
  this.pulse(target, this.changeset()
      .insert(read(data, format)));
}

import {load, read} from 'vega-loader';

export function loadOptions(_) {
  return arguments.length ? (this._loadopt = _, this) : this._loadopt;
}

export function request(target, url, format) {
  var df = this;

  df._requests = 1 + (df._requests || 0);

  function decrement() {
    if (--df._requests === 0) df.runAfter(function() { df.run(); });
  }

  load(url, df._loadopt)
    .then(function(data) { return df.ingest(target, data, format); })
    .then(decrement)
    .catch(function(error) {
      df.warn('Loading failed: ' + url, error);
      decrement()
    });
}

export function ingest(target, data, format) {
  this.pulse(target, this.changeset()
      .insert(read(data, format)));
}

Move from callbacks to promises.


function createNavLink(id, args = {}) {
  const {text, to, href, authenticated, rest} = args;
  return {
    authenticated,
    id,
    text: text || id.charAt(0).toUpperCase() + id.slice(1),
    to: to || '/' + id,
    href,
    ...rest,
  };
}

export default {
  db: {
    support: {
      chat: 'https://www.hipchat.com/gv1XLjgaV',
      bugs: 'https://github.com/cape-io/cape-editor/issues',
    },
    title: 'ACF Client',
    description: 'Membership Fun',
    image: 'https://pbs.twimg.com/profile_images/568082657292976128/SNZjMW8N.png',
    tagline: 'Create Anywhere Publish Everywhere',
    navLinks: [
      createNavLink('about', {text: 'About Us'}),
      createNavLink('members'),
      createNavLink('acf40', {
        text: 'ACF 40',
        href: 'https://composersforum.org/acf40',
      }),
    ],
  },
};

function createNavLink(id, args = {}) {
  const {text, to, href, authenticated, rest} = args;
  return {
    authenticated,
    id,
    text: text || id.charAt(0).toUpperCase() + id.slice(1),
    to: to || '/' + id,
    href,
    ...rest,
  };
}

export default {
  db: {
    support: {
      chat: 'https://www.hipchat.com/gv1XLjgaV',
      bugs: 'https://github.com/cape-io/cape-editor/issues',
    },
    title: 'ACF Client',
    description: 'Membership Fun',
    image: 'https://pbs.twimg.com/profile_images/568082657292976128/SNZjMW8N.png',
    tagline: 'Create Anywhere Publish Everywhere',
    navLinks: [
      createNavLink('about', {text: 'About Us'}),
      createNavLink('members'),
      createNavLink('acf40', {
        text: 'ACF 40',
        href: 'https://composersforum.org/acf40',
      }),
      createNavLink('discover', {
        text: 'Discover',
        href: 'https://composersforum.org/discover',
      }),
      createNavLink('get-involved', {
        text: 'Get Involved',
        href: 'https://composersforum.org/get-involved',
      }),
      createNavLink('opportunities-upcoming', {
        text: 'Opportunities',
        href: 'https://composersforum.org/opportunities-upcoming',
      }),
      createNavLink('programs', {
        text: 'Programs',
        href: 'https://composersforum.org/programs',
      }),
      createNavLink('about', {
        text: 'About Us',
        href: 'https://composersforum.org/about',
      }),
    ],
  },
};

Add nav links from existing website


(function() {

  window.HandArrow = function(parent) {
    this.followRadius = 0.5;
    this.parent = parent;
    this.mesh = undefined;

    new THREE.ObjectLoader().load("meshes/MozillaVR-3D-arrow-01.json", function(mesh) {
      this.mesh = mesh;
      this.mesh.position.set(0,0,-0.2);
      this.mesh.material = new THREE.MeshPhongMaterial( { color: 0x00FF00, shading: THREE.SmoothShading } );
      this.mesh.scale.set(0.03,0.03,0.03);
      this.parent.add(this.mesh);
      console.log("ADDED ARROW MESH");
    }.bind(this));
  };

  window.HandArrow.prototype = {
    update: function(toFollow, toPointTo) {
      if ( this.mesh !== undefined ) {
        // TODO: do some stuff.
        var diff = new THREE.Vector3().copy(toPointTo).sub(toFollow);
        var direction = new THREE.Vector3().copy(diff).normalize();
        this.mesh.position = new  THREE.Vector3().copy(toFollow).add(new THREE.Vector3().copy(direction).multiplyScalar(diff.length() / 2.0));
      }

    }
  };
}).call(this);

(function() {

  window.HandArrow = function(parent) {
    this.followRadius = 0.08;
    this.parent = parent;
    this.mesh = undefined;

    new THREE.ObjectLoader().load("meshes/MozillaVR-3D-arrow-01.json", function(mesh) {
      this.mesh = mesh;
      this.mesh.position.set(0,0,-0.2);
      this.mesh.material = new THREE.MeshPhongMaterial( { color: 0x00FF00, shading: THREE.SmoothShading } );
      this.mesh.scale.set(0.03,0.03,0.03);
      //this.mesh.visible = false;
      this.parent.add(this.mesh);
      console.log("ADDED ARROW MESH");
    }.bind(this));
  };

  window.HandArrow.prototype = {
    update: function(toFollow, toPointTo) {
      var follow = new THREE.Vector3(toFollow[0], toFollow[1], toFollow[2]);
      var pointTo = new THREE.Vector3(toPointTo[0], toPointTo[1], toPointTo[2]);
      if ( this.mesh !== undefined ) {
        var diff = new THREE.Vector3().copy(pointTo).sub(follow);
        var direction = new THREE.Vector3().copy(diff).normalize();
        this.mesh.position.copy(new  THREE.Vector3().copy(follow).add(new THREE.Vector3().copy(direction).multiplyScalar(this.followRadius)));
        this.mesh.lookAt(pointTo);
        this.mesh.rotateOnAxis (new THREE.Vector3(1,0,0), -Math.PI/2.0);
        this.mesh.rotateOnAxis (new THREE.Vector3(0,1,0), Math.PI/2.0);
      }

    }
  };
}).call(this);

Implement point and follow update


// extensions initialisation

import _ from "lodash";
import {registerComponents} from '../common/module-utils';
import {dynamicSections, dynamicSectionsByKind} from "../dynamic-section/dynamic-section-definitions";

import dbChangeInitiativeBrowser from './components/change-initiative/change-initiative-browser/db-change-initiative-browser';
import dbChangeInitiativeSection from './components/change-initiative/change-initiative-section/db-change-initiative-section';

export const init = (module) => {

    registerComponents(module, [
        dbChangeInitiativeBrowser,
        dbChangeInitiativeSection
    ]);

    overrideApplicationDynamicSections();
};

function overrideApplicationDynamicSections() {
    dynamicSections.dbChangeInitiativesSection = {
        componentId: 'db-change-initiative-section',
        name: 'Change Initiatives',
        icon: 'paper-plane-o',
        id: 10000
    };

    dynamicSectionsByKind["APPLICATION"] = _.map(
        dynamicSectionsByKind["APPLICATION"],
        ds => ds.id === dynamicSections.changeInitiativeSection.id
                ? dynamicSections.dbChangeInitiativesSection
                : ds
    );
}

// extensions initialisation

import _ from "lodash";
import {registerComponents} from '../common/module-utils';
import {dynamicSections, dynamicSectionsByKind} from "../dynamic-section/dynamic-section-definitions";

import dbChangeInitiativeBrowser from './components/change-initiative/change-initiative-browser/db-change-initiative-browser';
import dbChangeInitiativeSection from './components/change-initiative/change-initiative-section/db-change-initiative-section';

export const init = (module) => {

    registerComponents(module, [
        dbChangeInitiativeBrowser,
        dbChangeInitiativeSection
    ]);

    overrideChangeInitiativeSection();
};

function overrideChangeInitiativeSection() {
    dynamicSections.dbChangeInitiativesSection = {
        componentId: 'db-change-initiative-section',
        name: 'Change Initiatives',
        icon: 'paper-plane-o',
        id: 10000
    };

    _.forIn(dynamicSectionsByKind, (v, k) => dynamicSectionsByKind[k] = _.map(
            v,
            ds => ds.id === dynamicSections.changeInitiativeSection.id
                ? dynamicSections.dbChangeInitiativesSection
                : ds
        ));
}

Fix change initiative section - Override GH section with DB specific one


import fs from 'fs'
import http from 'http';
import path from 'path';

import React from 'react';
import Baobab from 'baobab';
import {root} from 'baobab-react/higher-order';

import defaultData from './data';
import Html from './components/Html';
import Layout from './components/Layout';

function renderHtml(res, data, webpackAssets) {
  const BaobabEnrichedLayout = root(Layout, data);
  const markup = React.renderToString(<BaobabEnrichedLayout />);
  const html = React.renderToStaticMarkup(
      <Html webpackAssets={webpackAssets} markup={markup}
            state={data.get()} />
  );
  res.send(`<!doctype html>\n${html}`);
}

export default async function(req, res, next, webpackAssets) {
  defaultData.addedKey1ThatWorks = 'addedValue1';

  const data = new Baobab(defaultData, {
    syncwrite: true
  });

  //data.select('addedKey2ThatFails').set('addedValue2');

  renderHtml(res, data, webpackAssets);
}

import fs from 'fs'
import http from 'http';
import path from 'path';

import React from 'react';
import Baobab from 'baobab';
import {root} from 'baobab-react/higher-order';

import defaultData from './data';
import Html from './components/Html';
import Layout from './components/Layout';

function renderHtml(res, data, webpackAssets) {
  const BaobabEnrichedLayout = root(Layout, data);
  const markup = React.renderToString(<BaobabEnrichedLayout />);
  const html = React.renderToStaticMarkup(
      <Html webpackAssets={webpackAssets} markup={markup}
            state={data.get()} />
  );
  res.send(`<!doctype html>\n${html}`);
}

export default function(req, res, next, webpackAssets) {
  defaultData.addedKey1ThatWorks = 'addedValue1';

  const data = new Baobab(defaultData, {
    syncwrite: true
  });

  //data.select('addedKey2ThatFails').set('addedValue2');

  renderHtml(res, data, webpackAssets);
}

Remove async that's not required for now.


const $ = require('jquery');
const Board = require('./board');
const Game = require('./game');

function start() {
    var board = new Board();
    var game = new Game(board);
    board.game = game;
    game.welcome();

    var canvasElement = document.getElementById('game');
    canvasElement.addEventListener('click', update, false);

    function update(event) {
        var pos = getMousePos(canvasElement, event);
        var posx = pos.x;
        var posy = pos.y;
        board.respondToClick(posx, posy);
        board.renderBoard(canvasElement);
    }

    function getMousePos(canvas, evt) {
        var rect = canvas.getBoundingClientRect();
        return {
            x: evt.clientX - rect.left,
            y: evt.clientY - rect.top
        };
    }
}

$(document).ready( function () {
    start();
});

const $ = require('jquery');
const Board = require('./board');
const Game = require('./game');

function start() {
    var board = new Board();
    var game = new Game(board);
    board.game = game;
    game.welcome();

    var canvasElement = document.getElementById('game');
    canvasElement.addEventListener('click', update, false);

    function update(event) {
        var pos = getMousePos(canvasElement, event);
        var posx = pos.x;
        var posy = pos.y;
        board.renderBoard(canvasElement);
        board.respondToClick(posx, posy);
    }

    function getMousePos(canvas, evt) {
        var rect = canvas.getBoundingClientRect();
        return {
            x: evt.clientX - rect.left,
            y: evt.clientY - rect.top
        };
    }
}

$(document).ready( function () {
    start();
});

Order in a more intuitive way


'use strict';

var config = require('../config/configuration.js');

function htmlReplace(str) {
  return str.replace(/<\/?[^>]+\/?>/g, ' ').replace(/ +/g, ' ').replace(/ ?\n ?/g, '\n').trim();
}

module.exports = function(path, document, changes, finalCb) {
  if(!document.data.html) {
    changes.data.html = document.metadata.text;
  }
  else if(!document.metadata.text) {
    changes.metadata.text = htmlReplace(document.data.html);
  }

  changes.metadata.text = (changes.metadata.text) ? changes.metadata.text : document.metadata.text;

  if(document.data.html) {
    config.separators.html.forEach(function(separator) {
      var tmpHTML = document.data.html.split(separator)[0];

      if(tmpHTML !== document.data.html) {
        changes.metadata.text = htmlReplace(tmpHTML);
      }
    });
  }

  config.separators.text.forEach(function(separator) {
    var tmpText = changes.metadata.text.split(separator)[0];

    if(tmpText !== changes.metadata.text) {
      changes.metadata.text = tmpText;
    }
  });

  finalCb(null, changes);
};

'use strict';

var config = require('../config/configuration.js');

function htmlReplace(str) {
  return str.replace(/<\/?[^>]+\/?>/g, ' ').replace(/ +/g, ' ').replace(/ ?\n ?/g, '\n').trim();
}

module.exports = function(path, document, changes, finalCb) {
  if(!document.data.html) {
    changes.data.html = document.metadata.text;
  }
  else if(!document.metadata.text) {
    changes.metadata.text = htmlReplace(document.data.html);
  }

  changes.metadata.text = (changes.metadata.text) ? changes.metadata.text : document.metadata.text;

  if(document.data.html) {
    config.separators.html.forEach(function(separator) {
      var tmpHTML = document.data.html.split(separator)[0];

      if(tmpHTML !== document.data.html) {
        changes.metadata.text = htmlReplace(tmpHTML);
      }
    });
  }

  if(changes.metadata.text) {
    config.separators.text.forEach(function(separator) {
      var tmpText = changes.metadata.text.split(separator)[0];

      if(tmpText !== changes.metadata.text) {
        changes.metadata.text = tmpText;
      }
    });
  }

  finalCb(null, changes);
};

Fix a bug with undefined split


const CLIEngine = require('eslint').CLIEngine;

function makeFixer(options) {
  if (!options || !Array.isArray(options.rules) || !options.rules.length) {
    return false;
  }
  const rulesToFix = options.rules;

  return function (eslintMessage) {
    if (rulesToFix.includes(eslintMessage.ruleId)) {
      return true;
    }
    return false;
  };
}

function getEslintCli(options) {
  return new CLIEngine(options);
}

function calculateFixes(files, eslintCli) {
  return eslintCli.executeOnFiles(files);
}

function applyFixes(report) {
  CLIEngine.outputFixes(report);
}

module.exports = {
  makeFixer,
  getEslintCli,
  calculateFixes,
  applyFixes,
};

const CLIEngine = require('eslint').CLIEngine;

/**
 * Creates a fixing function or boolean that can be provided as eslint's `fix`
 * option.
 *
 * @param  {Object|boolean} options Either an options object, or a boolean
 * @return {Function|boolean}       `fix` option for eslint
 */
function makeFixer(options) {
  if (!options) {
    return false;
  }

  if (typeof options === 'boolean') {
    return options;
  }

  const rulesToFix = options.rules;

  if (rulesToFix) {
    return function (eslintMessage) {
      if (rulesToFix.includes(eslintMessage.ruleId)) {
        return true;
      }
      return false;
    };
  }

  // Fallback
  return false;
}

function getEslintCli(options) {
  return new CLIEngine(options);
}

function calculateFixes(files, eslintCli) {
  return eslintCli.executeOnFiles(files);
}

function applyFixes(report) {
  CLIEngine.outputFixes(report);
}

module.exports = {
  makeFixer,
  getEslintCli,
  calculateFixes,
  applyFixes,
};

Allow `makeFixer` to take a boolean


var BfsSetupSearch = BfsSetupSearch || {};

BfsSetupSearch.init = function() {
  $(function() {
    $('div.quickfindContainer input#setupSearch').val(' ');
  });
}

chrome.storage.sync.get({
  'setupsearch': true
}, function(item) {
  if (item.setupsearch === true) {
    if ($('div.quickfindContainer input#setupSearch').size() > 0) {
      BfsSetupSearch.init();
    }
  }
});

var BfsSetupSearch = BfsSetupSearch || {};

BfsSetupSearch.init = function() {
  $(function() {
    if ($('div.quickfindContainer input#setupSearch').size() > 0) {
      $('div.quickfindContainer input#setupSearch').val(' ');
    } else {
      BfsSetupSearch.lightningInit();
    }
  });
}

BfsSetupSearch.lightningInit = function() {
  var targetNode = document.documentElement;
  var config = { childList: true, subtree: true };

  var observer = new MutationObserver(function(mutationsList, observer) {
    for (var mutation of mutationsList) {
      mutation.addedNodes.forEach(function(node) {
        if (node.matches && node.matches('.tree-filter')) {
          let input = document.querySelector('input.filter-box');
          BfsSetupSearch.handleLightningInput(input);

          observer.disconnect();
        }
      });
    }
  });

  observer.observe(targetNode, config);
}

BfsSetupSearch.handleLightningInput = function(input) {
  input.addEventListener('blur', event => {
    if (event.relatedTarget.matches('body.desktop')) {
      setTimeout(() => {
        input.focus();
      });
    }
  });
}

chrome.storage.sync.get({
  'setupsearch': true
}, function(item) {
  if (item.setupsearch === true) {
    BfsSetupSearch.init();
  }
});

Update for lightning experience search


var spark = require('textspark');

var generateMetricParams =  function(namespace, metric, unit, instance_id, dimension, period) {
     var endDate = new Date();
     var startDate = new Date(endDate);
     var durationMinutes = 60;

     return {
         EndTime: endDate,
         MetricName: metric,
         Namespace: 'AWS/' + namespace,
         Period: period,
         StartTime: new Date(startDate.setMinutes(endDate.getMinutes() - durationMinutes)),
         Statistics: [ 'Average' ],
         Dimensions: [{Name: dimension, Value: instance_id}],
         Unit: unit
     };
};

module.exports = {
    displayMetric: function(aws, bot, channel, namespace, metric, unit, instance_id, dimension, period){
        var params = generateMetricParams(namespace, metric, unit, instance_id, dimension, period);

        aws.getMetricStatistics(params, function(err, data) {
            if (err){
                console.log(err);
            }
            else{
                var datapoints = data['Datapoints']

                datapoints.sort(function(x, y){
                        return x.Timestamp - y.Timestamp;
                })
                var processed_data = [];
                datapoints.forEach(function (val, index, array) {
                    processed_data.push(Number(val['Average']).toFixed(2));
                });
                bot.postMessage(channel, metric + ": " + spark(processed_data), {as_user: true});
            }
        });
    }
};

var spark = require('textspark');

var generateMetricParams =  function(namespace, metric, unit, instance_id, dimension, period) {
     var endDate = new Date();
     var startDate = new Date(endDate);
     var durationMinutes = 120;

     return {
         EndTime: endDate,
         MetricName: metric,
         Namespace: 'AWS/' + namespace,
         Period: period,
         StartTime: new Date(startDate.setMinutes(endDate.getMinutes() - durationMinutes)),
         Statistics: [ 'Average' ],
         Dimensions: [{Name: dimension, Value: instance_id}],
         Unit: unit
     };
};

module.exports = {
    displayMetric: function(aws, bot, channel, namespace, metric, unit, instance_id, dimension, period){
        var params = generateMetricParams(namespace, metric, unit, instance_id, dimension, period);

        aws.getMetricStatistics(params, function(err, data) {
            if (err){
                console.log(err);
            }
            else{
                var datapoints = data['Datapoints']

                if(datapoints && datapoints.length > 0){

                    datapoints.sort(function(x, y){
                            return x.Timestamp - y.Timestamp;
                    })
                    var processed_data = [];
                    datapoints.forEach(function (val, index, array) {
                        processed_data.push(Number(val['Average']).toFixed(2));
                    });
                    bot.postMessage(channel, metric + ": " + spark(processed_data), {as_user: true});
                }
                else{
                    bot.postMessage(channel, "Id '" + instance_id + "' not found", {as_user: true});
                }
            }
        });
    }
};

Increase time range and id filtering


module.exports = {
    entry: {
        "demo-perseus": "./src/demo-perseus.js",
        "frame-perseus": "./src/perseus-frame.js",
    },
    output: {
        path: "./build",
        publicPath: "/build/",
        filename: "[name].js",
    },
    devServer: {
        historyApiFallback: "/build/",
    },
    plugins: require('./webpack.config.js').plugins,
    module: require('./webpack.config.js').module,
};

module.exports = {
    entry: {
        "demo-perseus": "./src/demo-perseus.js",
        "frame-perseus": "./src/perseus-frame.js",
    },
    output: {
        path: "./build",
        publicPath: "build/",
        filename: "[name].js",
    },
    devServer: {
        historyApiFallback: "/build/",
    },
    plugins: require('./webpack.config.js').plugins,
    module: require('./webpack.config.js').module,
};

Fix perseus demo loading of extra widgets


import Ember from 'ember';
import config from 'ember-get-config';

export default Ember.Service.extend({
    store: Ember.inject.service(),
    id: config.PREPRINTS.provider,

    provider: Ember.computed('id', function() {
        const id = this.get('id');

        if (!id)
            return;

        return this
            .get('store')
            .findRecord('preprint-provider', id);
    }),

    isProvider: Ember.computed('id', function() {
        const id = this.get('id');
        return id && id !== 'osf';
    }),

    stylesheet: Ember.computed('id', function() {
        const id = this.get('id');

        if (!id)
            return;

        const suffix = config.ASSET_SUFFIX ? `-${config.ASSET_SUFFIX}` : '';
        return `/preprints/assets/css/${id}${suffix}.css`;
    }),

    signupUrl: Ember.computed('id', function() {
        const query = Ember.$.param({
            campaign: `${this.get('id')}-preprints`
        });

        return `${config.OSF.url}register?${query}`;
    }),
});

import Ember from 'ember';
import config from 'ember-get-config';

export default Ember.Service.extend({
    store: Ember.inject.service(),
    id: config.PREPRINTS.provider,

    provider: Ember.computed('id', function() {
        const id = this.get('id');

        if (!id)
            return;

        return this
            .get('store')
            .findRecord('preprint-provider', id);
    }),

    isProvider: Ember.computed('id', function() {
        const id = this.get('id');
        return id && id !== 'osf';
    }),

    stylesheet: Ember.computed('id', function() {
        const id = this.get('id');

        if (!id)
            return;

        const suffix = config.ASSET_SUFFIX ? `-${config.ASSET_SUFFIX}` : '';
        return `/preprints/assets/css/${id}${suffix}.css`;
    }),

    signupUrl: Ember.computed('id', function() {
        const query = Ember.$.param({
            campaign: `${this.get('id')}-preprints`,
            next: window.location.href
        });

        return `${config.OSF.url}register?${query}`;
    }),
});

Add next query param to signup url


import { createGlobalStyle } from 'styled-components';

export const GlobalStyle = createGlobalStyle`
  * {
    box-sizing: border-box;
  }

  html, body {
    margin: 0;
    padding: 0;
    color: #1c1c1c;
    background-color: #ffffff;
    font-size: 16px;
    font-family: 'proxima-nova', helvetica, arial, clean, sans-serif;
  }

  h1, h2, h3, h4, h5, h6 {
    font-size: 100%;
  }

  h1 {
      margin-bottom: 1em
  }

  h2 {
      font-size: 1.2rem
  }

  h4 {
      margin-top: 2rem
  }

  a {
    color: #4078c0;
    text-decoration: underline;
  }

`;

import { createGlobalStyle } from 'styled-components';

export const GlobalStyle = createGlobalStyle`
  * {
    box-sizing: border-box;
  }

  .wf-loading {
    visibility: hidden;
  }

  html, body {
    margin: 0;
    padding: 0;
    color: #1c1c1c;
    background-color: #ffffff;
    font-size: 16px;
    font-family: 'proxima-nova', helvetica, arial, clean, sans-serif;
  }

  h1, h2, h3, h4, h5, h6 {
    font-size: 100%;
  }

  h1 {
      margin-bottom: 1em
  }

  h2 {
      font-size: 1.2rem
  }

  h4 {
      margin-top: 2rem
  }

  a {
    color: #4078c0;
    text-decoration: underline;
  }

`;

Hide content while webfont loading


import React from 'react';
import { connect } from 'react-redux';

import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackMixpanelEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

Add Mixpanel event on Control clicks


/*
 * textnodes
 *
 *
 * Copyright (c) 2014 Mark Florian
 * Licensed under the MIT license.
 */

(function($) {

  // Collection method.
  $.fn.textnodes = function() {
    return this.each(function(i) {
      // Do something awesome to each selected element.
      $(this).html('awesome' + i);
    });
  };

  // Static method.
  $.textnodes = function(options) {
    // Override default options with passed-in options.
    options = $.extend({}, $.textnodes.options, options);
    // Return something awesome.
    return 'awesome' + options.punctuation;
  };

  // Static method default options.
  $.textnodes.options = {
    punctuation: '.'
  };

  // Custom selector.
  $.expr[':'].textnodes = function(elem) {
    // Is this element awesome?
    return $(elem).text().indexOf('awesome') !== -1;
  };

}(jQuery));

/*
 * textnodes
 *
 *
 * Copyright (c) 2014 Mark Florian
 * Licensed under the MIT license.
 */

(function($) {

  var rNonWhiteSpace = /\S/;

  function getChildTextNodes($el, list, includeWhiteSpace) {
    $el.contents().each(function () {
      console.log("called");
      if (this.nodeType === 3) {
        if (
            includeWhiteSpace &&
            rNonWhiteSpace.test(this.textContent || this.innerText)
           ) {
          list.push(this);
        }
      } else if (this.nodeType === 1) {
        getChildTextNodes($(this), list);
      }
    });
  }

  $.fn.textNodes = function (includeWhiteSpace) {
    var textNodes = [];
    getChildTextNodes(this, textNodes, includeWhiteSpace);
    return $(textNodes);
  };

}(jQuery));

Add first draft of plugin.


import '../../api/server';
import '../facebook';
import seedData from '../seed-data';

Meteor.startup(() => {
  process.env.MAIL_URL = Meteor.settings.MAIL_URL;

  seedData();
});

import '../../api/server';
import '../facebook';
import '../methods';
import seedData from '../seed-data';

Meteor.startup(() => {
  process.env.MAIL_URL = Meteor.settings.MAIL_URL;

  seedData();
});

Set Meteor methods on startup


module.exports = function(config) {
  config.set({
    basePath: '.',
    frameworks: ['jquery-3.2.1', 'jasmine-jquery', 'jasmine'],
    browsers: ['ChromeHeadless'],
    files: [
      'spec/helpers/tampermonkeyStubs.js',
      'docs/fateOfAllFools.js',
      'spec/helpers/!(tampermonkeyStubs).js',
      {pattern: 'spec/javascripts/fixtures/*.html', included: false},
      'spec/javascripts/*.spec.js'
    ]
  })
};

module.exports = function(config) {
  config.set({
    basePath: '.',
    frameworks: ['jquery-3.2.1', 'jasmine-jquery', 'jasmine'],
    browsers: ['ChromeHeadless'],
    files: [
      'spec/helpers/tampermonkeyStubs.js',
      'build/fateOfAllFools.js',
      'docs/fateOfAllFools.js',
      'spec/helpers/!(tampermonkeyStubs).js',
      {pattern: 'spec/javascripts/fixtures/*.html', included: false},
      'spec/javascripts/*.spec.js'
    ]
  })
};

Add build path for Travis use


export { BaseStepperButton } from './BaseStepperButton';
export { BaseStepperInput } from './BaseStepperInput';
export { StepperInputWithLabel } from './StepperInputWithLabel';
export { Incrementor } from './Incrementor';
export { TimeEditor } from './TimeEditor';

Add exports for new components


// Karma configuration

// base path, that will be used to resolve files and exclude
basePath = '';

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER,
  'app/bower_components/angular/angular.js',
  'app/bower_components/angular-mocks/angular-mocks.js',
  'app/scripts/*.js',
  'app/scripts/**/*.js',
  'test/mock/**/*.js',
  'test/spec/**/*.js'
];

// list of files to exclude
exclude = [];

// test results reporter to use
// possible values: dots || progress || growl
reporters = ['progress'];

// web server port
port = 8080;

// cli runner port
runnerPort = 9100;

// enable / disable colors in the output (reporters and logs)
colors = true;

// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_INFO;

// enable / disable watching file and executing tests whenever any file changes
autoWatch = false;

// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = ['Chrome'];

// If browser does not capture in given timeout [ms], kill it
captureTimeout = 5000;

// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;

// Karma configuration

// base path, that will be used to resolve files and exclude
basePath = '';

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER,
  'app/bower_components/angular/angular.js',
  'app/bower_components/angular-mocks/angular-mocks.js',
  'app/scripts/*.js',
  'app/scripts/**/*.js',
  'test/mock/**/*.js',
  'test/spec/**/*.js'
];

// list of files to exclude
exclude = [];

// test results reporter to use
// possible values: dots || progress || growl
reporters = ['progress'];

// web server port
port = 8080;

// cli runner port
runnerPort = 9100;

// enable / disable colors in the output (reporters and logs)
colors = true;

// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_INFO;

// enable / disable watching file and executing tests whenever any file changes
autoWatch = false;

// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers = ['Firefox'];

// If browser does not capture in given timeout [ms], kill it
captureTimeout = 5000;

// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;

Change browser to run tests in to Firefox


/* global IntersectionObserver */
var scroller = document.querySelector('#scroller');
var sentinel = document.querySelector('#sentinel');
var counter = 1;

function loadItems(n) {
  for (var i = 0; i < n; i++) {
    var newItem = document.createElement('div');
    newItem.classList.add('item');
    newItem.textContent = 'Item ' + counter++;
    scroller.appendChild(newItem);
  }
}

var intersectionObserver = new IntersectionObserver(entries => {
  // If intersectionRatio is 0, the sentinel is out of view
  // and we do not need to do anything.
  if (entries[0].intersectionRatio <= 0) {
    return;
  }
  loadItems(10);
  // appendChild will move the existing element, so there is no need to
  // remove it first.
  scroller.appendChild(sentinel);
  loadItems(5);
  ChromeSamples.setStatus('Loaded up to item ' + counter);
});
intersectionObserver.observe(sentinel);

/* global IntersectionObserver */
var scroller = document.querySelector('#scroller');
var sentinel = document.querySelector('#sentinel');
var counter = 1;

function loadItems(n) {
  for (var i = 0; i < n; i++) {
    var newItem = document.createElement('div');
    newItem.classList.add('item');
    newItem.textContent = 'Item ' + counter++;
    scroller.appendChild(newItem);
  }
}

var intersectionObserver = new IntersectionObserver(entries => {
  // If the browser is busy while scrolling happens, multiple entries can
  // accumulate between invocations of this callback, so we loop over
  // them.
  entries.forEach(entry => {
    // If intersectionRatio is 0, the sentinel is out of view
    // and we do not need to do anything.
    if (entry.intersectionRatio > 0) {
      loadItems(10);
      // appendChild will move the existing element, so there is no need to
      // remove it first.
      scroller.appendChild(sentinel);
      loadItems(5);
      ChromeSamples.setStatus('Loaded up to item ' + counter);

      // We have already loaded more content, no need to look at any more
      // entries.
      return;
    }
  });
});
intersectionObserver.observe(sentinel);

Check all entries in callback


const getMarkdown = require('./getMarkdown')
const config = require('../config.js')

module.exports = (root) => new Promise((resolve, reject) => {
  getMarkdown(`${root}/*`)
    .then((mdArr) => {
      resolve(
        mdArr.map((md) => {
          return {
            name: md.name,
            url: config.getFullUrl(`anotherblog/${md.name}`),
            title: md.title,
            teaser: md.paragraphs[0],
            articleContent: md.html,
            author: {
              name: 'Luca Nils Schmid',
              url: config.getFullUrl(),
              email: 'allspamhere@kriegslustig.me'
            },
            created: md.ctime
          }
        })
        .sort((a, b) => {
          return (
            a.created.getTime() > b.create
              ? 1
              : -1
          )
        })
      )
    })
    .catch(reject)
})

const getMarkdown = require('./getMarkdown')
const config = require('../config.js')

module.exports = (root) => new Promise((resolve, reject) => {
  getMarkdown(`${root}/*`)
    .then((mdArr) => {
      resolve(
        mdArr.map((md) => {
          return {
            name: md.name,
            url: config.getFullUrl(`anotherblog/${md.name}`),
            title: md.title,
            teaser: md.paragraphs[0],
            articleContent: md.html,
            author: {
              name: 'Luca Nils Schmid',
              url: config.getFullUrl(),
              email: 'allspamhere@kriegslustig.me'
            },
            created: md.ctime
          }
        })
        .sort((a, b) => {
          return (
            a.created.getTime() > b.created.getTime()
              ? -1
              : 1
          )
        })
      )
    })
    .catch(reject)
})

Fix the broken blog sorting mechanism


var os      = require('os')
	, path    = require('path')
	, express = require('express')
	, less    = require('less-middleware')
	, tmp     = path.join(os.tmpDir(), 'TesselKey')
	, root    = __dirname
	, pub     = path.join(root, 'public')
	, images  = path.join(pub, 'images')
	, scripts = path.join(pub, 'scripts')
	, views   = path.join(root, 'views')
	, app     = express()
	, trimmer = require('./modules/trimmer.js')
;

app.disable('x-powered-by');

app.configure(function() {
	app.use(trimmer());
	//app.use(express.compress());
	app.use(express.favicon());
	app.use('/images',  express.static(images));
	app.use('/scripts', express.static(scripts));
	app.use(less({ src: pub, dest: tmp }));
	app.use(express.static(tmp));
	//app.use(trimmer());
	app.use(express.static(views));
});

app.listen(80);

var os      = require('os')
	, path    = require('path')
	, express = require('express')
	, less    = require('less-middleware')
	, tmp     = path.join(os.tmpDir(), 'TesselKey')
	, root    = __dirname
	, pub     = path.join(root, 'public')
	, images  = path.join(pub, 'images')
	, scripts = path.join(pub, 'scripts')
	, views   = path.join(root, 'views')
	, app     = express()
	, trimmer = require('./modules/trimmer.js')
;

app.disable('x-powered-by');

app.configure(function() {
	app.use(express.compress());
	app.use(trimmer());
	app.use(express.favicon());
	app.use('/images',  express.static(images));
	app.use('/scripts', express.static(scripts));
	app.use(less({ src: pub, dest: tmp }));
	app.use(express.static(tmp));
	app.use(express.static(views));
});

app.listen(80);

Allow compression and whitespace trimming.


const path = require('path');
var DuplicatePackageCheckerPlugin = require('../');

module.exports = {
  entry: './entry.js',
  context: __dirname,
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new DuplicatePackageCheckerPlugin()
  ]
};

const path = require('path');
var DuplicatePackageCheckerPlugin = require('../index.js');

module.exports = {
  entry: './entry.js',
  context: __dirname,
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new DuplicatePackageCheckerPlugin()
  ]
};

Use source file when testing, not built