'use strict';

var path = require('path');
var BinWrapper = require('bin-wrapper');
var pkg = require('../package.json');
var url = 'https://raw.githubusercontent.com/imagemin/gifsicle-bin/' + pkg.version + '/vendor/';

module.exports = new BinWrapper()
	.src(url + 'osx/gifsicle', 'darwin')
	.src(url + 'linux/x86/gifsicle', 'linux', 'x86')
	.src(url + 'linux/x64/gifsicle', 'linux', 'x64')
	.src(url + 'freebsd/x86/gifsicle', 'freebsd', 'x86')
	.src(url + 'freebsd/x64/gifsicle', 'freebsd', 'x64')
	.src(url + 'win/x86/gifsicle.exe', 'win32', 'x86')
	.src(url + 'win/x64/gifsicle.exe', 'win32', 'x64')
	.dest(path.join(__dirname, '../vendor'))
	.use(process.platform === 'win32' ? 'gifsicle.exe' : 'gifsicle');

'use strict';

var path = require('path');
var BinWrapper = require('bin-wrapper');
var pkg = require('../package.json');
var url = 'https://raw.githubusercontent.com/imagemin/gifsicle-bin/v' + pkg.version + '/vendor/';

module.exports = new BinWrapper()
	.src(url + 'osx/gifsicle', 'darwin')
	.src(url + 'linux/x86/gifsicle', 'linux', 'x86')
	.src(url + 'linux/x64/gifsicle', 'linux', 'x64')
	.src(url + 'freebsd/x86/gifsicle', 'freebsd', 'x86')
	.src(url + 'freebsd/x64/gifsicle', 'freebsd', 'x64')
	.src(url + 'win/x86/gifsicle.exe', 'win32', 'x86')
	.src(url + 'win/x64/gifsicle.exe', 'win32', 'x64')
	.dest(path.join(__dirname, '../vendor'))
	.use(process.platform === 'win32' ? 'gifsicle.exe' : 'gifsicle');

Use git tags prepended with v


"use strict";

var sass = require("node-sass");
var through = require("through");
var path = require("path");
var extend = require('util')._extend;

module.exports = function (fileName, globalOptions) {
    if (!/(\.scss|\.css)$/i.test(fileName)) {
        return through();
    }

    var inputString = "";

    return through(
        function (chunk) {
            inputString += chunk;
        },
        function () {
            var options, css, moduleBody;

            // new copy of globalOptions for each file
            options = extend({}, globalOptions || {});
            options.includePaths = extend([], (globalOptions ? globalOptions.includePaths : {}) || []);

            options.data = inputString;
            options.includePaths.unshift(path.dirname(fileName));

            try {
                css = sass.renderSync(options);
            } catch (err) {
                this.emit('error', err);
                return;
            }

            var escapedCSS = JSON.stringify(css)

            moduleBody = "var css = " + escapedCSS + ";" +
                "(require('sassify'))(css); module.exports = css;";

            this.queue(moduleBody);
            this.queue(null);
        }
    );
};

"use strict";

var sass = require("node-sass");
var through = require("through");
var path = require("path");
var extend = require('util')._extend;

module.exports = function (fileName, globalOptions) {
    if (!/(\.scss|\.css)$/i.test(fileName)) {
        return through();
    }

    var inputString = "";

    return through(
        function (chunk) {
            inputString += chunk;
        },
        function () {
            var options, css, moduleBody;

            // new copy of globalOptions for each file
            options = extend({}, globalOptions || {});
            options.includePaths = extend([], (globalOptions ? globalOptions.includePaths : {}) || []);

            // Resolve include paths to baseDir
            if(options.baseDir) {
                options.includePaths = options.includePaths.map(function (p) { return path.dirname(path.resolve(options.baseDir, p)); });
            }

            options.data = inputString;
            options.includePaths.unshift(path.dirname(fileName));

            try {
                css = sass.renderSync(options);
            } catch (err) {
                this.emit('error', err);
                return;
            }

            var escapedCSS = JSON.stringify(css)

            moduleBody = "var css = " + escapedCSS + ";" +
                "(require('sassify'))(css); module.exports = css;";

            this.queue(moduleBody);
            this.queue(null);
        }
    );
};

Handle baseDir option to resolve include paths before rendering;


(function() {
  'use strict';

  program.prompt([
    'Hello—I\'m Prompt.'
  ]);

}());

(function() {
  'use strict';

  const stdin = document.getElementById('stdin');
  let lastKey;

  program.scrollBottom();
  setTimeout(() => {
    program.prompt([
      'Hello—I\'m Prompt.'
    ]);
    setTimeout(() => {
      program.prompt([
        'I don\'t do anything yet, so you can send me messages and I won\'t reply to you!'
      ]);
    }, 1500);
  }, 1000);

  function finalMsg () {
    const e = event;
    const code = e.keyCode || e.which;
    if (lastKey !== 13) {
      if (code === 13) { // Enter keycode
        setTimeout(() => {
          if (program.history.length === 1) {
            setTimeout(() => {
              program.prompt([
                'Actually, I do keep scrolling when you return things, but that\'s it.'
              ]);
            }, 600);
          } else if (program.history.length === 2) {
            setTimeout(() => {
              program.prompt([
                'Have fun typing to no one.'
              ]);
            }, 600);
          }
        }, 50);
      }
    }
    lastKey = code;
  }

  stdin.addEventListener('keyup', finalMsg );

}());

Add personality (like a homepage)


var path = require("path");

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-gitbook');
    grunt.loadNpmTasks('grunt-gh-pages');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.initConfig({
        'gitbook': {
            development: {
                input: "./",
                github: "daikeren/django_tutorial"
            }
        },
        'gh-pages': {
            options: {
                base: '_book',
                silent: true

            },
            src: ['**']
        },
        'clean': {
            files: '.grunt'
        }
    });

    grunt.registerTask('publish', [
        'gitbook',
        'gh-pages',
        'clean'
    ]);
    grunt.registerTask('default', 'gitbook');
};

var path = require("path");

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-gitbook');
    grunt.loadNpmTasks('grunt-gh-pages');
    grunt.loadNpmTasks('grunt-contrib-clean');

    grunt.initConfig({
        'gitbook': {
            development: {
                input: "./",
                github: "daikeren/django_tutorial"
            }
        },
        'gh-pages': {
            options: {
                base: '_book'

            },
            src: ['**']
        },
        'clean': {
            files: '.grunt'
        }
    });

    grunt.registerTask('publish', [
        'gitbook',
        'gh-pages',
        'clean'
    ]);
    grunt.registerTask('default', 'gitbook');
};

Remove silence to see what happen...


module.exports = function(grunt) {
  var allSassFiles = [];

  var path = require('path');

  grunt.file.recurse(
    "./stylesheets/",
    function(abspath, rootdir, subdir, filename) {
      if (filename.match(/\.scss/)) {
        allSassFiles.push("@import '" + abspath + "';");
      }
    }
  );

  grunt.file.write(
    "./spec/stylesheets/test.scss",
    allSassFiles.join("\n")
  );

  grunt.initConfig({
    clean: {
      sass: ["spec/stylesheets/test*css"]
    },
    jasmine: {
      javascripts: {
        src: [
          'node_modules/jquery-browser/lib/jquery.js',
          'javascripts/**/*.js'
        ],
        options: {
          specs: 'spec/unit/*Spec.js',
          helpers: 'spec/unit/*Helper.js'
        }
      }
    },
    sass: {
      development: {
        files: {
          './spec/stylesheets/test-out.css': './spec/stylesheets/test.scss'
        },
        options: {
          loadPath: [
            './stylesheets'
          ],
          style: 'nested',
        }
      },
    }
  });
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.registerTask('test', ['sass', 'clean', 'jasmine']);
  grunt.registerTask('default', ['test']);
};

module.exports = function(grunt) {
  var allSassFiles = [];

  var path = require('path');

  grunt.file.recurse(
    "./stylesheets/",
    function(abspath, rootdir, subdir, filename) {
      if(typeof subdir !== 'undefined'){
        var relpath = subdir + '/' + filename;
      } else {
        var relpath = filename;
      }
      if (filename.match(/\.scss/)) {
        allSassFiles.push("@import '" + relpath + "';");
      }
    }
  );

  grunt.file.write(
    "./spec/stylesheets/test.scss",
    allSassFiles.join("\n")
  );

  grunt.initConfig({
    clean: {
      sass: ["spec/stylesheets/test*css"]
    },
    jasmine: {
      javascripts: {
        src: [
          'node_modules/jquery-browser/lib/jquery.js',
          'javascripts/**/*.js'
        ],
        options: {
          specs: 'spec/unit/*Spec.js',
          helpers: 'spec/unit/*Helper.js'
        }
      }
    },
    sass: {
      development: {
        files: {
          './spec/stylesheets/test-out.css': './spec/stylesheets/test.scss'
        },
        options: {
          loadPath: [
            './stylesheets'
          ],
          style: 'nested',
        }
      },
    }
  });
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.registerTask('test', ['sass', 'clean', 'jasmine']);
  grunt.registerTask('default', ['test']);
};

Make `@import` paths relative to `./stylesheets`


import styles from './BookList.css';
import classes from 'classnames';

export default ({title, zebra, children, className}) => (

        {title ?
{title}
 : ''}

            {children}

);

import styles from './BookList.css';
import classes from 'join-classnames';

export default ({title, zebra, children, className}) => (

        {title ?
{title}
 : ''}

            {children}

);

Fix improper import of join-classnames


/*
 * boilerplate-redux-react
 *
 * Copyright(c) 2015 André König <andre.koenig@posteo.de>
 * MIT Licensed
 *
 */

/**
 * @author André König <andre.koenig@posteo.de>
 *
 */

import React, {Component} from 'react';
import {Provider} from 'react-redux';

import {createStore} from './store';

import {Welcome} from './containers';

const store = createStore();

class App extends Component {

	render() {
		return (
			<Provider store={store}>
				<Welcome />
			</Provider>
		);
	}

}

export default App;

/*
 * boilerplate-redux-react
 *
 * Copyright(c) 2015 André König <andre.koenig@posteo.de>
 * MIT Licensed
 *
 */

/**
 * @author André König <andre.koenig@posteo.de>
 *
 */

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

import {createStore} from './store';

import {Welcome} from './containers';

const store = createStore();

export default () =>
	<Provider store={store}>
		<Welcome />
	</Provider>;

Convert app component to function


"use strict";

var user = module.parent.require('./user'),
    db = module.parent.require('./database'),
    plugin = {};

plugin.init = function(params, callback) {
	var app = params.router,
		middleware = params.middleware,
		controllers = params.controllers;

	app.get('/admin/custom-topics', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/custom-topics', renderAdmin);

	callback();
};

plugin.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/custom-topics',
		icon: 'fa-tint',
		name: 'Custom Topics'
	});

	callback(null, header);
};

plugin.onTopicCreate = function(data, callback) {
    console.log("Topic created!");

    // data.topic, this is the topic that will be saved to the database
    // data.data, this is the data that is submitted from the client side
    // Now all you have to do is validate `data.myCustomField` and set it in data.topic.

    if (data.data.age) {
        data.topic.age = data.data.age;
    }

    console.dir(data);

    callback(null, data);
};

function renderAdmin(req, res, next) {
	res.render('admin/custom-topics', {});
}

module.exports = plugin;

"use strict";

var user = module.parent.require('./user'),
    db = module.parent.require('./database'),
    plugin = {};

plugin.init = function(params, callback) {
	var app = params.router,
		middleware = params.middleware,
		controllers = params.controllers;

	app.get('/admin/custom-topics', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/custom-topics', renderAdmin);

	callback();
};

plugin.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/custom-topics',
		icon: 'fa-tint',
		name: 'Custom Topics'
	});

	callback(null, header);
};

plugin.onTopicCreate = function(data, callback) {
    console.log("Topic created!");

    // data.topic, this is the topic that will be saved to the database
    // data.data, this is the data that is submitted from the client side
    // Now all you have to do is validate `data.myCustomField` and set it in data.topic.

    if (data.data.age) {
        data.data.content += '<b>Age: </b>' + data.data.age + '</br>';
    }

    console.dir(data);

    callback(null, data);
};

function renderAdmin(req, res, next) {
	res.render('admin/custom-topics', {});
}

module.exports = plugin;

Add ‘age’ to post content


import * as Backbone from 'backbone';
import AppRouter from './routers/app-router';

let router = new AppRouter();
Backbone.history.start({pushState: true});

import * as Backbone from 'backbone';
import AppRouter from './routers/app-router';

new AppRouter();
Backbone.history.start({pushState: true});

Remove unused variable name for router


var path = require('path');

module.exports = {
  entry: './index.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
	test: /\.js$/,
	exclude: /node_modules/,
	loader: "babel-loader"
      }
    ]
  }
};

var path = require('path');

module.exports = {
  entry: './index.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
	test: /\.js$/,
	exclude: /node_modules/,
	loader: "babel-loader"
      },
      {
	test: /\.scss$/,
        loaders: ['style', 'css', 'sass'],
      }
    ]
  },
  sassLoader: {
    includePath: [path.resolve(__dirname, './styles')]
  }
};

Add scss loader to webpack


$(document).ready(function() {
  var $toc = $('#toc');

  function format (title) {
    var $title = $(title),
        txt = $title.text(),
        id = $title.attr('id');
    return "
 " + txt + "
";
  }
  // return;

  if($toc.length) {
    var $h3s = $('.container .col-md-9 :header');
    var titles = $h3s.map(function () {
      return format(this);
    }).get().join('');
    $toc.html(titles);
  }

  $("#toc").affix();

});

$(document).ready(function() {
  var $toc = $('#toc');

  function format (item) {
    if (item.children && item.children.length > 0) {
      return "
 " + item.title + "
"
        + item.children.map(format).join('')
        + "

";
    } else {
      return "
 " + item.title + "
";
    }
  }
  // return;

  if($toc.length) {
    var $h3s = $('.container .col-md-9 :header');

    var tocTree = [];
    var lastRoot;

    $h3s.each(function(i, el) {
      var $el = $(el);
      var id = $el.attr('id');
      var title = $el.text();
      var depth = parseInt($el.prop("tagName")[1]);

      if(depth > 3)
        return;

      if (lastRoot && depth > lastRoot.depth) {
        lastRoot.children.push({id: id, title: title });
      } else {
        lastRoot = {depth: depth,
                    title: title,
                    id: id,
                    children: []};
        tocTree.push(lastRoot);
      }
    });

    var titles = tocTree.map(format).join('');

    $toc.html(titles);
  }

  $("#toc").affix();

  $('#side-navigation').on('activate.bs.scrollspy', function (e) {
    var parent = $(e.target).parent().parent()[0];

    if (parent.tagName == "LI") {
      $(parent).addClass("active")
    }

  })

});

Fix problem with TOC and nested tags


define(function (require) {
    'use strict';

    var Marionette = require('marionette'),

        LearnersRouter;

    LearnersRouter = Marionette.AppRouter.extend({
        // Routes intended to show a page in the app should map to method names
        // beginning with "show", e.g. 'showLearnerRosterPage'.
        appRoutes: {
            '(/)(?*queryString)': 'showLearnerRosterPage',
            ':username(/)(?*queryString)': 'showLearnerDetailPage',
            '*notFound': 'showNotFoundPage'
        },

        onRoute: function (routeName) {
            if (routeName.indexOf('show') === 0) {
                this.options.controller.triggerMethod('showPage');
            }
        },

        initialize: function (options) {
            this.options = options || {};
            this.learnerCollection = options.controller.options.learnerCollection;
            this.listenTo(this.learnerCollection, 'sync', this.updateUrl);
            Marionette.AppRouter.prototype.initialize.call(this, options);
        },

        // Called on LearnerCollection update. Converts the state of the collection (including any filters, searchers,
        // sorts, or page numbers) into a url and then navigates the router to that url.
        updateUrl: function () {
            this.navigate(this.learnerCollection.getQueryString());
        },

    });

    return LearnersRouter;
});

define(function (require) {
    'use strict';

    var Marionette = require('marionette'),

        LearnersRouter;

    LearnersRouter = Marionette.AppRouter.extend({
        // Routes intended to show a page in the app should map to method names
        // beginning with "show", e.g. 'showLearnerRosterPage'.
        appRoutes: {
            '(/)(?*queryString)': 'showLearnerRosterPage',
            ':username(/)(?*queryString)': 'showLearnerDetailPage',
            '*notFound': 'showNotFoundPage'
        },

        onRoute: function (routeName) {
            if (routeName.indexOf('show') === 0) {
                this.options.controller.triggerMethod('showPage');
            }
        },

        initialize: function (options) {
            this.options = options || {};
            this.learnerCollection = options.controller.options.learnerCollection;
            this.listenTo(this.learnerCollection, 'sync', this.updateUrl);
            Marionette.AppRouter.prototype.initialize.call(this, options);
        },

        // Called on LearnerCollection update. Converts the state of the collection (including any filters, searchers,
        // sorts, or page numbers) into a url and then navigates the router to that url.
        updateUrl: function () {
            this.navigate(this.learnerCollection.getQueryString(), {replace: true});
        },

    });

    return LearnersRouter;
});

Replace browser history on roster filter update


if (process.env.NODE_ENV !== 'production') {
  // we're importing script and styles from given plugin
  // this method should be only used in development
  // for production specify your plugin in package.json
  require('epl-plugin/dev');
  require('epl-plugin/dev/style.css');

  // add plugins in development mode here
  // require('epl-plugin/dev');
  // require('epl-plugin/dev/style.css');
}

// plugin handlers
const plugins = Object.keys(window)
  .filter(key => {
    return key.match(/^__EPL_/);
  })
  .map(key => console.log('Loaded:', key.replace('__EPL_', '')) || window[key]);
console.log(plugins);

export default plugins;

if (process.env.NODE_ENV !== 'production') {
  // we're importing script and styles from given plugin
  // this method should be only used in development
  // for production specify your plugin in package.json
  // require('epl-plugin/dev');
  // require('epl-plugin/dev/style.css');
}

// plugin handlers
const plugins = Object.keys(window)
  .filter(key => {
    return key.match(/^__EPL_/);
  })
  .map(key => console.log('Loaded:', key.replace('__EPL_', '')) || window[key]);
console.log(plugins);

export default plugins;

Remove epl-plugin in development mode


const { join } = require('path')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

module.exports = {
  webpack: function (config, { dev }) {
    if (!dev) {
      config.plugins.push(new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        openAnalyzer: false,
        reportFilename: join(__dirname, 'reports/bundles.html'),
        defaultSizes: 'gzip'
      }))
    }

    return config
  }
}

const webpack = require('webpack')
const { join } = require('path')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

module.exports = {
  webpack: function (config, { dev }) {
    config.plugins.push(
      new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /fr/)
    )

    if (!dev) {
      config.plugins.push(new BundleAnalyzerPlugin({
        analyzerMode: 'static',
        openAnalyzer: false,
        reportFilename: join(__dirname, 'reports/bundles.html'),
        defaultSizes: 'gzip'
      }))
    }

    return config
  }
}

Remove useless locales from moment


// https://github.com/odanielson/vector_2d.js
// Copyright 2013, odanielson@github.com
// MIT-license

var Norm = function(v) {
    return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
}

var Normalize = function(v) {
    var norm = Norm(v);
    return [v[0] / norm, v[1] / norm];
}

var Subtract = function(a, b) {
    return [a[0] - b[0], a[1] - b[1]];
}

var Add = function(a, b) {
    return [a[0] + b[0], a[1] + b[1]];
}

var Multiply = function(v, k) {
    return [v[0] * k, v[1] * k];
}

var ComplexMultiply = function(v1, v2) {
    return [ (v1[0] * v2[0]) - (v1[1] * v2[1]), (v1[0] * v2[1]) + (v2[0] * v1[1]) ];
}

var RotateLeft = function(v) {
    return ComplexMultiply(v, [0,-1]);
}

Add git src at top.


var express = require('express'),
    router = express.Router(),
    bcrypt = require('bcrypt'),
    db = require('../models/db'),
    index = {};

index.all = function(oauth){
  /* GET home page. */
  router.get('/', oauth.authorise(), function(req, res) {
    res.render('index', { title: 'Express' });
  });

  return router;
};

module.exports = index;

var express = require('express'),
    router = express.Router(),
    bcrypt = require('bcrypt'),
    db = require('../models/db'),
    index = {};

index.all = function(oauth){
  /* GET home page. */
  router.get('/', oauth.authorise(), function(req, res) {
    res.render('index', { title: 'Express' });
  });

  /* GET Register new user redirects to users/new. */
  router.get('/register', function(req, res) {
    res.redirect('/users/new');
  });

  return router;
};

module.exports = index;

Add /register route to redirect to users/new


import expect from 'expect';
import I from 'immutable';

import {Note} from '../../../../../reducers/chart';
import {NOTE_HEIGHT} from '../..//constants';

import {
  WIDTH,
  HEIGHT,
  renderNotes,
} from '../';

class MockContext {
  constructor() {
    this.canvas = {
      width: WIDTH,
      height: HEIGHT,
    };

    this.fillRect = expect.createSpy();
    this.strokeRect = expect.createSpy();
  }
}

describe('<CanvasChart>', () => {
  it('renders notes on the judgement line when the scroll offset matches the note offset', () => {
    const offsetPositionYPercent = 0.9;
    const offset = 20 * 24 + 12;

    const note = new Note({
      col: 0,
      totalOffset: offset,
    });

    const ctx = new MockContext();

    const offsetBarY = (1 - offsetPositionYPercent) * HEIGHT;

    renderNotes(ctx, {
      notes: I.Set([note]),
      beatSpacing: 160,
      offset,
      offsetBarY,
    });

    expect(ctx.fillRect.calls.length).toEqual(1);
    expect(ctx.fillRect.calls[0].arguments[1]).toEqual(offsetBarY - (NOTE_HEIGHT / 2));
  });
});

import expect from 'expect';
import I from 'immutable';

import {Note} from '../../../../../reducers/chart';
import {NOTE_HEIGHT} from '../../constants';
import {playerColors} from '../../../../../config/constants';

import {
  WIDTH,
  HEIGHT,
  renderNotes,
} from '../';

class MockContext {
  constructor() {
    this.canvas = {
      width: WIDTH,
      height: HEIGHT,
    };

    this.fillRect = expect.createSpy();
    this.strokeRect = expect.createSpy();
  }
}

describe('<CanvasChart>', () => {
  it('renders notes on the judgement line when the scroll offset matches the note offset', () => {
    const offsetPositionYPercent = 0.9;
    const offset = 20 * 24 + 12;

    const note = new Note({
      col: 0,
      totalOffset: offset,
    });

    const ctx = new MockContext();

    const offsetBarY = (1 - offsetPositionYPercent) * HEIGHT;

    renderNotes(ctx, {
      colors: playerColors,
      notes: I.Set([note]),
      beatSpacing: 160,
      offset,
      offsetBarY,
    });

    expect(ctx.fillRect.calls.length).toEqual(1);
    expect(ctx.fillRect.calls[0].arguments[1]).toEqual(offsetBarY - (NOTE_HEIGHT / 2));
  });
});

Fix canvas chart test missing colors


function init() {
    test();
}

function test() {
    const divObject = document.getElementById('content');

    // create database reference
    const dbRefObject = firebase.database().ref().child('object');

    // sync object data
    dbRefObject.on('value', snap => console.log(snap.val()));
}

function init() {
    test();
}

function test() {
    const divObject = document.getElementById('content');

    // create database reference
    const dbRefObject = firebase.database().ref().child('object');

    // sync object data
    dbRefObject.on('value', snap => {
        divObject.innerHTML = JSON.stringify(snap.val(), null, 3);
    });
}

Write object data to div element as a JSON string.


Template.bookmarksList.helpers({
    bookmarks: function() {
        if (Meteor.user()) {
            var searchTags = document.getElementById("tagSearch").value;
            if (searchTags != undefined) {
                return Bookmarks.find({
                    tags: searchTags
                }, {
                    sort: {
                        dateCreated: -1
                    }
                })
            } else {
                return (Bookmarks.find({
                    userId: Meteor.user()._id
                }))
            }
        }
    }
});

Template.bookmarksList.helpers({
    bookmarks: function() {
        if (Meteor.user()) {
            return (Bookmarks.find({
                userId: Meteor.user()._id
            }))
        }
    }
});

Fix bug of the last commit


/* global angular */

angular
  .module('speciesApp')
  .factory('SpeciesService', [
    'API_BASE_URL',
    '$resource',
    SpeciesService
  ])

function SpeciesService (API_BASE_URL, $resource) {
  return $resource(
    API_BASE_URL,
    null,
    {
      getKingdoms: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getkingdomnames'
        }
      },
      getClasses: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getclassnames'
        }
      }
    }
  )
}

/* global angular */

angular
  .module('speciesApp')
  .factory('SpeciesService', [
    'API_BASE_URL',
    '$resource',
    SpeciesService
  ])

function SpeciesService (API_BASE_URL, $resource) {
  return $resource(
    API_BASE_URL,
    null,
    {
      getKingdoms: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getkingdomnames'
        }
      },
      getClasses: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getclassnames'
        }
      },
      getFamilies: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getfamilynames'
        }
      }
    }
  )
}

Add an action to get family names by class and kingdom


// Main starting point of the application
const express = require('express');
const path = require('path')
const http = require('http');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const app = express();
const router = require('./router');
const mongoose = require('mongoose');
const cors = require('cors');

// DB Setup
mongoose.connect('mongodb://localhost:auth/auth');

// App Setup
app.use(morgan('combined'));
app.use(cors());
app.use(express.static(path.resolve(__dirname, '..', 'build')));
app.use(bodyParser.json({ limit: '5mb' }));
app.use(bodyParser.urlencoded({ limit: '5mb', extended: true }));
router(app);

// Server Setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);
console.log('Server listening on:', port);

// Main starting point of the application
const express = require('express');
const path = require('path')
const http = require('http');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const app = express();
const router = require('./router');
const mongoose = require('mongoose');
const cors = require('cors');

// DB Setup
mongoose.connect(process.env.MONGODB_URI);

// App Setup
app.use(morgan('combined'));
app.use(cors());
app.use(express.static(path.resolve(__dirname, '..', 'build')));
app.use(bodyParser.json({ limit: '5mb' }));
app.use(bodyParser.urlencoded({ limit: '5mb', extended: true }));
router(app);

// Server Setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);
console.log('Server listening on:', port);

Change mongodb uri to be an env var.


require('babel-core/register');
require('dotenv').load();

const serverConfig = require('./config');

// To ignore webpack custom loaders on server.
serverConfig.webpackStylesExtensions.forEach((ext) => {
  require.extensions[`.${ext}`] = () => {};
});

require('./main');

require('babel-core/register');

const path = require('path');
require('dotenv').load({ path: path.resolve(__dirname, '../.env') });

const serverConfig = require('./config');

// To ignore webpack custom loaders on server.
serverConfig.webpackStylesExtensions.forEach((ext) => {
  require.extensions[`.${ext}`] = () => {};
});

require('./main');

Use absolute path to .env file just in case


/**@class	A matcher piece that calls `compare` on its element's text.
*/
var TextExtractor = new Class( /** @lends matchers.TextExtractor# */ {
	/** The attribute that should be extracted from the element.
	* If not defined, defaults to using this class' `type` property.
	*
	*@type	{String}
	*/
	attribute: null,

	onElementFound: function(element) {
		element.getText().then(this.compare, this.fail);
	}
});

module.exports = TextExtractor;	// CommonJS export

/**@class	A matcher piece that calls `compare` on its element's text.
*/
var TextExtractor = new Class( /** @lends matchers.TextExtractor# */ {
	/** The attribute that should be extracted from the element.
	* If not defined, defaults to using this class' `type` property.
	*
	*@type	{String}
	*/
	attribute: null,

	onElementFound: function(element) {
		element.text().then(this.compare, this.fail);
	}
});

module.exports = TextExtractor;	// CommonJS export

Update text accessors for WD Pass all tests


/**
* Copyright 2012-2021, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

module.exports = {
    _isLinkedToArray: 'frames_entry',

    group: {
        valType: 'string',
        description: [
            'An identifier that specifies the group to which the frame belongs,',
            'used by animate to select a subset of frames.'
        ].join(' ')
    },
    name: {
        valType: 'string',
        description: 'A label by which to identify the frame'
    },
    traces: {
        valType: 'any',
        description: [
            'A list of trace indices that identify the respective traces in the',
            'data attribute'
        ].join(' ')
    },
    baseframe: {
        valType: 'string',
        description: [
            'The name of the frame into which this frame\'s properties are merged',
            'before applying. This is used to unify properties and avoid needing',
            'to specify the same values for the same properties in multiple frames.'
        ].join(' ')
    },
    data: {
        valType: 'any',
        role: 'object',
        description: [
            'A list of traces this frame modifies. The format is identical to the',
            'normal trace definition.'
        ].join(' ')
    },
    layout: {
        valType: 'any',
        role: 'object',
        description: [
            'Layout properties which this frame modifies. The format is identical',
            'to the normal layout definition.'
        ].join(' ')
    }
};

/**
* Copyright 2012-2021, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

module.exports = {
    _isLinkedToArray: 'frames_entry',

    group: {
        valType: 'string',
        description: [
            'An identifier that specifies the group to which the frame belongs,',
            'used by animate to select a subset of frames.'
        ].join(' ')
    },
    name: {
        valType: 'string',
        description: 'A label by which to identify the frame'
    },
    traces: {
        valType: 'any',
        description: [
            'A list of trace indices that identify the respective traces in the',
            'data attribute'
        ].join(' ')
    },
    baseframe: {
        valType: 'string',
        description: [
            'The name of the frame into which this frame\'s properties are merged',
            'before applying. This is used to unify properties and avoid needing',
            'to specify the same values for the same properties in multiple frames.'
        ].join(' ')
    },
    data: {
        valType: 'any',
        description: [
            'A list of traces this frame modifies. The format is identical to the',
            'normal trace definition.'
        ].join(' ')
    },
    layout: {
        valType: 'any',
        description: [
            'Layout properties which this frame modifies. The format is identical',
            'to the normal layout definition.'
        ].join(' ')
    }
};

Revert "revert data and layout role to be object"


var fs = require('fs');
var moment = require('moment');
var rank = require('librank').rank;

// data backup path
var data_path = "./data/talks/";

// read all files in the database
var res = fs.readdirSync(data_path);

// current timestamp
var now = moment();

// calculate difference of hours between two timestamps
function countHours(start, end) {
    var duration = moment.duration(end.diff(start));
    var hours = duration.asHours();
    return hours;
}

// calculate ranking for all elements in the database
res.forEach(function(element) {
    // full fs file path
    var talk_path = data_path + element;
    // get its data
    var talk = require(talk_path);
    // points
    var points = talk.voteCount + 1;
    // hours
    var hours = countHours(talk.created, now);
    // gravity
    var gravity = 1.8;
    // ranking calculation
    var score = rank.rank(points, hours, gravity);
    console.log(score, " - ", talk.title);
});

var fs = require('fs');
var moment = require('moment');
var rank = require('librank').rank;

// data backup path
var data_path = "./data/talks/";

// read all files in the database
var res = fs.readdirSync(data_path);

// current timestamp
var now = moment();

// calculate difference of hours between two timestamps
function countHours(start, end) {
    var duration = moment.duration(end.diff(start));
    var hours = duration.asHours();
    return hours;
}

// calculate ranking for all elements in the database
res.forEach(function(element) {
    /*
    // full fs file path
    var talk_path = data_path + element;
    // get its data
    var talk = require(talk_path);
    // points
    var points = talk.voteCount + 1;
    // hours
    var hours = countHours(talk.created, now);
    // gravity
    var gravity = 1.8;
    // ranking calculation
    var score = rank.rank(points, hours, gravity);
    console.log(score, " - ", talk.title);
    */
});

Remove logic on update remote


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

'use strict';

const path = require('path');

const unzip = require('../../shared/unzip.js');

const { Installer } = require('../../shared/installer.js');

const extract = ({ filePath, binary, os }) => {
	return new Promise(async (resolve, reject) => {
		const tmpPath = path.dirname(filePath);
		await unzip({
			from: filePath,
			to: tmpPath,
		});
		const installer = new Installer({
			engine: binary,
			path: tmpPath,
		});
		switch (os) {
			case 'linux64': {
				installer.installBinary({ 'qjsbn': binary });
				break;
			}
		}
		resolve();
	});
};

module.exports = extract;

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

'use strict';

const path = require('path');

const unzip = require('../../shared/unzip.js');

const { Installer } = require('../../shared/installer.js');

const extract = ({ filePath, binary, os }) => {
	return new Promise(async (resolve, reject) => {
		const tmpPath = path.dirname(filePath);
		await unzip({
			from: filePath,
			to: tmpPath,
		});
		const installer = new Installer({
			engine: binary,
			path: tmpPath,
		});
		switch (os) {
			case 'linux64': {
				installer.installBinary({ 'qjs': binary });
				break;
			}
		}
		resolve();
	});
};

module.exports = extract;

Update quickjs installer per latest upstream changes


(function ($) {

    $(function () {
        ZeroClipboard.config({swfPath: $('[data-swf-path]').data('swf-path')});

        $('pre').each(function () {
            if (!$(this).find('code').length) {
                var code = $(this).html();
                $(this).html('');
                $('').html(code).appendTo($(this));
            }
        }).append('
Copy
');

        $('.plugin-zclip').each(function () {
            var $this = $(this),
                client = new ZeroClipboard($this[0]);

            client.on( "ready", function() {
                client.on('copy', function () {
                    ZeroClipboard.clearData();
                    var $code = $this.parent().find('code').clone();
                    $code.find('.line-numbers').remove();
                    ZeroClipboard.setData('text/plain', $code.text());
                    $this.text('Copied!');
                });
            });
        });
    });
})(jQuery.noConflict());

Fix conflict with jQuery and prototype


import Component from '@ember/component';

import $ from 'jquery';
import ol from 'openlayers';

export default class PlaneLabelOverlay extends Component {
  tagName = '';

  map = null;
  flight = null;
  position = null;
  overlay = null;

  init() {
    super.init(...arguments);

    let badgeStyle = `display: inline-block; text-align: center; background: ${this.get('flight.color')}`;

    let id = this.getWithDefault('flight.competition_id', '') || this.getWithDefault('flight.registration', '');
    let badge = $(`<span class="badge plane_marker" style="${badgeStyle}">
      ${id}
    </span>`);

    this.set(
      'overlay',
      new ol.Overlay({
        element: badge.get(0),
      }),
    );
  }

  didReceiveAttrs() {
    super.didReceiveAttrs(...arguments);
    this.overlay.setPosition(this.position);
  }

  didInsertElement() {
    super.didInsertElement(...arguments);
    let overlay = this.overlay;
    this.map.addOverlay(overlay);

    let width = $(overlay.getElement()).width();
    overlay.setOffset([-width / 2, -40]);
  }

  willDestroyElement() {
    super.willDestroyElement(...arguments);
    let overlay = this.overlay;
    this.map.removeOverlay(overlay);
  }
}

import Component from '@ember/component';

import $ from 'jquery';
import ol from 'openlayers';

export default class PlaneLabelOverlay extends Component {
  tagName = '';

  map = null;
  flight = null;
  position = null;
  overlay = null;

  init() {
    super.init(...arguments);

    let badgeStyle = `display: inline-block; text-align: center; background: ${this.get('flight.color')}`;

    let id = this.getWithDefault('flight.competition_id', '') || this.getWithDefault('flight.registration', '');
    let badge = $(`<span class="badge plane_marker" style="${badgeStyle}">
      ${id}
    </span>`);

    this.set(
      'overlay',
      new ol.Overlay({
        element: badge.get(0),
      }),
    );
  }

  didReceiveAttrs() {
    super.didReceiveAttrs(...arguments);
    this.overlay.setPosition(this.position);
  }

  didInsertElement() {
    super.didInsertElement(...arguments);
    let overlay = this.overlay;
    this.map.addOverlay(overlay);

    let width = overlay.getElement().offsetWidth;
    overlay.setOffset([-width / 2, -40]);
  }

  willDestroyElement() {
    super.willDestroyElement(...arguments);
    let overlay = this.overlay;
    this.map.removeOverlay(overlay);
  }
}

Use `offsetWidth` instead of jQuery


"use strict";
const test = require('tape');
const randoPort = require('../../lib/randoport');
const _ = require('lodash');

test('empty object', t => {
  t.plan(1);

  const emptyObject = {};

  const r = randoPort(emptyObject);

  t.equal(typeof r.port, 'number');
});

test('object with other properties', t => {
  t.plan(4);

  const obj = {
    a: false,
    b: 'test',
    c: 99
  };

  const r = randoPort(obj);

  t.equal(obj.a, false);
  t.equal(obj.b, 'test');
  t.equal(obj.c, 99);
  t.equal(typeof r.port, 'number');
});

test('randoPort returns new object', t => {
  t.plan(1);
  const o = {};
  t.notEqual(o, randoPort(o));
});

test('object with existing port', t => {
  t.plan(5000);
  let i = 0;
  for(i; i < 5000; i +=1) {
    const o = {
      "port": _.random(4000, 4999)
    };

    t.notEqual(o.port, randoPort(o).port);
  }
});
// test('when 4200 is generated as port', t => {});

"use strict";
const test = require('tape');
const randoPort = require('../../lib/randoport');
const _ = require('lodash');

test('empty object', t => {
  t.plan(1);

  const emptyObject = {};

  const r = randoPort(emptyObject);

  t.equal(typeof r.port, 'number');
});

test('object with other properties', t => {
  t.plan(4);

  const obj = {
    a: false,
    b: 'test',
    c: 99
  };

  const r = randoPort(obj);

  t.equal(obj.a, false);
  t.equal(obj.b, 'test');
  t.equal(obj.c, 99);
  t.equal(typeof r.port, 'number');
});

test('randoPort returns new object', t => {
  t.plan(1);
  const o = {};
  t.notEqual(o, randoPort(o));
});

test('object with existing port or blacklisted port', t => {
  t.plan(10000);
  let i = 0;
  for(i; i < 5000; i +=1) {
    const o = {
      "port": _.random(4000, 4999)
    };

    const r = randoPort(o);
    t.notEqual(o.port, r.port);
    t.notEqual(4200, r.port);
  }
});
// test('when 4200 is generated as port', t => {});

Test against testting as default ember port.


/*
 * Copyright (c) 2011 Kevin Decker (http://www.incaseofstairs.com/)
 * See LICENSE for license information
 */
$(document).ready(function() {
    var canvas = document.getElementById("imageDisplay"),
        linePreview = document.getElementById("linePreview"),
        context = canvas.getContext("2d");

    var dragStart, dragEnd;
    $(canvas).mousedown(function(event) {
        dragStart = {x: event.offsetX, y: event.offsetY};
    }).mousemove(function(event) {
        if (dragStart) {
            dragEnd = {x: event.offsetX, y: event.offsetY};
            $("#lineInfo").text("dragStart: " + JSON.stringify(dragStart) + " dragEnd: " + JSON.stringify(dragEnd) + " deltaX: " + (dragEnd.x-dragStart.x) + " deltaY: "+ (dragEnd.y-dragStart.y) + " m: " + (dragEnd.y-dragStart.y)/(dragEnd.x-dragStart.x));

            var imageData = ImageDataUtils.getLinePixels(context, dragStart, dragEnd),
                stretcher = ImageDataUtils.createCanvasFromImageData(imageData);

            linePreview.src = stretcher.toDataURL();
        }
    }).mouseup(function(event) {
        dragStart = undefined;
    });
});

/*
 * Copyright (c) 2011 Kevin Decker (http://www.incaseofstairs.com/)
 * See LICENSE for license information
 */
$(document).ready(function() {
    var canvas = document.getElementById("imageDisplay"),
        linePreview = document.getElementById("linePreview"),
        context = canvas.getContext("2d");

    var dragStart, dragEnd, imageData;

    $(canvas).mousedown(function(event) {
        dragStart = {x: event.offsetX, y: event.offsetY};
    }).mousemove(function(event) {
        if (dragStart) {
            dragEnd = {x: event.offsetX, y: event.offsetY};

            // Output debug info. To be removed after the UI is worked out
            $("#lineInfo").text("dragStart: " + JSON.stringify(dragStart) + " dragEnd: " + JSON.stringify(dragEnd) + " deltaX: " + (dragEnd.x-dragStart.x) + " deltaY: "+ (dragEnd.y-dragStart.y) + " m: " + (dragEnd.y-dragStart.y)/(dragEnd.x-dragStart.x));

            // Collect the line data while the user is dragging
            imageData = ImageDataUtils.getLinePixels(context, dragStart, dragEnd);

            // Display the line preview
            var stretcher = ImageDataUtils.createCanvasFromImageData(imageData);
            linePreview.src = stretcher.toDataURL();
        }
    }).mouseup(function(event) {
        dragStart = undefined;
    });
});

Store the line data that is collected while dragging.


document.getElementById('generate').onclick = genPassword;

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function genPassword() {
    var passLength = document.getElementById('length').value;
    var pass="";
    for (i = 0; i < passLength; i++) {
        var a = [];

        if (document.getElementById('d').checked) {
            a.push([48, 57]);
        }

        if (document.getElementById('u').checked) {
            a.push([65, 90]);
        }

        if (document.getElementById('l').checked) {
            a.push([97, 122]);
        }

        if (document.getElementById('un').checked) {
            a.push([161, 9999]);
        }

        if (document.getElementById('s').checked) {
            a.push([33, 47]);
        }

        if (document.getElementById('sp').checked) {
            a.push([32, 32]);
        }

        var length = a.length;
        var type = getRandomInt(0, length - 1);
        console.log("a: "+(a[type][0]));
        var min = a[type][0];
        var max = a[type][1];
        var randUnicode = String.fromCharCode(getRandomInt(min, max));
        pass+=randUnicode;
    }
    document.getElementById('password').value = pass.toString();
}

document.getElementById('generate').onclick = genPassword;

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function genPassword() {
    var pass = "";
    for (i = 0; i < document.getElementById('length').value; i++) {
        var a = [];

        if (document.getElementById('d').checked) {
            a.push([48, 57]);
        }

        if (document.getElementById('u').checked) {
            a.push([65, 90]);
        }

        if (document.getElementById('l').checked) {
            a.push([97, 122]);
        }

        if (document.getElementById('un').checked) {
            a.push([161, 9999]);
        }

        if (document.getElementById('s').checked) {
            a.push([33, 47]);
        }

        if (document.getElementById('sp').checked) {
            a.push([32, 32]);
        }

        var type = getRandomInt(0, a.length - 1);
        var min = a[type][0];
        var max = a[type][1];
        var randUnicode = String.fromCharCode(getRandomInt(min, max));
        pass += randUnicode;
    }
    document.getElementById('password').value = pass.toString();
}

Remove console log and rework vars.


module.exports = [
	'\n\n\n',
	'/**',
	' * DEVELOPMENT MODE ONLY',
	' *',
	' * Browser-sync script loader',
	' * to enable script/style injection',
	' *',
	' * Run "gulp build" to generate the theme',
	' * for production before deploying!',
	' *',
	' */',
	'add_action( \'wp_head\', function () { ?>',
	'\t<script type="text/javascript" id="__bs_script__">//<![CDATA[',
	'\t\tdocument.write("<script async src=\'http://HOST:3000/browser-sync/browser-sync-client.js\'><\\/script>".replace("HOST", location.hostname));',
	'\t//]]></script>',
	'<?php }, 999);'
].join('\n');

var bsConfig = require('../config/browser-sync');
var port = bsConfig.port || 3000;

module.exports = [
	'\n\n\n',
	'/**',
	' * DEVELOPMENT MODE ONLY',
	' *',
	' * Browser-sync script loader',
	' * to enable script/style injection',
	' *',
	' * Run "gulp build" to generate the theme',
	' * for production before deploying!',
	' *',
	' */',
	'add_action( \'wp_head\', function () { ?>',
	'\t<script type="text/javascript" id="__bs_script__">//<![CDATA[',
	'\t\tdocument.write("<script async src=\'http://HOST:' + port + '/browser-sync/browser-sync-client.js\'><\\/script>".replace("HOST", location.hostname));',
	'\t//]]></script>',
	'<?php }, 999);'
].join('\n');

Allow bs snippet to read the port from config


var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        Hello, world! I am a CommentBox.
      </div>
    );
  }
});

React.render(
  <CommentBox />,
  document.getElementById('content')
);

/*
  CommentBox
 */

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

Comments

        <CommentList />
        <CommentForm />

    );
  }
});

/*
  CommentList
 */

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

        Hello, world! I am a CommentList.

    );
  }
});

/*
  CommentForm
 */

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

        Hello, world! I am a CommentForm.

    );
  }
});

/*
  Initialize the app
 */

React.render(
  <CommentBox />,
  document.getElementById('content')
);

Build CommentList and CommentForm components


'use babel';

let InteractiveConfigurationPanel = null;

export default {
  initialize() {
    if (InteractiveConfigurationPanel !== null) return ;

    InteractiveConfigurationPanel = require('./interactive-configuration-panel');
    this.configurationPanel = new InteractiveConfigurationPanel();
  },

  activate(state) { },

  provideUserSupportHelper() { return this; },

  deactivate() {
    if (this.configurationPanel !== null) {
      this.configurationPanel.destroy();
    }
  },

  serialize() {
    return { };
  },

  getInteractiveConfigurationPanel() {
    this.initialize();
    return this.configurationPanel;
  }
};

'use babel';

let InteractiveConfigurationPanel = null;
let RandomTipPanel = null;

export default {
  tips: [],

  initializeInteractiveConfiguration() {
    if (InteractiveConfigurationPanel !== null) return ;

    InteractiveConfigurationPanel = require('./interactive-configuration-panel');
    this.configurationPanel = new InteractiveConfigurationPanel();
  },
  initializeRandomTips() {
    if (RandomTipPanel !== null) return;
    RandomTipPanel = require('./random-tip-panel');
  },

  activate(state) { },

  provideUserSupportHelper() { return this; },

  deactivate() {
    if (this.configurationPanel !== null) {
      this.configurationPanel.destroy();
    }
    for (const tip of tips) tip.destory()
  },

  serialize() {
    return { };
  },

  getInteractiveConfigurationPanel() {
    this.initializeInteractiveConfiguration();
    return this.configurationPanel;
  },

  createRandomTipPanel(configurationPrefix, words) {
    this.initializeRandomTips();

    const panel = new RandomTipPanel(configurationPrefix, words);
    this.tips.push(panel)

    return panel;
  }
};

Enable other packages to use random tips feature


import React from "react"
import classes from "./HeaderBar.module.css"
import Logo from "../../assets/sGonksLogo.png"
import { Link, NavLink } from "react-router-dom"
import LinkButton from "../UI/LinkButton/LinkButton"
import LoginButtonSet from "../UI/LoginButtonSet/LoginButtonSet";

const HeaderBar = (props) => {
  let innerNavLinks = [
    { linkTo: "/", display: "My sGonks" },
    { linkTo: "/", display: "Competition" },
    { linkTo: "/", display: "Marketplace" },
  ]

  let innerNav = props.innerNav ? (

        {innerNavLinks.map((link) => {
          return (

              <NavLink to={link.linkTo} activeClassName={classes.active}>
                {link.display}
              </NavLink>

          )
        })}

  ) : null

  return (

      <Link to="/">

      </Link>

        {innerNav}

        <LoginButtonSet></LoginButtonSet>

  )
}

export default HeaderBar

import React from "react"
import classes from "./HeaderBar.module.css"
import Logo from "../../assets/sGonksLogo.png"
import { Link, NavLink } from "react-router-dom"
import LinkButton from "../UI/LinkButton/LinkButton"
import LoginButtonSet from "../UI/LoginButtonSet/LoginButtonSet";

const HeaderBar = (props) => {
  let innerNavLinks = [
    { linkTo: "/", display: "My sGonks", key: "mysgonks"},
    { linkTo: "/", display: "Competition", key: "competition" },
    { linkTo: "/", display: "Marketplace", key: "marketplace" },
  ]

  let innerNav = props.innerNav ? (

        {innerNavLinks.map((link) => {
          return (

              <NavLink to={link.linkTo} activeClassName={classes.active}>
                {link.display}
              </NavLink>

          )
        })}

  ) : null

  return (

      <Link to="/">

      </Link>

        {innerNav}

        <LoginButtonSet></LoginButtonSet>

  )
}

export default HeaderBar

Add key to list to optimise rendering


var $ = require('jquery');

module.exports = {

	$doc: $(document),

	setupDom: function () {

		this.$el = $(this.element);

		this.$el.addClass(this.options.classes.originalSelect);

		// Setup wrapper
		this.$wrapper = $('<div />', {
			'class': this.options.classes.wrapper
		});

		// Setup select
		this.$select = $('<button />', {
			'class': this.options.classes.select
		});

		// Setup option list
		this.$optionList = $('<ul />', {
			'class': [this.options.classes.optionList, this.options.classes.isHidden].join(' ')
		});

		this.renderOptions();
		this.renderSelect(this.getContent());
		this.setActiveOption(this.getValue(), true);

		// Go!
		this.$wrapper
			.insertBefore(this.$el)
			.append(this.$el, this.$select, this.$optionList);

	},

	destroyDom: function () {

		this.$el
			.removeClass(this.options.classes.originalSelect)
			.insertBefore(this.$wrapper);

		this.$wrapper.remove();

	}

};

var $ = require('jquery');

module.exports = {

	$doc: $(document),

	setupDom: function () {

		this.$el = $(this.element);

		this.$el.addClass(this.options.classes.originalSelect);

		// Setup wrapper
		this.$wrapper = $('<div />', {
			'class': this.options.classes.wrapper
		});

		// Setup select
		this.$select = $('<button />', {
			type: 'button',
			'class': this.options.classes.select
		});

		// Setup option list
		this.$optionList = $('<ul />', {
			'class': [this.options.classes.optionList, this.options.classes.isHidden].join(' ')
		});

		this.renderOptions();
		this.renderSelect(this.getContent());
		this.setActiveOption(this.getValue(), true);

		// Go!
		this.$wrapper
			.insertBefore(this.$el)
			.append(this.$el, this.$select, this.$optionList);

	},

	destroyDom: function () {

		this.$el
			.removeClass(this.options.classes.originalSelect)
			.insertBefore(this.$wrapper);

		this.$wrapper.remove();

	}

};

Add button type to select


define(['itemObjectModel'], function(itemOM) {

  'use strict';

  function open(item) {

    function onSubitem(subitem) {
      switch(subitem.dataset.resourceType) {
        case 'STR ':
          subitem.getDataObject()
            .then(function(names) {
              names = names.split(/;/g);
            });
            for (var i = 0; i < names.length; i++) {
              var namedItem = itemOM.createItem(names[i]);
              item.addItem(namedItem);
            }
          break;
        case 'CSND':
          break;
      }
    }

    var subitems = item.subitemsElement && item.subitemsElement.children;
    if (subitems) {
      for (var i = 0; i < subitems.length; i++) {
        onSubitem(subitems[i]);
      }
    }
    item.addEventListener(itemOM.EVT_ITEM_ADDED, function(e) {
      onSubitem(e.detail.item);
    });

    return Promise.resolve(item);
  }

  return open;

});

define(['itemObjectModel'], function(itemOM) {

  'use strict';

  function open(item) {

    function onSubitem(subitem) {
      switch(subitem.dataset.resourceType) {
        case 'STR ':
          subitem.getDataObject()
            .then(function(names) {
              names = names.split(/;/g);
              for (var i = 0; i < names.length; i++) {
                var namedItem = itemOM.createItem(names[i]);
                item.addItem(namedItem);
              }
            });
          break;
        case 'CSND':
          break;
      }
    }

    var subitems = item.subitemsElement && item.subitemsElement.children;
    if (subitems) {
      for (var i = 0; i < subitems.length; i++) {
        onSubitem(subitems[i]);
      }
    }
    item.addEventListener(itemOM.EVT_ITEM_ADDED, function(e) {
      onSubitem(e.detail.item);
    });

    return Promise.resolve(item);
  }

  return open;

});

Move named item loop to the right scope


// Module dependencies.
var express = require('express');
var router = express.Router();
var api = {};

// ALL
api.appointmentItems = function(req) {
  return req.store.recordCollection('AppointmentItem');
};

// GET
api.appointmentItem = function(req) {
  return req.store.recordItemById('AppointmentItem', req.params.id);
};

// POST
api.addAppointmentItem = function(req) {
  return req.store.createRecord('AppointmentItem', {
    beforeSave(model, save) {
      model.client = req.user;
      save();
    },
  });
};

// PUT
api.editAppointmentItem = function(req) {
  return req.store.updateRecord('AppointmentItem', req.params.id);
};

// DELETE
api.deleteAppointmentItem = function(req) {
  return req.store.destroyRecord('AppointmentItem', req.params.id);
};

router.get('/appointmentItems', api.appointmentItems);
router.post('/appointmentItems', api.addAppointmentItem);

router.route('/appointmentItems/:id')
  .get(api.appointmentItem)
  .put(api.editAppointmentItem)
  .delete(api.deleteAppointmentItem);

module.exports = router;

// Module dependencies.
var express = require('express');
var router = express.Router();
var api = {};

// ALL
api.appointmentItems = function(req) {
  return req.store.recordCollection('AppointmentItem', {include: ['service']});
};

// GET
api.appointmentItem = function(req) {
  return req.store.recordItemById('AppointmentItem', req.params.id, {include: ['service']});
};

// POST
api.addAppointmentItem = function(req) {
  return req.store.createRecord('AppointmentItem', {
    include: ['service'],
    beforeSave(model, save) {
      model.client = req.user;
      save();
    },
  });
};

// PUT
api.editAppointmentItem = function(req) {
  return req.store.updateRecord('AppointmentItem', req.params.id, {include: ['service']});
};

// DELETE
api.deleteAppointmentItem = function(req) {
  return req.store.destroyRecord('AppointmentItem', req.params.id);
};

router.get('/appointmentItems', api.appointmentItems);
router.post('/appointmentItems', api.addAppointmentItem);

router.route('/appointmentItems/:id')
  .get(api.appointmentItem)
  .put(api.editAppointmentItem)
  .delete(api.deleteAppointmentItem);

module.exports = router;

Load service w/ apt items


hqDefine('toggle_ui/js/flags', [
    'jquery',
    'knockout',
    'reports/js/config.dataTables.bootstrap',
    'hqwebapp/js/components.ko',    // select toggle widget
], function (
    $,
    ko,
    datatablesConfig
) {
    var dataTableElem = '.datatable';
    var viewModel = {
        tagFilter: ko.observable(null),
    };
    $.fn.dataTableExt.afnFiltering.push(
        function (oSettings, aData, iDataIndex) {
            if (viewModel.tagFilter() === 'all') {
                return true;
            }
            var tag = aData[0].replace(/\n/g," ").replace(/<.*?>/g, "");
            if (viewModel.tagFilter() === "Solutions" && tag.includes("Solutions")) {
                return true;
            }
            return tag === viewModel.tagFilter();
        }
    );
    $('#table-filters').koApplyBindings(viewModel);
    var table = datatablesConfig.HQReportDataTables({
        dataTableElem: dataTableElem,
        showAllRowsOption: true,
        includeFilter: true,
    });
    table.render();

    viewModel.tagFilter.subscribe(function (value) {
        table.datatable.fnDraw();
    });
});

hqDefine('toggle_ui/js/flags', [
    'jquery',
    'knockout',
    'reports/js/config.dataTables.bootstrap',
    'hqwebapp/js/components.ko',    // select toggle widget
], function (
    $,
    ko,
    datatablesConfig
) {
    var dataTableElem = '.datatable';
    var viewModel = {
        tagFilter: ko.observable(null),
    };
    $.fn.dataTableExt.afnFiltering.push(
        function (oSettings, aData, iDataIndex) {
            if (viewModel.tagFilter() === 'all') {
                return true;
            }
            var tag = aData[0].replace(/\s+/g," ").replace(/<.*?>/g, "").replace(/^\d+ /, "");
            if (viewModel.tagFilter() === "Solutions" && tag.includes("Solutions")) {
                return true;
            }
            return tag === viewModel.tagFilter();
        }
    );
    $('#table-filters').koApplyBindings(viewModel);
    var table = datatablesConfig.HQReportDataTables({
        dataTableElem: dataTableElem,
        showAllRowsOption: true,
        includeFilter: true,
    });
    table.render();

    viewModel.tagFilter.subscribe(function (value) {
        table.datatable.fnDraw();
    });
});

Fix UI filters on Feature Flags page


exports.init = init;

function init(genericAWSClient) {
  return createSimpleQueueServiceClient;

  function createSimpleQueueServiceClient(accessKeyId, secretAccessKey, options) {
    options = options || {};
    var client = genericAWSClient({
      host: options.host || "sqs.us-east-1.amazonaws.com",
      path: options.path || "/",
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
      secure: options.secure,
      agent: options.agent,
      token: options.token
    });

    return {
      client: aws,
      call: call
    };

    function call(action, query, callback) {
      query["Action"] = action
      query["Version"] = options.version || '2011-10-01'
      query["SignatureMethod"] = "HmacSHA256"
      query["SignatureVersion"] = "2"
      return client.call(action, query, callback);
    }
  }
}

exports.init = init;

function init(genericAWSClient) {
  return createSimpleQueueServiceClient;

  function createSimpleQueueServiceClient(accessKeyId, secretAccessKey, options) {
    options = options || {};
    var client = genericAWSClient({
      host: options.host || "sqs.us-east-1.amazonaws.com",
      path: options.path || "/",
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
      secure: options.secure,
      agent: options.agent,
      token: options.token
    });

    return {
      client: client,
      call: call
    };

    function call(action, query, callback) {
      query["Action"] = action
      query["Version"] = options.version || '2011-10-01'
      query["SignatureMethod"] = "HmacSHA256"
      query["SignatureVersion"] = "2"
      return client.call(action, query, callback);
    }
  }
}

Fix error with undeclared variable


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

module.exports = function(/* environment, appConfig */) {
  return {
    name: "<%= name %>",
    short_name: "<%= name %>",
    description: "",
    start_url: "/",
    display: "standalone",
    background_color: "#fff",
    theme_color: "#fff",
    icons: [
    ]
  };
}

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

module.exports = function(/* environment, appConfig */) {
  // See https://github.com/san650/ember-web-app#documentation for a list of
  // supported properties

  return {
    name: "<%= name %>",
    short_name: "<%= name %>",
    description: "",
    start_url: "/",
    display: "standalone",
    background_color: "#fff",
    theme_color: "#fff",
    icons: [
    ]
  };
}

Add link to documentation in blueprint


'use strict';

let cli     = require('heroku-cli-util');
let request = require('request');

module.exports = {
  topic: 'builds',
  command: 'output',
  needsAuth: true,
  needsApp: true,
  description: 'show build output',
  help: 'Show build output for a Heroku app',
  variableArgs: true,
  run: cli.command(showOutput)
};

function showOutput(context, heroku) {
  let id = context.args[0];

  if (!id) {
    cli.error("Usage: heroku builds:output <id> -a <app>")
    return
  }

  return heroku.request({
    path: `/apps/${context.app}/builds/${id}`,
    method: 'GET',
    headers: {
      'Accept': 'application/vnd.heroku+json; version=3.streaming-build-output'
    },
    parseJSON: true
  }).then(function (build) {
    request(build.output_stream_url).pipe(process.stdout);
  });
}

'use strict';

let cli     = require('heroku-cli-util');
let request = require('request');

module.exports = {
  topic: 'builds',
  command: 'output',
  needsAuth: true,
  needsApp: true,
  description: 'show build output',
  help: 'Show build output for a Heroku app',
  args: [
    {
      name: 'id',
      optional: false,
      hidden: false
    }
  ],
  run: cli.command(showOutput)
};

function showOutput(context, heroku) {
  let id = context.args.id;

  return heroku.request({
    path: `/apps/${context.app}/builds/${id}`,
    method: 'GET',
    headers: {
      'Accept': 'application/vnd.heroku+json; version=3.streaming-build-output'
    },
    parseJSON: true
  }).then(function (build) {
    request(build.output_stream_url).pipe(process.stdout);
  });
}

Leverage command line validation via @dickeyxxx


import mix from '../src/index';
import test from 'ava';

test('that it can merge config', t => {
    Config.merge({
        versioning: true,
        foo: 'bar'
    });

    t.is('bar', Config.foo);
    t.true(Config.versioning);
});

test('that it intelligently builds the Babel config', t => {
    // Given the user has a custom .babelrc file...
    new File('.babelrc').write({
        'plugins': ['transform-object-rest-spread']
    });

    // And we construct the Babel config for Mix...
    let options = Config.babel();

    // Then it should smartly merge the user's .babelrc with Mix's.
    t.deepEqual(['transform-object-rest-spread'], options.plugins)
    t.is('env', options.presets[0][0]);

    // Clean up.
    File.find('.babelrc').delete();
});

import mix from '../src/index';
import test from 'ava';

test('that it can merge config', t => {
    Config.merge({
        versioning: true,
        foo: 'bar'
    });

    t.is('bar', Config.foo);
    t.true(Config.versioning);
});

test('that it intelligently builds the Babel config', t => {
    // Given the user has a custom .babelrc file...
    new File('.babelrc').write({
        'plugins': ['arbitrary-plugin']
    });

    // And we construct the Babel config for Mix...
    let options = Config.babel();

    // Then it should smartly merge the user's .babelrc with Mix's.
    t.deepEqual(['transform-object-rest-spread', 'arbitrary-plugin'], options.plugins)
    t.is('env', options.presets[0][0]);

    // Clean up.
    File.find('.babelrc').delete();
});

Fix failing test by adapting it to new conditions


import babel from 'rollup-plugin-babel'
import resolve from '@rollup/plugin-node-resolve'
import json from '@rollup/plugin-json'
import autoExternal from 'rollup-plugin-auto-external'
import pkg from './package.json'
import fs from 'fs'
import gitrev from 'git-rev-promises'

Promise.all([
  gitrev.long(),
  gitrev.branch(),
  gitrev.tags(),
  gitrev.date()
]).then(([hash, branch, tags, date]) => {
  const buildJson = JSON.stringify({
    version: pkg.version,
    hash,
    branch,
    tags,
    date
  })
  fs.writeFile('build.json', buildJson, 'utf8', () => {
    console.info('Build information saved')
  })
})

const config = {
  input: 'src/index.mjs',
  output: {
    dir: 'dist',
    format: 'esm',
    entryFileNames: '[name].mjs',
    sourcemap: true
  },
  preserveModules: true,
  plugins: [autoExternal(), json(), resolve(), babel({ externalHelpers: true })]
}

export default config

/* eslint-disable no-console */
import json from '@rollup/plugin-json'
import resolve from '@rollup/plugin-node-resolve'
import fs from 'fs'
import gitrev from 'git-rev-promises'
import autoExternal from 'rollup-plugin-auto-external'
import babel from 'rollup-plugin-babel'
import pkg from './package.json'

Promise.all([
  gitrev.long(),
  gitrev.branch(),
  gitrev.tags(),
  gitrev.date(),
]).then(([hash, branch, tags, date]) => {
  const buildJson = JSON.stringify({
    version: pkg.version,
    hash,
    branch,
    tags,
    date,
  })
  fs.writeFile('build.json', buildJson, 'utf8', () => {
    console.info('Build information saved')
  })
})

const config = {
  input: 'src/index.mjs',
  output: {
    dir: 'dist',
    format: 'esm',
    entryFileNames: '[name].mjs',
    sourcemap: true,
  },
  external: ['nanoid/async'],
  preserveModules: true,
  plugins: [autoExternal(), json(), resolve(), babel({ externalHelpers: true })],
}

export default config

Fix issue caused by Rollup.js having a nervous breakdown if an import from a dependency includes a forward flash.


Package.describe({
  name: 'extremeandy:es6-promisify',
  version: '0.1.2',
  summary: 'es6-promisify for Meteor',
  git: 'https://github.com/extremeandy/meteor-es6-promisify',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.2.0.1');
  api.use('ecmascript');
  api.use('cosmos:browserify@0.7.0', 'client');

  api.addFiles('promise-override.js');
  api.addFiles('es6-promisify_server.js', 'server');
  //.browserify.js extension is required due to a meteor bug to get it to handle properly. This will be fixed shortly.
  api.addFiles('es6-promisify_client.browserify.js', 'client');

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

Npm.depends({
  'es6-promisify': '3.0.0'
});

Package.describe({
  name: 'extremeandy:es6-promisify',
  version: '0.1.3',
  summary: 'es6-promisify for Meteor',
  git: 'https://github.com/extremeandy/meteor-es6-promisify',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.2.0.1');
  api.use('ecmascript');
  api.use('cosmos:browserify@0.7.0', 'client');

  api.addFiles('promise-override.js');
  api.addFiles('es6-promisify_server.js', 'server');
  //.browserify.js extension is required due to a meteor bug to get it to handle properly. This will be fixed shortly.
  api.addFiles('es6-promisify_client.browserify.js', 'client');

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

Npm.depends({
  'es6-promisify': '3.0.0'
});

Change version number to 0.1.3


describe('String#trim', function () {

    it('should be defined', function () {
        assert('Some text').should(respondTo, 'trim');
    });

    it('should remove spaces at end and start of string', function () {

        var strings = ['Some text', '  Some text', 'Some text  ', '  Some text  '];

        for (var i = 0; i < strings.length; i += 1) {
            assert(strings[0].trim()).should(eql, 'Some text');
        }
    });
});

describe('String.prototype.trim ( )', function () {

    it('should be defined', function () {
        assert('Some text').should(respondTo, 'trim');
    });

    it('should remove whitespace from both ends of the string', function () {

        var strings = ['Some text', '  Some text', 'Some text  ', '  Some text  '];

        for (var i = 0; i < strings.length; i += 1) {
            assert(strings[0].trim()).should(eql, 'Some text');
        }
    });
});

Fix describtion of String.prototype.trim examples


import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import pug from 'rollup-plugin-pug';
import babel from 'rollup-plugin-babel';

export default {
  entry: 'src/index.js',
  format: 'umd',
  moduleName: 'XpClient',
  plugins: [resolve(), commonjs(), pug(), babel({
    exclude: 'node_modules/**' // only transpile our source code
  })],
  external: ['unfetch', 'javascript-state-machine'],
  dest: 'lib/xpclient.js',
  sourceMap: true,
  globals: {
    'javascript-state-machine': 'StateMachine',
    unfetch: 'unfetch'
  }
};

import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import pug from 'rollup-plugin-pug';
import babel from 'rollup-plugin-babel';

export default {
  entry: 'src/index.js',
  format: 'umd',
  moduleName: 'XpClient',
  plugins: [resolve(), commonjs(), pug(), babel({
    exclude: 'node_modules/**' // only transpile our source code
  })],
  external: ['unfetch', 'javascript-state-machine', 'spin'],
  dest: 'lib/xpclient.js',
  sourceMap: true,
  globals: {
    'javascript-state-machine': 'StateMachine',
    unfetch: 'unfetch',
    spin: 'Spinner'
  }
};

Add spin to rollup's externals.


import React, { Component } from 'react';

class Letters extends Component {

  constructor(props) {
    super(props);
    this.state = { type: props.statement };
  }

  render() {
    return (

        <button className="entry row1 col1">a</button>
        <button className="entry row1 col2">b</button>
        <button className="entry row1 col3">c</button>
        <button className="entry row1 col4">d</button>
        <button className="entry row1 col5">e</button>
        <button className="entry row1 col6">f</button>

        <button className="entry row2 col1">g</button>
        <button className="entry row2 col2">h</button>
        <button className="entry row2 col3">i</button>
        <button className="entry row2 col4">j</button>
        <button className="entry row2 col5">k</button>
        <button className="entry row2 col6">l</button>

        <button className="entry row3 col1">m</button>
        <button className="entry row3 col2">n</button>
        <button className="entry row3 col3">o</button>
        <button className="entry row3 col4">p</button>
        <button className="entry row3 col5">q</button>
        <button className="entry row3 col6">r</button>

        <button className="entry row4 col1">s</button>
        <button className="entry row4 col2">t</button>
        <button className="entry row4 col3">u</button>
        <button className="entry row4 col4">v</button>
        <button className="entry row4 col5">w</button>
        <button className="entry row4 col6">x</button>

        <button className="entry-extra-wide row5 col1">____</button>
        <button className="entry row5 col3">y</button>
        <button className="entry row5 col4">z</button>
        <button className="entry-extra-wide row5 col6">↑</button>

    )
  }
}

export default Letters;

import React, { Component } from 'react';

class Letters extends Component {

  constructor(props) {
    super(props);
    this.state = { type: props.statement };
    this.createButtons = this.createButtons.bind(this);
  }

  createButtons() {
    let buttons = [];
    for (let i = 0; i < 26; i++) {
      var rowNum = i / 6 + 1;
      var colNum = i % 6 + 1;
      var letter = (i + 10).toString(36);
      var className = "entry row" + rowNum + " col" + colNum;
      buttons.push(<button className={className}>{letter}</button>);
      if (i !== 0 && (i+1) % 6 === 0) {
        buttons.push(
)
      }
    }
    return buttons;
  }

  render() {
    return (

        {this.createButtons()}

    )
  }
}

export default Letters;

Refactor creating buttons for letters


// These should be easily identifiable and both visually and conceptually unique
// Don't include symbols that could be misconstrued to have some actual meaning
// (like a warning sign)
const emojis =
  '🦊🐸🦉🦄🐙🐳🌵🍀🍁🍄🌍⭐🔥🌈🍎🥯🌽🥞🥨🍕🌮🍦🎂🍿🏈🛼🏆🎧🎺🎲🚚✈🚀⛵⛺📻💰💎🧲🔭🪣🧸';

module.exports = {
  emoji(executionId) {
    // Create a simple hash of the execution ID
    const hash = [...executionId].reduce(
      (prev, curr) => prev + curr.charCodeAt(0),
      0,
    );

    // Return an emoji based on the hash. This is hopefully significantly
    // random that deploys near to each other in time don't get the same
    // symbol
    return [...emojis][hash % [...emojis].length];
  },
};

// These should be easily identifiable and both visually and conceptually unique
// Don't include symbols that could be misconstrued to have some actual meaning
// (like a warning sign)

const emojis = [
  '🦊',
  '🐸',
  '🦉',
  '🦄',
  '🐙',
  '🐳',
  '🌵',
  '🍀',
  '🍁',
  '🍄',
  '🌍',
  '⭐',
  '🔥',
  '🌈',
  '🍎',
  '🥯',
  '🌽',
  '🥞',
  '🥨',
  '🍕',
  '🌮',
  '🍦',
  '🎂',
  '🍿',
  '🏈',
  '🛼',
  '🏆',
  '🎧',
  '🎺',
  '🎲',
  '🚚',
  '✈',
  '🚀',
  '⛵',
  '⛺',
  '📻',
  '💰',
  '💎',
  '🧲',
  '🔭',
  '🪣',
  '🧸',
];

module.exports = {
  emoji(executionId) {
    // Create a simple hash of the execution ID
    const hash = [...executionId].reduce(
      (prev, curr) => prev + curr.charCodeAt(0),
      0,
    );

    // Return an emoji based on the hash. This is hopefully significantly
    // random that deploys near to each other in time don't get the same
    // symbol
    return emojis[hash % emojis.length];
  },
};

Fix emoji byte length issue


/* Stageprompt 2.0.1
*  =================
*
*  What is it?       Stageprompt is code for wiring up an analytics service to a user journey
*  How does it work? The user journey is described by adding data-journey attributes to tags in the html
*                    the attribute values are then parsed and use to fire a callback sending data to
*                    Google Analytics or some other provider.
*  Why use it?       Seperates the concerns of instumenting your user journey and sending that data
*                    to your analytics provider.
*
*  SEE HERE FOR MORE INFORMATION - https://github.com/alphagov/stageprompt
*/

var GOVUK = GOVUK || {};

GOVUK.performance = GOVUK.performance || {};

GOVUK.performance.stageprompt = (function () {

  var setup, setupForGoogleAnalytics, splitAction;

  splitAction = function (action) {
    var parts = action.split(':');
    if (parts.length <= 3) return parts;
    return [parts.shift(), parts.shift(), parts.join(':')];
  };

  setup = function (analyticsCallback) {
    var journeyStage = $('[data-journey]').attr('data-journey'),
        journeyHelpers = $('[data-journey-click]');

    if (journeyStage) {
      analyticsCallback.apply(null, splitAction(journeyStage));
    }

    journeyHelpers.on('click', function (event) {
      analyticsCallback.apply(null, splitAction($(this).data('journey-click')));
    });
  };

  setupForGoogleAnalytics = function () {
    setup(GOVUK.performance.sendGoogleAnalyticsEvent);
  };

  return {
    setup: setup,
    setupForGoogleAnalytics: setupForGoogleAnalytics
  };
}());

GOVUK.performance.sendGoogleAnalyticsEvent = function (category, event, label) {
  _gaq.push(['_trackEvent', category, event, label, undefined, true]);
};

Add documentation comment at top of file


function setAriaCurrent() {
	var title = document.title;
	if title== "Comunidad hispanohablante de NVDA " {
		var id = "inicio"
	}
	document.getElementById(id).removeAttribute("accesskey");
	document.getElementById(id).setAttribute("aria-current", "page");
}

setAriaCurrent();

function setAriaCurrent() {
	var title = document.title.trim();
	if title === "Comunidad hispanohablante de NVDA") {
		document.getElementById("inicio".setAttribute("aria-current", "page");
	} else if title.includes("- Ayuda y descarga") {
		document.getElementById("ayuda").setAttribute("aria-current", "page")
	}
}

setAriaCurrent();

Use trim method in javascript


/**
 * @license
 * js-sorting <http://github.com/Tyriar/js-sorting>
 * Copyright 2014-2015 Daniel Imms <http://www.growingwiththeweb.com>
 * Released under the MIT license <http://github.com/Tyriar/js-sorting/blob/master/LICENSE>
 */
'use strict';

var defaultCompare = require('./common/default-compare');
var defaultSwap = require('./common/default-swap');

function sort(array, compare, swap) {
  var i;
  var sorted = false;

  while(!sorted) {
    sorted = true;
    for (i = 1; i < array.length - 1; i += 2) {
      if (compare(array[i], array[i + 1]) > 0) {
        swap(array, i, i + 1);
        sorted = false;
      }
    }

    for (i = 0; i < array.length - 1; i += 2) {
      if (compare(array[i], array[i + 1]) > 0) {
        swap(array, i, i + 1);
        sorted = false;
      }
    }
  }

  return array;
}

module.exports = function (array, customCompare, customSwap) {
  var compare = customCompare || defaultCompare;
  var swap = customSwap || defaultSwap;
  return sort(array, compare, swap);
};

/**
 * @license
 * js-sorting <http://github.com/Tyriar/js-sorting>
 * Copyright 2014-2015 Daniel Imms <http://www.growingwiththeweb.com>
 * Released under the MIT license <http://github.com/Tyriar/js-sorting/blob/master/LICENSE>
 */
'use strict';

var defaultCompare = require('./common/default-compare');
var defaultSwap = require('./common/default-swap');

function innerSort(array, i, compare, swap) {
  var sorted = true;
  for (; i < array.length - 1; i += 2) {
    if (compare(array[i], array[i + 1]) > 0) {
      swap(array, i, i + 1);
      sorted = false;
    }
  }
  return sorted;
}

function sort(array, compare, swap) {
  var sorted = false;
  while (!sorted) {
    sorted = innerSort(array, 1, compare, swap);
    sorted = sorted && innerSort(array, 0, compare, swap);
  }
  return array;
}

module.exports = function (array, customCompare, customSwap) {
  var compare = customCompare || defaultCompare;
  var swap = customSwap || defaultSwap;
  return sort(array, compare, swap);
};

Refactor odd even sort to reduce duplication


var uglify;
try {
  uglify = require('uglify-js');
} catch (err) { /* NOP */ }

process.on('message', function(msg) {
  var outputFile = msg.output,
      data = msg.data,
      sourceMap = msg.sourceMap,
      warnings = [];

  try {
    var warnFunction;
    try {
      warnFunction = uglify.AST_Node.warn_function;
      uglify.AST_Node.warn_function = function(msg) {
        warnings.push(msg);
      };
      var ast = uglify.parse(data),
          compressor = uglify.Compressor(),
          outputOptions = {};

      if (sourceMap) {
        sourceMap = uglify.SourceMap({
          file: outputFile,
          orig: sourceMap
        });
        outputOptions.source_map = sourceMap;
      }

      ast.figure_out_scope();
      ast = ast.transform(compressor);
      ast.figure_out_scope();
      ast.compute_char_frequency();
      ast.mangle_names();
      data = ast.print_to_string(outputOptions);

      process.send({
        data: {
          data: data,
          warnings: warnings,
          sourceMap: sourceMap && sourceMap.toString()
        }
      });
    } finally {
      uglify.AST_Node.warn_function = warnFunction;
    }
  } catch (err) {
    process.send({err: err.toString()});
  }
});

var uglify;
try {
  uglify = require('uglify-js');
} catch (err) { /* NOP */ }

process.on('message', function(msg) {
  var outputFile = msg.output,
      data = msg.data,
      sourceMap = msg.sourceMap,
      warnings = [];

  try {
    var warnFunction;
    try {
      warnFunction = uglify.AST_Node.warn_function;
      uglify.AST_Node.warn_function = function(msg) {
        warnings.push(msg);
      };
      var ast = uglify.parse(data),
          compressor = uglify.Compressor(),
          outputOptions = {};

      if (sourceMap) {
        sourceMap = uglify.SourceMap({
          file: outputFile,
          orig: sourceMap
        });
        outputOptions.source_map = sourceMap;
      }

      ast.figure_out_scope();
      ast = ast.transform(compressor);
      ast.figure_out_scope();
      ast.compute_char_frequency();
      ast.mangle_names();
      data = ast.print_to_string(outputOptions);

      process.send({
        data: {
          data: data,
          warnings: warnings,
          sourceMap: sourceMap && sourceMap.toString()
        }
      });
    } finally {
      uglify.AST_Node.warn_function = warnFunction;
    }
  } catch (err) {
    process.send({err: err.stack || err.msg || err.toString()});
  }
});

Improve error logging in uglify worker


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

var React = require('react'),
    App = require('./App');

React.renderComponent(<App />, document.body);

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

var React = require('react'),
    App = require('./App');

if ('production' !== process.env.NODE_ENV) {
  // Enable React devtools
  window['React'] = React;
}

React.renderComponent(<App />, document.body);

Add code to enable React devtools


const config = {
  nodeEnv: process.env.NODE_ENV,
  webConcurrency: process.env.WEB_CONCURRENCY || 1,
  port: process.env.PORT || 1337,
  apiUrl: process.env.API_URL || 'http://localhost:3000',
  authPort: process.env.AUTH_PORT || 3005,
  appDomain: `app.${process.env.APP_DOMAIN}` || 'localhost',
  timeout: 60000
}

module.exports = config

const config = {
  nodeEnv: process.env.NODE_ENV,
  webConcurrency: process.env.WEB_CONCURRENCY || 1,
  port: process.env.PORT || 1337,
  apiUrl: process.env.API_URL || 'http://localhost:3000',
  authPort: process.env.AUTH_PORT || 3005,
  appDomain: process.env.APP_DOMAIN || 'localhost',
  timeout: 60000
}

module.exports = config

Fix app_domain env for build app


Package.describe({
  summary: "A meteorite package that makes building dynamic two way forms easy",
  version: '0.2.1',
  name: "joshowens:simple-form",
  git: 'https://github.com/MeteorClub/simple-form'
});

Package.onUse(function(api) {
  api.versionsFrom("METEOR@0.9.0");
  api.use(['ui', 'templating', 'underscore', 'mrt:underscore-string-latest@2.3.3'], 'client');
  api.addFiles(['fileField.html', 'fileField.js', 'helper.js', 'simpleform.js'], 'client');
  api.export('SimpleForm', 'client');
});

Package.describe({
  summary: "A meteorite package that makes building dynamic two way forms easy",
  version: '0.2.2',
  name: "joshowens:simple-form",
  git: 'https://github.com/MeteorClub/simple-form'
});

Package.onUse(function(api) {
  api.versionsFrom("METEOR@0.9.0");
  api.use(['ui', 'templating', 'underscore', 'schnie:uploader', 'mrt:underscore-string-latest@2.3.3'], 'client');
  api.addFiles(['fileField.html', 'fileField.js', 'helper.js', 'simpleform.js'], 'client');
  api.export('SimpleForm', 'client');
});

Add uploaded as a dep.


export default ({ compiled: { html }, file }) =>
  new Promise(resolve => {
    // eslint-disable-next-line
    console.log(`<!-- FILE: ${file} -->\n${html}`)
    resolve()
  })

export default ({ compiled: { html }, file }) =>
  new Promise(resolve => {
    process.stdout.write(`<!-- FILE: ${file} -->\n${html}\n`, resolve);
  })

Use `process.stdout.write()` instead of `console.log()` so output is flushed.


import { BaseHandler, METHODS } from '@r/platform/router';
import * as subredditActions from 'app/actions/subreddits';

export default class ToggleSubredditSubscription extends BaseHandler {
  async [METHODS.POST](dispatch) {
    dispatch(subredditActions.toggleSubscription(this.bodyParams));
  }
}

import { BaseHandler, METHODS } from '@r/platform/router';
import * as subscriptionActions from 'app/actions/subscribedSubreddits';

export default class ToggleSubredditSubscription extends BaseHandler {
  async [METHODS.POST](dispatch) {
    dispatch(subscriptionActions.toggleSubscription(this.bodyParams));
  }
}

Fix subscription handler from refactor


Artist = (function(){

  // Artist constructor.  Given an object describing an artist, copy
  // all of its local properties into `this'.
  var Artist = function(record) {
  };

  Artist.prototype = {
    // Create a new record if the `id' property is `undefined',
    // otherwise update an existing record.  Return a promise from the
    // Ajax library.
    save: function() {
    },

    // Optional: Write a `destroy' method that deletes the artist.
    // Return a promise.
    destroy: function() {
    },
  };

  // Should fetch a single artist via Ajax.  Return a promise that
  // resolves to an instance of the Artist function.
  var fetchOne = function(id) {
  };

  // Should fetch all artists via Ajax.  Return a promise that
  // resolves to an array of Artist objects.
  };

  // Public interface:
  return {
    fetchOne: fetchOne,

    fetchAll: fetchAll,

    create: function(record) {
      return new Artist(record);
    },
  };
})();

Artist = (function(){

  // Artist constructor.  Given an object describing an artist, copy
  // all of its local properties into `this'.
  var Artist = function(record) {
  };

  Artist.prototype = {
    // Create a new record if the `id' property is `undefined',
    // otherwise update an existing record.  Return a promise from the
    // Ajax library.
    save: function() {
    },

    // Optional: Write a `destroy' method that deletes the artist.
    // Return a promise.
    destroy: function() {
    },
  };

  // Should fetch a single artist via Ajax.  Return a promise that
  // resolves to an instance of the Artist function.
  var fetchOne = function(id) {
  };

  // Should fetch all artists via Ajax.  Return a promise that
  // resolves to an array of Artist objects.
  var fetchAll = function() {
  };

  // Public interface:
  return {
    fetchOne: fetchOne,

    fetchAll: fetchAll,

    create: function(record) {
      return new Artist(record);
    },
  };
})();

Fix code after a bad rebase/merge


import test from "ava";

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

const localesPath = path.join(__dirname, "..", "locales");
fs.readdir(localesPath, (err, filenames) => {
  if (err) throw err;

  const filenameRegex = /^[a-z][a-z](_[A-Z]+)*\.json$/;
  filenames.forEach(filename => {
    test(`locale filename "${filename}" is valid`, t => {
      t.true(filenameRegex.test(filename));
    });
  });

  filenames.forEach(filename => {
    const content = fs.readFileSync(path.join(localesPath, filename), { encoding: "utf8" });
    let valid = false;
    try {
      JSON.parse(content);
      valid = true;
    } catch (e) {}
    test(`locale file "${filename}" is valid`, t => t.true(valid));
  });
});

import test from "ava";

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

const localesPath = path.join(__dirname, "..", "locales");
const filenameRegex = /^[a-z][a-z](_[A-Z]+)*\.json$/;

test("locale files are valid", t => {
  const filenames = fs.readdirSync(localesPath);
  filenames.forEach(filename => {
    /* test filename */
    if (!filenameRegex.test(filename)) t.fail(`locale filename "${filename}" is invalid`);

    /* test file json */
    const content = fs.readFileSync(path.join(localesPath, filename), { encoding: "utf8" });
    let valid = false;
    try {
      JSON.parse(content);
      valid = true;
    } catch (e) {}
    if (!valid) t.fail(`locale file "${filename}" is invalid`);
  });

  t.pass(); // if nothing has failed so far
});

Make locale tests into one test


/*
 * Footer Messages
 *
 * This contains all the text for the Footer component.
 */
import { defineMessages } from 'react-intl';

export default defineMessages({
  header: {
    id: 'app.components.Footer.header',
    defaultMessage: 'This is the Footer component !',
  },
});

/*
 * Footer Messages
 *
 * This contains all the text for the Footer component.
 */
import { defineMessages } from 'react-intl';

export default defineMessages({
  header: {
    id: 'app.components.Footer.header',
    defaultMessage: 'made by Tim Arioto',
  },
});

Add name to footer message


"use strict";

var Router = require("../router");

exports.testValidAcceptHeader = function (test) {
	var results = Router.prototype._parseAcceptHeader("application/json");
	test.deepEqual([{
		"type": "application",
		"subtype": "json",
		"params": {}
	}], results);
	test.done();
};

exports.testValidAcceptHeaderWithParams = function (test) {
	var results = Router.prototype._parseAcceptHeader("application/json;q=3;v=1");
	test.deepEqual([{
		"type": "application",
		"subtype": "json",
		"params": {
			"q": "3",
			"v": "1"
		}
	}], results);
	test.done();
};

Add test for media type w/ params


/**
 * Simple component to display a font icon (i.e. glyphicon or fontawesome)
 */
const React = require('react');
const classNames = require('classnames');

class Icon extends React.Component {
  render() {
    const cx = classNames({
      [this.props.type]: true,
      [`${this.props.type}-${this.props.name}`]: true
    });

    return <i className={cx} />;
  }
}

Icon.propTypes = {
  type: React.PropTypes.oneOf(['fa', 'glyphicon']),
  name: React.PropTypes.string.isRequired
};

Icon.defaultProps = {
  type: 'fa'
};

module.exports = Icon;

/**
 * Simple component to display a font icon (i.e. glyphicon or fontawesome)
 */
const React = require('react');
const classNames = require('classnames');

class Icon extends React.Component {
  render() {
    const cx = classNames(
      this.props.type,
      `${this.props.type}-${this.props.name}`
    );

    return <i className={cx} />;
  }
}

Icon.propTypes = {
  type: React.PropTypes.oneOf(['fa', 'glyphicon']),
  name: React.PropTypes.string.isRequired
};

Icon.defaultProps = {
  type: 'fa'
};

module.exports = Icon;

Fix computed properties were causing some issues with duplicated data props


import { createTransform } from 'redux-persist';
import CryptoJSCore from 'crypto-js/core';
import AES from 'crypto-js/aes';
import { makeEncryptor, makeDecryptor } from './helpers';

const makeSyncEncryptor = secretKey =>
  makeEncryptor(state => AES.encrypt(state, secretKey).toString());

const makeSyncDecryptor = (secretKey, onError) =>
  makeDecryptor(state => {
    const bytes = AES.decrypt(state, secretKey);
    const decryptedString = bytes.toString(CryptoJSCore.enc.Utf8);
    if (!decryptedString) {
      throw new Error(
        'Could not decrypt state. Please verify that you are using the correct secret key.'
      );
    }
    return JSON.parse(decryptedString);
}, onError);

export default config => {
  const inbound = makeSyncEncryptor(config.secretKey);
  const outbound = makeSyncDecryptor(config.secretKey, config.onError);
  return createTransform(inbound, outbound, config);
};

import { createTransform } from 'redux-persist';
import CryptoJSCore from 'crypto-js/core';
import AES from 'crypto-js/aes';
import { makeEncryptor, makeDecryptor } from './helpers';

const makeSyncEncryptor = secretKey =>
  makeEncryptor(state => AES.encrypt(state, secretKey).toString());

const makeSyncDecryptor = (secretKey, onError) =>
  makeDecryptor(state => {
    try {
      const bytes = AES.decrypt(state, secretKey);
      const decryptedString = bytes.toString(CryptoJSCore.enc.Utf8);
      return JSON.parse(decryptedString);
    } catch (err) {
      throw new Error(
        'Could not decrypt state. Please verify that you are using the correct secret key.'
      );
    }
  }, onError);

export default config => {
  const inbound = makeSyncEncryptor(config.secretKey);
  const outbound = makeSyncDecryptor(config.secretKey, config.onError);
  return createTransform(inbound, outbound, config);
};

Use try/catch to handle cases where the decryption could fail


export default function serviceColor(serviceTypeId) {
  const Colors = {
    purple: '#e8e9fc',
    orange: '#ffe7d6',
    green: '#e8fce8',
    gray: '#eee'
  };

  const map = {
    507: Colors.orange,
    502: Colors.green,
    503: Colors.green,
    504: Colors.green,
    505: Colors.gray,
    506: Colors.gray,
    508: Colors.purple
  };

  return map[serviceTypeId] || null;
};

Remove extra semicolon to appease the linter


export const resourceBreadcrumbs = {
  template: '<breadcrumbs items="$ctrl.items" active-item="$ctrl.activeItem"/>',
  bindings: {
    resource: '<'
  },
  controller: class ResourceBreadcrumbsController {
    constructor(ResourceBreadcrumbsService) {
      this.ResourceBreadcrumbsService = ResourceBreadcrumbsService;
    }

    $onInit() {
      this.items = [
        {
          label: 'Project dashboard',
          state: 'project.details',
          params: {
            uuid: this.resource.project_uuid
          }
        },
        ...this.ResourceBreadcrumbsService.get(this.resource),
      ];

      this.activeItem = this.resource.name;
    }
  }
};

export const resourceBreadcrumbs = {
  template: '<breadcrumbs items="$ctrl.items" active-item="$ctrl.activeItem"/>',
  bindings: {
    resource: '<'
  },
  controller: class ResourceBreadcrumbsController {
    constructor(ResourceBreadcrumbsService) {
      this.ResourceBreadcrumbsService = ResourceBreadcrumbsService;
    }

    $onInit() {
      this.items = [
        {
          label: 'Project dashboard',
          state: 'project.details',
          params: {
            uuid: this.resource.project_uuid
          }
        },
        ...this.ResourceBreadcrumbsService.get(this.resource),
      ];

      this.activeItem = this.resource.name;
    }

    $doCheck() {
      if (this.activeItem !== this.resource.name) {
        this.activeItem = this.resource.name;
      }
    }
  }
};

Update resource name in breadcrumbs if it has been changed (WAL-347)