/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#!/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  // connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  // migrate: 'alter'

};

/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#!/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  migrate: 'alter'

};

Use LocalDisk as data storage


import Ember from 'ember';

const {
  Route,
  RSVP
} = Ember;

export default Route.extend({

  deactivate() {
    this.store.peekAll('team-member').forEach((team) => {
      if (team.get('isNew')) {
        this.store.deleteRecord(team);
      }
    });
  },

  actions: {
    assignMember(member) {
      const flashMessages = Ember.get(this, 'flashMessages');

      return member.save().then(() => {
        this.currentModel.get('teamMembers').addObject(member);

        flashMessages.success(member.get('name')+' has been assigned to the team');
      }).catch(() => {
        member.rollback();

        flashMessages.danger('Unable to assign member to the team');
      });
    },

    removeMember(member) {
      const flashMessages = Ember.get(this, 'flashMessages');

      return member.destroyRecord().then(() => {
        flashMessages.success(member.get('name')+' has been removed from the team');
      }).catch(() => {
        member.rollback();

        flashMessages.danger('Unable to remove member from the team');
      });
    }
  },

  model: function (params) {
    const store = this.store;
    const teamId = params.id;
    const tournament = this.modelFor('tournament');

    return RSVP.hash({
      team: store.find('team', teamId),
      matches: store.query('match', {tournamentId: tournament.get('id'), teamId}),
      teamMembers: store.query('team-member', {teamId})
    }).then((hash) => {
      hash.team.set('teamMembers', hash.teamMembers);

      return hash;
    });
  }
});

import Ember from 'ember';

const {
  Route,
  RSVP
} = Ember;

export default Route.extend({

  deactivate() {
    this.store.peekAll('team-member').forEach((team) => {
      if (team.get('isNew')) {
        this.store.deleteRecord(team);
      }
    });
  },

  actions: {
    assignMember(member) {
      const flashMessages = Ember.get(this, 'flashMessages');

      return member.save().then(() => {
        flashMessages.success(member.get('name')+' has been assigned to the team');
      }).catch(() => {
        member.rollback();

        flashMessages.danger('Unable to assign member to the team');
      });
    },

    removeMember(member) {
      const flashMessages = Ember.get(this, 'flashMessages');

      return member.destroyRecord().then(() => {
        flashMessages.success(member.get('name')+' has been removed from the team');
      }).catch(() => {
        member.rollback();

        flashMessages.danger('Unable to remove member from the team');
      });
    }
  },

  model: function (params) {
    const store = this.store;
    const teamId = params.id;
    const tournament = this.modelFor('tournament');

    return RSVP.hash({
      team: store.find('team', teamId),
      matches: store.query('match', {tournamentId: tournament.get('id'), teamId}),
      teamMembers: store.query('team-member', {teamId})
    }).then((hash) => {
      hash.team.set('teamMembers', hash.teamMembers);

      return hash;
    });
  }
});

Fix assign member to the team


// Heroku defines the environment variable PORT, and requires the binding address to be 0.0.0.0
var host = process.env.PORT ? '0.0.0.0' : '127.0.0.1';
var port = process.env.PORT || 9199;

var cors_proxy = require('cors-anywhere');
cors_proxy.createServer({
    //requireHeader: ['origin', 'x-requested-with'],
    removeHeaders: ['cookie', 'cookie2'],
	secure: 'false',
}).listen(port, host, function() {
    console.log('Running CORS Anywhere on ' + host + ':' + port);
});

// Heroku defines the environment variable PORT, and requires the binding address to be 0.0.0.0
var host = '0.0.0.0'; //'127.0.0.1'; //process.env.PORT ? '0.0.0.0' : '127.0.0.1';
var port = process.env.PORT || 9199;

var cors_proxy = require('cors-anywhere');
cors_proxy.createServer({
    //requireHeader: ['origin', 'x-requested-with'],
    removeHeaders: ['cookie', 'cookie2'],
	secure: 'false',
}).listen(port, host, function() {
    console.log('Running CORS Anywhere on ' + host + ':' + port);
});

Update cors port to bind to default addr


'use strict';

app.factory('locationStorageService', function() {
	var STORAGE_ID = 'locations-angularjs';

	// local storage
	if (!window.localStorage) {

		return {
			get: function() {
				return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
			},

			set: function(locations) {
				localStorage.setItem(STORAGE_ID, JSON.stringify(locations));
			}
		};

	}

	// ajax storage

	// return {
	// 	get: function() {
	// 		alert('i was retrieved from server');
	// 	},
	// 	set: function(value) {
	// 		alert('im storing my shiz on the server');
	// 	}
	// }
});

'use strict';

app.factory('locationStorageService', function() {
	var STORAGE_ID = 'locations-angularjs';

	return {
		get: function() {
			return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
		},

		set: function(locations) {
			localStorage.setItem(STORAGE_ID, JSON.stringify(locations));
		}
	};
});

Revert "rough demo of multiple storage service solution"


var express = require('express');

var app = express();

app.get('/', function(request, response) {
    response.sendfile(__dirname + '/index.html');
});

app.configure(function() {
    app.use('/css', express.static(__dirname + '/css'));
    app.use('/flash', express.static(__dirname + '/flash'));
    app.use('/img', express.static(__dirname + '/img'));
    app.use('/js', express.static(__dirname + '/js'));
    app.use('/lib', express.static(__dirname + '/lib'));
    app.use('/videos', express.static(__dirname + '/videos'));
    app.use('/favicon.ico', express.static(__dirname + '/favicon.ico'));
});

var port = process.env.PORT || 8000;
app.listen(port, function() {
    console.log('Listening on ' + port);
});

var express = require('express');

var app = express();

app.get('/', function(request, response) {
    response.sendfile(__dirname + '/index.html');
});

app.configure(function() {
    app.use('/css', express.static(__dirname + '/css'));
    app.use('/flash', express.static(__dirname + '/flash'));
    app.use('/img', express.static(__dirname + '/img'));
    app.use('/js', express.static(__dirname + '/js'));
    app.use('/lib', express.static(__dirname + '/lib'));
    app.use('/plugin', express.static(__dirname + '/plugin'));
    app.use('/videos', express.static(__dirname + '/videos'));
    app.use('/favicon.ico', express.static(__dirname + '/favicon.ico'));
});

var port = process.env.PORT || 8000;
app.listen(port, function() {
    console.log('Listening on ' + port);
});

Fix for plugins directory not being available


angular.module('MyApp', ['ngResource', 'ngMessages', 'ngRoute', 'ngAuth', 'mgcrea.ngStrap'])
  .config(function($routeProvider, AuthProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
      })
      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      })
      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl'
      })
      .when('/logout', {
        template: null,
        controller: 'LogoutCtrl'
      })
      .when('/protected', {
        templateUrl: 'views/protected.html',
        controller: 'ProtectedCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });

    AuthProvider.configure({
      apiUrl: '/api'
    });
  });

angular.module('MyApp', ['ngResource', 'ngMessages', 'ngRoute', 'ngAuth', 'mgcrea.ngStrap'])
  .config(function($routeProvider, AuthProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/home.html',
        controller: 'HomeCtrl'
      })
      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      })
      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl'
      })
      .when('/logout', {
        template: null,
        controller: 'LogoutCtrl'
      })
      .when('/protected', {
        templateUrl: 'views/protected.html',
        controller: 'ProtectedCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });

    AuthProvider.configure({
      apiUrl: '/api',
      facebook: {
        clientId: '',
        redirectUri: '',
        scopes: []
      },
      hackerSchool: {
        clientId: '',
        redirectUri: '',
        scopes: [],
        verificationEndpoint: '',
        authorizationEndpoint: ''
      }
    });
  });

Add api example usage inside configure method


// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
var vscode = require('vscode');

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "quick-pick-related-files" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with  registerCommand
    // The commandId parameter must match the command field in package.json
    var disposable = vscode.commands.registerCommand('extension.sayHello', function () {
        // The code you place here will be executed every time your command is executed

        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World!');
    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;

const vscode = require('vscode');

function quickOpen() {
  const { fileName } = vscode.window.activeTextEditor.document;
  const fileNameParts = fileName.split('/');
  const prefilledText = fileNameParts[fileNameParts.length - 1];
  vscode.commands.executeCommand('workbench.action.quickOpen', prefilledText);
}

function activate(context) {
  context.subscriptions.push(vscode.commands.registerCommand('extension.sayHello', quickOpen));
}

module.exports.activate = activate;

Create basic proof of concept


export class UnknownCharError extends Error {
    constructor(unknownChar) {
        super(`Character '${unknownChar}' is not a part of the alphabet.`)
    }
}

export class UnknownStateError extends Error {
    constructor(stateName) {
        super(`State '${stateName}' doesn't exist in the automata.`)
    }
}

export class DeterminismError extends Error {
    constructor(state, a) {
        super(`State '${state}' already has a transition with character '${a}'.`)
    }
}

export class NoInitialStateError extends Error {
    constructor() {
        super('No initial state has been set.')
    }
}

export class DuplicateStateError extends Error {
    constructor(name) {
        super(`State '${name}' already exists.`)
    }
}

export class DuplicateTransitionError extends Error {
    constructor({ from, a, to }) {
        super(`A transition from state '${from}' with char '${a}' to state '${to}' already exists.`)
    }
}

export class NoFinalStatesError extends Error {
    constructor() {
        super('No final states have been defined.')
    }
}

export class InvalidPDATransition extends Error {
    constructor(str) {
        super(`Invalid transition symbol '${str}'.`)
    }
}

Add error for invalid pda transitions


window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

(function (global, GOVUK) {
  'use strict'

  var $ = global.jQuery

  GOVUK.Modules.TrackBrexitQaChoices = function () {
    this.start = function (element) {
      track(element)
    }

    function track (element) {
      element.on('submit', function (event) {
        var $checkedOption, eventLabel, options
        var $submittedForm = $(event.target)
        var $checkedOptions = $submittedForm.find('input:checked')
        var questionKey = $submittedForm.data('question-key')

        if ($checkedOptions.length) {
          $checkedOptions.each(function (index) {
            $checkedOption = $(this)
            var checkedOptionId = $checkedOption.attr('id')
            var checkedOptionLabel = $submittedForm.find('label[for="' + checkedOptionId + '"]').text().trim()
            eventLabel = checkedOptionLabel.length
              ? checkedOptionLabel
              : $checkedOption.val()

            options = { transport: 'beacon', label: eventLabel }

            GOVUK.SearchAnalytics.trackEvent('brexit-checker-qa', questionKey, options)
          })
        } else {
          // Skipped questions
          options = { transport: 'beacon', label: 'no choice' }

          GOVUK.SearchAnalytics.trackEvent('brexit-checker-qa', questionKey, options)
        }
      })
    }
  }
})(window, window.GOVUK)

window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

(function (global, GOVUK) {
  'use strict'

  GOVUK.Modules.TrackBrexitQaChoices = function () {
    this.start = function (element) {
      track(element)
    }

    function track (element) {
      element.on('submit', function (event) {
        var $checkedOption, eventLabel, options
        var $submittedForm = event.target
        var $checkedOptions = $submittedForm.querySelectorAll('input:checked')
        var questionKey = $submittedForm.data('question-key')

        if ($checkedOptions.length) {
          $checkedOptions.each(function (index) {
            $checkedOption = $(this)
            var checkedOptionId = $checkedOption.attr('id')
            var checkedOptionLabel = $submittedForm.find('label[for="' + checkedOptionId + '"]').text().trim()
            eventLabel = checkedOptionLabel.length
              ? checkedOptionLabel
              : $checkedOption.val()

            options = { transport: 'beacon', label: eventLabel }

            GOVUK.SearchAnalytics.trackEvent('brexit-checker-qa', questionKey, options)
          })
        } else {
          // Skipped questions
          options = { transport: 'beacon', label: 'no choice' }

          GOVUK.SearchAnalytics.trackEvent('brexit-checker-qa', questionKey, options)
        }
      })
    }
  }
})(window, window.GOVUK)

Change jquery `find` to `querySelectorAll`


module.exports = function(grunt) {

    //grunt-sass
    grunt.config('sass', {
        options: {
            outputStyle: 'expanded',
            //includePaths: ['<%= config.scss.includePaths  %>'],
            imagePath: '../<%= config.image.dir  %>'
        },
        dist: {
            files: {
                '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>': '<%= config.scss.dir  %>/<%= config.scss.file  %>'
            }
        }
    });

    //grunt-autoprefixer
    grunt.config('autoprefixer', {
        options: {
            browsers: ['> 1%', 'last 2 versions', 'ie 8', 'ie 9', 'ie 10']
        },
        dist: {
            files: {
                '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>': '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>'
            }
        }
    });

    //grunt-contrib-cssmin
    grunt.config('cssmin', {
        target: {
            src: '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>',
            dest: '<%= config.css.dir  %>/<%= config.css.file  %>'
        }
    });

    //grunt-contrib-csslint
    grunt.config('csslint', {
        options: {
            csslintrc: 'grunt/.csslintrc'
        },
        strict: {
            src: ['<%= config.css.dir  %>/<%= config.css.file  %>']
        }
    });
};

module.exports = function(grunt) {

    //grunt-sass
    grunt.config('sass', {
        options: {
            outputStyle: 'expanded',
            //includePaths: ['<%= config.scss.includePaths  %>'],
            imagePath: '../<%= config.image.dir  %>',
            sourceComments: true
        },
        dist: {
            files: {
                '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>': '<%= config.scss.dir  %>/<%= config.scss.file  %>'
            }
        }
    });

    //grunt-autoprefixer
    grunt.config('autoprefixer', {
        options: {
            browsers: ['> 1%', 'last 2 versions', 'ie 8', 'ie 9', 'ie 10']
        },
        dist: {
            files: {
                '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>': '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>'
            }
        }
    });

    //grunt-contrib-cssmin
    grunt.config('cssmin', {
        target: {
            src: '<%= config.css.dir  %>/<%= config.css.fileExpanded  %>',
            dest: '<%= config.css.dir  %>/<%= config.css.file  %>'
        }
    });

    //grunt-contrib-csslint
    grunt.config('csslint', {
        options: {
            csslintrc: 'grunt/.csslintrc'
        },
        strict: {
            src: ['<%= config.css.dir  %>/<%= config.css.file  %>']
        }
    });
};

Enable debug comments in grunt-sass output file


/**
 * The authorization token with which calls to the API are made.
 * @type {String}
 */
let token = '';

/**
 * Authorize via OAuth 2.0 for Dropbox API calls.
 *
 * @parameter {string} clientId your app's key
 * @parameter {string} redirectUri the uri where the user should be redirected
 * to, after authorization has completed
 * @return {function} a promise that resolves or fails both with the returned
 * HTTP status code
 */
const authorize = (clientId, redirectUri = '') => {
  return new Promise((resolve, reject) => {
    reject('not implemented yet');
  });
};

/**
 * Set the token that is used for all Dropbox API calls to the given value.
 * @param  {string} t The new token value.
 */
const setToken = (t) => {
  if (typeof t !== 'string') {
    throw new Error('invalid argument ' + t + ' (expected: string)');
  }
  token = t;
};

module.exports = {
  authorize,
  setToken
};

/**
 * The authorization token with which calls to the API are made.
 * @type {String}
 */
let token = '';

const AUTHORIZE_ENDPOINT = 'https://www.dropbox.com/oauth2/authorize';

const getAuthorizationUrl = (clientId) => {
  return AUTHORIZE_ENDPOINT + '?' +
    'response_type=token' +
    'client_id=' + clientId;
};

/**
 * Authorize via OAuth 2.0 for Dropbox API calls.
 *
 * @parameter {string} clientId your app's key
 * @parameter {string} redirectUri the uri where the user should be redirected
 * to, after authorization has completed
 * @return {function} a promise that resolves or fails both with the returned
 * HTTP status code
 */
const authorize = (clientId, redirectUri = '') => {
  return new Promise((resolve, reject) => {
    reject('Not implemented yet, please obtain a token manually by calling ' +
      getAuthorizationUrl(clientId));
  });
};

/**
 * Set the token that is used for all Dropbox API calls to the given value.
 * @param  {string} t The new token value.
 */
const setToken = (t) => {
  if (typeof t !== 'string') {
    throw new Error('invalid argument ' + t + ' (expected: string)');
  }
  token = t;
};

module.exports = {
  authorize,
  setToken
};

Return the authorization url when `authorize` fails


import React, {Component, PropTypes} from 'react'
import {Scrollbars} from 'react-custom-scrollbars'

class FancyScrollbox extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    const {children, className} = this.props

    return (
      <Scrollbars
        className={`fancy-scroll--container ${className}`}
        autoHide={true}
        autoHideTimeout={1000}
        autoHideDuration={250}
        renderTrackHorizontal={props => <div {...props} className="fancy-scroll--track-h"/>}
        renderTrackVertical={props => <div {...props} className="fancy-scroll--track-v"/>}
        renderThumbHorizontal={props => <div {...props} className="fancy-scroll--thumb-h"/>}
        renderThumbVertical={props => <div {...props} className="fancy-scroll--thumb-v"/>}
      >
        {children}
      </Scrollbars>
    )
  }
}

const {node, string} = PropTypes

FancyScrollbox.propTypes = {
  children: node.isRequired,
  className: string.isRequired,
}

export default FancyScrollbox

import React, {Component, PropTypes} from 'react'
import classnames from 'classnames'
import {Scrollbars} from 'react-custom-scrollbars'

class FancyScrollbox extends Component {
  constructor(props) {
    super(props)
  }

  static defaultProps = {
    autoHide: true,
  }

  render() {
    const {autoHide, children, className} = this.props

    return (
      <Scrollbars
        className={classnames('fancy-scroll--container', {[className]: className})}
        autoHide={autoHide}
        autoHideTimeout={1000}
        autoHideDuration={250}
        renderTrackHorizontal={props => <div {...props} className="fancy-scroll--track-h"/>}
        renderTrackVertical={props => <div {...props} className="fancy-scroll--track-v"/>}
        renderThumbHorizontal={props => <div {...props} className="fancy-scroll--thumb-h"/>}
        renderThumbVertical={props => <div {...props} className="fancy-scroll--thumb-v"/>}
      >
        {children}
      </Scrollbars>
    )
  }
}

const {bool, node, string} = PropTypes

FancyScrollbox.propTypes = {
  children: node.isRequired,
  className: string,
  autoHide: bool,
}

export default FancyScrollbox

Add autoHide prop to pass down


/* @flow */
import React from 'react'
import MessageView from '../Message'
import type { Message } from 'types'
import styles from './style.css'

type Props = {
  messages: Array<Message>
}

export default class MessageList extends React.Component<*, Props, *> {
  constructor (props: Props) {
    super(props)
    // Every minute, update chat timestamps
    setInterval(() => this.forceUpdate(), 60000)
  }

  componentDidUpdate () {
    this.refs.scroll.scrollTop += 10000
  }

  render () {
    return

      {this.props.messages.map(message =>
        <MessageView key={message.key} message={message} />
      )}

  }
}

/* @flow */
import React from 'react'
import MessageView from '../Message'
import type { Message } from 'types'
import styles from './style.css'

type Props = {
  messages: Array<Message>
}

export default class MessageList extends React.Component<*, Props, *> {
  timer: Object

  constructor (props: Props) {
    super(props)
  }

  componentDidMount () {
    // Every minute, update chat timestamps
    this.timer = setInterval(() => this.forceUpdate(), 60000)
  }

  componentDidUpdate () {
    this.refs.scroll.scrollTop += 10000
  }

  componentWillUnmount () {
    clearInterval(this.timer)
  }

  render () {
    return

      {this.props.messages.map(message =>
        <MessageView key={message.key} message={message} />
      )}

  }
}

Fix forceUpdate sometimes called on unmounted component.


function doClick(e) {
    alert($.label.text);
}

$.index.open();

// var FileLoader = require("file_loader");

function urlFromSelection(selection) {
  return "http://localhost:3000/" + selection.toLowerCase() + ".jpg";
}

function callbackImageChange(e) {
  var url = urlFromSelection(e.selectedValue[0]);
  Ti.API.info("Requested URL: " + url);
  $.callbackImage.image = url;
}

function promiseImageChange(e) {
  var url = urlFromSelection(e.selectedValue[0]);
  Ti.API.info("Requested URL: " + url);
  $.promiseImage.image = url;
}

$.callbackPicker.addEventListener("change", callbackImageChange);
$.promisePicker.addEventListener("change", promiseImageChange);

$.index.open();

Add ability to change image


var path = require("path")

const PROJECT_ROOT = path.resolve(__dirname)

module.exports = {
  entry: [
    "./scripts/dash-spinner/bar.js",
    "./scripts/dash-spinner/foo.js",
    "./scripts/dash-spinner/foo.coffee"
  ],
  output: {
    path: "./dist",
    filename: "new-spinner.js"
  },
  module: {
    loaders:[
      {
        include: [
          path.join(PROJECT_ROOT, "scripts"),
          path.join(PROJECT_ROOT, "spec")
        ],
        loader: "babel-loader",
        test: /\.js$/
      }, {
        include: [
          path.join(PROJECT_ROOT, "scripts"),
          path.join(PROJECT_ROOT, "spec")
        ],
        loader: "babel!coffee",
        test: /\.coffee$/
      }
    ]
  },
  resolve: {
    alias: {
      "dash_spinner": path.join(PROJECT_ROOT, "scripts", "dash-spinner")
    }
  }
}

var webpack = require("webpack");
var path = require("path")

const PROJECT_ROOT = path.resolve(__dirname)

module.exports = {
  entry: [
    "./bower_components/underscore/underscore.js",
    "./scripts/dash-spinner/bar.js",
    "./scripts/dash-spinner/foo.js",
    "./scripts/dash-spinner/foo.coffee"
  ],
  output: {
    path: "./dist",
    filename: "new-spinner.js"
  },
  module: {
    loaders:[
      {
        include: [
          path.join(PROJECT_ROOT, "scripts"),
          path.join(PROJECT_ROOT, "spec")
        ],
        loader: "babel-loader",
        test: /\.js$/
      }, {
        include: [
          path.join(PROJECT_ROOT, "scripts"),
          path.join(PROJECT_ROOT, "spec")
        ],
        loader: "babel!coffee",
        test: /\.coffee$/
      }
    ]
  },
  plugins: [
    new webpack.ResolverPlugin(
      new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"])
    )
  ],
  resolve: {
    alias: {
      "dash_spinner": path.join(PROJECT_ROOT, "scripts", "dash-spinner")
    }
  }
}

Add support for Bower to Webpack


'use strict';

// MODULES //

var r = require( 'repl' );
var getKeys = require( 'object-keys' ).shim();
var namespace = require( '@stdlib/namespace' );
var defaults = require( './defaults.js' );

// REPL //

/**
* Starts a REPL environment.
*
* @returns {Object} REPL server
*
* @example
* repl();
*/
function repl() {
	var context;
	var server;
	var opts;
	var keys;
	var key;
	var i;

	opts = defaults();
	server = r.start( opts );

	// Create a REPL context:
	context = namespace( {} );

	// Bind all context values to the REPL server...
	keys = getKeys( context );
	for ( i = 0; i < keys.length; i++ ) {
		key = keys[ i ];
		server.context[ key ] = context[ key ];
	}
	return server;
} // end FUNCTION repl()

// EXPORTS //

module.exports = repl;

'use strict';

// MODULES //

var r = require( 'repl' );
var getKeys = require( 'object-keys' ).shim();
var namespace = require( '@stdlib/namespace' );
var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
var defaults = require( './defaults.js' );

// REPL //

/**
* Starts a REPL environment.
*
* @returns {Object} REPL server
*
* @example
* repl();
*/
function repl() {
	var context;
	var server;
	var opts;
	var keys;
	var key;
	var i;

	opts = defaults();
	server = r.start( opts );

	// Create a REPL context:
	context = namespace( {} );

	// Bind all context values to the REPL server...
	keys = getKeys( context );
	for ( i = 0; i < keys.length; i++ ) {
		key = keys[ i ];
		setReadOnly( server.context, key, context[ key ] );
	}
	return server;
} // end FUNCTION repl()

// EXPORTS //

module.exports = repl;

Make REPL context properties read-only


/* ============================================================
   tableCopy plugin
   Copy the selected table to the clipboard
   Returns a promise, which resolves true if the copy
   succeeds, false if it is cancelled.
   To Do: clean up hidden table cells, links, etc.
*/
;(function() {
    "use strict";
    jQuery.fn.tableCopy = function() {
        var deferred = new jQuery.Deferred();
        var clone = this.clone();
        var wrapper = $('<div>');
        wrapper.append(clone);
        var html = wrapper.html();

        return qcode.copy(html);
    }
})();

/* ============================================================
   tableCopy plugin
   Copy the selected table to the clipboard
   Returns a promise, which resolves true if the copy
   succeeds, false if it is cancelled.
   To Do: clean up hidden table cells, links, etc.
*/
;(function() {
    "use strict";
    jQuery.fn.tableCopy = function(filter) {
        var deferred = new jQuery.Deferred();
        var clone = this.clone();
        if ( typeof filter === "function" ) {
            clone = filter(clone);
        }
        var wrapper = $('<div>');
        wrapper.append(clone);
        var html = wrapper.html();

        return qcode.copy(html);
    }
})();

Support filtering table contents before copying


/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule Config
 */
'use strict';

export default {
  api: {
    // host: 'https://exp.host',
    host: 'https://ed56a018.ngrok.io',
  },
};

/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule Config
 */
'use strict';

export default {
  api: {
    host: 'https://exp.host',
    // host: 'https://ed56a018.ngrok.io',
  },
};

Fix exponent/js tests, fix up error message in PushRouter


app.controller('DomainsCtrl', ['$scope', 'Domains',
    function($scope, Domains) {
        $scope.domains = Domains.query();
    }
]);

app.controller('DomainsCtrl', ['$scope', '$log', 'Domains',
    function($scope, $log, Domains) {
        $scope.domains = Domains.query();
    }
]);

Make $log available within our DomainsCtrl


// ### Part of the [Rosy Framework](http://github.com/ff0000/rosy)
/* site.js */

// ## The RED Namespace
var RED = RED || {};

// ## Local Namespace
// Example Site object, controls global functionality and instantiates the Example Default Page.
// You should replace the namespace "Example" with your own Site namespace, this is only an example.
var Example = Example || {};

// Site shell object

// Model manager and shell manager
RED.SITE = $.extend(true, Example, RED, (function () {

	return {

		models : {},

		init : function () {
			// Create the site shell
			this.models.Shell = new Example.Shell();

			// Wait for DOMContentLoaded
			$(document).ready(this.onReady.call(this));
		},

		onReady : function () {
			var body = $("body"),
			    // Use `attr("data-page-class")` if < jQuery 1.6
				pageClass = body.data("pageClass");

			// creates `Page()` based on `<div data-page-class="Home">`
			if (pageClass && typeof Example.Page[pageClass] === "function") {
				this.models[pageClass] = new Example.Page[pageClass]();
			// defaults to `Example.Page();`
			} else {
				this.models.page = new Example.Page();
			}
		}
	};
}()));

// ### Part of the [Rosy Framework](http://github.com/ff0000/rosy)
/* site.js */

// ## The RED Namespace
var RED = RED || {};

// ## Local Namespace
// Example Site object, controls global functionality and instantiates the Example Default Page.
// You should replace the namespace "Example" with your own Site namespace, this is only an example.
var Example = Example || {};

// Site shell object

// Model manager and shell manager
RED.SITE = $.extend(true, Example, RED, (function () {

	return {

		models : {},

		init : function () {
			// Create the site shell
			this.models.Shell = new Example.Shell();

			// Wait for DOMContentLoaded
			$(document).ready(this.onReady.call(this));
		},

		createModel : function (page, vars) {
			var master = Example.Page,
			    model = (page && typeof master[page] === "function" ? master[page] : master);

			return this.models[page || "page"] = new model(vars);
		},

		getModel : function (page) {
			return this.models[page];
		},

		getModels : function () {
			return this.models;
		},

		onReady : function () {
			var body = $("body"),
			    // Use `attr("data-page-class")` if < jQuery 1.6
				pageClass = body.data("pageClass");

			// creates `Page()` based on `<div data-page-class="Home">`
			this.createModel(pageClass);
		}
	};
}()));

Move functionality into a createModel method. Allows for model instantiation outside of this static class. Creating getters for model retrieval.


import React from 'react';
import AutoLinkText from 'react-autolink-text';

React.render(<AutoLinkText />, document.getElementById('app'));

import React from 'react';
import AutoLinkText from 'react-autolink-text';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ''};
  }

  _onTextChange(e) {
    this.setState({text: e.target.value});
  }

  render() {
    return (

        <AutoLinkText text={this.state.text} maxLength={10} />

        <textarea value={this.state.text} onChange={this._onTextChange.bind(this)} />

    );
  }
}

React.render(<App />, document.getElementById('app'));

Update example to include a text area to input text.


exports.conf = {
  "tasks": {
    "all_docs": function (util, doc) {
      console.log(doc);
    }
  }
};

exports.conf = {
  "tasks": {
    "all_docs": function (util, doc) {
      util.log(doc);
    }
  }
};

Use util.log for logging example.


define(function() {
    'use strict';

    /**
     * @class
     */
    function CurrencyParser() { }

    function ParseError() { }

    var penceOnlyParser = function (str) {
        var found = str.match(/^(\d+)p?$/);
        if (found === null)
            throw new ParseError();
        else
            return parseInt(found[1]);
    };

    var poundsOnlyParser = function (str) {
        var found = str.match(/^£(\d+)p?$/);
        if (found === null)
            throw new ParseError();
        else
            return parseInt(found[1] * 100);
    };

    var poundsAndPenceParser = function (str) {
        var found = str.match(/^£?(\d+(\.\d*))p?$/);
        if (found === null)
            throw new ParseError();
        else
            return Math.round(parseFloat(found[1]) * 100);
    };

    var parsers = [
        penceOnlyParser,
        poundsOnlyParser,
        poundsAndPenceParser
    ];

    /**
     * Parse the supplied string as a number of British pence
     * @param {String} subject - The string to be parsed
     * @returns {Number} The value of the string in pence
     */
    CurrencyParser.prototype.parseString = function(str) {
        for (var i = 0; i < parsers.length; i++) {
            try {
                var parser = parsers[i];
                return parser(str)
            } catch (e) {
                continue;
            }
        }

        // Return undefined if no parser succeeded
    };

    // Return constructor
    return CurrencyParser;
});

define(function() {
    'use strict';

    /**
     * @class
     */
    function CurrencyParser() { }

    /**
     * Array of recognised currency formats, each with a regex pattern
     * and a parser function which receives the first matched group.
     */
    var formats = [
        {
            // Just pence, with optional 'p'
            pattern: /^(\d+)p?$/,
            parser: function (str) { return parseInt(str); }
        },
        {
            // Just pounds, with '£' and optional 'p'
            pattern: /^£(\d+)p?$/,
            parser: function (str) { return parseInt(str) * 100; }
        },
        {
            // Pounds and pence, with decimal point, and optional '£' and/or 'p'
            pattern: /^£?(\d+(\.\d*))p?$/,
            parser: function (str) { return Math.round(parseFloat(str) * 100); }
        },
    ];

    /**
     * Parse the specified string and return a number of pence Sterling
     * @param {String} input - The string to be parsed
     * @returns {Number} Value in pence, or undefined if parsing failed
     */
    CurrencyParser.prototype.parseString = function(input) {
        for (var i = 0; i < formats.length; i++) {
            var format = formats[i];
            var matches = input.match(format.pattern);
            if (matches)
                return format.parser(matches[1]);
        }

        // Return undefined if no format was matched
    };

    // Return constructor
    return CurrencyParser;
});

Refactor parser again to remove use of exceptions


/**
 * Module Dependencies.
 */

var debug = require('debug')('metalsmith-static');
var ncp   = require('ncp').ncp;
var path  = require('path');
var fsextra    = require('fs-extra');

/**
 * Module Exports
 */

module.exports = function(assets) {
  assets = assets || [{}];
  assets = !Array.isArray(assets) ? [ assets ] : assets;

  return function(files, metalsmith, done) {
    assets.forEach(function(opts) {
      var relSrc = opts.src || 'public';
      var relDest = opts.dest || 'public';
      var createDest = opts.createDest || true;

      var src = path.join(metalsmith.dir, relSrc);
      var dst = path.join(metalsmith.dir, metalsmith._dest, relDest);

      if (createDest) {
        var dir = path.dirname(dst);

        debug('creating: %s', dir);
        fsextra.mkdirpSync(dir);
      }

      ncp(src, dst, function(err) {
        if (err) return done(err);
        done();
      });
    });
  };
};

/**
 * Module Dependencies.
 */

var debug = require('debug')('metalsmith-static');
var ncp   = require('ncp').ncp;
var path  = require('path');
var fsextra    = require('fs-extra');

/**
 * Module Exports
 */

module.exports = function(assets) {
  assets = assets || [{}];
  assets = !Array.isArray(assets) ? [ assets ] : assets;

  return function(files, metalsmith, done) {
    assets.forEach(function(opts) {
      var relSrc = opts.src || 'public';
      var relDest = opts.dest || 'public';
      var createDest = opts.createDest || true;

      var src = path.join(metalsmith.dir, relSrc);
      var dst = path.join(metalsmith.dir, metalsmith._dest, relDest);

      if (createDest) {
        var dir = path.dirname(dst);

        debug('creating: %s', dir);
        fsextra.mkdirpSync(dir);
      }

      ncp(src, dst, function(err) {
        if (err) return done(err);
      });
    });

    done();
  };
};

Call done() only when really done.


goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.layer.Tile');
goog.require('ol.source.OSM');

var map = new ol.Map({
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  renderer: ol.RendererHint.CANVAS,
  target: 'map',
  view: new ol.View2D({
    center: [0, 0],
    zoom: 2
  })
});

var exportPNGElement = document.getElementById('export-png');

if ('download' in exportPNGElement) {
  exportPNGElement.addEventListener('click', function(e) {
    e.target.href = map.getRenderer().getCanvas().toDataURL('image/png');
  }, false);
} else {
  var info = document.getElementById('no-download');
  /**
   * display error message
   */
  info.style.display = '';
}

goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
goog.require('ol.layer.Tile');
goog.require('ol.source.OSM');

var map = new ol.Map({
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  renderer: ol.RendererHint.CANVAS,
  target: 'map',
  view: new ol.View2D({
    center: [0, 0],
    zoom: 2
  })
});

var exportPNGElement = document.getElementById('export-png');

if ('download' in exportPNGElement) {
  exportPNGElement.addEventListener('click', function(e) {
    map.once('postcompose', function(event) {
      var canvas = event.getContext().canvas;
      exportPNGElement.href = canvas.toDataURL('image/png');
    });
    map.render();
  }, false);
} else {
  var info = document.getElementById('no-download');
  /**
   * display error message
   */
  info.style.display = '';
}

Use 'postcompose' event to export the map as png image


// UI is a singleton with events
function UI(){};
UI.prototype = new EventEmitter();
UI = new UI();

// fires a custom event after the user has finished resizing the window
UI.once('init', function(){
	var UI = this
	  , settle_time = 100 // ms to wait before firing our event
	  , resize_timer;

	function emitResized(){
		UI.emit('resized');
		resize_timer = null;
	}

	$(window).resize(function(){
		if(resize_timer) clearTimeout(resize_timer);
		resize_timer = setTimeout(emitResized, settle_time);
	});
});

UI.once('init', function(){
	if(!readonly){
		IO.socket.on('connect', function(){
			$('#message').attr('disabled', false).focus();
		});
	}
});

UI.update_connection_status = function(text){
	$('#connection_status').text(text);
};

// UI is a singleton with events
function UI(){};
UI.prototype = new EventEmitter();
UI = new UI();

UI.once('init', function(){
	if(!readonly){
		IO.socket.on('connect', function(){
			$('#message').attr('disabled', false).focus();
		});
	}
});

UI.update_connection_status = function(text){
	$('#connection_status').text(text);
};

Remove remnants of JS layout resizing code


/**
 * garp.welcomepanel.js
 *
 * Convenience Class, creates a panel with a message from HTML markup in de view.
 *
 * @class Garp.WelcomePanel
 * @extends Panel
 * @author Peter
 */

Garp.WelcomePanel = function(cfg){
	this.html = Ext.getDom('welcome-panel-content').innerHTML;
	Garp.WelcomePanel.superclass.constructor.call(this, cfg);
};

Ext.extend(Garp.WelcomePanel, Ext.Container, {
	border: true
	/*,
	hideButtonArr: ['fileMenu', 'editMenu', 'viewMenu'],
	listeners: {
		'render': function(){
			Ext.each(this.hideButtonArr, function(item){
				Garp.toolbar[item].hide();
			});
			this.getEl().fadeIn({duration:2.2});
			if (Garp.viewport && Garp.viewport.formPanelCt) {
				Garp.viewport.formPanelCt.hide();
			}
		},
		'destroy': function(){
			Ext.each(this.hideButtonArr, function(item){
				Garp.toolbar[item].show();
			});
			if (Garp.viewport && Garp.viewport.formPanelCt) {
				Garp.viewport.formPanelCt.show();
			}
		}
	}*/
});

/**
 * garp.welcomepanel.js
 *
 * Convenience Class, creates a panel with a message from HTML markup in de view.
 *
 * @class Garp.WelcomePanel
 * @extends Panel
 * @author Peter
 */

Garp.WelcomePanel = function(cfg){
	this.html = Ext.getDom('welcome-panel-content').innerHTML;
	Garp.WelcomePanel.superclass.constructor.call(this, cfg);
};

Ext.extend(Garp.WelcomePanel, Ext.Container, {
	border: true,
	hideButtonArr: ['newButton', 'deleteButton', 'separator'],
	listeners: {
		'render': function () {
			Ext.each(this.hideButtonArr, function(item){
				Garp.toolbar[item].hide();
			});
		},
		'destroy': function () {
			Ext.each(this.hideButtonArr, function(item){
				Garp.toolbar[item].show();
			});
		}
	}
});

Hide new / delete button on welcome page


'use strict';

const GtfsGenerator = require('../lib/GtfsGenerator');

/* Invoke the generator based on environment variables. */
GtfsGenerator.fromEnvironmentVariables().generate('output_data');

'use strict';

const GtfsGenerator = require('../lib/GtfsGenerator');

/* Invoke the generator based on environment variables. */
var path = process.argv.length < 3 ? 'output_data' : process.argv[2];
GtfsGenerator.fromEnvironmentVariables().generate(path);

Allow output path to be changed when invoking with envir variables


var Observable = require("FuseJS/Observable");
var GeoLocation = require("FuseJS/GeoLocation");
var Model = require('Sanntidsappen/Model');

var search_string = Observable('');
var search_results = Observable();
var StopsList = require('Assets/Stops');

var stops = Observable();

search_string.onValueChanged(function (item) {
  if (item.length > 2) {
    search_results.replaceAll(StopsList.filter(function (stop) {
      return stop.name.toUpperCase().indexOf(search_string.value.toUpperCase()) > -1;
    }));
  }
  else {
    return search_results.clear();
  }
});

// GeoLocations
// Gets nearest stops using the devices current location
var continuousLocation = GeoLocation.observe('changed')//.map(JSON.stringify);

var intervalMs = 1000;
var desiredAccuracyInMeters = 20;

GeoLocation.startListening(intervalMs, desiredAccuracyInMeters);

continuousLocation.onValueChanged(function (val) {
  if (val && val.latitude && val.longitude) {
    stops = Model.GetNearestStops(val.latitude, val.longitude).inner();
  } else {
    stops.replaceAll(StopsList.slice(33, 50));
  }
});

module.exports = {
  search_string: search_string,
  search_results: search_results,
  stops: stops
}

var Observable = require("FuseJS/Observable");
var GeoLocation = require("FuseJS/GeoLocation");
var Model = require('Sanntidsappen/Model');
var StopsList = require('Assets/Stops');

var search_string = Observable('');
var search_results = Observable();
var distancePresent = Observable();
var stops = Observable();

search_string.onValueChanged(function (item) {
  distancePresent.value = false;
  if (item.length > 2) {
    search_results.replaceAll(StopsList.filter(function (stop) {
      return stop.name.toUpperCase().indexOf(search_string.value.toUpperCase()) > -1;
    }));
    stops.replaceAll(search_results)
  }
  else {
    search_results.clear();
  }
});

// GeoLocations
// Gets nearest stops using the devices current location
var continuousLocation = GeoLocation.observe('changed')

var intervalMs = 1000;
var desiredAccuracyInMeters = 20;

GeoLocation.startListening(intervalMs, desiredAccuracyInMeters);

continuousLocation.onValueChanged(function (val) {
  if (search_results.length === 0) {
    if (val && val.latitude && val.longitude) {
      stops = Model.GetNearestStops(val.latitude, val.longitude).inner();
      distancePresent.value = true;
    } else {
      stops.replaceAll(StopsList.slice(33, 50));
      distancePresent.value = false;
    }
  }
});

module.exports = {
  distancePresent,
  search_string: search_string,
  search_results: search_results,
  stops: stops
}

Add distance field present check


// @flow

import { h, render } from 'preact';
import { withProps } from './with-props';
import { withRender } from './with-render';
import { withUnique } from './with-unique';

export const withComponent = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  class extends withRender(withUnique(withProps(Base))) {
    _preactDom: Object;
    rendererCallback (shadowRoot: Node, renderCallback: () => Object) {
      this._preactDom = render(renderCallback(), shadowRoot, this._preactDom || shadowRoot.children[0]);
    }
  };

export const Component: Class<HTMLElement> = withComponent(typeof window === 'undefined' ? class {} : HTMLElement);
export { h };

// @flow

import { h, render } from "preact";
import { withProps } from "./with-props";
import { withRender } from "./with-render";
import { withUnique } from "./with-unique";

const withAll = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  withProps(withRender(withUnique(Base)));

const withPreact = (Base?: Class<any> = HTMLElement): Class<HTMLElement> =>
  class extends Base {
    _preactDom: Object;
    rendererCallback(shadowRoot: Node, renderCallback: () => Object) {
      this._preactDom = render(
        renderCallback(),
        shadowRoot,
        this._preactDom || shadowRoot.children[0]
      );
    }
  };

export const withComponent = withPreact;
export const Component: Class<HTMLElement> = withAll(
  withPreact(typeof window === "undefined" ? class {} : HTMLElement)
);
export { h };

Prepare to move preact renderer out.


/**
 * Js-coding-standards
 *
 * @author      Robert Rossmann <rr.rossmann@me.com>
 * @copyright   2016 STRV
 * @license     http://choosealicense.com/licenses/bsd-3-clause  BSD-3-Clause License
 */

'use strict'

module.exports = {

  extends: [
    './recommended.js',
    './accessibility.js',
  ],

  rules: {
    'operator-linebreak': 0,
    'require-jsdoc': 0,
  },

  plugins: [
    'react',
  ],

  // Configures the react plugin to treat some rules with regard to this specific React.js version
  settings: {
    react: {
      version: '15.5',
    },
  },
}

/**
 * Js-coding-standards
 *
 * @author      Robert Rossmann <rr.rossmann@me.com>
 * @copyright   2016 STRV
 * @license     http://choosealicense.com/licenses/bsd-3-clause  BSD-3-Clause License
 */

'use strict'

module.exports = {

  extends: [
    './recommended.js',
    './accessibility.js',
  ],

  rules: {
    'operator-linebreak': 0,
  },

  plugins: [
    'react',
  ],

  // Configures the react plugin to treat some rules with regard to this specific React.js version
  settings: {
    react: {
      version: '15.5',
    },
  },
}

Remove unused require-jsdoc override from react env


'use strict';

/* global Connector */

Connector.playerSelector = '#page_sidebar';

Connector.getArtist = function() {
	var artists = $.map($('.player-track-artist').children(), function(artist) {
		return $.text(artist);
	});
	return artists.join(', ');
};

Connector.trackSelector = '.player-track-title';

Connector.trackArtImageSelector = '.player-cover img';

Connector.isPlaying = function () {
	return $('.control').hasClass('control-pause');
};

'use strict';

/* global Connector */

Connector.playerSelector = '#page_sidebar';

Connector.getArtist = function() {
	var artists = $.map($('.player-track-artist').children(), function(artist) {
		return $.text(artist);
	});
	return artists.join(', ');
};

Connector.trackSelector = '.player-track-title';

Connector.currentTimeSelector = '.player-progress .progress-time';

Connector.durationSelector = '.player-progress .progress-length';

Connector.trackArtImageSelector = '.player-cover img';

Connector.isPlaying = function () {
	return $('.control').hasClass('control-pause');
};

Add current time/duration for Deezer connector


var cbus = {};

var xhr = function(url, callback) {
    var oReq = new XMLHttpRequest();
    oReq.onload = function(e){
        callback(this.responseText, e);
    };
    oReq.open("get", url, true);
    oReq.send();
};

var colonSeparateDuration = function(num) { // in seconds
    if (typeof num == "number" && !(Number.isNaN || isNaN)(num)) {
        var minutes = Math.floor(num / 60);
        var seconds = Math.floor(num % 60);
        return "" + minutes + ":" + zpad(seconds, 2);
    } else {
        return "--:--";
    }
};

var zpad = function pad(n, width, z) { // by user Pointy on SO: stackoverflow.com/a/10073788
    z = z || "0";
    n = n + "";
    return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
};

var mergeObjects = function(a, b){
    var result = {};

    for (var key in a) {
        result[key] = a[key];
    }
    for (var key in b) {
        result[key] = b[key];
    }

    return result;
};

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var cbus = {};

var xhr = function(url, callback) {
    var oReq = new XMLHttpRequest();
    oReq.onload = function(e){
        callback(this.responseText, e);
    };
    oReq.open("get", url, true);
    oReq.send();
};

var colonSeparateDuration = function(num) { // in seconds
    if (typeof num == "number" && !(Number.isNaN || isNaN)(num)) {
        var hours = Math.floor(num / 60 / 60);
        var minutes = Math.floor(num / 60) - hours * 60;
        var seconds = Math.floor(num % 60);
        return (hours !== 0 ? "" + hours + ":" : "") + zpad(minutes, 2) + ":" + zpad(seconds, 2);
    } else {
        return "--:--";
    }
};

var zpad = function pad(n, width, z) { // by user Pointy on SO: stackoverflow.com/a/10073788
    z = z || "0";
    n = n + "";
    return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
};

var mergeObjects = function(a, b){
    var result = {};

    for (var key in a) {
        result[key] = a[key];
    }
    for (var key in b) {
        result[key] = b[key];
    }

    return result;
};

window.AudioContext = window.AudioContext || window.webkitAudioContext;

Support hours place in colonSeparateDuration


App.Graphs = Ember.Object.extend({
  graph: function(emberId, entityName, entityType) {
    $.ajax({
      url: ((!localStorage.apiDomain) ? '' : '//' + localStorage.apiDomain) + '/graphs/getGraphs.php?entityType=' + entityType + '&entityName=' + entityName,
      type: 'GET',
      dataType: 'json',
      success: function (data, textStatus, jqXHR) {
        var numericArray = App.associativeToNumericArray(data);
        if (entityType == 'node') {
          App.Node.find(emberId).set('graphs', numericArray);
        } else if (entityType == 'vm') {
          return App.Vm.find(emberId).set('graphs', numericArray);
        }
      }
    });
  }
});

App.graphs = App.Graphs.create();

App.Graphs = Ember.Object.extend({
  graph: function(emberId, entityName, entityType) {
    hash = {
      url: ((!localStorage.apiDomain) ? '' : '//' + localStorage.apiDomain) + '/api/v1/graphs?entityType=' + entityType + '&entityName=' + entityName,
      type: 'GET',
      dataType: 'json',
      success: function (data, textStatus, jqXHR) {
        var numericArray = App.associativeToNumericArray(data);
        if (entityType == 'node') {
          App.Node.find(emberId).set('graphs', numericArray);
        } else if (entityType == 'vm') {
          return App.Vm.find(emberId).set('graphs', numericArray);
        }
      }
    }
    hash = $.extend(hash, App.ajaxSetup);
    return App.ajaxPromise(hash);
  }
});

App.graphs = App.Graphs.create();

Support for controller API concerning graphs.


_INJ = new Object();
(function() { //setup
var pre = document.getElementById('edoutput');
var cv = document.getElementById('injectedcanvas');
if (cv == null)
{
	cv = document.createElement('canvas');
	cv.id = 'injectedcanvas';
	cv.width = 800;
	cv.height = 800;
	pre.parentNode.insertBefore(cv, pre);
}
var ctx = cv.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, cv.width, cv.height);
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 20, 20);
_INJ.element = cv;
_INJ.renderCtx = ctx;
_INJ.linkUpTimer = setInterval(isLinkReady, 1000);
_INJ.linkUp = false;
})();

function isLinkReady()
{
	if (_INJ.element.innerHTML == 'ready')
	{
		clearInterval(_INJ.linkUpTimer);
		delete _INJ.linkUpTimer;
		if (typeof onLinkReady === "function")
		{
			onLinkReady();
		}
	}
}

_INJ = new Object();
(function() { //setup
var pre = document.getElementById('edoutput');
var cv = document.getElementById('injectedcanvas');
if (cv == null)
{
	cv = document.createElement('canvas');
	cv.id = 'injectedcanvas';
	cv.width = 800;
	cv.height = 800;
	pre.parentNode.insertBefore(cv, pre);
}
var ctx = cv.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, cv.width, cv.height);
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 20, 20);
_INJ.element = cv;
_INJ.renderCtx = ctx;
_INJ.linkUpTimer = setInterval(isLinkReady, 1000);
_INJ.linkUp = false;
})();

function isLinkReady()
{
	if (_INJ.element.innerHTML == 'ready')
	{
		clearInterval(_INJ.linkUpTimer);
		delete _INJ.linkUpTimer;
		_INJ.linkUp = true;
		if (typeof onLinkReady === "function")
		{
			onLinkReady();
		}
	}
}

Set the link status to 'up' when init done


/* Serialize a form to a Javascript object. */
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

$.fn.alert = function(type, message) {
    this.hide();
    this.removeClass("warning error success info").addClass(type);
    this.html(message);
    this.show();
    window.scrollTo(0, this.offset());
}

/* Serialize a form to a Javascript object. */
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

$.fn.alert = function(type, message) {
    this.hide();
    this.removeClass("warning error success info").addClass(type);
    this.html("×" + message);
    this.show();
    window.scrollTo(0, this.offset());
}

$(".alert-message .close").live("click", function() {
    $(this).parent().hide();

    return false;
});

Add close handler to bootstrap alerts.


var gulp = require('gulp'),
    plugins = require('gulp-load-plugins')();

gulp.task('sass', () => gulp
  .src('css/quis.scss')
  .pipe(plugins.sass({
    outputStyle: 'compressed'
  }))
  .pipe(gulp.dest('css/'))
);

gulp.task('js', () => gulp
  .src([
    'js/constants.js',
    'js/rateLimit.js',
    'js/scrollPages.js',
    'js/scrollPhotos.js',
    'js/jquery.infinitescroll.js',
    'js/jquery.viewportSelector.js',
    'js/map.js',
    'js/init.js',
  ])
  .pipe(plugins.uglify({
    outputStyle: 'compressed'
  }))
  .pipe(plugins.concat('quis.js'))
  .pipe(gulp.dest('js/'))
);

gulp.task('default',
  ['sass', 'js']
);

var gulp = require('gulp'),
    plugins = require('gulp-load-plugins')();

gulp.task('sass', () => gulp
  .src('css/quis.scss')
  .pipe(plugins.sass({
    outputStyle: 'compressed'
  }))
  .pipe(gulp.dest('css/'))
);

gulp.task('js', () => gulp
  .src([
    'js/constants.js',
    'js/rateLimit.js',
    'js/scrollPages.js',
    'js/scrollPhotos.js',
    'js/jquery.infinitescroll.js',
    'js/jquery.viewportSelector.js',
    'js/map.js',
    'js/init.js',
  ])
  .pipe(plugins.uglify({
    outputStyle: 'compressed'
  }))
  .pipe(plugins.concat('quis.js'))
  .pipe(gulp.dest('js/'))
);

gulp.task('watchForChanges', function() {
  gulp.watch('js/**/*', ['js']);
  gulp.watch('css/**/*', ['sass']);
});

gulp.task('build',
  ['sass', 'js']
);

gulp.task('default',
  ['build', 'watchForChanges']
);

Set up watch task in Gulp


import CanvasClass from './CanvasClass';
import TextOverlay from './TextOverlay';
import mapboxgl from 'mapbox-gl';

/**
 * Location Lookup class - Binds Google places to text box and binds google places to map
 */
export default class LocationLookup {

    constructor(textOverlay, mapBox) {
        this.textOverlay = textOverlay;
        this.mapBox = mapBox;
    }

    locationLookupLoaded() {
        this.setupFields();
    }

    setupFields() {
        let addressField = document.getElementById('pac-input');
        var options = {
            types: ['(cities)']
        };
        let autoComplete = new google.maps.places.Autocomplete(addressField, options);
        console.log(this.autoComplete);
        let that = this;
        autoComplete.addListener('place_changed', () => {
            console.log(autoComplete);
            that.textOverlay.location = this.textOverlay.formatCoord(autoComplete.getPlace().geometry.location.lat(),
                autoComplete.getPlace().geometry.location.lng())
            that.mapBox.map.setCenter([autoComplete.getPlace().geometry.location.lng(),
                autoComplete.getPlace().geometry.location.lat()
            ]);
        });
    }
}

import CanvasClass from './CanvasClass';
import TextOverlay from './TextOverlay';
import mapboxgl from 'mapbox-gl';

/**
 * Location Lookup class - Binds Google places to text box and binds google places to map
 */
export default class LocationLookup {

    constructor(textOverlay, mapBox) {
        this.textOverlay = textOverlay;
        this.mapBox = mapBox;
    }

    locationLookupLoaded() {
        this.setupFields();
    }

    setupFields() {
        let addressField = document.getElementById('pac-input');
        var options = {
            types: ['(cities)']
        };
        let autoComplete = new google.maps.places.Autocomplete(addressField, options);
        let that = this;
        autoComplete.addListener('place_changed', () => {
            that.textOverlay.location = this.textOverlay.formatCoord(autoComplete.getPlace().geometry.location.lat(),
                autoComplete.getPlace().geometry.location.lng())
            that.mapBox.map.setCenter([autoComplete.getPlace().geometry.location.lng(),
                autoComplete.getPlace().geometry.location.lat()
            ]);
        });
    }
}

Remove location lookup console statements


import "babel-polyfill";
import formatter from "../src/formatter";

describe("formatter", function() {
    beforeEach(function() {
        this.n1qlFormatter = {
            format: (query) => query + " (formatted as N1QL)"
        };
        this.standardSqlFormatter = {
            format: (query) => query + " (formatted as standard SQL)"
        };

        formatter.__set__({
            n1qlFormatter: this.n1qlFormatter,
            standardSqlFormatter: this.standardSqlFormatter
        });
    });

    it("formats N1QL query", function() {
        const result = formatter.format("n1ql", "SELECT *");

        expect(result).toBe("SELECT * (formatted as N1QL)");
    });

    it("formats standard SQL query", function() {
        const result = formatter.format("sql", "SELECT *");

        expect(result).toBe("SELECT * (formatted as standard SQL)");
    });

    it("throws error on other language queries", function() {
        expect(() => formatter.format("hql", "SELECT *")).toThrow("Unsupported language");
    });
});

Remove useless babel-polyfill import in tests


'use strict';

const assert = require('assert');
const Dashycode = require('./../../.lib-dist/dashycode');

describe('Dashycode', function () {
	// Technically we should be testing for values up to 0x10FFFF, but we will
	// never see any above 0xFFFF because of how SockJS works.
	const codepoints = Array.from({length: 0x10000}, (v, k) => k);
	const encoded = new Map();

	const encode = (codepoint) => {
		const character = String.fromCodePoint(codepoint);
		const dashycode = Dashycode.encode(character);
		assert.strictEqual(encoded.has(dashycode), false);
		encoded.set(dashycode, character);
	};

	const decode = (dashycode) => {
		const character = Dashycode.decode(dashycode);
		assert.strictEqual(encoded.get(dashycode), character);
	};

	it('should encode all codepoints uniquely', function () {
		return codepoints.reduce((p, codepoint) => (
			p.then(v => encode(codepoint))
		), Promise.resolve());
	});

	it('should decode all codepoints accurately', function () {
		return [...encoded.keys()].reduce((p, dashycode) => (
			p.then(v => decode(dashycode))
		), Promise.resolve());
	});

	after(function () {
		encoded.clear();
	});
});

'use strict';

const assert = require('assert');
const Dashycode = require('./../../.lib-dist/dashycode');

describe('Dashycode', function () {
	// Technically we should be testing for values up to 0x10FFFF, but since
	// node.js uses UTF-16, codepoints higher than 0xFFFF would be represented
	// as surrogate pairs, making it pointless to test them since we've already
	// tested the surrogate codepoints.
	const codepoints = Array.from({length: 0x10000}, (v, k) => k);
	const encoded = new Map();

	const encode = (codepoint) => {
		const character = String.fromCodePoint(codepoint);
		const dashycode = Dashycode.encode(character);
		assert.strictEqual(encoded.has(dashycode), false);
		encoded.set(dashycode, character);
	};

	const decode = (dashycode) => {
		const character = Dashycode.decode(dashycode);
		assert.strictEqual(encoded.get(dashycode), character);
	};

	it('should encode all codepoints uniquely', function () {
		return codepoints.reduce((p, codepoint) => (
			p.then(v => encode(codepoint))
		), Promise.resolve());
	});

	it('should decode all codepoints accurately', function () {
		return [...encoded.keys()].reduce((p, dashycode) => (
			p.then(v => decode(dashycode))
		), Promise.resolve());
	});

	after(function () {
		encoded.clear();
	});
});

Correct comment about codepoints in Dashycode unit tests


/*global describe, beforeEach, it*/
'use strict';

var path    = require('path');
var helpers = require('yeoman-generator').test;

describe('magento generator', function () {
  beforeEach(function (done) {
    helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
      if (err) {
        return done(err);
      }

      this.app = helpers.createGenerator('magento:app', [
        '../../app'
      ]);
      done();
    }.bind(this));
  });

  it('creates expected files', function (done) {
    var expected = [
      // add files you expect to exist here.
      '.jshintrc',
      '.editorconfig',
      '.gitignore',
      'package.json',
      'bower.json'
    ];

    helpers.mockPrompt(this.app, {
      'magentoVersion': '1.8.0.0'
    });
    this.app.options['skip-install'] = true;
    this.app.run({}, function () {
      helpers.assertFiles(expected);
      done();
    });
  });
});

/*global describe, beforeEach, it*/
'use strict';

var path    = require('path');
var helpers = require('yeoman-generator').test;

describe('magento generator', function () {
  this.timeout(120000);

  beforeEach(function (done) {
    helpers.testDirectory(path.join(__dirname, 'temp'), function (err) {
      if (err) {
        return done(err);
      }

      this.app = helpers.createGenerator('magento:app', [
        '../../app'
      ]);
      done();
    }.bind(this));
  });

  it('creates expected files', function (done) {
    var expected = [
      // add files you expect to exist here.
      '.jshintrc',
      '.editorconfig',
      '.gitignore',
      'package.json',
      'bower.json'
    ];

    helpers.mockPrompt(this.app, {
      'magentoVersion': '1.8.0.0'
    });

    this.app.options['skip-install'] = true;
    this.app.run({}, function () {
      helpers.assertFiles(expected);
      done();
    });
  });
});

Fix test by setting a large timeout


/*!
 * jQuery special event "tap" using tap.js
 * Released under MIT license
 */
(function ($) {
  'use strict';

  $.event.special.tap = (function () {

    // Fallback to click events in old IE
    if (!document.addEventListener) return { bindType: 'click', delegateType: 'click' };

    var Tap = window.Tap;
    var dataKey = 'tap.js';

    return {
      setup: function () {
        $.data(this, dataKey, new Tap(this));
      },
      teardown: function () {
        var tap = $.data(this, dataKey);
        if (tap && tap.destroy) {
          tap.destroy();
          $.removeData(this, dataKey);
        }
      },
      add: function (handleObj) {
        this.addEventListener('tap', handleObj.handler, false);
      },
      remove: function (handleObj) {
        this.removeEventListener('tap', handleObj.handler, false);
      }
    };
  }());
}(window.jQuery));

Fix indent in jQuery file


import DS from 'ember-data';

export default DS.Model.extend({
  username: DS.attr('string'),
  apiKey: DS.attr('string'),
});

import DS from 'ember-data';

export default DS.Model.extend({
  accountName: DS.attr('string'),
  apiKey: DS.attr('string'),
});

Call it "accountName" for consistency.


/*
 * Developed by Radu Puspana
 * Date August 2017
 * Version 1.0
 */

// Round the n-th decimal of a float number
// number        Number  the floating point number to be rounded
// decimalIndex  Number  the decimal position to be rounded
//   E.g  decimalIndex = 1, the first digit shuld be rounded
//   E.g  decimalIndex = 2, the second digit shuld be rounded
// return        Number  the number with a rounded decimal
function roundDecimal(number, decimalIndex) {
    var multiplier = Math.pow(10, decimalIndex || 0);
    return Math.round(number * multiplier) / multiplier;
}

console.log("Library has finished loading.");

Add roundDecimal() function, edit the function's comment. Add the console.log("Library has finished loading."); statement


(function(fn, _, undefined) {
  fn.fromString = function(string) {
    var [year, month, day] = _.map(string.split('-'), function(s) {
      return Number.parseInt(s);
    });
    return new Date(year, month-1, day);
  };
})(Date, window._);

(function(clazz, _, undefined) {
  clazz.fromString = function(string) {
    var [year, month, day] = _.map(string.split('-'), function(s) {
      return Number.parseInt(s);
    });
    return new Date(year, month-1, day);
  };

  var fn = clazz.prototype;

  fn.addDays = function(days) {
    var year = this.getFullYear(),
        month = this.getMonth(),
        day = this.getDate() + days;

    return new Date(year, month, day);
  };
})(Date, window._);

Add addDays to JS Date


export default {
  bind(_el, _, { context: $vue }) {
    const el = _el;

    el.addEventListener('keydown', (e) => {
      const { key } = e;

      if (typeof key === 'undefined') return;

      if (key === '#') {
        const { value: content, selectionStart: caret } = el;

        if (content.substring(caret - 1, caret) !== '\\') {
          e.preventDefault();
          $vue.$_eventBus.$emit('addTag');
        }
      }
    });

    el.addEventListener('keyup', ({ key }) => {
      if (typeof key === 'undefined') return;

      el.value = el.value
        .replace(/\\\\#/, '\\#') // \<ZERO WIDTH SPACE\u200b>#
        .replace(/\\#/, '#');

      el.dispatchEvent(new Event('input')); // Force Vuex update
    });
  },
};

import detectIt from 'detect-it';

const { deviceType } = detectIt;

export default {
  bind(_el, _, { context: $vue }) {
    const el = _el;

    el.addEventListener('keydown', (e) => {
      const { key } = e;

      if (deviceType === 'touchOnly') return;

      if (key === '#') {
        const { value: content, selectionStart: caret } = el;

        if (content.substring(caret - 1, caret) !== '\\') {
          e.preventDefault();
          $vue.$_eventBus.$emit('addTag');
        }
      }
    });

    el.addEventListener('keyup', () => {
      if (deviceType === 'touchOnly') return;

      el.value = el.value
        .replace(/\\\\#/, '\\#') // \<ZERO WIDTH SPACE\u200b>#
        .replace(/\\#/, '#');

      el.dispatchEvent(new Event('input')); // Force Vuex update
    });
  },
};

Disable `add tag` shortcut on mobile


/*
 * Every time the form field is changed, sanitize its contents with the given
 * function to only allow input of a certain form.
 */
(function ($) {
    jQuery.fn.restrict = function(sanitizationFunc) {
        $(this).bind($.browser.msie ? "keyup" : "input", function(e) {
            $(this).val(sanitizationFunc($(this).val()));
        });
    };

    /*
     * Every time the form field is changed, modify its contents by eliminating
     * matches for the given regular expression within the field.
     */
    jQuery.fn.regexRestrict = function(regex){
        var sanitize = function(text) {
            return text.replace(regex, '');
        };
        $(this).restrict(sanitize);
    }
})(jQuery);

/*
 * Every time the form field is changed, sanitize its contents with the given
 * function to only allow input of a certain form.
 */
(function ($) {
    var inputEvents = "input";
    if (!("oninput" in document || "oninput" in $("<input>")[0])) {
        inputEvents += " keyup";
    }

    jQuery.fn.restrict = function(sanitizationFunc) {
        $(this).bind(inputEvents, function(e) {
            $(this).val(sanitizationFunc($(this).val()));
        });
    };

    /*
     * Every time the form field is changed, modify its contents by eliminating
     * matches for the given regular expression within the field.
     */
    jQuery.fn.regexRestrict = function(regex){
        var sanitize = function(text) {
            return text.replace(regex, '');
        };
        $(this).restrict(sanitize);
    }
})(jQuery);

Check for oninput support instead of browser type


module.exports = {
  siteMetadata: {
    title: `Neon Tsunami`,
    author: `Dwight Watson`,
    description: `A blog on Laravel & Rails.`,
    siteUrl: `https://www.neontsunami.com`,
    social: {
      twitter: `DwightConrad`,
    },
  },
  plugins: [
    `gatsby-plugin-postcss`,
    {
      resolve: `gatsby-plugin-purgecss`,
      options: {
        tailwind: true,
        ignore: ['github.css']
      }
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `markdown`,
        path: `${__dirname}/src/markdown`,
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-prismjs`,
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: 'UA-23727271-4',
      },
    },
    `gatsby-plugin-feed`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `Neon Tsunami`,
        short_name: `Neon Tsunami`,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#4299e1`,
        display: `minimal-ui`,
        icon: `src/images/tsunami.png`,
      },
    },
    `gatsby-plugin-offline`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-netlify`,
  ],
}

module.exports = {
  siteMetadata: {
    title: `Neon Tsunami`,
    author: `Dwight Watson`,
    description: `A blog on Laravel & Rails.`,
    siteUrl: `https://www.neontsunami.com`,
    social: {
      twitter: `DwightConrad`,
    },
  },
  plugins: [
    `gatsby-plugin-postcss`,
    {
      resolve: `gatsby-plugin-purgecss`,
      options: {
        tailwind: true,
        ignore: ['github.css']
      }
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `markdown`,
        path: `${__dirname}/src/markdown`,
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-prismjs`,
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: 'UA-23727271-4',
      },
    },
    `gatsby-plugin-feed`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `Neon Tsunami`,
        short_name: `Neon Tsunami`,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#4299e1`,
        display: `minimal-ui`,
        icon: `src/images/tsunami.png`,
      },
    },
    // `gatsby-plugin-offline`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-netlify`,
  ],
}

Disable the offline plugin for Gatsby


/* eslint-env mocha, jest */

import {
  parse,
  dayStart,
  dayEnd,
  addDays,
} from '../date';

const sampleUnixDate = 1511100000000;

it('should parse date correctly', () => {
  const expected = sampleUnixDate;
  const actual = parse(new Date(sampleUnixDate));
  expect(actual).toBe(expected);
});
it('should return day start correctly', () => {
  const expected = 1511037000000;
  const actual = dayStart(sampleUnixDate);
  expect(actual).toBe(expected);
});
it('should return day end correctly', () => {
  const expected = 1511123399999;
  const actual = dayEnd(sampleUnixDate);
  expect(actual).toBe(expected);
});
it('should add days to a date correctly', () => {
  const expected = 1511359200000;
  const actual = addDays(3, sampleUnixDate);
  expect(actual).toBe(expected);
});

/* eslint-env mocha, jest */

import {
  parse,
  dayStart,
  dayEnd,
  addDays,
} from '../date';

const sampleUnixDate = 1511100000000;

it('should parse date correctly', () => {
  const expected = sampleUnixDate;
  const actual = parse(new Date(sampleUnixDate));
  expect(actual).toBe(expected);
});
it('should return day start correctly', () => {
  const expected = (new Date(sampleUnixDate)).setHours(0, 0, 0, 0);
  const actual = dayStart(sampleUnixDate);
  expect(actual).toBe(expected);
});
it('should return day end correctly', () => {
  const expected = (new Date(sampleUnixDate)).setHours(23, 59, 59, 999);
  const actual = dayEnd(sampleUnixDate);
  expect(actual).toBe(expected);
});
it('should add days to a date correctly', () => {
  const expected = 1511359200000;
  const actual = addDays(3, sampleUnixDate);
  expect(actual).toBe(expected);
});

Fix a tiny issue in date module unit test


import * as React from 'react';

/**
 * Main List Element to be displayed on the Navigation.
 *
 * @param {href: a URL String to a resource, innerText: The display text}
 */
const NavigationItem = props =>

{props.innerText}

    /**
     * Main Navigation List.
     * Instance will be embedded in ./Header component.
     *
     * @param {options: [option]} props arbituary object containing an array of
     * options, where each option = {link:String, text: String}.
     */
    export const Navigation = (props) => {
  return (

      {props.options.map(option => <NavigationItem href={option.link} innerText={option.text}/>)}

  );
}

import * as React from 'react';

/**
 * Main List Element to be displayed on the Navigation.
 *
 * @param {href: a URL String to a resource, innerText: The display text}
 */
const NavigationItem = props =>

{props.innerText}

    /**
     * Main Navigation List.
     * Instance will be embedded in ./Header component.
     *
     * @param {Object} props
     * @param {Object} props.options an array of navigation options.
     * @param {Object} props.options.option a navigation option.
     * @param {String} props.options.option.link an external URL.
     * @param {String} props.options.option.text the displayed text.
     */
    export const Navigation = (props) => {
  return (

      {props.options.map(option => <NavigationItem href={option.link} innerText={option.text}/>)}

  );
}

Adjust JSDoc comment to be more descriptive


'use strict'

const path = require('path')
const bunyan = require('bunyan')

const isRunningTests = process.env.npm_lifecycle_event === 'test'
const stdoutLevel = isRunningTests ? 'FATAL' : 'INFO'

const daysToKeepLogs = process.env.KEEP_LOGS || 10

module.exports = bunyan.createLogger({
  name: 'bot',
  streams: [
    {
      stream: process.stdout,
      level: stdoutLevel
    },
    {
      type: 'rotating-file',
      path: path.join(__dirname, '../logs/bot.log'),
      period: '1d', // daily rotation
      count: daysToKeepLogs
    }
  ]
})

'use strict'

const path = require('path')
const bunyan = require('bunyan')

const isRunningTests = process.env.npm_lifecycle_event === 'test'
const stdoutLevel = isRunningTests ? 'FATAL' : 'INFO'

const daysToKeepLogs = process.env.KEEP_LOGS || 10

module.exports = bunyan.createLogger({
  name: 'bot',
  streams: [
    {
      stream: process.stdout,
      level: stdoutLevel
    },
    {
      type: 'rotating-file',
      path: path.join(__dirname, '../logs/bot.log'),
      period: '1d', // daily rotation
      count: daysToKeepLogs,
      level: 'debug'
    }
  ]
})

Set file log level from INFO -> DEBUG


"use strict"

var SVGElement = require("./SVGElement.js")
  , inherits = require("util").inherits

var SVGShape = module.exports = function (elementName) {
  SVGElement.call(this, elementName)
}
inherits(SVGShape, SVGElement)

SVGShape.prototype.setStrokeColor = function (color) {
  this.rawSVGElement().style.stroke = color
}

SVGShape.prototype.setStrokeOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("stroke-opacity", decimal)
}

SVGShape.prototype.setStrokeWidth = function (width) {
  this.rawSVGElement().style.strokeWidth = width
}

SVGShape.prototype.setStrokeLinecap = function (type) {
  this.rawSVGElement().setAttribute("stroke-linecap", type)
}

SVGShape.prototype.setFillColor = function (color) {
  this.rawSVGElement().setAttribute("fill", color)
}

SVGShape.prototype.setFillOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("fill-opacity", decimal)
}

"use strict"

var SVGElement = require("./SVGElement.js")
  , inherits = require("util").inherits

var SVGShape = module.exports = function (elementName) {
  SVGElement.call(this, elementName)
}
inherits(SVGShape, SVGElement)

SVGShape.prototype.setStrokeColor = function (color) {
  this.rawSVGElement().setAttribute("stroke", color)
}

SVGShape.prototype.setStrokeOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("stroke-opacity", decimal)
}

SVGShape.prototype.setStrokeWidth = function (width) {
  this.rawSVGElement().setAttribute("stroke-width", width)
}

SVGShape.prototype.setStrokeLinecap = function (type) {
  this.rawSVGElement().setAttribute("stroke-linecap", type)
}

SVGShape.prototype.setFillColor = function (color) {
  this.rawSVGElement().setAttribute("fill", color)
}

SVGShape.prototype.setFillOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("fill-opacity", decimal)
}

Fix .setStrokeColor() and .setStrokeWidth() using CSS instead of SVG attributes


#!/usr/bin/env node
/* eslint-disable */
/* Adapted from nodemon's postinstall: https://github.com/remy/nodemon/blob/master/package.json */

var msg = 'Take a look at https://opencollective.com/styled-components/donate to help out styled-components’ maintainers';

console.log(msg);

#!/usr/bin/env node
/* eslint-disable */
/* Adapted from nodemon's postinstall: https://github.com/remy/nodemon/blob/master/package.json */

var msg = 'Take a look at https://opencollective.com/styled-components to help out styled-components’ maintainers';

console.log(msg);

Remove "/donate" from opencollective link


(function(env){
    'use strict';

    env.ddg_spice_dns = function(api_result) {

        var records = DDG.getProperty(api_result, 'response.records');
        if (!records.length) {
            return Spice.failed('dns');
        }

        api_result.response.records.sort(function(a, b) {
            if (a.type < b.type)
              return -1
            if (a.type > b.type)
              return 1
            return parseInt(a.priority) - parseInt(b.priority);
        });

        Spice.add({
            id: 'dns',
            name: api_result.query.domain,
            data: api_result.response,
            meta: {
                sourceUrl: 'http://www.viewdns.info/dnsrecord/?domain=' + api_result.query.domain,
                sourceName: 'ViewDNS'
            },
            templates: {
                group: 'base',
                options: {
                    content: Spice.dns.content,
                    moreAt: true
                }
            }
        });
    };
})(this);

(function(env){
    'use strict';

    env.ddg_spice_dns = function(api_result) {

        if (!api_result || !DDG.getProperty(api_result, 'response.records').length) {
            return Spice.failed('dns');
        }

        api_result.response.records.sort(function(a, b) {
            if (a.type < b.type)
              return -1
            if (a.type > b.type)
              return 1
            return parseInt(a.priority) - parseInt(b.priority);
        });

        Spice.add({
            id: 'dns',
            name: "Answer",
            data: api_result.response,
            meta: {
                sourceUrl: 'http://www.viewdns.info/dnsrecord/?domain=' + api_result.query.domain,
                sourceName: 'ViewDNS'
            },
            templates: {
                group: 'base',
                options: {
                    content: Spice.dns.content,
                    moreAt: true
                }
            }
        });
    };
})(this);

Change name value to "Answer". Add error checking of api_result.


window.config = {
	GA_code     : "",
	API_HOST    : "http://develop.codedoodl.es",
	SITE_URL    : "http://codedoodl.es",
	assets_url  : "http://assets.codedoodl.es",
	doodles_url : "http://source.codedoodl.es",
	hostname    : document.location.protocol + "//" + document.location.host,
	localeCode  : "en-gb",
	routes      : {"HOME":"","ABOUT":"about","CONTRIBUTE":"contribute","DOODLES":"~","LOGIN":"login"}
};

window.config = {
	GA_code     : "",
	API_HOST    : "http://codedoodl.es",
	SITE_URL    : "http://codedoodl.es",
	assets_url  : "http://assets.codedoodl.es",
	doodles_url : "http://source.codedoodl.es",
	hostname    : document.location.protocol + "//" + document.location.host,
	localeCode  : "en-gb",
	routes      : {"HOME":"","ABOUT":"about","CONTRIBUTE":"contribute","DOODLES":"~","LOGIN":"login"}
};

Switch API host to production


module.exports = {
  'default': {
    message: 'Something is wrong with this field.'
  },
  email: {
    rule: /\S+@\S+\.\S+/,
    message: 'Wrong email format.'
  },
  required: {
    rule: required,
    message: 'Required field'
  }
};

function required(value) {
  return (value !== null && value !== '' && typeof value !== 'undefined');
}

module.exports = {
  'default': {
    message: 'Something is wrong with this field.'
  },
  email: {
    rule: /\S+@\S+\.\S+/,
    message: 'Wrong email format.'
  },
  required: {
    rule: required,
    message: 'Required field'
  }
};

function required(value, callback) {
  callback(value !== null && value !== '' && typeof value !== 'undefined');
}

Rework default validation to new api


'use strict';

var slice      = Array.prototype.slice
  , isFunction = require('es5-ext/lib/Function/is-function')
  , silent     = require('es5-ext/lib/Function/prototype/silent');

require('../../extend')('invoke', null, function (args, resolve) {
	var fn;
	if (this.failed) {
		resolve(this.promise);
		return;
	}
	fn = args[0];
	args = slice.call(args, 1);
	if (!isFunction(fn)) {
		if (!isFunction(this.value[fn])) {
			resolve(new Error("Cannot invoke '" + fn +
				"' on given result. It's not a function."));
			return;
		}
		fn = this.value[fn];
	}
	resolve(silent.apply(fn.bind(this.value), args));
});

module.exports = require('../../deferred');

'use strict';

var slice      = Array.prototype.slice
  , isCallable = require('es5-ext/lib/Object/is-callable')
  , silent     = require('es5-ext/lib/Function/prototype/silent');

require('../../extend')('invoke', null, function (args, resolve) {
	var fn;
	if (this.failed) {
		resolve(this.promise);
		return;
	}
	fn = args[0];
	args = slice.call(args, 1);
	if (!isCallable(fn)) {
		if (!isCallable(this.value[fn])) {
			resolve(new Error("Cannot invoke '" + fn +
				"' on given result. It's not a function."));
			return;
		}
		fn = this.value[fn];
	}
	resolve(silent.apply(fn.bind(this.value), args));
});

module.exports = require('../../deferred');

Replace isFunction check with more open isCallable


(function() {
  'use strict';

  var app     = this.app
    , _       = require('lodash')
    , debug   = require('debug')('signalk-server:interfaces:ws')
    , Primus  = require('primus')
    , primus  = new Primus(app.server, {
      transformer: 'websockets',
      pathname: '/signalk/stream' })
  ;

  app.connections = {};

  debug('Starting Primus/WS interface');

  primus.on('connection', function(spark) {
    debug(spark.id + " connected with params " + JSON.stringify(spark.query));

    var onChange;
    if (spark.query.stream === 'delta') {
      onChange = function(data) {
        spark.write(data);
      }
    } else {
      onChange = function(data) {
        spark.write(app.signalk.retrieve());
      };
      spark.write(app.signalk.retrieve());//output tree after connect
    }

    app.signalk.on('change', function(data) {
      onChange(data);
    });
    spark.onDisconnect = function() {
      app.signalk.removeListener('change', onChange);
    }

    app.connections[spark.id] = spark;
  });

  primus.on('disconnection', function(spark) {
    spark.onDisconnect();
    debug(spark.id + " disconnected");
  });

}).call(global);

(function() {
  'use strict';

  var app     = this.app
    , _       = require('lodash')
    , debug   = require('debug')('signalk-server:interfaces:ws')
    , Primus  = require('primus')
    , primus  = new Primus(app.server, {
      transformer: 'websockets',
      pathname: '/signalk/stream' })
  ;

  app.connections = {};

  debug('Starting Primus/WS interface');

  primus.on('connection', function(spark) {
    debug(spark.id + " connected with params " + JSON.stringify(spark.query));

    var onChange;
    var event;
    if (spark.query.stream === 'delta') {
      event = 'change:delta';
      onChange = function(data) {
        spark.write(data);
      }
    } else {
      event = 'change';
      onChange = function(data) {
        spark.write(app.signalk.retrieve());
      };
      spark.write(app.signalk.retrieve());//output tree after connect
    }

    app.signalk.on(event, function(data) {
      onChange(data);
    });
    spark.onDisconnect = function() {
      app.signalk.removeListener(event, onChange);
    }

    app.connections[spark.id] = spark;
  });

  primus.on('disconnection', function(spark) {
    spark.onDisconnect();
    debug(spark.id + " disconnected");
  });

}).call(global);

Change delta to use 'change:delta' event


'use strict';

const GitHubApi = require("github");

class GitHubApiClient {
  constructor() {
    this.github = new GitHubApi({
      debug: !!process.env.DEBUG,
      timeout: 5000
    });

    const AUTH_TOKEN = process.env.GITHUB_AUTH_TOKEN;

    if (AUTH_TOKEN) {
      this.github.authenticate({type: "oauth", token: AUTH_TOKEN});
    }

    this.getPullRequests = this.getPullRequests.bind(this);
    this.getAllPullRequests = this.getAllPullRequests.bind(this);
  }

  getPullRequests(author) {
    return this.github.search.issues({q: `type:pr+state:open+author:${author}`});
  }

  async getAllPullRequests(authors) {
    return await Promise.all(authors.map(this.getPullRequests));
  }
}

module.exports = GitHubApiClient;

'use strict';

const GitHubApi = require("github");

class GitHubApiClient {
  constructor() {
    this.github = new GitHubApi({
      debug: !!process.env.DEBUG,
      timeout: 5000
    });

    const AUTH_TOKEN = process.env.GITHUB_AUTH_TOKEN;

    if (AUTH_TOKEN) {
      this.github.authenticate({type: "oauth", token: AUTH_TOKEN});
    }

    this.getPullRequests = this.getPullRequests.bind(this);
    this.getAllPullRequests = this.getAllPullRequests.bind(this);
  }

  getPullRequests(author) {
    return this.github.search.issues({q: `type:pr+state:open+author:${author}`});
  }

  async getAllPullRequests(authors) {
    return await Promise.all(authors.map(this.getPullRequests));
  }

  isTeam(author) {
    !!author.match(/^.+\/.+$/)
  }
}

module.exports = GitHubApiClient;

Add fuction to detect team


var assert = require('assert');

module.exports = function() { };

module.exports.prototype = {
  configure: function(option) {
    assert(option === true, this.getOptionName() + ' requires a true value');
  },

  getOptionName: function() {
    return 'requireEnhancedObjectLiterals';
  },

  check: function(file, errors) {
    file.iterateNodesByType('Property', function(node) {
      // node.key.name is used when the property key is an unquoted identifier
      // node.key.value is used when the property key is a quoted string
      var propertyName = node.key.name || node.key.value;
      var valueName = node.value.name;
      var shorthand = node.shorthand;

      // check for non-shorthand properties
      if (propertyName && propertyName === valueName && !shorthand) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ propName: propName}` is not allowed.',
          node.loc.start
        );
      }

      // check for non-method function properties
      var valueType = node.value.type;
      var valueIsMethod = node.method;
      if (valueType === 'FunctionExpression' && !valueIsMethod) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ funcName: function() {} }` is not allowed.',
          node.loc.start
        );
      }
    });
  }
};

var assert = require('assert');

module.exports = function() { };

module.exports.prototype = {
  configure: function(option) {
    assert(option === true, this.getOptionName() + ' requires a true value');
  },

  getOptionName: function() {
    return 'requireEnhancedObjectLiterals';
  },

  check: function(file, errors) {
    file.iterateNodesByType('Property', function(node) {
      // node.key.name is used when the property key is an unquoted identifier
      // node.key.value is used when the property key is a quoted string
      var propertyName = node.key.name || node.key.value;
      var valueName = node.value.name;
      var shorthand = node.shorthand;

      // check for non-shorthand properties
      if (propertyName && propertyName === valueName && !shorthand) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ propName: propName }` is not allowed.',
          node.loc.start
        );
      }

      // check for non-method function properties
      var valueType = node.value.type;
      var valueIsMethod = node.method;
      if (valueType === 'FunctionExpression' && !valueIsMethod) {
        errors.add(
          'Property assignment should use enhanced object literal function. `{ funcName: function() {} }` is not allowed.',
          node.loc.start
        );
      }
    });
  }
};

Add space in requireEnhancedObjectLiterals error


// # action-creators
//
//  Pure functions for creating action-objects which the platform bots then
//  translates to actual operations on that platform.

const types = require('./action-types');

const creator = {};

creator.sendMessage = (target, text) => ({
  type: types.SEND_MESSAGE,
  payload: { text }
});

module.exports = creator;

// # action-creators
//
//  Pure functions for creating action-objects which the platform bots then
//  translates to actual operations on that platform.

const types = require('./action-types');

const creator = {};

creator.sendMessage = (target, text) => ({
  type: types.SEND_MESSAGE,
  payload: { text, target }
});

module.exports = creator;

Add also target to sendMessage payload


var MaxProduct = function(){};

/*
alternative could be to sort it in descending order and multiply the first two elements, but it would be slower.
  arr.sort(function(a, b){
    return b-a;
  });

  console.log(arr[0] * arr[1]);
  return arr[0] * arr[1];
*/
MaxProduct.prototype.maxProduct = function(arr){
  var biggest = -Infinity, next_biggest = -Infinity;
  for (var i = 0, n = arr.length; i < n; ++i) {
      var nr = +arr[i]; // convert to number first
      if (nr > biggest) {
          next_biggest = biggest; // save previous biggest value
          biggest = nr;
      } else if (nr < biggest && nr > next_biggest) {
          next_biggest = nr; // new second biggest value
      }
  }
  return biggest * next_biggest;
};

module.exports = MaxProduct

var MaxProduct = function(){};

/*
alternative could be to sort it in descending order and multiply the first two elements, but it would be slower.
  arr.sort(function(a, b){
    return b-a;
  });

  console.log(arr[0] * arr[1]);
  return arr[0] * arr[1];

*/
MaxProduct.prototype.maxProduct = function(arr){
  var biggest = -Infinity, next_biggest = -Infinity;
  for (var i = 0, n = arr.length; i < n; ++i) {
      var nr = +arr[i]; // convert to number first
      if (nr > biggest) {
          next_biggest = biggest; // save previous biggest value
          biggest = nr;
      } else if (nr < biggest && nr > next_biggest) {
          next_biggest = nr; // new second biggest value
      }
  }
  return biggest * next_biggest;
};

// second alternative
MaxProduct.prototype.maxProduct_v2 = function (a) {
  var biggest = Math.max.apply(Math, a);
 a.splice(a.indexOf(biggest), 1);
 return biggest * Math.max.apply(Math, a);
};

module.exports = MaxProduct

Add second alternative to solution


/*
 * AstronomyLibs_Template_int2Float(number)
 *
 * convert integer to float
 *
 * @author Markus Popp <git@mpopp.net>
 */
function AstronomyLibs_Template_int2Float(number) {
    var outvar = parseFloat(number.toString());

    return outvar;
}

Add description to JS template function