'use strict';

const StripeResource = require('../StripeResource');
const stripeMethod = StripeResource.method;

module.exports = StripeResource.extend({
  path: 'invoices',

  includeBasic: ['create', 'del', 'list', 'retrieve', 'update'],

  finalizeInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/finalize',
  }),

  listLineItems: stripeMethod({
    method: 'GET',
    path: '{id}/lines',
  }),

  listUpcomingLineItems: stripeMethod({
    method: 'GET',
    path: 'upcoming/lines',
  }),

  markUncollectible: stripeMethod({
    method: 'POST',
    path: '{id}/mark_uncollectible',
  }),

  pay: stripeMethod({
    method: 'POST',
    path: '{id}/pay',
  }),

  retrieveUpcoming: stripeMethod({
    method: 'GET',
    path: 'upcoming',
  }),

  sendInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/send',
  }),

  voidInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/void',
  }),
});

'use strict';

const StripeResource = require('../StripeResource');
const stripeMethod = StripeResource.method;

module.exports = StripeResource.extend({
  path: 'invoices',

  includeBasic: ['create', 'del', 'list', 'retrieve', 'update'],

  finalizeInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/finalize',
  }),

  listLineItems: stripeMethod({
    method: 'GET',
    path: '{id}/lines',
    methodType: 'list',
  }),

  listUpcomingLineItems: stripeMethod({
    method: 'GET',
    path: 'upcoming/lines',
    methodType: 'list',
  }),

  markUncollectible: stripeMethod({
    method: 'POST',
    path: '{id}/mark_uncollectible',
  }),

  pay: stripeMethod({
    method: 'POST',
    path: '{id}/pay',
  }),

  retrieveUpcoming: stripeMethod({
    method: 'GET',
    path: 'upcoming',
  }),

  sendInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/send',
  }),

  voidInvoice: stripeMethod({
    method: 'POST',
    path: '{id}/void',
  }),
});

Make autopagination functions work for listLineItems and listUpcomingLineItems


#!/usr/bin/env node

let lib = require('../index'),
    Point2D = lib.Point2D,
    Intersection = lib.Intersection;

let circle = {
    cx: new Point2D(0, 0),
    r: 50,
};

let rectangle = {
    p1: new Point2D(0, 0),
    p2: new Point2D(60, 30)
};

let result = Intersection.intersectCircleRectangle(
    circle.cx, circle.r,
    rectangle.p1, rectangle.p2
);

console.log(result);

#!/usr/bin/env node

let lib = require('../index'),
    Point2D = lib.Point2D,
    Intersection = lib.Intersection;

let circle = {
    center: new Point2D(0, 0),
    radius: 50,
};

let rectangle = {
    topLeft: new Point2D(0, 0),
    bottomRight: new Point2D(60, 30)
};

let result = Intersection.intersectCircleRectangle(
    circle.center, circle.radius,
    rectangle.topLeft, rectangle.bottomRight
);

console.log(result);

Fix property names in example


'use strict';
module.exports = {
  up: function(queryInterface, Sequelize) {
    return queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      usernamee: {
        type: Sequelize.STRING
      },
      password: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: function(queryInterface, Sequelize) {
    return queryInterface.dropTable('Users');
  }
};

'use strict';
module.exports = {
  up: function(queryInterface, Sequelize) {
    return queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      username: {
        type: Sequelize.STRING
      },
      password: {
        type: Sequelize.STRING
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: function(queryInterface, Sequelize) {
    return queryInterface.dropTable('Users');
  }
};

Fix a typo caused error


require('babel-register');

const RG = require('../client/src/battles');
const RGTest = require('../tests/roguetest');

const fact = new RG.Factory.Base();

const levels = [];

for (let i = 0; i < 1000; i++) {
    const level = fact.createLevel('arena', 100, 100);
    levels.push(JSON.stringify(level.toJSON()));
    if (i % 50 === 0) {
        RGTest.printMemUsage(`${i} levels created`);
    }
}

console.log(`Created ${levels.length} levels OK`);
RGTest.printMemUsage('END OF TEST');

require('babel-register');

const RG = require('../client/src/battles');
const RGTest = require('../tests/roguetest');

const fact = new RG.Factory.Base();

const levels = [];

let selectCase = 0;
if (process.argv.length > 2) {
    selectCase = parseInt(process.argv[2], 10);
}

for (let i = 0; i < 1000; i++) {
    const level = fact.createLevel('arena', 100, 100);
    // Case 1: Pure Map.Level object
    if (selectCase === 0) {levels.push(level);}
    // Case 2: Serialized Map.Level object
    if (selectCase === 1) {levels.push(level.toJSON());}
    // Case 3: JSON stringified
    if (selectCase === 2) {levels.push(JSON.stringify(level));}
    if (i % 50 === 0) {
        RGTest.printMemUsage(`${i} levels created`);
    }
}

console.log(`Created ${levels.length} levels OK`);
RGTest.printMemUsage('END OF TEST');

Test case can be now given from command line.


'use strict'

const banner = [
  '/**',
  ' * Sanity role: ROLE_NAME',
  ' * ',
  ' * Sanity plugin loader multi-fulfiller wrapper',
  ' * Imports all fulfillers, then exports them as an array',
  ' */'
]

module.exports = function multiFulfillerHandler(opts, callback) {
  const fulfillers = opts.roles.fulfilled[opts.role]

  const result = banner
    .concat(fulfillers.map((fulfiller, i) =>
      `import fulfiller${i} from '${fulfiller.path}'`
    ))
    .concat(['\nmodule.exports = ['])
    .concat(fulfillers.map((__, i) => `  fulfiller${i}`).join(',\n'))
    .concat([']\n'])
    .join('\n')
    .replace(/ROLE_NAME/g, opts.role)

  callback(null, result)
}

'use strict'

const banner = [
  '/**',
  ' * Sanity role: ROLE_NAME',
  ' * ',
  ' * Sanity plugin loader multi-fulfiller wrapper',
  ' * Imports all fulfillers, then exports them as an array',
  ' */'
]

const normalizer = [
  'function normalize(mod) {',
  '  return mod && mod.__esModule ? mod["default"] : mod',
  '}', ''
]

module.exports = function multiFulfillerHandler(opts, callback) {
  const fulfillers = opts.roles.fulfilled[opts.role]

  const result = banner
    .concat(normalizer)
    .concat(['\nmodule.exports = ['])
    .concat(fulfillers.map(
      (fulfiller, i) => `  require('${fulfiller.path}')`
    ).join(',\n'))
    .concat(['].map(normalize)\n'])
    .join('\n')
    .replace(/ROLE_NAME/g, opts.role)

  callback(null, result)
}

Use node require over imports to skip the need for babel compiling


define([
    'libs/bean',
    'libs/bonzo',
    'libs/qwery'
], function(
    bean,
    bonzo,
    qwery
) {

    var defaultValue = bonzo(qwery('.sign-up__input')).attr('value');

    return {
        init: function() {
            this.bindEvents();
        },

        bindEvents: function() {
            bean.on(document.body, 'focusin', '.sign-up__input', function(e) {
                this.focusIn();
            }.bind(this));
            bean.on(document.body, 'focusout', '.sign-up__input', function(e) {
                this.focusOut();
            }.bind(this));
        },

        focusIn: function() {
            bonzo(qwery('.sign-up__input')).addClass('is-focused');
            if (bonzo(qwery('.sign-up__input')).attr('value') === defaultValue) {
                bonzo(qwery('.sign-up__input')).attr('value', '');
            }
        },

        focusOut: function() {
            bonzo(qwery('.sign-up__input')).removeClass("is-focused");
            if (bonzo(qwery('.sign-up__input')).attr('value') === '') {
                bonzo(qwery('.sign-up__input')).attr('value', defaultValue);
            }
        }
    }
});

define([
    'libs/bean',
    'libs/bonzo',
    'libs/qwery'
], function(
    bean,
    bonzo,
    qwery
) {

    var defaultValue = bonzo(qwery('.sign-up__input')).attr('value');

    return {
        init: function() {
            this.bindEvents();
        },

        bindEvents: function() {
            document.getElementsByClassName("sign-up__input")[0].onfocus = function () {
                this.focusIn();
            }.bind(this);
            document.getElementsByClassName("sign-up__input")[0].onblur = function () {
                this.focusOut();
            }.bind(this);
        },

        focusIn: function() {
            bonzo(qwery('.sign-up__input')).addClass('is-focused');
            if (bonzo(qwery('.sign-up__input')).attr('value') === defaultValue) {
                bonzo(qwery('.sign-up__input')).attr('value', '');
            }
        },

        focusOut: function() {
            bonzo(qwery('.sign-up__input')).removeClass("is-focused");
            if (bonzo(qwery('.sign-up__input')).attr('value') === '') {
                bonzo(qwery('.sign-up__input')).attr('value', defaultValue);
            }
        }
    }
});

Fix focus events for Firefox


export const dataKey = 'value'
const createOnDragStart =
  (name, value) =>
    event => {
      event.dataTransfer.setData(dataKey, value)
    }

export default createOnDragStart

export const dataKey = 'text'
const createOnDragStart =
  (name, value) =>
    event => {
      event.dataTransfer.setData(dataKey, value)
    }

export default createOnDragStart

Change drag dataKey to 'text'


import React, { PropTypes } from 'react'

import SearchFilter from './SearchFilter'
import Labels from '../constants/Labels'

export default React.createClass({
  propTypes: {
    filters: PropTypes.array.isRequired,
    locationQuery: PropTypes.object.isRequired,
    setFilter: PropTypes.func.isRequired
  },
  getDefaultProps () {
    return {
      filters: []
    }
  },
  renderEmpty () {
    return

  },
  render () {
    let groupedFilters = {}
    if (this.props.locationQuery.query && this.props.filters) {
      this.props.filters.forEach(filter => {
        groupedFilters[ filter.aggregation ] = groupedFilters[ filter.aggregation ] || []
        groupedFilters[ filter.aggregation ].push(filter)
      })

      return (

Avgrens søket ditt

            {Object.keys(groupedFilters).map(aggregation => {
              let filtersByAggregation = groupedFilters[ aggregation ]
              return (
                <SearchFilter
                  key={aggregation}
                  title={Labels[aggregation]}
                  aggregation={aggregation}
                  filters={filtersByAggregation}
                  locationQuery={this.props.locationQuery}
                  setFilter={this.props.setFilter}/>
              )
            })}

      )
    } else {
      return this.renderEmpty()
    }
  }
})

import React, { PropTypes } from 'react'

import SearchFilter from './SearchFilter'
import Labels from '../constants/Labels'

export default React.createClass({
  propTypes: {
    filters: PropTypes.array.isRequired,
    locationQuery: PropTypes.object.isRequired,
    setFilter: PropTypes.func.isRequired
  },
  getDefaultProps () {
    return {
      filters: []
    }
  },
  renderEmpty () {
    return

  },
  render () {
    let groupedFilters = {}
    if (this.props.locationQuery.query && this.props.filters) {
      this.props.filters.forEach(filter => {
        groupedFilters[ filter.aggregation ] = groupedFilters[ filter.aggregation ] || []
        groupedFilters[ filter.aggregation ].push(filter)
      })

      return (

Avgrens søket ditt

            {Object.keys(groupedFilters).map(aggregation => {
              let filtersByAggregation = groupedFilters[ aggregation ]
              return (
                <SearchFilter
                  key={aggregation}
                  title={Labels[aggregation] || aggregation}
                  aggregation={aggregation}
                  filters={filtersByAggregation}
                  locationQuery={this.props.locationQuery}
                  setFilter={this.props.setFilter}/>
              )
            })}

      )
    } else {
      return this.renderEmpty()
    }
  }
})

Add fallback on filter names


import addons, { makeDecorator } from '@storybook/addons';
import marked from 'marked';

function renderMarkdown(text, options) {
  marked.setOptions({ ...marked.defaults, options });
  return marked(text);
}

export const withNotes = makeDecorator({
  name: 'withNotes',
  parameterName: 'notes',
  skipIfNoParametersOrOptions: true,
  allowDeprecatedUsage: true,
  wrapper: (getStory, context, { options, parameters }) => {
    const channel = addons.getChannel();

    const storyOptions = parameters || options;

    const { text, markdown, markdownOptions } =
      typeof storyOptions === 'string' ? { text: storyOptions } : storyOptions;

    if (!text && !markdown) {
      throw new Error('You must set of one of `text` or `markdown` on the `notes` parameter');
    }

    channel.emit('storybook/notes/add_notes', text || renderMarkdown(markdown, markdownOptions));

    return getStory(context);
  },
});

export const withMarkdownNotes = (text, options) =>
  withNotes({
    markdown: text,
    markdownOptions: options,
  });

import addons, { makeDecorator } from '@storybook/addons';
import marked from 'marked';

function renderMarkdown(text, options) {
  return marked(text, { ...marked.defaults, ...options });
}

export const withNotes = makeDecorator({
  name: 'withNotes',
  parameterName: 'notes',
  skipIfNoParametersOrOptions: true,
  allowDeprecatedUsage: true,
  wrapper: (getStory, context, { options, parameters }) => {
    const channel = addons.getChannel();

    const storyOptions = parameters || options;

    const { text, markdown, markdownOptions } =
      typeof storyOptions === 'string' ? { text: storyOptions } : storyOptions;

    if (!text && !markdown) {
      throw new Error('You must set of one of `text` or `markdown` on the `notes` parameter');
    }

    channel.emit('storybook/notes/add_notes', text || renderMarkdown(markdown, markdownOptions));

    return getStory(context);
  },
});

export const withMarkdownNotes = (text, options) =>
  withNotes({
    markdown: text,
    markdownOptions: options,
  });

Fix how markdownOptions are passed to marked


var cradle = require('cradle');
var applyDesignDocuments = require('./design-documents');

var couchLocation = process.env.COUCHDB || process.env.CLOUDANT_URL;
var couch = new(cradle.Connection)(couchLocation, 5984, {
  cache: true,
  raw: false
});

var db = couch.database('emergencybadges');

db.setup = function (callback) {
  db.exists(function (err, exists) {
    if (err) {
      console.log('error', err);
    } else if (exists) {
      console.log('Database exists.');
      if (typeof callback === 'function') callback();
    } else {
      console.log('Database does not exist. Creating.');
      db.create(function (err, res) {
        if (!err) {
          applyDesignDocuments(db);
          if (typeof callback === 'function') callback();
        }
      });
    }
  });
};

db.updateDesignDocuments = function (callback) {
  applyDesignDocuments(db);
  if (typeof callback === 'function') callback();
};

module.exports = db;

var cradle = require('cradle');
var applyDesignDocuments = require('./design-documents');

var couchLocation = process.env.CLOUDANT_URL || process.env.COUCHDB || 'http://localhost';
var couch = new(cradle.Connection)(couchLocation, 5984, {
  cache: true,
  raw: false
});

var db = couch.database('emergencybadges');

db.setup = function (callback) {
  db.exists(function (err, exists) {
    if (err) {
      console.log('error', err);
    } else if (exists) {
      console.log('Database exists.');
      if (typeof callback === 'function') callback();
    } else {
      console.log('Database does not exist. Creating.');
      db.create(function (err, res) {
        if (!err) {
          applyDesignDocuments(db);
          if (typeof callback === 'function') callback();
        }
      });
    }
  });
};

db.updateDesignDocuments = function (callback) {
  applyDesignDocuments(db);
  if (typeof callback === 'function') callback();
};

module.exports = db;

Change order of Couch locations


import DS from "ember-data";

export default DS.Model.extend( {
	body: DS.attr("string"),
	solution: DS.attr("string"),
	thread: DS.belongsTo("thread",  { async: true }),
	modules: DS.hasMany("module"),

	best_scores: DS.hasMany("user-score"), // Top 5

	comment: DS.belongsTo("thread", { async: true }),
	achievements: DS.hasMany("achievements", { async: true })
});

import DS from "ember-data";

export default DS.Model.extend( {
	body: DS.attr("string"),
	solution: DS.attr("string"),
	thread: DS.belongsTo("thread"),
	modules: DS.hasMany("module"),

	best_scores: DS.hasMany("user-score"), // Top 5

	comment: DS.belongsTo("thread"),
	achievements: DS.hasMany("achievements", { async: true })
});

Make comment and task thread included


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

let version;
module.exports = () => {
  if (!version) {
    version = `v${require(path.join(process.cwd(), 'package.json')).version}`;
    try {
      version += '-' + fs.readFileSync(path.join(process.cwd(), 'REVISION'), 'utf-8');
    } catch (e) {
      if (e.code !== 'ENOENT') {
        throw e;
      }
    }
  }
  return version;
};

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

let version;
module.exports = () => {
  if (!version) {
    version = `v${require(path.join(process.cwd(), 'package.json')).version}`;
    try {
      version += '-' + fs.readFileSync(path.join(process.cwd(), 'REVISION'), 'utf-8').trim();
    } catch (e) {
      if (e.code !== 'ENOENT') {
        throw e;
      }
    }
  }
  return version;
};

Trim revision string in version package


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

// list of files / patterns to load in the browser
files = [
  ANGULAR_SCENARIO,
  ANGULAR_SCENARIO_ADAPTER,
  'test/e2e/*.spec.js'
];

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

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

// web server port
port = 9877;

// cli runner port
runnerPort = 9101;

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

urlRoot = '/__karma/';

proxies = {
  '/': 'http://localhost:3000/'
};

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

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

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

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

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

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

// list of files / patterns to load in the browser
files = [
  ANGULAR_SCENARIO,
  ANGULAR_SCENARIO_ADAPTER,
  'test/e2e/*.spec.js'
];

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

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

// web server port
port = 9877;

// cli runner port
runnerPort = 9101;

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

urlRoot = '/__karma/';

proxies = {
  '/': 'http://localhost:3000/'
};

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

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

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

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

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

Migrate to PhantomJS for testing in travis


import React from "react";
import { render } from "react-dom";
import ProjectSamples from "./components/ProjectSamples";
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/query";
import { Provider } from "react-redux";
import { samplesApi } from "../../../apis/projects/samples";
import { associatedProjectsApi } from "../../../apis/projects/associated-projects";
import samplesReducer from "../redux/samplesSlice";
import userReducer from "../redux/userSlice";
import { projectApi } from "../../../apis/projects/project";

export const store = configureStore({
  reducer: {
    user: userReducer,
    samples: samplesReducer,
    [projectApi.reducerPath]: projectApi.reducer,
    [samplesApi.reducerPath]: samplesApi.reducer,
    [associatedProjectsApi.reducerPath]: associatedProjectsApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(
      samplesApi.middleware,
      associatedProjectsApi.middleware
    ),
  devTools: process.env.NODE_ENV !== "production",
});
setupListeners(store.dispatch);

render(
  <Provider store={store}>
    <ProjectSamples />
  </Provider>,
  document.getElementById("root")
);

import React from "react";
import { render } from "react-dom";
import ProjectSamples from "./components/ProjectSamples";
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/query";
import { Provider } from "react-redux";
import { samplesApi } from "../../../apis/projects/samples";
import { associatedProjectsApi } from "../../../apis/projects/associated-projects";
import samplesReducer from "../redux/samplesSlice";
import { projectApi } from "../../../apis/projects/project";

export const store = configureStore({
  reducer: {
    samples: samplesReducer,
    [projectApi.reducerPath]: projectApi.reducer,
    [samplesApi.reducerPath]: samplesApi.reducer,
    [associatedProjectsApi.reducerPath]: associatedProjectsApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(
      samplesApi.middleware,
      associatedProjectsApi.middleware
    ),
  devTools: process.env.NODE_ENV !== "production",
});
setupListeners(store.dispatch);

render(
  <Provider store={store}>
    <ProjectSamples />
  </Provider>,
  document.getElementById("root")
);

Remove user slice since since not used


var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var ObjectId = mongoose.Schema.Types.ObjectId;
var Location = require('./location_model');

var XeroAccountSchema   = new Schema({
	location_id: { type: ObjectId, ref: 'Location' },
	consumer_key: String,
	consumer_secret: String
});

XeroAccountSchema.set("_perms", {
	super_user: "crud",
	admin: "r",
	all: ""
});

module.exports = mongoose.model('XeroAccount', XeroAccountSchema);

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var ObjectId = mongoose.Schema.Types.ObjectId;
var Location = require('./location_model');

var XeroAccountSchema   = new Schema({
	location_id: { type: ObjectId, ref: 'Location' },
	consumer_key: String,
	consumer_secret: String
});

XeroAccountSchema.set("_perms", {
	super_user: "crud",
	admin: "r",
	user: "r",
	all: ""
});

module.exports = mongoose.model('XeroAccount', XeroAccountSchema);

Allow users to read xeroaccount for booking


// @flow
import React from 'react';
import styled from 'styled-components';

const Wrapper = styled.div`
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  top: 0;
  left: 0;
  padding: 8px;
`;

const ZoomButton = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: #ccc;
  border: 1px solid #000000;
  cursor: pointer;
  text-align: center;
  margin: 0 0 8px 0;
`;

type Props = {
  zoomIn: (val: number) => void,
  zoomOut: (val: number) => void,
}

export default class FloorplanUI extends React.Component {

  props: Props;

  render() {
    return (
      <Wrapper>
        <ZoomButton onClick={() => this.props.zoomIn(0.5)}>+</ZoomButton>
        <ZoomButton onClick={() => this.props.zoomOut(0.5)}>-</ZoomButton>
      </Wrapper>
    );
  }
}

// @flow
import React from 'react';
import styled from 'styled-components';

const ZoomButtons = styled.div`
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  top: 0;
  left: 0;
  padding: 8px;
`;

const ZoomButton = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: #ccc;
  border: 1px solid #000000;
  cursor: pointer;
  text-align: center;
  margin: 0 0 8px 0;
`;

const Tooltip = styled.div`
  position: absolute;
  display: flex;
  justify-content: center;
  left: ${props => props.x}px;
  top: ${props => props.y}px;
  background-color: #000000;
  color: #ffffff;
  padding: 8px;
  border-radius: 4px;
  opacity: 0.8;
`;

type Props = {
  zoomIn: (val: number) => void,
  zoomOut: (val: number) => void,
}

export default class FloorplanUI extends React.Component {

  props: Props;

  render() {
    return (
      <div>
        <ZoomButtons>
          <ZoomButton onClick={() => this.props.zoomIn(0.5)}>+</ZoomButton>
          <ZoomButton onClick={() => this.props.zoomOut(0.5)}>-</ZoomButton>
        </ZoomButtons>
        <Tooltip x={400} y={300}>
          {"Zergov | C-30"}
        </Tooltip>
      </div>
    );
  }
}

Add tooltip mockup and ui component


import { KEYBINDING_UPDATE } from 'core/actions/types';
import { defaultCommands } from './defaults';

export default function keyboardConfigReducer(
  state = defaultCommands,
  action,
) {
  if (action.type !== KEYBINDING_UPDATE || !(command in state)) {
    return state;
  }
  const { command, keyBinding } = action.payload;
  const newCommand = Object.assign({}, state[command], { command: keyBinding });
  return Object.assign(
    {},
    state,
    { [command]: newCommand },
  );
}

import { KEYBINDING_UPDATE, KEYBINDING_DEFAULT_RESET } from 'core/actions/types';
import { defaultCommands } from './defaults';

export default function keyboardConfigReducer(
  state = defaultCommands,
  action,
) {
  const { type } = action;
  switch (type) {
    case KEYBINDING_UPDATE: {
      const { command, keyBinding } = action.payload;
      return Object.assign(
        {},
        state,
        { [command]: Object.assign({}, state[command], { command: keyBinding }) },
      );
    }
    case KEYBINDING_DEFAULT_RESET:
      return defaultCommands;
    default:
      return state;
  }
}

Use switch-case for handling actions


const Path = require('path')

const action = process.argv[2]
var args = process.argv.slice(3)
var env, bin

switch (action) {
  case 'build':
    bin = 'webpack'
    env = 'production'
    break

  case 'dist':
  case 'stats':
    bin = 'webpack'
    env = 'production'
    args.push('-p')
    break

  case 'dev':
  case 'start':
    bin = 'webpack-dev-server'
    env = 'development'
    args.push('--hot')
    break

  case '--version':
  case '-v':
    console.log(require('../package.json').version)
    process.exit(1)
    return

  default:
    console.log('missing command: build, dev or dist')
    process.exit(1)
}

var cmd = []
if (!process.env.NODE_ENV) {
  cmd.push(`NODE_ENV=${env}`) // add NODE_ENV only if not already defined
}
cmd.push(require.resolve(`.bin/${bin}`))
cmd.push('--config')
cmd.push(Path.resolve(__dirname, '../webpackfile.js'))
cmd.push('--progress')

if (action === 'stats') {
  cmd.push('--profile', '--json')
} else if (bin === 'webpack') {
  cmd.push('--hide-modules') // webpack-dev-server does not have hide-modules
}

cmd.push(...args)

console.log(cmd.join(' '))

const Path = require('path')

const action = process.argv[2]
var args = process.argv.slice(3)
var env, bin

switch (action) {
  case 'build':
    bin = 'webpack'
    env = 'production'
    break

  case 'dist':
  case 'stats':
    bin = 'webpack'
    env = 'production'
    args.push('-p')
    break

  case 'dev':
  case 'start':
    bin = 'webpack-dev-server'
    env = 'development'
    args.push('--hot')
    break

  case '--version':
  case '-v':
    console.log(require('../package.json').version)
    process.exit(1)
    return

  default:
    console.log('missing command: build, dev, dist or stats')
    process.exit(1)
}

var cmd = []
if (!process.env.NODE_ENV) {
  cmd.push(`NODE_ENV=${env}`) // add NODE_ENV only if not already defined
}
cmd.push(require.resolve(`.bin/${bin}`))
cmd.push('--config')
cmd.push(Path.resolve(__dirname, '../webpackfile.js'))
cmd.push('--progress')

if (action === 'stats') {
  cmd.push('--profile', '--json')
} else if (bin === 'webpack') {
  cmd.push('--hide-modules') // webpack-dev-server does not have hide-modules
}

cmd.push(...args)

console.log(cmd.join(' '))

Add stats command to help


Meteor.methods({
  'addFirstUserToAdminRole': function () {
    /*
     Look for existing users and set initial user as admin
    */

    // Count registered users
    var userCount = Meteor.users().count();

    // If there is only one user
    if (userCount === 1) {
      // Get the only user
      var user = Meteor.users.findOne();

      // Get the user's roles
      var userRoles = Roles.getRolesForUser(user);

      // Define the admin role
      // TODO: refactor this to take the role name as input parameter
      // TODO: create role if it doesnt exist
      var adminRole = 'admin';

      // Check if current user is admin
      var userIsAdmin = _.contains(userRoles, adminRole);

      // If user is not admin
      if (!userIsAdmin) {
        // Add user to admin role
        Roles.addUsersToRoles(userId, ['admin']);
        console.log("Added first user as admin.");
      } else {
        console.log("First user already has admin role.")
      }
    }
  }
});

Meteor.methods({
  'addFirstUserToAdminRole': function (adminRole) {
    /*
     Look for existing users and set initial user as admin
    */

    // Set default admin role if none provided
    var adminRole = adminRole || 'admin';

    // Create admin role if not existing
    Meteor.call('createRoleIfNotExisting', adminRole);

    // Count registered users
    var userCount = Meteor.users().count();

    // If there is only one user
    if (userCount === 1) {
      // Get the only user
      var user = Meteor.users.findOne();

      // Get the user's roles
      var userRoles = Roles.getRolesForUser(user);

      // Check if current user is admin
      var userIsAdmin = _.contains(userRoles, adminRole);

      // If user is not admin
      if (!userIsAdmin) {
        // Add user to admin role
        Roles.addUsersToRoles(userId, [adminRole]);
        console.log("Added first user as admin.");
      } else {
        console.log("First user already has admin role.")
      }
    }
  }
});

Allow adminRole argument; create role if needed


(function (window) {
    window.__env = window.__env || {};
    if (window.location.port) {
        window.__env.hostUrl = window.location.protocol + '//' + window.location.hostname + ':' + window.location.port + '/';
    }
    else {
        window.__env.hostUrl = window.location.protocol + '//' + window.location.hostname + '/';
    }
    window.__env.apiUrl = window.__env.hostUrl + 'api/';
    window.__env.intontationUrl = window.__env.hostUrl + 'intonation/';
    window.__env.annotatorUrl = window.__env.hostUrl + 'annotator/api/';
    window.__env.baseUrl = '/';
    window.__env.siteName = 'ISCAN';
    window.__env.enableDebug = true;
}(this));

(function (window) {
    window.__env = window.__env || {};
    if (window.location.port) {
        window.__env.hostUrl = window.location.protocol + '//' + window.location.hostname + ':' + window.location.port + window.location.pathname;
    }
    else {
        window.__env.hostUrl = window.location.protocol + '//' + window.location.hostname + '/';
    }
    window.__env.apiUrl = window.__env.hostUrl + 'api/';
    window.__env.intontationUrl = window.__env.hostUrl + 'intonation/';
    window.__env.annotatorUrl = window.__env.hostUrl + 'annotator/api/';
    window.__env.baseUrl = '/';
    window.__env.siteName = 'ISCAN';
    window.__env.enableDebug = true;
}(this));

Fix for pathnames other than "/"


import Vector from "./Vector.js";
import * as constants from "./constants";
import * as pointUtils from "./pointUtils";

import * as movementUtils from "./movementUtils";

let initNum = 0;
export default class Body {
    constructor(game, center, options = {}) {
        this.game = game;
        this.center = center;

        this.isAlive = true;
        this.type = "body";
        this.id = initNum;

        this.mass = constants.EarthMass / 100;
        this.radius = this.mass / constants.EarthMass * constants.EarthRadius;

        this.speed = options.initialSpeed;

        initNum++;
    }

    update(timeSinceUpdate) {
        const affectingObjects = this.game.attractors.concat(this.game.deflectors);

        let { acceleration, delta } = movementUtils.moveObject(this,
            timeSinceUpdate,
            affectingObjects);

        let min = 255;
        let max = 360;
        let hue = Math.min(acceleration / max, 1) * (max - min) + min;
        this.color = `hsl(${hue}, 100%, 70%)`;

        this.isAlive = !pointUtils.willCollide(this, delta, affectingObjects);
    }
}

import Vector from "./Vector.js";
import * as constants from "./constants";
import * as pointUtils from "./pointUtils";

import * as movementUtils from "./movementUtils";

let initNum = 0;
export default class Body {
    constructor(game, center, options = {}) {
        this.game = game;
        this.center = center;

        this.isAlive = true;
        this.type = "body";
        this.id = initNum;

        this.mass = constants.EarthMass / 100;
        this.radius = this.mass / constants.EarthMass * constants.EarthRadius;

        this.speed = options.initialSpeed || new Vector(
            7000,
            Math.PI,
            "m/s"
        );

        initNum++;
    }

    update(timeSinceUpdate) {
        const affectingObjects = this.game.attractors.concat(this.game.deflectors);

        let { acceleration, delta } = movementUtils.moveObject(this,
            timeSinceUpdate,
            affectingObjects);

        let min = 255;
        let max = 360;
        let hue = Math.min(acceleration / max, 1) * (max - min) + min;
        this.color = `hsl(${hue}, 100%, 70%)`;

        this.isAlive = !pointUtils.willCollide(this, delta, affectingObjects);
    }
}

Fix everything breaking when a body is created without an initialSpeed


var SettingsGeneralController = Ember.ObjectController.extend({
    isDatedPermalinks: function (key, value) {
        // setter
        if (arguments.length > 1) {
            this.set('permalinks', value ? '/:year/:month/:day/:slug/' : '/:slug/');
        }

        // getter
        var slugForm = this.get('permalinks');

        return slugForm !== '/:slug/';
    }.property('permalinks'),

    themes: function () {
        return this.get('availableThemes').reduce(function (themes, t) {
            var theme = {};

            theme.name = t.name;
            theme.label = t.package ? t.package.name + ' - ' + t.package.version : t.name;
            theme.package = t.package;
            theme.active = !!t.active;

            themes.push(theme);

            return themes;
        }, []);
    }.property('availableThemes').readOnly(),

    actions: {
        save: function () {
            var self = this;

            return this.get('model').save().then(function (model) {
                self.notifications.closePassive();
                self.notifications.showSuccess('Settings successfully saved.');

                return model;
            }).catch(function (errors) {
                self.notifications.closePassive();
                self.notifications.showErrors(errors);
            });
        },
    }
});

export default SettingsGeneralController;

var SettingsGeneralController = Ember.ObjectController.extend({
    isDatedPermalinks: function (key, value) {
        // setter
        if (arguments.length > 1) {
            this.set('permalinks', value ? '/:year/:month/:day/:slug/' : '/:slug/');
        }

        // getter
        var slugForm = this.get('permalinks');

        return slugForm !== '/:slug/';
    }.property('permalinks'),

    themes: function () {
        return this.get('availableThemes').reduce(function (themes, t) {
            var theme = {};

            theme.name = t.name;
            theme.label = t.package ? t.package.name + ' - ' + t.package.version : t.name;
            theme.package = t.package;
            theme.active = !!t.active;

            themes.push(theme);

            return themes;
        }, []);
    }.property().readOnly(),

    actions: {
        save: function () {
            var self = this;

            return this.get('model').save().then(function (model) {
                self.notifications.closePassive();
                self.notifications.showSuccess('Settings successfully saved.');

                return model;
            }).catch(function (errors) {
                self.notifications.closePassive();
                self.notifications.showErrors(errors);
            });
        },
    }
});

export default SettingsGeneralController;

Fix active theme selector. Add validation to API.


$(document).ready(function () {
	$('textarea').focus();

	$('form').on('submit', function (event) {
		event.preventDefault();

		var text = $('textarea').val();
		var y = 0;

		if (text == '') {
			text = 'You should probably enter some text next time.'
		}

		$('header a').addClass('dark');
		$('body').css('background-color', '#090919');
		$('#container').html('
' + text + '
')

		scrollText(y);

		function scrollText(y) {
			setTimeout(function () {
				var newY = y;
				var height = $('#teleprompt_screen').height();

				if (newY > -1 * height - 150) {
					newY -= 1;
					$('#teleprompt_screen').css('top', newY);

					scrollText(newY);
				}
			}, 30);
		};

	});
	// get the height of the screen
	// move the text up the screen by changing the top CSS

});

$(document).ready(function () {
	$('textarea').focus();

	$('form').on('submit', function (event) {
		event.preventDefault();

		var text = $('textarea').val();
		var y = 0;

		if (text.replace(/^\s+|\s+$/g, '') == '') {
			text = 'You should probably enter some text next time.'
		}

		$('header a').addClass('dark');
		$('body').css('background-color', '#090919');
		$('#container').html('
' + text + '
')

		scrollText(y);

		function scrollText(y) {
			setTimeout(function () {
				var newY = y;
				var height = $('#teleprompt_screen').height();

				if (newY > -1 * height - 150) {
					newY -= 1;
					$('#teleprompt_screen').css('top', newY);

					scrollText(newY);
				}
			}, 30);
		};

	});
	// get the height of the screen
	// move the text up the screen by changing the top CSS

});

Expand check for empty strings


var node = nodeDefinitions(
    function(globalId, ast) {
        var gid = fromGlobalId(globalId);
        var fieldKind = gid.id.match(/^[0-9]+$/) ? 'IntValue' : 'StringValue';
        var override = { arguments: [{ name: { value: 'id' }, value: { value: id, kind: fieldKind } }] };
        return getEntityResolver(gid.type)(null, {}, ast, override);
    },
    function(data) {
        return data.__type;
    }
);

var nodeInterface = node.nodeInterface;
var nodeField = node.nodeField;

var node = nodeDefinitions(
    function(globalId, ast) {
        var gid = fromGlobalId(globalId);
        var fieldKind = gid.id.match(/^[0-9]+$/) ? 'IntValue' : 'StringValue';
        var override = { arguments: [{ name: { value: 'id' }, value: { value: gid.id, kind: fieldKind } }] };
        return getEntityResolver(gid.type)(null, {}, ast, override);
    },
    function(data) {
        return data.__type;
    }
);

var nodeInterface = node.nodeInterface;
var nodeField = node.nodeField;

Fix error in node definition code


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

export default {
    entry: 'src/index.js',
    plugins: [
        commonjs(),
        resolve({
            customResolveOptions: {
                moduleDirectory: 'node_modules',
            }
        }),
        babel({
            exclude: 'node_modules/**'
        })
    ],
    external: ['knockout', 'react', 'prop-types', 'create-react-class'],
    globals: {
        knockout: 'ko',
        react: 'React',
        'prop-types': 'PropTypes',
        'create-react-class': 'createReactClass'
    },
    moduleName: 'komx-react',
    targets: [
        { dest: 'dist/komx-react.umd.js', format: 'umd' },
        { dest: 'dist/komx-react.es.js', format: 'es' }
    ],
};

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

export default {
    entry: 'src/index.js',
    plugins: [
        commonjs(),
        resolve({
            customResolveOptions: {
                moduleDirectory: 'node_modules',
            }
        }),
        babel({
            exclude: 'node_modules/**'
        })
    ],
    external: ['knockout', 'react'],
    globals: {
        knockout: 'ko',
        react: 'React'
    },
    moduleName: 'komx-react',
    targets: [
        { dest: 'dist/komx-react.umd.js', format: 'umd' },
        { dest: 'dist/komx-react.es.js', format: 'es' }
    ],
};

Remove prop-types and create-react-class as externals.


/**
 * This file specifies the database collection and field names.
 */
export const COLLECTION_TRIPS = 'trips';
export const TRIPS_TITLE = 'title';
export const TRIPS_DESCRIPTION = 'description';
export const TRIPS_DESTINATION = 'destination';
export const TRIPS_START_DATE = 'start_date';
export const TRIPS_END_DATE = 'end_date';
export const TRIPS_ACCEPTED_COLLABS = 'accepted_collaborator_uid_arr';
export const TRIPS_PENDING_COLLABS = 'pending_collaborator_uid_arr';
export const TRIPS_REJECTED_COLLABS = 'rejected_collaborator_uid_arr';
export const TRIPS_UPDATE_TIMESTAMP = 'update_timestamp';
/**
 * NOTE: The following constant corresponds to the general collaborator field in
 * {@link_RawTripData} and is not an actual field in a trip document.
 */
export const RAW_COLLAB_EMAILS = 'collaboratorEmails';

export const COLLECTION_ACTIVITIES = 'activities';
export const ACTIVITIES_START_TIME = 'start_time';
export const ACTIVITIES_END_TIME = 'end_time';
export const ACTIVITIES_START_TZ = 'start_tz';
export const ACTIVITIES_END_TZ = 'end_tz';
export const ACTIVITIES_TITLE = 'title';
export const ACTIVITIES_DESCRIPTION = 'description';
export const ACTIVITIES_START_COUNTRY = 'start_country';
export const ACTIVITIES_END_COUNTRY = 'end_country';

/**
 * This file specifies the database collection and field names.
 */
export const COLLECTION_TRIPS = 'trips';
export const TRIPS_TITLE = 'title';
export const TRIPS_DESCRIPTION = 'description';
export const TRIPS_DESTINATION = 'destination';
export const TRIPS_START_DATE = 'start_date';
export const TRIPS_END_DATE = 'end_date';
export const TRIPS_ACCEPTED_COLLABS = 'accepted_collaborator_uid_arr';
export const TRIPS_PENDING_COLLABS = 'pending_collaborator_uid_arr';
export const TRIPS_REJECTED_COLLABS = 'rejected_collaborator_uid_arr';
export const TRIPS_UPDATE_TIMESTAMP = 'update_timestamp';
/**
 * NOTE: The following constant corresponds to the general collaborator field in
 * {@link RawTripData} and is not an actual field in a trip document.
 */
export const RAW_COLLAB_EMAILS = 'collaboratorEmails';

export const COLLECTION_ACTIVITIES = 'activities';
export const ACTIVITIES_START_TIME = 'start_time';
export const ACTIVITIES_END_TIME = 'end_time';
export const ACTIVITIES_START_TZ = 'start_tz';
export const ACTIVITIES_END_TZ = 'end_tz';
export const ACTIVITIES_TITLE = 'title';
export const ACTIVITIES_DESCRIPTION = 'description';
export const ACTIVITIES_START_COUNTRY = 'start_country';
export const ACTIVITIES_END_COUNTRY = 'end_country';

Replace accidental _ with space in JSDoc link.


document.getElementById("ayuda").setAttribute("aria-current", "page");

function prueba() {
	alert("prueba");
}

var aside = document.getElementById("complementario");
var button = document.createElement("BUTTON");
var text = document.createTextNode("Prueba");
button.appendChild(text);
aside.appendChild(button);
button.addEventListener("click", prueba());

document.getElementById("ayuda").setAttribute("aria-current", "page");

function prueba() {
	alert("prueba");
}

var aside = document.getElementById("complementario");
var button = document.createElement("BUTTON");
var text = document.createTextNode("Prueba");
button.appendChild(text);
aside.appendChild(button);
button.addEventListener("click", prueba);

Update script, hope fixed now


import express from 'express';
import logger from 'morgan';
import validator from 'express-validator';
import bodyParser from 'body-parser';
import verifyToken from './middlewares/auth';
import valueChecker from './middlewares/valueChecker';
import router from './routes/router';

const app = express();
const port = process.env.PORT || 8000;

app.use(logger('dev'));
app.use(express.static('public'));

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

app.use('/api/v1', verifyToken);
app.use(valueChecker);
app.use(router);

app.use('*', (req, res) => res.redirect(302, '/'));

app.listen(port);

export default app;

import express from 'express';
import logger from 'morgan';
import validator from 'express-validator';
import bodyParser from 'body-parser';
import verifyToken from './middlewares/verifyToken';
import valueChecker from './middlewares/valueChecker';
import router from './routes/router';

const app = express();
const port = process.env.PORT || 8000;

app.use(logger('dev'));
app.use(express.static('public'));

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

app.use('/api/v1', verifyToken);
app.use(valueChecker);
app.use(router);

app.use('*', (req, res) => res.redirect(302, '/'));

app.listen(port);

export default app;

Modify the imported module filename


var wompt = require('./includes');

function MetaUserManager(){
	this.meta_users = {};
	this.expirer = new wompt.Expirer(this.meta_users,{
		keep_if: function(mu){
			return mu.clients.count > 0;
		},
		time_attribute:'touched'
	});
}

var p = MetaUserManager.prototype;
p.get = function(id){
	return this.meta_users[id];
}

p.set = function(id, mu){
	this.meta_users[id] = mu;
}

module.exports = MetaUserManager;

var wompt = require('./includes');

function MetaUserManager(){
	this.meta_users = {};

	this.expirer = new wompt.Expirer(this.meta_users,{
		keep_if: function(mu){
			return mu.clients.count > 0;
		},
		time_attribute:'touched'
	});
}

var p = MetaUserManager.prototype;
p.get = function getMU(id){
	return this.meta_users[id];
}

// Get a MU by ID or call the provided function and pass the "set" function
// which is the same as like this.set()
// returns the existing MU or the created one.
p.getOrCreate = function getOrCreateMU(id, create){
	var result, self = this;

	function set(id, object){
		result = self.set(id, object);
	}

	return self.get(id) || (create(set) || result);
}

p.set = function setMU(id, mu){
	return this.meta_users[id] = mu;
}
module.exports = MetaUserManager;

Add a simple getOrCreate() function to MetaUserManager and add names to it's anonymous functions


/* eslint-env mocha */
const InsertionSort = require('../../../src').Algorithms.Sorting.InsertionSort;
const assert = require('assert');

describe('Insertion Sort', () => {
  it('should have no data when empty initialization', () => {
    const inst = new InsertionSort();
    assert.equal(inst.size, 0);
    assert.deepEqual(inst.unsortedList, []);
    assert.deepEqual(inst.sortedList, []);
  });

  it('should sort the array', () => {
    const inst = new InsertionSort([2, 1, 3, 4]);
    assert.equal(inst.size, 4);

    assert.deepEqual(inst.unsortedList, [2, 1, 3, 4]);
    assert.deepEqual(inst.sortedList, [1, 2, 3, 4]);
    assert.equal(inst.toString(), '1, 2, 3, 4');
  });

});

/* eslint-env mocha */
const InsertionSort = require('../../../src').Algorithms.Sorting.InsertionSort;
const assert = require('assert');

describe('Insertion Sort', () => {
  it('should have no data when empty initialization', () => {
    const inst = new InsertionSort();
    assert.equal(inst.size, 0);
    assert.deepEqual(inst.unsortedList, []);
    assert.deepEqual(inst.sortedList, []);
  });

  it('should sort the array', () => {
    const inst = new InsertionSort([2, 1, 3, 4]);
    assert.equal(inst.size, 4);

    assert.deepEqual(inst.unsortedList, [2, 1, 3, 4]);
    assert.deepEqual(inst.sortedList, [1, 2, 3, 4]);
    assert.equal(inst.toString(), '1, 2, 3, 4');
  });

  it('should sort the array in ascending order with few equal vals', () => {
    const inst = new InsertionSort([2, 1, 3, 4, 2], (a, b) => a < b);
    assert.equal(inst.size, 5);

    assert.deepEqual(inst.unsortedList, [2, 1, 3, 4, 2]);
    assert.deepEqual(inst.sortedList, [1, 2, 2, 3, 4]);
    assert.equal(inst.toString(), '1, 2, 2, 3, 4');
  });

});

Test Insertion Sort: Test for sorting in ascending order with equal values


/* eslint-env mocha */
'use strict'

const chai = require('chai')
chai.should()

let result = ''

module.exports = function () {
  this.registerHandler('BeforeFeatures', function (features, cb) {
    result += features.length
    cb()
  })

  this.registerHandler('BeforeFeature', function (feature, cb) {
    result += feature.getName()
    cb()
  })

  this.registerHandler('BeforeScenario', function (scenario, cb) {
    result += scenario.getName()
    cb()
  })

  this.registerHandler('BeforeStep', function (step, cb) {
    this.click('#before-step')
    result += step.getName()
    cb()
  })

  this.registerHandler('AfterStep', function (step, cb) {
    result += step.getName()
    cb()
  })

  this.registerHandler('AfterScenario', function (scenario, cb) {
    result += scenario.getName()
    cb()
  })

  this.registerHandler('AfterFeature', function (feature, cb) {
    result += feature.getName()
    cb()
  })

  this.registerHandler('AfterFeatures', function (features, cb) {
    result += features.length
    if (process.send) process.send(result)
    cb()
  })
}

/* eslint-env mocha */
'use strict'

const chai = require('chai')
chai.should()

let result = ''

module.exports = function () {
  this.registerHandler('BeforeFeatures', function (features, cb) {
    result += features.length
    cb()
  })

  this.registerHandler('BeforeFeature', function (feature, cb) {
    result += feature.getName()
    cb()
  })

  this.registerHandler('BeforeScenario', function (scenario, cb) {
    result += scenario.getName()
    cb()
  })

  this.registerHandler('BeforeStep', function (step, cb) {
    result += step.getName()
    cb()
  })

  this.registerHandler('AfterStep', function (step, cb) {
    result += step.getName()
    cb()
  })

  this.registerHandler('AfterScenario', function (scenario, cb) {
    result += scenario.getName()
    cb()
  })

  this.registerHandler('AfterFeature', function (feature, cb) {
    result += feature.getName()
    cb()
  })

  this.registerHandler('AfterFeatures', function (features, cb) {
    result += features.length
    if (process.send) process.send(result)
    cb()
  })
}

Fix event handler test with callback


var columns = 3
var itemPadding = 10

function widthGiven(itemCount) {
    return window.innerWidth / Math.min(columns, itemCount) - itemPadding
}

function heightGiven(itemCount) {
    return window.innerHeight / Math.ceil(itemCount / columns) - itemPadding
}

function styleListItems() {
    var itemCount = $('li').size()
    $('.outerContainer')
        .height(heightGiven(itemCount))
        .width(widthGiven(itemCount))

    scaleFontToContainerSize()
}

function scaleFontToContainerSize() {
    $(".outerContainer").fitText(1.75)
}

function grabLatestData() {
    $.getJSON("/projects").then(function(data){
        $('#projects').empty()

        data.body.forEach(function(project){
            var buildStatus = project.lastBuildStatus
            if(buildStatus !== "Success"){
                $('#projects').append("

" +
                project.name + "

")
            }
        })

        styleListItems()
    })
}

grabLatestData(); // run immediately
setInterval(grabLatestData, 5000);

var columns = 3
var itemPadding = 10

function numberOfColumns() {
    var buildStatusCount = $('li').size()
    return Math.min(columns, $('li').size())
}

function numberOfRows() {
    var buildStatusCount = $('li').size()
    return Math.ceil(buildStatusCount / columns)
}

function buildStatusWidth() {
    return window.innerWidth / numberOfColumns() - itemPadding
}

function buildStatusHeight() {
    return window.innerHeight / numberOfRows() - itemPadding
}

function styleListItems() {
    $('.outerContainer').height(buildStatusHeight()).width(buildStatusWidth())
    scaleFontToContainerSize()
}

function scaleFontToContainerSize() {
    $(".outerContainer").fitText(1.75)
}

function addBuildStatusToScreen(project) {
    var buildStatus = project.lastBuildStatus
    if(buildStatus !== "Success"){
        $('#projects').append("

" +
        project.name + "

")
    }
}

function addListItems(data) {
    $('#projects').empty()
    data.body.forEach(addBuildStatusToScreen)
}

function updateBuildMonitor() {
    $.getJSON("/projects").then(function(data){
        addListItems(data)
        styleListItems()
    })
}

updateBuildMonitor() // run immediately
setInterval(updateBuildMonitor, 5000)

Refactor Javascript now concepts are understood


  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-10687369-9', 'cloudcv.io');
  ga('send', 'pageview');

  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-10687369-8', 'cloudcv.io');
  ga('send', 'pageview');

Update google analytics tracking code


#!/usr/bin/env node

var readJson = require('read-package-json');
var minimist = require('minimist');
var path = require('path');
var url = require('url');

var shields = require('../');

var argv = minimist(process.argv.slice(2), {
  alias: {
    v: 'version'
  }
});

if (argv.version) {
  console.log(require('../package.json').version);
  return;
}

// no args
if (!argv._.length) {
  var usage = [
    'Shield generator for your current project.',
    '',
    'Usage:',
    '  shields [travis] [gemnasium]'
  ];

  console.log(usage.join('\n'));

  return;
}

var p = path.resolve('./package.json');

readJson(p, function(error, pkg) {
  if (error) {
    throw error;
  }

  var slug = url.parse(pkg.repository.url).path.slice(1);

  var links = [''];

  argv._.forEach(function(service) {
    var shield = shields(slug, service);
    var status = service === 'travis' ? 'Build' : 'Dependency';
    console.log('[![' + status + ' Status][' + service + '-svg]][' + service + ']');
    links.push('   [' + service + ']: ' + shield.link);
    links.push('   [' + service + '-svg]: ' + shield.svg);
  });
  console.log(links.join('\n'));
});

#!/usr/bin/env node

var readJson = require('read-package-json');
var minimist = require('minimist');
var path = require('path');
var url = require('url');

var shields = require('../');

var argv = minimist(process.argv.slice(2), {
  alias: {
    v: 'version'
  }
});

if (argv.version) {
  console.log(require('../package.json').version);
  return;
}

// no args
if (!argv._.length) {
  var usage = [
    'Shield generator for your current project.',
    '',
    'Usage:',
    '  shields [travis] [gemnasium]',
    '  shields -v | --version',
    '',
    'Options:',
    '  -v --version  Show version'
  ];

  console.log(usage.join('\n'));

  return;
}

var p = path.resolve('./package.json');

readJson(p, function(error, pkg) {
  if (error) {
    throw error;
  }

  var slug = url.parse(pkg.repository.url).path.slice(1);

  var links = [''];

  argv._.forEach(function(service) {
    var shield = shields(slug, service);
    var status = service === 'travis' ? 'Build' : 'Dependency';
    console.log('[![' + status + ' Status][' + service + '-svg]][' + service + ']');
    links.push('   [' + service + ']: ' + shield.link);
    links.push('   [' + service + '-svg]: ' + shield.svg);
  });
  console.log(links.join('\n'));
});

Add version flag to usage info


var express = require('express');

module.exports = exports = function (options) {
  var BasicAuth = require('./middleware/auth/basic');

  var TradesRouter = require('./routers/trades');
  var UserRouter = require('./routers/user');
  var UsersRouter = require('./routers/users');
  var LabelsRouter = require('./routers/labels');

  var app = express();

  app.use(BasicAuth({ users: options.users }));

  app.use(UserRouter({ users: options.users }));
  app.use(UsersRouter({ users: options.users }));
  app.use(TradesRouter({ trades: options.trades }));
  app.use(LabelsRouter({ labels: options.labels }));

  app.all('*', function (req, res, next) {
    next(new Error('not_found'));
  });

  app.use(function(err, req, res, next) {
    res.send(500, { error: 'something' });
  });

  return app;
};

var express = require('express');

module.exports = exports = function (options) {
  var BasicAuth = require('./middleware/auth/basic');

  var TradesRouter = require('./routers/trades');
  var UserRouter = require('./routers/user');
  var UsersRouter = require('./routers/users');
  var LabelsRouter = require('./routers/labels');

  var app = express();

  app.use(BasicAuth({ users: options.users }));

  app.use(UserRouter({ users: options.users }));
  app.use(UsersRouter({ users: options.users }));
  app.use(TradesRouter({ trades: options.trades }));
  app.use(LabelsRouter({ labels: options.labels }));

  app.all('*', function (req, res, next) {
    next(new Error('not_found'));
  });

  app.use(function(err, req, res, next) {
    res.send(500, { error: err.message });
  });

  return app;
};

Return error message on HTTP 500


//Dear source-code readers, pay no attention to these global variables
var APP_ID = 'YtKfTS2Zo8ELUk63LzB0';
var APP_CODE = 'SB1wcsUEGzLA3SRHJJ7CPw';

// Reference to the root of the Firebase database
var database = new Firebase("https://brilliant-torch-6592.firebaseio.com/");

$("#upload").click(function() {
	var name = $("#nameInput").val();
	var text = $("#messageInput").val();
	if (name.length != 0 && text.length != 0) { // Don't upload if there's no information in the textboxes
		console.log(name.length);
		console.log(text.length);

		database.push({name: name, text: text});

		database.on('child_added', function(update) {
			console.log("Callback function: New data has been added to the database");
		});
	}else {
		console.log("Please input values into the textbox");
	}

});

console.log('LOADED BACKEND');

//Dear source-code readers, pay no attention to these global variables
var APP_ID = 'YtKfTS2Zo8ELUk63LzB0';
var APP_CODE = 'SB1wcsUEGzLA3SRHJJ7CPw';

// Reference to the root of the Firebase database
var database = new Firebase("https://battleshiphere.firebaseio.com/");

$("#upload").click(function() {
	var name = $("#nameInput").val();
	var text = $("#messageInput").val();
	if (name.length != 0 && text.length != 0) { // Don't upload if there's no information in the textboxes
		console.log(name.length);
		console.log(text.length);

		database.push({name: name, text: text});

		database.on('child_added', function(update) {
			console.log("Callback function: New data has been added to the database");
		});
	}else {
		console.log("Please input values into the textbox");
	}

});

console.log('LOADED BACKEND');

Change Database with a more fitting name


#!/usr/bin/env node
/*
 * mochify.js
 *
 * Copyright (c) 2014 Maximilian Antoni <mail@maxantoni.de>
 *
 * @license MIT
 */
'use strict';

var mochify = require('../lib/mochify');
var args    = require('../lib/args');

var opts = args(process.argv.slice(2));
var _    = opts._.length
  ? opts._.join(' ')
  : null;

function error() {
  if (!opts.watch) {
    process.nextTick(function () {
      process.exit(1);
    });
  }
}

mochify(_, opts)
  .on('error', error)
  .bundle(function (err) {
    if (err) {
      error();
    }
  });

#!/usr/bin/env node
/*
 * mochify.js
 *
 * Copyright (c) 2014 Maximilian Antoni <mail@maxantoni.de>
 *
 * @license MIT
 */
'use strict';

var mochify = require('../lib/mochify');
var args    = require('../lib/args');

var opts = args(process.argv.slice(2));
var _    = opts._.length
  ? opts._.join(' ')
  : null;

function error() {
  if (!opts.watch) {
    process.nextTick(function () {
      process.exit(1);
    });
  }
}

mochify(_, opts)
  .on('error', error)
  .bundle();

Remove obsolete error handling callback


import React from "react"
import Form from "./components/form.js"
import Input from "./components/input.js"
import Submit from "./components/submit.js"
import FormErrorList from "./components/form_error_list.js"
import Fieldset from "./components/fieldset.js"

export const form = React.createFactory(Form)
export const input = React.createFactory(Input)
export const submit = React.createFactory(Submit)
export const formErrorList = React.createFactory(FormErrorList)
export const fieldset = React.createFactory(Fieldset)

import React from "react"
import Form from "./components/form.js"
import Input from "./components/input.js"
import Submit from "./components/submit.js"
import FormErrorList from "./components/form_error_list.js"
import Fieldset from "./components/fieldset.js"
import FieldsetText from "./components/fieldset_text.js"

export const form = React.createFactory(Form)
export const input = React.createFactory(Input)
export const submit = React.createFactory(Submit)
export const formErrorList = React.createFactory(FormErrorList)
export const fieldset = React.createFactory(Fieldset)
export const fieldsetText = React.createFactory(FieldsetText)

Add Fieldset Text Factory For DSL


// @flow

import styles from './Icon.css';

type Props = {
  /** Name of the icon can be found on the webpage*/
  name: string,
  scaleOnHover?: boolean,
  className?: string,
  size?: number,
  style?: Object,
};

/**
 * Render an Icon like this with the name of your icon:
 *
 * <Icon name="add" />
 *
 * Names can be found here:
 * https://ionic.io/ionicons
 *
 */
const Icon = ({
  name = 'star',
  scaleOnHover = false,
  className,
  style = {},
  size = 24,
  ...props
}: Props) => {
  return (
    <ion-icon
      name={name}
      class={className}
      style={{
        fontSize: `${size.toString()}px`,
        lineHeight: 2,
        ...style,
      }}
      {...(props: Object)}
    ></ion-icon>
  );
};

Icon.Badge = ({ badgeCount, ...props }: Props & { badgeCount: number }) => {
  const icon = <Icon {...props} />;

  if (!badgeCount) {
    return icon;
  }

  return (

      {badgeCount}
      {icon}

  );
};

Icon.Badge.displayName = 'IconBadge';

export default Icon;

// @flow

import styles from './Icon.css';

type Props = {
  /** Name of the icon can be found on the webpage*/
  name: string,
  scaleOnHover?: boolean,
  className?: string,
  size?: number,
  style?: Object,
};

/**
 * Render an Icon like this with the name of your icon:
 *
 * <Icon name="add" />
 *
 * Names can be found here:
 * https://ionic.io/ionicons
 *
 */
const Icon = ({
  name = 'star',
  scaleOnHover = false,
  className,
  style = {},
  size = 24,
  ...props
}: Props) => {
  return (
    <div
      className={className}
      style={{
        fontSize: `${size.toString()}px`,
        ...style,
      }}
      {...(props: Object)}
    >
      <ion-icon name={name}></ion-icon>

  );
};

Icon.Badge = ({ badgeCount, ...props }: Props & { badgeCount: number }) => {
  const icon = <Icon {...props} />;

  if (!badgeCount) {
    return icon;
  }

  return (

      {badgeCount}
      {icon}

  );
};

Icon.Badge.displayName = 'IconBadge';

export default Icon;

Move className onto a div wrapping ion-icon


require('babel-polyfill');

// jsdom doesn't have support for localStorage at the moment
global.localStorage = require('localStorage');

// Tests should just mock responses for the json API
// so let's just default to a noop
var RequestUtil = require('mesosphere-shared-reactjs').RequestUtil;
RequestUtil.json = function () {};

// jsdom doesn't have support for requestAnimationFrame so we polyfill it.
// https://gist.github.com/paulirish/1579671
(function() {
  var lastTime = 0;
  var vendors = ['ms', 'moz', 'webkit', 'o'];

  for (var x = 0; x < vendors.length && !global.requestAnimationFrame; ++x) {
    global.requestAnimationFrame = global[vendors[x] + 'RequestAnimationFrame'];
    global.cancelAnimationFrame = global[vendors[x] + 'CancelAnimationFrame']
      || global[vendors[x] + 'CancelRequestAnimationFrame'];
  }

  if (!global.requestAnimationFrame)
    global.requestAnimationFrame = function (callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = global.setTimeout(
        function () {
          callback(currTime + timeToCall);
        },
        timeToCall
      );

      lastTime = currTime + timeToCall;
      return id;
    };

  if (!global.cancelAnimationFrame) {
    global.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
  }
}());

Implement a better requestAnimationFrame polyfill


import Ember from 'ember';
import moment from 'moment';
import stringToHue from 'bus-detective/utils/string-to-hue';
var inject = Ember.inject;

export default Ember.Component.extend({
  tagName: 'li',
  clock: inject.service(),
  attributeBindings: ['style'],
  classNames: ['arrival'],
  classNameBindings: ['isPast:arrival--past'],

  timeFromNow: Ember.computed('clock.time', function() {
    return moment(this.get('arrival.time')).fromNow('mm');
  }),

  isPast: Ember.computed('clock.time', function() {
    return new Date(this.get('arrival.time')) < new Date();
  }),

  style: Ember.computed('arrival.route_id', function() {
    var hue = stringToHue(this.get('arrival.route_id').toString());
    return `color:            hsl(${hue}, 60%, 100%);
            background-color: hsl(${hue}, 50%, 55%);
            border-color:     hsl(${hue}, 50%, 55%);`;
  })
});

import Ember from 'ember';
import moment from 'moment';
import stringToHue from 'bus-detective/utils/string-to-hue';
var inject = Ember.inject;

export default Ember.Component.extend({
  tagName: 'li',
  clock: inject.service(),
  attributeBindings: ['style'],
  classNames: ['timeline__event'],
  classNameBindings: ['isPast:arrival--past'],

  timeFromNow: Ember.computed('clock.time', function() {
    return moment(this.get('arrival.time')).fromNow('mm');
  }),

  isPast: Ember.computed('clock.time', function() {
    return new Date(this.get('arrival.time')) < new Date();
  }),

  style: Ember.computed('arrival.route_id', function() {
    var hue = stringToHue(this.get('arrival.route_id').toString());
    return `color:            hsl(${hue}, 60%, 100%);
            background-color: hsl(${hue}, 50%, 55%);
            border-color:     hsl(${hue}, 50%, 55%);`;
  })
});

Change the class name on the arrival component container


/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    // Add options here
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  app.import('bower_components/d3/d3.js');
  // Customized version of nvd3
  app.import('vendor/nvd3/build/nv.d3.css');
  app.import('vendor/nvd3/build/nv.d3.js');

  return app.toTree();
};

/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    // Add options here
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  app.import('bower_components/d3/d3.js');
  app.import('bower_components/lodash/lodash.min.js');
  // Customized version of nvd3
  app.import('vendor/nvd3/build/nv.d3.css');
  app.import('vendor/nvd3/build/nv.d3.js');

  return app.toTree();
};

Add lodash to production build.


var fs   = require('fs');
var glob = require('glob');
var _    = require('underscore');

var FeaturePathExpander = {
  expandPaths: function expandPaths(paths) {
    var Cucumber     = require('../../../cucumber');
    var PathExpander = Cucumber.Cli.ArgumentParser.PathExpander;

    var expandedPaths = PathExpander.expandPathsWithGlobString(paths, FeaturePathExpander.GLOB_FEATURE_FILES_IN_DIR_STRING);
    return expandedPaths;
  }
};
FeaturePathExpander.GLOB_FEATURE_FILES_IN_DIR_STRING = "**/*.feature";
module.exports = FeaturePathExpander;

Remove unneeded requires from FeaturePathExpander


const chalk = require('chalk');
const fs = require('fs');
const path = require('path');
const tilde = require('tilde-expansion');
const shell = require('shelljs');
const appRootDir = require('app-root-dir').get();
const setConfigProp = require('./setConfigProp');
const newTodoMonth = require('./newTodoMonth');

const init = function({ dir, withGit=false }){
  tilde(dir, (expandedDir) => {
    if (!fs.existsSync(expandedDir)) return console.log(chalk.red('Directory for workbook doesnt exist'));
    console.log(chalk.green(`Initializing new todo workbook in ${dir}`));
    const configPath = path.join(appRootDir, 'config.json');
    const todoRoot = path.join(expandedDir, 'todo');

    if (!fs.existsSync( todoRoot )) fs.mkdirSync( todoRoot );
    writeConfig( configPath, todoRoot );
    setConfigProp({ todoRoot, withGit });
    newTodoMonth( todoRoot );
    if (withGit) {
      console.log(chalk.blue('Initialising with git'));
      shell.cd(expandedDir);
      shell.exec('git init');
      shell.exec('git add --all');
      shell.exec('git commit -m "Initial Commit"');
    }
  });
};

const writeConfig = function(configPath){
  console.log(chalk.green('Writing config'));
  fs.writeFileSync(configPath, JSON.stringify({}));
}

module.exports = init;

const chalk = require('chalk');
const fs = require('fs');
const path = require('path');
const tilde = require('tilde-expansion');
const shell = require('shelljs');
const appRootDir = require('app-root-dir').get();
const setConfigProp = require('./setConfigProp');
const newTodoMonth = require('./newTodoMonth');

const init = function({ dir, withGit=false }){
  tilde(dir, (expandedDir) => {
    if (!fs.existsSync(expandedDir)) return console.log(chalk.red('Directory for workbook doesnt exist'));
    console.log(chalk.green(`Initializing new todo workbook in ${dir}`));
    const configPath = path.join(appRootDir, 'config.json');

    let todoRoot = expandedDir;
    if(path.parse(expandedDir).name !== 'todo') todoRoot = path.join(expandedDir, 'todo');

    if (!fs.existsSync( todoRoot )) fs.mkdirSync( todoRoot );
    writeConfig( configPath, todoRoot );
    setConfigProp({ todoRoot, withGit });
    newTodoMonth( todoRoot );
    if (withGit) {
      console.log(chalk.blue('Initialising with git'));
      shell.cd(todoRoot);
      shell.exec('git init');
      shell.exec('git add --all');
      shell.exec('git commit -m "Initial Commit"');
    }
  });
};

const writeConfig = function(configPath){
  console.log(chalk.green('Writing config'));
  fs.writeFileSync(configPath, JSON.stringify({}));
}

module.exports = init;

Add check for existing /todo folder in path


$( document ).ready(function() {
	var typer = $('.typeahead');

	var genders = new Bloodhound({
		datumTokenizer: Bloodhound.tokenizers.whitespace,
		queryTokenizer: Bloodhound.tokenizers.whitespace,
		prefetch: 'https://raw.githubusercontent.com/anne-decusatis/genderamender/master/genders.json'
	});
	typer.typeahead({
		hint: true,
		highlight: true,
		minLength: 1
	},
	{
		name: 'genders',
		source: genders
	});

	typer.bind('typeahead:select', function(ev, suggestion) {
		var choices = $('#user-genders');
	    choices.html( choices.html() + "<li>" + suggestion + "  </li>" ); //FIXME: i'm 100% certain there's a better way for this
	});
});

$( document ).ready(function() {
	var typer = $('.typeahead');
	var genders = new Bloodhound({
		datumTokenizer: Bloodhound.tokenizers.whitespace,
		queryTokenizer: Bloodhound.tokenizers.whitespace,
		prefetch: 'https://raw.githubusercontent.com/anne-decusatis/genderamender/master/genders.json',
        sorter: function(a, b) {
            var i = typer.val().toLowerCase();

            // If there's a complete match it wins.
            if(i == a) { return -1; }
            else if (i == b) { return  1; }

            // Otherwise just sort it naturally.
            else if (a < b) { return -1; }
            else if (a > b) { return  1; }
            else return 0;
        }
	});

	typer.typeahead({
		hint: true,
		highlight: true,
		minLength: 1

	},
	{
		name: 'genders',
		source: genders
	});

	typer.bind('typeahead:select', function(ev, suggestion) {
		var choices = $('#user-genders');
	    choices.html( choices.html() + "<li>" + suggestion + "  </li>" ); //FIXME: i'm 100% certain there's a better way for this
	});
});

Make typeahead sort-- promoting identical matches, or just naturally.


import Post from 'src/models/PostModel';
import { buildGalleryPost } from 'src/server/modules/build';

export default (req, res, next) => {
  Post.paginate({}, {
    page: req.query.page,
    limit: req.query.limit,
    sort: '-postDate',
    select: 'title images postDate',
  })
    .then((data) => {
      const posts = data.docs.map(post => buildGalleryPost(post));
      res.send(posts.join());
    })
    .catch(err => next(err));
}

import Post from 'src/models/PostModel';
import { buildGalleryPost } from 'src/server/modules/build';

export default (req, res, next) => {
  let dataRequest;
  if (req.query.page) {
    dataRequest = Post.paginate({}, {
      page: req.query.page,
      limit: req.query.limit,
      sort: '-postDate',
      select: 'title images postDate',
    })
      .then(data => data.docs)
  } else {
    dataRequest = Post
      .find({})
      .sort('-postDate')
      .select('title images postDate')
      .lean();
  }
  dataRequest
    .then((data) => {
      const posts = data.map(post => buildGalleryPost(post));
      res.send(posts.join());
    })
    .catch(err => next(err));
}

Handle paged requests differently than get-all requests


import React from 'react';
import {render} from 'react-dom';
import InfiniteCalendar, {
  Calendar,
  defaultMultipleDateInterpolation,
  withMultipleDates,
} from 'react-infinite-calendar';
import 'react-infinite-calendar/styles.css';

render(
  <InfiniteCalendar
    Component={withMultipleDates(Calendar)}
    /*
     * The `interpolateSelection` prop allows us to map the resulting state when a user selects a date.
     * By default, it adds the date to the selected dates array if it isn't already selected.
     * If the date is already selected, it removes it.
     *
     * You could re-implement this if this isn't the behavior you want.
     */
    interpolateSelection={defaultMultipleDateInterpolation}
    selected={[new Date(2017, 1, 10), new Date(2017, 1, 18), new Date()]}
  />,
  document.querySelector('#root')
);

import React from 'react';
import {render} from 'react-dom';
import InfiniteCalendar, {
  Calendar,
  defaultMultipleDateInterpolation,
  withMultipleDates,
} from 'react-infinite-calendar';
import 'react-infinite-calendar/styles.css';

const MultipleDatesCalendar = withMultipleDates(Calendar);

render(
  <InfiniteCalendar
    Component={MultipleDatesCalendar}
    /*
     * The `interpolateSelection` prop allows us to map the resulting state when a user selects a date.
     * By default, it adds the date to the selected dates array if it isn't already selected.
     * If the date is already selected, it removes it.
     *
     * You could re-implement this if this isn't the behavior you want.
     */
    interpolateSelection={defaultMultipleDateInterpolation}
    selected={[new Date(2017, 1, 10), new Date(2017, 1, 18), new Date()]}
  />,
  document.querySelector('#root')
);

Update withMultipleDates HOC example usage


describe('Login', function () {
	beforeEach(function () {
		browser.get('/login');
	});

	it('should have <my-login>', function () {
		var login = element(by.css('my-app my-login'));
		expect(login.isPresent()).toEqual(true);

		login.element(by.name("email")).sendKeys("test@gmail.com");
		login.element(by.name("password")).sendKeys("azerty1988");

		login.element(by.className("btn")).click();

		/** Ajax call login here, waiting post request finished **/
		browser.waitForAngular().then(function () {
			const success = login.element(by.className("alert-success"));
			expect(success.isPresent()).toEqual(true);
		});
	});
});

describe('Login', function () {
	beforeEach(function () {
		browser.get('/login');
	});

	it('should have <my-login>', function () {
		var login = element(by.css('my-app my-login'));
		expect(login.isPresent()).toEqual(true);

		login.element(by.name("email")).sendKeys("test@gmail.com");
		login.element(by.name("password")).sendKeys("mptest");

		login.element(by.className("btn")).click();

		/** Ajax call login here, waiting post request finished **/
		browser.waitForAngular().then(function () {
			const success = login.element(by.className("alert-success"));
			expect(success.isPresent()).toEqual(true);
		});
	});
});

Update login component test e2e


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

var settings = require('../red').settings;

var storageType = settings.storageModule || "localfilesystem";

module.exports = require("./"+storageType);

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

var settings = require('../red').settings;

var mod;

if (settings.storageModule) {
    if (typeof settings.storageModule === "string") {
        // TODO: allow storage modules to be specified by absolute path
        mod = require("./"+settings.storageModule);
    } else {
        mod = settings.storageModule;
    }
} else {
    mod = require("./localfilesystem");
}

module.exports = mod;

Allow storage module to be set explicitly


const messageHelpers = require('../lib/message_helpers');

const handler = async function(message) {
  const args = message.content.split(' ').slice(1);

  // the first argument is non-optional, fail if not provided
  if (args.length < 1) {
    await messageHelpers.sendError(message,
      'You must specify a number of messages to be deleted');
    return;
  }

  const n = Number(args[0]);
  if (isNaN(n)) {
    await messageHelpers.sendError(message, `Invalid argument for n: ${n}`);
    return;
  }

  // the user must be in a channel for this command to be useful
  if (!message.channel) {
    await messageHelpers.sendError(message,
      `You must send this command from a channel for it to be useful`);
    return;
  }

  // delete the messages
  await message.channel.bulkDelete(n);
  await message.delete();
};

module.exports = {
  bind: 'purge',
  handler: handler,
  help: 'Purge the last <n> messages from the channel this command' +
    'is invoked in.',
  administrative: true,
};

const messageHelpers = require('../lib/message_helpers');

const handler = async function(message) {
  const args = message.content.split(' ').slice(1);

  // the first argument is non-optional, fail if not provided
  if (args.length < 1) {
    await messageHelpers.sendError(message,
      'You must specify a number of messages to be deleted');
    return;
  }

  const n = Number(args[0]);
  if (isNaN(n)) {
    await messageHelpers.sendError(message, `Invalid argument for n: ${n}`);
    return;
  }

  // the user must be in a channel for this command to be useful
  if (!message.channel) {
    await messageHelpers.sendError(message,
      `You must send this command from a channel for it to be useful`);
    return;
  }

  // delete the messages
  await message.channel.bulkDelete(n);
  await message.delete();
};

module.exports = {
  bind: 'purge',
  handler: handler,
  help: 'Purge the last <n> messages from the channel this command' +
    ' is invoked in.',
  administrative: true,
};

Fix typo in help text


'use strict';

/**
 * @fileoverview Login check middleware
 */
const config = require('config');

/*
 * Check authenticated status. Redirect to login page
 *
 * @param {object} req Express HTTP request
 * @param {object} res Express HTTP response
 * @param {object} next Express callback
 *
 */
const authenticated = (req, res, next) => {
  if ((!req.isAuthenticated || !req.isAuthenticated()) && req.url !== config.authentication.login.path && !req.url.startsWith('/css') && !req.url.startsWith('/js') && !req.url.startsWith('/images') && !req.url.startsWith('/favicon')) {
    res.redirect('/login');
  }
  else {
    // consistent-return turned off because this doesn't return a value
    next(); // eslint-disable-line callback-return
  }
};

/**
 * @param {object} app - Express app
 *
 * @returns {object} app - Modified Express app
 */
const authInit = (app) => {
  return new Promise((res) => {
    app.use(authenticated);

    res(app);
  });
};

module.exports = authInit;

'use strict';

/**
 * @fileoverview Login check middleware
 */
const config = require('config');

/*
 * Check authenticated status. Redirect to login page
 *
 * @param {object} req Express HTTP request
 * @param {object} res Express HTTP response
 * @param {object} next Express callback
 *
 */
const authenticated = (req, res, next) => {
  if ((!req.isAuthenticated || !req.isAuthenticated()) && req.url !== config.authentication.login.path && !req.url.startsWith('/css') && !req.url.startsWith('/js') && !req.url.startsWith('/images') && !req.url.startsWith('/favicon') && !req.url.startsWith('/api')) {
    res.redirect('/login');
  }
  else {
    // consistent-return turned off because this doesn't return a value
    next(); // eslint-disable-line callback-return
  }
};

/**
 * @param {object} app - Express app
 *
 * @returns {object} app - Modified Express app
 */
const authInit = (app) => {
  return new Promise((res) => {
    app.use(authenticated);

    res(app);
  });
};

module.exports = authInit;

Remove login redirect for APIs


"use strict";

var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
// Thanks to:
// http://fightingforalostcause.net/misc/2006/compare-email-regex.php
// http://thedailywtf.com/Articles/Validating_Email_Addresses.aspx
// http://stackoverflow.com/questions/201323/what-is-the-best-regular-expression-for-validating-email-addresses/201378#201378
exports.validate = function(email)
{
	if (!email)
		return false;

	if(email.length>254)
		return false;

	var valid = tester.test(email);
	if(!valid)
		return false;

	// Further checking of some things regex can't handle
	var parts = email.split("@");
	if(parts[0].length>64)
		return false;

	var domainParts = parts[1].split(".");
	if(domainParts.some(function(part) { return part.length>63; }))
		return false;

	return true;
}

'use strict';

var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
// Thanks to:
// http://fightingforalostcause.net/misc/2006/compare-email-regex.php
// http://thedailywtf.com/Articles/Validating_Email_Addresses.aspx
// http://stackoverflow.com/questions/201323/what-is-the-best-regular-expression-for-validating-email-addresses/201378#201378
exports.validate = function (email) {
  if (!email) return false;

  if (email.length > 256) return false;

  if (!tester.test(email)) return false;

  // Further checking of some things regex can't handle
  var [account, address] = email.split('@');
  if (account.length > 64) return false;

  var domainParts = address.split('.');
  if (domainParts.some(function (part) {
    return part.length > 63;
  })) return false;

  return true;
};

Apply total length of RFC-2821


var Database = require('./lib/database')
var mongodb = require('mongodb')

module.exports = function (connString, cols, options) {
  var db = new Database(connString, cols, options)
  if (typeof Proxy !== 'undefined') {
    var handler = {
      get: function (obj, prop) {
        // Work around for event emitters to work together with harmony proxy
        if (prop === 'on' || prop === 'emit') {
          return db[prop].bind(db)
        }

        if (db[prop]) return db[prop]
        db[prop] = db.collection(prop)
        return db[prop]
      }
    }
    var p = Proxy.create === undefined ? new Proxy({}, handler) : Proxy.create(handler)
    return p
  }

  return db
}

// expose bson stuff visible in the shell
module.exports.Binary = mongodb.Binary
module.exports.Code = mongodb.Code
module.exports.DBRef = mongodb.DBRef
module.exports.Double = mongodb.Double
module.exports.Long = mongodb.Long
module.exports.NumberLong = mongodb.Long // Alias for shell compatibility
module.exports.MinKey = mongodb.MinKey
module.exports.MaxKey = mongodb.MaxKey
module.exports.ObjectID = mongodb.ObjectID
module.exports.ObjectId = mongodb.ObjectId
module.exports.Symbol = mongodb.Symbol
module.exports.Timestamp = mongodb.Timestamp

var Database = require('./lib/database')
var mongodb = require('mongodb')

module.exports = function (connString, cols, options) {
  var db = new Database(connString, cols, options)
  if (typeof Proxy !== 'undefined') {
    var handler = {
      get: function (obj, prop) {
        // Work around for event emitters to work together with harmony proxy
        if (prop === 'on' || prop === 'emit') {
          return db[prop].bind(db)
        }

        if (db[prop]) return db[prop]
        db[prop] = db.collection(prop)
        return db[prop]
      }
    }

    return Proxy.create === undefined ? new Proxy({}, handler) : Proxy.create(handler)
  }

  return db
}

// expose bson stuff visible in the shell
module.exports.Binary = mongodb.Binary
module.exports.Code = mongodb.Code
module.exports.DBRef = mongodb.DBRef
module.exports.Double = mongodb.Double
module.exports.Long = mongodb.Long
module.exports.NumberLong = mongodb.Long // Alias for shell compatibility
module.exports.MinKey = mongodb.MinKey
module.exports.MaxKey = mongodb.MaxKey
module.exports.ObjectID = mongodb.ObjectID
module.exports.ObjectId = mongodb.ObjectId
module.exports.Symbol = mongodb.Symbol
module.exports.Timestamp = mongodb.Timestamp

Return the created proxy instead of assigning it to a var p


"use strict";
var windows = process.platform.indexOf("win") === 0;

function clear()
{
	var i,lines;
	var stdout = "";

	if (windows === false)
	{
		stdout += "\033[2J";
	}
	else
	{
		lines = process.stdout.getWindowSize()[1];

		for (i=0; i<lines; i++)
		{
			stdout += "\r\n";
		}
	}

	// Reset cursur
	stdout += "\033[0f";

	process.stdout.write(stdout);
}

module.exports = clear;

"use strict";
var windows = process.platform.indexOf("win") === 0;

function clear()
{
	var i,lines;
	var stdout = "";

	if (windows === false)
	{
		stdout += "\x1B[2J";
	}
	else
	{
		lines = process.stdout.getWindowSize()[1];

		for (i=0; i<lines; i++)
		{
			stdout += "\r\n";
		}
	}

	// Reset cursur
	stdout += "\x1B[0f";

	process.stdout.write(stdout);
}

module.exports = clear;

Update octal literal to hex


'use strict';
var condenseKeys = require('condense-keys');
var got = require('got');

module.exports = function (buf, cb) {
	got.post('https://api.imgur.com/3/image', {
		headers: {authorization: 'Client-ID 34b90e75ab1c04b'},
		body: buf
	}, function (err, res) {
		if (err) {
			cb(err);
			return;
		}

		res = JSON.parse(res);
		res = condenseKeys(res.data);
		res.date = new Date(res.datetime * 1000);

		delete res.datetime;

		cb(null, res);
	});
};

'use strict';
var condenseKeys = require('condense-keys');
var got = require('got');

module.exports = function (buf, cb) {
	got.post('https://api.imgur.com/3/image', {
		json: true,
		headers: {authorization: 'Client-ID 34b90e75ab1c04b'},
		body: buf
	}, function (err, res) {
		if (err) {
			cb(err);
			return;
		}

		res = condenseKeys(res.data);
		res.date = new Date(res.datetime * 1000);

		delete res.datetime;

		cb(null, res);
	});
};

Use `json` option in `got`


'use strict'

var detect = require('acorn-globals');

var lastSRC = '(null)';
var lastRes = true;
var lastConstants = undefined;

module.exports = isConstant;
function isConstant(src, constants) {
  src = '(' + src + ')';
  if (lastSRC === src && lastConstants === constants) return lastRes;
  lastSRC = src;
  lastConstants = constants;
  try {
    Function('return (' + src + ')');
    return lastRes = (detect(src).filter(function (key) {
      return !constants || !(key.name in constants);
    }).length === 0);
  } catch (ex) {
    return lastRes = false;
  }
}
isConstant.isConstant = isConstant;

isConstant.toConstant = toConstant;
function toConstant(src, constants) {
  if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
  return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
    return constants[key];
  }));
}

'use strict'

var detect = require('acorn-globals');

var lastSRC = '(null)';
var lastRes = true;
var lastConstants = undefined;

module.exports = isConstant;
function isConstant(src, constants) {
  src = '(' + src + ')';
  if (lastSRC === src && lastConstants === constants) return lastRes;
  lastSRC = src;
  lastConstants = constants;
  try {
    isExpression(src);
    return lastRes = (detect(src).filter(function (key) {
      return !constants || !(key.name in constants);
    }).length === 0);
  } catch (ex) {
    return lastRes = false;
  }
}
isConstant.isConstant = isConstant;

isConstant.toConstant = toConstant;
function toConstant(src, constants) {
  if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
  return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
    return constants[key];
  }));
}

function isExpression(src) {
  try {
    eval('throw "STOP"; (function () { return (' + src + '); })()');
    return false;
  }
  catch (err) {
    return err === 'STOP';
  }
}

Use a safer test for isExpression


var digio_api = require('../digio-api')
, config = require('./config')

var api = new digio_api(config.token)

// api.droplets.list_droplets(function (err, data) {
//   console.log(data);
// });
//
// api.domains.list_all_domains(function (err, data) {
//   console.log(data);
// })
//
// api.domains.create_domain_record('tmn2.io', '191.168.0.1', function (err, data) {
//   console.log(data);
// })

var digio_api = require('../digio-api')
, config = require('./config')

var api = new digio_api(config.token)

// api.droplets.list_droplets(function (err, data) {
//   console.log(data);
// });
//
// api.domains.list(function (err, data) {
//   console.log(data);
// })
//
// api.domains.create('tmn2.io', '191.168.0.1', function (err, data) {
//   console.log(data);
// })
//
// api.domains.get('tmn.io', function (err, data) {
//   console.log(data);
// })

Add calls to domains get functionality


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

module.exports = {
  name: 'ember-cli-moment-range',

  included: function () {
    this._super.included.apply(this, arguments);

    this.import('bower_components/moment-range/dist/moment-range.min.js');
  }
};

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

module.exports = {
  name: 'ember-cli-moment-range',

  included: function (app) {
    this._super.included.apply(this, arguments);

    while (app.app) {
      app = app.app;
    }

    this.import(app.bowerDirectory + '/moment-range/dist/moment-range.min.js');
  }
};

Add safeguard for nested addons && fix import path


export default class {
  constructor (win, keys) {
    this.keys = keys
    this.win = win
    this.id = 'keypad'

    this.buttons = []
    for (var key in this.keys) {
      this.buttons[this.keys[key]] = {pressed: false}
    }

    this.onkey = function (event) {
      if (event.which in this.keys) {
        let pressed
        if (event.type === 'keyup') {
          pressed = true
        } else if (event.type === 'keydown') {
          pressed = false
        }
        this.buttons[this.keys[event.which]].pressed = pressed
        this.timestamp = event.timeStamp
        event.preventDefault()
      }
    }.bind(this)

    this.connect()
  }

  connect () {
    this.connected = true
    this.win.addEventListener('keydown', this.onkey)
    this.win.addEventListener('keyup', this.onkey)
  }

  disconnect () {
    this.connected = false
    this.win.removeEventListener('keydown', this.onkey)
    this.win.removeEventListener('keyup', this.onkey)
  }
}

export default class {
  constructor (win, keys) {
    this.keys = keys
    this.win = win
    this.id = 'keypad'
    this.mapping = 'standard'

    this.buttons = []
    for (var key in this.keys) {
      this.buttons[this.keys[key]] = {pressed: false}
    }

    this.onkey = function (event) {
      if (event.which in this.keys) {
        let pressed
        if (event.type === 'keyup') {
          pressed = true
        } else if (event.type === 'keydown') {
          pressed = false
        }
        this.buttons[this.keys[event.which]].pressed = pressed
        this.timestamp = event.timeStamp
        event.preventDefault()
      }
    }.bind(this)

    this.connect()
  }

  connect () {
    this.connected = true
    this.win.addEventListener('keydown', this.onkey)
    this.win.addEventListener('keyup', this.onkey)
  }

  disconnect () {
    this.connected = false
    this.win.removeEventListener('keydown', this.onkey)
    this.win.removeEventListener('keyup', this.onkey)
  }
}

Set mapping to standard for gamepad object.


module.exports = isbnValidator;

function isbnValidator(isbnCode){

  var self = this;

  if (typeof(isbnCode) !== "string") return false;

  self.replaceDashes = function format(sequence){
      return sequence.replace(/\-/g, '');
  }

  self.sumOfSequence = function hasRemainder(sequence){
    var numberTenInISBN = "X";
    var sum = 0;
    var factor = 10;
    var characterIndex = 0;
    var isValid = undefined;
    var sequenceEnd = sequence.length - 1;

    for(; factor > 0; factor--, characterIndex++){
      if (characterIndex === sequenceEnd &&
        sequence.charAt(characterIndex) === numberTenInISBN){
        sum += 10 * factor;
      } else {
        sum += sequence.charAt(characterIndex) * factor;
      }
    }
    return sum;
  };

  self.isCorrectLength = function correctFormat(isbn){
    return (isbn.length === 10) ? true : false;
  };

  self.validate = function validate(sequence){
    var isValid = (sumOfSequence(sequence) % 11 === 0) ? true : false;
    return isValid;
  }

  self.isbn = self.replaceDashes(isbnCode);

  if(self.isCorrectLength(self.isbn)) {
    return self.validate((self.isbn));
  } else {
    return false;
  }
};

module.exports = isbnValidator;

function isbnValidator(isbnCode){

  var self = this;

  if (typeof(isbnCode) !== "string") return false;

  self.replaceDashes = function format(sequence){
      return sequence.replace(/\-/g, '');
  }

  self.sumOfSequence = function hasRemainder(sequence){
    var numberTenInISBN = "X";
    var sum = 0;
    var factor = 10;
    var characterIndex = 0;
    var isValid = undefined;
    var sequenceEnd = sequence.length - 1;

    for(; factor > 0; factor--, characterIndex++){
      if (characterIndex === sequenceEnd &&
        sequence.charAt(characterIndex) === numberTenInISBN){
        sum += 10 * factor;
      } else {
        sum += sequence.charAt(characterIndex) * factor;
      }
    }
    return sum;
  };

  self.isCorrectFormat = function correctFormat(isbn){
    return isbn.match(/\d\d\d\d\d\d\d\d\d[0-9|xX]$/);
  };

  self.validate = function validate(sequence){
    var isValid = (sumOfSequence(sequence) % 11 === 0) ? true : false;
    return isValid;
  }

  self.isbn = self.replaceDashes(isbnCode);

  if(self.isCorrectFormat(self.isbn)) {
    return self.validate((self.isbn));
  } else {
    return false;
  }
};

Use regex search for format type