import { take, takeEvery, call, put } from 'redux-saga/effects';
import * as actions from '../actions/auth';
import * as auth from '../fetch/auth';
import { browserHistory } from 'react-router';

function* signUp(action) {
  const { name, email, password } = action;
  let response = yield call(auth.signUp, name, email, password);
  switch (response.status) {
    case 200:
      yield put(actions.signUpSuccess());
      break;
    case 409:
      yield put(actions.signUpFail("Email is already in use"));
      break;
    default:
      yield put(actions.signUpFail("Sign up failed"));
  }
  //response.json().then(data => console.log(data));
}

export function* watchSignUpRequest() {
  yield takeEvery('AUTH_SIGNUP_REQUEST', signUp);
}

import { take, takeEvery, call, put } from 'redux-saga/effects';
import * as actions from '../actions/auth';
import { authActions } from '../actions/action-types.js';
import * as auth from '../fetch/auth';
import { browserHistory } from 'react-router';

function* signUp(action) {
  const { name, email, password } = action;
  let response = yield call(auth.signUp, name, email, password);
  switch (response.status) {
    case 200:
      yield put(actions.signUpSuccess());
      break;
    case 409:
      yield put(actions.signUpFail("Email is already in use"));
      break;
    default:
      yield put(actions.signUpFail("Sign up failed"));
  }
}

export function* watchSignUpRequest() {
  yield takeEvery(authActions.AUTH_SIGNUP_REQUEST, signUp);
}

Remove commented test code from sign up saga


Extension = window.Extension || {};

Extension.LayoutView = Marionette.LayoutView.extend({
    /**
     * Removed default action on hash links
     * in order not to trigger router methods
     */
    preventHashChange: function(event) {
        var $target = $(event.currentTarget);

        if (_.string.startsWith($target.attr('href'), '#')) {
            event.preventDefault();
        }
    },

    /**
     * Open a view in a modal
     *
     * Attach a view to modal region
     * and trigger modal initialization
     */
    openModal: function(modalView, options) {
        var self = this;

        this.modal.show(modalView);

        $.magnificPopup.open(_.defaults({}, options, {
            items: {
                src: this.modal.$el,
                type: 'inline'
            },
            /*modal: true,*/
            midClick: true,
            callbacks: {
                open: function() {
                    self.modal.$el.on("click", ".mfp-close, .mfp-inline-close", function() {
                        App.layout.closeModal();
                    });

                    self.modal.$el.on("click", "a[href]", function(event) {
                        self.preventHashChange(event);
                    });
                },
                close: function() {
                    modalView.destroy();
                }
            }
        }));
    },

    /**
     * Close current modal
     *
     * A modal view class can be passed to prevent closing the wrong modal
     */
    closeModal: function(modalViewClass) {
        if (!modalViewClass || this.modal.currentView instanceof modalViewClass) {
            $.magnificPopup.close();
        }
    }
});

Extension = window.Extension || {};

Extension.LayoutView = Marionette.LayoutView.extend({
    /**
     * Removed default action on hash links
     * in order not to trigger router methods
     */
    preventHashChange: function(event) {
        var $target = $(event.currentTarget);

        if (_.string.startsWith($target.attr('href'), '#')) {
            event.preventDefault();
        }
    },

    /**
     * Open a view in a modal
     *
     * Attach a view to modal region
     * and trigger modal initialization
     */
    openModal: function(modalView, options) {
        var self = this;

        this.modal.show(modalView);

        $.magnificPopup.open(_.defaults({}, options, {
            items: {
                src: this.modal.$el,
                type: 'inline'
            },
            /*modal: true,*/
            midClick: true,
            callbacks: {
                open: function() {
                    self.modal.$el.on("click", ".mfp-close, .mfp-inline-close", function() {
                        App.layout.closeModal();
                    });

                    self.modal.$el.on("click", "a[href]", function(event) {
                        self.preventHashChange(event);
                    });
                },
                close: function() {
                    self.modal.currentView.destroy();
                }
            }
        }));
    },

    /**
     * Close current modal
     *
     * A modal view class can be passed to prevent closing the wrong modal
     */
    closeModal: function(modalViewClass) {
        if (!modalViewClass || this.modal.currentView instanceof modalViewClass) {
            $.magnificPopup.close();
        }
    }
});

Fix for close always current modal


import React from 'react'
import PropTypes from 'prop-types'

import withFetch from '../../hoc/with-fetch'

import Link from '../../link'

const SourceProducers = ({organization}) => (

    {organization.producers ?

        {organization.producers && organization.producers.length > 0} producteurs sont associés à votre organisation

          {organization.producers.map(producer => (

{producer._id}

          ))}

 :

        Aucun producteur n’est associé à votre organisation.

    }

    <Link prefetch href={`/publication/producers?oid=${organization._id}`} as={`/publication/${organization._id}/producers`}>

        Associer des producteurs

    </Link>

)

SourceProducers.propTypes = {
  organization: PropTypes.shape({
    _id: PropTypes.string.isRequired,
    producers: PropTypes.arrayOf(PropTypes.shape({
      _id: PropTypes.string.isRequired
    })).isRequired
  }).isRequired
}

export default withFetch(
  data => ({
    organization: data
  })
)(SourceProducers)

import React from 'react'
import PropTypes from 'prop-types'

import withFetch from '../../hoc/with-fetch'

import Link from '../../link'

const SourceProducers = ({organization}) => (

    {organization.producers && organization.producers.length > 0 ? (

        {organization.producers.length} producteurs sont associés à votre organisation

          {organization.producers.map(producer => (

{producer._id}

          ))}

    ) : (

        Aucun producteur n’est associé à votre organisation.

    )}

    <Link prefetch href={`/publication/producers?oid=${organization._id}`} as={`/publication/${organization._id}/producers`}>

        Associer des producteurs

    </Link>

)

SourceProducers.propTypes = {
  organization: PropTypes.shape({
    _id: PropTypes.string.isRequired,
    producers: PropTypes.arrayOf(PropTypes.shape({
      _id: PropTypes.string.isRequired
    })).isRequired
  }).isRequired
}

export default withFetch(
  data => ({
    organization: data
  })
)(SourceProducers)

Fix producer count on publication admin


/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import React, { Component, PropTypes } from 'react';
import s from './Result.scss';
import withStyles from '../../decorators/withStyles';

@withStyles(s)
class Result extends Component {

  constructor(props) {
    super(props)
  }

  static propTypes = {
    maxLines: PropTypes.number,
  };

  static defaultProps = {
    maxLines: 1,
  };

  render() {
    const result = this.props.result;
    const minutes = Number(result.startTime.slice(3,5));
    const seconds = Number(result.startTime.slice(6,8));
    const index = (minutes * 60) + seconds;
    const src = "http://localhost:8000/" + result.episode + "/" + index + ".jpg";

    return

{result.text}

  }

}

export default Result;

/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import React, { Component, PropTypes } from 'react';
import s from './Result.scss';
import withStyles from '../../decorators/withStyles';

@withStyles(s)
class Result extends Component {

  constructor(props) {
    super(props)
  }

  static propTypes = {
    maxLines: PropTypes.number,
  };

  static defaultProps = {
    maxLines: 1,
  };

  render() {
    const result = this.props.result;
    const key = result.episode + ':' + result.id
    const minutes = Number(result.startTime.slice(3,5));
    const seconds = Number(result.startTime.slice(6,8));
    const index = (minutes * 60) + seconds;
    const src = "http://localhost:8000/" + result.episode + "/" + index + ".jpg";

    return

{result.text}

  }

}

export default Result;

Use a unique key for each screenshot


import React, { Component, PropTypes } from 'react';
import { questionProps } from "../prop-types/question_props";
import SearchResult from './SearchResult';

class FormQuestionList extends Component {
  render() {
    if(!this.props.questions){
      return (

Loading...

      );
    }
    return (

        {this.props.questions.map((q, i) => {
          let source = q;
          source.responseSets = [this.props.responseSets[i]];
          return <SearchResult key={i} type='question' result={{Source: source}} currentUser={{id: -1}} />;
        })}

    );
  }
}

FormQuestionList.propTypes = {
  questions: PropTypes.arrayOf(questionProps),
  responseSets: PropTypes.array
};

export default FormQuestionList;

import React, { Component, PropTypes } from 'react';
import { questionProps } from "../prop-types/question_props";
import SearchResult from './SearchResult';

class FormQuestionList extends Component {
  render() {
    if(!this.props.questions){
      return (

Loading...

      );
    }
    return (

        {this.props.questions.map((q, i) => {
          let source = Object.assign({}, q);
          source.responseSets = [this.props.responseSets[i]];
          return <SearchResult key={i} type='question' result={{Source: source}} currentUser={{id: -1}} />;
        })}

    );
  }
}

FormQuestionList.propTypes = {
  questions: PropTypes.arrayOf(questionProps),
  responseSets: PropTypes.array
};

export default FormQuestionList;

Fix bug breaking redux store shape


function Message(queue, message, headers, deliveryInfo) {
  // Crane uses slash ('/') separators rather than period ('.')
  this.queue = deliveryInfo.routingKey.replace(/\./g, '/');
  this.headers = {};
  if (deliveryInfo.contentType) { headers['content-type'] = deliveryInfo.contentType; }
  this.body = message;

  this._amqp = { queue: queue };
}

Message.prototype.ack = function() {
  this._amqp.queue.shift();
}

module.exports = Message;

function Message(queue, message, headers, deliveryInfo) {
  // Crane uses slash ('/') separators rather than period ('.')
  this.topic = deliveryInfo.routingKey.replace(/\./g, '/');
  this.headers = {};
  if (deliveryInfo.contentType) { headers['content-type'] = deliveryInfo.contentType; }
  // TODO: only set body if it has been parsed, otherwise set `data`
  this.body = message;

  this._amqp = { queue: queue };
}

Message.prototype.ack = function() {
  this._amqp.queue.shift();
}

module.exports = Message;

Set topic rather than queue.


/**
 * Generate a blob link.
 * @param {string} file - File name
 * @param {string} contents - Contents of the file
 * @param {string} title - The title of the new issue
 * @param {string} sha - Commit where this todo was introduced
 * @param {object} config - Config object
 * @returns {string} - GitHub blob link
 */
module.exports = function generateBlobLink (context, file, contents, title, sha, config) {
  const regexFlags = config.caseSensitive ? 'g' : 'gi'
  const re = new RegExp(`${config.keyword}\\s${title}`, regexFlags)

  const {repo, owner} = context.repo()
  const lines = contents.split(/\r\n|\r|\n/)
  const totalLines = lines.length
  const start = lines.findIndex(line => line.search(re) > -1) + 1
  let end = start + config.blobLines

  // If the proposed end of range is past the last line of the file
  // make the end the last line of the file.
  if (totalLines < end) {
    end = totalLines
  }

  let range = `L${start}-L${end}`

  // Cut off blob line if the issue-to-be is on the last line
  if (totalLines === start || config.blobLines === 1) {
    range = `L${start}`
  }

  return `https://github.com/${owner}/${repo}/blob/${sha}/${file}#${range}`
}

/**
 * Generate a blob link.
 * @param {string} file - File name
 * @param {string} contents - Contents of the file
 * @param {string} title - The title of the new issue
 * @param {string} sha - Commit where this todo was introduced
 * @param {object} config - Config object
 * @returns {string} - GitHub blob link
 */
module.exports = function generateBlobLink (context, file, contents, title, sha, config) {
  const regexFlags = config.caseSensitive ? 'g' : 'gi'
  const re = new RegExp(`${config.keyword}\\s${title}`, regexFlags)

  const {repo, owner} = context.repo()
  const lines = contents.split(/\r\n|\r|\n/)
  const totalLines = lines.length
  const start = lines.findIndex(line => line.search(re) > -1) + 1
  let end = start + config.blobLines

  // If the proposed end of range is past the last line of the file
  // make the end the last line of the file.
  if (totalLines < end) {
    end = totalLines - 1
  }

  let range = `L${start}-L${end}`

  // Cut off blob line if the issue-to-be is on the last line
  if (totalLines === start || config.blobLines === 1) {
    range = `L${start}`
  }

  return `https://github.com/${owner}/${repo}/blob/${sha}/${file}#${range}`
}

Fix total lines at end


'use strict';

var nodes = require('../node');

var typeNodeMap = Object.keys(nodes).reduce(function(typeNodeMap, modelName) {
    typeNodeMap[nodes[modelName].TYPE] = nodes[modelName];
    return typeNodeMap;
}, {});

function Factory(databaseService) {
    this.databaseService = databaseService;
    this.typeNodeMap = typeNodeMap;
}

module.exports = Factory;

/**
 * This should create and validate nodes before calling callback back.
 * That includes includes creating intermediate tables for async analysis operations.
 *
 * @param {Object} definition
 * @param {Function} callback
 */
Factory.prototype.create = function (definition, callback) {
    if (!this._supportsType(definition.type)) {
        return callback(new Error('Unknown analysis type: "' + definition.type + '"'));
    }

    var Node = this.getNodeClass(definition.type);
    Node.create(definition, this, this.databaseService, function (err, node) {
        try {
            // callback might create nodes based on `node`, if it throws we should call callback again
            // TODO Not the best approach, but fair enough for now
            return callback(err, node);
        } catch (err) {
            return callback(err);
        }
    });
};

Factory.prototype._supportsType = function(nodeType) {
    return this.typeNodeMap[nodeType];
};

Factory.prototype.getNodeClass = function(nodeType) {
    return this.typeNodeMap[nodeType];
};

'use strict';

var nodes = require('../node');

var typeNodeMap = Object.keys(nodes).reduce(function(typeNodeMap, modelName) {
    typeNodeMap[nodes[modelName].TYPE] = nodes[modelName];
    return typeNodeMap;
}, {});

function Factory(databaseService) {
    this.databaseService = databaseService;
    this.typeNodeMap = typeNodeMap;
}

module.exports = Factory;

/**
 * This should create and validate nodes before calling callback back.
 * That includes includes creating intermediate tables for async analysis operations.
 *
 * @param {Object} definition
 * @param {Function} callback
 */
Factory.prototype.create = function (definition, callback) {
    if (!this._supportsType(definition.type)) {
        return callback(new Error('Unknown analysis type: "' + definition.type + '"'));
    }

    var Node = this.getNodeClass(definition.type);
    definition.params.id = definition.id;
    Node.create(definition, this, this.databaseService, function (err, node) {
        try {
            // callback might create nodes based on `node`, if it throws we should call callback again
            // TODO Not the best approach, but fair enough for now
            return callback(err, node);
        } catch (err) {
            return callback(err);
        }
    });
};

Factory.prototype._supportsType = function(nodeType) {
    return this.typeNodeMap[nodeType];
};

Factory.prototype.getNodeClass = function(nodeType) {
    return this.typeNodeMap[nodeType];
};

Append definition id as param


var iTunes = require('../')
  , conn   = null

iTunes.createConnection(onConn);

function onConn (err, c) {
  if (err) throw err;
  conn = c;
  conn.selection( onSelection );
}

function onSelection (err, selection) {
  if (err) throw err;
  if (selection.length > 0) {
    var track = selection[0];
    track.artworks(onArtworks);
  } else {
    console.log('There are no tracks currently selected...');
  }
}

function onArtworks (err, artworks) {
  if (err) throw err;
  if (artworks.length > 0) {
    console.log(artworks);
  } else {
    console.log('The selected track does not have any album artwork...');
  }
}

var iTunes = require('../')
  , conn   = null

iTunes.createConnection(onConn);

function onConn (err, c) {
  if (err) throw err;
  conn = c;
  conn.selection( onSelection );
}

function onSelection (err, selection) {
  if (err) throw err;
  if (selection.length > 0) {
    var track = selection[0];
    track.artworks(onArtworks);
  } else {
    console.log('There are no tracks currently selected...');
  }
}

function onArtworks (err, artworks) {
  if (err) throw err;
  if (artworks.length > 0) {
    console.log(artworks);
    artworks[0].data(function (err, data) {
      if (err) throw err;
      console.log(data.constructor.name);
      console.log(data.length);
    });
  } else {
    console.log('The selected track does not have any album artwork...');
  }
}

Update to artwork example to get the `data` as well.


var fs = require('fs')
  , path = require('path')
  , Sequelize = require('sequelize')
  , _ = require('lodash')
  , config = require('../../config/secrets')
  , db = {};

var sequelize = new Sequelize('heroku_d1f5087a2a4be69', 'b3b2d36bdf806e', '9df7b11c', {
  host: 'us-cdbr-iron-east-04.cleardb.net',
  dialect: 'mysql',
  pool: {
    max: 5,
    min: 0,
    idle: 10000
  },
});

fs
  .readdirSync(__dirname)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js');
  })
  .forEach(function(file) {
    var model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function(modelName) {
  if ('associate' in db[modelName]) {
    db[modelName].associate(db);
  }
});

module.exports = _.extend({
  sequelize: sequelize,
  Sequelize: Sequelize
}, db);

var fs = require('fs')
  , path = require('path')
  , Sequelize = require('sequelize')
  , _ = require('lodash')
  , config = require('../../config/secrets')
  , db = {};

var sequelize = new Sequelize('heroku_d1f5087a2a4be69', 'b3b2d36bdf806e', '9df7b11c', {
  host: 'us-cdbr-iron-east-04.cleardb.net',
  dialect: 'mysql',
  pool: {
    max: 5,
    min: 0,
    idle: 5000
  },
});

fs
  .readdirSync(__dirname)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js');
  })
  .forEach(function(file) {
    var model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function(modelName) {
  if ('associate' in db[modelName]) {
    db[modelName].associate(db);
  }
});

module.exports = _.extend({
  sequelize: sequelize,
  Sequelize: Sequelize
}, db);

Decrease idle time for connection pool.


import Ember from 'ember'
import layout from './template'

const {
  Component,
  computed
} = Ember

export default Component.extend({
  classPrefix: 'svg-icon',
  icon: null,
  layout,
  tagName: 'svg',
  classNameBindings: ['classPrefix', 'computedClassName'],

  // SVG props
  attributeBindings: [
    'version',
    'xmlns',
    'xmlns:xlink',
    'width',
    'height',
    'viewBox'
  ],
  version: '1.1',
  xmlns: 'http://www.w3.org/2000/svg',
  'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  width: '20',
  height: '20',
  viewBox: '0 0 20 20',

  // Partial
  computedClassName: computed(function() {
    return `${this.get('classPrefix')}-${this.get('icon')}`
  }),
  partialPath: computed(function() {
    return `partials/-${this.get('computedClassName')}`
  })
})

import Ember from 'ember'
import layout from './template'

const {
  Component,
  computed
} = Ember

export default Component.extend({
  classPrefix: 'svg-icon',
  icon: null,
  layout,
  tagName: 'svg',
  classNameBindings: ['classPrefix', 'computedClassName'],

  // SVG props
  attributeBindings: [
    'version',
    'xmlns',
    'xmlns:xlink',
    'width',
    'height',
    'viewBox'
  ],
  version: '1.1',
  xmlns: 'http://www.w3.org/2000/svg',
  'xmlns:xlink': 'http://www.w3.org/1999/xlink',
  width: '20',
  height: '20',
  viewBox: '0 0 20 20',

  // Partial
  computedClassName: computed('classPrefix', 'icon', function() {
    return `${this.get('classPrefix')}-${this.get('icon')}`
  }),
  partialPath: computed('computedClassName', function() {
    return `partials/-${this.get('computedClassName')}`
  })
})

Update computed properties to update when input changes


$(document).ready(function() {
  // This is called after the document has loaded in its entirety
  // This guarantees that any elements we bind to will exist on the page
  // when we try to bind to them

  // See: http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
});

$(document).ready(function() {

 	$(".login_btn").on("click", function(event){
 			event.preventDefault();
 			console.log("YOU CLICKED")
 	});

 	$(".signup_btn").on("click", function(event){
 			event.preventDefault();
 			console.log("YOU CLICKED")
 	});

});

Access login and signup btns


'use strict';
var net = require('net');
var multilevel = require('multilevel');

var db = multilevel.client();
db._separator = '~';
db._rangeLowerLimit = '!';
db._rangeUpperLimit = '~';
db._makeKey = makeKey;

var conn = net.connect({
  port: parseInt(process.env.DB_PORT_3001_TCP_PORT),
  host: process.env.DB_PORT_3001_TCP_ADDR
});

console.log('Connect to multilevel through ' + process.env.DB_PORT_3001_TCP_ADDR + ':' + process.env.DB_PORT_3001_TCP_PORT);
conn.pipe(db.createRpcStream()).pipe(conn);

module.exports = exports = db;

/**
 * makeKey -- Create a key prefix to be used by a given constructor.
 *
 * @param constructor{String} -- constructor function.
 * @return {String} -- prefix key.
 */
function makeKey(constructor) {
  return constructor.name.toLowerCase() + db._separator;
}

'use strict';
var net = require('net');
var multilevel = require('multilevel');

var db = multilevel.client();
db._separator = '~';
db._rangeLowerLimit = '!';
db._rangeUpperLimit = '~';
db._makeKey = makeKey;

var conn = net.connect({
  port: parseInt(process.env.LVLDB_PORT),
  host: process.env.LVLDB_HOST
});

console.log('Connect to multilevel through ' + process.env.LVLDB_HOST + ':' + process.env.LVLDB_PORT);
conn.pipe(db.createRpcStream()).pipe(conn);

module.exports = exports = db;

/**
 * makeKey -- Create a key prefix to be used by a given constructor.
 *
 * @param constructor{String} -- constructor function.
 * @return {String} -- prefix key.
 */
function makeKey(constructor) {
  return constructor.name.toLowerCase() + db._separator;
}

Use environment variables defined by user.


import Rx from 'rxjs/Rx';
import difference from 'lodash.difference';

/**
 * Updates widget models based on the redux store state.
 */
export class ModelUpdater {

  /**
   * Public constructor.
   * @param  {Redux.store} store
   * @param  {widgets.WidgetManager} manager
   * @return {ModelUpdater}
   */
  constructor(store, manager) {
    // Listen for changes to the redux store widgets
    Rx.Observable.from(store)
      .pluck('document')
      .map(document => document.get('widgetModels'))
      .distinctUntilChanged((a, b) => !a || a.equals(b))
      .subscribe(this.reduxStateChange.bind(this, manager));
  }

  /**
   * Update the widget models based on the state store change
   * @param  {widgets.WidgetManager} manager
   * @param  {object} newState - state of the widgets key
   */
  reduxStateChange(manager, newState) {
    if (!newState) return;

    // Delete widgets that no longer exist in the state.
    manager.deleteModels(
      difference(Object.keys(manager.modelPromises), newState.keySeq().toJS())
    );

    // Set new states
    newState.entrySeq().forEach(modelState => {
      const [model, state] = modelState;
      manager.setModelState(model, state.toJS());
    });
  }
}

import Rx from 'rxjs/Rx';
import difference from 'lodash.difference';

/**
 * Updates widget models based on the redux store state.
 */
export class ModelUpdater {

  /**
   * Public constructor.
   * @param  {Redux.store} store
   * @param  {widgets.WidgetManager} manager
   * @return {ModelUpdater}
   */
  constructor(store, manager) {
    // Listen for changes to the redux store widgets
    this.widgetSubscriptions = {};
    Rx.Observable.from(store)
      .pluck('document')
      .map(document => document.get('widgetModels'))
      .distinctUntilChanged((a, b) => !a || a.equals(b))
      .subscribe(this.reduxStateChange.bind(this, store, manager));
  }

  /**
   * Update the widget models based on the state store change
   * @param  {widgets.WidgetManager} manager
   * @param  {object} newState - state of the widgets key
   */
  reduxStateChange(store, manager, newState) {
    if (!newState) return;

    // Delete widgets that no longer exist in the state.
    const deleted = difference(
      Object.keys(manager.modelPromises),
      newState.keySeq().toJS()
    );
    manager.deleteModels(deleted);
    deleted.forEach(id => {
      this.widgetSubscriptions[id].unsubscribe();
      delete this.widgetSubscriptions[id];
    });

    // Create missing state subscriptions.
    const created = difference(
      newState.keySeq().toJS(),
      Object.keys(this.widgetSubscriptions)
    );
    created.forEach(id => {
      this.widgetSubscriptions[id] = Rx.Observable.from(store)
        .pluck('document')
        .map(document => document.getIn(['widgetModels', id]))
        .distinctUntilChanged((a, b) => !a || a.equals(b))
        .subscribe(state => {
          manager.setModelState(id, state.toJS());
        });
    });
  }
}

Make the redux -> widgets logic a little bit smarter


define([

  'Backbone',

  //Template
  'text!templates/topNavTemplate.html'
], function(

  Backbone,

  //Template
  topNavTemplate

){

  var TopNavView = Backbone.View.extend({

    events: {
      'click a'       : 'preventDefault',
      'click .logout' : 'logout',
      'click .login'  : 'login'
    },

    template: _.template(topNavTemplate),

    initialize: function(){
      this.model.bind('change', this.render, this);
    },

    preventDefault: function(event){
      event.preventDefault();
    },

    logout: function(){
      this.model.logout();
    },

    login: function(){
      this.model.clear({silent: true});
      var form = $('#login-form').serialize();
      this.model.login(form);
    },

    render: function(){
      this.$el.html(this.template(this.model.toJSON()));
    }
  });

  return TopNavView;

});

define([

  'Backbone',

  //Template
  'text!templates/topNavTemplate.html'
], function(

  Backbone,

  //Template
  topNavTemplate

){

  var TopNavView = Backbone.View.extend({

    events: {
      'click a'       : 'preventDefault',
      'click .logout' : 'logout',
      'click .login'  : 'login'
    },

    template: _.template(topNavTemplate),

    initialize: function(){
      this.model.bind('change', this.render, this);
    },

    preventDefault: function(event){
      event.preventDefault();
    },

    logout: function(){
      var router = this.options.router;
      this.model.logout(function(){
        router.navigate('', true);
      });
    },

    login: function(){
      this.model.clear({silent: true});
      var form = $('#login-form').serialize();
      this.model.login(form);
    },

    render: function(){
      this.$el.html(this.template(this.model.toJSON()));
    }
  });

  return TopNavView;

});

Send user to home page wen logging out


// @flow

import * as React from 'react'
import moment from 'moment-timezone'
import {Alert} from 'react-native'
import {Column, Row} from '../components/layout'
import {ListRow, Detail, Title} from '../components/list'
import type {UpdateType} from './types'

type Props = {
	onPress: string => any,
	update: UpdateType,
}

export class UpdatesRow extends React.PureComponent<Props> {
	_onPress = () => {
		if (!this.props.update.link) {
			Alert.alert('There is nowhere to go for this update')
			return
		}
		this.props.onPress(this.props.update.link)
	}

	render() {
		const {update} = this.props

		const posted = moment
			.utc(update.datePublished)
			.utcOffset(-6)
			.format('MMM Do YYYY')

		return (
			<ListRow arrowPosition="center" onPress={this._onPress}>
				<Row alignItems="center">
					<Column flex={1}>
						<Title lines={2}>{posted}</Title>
						<Detail lines={3}>{update.title}</Detail>
					</Column>
				</Row>
			</ListRow>
		)
	}
}

// @flow

import * as React from 'react'
import moment from 'moment-timezone'
import {Alert} from 'react-native'
import {Column, Row} from '../components/layout'
import {ListRow, Detail, Title} from '../components/list'
import type {UpdateType} from './types'

type Props = {
	onPress: string => any,
	update: UpdateType,
}

const TIMEZONE = 'America/Winnipeg'

export class UpdatesRow extends React.PureComponent<Props> {
	_onPress = () => {
		if (!this.props.update.link) {
			Alert.alert('There is nowhere to go for this update')
			return
		}
		this.props.onPress(this.props.update.link)
	}

	calculateFromNow = (publishedOffset: moment) => {
		const today = moment
			.utc()
			.tz(TIMEZONE)
			.startOf('day')

		if (today.isAfter(publishedOffset, 'day')) {
			return moment.duration(publishedOffset - today).humanize(true)
		}

		return publishedOffset.fromNow()
	}

	render() {
		const {update} = this.props

		const publishedOffset = moment.utc(update.datePublished).tz(TIMEZONE)

		const posted = publishedOffset.format('MMMM D, YYYY')
		const fromNow = this.calculateFromNow(publishedOffset)

		return (
			<ListRow arrowPosition="center" onPress={this._onPress}>
				<Row alignItems="center">
					<Column flex={1}>
						<Title lines={1}>{`${posted} (${fromNow})`}</Title>
						<Detail lines={3}>{update.title}</Detail>
					</Column>
				</Row>
			</ListRow>
		)
	}
}

Adjust posted and add fromNow calculation


'use strict';

angular.module('confRegistrationWebApp')
  .controller('EditAnswerDialogCtrl', function ($scope, $modalInstance) {
    $scope.close = function () {
      $modalInstance.close('');
    };

    $scope.submit = function () {
      $modalInstance.close();
    };
  });

'use strict';

angular.module('confRegistrationWebApp')
  .controller('EditAnswerDialogCtrl', function ($scope, $modalInstance, AnswerCache) {
	$scope.originalAnswer = angular.copy($scope.answer);
    $scope.close = function () {
      $scope.$parent.answer = angular.copy($scope.originalAnswer);
      $modalInstance.close('');
    };

    $scope.submit = function () {
      $modalInstance.close();
    };
  });

Enable cancel button to revert the answer back to its previous value


'use strict';

const tls = require('tls');
const Promise = require('bluebird');
const moment = require('moment');

const SITES = ['porybox.com', 'hq.porygon.co'];

module.exports = {
  period: 60 * 60 * 24,
  concurrent: true,
  task () {
    return Promise.reduce(SITES, (messages, host) => {
      return getSecureSocket(host).then(socket => {
        if (!socket.authorized) {
          return messages.concat(`Warning: The site ${host} has an invalid SSL certificate. SSL error: ${socket.authorizationError}`);
        }
        const expirationTime = moment.utc(socket.getPeerCertificate().valid_to, 'MMM D HH:mm:ss YYYY');
        if (expirationTime < moment().add({days: 14})) {
          return messages.concat(`Warning: The SSL certificate for ${host} expires ${expirationTime.fromNow()}`);
        }
        return messages;
      });
    }, []);
  }
};

function getSecureSocket(host) {
  return new Promise(resolve => {
    const socket = tls.connect({host, port: 443, rejectUnauthorized: false}, () => resolve(socket));
  });
}

'use strict';

const tls = require('tls');
const Promise = require('bluebird');
const moment = require('moment');

const SITES = ['porybox.com', 'hq.porygon.co', 'modapps.porygon.co'];

module.exports = {
  period: 60 * 60 * 24,
  concurrent: true,
  task () {
    return Promise.reduce(SITES, (messages, host) => {
      return getSecureSocket(host).then(socket => {
        if (!socket.authorized) {
          return messages.concat(`Warning: The site ${host} has an invalid SSL certificate. SSL error: ${socket.authorizationError}`);
        }
        const expirationTime = moment.utc(socket.getPeerCertificate().valid_to, 'MMM D HH:mm:ss YYYY');
        if (expirationTime < moment().add({days: 14})) {
          return messages.concat(`Warning: The SSL certificate for ${host} expires ${expirationTime.fromNow()}`);
        }
        return messages;
      });
    }, []);
  }
};

function getSecureSocket(host) {
  return new Promise(resolve => {
    const socket = tls.connect({host, port: 443, rejectUnauthorized: false}, () => resolve(socket));
  });
}

Add new subdomain to cert checker


import React from "react";
import {IndexLink, Link} from 'react-router';

export default class Header extends React.Component {
  render() {
    return (

Redux universal boilerplate

          <IndexLink to="/" className="item" activeClassName="active">Homepage</IndexLink>
          <Link to="/trends" className="item" activeClassName="active">Github trends</Link>
          <Link to="/about" className="item" activeClassName="active">About</Link>
          <Link to="/login" className="item right" activeClassName="active">Login</Link>

    );
  }
}

import React from "react";
import {IndexLink, Link} from 'react-router';
import {connect} from 'react-redux';

@connect(
  state => ({
    loggedIn: state.auth.loggedIn
  })
)
export default class Header extends React.Component {
  render() {

    let links = [
      {
        to: '/trends',
        title: 'Github trends'
      },
      {
        to: '/about',
        title: 'About'
      },
    ];

    if (! this.props.loggedIn) {
      links.push({
        to: '/login',
        title: 'Login'
      });
    } else {
      links.push({
        to: '/profile',
        title: 'Profile'
      });
      links.push({
        to: '/logout',
        title: 'Logout'
      });
    }

    return (

Redux universal boilerplate

          <IndexLink to="/" className="item" activeClassName="active">Homepage</IndexLink>
          {
            links.map(function(link) {
              return <Link to={link.to} className="item" activeClassName="active">{link.title}</Link>
            })
          }

    );
  }
}

Add links dependent on `loggedIn`


Components.utils.import('resource://greasemonkey/util.js');

const EXPORTED_SYMBOLS = ['timeout'];

function timeout(aCallback, aDelay, aType) {
  var type = aType;
  if ('undefined' == typeof type) {
    type = Components.interfaces.nsITimer.TYPE_ONE_SHOT;
  }

  // Create the timer object.
  var timer = Components.classes["@mozilla.org/timer;1"]
      .createInstance(Components.interfaces.nsITimer);
  // Init the callback, with a closure reference to the timer, so that it is
  // not garbage collected before it fires.
  timer.init(
      {
        'observe': function() {
          timer;  // Here's the reference that keeps the timer alive!
          aCallback();
        }
      },
      aDelay, type);
}

const EXPORTED_SYMBOLS = ['timeout'];

function timeout(aCallback, aDelay) {
  // Create the timer object.
  var timer = Components.classes["@mozilla.org/timer;1"]
      .createInstance(Components.interfaces.nsITimer);

  // The timer object may be garbage collected before it fires, so we need to
  // keep a reference to it alive (https://bugzil.la/647998).
  // However, simply creating a closure over the timer object without using it
  // may not be enough, as the timer might get optimized out of the closure
  // scope (https://bugzil.la/640629#c9). To work around this, the timer object
  // is explicitly stored as a property of the observer.
  var observer = {
    'observe': function() {
      delete observer.timer;
      aCallback();
    },
    'timer': timer
  };

  timer.init(observer, aDelay, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

Use more robust way to keep the timer reference alive.


import React from 'react'
import InventoryContainer from '../containers/InventoryContainer'
import ShadowBox from '../Components/ShadowBox'
import FlexDiv from '../Components/FlexDiv'

const InventoryScene = props => {

  const maltColumns = [
    {name: "Malt Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  const yeastColumns = [
    {name: "Yeast Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  const hopColumns = [
    {name: "Hop Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  return(
    <FlexDiv>
      <ShadowBox>
        <InventoryContainer name="malt" columns={maltColumns} />
      </ShadowBox>
      <ShadowBox>
        <InventoryContainer name="yeast" columns={yeastColumns} />
      </ShadowBox>
      <ShadowBox>
        <InventoryContainer name="hops" columns={hopColumns} />
      </ShadowBox>
    </FlexDiv>
  )
}

export default InventoryScene

import React from 'react'
import InventoryContainer from '../containers/InventoryContainer'
import ShadowBox from '../Components/ShadowBox'
import FlexDiv from '../Components/FlexDiv'

const InventoryBox = ShadowBox.extend`
  width:100%;
  margin:10px;
`

const InventoryDiv = FlexDiv.extend`
  width:50%;
  margin-top: 50px;
`

const InventoryScene = props => {

  const maltColumns = [
    {name: "Malt Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  const yeastColumns = [
    {name: "Yeast Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  const hopColumns = [
    {name: "Hop Name", type: "text"},
    {name: "Amount", type: "number"}
  ]

  return(
    <InventoryDiv>
      <InventoryBox>
        <InventoryContainer name="malt" columns={maltColumns} />
      </InventoryBox>
      <InventoryBox>
        <InventoryContainer name="yeast" columns={yeastColumns} />
      </InventoryBox>
      <InventoryBox>
        <InventoryContainer name="hops" columns={hopColumns} />
      </InventoryBox>
    </InventoryDiv>
  )
}

export default InventoryScene

Add styles to Inventory Scene


var path = require('path');
var browserSync = require('browser-sync');
var gulp = require('gulp');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');

var webpackConfig = require(path.resolve('webpack.config.js'));
var bundler = webpack(webpackConfig);

gulp.task('browserSync', function() {
  browserSync({
    server: {
      baseDir: 'build'
    },
    files: [
      'build/**/*.html',
      'build/**/*.js'
    ],
    // browser: 'google chrome canary',
    browser: 'google-chrome',
    middleware: [
        webpackDevMiddleware(bundler, {
          // IMPORTANT: dev middleware can't access config, so we should
          // provide publicPath by ourselves
          publicPath: webpackConfig.output.publicPath,

          // pretty colored output
          stats: { colors: true }

          // for other settings see
          // http://webpack.github.io/docs/webpack-dev-middleware.html
        }),

        // bundler should be the same as above
        webpackHotMiddleware(bundler)
      ]
  });
});

var path = require('path');
var browserSync = require('browser-sync');
var gulp = require('gulp');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');

var webpackConfig = require(path.resolve('webpack.config.js'));
var bundler = webpack(webpackConfig);

gulp.task('browserSync', function() {
  browserSync({
    server: {
      baseDir: 'build'
    },
    files: [
      'build/**/*.html',
      'build/**/*.js'
    ],
    browser: 'google chrome canary',
    middleware: [
        webpackDevMiddleware(bundler, {
          // IMPORTANT: dev middleware can't access config, so we should
          // provide publicPath by ourselves
          publicPath: webpackConfig.output.publicPath,

          // pretty colored output
          stats: { colors: true }

          // for other settings see
          // http://webpack.github.io/docs/webpack-dev-middleware.html
        }),

        // bundler should be the same as above
        webpackHotMiddleware(bundler)
      ]
  });
});

Reset browser sync to Google Chrome Canary


import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';

import { registerUser } from '../../../../redux/actions';
import styles from './styles.css';

class SignupPage extends Component {
    constructor() {
        super();
        this._submit = this._submit.bind(this);
    }

    _submit(data) {
        const { dispatch, history } = this.props;
        dispatch(registerUser(data))
            .then(() => {
                history.push('/login');
            });
    }

    render() {
        const { handleSubmit, signUpError } = this.props;

        return (

                    <form onSubmit={ handleSubmit(this._submit) }>

Email

                        <Field
                            className="signup__input"
                            name="email"
                            component="input"
                            type="text" />

Password

                        <Field
                            className="signup__input"
                            name="password"
                            component="input"
                            type="password" />
                        <button type="submit" className="signup__submit">Create</button>
                        { signUpError &&
{ signUpError }
 }
                    </form>

        );
    }
}

export default withRouter(connect(
    ({ root }) => ({ signUpError: root.signUpError })
)(reduxForm({ form: 'signup' })(SignupPage)));

import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';

import { registerUser } from '../../../../redux/actions';
import styles from './styles.css';

class SignupPage extends Component {
    constructor() {
        super();
        this._submit = this._submit.bind(this);
    }

    _submit(data) {
        const { dispatch, history } = this.props;
        dispatch(registerUser(data))
            .then(() => {
                // TODO: don't redirect if there was an error signing up for account
                history.push('/login');
            });
    }

    render() {
        const { handleSubmit, signUpError } = this.props;

        return (

                    <form onSubmit={ handleSubmit(this._submit) }>

Email

                        <Field
                            className="signup__input"
                            name="email"
                            component="input"
                            type="text" />

Password

                        <Field
                            className="signup__input"
                            name="password"
                            component="input"
                            type="password" />
                        <button type="submit" className="signup__submit">Create</button>
                        { signUpError &&
{ signUpError }
 }
                    </form>

        );
    }
}

export default withRouter(connect(
    ({ root }) => ({ signUpError: root.signUpError })
)(reduxForm({ form: 'signup' })(SignupPage)));

Add TODO for handling failed signup


var Bob = require('./bob');

describe("Bob", function() {
  var bob = new Bob();

  it("stating something", function() {
    var result = bob.hey('Tom-ay-to, tom-aaaah-to.');
    expect(result).toEqual('Whatever.');
  });

  xit("shouting", function() {
    var result = bob.hey('WATCH OUT!');
    expect(result).toEqual('Woah, chill out!');
  });

  xit("asking a question", function() {
    var result = bob.hey('Does this cryogenic chamber make me look fat?');
    expect(result).toEqual('Sure.');
  });

  xit("talking forcefully", function() {
    var result = bob.hey("Let's go make out behind the gym!");
    expect(result).toEqual('Whatever.');
  });

  xit("shouting numbers", function() {
    var result = bob.hey('1, 2, 3 GO!');
    expect(result).toEqual('Woah, chill out!');
  });

  xit("shouting with special characters", function() {
    var result = bob.hey('ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!');
    expect(result).toEqual('Woah, chill out!');
  });

  xit("silence", function() {
    var result = bob.hey('');
    expect(result).toEqual('Fine, be that way!');
  });

});

Fix "ReferenceError: Bob is not defined" for correct use of module.exports


(function(d3, fc, sc) {
    'use strict';

    sc.chart.xAxis = function() {

        var xAxisHeight = 23;
        var xScale = fc.scale.dateTime();
        var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient('bottom');

        var dataJoin = fc.util.dataJoin()
            .selector('g.x-axis')
            .element('g')
            .attr('class', 'x-axis');

        function preventTicksMoreFrequentThanPeriod(period) {
            var scaleTickSeconds = (xScale.ticks()[1] - xScale.ticks()[0]) / 1000;
            if (scaleTickSeconds < period.seconds) {
                xAxis.ticks(period.d3TimeInterval.unit, period.d3TimeInterval.value);
            } else {
                xAxis.ticks(6);
            }
        }

        function xAxisChart(selection) {
            selection.each(function(model) {
                var container = d3.select(this);

                var xAxisContainer = dataJoin(container, [model])
                    .layout({
                        position: 'absolute',
                        left: 0,
                        bottom: 0,
                        right: 0,
                        height: xAxisHeight
                    });

                container.layout();

                xScale.range([0, xAxisContainer.layout('width')])
                    .domain(model.viewDomain);

                preventTicksMoreFrequentThanPeriod(model.period);

                xAxisContainer.call(xAxis);
            });
        }

        return xAxisChart;
    };
})(d3, fc, sc);

(function(d3, fc, sc) {
    'use strict';

    sc.chart.xAxis = function() {

        var xAxisHeight = 23;
        var yAxisWidth = 60;
        var xScale = fc.scale.dateTime();
        var xAxis = d3.svg.axis()
            .scale(xScale)
            .orient('bottom');

        var dataJoin = fc.util.dataJoin()
            .selector('g.x-axis')
            .element('g')
            .attr('class', 'x-axis');

        function preventTicksMoreFrequentThanPeriod(period) {
            var scaleTickSeconds = (xScale.ticks()[1] - xScale.ticks()[0]) / 1000;
            if (scaleTickSeconds < period.seconds) {
                xAxis.ticks(period.d3TimeInterval.unit, period.d3TimeInterval.value);
            } else {
                xAxis.ticks(6);
            }
        }

        function xAxisChart(selection) {
            selection.each(function(model) {
                var container = d3.select(this);

                var xAxisContainer = dataJoin(container, [model])
                    .layout({
                        position: 'absolute',
                        left: 0,
                        bottom: 0,
                        right: yAxisWidth,
                        height: xAxisHeight
                    });

                container.layout();

                xScale.range([0, xAxisContainer.layout('width')])
                    .domain(model.viewDomain);

                preventTicksMoreFrequentThanPeriod(model.period);

                xAxisContainer.call(xAxis);
            });
        }

        return xAxisChart;
    };
})(d3, fc, sc);

Add correct margin to x-axis


/* Account login state manager
 */

import _ from 'lodash'
import db from './db'
import { storeKey } from './config'

export default setUser

function setUser(user) {
  storeKey('user', user.id)
  storeKey('remote', user.couch.url)
  const opts = { include_docs: true }
  return db.allDocs(opts)
  .then(rslt => {
    const author = { user: user.id }
    const docs = _.map(rslt.rows, 'doc')
    const oldDocs = _.reject(docs, author)
    const newDocs = oldDocs.map(e => _.merge(e, author))
    return db.bulkDocs(newDocs)
  })
}

/* Account login state manager
 */

import _ from 'lodash'
import db from './db'
import { storeKey } from './config'

export default setUser

function setUser(user) {
  storeKey('user', user.id)
  storeKey('remote', user.couch.url)
  const opts = { include_docs: true }
  return db.allDocs(opts)
  .then(rslt => {
    const author = { user: user.id }
    const docs = _.chain(rslt.rows)
      .map('doc')
      .reject(author)
      .map(e => _.merge(e, author))
      .value()
    return db.bulkDocs(docs)
  })
}

Use a chain instead of named variables


import Ember from 'ember';

/**
 * @module ivy-tree
 */

/**
 * @class IvyTreeComponent
 * @namespace IvyTree
 * @extends Ember.Component
 */
export default Ember.Component.extend({
  attributeBindings: ['aria-hidden', 'role'],
  classNames: ['ivy-tree-group'],
  tagName: 'ul',

  init: function() {
    this._super();
    Ember.run.once(this, this._registerWithTreeItemContainer);
  },

  willDestroy: function() {
    this._super();
    Ember.run.once(this, this._unregisterWithTreeItemContainer);
  },

  'aria-hidden': Ember.computed(function() {
    return (!this.get("treeItemContainer.isExpanded")) + ''; // coerce to 'true' or 'false'
  }).property("treeItemContainer.isExpanded"),

  role: 'group',

  /**
   * The `ivy-tree-item` component in which the group is defined.
   *
   * @property treeItemContainer
   * @type IvyTree.IvyTreeItemComponent
   * @readOnly
   */
  treeItemContainer: Ember.computed.alias('parentView').readOnly(),

  _registerWithTreeItemContainer: function() {
    this.get('treeItemContainer').registerTreeGroup(this);
  },

  _unregisterWithTreeItemContainer: function() {
    this.get('treeItemContainer').unregisterTreeGroup(this);
  }
});

import Ember from 'ember';

/**
 * @module ivy-tree
 */

/**
 * @class IvyTreeGroupComponent
 * @namespace IvyTree
 * @extends Ember.Component
 */
export default Ember.Component.extend({
  attributeBindings: ['aria-hidden', 'role'],
  classNames: ['ivy-tree-group'],
  tagName: 'ul',

  init: function() {
    this._super();
    Ember.run.once(this, this._registerWithTreeItemContainer);
  },

  willDestroy: function() {
    this._super();
    Ember.run.once(this, this._unregisterWithTreeItemContainer);
  },

  'aria-hidden': Ember.computed(function() {
    return (!this.get("treeItemContainer.isExpanded")) + ''; // coerce to 'true' or 'false'
  }).property("treeItemContainer.isExpanded"),

  role: 'group',

  /**
   * The `ivy-tree-item` component in which the group is defined.
   *
   * @property treeItemContainer
   * @type IvyTree.IvyTreeItemComponent
   * @readOnly
   */
  treeItemContainer: Ember.computed.alias('parentView').readOnly(),

  _registerWithTreeItemContainer: function() {
    this.get('treeItemContainer').registerTreeGroup(this);
  },

  _unregisterWithTreeItemContainer: function() {
    this.get('treeItemContainer').unregisterTreeGroup(this);
  }
});

Correct the class name.


import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { translate as $t, formatDate } from '../../helpers';
import { actions, get } from '../../store';

const Export = connect(
    (state, props) => {
        let access = get.accessById(state, props.account.bankAccess);
        let canBeSynced = !get.bankByUuid(state, access.bank).deprecated && access.enabled;
        return {
            canBeSynced
        };
    },
    (dispatch, ownProps) => {
        return {
            handleSync: () => {
                actions.runOperationsSync(dispatch, ownProps.account.bankAccess);
            }
        };
    }
)(props => {
    const lastSyncText = (

            {$t('client.operations.last_sync')}

            {formatDate.fromNow(props.account.lastChecked).toLowerCase()}

    );

    if (props.canBeSynced) {
        return (
            <button type="button" onClick={props.handleSync} className="btn transparent">
                {lastSyncText}

            </button>
        );
    }

    return lastSyncText;
});

Export.propTypes = {
    // Account to be resynced
    account: PropTypes.object.isRequired
};

export default Export;

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { translate as $t, formatDate } from '../../helpers';
import { actions, get } from '../../store';

const Export = connect(
    (state, props) => {
        let access = get.accessById(state, props.account.bankAccess);
        let canBeSynced = !get.bankByUuid(state, access.bank).deprecated && access.enabled;
        return {
            canBeSynced
        };
    },
    (dispatch, ownProps) => {
        return {
            handleSync: () => {
                actions.runOperationsSync(dispatch, ownProps.account.bankAccess);
            }
        };
    }
)(props => {
    const lastSyncText = (

            {$t('client.operations.last_sync')}

            {formatDate.fromNow(props.account.lastChecked).toLowerCase()}

    );

    if (props.canBeSynced) {
        return (
            <button
                type="button"
                onClick={props.handleSync}
                title={$t('client.operations.sync_now')}
                aria-label={$t('client.operations.sync_now')}
                className="btn transparent">
                {lastSyncText}

            </button>
        );
    }

    return lastSyncText;
});

Export.propTypes = {
    // Account to be resynced
    account: PropTypes.object.isRequired
};

export default Export;

Add title text to sync button


Change default storage to file


/*
 * This file contains methods to print message on console.
 *
 * @author Laurent Jourdren
 */

/*
 * Print a variable on stdout.
 * @param obj variable to print.
 * @return nothing
 */
function print(obj) {

  var nividicNames = JavaImporter();
  nividicNames.importPackage(Packages.fr.ens.transcriptome.nividic.js);

  with(nividicNames) {

    if (obj==null) {
      return;
    }

    if (obj instanceof Function) {
       Echo.out.print("Function");
       return;
    }

    if (obj instanceof Array) {

      if (obj.length>0 && (obj[0].construtor == Boolean || obj[0].constructor == Number)) {

        Echo.out.print(obj);
        return;
      }

      Echo.out.printArray(obj);
      return;
    }

    Echo.out.print(obj);
  }
}

/*
 * Print a variable on stdout with a return carriage at the end of the output.
 * @param obj variable to print.
 * @return nothing
 */
function println(obj) {

  print(obj);
  print("\n");
}

/*
 * This file contains methods to print message on console.
 *
 * @author Laurent Jourdren
 */

/*
 * Print a variable on stdout.
 * @param obj variable to print.
 * @return nothing
 */
function print(obj) {

  var nividicNames = JavaImporter();
  nividicNames.importPackage(Packages.fr.ens.transcriptome.nividic.js);

  with(nividicNames) {

    if (obj==null) {
      return;
    }

    if (obj instanceof Function) {
       Echo.out.print("Function");
       return;
    }

    if (obj instanceof Array) {

      if (obj.length>0 && (obj[0].construtor == Boolean || obj[0].constructor == Number)) {

        Echo.out.print(obj);
        return;
      }

     if (isNativeObjectArray(obj)) {
       Echo.out.printObjectArray(obj);
       return;
     }

     if (isNativePrimitiveArray(obj)) {
       Echo.out.printPrimitiveArray(obj);
       return;
     }
      return;
    }

    Echo.out.print(obj);
  }
}

/*
 * Print a variable on stdout with a return carriage at the end of the output.
 * @param obj variable to print.
 * @return nothing
 */
function println(obj) {

  print(obj);
  print("\n");
}

Fix print bug for Java primitive native array


#!/usr/bin/env node

/*global nodeca*/

'use strict';

// Create and run app
var app = require('nlib').Application.create({
  name: 'nodeca',
  root: __dirname
});

//
// Preset application version
//

nodeca.runtime.version = require('./package.json').version;

//
// Catch unexpected exceptions
//

process.on('uncaughtException', function (err) {
  nodeca.logger.warn('Uncaught exception');
  nodeca.logger.error(err.stack || err.message || err.toString());
});

//
// Handle SIGnals
//

function shutdown_gracefully() {
  nodeca.logger.info('Shutting down...');
  process.exit(0);
}

// shutdown gracefully on SIGTERM :
process.on('SIGTERM', shutdown_gracefully);
process.on('SIGINT',  shutdown_gracefully);

//
// Run application
//

app.run();

#!/usr/bin/env node

/*global nodeca*/

'use strict';

// Create and run app
var app = require('nlib').Application.create({
  name: 'nodeca',
  root: __dirname
});

//
// Preset application version
//

nodeca.runtime.version = require('./package.json').version;

//
// Catch unexpected exceptions
//

process.on('uncaughtException', function (err) {
  try {
    nodeca.logger.warn('Uncaught exception');
    nodeca.logger.error(err.stack || err.message || err.toString());
  } catch (err) {
    // THIS SHOULD NEVER-EVER-EVER HAPPEN -- THIS IS A WORST CASE SCENARIO
    // USAGE: ./nodeca.js 2>/var/log/nodeca-cf.log
    console.error(err.stack || err.toString());
  }
});

//
// Handle SIGnals
//

function shutdown_gracefully() {
  nodeca.logger.info('Shutting down...');
  process.exit(0);
}

// shutdown gracefully on SIGTERM :
process.on('SIGTERM', shutdown_gracefully);
process.on('SIGINT',  shutdown_gracefully);

//
// Run application
//

app.run();

Add try/catch into unhandled exception handler


/*global Ember */

var trailingHistory = Ember.HistoryLocation.extend({
    setURL: function (path) {
        var state = this.getState();
        path = this.formatURL(path);
        path = path.replace(/\/?$/, '/');

        if (state && state.path !== path) {
            this.pushState(path);
        }
    }
});

var registerTrailingLocationHistory = {
    name: 'registerTrailingLocationHistory',

    initialize: function (container, application) {
        application.register('location:trailing-history', trailingHistory);
    }
};

export default registerTrailingLocationHistory;

/*global Ember */

var trailingHistory = Ember.HistoryLocation.extend({
    formatURL: function () {
        return this._super.apply(this, arguments).replace(/\/?$/, '/');
    }
});

var registerTrailingLocationHistory = {
    name: 'registerTrailingLocationHistory',

    initialize: function (container, application) {
        application.register('location:trailing-history', trailingHistory);
    }
};

export default registerTrailingLocationHistory;

Fix trailing slashes output app-wide


var log4js = require('log4js'),
    logger = log4js.getLogger('VirtualBox');

logger.setLevel('INFO');

module.exports = logger;

var log4js = require('log4js'),
    logger = log4js.getLogger('VirtualBox');

logger.level = 'INFO';

module.exports = logger;

Add fix for loggerjs breaking change.


import Ember from 'ember';
import layout from '../templates/components/smd-selector-button';
import Setup from '../mixins/setup';

var get = Ember.get;

export default Ember.Component.extend(Setup, {

  layout: layout,

  tagName: 'button',
  classNames: ['datetime-picker-button'],
  classNameBindings: ['selected'],

  selected: Ember.computed('selectedValue', 'params', function(){
    return get(this, 'selectedValue') == get(this, 'params');
  }),

  click: function() {
    get(this, 'for').send(get(this, 'action'), get(this, 'params'));
  },

  displayValue: Ember.computed('label', function() {
    console.log('test');
    var label = get(this, 'label');
    var leftPad = get(this, 'leftPad');
    if (label < 10 && leftPad) {
      return '0' + label;
    } else {
      return label;
    }
  }),

  registerComponent: Ember.on('didInsertElement', function () {
    if (this.get('selected')) {
      this.attrs.onRegister(this.get('elementId'), this);
    }
  }),

  actions: {

    changeYear: function(delta) {
      get(this, 'parent').send('changeYear', delta);
    },

    changeMonth: function(delta) {
      get(this, 'parent').send('changeMonth', delta);
    },

    toggleMonthView: function() {
      get(this, 'parent').send('toggleMonthView')
    }

  }

});

import Ember from 'ember';
import layout from '../templates/components/smd-selector-button';
import Setup from '../mixins/setup';

var get = Ember.get;

export default Ember.Component.extend(Setup, {

  layout: layout,

  tagName: 'button',
  classNames: ['datetime-picker-button'],
  classNameBindings: ['selected'],

  selected: Ember.computed('selectedValue', 'params', function(){
    return get(this, 'selectedValue') == get(this, 'params');
  }),

  click: function() {
    get(this, 'for').send(get(this, 'action'), get(this, 'params'));
  },

  displayValue: Ember.computed('label', function() {
    console.log('test');
    var label = get(this, 'label');
    var leftPad = get(this, 'leftPad');
    if (label < 10 && leftPad) {
      return '0' + label;
    } else {
      return label;
    }
  }),

  registerComponent: Ember.on('didInsertElement', function () {
    console.log('register component');
    if (this.get('selected') && this.attrs.onRegister) {
      console.log('on register');
      this.attrs.onRegister(this.get('elementId'), this);
    }
  }),

  actions: {

    changeYear: function(delta) {
      get(this, 'parent').send('changeYear', delta);
    },

    changeMonth: function(delta) {
      get(this, 'parent').send('changeMonth', delta);
    },

    toggleMonthView: function() {
      get(this, 'parent').send('toggleMonthView')
    }

  }

});

Stop triggering onRegister if we haven't set it up yet.


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

var Options = function(configPath) {

    var rawData       = fs.readFileSync(path.join(process.cwd(), configPath), 'utf8');
    var data          = JSON.parse(rawData)
    this.import_paths = data.import_paths
    this.css_dir      = data.css_dir
    this.sass_dir     = data.sass_dir
}

Options.prototype = {

}

module.exports = Options

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

var Options = function(configPath) {

    this.configPath = this.normalizeConfigPath(configPath)
    this.configDirname = path.dirname(this.configPath)

    var rawData       = fs.readFileSync(this.configPath, 'utf8');
    var data          = JSON.parse(rawData)

    this.import_paths = data.import_paths.map(this.buildPath, this)
    this.css_dir      = this.buildPath(data.css_dir)
    this.sass_dir     = this.buildPath(data.sass_dir)
}

Options.prototype = {

    buildPath: function(filePath)
    {
        return path.isAbsolute(filePath) ?
            path.normalize(filePath) :
            path.join(this.configDirname, filePath)
    },

    normalizeConfigPath: function(configPath)
    {
        return path.isAbsolute(configPath) ?
            path.normalize(configPath) :
            path.join(process.cwd(), configPath)
    }
}

module.exports = Options

Normalize paths based on config.json dirname


// This file should pass linting based on the tweaks made to the standard,
// standard-jsx and standard-react presets.
import React from 'react'

// Allow == for typeof, which always returns a string
// Require Stroustrup brace style - else/catch etc. on a new line
if (typeof global == 'object') {
  console.log('global')
}
else if (typeof window == 'object') {
  console.log('window')
}

// Braces can be on same line
if (typeof window.__DATA__) { console.log('data') }

// A space after function name convention is not enforced either way
function space () {
  return 'space!'
}
function noSpace() {
  return 'no space!'
}

let OtherComponent = () =>
Test

let TestComponent = React.createClass({
  render() {
    // Multiline JSX statements don't need wrapping parens
    return

      {/* Declaration of propTypes is not enforced */}

Test {this.props.name}

      {/* Self-closing of components without children is not enforced */}
      <OtherComponent></OtherComponent>
      {`${space()} ${noSpace()}`}
      {/* Spacing before self-closing is not enforced */}
      <OtherComponent test/>
      <OtherComponent test />

  }
})

export default TestComponent

// This file should pass linting based on the tweaks made to the standard,
// standard-jsx and standard-react presets.
import React from 'react'

// Allow == for typeof, which always returns a string
// Require Stroustrup brace style - else/catch etc. on a new line
if (typeof global == 'object') {
  console.log('global')
}
else if (typeof window == 'object') {
  console.log('window')
}

// Braces can be on same line
if (typeof window.__DATA__) { console.log('data') }

// A space after function name convention is not enforced either way
function space () {
  return 'space!'
}
function noSpace() {
  return 'no space!'
}

// Dangling commas are allowed on multiline literals
let object = {
  a: 1,
  b: 2,
  c: 3,
}
let array = [
  1,
  2,
  3,
]
console.log(object, array)

let OtherComponent = () =>
Test

let TestComponent = React.createClass({
  render() {
    // Multiline JSX statements don't need wrapping parens
    return

      {/* Declaration of propTypes is not enforced */}

Test {this.props.name}

      {/* Self-closing of components without children is not enforced */}
      <OtherComponent></OtherComponent>
      {`${space()} ${noSpace()}`}
      {/* Spacing before self-closing is not enforced */}
      <OtherComponent test/>
      <OtherComponent test />

  }
})

export default TestComponent

Add a test for the comma-dangle change


// Dependencies
var MongoSyncFiles = require("../index")
  , Faker = require("faker")
  ;

// Create database instance
var MyDatabase = new MongoSyncFiles();

function generateFakeDataArray() {
    var docs = [];
    for (var i = 0; i < 30; ++i) {
        docs.push({
            name: Faker.Name.findName()
          , email: Faker.Internet.email()
          , age: Faker.Helpers.randomNumber(90)
        });
    }
    return docs;
}

// Create collection instance
var MyAwesomeCollection = MyDatabase.initCollection({
    inputFile: __dirname + "/docs-in.json"
  , outputFile: __dirname + "/docs-out.json"
  , uri: "mongodb://localhost:27017/test"
  , collection: "myCol"
  , autoInit: true
}, function (err) {

    // Handle error
    if (err) { throw err; }

    // Run a Mongo request
    MyAwesomeCollection.find({}).toArray(function (err, docs) {

        // Handle error
        if (err) { throw err; }

        // Output
        console.log("Documents: ", docs);

        // Insert
        MyAwesomeCollection.insert(generateFakeDataArray(), function (err, docs) {

            // Handle error
            if (err) { throw err; }

            console.log("Successfully inserted a new document: ", docs);
            console.log("Check out the content of the following file: ", MyAwesomeCollection._options.outputFile);

            // Close database
            MyAwesomeCollection.database.close();
        });
    });
});

// Dependencies
var MongoSyncFiles = require("../index")
  , Faker = require("faker")
  ;

function generateFakeDataArray() {
    var docs = [];
    for (var i = 0; i < 30; ++i) {
        docs.push({
            name: Faker.Name.findName()
          , email: Faker.Internet.email()
          , age: Faker.Helpers.randomNumber(90)
        });
    }
    return docs;
}

// Create database instance
var MyDatabase = new MongoSyncFiles({
    collections: [{
        inputFile: __dirname + "/docs-in.json"
      , outputFile: __dirname + "/docs-out.json"
      , uri: "mongodb://localhost:27017/test"
      , collection: "myCol"
      , autoInit: true
    }]
}, function (err, collections) {

    // Handle error
    if (err) { throw err; }

    var MyAwesomeCollection = collections.myCol;

    // Run a Mongo request
    MyAwesomeCollection.find({}).toArray(function (err, docs) {

        // Handle error
        if (err) { throw err; }

        // Output
        console.log("Documents: ", docs);

        // Insert
        MyAwesomeCollection.insert(generateFakeDataArray(), function (err, docs) {

            // Handle error
            if (err) { throw err; }

            console.log("Successfully inserted a new document: ", docs);
            console.log("Check out the content of the following file: ", MyAwesomeCollection._options.outputFile);

            // Close database
            MyAwesomeCollection.database.close();
        });
    });
});

Use `collections` array for syncing databases.


var promisify = require("../");
var assert = require("assert");

describe("Promisify", function() {
  it("can convert a basic async function", function(done) {
    function test(cb) {
      cb(null, true);
    }

    var wrappedTest = promisify(test);

    wrappedTest().then(function(value) {
      assert.ok(value);
      done();
    });
  });

  it("exports a callbacks array", function() {
    assert(Array.isArray(promisify.callbacks));
  });
});

var promisify = require("../");
var assert = require("assert");

describe("Promisify", function() {
  it("can convert a basic async function", function() {
    function test(cb) {
      cb(null, true);
    }

    var wrappedTest = promisify(test);

    return wrappedTest().then(function(value) {
      assert.ok(value);
    });
  });

  it("exports a callbacks array", function() {
    assert(Array.isArray(promisify.callbacks));
  });
});

Update test to use Mocha's promise handling


import DS from 'ember-data';
import { validator, buildValidations } from 'ember-cp-validations';

const Validations = buildValidations({
  name: validator('presence', true),
});

export default DS.Model.extend(Validations, {
  name: DS.attr(),
  apiScopeIds: DS.attr(),
  permissionIds: DS.attr(),
  admins: DS.attr(),
  createdAt: DS.attr(),
  updatedAt: DS.attr(),
  creator: DS.attr(),
  updater: DS.attr(),
}).reopenClass({
  urlRoot: '/api-umbrella/v1/admin_groups',
  singlePayloadKey: 'admin_group',
  arrayPayloadKey: 'data',
});

import DS from 'ember-data';
import { validator, buildValidations } from 'ember-cp-validations';

const Validations = buildValidations({
  name: validator('presence', true),
});

export default DS.Model.extend(Validations, {
  name: DS.attr(),
  apiScopeIds: DS.attr({ defaultValue() { return [] } }),
  permissionIds: DS.attr({ defaultValue() { return [] } }),
  admins: DS.attr(),
  createdAt: DS.attr(),
  updatedAt: DS.attr(),
  creator: DS.attr(),
  updater: DS.attr(),
}).reopenClass({
  urlRoot: '/api-umbrella/v1/admin_groups',
  singlePayloadKey: 'admin_group',
  arrayPayloadKey: 'data',
});

Fix admin groups not saving selected scopes or permissions.


const { flatten } = require('lodash')

const { globalFields } = require('../../macros')
const FILTER_CONSTANTS = require('../../../lib/filter-constants')
const PRIMARY_SECTOR_NAME = FILTER_CONSTANTS.COMPANIES.SECTOR.PRIMARY.NAME

const companyFiltersFields = function ({ sectorOptions }) {
  return [
    Object.assign({}, globalFields.headquarter_type, {
      name: 'headquarter_type',
      type: 'checkbox',
      modifier: 'option-select',
    }),
    {
      macroName: 'TextField',
      label: 'Company name',
      name: 'name',
    },
    {
      macroName: 'MultipleChoiceField',
      name: PRIMARY_SECTOR_NAME,
      type: 'checkbox',
      modifier: 'option-select',
      options: sectorOptions,
      label: 'Sector',
    },
    Object.assign({}, globalFields.countries, {
      type: 'checkbox',
      modifier: 'option-select',
    }),
    Object.assign({}, globalFields.ukRegions, {
      name: 'uk_region',
      type: 'checkbox',
      modifier: 'option-select',
    }),
  ].map(filter => {
    return Object.assign(filter, {
      modifier: flatten([filter.modifier, 'smaller', 'light', 'filter']),
    })
  })
}

module.exports = companyFiltersFields

const { flatten } = require('lodash')

const { globalFields } = require('../../macros')
const FILTER_CONSTANTS = require('../../../lib/filter-constants')
const PRIMARY_SECTOR_NAME = FILTER_CONSTANTS.COMPANIES.SECTOR.PRIMARY.NAME

const companyFiltersFields = function ({ sectorOptions }) {
  return [
    Object.assign({}, globalFields.headquarter_type, {
      name: 'headquarter_type',
      type: 'checkbox',
      modifier: 'option-select',
    }),
    {
      macroName: 'TextField',
      label: 'Company name',
      name: 'name',
    },
    {
      macroName: 'MultipleChoiceField',
      name: 'archived',
      type: 'checkbox',
      label: 'Status',
      options: [
        { value: 'false', label: 'Active' },
        { value: 'true', label: 'Inactive' },
      ],
      modifier: 'option-select',
    },
    {
      macroName: 'MultipleChoiceField',
      name: PRIMARY_SECTOR_NAME,
      type: 'checkbox',
      modifier: 'option-select',
      options: sectorOptions,
      label: 'Sector',
    },
    Object.assign({}, globalFields.countries, {
      type: 'checkbox',
      modifier: 'option-select',
    }),
    Object.assign({}, globalFields.ukRegions, {
      name: 'uk_region',
      type: 'checkbox',
      modifier: 'option-select',
    }),
  ].map(filter => {
    return Object.assign(filter, {
      modifier: flatten([filter.modifier, 'smaller', 'light', 'filter']),
    })
  })
}

module.exports = companyFiltersFields

Add active/inactive filter fields to companies


var isNonPrintableKey = function(keyCode) {
  var nonPrintableKeys, modifierKeys, arrowKeys, fKeys;
  modifierKeys = [8, 9, 13, 16, 17, 18, 19, 20, 27, 33, 34, 35, 26, 46, 93];
  arrowKeys = [37, 38, 39, 40, 91, 92];
  fKeys = [112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 144, 145];

  nonPrintableKeys = modifierKeys.concat(arrowKeys).concat(fKeys);

  return nonPrintableKeys.indexOf(keyCode) != -1
};

export { isNonPrintableKey };

var isNonPrintableKey = function(keyCode) {
  var nonPrintableKeys, modifierKeys, arrowKeys, fKeys;
  modifierKeys = [8, 9, 13, 16, 17, 18, 19, 20, 27, 33, 34, 35, 26, 46, 93, 224];
  arrowKeys = [37, 38, 39, 40, 91, 92];
  fKeys = [112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 144, 145];

  nonPrintableKeys = modifierKeys.concat(arrowKeys).concat(fKeys);

  return nonPrintableKeys.indexOf(keyCode) != -1
};

export { isNonPrintableKey };

Update nonprintable keys to include the cmd key


import React from "react"
import PropTypes from "prop-types"
import styles from "./index.css"

const Hero = ({children}) => (
    <div className={styles.hero}>
        {children}
    </div>
);

Hero.propTypes = {
    children: PropTypes.string.isRequired,
};

export default Hero;

import React from "react"
import PropTypes from "prop-types"
import styles from "./index.css"

const Hero = ({children}) => (
    <div className={styles.hero}>
        {children}
    </div>
);

Hero.propTypes = {
    children: PropTypes.node.isRequired,
};

export default Hero;

Fix children type for the Hero component.


function BetterContentController(){
    this.listeners = {};
}

BetterContentController.prototype.registerListener = function(client, func) {
    this.listeners[client] = func;
}
BetterContentController.prototype.removeListener = function(client) {
    this.listeners[client] = nil;
}

BetterContentController.prototype.onMessage = function(originator, message) {
    if (originator === module.exports.NATIVE_APP){
        if (this.listeners[module.exports.WEB_CLIENT]){
            this.listeners[module.exports.WEB_CLIENT](message);
        }
    }
    if (originator === module.exports.WEB_CLIENT){
        if (this.listeners[module.exports.NATIVE_APP]){
            this.listeners[module.exports.NATIVE_APP](message);
        }
    }
}

module.exports = BetterContentController;
module.exports.NATIVE_APP = 'nativeApp';
module.exports.WEB_CLIENT = 'webClient';

function BetterContentController(){
    this.listeners = {};
}

BetterContentController.prototype.registerListener = function(client, func) {
    this.listeners[client] = func;
}
BetterContentController.prototype.removeListener = function(client) {
    this.listeners[client] = null;
}

BetterContentController.prototype.onMessage = function(originator, message) {
    if (originator === module.exports.NATIVE_APP){
        if (this.listeners[module.exports.WEB_CLIENT]){
            this.listeners[module.exports.WEB_CLIENT](message);
        }
    }
    if (originator === module.exports.WEB_CLIENT){
        if (this.listeners[module.exports.NATIVE_APP]){
            this.listeners[module.exports.NATIVE_APP](message);
        }
    }
}

module.exports = BetterContentController;
module.exports.NATIVE_APP = 'nativeApp';
module.exports.WEB_CLIENT = 'webClient';

Fix typo (nil instaed of null)


function makeScatterplotSquare() {
    var data = makeNormallyDistributedData(100, 50, 20, 60, 10);
    var dataset = {data: data, metadata: {cssClass: "transparent-square"}};

    var xScale = new Plottable.LinearScale();
    var xAxis = new Plottable.XAxis(xScale, "bottom").tickSize(2, 0);
    var yScale = new Plottable.LinearScale();
    var yAxis = new Plottable.YAxis(yScale, "left")
                             .tickLabelPosition("bottom")
                             .tickSize(50);

    function rAccessor() { return 5; }
    var r  = new Plottable.SquareRenderer(dataset, xScale, yScale,
                                            null, null, rAccessor);

    var svg = d3.select("#scatterplot-square");
    var innerTable = new Plottable.Table([[yAxis, r],
                                          [null, xAxis]]);
    innerTable.renderTo(svg);
    xScale.padDomain().nice();
    yScale.padDomain().nice();

};

window.onload = makeScatterplotSquare;

function makeScatterplotSquare() {
    var data = makeNormallyDistributedData(100, 50, 20, 60, 10);
    var dataset = {data: data, metadata: {cssClass: "transparent-square"}};

    var xScale = new Plottable.LinearScale();
    var xAxis = new Plottable.XAxis(xScale, "bottom").tickSize(2, 0);
    var yScale = new Plottable.LinearScale();
    var yAxis = new Plottable.YAxis(yScale, "left")
                             .tickLabelPosition("bottom")
                             .tickSize(50);

    function rAccessor() { return 5; }
    var r  = new Plottable.SquareRenderer(dataset, xScale, yScale,
                                            null, null, rAccessor);

    var svg = d3.select("#scatterplot-square");
    svg.attr("width", 800).attr("height", 320);
    var innerTable = new Plottable.Table([[yAxis, r],
                                          [null, xAxis]]);
    innerTable.renderTo(svg);
    xScale.padDomain().nice();
    yScale.padDomain().nice();

};

window.onload = makeScatterplotSquare;

Set size on SVG in scatterplotSquare example.


const _ = require('lodash')

function noSuggestions() {
  return 'Sorry, no suggestions available.'
}

module.exports = (results) => {
  if (_.isEmpty(results.suggestionData)) {
    throw new Error('Cannot format data!')
  }

  const resultCount = _.reduce(results.suggestionData, (totalCount, { propertyDefinitions }) => (
    totalCount + _.reduce(propertyDefinitions, (localCount, { suggestions }) => (
      localCount + suggestions.filter(({ suggestions: localSuggestions }) => (
        !_.isEmpty(localSuggestions)
      )).length
    ), 0)
  ), 0)

  if (resultCount === 0) {
    return noSuggestions()
  }

  return 'not implemented'
}

const _ = require('lodash')

function noSuggestions() {
  return 'Sorry, no suggestions available.'
}

function countSuggestions(results) {
  return _.reduce(results.suggestionData, (totalCount, { propertyDefinitions }) => (
    totalCount + _.reduce(propertyDefinitions, (localCount, { suggestions }) => (
      localCount + suggestions.filter(({ suggestions: localSuggestions }) => (
        !_.isEmpty(localSuggestions)
      )).length
    ), 0)
  ), 0)
}

module.exports = (results) => {
  if (_.isEmpty(results) || _.isEmpty(results.suggestionData)) {
    throw new Error('Cannot format data!')
  }

  const resultCount = countSuggestions(results)

  if (resultCount === 0) {
    return noSuggestions()
  }

  return 'not implemented'
}

Move code to its own function.


$(document).ready(function() {

	autosize($('textarea.autosize'));

	$("form.validate").validate();

});

$(document).ready(function() {

	autosize($('textarea.autosize'));

	$("form.validate").each(function() {
		$(this).validate();
	});

});

Support for multiple form validation


(($docsify)=>{
  if(!$docsify){console.err("$docsify no exist.")}
  if ($docsify) {
    $docsify.plugins = [].concat(docsifyFooter(), $docsify.plugins)
  }

  function docsifyFooter() {
    return function(hook, vm) {
      hook.afterEach(function(html,next) {
        var footer = "
Set your footer in docsify config (window.$docsify.footer)";
        if(typeof $docsify.footer === "function"){
          footer = $docsify.footer();
        }else if($docsify.footer){
          footer = $docsify.footer;
        }
        next (html + footer);
      })

    };
  }

})(window.$docsify);

(($docsify)=>{
  if(!$docsify){console.err("$docsify no exist.")}
  if ($docsify) {
    $docsify.plugins = [].concat(docsifyFooter(), $docsify.plugins)
  }

  function docsifyFooter() {
    return function(hook, vm) {
      hook.mounted(function() {
        var section = document.querySelector("section.content");
        var footer = "
Set your footer in docsify config (window.$docsify.footer)";
        if(typeof $docsify.footer === "function"){
          footer = $docsify.footer();
        }else if($docsify.footer){
          footer = $docsify.footer;
        }
        var article = document.createElement("article");
        article.setAttribute("footer","");
        article.className = "markdown-section";
        article.innerHTML = footer;
        section.appendChild(article);
      })

    };
  }

})(window.$docsify);

Load footer plugin at hook.mounted.Isolate footer to an article element.


/*
 * Validate CPF string with the following format 000.000.000-00
 * Dots and dash separators are optional
 * @param string
 * @return boolean
 */
function isCpf(cpfNumber) {
  var cpfRegex = /^(\d{3}[\.]?\d{3}[\.]?\d{3}[-]?\d{2})$/
  return cpfRegex.test(cpfNumber)
}

/*
 * Export constant cpf
 */
export const cpf = {
  isCpf: isCpf
}

/*
 * Validate CPF string with the following format 000.000.000-00
 * Dots and dash separators are optional
 * @param string
 * @return boolean
 */
function isCpf(cpfNumber) {
  var cpfRegex = /^(\d{3}[\.]?\d{3}[\.]?\d{3}[-]?\d{2})$/
  // return cpfRegex.test(cpfNumber)
  if (cpfRegex.test(cpfNumber)) {
    var cpf = cpfNumber.replace(/\.|-/g, '')
    if (
        cpf === "00000000000" ||
        cpf === "11111111111" ||
        cpf === "22222222222" ||
        cpf === "33333333333" ||
        cpf === "44444444444" ||
        cpf === "55555555555" ||
        cpf === "66666666666" ||
        cpf === "77777777777" ||
        cpf === "88888888888" ||
        cpf === "99999999999"
    ) return false

    var final
    var sum = 0
    for (var i = 0; i < 9; i++)
        sum += parseInt(cpf.charAt(i)) * (10 - i)

    final = 11 - (sum % 11)

    if (final === 10 || final === 11) final = 0
    if (final != parseInt(cpf.charAt(9))) return false

    sum = 0
    for (var i = 0; i < 10; i++)
        sum += parseInt(cpf.charAt(i)) * (11 - i)

    final = 11 - (sum % 11)

    if (final === 10 || final === 11) final = 0
    if (final != parseInt(cpf.charAt(10))) return false

    return true
  }
}

/*
 * Export constant cpf
 */
export const cpf = {
  isCpf: isCpf
}

Add validation for true CPF number


$(function() {

	/* Add click event listeners to the squares of the month for entering the month calendar detail view. */
    $('[data-month]').each(function (i, monthSquare) {
        var oogstplanner = new Oogstplanner();

        $(monthSquare).bind('click', function () {

            var monthName = $(monthSquare).data('month');

            if (oogstplanner.getHasActionAttributeValue(monthName) === true) {
                oogstplanner.fillMonthCalendar(monthName);
                oogstplanner.toMonthCalendar();
            }
            else {
                var dutchMonthName = Util.convertToDisplayMonth(monthName);
                Notification.informational(dutchMonthName + ' heeft geen zaai- of oogstmomenten.',
                    'Bepaal eerst gewassen om te zaaien of te oogsten door deze rechts te zoeken en op de maand te slepen.');
            }

        });

    });

    /* Add click listener to login or register button which shows modal popup. */
    $('.register-or-login').on('click', function () {
        BootstrapDialog.show({
            title: 'De oogstplanner gemeenschap',
            message: $('

').load('/gebruiker/inloggenofregistreren')
        });
    });

    /* When search button is clicked search for users. */
    $('#search-button').on('click', function () {
        window.location.href='/gemeenschap/zoek/' + $('#search-input').val()
    });

});

$(function() {

	/* Add click event listeners to the squares of the month for entering the month calendar detail view. */
    $('[data-month]').each(function (i, monthSquare) {
        var oogstplanner = new Oogstplanner();

        $(monthSquare).bind('click', function () {

            var monthName = $(monthSquare).data('month');

            if (oogstplanner.getHasActionAttributeValue(monthName) === true) {
                oogstplanner.fillMonthCalendar(monthName);
                oogstplanner.toMonthCalendar();
            }
            else {
                var dutchMonthName = Util.convertToDisplayMonth(monthName);
                Notification.informational(dutchMonthName + ' heeft geen zaai- of oogstmomenten.',
                    'Bepaal eerst gewassen om te zaaien of te oogsten door deze links te zoeken en op de maand te slepen.');
            }

        });

    });

    /* Add click listener to login or register button which shows modal popup. */
    $('.register-or-login').on('click', function () {
        BootstrapDialog.show({
            title: 'De oogstplanner gemeenschap',
            message: $('

').load('/gebruiker/inloggenofregistreren')
        });
    });

    /* When search button is clicked search for users. */
    $('#search-button').on('click', function () {
        window.location.href='/gemeenschap/zoek/' + $('#search-input').val()
    });

});

Fix UI misinformation reported by user


module.exports = function(number, name, group, club) {
	this.number = (number = typeof number !== 'undefined' ? number : null);
	this.name = (name = typeof name !== 'undefined' ? name : null);
	this.group = (group = typeof group !== 'undefined' ? group : null);
	this.club = (club = typeof club !== 'undefined' ? club : null)
	this.id = null;

	this.isset = function(){
		if (this.number != null || this.name != null || this.group != null || this.club != null) {
			return true;
		}
		return false;
	}

	this.setId = function(id){
		this.id = id;

		return this;
	}
}

module.exports = function(number, name, group, club) {
	this.number = (number = typeof number !== 'undefined' ? number : '');
	this.name = (name = typeof name !== 'undefined' ? name : '');
	this.group = (group = typeof group !== 'undefined' ? group : '');
	this.club = (club = typeof club !== 'undefined' ? club : '')
	this.id = null;

	this.isset = function(){
		if (this.number != '' || this.name != '' || this.group != '' || this.club != '') {
			return true;
		}
		return false;
	}

	this.setId = function(id){
		this.id = id;

		return this;
	}
}

Change default value to empty string insted of null


// Map creation + initialisation
var map = L.map('map')
    .setView([55.95, -3.2], 14)
    .addLayer(L.mapbox.tileLayer('jeffbr13.h81gnbgn', {
        detectRetina: true
    }));

var soundIcon = L.icon({
    iconUrl: '/static/img/marker.png',
    iconRetinaUrl: '/static/img/marker@2X.png',
    iconSize: [12,12]
});

position_marker = L.marker([55.946, -3.186], {
    icon: soundIcon,
    draggable: true
});

// Events
function onMapClick(e) {
    $('#latitude').val(e.latlng.lat);
    $('#longitude').val(e.latlng.lng);
    position_marker.setLatLng(e.latlng).addTo(map);
}

function onMarkerDrag (e) {
    $('#latitude').val(e.target._latlng.lat);
    $('#longitude').val(e.target._latlng.lng);
}

map.on('click', onMapClick);
position_marker.on('drag', onMarkerDrag);

// Map creation + initialisation
var map = L.map('map')
    .setView([55.95, -3.2], 14)
    .addLayer(L.mapbox.tileLayer('socialsoundsproject.h9hbe4l4', {
        detectRetina: true
    }));

var soundIcon = L.icon({
    iconUrl: '/static/img/marker.png',
    iconRetinaUrl: '/static/img/marker@2X.png',
    iconSize: [12,12]
});

position_marker = L.marker([55.946, -3.186], {
    icon: soundIcon,
    draggable: true
});

// Events
function onMapClick(e) {
    $('#latitude').val(e.latlng.lat);
    $('#longitude').val(e.latlng.lng);
    position_marker.setLatLng(e.latlng).addTo(map);
}

function onMarkerDrag (e) {
    $('#latitude').val(e.target._latlng.lat);
    $('#longitude').val(e.target._latlng.lng);
}

map.on('click', onMapClick);
position_marker.on('drag', onMarkerDrag);

Set custom map tiles everywhere.


var Choosy = {
  promptAll: function(url) {
    var iframe = document.createElement("iframe");
    iframe.setAttribute("src", "x-choosy://prompt.all/" + escape(url));
    document.body.appendChild(iframe);
  }
};

chrome.browserAction.onClicked.addListener(function(tab) {
  Choosy.promptAll(tab.url);
});

chrome.contextMenus.onClicked.addListener(function (info, tab) {
  if (info.menuItemId === "choosy-menu-item") {
    Choosy.promptAll(info.linkUrl);
  }
});

chrome.runtime.onInstalled.addListener(function () {
  chrome.contextMenus.create({
    id: "choosy-menu-item",
    title: "Open with Choosy",
    contexts: ["link"]
  });
});

if (typeof browser === "undefined") {
  browser = chrome;
}

var Choosy = {
  promptAll: function(url) {
    var iframe = document.createElement("iframe");
    iframe.setAttribute("src", "x-choosy://prompt.all/" + escape(url));
    document.body.appendChild(iframe);
  }
};

browser.browserAction.onClicked.addListener(function(tab) {
  Choosy.promptAll(tab.url);
});

browser.contextMenus.onClicked.addListener(function (info, tab) {
  if (info.menuItemId === "choosy-menu-item") {
    Choosy.promptAll(info.linkUrl);
  }
});

browser.runtime.onInstalled.addListener(function () {
  browser.contextMenus.create({
    id: "choosy-menu-item",
    title: "Open with Choosy",
    contexts: ["link"]
  });
});

Use `browser` or `chrome` variable.


/**
 * Module dependencies.
 */
var passport 	= require('passport'),
    login 		= require('connect-ensure-login'),
  	router 		= require('express').Router(),
  	oauth2  	= require('./oauth2'),
  	user 			= require('./user');

router.get('/dialog/authorize', oauth2.authorization);
//router.post('/dialog/authorize/decision', oauth2.decision);
router.post('/oauth/token', oauth2.token);

router.get('/api/userinfo', user.info);

router.get('/', function(req, res) {
  res.send('OAuth 2.0 Server');
});

router.get('/login', function(req, res) {
  return res.render('login');
});

router.post('/login', passport.authenticate('local', { successReturnToOrRedirect: process.env.NODEBB, failureRedirect: '/login' }));

router.get('/register', function(req, res) {
  console.log(req.query);
  var callback = req.query.callback;
	return res.render('register', {callback});
});

router.post('/register', function(req, res) {
  user.create(req, res);
});

router.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});

router.get('/account', login.ensureLoggedIn(), function(req, res) {
  res.render('account', { user: req.user });
});

module.exports = router;

/**
 * Module dependencies.
 */
var passport 	= require('passport'),
    login 		= require('connect-ensure-login'),
  	router 		= require('express').Router(),
  	oauth2  	= require('./oauth2'),
  	user 			= require('./user');

router.get('/dialog/authorize', oauth2.authorization);
//router.post('/dialog/authorize/decision', oauth2.decision);
router.post('/oauth/token', oauth2.token);

router.get('/api/userinfo', user.info);

router.get('/', function(req, res) {
  res.send('OAuth 2.0 Server');
});

router.get('/login', function(req, res) {
  return res.render('login');
});

router.post('/login', passport.authenticate('local', { successReturnToOrRedirect: '/', failureRedirect: '/login' }));

router.get('/register', function(req, res) {
  console.log(req.query);
  var callback = req.query.callback;
	return res.render('register', {callback});
});

router.post('/register', function(req, res) {
  user.create(req, res);
});

router.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});

router.get('/account', login.ensureLoggedIn(), function(req, res) {
  res.render('account', { user: req.user });
});

module.exports = router;

Fix error in callback on login post


if(document.querySelector('html').getAttribute('manifest') ==
    '/_/chrome/newtab/manifest?espv=2&ie=UTF-8') {
  document.querySelector('#lga img').setAttribute('style', 'visibility: hidden');
}

Remove conditional to support latest Chrome


// development config
const { merge } = require("webpack-merge");
const webpack = require("webpack");
const commonConfig = require("./common");

module.exports = merge(commonConfig, {
  mode: "development",
  entry: [
    "react-hot-loader/patch", // activate HMR for React
    "webpack-dev-server/client?http://localhost:8080", // bundle the client for webpack-dev-server and connect to the provided endpoint
    "webpack/hot/only-dev-server", // bundle the client for hot reloading, only- means to only hot reload for successful updates
    "./index.tsx", // the entry point of our app
  ],
  devServer: {
    hot: true, // enable HMR on the server
  },
  devtool: "cheap-module-source-map",
  plugins: [
    new webpack.HotModuleReplacementPlugin(), // enable HMR globally
  ],
});

// development config
const { merge } = require("webpack-merge");
const webpack = require("webpack");
const commonConfig = require("./common");

module.exports = merge(commonConfig, {
  mode: "development",
  entry: [
    "react-hot-loader/patch", // activate HMR for React
    "webpack-dev-server/client?http://localhost:8080", // bundle the client for webpack-dev-server and connect to the provided endpoint
    "webpack/hot/only-dev-server", // bundle the client for hot reloading, only- means to only hot reload for successful updates
    "./index.tsx", // the entry point of our app
  ],
  devServer: {
    hot: true, // enable HMR on the server
    historyApiFallback: true, // fixes error 404-ish errors when using react router :see this SO question: https://stackoverflow.com/questions/43209666/react-router-v4-cannot-get-url
  },
  devtool: "cheap-module-source-map",
  plugins: [
    new webpack.HotModuleReplacementPlugin(), // enable HMR globally
  ],
});

Fix Error 404 related errors when using React Router


$('#import').click(function() {
    $('input[name=import]').click();
    return false;
});

$('input[name=import]').change(function() {
    if ($(this).val().substr(-8) != '.geojson') {
        alert("Please provide a .geojson file");
    } else {
        $('form').submit();
    }
});
$('form').submit(function() {
    window.setTimeout(function() {
        $('#import').attr('disabled', 'disabled');
        $('#loading').show();
    }, 0);
});

$('#import').click(function() {
    $('input[name=import]').click();
    return false;
});

$('input[name=import]').change(function() {
    if ($(this).val().substr(-4) != 'json') {
        alert("Please provide a .geojson file");
    } else {
        $('form').submit();
    }
});
$('form').submit(function() {
    window.setTimeout(function() {
        $('#import').attr('disabled', 'disabled');
        $('#loading').show();
    }, 0);
});

Allow .json files as well


var studentsController = require('../controllers/students');
var teachersController = require('../controllers/teachers');
var authenticationController = require('../controllers/authenticate');

module.exports = function(app, express, io) {

  app.get('/login', authenticationController.login);
  app.get('/signup', authenticationController.signup);

  //app.get('/teachers/poll', teachersController.pollClass);
  //app.get('/teachers/thumbs', teachersController.thumbsCheck);

  app.get('/teachers/thumbs', teachersController.thumbsCheck.bind(this, io));
  // app.post('/teachers/poll', teachersController.pollClass);

  app.get('/teachers/lessons/:classId', teachersController.getLessons);
  app.get('/teachers/polls/:lessonId', teachersController.getPolls);

  app.post('/teachers/polls', teachersController.newPoll);

  //app.get('/classDismissed', teachersController.endClass);

  app.get('/students/ready', studentsController.readyStage.bind(this, io));
};

var studentsController = require('../controllers/students');
var teachersController = require('../controllers/teachers');
var authenticationController = require('../controllers/authenticate');

module.exports = function(app, io) {

  app.get('/login', authenticationController.login);
  app.get('/signup', authenticationController.signup);

  //app.get('/teachers/poll', teachersController.pollClass);
  //app.get('/teachers/thumbs', teachersController.thumbsCheck);

  app.get('/teachers/thumbs', teachersController.thumbsCheck(io));
  // app.post('/teachers/poll', teachersController.pollClass);

  app.get('/teachers/lessons/:classId', teachersController.getLessons);
  app.get('/teachers/polls/:lessonId', teachersController.getPolls);

  app.post('/teachers/polls', teachersController.newPoll);

  //app.get('/classDismissed', teachersController.endClass);

  app.get('/students/ready', studentsController.readyStage(io));
};

Remove express reference, send only app object