const browserify = require('browserify')
const path = require('path')
const uglifyify = require('uglifyify')

const transform = require('./js-transform')

module.exports = function ({
  config,
  entry,
  env,
  minify
}) {
  const pipeline = browserify(entry, {
    basedir: process.cwd(),
    cache: {},
    debug: true,
    packageCache: {},
    paths: [
      path.join(__dirname, '/../node_modules'),
      path.join(process.cwd(), '/node_modules')
    ],
    transform: transform({
      config,
      env
    })
  })
  .on('error', function (err) {
    console.error(err.message)
    console.error(err.stack)
    process.exit(0)
  })
  .on('log', function (message) {
    console.log(message)
  })

  if (minify) {
    pipeline.transform({ global: true }, uglifyify)
  }

  return pipeline
}

const browserify = require('browserify')
const path = require('path')
const uglifyify = require('uglifyify')

const transform = require('./js-transform')

module.exports = function ({
  config,
  entry,
  env,
  minify
}) {
  const pipeline = browserify(entry, {
    basedir: process.cwd(),
    cache: {},
    debug: true,
    packageCache: {},
    paths: [
      path.join(__dirname, '/../node_modules'),
      path.join(process.cwd(), '/node_modules')
    ],
    transform: transform({
      config,
      env
    })
  })
  .on('error', function (err) {
    console.error(err.message)
    console.error(err.stack)
    process.exit(0)
  })
  .on('log', function (message) {
    console.log(message)
  })

  if (minify) {
    pipeline.transform(uglifyify, {global: true})
  }

  return pipeline
}

Fix the order of uglify.


require('dashboard/core');

Dashboard.ApplicationController = Ember.ObjectController.extend();

Dashboard.RepositoriesController = Ember.ArrayController.extend({
  sortProperties: 'full_name'.w(),
  loadWatchedRepositories: function(username) {
    this.get('dataSource').watchedRepositories(username, this, 'addObjects');
  }
});

require('dashboard/core');

Dashboard.ApplicationController = Ember.Controller.extend();

Dashboard.RepositoriesController = Ember.ArrayController.extend({
  sortProperties: 'full_name'.w(),
  loadWatchedRepositories: function(username) {
    this.get('dataSource').watchedRepositories(username, this, 'addObjects');
  }
});

Change ApplicationController to be an Ember.Controller


const assert = require('chai').assert
const { getFolderSubjects } = require('../checkAnyDataPresent.js')

describe('checkAnyDataPresent', () => {
  describe('getFolderSubjects()', () => {
    it('returns only unique subjects', () => {
      // Native FileList but an array simulates it
      const fileList = [
        { relativePath: 'sub-01/files' },
        { relativePath: 'sub-01/another' },
        { relativePath: 'sub-02/data' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 2)
    })
    it('filters out emptyroom subject', () => {
      const fileList = [
        { relativePath: 'sub-01/files' },
        { relativePath: 'sub-emptyroom/data' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 1)
    })
    it('works for deeply nested files', () => {
      const fileList = [
        { relativePath: 'sub-01/files/a.nii.gz' },
        { relativePath: 'sub-01/another/b.nii.gz' },
        { relativePath: 'sub-02/data/test' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 2)
    })
  })
})

const assert = require('chai').assert
const { getFolderSubjects } = require('../checkAnyDataPresent.js')

describe('checkAnyDataPresent', () => {
  describe('getFolderSubjects()', () => {
    it('returns only unique subjects', () => {
      // Pseudo-FileList object but an array simulates it
      const fileList = [
        { relativePath: 'sub-01/files' },
        { relativePath: 'sub-01/another' },
        { relativePath: 'sub-02/data' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 2)
    })
    it('filters out emptyroom subject', () => {
      const fileList = [
        { relativePath: 'sub-01/files' },
        { relativePath: 'sub-emptyroom/data' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 1)
    })
    it('works for deeply nested files', () => {
      const fileList = [
        { relativePath: 'sub-01/files/a.nii.gz' },
        { relativePath: 'sub-01/another/b.nii.gz' },
        { relativePath: 'sub-02/data/test' },
      ]
      assert.lengthOf(getFolderSubjects(fileList), 2)
    })
    it('works with object arguments', () => {
      const fileList = { 0: { relativePath: 'sub-01/anat/one.nii.gz' } }
      assert.lengthOf(getFolderSubjects(fileList), 1)
    })
  })
})

Add failing unit test for getFolderSubjects() with actual object


// Misc operations

framework.addJavaScriptHelp("startGUI", "", "start GUI");

function startGUI() {
    framework.startGUI();
}

framework.addJavaScriptHelp("stoptGUI", "", "stop GUI and switch to console mode");

function stopGUI() {
    framework.shutdownGUI();
}

framework.addJavaScriptHelp("quit", "", "exit Workcraft");

function quit() {
    framework.shutdown();
}

framework.addJavaScriptHelp("exit", "", "exit Workcraft");

function exit() {
    framework.shutdown();
}

// Misc operations

framework.addJavaScriptHelp("startGUI", "", "start GUI");

function startGUI() {
    framework.startGUI();
}

framework.addJavaScriptHelp("stoptGUI", "", "stop GUI and switch to console mode");

function stopGUI() {
    framework.shutdownGUI();
}

framework.addJavaScriptHelp("quit", "", "request Workcraft shutdown after script execution is complete");

function quit() {
    framework.shutdown();
}

framework.addJavaScriptHelp("exit", "", "request Workcraft shutdown after script execution is complete");

function exit() {
    framework.shutdown();
}

Improve description of wrapper functions


// Because the tests need to be run through Webpack, we use this file as a single
// entry point for all the tests, so any tests to be run must be require()'d here.

'use strict';

require('./test-example-list');

// Because the tests need to be run through Webpack, we use this file as a single
// entry point for all the tests, so any tests to be run must be require()'d here.

'use strict';

require('../src/utils');  // For Object.assign polyfill

require('./test-example-list');

Fix test failure due to missing Object.assign polyfill


import './AnnotationCommand.test'
import './annotationHelpers.test'
import './ArrayOperation.test'
import './ChangeHistory.test'
import './Clipboard.test'
import './Component.test'
import './ContainerSelection.test'
import './CustomSelection.test'
import './DefaultDOMElement.test'
import './Document.test'
import './DocumentChange.test'
import './documentHelpers.test'
import './DocumentIndex.test'
import './DOMElement.test'
import './DOMImporter.test'
import './DOMSelection.test'
import './Editing.test'
import './EditorSession.test'
import './EditorSessionNew.test'
import './Fragmenter.test'
import './HTMLExporter.test'
import './HTMLImporter.test'
import './InlineNode.test'
import './IsolatedNode.test'
import './KeyboardManager.test'
import './NodeSchema.test'
import './ObjectOperation.test'
import './OperationSerializer.test'
import './ParentNode.test'
import './paste.test'
import './prettyPrintXML.test'
import './RenderingEngine.test'
import './sanitizeHTML.test'
import './Surface.test'
import './tableHelpers.test'
import './TextOperation.test'
import './TextPropertyComponent.test'
import './TreeIndex.test'
import './utils.test'
import './XMLExporter.test'
import './XMLImporter.test'

import { substanceGlobals } from 'substance'
// throw in failed asserts
substanceGlobals.ASSERTS = true
// enable this to see more info from the RenderingEngine
// substanceGlobals.VERBOSE_RENDERING_ENGINE = true

Enable throwing asserts for tests.


exports.SiteMap = SiteMap;

/**
 * @see http://www.sitemaps.org/protocol.php
 *
 * @param docs
 * @returns {SiteMap}
 */
function SiteMap(docs){
  this.render = function(){
    var map = [];
    map.push('<?xml version="1.0" encoding="UTF-8"?>');
    map.push('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
    docs.forEach(function(doc){
      map.push(' <url><loc>http://docs.angularjs.org/#!/' +
                            encode(doc.section) + '/' +
                            encode(doc.name) +
                     '</loc><changefreq>weekly</changefreq></url>');
    });
    map.push('</urlset>');
    map.push('');
    return map.join('\n');
  };

  function encode(text){
    return text
      .replace(/&/mg, '&')
      .replace(/</mg, '<')
      .replace(/>/mg, '>')
      .replace(/'/mg, ''')
      .replace(/"/mg, '"');
  }
}

exports.SiteMap = SiteMap;

/**
 * @see http://www.sitemaps.org/protocol.php
 *
 * @param docs
 * @returns {SiteMap}
 */
function SiteMap(docs){
  this.render = function(){
    var map = [];
    map.push('<?xml version="1.0" encoding="UTF-8"?>');
    map.push('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
    docs.forEach(function(doc){
      map.push(' <url><loc>http://docs.angularjs.org/#!/' +
                            encode(doc.section) + '/' +
                            encode(doc.id) +
                     '</loc><changefreq>weekly</changefreq></url>');
    });
    map.push('</urlset>');
    map.push('');
    return map.join('\n');
  };

  function encode(text){
    return text
      .replace(/&/mg, '&')
      .replace(/</mg, '<')
      .replace(/>/mg, '>')
      .replace(/'/mg, ''')
      .replace(/"/mg, '"');
  }
}

Fix sitemap generator to use doc.id instead of doc.name


var questions = [
  Q1 = {
    question: "What do HTML means?",
    answer: "HyperText Markup Language"
  },
  Q2 = {
    question: "What do CSS means?",
    answer: "Cascading Style Sheet"
  }
];

var question = document.getElementById('question');

var questionNum = 0;

function createNum() {
  questionNum = Math.floor(Math.random() * 2);
}

createNum();
question.innerHTML = questions[questionNum].question;

Create a function that generate random question


var _ = require('lodash');
var React = require('react');

var RequirementSet = require('./requirementSet');

var getRandomInt = require('../helpers/getRandomInt')

var AreaOfStudy = React.createClass({
	render: function() {

		var areaDetails = this.props;//getAreaOfStudy(this.props.name, this.props.type);
		// console.log('area-of-study render')

		var requirementSets = _.map(areaDetails.sets, function(reqset) {
			return RequirementSet({
				key: reqset.description,
				name: reqset.description,
				needs: reqset.needs,
				count: reqset.count,
				requirements: reqset.reqs,
				courses: this.props.courses
			});
		}, this);

		return React.DOM.article({id: areaDetails.id, className: 'area-of-study'},
			React.DOM.details(null,
				React.DOM.summary(null,
					React.DOM.h1(null, areaDetails.title),
					React.DOM.progress({value: getRandomInt(0, 100), max: 100})
				),
				requirementSets
			)
		);
	}
});

module.exports = AreaOfStudy

var _ = require('lodash');
var React = require('react');

var RequirementSet = require('./requirementSet');

var getRandomInt = require('../helpers/getRandomInt')

var areas = {
	major: {
		'Computer Science': require('../../mockups/demo_csci_major'),
		'Asian Studies': require('../../mockups/demo_major')
	},
	degree: {
		'Bachelor of Arts': require('../helpers/checkElegibilityForGraduation'),
		'Bachelor of Music': require('../helpers/checkElegibilityForGraduation')
	},
	concentration: {
		'Computer Science': require('../../mockups/demo_csci_major'),
		'Asian Studies': require('../../mockups/demo_major')
	}
}

var AreaOfStudy = React.createClass({
	render: function() {

		var areaDetails = this.props;//getAreaOfStudy(this.props.name, this.props.type);
		// console.log('area-of-study render')

		var requirementSets = _.map(areaDetails.sets, function(reqset) {
			return RequirementSet({
				key: reqset.description,
				name: reqset.description,
				needs: reqset.needs,
				count: reqset.count,
				requirements: reqset.reqs,
				courses: this.props.courses
			});
		}, this);

		return React.DOM.article({id: areaDetails.id, className: 'area-of-study'},
			React.DOM.details(null,
				React.DOM.summary(null,
					React.DOM.h1(null, areaDetails.title),
					React.DOM.progress({value: getRandomInt(0, 100), max: 100})
				),
				requirementSets
			)
		);
	}
});

module.exports = AreaOfStudy

Make an object for areas in AreaOfStudy


var util = require("./util");
var colorify = util.colorify;
var argsToString = util.argsToString

module.exports = new Wog;

function Olg () {}

// log to server
Olg.prototype.log = function(){
    // get call stack
    var callStack = new Error().stack;

    // use knowledge that second call in stack is where wog.log was invoked
    callStack = callStack.split("\n");

    var callIndex = 2
    var callFrame = callStack[callIndex];
    var callInfoIndex = callFrame.indexOf("(");
    var callInfo = callFrame.slice(callInfoIndex+1);

    // split the call info by colons
    var details = callInfo.split(":");
    // first element will be the file
    var callerFile = details[0];
    // second will be the line number
    var lineNum = details[1];

    // pass arguments to converter to be treated as same array-like object
    var output = argsToString.apply(this, arguments);

    // trim callerFile of user's home path
    var home = process.env.HOME || '';
    if (home) {
        callerFile = callerFile.replace(home, "~");
    }

    // color
    callerFile = colorify(callerFile, "teal");
    lineNum = colorify(lineNum, "purple");

    // smart log
    console.log(output + " ---logged from " + callerFile + " at line " + lineNum);
}

var util = require("./util");
var colorify = util.colorify;
var argsToString = util.argsToString

module.exports = new Olg;

function Olg () {}

// log to server
Olg.prototype.log = function(){
    // get call stack
    var callStack = new Error().stack;

    // use knowledge that second call in stack is where wog.log was invoked
    callStack = callStack.split("\n");

    var callIndex = 2
    var callFrame = callStack[callIndex];
    var callInfoIndex = callFrame.indexOf("(");
    var callInfo = callFrame.slice(callInfoIndex+1);

    // split the call info by colons
    var details = callInfo.split(":");
    // first element will be the file
    var callerFile = details[0];
    // second will be the line number
    var lineNum = details[1];

    // pass arguments to converter to be treated as same array-like object
    var output = argsToString.apply(this, arguments);

    // trim callerFile of user's home path
    var home = process.env.HOME || '';
    if (home) {
        callerFile = callerFile.replace(home, "~");
    }

    // color
    callerFile = colorify(callerFile, "teal");
    lineNum = colorify(lineNum, "purple");

    // smart log
    console.log(output + " ---logged from " + callerFile + " at line " + lineNum);
}

Fix error from unchanged name


#!/usr/bin/env node

//
// See https://github.com/attm2x/m2x-nodejs/blob/master/README.md#example-usage
// for instructions
//

var config = require("./config");
var M2X = require("m2x-tessel");
var m2xClient = new M2X(config.api_key);
var stream = "temperature";
var stream_params = {
    "unit": {
        "label": "celsius",
        "symbol": "C"
    },
    "type": "numeric"
};

// Create the stream if it doesn't exist already
m2xClient.devices.updateStream(config.device, stream, stream_params, function(response) {
    if (response.isError()) {
        console.log("Cannot create stream:", response);
        return;
    }

    var temperature = 24;
    setInterval(function () {
        console.log("I'm updating stream values! (Press CTRL + C to stop)");

        // Update the latest stream value to our new value
        m2xClient.devices.updateStreamValue(config.device, stream, {"value": new_value}, function(result) {
            if (result.isError()) {
                console.log(result.error());
            }
        });

        new_value += 1;
    }, 5000);
});

#!/usr/bin/env node

//
// See https://github.com/attm2x/m2x-nodejs/blob/master/README.md#example-usage
// for instructions
//

var config = require("./config");
var M2X = require("m2x-tessel");
var m2xClient = new M2X(config.api_key);
var stream = "temperature";
var stream_params = {
    "unit": {
        "label": "celsius",
        "symbol": "C"
    },
    "type": "numeric"
};

// Create the stream if it doesn't exist already
m2xClient.devices.updateStream(config.device, stream, stream_params, function(response) {
    if (response.isError()) {
        console.log("Cannot create stream:", response);
        return;
    }

    var temperature = 24;
    setInterval(function () {
        console.log("I'm updating stream values! (Press CTRL + C to stop)");

        // Update the latest stream value to our new value
        m2xClient.devices.setStreamValue(config.device, stream, {"value": new_value}, function(result) {
            if (result.isError()) {
                console.log(result.error());
            }
        });

        new_value += 1;
    }, 5000);
});

Fix method name typo in examples


var Promise = require('promise');

function respondTo(message) {
  return new Promise(function(resolve, reject) {
    if (message) {
      resolve('Received message: ' + message);
    } else {
      reject('No message provided');
    }
  });
}

module.exports = function(config) {
  return {
    respondTo: respondTo
  };
}

var Promise = require('promise');

var matchers = [
  require('./matchers/predicate-matcher')('is')
];

function respondTo(message) {
  return new Promise(function(resolve, reject) {
    if (message) {
      resolve('Received message: ' + message);
      matchers.forEach(function(matcher) {
        var match = matcher(message);
        if(match) {
          console.log('Matched', match);
        }
      });
    } else {
      reject('No message provided');
    }
  });
}

module.exports = function(config) {
  return {
    respondTo: respondTo
  };
}

Add basic predicate-matcher to API


var plumber = require("gulp-plumber");
var through = require("through2");
var chalk   = require("chalk");
var newer   = require("gulp-newer");
var babel   = require("gulp-babel");
var watch   = require("gulp-watch");
var gutil   = require("gulp-util");
var gulp    = require("gulp");
var path    = require("path");

var scripts = "./packages/*/src/**/*.js";
var dest = "packages";

gulp.task("default", ["build"]);

gulp.task("build", function () {
  return gulp.src(scripts)
    .pipe(plumber({
      errorHandler: function (err) {
        gutil.log(err.stack);
      }
    }))
    .pipe(through.obj(function (file, enc, callback) {
      file._path = file.path;
      file.path = file.path.replace(/^([^\\]+)\/src/, "$1/lib");
      callback(null, file);
    }))
    .pipe(newer(dest))
    .pipe(through.obj(function (file, enc, callback) {
      gutil.log("Compiling", "'" + chalk.cyan(file._path) + "'...");
      callback(null, file);
    }))
    .pipe(babel())
    .pipe(gulp.dest(dest));
});

gulp.task("watch", ["build"], function (callback) {
  watch(scripts, function () {
    gulp.start("build");
  });
});

var plumber = require("gulp-plumber");
var through = require("through2");
var chalk   = require("chalk");
var newer   = require("gulp-newer");
var babel   = require("gulp-babel");
var watch   = require("gulp-watch");
var gutil   = require("gulp-util");
var gulp    = require("gulp");
var path    = require("path");

var scripts = "./packages/*/src/**/*.js";
var dest = "packages";

var srcEx, libFragment;

if (path.win32 === path) {
  srcEx = /(packages\\[^\\]+)\\src\\/;
  libFragment = "$1\\lib\\";
} else {
  srcEx = new RegExp("(packages/[^/]+)/src/");
  libFragment = "$1/lib/";
}

gulp.task("default", ["build"]);

gulp.task("build", function () {
  return gulp.src(scripts)
    .pipe(plumber({
      errorHandler: function (err) {
        gutil.log(err.stack);
      }
    }))
    .pipe(through.obj(function (file, enc, callback) {
      file._path = file.path;
      file.path = file.path.replace(srcEx, libFragment);
      callback(null, file);
    }))
    .pipe(newer(dest))
    .pipe(through.obj(function (file, enc, callback) {
      gutil.log("Compiling", "'" + chalk.cyan(file._path) + "'...");
      callback(null, file);
    }))
    .pipe(babel())
    .pipe(gulp.dest(dest));
});

gulp.task("watch", ["build"], function (callback) {
  watch(scripts, function () {
    gulp.start("build");
  });
});

Allow gulp build to work on Windows


(function() {
  'use strict';

  var gulp   = require('gulp');
  var clean  = require('gulp-clean');
  var uglify = require('gulp-uglify');
  var rename = require('gulp-rename');

  gulp.task('clean', function() {
    return gulp.src([
      './typestring.min.js',
    ], { read: false })
    .pipe(clean());
  });

  gulp.task('default', ['clean'], function() {
  gulp.src('./typestring.js')
    .pipe(uglify({ preserveComments: function(s, n) { return !n.pos; } }))
    .pipe(rename(function(path) { path.basename += '.min'; }))
    .pipe(gulp.dest('.'));
  });

})();

(function() {
  'use strict';

  var gulp   = require('gulp');
  var clean  = require('gulp-clean');
  var uglify = require('gulp-uglify');
  var rename = require('gulp-rename');

  gulp.task('clean', function() {
    return gulp.src([
      './dist/',
    ], { read: false })
    .pipe(clean());
  });

  gulp.task('default', ['clean'], function() {
  gulp.src('./typestring.js')
    .pipe(uglify({ preserveComments: function(s, n) { return !n.pos; } }))
    .pipe(rename(function(path) { path.basename += '.min'; }))
    .pipe(gulp.dest('./dist'));
  });

})();

Write min file to /dist


/**
 * Deletes an element from an array by its index in the array.
 *
 * @param {Array} array The original array.
 * @param {Number} index The index of the element to delete in the original array.
 * @return {Array} A new array with the element removed.
 * @example
 *
 * const originalArray = ['a', 'b', 'c', 'd', 'e'];
 * const resultArray = immutableDelete(originalArray, 2);
 * // -> originalArray ['a', 'b', 'c', 'd', 'e']
 * // -> resultArray ['a', 'b', 'd', 'e']
 */
export default function immutableDelete(array, index) {
  return index > 0
    ? array.slice(0, index).concat(array.slice(index + 1))
    : array.slice(0);
}

/**
 * Deletes an element from an array by its index in the array.
 *
 * @param {Array} array The original array.
 * @param {Number} index The index of the element to delete in the original array.
 * @return {Array} A new array with the element removed.
 * @example
 *
 * const originalArray = ['a', 'b', 'c', 'd', 'e'];
 * const resultArray = immutableDelete(originalArray, 2);
 * // -> originalArray ['a', 'b', 'c', 'd', 'e']
 * // -> resultArray ['a', 'b', 'd', 'e']
 */
export default function immutableDelete(array, index) {
  return index >= 0
    ? array.slice(0, index).concat(array.slice(index + 1))
    : array.slice(0);
}

Fix bug a elements are not deleted at index 0


var EventSource    = require("eventsource"),
    EventEmitter   = require("events").EventEmitter,
    utils          = require("radiodan-client").utils,
    http           = require("./http-promise"),
    bbcServicesURL = process.env.BBC_SERVICES_URL;

module.exports = { create: create };

function create() {
  var instance = new EventEmitter,
      logger   = utils.logger(__filename);

  instance.store = {};

  listenForEvents();

  return instance;

  function listenForEvents() {
    var url = bbcServicesURL+"/stream",
        es  = new EventSource(url),
        firstError = true;

    logger.debug("Connecting to", url);

    es.onmessage = handleMessage;

    es.onerror = function() {
      // always fails on startup
      if(firstError) {
        firstError = false;
      } else {
        logger.warn("EventSource error");
      }
    };
  }

  function handleMessage(msg) {
    var data = parseMessage(msg.data),
        store;

    if (data) {
      store = instance.store[data.service] = instance.store[data.service] || {};

      store[data.topic] = data.data;

      instance.emit(data.service, data.topic, data.data);
      instance.emit(data.service+"/"+data.topic, data.data);
    }
  }

  function parseMessage(data) {
    try {
      return JSON.parse(data);
    } catch(err) {
      logger.warn("Cannot parse", err);
      return false;
    }
  }
}

var EventSource    = require("eventsource"),
    EventEmitter   = require("events").EventEmitter,
    utils          = require("radiodan-client").utils,
    http           = require("./http-promise"),
    logger         = utils.logger(__filename),
    bbcServicesURL = process.env.BBC_SERVICES_URL;

if(typeof bbcServicesURL === "undefined") {
  logger.error("BBC_SERVICES_API not found in ENV");
  process.exit();
}

module.exports = { create: create };

function create() {
  var instance = new EventEmitter;

  instance.store = {};

  listenForEvents();

  return instance;

  function listenForEvents() {
    var url = bbcServicesURL+"/stream",
        es  = new EventSource(url),
        firstError = true;

    logger.debug("Connecting to", url);

    es.onmessage = handleMessage;

    es.onerror = function() {
      // always fails on startup
      if(firstError) {
        firstError = false;
      } else {
        logger.warn("EventSource error");
      }
    };
  }

  function handleMessage(msg) {
    var data = parseMessage(msg.data),
        store;

    if (data) {
      store = instance.store[data.service] = instance.store[data.service] || {};

      store[data.topic] = data.data;

      instance.emit(data.service, data.topic, data.data);
      instance.emit(data.service+"/"+data.topic, data.data);
    }
  }

  function parseMessage(data) {
    try {
      return JSON.parse(data);
    } catch(err) {
      logger.warn("Cannot parse", err);
      return false;
    }
  }
}

Exit process if BBC_SERVICES_URL is not set


/* @flow */
import React, { PureComponent } from 'react';
import { FlatList, StyleSheet } from 'react-native';

import type { Topic } from '../types';
import TopicItem from '../streams/TopicItem';
import { LoadingIndicator, SectionSeparatorBetween, SearchEmptyState } from '../common';

const styles = StyleSheet.create({
  list: {
    flex: 1,
    flexDirection: 'column',
  },
});

type Props = {
  topics: ?(Topic[]),
  onPress: (stream: string, topic: string) => void,
};

export default class TopicList extends PureComponent<Props> {
  props: Props;

  static defaultProps = {
    showDescriptions: false,
    showSwitch: false,
    selected: false,
    streams: [],
  };

  render() {
    const { topics, onPress } = this.props;

    if (!topics) {
      return <LoadingIndicator size={40} />;
    }

    if (topics.length === 0) {
      return <SearchEmptyState text="No topics found" />;
    }

    return (
      <FlatList
        style={styles.list}
        data={topics}
        keyExtractor={item => item.name}
        renderItem={({ item }) => (
          <TopicItem name={item.name} isMuted={false} unreadCount={0} onPress={onPress} />
        )}
        SectionSeparatorComponent={SectionSeparatorBetween}
      />
    );
  }
}

/* @flow */
import React, { PureComponent } from 'react';
import { FlatList, StyleSheet } from 'react-native';

import type { Topic } from '../types';
import TopicItem from '../streams/TopicItem';
import { LoadingIndicator, SectionSeparatorBetween, SearchEmptyState } from '../common';

const styles = StyleSheet.create({
  list: {
    flex: 1,
    flexDirection: 'column',
  },
});

type Props = {
  topics: ?(Topic[]),
  onPress: (stream: string, topic: string) => void,
};

export default class TopicList extends PureComponent<Props> {
  props: Props;

  static defaultProps = {
    showDescriptions: false,
    showSwitch: false,
    selected: false,
    streams: [],
  };

  render() {
    const { topics, onPress } = this.props;

    if (!topics) {
      return <LoadingIndicator size={40} />;
    }

    if (topics.length === 0) {
      return <SearchEmptyState text="No topics found" />;
    }

    return (
      <FlatList
        keyboardShouldPersistTaps="always"
        style={styles.list}
        data={topics}
        keyExtractor={item => item.name}
        renderItem={({ item }) => (
          <TopicItem name={item.name} isMuted={false} unreadCount={0} onPress={onPress} />
        )}
        SectionSeparatorComponent={SectionSeparatorBetween}
      />
    );
  }
}

Add keyboardShouldPersistTaps to Topic List screen


(function(window, google) {
  'use strict';

  var Placard = {};

  Placard.Map = function(el, config) {
    this.el = el;
    this.config = config;
    this._map = this._createMap();
  };

  Placard.Map.prototype._createMap = function() {
    return new google.maps.Map(document.getElementById(this.el), this.config);
  };

  Placard.Map.prototype.addPoint = function(point) {
    var newPoint = new Placard.Point(point, this);
    newPoint.addToMap(this);

    return this;
  };

  Placard.Point = function(point) {
    this.lat = point.lat;
    this.lng = point.lng;
  };

  Placard.Point.prototype.addToMap = function(map) {
    new google.maps.Marker({
      position: new google.maps.LatLng(this.lat, this.lng),
      map: map._map
    });

    return this;
  };

  window.Placard = Placard;
})(window, google);

(function(window, google) {
  'use strict';

  var Placard = {};

  Placard.Map = function(el, config) {
    this.el = el;
    this.config = config;
    this._map = this._createMap();
  };

  Placard.Map.prototype._createMap = function() {
    return new google.maps.Map(document.getElementById(this.el), this.config);
  };

  Placard.Map.prototype.addPoint = function(point) {
    var newPoint = new Placard.Point(point, this);
    newPoint.addToMap(this);

    return this;
  };

  Placard.Point = function(point) {
    this.lat = point.lat;
    this.lng = point.lng;
    this.info = point.info;
    this.window = this._createWindow();
  };

  Placard.Point.prototype.addToMap = function(map) {
    var self = this;

    var marker = new google.maps.Marker({
      position: new google.maps.LatLng(this.lat, this.lng),
      map: map._map
    });

    if (this.info) {
      google.maps.event.addListener(marker, 'click', function() {
        self.window._infoWindow.open(map._map, marker);
      });
    }

    return this;
  };

  Placard.Point.prototype._createWindow = function() {
    if (this.info) {
      return new Placard.Window(this.info);
    }
  };

  Placard.Window = function(info) {
    this.title = info.title;
    this.text = info.text;
    this._infoWindow = this.createInfoWindow();
  };

  Placard.Window.prototype.createInfoWindow = function() {
    return new google.maps.InfoWindow({
      content: '
' +
                 '
' + this.title  + '
' +
                 '
' + this.text + '
' +
               '
'
    });
  };

  window.Placard = Placard;
})(window, google);

Add initial support for info windows


/** Select a (group of) patterns from the GTFS feed */

import React, {Component, PropTypes} from 'react'

import SelectPatterns from './select-patterns'
import SelectFeedAndRoutes from './select-feed-and-routes'

export default class SelectFeedRouteAndPatterns extends Component {
  static propTypes = {
    feed: PropTypes.object,
    feeds: PropTypes.array.isRequired,
    onChange: PropTypes.func.isRequired,
    routes: PropTypes.array,
    selectedFeed: PropTypes.object,
    trips: PropTypes.array // trips can be null indicating a wildcard
  }

  selectTrips = ({ patterns, trips }) => {
    this.props.onChange(Object.assign({}, this.props, { patterns, trips }))
  }

  selectFeedAndRoutes = ({
    feed,
    routes
  }) => {
    this.props.onChange(Object.assign({}, this.props, { routes, feed, trips: null }))
  }

  render () {
    const {selectedFeed, feeds, routes, trips} = this.props
    const routePatterns = routes && routes.length === 1
      ? selectedFeed.routesById[routes[0]].patterns
      : false
    return (
      <div>
        <SelectFeedAndRoutes
          feeds={feeds}
          onChange={this.selectFeedAndRoutes}
          selectedFeed={selectedFeed}
          selectedRouteId={routes && routes[0]}
          />

        {routePatterns &&
          <SelectPatterns
            onChange={this.selectTrips}
            routePatterns={routePatterns}
            trips={trips}
            />
        }
      </div>
    )
  }
}

/** Select a (group of) patterns from the GTFS feed */

import React, {Component, PropTypes} from 'react'

import SelectPatterns from './select-patterns'
import SelectFeedAndRoutes from './select-feed-and-routes'

export default class SelectFeedRouteAndPatterns extends Component {
  static propTypes = {
    feed: PropTypes.object,
    feeds: PropTypes.array.isRequired,
    onChange: PropTypes.func.isRequired,
    routes: PropTypes.array,
    selectedFeed: PropTypes.object,
    trips: PropTypes.array // trips can be null indicating a wildcard
  }

  selectTrips = ({ patterns, trips }) => {
    this.props.onChange(Object.assign({}, this.props, { patterns, trips }))
  }

  selectFeedAndRoutes = ({
    feed,
    routes
  }) => {
    this.props.onChange(Object.assign({}, this.props, { routes, feed, trips: null }))
  }

  render () {
    const {selectedFeed, feeds, routes, trips} = this.props
    const routePatterns = selectedFeed && routes && routes.length === 1
      ? selectedFeed.routesById[routes[0]].patterns
      : false
    return (
      <div>
        <SelectFeedAndRoutes
          feeds={feeds}
          onChange={this.selectFeedAndRoutes}
          selectedFeed={selectedFeed}
          selectedRouteId={routes && routes[0]}
          />

        {routePatterns &&
          <SelectPatterns
            onChange={this.selectTrips}
            routePatterns={routePatterns}
            trips={trips}
            />
        }
      </div>
    )
  }
}

Make sure the feed exists before checking its routes


var shovel = require('../shovel'),
    util = require('../util'),
    temp = require('temp'),
    exec = require('child_process').exec;

function compile(file, cb) {
    exec('tsc ' + file, function (err, stdout, stderr) {
        file = file.replace('.ts', '.js');
        cb(file);
    });
}

module.exports.run = function run(opts, cb) {
    var dir = temp.mkdirSync('typescript');
    shovel.start(opts, cb, {
        solutionOnly: function () {
            // TODO: Support Setup Code
            return function (run) {
                var solutionFile = util.codeWriteSync('typescript', opts.solution, dir, 'solution.ts', true);
                compile(solutionFile, function (file) {
                    run({name: 'node', args: [file]});
                });
            }
        },
        fullProject: function () {
            throw 'Test framework is not supported';
        }
    });

};

var shovel = require('../shovel'),
    util = require('../util'),
    temp = require('temp'),
    exec = require('child_process').exec;

function compile(file, cb) {
    exec('tsc ' + file, cb);
}

module.exports.run = function run(opts, cb) {
    var dir = temp.mkdirSync('typescript');
    shovel.start(opts, cb, {
        solutionOnly: function () {
            // TODO: Support Setup Code
            return function (run) {
                var solutionFile = util.codeWriteSync('typescript', opts.solution, dir, 'solution.ts', true);
                compile(solutionFile, function (error, stdout, stderr) {
                    if (stdout) console.log(stdout);
                    if (stderr) {
                        console.error(stderr);
                        return;
                    }
                    if (error !== null) {
                        console.error('exec error: ' + error);
                        return;
                    }
                    run({name: 'node', args: [solutionFile.replace('.ts', '.js')]});
                });
            }
        },
        fullProject: function () {
            throw 'Test framework is not supported';
        }
    });

};

Standardize callback for exec... this isn't dry, probably should go into util


var data = [{
        "title": "Alice in Wonderland",
        "text": "Alice falls into a rabbit hole and enters a world full of imagination."
    },

    {
        "title": "The Lord of the Rings: The Fellowship of the Ring.",
        "text": "An unusual alliance of man, elf, dwarf, wizard and hobbit seek to destroy a powerful ring."
    }
];
var arr = [];
var dict = {}
for (var i in data) {
    for(var key in data[i]) {
        var words = data[i][key].split(" ");
        for(var j in words) {
            var word = words[j];
            if(dict.hasOwnProperty(word)) {
                var posArr = dict[word];
                if(parseInt(i)) {
                posArr.push(parseInt(i));
                dict[word] = posArr
                }
            }
            else {
                dict[word] = [parseInt(i)];
            }
        }
    }
}
dict;

var data = [{
        "title": "Alice in Wonderland",
        "text": "Alice falls into a rabbit hole and enters a world full of imagination."
    },

    {
        "title": "The Alice Lord of the Rings: The Fellowship of the Ring.",
        "text": "An unusual alliance of man, elf, dwarf, wizard and hobbit seek to destroy a powerful ring."
    }
];
var arr = [];
var dict = {}

var getIndex = function() {
    for (var i in data) {
        for (var key in data[i]) {
            var words = data[i][key].split(" ");
            for (var j in words) {
                var word = words[j].replace(/[:,.]/g, '')
                if (dict.hasOwnProperty(word)) {
                    var posArr = dict[word];
                    if (parseInt(i)) {
                        var position = parseInt(i)
                        if (posArr.indexOf(position) < 0) {
                            posArr.push(parseInt(i));
                            dict[word] = posArr
                        }
                    }
                } else {
                    dict[word] = [parseInt(i)];
                }
            }
        }
    }
    console.log(dict);
    return dict
}

getIndex();

var searchIndex = function(terms) {
    var check = {};
    check[terms] = getIndex()[terms]
    console.log(check);
    return check;
}
searchIndex("man");

Create getIndex and Search functions


function searchRequestBuilder(request) {
  var params = request.params;

  var queries = {};
  queries.result = {
    source: params.tableName,
    output: {
      startTime:   true,
      elapsedTime: true,
      count:       true,
      attributes:  true,
      recodes:     true
    }
  };

  if (params.attributes)
    queries.result.attributes = params.attributes.split(',');
  if (params.limit)
    queries.result.limit = parseInt(params.limit);
  if (params.match_escalation_threshold)
    queries.result.matchEscalationThreshold = parseInt(params.match_escalation_threshold);
  if (params.match_to)
    queries.result.matchTo = params.match_to.split(',');
  if (params.offset)
    queries.result.offset = parseInt(params.offset);
  if (params.query)
    queries.result.query = params.query;
  if (params.sort_by)
    queries.result.sortBy = params.sort_by.split(',');

  return { queries: queries };
}
exports.search = searchRequestBuilder;

var NUMERIC = /^-?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)$/;
function getNumericValue(params, key) {
  var value = params[key];
  if (!NUMERIC.test(value))
    throw new Error(key + ': ' +value + ' is invalid number');
  return parseFloat(value);
}

var INTEGER = /^-?[0-9]+$/;
function getIntegerValue(params, key) {
  var value = params[key];
  if (!INTEGER.test(value))
    throw new Error(key + ': ' +value + ' is invalid integer');
  return parseInt(value);
}

function searchRequestBuilder(request) {
  var params = request.params;
  if (!params.tableName)
    throw new Error('no source is specified');

  var queries = {};
  queries.result = {
    source: params.tableName,
    output: {
      startTime:   true,
      elapsedTime: true,
      count:       true,
      attributes:  true,
      recodes:     true
    }
  };

  if (params.attributes)
    queries.result.attributes = params.attributes.split(',');
  if (params.limit)
    queries.result.limit = getIntegerValue(params, 'limit');
  if (params.match_escalation_threshold)
    queries.result.matchEscalationThreshold = getIntegerValue(params, 'match_escalation_threshold');
  if (params.match_to)
    queries.result.matchTo = params.match_to.split(',');
  if (params.offset)
    queries.result.offset = getIntegerValue(params, 'offset');
  if (params.query)
    queries.result.query = params.query;
  if (params.sort_by)
    queries.result.sortBy = params.sort_by.split(',');

  return { queries: queries };
}
exports.search = searchRequestBuilder;

Implement validation for numeric/integer parameter


'use strict';

const BaseHandler = require('./BaseHandler');

class PatchHandler extends BaseHandler {
    /**
     * Write data to the DataStore and return the new offset.
     *
     * @param  {object} req http.incomingMessage
     * @param  {object} res http.ServerResponse
     * @return {function}
     */
    send(req, res) {
        const re = new RegExp('\\' + this.store.path + '\\/(\\w+)\/?'); // eslint-disable-line prefer-template
        const match = req.url.match(re);
        if (!match) {
            return super.send(res, 404);
        }

        const file_name = match[1];

        let offset = req.headers['upload-offset'];
        // The request MUST include a Upload-Offset header
        if (!offset) {
            return super.send(res, 409);
        }

        offset = parseInt(offset, 10);
        // The value MUST be a non-negative integer.
        if (isNaN(offset) || offset < 0) {
            return super.send(res, 400, { 'Content-Type': 'text/plain' }, 'Upload-Offset must be non-negative');
        }

        return this.store.write(req, file_name, offset).then((new_offset) => {
            return super.send(res, 201, { 'Upload-Offset': new_offset });
        })
        .catch((error) => {
            console.log(error);
            super.send(res, 404);
        });
    }
}

module.exports = PatchHandler;

'use strict';

const BaseHandler = require('./BaseHandler');

class PatchHandler extends BaseHandler {
    /**
     * Write data to the DataStore and return the new offset.
     *
     * @param  {object} req http.incomingMessage
     * @param  {object} res http.ServerResponse
     * @return {function}
     */
    send(req, res) {
        const re = new RegExp('\\' + this.store.path + '\\/(\\w+)\/?'); // eslint-disable-line prefer-template
        const match = req.url.match(re);
        if (!match) {
            return super.send(res, 404);
        }

        const file_name = match[1];

        let offset = req.headers['upload-offset'];
        // The request MUST include a Upload-Offset header
        if (!offset) {
            return super.send(res, 409);
        }

        offset = parseInt(offset, 10);
        // The value MUST be a non-negative integer.
        if (isNaN(offset) || offset < 0) {
            return super.send(res, 400, { 'Content-Type': 'text/plain' }, 'Upload-Offset must be non-negative');
        }

        return this.store.write(req, file_name, offset).then((new_offset) => {
            return super.send(res, 204, { 'Upload-Offset': new_offset });
        })
        .catch((error) => {
            console.log(error);
            super.send(res, 404);
        });
    }
}

module.exports = PatchHandler;

PATCH should 204 on success


// Returns a jQuery or AngularJS deferred object, or pass a success and fail callbacks if you don't want to use jQuery or AngularJS
var getAppVersion = {
  getVersionNumber: function (success, fail) {
    var toReturn, deferred;
    if ((typeof success) === 'undefined') {
      if(window.jQuery){
        deferred = jQuery.Deferred();
        toReturn = deferred;
      } else if(window.angular){
        var injector = angular.injector(["ng"]);
        var $q = injector.get("$q");
        deferred = $q.defer();
        toReturn = deferred.promise
      } else {
        return console.error('AppVersion either needs a success callback, or jQuery/AngularJS defined for using promises');
      }
      success = deferred.resolve;
      fail = deferred.reject;
    }
    // 5th param is NOT optional. must be at least empty array
    cordova.exec(success, fail, "AppVersion", "getVersionNumber", []);
    return toReturn;
  },
  getVersionCode: function (success, fail) {
    var toReturn, deferred;
    if ((typeof success) === 'undefined') {
      if(window.jQuery){
        deferred = jQuery.Deferred();
        toReturn = deferred;
      } else if(window.angular){
        var injector = angular.injector(["ng"]);
        var $q = injector.get("$q");
        deferred = $q.defer();
        toReturn = deferred.promise
      } else {
        return console.error('AppVersion either needs a success callback, or jQuery/AngularJS defined for using promises');
      }
      success = deferred.resolve;
      fail = deferred.reject;
    }
    // 5th param is NOT optional. must be at least empty array
    cordova.exec(success, fail, "AppVersion", "getVersionCode", []);
    return toReturn;
  }
};

module.exports = getAppVersion;

// Returns a jQuery or AngularJS deferred object, or pass a success and fail callbacks if you don't want to use jQuery or AngularJS
var getPromisedCordovaResult = function (command, success, fail) {
  var toReturn, deferred;
  if (success === undefined) {
    if(window.jQuery){
      deferred = jQuery.Deferred();
      toReturn = deferred;
    } else if(window.angular){
      var injector = angular.injector(["ng"]);
      var $q = injector.get("$q");
      deferred = $q.defer();
      toReturn = deferred.promise
    } else {
      return console.error('AppVersion either needs a success callback, or jQuery/AngularJS defined for using promises');
    }
    success = deferred.resolve;
    fail = deferred.reject;
  }
  // 5th param is NOT optional. must be at least empty array
  cordova.exec(success, fail, "AppVersion", command, []);
  return toReturn;
}

var getAppVersion = function (success, fail) {
  return getPromisedCordovaResult('getVersionNumber', success, fail);
};

getAppVersion.getVersionNumber = function (success, fail) {
  return getPromisedCordovaResult('getVersionNumber', success, fail);
};

getAppVersion.getVersionCode = function (success, fail) {
  return getPromisedCordovaResult('getVersionCode', success, fail);
};

module.exports = getAppVersion;

Simplify js that generates promises and fix bc break


import test from 'ava';
import Keyv from '../';

test('Keyv is a function', t => {
	t.is(typeof Keyv, 'function');
});

test('Keyv cannot be invoked without \'new\'', t => {
	t.throws(() => Keyv()); // eslint-disable-line new-cap
	t.notThrows(() => new Keyv());
});

test('.set(key, value) returns a Promise', t => {
	const store = new Keyv();
	t.true(store.set('foo', 'bar') instanceof Promise);
});

test('.set(key, value) resolves to value', async t => {
	const store = new Keyv();
	t.is(await store.set('foo', 'bar'), 'bar');
});

Test .set(key, value) resolves to value


(function() {
    var expect, Environment, Loader, templatesPath;

    if(typeof require != 'undefined') {
        expect = require('expect.js');
        Environment = require('../src/environment').Environment;
        Loader = require('../src/node-loaders').FileSystemLoader;
        templatesPath = 'tests/templates';
    }
    else {
        expect = window.expect;
        Environment = nunjucks.Environment;
        Loader = nunjucks.WebLoader;
        templatesPath = '../templates';
    }

    describe('api', function() {
        it('should always force compilation of parent template', function() {
            var env = new Environment(new Loader(templatesPath));

            var parent = env.getTemplate('base.html');
            var child = env.getTemplate('base-inherit.html');
            expect(child.render()).to.be('Foo*Bar*BazFizzle');
        });

        it('should handle correctly relative paths', function() {
            var env = new Environment(new Loader(templatesPath));

            var child1 = env.getTemplate('relative/test1.html');
            var child2 = env.getTemplate('relative/test2.html');

            expect(child1.render()).to.be('FooTest1BazFizzle');
            expect(child2.render()).to.be('FooTest2BazFizzle');
        });
    });
})();

(function() {
    var expect, Environment, Loader, templatesPath;

    if(typeof require != 'undefined') {
        expect = require('expect.js');
        Environment = require('../src/environment').Environment;
        Loader = require('../src/node-loaders').FileSystemLoader;
        templatesPath = 'tests/templates';
    }
    else {
        expect = window.expect;
        Environment = nunjucks.Environment;
        Loader = nunjucks.WebLoader;
        templatesPath = '../templates';
    }

    describe('api', function() {
        it('should always force compilation of parent template', function() {
            var env = new Environment(new Loader(templatesPath));

            var parent = env.getTemplate('base.html');
            var child = env.getTemplate('base-inherit.html');
            expect(child.render()).to.be('Foo*Bar*BazFizzle');
        });

        it('should handle correctly relative paths', function() {
            var env = new Environment(new Loader(templatesPath));

            var child1 = env.getTemplate('relative/test1.html');
            var child2 = env.getTemplate('relative/test2.html');

            expect(child1.render()).to.be('FooTest1BazFizzle');
            expect(child2.render()).to.be('FooTest2BazFizzle');
        });

        it('should handle correctly relative paths in renderString', function() {
            var env = new Environment(new Loader(templatesPath));
            expect(env.renderString('{% extends "./relative/test1.html" %}{% block block1 %}Test3{% endblock %}')).to.be('FooTest3BazFizzle');
        });
    });
})();

Add test for relative path and renderString


module.exports = {
    entityManager: require( 'easy/test/entitymanager.mock' ),
    request: require( 'easy/test/request.mock' ),
    response: require( 'easy/test/response.mock' ),
    fakeAsync: require( 'easy/test/fakeAsync.mock' ),
    directory: require( 'easy/test/directory.mock' ),
    logger: require( 'easy/test/logger.mock' ),
    logWriter: require( 'easy/test/logwriter.mock' ),
    logFileManager: require( 'easy/test/logfilemanager.mock' ),
    tokenManager: require( 'easy/test/tokenmanage.mock' ),
    authorization: require( 'easy/test/authorization.mock' )
}

module.exports = {
    entityManager: require( 'easy/test/entitymanager.mock' ),
    request: require( 'easy/test/request.mock' ),
    response: require( 'easy/test/response.mock' ),
    fakeAsync: require( 'easy/test/fakeAsync.mock' ),
    directory: require( 'easy/test/directory.mock' ),
    logger: require( 'easy/test/logger.mock' ),
    logWriter: require( 'easy/test/logwriter.mock' ),
    logFileManager: require( 'easy/test/logfilemanager.mock' ),
    tokenManager: require( 'easy/test/tokenmanager.mock' ),
    authorization: require( 'easy/test/authorization.mock' )
}

Fix problem filename in require


//
// angular-file-model
// ==================
//
// Directive that makes the inputs with type `file` to be
// available in the `$scope` and be assigned to a model.
//
(function () {
  'use strict';

  angular.module('file-model', [])

  .directive('fileModel', [
    '$parse',
    function ($parse) {
      return {
        restrict: 'A',
        link: function(scope, element, attrs) {
          var model = $parse(attrs.fileModel);
          var modelSetter = model.assign;

          element.bind('change', function(){
            scope.$apply(function(){
              if (element[0].files.length > 1) {
                modelSetter(scope, element[0].files);
              }
              else {
                modelSetter(scope, element[0].files[0]);
              }
            });
          });
        }
      };
    }
  ]);

})();

//
// angular-file-model
// ==================
//
// Directive that makes the inputs with type `file` to be
// available in the `$scope` and be assigned to a model.
//
(function () {
  'use strict';

  angular.module('file-model', [])

  .directive('fileModel', [
    '$parse',
    function ($parse) {
      return {
        restrict: 'A',
        link: function(scope, element, attrs) {
          var model = $parse(attrs.fileModel);
          var modelSetter = model.assign;

          element.bind('change', function(){
            scope.$apply(function(){
              if (attrs.multiple) {
                modelSetter(scope, element[0].files);
              }
              else {
                modelSetter(scope, element[0].files[0]);
              }
            });
          });
        }
      };
    }
  ]);

})();

Make the bound value more perdictable.


import { combineReducers } from 'redux';
import { routerReducer as routing } from 'react-router-redux';
import auth from './auth';

const rootReducer = combineReducers({
  auth,
  routing,
});

export default rootReducer;

import { combineReducers } from 'redux';
import { routerReducer as routing } from 'react-router-redux';
import auth from './auth';
import playlists from './user'

const rootReducer = combineReducers({
  auth,
  playlists,
  routing,
});

export default rootReducer;

Add playlists reducer to store


/**
 * Welcome to Pebble.js!
 *
 * This is where you write your app.
 */

var UI = require('ui');
var Vector2 = require('vector2');

var main = new UI.Card({
  title: 'PokePebble',
  banner: 'images/LogoPS.png',
  body: 'A Pebble client for Pokemon showdown.'
});

main.show();

main.on('click', 'up', function(e) {
  var menu = new UI.Menu({
    sections: [{
      items: [{
        title: 'Pebble.js',
        icon: 'images/menu_icon.png',
        subtitle: 'Can do Menus'
      }, {
        title: 'Second Item',
        subtitle: 'Subtitle Text'
      }]
    }]
  });
  menu.on('select', function(e) {
    console.log('Selected item #' + e.itemIndex + ' of section #' + e.sectionIndex);
    console.log('The item is titled "' + e.item.title + '"');
  });
  menu.show();
});

main.on('click', 'select', function(e) {
  var wind = new UI.Window({
    fullscreen: true,
  });
  var textfield = new UI.Text({
    position: new Vector2(0, 65),
    size: new Vector2(144, 30),
    font: 'gothic-24-bold',
    text: 'Text Anywhere!',
    textAlign: 'center'
  });
  wind.add(textfield);
  wind.show();
});

main.on('click', 'down', function(e) {
  var card = new UI.Card();
  card.title('A Card');
  card.subtitle('Is a Window');
  card.body('The simplest window type in Pebble.js.');
  card.show();
});

/**
 * Welcome to Pebble.js!
 *
 * This is where you write your app.
 */

var UI = require('ui');

var title = new UI.Card({
  title: 'PokePebble',
  banner: 'images/LogoPS.png',
  body: 'A Pebble client for Pokemon showdown. Press SELECT to begin.'
});

title.show();

// TODO: Log in

title.on('click', 'select', function (e) {
  var searching = new UI.Card();
  searching.title('Searching for an opponent.');
  searching.body('Press BACK to cancel.');
  searching.show();
  // TODO: Search for an opponent
  searching.on('click', 'back', function (e) {
    // TODO: cancel search
    title.show();
  });
});

Select button to search for opponent


// Load our letter icon generator
var materialLetterIcons = require('../generator');

// Generate the letter icons and export them to dist/
materialLetterIcons.generateIcons(function(err) {
    // Log errors
    if (err) {
        return console.log('Icon generator failed: ', err);
    }

    // We're good
    console.log('Icons generated successfully!');
});

// Load our letter icon generator
var materialLetterIcons = require('../generator') || require('material-letter-icons');

// Generate the letter icons and export them to dist/
materialLetterIcons.generateIcons(function(err) {
    // Log errors
    if (err) {
        return console.log('Icon generator failed: ', err);
    }

    // We're good
    console.log('Icons generated successfully!');
});

Add npm package require fallback in generator example


var bluebird = require('bluebird');
var request = bluebird.promisify(require('browser-request'));

module.exports.remotes = function remotes(host) {
  return request(host + '/api/clients')
    .spread(function(res, body) {
      var peers = JSON.parse(body);
      peers.sort();
      return peers;
    });
};

var bluebird = require('bluebird');
var request = bluebird.promisify(require('browser-request'));

module.exports.remotes = function remotes(host) {
  return request(host + '/api/clients')
    .spread(function(res, body) {
      var peers = JSON.parse(body);
      for (var i = 0; i < clients.length; i++) {
        if (peers[i].match(/^dashboard/)) {
          peers.splice(i, 1);
        }
      }
      peers.sort();
      return peers;
    });
};

Remove dashboards from peers list


import React from 'react';
import InputForm from './components/InputForm';
import OutputTable from './components/OutputTable';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <InputForm />
        <OutputTable />
      </header>
    </div>
  );
}

export default App;

import React from 'react';
import InputForm from './components/InputForm';
import OutputTable from './components/OutputTable';

function App() {
  return (
    <div className="App">
      <InputForm />
      <OutputTable />
    </div>
  );
}

export default App;

Remove header tag from app


Filters.filter("sortUsers", [function () {
    return function (input, channel)
    {
        if (input)
        {
            var users = input.sort(function (a, b)
            {
                var ranks = ["", "+", "%", "@"];

                if (a.ranks[channel.name] != b.ranks[channel.name])
                {
                    if (ranks.indexOf(a.ranks[channel.name]) > ranks.indexOf(b.ranks[channel.name]))
                    {
                        return -1;
                    }

                    return 1;
                }

                return a.nickName.localeCompare(b.nickName);
            });

            return users;
        } else
        {
            return input;
        }
    };
}]);

Filters.filter("sortUsers", [function () {
    return function (input, channel)
    {
        if (input)
        {
            var users = input.sort(function (a, b)
            {
                var ranks = ["", "+", "%", "@"];

                if (a.ranks[channel.name] === undefined)
                {
                    a.ranks[channel.name] = "";
                }

                if (b.ranks[channel.name] === undefined)
                {
                    b.ranks[channel.name] = "";
                }

                if (a.ranks[channel.name].charAt(0) != b.ranks[channel.name].charAt(0))
                {
                    if (ranks.indexOf(a.ranks[channel.name].charAt(0)) > ranks.indexOf(b.ranks[channel.name].charAt(0)))
                    {
                        return -1;
                    }

                    return 1;
                }

                return a.nickName.localeCompare(b.nickName);
            });

            return users;
        } else
        {
            return input;
        }
    };
}]);

Fix users with multiple ranks moving randomly in the userlist


var tmp = require('tmp');
var express = require('express');
var app = express();
app.use(express.bodyParser());

app.get('/vocalization/fr/homme', function (request, response) {
  response.header('Content-Type', 'audio/mpeg');
  response.header('Content-Disposition', 'inline; filename=test.mp3');
  var exec = require('child_process').exec;
  var text = request.query.texte;

  tmp.tmpName(function _tempNameGenerated(err, tempfile) {
    if (err) throw err;
    // Below espeak takes text and transform it to wav
    // using the mbrola voice (prefixed with mb-) then
    // we use lame transforms the raw audio to mp3.
    var command = 'espeak -v mb-fr1 -w ' + tempfile + ' -a 15 -p 50 -s 130 "' + text + '" | lame -r -m m -b 24 -s 16 ' + tempfile + ' ' + tempfile;
    exec(command, function (error, stdout, stderr) {
      response.sendfile(tempfile);
    });
  });
});

module.exports = app;

var tmp = require('tmp');
var express = require('express');
var app = express();
app.use(express.bodyParser());

app.get('/vocalization/fr/homme', function (request, response) {
  response.header('Content-Type', 'audio/mpeg');
  response.header('Content-Disposition', 'inline; filename=test.mp3');
  var spawn = require('child_process').spawn;

  tmp.tmpName(function _tempNameGenerated(err, espeakTmpfile) {
    if (err) throw err;
    var espeak = spawn('espeak', ['-vmb-fr1', '-w' + espeakTmpfile , '-s130', request.query.texte]);

    espeak.on('exit', function(exitCode){
      tmp.tmpName(function _tempNameGenerated(err, lameTmpfile) {
        if (err) throw err;
        // volume normalization with fast replaygain is used by default.
        var options = ['-r', '-mm', '--silent', '-b24', '-s16', espeakTmpfile, lameTmpfile];
        var lame = spawn('lame', options);

        lame.on('exit', function(exitCode){
          response.sendfile(lameTmpfile);
        });

        lame.stderr.on('data', function(data){
          console.log("Lame error: " + data);
        });

      });
    });

    espeak.stderr.on('data', function(data){
      console.log("Espeak error: " + data);
    });

  });

});

module.exports = app;

Refactor to use spawn and more events.


const app = require("../js/app.js");
const Log = require("logger");

app.start(function (config) {
	var bindAddress = config.address ? config.address : "localhost";
	var httpType = config.useHttps ? "https" : "http";
	Log.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port);
});

const app = require("../js/app.js");
const Log = require("logger");

app.start((config) => {
	const bindAddress = config.address ? config.address : "localhost";
	const httpType = config.useHttps ? "https" : "http";
	Log.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port);
});

Use es6 notation in serveronly


'use strict';
(() => {
  module.exports = (gulp, plugins, config) => {
    return () => {
      return gulp.src([`${config.patternsPath}/**/*.js`, `!${config.patternsPath}/**/*.min.js`])
        .pipe(plugins.changed(config.buildPath))
        .pipe(plugins.plumber({
          errorHandler: plugins.notify.onError('Error: <%= error.message %>')
        }))
        .pipe(plugins.rename({ suffix: '.min' }))
        .pipe(plugins.sourcemaps.init())
        .pipe(plugins.babel({
          presets: [
            ['env', { 'modules': false }]
          ],
          ignore: [
            // Keep these files from breaking until they're refactored to ES6 code.
            `${config.patternsPath}/components/dialogs/**/*.js`,
            `${config.patternsPath}/components/buttons/rocketbelt.dynamic-button.js`,
            `${config.patternsPath}/base/slipsum/**/*.js(on)?`
          ]
        }))
        .pipe(plugins.uglify())
        .pipe(plugins.size(config.sizeOptions))
        .pipe(plugins.sourcemaps.write('.', { sourceRoot: '.' }))
        .pipe(gulp.dest(config.buildPath))
      ;
    };
  };
})();

'use strict';
(() => {
  module.exports = (gulp, plugins, config) => {
    return () => {
      return gulp.src([
        `${config.patternsPath}/**/*.js`,
        `!${config.patternsPath}/**/*.min.js`,
        `!${config.patternsPath}/base/slipsum/**/*`
        ])
        .pipe(plugins.changed(config.buildPath))
        .pipe(plugins.plumber({
          errorHandler: plugins.notify.onError('Error: <%= error.message %>')
        }))
        .pipe(plugins.rename({ suffix: '.min' }))
        .pipe(plugins.sourcemaps.init())
        .pipe(plugins.babel({
          presets: [
            ['env', { 'modules': false }]
          ],
          ignore: [
            // Keep these files from breaking until they're refactored to ES6 code.
            `${config.patternsPath}/components/dialogs/**/*.js`,
            `${config.patternsPath}/components/buttons/rocketbelt.dynamic-button.js`
          ]
        }))
        .pipe(plugins.uglify())
        .pipe(plugins.size(config.sizeOptions))
        .pipe(plugins.sourcemaps.write('.', { sourceRoot: '.' }))
        .pipe(gulp.dest(config.buildPath))
      ;
    };
  };
})();

Exclude giant, static JS files from transpilation.


// django-dynamic-formset fixes for bootstrap 3

(function($) {
    $(document).ready(function(){
        function fixDeleteRow ($deleteClickable) {
            // Move .delete-row link immediately after the form input
            $deleteClickableList = $deleteClickable.parent();
            var $formGroup = $deleteClickableList.siblings().find(".controls");
            $deleteClickableList.appendTo($formGroup);
            // Strip its li tags
            $deleteClickableList.replaceWith($deleteClickable);
            // Style it with bootstrap3 classes
            $deleteClickable.addClass("btn btn-default");
        }

        // To apply on the initially generated .delete-row link
        var $deleteClickable = $(".delete-row");
        fixDeleteRow($deleteClickable);

        // Style add-row link with bootstrap3 classes
        var $addAnotherClickables = $(".add-row");
        $addAnotherClickables.addClass("btn btn-default");

        // To apply on subsequently generated .delete-row links
        $(".add-row").click(function(){
            // Move .delete-row link immediately after the form input
            var $deleteClickable = $(".delete-row").last();
            fixDeleteRow($deleteClickable);
        });
    });
})(jQuery);

// django-dynamic-formset fixes for bootstrap 3

(function($) {
    $(document).ready(function(){
        function fixDeleteRow ($deleteClickable) {
            // Move .delete-row link immediately after the form input
            var $deleteClickableList = $deleteClickable.parent();
            var $formInputField = $deleteClickableList.parent().find("input");
            $deleteClickableList.insertAfter($formInputField);
            // Strip its li tags
            $deleteClickableList.replaceWith($deleteClickable);
            // Style it with bootstrap3 classes
            $deleteClickable.addClass("btn btn-default");
        }

        // To apply on the initially generated .delete-row link
        var $deleteClickable = $(".delete-row");
        fixDeleteRow($deleteClickable);

        // Style add-row link with bootstrap3 classes
        var $addAnotherClickables = $(".add-row");
        $addAnotherClickables.addClass("btn btn-default");

        // To apply on subsequently generated .delete-row links
        $(".add-row").click(function() {
            // Move .delete-row link immediately after the form input
            var $deleteClickable = $(".delete-row").last();
            fixDeleteRow($deleteClickable);
        });

        // To apply on form validation errors
        // .help-block tag comes up when validation error is shown.
        if ($(".help-block").length > 0) {
            // Slice of the already styled last element.
            var $deleteClickables = $(".delete-row").slice(0, -1);
            for (i in $deleteClickables) {
                fixDeleteRow($deleteClickables.eq(i));
            }
        }

    });
})(jQuery);

Fix style errors on form validation


const startDate = new Date(Date.UTC(2007, 0, 10, 10, 0, 0)); // > 'Wed, 10 Jan 2007 10:00:00 GMT'
const endDate = new Date(Date.UTC(2007, 0, 10, 11, 0, 0));   // > 'Wed, 10 Jan 2007 11:00:00 GMT'

const dateTimeFormat = new Intl.DateTimeFormat('en', {
  hour: 'numeric',
  minute: 'numeric'
});

const parts = dateTimeFormat.formatRangeToParts(startDate, endDate);
for (const part of parts) {
  console.log(part);
}
// expected output:
// Object { type: "hour", value: "2", source: "startRange" }
// Object { type: "literal", value: ":", source: "startRange" }
// Object { type: "minute", value: "00", source: "startRange" }
// Object { type: "literal", value: " – ", source: "shared" }
// Object { type: "hour", value: "3", source: "endRange" }
// Object { type: "literal", value: ":", source: "endRange" }
// Object { type: "minute", value: "00", source: "endRange" }
// Object { type: "literal", value: " ", source: "shared" }
// Object { type: "dayPeriod", value: "AM", source: "shared" }

const startDate = new Date(Date.UTC(2007, 0, 10, 10, 0, 0)); // > 'Wed, 10 Jan 2007 10:00:00 GMT'
const endDate = new Date(Date.UTC(2007, 0, 10, 11, 0, 0));   // > 'Wed, 10 Jan 2007 11:00:00 GMT'

const dateTimeFormat = new Intl.DateTimeFormat('en', {
  hour: 'numeric',
  minute: 'numeric'
});

const parts = dateTimeFormat.formatRangeToParts(startDate, endDate);
for (const part of parts) {
  console.log(part);
}
// expected output (in GMT timezone)
// Object { type: "hour", value: "2", source: "startRange" }
// Object { type: "literal", value: ":", source: "startRange" }
// Object { type: "minute", value: "00", source: "startRange" }
// Object { type: "literal", value: " – ", source: "shared" }
// Object { type: "hour", value: "3", source: "endRange" }
// Object { type: "literal", value: ":", source: "endRange" }
// Object { type: "minute", value: "00", source: "endRange" }
// Object { type: "literal", value: " ", source: "shared" }
// Object { type: "dayPeriod", value: "AM", source: "shared" }

Add comment that results depend on timezone


function example () {

  var
    container = document.getElementById('demo'),
    H = Humble.Vis,
    E = Flotr.EventAdapter,
    data = [],
    hash = window.location.hash,
    amplitude = (hash ? .05 : .001),

    mainOptions = {
      height  : 200,
      width   : 600,
      data    : data,
      flotr   : {}
    },

    summaryOptions = {
      height  : 200,
      width   : 600,
      data    : data,
      flotr   : {
        handles : { show : true },
        selection : { mode : 'x'}
      }
    },

    vis, main, summary, interaction, i;

  for (i = 0; i < 1e6; i++) {
    data.push([i/10000, .2*Math.sin(i/10000) + i/100000 + amplitude * Math.sin(i/50)]);
  }

  if (hash !== '#minmax') {
    mainOptions.processData = function (o) {

      var
        datum = o.datum;

      datum
        .bound(o.min, o.max)
        .subsample(o.resolution);
    }
  }

  vis = new H.Visualization();
  main = new H.Child(mainOptions);
  summary = new H.Child(summaryOptions);
  interaction = new H.Interaction({leader : summary});

  vis.add(main);
  vis.add(summary);
  vis.render(container);
  interaction.add(H.Action.Selection);
  interaction.follow(main);
}

function example () {

  var
    container = document.getElementById('demo'),
    H = Humble.Vis,
    E = Flotr.EventAdapter,
    data = [],
    hash = window.location.hash,
    amplitude = (hash ? .05 : .001),

    mainOptions = {
      height  : 200,
      width   : 600,
      data    : data,
      flotr   : {}
    },

    summaryOptions = {
      height  : 200,
      width   : 600,
      data    : data,
      flotr   : {
        handles : { show : true },
        selection : { mode : 'x'}
      }
    },

    vis, main, summary, interaction, i;

  for (i = 0; i < 1e6; i++) {
    data.push([i/10000, .2*Math.sin(i/10000) + i/100000 + amplitude * Math.sin(i/50)]);
  }

  if (hash !== '#minmax') {
    mainOptions.processData = summaryOptions.processData = function (o) {

      var
        datum = o.datum;

      datum
        .bound(o.min, o.max)
        .subsample(o.resolution);
    }
  }

  vis = new H.Visualization();
  main = new H.Child(mainOptions);
  summary = new H.Child(summaryOptions);
  interaction = new H.Interaction({leader : summary});

  vis.add(main);
  vis.add(summary);
  vis.render(container);
  interaction.add(H.Action.Selection);
  interaction.follow(main);
}

Use same data processing for main and summary charts.


var fs = require('fs'),
    hbs = require('hbs');

module.exports = function(hbs) {
    var blocks = {};

    hbs.registerHelper('extend', function(name, context) {
        var block = blocks[name];
        if (!block) {
            block = blocks[name] = [];
        }

        block.push(context.fn(this));
    });

    hbs.registerHelper('block', function(name) {
        var val = (blocks[name] || []).join('\n');

        // clear the block
        blocks[name] = [];
        return val;
    });

}

// Register all the partials
fs.readdirSync(__dirname + '/views/partials').forEach(function(filename) {
    var partial = filename.slice(0, -4);
    hbs.registerPartial(partial, fs.readFileSync(__dirname + '/views/partials/' + filename).toString());
});;

var fs = require('fs'),
    hbs = require('hbs');

module.exports = function(hbs) {
    var blocks = {};

    hbs.registerHelper('extend', function(name, context) {
        var block = blocks[name];
        if (!block) {
            block = blocks[name] = [];
        }

        block.push(context.fn(this));
    });

    hbs.registerHelper('block', function(name) {
        var val = (blocks[name] || []).join('\n');

        // clear the block
        blocks[name] = [];
        return val;
    });
};

// Register any partials
var partialsDir = __dirname + '/views/partials';
if (fs.existsSync(partialsDir)) {
    fs.readdirSync(partialsDir).forEach(function(filename) {
        var partial = filename.slice(0, -4);
        hbs.registerPartial(partial, fs.readFileSync(__dirname + '/views/partials/' + filename).toString());
    });;
}

Fix for when views/partials/ doesn't exist.


#!/usr/bin/env node

var repl = require('repl');
var program = require('commander');
var defaults = require('./defaults.js');
var protocol = require('../lib/protocol.json');
var Chrome = require('../');

program
    .option('-h, --host <host>', 'Remote Debugging Protocol host', defaults.HOST)
    .option('-p, --port <port>', 'Remote Debugging Protocol port', defaults.PORT)
    .parse(process.argv);

var options = {
    'host': program.host,
    'port': program.port
};

Chrome(options, function (chrome) {
    var chromeRepl = repl.start({
        'prompt': 'chrome> ',
        'ignoreUndefined': true
    });

    // disconnect on exit
    chromeRepl.on('exit', function () {
        chrome.close();
    });

    // add protocol API
    for (var domainIdx in protocol.domains) {
        var domainName = protocol.domains[domainIdx].domain;
        chromeRepl.context[domainName] = chrome[domainName];
    }
}).on('error', function () {
    console.error('Cannot connect to Chrome');
});

#!/usr/bin/env node

var repl = require('repl');
var program = require('commander');
var defaults = require('../lib/defaults.js');
var protocol = require('../lib/protocol.json');
var Chrome = require('../');

program
    .option('-h, --host <host>', 'Remote Debugging Protocol host', defaults.HOST)
    .option('-p, --port <port>', 'Remote Debugging Protocol port', defaults.PORT)
    .parse(process.argv);

var options = {
    'host': program.host,
    'port': program.port
};

Chrome(options, function (chrome) {
    var chromeRepl = repl.start({
        'prompt': 'chrome> ',
        'ignoreUndefined': true
    });

    // disconnect on exit
    chromeRepl.on('exit', function () {
        chrome.close();
    });

    // add protocol API
    for (var domainIdx in protocol.domains) {
        var domainName = protocol.domains[domainIdx].domain;
        chromeRepl.context[domainName] = chrome[domainName];
    }
}).on('error', function () {
    console.error('Cannot connect to Chrome');
});

Fix REPL script require path


var Gulp = require('gulp'),

    Linoleum = require('linoleum');

var $jsFiles = Linoleum.jsFiles;
Linoleum.jsFiles = function() {
  return $jsFiles().concat('tasks/*.js', 'index.js');
};

require('linoleum/tasks/clean');
require('linoleum/tasks/lint');
require('linoleum/tasks/cover');

require('./index');

Gulp.task('build', ['clean', 'lint'], function(done) {
  Linoleum.runTask('webpack', done);
});
Gulp.task('cover', ['build'], function(done) {
  Linoleum.runTask(['cover:untested', 'cover:server', 'cover:web', 'cover:report'], done);
});

Linoleum.watch(Linoleum.jsFiles(), 'cover');

Gulp.task('travis', function(done) {
  Linoleum.runTask(['cover'], done);
});
Gulp.task('default', ['cover']);

var Gulp = require('gulp'),

    Linoleum = require('linoleum');

require('./index');

Gulp.task('build', ['clean', 'lint'], function(done) {
  Linoleum.runTask('webpack', done);
});
Gulp.task('cover', ['build'], function(done) {
  Linoleum.runTask(['cover:untested', 'cover:server', 'cover:web', 'cover:report'], done);
});

Linoleum.watch(Linoleum.jsFiles(), 'cover');

Gulp.task('travis', function(done) {
  Linoleum.runTask(['cover'], done);
});
Gulp.task('default', ['cover']);

require('linoleum/Gulpfile.local');

Use gulp local for self test


/* jshint node:true */
/*
$> node example/repl.js
S>

 */

var sphero = require('../lib/sphero');
var repl = require('repl');

var context = repl.start('S> ').context;
context.dev = '/dev/cu.Sphero-PBR-RN-SPP'; //Change this to match your device
context.sphero = sphero;
context.toolbelt = require('../lib/toolbelt');
context.commands = require('../lib/commands');
context.macro = require('../lib/macro-builder').macro;

context.o = { resetTimeout:true, requestAcknowledgement:true };
context.s = sphero();

context.s.on('error', function(error) {
  console.log('Sphero error:', error);
});

//context.s.open(dev);

/* jshint node:true */
/*
$> node example/repl.js
S>

 */

var sphero = require('../lib/sphero');
var repl = require('repl');

var context = repl.start('Spher⊚n > ').context;
context.dev = '/dev/cu.Sphero-PBR-RN-SPP'; //Change this to match your device
context.sphero = sphero;
context.toolbelt = require('../lib/toolbelt');
context.commands = require('../lib/commands');
context.macro = require('../lib/macro-builder').macro;

context.o = { resetTimeout:true, requestAcknowledgement:true };
context.s = sphero();

context.s.on('error', function(error) {
  console.log('Sphero error:', error);
});

//context.s.open(dev);

Make the REPL prompt fancier


(function () {

  var

    // save reference to global object
    // `window` in browser
    // `exports` on server
    root = this,

    // save the previous version of ejs
    _ejs = exports.ejs,

    // from underscore.js, used in utils
    ArrayProto = Array.prototype,
    ObjProto = Object.prototype,
    slice = ArrayProto.slice,
    hasOwnProp = ObjProto.hasOwnProperty,
    nativeForEach = ArrayProto.forEach,
    breaker = {},
    has,
    each,
    extend,

    // create ejs object
    ejs;

  if (typeof exports !== 'undefined') {
    ejs = exports;
  } else {
    ejs = root.ejs = {};
  }

(function () {

  var

    // save reference to global object
    // `window` in browser
    // `exports` on server
    root = this,

    // save the previous version of ejs
    _ejs = root.ejs,

    // from underscore.js, used in utils
    ArrayProto = Array.prototype,
    ObjProto = Object.prototype,
    slice = ArrayProto.slice,
    hasOwnProp = ObjProto.hasOwnProperty,
    nativeForEach = ArrayProto.forEach,
    breaker = {},
    has,
    each,
    extend,

    // create ejs object
    ejs;

  if (typeof exports !== 'undefined') {
    ejs = exports;
  } else {
    ejs = root.ejs = {};
  }

Fix saving old version of ejs for noConflict


import { mori, datascript } from 'datascript-mori';
import { byTimeStamp } from  '../ui-data/sort';
const { core } = datascript;
const { parse , count, reverse, take, map, nth, hashMap, sort } = mori;

const getData = (db) => {
  const query = `[:find ?start ?hasWon ?for-class ?against-class
                  :where [?e ":hasWon" ?hasWon]
                  [?e ":for/class" ?for-class]
                  [?e ":against/class" ?against-class]
                  [?e ":time/start" ?start]]`;

  return core.q(parse(query), db);
};

const extractData = (element) => {
  return hashMap('result', nth(element, 1) ? 'win' : 'loss',
      'as', nth(element, 2),
      'against', nth(element, 3));
};

export function winStreak (db) {
  return new Promise((resolve, reject) => {
    if(!db) {
      reject(new Error('Expected a result set'));
    }
    const result = getData(db);
    if(!count(result)) {
      resolve([]);
    }
    console.log(take(5, sort(byTimeStamp, result)));
    resolve(map(extractData, take(5, sort(byTimeStamp, result))));
  });
}

import { mori, datascript } from 'datascript-mori';
import { byTimeStamp } from  '../ui-data/sort';
const { core } = datascript;
const { count, hashMap, parse , map, nth, sort, take } = mori;

const getData = (db) => {
  const query = `[:find ?start ?hasWon ?for-class ?against-class
                  :where [?e ":hasWon" ?hasWon]
                  [?e ":for/class" ?for-class]
                  [?e ":against/class" ?against-class]
                  [?e ":time/start" ?start]]`;

  return core.q(parse(query), db);
};

const extractData = (element) => {
  return hashMap('result', nth(element, 1) ? 'win' : 'loss',
      'as', nth(element, 2),
      'against', nth(element, 3));
};

export function winStreak (db) {
  return new Promise((resolve, reject) => {
    if(!db) {
      reject(new Error('Expected a result set'));
    }
    const result = getData(db);
    if(!count(result)) {
      resolve([]);
    }

    resolve(map(extractData, take(5, sort(byTimeStamp, result))));
  });
}

Remove log and sort imported functions


// Includes
var add = require('./add.js');
var get = require('./get.js');

// Define
module.exports = function (cache, type, index, callbacks) {
  var got = get(cache, type, index);
  var item = got[0];
  var refresh = got[1];
  if (item) {
    callbacks.done(item);
    if (refresh) {
      callbacks.add(function (element) {
        add(cache, type, index, element);
      });
    }
  } else {
    callbacks.add(function (element) {
      add(cache, type, index, element);
      callbacks.done(element);
    });
  }
};

// Includes
var add = require('./add.js');
var get = require('./get.js');

// Define
module.exports = function (cache, type, index, callbacks) {
  var got = get(cache, type, index);
  var item = got[0];
  var refresh = got[1];
  if (item) {
    callbacks.done(item);
    if (refresh) {
      var group = cache[type];
      group.refresh = false;
      callbacks.add(function (element) {
        group.refresh = true;
        add(cache, type, index, element);
      });
    }
  } else {
    callbacks.add(function (element) {
      add(cache, type, index, element);
      callbacks.done(element);
    });
  }
};

Disable refresh when a refresh is already in progress


import Observable from '../util/Observable';

export default class MetaControl extends Observable {

    constructor() {
        super();
        this._registry = [];
        this._controlEvents = ['engage', 'disengage'];
    }

    _createEventProxy(event) {
        return (...args) => this.fireEvent.call(this, event, ...args);
    }

    addControl(control) {
        this._registry.push(control);

        const listeners = {};
        this._controlEvents.forEach(
            event => listeners[event] = this._createEventProxy(event)
        );

        control.attachListeners(listeners, this);
    }

    removeControl(control) {
        this._registry = _.without(this._registry, control);
        control.detachAllListeners(this);
    }

    destroy() {
        this._registry.forEach(
            control => control.detachAllListeners(this)
        );
        this._registry = [];
    }

}

import Observable from '../util/Observable';
import * as _ from 'lodash';

export default class MetaControl extends Observable {

    constructor() {
        super();
        this._registry = [];
        this._controlEvents = ['engage', 'disengage'];
    }

    _createEventProxy(event) {
        return (...args) => this.fireEvent.call(this, event, ...args);
    }

    addControl(control) {
        this._registry.push(control);

        const listeners = {};
        this._controlEvents.forEach(
            event => listeners[event] = this._createEventProxy(event)
        );

        control.attachListeners(listeners, this);
    }

    removeControl(control) {
        this._registry = _.without(this._registry, control);
        control.detachAllListeners(this);
    }

    destroy() {
        this._registry.forEach(
            control => control.detachAllListeners(this)
        );
        this._registry = [];
    }

}

Add lodash dependency. Still needed for _.without


module.exports = chain;

// steps, cb
// Each step is either:
// [fn, arg, arg, arg] or
// [obj, "method", arg, arg, arg]
// The eventual cb passed to chain is resolved with an array of
// all the results, and/or an error if one was returned.
function chain () /* step, step, ..., cb */ {
  var steps = Array.prototype.slice.call(arguments, 0),
    cb = steps.pop();

  if (typeof(cb) !== "function") {
    throw new Error("Invalid callback: "+typeof(cb));
  }

  (function S (step, results) {
    if (!step) return cb(null, results);
    var obj, fn;
    if (typeof step[0] === "object") {
      obj = step.shift();
    }
    if (typeof step[0] === "function") {
      fn = step.shift();
    } else {
      fn = obj[step.shift()];
    }
    step.push(function (er, ok) {
      if (er) cb(er, results);
      else {
        results.push(ok);
        S(steps.shift(), results);
      }
    });
    try {
      fn.apply(obj, step);
    } catch (er) {
      cb(er, results);
    }
  })(steps.shift(), []);
}

module.exports = chain;

// steps, cb
// Each step is either:
// [fn, arg, arg, arg] or
// [obj, "method", arg, arg, arg]
// The eventual cb passed to chain is resolved with an array of
// all the results, and/or an error if one was returned.
function chain () /* step, step, ..., cb */ {
  var steps = Array.prototype.slice.call(arguments, 0),
    cb = steps.pop();

  if (typeof(cb) !== "function") {
    throw new Error("Invalid callback: "+typeof(cb));
  }

  (function S (step, results) {
    if (!step) return cb(null, results);
    var obj, fn;
    if (typeof step[0] === "object") {
      obj = step.shift();
    }
    if (typeof step[0] === "function") {
      fn = step.shift();
    } else {
      fn = obj[step.shift()];
    }
    if (typeof fn !== "function") throw new Error(
      "Non-function in chain().  (Did you forget a comma?)");

    step.push(function (er, ok) {
      if (er) cb(er, results);
      else {
        results.push(ok);
        S(steps.shift(), results);
      }
    });
    try {
      fn.apply(obj, step);
    } catch (er) {
      cb(er, results);
    }
  })(steps.shift(), []);
}

Add the check that would've saved me 30 minutes just now.


module.exports = {
	events: {
		'click button.delete': 'delete',
		'click button.edit': 'edit',
	},

	initialize: function() {
		this.listenTo(this.model, 'sync', this.render);
		this.listenTo(this.model, 'destroy', this.remove);
	},

	cancelEdit: function() {
		this.$el.removeClass('editing');
	},

	edit: function(e) {
		e.preventDefault();
		this.$el.siblings().removeClass('editing');
		this.listenToOnce(this.model, 'cancel', this.cancelEdit);
		this.$el.addClass('editing');
		app.trigger('edit:' + this.model.name, this.model);
	},

	delete: function(e) {
		if(confirm('Are you sure you want to delete the employee?')) {
			this.model.destroy();
			this.remove();
		}
	},
};

module.exports = {
	events: {
		'click button.delete': 'delete',
		'click button.edit': 'edit',
	},

	tagName: 'article',

	initialize: function() {
		this.listenTo(this.model, 'sync', this.render);
		this.listenTo(this.model, 'destroy', this.remove);
	},

	cancelEdit: function() {
		this.$el.removeClass('editing');
	},

	edit: function(e) {
		e.preventDefault();

		this.$el.siblings().removeClass('editing');

		this.listenToOnce(this.model, 'cancel', this.cancelEdit);

		this.$el.addClass('editing');

		this.app.trigger('edit:' + this.model.name, this.model);
	},

	delete: function(e) {
		if(confirm('Are you sure you want to delete the employee?')) {
			this.model.destroy();
			this.remove();
		}
	},
};

Use this.app instead of app and set tagName to article.


/*global __webpack_public_path__*/
/*exported  __webpack_public_path__*/
require("match-media/matchMedia.js");
require("match-media/matchMedia.addListener.js");
require("babel-polyfill");
__webpack_public_path__ = window.__GS_PUBLIC_PATH__;

/*global __webpack_public_path__*/
/*exported  __webpack_public_path__*/
require("match-media/matchMedia.js");
require("match-media/matchMedia.addListener.js");
require("babel-polyfill");
// eslint-disable-next-line no-native-reassign
__webpack_public_path__ = window.__GS_PUBLIC_PATH__;

Disable eslint for webpack global


'use babel'

/* @flow */

import Path from 'path'
import type {Ucompiler$Config} from '../types'
import {read} from './common'
import {CONFIG_FILE_NAME} from '../defaults'

export async function getConfig(directory: string): Promise<Ucompiler$Config> {
  const configPath = Path.join(directory, CONFIG_FILE_NAME)
  try {
    return JSON.parse(await read(configPath))
  } catch (_) {
    throw new Error(`Malformed JSON configuration found at ${configPath}`)
  }
}

'use babel'

/* @flow */

import Path from 'path'
import type {Ucompiler$Config, Ucompiler$Config$Rule} from '../types'
import {read} from './common'
import {CONFIG_FILE_NAME} from '../defaults'

export async function getConfig(directory: string, ruleName: ?string): Promise<{
  config: Ucompiler$Config,
  rule: Ucompiler$Config$Rule
}> {
  const configPath = Path.join(directory, CONFIG_FILE_NAME)
  let config
  let rule

  try {
    config = JSON.parse(await read(configPath))
  } catch (_) {
    throw new Error(`Malformed JSON configuration found at ${configPath}`)
  }

  if (ruleName === null) {
    ruleName = config.defaultRule
  }
  for (const ruleEntry of config.rules) {
    if (ruleEntry.name === ruleName) {
      rule = ruleEntry
      break
    }
  }

  if (!rule) {
    throw new Error(`Rule '${ruleName}' not found in config ${configPath}`)
  }

  return {config, rule}
}

Implement rule detection in getConfig


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

const commander = require('commander');
const pkg = require('../package.json');

commander
  .version(pkg.version)
  .command('start [file]', 'Start a presentation')
  .command('create [file]', 'Create a new presentation')
  .parse(process.argv);

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

const commander = require('commander');

commander
  .command('start [file]', 'Start a presentation')
  .command('create [file]', 'Create a new presentation')
  .parse(process.argv);

Remove version information from CLI


var spawn = require('child_process').spawn;
var path = require('path');

module.exports = function (options) {
    options = options || {};
    options.url = options.url || 'http://nodejs.org';
    options.name = options.name || 'nodejs';

    var client;
    var keys = [];
    switch (process.platform) {
    case 'win32':
        client = path.resolve(__dirname + '/cefclient/cefclient');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight', 'ico'];
        break;
    case 'linux':
        client = path.resolve(__dirname + '/build/default/topcube');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight'];
        break;
    default:
        console.warn('');
        return null;
        break;
    }

    var args = [];
    for (var key in options) {
        if (keys.indexOf(key) !== -1) {
            args.push('--' + key + '=' + options[key]);
        }
    }

    var child = spawn(client, args);
    child.on('exit', function(code) {
        process.exit(code);
    });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    return child;
};

var spawn = require('child_process').spawn;
var path = require('path');

module.exports = function (options) {
    options = options || {};
    options.url = options.url || 'http://nodejs.org';
    options.name = options.name || 'nodejs';

    var client;
    var keys = [];
    switch (process.platform) {
    case 'win32':
        client = path.resolve(__dirname + '/cefclient/cefclient');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight', 'ico', 'cache-path'];
        break;
    case 'linux':
        client = path.resolve(__dirname + '/build/default/topcube');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight'];
        break;
    default:
        console.warn('');
        return null;
        break;
    }

    var args = [];
    for (var key in options) {
        if (keys.indexOf(key) !== -1) {
            args.push('--' + key + '=' + options[key]);
        }
    }

    var child = spawn(client, args);
    child.on('exit', function(code) {
        process.exit(code);
    });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    return child;
};

Allow 'cache-path' key to be passed to cefclient.


import _ from 'lodash';

import {
  ADD_CATEGORY_THRESHOLD,
  REMOVE_CATEGORY_THRESHOLD,
  SET_GLOBAL_THRESHOLD,
  RESET_ADD_THRESHOLD_FORM,
} from '../constants/ActionTypes';

const initialState = {
  value: 0,
  date: null,
  categories: [],
};

export default (state = initialState, action) => {
  switch (action.type) {
    case ADD_CATEGORY_THRESHOLD:
      return {
        ...state,
        categories: [
          ...state.categories,
          {
            categoryId: action.categoryId,
            value: action.value,
          }
        ],
      };
    case REMOVE_CATEGORY_THRESHOLD:
      const filteredCategories = _.filter(state.categories, (c) => c.id !== action.categoryId);
      return {
        ...state,
        categories: filteredCategories,
      };
    case SET_GLOBAL_THRESHOLD:
      return {
        ...state,
        value: action.value
      };
    case RESET_ADD_THRESHOLD_FORM:
      return {
        ...initialState
      };
    default:
      return state;
  }
};

import _ from 'lodash';

import {
  ADD_CATEGORY_THRESHOLD,
  REMOVE_CATEGORY_THRESHOLD,
  SET_GLOBAL_THRESHOLD,
  RESET_ADD_THRESHOLD_FORM,
} from '../constants/ActionTypes';

const initialState = {
  value: 0,
  date: null,
  categories: [],
};

export default (state = initialState, action) => {
  switch (action.type) {
    case ADD_CATEGORY_THRESHOLD:
      return {
        ...state,
        categories: [
          ...state.categories,
          {
            categoryId: action.categoryId,
            value: action.value,
          }
        ],
      };
    case REMOVE_CATEGORY_THRESHOLD:
      const filteredCategories = _.filter(state.categories, (c) => c.categoryId !== action.categoryId);
      return {
        ...state,
        categories: filteredCategories,
      };
    case SET_GLOBAL_THRESHOLD:
      return {
        ...state,
        value: action.value
      };
    case RESET_ADD_THRESHOLD_FORM:
      return {
        ...initialState
      };
    default:
      return state;
  }
};

Fix reducer for removing category threshold


var { requireNativeComponent, PropTypes } = require('react-native');

var iface = {
    name: 'CircleView',
    propTypes: {
      showTextWhileSpinning: PropTypes.bool,
      autoTextColor: PropTypes.bool,
      autoTextSize: PropTypes.bool,
      showUnit: PropTypes.bool,
      сontourColor: PropTypes.string,
      barColor: PropTypes.array,
      rimColor: PropTypes.string,
      value: PropTypes.number,
      maxValue: PropTypes.number,
      valueAnimated: PropTypes.number,
      contourSize: PropTypes.number,
      barWidth: PropTypes.number,
      rimWidth: PropTypes.number,
      unitSize: PropTypes.number,
      textSize: PropTypes.number,
    },
};

module.exports = requireNativeComponent('RCTCircleView', iface);

var { requireNativeComponent, PropTypes } = require('react-native');

var iface = {
    name: 'CircleView',
    propTypes: {
      showTextWhileSpinning: PropTypes.bool,
      autoTextColor: PropTypes.bool,
      autoTextSize: PropTypes.bool,
      showUnit: PropTypes.bool,
      сontourColor: PropTypes.string,
      barColor: PropTypes.array,
      rimColor: PropTypes.string,
      value: PropTypes.number,
      maxValue: PropTypes.number,
      valueAnimated: PropTypes.number,
      contourSize: PropTypes.number,
      barWidth: PropTypes.number,
      rimWidth: PropTypes.number,
      unitSize: PropTypes.number,
      textSize: PropTypes.number,
      renderToHardwareTextureAndroid: PropTypes.bool,
      onLayout: PropTypes.bool,
      importantForAccessibility: PropTypes.string,
      accessibilityLabel: PropTypes.string,
      accessibilityLiveRegion: PropTypes.string,
      accessibilityComponentType: PropTypes.string,
      testID: PropTypes.string
    }
};

module.exports = requireNativeComponent('RCTCircleView', iface);

Update required properties for RCTCircleView


// Electron entrypoint
'use strict';
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;

let template = [
  {
    label: 'Edit',
    submenu: [
      {
        label: 'Cut',
        accelerator: 'CommandOrControl+X',
        role: 'cut'
      },
      {
        label: 'Copy',
        accelerator: 'CommandOrControl+C',
        role: 'copy'
      },
      {
        label: 'Paste',
        accelerator: 'CommandOrControl+V',
        role: 'paste'
      }
    ]
  }
];

let mainWindow;
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {

  mainWindow = new BrowserWindow();
  mainWindow.maximize();

  mainWindow.loadURL('file://' + __dirname + '/static/index.html');
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools(); // Open dev tools
  }
  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  let menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
});

// Electron entrypoint
'use strict';
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;

let template = [
  {
    label: 'Dawn',
    submenu: [
      {
        label: 'Quit',
        accelerator: 'CommandOrControl+Q',
        click: function() { app.quit(); }
      }
    ]
  },
  {
    label: 'Edit',
    submenu: [
      {
        label: 'Cut',
        accelerator: 'CommandOrControl+X',
        role: 'cut'
      },
      {
        label: 'Copy',
        accelerator: 'CommandOrControl+C',
        role: 'copy'
      },
      {
        label: 'Paste',
        accelerator: 'CommandOrControl+V',
        role: 'paste'
      }
    ]
  }
];

let mainWindow;
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {

  mainWindow = new BrowserWindow();
  mainWindow.maximize();

  mainWindow.loadURL('file://' + __dirname + '/static/index.html');
  if (process.env.NODE_ENV === 'development') {
    mainWindow.webContents.openDevTools(); // Open dev tools
  }
  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  let menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
});

Add Quit button to menu