import { extendClock, ProgressiveBackoff } from '../universal/time'

const TimeSync = global.TimeSync

const localClock = () => new Date()

const serverSyncClock = () => {
  const serverTime = global.Tracker.nonreactive(() => TimeSync.serverTime())
  if (! serverTime) {
    console.warn('clock() called before time synced with server, defaulting to browser/client time')  // eslint-disable-line no-console
    return localClock()
  }
  return new Date(serverTime)
}

export default () => ({
  clock: extendClock(TimeSync ? serverSyncClock : localClock),
  ProgressiveBackoff,
})

import { extendClock, ProgressiveBackoff } from '../universal/time'

const TimeSync = global.TimeSync

const localClock = () => new Date()

let reportedCalledBeforeTimeSynced = false

const serverSyncClock = () => {
  const serverTime = global.Tracker.nonreactive(() => TimeSync.serverTime())
  if (! serverTime) {
    if (! reportedCalledBeforeTimeSynced) {
      console.log('clock() called before time synced with server, defaulting to browser/client time')  // eslint-disable-line no-console
      reportedCalledBeforeTimeSynced = true
    }
    return localClock()
  }
  return new Date(serverTime)
}

export default () => ({
  clock: extendClock(TimeSync ? serverSyncClock : localClock),
  ProgressiveBackoff,
})

Reduce log spam from time not being synced yet


import React, { Component } from 'react'
import { Router, Route, hashHistory } from 'react-router'
import './App.css'
import Setup              from './Setup'
import SetupWelcome       from './SetupWelcome'
import SetupWifiPassword  from './SetupWifiPassword'
import SetupWifiSelection from './SetupWifiSelection'
import SetupNuimo         from './SetupNuimo'
import SetupDevices       from './SetupDevices'
import SetupCompletion    from './SetupCompletion'
import SetupTutorialVideo from './SetupTutorialVideo'

class App extends Component {
  render() {
    //TODO: Use `browserHistory` instead of `hashHistory`, see also https://github.com/ReactTraining/react-router/blob/master/docs/guides/Histories.md#browserhistory
    return (
      <div className="App">
        <Router history={hashHistory}>
          <Route path='setup' component={Setup}>
            <Route path='welcome' component={SetupWelcome} />
            <Route path='wifi' component={SetupWifiSelection} />
            <Route path='wifi/:ssid' component={SetupWifiPassword} />
            <Route path='nuimo' component={SetupNuimo} />
            <Route path='devices' component={SetupDevices} />
            <Route path='completed' component={SetupCompletion} />
            <Route path='tutorial' component={SetupTutorialVideo} />
          </Route>
        </Router>
      </div>
    );
  }
}

export default App

import React, { Component } from 'react'
import { Router, Route, Redirect, hashHistory } from 'react-router'
import './App.css'
import Setup              from './Setup'
import SetupWelcome       from './SetupWelcome'
import SetupWifiPassword  from './SetupWifiPassword'
import SetupWifiSelection from './SetupWifiSelection'
import SetupNuimo         from './SetupNuimo'
import SetupDevices       from './SetupDevices'
import SetupCompletion    from './SetupCompletion'
import SetupTutorialVideo from './SetupTutorialVideo'

class App extends Component {
  render() {
    //TODO: Use `browserHistory` instead of `hashHistory`, see also https://github.com/ReactTraining/react-router/blob/master/docs/guides/Histories.md#browserhistory
    return (
      <div className="App">
        <Router history={hashHistory}>
          <Redirect from="/" to="/setup/welcome" />
          <Redirect from="/setup" to="/setup/welcome" />
          <Route path='setup' component={Setup}>
            <Route path='welcome' component={SetupWelcome} />
            <Route path='wifi' component={SetupWifiSelection} />
            <Route path='wifi/:ssid' component={SetupWifiPassword} />
            <Route path='nuimo' component={SetupNuimo} />
            <Route path='devices' component={SetupDevices} />
            <Route path='completed' component={SetupCompletion} />
            <Route path='tutorial' component={SetupTutorialVideo} />
          </Route>
        </Router>
      </div>
    );
  }
}

export default App

Add redirect route from root to setup


$(document).ajaxError(function(event, jXHR) {
    if (jXHR.status === 403) {
        var message = gettext(
            'You have been logged out of your edX account. ' +
            'Click Okay to log in again now. ' +
            'Click Cancel to stay on this page ' +
            '(you must log in again to save your work).'
        );

        if (window.confirm(message)) {
            var currentLocation = window.location.pathname;
            window.location.href = '/login?next=' + encodeURIComponent(currentLocation);
        }
    }
});

$(document).ajaxError(function(event, jXHR) {
    if (jXHR.status === 403) {
        if ($('.sneakpeek-warning').length) {
            return;
        }
        var message = gettext(
            'You have been logged out of your edX account. ' +
            'Click Okay to log in again now. ' +
            'Click Cancel to stay on this page ' +
            '(you must log in again to save your work).'
        );

        if (window.confirm(message)) {
            var currentLocation = window.location.pathname;
            window.location.href = '/login?next=' + encodeURIComponent(currentLocation);
        }
    }
});

Disable logout prompt for nonregistered users


import Ember from 'ember';
import RSVP from 'rsvp';
import DS from 'ember-data';

import config from '../config/environment';
import buildQueryParamsMixin from 'wholetale/mixins/build-query-params';

export default DS.RESTAdapter.extend(buildQueryParamsMixin, {
    tokenHandler: Ember.inject.service("token-handler"),
    authRequest: Ember.inject.service(),

    host: config.apiHost,
    namespace: config.apiPath,

    primaryKey: "_id",

    query(store, type, query) {
        let url = this.buildURL(type.modelName, null, null, 'query', query);

        console.log("sending request for sils ... ");
        return new RSVP.Promise(resolve => resolve([{ _id: 1, _modelType: "sils", icon: "http://lorempixel.com/400/400/abstract/" }]));

        //TODO: uncomment this after sils endpoint working
        // return this.get('authRequest').send(url, {headers:{'content-type':'application/json'}});
    },

    urlForQuery(query, modelName) {
        let url = this._super(query, modelName);

        if (query) {
            query.w = query.w || config.taleIconWidth;
            query.h = query.h || config.taleIconHeight;
            let q = this.buildQueryParams(query);
            url += "?" + q;
        }

        return url;
    }
});

import Ember from 'ember';
import RSVP from 'rsvp';
import DS from 'ember-data';

import config from '../config/environment';
import buildQueryParamsMixin from 'wholetale/mixins/build-query-params';

export default DS.RESTAdapter.extend(buildQueryParamsMixin, {
    tokenHandler: Ember.inject.service("token-handler"),
    authRequest: Ember.inject.service(),

    host: config.apiHost,
    namespace: config.apiPath,

    primaryKey: "_id",

    query(store, type, query) {
        let url = this.buildURL(type.modelName, null, null, 'query', query);

        console.log("sending request for sils ... ");
        return new RSVP.Promise(resolve => resolve([{ _id: 1, _modelType: "sils", icon: "https://lorempixel.com/400/400/abstract/" }]));

        //TODO: uncomment this after sils endpoint working
        // return this.get('authRequest').send(url, {headers:{'content-type':'application/json'}});
    },

    urlForQuery(query, modelName) {
        let url = this._super(query, modelName);

        if (query) {
            query.w = query.w || config.taleIconWidth;
            query.h = query.h || config.taleIconHeight;
            let q = this.buildQueryParams(query);
            url += "?" + q;
        }

        return url;
    }
});

Use https for lorempixel to avoid mixed-content security warnings


/*exported DepthElement*/
var DepthElement = (function() {
  'use strict';

  function DepthElement( options ) {
    options = options || {};

    this.el = options.el;
    if ( this.el && typeof this.el === 'string' ) {
      this.el = document.querySelector( this.el );
    }

    if ( !this.el ) {
      return;
    }

    this.z = options.z;

    this.setTransform();
  }

  DepthElement.prototype.setTransform = function() {
    var transform = 'translateZ(' + this.z + 'px)';

    this.el.style.webkitTransform = transform;
    this.el.style.transform = transform;
  };

  DepthElement.prototype.setBlurRadius = function( blurRadius ) {
    var blur = 'blur(' + blurRadius + 'px)';

    this.el.style.webkitFilter = blur;
    this.el.style.filter = blur;
  };

  return DepthElement;

}) ();

/*exported DepthElement*/
var DepthElement = (function() {
  'use strict';

  function DepthElement( options ) {
    options = options || {};

    this.el = options.el;
    if ( this.el && typeof this.el === 'string' ) {
      this.el = document.querySelector( this.el );
    }

    if ( !this.el ) {
      return;
    }

    this.z = options.z;

    this.setTransform();
  }

  DepthElement.prototype.setTransform = function() {
    var transform = 'translateZ(' + this.z + 'px)';

    this.el.style.webkitTransform = transform;
    this.el.style.transform = transform;
  };

  DepthElement.prototype.setBlurRadius = function( blurRadius ) {
    var blur = blurRadius ? 'blur(' + blurRadius + 'px)' : '';

    this.el.style.webkitFilter = blur;
    this.el.style.filter = blur;
  };

  return DepthElement;

}) ();

Remove blur if blur radius is 0.


function ResponseEditWindow(responseID) {
	var SurveyDetailsView = require('ui/common/surveys/SurveyDetailsView')
	var ResponsesIndexView = require('ui/common/responses/ResponsesIndexView')
	var ResponseEditView = require('ui/common/responses/ResponseEditView')

	var self = Ti.UI.createWindow({
		title : 'Edit Response',
		backgroundColor : "#fff"
	});
	var view = new ResponseEditView(responseID);
	self.add(view);
	view.addEventListener('ResponseEditView:savedResponse', function() {
		navGroup.close(self);
	})

	return self;
}

module.exports = ResponseEditWindow;

function ResponseEditWindow(responseID) {
	var SurveyDetailsView = require('ui/common/surveys/SurveyDetailsView')
	var ResponsesIndexView = require('ui/common/responses/ResponsesIndexView')
	var ResponseEditView = require('ui/common/responses/ResponseEditView')

	var self = Ti.UI.createWindow({
		title : 'Edit Response',
		backgroundColor : "#fff"
	});
	var view = new ResponseEditView(responseID);
	self.add(view);
	view.addEventListener('ResponsesEditView:savedResponse', function() {
		navGroup.close(self);
	})

	return self;
}

module.exports = ResponseEditWindow;

Fix mis-named event-handler that was never getting executed.


import Em from 'ember';
var get = Em.get;
var copy = Em.copy;

var ArrayPauser = Em.ArrayProxy.extend({
	isPaused: false,

	buffer: Em.A(),

	addToBuffer: function (idx, removedCount, added) {
		var buffer = get(this, 'buffer');
		buffer.pushObject([idx, removedCount, added]);
	},

	clearBuffer: function () {
		var buffer = get(this, 'buffer');
		var arrangedContent = get(this, 'arrangedContent');
		buffer.forEach(function (args) {
			arrangedContent.replace(args[0], args[1], args[2]);
		});
		buffer.clear();
	}.observes('isPaused'),

	arrangedContent: function () {
		var content = get(this, 'content');
		var clone = copy(content);
		return clone;
	}.property('content'),

	contentArrayDidChange: function (arr, idx, removedCount, addedCount) {
		var added = arr.slice(idx, idx + addedCount);
		var isPaused = get(this, 'isPaused');
		var arrangedContent;
		if (isPaused) {
			this.addToBuffer(idx, removedCount, added);
		}
		else {
			arrangedContent = get(this, 'arrangedContent');
			arrangedContent.replace(idx, removedCount, added);
		}
		this.enumerableContentDidChange();
	}
});

export default ArrayPauser;

import Em from 'ember';
var get = Em.get;
var copy = Em.copy;

var ArrayPauser = Em.ArrayProxy.extend({
	isPaused: false,

	buffer: function () {
		return Em.A();
	}.property(),

	addToBuffer: function (idx, removedCount, added) {
		var buffer = get(this, 'buffer');
		buffer.pushObject([idx, removedCount, added]);
	},

	clearBuffer: function () {
		var buffer = get(this, 'buffer');
		var arrangedContent = get(this, 'arrangedContent');
		buffer.forEach(function (args) {
			arrangedContent.replace(args[0], args[1], args[2]);
		});
		buffer.clear();
	}.observes('isPaused'),

	arrangedContent: function () {
		var content = get(this, 'content');
		var clone = copy(content);
		return clone;
	}.property('content'),

	contentArrayDidChange: function (arr, idx, removedCount, addedCount) {
		var added = arr.slice(idx, idx + addedCount);
		var isPaused = get(this, 'isPaused');
		var arrangedContent;
		if (isPaused) {
			this.addToBuffer(idx, removedCount, added);
		}
		else {
			arrangedContent = get(this, 'arrangedContent');
			arrangedContent.replace(idx, removedCount, added);
		}
		this.enumerableContentDidChange();
	}
});

export default ArrayPauser;

Use property function to define buffer


#!/usr/bin/env node
'use strict';

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

var http          = require('http');
var ip            = require('ip');
var exec          = require('child_process').exec;
var createHandler = require('github-webhook-handler');

var handler = createHandler({
    secret: config.SECRET,
    path:   config.HOOK_PATH
});

http.createServer(function (req, res) {
    handler(req, res, function(err) {
        res.statusCode = 404;
        res.end('error')
    });
}).listen(config.PORT, function () {
    console.log('GitHub webhook running at: ' + ip.address() + ':' + config.PORT + config.HOOK_PATH);
    console.log('Listening for commits to branch ' + config.BRANCH);
});

handler.on('push', function (event) {
    if (event.payload.ref === config.BRANCH) {
        console.log(new Date(), ' : Running deployment now...');
        exec('sh deploy.sh', function(error, stdout, stderr) {
            console.log('stdout: ' + stdout);
            console.log('stderr: ' + stderr);
            if (error !== null) {
                console.log('exec error: ' + error);
            }
            console.log('Deployment finished!');
        });
    }
});

handler.on('error', function (error) {
    console.error('Webhook error: ' + error);
});

#!/usr/bin/env node
'use strict';

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

var http          = require('http');
var ip            = require('ip');
var exec          = require('child_process').exec;
var createHandler = require('github-webhook-handler');

var handler = createHandler({
    secret: config.SECRET,
    path:   config.HOOK_PATH
});

http.createServer(function (req, res) {
    handler(req, res, function(err) {
        res.statusCode = 404;
        res.end('error')
    });
}).listen(config.PORT, function () {
    console.log('GitHub webhook running at: http://' + ip.address() + ':' + config.PORT + config.HOOK_PATH);
    console.log('Listening for commits to branch ' + config.BRANCH);
});

handler.on('push', function (event) {
    if (event.payload.ref === config.BRANCH) {
        console.log(new Date(), ' : Running deployment now...');
        exec('sh deploy.sh', function(error, stdout, stderr) {
            console.log('stdout: ' + stdout);
            console.log('stderr: ' + stderr);
            if (error !== null) {
                console.log('exec error: ' + error);
            }
            console.log('Deployment finished!');
        });
    }
});

handler.on('error', function (error) {
    console.error('Webhook error: ' + error);
});

Print http start to beginning


var jwt = require('express-jwt');
var Express = require('express');
var Webtask = require('webtask-tools');

var app = Express();

// endpoints

app.get('/users', function (req, res) {
  res.status(200).json({ foo: 'bar' });
});

module.exports = Webtask.fromExpress(app);

var jwt = require('express-jwt');
var Express = require('express');
var Webtask = require('webtask-tools');

var app = Express();

app.use(function getAuth0Config (req, res, done) {
  //TODO: switch to req.webtaskContext.secrets when available
  var secrets = req.webtaskContext.data;

  // load auth0 config object with secret values
  req.auth0 = {
    client_id: secrets.client_id,
    client_secret: secrets.client_secret,
    domain: secrets.domain,
    authz_claims: secrets.authz_claims
  };

  // assert that all values were populated
  var missingKeys = Object.keys(req.auth0).reduce(function (previous, key) {
    var secret = req.auth0[key];
    if (secret === undefined)
      previous.push(key);
    return previous;
  }, []);
  if (missingKeys.length > 0)
    return done(new Error('Missing secrets: ' + missingKeys));

  done();
});

// endpoints

app.get('/users', function (req, res) {
  res.status(200).json({ foo: 'bar' });
});

module.exports = Webtask.fromExpress(app);

Add middleware to collect Auth0 config via secrets


module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    banner: '/*! <%= pkg.name %> <%= pkg.version %> | Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.licenses[0].type %> License */\n',

    concat: {
      options: {
        stripBanners: {
          block: true
        },
        banner: '<%= banner %>'
      },
      dist: {
        src: [ 'fileHash.js' ],
        dest: 'fileHash.js'
      }
    },

    jshint: {
      files: [ 'fileHash.js' ]
    },

    nodeunit: {
      files: [ 'test/test-*.js' ],
    },

    watch: {
      files: [ 'fileHash.js', 'test/*.js' ],
      tasks: [ 'release' ]
    }
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');

  grunt.registerTask('test', [ 'jshint', 'nodeunit' ]);
  grunt.registerTask('compile', [ 'concat' ]);
  grunt.registerTask('release', [ 'test', 'compile' ]);
  grunt.registerTask('dev', [ 'watch' ]);
  grunt.registerTask('default', [ 'dev' ]);
};

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    banner: '/*! <%= pkg.name %> <%= pkg.version %> | Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.licenses[0].type %> License */\n',

    concat: {
      options: {
        stripBanners: {
          block: true
        },
        banner: '<%= banner %>'
      },
      dist: {
        src: [ 'fileHash.js' ],
        dest: 'fileHash.js'
      }
    },

    jshint: {
      files: [ 'fileHash.js' ]
    },

    nodeunit: {
      files: [ 'test/test-*.js' ],
    },

    watch: {
      files: [ 'fileHash.js', 'test/*.js' ],
      tasks: [ 'test' ]
    }
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');

  grunt.registerTask('test', [ 'jshint', 'nodeunit' ]);
  grunt.registerTask('compile', [ 'concat' ]);
  grunt.registerTask('release', [ 'test', 'compile' ]);
  grunt.registerTask('dev', [ 'watch' ]);
  grunt.registerTask('default', [ 'dev' ]);
};

Remove compile step from watch


/**
 * External imports
 */
import libdebug from 'debug';

// create debug logger
const debug = libdebug('storage-adapter');

const PROTOTYPE = {
  find() {},
};

export default function createAdapter(options) {
  debug('passed-in options (keys only): %j', Object.keys(options));

  return Object.create(PROTOTYPE, {
    storage: {
      value: options.storage,
    },
  });
}

/**
 * External imports
 */
import libdebug from 'debug';

// create debug logger
const debug = libdebug('storage-adapter');

const PROTOTYPE = {
  find() {},
};

export default function createAdapter(options = {}) {
  debug('passed-in options (keys only): %j', Object.keys(options));

  return Object.create(PROTOTYPE, {
    storage: {
      value: options.storage,
    },
  });
}

Update adapter factory to provide default options.


import {
  Future,
  isFuture,
  reject,
  of,
  never,
  isNever
} from './core';

import * as dispatchers from './dispatchers/index';

import {after} from './after';
import {both} from './both';
import {cache} from './cache';
import {chainRec} from './chain-rec';
import {encase, encase2, encase3, attempt} from './encase';
import {first} from './race';
import {fromPromise, fromPromise2, fromPromise3} from './from-promise';
import {go} from './go';
import {hook} from './hook';
import {node} from './node';
import {parallel} from './parallel';
import {rejectAfter} from './reject-after';

import {error} from './internal/throw';

if(typeof Object.create !== 'function') error('Please polyfill Object.create to use Fluture');
if(typeof Object.assign !== 'function') error('Please polyfill Object.assign to use Fluture');

export default Object.assign(Future, dispatchers, {
  Future,
  isFuture,
  reject,
  of,
  never,
  isNever,
  after,
  both,
  cache,
  first,
  fromPromise,
  fromPromise2,
  fromPromise3,
  hook,
  node,
  parallel,
  rejectAfter,
  chainRec,
  encase,
  encase2,
  encase3,
  attempt,
  go,
  try: attempt,
  do: go
});

import {
  Future,
  isFuture,
  reject,
  of,
  never,
  isNever
} from './core';

import * as dispatchers from './dispatchers/index.js';

import {after} from './after';
import {both} from './both';
import {cache} from './cache';
import {chainRec} from './chain-rec';
import {encase, encase2, encase3, attempt} from './encase';
import {first} from './race';
import {fromPromise, fromPromise2, fromPromise3} from './from-promise';
import {go} from './go';
import {hook} from './hook';
import {node} from './node';
import {parallel} from './parallel';
import {rejectAfter} from './reject-after';

import {error} from './internal/throw';

if(typeof Object.create !== 'function') error('Please polyfill Object.create to use Fluture');
if(typeof Object.assign !== 'function') error('Please polyfill Object.assign to use Fluture');

export default Object.assign(Future, dispatchers, {
  Future,
  isFuture,
  reject,
  of,
  never,
  isNever,
  after,
  both,
  cache,
  first,
  fromPromise,
  fromPromise2,
  fromPromise3,
  hook,
  node,
  parallel,
  rejectAfter,
  chainRec,
  encase,
  encase2,
  encase3,
  attempt,
  go,
  try: attempt,
  do: go
});

Make a first attempt to identify the build issue


'use strict';

const path = require('path');
const constants = require(path.resolve('src/utils/constants'));
const express = require('express');
const pokemonRoutes = require(path.resolve('src/routes/pokemon'));
const pokemonModel = require(path.resolve('src/model/pokemon'));
const bodyParser = require('body-parser');
const helmet = require('helmet');

/* App Configuration */
const app = express();
app.set('port', constants.server.port);
app.set('title', constants.app.name);
app.use(bodyParser.json());
app.use(helmet());

pokemonRoutes(app);

app.listen(app.get('port'), function () {

	pokemonModel.sync({force: true}).then(function () {
        console.log('Listening app ' + app.get('title') + ' on port ' + app.get('port'));
    });

});

module.exports = app;

'use strict';

const path = require('path');
const constants = require(path.resolve('src/utils/constants'));
const express = require('express');
const pokemonRoutes = require(path.resolve('src/routes/pokemon'));
const pokemonModel = require(path.resolve('src/model/pokemon'));
const bodyParser = require('body-parser');
const helmet = require('helmet');

/* App Configuration */
const app = express();
app.set('port', constants.server.port);
app.set('title', constants.app.name);
app.use(bodyParser.json());
app.use(helmet());

pokemonRoutes(app);

app.listen(app.get('port'), function () {

	pokemonModel.sync({force: true}).then(function () {
        console.log('Listening app ' + app.get('title') + ' on port ' + app.get('port'));
    }).error(function(error) {
        console.error('Error while starting app/sync database :(' + error);
	});

});

module.exports = app;

Handle with possible error on start .


import React from 'react';
import ReactDOM from 'react-dom';
import Homepage from './components/Homepage';
import Docs from './containers/Docs';
import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router';
import { useScroll } from 'react-router-scroll';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import './lib/autolinker';

ReactDOM.render(
  (
    <Router
      history={browserHistory}
    >
      <Route path="/" component={Homepage} />
      <Route path="/docs(/:sectionId/(:itemId))" component={Docs} />

    </Router>
  ),
  document.getElementById('root')
);

import React from 'react';
import ReactDOM from 'react-dom';
import Homepage from './components/Homepage';
import Docs from './containers/Docs';
import { Router, Route, browserHistory, applyRouterMiddleware } from 'react-router';
import { useScroll } from 'react-router-scroll';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import './lib/autolinker';

ReactDOM.render(
  (
    <Router
      history={browserHistory}
      render={applyRouterMiddleware(useScroll())}
    >
      <Route path="/" component={Homepage} />
      <Route path="/docs(/:sectionId/(:itemId))" component={Docs} />

    </Router>
  ),
  document.getElementById('root')
);

Add URL scroll management back.


const PlotCard = require('../../plotcard.js');

class BattleOfTheBlackwater extends PlotCard {
    setupCardAbilities() {
        this.whenRevealed({
            chooseOpponent: true,
            handler: context => {
                this.removeAllDupes(this.controller);
                this.removeAllDupes(context.opponent);

                this.game.addMessage('{0} uses {1} to have themself and {2} discard each duplicate they control',
                    this.controller, this, context.opponent);
            }
        });
    }

    removeAllDupes(player) {
        // TODO: This implementation only works for 2 player games. The wording
        // of the card is that you discard each duplicate you and your opponent
        // control, not discarding the dupes on cards you or the opponent
        // control. But for 2 player, discarding each dupe is fine.
        let characters = player.filterCardsInPlay(card => card.dupes.length > 0);
        let dupes = characters.reduce((dupes, card) => dupes.concat(card.dupes, []));
        player.discardCards(dupes, false);
    }
}

BattleOfTheBlackwater.code = '04120';

module.exports = BattleOfTheBlackwater;

const PlotCard = require('../../plotcard.js');

class BattleOfTheBlackwater extends PlotCard {
    setupCardAbilities() {
        this.whenRevealed({
            chooseOpponent: true,
            handler: context => {
                this.removeAllDupes(this.controller);
                this.removeAllDupes(context.opponent);

                this.game.addMessage('{0} uses {1} to have themself and {2} discard each duplicate they control',
                    this.controller, this, context.opponent);
            }
        });
    }

    removeAllDupes(player) {
        // TODO: This implementation only works for 2 player games. The wording
        // of the card is that you discard each duplicate you and your opponent
        // control, not discarding the dupes on cards you or the opponent
        // control. But for 2 player, discarding each dupe is fine.
        let characters = player.filterCardsInPlay(card => card.dupes.length > 0);
        if(characters.length > 0) {
            let dupes = characters.reduce((dupes, card) => dupes.concat(card.dupes), []);

            if(dupes.length > 0) {
                player.discardCards(dupes, false);
            }
        }
    }
}

BattleOfTheBlackwater.code = '04120';

module.exports = BattleOfTheBlackwater;

Add some checking around dupes so we dont end up trying to processing empty arrays add a default to the reduce so we dont end up trying to reduce nothing Fixes THRONETEKI-24J Fixed THRONETEKI-243


const events = {
  LOGIN: 1,
  LOGOUT: 2,
  SELECT_MODULE: 3,
};

export default events;

const events = {
  LOGIN: 1,
  LOGOUT: 2,
  SELECT_MODULE: 3,
  CHANGE_SERVICE_POINT: 4,
};

export default events;

Add change service point event


import {inject} from 'aurelia-framework';
import {LayoutService} from './services/layout-service';

import type {RouterConfiguration, Router} from 'aurelia-router';

@inject(LayoutService)
export class App {
  router: Router;
  layoutService: LayoutService;

  constructor(layoutService: LayoutService) {
    this.layoutService = layoutService;
  }

  configureRouter(config: RouterConfiguration, router: Router) {
    config.title = 'Bridge calculator';
    config.options.pushState = !location.host.includes('github'); // GitHub does not support push state
    config.map([
      { route: '', name: 'menu', moduleId: './menu' },
      { route: '/entry', name: 'entry', moduleId: './entry/entry', title: 'New Game' }
    ]);

    this.router = router;
  }

  showHelp() {

  }

  back() {
    this.router.navigateToRoute('menu');
  }
}

import {inject} from 'aurelia-framework';
import {LayoutService} from './services/layout-service';

import type {RouterConfiguration, Router} from 'aurelia-router';

@inject(LayoutService)
export class App {
  router: Router;
  layoutService: LayoutService;

  constructor(layoutService: LayoutService) {
    this.layoutService = layoutService;
  }

  configureRouter(config: RouterConfiguration, router: Router) {
    config.title = 'Bridge calculator';
    config.options.pushState = !location.host.includes('github'); // GitHub does not support push state
    config.map([
      { route: '', name: 'menu', moduleId: './menu/menu' },
      { route: '/entry', name: 'entry', moduleId: './entry/entry', title: 'New Game' }
    ]);

    this.router = router;
  }

  showHelp() {

  }

  back() {
    this.router.navigateToRoute('menu');
  }
}

Fix menu view not refactored


'use strict';

var mixin         = require('es5-ext/object/mixin')
  , d             = require('d')
  , DOMRadio      = require('dbjs-dom/input/enum').Radio
  , RadioBtnGroup = require('./_inline-button-group')

  , createOption = DOMRadio.prototype.createOption
  , Radio;

module.exports = Radio = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.controlsOptions = Object(options.controls);
	DOMRadio.call(this, document, type, options);
};

Radio.prototype = Object.create(DOMRadio.prototype);
mixin(Radio.prototype, RadioBtnGroup.prototype);
Object.defineProperties(Radio.prototype, {
	constructor: d(Radio),
	createOption: d(function (name) {
		var dom = createOption.call(this, name);
		this.listItems[name] = dom = dom.firstChild;
		return dom;
	})
});

'use strict';

var assign        = require('es5-ext/object/assign')
  , mixin         = require('es5-ext/object/mixin')
  , d             = require('d')
  , autoBind      = require('d/auto-bind')
  , DOMRadio      = require('dbjs-dom/input/enum').Radio
  , RadioBtnGroup = require('./_inline-button-group')

  , createOption = DOMRadio.prototype.createOption
  , reload =  DOMRadio.prototype.reload
  , Radio;

module.exports = Radio = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.controlsOptions = Object(options.controls);
	DOMRadio.call(this, document, type, options);
};

Radio.prototype = Object.create(DOMRadio.prototype);
mixin(Radio.prototype, RadioBtnGroup.prototype);
Object.defineProperties(Radio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (name) {
		var dom = createOption.call(this, name);
		this.listItems[name] = dom = dom.firstChild;
		return dom;
	})
}, autoBind({
	reload: d(function () {
		reload.apply(this, arguments);
		this.dom.appendChild(this.classHandlerScript);
	})
})));

Fix inline script visibility in enum handler


var _ = require("lodash");
var conf = require("./configuration.js");
var Logger = require("../logger.js");

module.exports = function() {
  var s_oauthData = conf.loadOAuthConf();

  var s_api = s_oauthData.map(function(tokens) {
    var userTokens = {
      API_OAUTH_TOKEN: tokens.token,
      API_OAUTH_TOKEN_SECRET: tokens.secret
    };

    var api = require("clever-client")(_.defaults(conf, {
      API_HOST: conf.API_HOST,
      API_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      API_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      logger: Logger
    }, userTokens));

    // Waiting for clever-client to be fully node compliant
    api.session.getAuthorization = function(httpMethod, url, params) {
      return api.session.getHMACAuthorization(httpMethod, url, params, userTokens);
    };

    return api;
  });

  return s_api;
};

var _ = require("lodash");
var conf = require("./configuration.js");
var Logger = require("../logger.js");

module.exports = function() {
  var s_oauthData = conf.loadOAuthConf();

  var s_api = s_oauthData.map(function(tokens) {
    var api = require("clever-client")(_.defaults(conf, {
      API_HOST: conf.API_HOST,
      API_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      API_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      API_OAUTH_TOKEN: tokens.token,
      API_OAUTH_TOKEN_SECRET: tokens.secret,
      logger: Logger
    }));

    // Waiting for clever-client to be fully node compliant
    api.session.getAuthorization = function(httpMethod, url, params) {
      return api.session.getHMACAuthorization(httpMethod, url, params, {
        user_oauth_token: tokens.token,
        user_oauth_token_secret: tokens.secret
      });
    };

    return api;
  });

  return s_api;
};

Fix getAuthorization tokens keys names


var dispatcher = require('../dispatcher');
var Empty = React.createClass({
  render: function() {
    return

;
  }
});

module.exports = {
  render: require('./render'),
  builtInStore: require('./built-in-store'),
  actionBuilder: require('./action-builder'),
  clear: require('./clear'),
  mountedComponents: require('./mounted-components'),
  changeMode(newMode, previousMode){
    var mode = {newMode: newMode, previousMode: previousMode};
    dispatcher.handleViewAction({data: {mode: mode}, type: 'update'});
  },
  changeRoute(newRoute){
    dispatcher.handleViewAction({data: {route: newRoute}, type: 'update'});
  },
  clearCartridge(){
    dispatcher.handleViewAction({
        data: {
          cartridgeComponent: {component: Empty},
          summaryComponent: {component: Empty},
          actions: {primary: [], secondary: []}
        },
        type: 'update'
      });
  }
};

var dispatcher = require('../dispatcher');
var Empty = React.createClass({
  render: function() {
    return

;
  }
});

module.exports = {
  render: require('./render'),
  builtInStore: require('./built-in-store'),
  actionBuilder: require('./action-builder'),
  clear: require('./clear'),
  mountedComponents: require('./mounted-components'),
  changeMode(newMode, previousMode){
    var mode = {newMode: newMode, previousMode: previousMode};
    dispatcher.handleViewAction({data: {mode: mode}, type: 'update'});
  },
  changeRoute(newRoute){
    dispatcher.handleViewAction({data: {route: newRoute}, type: 'update'});
  },
  clearCartridge(){
    dispatcher.handleViewAction({
        data: {
          cartridgeComponent: {component: Empty},
          barContentLeftComponent: {component: Empty},
          summaryComponent: {component: Empty},
          actions: {primary: [], secondary: []}
        },
        type: 'update'
      });
  }
};

Add the left bar in the clear cartrige.


// Remove the PUBLIC_URL, if defined
process.env.PUBLIC_URL = '';

require('babel-register')();

const { jsdom } = require('jsdom')
const moduleAlias = require('module-alias')
const path = require('path')

moduleAlias.addAlias('common', path.join(__dirname, '../../src'))

const exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('<!doctype html>');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

global.navigator = window.navigator = {
  userAgent: 'node.js',
  platform: 'node.js',
};

const chai = require('chai')
const chaiEnzyme = require('chai-enzyme')

global.expect = chai.expect
chai.use(chaiEnzyme())

// Remove the PUBLIC_URL, if defined
process.env.PUBLIC_URL = '';

process.env.INSPIRE_API_URL = 'inspire-api-url'

require('babel-register')();

const { jsdom } = require('jsdom')
const moduleAlias = require('module-alias')
const path = require('path')

moduleAlias.addAlias('common', path.join(__dirname, '../../src'))

const exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('<!doctype html>');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

global.navigator = window.navigator = {
  userAgent: 'node.js',
  platform: 'node.js',
};

const chai = require('chai')
const chaiEnzyme = require('chai-enzyme')

global.expect = chai.expect
chai.use(chaiEnzyme())

Set a default INSPIRE_API_URL value for tests


const gulp = require('gulp');

const spawn = require('child_process').spawn;
const spawnSync = require('child_process').spawnSync;

gulp.task('test', function(cb) {
    let command = [ 'run', 'python', 'manage.py', 'test' ];
    let args = process.argv;
    if (args[3] == '--test' && args[4]) {
        command.push(args[4]);
    }
    spawn(
        'pipenv',
        command,
        {
            stdio: 'inherit'
        }
    ).on('exit', (code) => {
        process.exit(code);
    });
});

gulp.task('coverage', function() {
    spawnSync(
        'pipenv',
        [
            'run',
            'coverage',
            'run',
            '--source=core,api',
            'manage.py',
            'test'
        ],
        {
            stdio: 'inherit'
        }
    ).on('exit', (code) => {
        process.exit(code);
    });
    spawnSync(
        'pipenv',
        [
            'run',
            'coverage',
            'report',
            '-m'
        ],
        {
            stdio: 'inherit'
        }
    ).on('exit', (code) => {
        process.exit(code);
    });
});

const gulp = require('gulp');

const spawn = require('child_process').spawn;

gulp.task('test', function(cb) {
    let command = [ 'run', 'python', 'manage.py', 'test' ];
    let args = process.argv;
    if (args[3] == '--test' && args[4]) {
        command.push(args[4]);
    }
    spawn(
        'pipenv',
        command,
        {
            stdio: 'inherit'
        }
    ).on('exit', (code) => {
        process.exit(code);
    });
});

gulp.task('coverage', function() {
    spawn(
        'pipenv',
        [
            'run',
            'coverage',
            'run',
            '--source=core,api',
            'manage.py',
            'test'
        ],
        {
            stdio: 'inherit'
        }
    ).on('exit', (code) => {
        if (code != 0) {
            process.exit(code);
        }
        spawn(
            'pipenv',
            [
                'run',
                'coverage',
                'report',
                '-m'
            ],
            {
                stdio: 'inherit'
            }
        ).on('exit', (code) => {
            process.exit(code);
        });
    });
});

Convert gulp coverage spawnSyncs to chained spawns.


import React from 'react';

const Accounts = ({id, className, accounts, accountsMap, account, paymenttype, onAccountsFieldChange }) => (
    <select id={id} className={className} onChange={(event) => onAccountsFieldChange(event.target.value, accountsMap, paymenttype)} value={account.fullName}>
        {accounts.map((val) => <option key={val.accountId}>{val.fullName}</option>)}
    </select>
);

export default Accounts;

import React from 'react';

const Accounts = ({id, className, accounts, accountsMap, account, paymenttype, onAccountsFieldChange }) => (
    <select id={id} className={className} onChange={(event) => onAccountsFieldChange(event.target.value, accountsMap, paymenttype)} value={account.fullName}>
        {accounts.map((val) => <option key={'account_' + val.accountId}>{val.fullName}</option>)}
    </select>
);

export default Accounts;

Make account react component keys unique


module.exports = {
	watching: {
		poll: true,
		interval: 10000
	},
	scripts: {
		loader: 'browserify',
		files: [
			'public/scripts/main.js'
		]
	},
	styles: {
		directory: 'public/styles'
	},
	server: {
		files: [
			'views/**/*.ejs',
			'routes/**/*.js',
			'models/**/*.js',
			'lib/**/*.js',
			'config/**/*.js',
			'errors/**/*.js',
			'app.js'
		]
	}
};

module.exports = {
	scripts: {
		loader: 'browserify',
		files: [
			'public/scripts/main.js'
		]
	},
	styles: {
		directory: 'public/styles'
	},
	server: {
		files: [
			'views/**/*.ejs',
			'routes/**/*.js',
			'models/**/*.js',
			'lib/**/*.js',
			'config/**/*.js',
			'errors/**/*.js',
			'app.js'
		]
	},
	watching: {
		poll: false,
		interval: 100
	}
};

Move around watch config and change default interval


/* globals babel */

// Dependencies

const {types} = Focus.component;

const LivePreview = React.createClass({
    displayName: 'LivePreview',
    propTypes: {
        code: types('string'),
        style: types('object')
    },
    style: {
        title: {
            margin: '15px',
            color: '#372B3F'
        },
        component: {
            padding: '5px'
        }
    },
    /**
    * Render the component.
    * @return {HTML} the rendered component
    */
    render() {
        const {code, style: mainStyle} = this.props;
        const {style} = this;
        let content;
        try {
            /* eslint-disable */
            content = eval(babel.transform(`(function(){${code}})()`).code);
            /* eslint-enable */
        } catch (e) {
            content = e.toString();
        }

        return (

Aperçu du composant

                    {content}

        );
    }
});

module.exports = LivePreview;

/* globals babel */

// Dependencies

const {types} = Focus.component;

const LivePreview = React.createClass({
    displayName: 'LivePreview',
    propTypes: {
        code: types('string'),
        style: types('object')
    },
    style: {
        title: {
            margin: '15px',
            color: '#372B3F'
        },
        component: {
            padding: '5px'
        }
    },
    /**
    * Render the component.
    * @return {HTML} the rendered component
    */
    render() {
        const {code, style: mainStyle} = this.props;
        const {style} = this;
        let content;
        try {
            /* eslint-disable */
            content = eval(babel.transform(`(function(){${code}})()`, {stage: 0}).code);
            /* eslint-enable */
        } catch (e) {
            content = e.toString();
        }

        return (

Aperçu du composant

                    {content}

        );
    }
});

module.exports = LivePreview;

Support ES7 in live example


var Invoice = DS.Model.extend({

  tax: DS.attr('number'),
  invoice_number: DS.attr('number'),
  items: DS.hasMany('InvoiceItem'),

  sub_total: function(){
    return this.get('items').getEach('total').reduce(function(sum, partial) {
      return sum + partial;
    }, 0);
  }.property("items.@each.total"),

  tax_total: function(){
    return this.get("sub_total")/100.0*this.get("tax")
  }.property("sub_total"),

  total: function(){
    return this.get("sub_total")+this.get("tax_total")
  }.property("sub_total", "tax_total")
});

export default Invoice;

var Invoice = DS.Model.extend({

  tax: DS.attr('number'),
  invoice_number: DS.attr('number'),
  items: DS.hasMany('InvoiceItem'),

  sub_total: function(){
    return this.get('items').getEach('total').reduce(function(sum, partial) {
      return sum + partial;
    }, 0);
  }.property("items.@each.total"),

  tax_total: function(){
    return this.get("sub_total")/100.0*this.get("tax");
  }.property("sub_total"),

  total: function(){
    return this.get("sub_total")+this.get("tax_total");
  }.property("sub_total", "tax_total")
});

export default Invoice;

Add semicolon to return statements


angular.module('app', ['ngJustGage']).controller('myController', function($scope) {});

angular.module('app', ['ngJustGage']).controller('myController', function($scope) {
  $scope.value1 = 1;
});

Add controller values for demo


import React, { Component } from 'react';
import styled from 'styled-components';

const FooterComponent = styled.footer`
  padding-bottom: 3rem;
  background: transparent;
`;

class Footer extends Component {
  render() {
    return (
      <FooterComponent className="footer">

              {' '}
              Made with{' '}

                ♥
              {' '}
              by Ted Serbinski and{' '}
              Shriyash Jalukar and{' '}
              Andrew Mager in Detroit, Michigan |{' '}
              Fork me on Github

      </FooterComponent>
    );
  }
}

export default Footer;

Fix source link in footer


// This will be our application entry. We'll setup our server here.
const app = require('../index'); // The express app we just created on index.js

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

// This will be our application entry. We'll setup our server here.
const app = require('../index'); // The express app we just created on index.js

const port = process.env.PORT || 3000;

app.listen(port, '192.168.43.196' || 'localhost', () => {
  console.log('Server started on port ' + port);
});

Include IP address so as to access my localhost from my mobile phone


/**
 * The SpinnerTabsPresenter class for the SpinnerTabsPresenter view.
 *
 * @return SpinnerTabsPresenter class.
 */
define([
  'underscore',
  'mps',
  'map/presenters/PresenterClass',
], function(_, mps, PresenterClass) {

  'use strict';

  var SpinnerTabsPresenter = PresenterClass.extend({

    init: function(view) {
      this.view = view;
      this._super();
    },

    /**
     * Application subscriptions.
     */
    _subscriptions: [{
      'Spinner/start': function(allowCancel) {
        this.view.start();

        if (allowCancel === false) {
          this.view.$el.find('button').hide();
        }
      }
    }, {
      'Spinner/stop': function() {
        this.view.stop();
      }
    }],

    cancel: function() {
      mps.publish('Spinner/cancel');
    }

  });

  return SpinnerTabsPresenter;
});

/**
 * The SpinnerTabsPresenter class for the SpinnerTabsPresenter view.
 *
 * @return SpinnerTabsPresenter class.
 */
define([
  'underscore',
  'mps',
  'map/presenters/PresenterClass',
], function(_, mps, PresenterClass) {

  'use strict';

  var SpinnerTabsPresenter = PresenterClass.extend({

    init: function(view) {
      this.view = view;
      this._super();
    },

    /**
     * Application subscriptions.
     */
    _subscriptions: [{
      'Spinner/start': function(allowCancel) {
        this.view.start();

        if (allowCancel === false) {
          this.view.$el.find('button').hide();
        } else {
          this.view.$el.show('button').hide();
        }
      }
    }, {
      'Spinner/stop': function() {
        this.view.stop();
      }
    }],

    cancel: function() {
      mps.publish('Spinner/cancel');
    }

  });

  return SpinnerTabsPresenter;
});

Fix loading indicator for subscribe view


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require turbolinks
//= require_tree .

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require bootstrap/dist/js/bootstrap
//= require turbolinks
//= require_tree .

Fix bootstrap JS in asset pipeline


'use strict';

angular.module('streamium.peer', [])

.service('PeerJS', function() {

  function getPeerJSConfig(config, index) {
    if (index >= config.peerServers.length) throw new Error('Invalid Server number');

    var ret  = angular.copy(config.peerJS);
    ret.key  = config.peerServers[index].key;
    ret.host = config.peerServers[index].host;
    ret.port = config.peerServers[index].port;
    ret.index = index;
    return ret;
  }

  return {
    primary: getPeerJSConfig(config, 0),
    secondary: getPeerJSConfig(config, 1),
    get: function(index) {
      return getPeerJSConfig(config, index);
    }
  };

});

'use strict';

angular.module('streamium.peer', [])

.service('PeerJS', function(bitcore) {

  function getPeerJSConfig(config, index) {
    if (index >= config.peerServers.length) throw new Error('Invalid Server number');

    var ret = angular.copy(config.peerJS);
    bitcore.deps._.extend(ret, config.peerServers[index]);
    ret.index = index;
    return ret;
  }

  return {
    primary: getPeerJSConfig(config, 0),
    secondary: getPeerJSConfig(config, 1),
    get: function(index) {
      return getPeerJSConfig(config, index);
    }
  };

});

Extend object; dont add undefined `keys`


import React from 'react'
import { Route, IndexRedirect } from 'react-router'

import Main from './Main'
import StorageIndex from './contents/StorageIndex'
import NoteList from './contents/NoteList'
import StorageCreate from './contents/StorageCreate'

const routes = (
  <Route path='/' component={Main}>

    <IndexRedirect to='storages/Notebook' />

    <Route path='storages/:storageName'>
      <IndexRedirect to='Notes' />
      <Route path='all-notes' component={NoteList} />
      <Route path='settings' component={StorageIndex} />
      <Route path='folders/:folderName' component={NoteList} />
    </Route>

    <Route path='new-storage' component={StorageCreate} />

  </Route>
)

export default routes

import React from 'react'
import { Route, IndexRedirect } from 'react-router'

import Main from './Main'
import StorageIndex from './contents/StorageIndex'
import NoteList from './contents/NoteList'
import StorageCreate from './contents/StorageCreate'

const routes = (
  <Route path='/' component={Main}>

    <IndexRedirect to='storages/Notebook' />

    <Route path='storages/:storageName'>
      <IndexRedirect to='folders/Notes' />
      <Route path='all-notes' component={NoteList} />
      <Route path='settings' component={StorageIndex} />
      <Route path='folders/:folderName' component={NoteList} />
    </Route>

    <Route path='new-storage' component={StorageCreate} />

  </Route>
)

export default routes

Fix redirect bug fix when first app start


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require bootstrap-sprockets
//= require jquery_ujs
//= require nicEdit
//= require bootstrap-datepicker
//= require_tree .

$.fn.datepicker.defaults.format = "yyyy-mm-dd";

$(function() {
    $('.local-time').each(function(_) {
        var dd = new Date($(this).text());
        $(this).text(dd);
    });
})

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require bootstrap-sprockets
//= require jquery_ujs
//= require nicEdit
//= require bootstrap-datepicker
//= require_tree .

$.fn.datepicker.defaults.format = "yyyy-mm-dd";

$(function() {
    $('.local-time').each(function(_) {
        var dd = new Date($(this).text());

        if (!isNaN(dd.getTime())) {
          $(this).text(dd);
        }
    });
})

Check for "invalid date" case in JS.


#!/usr/bin/env node
var Console = require("./console")
  , arg, options = {
      workDir: process.cwd()
    , consolePath: "/roil"
    , port : 3000
    }
  , args = process.argv.slice(2)
  , connect = require("connect")
  , server = connect.createServer()
  , c = new Console()

while (arg = args.shift()) {
  switch (arg) {
    case "-d":
    case "--work-dir":
      options.workDir = args.shift()
      break;
    case "-p":
    case "--port":
      options.port = parseInt(args.shift(), 10)
      break;
    case "-c":
    case "--console-path":
      options.consolePath = args.shift()
      break;
    case "--debug":
      options.debug = true
      break;
    defaut:
      throw new Error("Unknown argument: " + arg)
  }
}

c.attach(server, options)
server.listen(options.port)

if (options.debug) {
  var context = require("repl").start().context
  context.c = c
  context.s = server
  c._socketListener.on("connection", function(client) {
    client.on("message", function(msg) {
      console.log(msg)
    })
  })
  process.on("uncaughtException", function(e) {
    console.log(e.stack || e.message)
  })
}

#!/usr/bin/env node
var Console = require("./console")
  , arg, options = {
      workDir: process.cwd()
    , consolePath: "/roil/"
    , port : 3000
    }
  , args = process.argv.slice(2)
  , connect = require("connect")
  , server = connect.createServer()
  , c = new Console()

while (arg = args.shift()) {
  switch (arg) {
    case "-d":
    case "--work-dir":
      options.workDir = args.shift()
      break;
    case "-p":
    case "--port":
      options.port = parseInt(args.shift(), 10)
      break;
    case "-c":
    case "--console-path":
      options.consolePath = args.shift()
      break;
    case "--debug":
      options.debug = true
      break;
    defaut:
      throw new Error("Unknown argument: " + arg)
  }
}

c.attach(server, options)
server.listen(options.port)

if (options.debug) {
  var context = require("repl").start().context
  context.c = c
  context.s = server
  c._socketListener.on("connection", function(client) {
    client.on("message", function(msg) {
      console.log(msg)
    })
  })
  process.on("uncaughtException", function(e) {
    console.log(e.stack || e.message)
  })
}

Fix pathHead to ensure console works under that path


import make from "./make";

export const setHue = (color, hueValue) => {
  if (hueValue === null) return color;
  const { s, l, a } = color;
  return make.fromHsl(hueValue, s, l, a);
}

export const spin = (color, degrees = 0) => {
  const { h } = color;
  const newHue = h + degrees;

  if (newHue < 0) {
    return setHue(color, 360 - Math.abs(newHue));
  }

  if (newHue > 360) {
    return setHue(color, newHue - 360);
  }

  return setHue(color, newHue);
}

export const complimentary = (color) => spin(color, 180);

import make from "./make";

export const setHue = (color, hueValue) => {
  if (hueValue === null) return color;
  const { s, l, a } = color;
  return make.fromHsl(hueValue, s, l, a);
}

export const spin = (color, degrees = 0) => {
  const { h } = color;
  const newHue = h + degrees;

  return setHue(color, (newHue + 360) % 360);
}

export const complimentary = (color) => spin(color, 180);

Add modifier before modulus to eliminate check for +/-


Fix new NASM number regexp


import notificationActions from '../flux/notificationActions'
window.addEventListener('error', notificationActions.logError)
notificationActions.startProgress(1, 'Testing Progress Bars', 1, 4)
notificationActions.logMessage(0, 'Testing Messages')
notificationActions.logError({message: 'Testing Errors'})

import React from 'react'
import Notifications from '../elements/notifications'
React.render(React.createElement(Notifications), document.querySelector('.notifications'))

import notificationActions from '../flux/notificationActions'
window.addEventListener('error', notificationActions.logError)
notificationActions.startProgress(1, 'Testing Progress Bars', {max: 4, value: 1}, true)
notificationActions.logMessage(0, 'Testing Messages')
notificationActions.logError({message: 'Testing Errors'})

import React from 'react'
import Notifications from '../elements/notifications'
React.render(React.createElement(Notifications), document.querySelector('.notifications'))

Update the call to testing progress bars


'use strict';

/*
 * Module that contains a controller in charge of
 * viewing a user's profile. TBD
 */

angular.module('userprofile', [])

.controller('UserProfileCtrl', function($scope, $stateParams) {
           $scope.user = $stateParams.user;
});

'use strict';

/*
 * Module that contains a controller in charge of
 * viewing a user's profile, both your own and other's
 */

angular.module('userprofile', [])

.controller('UserProfileCtrl', function($scope, $localStorage, $stateParams) {
        if ($stateParams.user) {
           $scope.user  = $stateParams.user;
        } else {
            $scope.user = $localStorage.user;
        }
        console.log($scope.user);
});

Allow user profile controller to also load data from localStorage


import {Server} from 'hapi'
import inert from 'inert'
import getProjects from './app/get-projects'

/**
 * @param {string} configFilename The config filename
 * @param {number} port The port number
 */
export function serve(configFilename, port) {

  const server = new Server()

  server.connection({port})

  server.register(inert, err => {

    if (err) {
      console.log(err.stack)
      process.exit(1)
    }

    server.route({
      method: 'GET',
      path: '/api/projects',
      handler: (request, reply) => {
        reply(getProjects(configFilename))
      }
    })

    server.route({
      method: 'GET',
      path: '/site/{param*}',
      handler: { directory: { path: 'site', listing: true } }
    })

    server.start(err => {
      if (err) {
        console.log(err.stack)
        return
      }

      try {

        // checks if the config file is ok, does not use the returns projects object
        getProjects(configFilename)

      } catch (e) {

        console.log(e.stack)

        process.exit(1)
      }

      console.log('Server running at:', server.info.uri + '/site')
    })

  })

}

import {Server} from 'hapi'
import inert from 'inert'
import path from 'path'
import getProjects from './app/get-projects'

/**
 * @param {string} configFilename The config filename
 * @param {number} port The port number
 */
export function serve(configFilename, port) {

  const server = new Server()

  server.connection({port})

  server.register(inert, err => {

    if (err) {
      console.log(err.stack)
      process.exit(1)
    }

    server.route({
      method: 'GET',
      path: '/api/projects',
      handler: (request, reply) => {
        reply(getProjects(configFilename))
      }
    })

    server.route({
      method: 'GET',
      path: '/site/{param*}',
      handler: { directory: { path: `${path.dirname(__dirname)}/site`, listing: true } }
    })

    server.start(err => {
      if (err) {
        console.log(err.stack)
        return
      }

      try {

        // checks if the config file is ok, does not use the returns projects object
        getProjects(configFilename)

      } catch (e) {

        console.log(e.stack)

        process.exit(1)
      }

      console.log('Server running at:', server.info.uri + '/site')
    })

  })

}

Fix the static resources path


import {Server} from 'hapi'
import Mongoose from 'mongoose'

import Config from '../config'
import Routes from './routes'

let server = new Server()

Mongoose.connect(Config.mongo.url, (err) => {
  if (err) throw err
  console.log(`Connected to ${Config.mongo.url}`)
})

server.connection({
  host: Config.host,
  port: parseInt(Config.port)
})

server.route(Routes)

server.start(err => {
  if (err) throw err

  console.log('demographics is running at...', server.info.uri)
})

export default server

import {Server} from 'hapi'
import Mongoose from 'mongoose'

import Config from '../config'
import Routes from './routes'

let server = new Server()

Mongoose.connect(Config.mongo.url, (err) => {
  if (err) throw err
  console.log(`Connected to ${Config.mongo.url}`)
})

server.connection({
  host: Config.env !== 'production' ? Config.host : null,
  port: parseInt(Config.port, 10),
  routes: { cors: true }
})

server.route(Routes)

server.start(err => {
  if (err) throw err

  console.log('demographics is running at...', server.info.uri)
})

export default server

Add conditional to server connection host and allow CORS


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

global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
global.WebSocket = require('ws');

function EclairJS() {
  // SparkContext handles our Toree connection, so it returns an array of [ToreeKernelPromise, SparkContextClass]
  var result = require('./SparkContext.js')();

  var kernelP = result[0];

  return {
    SparkContext: result[1],

    SQLContext: require('./sql/SQLContext.js'),
    sql: {
      functions: require('./sql/functions.js')(kernelP),
    },

    storage: {
      StorageLevel: require('./storage/StorageLevel.js')(kernelP)
    },

    StreamingContext: require('./streaming/StreamingContext.js'),
    streaming: {
      KafkaUtils: require('./streaming/KafkaUtils.js'),
      Duration: require('./streaming/Duration.js')
    }
  }
}

module.exports = new EclairJS();

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

global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
global.WebSocket = require('ws');

function EclairJS() {
  // SparkContext handles our Toree connection, so it returns an array of [ToreeKernelPromise, SparkContextClass]
  var result = require('./SparkContext.js')();

  var kernelP = result[0];

  return {
    SparkContext: result[1],

    SQLContext: require('./sql/SQLContext.js'),
    sql: {
      functions: require('./sql/functions.js')(kernelP)
    },

    storage: {
      StorageLevel: require('./storage/StorageLevel.js')(kernelP)
    },

    StreamingContext: require('./streaming/StreamingContext.js'),
    streaming: {
      KafkaUtils: require('./streaming/KafkaUtils.js'),
      Duration: require('./streaming/Duration.js')
    }
  }
}

module.exports = new EclairJS();

Update to js-jupyter-services 0.5 for some reconnect fixes that are nice to have


(function(google) {
  google.load('search', '1', {
    language: 'en',
    nocss: true
  });

  google.setOnLoadCallback(function() {
    var cx = '000850824335660518947:3osiz3zllwq';
    var customSearchOptions = {};
    var customSearchControl =  new google.search.CustomSearchControl(cx, customSearchOptions);
    customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);

    var options = new google.search.DrawOptions();

    options.enableSearchResultsOnly();
    options.setAutoComplete(false);
    customSearchControl.draw('searchResult', options);

    function parseParamsFromUrl() {
      var params = {};
      var parts = window.location.search.substr(1).split('&');

      for (var i = 0; i < parts.length; i++) {
        var keyValuePair = parts[i].split('=');
        var key = decodeURIComponent(keyValuePair[0]);

        params[key] = keyValuePair[1] ?
          decodeURIComponent(keyValuePair[1].replace(/\+/g, ' ')) :
          keyValuePair[1];
      }

      return params;
    }

    var urlParams = parseParamsFromUrl();
    var queryParamName = 'q';

    if (urlParams[queryParamName]) {
      customSearchControl.execute(urlParams[queryParamName]);
    }
  }, true);
})(google);

(function(google) {
  'use strict';

  google.load('search', '1', {
    language: 'en',
    nocss: true
  });

  google.setOnLoadCallback(function() {
    var cx = '000850824335660518947:3osiz3zllwq';
    var customSearchOptions = {};
    var customSearchControl =  new google.search.CustomSearchControl(cx, customSearchOptions);
    customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);

    var options = new google.search.DrawOptions();

    options.enableSearchResultsOnly();
    options.setAutoComplete(false);
    customSearchControl.draw('searchResult', options);

    function parseParamsFromUrl() {
      var params = {};
      var parts = window.location.search.substr(1).split('&');

      for (var i = 0; i < parts.length; i++) {
        var keyValuePair = parts[i].split('=');
        var key = decodeURIComponent(keyValuePair[0]);

        params[key] = keyValuePair[1] ?
          decodeURIComponent(keyValuePair[1].replace(/\+/g, ' ')) :
          keyValuePair[1];
      }

      return params;
    }

    var urlParams = parseParamsFromUrl();
    var queryParamName = 'q';

    if (urlParams[queryParamName]) {
      customSearchControl.execute(urlParams[queryParamName]);
    }
  }, true);
})(google);

Set to "use strict" mode


var CommandUtil = require('../src/command_util').CommandUtil;
var l10n_file = __dirname + '/../l10n/commands/appraise.yml';
var l10n = new require('localize')(require('js-yaml').load(require('fs').readFileSync(l10n_file).toString('utf8')), undefined, 'zz');
exports.command = function (rooms, items, players, npcs, Commands)
{
	return function (args, player)
	{
		if (player.isInCombat()) {
			player.sayL10n(l10n, 'APPRAISE_COMBAT');
			return;
		}

		var room = rooms.getAt(player.getLocation());
		var target = CommandUtil.findNpcInRoom(npcs, args, room, player, true);

	}
};

var CommandUtil = require('../src/command_util').CommandUtil;
var l10n_file = __dirname + '/../l10n/commands/appraise.yml';
var l10n = new require('localize')(require('js-yaml').load(require('fs').readFileSync(l10n_file).toString('utf8')), undefined, 'zz');
exports.command = function (rooms, items, players, npcs, Commands)
{

	function getRelativeLevel(player, target){
		var difference = player - target;
		if (difference < -3){
			return 'TARGET_MUCH_STRONGER';
		} else if (difference < -1){
			return 'TARGET_STRONGER';
		} else if (difference > 3){
			return 'TARGET_MUCH_WEAKER';
		} else if (difference > 1){
			return 'TARGET_WEAKER';
		} else if (difference === 0){
			return 'TARGET_SAME_LEVEL'
		} else
			return 'MISSING_INFORMATION'
	}

	return function (args, player)
	{
		if (player.isInCombat()) {
			player.sayL10n(l10n, 'APPRAISE_COMBAT');
			return;
		}

		var room = rooms.getAt(player.getLocation());
		var target = CommandUtil.findNpcInRoom(npcs, args, room, player, true);

		console.log(target);
		if (!target){
			player.sayL10n(l10n, 'TARGET_NOT_FOUND');
			return;
		}

		var targetLevel = target.getAttribute(level) || null;
		var playerLevel = player.getAttribute(level) || null;
		var targetName = target.getShortDesc() || null;

		if (targetLevel && playerLevel && targetName) {
			player.sayL10n(l10n, getRelativeLevel(playerLevel, targetLevel), targetName);
			return;
		}

		player.sayL10n(l10n, 'MISSING_INFORMATION');
		return;

	}
};

Add function to return correct translation string, add logic to make function call if target is found and is an npc with a name and level.


var Timer = (function(Util) {
  'use strict';

  // Internal constants for the various timer states.
  var Waiting = 0, Inspecting = 1, Ready = 2, Running = 3, Stopped = 4;

  var state = Waiting;
  var startTime = undefined, endTime = undefined, solveTime = undefined;
  var intervalID = undefined;

  function isWaiting() { return state === Waiting; }
  function isReady() { return state === Ready; }
  function isRunning() { return state === Running; }

  function onRunning() {
    startTime = Util.getMilli();
    intervalID = Util.setInterval(runningEmitter, 100);
  }

  function onStopped() {
    endTime = Util.getMilli();
    Util.clearInterval(intervalID);
    setState(Waiting);
  }

  function setState(new_state) {
    state = new_state;
    switch(state) {
    case Running: onRunning(); break;
    case Stopped: onStopped(); break;
    }
  }

  function runningEmitter() {
    Event.emit("timer/running");
  }

  function triggerDown() {
    if (isWaiting()) {
      setState(Ready);
    } else if (isRunning()) {
      setState(Stopped);
    }
  }

  function triggerUp() {
    if (isReady()) {
      setState(Running);
    }
  }

  function getCurrent() {
    return Util.getMilli() - startTime;
  }

  return {
    triggerDown: triggerDown,
    triggerUp: triggerUp,
    getCurrent: getCurrent
  };
});

var Timer = (function(Util) {
  'use strict';

  // Internal constants for the various timer states.
  var Waiting = 0, Inspecting = 1, Ready = 2, Running = 3, Stopped = 4;

  var state = Waiting;
  var startTime = undefined, endTime = undefined, solveTime = undefined;
  var intervalID = undefined;

  function isWaiting() { return state === Waiting; }
  function isReady() { return state === Ready; }
  function isRunning() { return state === Running; }

  function onWaiting() {
    endTime = undefined;
  }

  function onRunning() {
    startTime = Util.getMilli();
    intervalID = Util.setInterval(runningEmitter, 100);
  }

  function onStopped() {
    endTime = Util.getMilli();
    Util.clearInterval(intervalID);
    setState(Waiting);
  }

  function setState(new_state) {
    state = new_state;
    switch(state) {
    case Waiting: onWaiting(); break;
    case Running: onRunning(); break;
    case Stopped: onStopped(); break;
    }
  }

  function runningEmitter() {
    Event.emit("timer/running");
  }

  function triggerDown() {
    if (isWaiting()) {
      setState(Ready);
    } else if (isRunning()) {
      setState(Stopped);
    }
  }

  function triggerUp() {
    if (isReady()) {
      setState(Running);
    }
  }

  function getCurrent() {
    return (endTime || Util.getMilli()) - startTime;
  }

  return {
    triggerDown: triggerDown,
    triggerUp: triggerUp,
    getCurrent: getCurrent
  };
});

Use the final endTime in getCurrent().


var write = require('./write'),
    geojson = require('./geojson'),
    prj = require('./prj'),
    JSZip = require('jszip');

module.exports = function(gj, options) {

    var zip = new JSZip(),
        layers = zip.folder(options && options.folder ? options.folder : 'layers');

    [geojson.point(gj), geojson.line(gj), geojson.polygon(gj)]
        .forEach(function(l) {
        if (l.geometries.length) {
            write(
                // field definitions
                l.properties,
                // geometry type
                l.type,
                // geometries
                l.geometries,
                function(err, files) {
                    var fileName = options && options.types[l.type.toLowerCase()] ? options.types[l.type.toLowerCase()] : l.type;
                    layers.file(fileName + '.shp', files.shp.buffer, { binary: true });
                    layers.file(fileName + '.shx', files.shx.buffer, { binary: true });
                    layers.file(fileName + '.dbf', files.dbf.buffer, { binary: true });
                    layers.file(fileName + '.prj', prj);
                });
        }
    });

    return zip.generate({compression:'STORE'});
};

var write = require('./write'),
    geojson = require('./geojson'),
    prj = require('./prj'),
    JSZip = require('jszip');

module.exports = function(gj, options) {

    var zip = new JSZip(),
        layers = zip.folder(options && options.folder ? options.folder : 'layers');

    [geojson.point(gj), geojson.line(gj), geojson.polygon(gj)]
        .forEach(function(l) {
        if (l.geometries.length) {
            write(
                // field definitions
                l.properties,
                // geometry type
                l.type,
                // geometries
                l.geometries,
                function(err, files) {
                    var fileName = options && options.types[l.type.toLowerCase()] ? options.types[l.type.toLowerCase()] : l.type;
                    layers.file(fileName + '.shp', files.shp.buffer, { binary: true });
                    layers.file(fileName + '.shx', files.shx.buffer, { binary: true });
                    layers.file(fileName + '.dbf', files.dbf.buffer, { binary: true });
                    layers.file(fileName + '.prj', prj);
                });
        }
    });

    var generateOptions = { compression:'STORE' };

    if (typeof window === 'undefined') {
      generateOptions.type = 'nodebuffer';
    }

    return zip.generate(generateOptions);
};

Set type option to nodebuffer if in node environment.


var keywords = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.whitespace,
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  // url points to a json file that contains an array of keywords
  prefetch: '/database/keywords.json'
});

// passing in `null` for the `options` arguments will result in the default
// options being used
$('#autocomplete .typeahead').typeahead(null, {
  name: 'keywords',
  source: keywords
});

function parse_concepts(response) {
  var result = [];
  for (index in response) {
    if (response[index].group == "keywords") {
      result.push(response[index].label);
    };
  };
  return result;
}

var keywords = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.whitespace,
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  // url points to a json file that contains an array of keywords
  prefetch: {
    url: 'http://chronosapi-chronoslod.rhcloud.com/concepts/c',
    transform: parse_concepts
  }
});

// passing in `null` for the `options` arguments will result in the default
// options being used
$('#autocomplete .typeahead').typeahead(null, {
  name: 'keywords',
  source: keywords
});

Use online keywords instead of pulling from app


module.exports = {
	package: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!includes/wpcom.php',
					'!templates/wpcom-*'
				],
				dest: '<%= paths.tmp %>',
				expand: true
			}
		]
	},
	wpcom: {
		files: [
			{
				cwd: 'src',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!audiotheme/**',
					'!includes/back-compat.php',
					'!includes/plugins/**',
					'!includes/vendor/class-audiotheme-themenotice.php',
					'!includes/wporg.php',
					'!languages/**'
				],
				dest: '<%= paths.wpcom %>',
				expand: true
			}
		]
	}
};

module.exports = {
	package: {
		files: [
			{
				cwd: '<%= paths.source %>',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!includes/wpcom.php',
					'!templates/wpcom-*'
				],
				dest: '<%= paths.tmp %>',
				expand: true
			}
		]
	},
	wpcom: {
		files: [
			{
				cwd: 'src',
				src: [
					'**',
					'!<%= paths.js %>/source/**',
					'!audiotheme/**',
					'!includes/back-compat.php',
					'!includes/plugins/**',
					'includes/plugins/jetpack.php',
					'!includes/vendor/class-audiotheme-themenotice.php',
					'!includes/wporg.php',
					'!languages/**'
				],
				dest: '<%= paths.wpcom %>',
				expand: true
			}
		]
	}
};

Include Jetpack plugin support in WPCOM builds.


module.exports = {
    // Packet constants
    PLAYER_START: "1",
    PLAYER_ADD: "2",
    PLAYER_ANGLE: "2",
    PLAYER_UPDATE: "3",
    PLAYER_ATTACK :"4",
    LEADERBOAD: "5",
    PLAYER_MOVE: "3",
    PLAYER_REMOVE: "4",
    LEADERS_UPDATE: "5",
    LOAD_GAME_OBJ: "6",
    PLAYER_UPGRADE: "6",
    GATHER_ANIM: "7",
    AUTO_ATK: "7",
    WIGGLE: "8",
    CLAN_CREATE: "8",
    PLAYER_LEAVE_CLAN: "9",
    STAT_UPDATE: "9",
    CLAN_REQ_JOIN: "10",
    UPDATE_HEALTH: "10",
    CLAN_ACC_JOIN: "11",
    CLAN_KICK: "12",
    ITEM_BUY: "13",
    UPDATE_AGE: "15",
    UPGRADES: "16",
    CHAT: "ch",
    CLAN_DEL: "ad",
    PLAYER_SET_CLAN: "st",
    SET_CLAN_PLAYERS: "sa",
    CLAN_ADD: "ac",
    CLAN_NOTIFY: "an",
    MINIMAP: "mm",
    UPDATE_STORE: "us",
    DISCONN: "d"
}

module.exports = {
    // Packet constants
    PLAYER_START: "1",
    PLAYER_ADD: "2",
    PLAYER_ANGLE: "2",
    PLAYER_UPDATE: "3",
    PLAYER_ATTACK :"4",
    LEADERBOAD: "5",
    PLAYER_MOVE: "3",
    PLAYER_REMOVE: "4",
    LEADERS_UPDATE: "5",
    SELECT_ITEM: "5",
    LOAD_GAME_OBJ: "6",
    PLAYER_UPGRADE: "6",
    GATHER_ANIM: "7",
    AUTO_ATK: "7",
    WIGGLE: "8",
    CLAN_CREATE: "8",
    PLAYER_LEAVE_CLAN: "9",
    STAT_UPDATE: "9",
    CLAN_REQ_JOIN: "10",
    UPDATE_HEALTH: "10",
    CLAN_ACC_JOIN: "11",
    CLAN_KICK: "12",
    ITEM_BUY: "13",
    UPDATE_AGE: "15",
    UPGRADES: "16",
    CHAT: "ch",
    CLAN_DEL: "ad",
    PLAYER_SET_CLAN: "st",
    SET_CLAN_PLAYERS: "sa",
    CLAN_ADD: "ac",
    CLAN_NOTIFY: "an",
    MINIMAP: "mm",
    UPDATE_STORE: "us",
    DISCONN: "d"
}

Add select item packet ID


//
// Like testTwo, but instead of simply replacing the nodes
// we first clone them. This leads to a single DOM op instead
// of `n+1` DOM ops.
//

(function() {
  var btn = document.querySelector('#test3');

  btn.addEventListener('click', function() {

    // Create our list, and append it to the test node
    var $list = $('

');
    var lis = new Array(10000);
    for(var i=0; i<lis.length; i++) {
      var $li = $('

').addClass('l'+i);
      lis[i] = $li;
      $list.append($li);
    }
    test.$el.append($list);

    // Rather than create a new tree from a template, I'm going ahead and cloning it
    var $listClone = $list.clone();

    // Kick off the test
    test.start('three');

    // Loop through the children in the cloned tree, replacing
    // them with cloned versions of the elements in the existing
    // tree. This causes 0 ops
    $listClone.children().each(function(index, li) {
      $(li).replaceWith(lis[index]);
    });

    // The only op is the replace
    test.$el.find('ul').append($listClone.contents());

    test.stop();
  });
})();

//
// Like testTwo, but instead of simply replacing the nodes
// we first clone them. This leads to a single DOM op instead
// of `n+1` DOM ops.
//

(function() {
  var btn = document.querySelector('#test3');

  btn.addEventListener('click', function() {

    // Create our list, and append it to the test node
    var $list = $('

');
    var lis = new Array(10000);
    for(var i=0; i<lis.length; i++) {
      var $li = $('

').addClass('l'+i);
      lis[i] = $li;
      $list.append($li);
    }
    test.$el.append($list);

    // Rather than create a new tree from a template, I'm going ahead and cloning it
    var $listClone = $list.clone();

    // Kick off the test
    test.start('three');

    // Loop through the children in the cloned tree, replacing
    // them with cloned versions of the elements in the existing
    // tree. This causes 0 ops
    $listClone.children().each(function(index, li) {
      $(li).replaceWith(lis[index].clone(true));
    });

    // The only op is the replace
    test.$el.find('ul').empty().append($listClone);

    test.stop();
  });
})();

Use cloning. Sorry, screwed that up.


var Showtimes = require('../')
var test = require('tap').test;
var s = new Showtimes(94118, {
  //date: 0
});

test('get theaters', function (t) {
  s.getTheaters(function (theaters) {
    t.equal(theaters.length, 18);
    t.end();
  });
});

var Showtimes = require('../');
var test = require('tap').test;
var s = null;

test('get theaters from zipcode', function (t) {
  s = new Showtimes(94118, {
    //date: 0
  });

  s.getTheaters(function (theaters) {
    t.equal(theaters.length, 18);
    t.end();
  });
});

test('get theaters from lat/long', function (t) {
  s = new Showtimes('37.7822890,-122.4637080', {
    //date: 0
  });

  s.getTheaters(function (theaters) {
    t.equal(theaters.length, 18);
    t.end();
  });
});

Test case for a lat/long-based location.


// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../../coverage/hslayers'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['ChromeHeadless'],
    singleRun: false,
    restartOnFileChange: true
  });
};

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../../coverage/hslayers'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['ChromeHeadless'],
    singleRun:
      typeof process.env.watch == 'undefined' ? true : !process.env.watch,
    restartOnFileChange: true
  });
};

Configure single run of tests


'use strict'

const expect = require('chai').expect

// Components
const nunjucks = require('nunjucks')
const components = require('../../lib/components')

// Styles
const paths = require('../../config/paths.json')
const sass = require('node-sass')

const expectComponentRenders = (name, input) => {
  let componentPath = components.templatePathFor(name)
  expect(() => {
    nunjucks.render(componentPath, input)
  }).to.not.throw()
}

describe('All components variants render without errors', () => {
  Object.keys(components.all).map(name => {
    describe(`${name}`, () => {
      let variants = components.getVariantsFor(name)
      for (let variant of variants) {
        it(`${variant.name}`, () => {
          console.log(variant.context)
          expectComponentRenders(name, variant.context)
        })
      }
    })
  })
})

describe('Styles', () => {
  it('should compile', done => {
    sass.render({
      file: paths.bundleScss + 'govuk-frontend.scss'
    }, error => {
      expect(error).to.equal(null)
      done()
    })
  })
})

'use strict'

const expect = require('chai').expect

// Components
const nunjucks = require('nunjucks')
const components = require('../../lib/components')

// Styles
const paths = require('../../config/paths.json')
const sass = require('node-sass')

const expectComponentRenders = (name, input) => {
  let componentPath = components.templatePathFor(name)
  expect(() => {
    nunjucks.render(componentPath, input)
  }).to.not.throw()
}

describe('All components variants render without errors', () => {
  Object.keys(components.all).map(name => {
    describe(`${name}`, () => {
      let variants = components.getVariantsFor(name)
      for (let variant of variants) {
        it(`${variant.name}`, () => {
          expectComponentRenders(name, variant.context)
        })
      }
    })
  })
})

describe('Styles', () => {
  it('should compile', done => {
    sass.render({
      file: paths.bundleScss + 'govuk-frontend.scss'
    }, error => {
      expect(error).to.equal(null)
      done()
    })
  })
})

Remove noisey console.log line inside smoke tests


import express from 'express';
import path from 'path';
import { createRouter } from './router';

export default function (apiKey, region, opts = {}) {
  const url = opts.url; // domain.tld/route
  // TODO: Ensure url is valid
  const route = ('/' + path.parse(url).base) || '/';
  // TODO: Check route

  const router = express.Router();
  router.use(route, createRouter(apiKey, region, route, opts));

  if (process.env.NODE_ENV !== 'production') {
    console.log(`Serving the tooltips on ${route}`);
  }

  return router;
};

import express from 'express';
import path from 'path';
import { createRouter } from './router';

export default function (apiKey, region, opts = {}) {
  const url = opts.url || ''; // domain.tld/route
  // TODO: Ensure url is valid
  const route = ('/' + path.parse(url).base) || '/';
  // TODO: Check route

  const router = express.Router();
  router.use(route, createRouter(apiKey, region, route, opts));

  if (process.env.NODE_ENV !== 'production') {
    console.log(`Serving the tooltips on ${route}`);
  }

  return router;
};

Fix undefined url not being set to the default value.


define([], function(){

  return function( butter ){
    var _button = document.createElement( "button" );

    _button.id = "add-popcorn";
    _button.innerHTML = "+Popcorn";
    _button.title = "Add a Popcorn Event to the timeline";

    _button.addEventListener( "click", function(){
      if( butter.ui.contentState === "timeline" ){
        butter.ui.setContentState( "add-popcorn" );
        butter.ui.contentStateLocked = true;
      }
      else{
        butter.ui.contentStateLocked = false;
        butter.ui.setContentState( "timeline" );
      }
    }, false );

    butter.ui.areas.tools.addComponent( _button, {
      states: [ "add-popcorn", "editor" ],
      transitionIn: function(){
        _button.setAttribute( "disabled", true );
        _button.innerHTML = "Done";
        _button.classList.add( "add-popcorn-done" );
      },
      transitionInComplete: function(){
        _button.removeAttribute( "disabled" );
      },
      transitionOut: function(){
        _button.setAttribute( "disabled", true );
        _button.innerHTML = "+Popcorn";
        _button.classList.remove( "add-popcorn-done" );
      },
      transitionOutComplete: function(){
        _button.removeAttribute( "disabled" );
      }
    });
  };
});

define([], function(){

  return function( butter ){
    var _button = document.createElement( "button" );

    _button.id = "add-popcorn";
    _button.title = "Add Popcorn Events to the timeline";
    _button.innerHTML = "+Popcorn";

    _button.addEventListener( "click", function(){
      if( butter.ui.contentState === "timeline" ){
        butter.ui.setContentState( "add-popcorn" );
        butter.ui.contentStateLocked = true;
      }
      else{
        butter.ui.contentStateLocked = false;
        butter.ui.setContentState( "timeline" );
      }
    }, false );

    butter.ui.areas.tools.addComponent( _button, {
      states: [ "add-popcorn", "editor" ],
      transitionIn: function(){
        _button.setAttribute( "disabled", true );
        _button.innerHTML = "Done";
        _button.title = "Finish adding Popcorn Events";
        _button.classList.add( "add-popcorn-done" );
      },
      transitionInComplete: function(){
        _button.removeAttribute( "disabled" );
      },
      transitionOut: function(){
        _button.setAttribute( "disabled", true );
        _button.innerHTML = "+Popcorn";
        _button.title = "Add Popcorn Events to the timeline";
        _button.classList.remove( "add-popcorn-done" );
      },
      transitionOutComplete: function(){
        _button.removeAttribute( "disabled" );
      }
    });
  };
});

Switch tooltip for +Popcorn and done button


import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';

import { Link } from 'react-router';

import { createPureComponent } from 'utils/createPureComponent';

export default createPureComponent({

  displayName: 'App',

  renderLink() {
    const isEditing = this.props.location.pathname === '/editor';
    const to = isEditing ? '/' : '/editor';
    const text = isEditing ? 'Play this level' : 'Edit this level';
    return (<Link to={to}>{text}</Link>);
  },

  render() {
    return (

        {this.props.children}

            {this.renderLink()}

    );
  }

});

import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';

import { Link } from 'react-router';

import { createPureComponent } from 'utils/createPureComponent';

export default createPureComponent({

  displayName: 'App',

  renderLink() {
    const isEditing = this.props.location.pathname === '/editor';
    const to = isEditing ? '/' : '/editor';
    const text = isEditing ? 'Play this level' : 'Edit this level';
    return (<Link to={to}>{text}</Link>);
  },

  render() {
    return (
      <div className="app">
        {this.props.children}
      </div>
    );
  }

});

Revert "Revert "Revert "Revert "Revert "Revert "kein editor""""""


'use strict'

const Record = require('../schemas/Record')
const aggregateTopFields = require('../aggregations/aggregateTopFields')
const aggregateRecentFields = require('../aggregations/aggregateRecentFields')
const aggregateNewFields = require('../aggregations/aggregateNewFields')
const sortings = require('../constants/sortings')
const createDate = require('../utils/createDate')

const get = async (ids, sorting, range, limit, dateDetails) => {

	const enhance = (entries) => {

		return entries.map((entry) => ({
			id: entry._id.siteReferrer,
			count: entry.count,
			date: createDate(dateDetails.userTimeZone, entry.created).userZonedDate
		}))

	}

	const aggregation = (() => {

		if (sorting === sortings.SORTINGS_TOP) return aggregateTopFields(ids, [ 'siteReferrer' ], range, limit, dateDetails)
		if (sorting === sortings.SORTINGS_NEW) return aggregateNewFields(ids, [ 'siteReferrer' ], limit)
		if (sorting === sortings.SORTINGS_RECENT) return aggregateRecentFields(ids, [ 'siteReferrer' ], limit)

	})()

	return enhance(
		await Record.aggregate(aggregation)
	)

}

module.exports = {
	get
}

'use strict'

const Record = require('../schemas/Record')
const aggregateTopFields = require('../aggregations/aggregateTopFields')
const aggregateRecentFields = require('../aggregations/aggregateRecentFields')
const aggregateNewFields = require('../aggregations/aggregateNewFields')
const sortings = require('../constants/sortings')
const createDate = require('../utils/createDate')

const get = async (ids, sorting, range, limit, dateDetails) => {

	const enhance = (entries) => {

		return entries.map((entry) => ({
			id: entry._id.siteReferrer,
			count: entry.count,
			created: createDate(dateDetails.userTimeZone, entry.created).userZonedDate
		}))

	}

	const aggregation = (() => {

		if (sorting === sortings.SORTINGS_TOP) return aggregateTopFields(ids, [ 'siteReferrer' ], range, limit, dateDetails)
		if (sorting === sortings.SORTINGS_NEW) return aggregateNewFields(ids, [ 'siteReferrer' ], limit)
		if (sorting === sortings.SORTINGS_RECENT) return aggregateRecentFields(ids, [ 'siteReferrer' ], limit)

	})()

	return enhance(
		await Record.aggregate(aggregation)
	)

}

module.exports = {
	get
}

Fix returned name for date


var EventEmitter = require("events").EventEmitter;
var sinon = require("sinon");
var util = require("util");
var _ = require("lodash");

var stub = {
  write         : sinon.stub().returns(stub),
  moveCursor    : sinon.stub().returns(stub),
  setPrompt     : sinon.stub().returns(stub),
  close         : sinon.stub().returns(stub),
  pause         : sinon.stub().returns(stub),
  resume        : sinon.stub().returns(stub),
  _getCursorPos : sinon.stub().returns(stub),
  output        : {
    end    : sinon.stub(),
    mute   : sinon.stub(),
    unmute : sinon.stub(),
    __raw__: '',
    write  : function (str) {
      this.__raw__ += str;
    }
  }
};

var ReadlineStub = function () {
  EventEmitter.apply(this, arguments);
};

util.inherits(ReadlineStub, EventEmitter);
_.assign(ReadlineStub.prototype, stub);

module.exports = ReadlineStub;

var EventEmitter = require("events").EventEmitter;
var sinon = require("sinon");
var util = require("util");
var _ = require("lodash");

var stub = {
  write         : sinon.stub().returns(stub),
  moveCursor    : sinon.stub().returns(stub),
  setPrompt     : sinon.stub().returns(stub),
  close         : sinon.stub().returns(stub),
  pause         : sinon.stub().returns(stub),
  resume        : sinon.stub().returns(stub),
  _getCursorPos : sinon.stub().returns({cols: 0, rows: 0}),
  output        : {
    end    : sinon.stub(),
    mute   : sinon.stub(),
    unmute : sinon.stub(),
    __raw__: '',
    write  : function (str) {
      this.__raw__ += str;
    }
  }
};

var ReadlineStub = function () {
  EventEmitter.apply(this, arguments);
};

util.inherits(ReadlineStub, EventEmitter);
_.assign(ReadlineStub.prototype, stub);

module.exports = ReadlineStub;

Fix rl mock for tests


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

const madge = require('../lib/api');
require('should');

describe('Flow', () => {
	const dir = __dirname + '/flow';

	it('extracts ES module ependencies', (done) => {
		madge(dir + '/es/calc.js').then((res) => {
			res.obj().should.eql({
				'math.js': [],
				'calc.js': ['math.js']
			});
			done();
		}).catch(done);
	});

	it('extracts CommonsJS module dependencies', (done) => {
		madge(dir + '/cjs/calc.js').then((res) => {
			res.obj().should.eql({
				'geometry.js': [],
				'math.js': [],
				'calc.js': ['geometry.js', 'math.js']
			});
			done();
		}).catch(done);
	});
});

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

const madge = require('../lib/api');
require('should');

describe('Flow', () => {
	const dir = __dirname + '/flow';

	it('extracts ES module ependencies', (done) => {
		madge(dir + '/es/calc.js').then((res) => {
			res.obj().should.eql({
				'math.js': [],
				'calc.js': ['math.js']
			});
			done();
		}).catch(done);
	});

	it('extracts CommonsJS module dependencies', (done) => {
		madge(dir + '/cjs/calc.js').then((res) => {
			res.obj().should.eql({
				'geometry.js': [],
				'calc.js': ['geometry.js']
			});
			done();
		}).catch(done);
	});

	it('extracts CommonsJS module dependencies with mixed import syntax', (done) => {
		madge(dir + '/cjs/calc.js', {detectiveOptions: {es6: {mixedImports: true}}}).then((res) => {
			res.obj().should.eql({
				'geometry.js': [],
				'math.js': [],
				'calc.js': ['geometry.js', 'math.js']
			});
			done();
		}).catch(done);
	});
});

Update test for mixed imports


import React, { Component, PropTypes } from "react"
import Helmet from "react-helmet"

import Header from "../components/Header"
import Footer from "../components/Footer"
import GATracker from "../components/GATracker"

export class LayoutContainer extends Component {
  static propTypes = {
    children: PropTypes.oneOfType([ PropTypes.array, PropTypes.object ]),
    location: PropTypes.object.isRequired,
    params: PropTypes.object,
  };

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
  };

  render() {
    const {
      pkg,
    } = this.context.metadata

    return (
      <GATracker params={ this.props.params }>
        <Helmet
          meta={ [
            { property: "og:site_name", content: pkg.config.siteName },
            { name: "twitter:site", content: `@${pkg.config.twitter}` },
          ] }
        />
        <Header />
        { this.props.children }
        <Footer />
      </GATracker>
    )
  }
}

export default LayoutContainer

import React, { Component, PropTypes } from "react"
import Helmet from "react-helmet"

import Header from "../components/Header"
import Footer from "../components/Footer"
import GATracker from "../components/GATracker"

export class LayoutContainer extends Component {
  static propTypes = {
    children: PropTypes.oneOfType([ PropTypes.array, PropTypes.object ]),
    location: PropTypes.object.isRequired,
    params: PropTypes.object,
  };

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
  };

  render() {
    const {
      pkg,
    } = this.context.metadata

    return (
      <GATracker params={ this.props.params }>
        <Helmet
          meta={ [
            { property: "og:site_name", content: pkg.config.siteName },
            { name: "twitter:site", content: `@${pkg.config.twitter}` },
            { name: "theme-color", content: "#0275d8" },
            { name: "msapplication-TileColor", content: "#0275d8" },
          ] }
        />
        <Header />
        { this.props.children }
        <Footer />
      </GATracker>
    )
  }
}

export default LayoutContainer

Add title color for Chrome mobile and Edge Mobile