import React, { Component } from 'react';

class RowSizeControls extends Component {
  render() {
    return (

        Images per row
        <input type="number" min="0" max="20" value={this.props.value} onChange={this.props.onChange} />

    );
  }
}

export default RowSizeControls;

import React, { Component } from 'react';

class RowSizeControls extends Component {
  render() {
    return (

        Images per row
        <input
          style={{
            display: 'block',
            margin: '0 auto',
          }}
          type="range"
          min="1"
          max="10"
          value={this.props.value}
          onChange={this.props.onChange} />
        {this.props.value}

    );
  }
}

export default RowSizeControls;

Change row size control to slider


/**
 * Traverses up the DOM until an element is found that contains the passed in
 * class
 *
 * @param  {Object} el   - The initial DOM element reference
 * @param  {String} clss - Name of the class to check for
 * @return {Object}      - The first DOM element that has the specified class
 */
export function findParentWithClass(el, clss) {
    while (true) {
        if (el.classList.contains(clss)) {
            break
        }
        el = el.parentNode
    }
    return el
}

/**
 * Traverses up the DOM until an element is found that contains the passed in
 * class
 *
 * @param  {Object} el   - The initial DOM element reference
 * @param  {String} clss - Name of the class to check for
 * @return {Object}      - The first DOM element that has the specified class
 */
export const findParentWithClass = (el, clss) => {
    while (true) {
        if (el.classList.contains(clss)) {
            break;
        }
        el = el.parentNode;
    }
    return el;
}

// http://stackoverflow.com/a/7557433
export const isElementInViewport = (el) => {
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= $(window).height() &&
        rect.right <= $(window).width()
    );
}

Move isElementInViewport fn from Thread


(function(){

function mochaSinon(sinon){
  if (typeof beforeEach !== "function") {
    throw "mocha-sinon relies on mocha having been loaded.";
  }

  beforeEach(function() {
    if (null == this.sinon) {
      this.sinon = sinon.sandbox.create();
    } else {
      this.sinon.restore();
    }
  });
}

(function(plugin){
  if (
    typeof window === "object"
    && typeof window.sinon === "object"
  ) {
    plugin(window.sinon);
  } else if (typeof require === "function") {
    var sinon = require('sinon');

    module.exports = function () {
      plugin(sinon);
    };

    plugin(sinon);
  } else {
    throw "We could not find sinon through a supported module loading technique.  Pull requests are welcome!";
  }
})(mochaSinon);

})();

(function(){

function mochaSinon(sinon){
  if (typeof beforeEach !== "function") {
    throw "mocha-sinon relies on mocha having been loaded.";
  }

  beforeEach(function() {
    if (null == this.sinon) {
      if (sinon.createSandbox) {
        // Sinon 2+ (sinon.sandbox.create triggers a deprecation warning in Sinon 5)
        this.sinon = sinon.createSandbox();
      } else {
        this.sinon = sinon.sandbox.create();
      }
    } else {
      this.sinon.restore();
    }
  });
}

(function(plugin){
  if (
    typeof window === "object"
    && typeof window.sinon === "object"
  ) {
    plugin(window.sinon);
  } else if (typeof require === "function") {
    var sinon = require('sinon');

    module.exports = function () {
      plugin(sinon);
    };

    plugin(sinon);
  } else {
    throw "We could not find sinon through a supported module loading technique.  Pull requests are welcome!";
  }
})(mochaSinon);

})();

Use sinon.createSandbox instead of sinon.sandbox.create, if available


function change_password_submit ()
{
	var pgpsender = new PGPSender ();

	pgpsender.user_passwd ($("[name=username]").val (), $("[name=passwd_password]").val (), $("[name=passwd_password_new]").val (), function (data){

		if ( data.status != 0 ){
			$("[name=passwd_password]").val ("");
			$("[name=passwd_password_new]").val ("");
			show_alert ("alert_passwd", "error", data.message+".");
			return;
		}

		show_alert ("alert_passwd", "success", "Password changed successfully.");
	});
}

$(document).ready (function (){
	$("[name=passwd_submit]").click (change_password_submit);
});

function change_password_submit ()
{
	var pgpsender = new PGPSender ();

	pgpsender.user_passwd ($("[name=username]").val (), $("[name=passwd_password]").val (), $("[name=passwd_password_new]").val (), function (data){

		$("[name=passwd_password]").val ("");
		$("[name=passwd_password_new]").val ("");

		if ( data.status != 0 ){
			show_alert ("alert_passwd", "error", data.message+".");
			return;
		}

		show_alert ("alert_passwd", "success", "Password changed successfully.");
	});
}

$(document).ready (function (){
	$("[name=passwd_submit]").click (change_password_submit);
});

Truncate inputs after the passwd form is submitted.


var BVB = (function () {

    "use strict";

    function getFavSpanOnLoad(recipe_id){
        var fav_action = _.contains(favs, recipe_id) ? "unfav" : "fav";
        var fav_image = _.contains(favs, recipe_id) ? "img/fav.png" : "img/unfav.png";

        return ''
    }

    function getFavSpan(recipe_id){
        var action = $("span[data-recipe-id=" + recipe_id + "]").data("action")
        var fav_action = (action === "fav") ? "unfav" : "fav";
        var fav_image = (action === "fav") ? "img/fav.png" : "img/unfav.png";

        return ''
    }

    return {
        getFavSpanOnLoad: getFavSpanOnLoad,
        getFavSpan: getFavSpan
    };

}());

var BVB = (function () {

    "use strict";

    function getFavSpanOnLoad(recipe_id){
        var fav_action = _.contains(favs, recipe_id) ? "unfav" : "fav";
        var fav_image = _.contains(favs, recipe_id) ? "img/fav.png" : "img/unfav.png";

        return ''
    }

    function getFavSpan(recipe_id){
        var actual_action = $("span[data-recipe-id=" + recipe_id + "]").data("action");
        var fav_action = null,
            fav_image = null;
        if (actual_action === "fav"){
            fav_action = "unfav";
            fav_image = "img/fav.png"
            favs.push(recipe_id);
        }else{
            fav_action = "fav";
            fav_image = "img/unfav.png"
            favs.splice(favs.indexOf(recipe_id), 1)
        }

        return ''
    }

    return {
        getFavSpanOnLoad: getFavSpanOnLoad,
        getFavSpan: getFavSpan
    };

}());

Add some dummy memory func to favs


import AWS from 'aws-sdk'
import WError from 'verror'
import { resources } from './cloudFormation'

const dynamoDbTable = resources.JobStreamDynamoDBTable.PhysicalResourceId

export function updateRecord (object) {
  const { REGION: region } = process.env
  const awsDynamoDb = new AWS.DynamoDB({ region })
  return new Promise((resolve, reject) => {
    const params = {
      TableName: dynamoDbTable,
      Key: { id: { S: 'jobs' } },
      AttributeUpdates: Object.keys(object).reduce((prev, key) => ({
        [key]: {
          Action: 'PUT',
          Value: {
            S: object[key]
          }
        },
        ...prev
      }), {})
    }
    awsDynamoDb.updateItem(params, (err, result) => {
      if (err) {
        return reject(new WError(err, 'S3'))
      }
      resolve(result)
    })
  })
}

import AWS from 'aws-sdk'
import WError from 'verror'
import { resources } from './cloudFormation'

const dynamoDbTable = resources.JobStreamDynamoDBTable.PhysicalResourceId

export function updateRecord (object) {
  const { REGION: region } = process.env
  const awsDynamoDb = new AWS.DynamoDB({ region })
  return new Promise((resolve, reject) => {
    const params = {
      TableName: dynamoDbTable,
      Key: { id: { S: 'jobs' } },
      AttributeUpdates: Object.keys(object).reduce((prev, key) => ({
        [key]: {
          Action: 'PUT',
          Value: {
            S: object[key]
          }
        },
        ...prev
      }), {})
    }
    awsDynamoDb.updateItem(params, (err, result) => {
      if (err) {
        return reject(new WError(err, 'DynamoDB'))
      }
      resolve(result)
    })
  })
}

Fix error wrapper for DynamoDB


// TODO: review - use VM status functions similar to the ovirt-engine
export function canStart (state) {
  return state && state === 'down'
}

// TODO: review
export function canShutdown (state) {
  return canRestart(state)
}

// TODO: review
export function canRestart (state) {
  return state && (canSuspend(state) || state === 'paused' || state === 'suspended')
}

// TODO: review
export function canSuspend (state) {
  return state && (state === 'up' || state === 'powering_up')
}

// TODO: review
export function canConsole (state) {
  return canSuspend(state)
}

/*
 public enum VmStatus {
 UNASSIGNED,
 DOWN,
 UP,
 POWERING_UP,
 PAUSED,
 MIGRATING,
 UNKNOWN,
 NOT_RESPONDING,
 WAIT_FOR_LAUNCH,
 REBOOT_IN_PROGRESS,
 SAVING_STATE,
 RESTORING_STATE,
 SUSPENDED,
 IMAGE_LOCKED,
 POWERING_DOWN;
 }
 */

export function canStart (state) {
  return state && (state === 'down' || state === 'paused' || state === 'suspended')
}

export function canShutdown (state) {
  return canRestart(state) || state === 'reboot_in_progress'
}

export function canRestart (state) {
  return state && (state === 'up' || state === 'migrating')
}

export function canSuspend (state) {
  return state && (state === 'up')
}

export function canConsole (state) {
  return state && (state === 'up' || state === 'powering_up' || state === 'powering_down' || state === 'paused' || state === 'migrating' || state === 'reboot_in_progress' || state === 'saving_state')
}

/*
 public enum VmStatus {
 UNASSIGNED,
 DOWN,
 UP,
 POWERING_UP,
 PAUSED,
 MIGRATING,
 UNKNOWN,
 NOT_RESPONDING,
 WAIT_FOR_LAUNCH,
 REBOOT_IN_PROGRESS,
 SAVING_STATE,
 RESTORING_STATE,
 SUSPENDED,
 IMAGE_LOCKED,
 POWERING_DOWN;
 }
 */

Fix mapping of [allowed actions] vs. [VM state]


import React from 'react';
import connect from 'react-redux';
import { fetchPost } from '../actions';

class PostsShow extends React.Component {
  componentDidMount() {
    const { id } = this.props.match.params;
    this.props.fetchPost(id);
  }

  render() {
    const { post } = this.props;

    if (!post) {
      return (

Loading ...

      );
    }

    return (

{post.title}

Categories: {post.categories}

{post.content}

    )
  }
}

function mapStateToProps({ posts }, ownProps) {
  return { post: posts[ownProps.match.params.id] };
}

export default connect(mapStateToProps, { fetchPost })(PostsShow);

import React from 'react';
import connect from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchPost } from '../actions';

class PostsShow extends React.Component {
  componentDidMount() {
    const { id } = this.props.match.params;
    this.props.fetchPost(id);
  }

  render() {
    const { post } = this.props;

    if (!post) {
      return (

Loading ...

      );
    }

    return (

        <Link to="/">Back to Index</Link>

{post.title}

Categories: {post.categories}

{post.content}

    );
  }
}

function mapStateToProps({ posts }, ownProps) {
  return { post: posts[ownProps.match.params.id] };
}

export default connect(mapStateToProps, { fetchPost })(PostsShow);

Add back to index link


import Ember from 'ember';

export default Ember.Component.extend({
  element: 'form',
  classNames: ['ui form'],

  isSaving: null,
  validationRules: null,
  attributeBindings: ['isSaving', 'validationRules'],

  setup: function () {
    let validationRules = this.get('validationRules');

    if (validationRules) {
      this.$().form({
        fields: validationRules,
        inline: true,
        on: 'blur'
      });
    }
  }.on('didInsertElement').observes('validationRules.[]'),

  validateForm: function () {
    if (this.get('isSaving')) {
      this.$().form('validate form');
    }
  }.observes('isSaving')
});

import Ember from 'ember';

export default Ember.Component.extend({
  element: 'form',
  classNames: ['ui form'],

  isSaving: null,
  validationRules: null,

  setup: function () {
    let validationRules = this.get('validationRules');
    let validationRulesObject = {};

    validationRules.forEach((item, index, enumerable) => {
      if (Ember.typeOf(item.identifier === 'string')) {
        validationRulesObject[item.identifier] = item;
      }
    });

    if (validationRules) {
      this.$().form({
        fields: validationRulesObject,
        inline: true,
        on: 'blur'
      });
    }
  }.on('didInsertElement').observes('validationRules.[]'),

  validateForm: function () {
    if (this.get('isSaving')) {
      this.$().form('validate form');
    }
  }.observes('isSaving')
});

Convert validation rules to object before applying to form


import 'jsdom-global/register';
import { expect } from 'chai';
import { Tracker } from '../src';

describe('Tracker', () => {
  it('Should not be undefined', () => expect(Tracker).to.not.be.undefined);

  it('Should track relatively to the window', () => {
    return true;
  });
});

Test (Tracker): Remove unsudes jsdom-global


var demand = require('must');

var landmark = require('../').init(),
	Types = landmark.Field.Types;

/** Test List and Fields */

var Test = landmark.List('Test');

Test.add({
	date: Types.Date,
	datetime: Types.Datetime
});

Test.register();

/** Test Item */

var item = new Test.model();

/** FieldType: Date */

describe("Fields", function() {
	describe("Date", function() {

		it('should parse without error via underscore date', function() {
			item._.date.parse('20131204', 'YYYYMMDD');
		});

		it('should be the date we expect', function() {
			demand(item._.date.format()).to.equal('4th Dec 2013');
			demand(item._.date.format('YYYYMMDD')).to.equal('20131204');
		});

		it('should be a moment object', function() {
			demand(item._.date.moment()._isAMomentObject);
		});

	});
});

var demand = require('must');

var landmark = require('../').init(),
	Types = landmark.Field.Types;

/** Test List and Fields */

var Test = landmark.List('Test');
var item;

before(function() {

Test.add({
	date: Types.Date,
	datetime: Types.Datetime,
	bool: Types.Boolean
});

Test.register();
/** Test Item */

item = new Test.model();
});

/** FieldType: Date */

describe("Fields", function() {
	describe("Date", function() {

		it('should parse without error via underscore date', function() {
			item._.date.parse('20131204', 'YYYYMMDD');
		});

		it('should be the date we expect', function() {
			demand(item._.date.format()).to.equal('4th Dec 2013');
			demand(item._.date.format('YYYYMMDD')).to.equal('20131204');
		});

		it('should be a moment object', function() {
			demand(item._.date.moment()._isAMomentObject);
		});

	});
	describe("Boolean", function() {

		it('should update it\'s model if data passed is boolean true', function() {
			Test.fields.bool.updateItem(item, {
				'bool': true
			});
			demand(item.bool).to.be.true();

		});

       		it('should update it\'s  model if data passed is string \'true\'', function() {
			Test.fields.bool.updateItem(item, {
				'bool': 'true'
			});
			demand(item.bool).to.be.true();

		});

	});
});

Add updateItem test for field Boolean.


import moment from 'moment';

const Utils = {

  valueToDate(s) {
    var date = moment(s, "YYYY-MM-DD", true);
    return date.isValid() ? date : null;
  },

  dateToValue(d) {
    return d.format("YYYY-MM-DD");
  },

  isSameDay(a, b) {
    return a.startOf('day').isSame(b.startOf('day'));
  },

  isPastDay(day) {
    return day.diff(moment(), 'day') < 0;
  },

  isToday(day) {
    return Utils.isSameDay(moment(), day);
  },

  today() {
    return moment();
  }

};

export default Utils;

import moment from 'moment';

const Utils = {

  valueToDate(s) {
    var date = moment.utc(s, "YYYY-MM-DD", true);
    return date.isValid() ? date : null;
  },

  dateToValue(d) {
    return d.format("YYYY-MM-DD");
  },

  isSameDay(a, b) {
    return a.startOf('day').isSame(b.startOf('day'));
  },

  isPastDay(day) {
    return day.diff(moment(), 'day') < 0;
  },

  isToday(day) {
    return Utils.isSameDay(moment(), day);
  },

  today() {
    return moment();
  }

};

export default Utils;

Use utc() for date comparison


import Ember from 'ember';

export default Ember.Route.extend({
    model(params) {
        return this.store.findRecord('node', params.node_id);
    },
    setupController(controller, model) {
        controller.set('editedTitle', model.get('title'));
        controller.set('editedCategory', model.get('category'));
        controller.set('editedDescription', model.get('description'));
        controller.set('editedIsPublic', model.get('public'));
        this._super(...arguments);
    }
});

import Ember from 'ember';

export default Ember.Route.extend({
    model(params) {
        // Include will be swapped with "embed". // GET /nodes/{node_id}/?embed=children
        return this.store.findRecord('node', params.node_id, { include: 'children' });
    },
    setupController(controller, model) {
        controller.set('editedTitle', model.get('title'));
        controller.set('editedCategory', model.get('category'));
        controller.set('editedDescription', model.get('description'));
        controller.set('editedIsPublic', model.get('public'));
        this._super(...arguments);
    }
});

Add example of using include (which turns to embed) to dummy app.


import { module } from 'qunit';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

export default function(name, options = {}) {
  module(name, {
    beforeEach() {
      this.application = startApp();

      if (options.beforeEach) {
        options.beforeEach.apply(this, arguments);
      }

      this.register = (fullName, Factory) => {
        let instance = this.application.__deprecatedInstance__;
        let registry = instance.register ? instance : instance.registry;

        return registry.register(fullName, Factory);
      };
    },

    afterEach() {
      destroyApp(this.application);

      if (options.afterEach) {
        options.afterEach.apply(this, arguments);
      }
    }
  });
}

import { module } from 'qunit';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';

export default function(name, options = {}) {
  module(name, {
    beforeEach() {
      this.application = startApp();

      // BugFix: Can be removed after 2.1.  If resolver is set then fallback doesn't happen properly
      // for more information: https://github.com/emberjs/ember.js/commit/e3ad9e2772e066459ddb3af78be45d9a6003f5ce
      var legacyRegistry = this.application.__deprecatedInstance__.registry;
      if (legacyRegistry) {
        legacyRegistry.resolver = function noOpResolverBugFix() {};
      }

      this.register = (fullName, Factory) => {
        let instance = this.application.__deprecatedInstance__;
        let registry = instance.register ? instance : instance.registry;

        return registry.register(fullName, Factory);
      };

      if (options.beforeEach) {
        options.beforeEach.apply(this, arguments);
      }
    },

    afterEach() {
      destroyApp(this.application);

      if (options.afterEach) {
        options.afterEach.apply(this, arguments);
      }
    }
  });
}

Fix `register` for 1.13.13 - 2.1 apps


module.exports = {
    effify: (message, _, msg) => {
        const effify = (str) => {
            const dict = {
                'á': 'a',
                'é': 'e',
                'í': 'i',
                'ó': 'o',
                'ú': 'u',
                'ï': 'i',
                'ü': 'u',
            };

            return str.replace(/[aeiouáéíóúü]/gi, char => (
                `${char}f${dict[char.toLowerCase()] || char.toLowerCase()}`
            ));
        };

        message.channel.send(effify(msg));
    },
};

module.exports = {
    effify: (message, _, msg) => {
        if (!msg) {
            message.channel.send('Provide some text to effify.');
        } else {
            message.channel.send(effify(msg));
        }

        function effify(str) {
            const dict = {
                'á': 'a',
                'é': 'e',
                'í': 'i',
                'ó': 'o',
                'ú': 'u',
                'ï': 'i',
                'ü': 'u',
            };

            return str.replace(/[aeiouáéíóúü]/gi, char => (
                `${char}f${dict[char.toLowerCase()] || char.toLowerCase()}`
            ));
        }
    },
};

Make s.effify handle empty msg


var expect = require('expect.js')
  , ffi = require('../')

describe('ForeignFunction', function () {

  describe('async', function () {

    it('should call static "abs" bindings asynchronously', function (done) {
      var abs = ffi.ForeignFunction.build(
                    ffi.Bindings.StaticFunctions.abs
                  , 'int32', [ 'int32' ], true)
      abs(-1234).on('success', function (res) {
        expect(res).to.equal(1234)
        done()
      })
    })

  })
})

var expect = require('expect.js')
  , ffi = require('../')

describe('ForeignFunction', function () {

  it('should call the static "abs" bindings', function () {
    var abs = ffi.ForeignFunction.build(
        ffi.Bindings.StaticFunctions.abs
      , 'int32', [ 'int32' ])
    expect(abs).to.be.a('function')
    expect(abs(-1234)).to.equal(1234)
  })

  describe('async', function () {

    it('should call the static "abs" bindings asynchronously', function (done) {
      var abs = ffi.ForeignFunction.build(
                    ffi.Bindings.StaticFunctions.abs
                  , 'int32', [ 'int32' ], true)
      expect(abs).to.be.a('function')
      abs(-1234).on('success', function (res) {
        expect(res).to.equal(1234)
        done()
      })
    })

  })
})

Add synchronous ForeignFunction test case.


'use strict';

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

function uniqueFile(file) {
  var testPath = file;
  var counter = null;
  while (fs.existsSync(testPath + '.png')) {
    counter = (counter || 0) + 1;
    testPath = file + counter;
  }

  return testPath + '.png';
}

function getFile(directory, title) {
  // Take the test title and remove all special characters; limit to 20 characters
  var name = title.replace(/[^\w]/g, '_').replace(/_{2,}/g, '_').substr(0, 40);

  var filePath = path.join(directory, name);
  return uniqueFile(filePath);
}

function writeFile(directory, title, data, encoding) {
  var filename = getFile(directory, title);
  fs.writeFileSync(filename, data, encoding || 'base64');
  return filename;
}
module.exports = writeFile;

'use strict';

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

var MAX_NAME_LENGTH = 40;

function uniqueFile(file) {
  var testPath = file;
  var counter = null;
  while (fs.existsSync(testPath + '.png')) {
    counter = (counter || 0) + 1;
    testPath = file + counter;
  }

  return testPath + '.png';
}

function getFile(directory, title) {
  var name = title
    // 1. Replace all special characters with `_`
    .replace(/[^\w]/g, '_')
    // 2. Collapse consecutive underscores
    .replace(/_{2,}/g, '_')
    // 3. Apply character limit so filenames don't get out of hand
    .substr(0, MAX_NAME_LENGTH);

  var filePath = path.join(directory, name);
  return uniqueFile(filePath);
}

function writeFile(directory, title, data, encoding) {
  var filename = getFile(directory, title);
  fs.writeFileSync(filename, data, encoding || 'base64');
  return filename;
}
module.exports = writeFile;

Fix docs on test name truncation


import Ember from 'ember';
import { test } from 'qunit';
import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';

const { $ } = Ember;

// Original images sizes (from AJAX Content-Length header)
const uncompressedSizes = {
  'jpg': 35863,
  'png': 11795,
  'svg': 4619
};

moduleForAcceptance('Acceptance | imagemin');

test('Image files are compressed', function(assert) {
  assert.expect(3);

  visit('/');

  andThen(function() {

    // Compare the Content-Length of each image against the original
    // file size. Dummy app is setup to enable compression, so the
    // file sizes should be smaller.
    Object.keys(uncompressedSizes).forEach((ext) => {
      const url = `/logo.${ext}`;
      const uncompressedSize = uncompressedSizes[ext];

      $.get(url).then((data, status, xhr) => {
        const compressedSize = parseInt(xhr.getResponseHeader('Content-Length'), 10);

        assert.ok(compressedSize < uncompressedSize);
      });
    });

  });
});

import Ember from 'ember';
import { test } from 'qunit';
import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';

const { $ } = Ember;

// Original images sizes (from AJAX Content-Length header)
const uncompressedSizes = {
  'jpg': 35863,
  'png': 11795,
  'svg': 4619
};

moduleForAcceptance('Acceptance | imagemin');

test('Image files are compressed', function(assert) {
  assert.expect(3);

  visit('/');

  andThen(function() {
    // Compare the Content-Length of each image against the original
    // file size. Dummy app is setup to enable compression, so the
    // file sizes should be smaller.
    Object.keys(uncompressedSizes).forEach((ext) => {
      const url = `/logo.${ext}`;
      const uncompressedSize = uncompressedSizes[ext];

      $.get(url).then((data, status, xhr) => {
        const compressedSize = parseInt(xhr.getResponseHeader('Content-Length'), 10);

        assert.ok(compressedSize < uncompressedSize, `${ext} file compressed`);
      });
    });
  });
});

Add assertion message to acceptance tests


/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  ENV.contentSecurityPolicy = {
    'default-src': "'none'",
    'script-src': "'self'",
    'font-src': "'self' fonts.gstatic.com",
    'connect-src': "'self'",
    'img-src': "'self'",
    'media-src': "'self'"
  }

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

Fix for security policy messages


'use strict';

angular.module('<%= scriptAppName %>')
  .filter('<%= cameledName %>', function () {
    return function (input) {
      return '<%= cameledName %> filter: ' + input;
    };
  });

'use strict';
(function(){
  var <%= cameledName %> = function(){

    return function (input) {
      return '<%= cameledName %> filter: ' + input;
    };

  };
  angular
    .module('<%= scriptAppName %>')
    .filter('<%= cameledName %>', <%= cameledName %>);

}).call(this);

Refactor Javascript Filter to match best practices


#! /usr/bin/env node

require('../dist')
  .run(process.argv.slice(2))
  .catch(() => {
    process.exit(1);
  });

#! /usr/bin/env node

require('../dist') // eslint-disable-line import/no-unresolved
  .run(process.argv.slice(2))
  .catch(() => {
    process.exit(1);
  });

Add eslint for js files (fix eslint error)


import React, { Component, PropTypes } from "react"
import { baseUrl } from "../../../config"
import joinUri from "join-uri"

export default class Avatar extends Component {
  static propTypes = {
    template: PropTypes.string.isRequired,
    size: PropTypes.number,
  };

  static defaultProps = {
    size: 120,
  }

  render() {
    const { template, size, ...props } = this.props
    const url = baseUrl + joinUri("/", template.replace("{size}", size))
    return (
      <img src={ url } { ...props } />
    )
  }
}

import React, { Component, PropTypes } from "react"
import { baseUrlHttp as baseUrl } from "../../../config"
import joinUri from "join-uri"

export default class Avatar extends Component {
  static propTypes = {
    template: PropTypes.string.isRequired,
    size: PropTypes.number,
  };

  static defaultProps = {
    size: 120,
  }

  render() {
    const { template, size, ...props } = this.props
    const url = baseUrl + joinUri("/", template.replace("{size}", size))
    return (
      <img src={ url } { ...props } />
    )
  }
}

Use http protocol for Avatar


/**
 * Streams with pub/sub principle
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 10.05.18
 */
Imcms.define('imcms-streams', [], function () {

    return {};
});

/**
 * Streams with pub/sub principle
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 10.05.18
 */
Imcms.define('imcms-streams', ['imcms-displacing-array'], function (DisplacingArray) {

    var Subscriber = function (topic) {
        (this.topic = topic).addSubscriber(this);
    };

    Subscriber.prototype = {
        onPublish: function (doOnPublish) {
            this.doOnPublish = doOnPublish;
            return this;
        },
        runFromStart: function () {
            this.topic.forEachPublication(this.reactOnPublication.bind(this))
        },
        getReactionOnPublication: function (content) {
            return function () {
                this.doOnPublish(content);
            }.bind(this);
        },
        reactOnPublication: function (content) {
            setTimeout(this.getReactionOnPublication(content));
        }
    };

    var Topic = function (name) {
        this.name = name;
        this.publications = new DisplacingArray(1024);
        this.subscribers = new DisplacingArray(1024);
    };

    Topic.prototype = {
        forEachPublication: function (doOnPublication) {
            this.publications.forEach(doOnPublication);
        },
        addSubscriber: function (subscriber) {
            this.subscribers.push(subscriber);
        },
        getAllSubscribers: function () {
            return this.subscribers;
        },
        savePublication: function (content) {
            this.publications.push(content);
        },
        publish: function (content) {
            this.savePublication(content);

            this.getAllSubscribers().forEach(function (subscriber) {
                subscriber.reactOnPublication(content);
            });
        }
    };

    var namePerTopic = {};

    function getOrCreateTopic(topicName) {
        return namePerTopic[topicName] || (namePerTopic[topicName] = new Topic(topicName));
    }

    var Publisher = function (topic) {
        this.topic = topic;
    };

    Publisher.prototype = {
        publish: function (content) {
            this.topic.publish(content);
        }
    };

    return {
        createSubscriberOnTopic: function (topicName) {
            return new Subscriber(getOrCreateTopic(topicName));
        },
        subscribeFromStart: function (topicName, doOnPublish) {
            this.createSubscriberOnTopic(topicName)
                .onPublish(doOnPublish)
                .runFromStart();
        },
        createPublisherOnTopic: function (topicName) {
            return new Publisher(getOrCreateTopic(topicName));
        }
    }
});

Add a "pin" icon to imCMS's panel: - Implemented streams module.


beforeEach(function() {
	this.array = [0, 2, 4, 6, 8, 10, 12, 14];
});

it("Should accept a function with three parameters: the value of the element, the index of the element and the object being traversed", function() {
	var array = this.array;

	array.some(function(value, index, object) {
		expect(object).to.be(array);
		expect(object[index]).to.be(value);
		return false;
	});
});

it("Should accept an optional 'this' argument as its second argument which becomes the 'this' value for the function argument", function() {
	var mockThis = { foo: "bar" };
	var array = this.array;

	array.some(function(value) {
		expect(this).to.be(mockThis);
		expect(this.foo).to.be(mockThis.foo);
	}, mockThis);
});

beforeEach(function() {
	this.array = [0, 2, 4, 6, 8, 10, 12, 14];
});

it("Should accept a function with three parameters: the value of the element, the index of the element and the object being traversed", function() {
	var array = this.array;

	array.some(function(value, index, object) {
		expect(object).to.be(array);
		expect(object[index]).to.be(value);
		return false;
	});
});

it("Should accept an optional 'this' argument as its second argument which becomes the 'this' value for the function argument", function() {
	var mockThis = { foo: "bar" };
	var array = this.array;

	array.some(function(value) {
		expect(this).to.be(mockThis);
		expect(this.foo).to.be(mockThis.foo);
		return false;
	}, mockThis);
});

it("Should pass a reference to the array as the third parameter to the function argument", function() {
	var array = [10];

	array.some(function(value, index, object) {
		object[0] = 100;
		return false;
	});

	expect(array[0]).to.be(100);
});

Test that the third parameter to the function is a reference to the array


const { execSync } = require("child_process");
const LoadingStrategy = require("./LoadingStrategy");
const VersionRange = require("./VersionRange");

class Native extends LoadingStrategy {
  load() {
    const versionString = this.validateAndGetSolcVersion();
    const command = "solc --standard-json";

    try {
      return {
        compile: options => String(execSync(command, { input: options })),
        version: () => versionString
      };
    } catch (error) {
      if (error.message === "No matching version found") {
        throw this.errors("noVersion", versionString);
      }
      throw new Error(error);
    }
  }

  validateAndGetSolcVersion() {
    let version;
    try {
      version = execSync("solc --version");
    } catch (error) {
      throw this.errors("noNative", null, error);
    }
    return new VersionRange(this.config).normalizeSolcVersion(version);
  }
}

module.exports = Native;

const { execSync } = require("child_process");
const LoadingStrategy = require("./LoadingStrategy");
const VersionRange = require("./VersionRange");

class Native extends LoadingStrategy {
  load() {
    const versionString = this.validateAndGetSolcVersion();
    const command = "solc --standard-json";
    const maxBuffer = 1024 * 1024 * 10;

    try {
      return {
        compile: options =>
          String(execSync(command, { input: options, maxBuffer })),
        version: () => versionString
      };
    } catch (error) {
      if (error.message === "No matching version found") {
        throw this.errors("noVersion", versionString);
      }
      throw new Error(error);
    }
  }

  validateAndGetSolcVersion() {
    let version;
    try {
      version = execSync("solc --version");
    } catch (error) {
      throw this.errors("noNative", null, error);
    }
    return new VersionRange(this.config).normalizeSolcVersion(version);
  }
}

module.exports = Native;

Increase maxBuffer to 10MB when compiling using native compiler


import { combineReducers, createStore } from 'redux'
import auth from '../modules/Auth/reducer'
import households from '../modules/Households/reducer'
import members from '../modules/Members/reducer'
import meals from '../modules/Meals/reducer'
import error from '../modules/Errors/reducer'
import { reducer as form } from 'redux-form'
import middleware from '../../middleware'
import { loadingBarReducer } from 'react-redux-loading-bar'

const rootReducer = combineReducers({
  auth,
  form,
  households,
  meals,
  members,
  error,
  loadingBar: loadingBarReducer,
})

export default createStore(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
  middleware
)

import { combineReducers, createStore } from 'redux'
import auth from '../modules/Auth/reducer'
import households from '../modules/Households/reducer'
import members from '../modules/Members/reducer'
import meals from '../modules/Meals/reducer'
import error from '../modules/Errors/reducer'
import { reducer as form } from 'redux-form'
import middleware from '../../middleware'
import { loadingBarReducer: loadingBar } from 'react-redux-loading-bar'

const rootReducer = combineReducers({
  auth,
  form,
  households,
  meals,
  members,
  error,
  loadingBar,
})

export default createStore(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
  middleware
)

Update reducer naming in combine reducers


'use strict';

var Subscript = require('./Subscript');
var SubscriptHTMLConverter = require('./SubscriptHTMLConverter');
var SubscriptXMLConverter = require('./SubscriptXMLConverter');
var AnnotationCommand = require('../../ui/AnnotationCommand');
var AnnotationComponent = require('../../ui/AnnotationComponent');
var AnnotationTool = require('../../ui/AnnotationTool');

module.exports = {
  name: 'subscript',
  configure: function(config) {
    config.addNode(Subscript);
    config.addConverter('html', SubscriptHTMLConverter);
    config.addConverter('xml', SubscriptXMLConverter);
    config.addComponent('subscript', AnnotationComponent);
    config.addCommand('Subscript', AnnotationCommand, { nodeType: 'subscript' });
    config.addTool('subscript', AnnotationTool);
    config.addIcon('subscript', { 'fontawesome': 'fa-subscript' });
    config.addStyle(__dirname, '_subscript.scss');
    config.addLabel('subscript', {
      en: 'Subscript',
      de: 'Tiefgestellt'
    });
  }
};

'use strict';

var Subscript = require('./Subscript');
var SubscriptHTMLConverter = require('./SubscriptHTMLConverter');
var SubscriptXMLConverter = require('./SubscriptXMLConverter');
var AnnotationCommand = require('../../ui/AnnotationCommand');
var AnnotationComponent = require('../../ui/AnnotationComponent');
var AnnotationTool = require('../../ui/AnnotationTool');

module.exports = {
  name: 'subscript',
  configure: function(config) {
    config.addNode(Subscript);
    config.addConverter('html', SubscriptHTMLConverter);
    config.addConverter('xml', SubscriptXMLConverter);
    config.addComponent('subscript', AnnotationComponent);
    config.addCommand('subscript', AnnotationCommand, { nodeType: 'subscript' });
    config.addTool('subscript', AnnotationTool);
    config.addIcon('subscript', { 'fontawesome': 'fa-subscript' });
    config.addStyle(__dirname, '_subscript.scss');
    config.addLabel('subscript', {
      en: 'Subscript',
      de: 'Tiefgestellt'
    });
  }
};

Fix case of command name


/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

importScripts('console.js');
importScripts('message_handler.js');
importScripts('../pdf.js');
importScripts('../fonts.js');
importScripts('../crypto.js');
importScripts('../glyphlist.js');
importScripts('handler.js');

// Listen for messages from the main thread.
var pdfDoc = null;

var handler = new MessageHandler("worker", this);
WorkerHandler.setup(handler);

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

importScripts('console.js');
importScripts('message_handler.js');
importScripts('../pdf.js');
importScripts('../fonts.js');
importScripts('../crypto.js');
importScripts('../glyphlist.js');
importScripts('../metrics.js');
importScripts('handler.js');

// Listen for messages from the main thread.
var pdfDoc = null;

var handler = new MessageHandler("worker", this);
WorkerHandler.setup(handler);

Add missing metrics.js for worker


var coalesce = require('./coalesce')
var assert = require('assert')
var fs = require('fs')
var path = require('path')
var util = require('util')

var main = require('prolific.main')

var shuttle = {
    main: coalesce(function () {
        return require.main.require('prolific.shuttle')
    }, { filename: null }),
    mine: coalesce(function () {
        return require('prolific.shuttle')
    }, { filename: null })
}

assert(shuttle.main.filename == shuttle.mine.filename,
    util.format(fs.readFileSync(path.join(__dirname, 'error.txt'), 'utf8'),
        shuttle.mine.filename, shuttle.main.filename))

exports.sink = coalesce(shuttle.mine.main, main)

var coalesce = require('./coalesce')
var assert = require('assert')
var fs = require('fs')
var path = require('path')
var util = require('util')

var sink = require('prolific.main')

var shuttle = {
    main: coalesce(function () {
        return require.main.require('prolific.shuttle')
    }, { filename: null }),
    mine: coalesce(function () {
        return require('prolific.shuttle')
    }, { filename: null })
}

assert(shuttle.main.filename == shuttle.mine.filename,
    util.format(fs.readFileSync(path.join(__dirname, 'error.txt'), 'utf8'),
        shuttle.mine.filename, shuttle.main.filename))

exports.sink = coalesce(shuttle.mine.sink, sink)

Fix bug, Shuttle exports Main as `sink`.


"use strict";

function Kablammo() {
  // When hit is to complement of nucleotide sequence (whether for query or
  // subject), we will show the HSPs relative to the coordinates of the
  // original (i.e., input or DB) sequence if this value is false, or relative
  // to the coordinates of its complement if this value is true.
  var use_complement_coords = false;

  this._aln_viewer    = new AlignmentViewer();
  this._aln_exporter  = new AlignmentExporter();
  this._grapher       = new Grapher(this._aln_viewer, this._aln_exporter, use_complement_coords);
  this._parser        = new BlastParser(use_complement_coords);
  this._loader        = new BlastResultsLoader(this._parser);
  this._iface         = new Interface(this._grapher, this._loader);
  this._img_exporter  = new ImageExporter('#results-container', '.export-to-svg', '.export-to-png');

  $('.start-tour').click();
}

function main() {
  new Kablammo();
}

main();

"use strict";

function Kablammo() {
  // When hit is to complement of nucleotide sequence (whether for query or
  // subject), we will show the HSPs relative to the coordinates of the
  // original (i.e., input or DB) sequence if this value is false, or relative
  // to the coordinates of its complement if this value is true.
  var use_complement_coords = false;

  this._aln_viewer    = new AlignmentViewer();
  this._aln_exporter  = new AlignmentExporter();
  this._grapher       = new Grapher(this._aln_viewer, this._aln_exporter, use_complement_coords);
  this._parser        = new BlastParser(use_complement_coords);
  this._loader        = new BlastResultsLoader(this._parser);
  this._iface         = new Interface(this._grapher, this._loader);
  this._img_exporter  = new ImageExporter('#results-container', '.export-to-svg', '.export-to-png');
}

function main() {
  new Kablammo();
}

main();

Remove mistakenly added testing line.


function halfBandwidth(scale: Function): number {
    if(scale.bandwidth) {
        return scale.bandwidth() / 2;
    }
    return 0;
}

/**
 * applyScaledValue
 *
 * Because primitive dimensions correlate to an onScreen pixel value the need
 * a slightly different calculation when applying the scale. This is abstracted away
 * to save the headache of knowing to recaclulate.
 *
 *
 * @param      {string}    dimensionName
 * @param      {Function}  scale
 * @param      {any}       value
 * @param      {object}    props     A chart elements inherited props
 * @return     {any}
 */
export default function applyScaledValue(dimensionName: sting, scale: Function, value: *, props: Object): * {
    // console.log(dimensionName, typeof scale, value);
    switch (dimensionName) {
        case 'x':
            return scale(value) + halfBandwidth(scale);

        case 'y':
            return props.height - (scale(value) + halfBandwidth(scale));

        default:
            // the default behavior is to apply current column through current scale
            return scale(value);
    }
}

function halfBandwidth(scale: Function): number {
    if(scale.bandwidth) {
        return scale.bandwidth() / 2;
    }
    return 0;
}

/**
 * applyScaledValue
 *
 * Because primitive dimensions correlate to an onScreen pixel value the need
 * a slightly different calculation when applying the scale. This is abstracted away
 * to save the headache of knowing to recaclulate.
 *
 *
 * @param      {string}    dimensionName
 * @param      {Function}  scale
 * @param      {any}       value
 * @param      {object}    props     A chart elements inherited props
 * @return     {any}
 */
export default function applyScaledValue(dimensionName: string, scale: Function, value: *, props: Object): * {
    if(value === null) {
        return value;
    }

    switch (dimensionName) {
        case 'x':
            return scale(value) + halfBandwidth(scale);

        case 'y':
            return props.height - (scale(value) + halfBandwidth(scale));

        default:
            // the default behavior is to apply current column through current scale
            return scale(value);
    }
}

Make sure not to scale null values


module.exports = {

    _interceptor: function (req, res) {
        var _this = this;

        this.options.Vue.http.interceptors.push(function (request, next) {
            if (req) { req.call(_this, request); }

            next(function (response) {
                if (res) { res.call(_this, response); }
            });
        });
    },

    _invalidToken: function (res) {
        if (res.status === 401) {
            this.logout();
        }
    },

    _httpData: function (res) {
        return res.data || {};
    },

    _http: function (data) {
        this.options.Vue.http(data).then(data.success, data.error);
    },

    _getHeaders: function (res) {
        var i,
            data = {},
            headers = res.headers.map;

        for (i in headers) {
            data[i] = headers[i][0];
        }

        return data;
    },

    _setHeaders: function (req, headers) {
        var i;

        for (i in headers) {
            req.headers.set(i, headers[i]);
        }
    }
};

module.exports = {

    _init: function () {
        if ( ! this.options.Vue.http) {
            return 'vue-resource.1.x.js : Vue.http must be set.';
        }
    },

    _interceptor: function (req, res) {
        var _this = this;

        this.options.Vue.http.interceptors.push(function (request, next) {
            if (req) { req.call(_this, request); }

            next(function (response) {
                if (res) { res.call(_this, response); }
            });
        });
    },

    _invalidToken: function (res) {
        if (res.status === 401) {
            this.logout();
        }
    },

    _httpData: function (res) {
        return res.data || {};
    },

    _http: function (data) {
        this.options.Vue.http(data).then(data.success, data.error);
    },

    _getHeaders: function (res) {
        var i,
            data = {},
            headers = res.headers.map;

        for (i in headers) {
            data[i] = headers[i][0];
        }

        return data;
    },

    _setHeaders: function (req, headers) {
        var i;

        for (i in headers) {
            req.headers.set(i, headers[i]);
        }
    }
};

Add _init check for Vue.http being set


/* global describe, it */

import {exec} from '../../../src/utils/util';
import assert from 'assert';

describe("Oi CLI", () => {

  it("can be invoked", () => {
    const result = exec('node dist/bin/oi.js -v');
    console.log(result);
    assert(result.stdout.trim().match(/\d+\.\d+\.\d+/), "Invalid version output");
  });

});

/* global describe, it */

import {exec} from '../../../src/utils/util';
import assert from 'assert';
require('chai').should();

describe("Oi CLI", () => {

  it("can be invoked", () => {
    const result = exec('node dist/bin/oi.js -v');
    result.stdout.trim().should.match(/^\d+\.\d+\.\d+$/);
  });

});

Use Chai to assert CLI


import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'

import isPlainObject from './isPlainObject'

export function getDefaultMiddleware() {
  return [thunk]
}

export function configureStore(options = {}) {
  const {
    reducer,
    middleware = getDefaultMiddleware(),
    devTools = true,
    preloadedState,
    enhancers = []
  } = options

  let rootReducer

  if (typeof reducer === 'function') {
    rootReducer = reducer
  } else if (isPlainObject(reducer)) {
    rootReducer = combineReducers(reducer)
  } else {
    throw new Error(
      'Reducer argument must be a function or an object of functions that can be passed to combineReducers'
    )
  }

  const middlewareEnhancer = applyMiddleware(...middleware)

  const storeEnhancers = [middlewareEnhancer, ...enhancers]

  let finalCompose = devTools ? composeWithDevTools : compose

  const composedEnhancer = finalCompose(...storeEnhancers)

  const store = createStore(rootReducer, preloadedState, composedEnhancer)

  return store
}

import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import immutableStateInvariant from 'redux-immutable-state-invariant'

import isPlainObject from './isPlainObject'

const IS_PRODUCTION = process.env.NODE_ENV === 'production'

export function getDefaultMiddleware(isProduction = IS_PRODUCTION) {
  const middlewareArray = [thunk]

  if (!isProduction) {
    middlewareArray.unshift(immutableStateInvariant())
  }

  return middlewareArray
}

export function configureStore(options = {}) {
  const {
    reducer,
    middleware = getDefaultMiddleware(),
    devTools = true,
    preloadedState,
    enhancers = []
  } = options

  let rootReducer

  if (typeof reducer === 'function') {
    rootReducer = reducer
  } else if (isPlainObject(reducer)) {
    rootReducer = combineReducers(reducer)
  } else {
    throw new Error(
      'Reducer argument must be a function or an object of functions that can be passed to combineReducers'
    )
  }

  const middlewareEnhancer = applyMiddleware(...middleware)

  const storeEnhancers = [middlewareEnhancer, ...enhancers]

  let finalCompose = devTools ? composeWithDevTools : compose

  const composedEnhancer = finalCompose(...storeEnhancers)

  const store = createStore(rootReducer, preloadedState, composedEnhancer)

  return store
}

Add mutation detection in dev by default


var path = require('path');
var fs = require('fs');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: path.join(__dirname, 'src/frontend/app'),
    cache: true,

    output: {
        path: path.join(__dirname, '/public/'),
        filename: 'bundle.[hash].js'
    },

    module: {
        loaders: [
            { test: /\.css$/,   loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
            { test: /\.jsx?$/,  loader: "babel-loader", exclude: /node_modules/ },
            { test: /\.less$/,  loader: "style-loader!css-loader!less-loader" },
            { test: /\.(ttf|eot|otf|svg|woff|woff2)(\?.+)?$/, loader: "url-loader?limit=10000" },
            { test: /\.scss$/,  loader: "style!css!sass?includePaths[]=" + (path.resolve(__dirname, "./node_modules")) }
        ]
    },

    plugins: [
        new ExtractTextPlugin('bundle.[hash].css'),
        function() {
            this.plugin('done', function(stats) {
                fs.writeFile(path.resolve(__dirname, './public/hash'), stats.hash);
            });
        }
    ],

    devtool: 'source-map'
};

var path = require('path');
var fs = require('fs');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack');

module.exports = {
    entry: path.join(__dirname, 'src/frontend/app'),
    cache: true,

    output: {
        path: path.join(__dirname, '/public/'),
        filename: 'bundle.[hash].js'
    },

    module: {
        loaders: [
            { test: /\.css$/,   loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
            { test: /\.jsx?$/,  loader: "babel-loader", exclude: /node_modules/ },
            { test: /\.less$/,  loader: "style-loader!css-loader!less-loader" },
            { test: /\.(ttf|eot|otf|svg|woff|woff2)(\?.+)?$/, loader: "url-loader?limit=10000" },
            { test: /\.scss$/,  loader: "style!css!sass?includePaths[]=" + (path.resolve(__dirname, "./node_modules")) }
        ]
    },

    plugins: [
        new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /nb$/),
        new ExtractTextPlugin('bundle.[hash].css'),
        function() {
            this.plugin('done', function(stats) {
                fs.writeFile(path.resolve(__dirname, './public/hash'), stats.hash);
            });
        }
    ],

    devtool: 'source-map'
};

Remove unused moment locales from webpack bundle


'use strict';

const amqplib = require('amqplib');
const amqpUri = require('amqp-uri');

/**
 * Nortstar client service.
 */
module.exports = {
  /**
   * Configure RabbitMQ client.
   */
  getClient() {
    if (!this.client) {
      this.client = amqplib.connect(amqpUri(sails.config.amqp.connection));
    }
    return this.client;
  },
  getChannel() {
    return this.getClient().then(conn => conn.createChannel());
  },
  publishUserPasswordResetTransactional(message) {
    const queueName = 'transactionalQueue';
    const exchangeName = 'transactionalExchange';
    const bindingPattern = '*.*.transactional';
    const routingKey = 'user.password_reset.transactional';
    const msgBuffer = new Buffer(message.toString());

    this.bindQueueToExchange(queueName, exchangeName, bindingPattern)
      .then((channel) => {
        channel.publish(exchangeName, routingKey, msgBuffer)
        .then(result => result);
      });
  },
  bindQueueToExchange(queueName, exchangeName, routingKey) {
    return new Promise((resolve, reject) => {
      this.getChannel()
        .then((channel) => {
          channel.assertExchange(exchangeName, 'topic').then(() => {
            channel.assertQueue(queueName).then(() => {
              channel.bindQueue(queueName, exchangeName, routingKey)
                .then(() => resolve(channel));
            });
          });
        })
        .catch(error => reject(error));
    });
  },
};

'use strict';

const amqplib = require('amqplib');
const amqpUri = require('amqp-uri');

/**
 * Nortstar client service.
 */
module.exports = {
  /**
   * Configure RabbitMQ client.
   */
  getClient() {
    if (!this.client) {
      this.client = amqplib.connect(amqpUri(sails.config.amqp.connection));
    }
    return this.client;
  },
  getChannel() {
    return this.getClient().then(conn => conn.createChannel());
  },
  publishUserPasswordResetTransactional(message) {
    const queueName = 'transactionalQueue';
    const exchangeName = 'transactionalExchange';
    const bindingPattern = '*.*.transactional';
    const routingKey = 'user.password_reset.transactional';
    const msgBuffer = new Buffer(JSON.stringify(message), 'utf-8');

    this.bindQueueToExchange(queueName, exchangeName, bindingPattern)
      .then((channel) => {
        channel.publish(exchangeName, routingKey, msgBuffer)
        .then(result => result);
      });
  },
  bindQueueToExchange(queueName, exchangeName, routingKey) {
    return new Promise((resolve, reject) => {
      this.getChannel()
        .then((channel) => {
          channel.assertExchange(exchangeName, 'topic').then(() => {
            channel.assertQueue(queueName).then(() => {
              channel.bindQueue(queueName, exchangeName, routingKey)
                .then(() => resolve(channel));
            });
          });
        })
        .catch(error => reject(error));
    });
  },
};

Fix payload sent to RabbitMQ.


/**
 *
 * Displays a single property of an object.
 *
 * ## Examples
 *
 *     <div
 *          data-hull-widget="property@hull"
 *          data-hull-id="me"
 *          data-hull-property="name"
 *          data-hull-default-text="Not found"
 *     ></div>
 *
 * ## Options
 *
 * - `id`: Required. The id of the object for which we want to display a property.
 * - `property`: Required. The name of the property to be displayed. Can be a path
 *   if the property is nested (_eg_ `stats.likes`)
 * - `default-text`: The text to be displayed if the value does not exist
 *
 * ## Template
 *
 * - `property`: Displays the property with no markup. Markup must be in the parents widget
 *   for easier customization
 *
 */
define({
  type: 'Hull',

  templates: [
    'property'
  ],

  datasources: {
    object: ':id'
  },

  refreshEvents: ['model.hull.me.change'],

  beforeRender: function(data) {
    "use strict";
    var defaultText = this.options.defaultText || 'No value';
    var value = this.findProp(data.object, this.options.property);
    return {
      property: value !== undefined ? value : defaultText
    };
  },

  findProp: function (obj, prop) {
    "use strict";
    prop.split('.').forEach(function (_p) {
      obj = !obj ? undefined : obj[_p];
    });
    return obj;
  }
});

/**
 *
 * Displays a single property of an object.
 *
 * ## Examples
 *
 *     <div
 *          data-hull-widget="property@hull"
 *          data-hull-id="me"
 *          data-hull-property="name"
 *          data-hull-default-text="Not found"
 *     ></div>
 *
 * ## Options
 *
 * - `id`: Required. The id of the object for which we want to display a property.
 * - `property`: Required. The name of the property to be displayed. Can be a path
 *   if the property is nested (_eg_ `stats.likes`)
 * - `default-text`: The text to be displayed if the value does not exist
 *
 * ## Template
 *
 * - `property`: Displays the property with no markup. Markup must be in the parents widget
 *   for easier customization
 *
 */
define({
  type: 'Hull',

  templates: [
    'property'
  ],

  datasources: {
    object: ':id'
  },

  refreshEvents: ['model.hull.me.change'],

  beforeRender: function(data) {
    "use strict";
    var defaultText = this.options.defaultText || 'No value';
    var value = this.findProp(data.object, this.options.property);
    return {
      property: value !== undefined ? value : defaultText
    };
  },

  findProp: function (obj, prop) {
    "use strict";
    var parts = prop.split('.');
    _.each(parts, function(p) {
      obj = !obj ? undefined : obj[p];
    });
    return obj;
  }
});

Use _.each instead of forEach


"use strict";
/**
 *  Collection of spines.
 **/
 define([
    "underscore",
    "backbone"
], function(_, Backbone) {

    var SpineCollection = Backbone.Collection.extend({
        filterKey: null,
        /**
         * sortBy comparator: return the title, by which BB will sort the collection.
         **/
        comparator: function(spine) {
            return spine.get("title");
        },

        url: function() {
            var url = "_view/spines";
            if (!_.isNull(this.filterKey)) {
                // Get a subset of the spines, by the given key.
                url = url + '?key="' + this.filterKey + '"';
            }

            return url;
        },
        /**
         * Parse the returned JSON.
         **/
        parse: function(response, options){
            var parsed = [];

            if (_.has(response, "rows") && _.isArray(response.rows)) {
                _.each(response.rows, function(row) {
                    parsed.push({
                        id: row.id,
                        firstLetter: row.value.firstLetter,
                        title: row.value.title
                    });
                });
            }

            return parsed;
        }
    });

    return SpineCollection;
});

"use strict";
/**
 *  Collection of spines.
 **/
 define([
    "underscore",
    "backbone"
], function(_, Backbone) {

    var SpineCollection = Backbone.Collection.extend({
        filterKey: null,
        /**
         * sortBy comparator: return the title, by which BB will sort the collection.
         **/
        comparator: function(spine) {
            return spine.get("title").toLowerCase();
        },

        url: function() {
            var url = "_view/spines";
            if (!_.isNull(this.filterKey)) {
                // Get a subset of the spines, by the given key.
                url = url + '?key="' + this.filterKey + '"';
            }

            return url;
        },
        /**
         * Parse the returned JSON.
         **/
        parse: function(response, options){
            var parsed = [];

            if (_.has(response, "rows") && _.isArray(response.rows)) {
                _.each(response.rows, function(row) {
                    parsed.push({
                        id: row.id,
                        firstLetter: row.value.firstLetter,
                        title: row.value.title
                    });
                });
            }

            return parsed;
        }
    });

    return SpineCollection;
});

Sort titles by lower case


import { createMachine, interpret } from 'xstate';
import './style.css';

const app = document.querySelector('#app');

app.innerHTML = `

XState JavaScript Template

Open the console!

  <button id="toggle">Toggle</button>
`;

// Edit your machine(s) here
const machine = createMachine({
  id: 'machine',
  initial: 'inactive',
  context: {
    count: 0
  },
  states: {
    inactive: {
      on: { TOGGLE: 'active' }
    },
    active: {
      on: { TOGGLE: 'inactive' }
    }
  }
});

const button = document.querySelector('#toggle');

// Edit your service(s) here
const service = interpret(machine).onTransition((state) => {
  console.log(state.value);
  button.dataset.state = state.value;
});

service.start();

button?.addEventListener('click', () => {
  service.send('TOGGLE');
});

import { interpret } from 'xstate';
import { toggleMachine } from './toggle.machine';
import './style.css';

const app = document.querySelector('#app');

app.innerHTML = `

XState JavaScript Template

Open the console!

  <button id="toggle">Toggle</button>
`;

const button = document.querySelector('#toggle');

// Edit your service(s) here
const service = interpret(toggleMachine).onTransition((state) => {
  console.log(state.value);
  button.dataset.state = state.value;
});

service.start();

button?.addEventListener('click', () => {
  service.send('TOGGLE');
});

Move machines to separate files


import React from 'react'
import PersonCard from '../people/PersonCard'
import PersonGrid from '../people/PersonGrid'
import { parsePost } from '../posts/PostParser'
import { getLinkObject, getLinkArray } from '../../util/json_helper'

export function onboardingCommunities(users) {
  return (

      {users.map((user, i) => {
        return <PersonCard ref={'personCard_' + i} user={user} key={i} />
      })}

  )
}

export function onboardingPeople(users) {
  return (

      {users.map((user, i) => {
        return <PersonGrid ref={'personGrid_' + i} user={user} key={i} />
      })}

  )
}

export function postsAsGrid(posts, json) {
  return (

      {posts.map((post, i) => {
        return (

            {parsePost(post, json)}

        )
      })}

  )
}

export function userDetail(users, json) {
  const posts = getLinkArray(users[0], 'posts', json)
  return postsAsGrid(posts, json)
}

export { getLinkObject, getLinkArray }

Remove export methods as they are in utils now.


// http://www.colourlovers.com/palette/3459622/Flowering_Tiles

var $elements = {
  game_over: document.getElementById('game_over'),
  you_won: document.getElementById('you_won'),
};
var canvas = SVG('canvas').size(window.innerWidth-40, window.innerHeight-document.getElementById('canvas').offsetTop-20);
var board = new Board(canvas);
var stopwatch = new StopWatch({delay: 5, timer: document.getElementById('instructions')});
var level = 4;
var MAX_LEVEL = 9;

board.draw(level);
board.on('start', function() {
  stopwatch.reset();
  stopwatch.start();
});
board.on('done', function() {
  stopwatch.stop();
  document.getElementById("level"+level).classList.add("done");
  if (level == MAX_LEVEL) {
    $elements.you_won.classList.add("active");
  } else {
    level += 1;
    setTimeout(function() {
      board.draw(level);
    }, 500);
  }
});
board.on('fail', function() {
  stopwatch.stop();
  $elements.game_over.classList.add("active");
});

document.getElementById('start_over').addEventListener('click', function() {
  location.reload();
});

document.getElementById('retry').addEventListener('click', function() {
  $elements.game_over.classList.remove("active");
  board.draw(level);
});

document.body.addEventListener('touchmove', function(e){ e.preventDefault(); });

Remove link to unused color palette


'use strict';

const Prismic = require('./prismic');
const Predicates = require('prismic.io').Predicates;
const _ = require('lodash');

const getQuotes = (api) => api
    .form('everything')
    .ref(Prismic.getEnvironment(api))
    .query(Predicates.at('document.type', 'quotes'))
    .submit()
    .then((quotes) => {
      return _.map(quotes.results, (result) => {
        return _.map(result.data, (datum) => {
          return datum.value[0].text;
        });
      });
    });

const homeHandler = (request, reply) => {
  Prismic.getAPI().then((api) => {
    const stagingReleaseRef =
      api.ref(process.env.PRISMIC_IO_STAGING_RELEASE_NAME);
    getQuotes(api).then((quotes) => {
      reply.view('quotes', {
        quotes: quotes,
        environment: stagingReleaseRef ? 'Staging' : 'Production'
      });
    });
  });
};

module.exports = {
  method: 'GET',
  path: '/',
  handler: homeHandler
};

'use strict';

const Prismic = require('./prismic');
const Predicates = require('prismic.io').Predicates;
const _ = require('lodash');

const getQuotes = (api) => api
    .form('everything')
    .ref(Prismic.getEnvironment(api))
    .query(Predicates.at('document.type', 'quotes'))
    .submit()
    .then((quotes) => {
      return _.map(quotes.results, (result) => {
        return _.map(result.data, (datum) => {
          return datum.value[0].text;
        });
      });
    });

const homeHandler = (request, reply) => {
  Prismic.getAPI().then((api) => {
    const isProductionEnvironment =
      Prismic.getEnvironment(api) === api.master();

    getQuotes(api).then((quotes) => {
      reply.view('quotes', {
        quotes: quotes,
        environment: isProductionEnvironment ? 'Production' : 'Staging'
      });
    });
  });
};

module.exports = {
  method: 'GET',
  path: '/',
  handler: homeHandler
};

Fix for current environment display


Update js so info box doesn't scroll after window size is < 1000px


import styled from './styled'

import type {
  BaseStylesType,
  ComponentStyleType,
  StyledType,
  StyledElementAttrsType,
  StyledElementType,
  TagNameOrStyledElementType
} from './types'

const getStyledArgs = (
  tagNameOrStyledElement: TagNameOrStyledElementType
): StyledElementAttrsType => {
  if (typeof tagNameOrStyledElement === 'string') {
    return {tagName: tagNameOrStyledElement, style: {}}
  }

  const {tagName, style} = tagNameOrStyledElement
  return {tagName, style}
}

const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): StyledType => {
  let sheet

  const mountSheet = () => {
    if (!sheet) {
      sheet = jss.createStyleSheet(baseStyles, {
        link: true,
        meta: 'sheet',
      }).attach()
    }

    return sheet
  }

  return Object.assign((
    tagNameOrStyledElement: TagNameOrStyledElementType
  ) => (
    ownStyle: ComponentStyleType
  ): StyledElementType => {
    const {tagName, style} = getStyledArgs(tagNameOrStyledElement)
    const elementStyle = {...style, ...ownStyle}

    return styled({tagName, baseStyles, elementStyle, mountSheet})
  }, {mountSheet, sheet, styles: baseStyles})
}

export default createStyled

import styled from './styled'

import type {
  BaseStylesType,
  ComponentStyleType,
  StyledType,
  StyledElementAttrsType,
  StyledElementType,
  TagNameOrStyledElementType
} from './types'

const getStyledArgs = (
  tagNameOrStyledElement: TagNameOrStyledElementType
): StyledElementAttrsType => {
  if (typeof tagNameOrStyledElement === 'string') {
    return {tagName: tagNameOrStyledElement, style: {}}
  }

  const {tagName, style} = tagNameOrStyledElement
  return {tagName, style}
}

const createStyled = (jss: Function) => (baseStyles: BaseStylesType = {}): StyledType => {
  let sheet

  const mountSheet = () => {
    if (!sheet) {
      sheet = jss.createStyleSheet(baseStyles, {
        link: true,
        meta: 'styled-jss',
      }).attach()
    }

    return sheet
  }

  return Object.assign((
    tagNameOrStyledElement: TagNameOrStyledElementType
  ) => (
    ownStyle: ComponentStyleType
  ): StyledElementType => {
    const {tagName, style} = getStyledArgs(tagNameOrStyledElement)
    const elementStyle = {...style, ...ownStyle}

    return styled({tagName, baseStyles, elementStyle, mountSheet})
  }, {mountSheet, sheet, styles: baseStyles})
}

export default createStyled

Set meta-name for sheet as "styled-jss"


import { expect } from 'chai';
import { shallow, mount } from 'enzyme';
import React from 'react';
import { Post } from '../../src/js/components/Post';

describe('<Post/>', () => {
  const post = {
    id: 0,
    title: 'Test Post',
    content: 'empty',
    description: 'empty',
    author: 'bot',
    slug: 'test-post',
    tags: ['test', 'react'],
  };

  it('renders post title', () => {
    const result = mount(<Post post={post} />);
    expect(result.find('.post__title').text()).to.eq('Test Post');
  });

  it('renders post metadata', () => {
    const result = shallow(<Post post={post} />);
    expect(result.find('.post__meta').text()).to.eq('a few seconds ago');
  });
});

import { expect } from 'chai';
import { mount } from 'enzyme';
import React from 'react';
import { Post } from '../../src/js/components/Post';

describe('<Post/>', () => {
  const post = {
    id: 0,
    title: 'Test Post',
    content: 'empty',
    description: 'empty',
    author: 'bot',
    slug: 'test-post',
    tags: ['test', 'react'],
  };

  it('renders post title', () => {
    const result = mount(<Post post={post} />);
    expect(result.find('.post__title').text()).to.eq('Test Post');
  });

  it('renders post metadata', () => {
    const result = mount(<Post post={post} />);
    expect(result.find('.post__meta').text()).to.eq('  a few seconds ago');
  });
});

Update Post tests to match rendered output


'use strict';

/* global Connector */

var trackInfo = null;

function updateTrackInfo() {
	trackInfo = JSON.parse(localStorage.getItem('audio_v10_track'));
}

/**
 * Decodes HTML entities in given text string
 * @param  {String} str String with HTML entities
 * @return {String}     Decoded string
 */
var decodeHtmlEntity = function(str) {
	if (str === null) {
		return null;
	}

	return str.replace(/&#(\d+);/g, function(match, dec) {
		return String.fromCharCode(dec);
	});
};

Connector.artistTrackSelector = '.top_audio_player_title';
Connector.playerSelector = '#top_audio_player';

Connector.getArtist = function () {
			updateTrackInfo();
			return decodeHtmlEntity(trackInfo[4]);
		};
Connector.getTrack = function () {
			updateTrackInfo();
			return decodeHtmlEntity(trackInfo[3]);
		};
Connector.getCurrentTime = function () {
			updateTrackInfo();
			var progress = parseFloat(localStorage.getItem('audio_v10_progress'));
			return Math.round(parseInt(trackInfo[5])*progress);
		};
Connector.getDuration = function () {
			updateTrackInfo();
			return parseInt(trackInfo[5]);
		};
Connector.isPlaying = function() {
	return $(this.playerSelector).hasClass('top_audio_player_playing');
		};

'use strict';

/* global Connector */

var INFO_TRACK = 3;
var INFO_ARTIST = 4;
var INFO_DURATION = 5;

var trackInfo = null;

function updateTrackInfo() {
	trackInfo = JSON.parse(localStorage.getItem('audio_v10_track'));
}

/**
 * Decodes HTML entities in given text string
 * @param  {String} str String with HTML entities
 * @return {String}     Decoded string
 */
var decodeHtmlEntity = function(str) {
	if (str === null) {
		return null;
	}

	return str.replace(/&#(\d+);/g, function(match, dec) {
		return String.fromCharCode(dec);
	});
};

Connector.artistTrackSelector = '.top_audio_player_title';
Connector.playerSelector = '#top_audio_player';

Connector.getArtist = function () {
			updateTrackInfo();
			return decodeHtmlEntity(trackInfo[INFO_ARTIST]);
		};
Connector.getTrack = function () {
			updateTrackInfo();
			return decodeHtmlEntity(trackInfo[INFO_TRACK]);
		};
Connector.getCurrentTime = function () {
			updateTrackInfo();
			var progress = parseFloat(localStorage.getItem('audio_v10_progress'));
			return Math.round(parseInt(trackInfo[INFO_DURATION]) * progress);
		};
Connector.getDuration = function () {
			updateTrackInfo();
			return parseInt(trackInfo[INFO_DURATION]);
		};
Connector.isPlaying = function() {
	return $(this.playerSelector).hasClass('top_audio_player_playing');
		};

Use constants instead of magic numbers


define([
    './module',
    'jquery',
    'text!./link.html'
], function(directives, $, linkTpl) {
    'use strict';

    directives.directive('zoriLink', ['$interval',
        function($interval) {
            function link(scope, element, attrs) {
                var timeoutId;
                var $link = $(element).find('a');

                function update() {
                    var nbClick = scope.link.nbClick;
                    var height = nbClick * 30 + 30;
                    var hue = nbClick * 10 + 10;

                    var style = {
                        height      : height + 'px',
                        'margin-top': -(height / 2) + 'px',
                        background  : 'linear-gradient(to bottom, hsl(' + hue + ', 99%, 65%) 50%, hsl(' + hue + ', 99%, 30%) 51%)'
                    };

                    $link.css(style);
                }

                scope.$watch(attrs.myCurrentTime, function(value) {
                    update();
                });

                element.on('$destroy', function() {
                    $interval.cancel(timeoutId);
                });

                timeoutId = $interval(function() {
                    update();
                }, 1000);
            }

            return {
                restrict: 'A',
                template: linkTpl,
                link: link
            };
        }
    ]);
});

define([
    './module',
    'jquery',
    'text!./link.html'
], function(directives, $, linkTpl) {
    'use strict';

    directives.directive('zoriLink', ['$interval',
        function($interval) {
            function link(scope, element, attrs) {
                var timeoutId;
                var $link = $(element).find('a');

                function update() {
                    var nbClick = scope.link.nbClick;
                    var height = nbClick * 30 + 30;
                    var hue = Math.min(nbClick * 10, 100);

                    var style = {
                        height      : height + 'px',
                        'margin-top': -(height / 2) + 'px'
                    };

                    if (nbClick > 0) {
                        style.background = 'linear-gradient(to bottom, hsl(' + hue + ', 99%, 65%) 50%, hsl(' + hue + ', 99%, 30%) 51%)';
                    }

                    $link.css(style);
                }

                scope.$watch(attrs.myCurrentTime, function(value) {
                    update();
                });

                element.on('$destroy', function() {
                    $interval.cancel(timeoutId);
                });

                timeoutId = $interval(function() {
                    update();
                }, 1000);
            }

            return {
                restrict: 'A',
                template: linkTpl,
                link: link
            };
        }
    ]);
});

Add a maximum hue for link color


/* eslint-env mocha */
const jumpsearch = require('../../../src').algorithms.Searching.jumpsearch;
const assert = require('assert');

describe('Jump Search', () => {
  it('should return -1 for empty array', () => {
    const index = jumpsearch([], 1);

    assert.equal(index, -1);
  });

  it('should return -1 for no element found in array', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 1);

    assert.equal(index, -1);
  });

  it('should return index of mid element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 4);

    assert.equal(index, 2);
  });

  it('should return index of first element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 2);

    assert.equal(index, 0);
  });

  it('should return index of last element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 6);

    assert.equal(index, 4);
  });
});

/* eslint-env mocha */
const jumpsearch = require('../../../src').algorithms.Searching.jumpsearch;
const assert = require('assert');

describe('Jump Search', () => {
  it('should return -1 for empty array', () => {
    const index = jumpsearch([], 1);

    assert.equal(index, -1);
  });

  it('should return -1 for no element found in array', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 1);

    assert.equal(index, -1);
  });

  it('should return index of mid element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 4);

    assert.equal(index, 2);
  });

  it('should return index of first element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 2);

    assert.equal(index, 0);
  });

  it('should return index of last element', () => {
    const index = jumpsearch([2, 3, 4, 5, 6], 6);

    assert.equal(index, 4);
  });

  it('should return index of last element', () => {
    const sortedArray = [];

    for (let i = 0; i <= 100; i += 2) {
      sortedArray.push(i);
    }

    assert.equal(jumpsearch(sortedArray, 6), 3);
    assert.equal(jumpsearch(sortedArray, 103), -1);
    assert.equal(jumpsearch(sortedArray, 79), -1);
  });
});

Test Jump Search: Test coverage improved to 94.12


function maybeLog(log, links) {
  links.forEach((link) => {
    if (link.broken) {
      log(link.description)
    }
  })
}

function maybeThrow(links) {
  const brokenLinks = links.filter(({broken}) => !!broken)
  if (brokenLinks.length) {
    let message = `You have ${brokenLinks.length} broken links:`
    brokenLinks.forEach(({description}) => {
      message += `\n${description}`
    })
    throw new Error(message)
  }
}

module.exports = (options, log = console.log) => {
  return (links) => {
    if (options.warn) {
      maybeLog(log, links)
    } else {
      maybeThrow(links)
    }
  }
}

function maybeLog(log, links) {
  links.forEach((link) => {
    if (link.broken) {
      log(link.description)
    }
  })
}

function maybeThrow(links) {
  const brokenLinks = links.filter(({broken}) => !!broken)

  if (brokenLinks.length) {
    let message = `You have ${brokenLinks.length} broken links:\n`
    brokenLinks.forEach(({filename, description}) => {
      message += `\n${filename} >>> ${description}`
    })
    throw new Error(message)
  }
}

module.exports = (options, log = console.log) => {
  return (links) => {
    if (options.warn) {
      maybeLog(log, links)
    } else {
      maybeThrow(links)
    }
  }
}

Add file name and some (subjective) readability in the log


import React, { Component } from 'react';
import Dispatcher from '../utils/front-end-dispatcher';
import SimpleButton from './simple-button'

var EmptySitesList = React.createClass({
  requestNewSite: function() {
    Dispatcher.send('addSite');
  },
  createNewSite: function() {
    Dispatcher.send('createSite');
  },
  render: function () {
    if(this.props.sitesReceived == true) {
      var childIcon =
;
      return (

Oh dear! This list is empty.

            <SimpleButton onClick={this.createNewSite} className="btn-create">

              Create
            </SimpleButton>
            <SimpleButton href="#" onClick={this.requestNewSite} className="btn-open">

              Open
            </SimpleButton>

      );
    } else {
      return (
 );
    }
  }
})

module.exports = EmptySitesList;

import React, { Component } from 'react';
import Dispatcher from '../utils/front-end-dispatcher';
import SimpleButton from './simple-button'

var EmptySitesList = React.createClass({
  requestNewSite: function() {
    Dispatcher.send('addSite');
  },
  createNewSite: function() {
    Dispatcher.send('createSite');
  },
  render: function () {
    var childIcon =
;
    return (

Oh dear! This list is empty.

          <SimpleButton onClick={this.createNewSite} className="btn-create">

            Create
          </SimpleButton>
          <SimpleButton href="#" onClick={this.requestNewSite} className="btn-open">

            Open
          </SimpleButton>

    );
  }
})

module.exports = EmptySitesList;

Fix empty window on very first open


const path = require('path');

module.exports = function makeSettingsPatch(name, dependencyConfig, projectConfig) {
  const relative = path.relative(
    projectConfig.sourceDir,
    dependencyConfig.sourceDir
  );

  /**
   * Replace pattern by patch in the passed content
   * @param  {String} content Content of the Settings.gradle file
   * @return {String}         Patched content of Settings.gradle
   */
  return function applySettingsPatch(content) {
    const pattern = `include ':app'`;
    const patch = `include ':${name}'\n` +
      `project(':${name}').projectDir = ` +
      `new File(rootProject.projectDir, '${relative}')`;

    return content.replace(pattern, `${pattern}\n${patch}`);
  };
};

const path = require('path');

module.exports = function makeSettingsPatch(name, dependencyConfig, projectConfig) {
  const relative = path.relative(
    projectConfig.sourceDir,
    dependencyConfig.settingsGradlePath
  );

  /**
   * Replace pattern by patch in the passed content
   * @param  {String} content Content of the Settings.gradle file
   * @return {String}         Patched content of Settings.gradle
   */
  return function applySettingsPatch(content) {
    const pattern = `include ':app'`;
    const patch = `include ':${name}'\n` +
      `project(':${name}').projectDir = ` +
      `new File(rootProject.projectDir, '${relative}')`;

    return content.replace(pattern, `${pattern}\n${patch}`);
  };
};

Fix wrong settings gradle patch


import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

import ExpPlayerRouteMixin from 'exp-player/mixins/exp-player-route';

export default Ember.Route.extend(AuthenticatedRouteMixin, ExpPlayerRouteMixin, {
    _getExperiment() {
        return new Ember.RSVP.Promise((resolve) => {
            resolve(this.modelFor('experiments.info'));
        });
    },
    _getSession(params, experiment) {
        var session = this.store.createRecord(experiment.get('sessionCollectionId'), {
            experimentId: experiment.id,
            profileId: 'tester0.prof1', // TODO fetch from service
            profileVersion: '',
            completed: false,
            feedback: '',
            hasReadFeedback: '',
            softwareVersion: '',
            expData: {},
            sequence: []
        });

        return experiment.getCurrentVersion().then(versionId => {
            session.set('experimentVersion', versionId);
            return session.save().then(() => session);
        });
        return new Ember.RSVP.Promise((resolve) => {
            resolve(session);
        });
    },
    actions: {
        willTransition: function(transition) {
            // FIXME: This won't prevent back button or manual URL change. See https://guides.emberjs.com/v2.3.0/routing/preventing-and-retrying-transitions/#toc_preventing-transitions-via-code-willtransition-code
            if (this.controller.isDirty() && !confirm('Are you sure you want to exit the experiment?')) {
                transition.abort();
                return false;
            } else {
                // Bubble this action to parent routes
                return true;
            }
        }
    }
});

import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

import ExpPlayerRouteMixin from 'exp-player/mixins/exp-player-route';

export default Ember.Route.extend(AuthenticatedRouteMixin, ExpPlayerRouteMixin, {
    currentUser: Ember.inject.service(),
    _getExperiment() {
        return new Ember.RSVP.Promise((resolve) => {
            resolve(this.modelFor('experiments.info'));
        });
    },
    _getSession(params, experiment) {
        return this.get('currentUser').getCurrentUser().then(([account, profile]) => {
            return this.store.createRecord(experiment.get('sessionCollectionId'), {
                experimentId: experiment.id,
                profileId: profile.get('id'),
                profileVersion: '', // TODO
                completed: false,
                feedback: '',
                hasReadFeedback: '',
                softwareVersion: '',
                expData: {},
                sequence: []
            });
        });
    },
    actions: {
        willTransition: function(transition) {
            // FIXME: This won't prevent back button or manual URL change. See https://guides.emberjs.com/v2.3.0/routing/preventing-and-retrying-transitions/#toc_preventing-transitions-via-code-willtransition-code
            if (this.controller.isDirty() && !confirm('Are you sure you want to exit the experiment?')) {
                transition.abort();
                return false;
            } else {
                // Bubble this action to parent routes
                return true;
            }
        }
    }
});

Use currentUser service to set session.profileId


var esformatter = require('esformatter');
var stringify = require("json-literal").stringify;
var fs = require("fs");
var path = require("path");

/**
 * Class that writes an object to a file (using the `json-literal` module)
 * @param {*} `data` the javascript object,
 * @constructor
 */
function Writer(data) {
    var prettyPrint = false;

    /**
     * Enable pretty printed output
     */
    this.pretty = function () {
        prettyPrint = true;
        return this;
    };

    /**
     * Write the data to a file (synchronously).
     * @param {string} `targetDir` the directory of the file
     * @param {string} `filename` the name of the file
     */
    this.writeTo = function (targetDir, filename) {
        // Remove wrapping parens which are always created by json-literal
        var node = stringify(data).replace(/^\((.*)\)$/, "$1");
        // Create a valid javascript module
        var string = "module.exports = function() { return " + node + "};";
        // If prettyprint is enabled, use esformatter to format
        // the resulting javascript.
        if (prettyPrint) {
            string = esformatter.format(string, {
                // indent is hard coded to 4 spaces at the moment
                indent: {
                    value: '    '
                }
            });
        }
        // Write the data...
        var targetFile = path.join(targetDir, filename);
        console.log("Writing ", targetFile);
        fs.writeFileSync(targetFile, string, "utf-8");
        console.log("done");
    }
}

module.exports = Writer;

var esformatter = require('esformatter');
var stringify = require("json-literal").stringify;
var fs = require("fs");
var path = require("path");

/**
 * Class that writes an object to a file (using the `json-literal` module)
 * @param {*} `data` the javascript object,
 * @constructor
 */
function Writer(data) {
    var prettyPrint = false;

    /**
     * Enable pretty printed output
     */
    this.pretty = function () {
        prettyPrint = true;
        return this;
    };

    /**
     * Write the data to a file (synchronously).
     * @param {string} `targetDir` the directory of the file
     * @param {string} `filename` the name of the file
     */
    this.writeTo = function (targetDir, filename) {
        // Remove wrapping parens which are always created by json-literal
        var node = stringify(data).replace(/^\((.*)\)$/, "$1");
        // Create a valid javascript module
        var string = "module.exports = " + node + ";";
        // If prettyprint is enabled, use esformatter to format
        // the resulting javascript.
        if (prettyPrint) {
            string = esformatter.format(string, {
                // indent is hard coded to 4 spaces at the moment
                indent: {
                    value: '    '
                }
            });
        }
        // Write the data...
        var targetFile = path.join(targetDir, filename);
        console.log("Writing ", targetFile);
        fs.writeFileSync(targetFile, string, "utf-8");
        console.log("done");
    }
}

module.exports = Writer;

Remove "function()" from database-files. Why was it inserted in the first place?


module.exports = (function(){

    function parseCommandOption( args ){

        var options = {}, token;

        while( token = args.shift() ){

            if( !token.startsWith("--") ){
                 options.extra = [token].concat(args);
                 break;
            }

            let optionType = token.replace(/^--/, "");
            let i, len;

            for( i = 0, len = args.length; i < len; ++i ){
                if( args[i].startsWith("--"))
                    break;
            }

            options[optionType] = args.splice(0, i);
        }

        return options;
    }

    var handlers = {};

    handlers.echo = ( ...args )=>{
        return {
            msg: args.join(" ")
        };
    }

    return handlers;
})();

module.exports = (function(){

    function parseCommandOption( args ){

        var options = {}, token;

        while( token = args.shift() ){

            if( !token.startsWith("--") ){
                 options.extra = [token].concat(args);
                 break;
            }

            let optionType = token.replace(/^--/, "");
            let i, len;

            for( i = 0, len = args.length; i < len; ++i ){
                if( args[i].startsWith("--"))
                    break;
            }

            options[optionType] = args.splice(0, i);
        }

        return options;
    }

    var handlers = {};

    handlers.echo = ( args )=>{

        var options = {
            msg: args.join(" ")
        }

        return [ "echo", options ];
        };
    }

    return handlers;
})();

Fix bug and update the return value ( port ).


export const ALERT_BANNER_ACTIVE = true

// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  url:
    'https://www.hashicorp.com/webinars/nomad-0-12-multi-cluster-federations-cni-advanced-scheduling-and-more/',
  tag: 'WEBINAR',
  text:
    'Join us for a webinar to learn about Nomad 0.12. This release included 15+ new features and our breakthrough Multi-Cluster Deployment. Register here!',
}

export const ALERT_BANNER_ACTIVE = true

// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  url:
    'https://www.hashicorp.com/blog/announcing-general-availability-of-hashicorp-nomad-0-12',
  tag: 'ANNOUNCING',
  text:
    'Nomad 0.12 is now generally available, which includes 15+ new features and our breakthrough Multi-Cluster Deployment. Learn more!',
}

Revert "updating banner for webinar"


// @flow
import _ from 'lodash'
import ProxySet from './_proxy-set'

import type Project from './project'
import type Composition from './composition'
import Layer from './layer'

export default class TimeLane
{
    static deserialize(timelaneJson: Object, comp: Composition)
    {
        const timelane = new TimeLane
        const config = _.pick(timelaneJson.config, ['name'])
        const layers = timelaneJson.layers.map(layerJson => Layer.deserialize(layerJson))

        Object.defineProperty(timelane, 'id', timelaneJson.id)
        timelane.layers = new Set(layers)
        Object.assign(timelane.config, config)

        return timelane
    }

    id: ?string = null
    layers: Set<Layer> = new Set()

    config: {
        name: ?string,
    } = {
        name: null
    }

    get name(): string { return this.config.name }
    set name(name: string) { this.config.name = name }

    constructor()
    {
        Object.seal(this)
    }

    toPreBSON(): Object
    {
        return {
            id: this.id,
            config: Object.assign({}, this.config),
            layers: Array.from(this.layers.values()).map(layer => layer.toPreBSON()),
        }
    }

    toJSON(): Object
    {
        return {
            id: this.id,
            config: Object.assign({}, this.config),
            layers: Array.from(this.layers.values()).map(layer => layer.toJSON()),
        }
    }
}

// @flow
import _ from 'lodash'
import ProxySet from './_proxy-set'

import type Project from './project'
import type Composition from './composition'
import Layer from './layer'

export default class TimeLane
{
    static deserialize(timelaneJson: Object, comp: Composition)
    {
        const timelane = new TimeLane
        const config = _.pick(timelaneJson.config, ['name'])
        const layers = timelaneJson.layers.map(layerJson => Layer.deserialize(layerJson))

        Object.defineProperty(timelane, 'id', {value: timelaneJson.id})
        timelane.layers = new Set(layers)
        Object.assign(timelane.config, config)

        return timelane
    }

    id: ?string = null
    layers: Set<Layer> = new Set()

    config: {
        name: ?string,
    } = {
        name: null
    }

    get name(): string { return this.config.name }
    set name(name: string) { this.config.name = name }

    constructor()
    {
        Object.seal(this)
    }

    toPreBSON(): Object
    {
        return {
            id: this.id,
            config: Object.assign({}, this.config),
            layers: Array.from(this.layers.values()).map(layer => layer.toPreBSON()),
        }
    }

    toJSON(): Object
    {
        return {
            id: this.id,
            config: Object.assign({}, this.config),
            layers: Array.from(this.layers.values()).map(layer => layer.toJSON()),
        }
    }
}

Fix incorrect id assign in Timelane


var sizeOf = require('image-size');
var sass = require('node-sass');
var project = require('../../package.json');
var config = require('../config.js');

var customFunctions = {
    'image-width($url: "")': function(url) {
        var image = url.getValue();
        var dimensions = sizeOf(config.src + 'img/' + image);
        return sass.types.String(dimensions.width.toString() + 'px');
    },
    'image-height($url: "")': function(url) {
        var image = url.getValue();
        var dimensions = sizeOf(config.src + 'img/' + image);
        return sass.types.String(dimensions.height.toString() + 'px');
    }
}

module.exports = customFunctions;

var sizeOf = require('image-size');
var sass = require('node-sass');
var project = require('../../package.json');
var config = require('../config.js');

var customFunctions = {
    'image-width($url: "")': function(url) {
        var image = url.getValue();
        var dimensions = sizeOf(config.src + 'img/' + image);
        return sass.types.Number(dimensions.width, 'px');
    },
    'image-height($url: "")': function(url) {
        var image = url.getValue();
        var dimensions = sizeOf(config.src + 'img/' + image);
        return sass.types.Number(dimensions.height, 'px');
    }
}

module.exports = customFunctions;

Change image-width and image-height sass function return type to number


(function (angular, undefined) {

    angular.module('expression-builder')
        .provider('expressionBuilder', ExpressionBuilderProvider);

    ExpressionBuilderProvider.$inject = [];

	function ExpressionBuilderProvider () {
		var templates = [];

		return {
			register: function (type, property, template) {
				Builder.prototype[property] = function () {

                    Object.create(type, {

                    });
                }
			},
			$get: function () {
				return Builder;
			}
		}
	}

    function Builder () {
        this.plan = [];
    }

    Builder.prototype.apply = function (node) {
        this.plan.forEach(function (p) {
             p(node);
        });
    };

})(angular);

(function (angular, undefined) {

    angular.module('expression-builder')
        .factory('expressionBuilder', Factory);

    Factory.$inject = [];

    function Factory () {
        return ExpressionBuilder;
    }

    function ExpressionBuilder (expressions) {
        function Builder () {
            this.plan = [];
        };

        Builder.prototype.apply = function (node) {
            plan.forEach(function (p) {
                p(node);
            });
        };

        expressions.forEach(function (expression) {
            Builder.prototype[expression.property] = function (id, parameters) {
                var build = function (node, context) {
                    var expression = Object.create(expression.prototype);
                    angular.extend(model, parameters);
                    node.add(model);
                };

                this.plan.push(build);

                return this;
            };
        });

        return new Builder();
    }

})(angular);

Use factory instead of provider


'use strict';

function ArgumentReadNode(argIndex) {
    assert(argIndex >= 0);

    this.execute = function (frame) {
        return frame.getArg(argIndex);
    };

    this.doPreEvaluated = function (frame, args) {
        return args[argIndex];
    };
}

'use strict';

function ArgumentReadNode(contextLevel, arg, source) {
    ContextualNode.call(this, contextLevel, source);
    var _this = this;

    assert(arg.getIndex() >= 0);

    this.execute = function (frame) {
        var ctx = _this.determineContext(frame);
        return ctx.getArgument(arg.getIndex());
    };
}
ArgumentReadNode.prototype = Object.create(ContextualNode.prototype);

Fix argument read node to be context node