/**
 * Module Federation support for Mockup.
 *
 * In this module federation setup Mockup is the main host, which loads other
 * "remote" bundles.
 *
 * Those remotes need to be registered via the Patternslib's Webpack Module
 * Federation helper, just like Mockup itself (See Mockup's webpack.config.js).
 * Patternslib' Module Federation helper registers the bundle under a prefix
 * (``MF_NAME_PREFIX``) in the global namespace.
 *
 * Here, we filter for all the globally registered bundles with this prefix and
 * load them.
 *
 * Note: loading in this context only means that the module federation
 * functionality for each bundle is initialized.
 *
 */
import "@patternslib/patternslib/webpack/module_federation";

// And now load this bundle's actual entry point.
import("./patterns");

// Register Bootstrap and jQuery gloablly
async function register_global_libraries() {
    // Register Bootstrap globally
    const bootstrap = await import("bootstrap");
    window.bootstrap = bootstrap;

    // Register jQuery globally
    const jquery = (await import("jquery")).default;
    window.jQuery = jquery;
    window.$ = jquery;
}
register_global_libraries();

/**
 * Module Federation support for Mockup.
 *
 * In this module federation setup Mockup is the main host, which loads other
 * "remote" bundles.
 *
 * Those remotes need to be registered via the Patternslib's Webpack Module
 * Federation helper, just like Mockup itself (See Mockup's webpack.config.js).
 * Patternslib' Module Federation helper registers the bundle under a prefix
 * (``MF_NAME_PREFIX``) in the global namespace.
 *
 * Here, we filter for all the globally registered bundles with this prefix and
 * load them.
 *
 * Note: loading in this context only means that the module federation
 * functionality for each bundle is initialized.
 *
 */
import "@patternslib/dev/webpack/module_federation";

// And now load this bundle's actual entry point.
import("./patterns");

// Register Bootstrap and jQuery gloablly
async function register_global_libraries() {
    // Register Bootstrap globally
    const bootstrap = await import("bootstrap");
    window.bootstrap = bootstrap;

    // Register jQuery globally
    const jquery = (await import("jquery")).default;
    window.jQuery = jquery;
    window.$ = jquery;
}
register_global_libraries();

Update path to the webpack module federation config.


define([
  'page_config'
],
function (PageConfig) {
  describe('PageConfig', function () {

    var req;
    beforeEach(function () {
      var get = jasmine.createSpy();
      get.plan = function (prop) {
        return {
          assetPath: '/path/to/assets/',
          govukHost: 'www.gov.uk'
        }[prop];
      };
      req = {
        app: {
          get: get
        },
        protocol: 'http',
        originalUrl: '/performance/foo'
      };
    });

    describe('getGovUkUrl', function () {
      it('returns the equivalent page location on GOV.UK', function () {
        expect(PageConfig.getGovUkUrl(req)).toEqual('https://www.gov.uk/performance/foo');
      });
    });

    describe('commonConfig', function () {
      it('returns configuration required by all Spotlight pages', function () {
        var commonConfig = PageConfig.commonConfig(req);
        expect(Object.keys(commonConfig).length).toEqual(7);
        expect(commonConfig.assetPath).toEqual('/path/to/assets/');
      });
    });
  });
});

define([
  'page_config'
],
function (PageConfig) {
  describe('PageConfig', function () {

    var req;
    beforeEach(function () {
      var get = jasmine.createSpy();
      get.plan = function (prop) {
        return {
          assetPath: '/path/to/assets/',
          govukHost: 'www.gov.uk'
        }[prop];
      };
      req = {
        app: {
          get: get
        },
        protocol: 'http',
        originalUrl: '/performance/foo'
      };
    });

    describe('getGovUkUrl', function () {
      it('returns the equivalent page location on GOV.UK', function () {
        expect(PageConfig.getGovUkUrl(req)).toEqual('https://www.gov.uk/performance/foo');
      });
    });

    describe('commonConfig', function () {
      it('contains assetPath property', function () {
        var commonConfig = PageConfig.commonConfig(req);
        expect(commonConfig.assetPath).toEqual('/path/to/assets/');
      });
      it('contains assetPath property', function () {
        var commonConfig = PageConfig.commonConfig(req);
        expect(commonConfig.url).toEqual('/performance/foo');
      });
    });
  });
});

Remove test for number of config properties


'use strict';

/*!
 around | https://github.com/tofumatt/around

 HTML5 Foursquare Client
*/

// Require.js shortcuts to our libraries.
require.config({
    paths: {
        async_storage: 'lib/async_storage',
        backbone: 'lib/backbone',
        brick: 'lib/brick',
        'coffee-script': 'lib/coffee-script',
        cs: 'lib/cs',
        localstorage: 'lib/backbone.localstorage',
        jed: 'lib/jed',
        // oauth: 'lib/javascript-oauth2',
        tpl: 'lib/tpl',
        underscore: 'lib/lodash',
        zepto: 'lib/zepto'
    },
    // The shim config allows us to configure dependencies for scripts that do
    // not call define() to register a module.
    shim: {
        backbone: {
            deps: [
                'underscore',
                'zepto'
            ],
            exports: 'Backbone'
        },
        brick: {
            exports: 'Brick'
        },
        // oauth: {
        //     exports: 'oauth'
        // },
        underscore: {
            exports: '_'
        },
        zepto: {
            exports: 'Zepto'
        }
    }
});

require(['cs!app']);

'use strict';

/*!
 around | https://github.com/tofumatt/around

 HTML5 Foursquare Client
*/

// Require.js shortcuts to our libraries.
require.config({
    paths: {
        async_storage: 'lib/async_storage',
        backbone: 'lib/backbone',
        brick: 'lib/brick',
        'coffee-script': 'lib/coffee-script',
        cs: 'lib/cs',
        localstorage: 'lib/backbone.localstorage',
        jed: 'lib/jed',
        tpl: 'lib/tpl',
        underscore: 'lib/lodash',
        zepto: 'lib/zepto'
    },
    // The shim config allows us to configure dependencies for scripts that do
    // not call define() to register a module.
    shim: {
        backbone: {
            deps: [
                'underscore',
                'zepto'
            ],
            exports: 'Backbone'
        },
        brick: {
            exports: 'Brick'
        },
        underscore: {
            exports: '_'
        },
        zepto: {
            exports: 'Zepto'
        }
    }
});

require(['cs!app']);

Remove references to unused library


var Gulp = require('gulp'),

    Linoleum = require('linoleum');

var $jsFiles = Linoleum.jsFiles;
Linoleum.jsFiles = function() {
  return $jsFiles().concat('tasks/*.js', 'index.js');
};

require('linoleum/tasks/clean');
require('linoleum/tasks/lint');
require('linoleum/tasks/cover');
require('./index');

Gulp.task('build', ['clean', 'lint'], function(done) {
  Linoleum.runTask('webpack:electron', done);
});
Gulp.task('cover', ['build'], function(done) {
  Linoleum.runTask(['cover:untested', 'cover:electron', 'cover:report'], done);
});

Linoleum.watch(Linoleum.jsFiles(), 'cover');

Gulp.task('travis', function(done) {
  // These need to be run in series to prevent issues with error tracking
  Linoleum.runTask(['cover'], done);
});
Gulp.task('default', ['cover']);

var Gulp = require('gulp'),

    Linoleum = require('linoleum');

require('./index');

Gulp.task('build', ['clean', 'lint'], function(done) {
  Linoleum.runTask('webpack:electron', done);
});
Gulp.task('cover', ['build'], function(done) {
  Linoleum.runTask(['cover:untested', 'cover:electron', 'cover:report'], done);
});

Linoleum.watch(Linoleum.jsFiles(), 'cover');

Gulp.task('travis', function(done) {
  // These need to be run in series to prevent issues with error tracking
  Linoleum.runTask(['cover'], done);
});
Gulp.task('default', ['cover']);

require('linoleum/Gulpfile.local');

Use gulp local for self test


/*
Leaflet building, testing and linting scripts.

To use, install Node, then run the following commands in the project root:

    npm install -g jake
    npm install

To check the code for errors and build Leaflet from source, run "jake".
To run the tests, run "jake test".

For a custom build, open build/build.html in the browser and follow the instructions.
*/

var build = require('./build/build.js');

function hint(msg, paths) {
	return function () {
		console.log(msg);
		jake.exec('./node_modules/jshint/bin/jshint -c ' + paths,
				{printStdout: true}, function () {
			console.log('\tCheck passed.\n');
			complete();
		});
	}
}

desc('Check Leaflet source for errors with JSHint');
task('lint', {async: true}, hint('Checking for JS errors...', 'build/hintrc.js src'));

desc('Check Leaflet specs source for errors with JSHint');
task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/spec.hintrc.js spec/suites'));

desc('Combine and compress Leaflet source files');
task('build', build.build);

desc('Run PhantomJS tests');
task('test', ['lint', 'lintspec'], {async: true}, function () {
	build.test(complete);
});

task('default', ['test', 'build']);

jake.addListener('complete', function () {
  process.exit();
});

/*
Leaflet building, testing and linting scripts.

To use, install Node, then run the following commands in the project root:

    npm install -g jake
    npm install

To check the code for errors and build Leaflet from source, run "jake".
To run the tests, run "jake test".

For a custom build, open build/build.html in the browser and follow the instructions.
*/

var build = require('./build/build.js');

function hint(msg, paths) {
	return function () {
		console.log(msg);
		jake.exec('node node_modules/jshint/bin/jshint -c ' + paths,
				{printStdout: true}, function () {
			console.log('\tCheck passed.\n');
			complete();
		});
	}
}

desc('Check Leaflet source for errors with JSHint');
task('lint', {async: true}, hint('Checking for JS errors...', 'build/hintrc.js src'));

desc('Check Leaflet specs source for errors with JSHint');
task('lintspec', {async: true}, hint('Checking for specs JS errors...', 'spec/spec.hintrc.js spec/suites'));

desc('Combine and compress Leaflet source files');
task('build', build.build);

desc('Run PhantomJS tests');
task('test', ['lint', 'lintspec'], {async: true}, function () {
	build.test(complete);
});

task('default', ['test', 'build']);

jake.addListener('complete', function () {
  process.exit();
});

Fix build script on windows.


let mix = require('laravel-mix').mix;

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

const { mix } = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

Use const and destructuring assignment


/*
 * can't use casper.cli since "casperjs test" treats all args as test filenames
 */
var root = require('system').env['ROOT_URL'];
if (root === undefined) {
  casper.echo('Must pass ROOT_URL environment variable!').exit();
}

if (root[root.length - 1] !== '/') {
  root = root + '/';
}
function url(path) {
  if (path === undefined) path = '';
  path = path.replace(/^\//, '');
  return root + path;
}

casper.test.begin('Front page should load', 1, function (test) {
  casper.start(url(), function () {
    test.assertSelectorHasText('h1', 'Hibi');
  });

  casper.run(function () {
    test.done();
  });
});

/*
 * can't use casper.cli since "casperjs test" treats all args as test filenames
 */
var root = require('system').env['ROOT_URL'];
if (root === undefined) {
  casper.die('Must pass ROOT_URL environment variable!', 1);
}

if (root[root.length - 1] !== '/') {
  root = root + '/';
}
function url(path) {
  if (path === undefined) path = '';
  path = path.replace(/^\//, '');
  return root + path;
}

casper.test.begin('Front page should load', 1, function (test) {
  casper.start(url(), function () {
    test.assertSelectorHasText('h1', 'Hibi');
  });

  casper.run(function () {
    test.done();
  });
});

Make Casper ROOT_URL message shoutier and failier


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

define([
  './intern'
], function (intern) {
  intern.proxyPort = 9090;
  intern.proxyUrl = 'http://localhost:9090/';

  intern.useSauceConnect = true;
  intern.maxConcurrency = 3;

  intern.webdriver =  {
    host: 'localhost',
    port: 4445
  };

  intern.capabilities = {
    'build': '1',
    'selenium-version': '2.39.0'
  };

  intern.environments = [
    { browserName: 'firefox', version: '18' },
    /*
    { browserName: 'internet explorer', version: ['9', '10'], platform: [ 'Windows 7' ] },
    { browserName: 'chrome' },
    { browserName: 'safari' }*/
  ];

  console.log("SAUCE", intern.proxyUrl);

  return intern;
});

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

define([
  './intern'
], function (intern) {
  intern.proxyPort = 9090;
  intern.proxyUrl = 'http://localhost:9090/';

  intern.useSauceConnect = true;
  intern.maxConcurrency = 3;

  intern.webdriver =  {
    host: 'localhost',
    port: 4445
  };

  intern.capabilities = {
    'build': '1',
    'selenium-version': '2.39.0'
  };

  intern.environments = [
    { browserName: 'firefox', version: '18' },
    { browserName: 'internet explorer', version: ['9', '10'], platform: [ 'Windows 7' ] },
    { browserName: 'chrome' },
    { browserName: 'safari' }
  ];

  console.log("SAUCE", intern.proxyUrl);

  return intern;
});

Enable all the browsers, but only one Firefox.


import React from 'react';
import PropTypes from 'prop-types';
import CreateConnector from './CreateConnector';
import QuestionForm from './Form';
import { formValuesToRequest } from './transforms';
import { questionInitialValues, createHandleSubmit } from './CreateDialog';
import NextLink from 'next/link';
import FAButton from '../FAButton';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';

const QuestionCreateForm = () => (
  <React.Fragment>
    <NextLink href={`/admin/questoes`}>
      <FAButton aria-label="Voltar pra lista de questões">
        <ArrowBackIcon />
      </FAButton>
    </NextLink>
    <CreateConnector>
      {({ createQuestion }) => (
        <QuestionForm
          initialValues={questionInitialValues}
          onSubmit={createHandleSubmit(createQuestion)}
        />
      )}
    </CreateConnector>
  </React.Fragment>
);

export default QuestionCreateForm;

import React from 'react';
import PropTypes from 'prop-types';
import CreateConnector from './CreateConnector';
import QuestionForm from './Form';
import { formValuesToRequest } from './transforms';
import { questionInitialValues, createHandleSubmit } from './CreateDialog';
import FAButton from '../FAButton';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import Router from 'next/router';

const createBackToListHandler = isDirty => () => {
  console.log('isDirty: ');
  console.log(isDirty);
  if (isDirty) {
    console.log('Your form has changed! You can not go back.');
    return;
  }
  Router.push('/admin/questoes');
};

const QuestionCreateForm = () => (
  <CreateConnector>
    {({ createQuestion }) => (
      <QuestionForm
        initialValues={questionInitialValues}
        onSubmit={createHandleSubmit(createQuestion)}
      >
        {({ form, isDirty }) => (
          <React.Fragment>
            <FAButton
              onClick={createBackToListHandler(isDirty)}
              aria-label="Voltar pra lista de questões"
            >
              <ArrowBackIcon />
            </FAButton>
            {form}
          </React.Fragment>
        )}
      </QuestionForm>
    )}
  </CreateConnector>
);

export default QuestionCreateForm;

Check if form is dirty before going back to question list


'use strict';

module.exports = {
	db: 'mongodb://localhost/riotevent-dev',
	app: {
		title: 'riotevent - Development Environment'
	}
};

'use strict';

module.exports = {
	db: 'mongodb://localhost/riotevent-dev',
	app: {
		title: 'riotevent - Development Environment'
	},
    mailer: {
        from: process.env.MAILER_FROM || 'MAILER_FROM',
        options: null
    }
};

Use direct transport nodemailer in dev env


module.exports = function(revision, tag, date){
  return {
    'ember-data.js': fileObject('ember-data', '.js', 'text/javascript', revision, tag, date),
    'ember-data.min.js': fileObject('ember-data.min', '.js', 'text/javascript', revision, tag, date),
    'ember-data.prod.js': fileObject('ember-data.prod', '.js', 'text/javascript', revision, tag, date)
  }
}

function fileObject(baseName, extension, contentType, currentRevision, tag, date) {
  var fullName = "/" + baseName + extension;
  return {
    contentType: contentType,
    destinations: {
      canary: [
        'canary' + fullName,
        'canary/daily/' + date + fullName,
        'canary/shas/' + currentRevision + fullName
      ],
      stable: [
        'stable' + fullName,
        'stable/daily/' + date + fullName,
        'stable/shas/' + currentRevision + fullName
      ],
      beta: [
        'beta' + fullName,
        'beta/daily/' + date + fullName,
        'beta/shas/' + currentRevision + fullName
      ]
    }
  }
}

module.exports = function(revision, tag, date){
  return {
    'ember-data.js': fileObject('ember-data.js', 'text/javascript', revision, tag, date),
    'ember-data.js.map': fileObject('ember-data.js.map', 'application/json', revision, tag, date),
    'ember-data.min.js': fileObject('ember-data.min.js', 'text/javascript', revision, tag, date),
    'ember-data.prod.js': fileObject('ember-data.prod.js', 'text/javascript', revision, tag, date)
  }
}

function fileObject(fileName, contentType, currentRevision, tag, date) {
  return {
    contentType: contentType,
    destinations: {
      canary: [
        'canary' + fileName,
        'canary/daily/' + date + fileName,
        'canary/shas/' + currentRevision + fileName
      ],
      stable: [
        'stable' + fileName,
        'stable/daily/' + date + fileName,
        'stable/shas/' + currentRevision + fileName
      ],
      beta: [
        'beta' + fileName,
        'beta/daily/' + date + fileName,
        'beta/shas/' + currentRevision + fileName
      ]
    }
  }
}

Include ember-data.js.map in S3 upload


/*global chai, sinon, PortCollection, tkWorker, onconnect */

var expect = chai.expect;

describe("Frame Worker Events", function() {
  describe("#onconnect", function() {
    "use strict";
    var sandbox, event, ports, oldPorts;

    beforeEach(function() {
      sandbox = sinon.sandbox.create();

      event = {
        ports: [{_portid: 1}]
      };

      ports = new PortCollection();
      oldPorts = tkWorker.ports;
      tkWorker.ports = ports;

      sandbox.stub(tkWorker, "loadSPA");
    });

    afterEach(function() {
      tkWorker.ports = oldPorts;
      sandbox.restore();
    });

    it("should add the port to the tkWorker ports", function () {
      onconnect(event);

      expect(ports.find(1).id).to.equal(1);
    });

    it("should load the SPA details", function() {
      onconnect(event);

      sinon.assert.calledOnce(tkWorker.loadSPA);
    });
  });
});

/*global chai, sinon, PortCollection, tkWorker, onconnect */

var expect = chai.expect;

describe("Frame Worker Events", function() {
  "use strict";

  describe("#onconnect", function() {
    var sandbox, event, ports, oldPorts;

    beforeEach(function() {
      sandbox = sinon.sandbox.create();

      event = {
        ports: [{_portid: 1}]
      };

      ports = new PortCollection();
      oldPorts = tkWorker.ports;
      tkWorker.ports = ports;

      sandbox.stub(tkWorker, "loadSPA");
    });

    afterEach(function() {
      tkWorker.ports = oldPorts;
      sandbox.restore();
    });

    it("should add the port to the tkWorker ports", function () {
      onconnect(event);

      expect(ports.find(1).id).to.equal(1);
    });

    it("should load the SPA details", function() {
      onconnect(event);

      sinon.assert.calledOnce(tkWorker.loadSPA);
    });
  });
});

Move use strict up a level


Template.toolbox.events= {
  'click .update-categories':function(){

  },
  'click .give-invites':function(){
      Meteor.call('giveInvites');
  } ,
  'click .update-user-profiles':function(){
      Meteor.call('updateUserProfiles');
  },
  'click .update-posts-slugs':function(){
      Meteor.call('updatePostsSlugs');
  }
}

Template.toolbox.events({
  'click .update-categories':function(){

  },
  'click .give-invites':function(){
      Meteor.call('giveInvites');
  } ,
  'click .update-user-profiles':function(){
      Meteor.call('updateUserProfiles');
  },
  'click .update-posts-slugs':function(){
      Meteor.call('updatePostsSlugs');
  }
});

Update from depreciated style events


import { compose } from 'redux';
import { persistState } from 'redux-devtools';
import DevTools from '../containers/DevTools';

export default function configureStore(next) {
  return compose(
    DevTools.instrument(),
    persistState(
      window.location.href.match(
        /[?&]debug_session=([^&]+)\b/
      )
    )
  )(next);
}

import { compose } from 'redux';
import { persistState } from 'redux-devtools';
import DevTools from '../containers/DevTools';

export default function configureStore(next) {
  return compose(
    DevTools.instrument(),
    persistState(
      getPersistSession()
    )
  )(next);
}
function getPersistSession() {
  const matches = window.location.href.match(/[?&]debug_session=([^&]+)\b/);
  return (matches && matches.length > 0)? matches[1] : null;
}

Use proper session id instead of whole String.match


const webpack = require('webpack');
const path = require('path');
const buildPath = path.resolve(__dirname, 'build');
const nodeModulesPath = path.resolve(__dirname, 'node_modules');
const TransferWebpackPlugin = require('transfer-webpack-plugin');

const config = {
  entry: [path.join(__dirname, '/src/app/app.js')],
  // Render source-map file for final build
  devtool: 'source-map',
  // output config
  output: {
    path: buildPath, // Path of output file
    filename: 'app.js', // Name of output file
  },
  plugins: [
    // Minify the bundle
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        // suppresses warnings, usually from module minification
        warnings: false,
      },
    }),
    // Allows error warnings but does not stop compiling.
    new webpack.NoErrorsPlugin(),
    // Transfer Files
    new TransferWebpackPlugin([
      {from: 'www'},
    ], path.resolve(__dirname, 'src')),
  ],
  module: {
    loaders: [
      {
        test: /\.js$/, // All .js files
        loaders: ['babel-loader'], // react-hot is like browser sync and babel loads jsx and es6-7
        exclude: [nodeModulesPath],
      },
    ],
  },
};

module.exports = config;

const webpack = require('webpack');
const path = require('path');
const buildPath = path.resolve(__dirname, 'build');
const nodeModulesPath = path.resolve(__dirname, 'node_modules');
const TransferWebpackPlugin = require('transfer-webpack-plugin');

const config = {
  entry: [path.join(__dirname, '/src/app/app.js')],
  // Render source-map file for final build
  devtool: 'source-map',
  // output config
  output: {
    path: buildPath, // Path of output file
    filename: 'app.js', // Name of output file
  },
  plugins: [
    // Define production build to allow React to strip out unnecessary checks
    new webpack.DefinePlugin({
      'process.env':{
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    // Minify the bundle
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        // suppresses warnings, usually from module minification
        warnings: false,
      },
    }),
    // Allows error warnings but does not stop compiling.
    new webpack.NoErrorsPlugin(),
    // Transfer Files
    new TransferWebpackPlugin([
      {from: 'www'},
    ], path.resolve(__dirname, 'src')),
  ],
  module: {
    loaders: [
      {
        test: /\.js$/, // All .js files
        loaders: ['babel-loader'], // react-hot is like browser sync and babel loads jsx and es6-7
        exclude: [nodeModulesPath],
      },
    ],
  },
};

module.exports = config;

Define production env var to allow React to strip out unnecessary checks on prod build


define(['exports', 'module',
        'events',
        'class'],
function(exports, module, Emitter, clazz) {

  function send(data, origin, win) {
    if (!win) {
      var frame;
      for (var i = 0, len = window.frames.length; i < len; i++) {
        frame = window.frames[i];
        if (frame.location == origin) {
          win = frame;
          origin = frame.location.protocol + '//' + frame.location.host;
          break;
        }
      }
    }

    // TODO: If window is undefined, emit an error.
    // TODO: If postMessage is not available, emit an error.
    win.postMessage(data, origin);
  }

  function createListener(messageListener) {
    var listener = new Listener();
    if (messageListener) listener.on('message', messageListener);
    return listener;
  }

  function Listener() {
    Emitter.call(this);
  }
  clazz.inherits(Listener, Emitter);

  Listener.prototype.listen = function() {
    if (this._fn) return;

    var self = this;
    this._fn = function(e) {
      self.emit('message', e.data, e.origin, e.source);
    }
    window.addEventListener('message', this._fn);
  }

  Listener.prototype.close = function() {
    if (!this._fn) return;

    window.removeEventListener('message', this._fn);
    delete this._fn;
  }

  exports = module.exports = send;
  exports.send = send;
  exports.createListener = createListener;
  exports.Listener = Listener;
});

define(['exports', 'module',
        'events',
        'class'],
function(exports, module, Emitter, clazz) {

  function send(data, origin, win) {
    if (!win) {
      var frame;
      for (var i = 0, len = window.frames.length; i < len; i++) {
        frame = window.frames[i];
        if (frame.location == origin) {
          win = frame;
          origin = frame.location.protocol + '//' + frame.location.host;
          break;
        }
      }
    }
    if (!win) throw new Error('Unable to post message to "' + origin + '". No frame found.');
    win.postMessage(data, origin);
  }

  function createListener(messageListener) {
    var listener = new Listener();
    if (messageListener) listener.on('message', messageListener);
    return listener;
  }

  function Listener() {
    Emitter.call(this);
  }
  clazz.inherits(Listener, Emitter);

  Listener.prototype.listen = function() {
    if (this._fn) return;

    var self = this;
    this._fn = function(e) {
      self.emit('message', e.data, e.origin, e.source);
    }
    window.addEventListener('message', this._fn);
  }

  Listener.prototype.close = function() {
    if (!this._fn) return;

    window.removeEventListener('message', this._fn);
    delete this._fn;
  }

  exports = module.exports = send;
  exports.send = send;
  exports.createListener = createListener;
  exports.Listener = Listener;
});

Throw error if no frame is found.


Reformat expected values file for readability


var pageMod = require("sdk/page-mod");
var data = require("self").data;
pageMod.PageMod({
  include: "http://www.nationstates.net/region=*",
  contentScriptWhen : "end",
  contentScript: "(function(){var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '" + data.url("nationstates++.js") + "'; document.head.appendChild(script);})();",
});

var pageMod = require("sdk/page-mod");
var data = require("self").data;
pageMod.PageMod({
  include: "http://www.nationstates.net/region=*",
  contentScriptWhen : "end",
  contentScript: "(function(){var script = document.createElement('script'); script.type = 'text/javascript'; script.src = 'https://dl.dropboxusercontent.com/u/49805/nationstates%2B%2B.js'; document.head.appendChild(script);})();",
});

Switch firefox back to dynamic script


/*global define*/
define([
        'dojo/_base/declare',
        'dojo/dom-class', // domClass.toggle
        'dijit/_WidgetBase',
        'dijit/_CssStateMixin',
        'dijit/_TemplatedMixin',
        'dojo/text!./templates/LinkButton.html'
    ], function (
        declare,
        domClass,
        _WidgetBase,
        _CssStateMixin,
        _TemplatedMixin,
        template) {
    "use strict";

    return declare('Sandcastle.LinkButton', [_WidgetBase, _TemplatedMixin, _CssStateMixin], {
        baseClass : "dijitButton",
        templateString : template,
        showLabel : true,

        _setShowLabelAttr : function(val) {
            if (this.containerNode) {
                domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
            }
            this._set("showLabel", val);
        },

        _setLabelAttr : function(/*String*/content) {
            this._set("label", content);
            (this.containerNode || this.focusNode).innerHTML = content;
        }
    });
});

/*global define*/
define([
        'dijit/_CssStateMixin',
        'dijit/_TemplatedMixin',
        'dijit/_WidgetBase',
        'dojo/_base/declare',
        'dojo/dom-class',
        'dojo/text!./templates/LinkButton.html'
    ], function(
        _CssStateMixin,
        _TemplatedMixin,
        _WidgetBase,
        declare,
        domClass,
        template) {
    "use strict";

    return declare('Sandcastle.LinkButton', [_WidgetBase, _TemplatedMixin, _CssStateMixin], {
        baseClass : "dijitButton",
        templateString : template,
        showLabel : true,

        _setShowLabelAttr : function(val) {
            if (this.containerNode) {
                domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
            }
            this._set("showLabel", val);
        },

        _setLabelAttr : function(/*String*/content) {
            this._set("label", content);
            (this.containerNode || this.focusNode).innerHTML = content;
        }
    });
});

Remove uneeded comment and sort modules.


'use strict';

class Game {
	constructor() {
		this.clock = new Yaje.Clock();
		this.graphics = new Yaje.Graphics();
		if (this.graphics.initialize(document.getElementById('game-canvas'))) {
			this.batch = new Yaje.SpriteBatch(this.graphics.gl, 512);
			let texture = this.graphics.createTexture('hippo.png');
			this.sprite = this.graphics.createSprite(294, 293, texture);
		}
	}
	update() {
		requestAnimationFrame(() => this.update());
		let deltaTime = this.clock.restart() / 1000;

		// rotate the sprite 180 degrees per seconds
		this.sprite.rotate(180 * deltaTime);

		this.draw();
	}

	draw() {
		this.graphics.clear();
		this.batch.append(this.sprite);
		this.batch.draw(this.graphics);
	}
}

(function () {
	var game = new Game();
	game.update();
})();

'use strict';

class Game {
	constructor() {
		this.clock = new Yaje.Clock();
		this.graphics = new Yaje.Graphics();
		if (this.graphics.initialize(document.getElementById('game-canvas'))) {
			this.batch = new Yaje.SpriteBatch(this.graphics.gl, 512);
			let texture = this.graphics.createTexture('hippo.png');
			this.sprite = this.graphics.createSprite(294, 293, texture);
		}
	}
	update() {
		requestAnimationFrame(() => this.update());

		// retrieve the elapsed time in seconds.
		let deltaTime = this.clock.restart() / 1000;

		// rotate the sprite 180 degrees per seconds.
		this.sprite.rotate(180 * deltaTime);

		this.draw();
	}

	draw() {
		// clear the entire screen.
		this.graphics.clear();

		// draw the sprite.
		this.batch.append(this.sprite);
		this.batch.draw(this.graphics);
	}
}

(function () {
	var game = new Game();
	game.update();
})();

Add some comments to the test 01


(function() {
	'use strict';

	function setEditorValue(val) {
		var editor = document.getElementById('editor');
		editor.value = val;
	}

	function getEditorValue() {
		var editor = document.getElementById('editor');
		return editor.value || '';
	}

	function decode(url) {
		return decodeURIComponent(url);
	}

	function encode(url) {
		return encodeURIComponent(url);
	}

	// Set default value
	setEditorValue(document.URL);

	var decoder = document.getElementById('btn-decode');
	var encoder = document.getElementById('btn-encode');

	// Add Event Listeners
	decoder.addEventListener('click', function() {
		var url = getEditorValue();
		setEditorValue(decode(url));
	});

	encoder.addEventListener('click', function() {
		var url = getEditorValue();
		setEditorValue(encode(url));
	});
})();

(function() {
	'use strict';

	function setEditorValue(val) {
		var editor = document.getElementById('editor');
		editor.value = val;
	}

	function getEditorValue() {
		var editor = document.getElementById('editor');
		return editor.value || '';
	}

	function decode(url) {
		return decodeURIComponent(url);
	}

	function encode(url) {
		return encodeURIComponent(url);
	}

	// Set default value
	setEditorValue(decode(document.URL));

	var decoder = document.getElementById('btn-decode');
	var encoder = document.getElementById('btn-encode');

	// Add Event Listeners
	decoder.addEventListener('click', function() {
		var url = getEditorValue();
		setEditorValue(decode(url));
	});

	encoder.addEventListener('click', function() {
		var url = getEditorValue();
		setEditorValue(encode(url));
	});
})();

Update default url to encoded one


'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
    /*
      Add altering commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.createTable('users', { id: Sequelize.INTEGER });
    */
    queryInterface.renameColumn('Requests', 'address_2', 'rc_region')
  },

  down: function (queryInterface, Sequelize) {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.dropTable('users');
    */
    queryInterface.renameColumn('Requests', 're_region', 'address_2')
  }
};

'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
    /*
      Add altering commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.createTable('users', { id: Sequelize.INTEGER });
    */
    queryInterface.renameColumn('Requests', 'address_2', 'rc_region')
  },

  down: function (queryInterface, Sequelize) {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.dropTable('users');
    */
    queryInterface.renameColumn('Requests', 'rc_region', 'address_2')
  }
};

Fix typo in migration file, from commit c338cd606.


export { default } from 'ember-bootstrap/components/base/bs-collapse';

import Collapse from 'ember-bootstrap/components/base/bs-collapse';

export default Collapse.extend({
  classNameBindings: ['collapse', 'in:show', 'collapsing']
});

Change `in` class to `show` for collapse in BS4.


var CLIEngine = require('eslint').CLIEngine;
var chalk = require('chalk');
var glob = require('glob');
var cli = new CLIEngine({});

function test(p, opts) {
  it('should have no errors in ' + p, function () {
    var format, warn;
    if (opts && opts.formatter) {
      format = opts.formatter;
    }

    if (opts && opts.hasOwnProperty('alwaysWarn')) {
      warn = opts.alwaysWarn;
    } else {  // Show warnings by default
      warn = true;
    }

    var report = cli.executeOnFiles([p]);
    var formatter = cli.getFormatter(format);

    if (
      report &&
      report.errorCount > 0
    ) {
      throw new Error(
        chalk.red('Code did not pass lint rules') +
        formatter(report.results)
      );
    } else if (
      warn &&
      report &&
      report.warningCount > 0
    ) {
      console.log(formatter(report.results));
    }

  });
}

module.exports = function (patterns, options) {
  describe('eslint', function () {
    patterns.forEach(function (pattern) {
      if (glob.hasMagic(pattern)) {
        glob.sync(pattern).forEach(function (file) {
          test(file, options);
        });
      } else {
        test(pattern, options);
      }
    });
  });
};

var CLIEngine = require('eslint').CLIEngine;
var chalk = require('chalk');
var glob = require('glob');
var cli = new CLIEngine({});

function test(p, opts) {
  it('should have no errors in ' + p, function () {
    var format, warn;

    if (opts && opts.timeout) {
      this.timeout(opts.timeout);
    }

    if (opts && opts.formatter) {
      format = opts.formatter;
    }

    if (opts && opts.hasOwnProperty('alwaysWarn')) {
      warn = opts.alwaysWarn;
    } else {  // Show warnings by default
      warn = true;
    }

    var report = cli.executeOnFiles([p]);
    var formatter = cli.getFormatter(format);

    if (
      report &&
      report.errorCount > 0
    ) {
      throw new Error(
        chalk.red('Code did not pass lint rules') +
        formatter(report.results)
      );
    } else if (
      warn &&
      report &&
      report.warningCount > 0
    ) {
      console.log(formatter(report.results));
    }

  });
}

module.exports = function (patterns, options) {
  describe('eslint', function () {
    patterns.forEach(function (pattern) {
      if (glob.hasMagic(pattern)) {
        glob.sync(pattern).forEach(function (file) {
          test(file, options);
        });
      } else {
        test(pattern, options);
      }
    });
  });
};

Allow timeout for test to be specified.


'use strict';
const path = require('path');
const webpackConfig = require('./webpack.config.js');
const loaders = require('./webpack/loaders');
const AotPlugin =  require('@ngtools/webpack').AotPlugin;

webpackConfig.module.rules = [
  loaders.tslint,
  loaders.ts,
  loaders.html,
  loaders.globalCss,
  loaders.componentCss,
  loaders.file,
];

webpackConfig.plugins = webpackConfig.plugins.concat([
  new AotPlugin({
    tsConfigPath: path.join(__dirname, './tsconfig-aot.json'),
    mainPath: path.join(__dirname, 'src', 'main.ts'),
    entryModule: path.join(__dirname, 'src', 'app', 'app.module#AppModule'),
  }),
]);

module.exports = webpackConfig;

'use strict';
const path = require('path');
const webpackConfig = require('./webpack.config.js');
const loaders = require('./webpack/loaders');
const AotPlugin =  require('@ngtools/webpack').AotPlugin;

webpackConfig.module.rules = [
  loaders.tslint,
  loaders.ts,
  loaders.html,
  { test: /\.css$/, use: 'raw-loader', include: /node_modules/ },
  loaders.globalCss,
  loaders.componentCss,
  loaders.file,
];

webpackConfig.plugins = webpackConfig.plugins.concat([
  new AotPlugin({
    tsConfigPath: path.join(__dirname, './tsconfig-aot.json'),
    mainPath: path.join(__dirname, 'src', 'main.ts'),
    entryModule: path.join(__dirname, 'src', 'app', 'app.module#AppModule'),
  }),
]);

module.exports = webpackConfig;

Handle 3rd party components that use styleUrls for CSS


// Use source if we have coffeescript otherwise use lib
try {
    require('coffee-script');
    module.exports = require(__dirname + "/src/cheerio.coffee");
} catch (e) {
    module.exports = require(__dirname + "/lib/cheerio.js");
}

Allow continuous development using source, just need to link npm package and then we don't have to cake watch anymore.


var resourceful = require('resourceful');

var Riak = resourceful.engines.Riak = function(config) {
  if(config && config.bucket) {
    this.bucket = config.bucket;
  } else {
    throw new Error('bucket must be set in the config for each model.')
  }

  this.db = require('riak-js').getClient(config);

  this.cache = new resourceful.Cache();
}

Riak.prototype.protocol = 'riak';

Riak.prototype.save = function (key, val, callback) {
  this.db.save(this.bucket, key, val, {}, callback);
};

Riak.prototype.get = function (key, callback) {
  this.db.get(this.bucket, key, {}, function(e, value, meta) {
    value._id = meta.key;
    callback(e, value);
  });
}

Riak.prototype.update = function(key, val, callback) {
  var that = this;
  that.get(key, function(err, old) {
    that.save(key, resourceful.mixin(old, val), callback);
  });
}

Riak.prototype.all = function(callback) {
  this.db.getAll(this.bucket, callback);
}

Riak.prototype.destroy = function(key, callback) {
  this.db.remove(this.bucket, key, callback);
}

var resourceful = require('resourceful');

var Riak = resourceful.engines.Riak = function(config) {
  if(config && config.bucket) {
    this.bucket = config.bucket;
  } else {
    throw new Error('bucket must be set in the config for each model.')
  }

  this.db = require('riak-js').getClient(config);

  this.cache = new resourceful.Cache();
}

Riak.prototype.protocol = 'riak';

Riak.prototype.save = function (key, val, callback) {
  this.db.save(this.bucket, key, val, {}, callback);
};

Riak.prototype.get = function (key, callback) {
  this.db.get(this.bucket, key, {}, function(e, value, meta) {
    value._id = meta.key;
    callback(e, value);
  });
}

Riak.prototype.update = function(key, val, callback) {
  var that = this;
  that.get(key, function(err, old) {
    that.save(key, resourceful.mixin(old, val), callback);
  });
}

Riak.prototype.all = function(callback) {
  this.db.getAll(this.bucket, function(e, all) {
    if(e) {
      callback(e);
    } else {
      var models = all.map(function(obj) {
        return obj.data;
      });
      callback(null, models);
    }
  });
}

Riak.prototype.destroy = function(key, callback) {
  this.db.remove(this.bucket, key, callback);
}

Make Model.all work as expected.


var hslToHex = require('tie-dye/hslToHex');

function hashbow(input, saturation, lightness) {
  var toColor, sum;

  var toColor, sum;

  switch (typeof input) {
    case 'object':
      toColor = JSON.stringify(input);
    break;
    case 'number':
      sum = input;
    break;
    case 'boolean':
      return hslToHex(input ? 120 : 0, saturation || 100, lightness || 50);
    break;
    case 'function':
      toColor = input.toString();
    break;
    case 'string':
    default:
      toColor = input;
  }

  if (sum === null) {
    sum = 0;
    toColor.split('').forEach(function (letter) {
      sum += letter.charCodeAt(0);
    });
  }

  sum = Math.abs(sum * sum);

  var color = hslToHex(sum % 360, saturation || 100, lightness || 50);
  return color;
}

module.exports = hashbow;

var hslToHex = require('tie-dye/hslToHex');

function hashbow(input, saturation, lightness) {

  var toColor, sum;
  saturation = saturation || 100;
  lightness = lightness || 50;

  switch (typeof input) {
    case 'object':
      toColor = JSON.stringify(input);
    break;
    case 'number':
      sum = input;
    break;
    case 'boolean':
      return hslToHex(input ? 120 : 0, saturation, lightness);
    break;
    case 'function':
      toColor = input.toString();
    break;
    case 'string':
    default:
      toColor = input;
  }

  if (sum === null) {
    sum = 0;
    toColor.split('').forEach(function (letter) {
      sum += letter.charCodeAt(0);
    });
  }

  sum = Math.abs(sum * sum);

  return hslToHex(sum % 360, saturation, lightness);
}

module.exports = hashbow;

Set default variables at start of function


'use strict';

var githubReadme = require('github-readme'),
    readmeToManPage = require('readme-to-man-page'),
    ghgot = require('gh-got');

module.exports = function (user, repo, opts, cb) {
  if (typeof opts == 'function') {
    cb = opts;
    opts = {};
  }

  if (opts.man == null) {
    opts.man = true;
  }

  githubReadme(user, repo, function (err, readme) {
    if (err) return cb(err);
    opts.man
      ? manPage(readme, cb)
      : cb(readme);
  });

  function manPage (readme, cb) {
    ghgot(['repos', user, repo].join('/'), function (err, info) {
      if (err) return cb(err);

      cb(null, readmeToManPage(readme, {
        name: repo,
        description: info.description,
        date: info.updated_at,
        section: 'github',
        manual: user + '/' + repo + (info.fork ? ' (fork)' : '')
      }));
    });
  }
};

'use strict';

var githubReadme = require('github-readme'),
    readmeToManPage = require('readme-to-man-page'),
    ghgot = require('gh-got');

module.exports = function (user, repo, opts, cb) {
  if (typeof opts == 'function') {
    cb = opts;
    opts = {};
  }

  if (opts.man == null) {
    opts.man = true;
  }

  githubReadme(user, repo, function (err, readme) {
    if (err) return cb(err);
    opts.man
      ? manPage(readme, cb)
      : cb(null, readme);
  });

  function manPage (readme, cb) {
    ghgot(['repos', user, repo].join('/'), function (err, info) {
      if (err) return cb(err);

      cb(null, readmeToManPage(readme, {
        name: repo,
        description: info.description,
        date: info.updated_at,
        section: 'github',
        manual: user + '/' + repo + (info.fork ? ' (fork)' : '')
      }));
    });
  }
};

Fix callback call in markdown mode


import querystring from 'querystring'

export default class FormData {
  constructor(req) {
    this.req = req
    this.parsed = false
    this.data = {}
  }

  async get(key) {
    let data = await this.parse()

    return data[key]
  }

  async parse() {
    if (this.parsed) {
      return this.data
    }

    return new Promise((resolve, reject) => {
      let body = ''

      this.req.setEncoding('utf8')

      this.req.on('data', (chunk) => {
        body += chunk
      })

      this.req.on('end', () => {
        this.data = querystring.parse(body)
        this.parsed = true

        resolve(this.data)
      })
    })
  }
}

import querystring from 'querystring'

export default class FormData {
  constructor(req) {
    this.req = req
    this.parsed = false
    this.data = {}
  }

  async get(key) {
    let data = await this.parse()

    return data[key]
  }

  async parse() {
    if (this.parsed) {
      return this.data
    }

    return new Promise((resolve, reject) => {
      let body = ''

      this.req.setEncoding('utf8')

      this.req.on('readable', () => {
        let buf = this.req.read()

        if (buf === null) {
          this.data = querystring.parse(body)
          this.parsed = true

          resolve(this.data)
        } else {
          body += buf
        }
      })
    })
  }
}

Switch to proper api for reading from streams


"use strict";

var headers = require('./headers');
var patch = require('./patch');
var serialize = require('./serialize');
var deserialize = require('./deserialize');
var removeRemoteMethods = require('./removeRemoteMethods');
var create = require('./create');
var update = require('./update');
var errors = require('./errors');

module.exports = function (app, options) {
  removeRemoteMethods(app, options);
  patch(app, options);
  serialize(app, options);
  deserialize(app, options);
  create(app, options);
  update(app, options);
  errors(app, options);
}

"use strict";

var headers = require('./headers');
var patch = require('./patch');
var serialize = require('./serialize');
var deserialize = require('./deserialize');
var removeRemoteMethods = require('./removeRemoteMethods');
var create = require('./create');
var update = require('./update');
var errors = require('./errors');

module.exports = function (app, options) {
  if (!options || !options.restApiRoot) {
    options = { restApiRoot: '/api' }
  }

  removeRemoteMethods(app, options);
  patch(app, options);
  serialize(app, options);
  deserialize(app, options);
  create(app, options);
  update(app, options);
  errors(app, options);
}

Add default options to module


var express = require("express");
var bodyParser = require("body-parser");
var databox_directory = require("./utils/databox_directory.js");

var timeseriesRouter = require('./timeseries.js');
var keyValueRouter = require('./keyvalue.js');
var actuateRouter = require('./actuate.js');

var app = express();

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

//TODO app.use(Macaroon checker);

app.get("/status", function(req, res) {
    res.send("active");
});

app.use('/api/actuate',actuateRouter(app));

app.use('/:var(api/data|api/ts)?',timeseriesRouter(app));

app.use('/api/key',keyValueRouter(app));

//Websocket connection to live stream data
var server = require('http').createServer(app);
var WebSocketServer = require('ws').Server
app.wss = new WebSocketServer({ server: server })
app.broadcastDataOverWebSocket = require('./broadcastDataOverWebSocket.js')(app)

databox_directory.register_datastore('databox-store-blob', ':8080/api')
  .then( (ids)=>{
	   server.listen(8080);
  })
  .catch((err) => {
  	console.log(err)
  });

 module.exports = app;

var express = require("express");
var bodyParser = require("body-parser");
var databox_directory = require("./utils/databox_directory.js");

var timeseriesRouter = require('./timeseries.js');
var keyValueRouter = require('./keyvalue.js');
var actuateRouter = require('./actuate.js');

var app = express();

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

var DATABOX_LOCAL_NAME = process.env.DATABOX_LOCAL_NAME;

//TODO app.use(Macaroon checker);

app.get("/status", function(req, res) {
    res.send("active");
});

app.use('/api/actuate',actuateRouter(app));

app.use('/:var(api/data|api/ts)?',timeseriesRouter(app));

app.use('/api/key',keyValueRouter(app));

//Websocket connection to live stream data
var server = require('http').createServer(app);
var WebSocketServer = require('ws').Server
app.wss = new WebSocketServer({ server: server })
app.broadcastDataOverWebSocket = require('./broadcastDataOverWebSocket.js')(app)

databox_directory.register_datastore(DATABOX_LOCAL_NAME, ':8080/api')
  .then( (ids)=>{
	   server.listen(8080);
  })
  .catch((err) => {
  	console.log(err)
  });

 module.exports = app;

Change to enable one store per driver


'use strict';

const path = require('path');
const pkg = require(path.resolve('./package.json'));
const semver = require('semver');
const versions = require('./versions.json');

const presetEnv = require.resolve('babel-preset-env');
const presetStage3 = require.resolve('babel-preset-stage-3');

module.exports = function() {
  const targets = {};

  if (pkg.browserslist) {
    targets.browsers = pkg.browserslist;
  }

  if (pkg.engines && pkg.engines.node) {
    const version = pkg.engines.node;

    if (semver.valid(version)) {
      targets.node = version;
    } else if (semver.validRange(version)) {
      targets.node = semver.minSatisfying(versions.node, version);
    }
  }

  return {
    presets: [
      [presetEnv, {targets}],
      presetStage3,
    ],
  };
};

'use strict';

const path = require('path');
const pkg = require(path.resolve('./package.json'));
const semver = require('semver');
const versions = require('./versions.json');

const presetEnv = require.resolve('babel-preset-env');
const presetStage3 = require.resolve('babel-preset-stage-3');

module.exports = function(context, options) {
  options = options || {};

  const modules = options.modules || false;
  const targets = {};

  if (pkg.browserslist) {
    targets.browsers = pkg.browserslist;
  }

  if (pkg.engines && pkg.engines.node) {
    const version = pkg.engines.node;

    if (semver.valid(version)) {
      targets.node = version;
    } else if (semver.validRange(version)) {
      targets.node = semver.minSatisfying(versions.node, version);
    }
  }

  return {
    presets: [
      [presetEnv, {modules, targets}],
      presetStage3,
    ],
  };
};

Add in a modules option that defaults to false


#!/usr/bin/env /usr/local/bin/node
const bitbar = require('bitbar');
const request = require('superagent');
const moment = require('moment');

var busStop = 18610;
var busAPIKey = 'TEST';
var busAPI = `http://api.pugetsound.onebusaway.org/api/where/arrivals-and-departures-for-stop/1_${busStop}.json?key=${busAPIKey}`;

var busInfo = {};

request
.get(busAPI)
.end(function(err, res) {
  var response = JSON.parse(res.text);
  console.log(response);

  var currentTime = response.currentTime;

  bitbar([
      {
          text: currentTime,
          color: bitbar.darkMode ? 'white' : 'red',
          dropdown: false
      },
      bitbar.sep,
      {
          text: 'Unicorns',
          color: '#ff79d7',
          submenu: [
              {
                  text: ':tv: Video',
                  href: 'https://www.youtube.com/watch?v=9auOCbH5Ns4'
              },
              {
                  text: ':book: Wiki',
                  href: 'https://en.wikipedia.org/wiki/Unicorn'
              }
          ]
      },
      bitbar.sep,
      'Ponies'
  ]);
});

#!/usr/bin/env /usr/local/bin/node
const bitbar = require('bitbar');
const request = require('superagent');
const moment = require('moment');

var busStop = 18610;
var busAPIKey = 'TEST';
var busAPI = `http://api.pugetsound.onebusaway.org/api/where/arrivals-and-departures-for-stop/1_${busStop}.json?key=${busAPIKey}`;

var busInfo = {};

request
.get(busAPI)
.end(function(err, res) {
  var response = JSON.parse(res.text);

  bitbar([
      {
          color: bitbar.darkMode ? 'white' : 'red',
          dropdown: false
      },
      bitbar.sep,
      {
          text: 'Unicorns',
          color: '#ff79d7',
          submenu: [
              {
                  text: ':tv: Video',
                  href: 'https://www.youtube.com/watch?v=9auOCbH5Ns4'
              },
              {
                  text: ':book: Wiki',
                  href: 'https://en.wikipedia.org/wiki/Unicorn'
              }
          ]
      },
      bitbar.sep,
      'Ponies'
  ]);
});

Add BitBar metadata, working app


module.exports = {
  presets: [
    require("babel-preset-react")
  ],
  plugins: [
    require("babel-plugin-transform-decorators-legacy"),
    require("babel-plugin-syntax-class-properties"),
    require("babel-plugin-transform-class-properties"),
    require("babel-plugin-syntax-object-rest-spread"),
    require("babel-plugin-transform-object-rest-spread")
  ]
};

module.exports = {
  presets: [
    require("babel-preset-react")
  ],
  plugins: [
    require("babel-plugin-transform-decorators-legacy").default,
    require("babel-plugin-syntax-class-properties"),
    require("babel-plugin-transform-class-properties"),
    require("babel-plugin-syntax-object-rest-spread"),
    require("babel-plugin-transform-object-rest-spread")
  ]
};

Fix the issue of invalid default property when bundling


require.config({
    baseUrl: '../',
    paths: {
        'text': 'bower_components/requirejs-text/text',
        '$': 'bower_components/jquery/dist/jquery',
        'bouncefix': 'bower_components/bouncefix.js/dist/bouncefix.min',
        'velocity': 'bower_components/mobify-velocity/velocity',
        'modal-center': 'dist/effect/modal-center',
        'sheet-bottom': 'dist/effect/sheet-bottom',
        'sheet-left': 'dist/effect/sheet-left',
        'sheet-right': 'dist/effect/sheet-right',
        'sheet-top': 'dist/effect/sheet-top',
        'plugin': 'bower_components/plugin/dist/plugin.min',
        'shade': 'bower_components/shade/dist/shade.min',
        'pinny': 'dist/pinny'
    },
    'shim': {
        '$': {
            exports: '$'
        }
    }
});

require.config({
    baseUrl: '../',
    paths: {
        'text': 'bower_components/requirejs-text/text',
        '$': 'lib/zeptojs/dist/zepto',
        'bouncefix': 'bower_components/bouncefix.js/dist/bouncefix.min',
        'velocity': 'bower_components/mobify-velocity/velocity',
        'modal-center': 'dist/effect/modal-center',
        'sheet-bottom': 'dist/effect/sheet-bottom',
        'sheet-left': 'dist/effect/sheet-left',
        'sheet-right': 'dist/effect/sheet-right',
        'sheet-top': 'dist/effect/sheet-top',
        'plugin': 'bower_components/plugin/dist/plugin.min',
        'shade': 'bower_components/shade/dist/shade.min',
        'pinny': 'dist/pinny'
    },
    'shim': {
        '$': {
            exports: '$'
        }
    }
});

Switch to Zepto for examples


// 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 jquery_ujs
//= 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 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_tree .

Remove jquery from dummy app


/* global angular */

angular
  .module('speciesApp')
  .factory('SpeciesService', [
    'API_BASE_URL',
    '$resource',
    SpeciesService
  ])

function SpeciesService (API_BASE_URL, $resource) {
  return $resource(
    API_BASE_URL,
    null,
    {
      getKingdoms: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getkingdomnames'
        }
      },
      getClasses: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getclassnames'
        }
      },
      getFamilies: {
        method: 'GET',
        isArray: false,
        params: {
          op: 'getfamilynames'
        }
      }
    }
  )
}

/* global angular */

angular
  .module('speciesApp')
  .factory('SpeciesService', [
    'API_BASE_URL',
    '$resource',
    SpeciesService
  ])

function SpeciesService (API_BASE_URL, $resource) {
  return $resource(
    API_BASE_URL,
    null,
    {
      getKingdoms: {
        method: 'GET',
        isArray: false,
        cache: true,
        params: {
          op: 'getkingdomnames'
        }
      },
      getClasses: {
        method: 'GET',
        isArray: false,
        cache: true,
        params: {
          op: 'getclassnames'
        }
      },
      getFamilies: {
        method: 'GET',
        isArray: false,
        cache: true,
        params: {
          op: 'getfamilynames'
        }
      }
    }
  )
}

Enable cache for all endpoints


// @flow

import * as React from 'react';
import { View } from 'react-native';
import { Touchable, NetworkImage, StyleSheet } from '@kiwicom/mobile-shared';

import GalleryButton from '../galleryButton/GalleryButton';

type Props = {|
  +thumbnailUrl: ?string,
  +photoCount: number,
  +openGallery: () => void,
|};

export default function RoomImage({
  thumbnailUrl,
  photoCount,
  openGallery,
}: Props) {
  return (
    <Touchable onPress={openGallery} disabled={thumbnailUrl == null}>
      <View>
        <NetworkImage source={{ uri: thumbnailUrl }} style={styles.thumbnail} />
        <View style={styles.galleryButton}>
          <GalleryButton
            count={photoCount}
            style={{ container: styles.galleryButtonContainer }}
          />
        </View>
      </View>
    </Touchable>
  );
}

const styles = StyleSheet.create({
  galleryButton: {
    position: 'absolute',
    bottom: 0,
    end: 0,
    start: 0,
  },
  galleryButtonContainer: {
    justifyContent: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.3)',
  },
  thumbnail: {
    width: 60,
    height: 80,
    borderRadius: 2,
  },
});

// @flow

import * as React from 'react';
import { View } from 'react-native';
import { Touchable, NetworkImage, StyleSheet } from '@kiwicom/mobile-shared';

import GalleryButton from '../galleryButton/GalleryButton';

type Props = {|
  +thumbnailUrl: ?string,
  +photoCount: number,
  +openGallery: () => void,
|};

export default function RoomImage({
  thumbnailUrl,
  photoCount,
  openGallery,
}: Props) {
  return (
    <Touchable onPress={openGallery} disabled={thumbnailUrl == null}>
      <View>
        <NetworkImage source={{ uri: thumbnailUrl }} style={styles.thumbnail} />
        <View style={styles.galleryButton}>
          <GalleryButton
            count={photoCount}
            style={{ container: styles.galleryButtonContainer }}
          />
        </View>
      </View>
    </Touchable>
  );
}

const styles = StyleSheet.create({
  galleryButton: {
    position: 'absolute',
    bottom: 0,
    end: 0,
    start: 0,
  },
  galleryButtonContainer: {
    justifyContent: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.3)',
    borderRadius: 0,
  },
  thumbnail: {
    width: 60,
    height: 80,
    borderRadius: 2,
  },
});

Remove border radius from room image overlay


// @flow

import type {
  ThreadSubscription,
  SubscriptionUpdateRequest,
} from 'lib/types/subscription-types';
import type { Viewer } from '../session/viewer';

import { ServerError } from 'lib/utils/errors';

import { dbQuery, SQL } from '../database';

async function userSubscriptionUpdater(
  viewer: Viewer,
  update: SubscriptionUpdateRequest,
): Promise<ThreadSubscription> {
  const query = SQL`
    SELECT subscription
    FROM memberships
    WHERE user = ${viewer.id} AND thread = ${update.threadID} AND role != 0
  `;
  const [ result ] = await dbQuery(query);
  if (result.length === 0) {
    throw new ServerError('not_member');
  }
  const row = result[0];

  const newSubscription = {
    ...row.subscription,
    ...update.updatedFields,
  };
  const saveQuery = SQL`
    UPDATE memberships
    SET subscription = ${JSON.stringify(newSubscription)}
    WHERE user = ${viewer.id} AND thread = ${update.threadID}
  `;
  await dbQuery(saveQuery);

  return newSubscription;
}

export {
  userSubscriptionUpdater,
};

// @flow

import type {
  ThreadSubscription,
  SubscriptionUpdateRequest,
} from 'lib/types/subscription-types';
import type { Viewer } from '../session/viewer';
import { updateTypes } from 'lib/types/update-types';

import { ServerError } from 'lib/utils/errors';
import { viewerIsMember } from 'lib/shared/thread-utils';

import { dbQuery, SQL } from '../database';
import { createUpdates } from '../creators/update-creator';
import { fetchThreadInfos } from '../fetchers/thread-fetchers';

async function userSubscriptionUpdater(
  viewer: Viewer,
  update: SubscriptionUpdateRequest,
): Promise<ThreadSubscription> {
  const threadInfos = await fetchThreadInfos(
    viewer,
    SQL`t.id = ${update.threadID}`,
  );
  const threadInfo = threadInfos[update.threadID];
  if (!viewerIsMember(threadInfo)) {
    throw new ServerError('not_member');
  }

  const promises = [];

  const newSubscription = {
    ...threadInfo.currentUser.subscription,
    ...update.updatedFields,
  };
  const saveQuery = SQL`
    UPDATE memberships
    SET subscription = ${JSON.stringify(newSubscription)}
    WHERE user = ${viewer.id} AND thread = ${update.threadID}
  `;
  promises.push(dbQuery(saveQuery));

  const time = Date.now();
  const updateDatas = [{
    type: updateTypes.UPDATE_THREAD,
    userID: viewer.id,
    time,
    threadInfo: {
      ...threadInfo,
      currentUser: {
        ...threadInfo.currentUser,
        subscription: newSubscription,
      },
    },
  }];
  promises.push(createUpdates(updateDatas, viewer.cookieID));

  await Promise.all(promises);
  return newSubscription;
}

export {
  userSubscriptionUpdater,
};

Send updateTypes.UPDATE_THREAD to user's other devices when ThreadSubscription changed


export const loadState = () => {
	try {
		const serializedState = localStorage.getItem('state')
		if (serializedState === null) {
			return undefined
		}
		return JSON.parse(serializedState)
	} catch (err) {
		return undefined
	}
}

export const saveState = (state) => {
	try {
		const serializedState = JSON.stringify(state);
		localStorage.setItem('state', serializedState)
	} catch (err) {
		//  Ignore write errors.
		console.error(err);
	}
}

export const clearState = () => {
	console.log("clearState()");
	try {
		localStorage.removeItem('state')
	} catch (err){
		console.error(err);
	}
}

export const loadState = () => {
	try {
		const serializedState = localStorage.getItem('state');
		if (serializedState === null) {
			return undefined
		}
		return JSON.parse(serializedState);
	} catch (err) {
		return undefined
	}
}

export const saveState = (state) => {
	try {
		const serializedState = JSON.stringify(state);
		localStorage.setItem('state', serializedState);
	} catch (err) {
		//  Ignore write errors.
		console.error(err);
	}
}

export const clearState = () => {
	console.log("clearState()");
	try {
		localStorage.removeItem('state');
	} catch (err) {
		console.error(err);
	}
}

// Cache
export const loadCache = () => {
	try {
		const serializedCache = localStorage.getItem('cache');
		if (serializedCache === null) {
			return undefined
		}
		return JSON.parse(serializedState);
	} catch (err) {
		return undefined
	}
}

export const saveCache = (cache) => {
	try {
		const serializedCache = JSON.stringify(state);
		localStorage.setItem('cache', serializedCache);
	} catch (err) {
		//  Ignore write errors.
		console.error(err);
	}
}

export const clearCache = () => {
	console.log("clearCache()");
	try {
		localStorage.removeItem('cache');
	} catch (err) {
		console.error(err);
	}
}

Add cache functions to localstorage


var should = require('should'),
    CLI    = require('../lib/CLI.js');

describe('CLI Tool', function () {
  var optimist = {}, argv = {};

  beforeEach(function () {
    optimist = {};
    argv = {};
  });

  it('should call showHelp if -h is passed', function (done) {
    argv = { h : true, help : true };
    optimist.showHelp = done;

    new CLI(argv, optimist);
  });

  it('should use stdin and stdout for input/output if files are not provided', function () {
    var cli = new CLI(argv, optimist);

    cli.in.should.equal(process.stdin);
    cli.out.should.equal(process.stdout);
  });
});

var should = require('should'),
    CLI    = require('../lib/CLI.js');

describe('CLI Tool', function () {
  var optimist = {}, argv = {},
    _log, _error;

  beforeEach(function () {
    optimist = {};
    argv = {};
    _log = console.log;
    _error = console.error;
  });

  afterEach(function () {
    console.log = _log;
    console.error = _error;
  });

  it('should call showHelp if -h is passed', function (done) {
    argv = { h : true, help : true };
    optimist.showHelp = done;

    new CLI(argv, optimist);
  });

  it('should use stdin and stdout for input/output if files are not provided', function () {
    var cli = new CLI(argv, optimist);

    cli.in.should.equal(process.stdin);
    cli.out.should.equal(process.stdout);
  });

  it('should return languages if -l is passed', function () {
  });

  it('should return error if file not found', function (done) {
    argv = { file : 'filethat!@#$%\n^CantExist' };

    console.error = function (error) {
      var notFoundExp = /not found/

      notFoundExp.test(error).should.equal(true);
      done();
    }
    var cli = new CLI(argv, optimist);
  });
});

Add file not found test


function AuthController($scope, $state, Auth, Flash) {
  $scope.login = function () {
    Auth.login($scope.user).then(function () {
      $state.go('home');
      $scope.successAlert();
    }, function (error) {
      $scope.errorAlert(error);
    });
  };

  $scope.register = function () {
    Auth.register($scope.user).then(function successCallback() {
      $state.go('home');
      $scope.successAlert();
    }, function errorCallback(err) {
      $scope.errorAlert(err);
    });
  };

  $scope.successAlert = function () {
      var message = 'Success!';
      var id = Flash.create('success', message, 5000);
  }

  $scope.errorAlert = function (error) {
      var message = 'Oops! Something went wrong.';
      var id = Flash.create('danger', message, 5000);
    }
}

AuthController.$inject = ['$scope', '$state', 'Auth', 'Flash'];
angular
  .module('app')
  .controller('AuthController', AuthController);

function AuthController($scope, $state, Auth, Flash) {
  $scope.login = function () {
    Auth.login($scope.user).then(function (res) {
      $state.go('home');
    }, function (error) {
      $scope.errorAlert(error);
    });
  };

  $scope.register = function () {
    Auth.register($scope.user).then(function successCallback() {
      $state.go('home');
      $scope.successAlert();
    }, function errorCallback(err) {
      $scope.errorAlert(err);
    });
  };

  $scope.successAlert = function (res) {
      var message = 'Welcome back ' + res.username + '';
      var id = Flash.create('success', message, 5000);
  }

  $scope.errorAlert = function (error) {
      var message = 'Oops! Something went wrong.';
      var id = Flash.create('danger', message, 5000);
    }
}

AuthController.$inject = ['$scope', '$state', 'Auth', 'Flash'];
angular
  .module('app')
  .controller('AuthController', AuthController);

Change alert message on login


//@flow
import React from "react";

import { Ul, Li, Nav } from "./styled-components";
import { LinkedInIcon } from "../../../../elements/linkedin-icon";
import { OctocatIcon } from "../../../../elements/octocat-icon";
import { Highlight } from "../../../../elements/highlight";
import { Chevron } from "../../../../elements/chevron";
import theme from "../../../../global/style/mainTheme";

export const MainNav = (props: {}) => (

<Highlight {...props} url="/">Projects</Highlight>

<Highlight {...props}>About</Highlight>

        <Highlight highlightColor="transparent" {...props}>
          <Chevron {...props}>Download</Chevron>
        </Highlight>

<Highlight {...props}>Contact</Highlight>

<LinkedInIcon />

<OctocatIcon />

);

//@flow
import React from "react";

import { Ul, Li, Nav } from "./styled-components";
import { LinkedInIcon } from "../../../../elements/linkedin-icon";
import { OctocatIcon } from "../../../../elements/octocat-icon";
import { Highlight } from "../../../../elements/highlight";
import { Chevron } from "../../../../elements/chevron";
import Bubble from "../../../../elements/bubble";
import theme from "../../../../global/style/mainTheme";

export const MainNav = (props: {}) => (

<Highlight {...props} url="/">Projects</Highlight>

<Highlight {...props}>About</Highlight>

        <Highlight highlightColor="transparent" {...props}>
          <Chevron {...props}>Download</Chevron>
          <Bubble
            {...props}
            textItems={[
              { label: "Resume", target: "#" },
              { label: "Portfoliontopolov", target: "#" }
            ]}
          />
        </Highlight>

<Highlight {...props}>Contact</Highlight>

<LinkedInIcon />

<OctocatIcon />

);

Include Bubble dropdown in navigation


import { chainableClassList } from './libs/helpers';
import purify from './libs/purify-dom';
import { initializeSearch } from './modules/search';
import initializeHistory from './modules/history';
import loadOptions from './modules/options';
import loadNextImage from './modules/load-next-image';
import weatherInfo from './components/weather-info';
import main from './components/main';
import loader from './components/loader';
import footer from './components/footer';
import header from './components/header';

const body = document.getElementById('app');
body.insertAdjacentHTML('afterbegin', purify.sanitize(`
    ${loader()}
    ${header()}
    ${main()}
    ${footer()}
  `));

loadNextImage();

loadOptions();

initializeHistory();

initializeSearch();

chrome.storage.local.get('forecast', (result) => {
  const { forecast } = result;

  if (forecast) {
    const footerComponent = document.getElementById('s-footer');
    footerComponent.insertAdjacentHTML('afterbegin',
      purify.sanitize(weatherInfo(forecast), { ADD_TAGS: ['use'] }));
  }
});

const uiElements = document.querySelectorAll('.s-ui');
uiElements.forEach(element => chainableClassList(element).remove('hidden'));

document.addEventListener('click', (node) => {
  if (node.target.matches('.popover *')) return;
  const popover = document.querySelectorAll('.popover .popover-content');
  popover.forEach(e => e.classList.remove('popover-content--is-visible'));
});

import { chainableClassList } from './libs/helpers';
import purify from './libs/purify-dom';
import { initializeSearch } from './modules/search';
import initializeHistory from './modules/history';
import loadOptions from './modules/options';
import loadNextImage from './modules/load-next-image';
import weatherInfo from './components/weather-info';
import main from './components/main';
import loader from './components/loader';
import footer from './components/footer';
import header from './components/header';

const body = document.getElementById('app');
body.insertAdjacentHTML('afterbegin', purify.sanitize(`
    ${loader()}
    ${header()}
    ${main()}
    ${footer()}
  `));

loadNextImage();

loadOptions();

initializeHistory();

initializeSearch();

chrome.storage.local.get('forecast', (result) => {
  const { forecast } = result;
  const weatherArea = document.getElementById('s-footer .weather');

  if (forecast) {
    weatherArea.insertAdjacentHTML('afterbegin',
      purify.sanitize(weatherInfo(forecast), { ADD_TAGS: ['use'] }));
  }
});

const uiElements = document.querySelectorAll('.s-ui');
uiElements.forEach(element => chainableClassList(element).remove('hidden'));

document.addEventListener('click', (node) => {
  if (node.target.matches('.popover *')) return;
  const popover = document.querySelectorAll('.popover .popover-content');
  popover.forEach(e => e.classList.remove('popover-content--is-visible'));
});

Change how weather forcast is inserted into the page


'use strict'

module.exports = {
	// This function finds the minimum and maximum values in an array of numbers.
	findMinMax: function(numlist) {
		if (numlist.length === 0) {
			return [];
		}

		numlist.sort( function (a,b) {return a - b} );

		if (numlist[0] == numlist[numlist.length - 1]) {
			return [numlist[0]];
		}
		else return [ numlist[0], numlist[numlist.length - 1] ];
	}
}

'use strict'

module.exports = {
	// This function finds the minimum and maximum values in an array of numbers.
	findMinMax: function(numlist) {
		if (numlist.length === 0) {
			return [];
		}

		if (typeof numlist[0] !== 'number') {
			return null;
		}

		numlist.sort( function (a,b) {return a - b} );

		if (numlist[0] == numlist[numlist.length - 1]) {
			return [numlist[0]];
		}
		else return [ numlist[0], numlist[numlist.length - 1] ];
	}
}

Implement findMinMax functionality for non-number arrays.


/**
 * Convenient Redis Storage mock for testing purposes
 */

var util = require ('util');

function StorageMocked(data){
  data = data || {};
  this.currentOutage = data.currentOutage;
}

exports = module.exports = StorageMocked;

StorageMocked.prototype.startOutage = function (service, outageData, callback) {
  this.currentOutage = outageData;
  process.nextTick(function(){
    callback(null);
  });
};

StorageMocked.prototype.getCurrentOutage = function (service, callback) {
  var self = this;
  process.nextTick(function(){
    callback(null, self.currentOutage);
  });
};

StorageMocked.prototype.saveLatency = function (service, timestamp, latency, callback) {
  process.nextTick(function(){
    callback(null);
  });
};

StorageMocked.prototype.archiveCurrentOutageIfExists = function (service, callback) {
  var self = this;
  process.nextTick(function(){
    callback(null, self.currentOutage);
  });
};

StorageMocked.prototype.flush_database = function (callback){
  process.nextTick(function(){
    callback(null);
  });
};

/**
 * Convenient Redis Storage mock for testing purposes
 */

var util = require ('util');

function StorageMocked(data){
  data = data || {};
  this.currentOutage = data.currentOutage;
}

exports = module.exports = StorageMocked;

StorageMocked.prototype.startOutage = function (service, outageData, callback) {
  this.currentOutage = outageData;
  setImmediate(function(){
    callback(null);
  });
};

StorageMocked.prototype.getCurrentOutage = function (service, callback) {
  var self = this;
  setImmediate(function(){
    callback(null, self.currentOutage);
  });
};

StorageMocked.prototype.saveLatency = function (service, timestamp, latency, callback) {
  setImmediate(function(){
    callback(null);
  });
};

StorageMocked.prototype.archiveCurrentOutageIfExists = function (service, callback) {
  var self = this;
  setImmediate(function(){
    callback(null, self.currentOutage);
  });
};

StorageMocked.prototype.flush_database = function (callback){
  setImmediate(function(){
    callback(null);
  });
};

Use setImmediate instead of process.nextTick (since 0.10)


import { Component } from 'react'
import PropTypes from 'prop-types'

export default class Redirect extends Component {

  static propTypes = {
    redirectPath: PropTypes.string.isRequired,
    redirect: PropTypes.func.isRequired
  };

  componentWillMount() {
    this.props.redirect(this.props, this.props.redirectPath)
  }

  render() {
    // Redirect should happen before this is rendered
    return null
  }
}

import { Component } from 'react'
import PropTypes from 'prop-types'

export default class Redirect extends Component {

  static propTypes = {
    redirectPath: PropTypes.string.isRequired,
    redirect: PropTypes.func.isRequired
  };

  UNSAFE_componentWillMount() {
    this.props.redirect(this.props, this.props.redirectPath)
  }

  render() {
    // Redirect should happen before this is rendered
    return null
  }
}

Add unsafe to component mount


const Configstore = require('configstore');
const Promise = require('promise');
const inquirer = require('inquirer');
const pkg = require('../../../../package.json');
const prompts = require('./setup-prompts');

class ServerConfig {

  static DEFAULTS = {
    'isFirstRun': true,
    'serverPort': '3000'
  }

  constructor(defaults = this.constructor.DEFAULTS) {
    this.db = new Configstore(pkg.name, defaults);
  }

  prompt() {
    return new Promise((resolve, reject) => {
      console.log('> Preparing initial setup\n');

      this.db.set('isFirstRun', false);

      inquirer.prompt(prompts, answers => {
        Object.keys(answers).map(key => {
          return this.db.set(key, answers[key]);
        });

        resolve('\n> Setup finished!\n');
      });
    });
  }
}

module.exports = new ServerConfig();

const Configstore = require('configstore');
const Promise = require('promise');
const inquirer = require('inquirer');
const pkg = require('../../../../package.json');
const prompts = require('./setup-prompts');

class ServerConfig {

  static DEFAULTS = {
    'isFirstRun': true,
    'serverPort': '3000'
  }

  constructor(defaults = this.constructor.DEFAULTS) {
    this.db = new Configstore(pkg.name, defaults);
  }

  prompt() {
    return new Promise((resolve, reject) => {
      console.log('> Preparing initial setup\n');

      inquirer.prompt(prompts, answers => {
        Object.keys(answers).map(key => {
          return this.db.set(key, answers[key]);
        });

        this.db.set('isFirstRun', false);

        resolve('\n> Setup finished!\n');
      });
    });
  }
}

module.exports = new ServerConfig();

Disable first run after configuration process


/**
 * @fileoverview CSS helper functions
 */

goog.provide('treesaver.css');

/**
 * Return the computedStyle object, which varies based on
 * browsers
 * @param {?Element} el
 * @return {Object}
 */
treesaver.css.getStyleObject = function(el) {
  return document.defaultView.getComputedStyle(el, null);
};

// IE doesn't support getComputedStyle
if (SUPPORT_IE &&
    !(document.defaultView && document.defaultView.getComputedStyle)) {
  // Patch to use MSIE API
  treesaver.css.getStyleObject = function(el) {
    return el.currentStyle;
  };
}

Remove IE8 support from CSS module


import fetch from 'focus-core/network/fetch';
import personUrl from '../config/server/persons';
import omit from 'lodash/object/omit';

export default {
    loadPerson(id) {
        console.log(`[PERSON] call loadPerson(${id}) method`);
        return fetch(personUrl.load({urlData: {id}}), {isCORS: true});
    },
    loadPersonMovies(id) {
        console.log(`[PERSON] call loadPersonMovies(${id}) method`);
        return fetch(personUrl.load({urlData: {id}}), {isCORS: true}).then(({movieLinks}) => {
            return movieLinks;
        });
    },
    updatePersonBiography(data) {
        const personId = data.id;
        console.log(`[PERSON] call savePersonBiography method. data=${JSON.stringify(data)}`);
        const newData = omit(data, ['movieLinks']);
        return fetch(personUrl.update({urlData: {id: personId}, data: newData}), {isCORS: true});
    },
    updatePersonIdentity(data) {
        const personId = data.id;
        console.log(`[PERSON] call savePersonIdentity method. data=${JSON.stringify(data)}`);
        const newData = omit(data, ['movieLinks']);
        return fetch(personUrl.update({urlData: {id: personId}, data: newData}), {isCORS: true});
    }
}

import fetch from 'focus-core/network/fetch';
import personUrl from '../config/server/persons';
import omit from 'lodash/object/omit';

export default {
    loadPerson(id) {
        console.log(`[PERSON] call loadPerson(${id}) method`);
        return fetch(personUrl.load({urlData: {id}}), {isCORS: true});
    },
    loadPersonMovies(id) {
        console.log(`[PERSON] call loadPersonMovies(${id}) method`);
        return fetch(personUrl.load({urlData: {id}}), {isCORS: true}).then((data) => {
            if(data && data.movieLinks) {
              return data.movieLinks
            }
            return [];
        });
    },
    updatePersonBiography(data) {
        const personId = data.id;
        console.log(`[PERSON] call savePersonBiography method. data=${JSON.stringify(data)}`);
        const newData = omit(data, ['movieLinks']);
        return fetch(personUrl.update({urlData: {id: personId}, data: newData}), {isCORS: true});
    },
    updatePersonIdentity(data) {
        const personId = data.id;
        console.log(`[PERSON] call savePersonIdentity method. data=${JSON.stringify(data)}`);
        const newData = omit(data, ['movieLinks']);
        return fetch(personUrl.update({urlData: {id: personId}, data: newData}), {isCORS: true});
    }
}

Fix bug cannot read movilinks of undefined


var assert = require('chai').assert;
var request = require('supertest');

var container_url = 'http://localhost:8080/annotations/';

const MEDIA_TYPE = 'application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"';

describe('SHOULDs', function() {
  // Annotation Container Tests
  describe('4. Annotation Containers', function() {
    it.skip('SHOULD implement the LDP Basic Container specification');
    it('SHOULD use HTTPS rather than HTTP', function(done) {
      var first_five = container_url.substr(0,5);
      assert.equal(first_five, 'https');
      done();
    });
  });
});

var assert = require('chai').assert;
var request = require('supertest');

var container_url = 'http://localhost:8080/annotations/';

const MEDIA_TYPE = 'application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"';

describe('SHOULDs', function() {
  // Annotation Container Tests
  describe('4. Annotation Containers', function() {
    it.skip('SHOULD implement the LDP Basic Container specification');
    it('SHOULD use HTTPS rather than HTTP', function(done) {
      var first_five = container_url.substr(0,5);
      assert.equal(first_five, 'https');
      done();
    });
  });

  describe('4.1.1 Client Preferences', function() {
    // if the client has no preference, the server...
    it('SHOULD return the full annotation descriptions', function(done) {
      request(container_url)
        .get('')
        .expect(function(res) {
          // check for `items`
          if (!('items' in res.body)) {
            throw new Error('Missing `items` array');
          } else {
            // check that the first item (at least) has a @context
            // TODO: use some of the JSON Schemas from
            //   https://github.com/w3c/web-annotation-tests
            res.body.items.forEach(function(item) {
              if (!('@context' in item)) {
                throw new Error('First item is not an Annotation');
              }
            });
          }
        })
        .expect(200, done);
    });
  });
});

Add default full annotation description SHOULD check


$( window ).load(function() {
  var togglePanel = function(panelId, link){
    var panel = $(panelId);
    panel.toggle();
    if(panel.is(":visible")){
      $(link).html('Hide');
    }else{
      $(link).html('Show');
    }
  };
  $('#togglePanel').click(function(){ togglePanel('#input_panel', this); });
  $('#toggleLegend').click(function(){ togglePanel('#zone_legend', this); });
  $('#toggleMapLegend').click(function(){ togglePanel('#map_legend', this); });

  var initMap = function(){
    if($(map).data('leaflet-map')){
      // lMap is the leaflet map object see http://leafletjs.com/reference.html
      var lMap = $(map).data('leaflet-map');
      L.control.scale().addTo(lMap);
    }
    else {
      setTimeout(initMap, 100);
    }
  };
  initMap();

  $("#printBtn").click(function(){
    $('#map').print();
  });
});

$( window ).load(function() {
  var togglePanel = function(panelId, link){
    var panel = $(panelId);
    panel.toggle();
    if(panel.is(":visible")){
      $(link).html('Hide');
    }else{
      $(link).html('Show');
    }
  };
  $('#togglePanel').click(function(){ togglePanel('#input_panel', this); });
  $('#toggleLegend').click(function(){ togglePanel('#zone_legend', this); });
  $('#toggleMapLegend').click(function(){ togglePanel('#map_legend', this); });
  $('select').addClass("form-control");
  var initMap = function(){
    if($(map).data('leaflet-map')){
      // lMap is the leaflet map object see http://leafletjs.com/reference.html
      var lMap = $(map).data('leaflet-map');
      L.control.scale().addTo(lMap);
    }
    else {
      setTimeout(initMap, 100);
    }
  };
  initMap();

  $("#printBtn").click(function(){
    $('#map').print();
  });
});

Add missing class to select


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

function isDirectory(dir) {
  return fs.lstatSync(dir).isDirectory();
}

module.exports = {

  devtool: 'inline-source-map',

  entry: fs.readdirSync(__dirname).reduce(function (entries, dir) {
    var isDraft = dir.charAt(0) === '_';

    if (!isDraft && isDirectory(path.join(__dirname, dir)))
      entries[dir] = path.join(__dirname, dir, 'app.js');

    return entries;
  }, {}),

  output: {
    path: 'examples/__build__',
    filename: '[name].js',
    chunkFilename: '[id].chunk.js',
    publicPath: '/__build__/'
  },

  module: {
    loaders: [
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
    ]
  },

  resolve: {
    alias: {
      'react-router': '../../modules/index'
    }
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin('shared.js'),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    })
  ]

};

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

function isDirectory(dir) {
  return fs.lstatSync(dir).isDirectory();
}

module.exports = {

  devtool: 'inline-source-map',

  entry: fs.readdirSync(__dirname).reduce(function (entries, dir) {
    var isDraft = dir.charAt(0) === '_';

    if (!isDraft && isDirectory(path.join(__dirname, dir)))
      entries[dir] = path.join(__dirname, dir, 'app.js');

    return entries;
  }, {}),

  output: {
    path: 'examples/__build__',
    filename: '[name].js',
    chunkFilename: '[id].chunk.js',
    publicPath: '/__build__/'
  },

  module: {
    loaders: [
      { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?optional=es7.classProperties' }
    ]
  },

  resolve: {
    alias: {
      'react-router': '../../modules'
    }
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin('shared.js'),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    })
  ]

};

Allow examples to use ES7 class properties


(function() {
  "use strict";
  window.GOVUK = window.GOVUK || {};

  function CollapsibleCollection(options){
    this.collapsibles = {};
    this.sections = options.el.find('section');

    this.sections.each($.proxy(this.initCollapsible, this));

    this.closeAll();
  }

  CollapsibleCollection.prototype.initCollapsible = function initCollapsible(sectionIndex){
    var $section = $(this.sections[sectionIndex]);
    var collapsible = new GOVUK.Collapsible($section);
    this.collapsibles[$section.attr('data-section-id')] = collapsible;
  }

  CollapsibleCollection.prototype.closeAll = function closeAll(){
    for (var section in this.collapsibles) {
      this.collapsibles[section].close();
    }
  }

  CollapsibleCollection.prototype.openAll = function openAll(){
    for (var section in this.collapsibles) {
      this.collapsibles[section].open();
    }
  }

  GOVUK.CollapsibleCollection = CollapsibleCollection;
}());

(function() {
  "use strict";
  window.GOVUK = window.GOVUK || {};

  function CollapsibleCollection(options){
    this.collapsibles = {};
    this.$sections = options.el.find('section');

    this.$sections.each($.proxy(this.initCollapsible, this));

    this.closeAll();
  }

  CollapsibleCollection.prototype.initCollapsible = function initCollapsible(sectionIndex){
    var $section = $(this.sections[sectionIndex]);
    var collapsible = new GOVUK.Collapsible($section);
    this.collapsibles[$section.attr('data-section-id')] = collapsible;
  }

  CollapsibleCollection.prototype.closeAll = function closeAll(){
    for (var sectionId in this.collapsibles) {
      this.collapsibles[sectionId].close();
    }
  }

  CollapsibleCollection.prototype.openAll = function openAll(){
    for (var sectionId in this.collapsibles) {
      this.collapsibles[sectionId].open();
    }
  }

  GOVUK.CollapsibleCollection = CollapsibleCollection;
}());

Use $sections instead of sections


import { createBrowserHistory } from 'history';

// Custom history component handles scroll position for back button, hashes,
// and normal links
const browserHistory = createBrowserHistory();
browserHistory.listen((location) => {
  setTimeout(() => {
    if (location.action === 'POP') {
      return;
    }
    const hash = window.location.hash;
    if (hash) {
      const element = document.querySelector(hash);
      if (element) {
        element.scrollIntoView({
          block: 'start',
          behavior: 'smooth'
        });
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
});

export default browserHistory;

import { createBrowserHistory } from 'history';

// Custom history component handles scroll position for back button, hashes,
// and normal links
const browserHistory = createBrowserHistory();
browserHistory.listen((location) => {
  setTimeout(() => {
    if (location.action === 'POP') {
      return;
    }
    const hash = window.location.hash;
    if (hash) {
      const element = document.getElementById(hash);
      if (element) {
        element.scrollIntoView({
          block: 'start',
          behavior: 'smooth'
        });
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
});

export default browserHistory;

Fix benign JS error from clicking reference links in ArticleViewer


var restify = require('express-restify-mongoose');
var Artist = require('../api/models/artist');
var Info = require('../api/models/info');
var News = require('../api/models/news');
var Event = require('../api/models/event');
var Location = require('../api/models/location');
var Festival = require('../api/models/festival');
var Localise = require('../lib/localise');

function shiftTimes(item) {
  onlyAttributes = ['start_time', 'end_time'];

  Object.keys(item)
    .filter(function(key) {
      return onlyAttributes.indexOf(key) > -1;
    })
    .forEach(function(key) {
      // add one hour for time attributes
      var shiftedTimestamp =
        new Date(item[key]).getTime() +
        60 * 60 * 1000/*ms*/;
      item[key] = new Date(shiftedTimestamp).toString();
    });
}

function transformItem(item) {
  shiftTimes(item);
}

function transformResult(result) {
  if (result instanceof Array) {
    result.forEach(function(item) {
      transformItem(item);
    });
  } else {
    transformItem(result);
  }
  return result;
}

module.exports = function(app, apiVersion) {
  restify.defaults({
    outputFn: Localise.localiseApiCallResult,
    version: apiVersion,
    private: ['__v']
  });

  restify.serve(app, Artist);
  restify.serve(app, Info, {
    plural: false,
    contextFilter: function(model, req, cb) {
      // Sort by creation timestamp (included in Mongo's _id field)
      cb(model.find().sort('_id'));
    }
  });
  restify.serve(app, News);
  restify.serve(app, Event, {
    outputFn: function(req, res) {
      res
        .status(req.erm.statusCode)
        .json(
          transformResult(req.erm.result)
        );
    }
  });
  restify.serve(app, Location);
  restify.serve(app, Festival, { plural: false });
};

var restify = require('express-restify-mongoose');
var Artist = require('../api/models/artist');
var Info = require('../api/models/info');
var News = require('../api/models/news');
var Event = require('../api/models/event');
var Location = require('../api/models/location');
var Festival = require('../api/models/festival');
var Localise = require('../lib/localise');

module.exports = function(app, apiVersion) {
  restify.defaults({
    outputFn: Localise.localiseApiCallResult,
    version: apiVersion,
    private: ['__v']
  });

  restify.serve(app, Artist);
  restify.serve(app, Info, {
    plural: false,
    contextFilter: function(model, req, cb) {
      // Sort by creation timestamp (included in Mongo's _id field)
      cb(model.find().sort('_id'));
    }
  });
  restify.serve(app, News);
  restify.serve(app, Event);
  restify.serve(app, Location);
  restify.serve(app, Festival, { plural: false });
};

Revert "Add 1 hour to datetimes for start_time and end_time in the events API result"


const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')

export default function (userAppPath) {
  const windowAdapter = new FileSync(userAppPath)
  const windowSettings = low(windowAdapter)
  windowSettings.defaults({
    windowState: {
      height: 800,
      useContentSize: true,
      width: 400,
      show: false,
      minWidth: 300,
      x: undefined,
      y: undefined
    }
  }).write()

  return {
    getWindowState () {
      return windowSettings.get('windowState')
        .cloneDeep()
        .value()
    },
    updateWindowState (updateProp) {
      return windowSettings.get('windowState')
        .assign(updateProp)
        .write()
    }
  }
}

const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
const fs = require('fs')

export default function (userAppPath) {
  if (!fs.existsSync(userAppPath.split('window.json')[0])) {
    fs.mkdirSync(userAppPath.split('window.json')[0])
  }
  const windowAdapter = new FileSync(userAppPath)
  const windowSettings = low(windowAdapter)
  windowSettings.defaults({
    windowState: {
      height: 800,
      useContentSize: true,
      width: 600,
      show: false,
      minWidth: 300,
      x: undefined,
      y: undefined
    }
  }).write()

  return {
    getWindowState () {
      return windowSettings.get('windowState')
        .cloneDeep()
        .value()
    },
    updateWindowState (updateProp) {
      return windowSettings.get('windowState')
        .assign(updateProp)
        .write()
    }
  }
}

Fix bug which cause ENOENT window.json


var PROPERTY_SS_ID = 'SPREADSHEET_ID';
var RULE_KEY = 'MAILMAN_PROP_RULES';
var MAILMAN_VERSION = 'pre0.27';
var APP_NAME = 'Mailman';
var LAST_AUTH_KEY = 'MAILMAN_AUTH_EMAIL';

var SPREADSHEET_ID = null;

var PROPERTY_SS_ID = 'SPREADSHEET_ID';
var RULE_KEY = 'MAILMAN_PROP_RULES';
var MAILMAN_VERSION = 'pre0.28';
var APP_NAME = 'Mailman';
var LAST_AUTH_KEY = 'MAILMAN_AUTH_EMAIL';

var SPREADSHEET_ID = null;

Increment the version to pre0.28.


// Code goes here

/*
 * AngularJS invokes a function with the same name as the value of the `ng-controller` attribute on an HTML tag.
 *
 * AngularJS invokes this function with a single parameter, `$scope`, (yes, this is a valid JavaScript variable name).
 * The value of this parameter is the JavaScript scope (environment) used when evaluating AngularJS binding expressions
 * in the scope of the tag containing the `ng-controller="MainController"` attribute.
 *
 * This commit investigates using an anonymous function for the controller.
 */
angular.module('first-controller', []).controller('MainController', ['$scope', function($scope) {
	$scope.message = "Hello, Angular World!";
}]);

// Code goes here

/*
 * AngularJS invokes a function with the same name as the value of the `ng-controller` attribute on an HTML tag.
 *
 * AngularJS invokes this function with a single parameter, `$scope`, (yes, this is a valid JavaScript variable name).
 * The value of this parameter is the JavaScript scope (environment) used when evaluating AngularJS binding expressions
 * in the scope of the tag containing the `ng-controller="MainController"` attribute.
 */
var MainController = function($scope) {
	$scope.message = "Hello, Angular World!";
}

MainController.inject = ['$scope'];
angular.module('first-controller', []).controller('MainController', MainController);

/*
 * Eureka! It works. (Finally.)
 */

Revert "One can use an anonymous function"