'use strict';

var deepFind = function(obj, path) {

  if (((typeof obj !== "object") && (typeof obj !== "function")) | obj === null) {
	  return undefined;
  }
  if (typeof path === 'string') {
    path = path.split('.');
  }

  if (!Array.isArray(path)) {
    path = path.concat();
  }

  return path.reduce(function (o, part) {
    var keys = part.match(/\[(.*?)\]/);
    if (keys) {
      var key = part.replace(keys[0], '');
      return o[key][keys[1]];
    }
    return o[part];
  }, obj);
};

module.exports = deepFind;

'use strict';

var deepFind = function(obj, path) {

  if (((typeof obj !== "object") && (typeof obj !== "function")) || obj === null) {
	  return undefined;
  }
  if (typeof path === 'string') {
    path = path.split('.');
  }

  if (!Array.isArray(path)) {
    path = path.concat();
  }

  return path.reduce(function (o, part) {
    var keys = part.match(/\[(.*?)\]/);
    if (keys) {
      var key = part.replace(keys[0], '');
      return o[key][keys[1]];
    }
    return o[part];
  }, obj);
};

module.exports = deepFind;

Fix OR ( || ) in if statement


function truthy(val) { return val; }
function trim(str) { return str.trim(); }

function listToArray (str, delimiter) {
	if (!str || typeof str !== 'string') {
		return [];
	}
	if (!delimiter) {
		delimiter = ' ';
		str = str.replace(/\,/g, ' ');
	}
	return str.split(delimiter).map(trim).filter(truthy);
}

module.exports = listToArray;

function truthy(val) { return val; }
function trim(str) { return str.trim(); }

function listToArray (str, delimiter) {
	if (Array.isArray(str)) {
		return str;
	}
	if (!str || typeof str !== 'string') {
		return [];
	}
	if (!delimiter) {
		delimiter = ' ';
		str = str.replace(/\,/g, ' ');
	}
	return str.split(delimiter).map(trim).filter(truthy);
}

module.exports = listToArray;

Return an array when it's passed


const { exec } = require('@actions/exec');
const getWorkspaces = require('get-workspaces').default;

async function execWithOutput(command, args, options) {
  let myOutput = '';
  let myError = '';

  return {
    code: await exec(command, args, {
      listeners: {
        stdout: data => {
          myOutput += data.toString();
        },
        stderr: data => {
          myError += data.toString();
        }
      },

      ...options
    }),
    stdout: myOutput,
    stderr: myError
  };
}

const publishablePackages = ['xstate', '@xstate/fsm', '@xstate/test'];

(async () => {
  const currentBranchName = (await execWithOutput('git', [
    'rev-parse',
    '--abbrev-ref',
    'HEAD'
  ])).stdout.trim();

  if (!/^changeset-release\//.test(currentBranchName)) {
    return;
  }

  const latestCommitMessage = (await execWithOutput('git', [
    'log',
    '-1',
    '--pretty=%B'
  ])).stdout.trim();

  await exec('git', ['reset', '--mixed', 'HEAD~1']);

  const workspaces = await getWorkspaces();

  for (let workspace of workspaces) {
    if (publishablePackages.includes(workspace.name)) {
      continue;
    }
    await exec('git', ['checkout', '--', workspace.dir]);
  }

  await exec('git', ['add', '.']);
  await exec('git', ['commit', '-m', latestCommitMessage]);
  await exec('git', ['push', 'origin', currentBranchName, '--force']);
})();

const { exec } = require('@actions/exec');
const getWorkspaces = require('get-workspaces').default;

async function execWithOutput(command, args, options) {
  let myOutput = '';
  let myError = '';

  return {
    code: await exec(command, args, {
      listeners: {
        stdout: data => {
          myOutput += data.toString();
        },
        stderr: data => {
          myError += data.toString();
        }
      },

      ...options
    }),
    stdout: myOutput,
    stderr: myError
  };
}

const publishablePackages = [
  'xstate',
  '@xstate/fsm',
  '@xstate/graph',
  '@xstate/test'
];

(async () => {
  const currentBranchName = (
    await execWithOutput('git', ['rev-parse', '--abbrev-ref', 'HEAD'])
  ).stdout.trim();

  if (!/^changeset-release\//.test(currentBranchName)) {
    return;
  }

  const latestCommitMessage = (
    await execWithOutput('git', ['log', '-1', '--pretty=%B'])
  ).stdout.trim();

  await exec('git', ['reset', '--mixed', 'HEAD~1']);

  const workspaces = await getWorkspaces();

  for (let workspace of workspaces) {
    if (publishablePackages.includes(workspace.name)) {
      continue;
    }
    await exec('git', ['checkout', '--', workspace.dir]);
  }

  await exec('git', ['add', '.']);
  await exec('git', ['commit', '-m', latestCommitMessage]);
  await exec('git', ['push', 'origin', currentBranchName, '--force']);
})();

Add @xstate/graph to changeset-managed packages


function ArraySeq(array) {
    this.array = array;
    this.counter = 0;
}
Object.defineProperty(ArraySeq.prototype, "atEnd", {
    get: function() {
        return this.counter == this.array.length;
    }
});
ArraySeq.prototype.getNext = function() {
    return this.array[this.counter++];
}

function RangeSeq(from, to) {
    this.array = [];
    for (var i = from; i <= to; ++i)
        this.array.push(i);
    this.counter = 0;
}
Object.defineProperty(RangeSeq.prototype, "atEnd", {
    get: function() {
        return this.counter == this.array.length;
    }
});
RangeSeq.prototype.getNext = function() {
    return this.array[this.counter++];
}

function logFive(sequence) {
    for (var i = 0; i < 5; ++i) {
        if (sequence.atEnd)
            break;
        else
            console.log(sequence.getNext());
    }
}

class Group {
    constructor() {
        this.members = [];
    }

    add(elem) {
        // Could use Group.has() here instead
        if (!this.members.includes(elem)) {
            this.members.push(elem);
        }
    }

    delete(elem) {
        // Don't really need if check here, filter if element is not there is
        // no-op
        if (this.members.includes(elem)) {
            this.members = this.members.filter(e => e !== elem);
        }
    }

    has(elem) {
        return this.members.includes(elem);
    }

    // Better name: collection
    static from(iterable) {
        let group = new Group();
        for (let elem of iterable) {
            group.add(elem);
        }
        return group;
    }

    [Symbol.iterator]() {
        return new GroupIterator(this);
    }
}

class GroupIterator {
    constructor(group) {
        this.idx = 0;
        this.group = group;
    }

    next() {
        if (this.idx == this.group.members.length) return {done: true};

        let value = this.group.members[this.idx];
        ++this.idx;
        return {value, done: false};
    }
}

Add Chapter 06, exercise 3


'use strict'

var isPresent = require('is-present')
var hasClass = require('has-class-selector')
var SelectorTokenizer = require('css-selector-tokenizer')

module.exports = function classRepeat (selector, options) {
  if (typeof selector !== 'string') {
    throw new TypeError('class-repeat expects a string')
  }

  options = options || {}
  options.repeat = options.repeat || 2

  if (hasClass(selector)) {
    var tokens = SelectorTokenizer.parse(selector).nodes[0] || { nodes: [] }
    var tokensWithRepeatedClasses = []
    tokens.nodes.map(function (node, index) {
      if (node.type === 'class' || isPseudo(node)) {
        for (var i = 0; i < options.repeat; i++) {
          tokensWithRepeatedClasses.push(node)

          var nextToken = tokens.nodes[index + 1]
        }
      } else {
        tokensWithRepeatedClasses.push(node)
      }
    })

    return SelectorTokenizer.stringify({
      type: 'selectors',
      nodes: [{
        type: 'selector',
        nodes: tokensWithRepeatedClasses
      }]
    })
  } else {
    return selector
  }
}

function isPseudo(token) {
  return token && (token.type === 'pseudo-element' || token.type === 'pseudo-class')
}

'use strict'

var isPresent = require('is-present')
var hasClass = require('has-class-selector')
var SelectorTokenizer = require('css-selector-tokenizer')

module.exports = function classRepeat (selector, options) {
  if (typeof selector !== 'string') {
    throw new TypeError('class-repeat expects a string')
  }

  options = options || {}
  options.repeat = options.repeat || 2

  if (hasClass(selector)) {
    var tokens = SelectorTokenizer.parse(selector).nodes[0] || { nodes: [] }
    var tokensWithRepeatedClasses = []
    tokens.nodes.map(function (node, index) {
      if (node.type === 'class' || isPseudo(node)) {
        for (var i = 0; i < options.repeat; i++) {
          tokensWithRepeatedClasses.push(node)
        }
      } else {
        tokensWithRepeatedClasses.push(node)
      }
    })

    return SelectorTokenizer.stringify({
      type: 'selectors',
      nodes: [{
        type: 'selector',
        nodes: tokensWithRepeatedClasses
      }]
    })
  } else {
    return selector
  }
}

function isPseudo(token) {
  return token && (token.type === 'pseudo-element' || token.type === 'pseudo-class')
}

Remove no-longer-necessary line of code


const Rx = require('rx');
const hound = require('hound');
const jp = require('jupyter-paths');

const watcher = hound.watch(jp.paths.runtime[0]);

function isKernelJSON(filepath) {
    var name = path.basename(filepath);
    return name.startsWith('kernel') && name.endsWith('.json');
}

const source = Rx.Observable.fromEvent(watcher, 'create')
                .filter(function(value, index, observable) {
                    return isKernelJSON(value);
                });

const observer = Rx.Observer.create(
    function (value) {
        callback(value);
    },
    function (error) {
        console.log(error);
    }
);

module.exports = Rx.Subject.create(observer, source);

const Rx = require('rx');
const hound = require('hound');
const jp = require('jupyter-paths');

const watcher = hound.watch(jp.paths.runtime[0]);

function isKernelJSON(filepath) {
    var name = path.basename(filepath);
    return name.startsWith('kernel') && name.endsWith('.json');
}

const source = Rx.Observable.fromEvent(watcher, 'create')
                .filter(function(value, index, observable) {
                    return isKernelJSON(value);
                });

const observer = Rx.Observer.create(
    function (value) {
        return value;
    },
    function (error) {
        console.log(error);
    }
);

module.exports = Rx.Subject.create(observer, source);

Update onNext function in Observer


// @private
// Calls the callback passing to it the Backbone object every time it's detected.
// The function uses multiple methods of detection.
var patchDefine = function(callback) {

    // AMD
    patchFunctionLater(window, "define", function(originalFunction) { return function() {
        // function arguments: (id? : String, dependencies? : Array, factory : Function)

        // make arguments editable
        var argumentsArray = Array.prototype.slice.call(arguments);
        // find the factory function to patch it
        for (var i=0,l=argumentsArray.length; i<l; i++) {
            if (typeof argumentsArray[i] == "function") {
                // factory function found, patch it.
                // NOTE: in the patcher function, specify the parameters for the
                // default modules, or in case of a module with no dependencies but
                // that uses the default modules internally, the original define would see a 0-arity
                // function and would call it without them (see define() in the AMD API)
                patchFunction(argumentsArray, i, function(originalFunction) {
                  return function(require, exports, modules) {
                    var module = originalFunction.apply(this, arguments);

                    // check if Backbone has been defined by the factory fuction
                    // (some factories set "this" to Backbone)
                    var BackboneCandidate = module || this;
                    callback(BackboneCandidate);
                    return module;
                }});

                break;
            }
        }
        return originalFunction.apply(this, argumentsArray);
    }});
};

// @private
// Calls the callback passing to it the Backbone object every time it's detected.
// The function uses multiple methods of detection.
var patchDefine = function(callback) {

    // AMD
    patchFunctionLater(window, "define", function(originalFunction) { return function() {
        // function arguments: (id? : String, dependencies? : Array, factory : Function)

        // make arguments editable
        var argumentsArray = Array.prototype.slice.call(arguments);
        // find the factory function to patch it
        for (var i=0,l=argumentsArray.length; i<l; i++) {
            if (typeof argumentsArray[i] == "function") {
                // factory function found, patch it.
                // NOTE: in the patcher function, specify the parameters for the
                // default modules, or in case of a module with no dependencies but
                // that uses the default modules internally, the original define would see a 0-arity
                // function and would call it without them (see define() in the AMD API)
                patchFunction(argumentsArray, i, function(originalFunction) {
                  return function(require, exports, modules) {
                    var module = originalFunction.apply(this, arguments);

                    // check if Backbone has been defined by the factory fuction
                    // (some factories set "this" to Backbone)
                    var BackboneCandidate = module || this;//

                    var isBackbone = isObject(BackboneCandidate) &&
                                     typeof BackboneCandidate.View == "function" &&
                                     typeof BackboneCandidate.Model == "function" &&
                                     typeof BackboneCandidate.Collection == "function" &&
                                     typeof BackboneCandidate.Router == "function";
                    if (isBackbone) {
                        callback(BackboneCandidate);
                    }

                    return module;
                }});

                break;
            }
        }
        return originalFunction.apply(this, argumentsArray);
    }});
};

Fix amdDefine patch to check for backboneCandidate


'use strict';

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

I18n.locale = RNI18n.locale;

module.exports = I18n;

'use strict';

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

I18n.locale = RNI18n.locale.replace(/_/, '-');

module.exports = I18n;

Convert en_US to en-US for i18n.js


'use strict';

import {Platform, NativeModules} from "react-native";
const DocumentPicker = NativeModules.RNDocumentPicker;

/**
 * Android requires mime types, iOS is a bit more complicated:
 *
 * @see https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
 */
class DocumentPickerUtil {
  static allFiles() {
    return (Platform.OS === 'android') ? "*/*" : "public.content";
  }

  static images() {
    return (Platform.OS === 'android') ? "image/*" : "public.image";
  }

  static plainText() {
    return (Platform.OS === 'android') ? "text/plain" : "public.plain-text";
  }

  static audio() {
    return (Platform.OS === 'android') ? "audio/*" : "public.audio";
  }

  static pdf() {
    return (Platform.OS === 'android') ? "application/pdf" : "com.adobe.pdf";
  }
}

module.exports = {DocumentPickerUtil, DocumentPicker};

'use strict';
import {Platform, NativeModules} from "react-native";
const {RNDocumentPicker} = NativeModules;

if (!RNDocumentPicker) {
  // Use a timeout to ensure the warning is displayed in the YellowBox
  setTimeout(() => {
    console.warn('RNDocumentPicker: Native module is not available, make sure you have finished the installation process and rebuilt your app');
  }, 0);
}

class DocumentPicker {
  static show(opts) {
    opts = opts || {};
    return RNDocumentPicker.show(opts);
  }
}

/**
 * Android requires mime types, iOS is a bit more complicated:
 *
 * @see https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
 */
class DocumentPickerUtil {
  static allFiles() {
    return (Platform.OS === 'android') ? "*/*" : "public.content";
  }

  static images() {
    return (Platform.OS === 'android') ? "image/*" : "public.image";
  }

  static plainText() {
    return (Platform.OS === 'android') ? "text/plain" : "public.plain-text";
  }

  static audio() {
    return (Platform.OS === 'android') ? "audio/*" : "public.audio";
  }

  static pdf() {
    return (Platform.OS === 'android') ? "application/pdf" : "com.adobe.pdf";
  }
}

module.exports = {DocumentPickerUtil, DocumentPicker};

Add a warning when native code is not loaded.


var player = require('chromecast-player')()

player.attach(function (err, p) {
  if (err) console.error('An error ocurred while attaching the player: ' + err)
  var playButton = document.querySelector('.play')
  var playButtonIcon = document.querySelector('.play i.fa');

  playButton.addEventListener('click', function () {
    p.getStatus(function (err, status) {
      if (status.playerState === 'PLAYING') {
        p.pause(function () {
          playButtonIcon.classList.remove('fa-play')
          playButtonIcon.classList.add('fa-pause')
        })
      } else {
        p.play(function () {
          playButtonIcon.classList.remove('fa-pause')
          playButtonIcon.classList.add('fa-play')
        })
      }
    })
  })
})

var player = require('chromecast-player')()

player.attach(function (err, p) {
  if (err) console.error('An error ocurred while attaching the player: ' + err)
  var playButton = document.querySelector('.play')
  var playButtonIcon = document.querySelector('.play i.fa')

  playButton.addEventListener('click', function () {
    p.getStatus(function (err, status) {
      if (err) console.error('An error ocurred while getting status of player: ' + err)
      if (status.playerState === 'PLAYING') {
        p.pause(function () {
          playButtonIcon.classList.remove('fa-play')
          playButtonIcon.classList.add('fa-pause')
        })
      } else {
        p.play(function () {
          playButtonIcon.classList.remove('fa-pause')
          playButtonIcon.classList.add('fa-play')
        })
      }
    })
  })
})

Handle errors and fix code styling


var through   = require('through2');
var gutil     = require('gulp-util');
var extend    = require('extend');
var vinylFile = require('vinyl-file');

var StringDecoder = require('string_decoder').StringDecoder;
var Analyzer      = require('./Analyzer');
var Logger        = require('./Logger');

var resolver = null;

module.exports = function(config) {
	var firstRun = false;

	config = extend(true, {
		stopperOnFirstRun: true,
		debug: false
	}, config);

	Logger.level = config.debug ? Logger.DEBUG : Logger.WARN;

	if (resolver === null) {
		resolver = new Analyzer(config);
		firstRun = true;
	}

	return through.obj(function(file, enc, cb) {
		var decoder = new StringDecoder(enc);
		var content = decoder.write(file.contents);

		var dependencies = resolver.resolveDependencies(content, file.path);

		if (!firstRun) {
			for (var i = 0; i < dependencies.length; i++) {
				var dependency = dependencies[i];
				var dependencyFile = vinylFile.readSync(dependency, {
					cwd: file.cwd,
					base: file.base
				});

				this.push(dependencyFile);
			}
		}

		if (!config.stopperOnFirstRun) {
			this.push(file);
		}

		cb();
	});
};

var through   = require('through2');
var gutil     = require('gulp-util');
var extend    = require('extend');
var vinylFile = require('vinyl-file');

var StringDecoder = require('string_decoder').StringDecoder;
var Analyzer      = require('./Analyzer');
var Logger        = require('./Logger');

var resolver = null;

module.exports = function(config) {
	var firstRun = false;

	config = extend(true, {
		stopperOnFirstRun: true,
		debug: false
	}, config);

	Logger.level = config.debug ? Logger.DEBUG : Logger.WARN;

	if (resolver === null) {
		resolver = new Analyzer(config);
		firstRun = true;
	}

	return through.obj(function(file, enc, cb) {
		var decoder = new StringDecoder(enc);
		var content = decoder.write(file.contents);

		var dependencies = resolver.resolveDependencies(content, file.path);

		if (!firstRun) {
			for (var i = 0; i < dependencies.length; i++) {
				var dependency = dependencies[i];
				var dependencyFile = vinylFile.readSync(dependency, {
					cwd: file.cwd,
					base: file.base
				});

				this.push(dependencyFile);
			}
		}

		if (!firstRun || !config.stopperOnFirstRun) {
			this.push(file);
		}

		cb();
	});
};

Fix pushing the original files back on the stream


var browserify = require('browserify')
  , shim       = require('browserify-shim')
  , hbsfy      = require('hbsfy')
  , envify     = require('envify')
  , brfs       = require('brfs')

module.exports = function (opts, cb) {
  if (!opts.shim) opts.shim = {}
  var bundle = shim(browserify(), opts.shim)
  bundle.require(require.resolve(opts.entry), {entry: true})
  opts.transforms = [envify, hbsfy, brfs].concat(opts.transforms || [])
  opts.transforms.forEach(function (transform) {
    bundle.transform(transform)
  })
  bundle.bundle({debug: opts.debug || false}, cb)
}

var browserify = require('browserify')
  , shim       = require('browserify-shim')
  , hbsfy      = require('hbsfy')
  , envify     = require('envify')
  , brfs       = require('brfs')

module.exports = function (opts, cb) {
  if (!opts.shim) opts.shim = {}
  var bundle = shim(browserify(), opts.shim)

  bundle.require(require.resolve(opts.entry), {entry: true})

  // ensure brfs runs last because it requires valid js
  opts.transforms = [envify, hbsfy].concat(opts.transforms || []).concat(brfs)
  opts.transforms.forEach(function (transform) {
    bundle.transform(transform)
  })

  bundle.bundle({debug: opts.debug || false}, cb)
}

Make brfs run after all other transforms


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

var SassLinter = require('broccoli-sass-lint');
var mergeTrees = require('broccoli-merge-trees');

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

  included: function(app) {
    if (!app.isTestingSassLintAddon) {
      this._super.included(app);
    }

    this.app = app;
    this.sassLintOptions = app.options.sassLint || {};
  },

  lintTree: function(type, tree) {
    var formattedResults, mergedTrees, results;

    if (type === 'app') {
      mergedTrees = mergeTrees([this.app.trees.styles]);

      return new SassLinter(mergedTrees, this.sassLintOptions);
    } else {
      return tree;
    }
  },
};

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

var SassLinter = require('broccoli-sass-lint');
var mergeTrees = require('broccoli-merge-trees');

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

  included: function(app) {
    if (!app.isTestingSassLintAddon) {
      this._super.included(app);
    }

    this.app = app;
    this.sassLintOptions = app.options.sassLint || {};
  },

  lintTree: function(type, tree) {
    var mergedTrees;

    if (type === 'app') {
      mergedTrees = mergeTrees([this.app.trees.styles]);

      return new SassLinter(mergedTrees, this.sassLintOptions);
    } else {
      return this.app.trees.styles;
    }
  },
};

Fix "No tests were run"


/**
 * espower-source - Power Assert instrumentor from source to source.
 *
 * https://github.com/twada/espower-source
 *
 * Copyright (c) 2014 Takuto Wada
 * Licensed under the MIT license.
 *   https://raw.github.com/twada/espower-source/master/MIT-LICENSE.txt
 */
var espower = require('espower'),
    esprima = require('esprima'),
    escodegen = require('escodegen'),
    merge = require('lodash.merge'),
    sourceMap = require('convert-source-map');

function espowerSourceToSource(jsCode, filepath, options) {
    'use strict';

    var jsAst, espowerOptions, modifiedAst, escodegenOutput, code, map;

    jsAst = esprima.parse(jsCode, {tolerant: true, loc: true, tokens: true, source: filepath});
    espowerOptions = merge(merge(espower.defaultOptions(), options), {
        destructive: true,
        path: filepath,
        source: jsCode
    });
    modifiedAst = espower(jsAst, espowerOptions);
    escodegenOutput = escodegen.generate(modifiedAst, {
        sourceMap: true,
        sourceMapWithCode: true
    });
    code = escodegenOutput.code; // Generated source code
    map = sourceMap.fromJSON(escodegenOutput.map);
    map.sourcemap.sourcesContent = [jsCode];
    return code + '\n' + map.toComment() + '\n';
}

module.exports = espowerSourceToSource;

/**
 * espower-source - Power Assert instrumentor from source to source.
 *
 * https://github.com/twada/espower-source
 *
 * Copyright (c) 2014 Takuto Wada
 * Licensed under the MIT license.
 *   https://raw.github.com/twada/espower-source/master/MIT-LICENSE.txt
 */
var espower = require('espower'),
    esprima = require('esprima'),
    escodegen = require('escodegen'),
    merge = require('lodash.merge'),
    sourceMap = require('convert-source-map');

function espowerSourceToSource(jsCode, filepath, options) {
    'use strict';

    var jsAst, espowerOptions, modifiedAst, escodegenOutput, code, map;

    jsAst = esprima.parse(jsCode, {tolerant: true, loc: true, tokens: true, raw: true, source: filepath});
    espowerOptions = merge(merge(espower.defaultOptions(), options), {
        destructive: true,
        path: filepath,
        source: jsCode
    });
    modifiedAst = espower(jsAst, espowerOptions);
    escodegenOutput = escodegen.generate(modifiedAst, {
        sourceMap: true,
        sourceMapWithCode: true
    });
    code = escodegenOutput.code; // Generated source code
    map = sourceMap.fromJSON(escodegenOutput.map);
    map.sourcemap.sourcesContent = [jsCode];
    return code + '\n' + map.toComment() + '\n';
}

module.exports = espowerSourceToSource;

Add raw option (even if ignored by esprima 1.1.x)


import React from 'react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import { line } from 'd3-shape';

LinePath.propTypes = {
  innerRef: PropTypes.func,
  data: PropTypes.array,
  curve: PropTypes.func,
  defined: PropTypes.oneOf([PropTypes.func, PropTypes.bool]),
  x: PropTypes.oneOfType([PropTypes.func, PropTypes.number]),
  y: PropTypes.oneOfType([PropTypes.func, PropTypes.number])
};

export default function LinePath({
  children,
  data,
  x,
  y,
  fill = 'transparent',
  className,
  curve,
  innerRef,
  defined = () => true,
  ...restProps
}) {
  const path = line();
  if (x) path.x(x);
  if (y) path.y(y);
  if (defined) path.defined(defined);
  if (curve) path.curve(curve);
  if (children) return children({ path });
  return (
    <path
      ref={innerRef}
      className={cx('vx-linepath', className)}
      d={path(data)}
      fill={fill}
      {...restProps}
    />
  );
}

import React from 'react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import { line } from 'd3-shape';

LinePath.propTypes = {
  innerRef: PropTypes.func,
  data: PropTypes.array,
  curve: PropTypes.func,
  defined: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
  x: PropTypes.oneOfType([PropTypes.func, PropTypes.number]),
  y: PropTypes.oneOfType([PropTypes.func, PropTypes.number])
};

export default function LinePath({
  children,
  data,
  x,
  y,
  fill = 'transparent',
  className,
  curve,
  innerRef,
  defined = () => true,
  ...restProps
}) {
  const path = line();
  if (x) path.x(x);
  if (y) path.y(y);
  if (defined) path.defined(defined);
  if (curve) path.curve(curve);
  if (children) return children({ path });
  return (
    <path
      ref={innerRef}
      className={cx('vx-linepath', className)}
      d={path(data)}
      fill={fill}
      {...restProps}
    />
  );
}

Fix proptype for linepath 'defined' prop


var strftime = require('prettydate').strftime;

module.exports = function generateCommonLog(request, response)
{
    var protocol = 'HTTP/' + request.httpVersion;
    var tstamp =  '[' + strftime(new Date(response._time), '%d/%b/%Y:%H:%M:%S %z') + ']';
    var requestline = '"' + [request.method, request.url, protocol].join(' ') + '"';
    var payload_len = response._data ? response._data.length : '-';

    return [request.socket.remoteAddress, '-', '-', tstamp, requestline, response.statusCode, payload_len].join(' ');
};

var strftime = require('prettydate').strftime;

module.exports = function generateCommonLog(request, response)
{
    var protocol = 'HTTP/' + request.httpVersion;
    var payload_len = response._data ? response._data.length : '-';
    var UA = request.headers['user-agent'] || '';
    var referer = request.headers['referer'] || '';

    var fields = [
        request.socket.remoteAddress, // client ip
        '-',  // RFC 1413, never used
        '-',   // userid as determined by http auth
        '[' + strftime(new Date(response._time), '%d/%b/%Y:%H:%M:%S %z') + ']', // time
        '"' + [request.method, request.url, protocol].join(' ') + '"',
        response.statusCode,
        payload_len,
        '"' + referer + '"',
        '"' + UA + '"',
    ];

    return fields.join(' ');
};

Add user agent & referrer.


#!/usr/bin/env node
'use strict'
const commitStream = require('commit-stream')
const listStream = require('list-stream')
const program = require('commander')
const split2 = require('split2')

const spawn = require('child_process').spawn

const version = require('./package').version

program
  .version(version)
  .parse(process.argv)

spawn('bash', ['-c', 'git log'])
  .stdout
    .pipe(split2())
    .pipe(commitStream())
    .pipe(listStream.obj(onCommitList))

function onCommitList (error, commitList) {
  if (error) throw error

  commitList.forEach(function (commit) {
    console.log(commit)
  })
}

#!/usr/bin/env node
'use strict'
const commitStream = require('commit-stream')
const listStream = require('list-stream')
const program = require('commander')
const split2 = require('split2')

const spawn = require('child_process').spawn

const pkg = require('./package')

program
  .version(pkg.version)
  .description(pkg.description)
  .parse(process.argv)

spawn('bash', ['-c', 'git log'])
  .stdout
    .pipe(split2())
    .pipe(commitStream())
    .pipe(listStream.obj(onCommitList))

function onCommitList (error, commitList) {
  if (error) throw error

  commitList.forEach(function (commit) {
    console.log(commit)
  })
}

Add description to command line help


const express = require('express');

const app = express();

app.get('/', (req, res) => {
  const ip = req.ip;
  const acceptLanguage = req.get('Accept-Language');
  const userAgent = req.get('User-Agent');
  res.send({
    'IP address': ip.substring(ip.lastIndexOf(':') + 1, ip.length),
    language: acceptLanguage.split(',')[0],
    OS: userAgent.substring(userAgent.indexOf('(') + 1, userAgent.indexOf(')')),
  });
});

app.listen(process.env.PORT || 8000, () => console.log('App is running on http://localhost:8000'));

const express = require('express');

const app = express();

app.get('/', (req, res) => {
  const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
  const acceptLanguage = req.get('Accept-Language');
  const userAgent = req.get('User-Agent');
  res.send({
    'IP address': ip,
    language: acceptLanguage.split(',')[0],
    OS: userAgent.substring(userAgent.indexOf('(') + 1, userAgent.indexOf(')')),
  });
});

app.listen(process.env.PORT || 8000, () => console.log('App is running on http://localhost:8000'));

Fix IP address fetching bug


'use strict';

import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
const { RNPusherPushNotifications } = NativeModules
const rnPusherPushNotificationsEmitter = new NativeEventEmitter(RNPusherPushNotifications)

export default {
  setAppKey: (appKey) => {
    RNPusherPushNotifications.setAppKey(appKey)
  },
  subscribe: (channel, onError, onSuccess) => {
    if (Platform.os === 'ios') {
      RNPusherPushNotifications.subscribe(channel)
    } else {
      RNPusherPushNotifications.subscribe(channel, onError, onSuccess)
    }
  },
  unsubscribe: (channel, onError, onSuccess) => {
    if (Platform.os === 'ios') {
      RNPusherPushNotifications.unsubscribe(channel)
    } else {
      RNPusherPushNotifications.unsubscribe(channel, onError, onSuccess)
    }
  },
  on: (eventName, callback) => {
    rnPusherPushNotificationsEmitter.addListener(
      eventName,
      (payload) => callback(payload)
    )
  }
};

'use strict';

import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
const { RNPusherPushNotifications } = NativeModules
const rnPusherPushNotificationsEmitter = new NativeEventEmitter(RNPusherPushNotifications)

export default {
  setAppKey: (appKey) => {
    RNPusherPushNotifications.setAppKey(appKey)
  },
  subscribe: (channel, onError, onSuccess) => {
    if (Platform.OS === 'ios') {
      RNPusherPushNotifications.subscribe(channel)
    } else {
      RNPusherPushNotifications.subscribe(channel, onError, onSuccess)
    }
  },
  unsubscribe: (channel, onError, onSuccess) => {
    if (Platform.OS === 'ios') {
      RNPusherPushNotifications.unsubscribe(channel)
    } else {
      RNPusherPushNotifications.unsubscribe(channel, onError, onSuccess)
    }
  },
  on: (eventName, callback) => {
    rnPusherPushNotificationsEmitter.addListener(
      eventName,
      (payload) => callback(payload)
    )
  }
};

Fix typo in Platform check


/**
 * power-assert.js - Power Assert in JavaScript.
 *
 * https://github.com/power-assert-js/power-assert
 *
 * Copyright (c) 2013-2016 Takuto Wada
 * Licensed under the MIT license.
 *   https://github.com/power-assert-js/power-assert/blob/master/MIT-LICENSE.txt
 */
'use strict';

var baseAssert = require('assert');
var empower = require('empower');
var formatter = require('power-assert-formatter');
var extend = require('xtend');
var define = require('define-properties');
var empowerOptions = {
    modifyMessageOnRethrow: true,
    saveContextOnRethrow: true
};

function customize (customOptions) {
    var options = customOptions || {};
    var poweredAssert = empower(
        baseAssert,
        formatter(options.output),
        extend(empowerOptions, options.assertion)
    );
    poweredAssert.customize = customize;
    return poweredAssert;
}

var defaultAssert = customize();
define(defaultAssert, { '__esModule': true });
defaultAssert.default = defaultAssert;
module.exports = defaultAssert;

/**
 * power-assert.js - Power Assert in JavaScript.
 *
 * https://github.com/power-assert-js/power-assert
 *
 * Copyright (c) 2013-2016 Takuto Wada
 * Licensed under the MIT license.
 *   https://github.com/power-assert-js/power-assert/blob/master/MIT-LICENSE.txt
 */
'use strict';

var baseAssert = require('assert');
var empower = require('empower');
var formatter = require('power-assert-formatter');
var extend = require('xtend');
var define = require('define-properties');
var empowerOptions = {
    modifyMessageOnRethrow: true,
    saveContextOnRethrow: true
};

function customize (customOptions) {
    var options = customOptions || {};
    var poweredAssert = empower(
        baseAssert,
        formatter(options.output),
        extend(empowerOptions, options.assertion)
    );
    poweredAssert.customize = customize;
    return poweredAssert;
}

var defaultAssert = customize();
define(defaultAssert, { '__esModule': true });
defaultAssert['default'] = defaultAssert;
module.exports = defaultAssert;

Access property `default` using bracket notation


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

module.exports = {
  name: 'ember-pikaday',

  options: {
    nodeAssets: {
      pikaday: {
        import: ['pikaday.js', 'css/pikaday.css']
      }
    }
  }
};

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

module.exports = {
  name: 'ember-pikaday',

  options: {
    nodeAssets: {
      pikaday: {
        import: [
          { enabled: process.env.EMBER_CLI_FASTBOOT !== 'true', path: 'pikaday.js' },
          'css/pikaday.css'
        ]
      }
    }
  }
};

Remove pikaday.js from fastboot build


const ObservableQueryPaginator = require("./QueryPaginator").ObservableQueryPaginator
const CreateMySQLConnectionFromConfigFile =
    require("./ConnectionUtils").CreateMySQLConnectionFromConfigFile
const Database = require("./Database")

module.exports = {
    ObservableQueryPaginator: ObservableQueryPaginator,
    CreateMySQLConnectionFromConfigFile: CreateMySQLConnectionFromConfigFile,
    AbstractMySQLDatabase: Database.AbstractMySQLDatabase,
    runWithDB: runWithDB
}

const ObservableQueryPaginator = require("./QueryPaginator").ObservableQueryPaginator
const CreateMySQLConnectionFromConfigFile =
    require("./ConnectionUtils").CreateMySQLConnectionFromConfigFile
const Database = require("./Database")

module.exports = {
    ObservableQueryPaginator: ObservableQueryPaginator,
    CreateMySQLConnectionFromConfigFile: CreateMySQLConnectionFromConfigFile,
    AbstractMySQLDatabase: Database.AbstractMySQLDatabase,
    runWithDB: Database.runWithDB
}

Fix runWithDB not exported correctly


/**
 * Module dependencies
 */

var crypto = require('crypto');

/**
 * 62 characters in the ascii range that can be used in URLs without special
 * encoding.
 */
var UIDCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

/**
 * Make a Buffer into a string ready for use in URLs
 *
 * @param {String}
 * @returns {String}
 * @api private
 */
function tostr(bytes) {
  var chars, r, i;

  r = [];
  for (i = 0; i < bytes.length; i++) {
    r.push(UIDCHARS[bytes[i] % UIDCHARS.length]);
  }

  return r.join('');
}

/**
 * Generate an Unique Id
 *
 * @param {Number} length  The number of chars of the uid
 * @param {Number} cb (optional)  Callback for async uid generation
 * @api public
 */

function uid(length, cb) {

  if (typeof cb === 'undefined') {
    return tostr(crypto.randomBytes(length));
  } else {
    crypto.randomBytes(length, function(err, bytes) {
       if (err) return cb(err);
       cb(null, tostr(bytes));
    })
  }
}

/**
 * Exports
 */

module.exports = uid;

/**
 * Module dependencies
 */

var crypto = require('crypto');

/**
 * 62 characters in the ascii range that can be used in URLs without special
 * encoding.
 */
var UIDCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

/**
 * Make a Buffer into a string ready for use in URLs
 *
 * @param {String}
 * @returns {String}
 * @api private
 */
function tostr(bytes) {
  var chars, r, i;

  r = [];
  for (i = 0; i < bytes.length; i++) {
    r.push(UIDCHARS[bytes[i] % UIDCHARS.length]);
  }

  return r.join('');
}

/**
 * Generate an Unique Id
 *
 * @param {Number} length  The number of chars of the uid
 * @param {Number} cb (optional)  Callback for async uid generation
 * @api public
 */

function uid(length, cb) {

  if (typeof cb === 'undefined') {
    return tostr(crypto.pseudoRandomBytes(length));
  } else {
    crypto.pseudoRandomBytes(length, function(err, bytes) {
       if (err) return cb(err);
       cb(null, tostr(bytes));
    })
  }
}

/**
 * Exports
 */

module.exports = uid;

Use pseudoRandomBytes, which maps to OpenSSL's RAND_pseudo_bytes which for uid generation is fine


var util = require('util')
  , stream = require('stream')
var isstream = require('isstream')

util.inherits(BulkStream, stream.Readable)
module.exports = BulkStream

function BulkStream () {
  stream.Readable.call(this)

  var self = this
  self._items = []

  process.nextTick(function () {
    self.start()
  })
}

BulkStream.prototype.append = function (item) {
  if (isstream(item)) {
    item.pause()
  }
  this._items.push(item)
}

BulkStream.prototype.start = function () {
  var self = this

  var read = function (index) {
    if (index === self._items.length) {
      process.nextTick(function () {
        self.push(null)
      })
      return
    }

    var item = self._items[index]
    if (isstream(item)) {
      item.on('data', function (chunk) {
        self.push(chunk)
      })
      item.on('end', function () {
        read(++index)
      })
      item.resume()
    }
    else {
      self.push(item)
      read(++index)
    }
  }

  read(0)
}

BulkStream.prototype._read = function (n) {}

var util = require('util')
  , stream = require('stream')
var isstream = require('isstream')

util.inherits(BulkStream, stream.Readable)
module.exports = BulkStream

function BulkStream () {
  stream.Readable.call(this)

  var self = this
  self._items = []
  self._streams = []
  self._index = 0
}

BulkStream.prototype.append = function (item) {
  var self = this
  if (isstream(item)) {
    item.pause()
    self._streams.push(item)
  }
  self._items.push(item)
}

BulkStream.prototype._read = function (n) {
  var self = this

  if (self._index === self._items.length) {
    // process.nextTick(function () {
    //   self.push(null)
    // })
    return
  }

  var item = self._items[self._index]
  if (item._reading) {
    return
  }

  if (isstream(item)) {
    item._reading = true
    item.on('readable', function () {
      var chunk = item.read()
      if (chunk === null) {
        self._index++
        self._read()
      }
      else {
        self.push(chunk)
      }
    })
  }
  else {
    self._index++
    self.push(item)
  }
}

Refactor to use the _read method


const logger = require('winston');
const request = require('request-promise');

class Service {
  constructor (options) {
    this.options = options || {};
  }

  setup(app) {
    this.app = app;
  }

  get(issueKey) {
    return this.request(issueKey);
  }

  /**
   * Perform the request to the Jenkins instance based on the configuration in order to retreive the status of an issue.
   * @param issueKey The key of the issue to get the status for
   * @returns {Promise} A promise which resolves with the current status for the issue.
   */
  request(issueKey) {
    let requestOptions = {
      uri: `${this.options.baseUrl}/job/${this.options.jobNameTemplate.replace('{key}', issueKey)}/api/json?tree=builds[number,url,timestamp,id,building,result]`,
      json: true,
      transform: this.transform
    };

    if (this.options.auth && this.options.auth.type !== 'none') {
      requestOptions.auth = this.options.auth;
    }

    logger.info('Requesting status from', requestOptions.uri);

    return request(requestOptions);
  }

  /**
   * Transforms the response body to a normalized status
   * @param body The body of the response from Jenkins.
   * @returns {Array} An array containing the normalized status.
   */
  transform(body) {
    // TODO transform status
    return body;
  }
}

module.exports = function (options) {
  return new Service(options);
};

module.exports.Service = Service;

const logger = require('winston');
const request = require('request-promise');
const Status = require('../../models/status');

class JenkinsStatusesService {
  constructor (options) {
    this.options = options || {};
  }

  setup(app) {
    this.app = app;
  }

  get(issueKey) {
    return this.request(issueKey);
  }

  /**
   * Perform the request to the Jenkins instance based on the configuration in order to retreive the status of an issue.
   * @param issueKey The key of the issue to get the status for
   * @returns {Promise} A promise which resolves with the current status for the issue.
   */
  request(issueKey) {
    let requestOptions = {
      uri: `${this.options.baseUrl}/job/${this.options.jobNameTemplate.replace('{key}', issueKey)}/api/json?tree=builds[number,url,timestamp,id,building,result]`,
      json: true,
      transform: this.transform
    };

    if (this.options.auth && this.options.auth.type !== 'none') {
      requestOptions.auth = this.options.auth;
    }

    logger.info('Requesting status from', requestOptions.uri);

    return request(requestOptions);
  }

  /**
   * Transforms the response body to a normalized status
   * @param body The body of the response from Jenkins.
   * @returns {Array} An array containing the normalized status.
   */
  transform(body) {
    // TODO transform status
    return new Status(body);
  }
}

module.exports = function (options) {
  return new JenkinsStatusesService(options);
};

module.exports.Service = JenkinsStatusesService;

Use status model in Jenkins service


module.exports = {

  onLoad: function(callback) {
    if (typeof callback !== 'function') {
      throw new Error('"onLoad" must have a callback function');
    }
    withLiveReactload(function(lrload) {
      var winOnload = window.onload;
      if (!winOnload || !winOnload.__is_livereactload) {
        window.onload = function() {
          // initial load event
          callback(lrload.state);
          lrload.winloadDone = true;
          if (typeof winOnload === 'function' && !winOnload.__is_livereactload) {
            winOnload();
          }
        }
      }
      window.onload.__is_livereactload = true;
      if (lrload.winloadDone) {
        // reload event
        callback(lrload.state)
      }
    })
  },

  setState: function(state) {
    withLiveReactload(function(lrload) {
      lrload.state = state;
    })
  },

  expose: function(cls, id) {
    withLiveReactload(function(lrload) {
      if (lrload.makeHot) {
        lrload.makeHot({exports: cls}, 'CUSTOM_' + id);
      }
    })
  }

}

function withLiveReactload(cb) {
  if (typeof window !== 'undefined') {
    var lrload = window.__livereactload;
    if (lrload) {
      cb(lrload);
    }
  }
}

module.exports = {

  onLoad: function(callback) {
    if (typeof callback !== 'function') {
      throw new Error('"onLoad" must have a callback function');
    }

    withLiveReactload(setupOnLoadHandlers)
    withoutLiveReactload(function() {
      setupOnLoadHandlers({})
    })

    function setupOnLoadHandlers(lrload) {
      var winOnload = window.onload;
      if (!winOnload || !winOnload.__is_livereactload) {
        window.onload = function () {
          // initial load event
          callback(lrload.state);
          lrload.winloadDone = true;
          if (typeof winOnload === 'function' && !winOnload.__is_livereactload) {
            winOnload();
          }
        }
      }
      window.onload.__is_livereactload = true;
      if (lrload.winloadDone) {
        // reload event
        callback(lrload.state)
      }
    }
  },

  setState: function(state) {
    withLiveReactload(function(lrload) {
      lrload.state = state;
    })
  },

  expose: function(cls, id) {
    withLiveReactload(function(lrload) {
      if (lrload.makeHot) {
        lrload.makeHot({exports: cls}, 'CUSTOM_' + id);
      }
    })
  }

}

function withLiveReactload(cb) {
  if (typeof window !== 'undefined') {
    var lrload = window.__livereactload;
    if (lrload) {
      cb(lrload);
    }
  }
}

function withoutLiveReactload(cb) {
  if (typeof window === 'undefined' || !window.__livereactload) {
    cb();
  }
}

Fix onLoad loading bug if livereactload transformer is not set


#!/usr/bin/env node
const express = require('express');
const fileExists = require('file-exists');
const bodyParser = require('body-parser');
const fs = require('fs');
const { port = 8123 } = require('minimist')(process.argv.slice(2));

const app = express();

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

app.get('*', (req, res) => {
    const filePath = req.get('X-Requested-File-Path');

    if (!filePath) {
        return res.status(500).send('Server did not provide file path');
    }

    if (!fileExists(filePath)) {
        return res.status(500).send('Cannot find such file on the server');
    }

    try {
        require(filePath)(req, res); // eslint-disable-line global-require
    } catch (e) {
        return res.status(500).send(`<pre>${e.stack}</pre>`);
    }

    fs.watchFile(filePath, () => {
        delete require.cache[filePath];
        fs.unwatchFile(filePath);
    });

    return undefined;
});

app.listen(port, () => {
    console.log(`node-direct listening on port ${port}!`);
});

#!/usr/bin/env node
const express = require('express');
const fileExists = require('file-exists');
const bodyParser = require('body-parser');
const fs = require('fs');
const { port = 8123 } = require('minimist')(process.argv.slice(2));

const app = express();

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

app.get('*', (req, res) => {
    const filePath = req.get('X-Requested-File-Path');

    if (!filePath) {
        return res.status(500).send('Server did not provide file path');
    }

    if (!fileExists(filePath)) {
        return res.status(500).send('Cannot find such file on the server');
    }

    try {
        require(filePath)(req, res); // eslint-disable-line global-require
    } catch (e) {
        return res.status(500).send(`<pre>${e.stack}</pre>`);
    }

    const watcher = fs.watch(filePath, (eventType) => {
        if (eventType === 'change') {
            delete require.cache[filePath];
            watcher.close();
        }
    });

    return undefined;
});

app.listen(port, () => {
    console.log(`node-direct listening on port ${port}!`);
});

Use watch instead of watchFile


/*!
 * helper-markdown <https://github.com/jonschlinkert/helper-markdown>
 *
 * Copyright (c) 2014 Jon Schlinkert, contributors.
 * Licensed under the MIT license.
 */

'use strict';

var isObject = require('isobject');
var Remarkable = require('remarkable');
var merge = require('mixin-deep');

module.exports = function markdown(config) {
  if (typeof config === 'string') {
    return helper.apply(this, arguments);
  }

  config = config || {};
  if (config.fn || config.hash || arguments.length > 1) {
    return helper.apply(this, arguments);
  }

  function helper(context, options) {
    if (typeof context === 'string') {
      var opts = merge({}, config, options);
      var md = new Remarkable(opts);
      return md.render(context);
    }

    if (isObject(context) && typeof context.fn === 'function') {
      options = context;
      context = {};
    }

    options = merge({ html: true, breaks: true }, config, options);
    options = merge({}, options, options.markdown, options.hash);
    if (options.hasOwnProperty('lang')) {
      options.langPrefix = options.lang;
    }

    var md = new Remarkable(options);
    var ctx = merge({}, options, this, context);
    return md.render(options.fn(ctx));
  }

  return helper;
};

/*!
 * helper-markdown <https://github.com/jonschlinkert/helper-markdown>
 *
 * Copyright (c) 2014 Jon Schlinkert, contributors.
 * Licensed under the MIT license.
 */

'use strict';

var isObject = require('isobject');
var Remarkable = require('remarkable');
var merge = require('mixin-deep');

module.exports = function markdown(config) {
  if (typeof config === 'string') {
    return helper.apply(this, arguments);
  }

  config = config || {};
  if (config.fn || config.hash || arguments.length > 1) {
    return helper.apply(this, arguments);
  }

  function helper(context, options) {
    if (typeof context === 'string') {
      var opts = merge({}, config, options);
      var md = new Remarkable(opts);
      return md.render(context);
    }

    if (isObject(context) && typeof context.fn === 'function') {
      options = context;
      context = {};
    }

    options = merge({ html: true, breaks: true }, config, options);
    options = merge({}, options, options.markdown, options.hash);
    if (options.hasOwnProperty('lang')) {
      options.langPrefix = options.lang;
    }

    var md = new Remarkable(options);
    var ctx = merge({}, options, this, this.context, context);
    return md.render(options.fn(ctx));
  }

  return helper;
};

Extend block context in order to support additional compilation engines (e.g. Assemble).


import postcss from 'postcss';

const postcssFlexibility = postcss.plugin('postcss-flexibility', () => css => {
	css.walkRules(rule => {
		const isDisabled = rule.some(({prop, text}) =>
			prop === '-js-display' || text === 'flexibility-disable'
		);

		if (!isDisabled) {
			rule.walkDecls('display', decl => {
				const {value} = decl;
				if (value === 'flex') {
					decl.cloneBefore({prop: '-js-display'});
				}
			});
		}
	});
});

export default postcssFlexibility;

import postcss from 'postcss';

const postcssFlexibility = postcss.plugin('postcss-flexibility', () => css => {
	css.walkRules(rule => {
		const isDisabled = rule.some(({prop, text}) =>
			prop === '-js-display' || text === 'flexibility-disable' || text === '! flexibility-disable'
		);

		if (!isDisabled) {
			rule.walkDecls('display', decl => {
				const {value} = decl;
				if (value === 'flex') {
					decl.cloneBefore({prop: '-js-display'});
				}
			});
		}
	});
});

export default postcssFlexibility;

Add support for loud comments


import React from 'react';
import loadable from 'react-loadable';
import { Components } from './components.js';

export const dynamicLoader = componentImport => loadable({
  loader: () => componentImport,
  loading: Components.DynamicLoading,
  // serverSideRequirePath: adminPath
});

export const getDynamicComponent = componentImport => React.createElement(dynamicLoader(componentImport));

import React from 'react';
import loadable from 'react-loadable';
import { Components } from './components.js';

export const dynamicLoader = componentImport => loadable({
  loader: () => componentImport,
  loading: Components.DynamicLoading,
  // serverSideRequirePath: adminPath
});

export const getDynamicComponent = (componentImport, props) => React.createElement(dynamicLoader(componentImport), props);

Add props to dynamic components (PR Worthy)


var es = require("event-stream");
var path = require("path");
"use strict";

var istanbul = require("istanbul");
var hook = istanbul.hook;
var Report = istanbul.Report;
var Collector = istanbul.Collector;
var instrumenter = new istanbul.Instrumenter();

var plugin  = module.exports = function (param) {

  function createMatcher(path) {
    return function (requestedPath) {
      return path === requestedPath;
    };
  }

  function bypassContent(code) {
    return function() { return String(code); };
  }

  return es.map(function (file, cb) {
    if (!file.contents instanceof Buffer) {
      return cb(new Error("gulp-istanbul: streams not supported"), undefined);
    }

    instrumenter.instrument(String(file.contents), file.path, function(err, code) {
      if (!err) file.contents = new Buffer(code);
      hook.hookRequire(createMatcher(file.path), bypassContent(file.contents));
      cb(err, file);
    });
  });
};

plugin.writeReports = function (dir) {
  dir = dir || path.join(process.cwd(), "coverage");

  var collector = new Collector();
  collector.add(global.__coverage__);

  var reports = [
      Report.create("lcov", { dir: dir }),
      Report.create("text"),
      Report.create("text-summary")
  ];
  reports.forEach(function (report) { report.writeReport(collector, true); })
};

var es = require("event-stream");
var path = require("path");
"use strict";

var istanbul = require("istanbul");
var hook = istanbul.hook;
var Report = istanbul.Report;
var Collector = istanbul.Collector;
var instrumenter = new istanbul.Instrumenter();

var plugin  = module.exports = function () {
  var fileMap = {};

  hook.hookRequire(function (path) {
    return !!fileMap[path];
  }, function (code, path) {
    return fileMap[path];
  });

  return es.map(function (file, cb) {
    if (!file.contents instanceof Buffer) {
      return cb(new Error("gulp-istanbul: streams not supported"), undefined);
    }

    instrumenter.instrument(file.contents.toString(), file.path, function (err, code) {
      if (!err) file.contents = new Buffer(code);
      fileMap[file.path] = file.contents.toString();
      cb(err, file);
    });
  });
};

plugin.writeReports = function (dir) {
  dir = dir || path.join(process.cwd(), "coverage");

  return es.through(function () {}, function () {
    var collector = new Collector();
    collector.add(global.__coverage__);

    var reports = [
        Report.create("lcov", { dir: dir }),
        Report.create("text"),
        Report.create("text-summary")
    ];
    reports.forEach(function (report) { report.writeReport(collector, true); })
  });

};

Fix task to output coverage for every files


module.exports = {
  rules: {
    "no-loops": require('./lib/no-loops')
  }
};

module.exports = {
  rules: {
    "no-loops": require('./lib/no-loops')
  },
  rulesConfig: {
    "no-loops": 2
  }
};

Make rule active with error by default


module.exports = createServerCluster

var http = require('http')
  , clusterMaster = require('clustered')
  , cpus = require('os').cpus()
  , extend = require('lodash.assign')

function createServerCluster(server, logger, options) {
  if (!server) {
    throw new Error('Must pass in an instance of a server')
  }

  if (!logger) {
    throw new Error('Must pass in an instance of a logger')
  }

  var defaults =
    { port: 5678
    , numProcesses: Math.ceil(cpus.length * 0.7)
    }
  options = extend({}, defaults, options)

  clusterMaster(function () {
      var httpServer = http.createServer(function (req, res) {
        return server(req, res)
      }).listen(options.port, function () {
        server.emit('started', httpServer)
      })
    }
  , { logger: logger
    , size: options.numProcesses
    }
  )
}

module.exports = createServerCluster

var http = require('http')
  , clusterMaster = require('clustered')
  , extend = require('lodash.assign')

function createServerCluster(server, logger, options) {
  if (!server) {
    throw new Error('Must pass in an instance of a server')
  }

  if (!logger) {
    throw new Error('Must pass in an instance of a logger')
  }

  var defaults =
    { port: 5678
    , numProcesses: 1
    }
  options = extend({}, defaults, options)

  clusterMaster(function () {
      var httpServer = http.createServer(function (req, res) {
        return server(req, res)
      }).listen(options.port, function () {
        server.emit('started', httpServer)
      })
    }
  , { logger: logger
    , size: options.numProcesses
    }
  )
}

Remove use of numpCpus lookup


const config = require('./landing.config'),
      Login  = require('./login/login.module');

const moduleName   = 'landing-bundle',
      dependencies = [ Login ];

angular.module(moduleName, dependencies)
	.config(config);

module.exports = moduleName;

const config = require('./landing.config'),
      Login  = require('./login/login.module');
      EnableStudent = require('./enable-student/enable-student.module');

const moduleName   = 'landing-bundle',
      dependencies = [ Login ];

angular.module(moduleName, dependencies)
	.config(config);

module.exports = moduleName;

Add new element for enabling student bundle


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

var SEPARATOR = process.platform === 'win32' ? ';' : ':';
var env = Object.assign({}, process.env);
env.PATH = path.resolve('./node_modules/.bin') + SEPARATOR + env.PATH;

/**
 * Execute a command
 * Reference: https://www.nczonline.net/blog/2016/03/mimicking-npm-script-in-node-js/
 *
 * @param {String} command The command to execute.
 */
module.exports = function(command) {
  var output = execSync(command, {
    cwd: process.cwd(),
    env: env
  });

  console.log(output.toString().trim());
};

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

var SEPARATOR = process.platform === 'win32' ? ';' : ':';
var env = Object.assign({}, process.env);
env.PATH = path.resolve('./node_modules/.bin') + SEPARATOR + env.PATH;

/**
 * Execute a command
 * Reference: https://www.nczonline.net/blog/2016/03/mimicking-npm-script-in-node-js/
 *
 * @param {String} command The command to execute.
 */
module.exports = function(command) {
  var output = execSync(command, {
    cwd: process.cwd(),
    env: env,
    shell: true,
    stdio: 'inherit'
  });
};

Add option for shell and stdio in execute command


import { moduleForComponent, test } from 'ember-qunit';<% if (testType === 'integration') { %>
import hbs from 'htmlbars-inline-precompile';<% } %>

moduleForComponent('<%= componentPathName %>', '<%= friendlyTestDescription %>', {
  <% if (testType === 'integration' ) { %>integration: true<% } else if(testType === 'unit') { %>// Specify the other units that are required for this test
  // needs: ['component:foo', 'helper:bar'],
  unit: true<% } %>
});

test('it renders', function(assert) {
<% if (testType === 'integration' ) { %>// Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  this.render(hbs`{{<%= componentPathName %>}}`);

  assert.equal(this.$().text().trim(), '');

  // Template block usage:
  this.render(hbs`
    {{#<%= componentPathName %>}}
      template block text
    {{/<%= componentPathName %>}}
  `);

  assert.equal(this.$().text().trim(), 'template block text');<% } else if(testType === 'unit') { %>
  // Creates the component instance
  /*let component =*/ this.subject();
  // Renders the component to the page
  this.render();
  assert.equal(this.$().text().trim(), '');<% } %>
});

import { moduleForComponent, test } from 'ember-qunit';<% if (testType === 'integration') { %>
import hbs from 'htmlbars-inline-precompile';<% } %>

moduleForComponent('<%= componentPathName %>', '<%= friendlyTestDescription %>', {
  <% if (testType === 'integration' ) { %>integration: true<% } else if(testType === 'unit') { %>// Specify the other units that are required for this test
  // needs: ['component:foo', 'helper:bar'],
  unit: true<% } %>
});

test('it renders', function(assert) {
<% if (testType === 'integration' ) { %>
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  this.render(hbs`{{<%= componentPathName %>}}`);

  assert.equal(this.$().text().trim(), '');

  // Template block usage:
  this.render(hbs`
    {{#<%= componentPathName %>}}
      template block text
    {{/<%= componentPathName %>}}
  `);

  assert.equal(this.$().text().trim(), 'template block text');<% } else if(testType === 'unit') { %>
  // Creates the component instance
  /*let component =*/ this.subject();
  // Renders the component to the page
  this.render();
  assert.equal(this.$().text().trim(), '');<% } %>
});

Put comment on seperate line


jQuery(function($){//on document ready
  //autocomplete
  $('input.autocomplete').each(function(){
    var $input = $(this);
    $input.autocomplete($input.attr('data-autocomplete-url'));
  });
});
function updatePosts(url){
  $.get(url,function(data){
      if (data) {
        $(data).hide().appendTo("#posts").show('slow');
        document.getElementById('player').play();
      }
  },'js');
}

var titleHolder = document.title;

jQuery(function($){//on document ready
  //autocomplete
  $('input.autocomplete').each(function(){
    var $input = $(this);
    $input.autocomplete($input.attr('data-autocomplete-url'));
  });

  $('html').mouseover(function() {
    gainedFocus();
  });

});

function updatePosts(url){
  $.get(url,function(data){
      if (data) {
        $(data).hide().appendTo("#posts").show('slow');
        lostFocus();
        blinkTitle(1);
        document.getElementById('player').play();
      }
  },'js');
}

function blinkTitle(state) {
  if (windowIsActive != true) {
    if (state == 1) {
      document.title = "[new!] - " + titleHolder;
      state = 0;
    } else {
      document.title = "" + titleHolder;
      state = 1;
    }

    setTimeout("blinkTitle(" + state + ")", 1600);
  } else {
    document.title = titleHolder;
  }
}

function lostFocus() {
  windowIsActive = false;
}

function gainedFocus() {
  windowIsActive = true;
}

Add blink effect on page title when a new post is added


const matchTimestamps = function (inputText) {
  const lengthOfDelimiter = 1 // Have to advance past/before the opening/closing brackets
  let bracketPattern = /\[(\d{1,2}[:.]){1,3}\d{2}\]/g
  let match = bracketPattern.exec(inputText)
  let matches = []
  let startingIndex
  let matchLength

  while (match !== null) {
    startingIndex = (match.index + lengthOfDelimiter)
    matchLength = (bracketPattern.lastIndex - startingIndex - lengthOfDelimiter)
    matches = matches.concat({
      index: startingIndex,
      length: matchLength
    })
    match = bracketPattern.exec(inputText)
  }
  return (matches)
}

module.exports = matchTimestamps

const matchTimestamps = function (inputText) {
  const lengthOfDelimiter = 1 // Have to advance past/before the opening/closing brackets
  let bracketPattern = /\[(\d|:|.)+\]/g
  let match = bracketPattern.exec(inputText)
  let matches = []
  let startingIndex
  let matchLength

  while (match !== null) {
    startingIndex = (match.index + lengthOfDelimiter)
    matchLength = (bracketPattern.lastIndex - startingIndex - lengthOfDelimiter)
    matches = matches.concat({
      index: startingIndex,
      length: matchLength
    })
    match = bracketPattern.exec(inputText)
  }
  return (matches)
}

module.exports = matchTimestamps

Simplify the pattern for matching timestamps


// Socket actions
export const SOCKET_SET_OPEN        = Symbol("SOCKET_SET_OPEN");
export const SOCKET_SET_OPENING     = Symbol("SOCKET_SET_OPENING");
export const SOCKET_SET_CLOSED      = Symbol("SOCKET_SET_CLOSED");
export const SOCKET_SET_CLOSING     = Symbol("SOCKET_SET_CLOSING");
export const SOCKET_SET_ERROR       = Symbol("SOCKET_SET_ERROR");
export const SOCKET_RECEIVE_MESSAGE = Symbol("SOCKET_RECEIVE_MESSAGE");
export const SOCKET_SEND_MESSAGE    = Symbol("SOCKET_SEND_MESSAGE");

// Room actions
export const ROOM_SELECT = Symbol("ROOM_SELECT");
export const ROOM_JOIN   = Symbol("ROOM_JOIN");

// Socket actions
export const SOCKET_SET_OPEN        = "SOCKET_SET_OPEN";
export const SOCKET_SET_OPENING     = "SOCKET_SET_OPENING";
export const SOCKET_SET_CLOSED      = "SOCKET_SET_CLOSED";
export const SOCKET_SET_CLOSING     = "SOCKET_SET_CLOSING";
export const SOCKET_SET_ERROR       = "SOCKET_SET_ERROR";
export const SOCKET_RECEIVE_MESSAGE = "SOCKET_RECEIVE_MESSAGE";
export const SOCKET_SEND_MESSAGE    = "SOCKET_SEND_MESSAGE";

// Room actions
export const ROOM_SELECT = "ROOM_SELECT";
export const ROOM_JOIN   = "ROOM_JOIN";
export const ROOM_SAY    = "ROOM_SAY";

Use string action types for redux-logger.


(function () {
  'use strict';

  moj.Modules.Shame = {
    init: function () {
      // Polyfill a given set of elements
      $('details').details();
      $('body').addClass($.fn.details.support ? 'details' : 'no-details');
    }
  };
}());

(function () {
  'use strict';

  moj.Modules.Shame = {
    init: function () {
      // Polyfill a given set of elements
      $('details').details();
      $('body').addClass($.fn.details.support ? 'details' : 'no-details');

      this.bindEvents();
    },

    bindEvents: function () {
      $(':input[type=number]').on('mousewheel',function() {
        $(this).blur();
      });
    }
  };
}());

Disable scrolling on number fields


// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

import VueBootstrap from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(VueBootstrap)

// HTTP Client we use.
import Axios from 'axios'

// TODO: Set these during build time.
Axios.defaults.baseURL = 'http://localhost:8080'
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

import VueBootstrap from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(VueBootstrap)

// HTTP Client we use.
import Axios from 'axios'

// TODO: Set these during build time.
Axios.defaults.baseURL = 'http://localhost:8080'
Axios.defaults.headers['Content-Type'] = 'application/json; charset=UTF-8'
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

Add Content-Type to all requests


'use strict';

import React, {
  AppRegistry,
  Component,
  StyleSheet,
} from 'react-native';

import WeatherView from './WeatherView';

class ReactNativeWeather extends Component {
  render() {
    return (
      <WeatherView />
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

export default ReactNativeWeather;

'use strict';

import React, {
  AppRegistry,
  Component,
  StyleSheet,
} from 'react-native';

import WeatherView from './WeatherView';

class ReactNativeWeather extends Component {
  render() {
    return (
      <WeatherView />
    );
  }
};

export default ReactNativeWeather;

Remove unnecessary styles in app.js


tinyMCE.init({
	       theme:"advanced",
	       mode:"none",
	       plugins: "contextmenu,paste,spellchecker,style",
	       theme_advanced_styles : "Heading 1=mce_header1;Heading 2=mce_header2;Heading 3=mce_header3",
               theme_advanced_toolbar_location: "top",
               theme_advanced_toolbar_align: "left",
               theme_advanced_resizing: true,
               theme_advanced_resize_horizontal: true,
               paste_auto_cleanup_on_paste: true,
	       theme_advanced_statusbar_location : "bottom",
               theme_advanced_buttons1: "formatselect,bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,indent,outdent,separator,bullist,numlist,forecolor,backcolor,separator,link,unlink,image,undo,redo,separator,code,cleanup,removeformat",
               theme_advanced_buttons2: "",
               theme_advanced_buttons3: "",
	       editor_selector: "tinymce",
	       content_css: "/stylesheets/editor.css",
	       convert_newlines_to_brs: false,
//	       cleanup_serializer: 'xml',
//	       encoding: 'xml',
	       entity_encoding: 'raw',
	       debug: true
             });

tinyMCE.init({
	       theme:"advanced",
	       mode:"none",
	       plugins: "contextmenu,paste,spellchecker,style,table",
	       theme_advanced_styles : "Heading 1=mce_header1;Heading 2=mce_header2;Heading 3=mce_header3",
               theme_advanced_toolbar_location: "top",
               theme_advanced_toolbar_align: "left",
               theme_advanced_resizing: true,
               theme_advanced_resize_horizontal: true,
               paste_auto_cleanup_on_paste: true,
	       theme_advanced_statusbar_location : "bottom",
               theme_advanced_buttons1: "formatselect,bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,indent,outdent,separator,bullist,numlist,forecolor,backcolor,separator,link,unlink,image,undo,redo,separator,code,cleanup,removeformat",
               theme_advanced_buttons2: "tablecontrols",
               theme_advanced_buttons3: "",
	       editor_selector: "tinymce",
	       content_css: "/stylesheets/editor.css",
	       convert_newlines_to_brs: false,
//	       cleanup_serializer: 'xml',
//	       encoding: 'xml',
	       entity_encoding: 'raw',
	       debug: true
             });

Enable table controls for WYSIWYG editor


import Mustache from 'mustache'
let capitalize = require('lodash/string/capitalize')

let template = `Dear {{name}},

Your request to download course materials for course {{{coursename}}} has been received and currently being processed. You will receive another email when your request has been processed.

Unique Request ID: {{id}}

Course Name: {{{coursename}}}

Delivery Method: {{getDeliveryMethod}}

Media Types: {{getMediaTypes}}

Name: {{name}}

Email: {{email}}

Phone: {{phone}}

{{#mailingAddress}}
Mailing Address:
{{{mailingAddress}}}
{{city}}
{{#state}}{{state}}{{/state}}
{{country}}
{{postalCode}}

{{/mailingAddress}}
Please check the above details, and send us an email if there are errors or ommisions.

Regards

MOOC Fetcher Support
`

let renderers = {
  getDeliveryMethod() {
    let mapping = {
      dropbox: 'Dropbox',
      'usb-drive': 'USB Drive',
      gdrive: 'Google Drive'
    }

    return mapping[this.deliverymethod]
  },
  getMediaTypes() {
    return (this.mediatype.map((m) => capitalize(m))).join(', ')
  }
}

exports.format = function(inputs) {
  let templateInputs = Object.assign({}, inputs, renderers),
    subject = `Your download request for ${inputs.coursename} has been received`

  let emailText = Mustache.render(template, templateInputs)

  return {subject, emailText}
}

import Mustache from 'mustache'
let capitalize = require('lodash/capitalize')

let template = `Dear {{name}},

Your request to download course materials for course {{{coursename}}} has been received and currently being processed. You will receive another email when your request has been processed.

Unique Request ID: {{id}}

Course Name: {{{coursename}}}

Delivery Method: {{getDeliveryMethod}}

Media Types: {{getMediaTypes}}

Name: {{name}}

Email: {{email}}

Phone: {{phone}}

{{#mailingAddress}}
Mailing Address:
{{{mailingAddress}}}
{{city}}
{{#state}}{{state}}{{/state}}
{{country}}
{{postalCode}}

{{/mailingAddress}}
Please check the above details, and send us an email if there are errors or ommisions.

Regards

MOOC Fetcher Support
`

let renderers = {
  getDeliveryMethod() {
    let mapping = {
      dropbox: 'Dropbox',
      'usb-drive': 'USB Drive',
      gdrive: 'Google Drive'
    }

    return mapping[this.deliverymethod]
  },
  getMediaTypes() {
    return (this.mediatype.map((m) => capitalize(m))).join(', ')
  }
}

exports.format = function(inputs) {
  let templateInputs = Object.assign({}, inputs, renderers),
    subject = `Your download request for ${inputs.coursename} has been received`

  let emailText = Mustache.render(template, templateInputs)

  return {subject, emailText}
}

Fix lodash function import after lodash upgrade


'use strict';

const EventEmitter = require('events');

/**
 * Tracker Manager
 * @interface
 */
class ITrackerManager extends EventEmitter {
	constructor() {
		super();

		if (this.constructor === ITrackerManager) {
			throw new TypeError('Can not create new instance of interface');
		}
	}

	/**
	 * Include tracker in search
	 * @param {ITracker} tracker
	 * @return {string|undefined} UID of tracker in list
	 */
	include(tracker) {
		throw new TypeError('Method "include" is not implemented.');
	}

	/**
	 * Exclude tracker from search
	 * @param {string} uid
	 * @return {ITracker|undefined} Excluded Tracker instance
	 */
	exclude(uid) {
		throw new TypeError('Method "exclude" is not implemented.');
	}

	/**
	 * Search in all included trackers
	 * @param {SearchParams} searchParams
	 */
	search(searchParams) {
		throw new TypeError('Method "search" is not implemented.');
	}
}

module.exports = ITrackerManager;

'use strict';

const EventEmitter = require('events');

/**
 * Tracker Manager
 * @interface
 */
class ITrackerManager extends EventEmitter {
	constructor() {
		super();

		if (this.constructor === ITrackerManager) {
			throw new TypeError('Can not create new instance of interface');
		}
	}

	/**
	 * Include tracker in search
	 * @param {ITracker} tracker
	 * @return {string|undefined} UID of tracker in list
	 */
	include(tracker) {
		throw new TypeError('Method "include" is not implemented.');
	}

	/**
	 * Exclude tracker from search
	 * @param {string} uid
	 * @return {boolean} status of excluding
	 */
	exclude(uid) {
		throw new TypeError('Method "exclude" is not implemented.');
	}

	/**
	 * Search in all included trackers
	 * @param {SearchParams} searchParams
	 */
	search(searchParams) {
		throw new TypeError('Method "search" is not implemented.');
	}
}

module.exports = ITrackerManager;

Change type of returning value


/* jshint esversion: 6 */
window.onload = () => {
  /* TODO: put in some if (localizationEnabled) block in case people who use non-english browser still wants english extension? */
  translate('name', 'title', 'extName');
  translate('id', 'battleLink', 'battle');
  translate('class', 'by', 'by');
};

function translate (type, name, message) {
  if (type === 'name') document.getElementsByTagName(name)[0].innerHTML = chrome.i18n.getMessage(message);
  else if (type === 'id') document.getElementById(name).innerHTML = chrome.i18n.getMessage(message);
  else if (type === 'class') {
    var elements = document.getElementsByClassName(name);
    console.log(elements.length);
    for (var i = 0; i < elements.length; i++) {
      elements[i].innerHTML = chrome.i18n.getMessage(message);
    }
  }
}

/*
chrome.browserAction.onClicked.addListener(function(tab) {

    var port = chrome.extension.connect({name: "Sample Communication"});
    port.postMessage("Hi BackGround");
    port.onMessage.addListener(function(msg) {
            console.log("message recieved"+ msg);
    });

}); */

/* jshint esversion: 6 */
window.onload = () => {
  /* TODO: put in some if (localizationEnabled) block in case people who use non-english browser still wants english extension? */
  translate('name', 'title', 'extName');
  translate('id', 'battleLink', 'battle');
  translate('class', 'by', 'by');
};

function translate (type, name, message) {
  switch (type) {
    case 'name':
      document.getElementsByTagName(name)[0].innerHTML = chrome.i18n.getMessage(message);
      break;
    case 'id':
      document.getElementById(name).innerHTML = chrome.i18n.getMessage(message);
      break;
    case 'class':
      var elements = document.getElementsByClassName(name);
      console.log(elements.length);
      for (var i = 0; i < elements.length; i++) {
        elements[i].innerHTML = chrome.i18n.getMessage(message);
      }
      break;
  }
}

/*
chrome.browserAction.onClicked.addListener(function(tab) {

    var port = chrome.extension.connect({name: "Sample Communication"});
    port.postMessage("Hi BackGround");
    port.onMessage.addListener(function(msg) {
            console.log("message recieved"+ msg);
    });

}); */

Use switch in translate function instead


// ------------------------------------------------------------------------------
//
// ImagePreloader
//
// ------------------------------------------------------------------------------
define([ "jquery" ], function($) {

  "use strict";

  var ImagePreloader = function() {};

  ImagePreloader.prototype.loadImages = function($images, callback) {
    var loadedImages = 0,
      badTags = 0,
      loadHandler;

    $images.each(function() {

      var image = new Image(),
          $el = $(this),
          src = $el.attr("src"),
          backgroundImage = $el.css("backgroundImage");

      // Search for css background style
      if ( src === undefined && backgroundImage != "none" ) {
        var pattern = /url\("{0,1}([^"]*)"{0,1}\)/;
        src = pattern.exec(backgroundImage)[1];
      } else {
        badTags++;
      }

      loadHandler = function() {
        loadedImages++;
        if ( loadedImages == ($images.length - badTags) ){
          callback();
        }
      };
        // Load images
      $(image)
        .load(loadHandler)
        .error(loadHandler)
        .attr("src", src);
    });
  };

  return ImagePreloader;
});

Fix failing build because there was an extra space in JavaScript. Yep, that's right, a build failed because of a space in JavaScript.


const getBasicInfo = require('../db/controllers/getUserBasicInfoGivenFBId.js');
const getLanguageInfo = require('../db/controllers/getUserLanguageInfoGivenUserId.js');
const helpers = require('../db/controllers/helpers.js');

module.exports = (req, res, next) => {
  // const fbId = req.user.id;
  const fbId = '22222';
  getBasicInfo(fbId)
  .then(helpers.pluckUser)
  .then(user => {
    req.idioma.profile = user;
    return user.id;
  })
  .then(getLanguageInfo)
  .then(helpers.pluckLanguages)
  .then(languagesObj => {
    req.idioma.profile.languages = languagesObj;
  })
  .then(() => next());
};

const getBasicInfo = require('../db/controllers/getUserBasicInfoGivenFBId.js');
const getLanguageInfo = require('../db/controllers/getUserLanguageInfoGivenUserId.js');
const helpers = require('../db/controllers/helpers.js');

module.exports = (req, res, next) => {
  const fbId = req.user.id;
  getBasicInfo(fbId)
  .then(helpers.pluckUser)
  .then(user => {
    req.idioma.profile = user;
    return user.id;
  })
  .then(getLanguageInfo)
  .then(helpers.pluckLanguages)
  .then(languagesObj => {
    req.idioma.profile.languages = languagesObj;
  })
  .then(() => next());
};

Use real facebook id instead of dummy


module.exports = function(grunt) {
  grunt.initConfig({
    coffee: {
      compile: {
        options: {
          bare: false,
          join: true
        },
        files: {
          'blitz.js': ['lib/blitz.coffee'],
          'test/blitz_test.js': ['test/blitz_test.coffee']
        }
      }
    },

    uglify: {
      options: {
        report: 'min'
      },
      dist: {
        src: 'blitz.js',
        dest: 'blitz.min.js'
      }
    },

    mocha: {
      src: ['test/test.html'],
      options: {
        bail: true,
        log: true,
        run: true,
        mocha: {
          ignoreLeaks: true
        }
      }
    },

    sass: {
      dist: {
        options: {
          outputStyle: 'nested'
        },
        files: {
          'blitz.css': 'blitz.scss'
        }
      }
    },

    watch: {
      files: ['lib/*.coffee', 'test/*.coffee'],
      tasks: ['coffee:compile']
    }
  });

  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-mocha');

  grunt.registerTask('default', ['coffee:compile', 'sass', 'mocha']);
  grunt.registerTask('release', ['default', 'uglify']);
};

module.exports = function(grunt) {
  grunt.initConfig({
    coffee: {
      compile: {
        options: {
          bare: false,
          join: true
        },
        files: {
          'blitz.js': ['lib/blitz.coffee'],
          'test/blitz_test.js': ['test/blitz_test.coffee']
        }
      }
    },

    uglify: {
      options: {
        report: 'min'
      },
      dist: {
        src: 'blitz.js',
        dest: 'blitz.min.js'
      }
    },

    mocha: {
      src: ['test/test.html'],
      options: {
        bail: true,
        log: true,
        run: true,
        mocha: {
          ignoreLeaks: true
        }
      }
    },

    sass: {
      dist: {
        options: {
          outputStyle: 'nested'
        },
        files: {
          'blitz.css': 'blitz.scss'
        }
      }
    },

    watch: {
      scripts: {
        files: ['lib/*.coffee', 'test/*.coffee'],
        tasks: ['coffee:compile']
      },
      sass: {
        files: ['*.scss'],
        tasks: ['sass']
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-mocha');

  grunt.registerTask('default', ['coffee:compile', 'sass', 'mocha']);
  grunt.registerTask('release', ['default', 'uglify']);
};

Update grunt watch to include sass


/// <reference path="typings/globals/jasmine/index.d.ts" />
'use strict';

describe('javaScript', function () {
    it('Revealing prototype pattern', function () {
        var Calculator = function(seed){
            this.seed = seed;
        };
        Calculator.prototype = function  (){
            function add (item){
                return this.seed + item;
            };
            return {
                add: add
            };
        }();

        var calculator = new Calculator(2);
        var result = calculator.add(3);
        expect(result).toBe(5);
    });
});

Add a reference to jasmine typings


function PostController(post, Auth, PostsService, $stateParams, categories, Flash) {
  var ctrl = this;

  if (post) {
    ctrl.data = post.data;
  }

  if (categories) {
    ctrl.categories = categories.data;
  }

  var currentUser;

  Auth.currentUser().then(function (user) {
    ctrl.currentUser = user
  })

  ctrl.editPost = function () {
    return ctrl.currentUser.id === ctrl.data.author.id
  }

  ctrl.save = function () {
    if ($stateParams.id && ctrl.editPost()) {
      PostsService.updatePost($stateParams.id, ctrl.data);
    } else if (!$stateParams.id) {
      PostsService.addPost(ctrl.data)
    } else {
      var message = "Oops, you can't do that!";
      var id = Flash.create('danger', message, 5000);
    }
  }

  ctrl.removePost =  function () {
    if ($stateParams.id && ctrl.editPost()) {
      PostsService.deletePost($stateParams.id);
    } else {
      var message = "Oops, you can't do that!";
      var id = Flash.create('danger', message, 5000);
    }
  }
}

PostController.$inject = ['post', 'Auth', 'PostsService', '$stateParams', 'categories', 'Flash'];
angular
  .module('app')
  .controller('PostController', PostController)

function PostController(post, Auth, PostsService, $stateParams, categories, Flash) {
  var ctrl = this;

  if (post) {
    ctrl.data = post.data;
  }

  if (categories) {
    ctrl.categories = categories.data;
  }

  var currentUser;

  Auth.currentUser().then(function (user) {
    ctrl.currentUser = user
  })

  ctrl.editPost = function () {
    if (ctrl.currentUser) {
      return ctrl.currentUser.id === ctrl.data.author.id
    }
  }

  ctrl.save = function () {
    if ($stateParams.id && ctrl.editPost()) {
      PostsService.updatePost($stateParams.id, ctrl.data);
    } else if (!$stateParams.id) {
      PostsService.addPost(ctrl.data)
    } else {
      var message = "Oops, you can't do that!";
      var id = Flash.create('danger', message, 5000);
    }
  }

  ctrl.removePost =  function () {
    if ($stateParams.id && ctrl.editPost()) {
      PostsService.deletePost($stateParams.id);
    } else {
      var message = "Oops, you can't do that!";
      var id = Flash.create('danger', message, 5000);
    }
  }
}

PostController.$inject = ['post', 'Auth', 'PostsService', '$stateParams', 'categories', 'Flash'];
angular
  .module('app')
  .controller('PostController', PostController)

Check currentUser before calling id method


var util = require('util')
  , twitter = require('twitter')
  , colors = require('colors')
  , repl = require('repl')
  , keys = require('./keys')
  , local = repl.start()
  , first = true

var twish = new twitter(keys)

local.context.repl = local
local.context.colors = colors
local.context.twish = twish

local.defineCommand('tweet', function(tweet){
  twish
    .verifyCredentials(function(data) {
      console.log(util.inspect(data))
    })
    .updateStatus(tweet,
      function(data) {
        console.log(util.inspect(data))
    })
})
local.commands['.tweet'].help = 'Tweet as currently signed in user'

twish.stream('user', {track:'gkatsev', delimited:20}, function(stream){
  stream.on('data', function(data){
    if(first){
      first = false
      return
    }
    setTimeout(function(){
      var obj = {}
      obj.user = data.user.screen_name
      obj.text = data.text
      obj.data = data.created_at
      process.stdout.write(JSON.stringify(obj, null, '  '))
    }, 500)
  })
})

module.exports = twish

var util = require('util')
  , twitter = require('twitter')
  , colors = require('colors')
  , repl = require('repl')
  , keys = require('./keys')
  , local = repl.start()
  , first = true

var twish = new twitter(keys)

local.context.repl = local
local.context.colors = colors
local.context.twish = twish

local.defineCommand('tweet', function(tweet){
  twish
    .verifyCredentials(function(data) {
      console.log(util.inspect(data))
    })
    .updateStatus(tweet,
      function(data) {
        console.log(util.inspect(data))
    })
})
local.commands['.tweet'].help = 'Tweet as currently signed in user'

local.defineCommand('reload', function(){
  twish.get('/statuses/home_timeline.json', function(data){
    data.reverse().forEach(function(data){writeData(data)})
  })
})
local.commands['.reload'].help = 'Reload the home timeline'

local.defineCommand('replies', function(){
  twish.get('/statuses/mentions.json', function(data){
    data.reverse().forEach(function(data){writeData(data)})
  })
})
local.commands['.replies'].help = "Get latest replies"

//twish.stream('statuses/sample', function(stream){
twish.stream('user', {track:'gkatsev', delimited:20}, function(stream){
  stream.on('data', function(data){
    if(first){
      first = false
      return
    }
    writeData(data)
  })
})

function writeData(data){
  setTimeout(function(){
    console.log(
      colors.yellow(data.created_at)
    , colors.green(data.user.screen_name)
    , colors.white(data.text)
    )
  }, 50)
}
module.exports = twish

Add reload and replies commands


import {GroupMark} from '../marks/marktypes';

export default function(role, style, name, dataRef, interactive, encode, marks) {
  return {
    type: GroupMark,
    name: name,
    role: role,
    style: style,
    from: dataRef,
    interactive: interactive || false,
    encode: encode,
    marks: marks
  };
}

import {GroupMark} from '../marks/marktypes';

export default function(role, style, name, dataRef, interactive, encode, marks, layout) {
  return {
    type: GroupMark,
    name: name,
    role: role,
    style: style,
    from: dataRef,
    interactive: interactive || false,
    encode: encode,
    marks: marks,
    layout: layout
  };
}

Update guide group util to include layout option.


window._trackJs = {
    onError: function(payload, error) {

        // ignore an error caused by DealPly (http://www.dealply.com/) chrome extension
        if (payload.message.indexOf("DealPly") > 0) {
            return false;
        }

        if (payload.message.indexOf("NSA") > 0) {
            payload.message = "Redacted!";
        }

        // remove 401 network events, they happen all the time for us!
        payload.network = payload.network.filter(function(item) {
            if (item.statusCode !== 401) {
                return true;
            } else {
                return false;
            }
        });

        return true;
    }
};

window._trackJs = {
    onError: function(payload, error) {

        // ignore an error caused by DealPly (http://www.dealply.com/) chrome extension
        if (payload.message.indexOf("DealPly") > 0) {
            return false;
        }

        payload.network = payload.network.filter(function(item) {

            // ignore random errors from Intercom
            if (item.statusCode === 403 && payload.message.indexOf("intercom") > 0) {
                return false;
            }

            return true;
        });

        return true;
    }
};

Add ignore for 303 Intercom error not related to us


export default class RandomUsersRetriever {
  getUsers(numberOfUsers) {
    const users = [];

    // Add nodes to random parents
    for (let i = 0; i < numberOfUsers; i++) {
      const user = this.getAppUser(
        i,
        i === 0 ? null : this._getRandomIntInclusive(0, i)); // eslint-disable-line no-magic-numbers

      users.push(user);
    }

    return Promise.resolve(users);
  }

  getAppUser(i, managerId) {
    const user = {
      id: i,
      displayName: '{{ displayName }}',
      jobTitle: '{{ job_title }}',
      department: '{{ department }}',
      userPrincipalName: `${i}@example.com`,
      city: 'New York',
      state: 'NY',
      country: 'USA',
      email: `${i}@example.com`,
      telephoneNumber: '555-555-1212',
      manager: managerId ? this.getAppUser(managerId) : null
    };

    this.counter++;

    return user;
  }

  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
  _getRandomIntInclusive(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min; // eslint-disable-line no-magic-numbers
  }
}

/* eslint-disable no-magic-numbers */

export default class RandomUsersRetriever {
  getUsers(numberOfUsers, numberOfDepartments = 1) {
    const users = [];

    // Add nodes to random parents
    for (let i = 0; i < numberOfUsers; i++) {
      const user = this.getAppUser(
        i,
        i === 0 ? null : this._getRandomIntInclusive(0, i),
        `Department ${this._getRandomIntInclusive(1, numberOfDepartments)}`);

      users.push(user);
    }

    return Promise.resolve(users);
  }

  getAppUser(i, managerId, department) {
    const user = {
      id: i,
      displayName: '{{ displayName }}',
      jobTitle: '{{ job_title }}',
      department: department,
      userPrincipalName: `${i}@example.com`,
      city: 'New York',
      state: 'NY',
      country: 'USA',
      email: `${i}@example.com`,
      telephoneNumber: '555-555-1212',
      manager: managerId ? this.getAppUser(managerId) : null
    };

    this.counter++;

    return user;
  }

  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
  _getRandomIntInclusive(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

Add configurable numberOfDepartments to RandomUsersRetriever getUsers


import assert from 'assert';
import { parse } from 'graphql';
import { validate } from 'graphql/validation';
import { buildASTSchema } from 'graphql/utilities/buildASTSchema';

import { FieldsAreCamelCased } from '../../src/rules/fields_are_camel_cased';

describe('FieldsAreCamelCased rule', () => {
  it('catches fields that have are not camelcased', () => {
    const ast = parse(`
      type Query {
        # Invalid
        invalid_name: String

        # Valid
        thisIsValid: String

        # Invalid
        ThisIsInvalid: String
      }

      interface Something {
        # Invalid
        invalid_name: String

        # Valid
        thisIsValid: String
      }
    `);

    const schema = buildASTSchema(ast);
    const errors = validate(schema, ast, [FieldsAreCamelCased]);

    assert.equal(errors.length, 3);

    assert.equal(errors[0].ruleName, 'fields-are-camel-cased');
    assert.equal(
      errors[0].message,
      'The field `Query.invalid_name` is not camel cased.'
    );
    assert.deepEqual(errors[0].locations, [{ line: 4, column: 9 }]);

    assert.equal(errors[1].ruleName, 'fields-are-camel-cased');
    assert.equal(
      errors[1].message,
      'The field `Query.ThisIsInvalid` is not camel cased.'
    );
    assert.deepEqual(errors[1].locations, [{ line: 10, column: 9 }]);

    assert.equal(errors[2].ruleName, 'fields-are-camel-cased');
    assert.equal(
      errors[2].message,
      'The field `Something.invalid_name` is not camel cased.'
    );
    assert.deepEqual(errors[2].locations, [{ line: 15, column: 9 }]);
  });
});

import { FieldsAreCamelCased } from '../../src/rules/fields_are_camel_cased';
import { expectFailsRule, expectPassesRule } from '../assertions';

describe('FieldsAreCamelCased rule', () => {
  it('catches fields that have are not camelcased', () => {
    expectFailsRule(
      FieldsAreCamelCased,
      `
      type Query {
        # Invalid
        invalid_name: String

        # Valid
        thisIsValid: String

        # Invalid
        ThisIsInvalid: String
      }

      interface Something {
        # Invalid
        invalid_name: String

        # Valid
        thisIsValid: String
      }
    `,
      [
        {
          message: 'The field `Query.invalid_name` is not camel cased.',
          locations: [{ line: 4, column: 9 }],
        },
        {
          message: 'The field `Query.ThisIsInvalid` is not camel cased.',
          locations: [{ line: 10, column: 9 }],
        },
        {
          message: 'The field `Something.invalid_name` is not camel cased.',
          locations: [{ line: 15, column: 9 }],
        },
      ]
    );
  });
});

Use test helpers in FieldsAreCamelCased tests


/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/
'use strict';

togglbutton.render('#layout-container:not(.toggl)', {observe: true}, function (elem) {
  var link,
  description,
  project = $('.objectlink.breadcrumb-link.first-item', elem).textContent;

  description = function(){
    var delimiter = ' > ',
    w_type = window.location.pathname.split('/')[1],
    w_id = $('#layout-content').innerHTML.split('detailObjID":"')[1].split('"')[0],
    w_name = $('.detail-name-edit', elem).textContent;

    return w_type + delimiter + w_name + delimiter + w_id;
  };

  link = togglbutton.createTimerLink({
    className: 'attask',
    description: description,
    projectName: project
  });

  link.style.float = 'right';

  $('.user-actions').appendChild(link);

});

/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/
'use strict';

togglbutton.render('#layout-container:not(.toggl)', {observe: true}, function (elem) {
  var link,
  description,
  project = $('.objectlink.breadcrumb-link.first-item', elem).textContent;

  description = function(){
    var delimiter = ' > ',
    w_type = window.location.pathname.split('/')[1],
    w_id = $('#layout-content').innerHTML.split('detailObjID":"')[1].split('"')[0],
    w_name = $('.detail-name-edit', elem).textContent;

    return w_type + delimiter + w_name + delimiter + w_id;
  };

  link = togglbutton.createTimerLink({
    className: 'attask',
    description: description,
    projectName: project
  });

  var t_style = document.createElement("style");
  t_style.innerHTML = ".toggl-button{height: 62px;line-height: 62px;background-color:#333;background-position: 1em 50%;padding: 0 1em 0 2.75em;transition: all 0.5s ease;}#nav-toggl{height: 62px; vertical-align: middle; } .toggl-button:hover{background-color:#000 !important;} .toggl-button.active{background-color:#000;}";
  document.head.appendChild(t_style);

  var t_container = document.createElement("li");
  t_container.id = "nav-toggl";
  t_container.className = "navbar-item";

  t_container.appendChild(link);

  var navgroup = document.querySelector('.navbar-item-group.right');
  navgroup.insertBefore(t_container, navgroup.children[0]);

});

Move button to top nav bar Change styling to match Toggl color scheme