import getPath from './getPath'
import createDataListener from './createDataListener'

function updateInstanceData(db, schema, props, data, self, prop, val) {

  // Unsubscribe all listeners for this prop
  props.schema.subscribes[prop].forEach(y => y())
  props.schema.subscribes[prop] = []

  // For all the paths that are impacted by this prop
  props.schema.tokens[prop].props.forEach(x => {
    let path = getPath(schema, props, self, x)

    let listener = createDataListener(db, path, data, x)

    props.schema.subscribes[prop].push(listener)
  })

}

export default updateInstanceData

import getPath from './getPath'
import createDataListener from './createDataListener'

function updateInstanceData(db, schema, props, data, self, prop, val) {

  // Unsubscribe all listeners for this prop
  if (props.schema.subscribes[prop]) {
    props.schema.subscribes[prop].forEach(y => y())
  }

  props.schema.subscribes[prop] = []

  // For all the paths that are impacted by this prop
  if (props.schema.tokens[prop] && props.schema.tokens[prop].props) {
    props.schema.tokens[prop].props.forEach(x => {
      let path = getPath(schema, props, self, x)

      let listener = createDataListener(db, path, data, x)

      props.schema.subscribes[prop].push(listener)
    })
  }
}

export default updateInstanceData

Fix error on view when token or subscribes props are undefined


const winston = require('winston');

const { format, transports, createLogger } = winston;

let logLevel = process.env.LOG_LEVEL;

if (!logLevel) {
	switch (process.env.NODE_ENV) {
		case 'default':
		case 'development':
			logLevel = 'debug';
			break;
		case 'test':
			logLevel = 'emerg';
			break;
		case 'production':
		default:
			logLevel = 'info';
	}
}

const addType = format.printf((log) => {
	if (log.stack || log.level === 'error') {
		log.type = 'error';
	} else {
		log.type = 'log';
	}
	return log;
});

const colorize = process.env.NODE_ENV !== 'production';
let formater;
if (process.env.NODE_ENV === 'test') {
	formater = format.combine(
		format.prettyPrint({ depth: 1, colorize }),
	);
} else {
	formater = format.combine(
		format.errors({ stack: true }),
		format.timestamp(),
		addType,
		format.prettyPrint({ depth: 3, colorize }),
	);
}

const logger = createLogger({
	levels: winston.config.syslog.levels,
	format: formater,
	transports: [
		new transports.Console({
			level: logLevel,
			handleExceptions: true,
		}),
	],
	exitOnError: false,
});

module.exports = logger;

const winston = require('winston');

const { format, transports, createLogger } = winston;

let logLevel = process.env.LOG_LEVEL;

if (!logLevel) {
	switch (process.env.NODE_ENV) {
		case 'development':
			logLevel = 'debug';
			break;
		case 'test':
			logLevel = 'emerg';
			break;
		case 'default':
		case 'production':
		default:
			logLevel = 'error';
	}
}

const addType = format.printf((log) => {
	if (log.stack || log.level === 'error') {
		log.type = 'error';
	} else {
		log.type = 'log';
	}
	return log;
});

const colorize = process.env.NODE_ENV !== 'production';
let formater;
if (process.env.NODE_ENV === 'test') {
	formater = format.combine(
		format.prettyPrint({ depth: 1, colorize }),
	);
} else {
	formater = format.combine(
		format.errors({ stack: true }),
		format.timestamp(),
		addType,
		format.prettyPrint({ depth: 3, colorize }),
	);
}

const logger = createLogger({
	levels: winston.config.syslog.levels,
	format: formater,
	transports: [
		new transports.Console({
			level: logLevel,
			handleExceptions: true,
		}),
	],
	exitOnError: false,
});

module.exports = logger;

Set default log level to error


var Views = {};
Views.ListingView = Backbone.View.extend({
	initialise: function() {
		this.listenTo(this.collection, 'add', this.render);
		this.listenTo(this.collection, 'change', this.render);
		this.listenTo(this.collection, 'remove', this.render);
		this.childViews = [];
	},

	render: function() {
		this.childViews = [];

		var fragment = document.createDocumentFragment();

		var currentChildView;
		var that = this;
		this.collection.each(function(currentModel) {
			currentChildView = new Views.ItemView({
				model: currentModel
			});
			that.childViews.push(currentChildView);
			fragment.appendChild(currentChildView.render().el);
		});

		this.$el.html(fragment);
		return this;
	}
});

Views.ItemView = Backbone.View.extend({
	template: Templates.ItemListingTemplate,
	initialise: function() {
		this.listenTo(this.model, "change", this.render);
		this.id = "item-" + this.model.attributes.id;
	},

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

var Views = {};
Views.ListingView = Backbone.View.extend({
	initialize: function() {
		this.listenTo(this.collection, 'add', this.collectionAdded);
		this.listenTo(this.collection, 'change', this.render);
		this.listenTo(this.collection, 'remove', this.render);

		this.childViews = [];

		var that = this;
		this.collection.each(function(item) {
			that.collectionAdded(item);
		});
	},

	render: function() {
		var fragment = document.createDocumentFragment();

		_(this.childViews).each(function(currentView) {
			fragment.appendChild(currentView.render().el);
		});

		this.$el.html(fragment);
		return this;
	},

	collectionAdded: function(item) {
		this.childViews.push(new Views.ItemView({
			model: item
		}));
		this.render();
	}
});

Views.ItemView = Backbone.View.extend({
	template: Templates.ItemListingTemplate,
	initialize: function() {
		this.listenTo(this.model, "change", this.render);
		this.id = "item-" + this.model.attributes.id;
	},

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

Fix incorrect spelling of initialize, simplify rendering.


module.exports = (function Nfield () {

  var Promise = require('bluebird');

  var nfieldInstance;

  function createNfieldInstance (nfieldParams, requestParams) {

    var request;
    var nfieldOptions = nfieldParams || {};
    var requestOptions = requestParams || {};

  }

  return {
    init : function (nfieldParams, requestParams) {
      if (!nfieldInstance) {
        nfieldInstance = createNfieldInstance(nfieldParams, requestParams);
      }
      return nfieldInstance;
    }
  };

})();

module.exports = (function Nfield () {

  var Promise = require('bluebird');

  var nfieldInstance;

  function createNfieldInstance (nfieldParams, requestParams) {

    var request;
    var nfieldOptions = nfieldParams || {};
    var requestOptions = requestParams || {};

    function updateParams (nP, rP) {

      nfieldOptions = nP || nfieldOptions;
      requestOptions = rP || requestOptions;

      requestOptions.baseUrl = nfieldOptions.server || 'https://api.nfieldmr.com/';

      request = Promise.promisify(require('request').defaults(requestOptions));

    }

    function signIn (credentials, callback) {
      return request({
        method : 'post',
        uri : 'v1/SignIn',
        json : credentials
      }).nodeify(callback);
    }

    updateParams(nfieldOptions, requestOptions);

    return {
      configure : function publicConfigure (nP, rP) {
        updateParams(nP, rP);
      },
      signIn : function publicSignIn (cred, cb) {
        return signIn(cred, cb);
      }
    };

  }

  return {
    init : function (nfieldParams, requestParams) {
      if (!nfieldInstance) {
        nfieldInstance = createNfieldInstance(nfieldParams, requestParams);
      }
      return nfieldInstance;
    }
  };

})();

Add signIn() and configure() methods to Nfield module


const {readFileSync} = require('fs');

module.exports = {
  up: function (queryInterface, Sequelize) {

    queryInterface.renameColumn(
      'users',
      'subscribedPodcastFeedUrls',
      'subscribedPodcastIds'
    );

    queryInterface.addColumn(
      'mediaRefs',
      'podcastId',
      Sequelize.TEXT
    );

  },

  down: function (queryInterface) {
    return queryInterface.dropAllTables();
  }
}

const {readFileSync} = require('fs');

module.exports = {
  up: function (queryInterface, Sequelize, done) {

    return [
      queryInterface.renameColumn(
        'users',
        'subscribedPodcastFeedUrls',
        'subscribedPodcastIds'
      ),
      queryInterface.addColumn(
        'mediaRefs',
        'podcastId',
        Sequelize.TEXT
      )
    ]

  },

  down: function (queryInterface) {
    return queryInterface.dropAllTables();
  }
}

Update migration script to handle async


/* Returns a clever domain for the given text, if one exists. */
function checkDomains() {

  /* Get the input string */
  var userString = document.getElementById("domainInput").value;

  /* Sample list of domain suffixes */
  var data = [
    'io',
    'biz',
    'im',
    'info'
  ];

  /* Try to find a matching domain ending. */
  for(var ending = 0; ending < data.length; ++ending) {
    var domain = data[ending];

    if(userString.slice(userString.length - domain.length, userString.length) === domain) {
      var domainPrefix = userString.slice(0, userString.lastIndexOf(domain[0]) );
      var pickedDomain = domainPrefix + '.' + domain;
      break;
    };

  };

  /* Check if a domain was found. */
  if (typeof domainPrefix === 'undefined') {
    message = 'No clever domains were found for \'' + userString + '\' :(';
  }

  else {
    message = 'Congrats! We\'ve found a domain for you: ' + pickedDomain;
  }

  setDivMessage(message);
  return;

};

/* Set the message to the output div. */
function setDivMessage(message) {
  var outputDiv = document.getElementById("domainOutput");
  outputDiv.textContent = message;
  outputDiv.style.display = "block";
};

/* Returns a clever domain for the given text, if one exists. */
function checkDomains() {

  /* Get the input string */
  var userString = document.getElementById("domainInput").value;

  /* Sample list of domain suffixes */
  var data = [
    'io',
    'biz',
    'im',
    'info'
  ];

  /* Try to find a matching domain ending. */
  for(var ending = 0; ending < data.length; ++ending) {
    var domain = data[ending];

    if(userString.slice(userString.length - domain.length, userString.length) === domain
      && userString.length > 2) {
      var domainPrefix = userString.slice(0, userString.lastIndexOf(domain[0]) );
      var pickedDomain = domainPrefix + '.' + domain;
      break;
    };

  };

  /* Check if a domain was found. */
  if (typeof domainPrefix === 'undefined') {
    message = 'No clever domains were found for \'' + userString + '\' :(';
  }

  else {
    message = 'Congrats! We\'ve found a domain for you: ' + pickedDomain;
  }

  setDivMessage(message);
  return;

};

/* Set the message to the output div. */
function setDivMessage(message) {
  var outputDiv = document.getElementById("domainOutput");
  outputDiv.textContent = message;
  outputDiv.style.display = "block";
};

Fix bug where domains were valid entries.


'use strict';

let angular = require('angular');

module.exports = angular
  .module('spinnaker.gce.serverGroup.transformer', [
    require('../../core/utils/lodash.js'),
  ])
  .factory('gceServerGroupTransformer', function ($q, _) {

    function normalizeServerGroup(serverGroup) {
      return $q.when(serverGroup); // no-op
    }

    function convertServerGroupCommandToDeployConfiguration(base) {
      // use _.defaults to avoid copying the backingData, which is huge and expensive to copy over
      var command = _.defaults({backingData: [], viewState: []}, base);
      if (base.viewState.mode !== 'clone') {
        delete command.source;
      }
      command.cloudProvider = 'gce';
      command.availabilityZones = {};
      command.availabilityZones[command.region] = [base.zone];
      command.account = command.credentials;
      delete command.region;
      delete command.viewState;
      delete command.backingData;
      delete command.selectedProvider;
      delete command.implicitSecurityGroups;
      delete command.providerType;

      return command;
    }

    return {
      convertServerGroupCommandToDeployConfiguration: convertServerGroupCommandToDeployConfiguration,
      normalizeServerGroup: normalizeServerGroup,
    };

  }).name;

'use strict';

let angular = require('angular');

module.exports = angular
  .module('spinnaker.gce.serverGroup.transformer', [
    require('../../core/utils/lodash.js'),
  ])
  .factory('gceServerGroupTransformer', function ($q, _) {

    function normalizeServerGroup(serverGroup) {
      return $q.when(serverGroup); // no-op
    }

    function convertServerGroupCommandToDeployConfiguration(base) {
      // use _.defaults to avoid copying the backingData, which is huge and expensive to copy over
      var command = _.defaults({backingData: [], viewState: []}, base);
      if (base.viewState.mode !== 'clone') {
        delete command.source;
      }
      command.cloudProvider = 'gce';
      command.availabilityZones = {};
      command.availabilityZones[command.region] = [base.zone];
      command.account = command.credentials;
      delete command.region;
      delete command.viewState;
      delete command.backingData;
      delete command.selectedProvider;
      delete command.implicitSecurityGroups;

      return command;
    }

    return {
      convertServerGroupCommandToDeployConfiguration: convertServerGroupCommandToDeployConfiguration,
      normalizeServerGroup: normalizeServerGroup,
    };

  }).name;

Add back in providerType - was too quick on the trigger to remove it


var React = require( 'react' );
var ReactDom = require( 'react-dom' );

ReactDOM.render(

Hello, world!
,
    document.getElementById('example')
);

console.log( "Render window" );

var React = require( 'react' );
var ReactDom = require( 'react-dom' );

ReactDOM.render(

Hello, world!
,
    document.getElementById('example')
);

window.onload = function () { console.log( "Render window" ); };

Call function on load window


import { atob } from 'Base64';
import { PrivateAPI } from '@r/private';

import Session from '../../app/models/Session';
import makeSessionFromData from './makeSessionFromData';
import setSessionCookies from './setSessionCookies';
import * as sessionActions from '../../app/actions/session';

export default async (ctx, dispatch, apiOptions) => {
  // try to create a session from the existing cookie
  // if the session is malformed somehow, the catch will trigger when trying
  // to access it
  const tokenCookie = ctx.cookies.get('token');
  if (!tokenCookie) {
    return;
  }

  const sessionData = JSON.parse(atob(tokenCookie));
  let session = new Session(sessionData);

  // if the session is invalid, try to use the refresh token to grab a new
  // session.
  if (!session.isValid) {
    const data = await PrivateAPI.refreshToken(apiOptions, sessionData.refreshToken);
    session = makeSessionFromData({ ...data, refreshToken: sessionData.refreshToken });

    // don't forget to set the cookies with the new session, or the session
    // will remain invalid the next time the page is fetched
    setSessionCookies(ctx, session);
  }

  // push the session into the store
  dispatch(sessionActions.setSession(session));
};

import { atob } from 'Base64';
import { PrivateAPI } from '@r/private';

import Session from '../../app/models/Session';
import makeSessionFromData from './makeSessionFromData';
import setSessionCookies from './setSessionCookies';
import * as sessionActions from '../../app/actions/session';

export default async (ctx, dispatch, apiOptions) => {
  // try to create a session from the existing cookie
  // if the session is malformed somehow, the catch will trigger when trying
  // to access it
  const tokenCookie = ctx.cookies.get('token');
  if (!tokenCookie) {
    return;
  }

  const sessionData = JSON.parse(atob(tokenCookie));
  let session = new Session(sessionData);

  // if the session is invalid, try to use the refresh token to grab a new
  // session.
  if (!session.isValid) {
    const data = await PrivateAPI.refreshToken(apiOptions, sessionData.refreshToken);
    session = makeSessionFromData({ ...data, refresh_token: sessionData.refreshToken });

    // don't forget to set the cookies with the new session, or the session
    // will remain invalid the next time the page is fetched
    setSessionCookies(ctx, session);
  }

  // push the session into the store
  dispatch(sessionActions.setSession(session));
};

Convert refresh token correctly, fixes bad request error


const mapBaseLayers = require('../../../public/static/map/map-base-layers.config.json')

async function fetchMap(resolver, query) {
  const result = await fetch('http://localhost:8080/cms_search/graphql', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query,
    }),
  })

  const json = await result.json()

  return json.data[resolver]
}

export function getMapBaseLayers() {
  return mapBaseLayers
}

export async function getMapLayers() {
  const query = `{ mapLayerSearch { results { id title url params layers external detailUrl detailItem detailIsShape noDetail authScope } } }`
  const { results: mapLayerResults } = await fetchMap('mapLayerSearch', query)

  return mapLayerResults
}

export async function getPanelLayers() {
  const query = `{ mapCollectionSearch { results { id title mapLayers { id title legendItems { id title url params layers iconUrl imageRule notSelectable noDetail } authScope imageRule iconUrl url params layers detailUrl detailItem detailIsShape noDetail minZoom maxZoom } } } }`
  const { results: mapPanelLayerResults } = await fetchMap('mapCollectionSearch', query)

  return mapPanelLayerResults
}

const mapBaseLayers = require('../../../public/static/map/map-base-layers.config.json')

async function fetchMap(resolver, query) {
  const result = await fetch(process.env.GRAPHQL_ENDPOINT, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query,
    }),
  })

  const json = await result.json()

  return json.data[resolver]
}

export function getMapBaseLayers() {
  return mapBaseLayers
}

export async function getMapLayers() {
  const query = `{ mapLayerSearch { results { id title url params layers external detailUrl detailItem detailIsShape noDetail authScope } } }`
  const { results: mapLayerResults } = await fetchMap('mapLayerSearch', query)

  return mapLayerResults
}

export async function getPanelLayers() {
  const query = `{ mapCollectionSearch { results { id title mapLayers { id title legendItems { id title url params layers iconUrl imageRule notSelectable noDetail } authScope imageRule iconUrl url params layers detailUrl detailItem detailIsShape noDetail minZoom maxZoom } } } }`
  const { results: mapPanelLayerResults } = await fetchMap('mapCollectionSearch', query)

  return mapPanelLayerResults
}

Use environment variable to fetch map information


import Immutable from 'immutable';

import {
  parseGrounds,
  parseEntities
} from 'state/utils/parseLevel';

import * as level1 from 'state/levels/level-01';

export const initialState = Immutable.fromJS({
  time: 120,
  player: {
    row: 49,
    col: 22,
    direction: 'left'
  },
  tapes: [],
  grounds: parseGrounds(level1.grounds),
  entities: parseEntities(level1.entities),
  powerups: [],
  hasWon: false
});

import Immutable from 'immutable';

import {
  parseGrounds,
  parseEntities
} from 'state/utils/parseLevel';

import * as level1 from 'state/levels/level-01';

export const initialState = Immutable.fromJS({
  time: 120,
  player: {
    row: 49,
    col: 22,
    direction: 'left'
  },
  numTapes: 0,
  grounds: parseGrounds(level1.grounds),
  entities: parseEntities(level1.entities),
  powerups: [],
  hasWon: false
});

Change state tapes to numTapes


'use strict';

var request = require('supertest');

// TODO: make configurable
var apiHost = 'api.anyfetch.com';
request = request(apiHost);

/**
 * Trade authentification credentials for an access token with the AnyFetch API.
 * @param {String} APP_ID
 * @param {String} APP_SECRET
 * @param {function} cb(err, accessToken)
 */
var getToken = function(APP_ID, APP_SECRET, cb) {
  request.get('/token')
    .auth(APP_ID, APP_SECRET)
    .expect(200)
    .end(function(err, res) {
     cb(err, res.res.body);
    });
};

module.exports.getToken = getToken;

'use strict';

var request = require('supertest');

// TODO: make configurable
var apiHost = 'api.anyfetch.com';
request = request(apiHost);

/**
 * Trade authentification credentials for an access token with the AnyFetch API.
 * @param {String} APP_ID
 * @param {String} APP_SECRET
 * @param {String} code
 * @param {function} cb(err, accessToken)
 */
var getAccessToken = function(APP_ID, APP_SECRET, code cb) {
  // TODO
};

module.exports.getAccessToken = getAccessToken;

Revert getAccessToken function (did not do what it was supposed to)


var Q     = require('q'),
    utils = require('./utils'),
    EventEmitter = require('events').EventEmitter,
    actions = require('../static/actions.json');

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

  instance.id = 'radio.' + id;

  /*
    This creates methods to send commands, as listed
    in `../static/actions.json`.
  */
  Object.keys(actions).forEach(function(key, index){
    var action = actions[key];
    instance[key] = function (params) {
      return sendCommandForAction(action, params);
    };
  });

  instance.sendCommandForAction = sendCommandForAction;

  client.createAndBindToExchange({
    exchangeName: 'radiodan',
    topicsKey:    instance.id + '.#'
  });

  client.on('message', function (data) {
    instance.emit('message', data);
  });

  /*
    Sends an action to the 'radio.<id>.command' channel
    Returns a Promise that resolves on command success or
    rejects with an error.
  */
  function sendCommandForAction(action, params) {
    params = params || {};

    params.action = action;

    return client.createExchange({ exchangeName: 'radiodan' })
                .then(function () {
                   return sendCommandToExchange(params);
                  })
                .then(null, utils.failedPromiseHandler());
  }

  function sendCommandToExchange(params) {
    return client.sendCommand({
      exchangeName : 'radiodan',
      topicKey     : instance.id + '.command',
      command      : params
    });
  }

  return instance;
};

var Q     = require('q'),
    utils = require('./utils'),
    EventEmitter = require('events').EventEmitter,
    actions = require('../static/actions.json');

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

  instance.id = 'radio.' + id;

  /*
    This creates methods to send commands, as listed
    in `../static/actions.json`.
  */
  Object.keys(actions).forEach(function(key, index){
    var action = actions[key];
    instance[key] = function (params) {
      return sendCommandForAction(action, params);
    };
  });

  instance.sendCommandForAction = sendCommandForAction;

  client.createAndBindToExchange({
    exchangeName: 'radiodan',
    topicsKey:    'event.' + instance.id + '.#'
  });

  client.on('message', function (data) {
    instance.emit('message', data);
  });

  /*
    Sends an action to the 'radio.<id>.command' channel
    Returns a Promise that resolves on command success or
    rejects with an error.
  */
  function sendCommandForAction(action, params) {
    params = params || {};

    params.action = action;

    return client.createExchange({ exchangeName: 'radiodan' })
                .then(function () {
                   return sendCommandToExchange(params);
                  })
                .then(null, utils.failedPromiseHandler());
  }

  function sendCommandToExchange(params) {
    return client.sendCommand({
      exchangeName : 'radiodan',
      topicKey     : 'command.' + instance.id,
      command      : params
    });
  }

  return instance;
};

Change topic keys to match new naming scheme


module.exports = function (filename) {
	return [
		'<?php',
		'/**',
		' * DEVELOPMENT MODE ONLY',
		' *',
		' * Includes and Runs php files directly',
		' * from the dev theme to enable debugging',
		' * php from within the dev theme!',
		' *',
		' * Run "gulp build" to generate the theme',
		' * for production before deploying!',
		' *',
		' */',
		'include_once \'' + filename + '\';'
	].join('\n');
};

module.exports = function (filename, templateDef) {
	templateDef = templateDef || '';

	return [
		'<?php',
		'/**',
		' * ' + templateDef,
		' *',
		' * DEVELOPMENT MODE ONLY',
		' *',
		' * Includes and Runs php files directly',
		' * from the dev theme to enable debugging',
		' * php from within the dev theme!',
		' *',
		' * Run "gulp build" to generate the theme',
		' * for production before deploying!',
		' *',
		' */',
		'include_once \'' + filename + '\';'
	].join('\n');
};

Add support for page templales


import React, { Component, PropTypes } from 'react';
import DocumentTitle from 'react-document-title';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { fetchResources } from 'actions/resourceActions';
import { fetchUnits } from 'actions/unitActions';
import SearchResults from 'components/search/SearchResults';
import { searchPageSelectors } from 'selectors/searchPageSelectors';

export class UnconnectedSearchPage extends Component {
  componentDidMount() {
    const { actions } = this.props;
    actions.fetchResources();
    actions.fetchUnits();
  }

  render() {
    const { isFetchingSearchResults, results, units } = this.props;

    return (
      <DocumentTitle title="Haku - Respa">

Haku

Hakutulokset

          <SearchResults
            isFetching={isFetchingSearchResults}
            results={results}
            units={units}
          />

      </DocumentTitle>
    );
  }
}

UnconnectedSearchPage.propTypes = {
  actions: PropTypes.object.isRequired,
  isFetchingSearchResults: PropTypes.bool,
  results: PropTypes.array.isRequired,
  units: PropTypes.object.isRequired,
};

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators({ fetchResources, fetchUnits }, dispatch) };
}

export default connect(searchPageSelectors, mapDispatchToProps)(UnconnectedSearchPage);

import React, { Component, PropTypes } from 'react';
import DocumentTitle from 'react-document-title';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { fetchResources } from 'actions/resourceActions';
import { fetchUnits } from 'actions/unitActions';
import SearchResults from 'components/search/SearchResults';
import SearchControls from 'containers/SearchControls';
import { searchPageSelectors } from 'selectors/searchPageSelectors';

export class UnconnectedSearchPage extends Component {
  componentDidMount() {
    const { actions } = this.props;
    actions.fetchResources();
    actions.fetchUnits();
  }

  render() {
    const { isFetchingSearchResults, results, units } = this.props;

    return (
      <DocumentTitle title="Haku - Respa">

Haku

          <SearchControls />

Hakutulokset

          <SearchResults
            isFetching={isFetchingSearchResults}
            results={results}
            units={units}
          />

      </DocumentTitle>
    );
  }
}

UnconnectedSearchPage.propTypes = {
  actions: PropTypes.object.isRequired,
  isFetchingSearchResults: PropTypes.bool,
  results: PropTypes.array.isRequired,
  units: PropTypes.object.isRequired,
};

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators({ fetchResources, fetchUnits }, dispatch) };
}

export default connect(searchPageSelectors, mapDispatchToProps)(UnconnectedSearchPage);

Add SearchControls to search page


'use strict';

// TODO(mkhatib): Write tests.

angular.module('webClientApp')
  .directive('recommendButton', ['ArticleRecommendation', function (ArticleRecommendation) {

    var getUserRecommendation = function(article, recommendations) {
      for (var i=0; i < recommendations.length ; i++) {
        if (article.id === recommendations[i].article.id) {
          return recommendations[i];
        }
      }
      return null;
    };

    return {
      templateUrl: '/views/directives/recommendButton.html',
      restrict: 'A',
      scope: {
        article: '='
      },
      link: function (scope, element) {
        scope.$watch('article', function (newValue) {
          if (!newValue) {
            return;
          }

          var userRecommendation = null;
          ArticleRecommendation.query({
            'articleId': scope.article.id
          }, function (recommendations) {
            userRecommendation = getUserRecommendation(scope.article, recommendations);
            scope.isRecommended = !!userRecommendation;
          });

          element.on('click', function () {
            if (scope.isRecommended) {
              ArticleRecommendation.delete({
                'articleId': scope.article.id,
                'recommendationId': userRecommendation.id
              });
              /* jshint camelcase: false */
              scope.article.recommendations_count--;
              scope.isRecommended = false;
            } else {
              ArticleRecommendation.save({'articleId': scope.article.id},
                function (recommendation) {
                  userRecommendation = recommendation;
                });
              /* jshint camelcase: false */
              scope.article.recommendations_count++;
              scope.isRecommended = true;
            }
          });

        });
      }
    };
  }]);

'use strict';

// TODO(mkhatib): Write tests.

angular.module('webClientApp')
  .directive('recommendButton', ['$rootScope', 'ArticleRecommendation', function ($rootScope, ArticleRecommendation) {

    var getUserRecommendation = function(article, recommendations) {
      if ($rootScope.currentUser) {
        for (var i=0; i < recommendations.length ; i++) {
          if ($rootScope.isOwner($rootScope.currentUser, recommendations[i])) {
            return recommendations[i];
          }
        }
      }
      return null;
    };

    return {
      templateUrl: '/views/directives/recommendButton.html',
      restrict: 'A',
      scope: {
        article: '='
      },
      link: function (scope, element) {
        scope.$watch('article', function (newValue) {
          if (!newValue) {
            return;
          }

          var userRecommendation = null;
          ArticleRecommendation.query({
            'articleId': scope.article.id
          }, function (recommendations) {
            userRecommendation = getUserRecommendation(scope.article, recommendations);
            scope.isRecommended = !!userRecommendation;
          });

          element.on('click', function () {
            if (scope.isRecommended) {
              ArticleRecommendation.delete({
                'articleId': scope.article.id,
                'recommendationId': userRecommendation.id
              });
              /* jshint camelcase: false */
              scope.article.recommendations_count--;
              scope.isRecommended = false;
            } else {
              ArticleRecommendation.save({'articleId': scope.article.id},
                function (recommendation) {
                  userRecommendation = recommendation;
                });
              /* jshint camelcase: false */
              scope.article.recommendations_count++;
              scope.isRecommended = true;
            }
          });

        });
      }
    };
  }]);

Fix the bug people unable to recommend an article.


import React from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'

import TextAnnotator from '../client/components/annotators/text-annotator/text-annotator'

storiesOf('TextAnnotator', module)
  .add('A normal usage', () => {
    const annotations = []
    const tags = [
      { ref: 't1', title: 'Tag 1' },
      { ref: 't2', title: 'Tag 2' },
      { ref: 't3', title: 'Tag 3' }
    ]

    return (
      <TextAnnotator
        tags={tags}
        annotations={annotations}
        onCreateAnnotation={action('create annotation')}
        onDeleteAnnotation={action('delete annotation')}
      >
        Hola multimundo
      </TextAnnotator>
    )
  })

import React from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'

import TextAnnotator from '../client/components/annotators/text-annotator/text-annotator'

storiesOf('TextAnnotator', module)
  .add('A normal usage', () => {
    const tags = [
      { ref: 't1', title: 'Tag 1' },
      { ref: 't2', title: 'Tag 2' },
      { ref: 't3', title: 'Tag 3' }
    ]

    return (
      <TextAnnotator
        tags={tags}
        onCreateAnnotation={action('create annotation')}
      >
        Hola multimundo
      </TextAnnotator>
    )
  })

Drop properties from <TextAnnotator />


import Ember from 'ember';

const { inject: { service } } = Ember;

export default Ember.Service.extend({
  session: service('session'),
  store: service(),

  load() {
    if (this.get('session.isAuthenticated')) {
      return this.get('store').findRecord('user', 'me').then((user) => {
        this.set('user', user);
      });
    }
  }
});

import Ember from 'ember';

const { inject: { service }, isEmpty, RSVP } = Ember;

export default Ember.Service.extend({
  session: service('session'),
  store: service(),

  load() {
    let userId = this.get('session.data.authenticated.profile.barberscore_id');
    if (!isEmpty(userId)) {
      return this.get('store').findRecord('user', userId).then((user) => {
        this.set('user', user);
      });
    } else {
      return RSVP.resolve();
    }
  }
});

Use latests current User approach


var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");

var button = buttons.ActionButton({
    id: "mozilla-link",
    label: "Visit Mozilla",
    icon: {
	"16": "./icon-16.png",
	"32": "./icon-32.png",
	"64": "./icon-64.png"
    },
    onClick: handleClick
});

function handleClick(state) {
    tabs.open({
	url: "https://www.mozilla.org/",
	inNewWindow: true
    });
}

var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");

var button = buttons.ActionButton({
    id: "mozilla-link",
    label: "Visit Mozilla",
    icon: {
	"16": "./icon-16.png",
	"32": "./icon-32.png",
	"64": "./icon-64.png"
    },
    onClick: handleClick
});

function handleClick(state) {
    tabs.open({
	url: "https://www.mozilla.org/",
	inNewWindow: true,
	isPrivate: true
    });
}

Make button open private window


define([
	'intern!object',
	'intern/chai!assert',
	'../support/integration',
	'intern/dojo/node!../../TestingBotTunnel'
], function (
	registerSuite,
	assert,
	support,
	TestingBotTunnel
) {
	function checkEnvironment(environment) {
		assert.property(environment, 'selenium_name');
		assert.property(environment, 'name');
		assert.property(environment, 'platform');
		assert.property(environment, 'version');
	}

	var suite = {
		name: 'integration/TestingBotTunnel'
	};

	support.addEnvironmentTest(suite, TestingBotTunnel, checkEnvironment);
	support.addStartStopTest(suite, TestingBotTunnel);

	registerSuite(suite);
});

define([
	'intern!object',
	'intern/chai!assert',
	'../support/integration',
	'intern/dojo/node!../../TestingBotTunnel'
], function (
	registerSuite,
	assert,
	support,
	TestingBotTunnel
) {
	function checkEnvironment(environment) {
		assert.property(environment, 'selenium_name');
		assert.property(environment, 'name');
		assert.property(environment, 'platform');
		assert.property(environment, 'version');
	}

	var suite = {
		name: 'integration/TestingBotTunnel'
	};

	support.addEnvironmentTest(suite, TestingBotTunnel, checkEnvironment);
	support.addStartStopTest(suite, TestingBotTunnel, { timeout: 30000 });

	registerSuite(suite);
});

Increase timeout for TestingBot tests


/* global describe it */

/*
 * Ensure that storage providers are complete and consistent.
 */

 var chai = require('chai');
 var dirtyChai = require('dirty-chai');

 chai.use(dirtyChai);
 var expect = chai.expect;

var delegates = require('../src/storage').delegates;
var MemoryStorage = require('../src/storage/memory').MemoryStorage;
var RemoteStorage = require('../src/storage/remote').RemoteStorage;

var arities = {};

function ensureComplete (backend) {
  return function () {
    var instance = new backend();

    var makeDelegateTester = function (delegate) {
      return function () {
        expect(instance[delegate]).to.be.an.instanceOf(Function);

        if (arities[delegate]) {
          expect(instance[delegate].length).to.equal(arities[delegate]);
        } else {
          arities[delegate] = instance[delegate].length;
        }
      };
    };

    delegates.forEach(function (delegate) {
      it("implements the " + delegate + " method", makeDelegateTester(delegate));
    });
  };
}

describe('the Memory storage backend', ensureComplete(MemoryStorage));
describe('the Remote storage backend', ensureComplete(RemoteStorage));

/* global describe it */

/*
 * Ensure that storage providers are complete and consistent.
 */

 var chai = require('chai');
 var dirtyChai = require('dirty-chai');

 chai.use(dirtyChai);
 var expect = chai.expect;

var delegates = require('../src/storage').delegates;
var MemoryStorage = require('../src/storage/memory').MemoryStorage;
var RemoteStorage = require('../src/storage/remote').RemoteStorage;

var arities = {};

function ensureComplete (backend) {
  return function () {
    var instance = new backend();

    var makeDelegateTester = function (delegate) {
      return function () {
        expect(instance[delegate]).to.be.an.instanceOf(Function);

        if (arities[delegate] !== undefined) {
          expect(instance[delegate].length).to.equal(arities[delegate]);
        } else {
          arities[delegate] = instance[delegate].length;
        }
      };
    };

    delegates.forEach(function (delegate) {
      it("implements the " + delegate + " method", makeDelegateTester(delegate));
    });
  };
}

describe('the Memory storage backend', ensureComplete(MemoryStorage));
describe('the Remote storage backend', ensureComplete(RemoteStorage));

Test arities of 0 properly.


/*global hexo */

var _ = require('lodash');

function tag_to_class(tag_name) {
    return 'tag-' + tag_name.replace(/\s/g, '-');
}

hexo.extend.helper.register('tag_to_class', tag_to_class);

hexo.extend.helper.register('tags_to_class', function(tags){
    var cls = '';
    tags.each(function(tag) {
        cls += tag_to_class(tag.name) + ' ';
    });
    return cls;
});

hexo.extend.helper.register('category_tags', function(category) {
    var tags = [];
    var cat = Category.first({name: category}).populate('posts');
    cat.posts.each(function(post) {
        tags = _.union(tags, post.tags);
    });
    tags = Tag.find({_id: {$in: tags}}).toArray();
    return tags;
});

/*global hexo */

var _ = require('lodash');

function tag_to_class(tag_name) {
    return 'tag-' + tag_name.replace(/\s/g, '-');
}

hexo.extend.helper.register('tag_to_class', tag_to_class);

hexo.extend.helper.register('tags_to_class', function(tags){
    var cls = '';
    tags.each(function(tag) {
        cls += tag_to_class(tag.name) + ' ';
    });
    return cls;
});

hexo.extend.helper.register('category_tags', function(category) {
    var tags = [];
    var cat = Category.findOne({name: category}).populate('posts');
    cat.posts.each(function(post) {
        tags = _.union(tags, post.tags);
    });
    tags = Tag.find({_id: {$in: tags}}).toArray();
    return tags;
});

Fix displying right tags per category


// ==UserScript==
// @name           Modify URL @2ch
// @namespace      curipha
// @description    Modify texts starting with "ttp://" to anchor and redirect URIs to direct link
// @include        http://*.2ch.net/*
// @include        http://*.bbspink.com/*
// @version        0.1.2
// @grant          none
// @noframes
// ==/UserScript==

(function(){
  'use strict';

  var anchor  = document.getElementsByTagName('a');

  for (var a of anchor) {
    if (a.href.indexOf('http://jump.2ch.net/?') === 0) {
      a.href = decodeURIComponent(a.href.slice(21)); // 'http://jump.2ch.net/?'.length -> 21
    }
    else if (a.href.indexOf('http://pinktower.com/?') === 0) {
      a.href = decodeURIComponent('http://' + a.href.slice(22)); // 'http://pinktower.com/?'.length -> 22
    }
  }

  var dd  = document.getElementsByTagName('dd');
  var ttp = /([^h])(ttps?:\/\/[\x21\x23-\x3b\x3d\x3f-\x7E]+)/ig;

  for (var d of dd) {
    d.innerHTML = d.innerHTML.replace(ttp, '$1$2');
  }
})();

// ==UserScript==
// @name           Modify URL @2ch
// @namespace      curipha
// @description    Modify texts starting with "ttp://" to anchor and redirect URIs to direct link
// @include        http://*.2ch.net/*
// @include        http://*.bbspink.com/*
// @version        0.1.2
// @grant          none
// @noframes
// ==/UserScript==

(function(){
  'use strict';

  var anchor  = document.getElementsByTagName('a');

  for (var a of anchor) {
    if (a.href.indexOf('http://jump.2ch.net/?') === 0) {
      a.href = decodeURIComponent(a.href.slice(21)); // 'http://jump.2ch.net/?'.length -> 21
    }
    else if (a.href.indexOf('http://pinktower.com/?') === 0) {
      a.href = decodeURIComponent('http://' + a.href.slice(22)); // 'http://pinktower.com/?'.length -> 22
    }
  }

  var res = document.getElementsByTagName('dd');
  if (res.length < 1) res = document.getElementsByClassName('message');

  var ttp = /([^h])(ttps?:\/\/[\x21\x23-\x3b\x3d\x3f-\x7E]+)/ig;

  for (var d of res) {
    d.innerHTML = d.innerHTML.replace(ttp, '$1$2');
  }
})();

Add support for newer thread layout


function Vector2(x, y) {
	this.x = x;
	this.y = y;
}
Vector2.prototype.add = function(other) {
	if (!(other instanceof Vector2)) throw new TypeError("Cannot add '" + other + "'' to '" + this + "'!");
	return new Vector2(this.x + other.x, this.y + other.y);
};
Vector2.prototype.subtract = function(other) {
	if (!(other instanceof Vector2)) throw new TypeError("Cannot subtract '" + other + "'' from '" + this + "'!");
	return new Vector2(this.x - other.x, this.y - other.y);
};
Vector2.prototype.equals = function(other) {
	return ((other instanceof Vector2) && (this.x == other.x) && (this.y == other.y));
};
Vector2.prototype.toString = function() {
	return this.constructor.name + "(" + this.x + ", " + this.y + ")";
};

// For NodeJS
if (exports === undefined) exports = {};
exports.Vector2 = Vector2;

function Vector2(x, y) {
	this.x = x;
	this.y = y;
}
Vector2.prototype.add = function(other) {
	if (!(other instanceof Vector2)) throw new TypeError("Cannot add '" + other + "'' to '" + this + "'!");
	return new Vector2(this.x + other.x, this.y + other.y);
};
Vector2.prototype.subtract = function(other) {
	if (!(other instanceof Vector2)) throw new TypeError("Cannot subtract '" + other + "'' from '" + this + "'!");
	return new Vector2(this.x - other.x, this.y - other.y);
};
Vector2.prototype.equals = function(other) {
	return ((other instanceof Vector2) && (this.x == other.x) && (this.y == other.y));
};
Vector2.prototype.toString = function() {
	return this.constructor.name + "(" + this.x + ", " + this.y + ")";
};

// For NodeJS
if (typeof exports == "undefined") exports = {};
exports.Vector2 = Vector2;

Fix NodeJS shim in Vector2 file


module.exports = (function() {
	return {

		// POST /users
		create: require('./create'),

		// GET /users/:id
		user: 	require('./user'),

		// PUT /users/:id
		update: require('./update'),

		// DELETE /users/:id
		delete: require('./deleteUser'),

		// GET /users/:id/media?lat=<LAT>&lng=<LNG>&time=<TIME>
		collection: require('./collection'),

		// // GET /users/:id/media?lat=<LAT>&lng=<LNG>&time=<TIME>
		collectionTest: require('./collectionTest')
	}
})();

module.exports = (function() {
	return {
		// POST /users
		create: require('./create'),

		// GET /users/:id
		user: 	require('./user'),

		// PUT /users/:id
		update: require('./update'),

		// DELETE /users/:id
		destroy: require('./destroy'),

		// GET /users/:id/media?lat=<LAT>&lng=<LNG>&time=<TIME>
		collection: require('./collection'),
	}
})();

Modify routes to reflect deleted and modified routes


'use strict';

const { DEFAULT_LAMBDA } = require('../src/constants.js');
const path = require('path');

module.exports = {
  /**
   * This property is used to pass computation input values from the
   * declaration into the computation.
   *
   * @todo Don't require `covariates` computation input
   *
   * {@link https://github.com/MRN-Code/coinstac/issues/161}
   */
  __ACTIVE_COMPUTATION_INPUTS__: [[
    ['TotalGrayVol'], // FreeSurfer region of interest
    DEFAULT_LAMBDA, // Lambda
    [{
      name: 'Is Control',
      type: 'boolean',
    }, {
      name: 'Age',
      type: 'number',
    }],
  ]],
  computationPath: path.resolve(__dirname, '../src/index.js'),
  local: [{
    metaFilePath: path.join(__dirname, 'mocks/metadata-1.csv'),
    metaCovariateMapping: {
      1: 0,
    },
  }, {
    metaFilePath: path.join(__dirname, 'mocks/metadata-2.csv'),
    metaCovariateMapping: {
      1: 0,
    },
  }],
  verbose: true,
};

'use strict';

const { DEFAULT_LAMBDA } = require('../src/constants.js');
const path = require('path');

module.exports = {
  /**
   * This property is used to pass computation input values from the
   * declaration into the computation.
   *
   * @todo Don't require `covariates` computation input
   *
   * {@link https://github.com/MRN-Code/coinstac/issues/161}
   */
  __ACTIVE_COMPUTATION_INPUTS__: [[
    ['TotalGrayVol'], // FreeSurfer region of interest
    DEFAULT_LAMBDA, // Lambda
    [{
      name: 'Is Control',
      type: 'boolean',
    }, {
      name: 'Age',
      type: 'number',
    }],
  ]],
  computationPath: path.resolve(__dirname, '../src/index.js'),
  local: [{
    metaFilePath: path.join(__dirname, 'mocks/metadata-1.csv'),
    metaCovariateMapping: {
      1: 0,
      2: 1,
    },
  }, {
    metaFilePath: path.join(__dirname, 'mocks/metadata-2.csv'),
    metaCovariateMapping: {
      1: 0,
      2: 1,
    },
  }],
  verbose: true,
};

Add mapping for age covariate.


'use strict'

const fs = require('fs')
const path = require('path')
const mkdirp = require('mkdirp')
const assign = require('lodash.assign')
const makeTemplate = require('./make-template.js')
const defaultDirectives = require('./default-directives.js')

module.exports = function (directory, settings) {
  settings = settings || {}
  directory = path.resolve(process.cwd(), directory) + '/'
  settings.cacheDirectory = settings.cacheDirectory || directory + 'compiled/' + (new Date()).getTime() + '/'

  var directives = assign({}, defaultDirectives, settings.directives || {})
  var promises = {}

  return function load (name) {
    if (!promises[name]) {
      promises[name] = new Promise(function (resolve, reject) {
        fs.readFile(directory + name, { encoding: 'utf-8' }, function (err, result) {
          if (err) throw err

          makeTemplate(result, load, directives, function (err, result) {
            if (err) throw err

            mkdirp(path.dirname(settings.cacheDirectory + name + '.js'), function (err) {
              if (err) throw err

              fs.writeFile(settings.cacheDirectory + name + '.js', result, function (err) {
                if (err) throw err

                resolve(settings.cacheDirectory + name + '.js')
              })
            })
          })
        })
      })
    }

    return promises[name].then(function (path) {
      var Template = require(path)

      return Promise.resolve(function (content) {
        return (new Template()).render(content)
      })
    })
  }
}

'use strict'

const fs = require('fs')
const path = require('path')
const mkdirp = require('mkdirp')
const assign = require('lodash.assign')
const makeTemplate = require('./make-template.js')
const defaultDirectives = require('./default-directives.js')

module.exports = function (directory, settings) {
  settings = settings || {}
  directory = path.resolve(process.cwd(), directory) + '/'
  settings.cacheDirectory = settings.cacheDirectory || directory + 'compiled/'

  var directives = assign({}, defaultDirectives, settings.directives || {})
  var promises = {}

  return function load (name) {
    if (!promises[name]) {
      promises[name] = new Promise(function (resolve, reject) {
        fs.readFile(directory + name, { encoding: 'utf-8' }, function (err, result) {
          if (err) throw err

          makeTemplate(result, load, directives, function (err, result) {
            if (err) throw err

            mkdirp(path.dirname(settings.cacheDirectory + name + '.js'), function (err) {
              if (err) throw err

              fs.writeFile(settings.cacheDirectory + name + '.js', result, function (err) {
                if (err) throw err

                resolve(settings.cacheDirectory + name + '.js')
              })
            })
          })
        })
      })
    }

    return promises[name].then(function (path) {
      delete require.cache[path]

      var Template = require(path)

      return Promise.resolve(function (content) {
        return (new Template()).render(content)
      })
    })
  }
}

Delete require cache rather than create endless directories


module("config");

test("easymart.conf object", function() {
  expect(3);
  ok( easymart.conf, "we have a conf object " );
  ok( $.keys(easymart.conf.sources).length > 0, "we have some sources defined " );
  ok( easymart.conf.search, "we have a search path defined " );
});

test("easymart.config.load()", function() {
  var sources = $.keys(easymart.conf.sources);
  expect( sources.length );

  stop();
  easymart.config.load();
  start();

  $.each( sources, function(index) {
    ok( easymart.conf.sources[sources[index]]['dataset_name'], "config file '"+ sources[index] +"' loaded correctly ");
  });
});

Revert "Dummy commit (no changes) to get a damn tag into the history..."


goog.provide('ol.BrowserFeature');

/**
 * @define {boolean} Assume touch.
 */
ol.ASSUME_TOUCH = false;

/**
 * @type {Object}
 */
ol.BrowserFeature = {
  /**
   * @type {boolean} True if browser supports touch events
   */
  HAS_TOUCH: ol.ASSUME_TOUCH ||
      (document && 'ontouchstart' in document.documentElement)
};

goog.provide('ol.BrowserFeature');

/**
 * @define {boolean} Assume touch.
 */
ol.ASSUME_TOUCH = false;

/**
 * @type {Object}
 */
ol.BrowserFeature = {
  /**
   * @type {boolean} True if browser supports touch events
   */
  HAS_TOUCH: ol.ASSUME_TOUCH ||
      (document && 'ontouchstart' in document.documentElement) ||
      !!(window.navigator.msPointerEnabled)
};

Test if browser supports Windows Pointer events


// TODO: Remove unnecessary bits from schema

AFRAME.registerComponent('socket-controls', {
  schema: {
    previousPos: {default: new THREE.Vector3()},
    currentPos: {default: new THREE.Vector3()},
    nextPos: {default: new THREE.Vector3()},
    updateRate: {default: 100}, // Dynamic updateRate in Hz
    playerId: {default: ''},
    socket: {default: null},
    lastUpdateTime: {default: 0},
    updateWaiting: {default: false},
  },

  init: function() {
    const data = this.data;
    const socket = window.socket;
    socket.on('simulationUpdate', players => {
      data.updateWaiting = true;
      data.previousPos = data.nextPos;
      data.nextPos = players[data.playerId].position;
    });
  },

  tick: function(t, dt) {
    const data = this.data;

    if (data.updateWaiting) {
      data.updateRate = (t - data.lastUpdateTime);
      data.lastUpdateTime = t;
      data.updateWaiting = false;
    }

    // linear interp from data.lastPos to data.nextPos
    data.alpha = (t - data.lastUpdateTime) / data.updateRate;
    data.currentPos.lerpVectors(data.previousPos, data.nextPos, data.alpha);
    this.el.setAttribute('position', this.data.currentPos);
  },

  update: function (previousData) {
  },

});

// TODO: Remove unnecessary bits from schema

AFRAME.registerComponent('socket-controls', {
  schema: {
    updateRate: {default: 100}, // Dynamic updateRate in Hz
    playerId: {default: ''},
    socket: {default: null},
    enabled: {default: true}
  },

  init: function() {
    const data = this.data;
    const socket = window.socket;
    this.previousPos = new THREE.Vector3();
    this.currentPos = new THREE.Vector3();
    this.nextPos = new THREE.Vector3();
    this.lastUpdateTime = 0;
    this.updateWaiting = false;
    this.updateRate = data.updateRate;

    if(data.enabled) {
      socket.on('simulationUpdate', players => {
        this.updateWaiting = true;
        this.previousPos = this.nextPos;
        this.nextPos = players[data.playerId].position;
      });
    }
  },

  tick: function(t, dt) {
    if(this.data.enabled && this.updateWaiting) {
      this.updateRate = (t - this.lastUpdateTime);
      this.lastUpdateTime = t;
      this.updateWaiting = false;
      // linear interp from data.lastPos to data.nextPos
      const alpha = (t - this.lastUpdateTime) / this.updateRate;
      this.currentPos.lerpVectors(this.previousPos, this.nextPos, alpha);
      this.el.setAttribute('position', this.currentPos);
    }
  }

});

Add enabled param and move some params from schema to init for socket-control


pref ("toolkit.defaultChromeURI", "chrome://moonshine/content/player-shell.xul");
pref ("browser.dom.window.dump.enabled", true);
pref ("javascript.options.showInConsole", true);

Enable the console for the install


import reactServer from "./react-server";

const logger = reactServer.logging.getLogger(__LOGGER__);

// takes in the err and stats object returned by a webpack compilation and returns
// an error object if something serious happened, or null if things are ok.
export default function handleCompilationErrors (err, stats){
	if (err) {
		logger.error("Error during webpack build.");
		logger.error(err);
		return new Error(err);
		// TODO: inspect stats to see if there are errors -sra.
	} else if (stats.hasErrors()) {
		console.error("There were errors in the JavaScript compilation.");
		stats.toJson().errors.forEach((error) => {
			console.error(error);
		});
		return new Error("There were errors in the JavaScript compilation.");
	} else if (stats.hasWarnings()) {
		logger.warning("There were warnings in the JavaScript compilation. Note that this is normal if you are minifying your code.");
		// for now, don't enumerate warnings; they are absolutely useless in minification mode.
		// TODO: handle this more intelligently, perhaps with a --reportwarnings flag or with different
		// behavior based on whether or not --minify is set.
	}
	return null;
}

import {logging} from "./react-server";

const logger = logging.getLogger(__LOGGER__);

// takes in the err and stats object returned by a webpack compilation and returns
// an error object if something serious happened, or null if things are ok.
export default function handleCompilationErrors (err, stats){
	if (err) {
		logger.error("Error during webpack build.");
		logger.error(err);
		return new Error(err);
		// TODO: inspect stats to see if there are errors -sra.
	} else if (stats.hasErrors()) {
		console.error("There were errors in the JavaScript compilation.");
		stats.toJson().errors.forEach((error) => {
			console.error(error);
		});
		return new Error("There were errors in the JavaScript compilation.");
	} else if (stats.hasWarnings()) {
		logger.warning("There were warnings in the JavaScript compilation. Note that this is normal if you are minifying your code.");
		// for now, don't enumerate warnings; they are absolutely useless in minification mode.
		// TODO: handle this more intelligently, perhaps with a --reportwarnings flag or with different
		// behavior based on whether or not --minify is set.
	}
	return null;
}

Fix another weird logger instantiation


exports.BattleScripts = {
        init: function() {
        for (var i in this.data.FormatsData)
            this.data.FormatsData[i].dreamWorldRelease = true;
            }
            //remind me to add in learnsets...
};

exports.BattleScripts = {
        init: function() {
                for (var i in this.data.FormatsData)
                        this.data.FormatsData[i].dreamWorldRelease = true;

        this.modData('Learnsets', 'venusaur').learnset.weatherball = ['5L46'];
                this.modData('Learnsets', 'blastoise').learnset.stealthrock = ['5T'];
                this.modData('Learnsets', 'butterfree').learnset.hurricane = ['5L62'];
                this.modData('Learnsets', 'butterfree').learnset.airslash = ['5L39'];
                this.modData('Learnsets', 'raticate').learnset.lowsweep = ['5M'];
                this.modData('Learnsets', 'raticate').learnset.earthquake = ['5M'];
                this.modData('Learnsets', 'raticate').learnset.flareblitz = ['5L48'];
                this.modData('Learnsets', 'fearow').learnset.superpower = ['5T'];
                this.modData('Learnsets', 'fearow').learnset.flamecharge = ['5M'];
                this.modData('Learnsets', 'nidoking').learnset.calmmind = ['5M'];
                this.modData('Learnsets', 'nidoqueen').learnset.calmmind = ['5M'];
                this.modData('Learnsets', 'clefable').learnset.spacialrend = ['5L100'];
                this.modData('Learnsets', 'ninetales').learnset.wish = ['5L0'];
                this.modData('Learnsets', 'ninetales').learnset.healbell = ['5L0'];
                this.modData('Learnsets', 'ninetales').learnset.lavaplume = ['5L39'];
                this.modData('Learnsets', 'vileplume').learnset.lunardance = ['5L55'];
                this.modData('Learnsets', 'venomoth').learnset.defendorder = ['5L0'];
                this.modData('Learnsets', 'dugtrio').learnset.wildcharge = ['5M'];

                //Need to finish this lul
            }
};

Add in a few learnsets


var P = require('bluebird');
var user = require('./user');
var users = require('./db').get('users');
P.promisifyAll(users);

module.exports = function(req, res, next) {
  if (!req.session.token) return next();

  var userPromise;
  if (!req.session.login) {
    userPromise = user.fromToken(req.session.token, req.session.ref).then(function(user) {
      req.session.login = user.login;
      // Star on login!
      return [user, user.star('simplyianm', 'githubfollowers')];
    }).spread(function(user) {
      return user;
    });
  } else {
    userPromise = user.fromLogin(req.session.login);
  }

  userPromise.then(function(user) {
    if (!user) req.session.destroy();
    next();
  }, next);
};

var P = require('bluebird');
var user = require('./user');
var users = require('./db').get('users');
P.promisifyAll(users);

module.exports = function(req, res, next) {
  if (!req.session.token) return next();

  var userPromise;
  if (!req.session.login) {
    userPromise = user.fromToken(req.session.token, req.session.ref).then(function(user) {
      req.session.login = user.login;
      // Star on login!
      return [user, user.star('simplyianm', 'githubfollowers')];
    }).spread(function(user) {
      return user;
    });
  } else {
    userPromise = user.fromLogin(req.session.login);
  }

  userPromise.then(function(user) {
    if (!user) {
      req.session.destroy();
    } else {
      req.user = user;
    }
    next();
  }, next);
};

Fix user not being set


const UPDATE_MONITORS = 'scratch-gui/monitors/UPDATE_MONITORS';

const initialState = [];

const reducer = function (state, action) {
    if (typeof state === 'undefined') state = initialState;
    switch (action.type) {
    case UPDATE_MONITORS:
        return action.monitors;
    default:
        return state;
    }
};

reducer.updateMonitors = function (monitors) {
    return {
        type: UPDATE_MONITORS,
        monitors: monitors,
        meta: {
            throttle: 30
        }
    };
};

module.exports = reducer;

const UPDATE_MONITORS = 'scratch-gui/monitors/UPDATE_MONITORS';

const initialState = [];

const reducer = function (state, action) {
    if (typeof state === 'undefined') state = initialState;
    switch (action.type) {
    case UPDATE_MONITORS:
        return [...action.monitors];
    default:
        return state;
    }
};

reducer.updateMonitors = function (monitors) {
    return {
        type: UPDATE_MONITORS,
        monitors: monitors,
        meta: {
            throttle: 30
        }
    };
};

module.exports = reducer;

Make a copy of monitor data


/*
 * AET
 *
 * Copyright (C) 2013 Cognifide Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define(['angularAMD'], function (angularAMD) {
  'use strict';
  angularAMD.factory('endpointConfiguration', function () {
    return {
      getEndpoint: function () {
        var config = {
          // 'getUrl': '/api/'
          //'getUrl': 'http://aet-vagrant/api/'
        'getUrl': 'http://localhost:8181/api/'
        };
        return config;
      }
    };
  });

});

/*
 * AET
 *
 * Copyright (C) 2013 Cognifide Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define(['angularAMD'], function (angularAMD) {
  'use strict';
  angularAMD.factory('endpointConfiguration', function () {
    return {
      getEndpoint: function () {
        var config = {
          'getUrl': '/api/'
          //'getUrl': 'http://aet-vagrant/api/'
        };
        return config;
      }
    };
  });

});

Revert change in endpoint configuration


jQuery(document).ready(function($) {
  $(".chzn-select").chosen({allow_single_deselect: true});

  $("#completed_fact_check_requests").markLinkedAnchor();

  $('fieldset.sortable legend').each(function () {
    $(this).append(' (drag up and down to re-order)');
  })
})

jQuery(document).ready(function($) {
  $(".chzn-select").chosen({allow_single_deselect: true, search_contains: true});

  $("#completed_fact_check_requests").markLinkedAnchor();

  $('fieldset.sortable legend').each(function () {
    $(this).append(' (drag up and down to re-order)');
  })
})

Configure chosen.js to allow search for multiple words.


import { global, isBrowser, isWorker } from './common.js';
import SystemJSProductionLoader from './systemjs-production-loader.js';

SystemJSProductionLoader.prototype.version = VERSION;

var System = new SystemJSProductionLoader();

// only set the global System on the global in browsers
if (isBrowser || isWorker) {
  global.SystemJS = System;

  // dont override an existing System global
  if (!global.System) {
    global.System = System;
  }
  // rather just extend or set a System.register on the existing System global
  else {
    var register = global.System.register;
    global.System.register = function () {
      if (register)
        register.apply(this, arguments);
      System.register.apply(this, arguments);
    };
  }
}

if (typeof module !== 'undefined' && module.exports)
  module.exports = System;

import { global, isBrowser, isWorker } from './common.js';
import SystemJSProductionLoader from './systemjs-production-loader.js';

SystemJSProductionLoader.prototype.version = VERSION;

var System = new SystemJSProductionLoader();

// only set the global System on the global in browsers
if (isBrowser || isWorker) {
  global.SystemJS = System;

  // dont override an existing System global
  if (!global.System) {
    global.System = System;
  }
  // rather just extend or set a System.register on the existing System global
  else {
    var register = global.System.register;
    global.System.register = function () {
      if (register)
        register.apply(this, arguments);
      System.register.apply(System, arguments);
    };
  }
}

if (typeof module !== 'undefined' && module.exports)
  module.exports = System;

Fix the context scope of SystemJS.register when called from browser's System.register


var Publish = {
    enableSend: function() {
        MovimUtils.removeClass('#button_send', 'disabled');
    },
    disableSend: function() {
        MovimUtils.addClass('#button_send', 'disabled');
    }
}

var PublishBrief = {
    togglePrivacy: function() {
        var checked = document.querySelector('#publishbrief form #open');

        var button = document.querySelector('#publishbrief span.privacy i');
        MovimUtils.removeClass(button, 'zmdi-lock-outline');
        MovimUtils.removeClass(button, 'zmdi-lock-globe');

        // Public
        if(checked.checked) {
            checked.checked = false;
            MovimUtils.addClass(button, 'zmdi-lock-outline');
        } else {
            checked.checked = true;
            MovimUtils.addClass(button, 'zmdi-globe');
        }

        PublishBrief_ajaxDisplayPrivacy(checked.checked);
    },
    addUrl: function() {
        var url = document.querySelector('#url');
        var embed = document.querySelector('#embed');
        embed.value = url.value;
        embed.onchange();
    },
    clearEmbed: function() {
        document.querySelector('input[name=embed]').value = '';
        PublishBrief_ajaxClearEmbed();
    }
}

Upload.attach(function() {
    var embed = document.querySelector('input[name=embed]');
    embed.value = Upload.get;
    embed.onchange();
});

var Publish = {
    enableSend: function() {
        MovimUtils.removeClass('#button_send', 'disabled');
    },
    disableSend: function() {
        MovimUtils.addClass('#button_send', 'disabled');
    }
}

var PublishBrief = {
    togglePrivacy: function() {
        var checked = document.querySelector('#publishbrief form #open');

        var button = document.querySelector('#publishbrief span.privacy i');
        MovimUtils.removeClass(button, 'zmdi-lock-outline');
        MovimUtils.removeClass(button, 'zmdi-lock-globe');

        // Public
        if(checked.checked) {
            checked.checked = false;
            MovimUtils.addClass(button, 'zmdi-lock-outline');
        } else {
            checked.checked = true;
            MovimUtils.addClass(button, 'zmdi-globe');
        }

        PublishBrief_ajaxDisplayPrivacy(checked.checked);
    },
    addUrl: function() {
        var url = document.querySelector('#url');
        var embed = document.querySelector('#embed');
        embed.value = url.value;
        embed.onchange();
    },
    clearEmbed: function() {
        document.querySelector('input[name=embed]').value = '';
        PublishBrief_ajaxClearEmbed();
    }
}

MovimWebsocket.attach(function() {
    PublishBrief_ajaxClearEmbed();
});

Upload.attach(function() {
    var embed = document.querySelector('input[name=embed]');
    embed.value = Upload.get;
    embed.onchange();
});

Refresh the embed block onload in PublishBrief


import React, { Component } from 'react';
/* component styles */
// import { styles } from './styles.scss';

export class About extends Component {
  render() {
    return (
      // <about className={`${styles}`}>
      <about>

              twitchBot was created to give a user a visual representation of what is going on in a Twitch channel - the world’s leading social video platform and community for gamers. With our mood charts and graphs analyzing the realtime flow of comments, a user can instantly decide whether to join the channel. Chat messages are send to the Watson cognitive system API to return an analysis of emotional & social tone that is streamed to connected users.

              Users select one of the top 25 currently active twitch streams and are presented with realtime visuals of the mood, frequency and length of incoming messages. Users may also view long term data from 4 channels selected by our team.

      </about>
    );
  }
}

import React, { Component } from 'react';
/* component styles */
// import { styles } from './styles.scss';

export class About extends Component {
  render() {
    return (
      // <about className={`${styles}`}>
      <about>

              Chatson was created to give a user a visual representation of what is going on in a Twitch channel - the world’s leading social video platform and community for gamers. With our mood charts and graphs analyzing the realtime flow of comments, a user can instantly decide whether to join the channel. Chat messages are send to the Watson cognitive system API to return an analysis of emotional & social tone that is streamed to connected users.

              Users select one of the top 25 currently active streams and are presented with realtime visuals of the mood, frequency and length of incoming messages. Users may also view long term data from 4 channels selected by our team.

      </about>
    );
  }
}

Update paragraph to new name


import React from 'react';
import classnames from 'classnames';
import { spacingStyles } from '../Spacing/utils';
import { Icon } from '../Icon';
import { Tooltip } from '../Tooltip';
import { Spinner } from '../Spinner';

function Badge({ children, margin, padding, state, tooltip, icon, theme, loading }) {
  const style = spacingStyles({ margin, padding });
  const className = classnames('ui-badge', `ui-badge-${theme || state}`);

  function renderChildrenWithIcon() {
    if (icon) {
      return <Icon name={icon} noMargin /> {children};
    }

    return <Spinner /> {children};
  }

  const badge = (

      {(icon || loading)
        ? renderChildrenWithIcon()
        : children}

  );

  return tooltip
    ? <Tooltip content={tooltip}>{badge}</Tooltip>
    : badge;
}

Badge.propTypes = {
  state: React.PropTypes.string.isRequired,
  children: React.PropTypes.node,
  icon: React.PropTypes.oneOfType([
    React.PropTypes.bool,
    React.PropTypes.string,
  ]),
  tooltip: React.PropTypes.node,
  margin: React.PropTypes.string,
  padding: React.PropTypes.string,
  theme: React.PropTypes.oneOf(['success', 'danger', 'default', 'info']).isRequired,
  loading: React.PropTypes.bool,
};

Badge.defaultProps = {
  state: 'default',
  theme: 'default',
};

export default Badge;

import React from 'react';
import classnames from 'classnames';
import { spacingStyles } from '../Spacing/utils';
import { Icon } from '../Icon';
import { Tooltip } from '../Tooltip';
import { Spinner } from '../Spinner';

function Badge(props) {
  const { children, margin, padding, state, tooltip, icon, theme, loading, label } = props;

  const style = spacingStyles({ margin, padding });
  const className = classnames('ui-badge', `ui-badge-${theme || state}`);

  function renderChildrenWithIcon() {
    if (icon) {
      return <Icon name={icon} noMargin /> {children};
    }

    return <Spinner /> {children};
  }

  const badge = (

      {label && {label}}
      {(icon || loading)
        ? renderChildrenWithIcon()
        : children}

  );

  return tooltip
    ? <Tooltip content={tooltip}>{badge}</Tooltip>
    : badge;
}

Badge.propTypes = {
  state: React.PropTypes.string.isRequired,
  children: React.PropTypes.node,
  icon: React.PropTypes.oneOfType([
    React.PropTypes.bool,
    React.PropTypes.string,
  ]),
  tooltip: React.PropTypes.node,
  margin: React.PropTypes.string,
  padding: React.PropTypes.string,
  theme: React.PropTypes.oneOf(['success', 'danger', 'default', 'info']).isRequired,
  loading: React.PropTypes.bool,
  label: React.PropTypes.string,
};

Badge.defaultProps = {
  state: 'default',
  theme: 'default',
};

export default Badge;

Introduce a label prop for badges


import Rails from 'rails-ujs';
import jQuery from 'jquery';

// We use `jQuery.active` in our capybara suit to wait for ajax requests.
// Newer jQuery-less version of rails-ujs is breaking it.
// We have to set `ajax:complete` listener on the same element as the one
// we catch ajax:send on as by the end of the request
// the old element may be removed from DOM.
Rails.delegate(document, '[data-remote]', 'ajax:send', ({ target }) => {
  let callback = () => {
    jQuery.active--;
    target.removeEventListener('ajax:complete', callback);
  };
  target.addEventListener('ajax:complete', callback);
  jQuery.active++;
});

// `smart_listing` gem is overriding `$.rails.href` method. When using newer
// jQuery-less version of rails-ujs it breaks.
// https://github.com/Sology/smart_listing/blob/master/app/assets/javascripts/smart_listing.coffee.erb#L9
addEventListener('load', () => {
  const { href } = Rails;
  Rails.href = function(element) {
    return element.href || href(element);
  };
});

// rails-ujs installs CSRFProtection for its own ajax implementation. We might need
// CSRFProtection for jQuery initiated requests. This code is from jquery-ujs.
jQuery.ajaxPrefilter((options, originalOptions, xhr) => {
  if (!options.crossDomain) {
    CSRFProtection(xhr);
  }
});

function csrfToken() {
  return jQuery('meta[name=csrf-token]').attr('content');
}

function CSRFProtection(xhr) {
  let token = csrfToken();
  if (token) {
    xhr.setRequestHeader('X-CSRF-Token', token);
  }
}

Add csrf token protection to jQuery initiated requests


const morgan = require('morgan');

module.exports = (server) => {
	switch (server.get('env')) {
		case 'production':
			server.use(morgan('tiny'));
			break;

		case 'development':
		default:
			server.use(morgan('dev'));
			break;
	}
};

const morgan = require('morgan');

module.exports = (server) => {
	switch (server.get('env')) {
		case 'development':
		default:
			server.use(morgan('dev'));
			break;

		case 'production':
			server.use(morgan('tiny'));
			break;
	}
};

Switch order in log script for consistency.


//* @public

enyo.string = {
	/** return string with white space at start and end removed */
	trim: function(inString) {
		return inString.replace(/^\s+|\s+$/g,"");
	},
	/** return string with leading and trailing quote characters removed, e.g. "foo" becomes foo */
	stripQuotes: function(inString) {
		var c0 = inString.charAt(0);
		if (c0 == '"' || c0 == "'") {
			inString = inString.substring(1);
		}
		var l = inString.length - 1, cl = inString.charAt(l);
		if (cl == '"' || cl == "'") {
			inString = inString.substr(0, l);
		}
		return inString;
	},
	//* Encode a string to Base64
	toBase64: function(inText) { return window.btoa(inText); },
	//* Decode string from Base64. Throws exception on bad input.
	fromBase64: function(inText) { return window.atob(inText); }
};

if (!(window.btoa && window.atob)) {
	enyo.string.toBase64 = enyo.string.fromBase64 = function(inText) {
		enyo.error("Your browser does not support native base64 operations");
		return inText;
	};
};

//* @public

enyo.string = {
	/** return string with white space at start and end removed */
	trim: function(inString) {
		return inString.replace(/^\s+|\s+$/g,"");
	},
	/** return string with leading and trailing quote characters removed, e.g. "foo" becomes foo */
	stripQuotes: function(inString) {
		var c0 = inString.charAt(0);
		if (c0 == '"' || c0 == "'") {
			inString = inString.substring(1);
		}
		var l = inString.length - 1, cl = inString.charAt(l);
		if (cl == '"' || cl == "'") {
			inString = inString.substr(0, l);
		}
		return inString;
	}
};

Remove base64 methods from extras, not used in any Enyo team code, seems buggy, and outside our competency


const UserDB = require('../db/userdb').Users

exports.form = (req, res, next) => {
  res.render('register', {title: 'Welcome to Chat app'})
}
exports.submit = (req, res, next) => {
  let data = req.body.user
  console.log('data->', data)
  console.log('data.name->', data.name, 'data.pass->', data.pass)
  UserDB.find(data, (err, user) => {
    if (err) return next(err)
    console.log('user->', user)
    if (user) {
      console.log('user exists')
       //res.render('register', {err: 'UserName already taken'})
      // res.error('Username already taken')
      res.redirect('back')
    } else {
      UserDB.create(data, (err, user) => {
        if (err) return next(err)
        res.redirect('/')
      })
    }
  })
}

const UserDB = require('../db/userdb').Users

exports.form = (req, res, next) => {
  res.render('register', {title: 'Welcome to Chat app'})
}
exports.submit = (req, res, next) => {
  let data = req.body.user
  console.log('data->', data)
  console.log('data.name->', data.name, 'data.pass->', data.pass)
  if (data.name && data.pass) {
    UserDB.find(data, (err, user) => {
      if (err) return next(err)
      console.log('user->', user)
      if (user) {
        console.log('user exists')
         //res.render('register', {err: 'UserName already taken'})
        // res.error('Username already taken')
        res.redirect('back')
      } else {
        UserDB.create(data, (err, user) => {
          if (err) return next(err)
          res.redirect('/')
        })
      }
    })
  } else {
    console.log('UserName and Password are mandatory for registration')
    res.redirect('back')
  }

}

Add check for empty userName and password


export default (props) => (
  <img className="pure-img" src={`https://chalees-min.imgix.net${props.imagePath}?w=726&fit=clip&auto=format,compress`} />
)

export default (props) => (
  <img className="pure-img" src={`https://chalees-min.imgix.net${props.imagePath}?w=726&fit=max&auto=format,compress`} />
)

Stop upscaling image type learning item images


export default {
    root: {
        isAuthenticated: false,
        isAuthenticationInProgress: false
    }
}

export default {
    root: {
        currentSessionTime: 0,
        isAuthenticated: false,
        isAuthenticationInProgress: false
    }
}

Add session time to initial state


const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const pathsToClean = ['dist'];

const config = {
  entry: [
    './src/index.js'
    // entry point of the app
  ],

  output: {
    filename: 'bundle.js',

    path: path.resolve(__dirname, 'dist'),

    publicPath: ''
    // necessary for HMR to know where to load the hot update chunks
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'],
        exclude: /node_modules/
      }, {
        test: /\.scss$/,
        use: [{
          loader: 'style-loader'
          // creates style nodes from JS strings
        }, {
          loader: 'css-loader'
          // translates CSS into CommonJS
        }, {
          loader: 'sass-loader'
          // compiles Sass to CSS
        }]
      }, {
        test: /\.(jpg|png|svg)$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 25000,
            name: '[name].[ext]'
          }
        }
      }
    ]
  },

  plugins: [
    new webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false
    }),

    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false
    }),

    new HtmlWebpackPlugin({ template: 'index.html' }),

    new CleanWebpackPlugin(pathsToClean, { verbose: true })
  ]
};

module.exports = config;

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const pathsToClean = ['dist'];

const config = {
  entry: [
    './src/index.js'
    // entry point of the app
  ],

  output: {
    filename: '[name].[chunkhash:8].js',

    path: path.resolve(__dirname, 'dist'),

    publicPath: ''
    // necessary for HMR to know where to load the hot update chunks
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'],
        exclude: /node_modules/
      }, {
        test: /\.scss$/,
        use: [{
          loader: 'style-loader'
          // creates style nodes from JS strings
        }, {
          loader: 'css-loader'
          // translates CSS into CommonJS
        }, {
          loader: 'sass-loader'
          // compiles Sass to CSS
        }]
      }, {
        test: /\.(jpg|png|svg)$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 25000,
            name: '[name].[ext]'
          }
        }
      }
    ]
  },

  plugins: [
    new webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false
    }),

    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      comments: false
    }),

    new HtmlWebpackPlugin({ template: 'index.html' }),

    new CleanWebpackPlugin(pathsToClean, { verbose: true })
  ]
};

module.exports = config;

Add hash to webpack bundle name


Rx.Observable
  .fromEvent(document.getElementById("search"), "keyup")
  .map(event => {
    return event.target.value;
  })
  .switchMap(searchQuery => {
    /*return axios.all([
      axios.get("http://localhost:3334/movies/" + searchQuery),
      axios.get("http://localhost:3334/tv/" + searchQuery)
    ]);*/
    return axios.get("http://localhost:3334/movies/" + searchQuery);
  })
  .map(results => {
    /*var movies = results[0].data;
    var tv = results[1].data;
    return movies.concat(tv);*/
    return results.data;
  })
  .subscribe(results => {
    renderResults(results);
  });

function renderResults(results) {
  var searchResultsElement = document.getElementById("searchResults");
  searchResultsElement.innerHTML = "";

  for (var i = 0; i < results.length; i++) {
    var el = document.createElement("li");
    el.innerHTML = results[i];
    searchResultsElement.appendChild(el);
  }
}

Rx.Observable
  .fromEvent(document.getElementById("search"), "keyup")
  .map(event => {
    return event.target.value;
  })
  .switchMap(searchQuery => {
    return Rx.Observable.fromPromise(
      axios.get("http://localhost:3334/movies/" + searchQuery)
    );
  })
  .map(results => {
    /*var movies = results[0].data;
    var tv = results[1].data;
    return movies.concat(tv);*/
    return results.data;
  })
  .subscribe(results => {
    renderResults(results);
  });

function renderResults(results) {
  var searchResultsElement = document.getElementById("searchResults");
  searchResultsElement.innerHTML = "";

  for (var i = 0; i < results.length; i++) {
    var el = document.createElement("li");
    el.innerHTML = results[i];
    searchResultsElement.appendChild(el);
  }
}

Create observable out of promise explicitly for better understanding


/* @flow */
import React, { Component, PropTypes } from "react"
import { Element as ReactElement } from "react"

import ReactHigherEventProxyContextTypes from "./ReactHigherEventProxyContextTypes"

type EventProps = {
  [key: string]: Function,
}

class ReactHigherEventProxy extends Component<void, Props, void> {
  componentDidMount() {
    const { higherEventProxy } = this.context
    this.unsubscribe = higherEventProxy.subscribe(this.forceUpdate.bind(this))
  }
  componentWillUnmount() {
    this.unsubscribe()
  }
  getEventProps(): EventProps {
    const { higherEventProxy } = this.context
    return Array.from(higherEventProxy.events.keys()).reduce((acc, key) => {
      return {
        ...acc,
        [key]: higherEventProxy.handleEvent.bind(null, key),
      }
    }, {})
  }
  render(): ReactElement {
    const { children, ...props } = this.props
    return (
      <div {...props} {...this.getEventProps()}>
        {children}
      </div>
    )
  }
}

ReactHigherEventProxy.contextTypes = ReactHigherEventProxyContextTypes

type Props = {
  children?: any,
}

export default ReactHigherEventProxy

/* @flow */
import React, { Component, PropTypes } from "react"
import { Element as ReactElement } from "react"

import ReactHigherEventProxyContextTypes from "./ReactHigherEventProxyContextTypes"

type EventProps = {
  [key: string]: Function,
}

class ReactHigherEventProxy extends Component<void, Props, void> {
  componentDidMount() {
    const { higherEventProxy } = this.context
    this.unsubscribe = higherEventProxy.subscribe(this.forceUpdate.bind(this))
  }
  componentWillUnmount() {
    this.unsubscribe()
  }
  getEventProps(): EventProps {
    const { higherEventProxy } = this.context
    const eventProps = {}
    higherEventProxy.events.forEach((key) => {
      eventProps[key] = (event) => {
        if(this.props[key]) {
          this.props[key](event)
        }
        higherEventProxy.handleEvent(key, event)
      }
    })
    return eventProps
  }
  render(): ReactElement {
    const { children, ...props } = this.props
    return (
      <div {...props} {...this.getEventProps()}>
        {children}
      </div>
    )
  }
}

ReactHigherEventProxy.contextTypes = ReactHigherEventProxyContextTypes

type Props = {
  children?: any,
}

export default ReactHigherEventProxy

Support event handlers on proxy, reduce iterations


var flux = require('flux-redux');

/**
 * Hot reloading stores!
 *
 * The trick is to always re-use the `dispatcher` instance
 *
 * dispatcher.addStore will use the stores' merge functions
 * when re-adding another store with the same key
 */
var dispatcher = flux.createDispatcher();
if (module.hot) {
  if (module.hot.data) {
    dispatcher = module.hot.data.dispatcher;
  }
  module.hot.accept();
  module.hot.dispose(hot => hot.dispatcher = dispatcher);
}
// Server data hydration - this should be in the core lib
/*global initialStoreData*/
if (typeof initialStoreData == 'object') {
  Object.keys(initialStoreData).forEach(k => {
    dispatcher.addStore(k, {initial: () => initialStoreData[k]});
    delete initialStoreData[k];
  });
}

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

var routing = require('./stores/routing');
dispatcher.addInterceptor('routing', routing.interceptor);
dispatcher.addStore('routing', routing.store);

var films = require('./stores/films');
data.addHandlers(films.handlers);
dispatcher.addStore('films', films.store);

var planets = require('./stores/planets');
data.addHandlers(planets.handlers);
dispatcher.addStore('planets', planets.store);

module.exports = dispatcher;

var flux = require('flux-redux');

/**
 * To take over from server rendering, we must re-use the data that the
 * server used, by passing it into the dispatcher init
 */
/*global initialStoreData*/
var state = typeof initialStoreData == 'object' ? initialStoreData : {};

var dispatcher = flux.createDispatcher({state});

/**
 * Hot reloading stores!
 *
 * The trick is to always re-use the `dispatcher` instance
 *
 * dispatcher.addStore will use the stores' merge functions
 * when re-adding another store with the same key
 */
if (module.hot) {
  if (module.hot.data) {
    dispatcher = module.hot.data.dispatcher;
  }
  module.hot.accept();
  module.hot.dispose(hot => hot.dispatcher = dispatcher);
}

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

var routing = require('./stores/routing');
dispatcher.addInterceptor('routing', routing.interceptor);
dispatcher.addStore('routing', routing.store);

var films = require('./stores/films');
data.addHandlers(films.handlers);
dispatcher.addStore('films', films.store);

var planets = require('./stores/planets');
data.addHandlers(planets.handlers);
dispatcher.addStore('planets', planets.store);

module.exports = dispatcher;

Update example to use state-reuse init


'use strict';

/**
 * @file
 * Delivers a component representing one single category in the autocomplete
 * parent autocomplete component.
 */

import React from 'react';
import {isArray, includes} from 'lodash';
import AutoCompleteRow from './AutoCompleteRow.component';

var AutoCompleteCategory = React.createClass({
  displayName: 'AutoCompleteCategory',
  propTypes: {
    data: React.PropTypes.array,
    label: React.PropTypes.object
  },

  getLabel(label) {
    return (

        {label}

    );
  },

  render() {
    let data = this.props.data || [];
    if (!isArray(data)) {
      data = new Array(data);
    }

    const label = this.props.label || null;
    const labelToRender = (label) ? this.getLabel(label) : '';

    const showImage = label && !includes(['Forfatter', 'Emne'], label.props.children);

    const rows = data.map((value, key) => {
      return (
        <AutoCompleteRow href={value.href} image={value.image} imageComp={value.imageComp} key={key} showImage={showImage} text={value.text} />
      );
    });

    return (

        {labelToRender}

          {rows}

    );
  }
});

export default AutoCompleteCategory;

'use strict';

/**
 * @file
 * Delivers a component representing one single category in the autocomplete
 * parent autocomplete component.
 */

import React from 'react';
import {isArray, includes} from 'lodash';
import AutoCompleteRow from './AutoCompleteRow.component';

var AutoCompleteCategory = React.createClass({
  displayName: 'AutoCompleteCategory',
  propTypes: {
    data: React.PropTypes.array,
    label: React.PropTypes.object
  },

  getLabel(label) {
    return (

        {label}

    );
  },

  render() {
    let data = this.props.data || [];
    if (!isArray(data)) {
      data = new Array(data);
    }

    const label = this.props.label || null;
    const labelToRender = (label) ? this.getLabel(label) : '';

    const showImage = label && !includes(['Forfatter', 'Emne'], label.props.children);

    const rows = data.map((value) => {
      return (
        <AutoCompleteRow href={value.href} image={value.image} imageComp={value.imageComp} key={value.text + value.href} showImage={showImage} text={value.text} />
      );
    });

    return (

        {labelToRender}

          {rows}

    );
  }
});

export default AutoCompleteCategory;

Fix problem with image not updating.


module.exports = {
  stories: [{ directory: '../stories/', files: '**/*.stories.@(json|yaml|yml)' }],
  logLevel: 'debug',
  addons: [
    '@storybook/addon-docs',
    '@storybook/addon-a11y',
    '@storybook/addon-actions',
    '@storybook/addon-backgrounds',
    '@storybook/addon-links',
    '@storybook/addon-controls',
  ],
  features: {
    previewCsfV3: true,
  },
};

module.exports = {
  stories: ['../stories/**/*.stories.@(json|yaml|yml)'],
  logLevel: 'debug',
  addons: [
    '@storybook/addon-docs',
    '@storybook/addon-a11y',
    '@storybook/addon-actions',
    '@storybook/addon-backgrounds',
    '@storybook/addon-links',
    '@storybook/addon-controls',
  ],
  features: {
    previewCsfV3: true,
  },
};

Revert example to use the new glob patterns for auto titles


'use strict';

const Promise = require('bluebird');

const {MongoClient} = require('mongodb');

const MONGO_URL = process.env.MONGO_URL;
const MONGO_COLLECTION = process.env.MONGO_COLLECTION;

const mdb_delayed = MongoClient.connect(MONGO_URL);

class Plebiscite {
    constructor({db}) {
        this._db = db;
    }

    * _getCollection() {
        return (yield this._db).collection(MONGO_COLLECTION);
    }

    * find({substance, offset, limit}) {
        const collection = yield* this._getCollection();

        return yield collection.find({
            'substanceInfo.substance': substance
        })
        .sort({'meta.published': -1})
        .skip(offset)
        .limit(limit)
        .toArray();
    }
}

module.exports = new Plebiscite({db: mdb_delayed});

'use strict';

const Promise = require('bluebird');

const {MongoClient} = require('mongodb');

const MONGO_URL = process.env.MONGO_URL;
const MONGO_COLLECTION = process.env.MONGO_COLLECTION;

const mdb_delayed = MongoClient.connect(MONGO_URL);

class Plebiscite {
    constructor({db}) {
        this._db = db;
    }

    * _getCollection() {
        return (yield this._db).collection(MONGO_COLLECTION);
    }

    * find({substance, offset, limit}) {
        const collection = yield* this._getCollection();

        const query = {};

        if (!substance === true) {
            query['substanceInfo.substance'] = substance;
        }

        return yield collection.find(query)
        .sort({'meta.published': -1})
        .skip(offset)
        .limit(limit)
        .toArray();
    }
}

module.exports = new Plebiscite({db: mdb_delayed});

Allow empty query for substances


import UpstreamDredd from 'dredd';

import Reporter from './reporter';

export default class Dredd extends UpstreamDredd {
    constructor(config, logger) {
        super(config);

        this.canceled = false;

        this.reporter = new Reporter(this.configuration.emitter, this.stats, logger,
            config.options.verbose);
    }

    cancel() {
        this.reporter.cancel();
        this.canceled = true;
    }

    run(watch = false) {
        super.run((err) => {
            // We need to check this before the error, because when roc
            // restarts the application dredd will fail with a network error
            if (err && !this.canceled) {
                throw err;
            } else if (this.canceled) {
                this.canceled = false;
            }

            if (!watch) {
                process.exit(0);
            }
        });
    }
}

import UpstreamDredd from 'dredd';

import Reporter from './reporter';

export default class Dredd extends UpstreamDredd {
    constructor(config, logger) {
        super(config);

        this.canceled = false;

        this.reporter = new Reporter(this.configuration.emitter, this.stats, logger,
            config.options.verbose);
    }

    cancel() {
        this.reporter.cancel();
        this.canceled = true;
    }

    run(watch = false) {
        super.run((err) => {
            // We need to check this before the error, because when roc
            // restarts the application dredd will fail with a network error
            if (err && !this.canceled) {
                throw err;
            } else if (this.canceled) {
                this.canceled = false;
            }

            if (!watch) {
                if (this.stats.failures || this.stats.errors) {
                    process.exit(1);
                } else {
                    process.exit(0);
                }
            }
        });
    }
}

Return exit status `1` when one or more tests are failing


'use strict'

var express = require('express')
const querystring = require('querystring')

const config = require('../config/environment')

var router = express.Router()

router.get('/github', (req, res) => {
  var params = querystring.stringify({
    client_id: config.github.application.id,
    redirect_uri: config.github.application.redirectURI,
    state: '1234',
    scopes: 'repo'
  })
  res.redirect('https://github.com/login/oauth/authorize?' + params)
})

router.get('/', (req, res) => {
  res.json({
    github: config.session.cookie.signed ? req.signedCookies.github : req.cookies.github
  })
})

router.delete('/', (req, res) => {
  res.clearCookie('github')
  res.json({
    github: true
  })
})

module.exports = router

'use strict'

var express = require('express')
const querystring = require('querystring')

const config = require('../config/environment')

var router = express.Router()

router.get('/github', (req, res) => {
  var params = querystring.stringify({
    client_id: config.github.application.id,
    redirect_uri: config.github.application.redirectURI,
    state: '1234',
    scope: 'repo'
  })
  res.redirect('https://github.com/login/oauth/authorize?' + params)
})

router.get('/', (req, res) => {
  res.json({
    github: config.session.cookie.signed ? req.signedCookies.github : req.cookies.github
  })
})

router.delete('/', (req, res) => {
  res.clearCookie('github')
  res.json({
    github: true
  })
})

module.exports = router

Fix typo to fetch appropriate scopes


$(function(){
	var owl = $('#owlCarousel');

	owl.owlCarousel({
		lazyLoad: true,
		responsiveClass: true,
		autowidth: true,
		loop: true,
		responsive: {
			0: {
				items: 1
			},
			480: {
				items: 2,
				margin: 20
			}
		}
	});

	$('#owlCarouselLeft').click(function(){
		owl.trigger('prev.owl.carousel')
	});

	$('#owlCarouselRight').click(function(){
		owl.trigger('next.owl.carousel')
	});
});

$(function(){
	var owl = $('#owlCarousel');

	owl.owlCarousel({
		lazyLoad: true,
		responsiveClass: true,
		autowidth: true,
		loop: true,
		responsive: {
			0: {
				items: 1
			},
			480: {
				items: 2,
				margin: 20
			}
		}
	});

	$('#owlCarouselLeft').click(function(){
		owl.trigger('prev.owl.carousel')
	});

	$('#owlCarouselRight').click(function(){
		owl.trigger('next.owl.carousel')
	});

	// Smooth Scroll Anchor
	$(function() {
		$('a[href*="#"]:not([href="#"])').click(function() {
			if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
				var target = $(this.hash);
				target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
				if (target.length) {
					$('html, body').animate({
					scrollTop: target.offset().top
				}, 1000);
				return false;
			}
		}
		});
	});
});

Add Smooth Scroll for Anchor