const mediaExtensions = {
  audio: [
    '.aac', '.asf', '.flac', '.m2a', '.m4a', '.m4b', '.mp2', '.mp4',
    '.mp3', '.oga', '.ogg', '.opus', '.wma', '.wav', '.wv', '.wvp'],
  video: [
    '.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', 'mpg', 'mpeg',
    '.ogv', '.webm', '.wmv'],
  image: ['.gif', '.jpg', '.jpeg', '.png']
}

module.exports = mediaExtensions

const mediaExtensions = {
  audio: [
    '.aac', 'aif', 'aiff', '.asf', '.flac', '.m2a', '.m4a', '.m4b',
    '.mp2', '.mp3', '.mpc', '.oga', '.ogg', '.opus', 'spx', '.wma',
    '.wav', '.wv', '.wvp'],
  video: [
    '.avi', '.mp4', '.m4v', '.webm', '.mov', '.mkv', 'mpg', 'mpeg',
    '.ogv', '.webm', '.wmv'],
  image: ['.gif', '.jpg', '.jpeg', '.png']
}

module.exports = mediaExtensions

Add aif/aiff, spx (Speex) & mpc (Musepack) as audio file extensions. Remove clashing .mp4 extension (with video) from audio.


'use strict';

describe('Directive: alertMessages', function() {

  // load the directive's module
  beforeEach(module('polestar'));

  var element,
    scope;

  beforeEach(module('polestar', function($provide) {
    var mock = {
      alerts: [
        {name: 'foo'},
        {name: 'bar'}
      ]
    };
    $provide.value('Alerts', mock);
  }));

  beforeEach(inject(function($rootScope) {
    scope = $rootScope.$new();
  }));

  it('should show alert messages', inject(function($compile) {
    element = angular.element('<alert-messages></alert-messages>');
    element = $compile(element)(scope);
    scope.$digest();

    expect(element.find('.alert-item').length).to.eql(2);
  }));
});

'use strict';

describe('Directive: alertMessages', function() {

  var element,
    scope;

  // load the directive's module
  beforeEach(module('vlui', function($provide) {
    // Mock the alerts service
    $provide.value('Alerts', {
      alerts: [
        {msg: 'foo'},
        {msg: 'bar'}
      ]
    });
  }));

  beforeEach(inject(function($rootScope, $compile) {
    scope = $rootScope.$new();
    var $el = angular.element('<alert-messages></alert-messages>');
    element = $compile($el)(scope);
    scope.$digest();
  }));

  it('should show alert messages', function() {
    expect(element.find('.alert-item').length).to.equal(2);
    // Ignore the close buttons, which use an HTML entity for the close icon
    element.find('a').remove();
    expect(element.find('.alert-item').eq(0).text().trim()).to.equal('foo');
    expect(element.find('.alert-item').eq(1).text().trim()).to.equal('bar');
  });
});

Migrate the tests for the alert service to vlui


/**
 * Mycockpit Directive
 */
(function() {
    'use strict';

    angular
        .module('dashboard')
        .directive('dashboard', ['dashboardFactory', function(dashboardFactory) {

            // Width of the dashboard container
            var currentWidth;
            // To detet a change of column
            var lastNumberColumns;
            // Usually currentWidth / minWidth where max is numberMaxOfColumn
            var numberOfColumnPossible;
            // Width of columns in % to use in ng-style
            var columnsWidth;
            // Maximum number of columns
            var numberMaxOfColumn;
            // Thread to avoir too much event trigger during resize
            var timeout;

            return {
                restrict: 'E',
                scope: {
                    'id': '@',
                    'width': '@',
                    'columns': '@',
                    'columnsMinWidth': '@'
                },
                templateUrl: 'dashboard.directive.html',
                controller: ['$scope', function(scope) {
                    var screenWidth = $( window ).width();

                    scope.dashboard = dashboardFactory.get(scope.id);

                    scope.dashboard.setOptions({
                        'width': scope['width'],
                        'columns': scope['columns'],
                        'columnsMinWidth': scope['columnsMinWidth']
                    });

                    scope.dashboard.refresh();

                    // On resize we refresh
                    window.addEventListener('resize', function(event) {

                        if ($( window ).width() !== screenWidth) {
                            clearTimeout(timeout);
                            timeout = setTimeout(function () {
                                    scope.dashboard.refresh();
                                    scope.$apply();
                            }, 150);
                        }
                    }, true);
                }]
            };
        }]);
})();

/**
 * Mycockpit Directive
 */
(function() {
    'use strict';

    angular
        .module('dashboard')
        .directive('dashboard', ['dashboardFactory', function(dashboardFactory) {

            // Width of the dashboard container
            var currentWidth;
            // To detet a change of column
            var lastNumberColumns;
            // Usually currentWidth / minWidth where max is numberMaxOfColumn
            var numberOfColumnPossible;
            // Width of columns in % to use in ng-style
            var columnsWidth;
            // Maximum number of columns
            var numberMaxOfColumn;
            // Thread to avoir too much event trigger during resize
            var timeout;

            return {
                restrict: 'E',
                scope: {
                    'id': '@',
                    'width': '@',
                    'columns': '@',
                    'columnsMinWidth': '@'
                },
                templateUrl: 'dashboard.directive.html',
                controller: ['$scope', function(scope) {
                    currentWidth = $( window ).width();

                    scope.dashboard = dashboardFactory.get(scope.id);

                    scope.dashboard.setOptions({
                        'width': scope['width'],
                        'columns': scope['columns'],
                        'columnsMinWidth': scope['columnsMinWidth']
                    });

                    scope.dashboard.refresh();

                    // On resize we refresh
                    window.addEventListener('resize', function(event) {

                        if ($( window ).width() !== currentWidth) {
                            // update currentWidth with current window width
                            currentWidth = $( window ).width();
                            clearTimeout(timeout);
                            timeout = setTimeout(function () {
                                scope.dashboard.refresh();
                                scope.$apply();
                            }, 150);
                        }
                    }, true);
                }]
            };
        }]);
})();

Update dashboard current width when window is resized (eg: when table orientation changes).


import React, { Component } from "react";
import { connect } from "react-redux";
import { employeeUpdate, employeeEdit, employeeEditSave } from "../../actions";
import { Card, CardSection, Button } from "../common";
import EmployeeForm from "./EmployeeForm";

class EmployeeEdit extends Component {
  componentWillMount() {
    this.props.employeeEdit(this.props.employee);
  }

  onButtonClick = () => {
    const { name, phone, shift } = this.props;
    this.props.employeeEditSave({
      name,
      phone,
      shift,
      uid: this.props.employee.uid
    });
  };

  render() {
    return (
      <Card>
        <EmployeeForm />
        <CardSection>
          <Button onPress={this.onButtonClick}>Save Changes</Button>
        </CardSection>
      </Card>
    );
  }
}

const mapStateToProps = state => {
  const { name, phone, shift } = state.employeeForm;
  return { name, phone, shift };
};

export default connect(mapStateToProps, {
  employeeUpdate,
  employeeEdit,
  employeeEditSave
})(EmployeeEdit);

import React, { Component } from "react";
import { connect } from "react-redux";
import Communications from "react-native-communications";
import { employeeUpdate, employeeEdit, employeeEditSave } from "../../actions";
import { Card, CardSection, Button, ConfirmDialog } from "../common";
import EmployeeForm from "./EmployeeForm";

class EmployeeEdit extends Component {
  componentWillMount() {
    this.props.employeeEdit(this.props.employee);
  }

  onButtonClick = () => {
    const { name, phone, shift } = this.props;
    this.props.employeeEditSave({
      name,
      phone,
      shift,
      uid: this.props.employee.uid
    });
  };

  onTextClick = () => {
    const { phone, shift } = this.props;
    Communications.text(phone, `Your upcome shift is on ${shift}`);
  };

  onFireClick = () => {};

  render() {
    return (
      <Card>
        <EmployeeForm />

        <CardSection>
          <Button onPress={this.onButtonClick}>Save Changes</Button>
        </CardSection>

        <CardSection>
          <Button onPress={this.onTextClick}>Text employee</Button>
        </CardSection>

        <CardSection>
          <Button onPress={this.onFireClick}>Fire</Button>
        </CardSection>
      </Card>
    );
  }
}

const mapStateToProps = state => {
  const { name, phone, shift } = state.employeeForm;
  return { name, phone, shift };
};

export default connect(mapStateToProps, {
  employeeUpdate,
  employeeEdit,
  employeeEditSave
})(EmployeeEdit);

Add callback function to text button


'use strict'

module.exports = (function(){
    /**
     * Import modules
     */
    const footballDb = require('./../db/footballDb')
    /**
     * football module API
     */
    return {
        'leagues': leagues,
        'leagueTable': leagueTable
    }
    /**
     * football module API -- leagueTable
     */
    function leagueTable(id) { // Auto parses id from query-string
        return footballDb
            .leagueTable(id)
            .then(league => {
                league.title = 'League Table'
                return league
            })
    }
    /**
     * football module API -- leagues
     */
    function leagues() {
        return footballDb
            .leagues()
            .then(leagues => {
                leagues = leaguesWithLinks(leagues)
                return {
                    'title':'Leagues',
                    'leagues': leagues
                }
            })
    }

    /**
     * Utility auxiliary function
     */
    function leaguesWithLinks(leagues) {
        return leagues.map(item => {
            item.leagueHref = "/football/leagueTable?id=" + item.id
            return item
        })
    }
})()

'use strict'

module.exports = (function(){
    /**
     * Import modules
     */
    const footballDb = require('./../db/footballDb')
    /**
     * football module API
     */
    return {
        'leagues': leagues,
        'leagueTable_id': leagueTable_id
    }
    /**
     * football module API -- leagueTable
     */
    function leagueTable_id(id) { // Auto parses id from query-string
        return footballDb
            .leagueTable(id)
            .then(league => {
                league.title = 'League Table'
                return league
            })
    }
    /**
     * football module API -- leagues
     */
    function leagues() {
        return footballDb
            .leagues()
            .then(leagues => {
                leagues = leaguesWithLinks(leagues)
                return {
                    'title':'Leagues',
                    'leagues': leagues
                }
            })
    }

    /**
     * Utility auxiliary function
     */
    function leaguesWithLinks(leagues) {
        return leagues.map(item => {
            item.leagueHref = "/football/leagueTable/" + item.id
            return item
        })
    }
})()

Change id query parameter to route parameter


$(function() {
  //tabs
	$('ul.tabs li').click(function(){
		var tab_id = $(this).attr('data-tab');

		$('ul.tabs li').removeClass('current');
		$('.tab-content').removeClass('current');

		$(this).addClass('current');
		$("#"+tab_id).addClass('current');
	})

  //retina
  retinajs();

  //sticky navigation
  $("#sticker").sticky({
    topSpacing: 105
  }).on('sticky-end', function() {
    //$("#sticker").addClass("sticky-end");
    //setTimeout(function(){
    //  $("#sticker").removeClass("sticky-end");
    //}, 1000);
  });

  //slider settings
  $('.slick-slider').slick({
    arrows: false,
    infinite: true,
    slidesToShow: 6,
    slidesToScroll: 5,
    responsive: [
      {
        breakpoint: 1024,
        settings: {
          slidesToShow: 3,
          slidesToScroll: 2,
          infinite: true,
        }
      },
      {
        breakpoint: 700,
        settings: {
          slidesToShow: 2,
          slidesToScroll: 1
        }
      }
    ]
  });

  $('header .next').click(function(){
    $(".slick-slider").slick('slickNext');
  });
});

$(function() {
  //tabs
	$('ul.tabs li').click(function(){
		var tab_id = $(this).attr('data-tab');

		$('ul.tabs li').removeClass('current');
		$('.tab-content').removeClass('current');

		$(this).addClass('current');
		$("#"+tab_id).addClass('current');
	})

  //retina
  retinajs();

  //sticky navigation
  $("#sticker").sticky({
    topSpacing: 105
  }).on('sticky-end', function() {
    //$("#sticker").addClass("sticky-end");
    //setTimeout(function(){
    //  $("#sticker").removeClass("sticky-end");
    //}, 1000);
  });

  //slider settings
  $('.slick-slider').slick({
    arrows: false,
    infinite: true,
    slidesToShow: 6,
    slidesToScroll: 3,
    responsive: [
      {
        breakpoint: 1024,
        settings: {
          slidesToShow: 3,
          slidesToScroll: 2,
          infinite: true,
        }
      },
      {
        breakpoint: 700,
        settings: {
          slidesToShow: 2,
          slidesToScroll: 1
        }
      }
    ]
  });

  $('header .next').click(function(){
    $(".slick-slider").slick('slickNext');
  });
});

Change slider scroll count to 3 instead of 5


module.exports = function() {
	return {
		bail: true,
		module: {
			loaders: [
				{
					test: /\.ts$/,
					exclude: /node_modules/,
					loader: 'ts-loader',
				},
			],
		},
		resolve: {
			extensions: ['', '.webpack.js','.web.js', '.js', '.ts'],
		},
	};
};

module.exports = function() {
	return {
		bail: true,
		module: {
			loaders: [
				{
					test: /\.ts$/,
					exclude: /node_modules/,
					loader: 'ts-loader',
				},
			],
		},
		resolve: {
			extensions: ['', '.webpack.js','.web.js', '.ts', '.js'],
		},
	};
};

Resolve typescript files *before* resolving javascript files.


import connect from 'connect';
import faker from 'faker';
import path from 'path';
import serveStatic from 'serve-static';
import uuid from 'node-uuid';
import {Server as WebSocketServer} from 'ws';

connect().use(serveStatic(path.join(__dirname, '../'))).listen(8080);

class ChatServer {
  constructor(port) {
    this.wss = new WebSocketServer({port: port});
    this.wss.on('connection', this.handleConnection);
  }

  sendMessage = (nick, event, data) => {
    const payload = JSON.stringify({
      id: uuid.v4(),
      nick,
      event,
      message: data
    });
    this.wss.clients.forEach(client => client.send(payload));
  };

  handleConnection = (ws) => {
    const nick = faker.internet.userName();
    this.sendMessage(nick, 'join');
    ws.on('message', message => this.sendMessage(nick, 'message', message));
    ws.on('close', message => this.sendMessage(nick, 'leave'));
  }
}

let server = new ChatServer(6639);

process.on('SIGINT', () => {
  setTimeout(process.exit, 100);
});

import connect from 'connect';
import faker from 'faker';
import path from 'path';
import serveStatic from 'serve-static';
import uuid from 'node-uuid';
import {Server as WebSocketServer} from 'ws';

connect().use(serveStatic(path.join(__dirname, '../'))).listen(8080);

class ChatServer {
  constructor(port) {
    this.wss = new WebSocketServer({port});
    this.wss.on('connection', this.handleConnection);
  }

  sendMessage = (nick, event, data) => {
    const payload = JSON.stringify({
      id: uuid.v4(),
      nick,
      event,
      message: data
    });
    this.wss.clients.forEach(client => client.send(payload));
  };

  handleConnection = (ws) => {
    const nick = faker.internet.userName();
    this.sendMessage(nick, 'join');
    ws.on('message', message => this.sendMessage(nick, 'message', message));
    ws.on('close', message => this.sendMessage(nick, 'leave'));
  }
}

let server = new ChatServer(6639);

process.on('SIGINT', () => {
  setTimeout(process.exit, 100);
});

Simplify object in ChatServer constructor.


var network = function () {
    this.port = 8080;
    this.ip = "127.0.0.1";
};

module.exports = new network();

var network = function () {
    this.port = process.env.PORT || 8080;
    this.ip = "0.0.0.0";
};

module.exports = new network();

Change port and ip for azure host


let ivm = require('isolated-vm');
let isolate = new ivm.Isolate;

function makeContext() {
	let context = isolate.createContextSync();
	let global = context.global;
	global.setSync('ivm', ivm);
	isolate.compileScriptSync(`
		function makeReference(ref) {
			return new ivm.Reference(ref);
		}
		function isReference(ref) {
			return ref instanceof ivm.Reference;
		}
	`).runSync(context);
	return {
		makeReference: global.getSync('makeReference'),
		isReference: global.getSync('isReference'),
	};
}

let context1 = makeContext();
let context2 = makeContext();
[ context1, context2 ].forEach(context => {
	if (!context.isReference(new ivm.Reference({}))) {
		console.log('fail1');
	}
	if (!context.isReference(context.makeReference(1))) {
		console.log('fail2');
	}
});
if (context1.isReference(context2.makeReference(1).derefInto())) {
	console.log('fail3');
}
console.log('pass');

let ivm = require('isolated-vm');
let isolate = new ivm.Isolate;

function makeContext() {
	let context = isolate.createContextSync();
	let global = context.global;
	global.setSync('ivm', ivm);
	isolate.compileScriptSync(`
		function makeReference(ref) {
			return new ivm.Reference(ref);
		}
		function isReference(ref) {
			return ref instanceof ivm.Reference;
		}
	`).runSync(context);
	return {
		makeReference: global.getSync('makeReference'),
		isReference: global.getSync('isReference'),
	};
}

let context1 = makeContext();
let context2 = makeContext();
[ context1, context2 ].forEach(context => {
	if (!context.isReference(new ivm.Reference({}))) {
		console.log('fail1');
	}
	if (!context.isReference(context.makeReference({}))) {
		console.log('fail2');
	}
});
if (context1.isReference(context2.makeReference({}).derefInto())) {
	console.log('fail3');
}
console.log('pass');

Fix debug build test failure


'use strict';

var logger = require('./logger'),
    mongoose = require('mongoose');

// Log unexpected events.
var events = ['disconnecting', 'disconnected', 'close', 'reconnected', 'error'];
events.forEach(function(event) {
  mongoose.connection.on(event, function() {
    logger.error('Mongo '+ event, arguments);
  });
});

module.exports = function(callback) {
  var config = require('api-umbrella-config').global();

  // Connect to mongo using mongoose.
  //
  // Note: For this application, we don't particularly need an ODM like
  // Mongoose, and the lower-level mongodb driver would meet our needs.
  // However, when using the standalone driver, we were experiencing
  // intermittent mongo connection drops in staging and production
  // environments. I can't figure out how to reproduce the original issue in a
  // testable way, so care should be taken if switching how mongo connects. See
  // here for more details: https://github.com/NREL/api-umbrella/issues/17
  mongoose.connect(config.get('mongodb.url'), config.get('mongodb.options'), callback);
};

'use strict';

var logger = require('./logger'),
    mongoose = require('mongoose');

// Log unexpected events.
var events = ['disconnecting', 'disconnected', 'close', 'reconnected', 'error'];
events.forEach(function(event) {
  mongoose.connection.on(event, function(error) {
    var logEvent = true;

    if(event === 'disconnecting') {
      mongoose.expectedCloseInProgress = true;
    }

    if(mongoose.expectedCloseInProgress) {
      if(event === 'disconnecting' || event === 'disconnected' || event === 'close') {
        logEvent = false;
      }
    }

    if(event === 'close') {
      mongoose.expectedCloseInProgress = false;
    }

    if(logEvent) {
      logger.error('Mongo event: ' + event, error);
    }
  });
});

module.exports = function(callback) {
  var config = require('api-umbrella-config').global();

  // Connect to mongo using mongoose.
  //
  // Note: For this application, we don't particularly need an ODM like
  // Mongoose, and the lower-level mongodb driver would meet our needs.
  // However, when using the standalone driver, we were experiencing
  // intermittent mongo connection drops in staging and production
  // environments. I can't figure out how to reproduce the original issue in a
  // testable way, so care should be taken if switching how mongo connects. See
  // here for more details: https://github.com/NREL/api-umbrella/issues/17
  mongoose.connect(config.get('mongodb.url'), config.get('mongodb.options'), callback);
};

Improve mongo logging, so we only log unexpected disconnects.


module.exports = {
    steamID: "",
    steamNick: "",
    steamError: "",
    reconnectTries: 0,
    connected: false,
    away: false,
    lastInvite: "",
    lastChat: "",
    ghostTimer: null,
    users: {},   // steamID: { persona_state, player_name, game_name }
    clans: {},   // chatID:  { clan_name, user_count, user_live, members: { steamID: rank } }
    friends: [], // [ steamID, ... ]
    chats: [ "log" ],
    currentChat: "log"
}; // TODO: move steam-specific keys to steam and ui-specific to UI

module.exports = {
    steamID: "",
    steamNick: "",
    steamError: "",
    reconnectTries: 0,
    connected: false,
    away: false,
    lastInvite: "",
    lastChat: "",
    ghostTimer: null,
    users: {},   // steamID: { persona_state, player_name, game_name }
    clans: {},   // chatID:  { clan_name, user_count, user_live, members: { steamID: rank } }
    friends: [], // [ steamID, ... ]
    chats: [], // [ "log" | steamID | chatID, ... ]
    currentChat: "log"
}; // TODO: move steam-specific keys to steam and ui-specific to UI

Fix duplicate log chats bug


const HTTPStatus = require('http-status-codes');

// eslint-disable-next-line no-unused-vars
function handleErrorResponse(err, req, res, next) {
  const responseObject =
    {
      type: err.type || 'about:blank',
      title:
        err.message ||
        HTTPStatus.getStatusText((err.statusCode || HTTPStatus.INTERNAL_SERVER_ERROR)),
      status: err.statusCode || HTTPStatus.INTERNAL_SERVER_ERROR,
    };

  if (typeof err.detail !== 'undefined') {
    responseObject.detail = err.detail;
  }

  res.status(responseObject.status);
  res.send(responseObject);
}

module.exports = handleErrorResponse;

const HTTPStatus = require('http-status-codes');

// eslint-disable-next-line no-unused-vars
function handleErrorResponse(err, req, res, next) {
  const responseObject =
    {
      title:
        err.message ||
        HTTPStatus.getStatusText((err.statusCode || HTTPStatus.INTERNAL_SERVER_ERROR)),
      status: err.statusCode || HTTPStatus.INTERNAL_SERVER_ERROR,
    };

  if (typeof err.detail !== 'undefined') {
    responseObject.detail = err.detail;
  }

  res.status(responseObject.status);
  res.send(responseObject);
}

module.exports = handleErrorResponse;

Implement recommendation for json:api v1 error response


import { connect } from "react-redux"

import * as trackActions from '../actions/trackActions'
import * as playlistActions from '../actions/playlistActions'

import VideoFeed from './VideoFeed'

const mapStateToProps = (state) => ({
  activeFeedId: state.main.show,
  items: state[state.main.show].items,
  selectedId: state[state.main.show].selectedId
})

const mapDispatchToProps = (dispatch) => ({
  tracks: {
    onUpvote: (track) => dispatch(trackActions.upvoteTrack(track)),
    onDownvote: (track) => dispatch(trackActions.downvoteTrack(track)),
    onClickItem: (track) => dispatch(trackActions.setFeedNavigate(track.id))
  },
  playlists: {
    onUpvote: (playlist) => dispatch(playlistActions.upvotePlaylist(playlist)),
    onDownvote: (playlist) => dispatch(playlistActions.downvotePlaylist(playlist)),
    onClickItem: (playlist) => dispatch(playlistActions.setFeedNavigate(playlist.id))
  }
})

const mergeProps = (stateProps, dispatchProps, ownProps) => ({
  ...ownProps,
  ...stateProps,
  ...dispatchProps[stateProps.activeFeedId]
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(VideoFeed)

import { connect } from "react-redux"
import _ from 'lodash'

import * as trackActions from '../actions/trackActions'
import * as playlistActions from '../actions/playlistActions'

import VideoFeed from './VideoFeed'

const mapStateToProps = (state) => ({
  activeFeedId: state.main.show,
  items: _.orderBy(state[state.main.show].items, ["value", "created_at"], ['desc', 'asc']),
  selectedId: state[state.main.show].selectedId
})

const mapDispatchToProps = (dispatch) => ({
  tracks: {
    onUpvote: (track) => dispatch(trackActions.upvoteTrack(track)),
    onDownvote: (track) => dispatch(trackActions.downvoteTrack(track)),
    onClickItem: (track) => dispatch(trackActions.setFeedNavigate(track.id))
  },
  playlists: {
    onUpvote: (playlist) => dispatch(playlistActions.upvotePlaylist(playlist)),
    onDownvote: (playlist) => dispatch(playlistActions.downvotePlaylist(playlist)),
    onClickItem: (playlist) => dispatch(playlistActions.setFeedNavigate(playlist.id))
  }
})

const mergeProps = (stateProps, dispatchProps, ownProps) => ({
  ...ownProps,
  ...stateProps,
  ...dispatchProps[stateProps.activeFeedId]
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(VideoFeed)

Sort items by votes annd created at


'use strict';

// Load requirements
const parser = require('parse-torrent-name');

// Create promise to resolve with dataset
module.exports = function(filename) {
  return new Promise((resolve, reject) => {

    // Variables
    let source;

    // No file provided
    if ( filename === undefined ) {
      return reject('No filename provided');
    }

    // Check for tmdb key
    /* istanbul ignore if */
    if ( process.env.TMDB_KEY ) {
      source = require('./show/tmdb')(process.env.TMDB_KEY);
    } else {
      source = require('./show/tvmaze')();
    }

    // Decode episode details from filename
    let details = parser(filename);

    // Check we have what we need to continue
    // TODO: Replace with better validation
    if ( ! details.title || ! details.season || ! details.episode ) {
      return resolve({});
    }

    // Check for the show in cache
    source.findShow(details.title).then((show) => {
      return source.findEpisode(details.title, details.season, details.episode);
    }).then((episode) => {
      episode.type = 'show';
      return resolve(episode);
    }).catch((err) => {
      return reject(err);
    });
  });
};

'use strict';

// Load requirements
const parser = require('parse-torrent-name');

// Load libraries
const settings = __require('libs/settings');

// Create promise to resolve with dataset
module.exports = function(filename) {
  return new Promise((resolve, reject) => {

    // Variables
    let source;

    // No file provided
    if ( filename === undefined ) {
      return reject('No filename provided');
    }

    // Check for tmdb key
    /* istanbul ignore if */
    if ( process.env.TMDB_KEY) {
      source = require('./show/tmdb')(process.env.TMDB_KEY);
    } else if ( settings.tmdb && settings.tmdb.key ) {
      source = require('./show/tmdb')(settings.tmdb.key);
    } else {
      source = require('./show/tvmaze')();
    }

    // Decode episode details from filename
    let details = parser(filename);

    // Check we have what we need to continue
    // TODO: Replace with better validation
    if ( ! details.title || ! details.season || ! details.episode ) {
      return resolve({});
    }

    // Check for the show in cache
    source.findShow(details.title).then((show) => {
      return source.findEpisode(details.title, details.season, details.episode);
    }).then((episode) => {
      episode.type = 'show';
      return resolve(episode);
    }).catch((err) => {
      return reject(err);
    });
  });
};

Add settings support for tmdb keys


"use strict";

var assert = require("@sinonjs/referee").assert;
var createSet = require("./create-set");

describe("createSet", function() {
    describe("when called without arguments", function() {
        it("returns an empty Set", function() {
            var set = createSet();

            assert.isSet(set);
            assert.equals(set.size, 0);
        });
    });

    describe("when called with a non-empty Array", function() {
        it("returns a Set with the same (distinct) members", function() {
            var array = [0, 1, 1, 1, 1, 2, 3, 4];
            var set = createSet(array);

            set.forEach(function(value) {
                assert.isTrue(array.includes(value));
            });
        });
    });

    describe("when called with non-Array or empty Array argument", function() {
        it("throws a TypeError", function() {
            var invalids = [
                {},
                new Date(),
                new Set(),
                new Map(),
                null,
                undefined
            ];

            invalids.forEach(function(value) {
                assert.exception(function() {
                    createSet(value);
                }, /createSet can be called with either no arguments or an Array/);
            });
        });
    });
});

"use strict";

var assert = require("@sinonjs/referee").assert;
var createSet = require("./create-set");

describe("createSet", function() {
    describe("when called without arguments", function() {
        it("returns an empty Set", function() {
            var set = createSet();

            assert.isSet(set);
            assert.equals(set.size, 0);
        });
    });

    describe("when called with a non-empty Array", function() {
        it("returns a Set with the same (distinct) members", function() {
            var array = [0, 1, 1, 1, 1, 2, 3, 4];
            var set = createSet(array);

            set.forEach(function(value) {
                assert.isTrue(array.indexOf(value) !== -1);
            });
        });
    });

    describe("when called with non-Array or empty Array argument", function() {
        it("throws a TypeError", function() {
            var invalids = [
                {},
                new Date(),
                new Set(),
                new Map(),
                null,
                undefined
            ];

            invalids.forEach(function(value) {
                assert.exception(function() {
                    createSet(value);
                }, /createSet can be called with either no arguments or an Array/);
            });
        });
    });
});

Fix broken build in IE11


'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const marked = require('marked');
const ECT = require('ect');

function md2html(filePath, cb) {
  fs.readFile(filePath, 'utf8', (err, mdString) => {
    if (err) {
      cb(err);
      return;
    }
    const content = marked(mdString);
    const renderer = ECT({ root : __dirname });
    const data = { title: filePath, content : content, dirname : __dirname };
    const html = renderer.render('template.ect', data);
    cb(null, html);
  });
}

function toHtmlFile(mdFilePath, cb) {
  md2html(mdFilePath, (err, html) => {
    if (err) {
      cb(err);
      return;
    }
    const name = path.basename(mdFilePath, path.extname(mdFilePath))
    const htmlPath = path.join(os.tmpdir(), name + '.html');
    fs.writeFile(htmlPath, html, (err) => {
      cb(err, htmlPath)
    });
  });
}

exports.toHtmlFile = toHtmlFile;

'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const marked = require('marked');
const ECT = require('ect');

function md2html(filePath, cb) {
  fs.readFile(filePath, 'utf8', (err, mdString) => {
    if (err) {
      cb(err);
      return;
    }
    const markedRenderer = new marked.Renderer();
    markedRenderer.image = function(href, title, text) {
      const imgAbsolutePath = path.resolve(path.dirname(filePath), href);
      return marked.Renderer.prototype.image.call(this, imgAbsolutePath, title, text);
    }
    const content = marked(mdString, { renderer: markedRenderer });
    const data = { title: filePath, content : content, dirname : __dirname };
    const renderer = ECT({ root : __dirname });
    const html = renderer.render('template.ect', data);
    cb(null, html);
  });
}

function toHtmlFile(mdFilePath, cb) {
  md2html(mdFilePath, (err, html) => {
    if (err) {
      cb(err);
      return;
    }
    const name = path.basename(mdFilePath, path.extname(mdFilePath))
    const htmlPath = path.join(os.tmpdir(), name + '.html');
    fs.writeFile(htmlPath, html, (err) => {
      cb(err, htmlPath)
    });
  });
}

exports.toHtmlFile = toHtmlFile;

Fix image file path at generated html file.


const { get } = require('lodash')
const { client } = require('nightwatch-cucumber')
const { When } = require('cucumber')

When(/^I (?:navigate|go|open|visit).*? `(.+)` page$/, async function (pageName) {
  try {
    const page = get(client.page, pageName)
    await page().navigate()
  } catch (error) {
    throw new Error(`The page object '${pageName}' does not exist`)
  }
})

const { assign, camelCase, find, get, set } = require('lodash')
const { client } = require('nightwatch-cucumber')
const { When } = require('cucumber')

const fixtures = require('../../features/setup/fixtures')

When(/^I (?:navigate|go|open|visit).*? `(.+)` page$/, async function (pageName) {
  try {
    const page = get(client.page, pageName)
    await page().navigate()
  } catch (error) {
    throw new Error(`The page object '${pageName}' does not exist`)
  }
})

When(/^I (?:navigate|go|open|visit).*? `(.+)` page using `(.+)` `(.+)` fixture$/, async function (pageName, entity, fixtureName) {
  const Page = get(client.page, pageName)()

  const fixture = find(fixtures[entity], { name: fixtureName })

  // TODO: Need to find a way to remove needing to store the item in state
  const entityTypeFieldName = camelCase(entity)
  set(this.state, entityTypeFieldName, assign({}, get(this.state, entityTypeFieldName), fixture))

  await Page.navigate(Page.url(fixtureName))
})

Add support for a new shared fixture step definition


var path = '../data/snippets';

// TODO list this folder

var fs = require('fs');
var os = require('os');

var path = '../data/snippets';

var folders = fs.readdirSync(path);
var files = {};

folders.forEach(function (folder) {
    files[folder] = fs.readdirSync(path+'/'+folder);
});

console.log(files);

Read files with NodeJS script


Package.describe({
  summary: "Reactive bootstrap modals for meteor",
  version: "1.0.4",
  git: "https://github.com/jchristman/reactive-modal.git",
  name: "jchristman:reactive-modal"
});

Package.on_use(function (api) {
  if(api.versionsFrom){
    api.versionsFrom('METEOR@1.0');
  }
  api.use(['underscore', 'jquery','templating', 'reactive-var@1.0.5'], 'client');
  api.add_files(['lib/reactive-modal.html', 'lib/reactive-modal.js', 'lib/ev.js'], "client");
  api.export('ReactiveModal', ['client']);
});

Package.describe({
  summary: "Reactive bootstrap modals for meteor",
  version: "1.0.5",
  git: "https://github.com/jchristman/reactive-modal.git",
  name: "jchristman:reactive-modal"
});

Package.on_use(function (api) {
  if(api.versionsFrom){
    api.versionsFrom('METEOR@1.0');
  }
  api.use(['underscore', 'jquery','templating', 'reactive-var@1.0.6'], 'client');
  api.add_files(['lib/reactive-modal.html', 'lib/reactive-modal.js', 'lib/ev.js'], "client");
  api.export('ReactiveModal', ['client']);
});

Update depedencies and version number


'use strict';

// Load our requirements
const i18n = require('i18n'),
      path = require('path'),
      logger = require('../log');

// Variables
let localeDir = path.resolve('./locales');

// Configure the localization engine
i18n.configure({
  locales: require('./available-locales')(localeDir),
  defaultLocale: 'en',
  directory: localeDir,
  syncFiles: true,
  autoReload: true,
  register: global,
  api: {
    '__': 'lang',
    '__n': 'plural'
  },
  logDebugFn: (msg) => {
    return logger.debug(msg);
  },
  logWarnFn: (msg) => {
    return logger.warn(msg);
  },
  logErrorFn: (msg) => {
    return logger.error(msg);
  }
});

// Set the default locale
i18n.setLocale(( process.env.LANG !== undefined ? process.env.LANG : 'en' ));

// Export for future use
module.exports = i18n;

'use strict';

// Load our requirements
const i18n = require('i18n'),
      path = require('path'),
      logger = require('../log');

// Variables
let localeDir = path.resolve('./locales');

// Configure the localization engine
i18n.configure({
  locales: require('./available-locales')(localeDir),
  defaultLocale: 'en',
  directory: localeDir,
  syncFiles: true,
  autoReload: true,
  register: global,
  api: {
    '__': 'lang',
    '__n': 'plural'
  }
});

// Set the default locale
i18n.setLocale( process.env.LOCALE || 'en' );

// Export for future use
module.exports = i18n;

Revert to default logger for i18n


var eslint = require('eslint')
var path = require('path')
var pkg = require('./package.json')

module.exports = {
  bugs: pkg.bugs.url,
  cmd: 'standard',
  cwd: __dirname,
  eslint: eslint,
  eslintConfig: {
    configFile: path.join(__dirname, 'eslintrc.json')
  },
  formatter: 'Formatting is no longer included with standard. Install it separately: "npm install -g standard-format"',
  homepage: pkg.homepage,
  tagline: 'Use JavaScript Standard Style',
  version: pkg.version
}

var eslint = require('eslint')
var path = require('path')
var pkg = require('./package.json')

module.exports = {
  bugs: pkg.bugs.url,
  cmd: 'standard',
  eslint: eslint,
  eslintConfig: {
    configFile: path.join(__dirname, 'eslintrc.json')
  },
  formatter: 'Formatting is no longer included with standard. Install it separately: "npm install -g standard-format"',
  homepage: pkg.homepage,
  tagline: 'Use JavaScript Standard Style',
  version: pkg.version
}

Remove cwd option to standard-engine


'use strict';

import * as _ from 'lodash'
import * as React from 'react'
import * as humanize from 'humanize-plus'

var Course = React.createClass({
	render() {
		let title = this.props.info.type === 'Topic' ? this.props.info.name : this.props.info.title;

		let summary = React.DOM.article({className: 'course'},
			React.DOM.div({className: 'info-rows'},
				React.DOM.h1({className: 'title'}, title),
				React.DOM.span({className: 'details'},
					React.DOM.span({className: 'identifier'},
						React.DOM.span({className: 'department'}, this.props.info.dept),
						' ', React.DOM.span({className: 'number'}, this.props.info.num),
						this.props.info.sect ?
							React.DOM.span({className: 'section'}, this.props.info.sect) :
							''),
					React.DOM.span({className: 'professors'}, humanize.oxford(this.props.info.profs)))
			),
			React.DOM.div({
				className: 'info-button',
			})
		)

		return summary;
	}
})

export default Course

'use strict';

import * as _ from 'lodash'
import * as React from 'react'
import * as humanize from 'humanize-plus'

import {DragDropMixin} from '../../node_modules/react-dnd/dist/ReactDND.min'
import itemTypes from '../objects/itemTypes'

var Course = React.createClass({
	mixins: [DragDropMixin],

	configureDragDrop(registerType) {
		registerType(itemTypes.COURSE, {
			dragSource: {
				beginDrag() {
					return {
						item: {clbid: this.props.info.clbid}
					}
				}
			}
		})
	},

	render() {
		let title = this.props.info.type === 'Topic' ? this.props.info.name : this.props.info.title;

		let summary = React.DOM.article(
			Object.assign({className: 'course'}, this.dragSourceFor(itemTypes.COURSE)),

			React.DOM.div({className: 'info-rows'},
				React.DOM.h1({className: 'title'}, title),
				React.DOM.span({className: 'details'},
					React.DOM.span({className: 'identifier'},
						React.DOM.span({className: 'department'}, this.props.info.dept),
						' ', React.DOM.span({className: 'number'}, this.props.info.num),
						this.props.info.sect ?
							React.DOM.span({className: 'section'}, this.props.info.sect) :
							''),
					React.DOM.span({className: 'professors'}, humanize.oxford(this.props.info.profs)))
			),
			React.DOM.div({
				className: 'info-button',
			})
		)

		return summary;
	}
})

export default Course

Make Course draggable When dropped, it returns {clbid: <it's clbid>}


//////////////////////////////
// Handles the Ticket model //
//////////////////////////////

'use strict';

module.exports = function (sequelize, DataTypes) {
    var Ticket = sequelize.define('Ticket', {
        id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
        },

        username: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false
        },

        student: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        contributor: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        paid: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        paid_at: {
            type: DataTypes.DATE,
        },

        paid_with: {
            type: DataTypes.STRING(45)
        },

        temporarlyOut: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        }

        // Association with Price
        // Association with Event
        // Association with Mean of Payment
    }, {
        underscored: true,
        paranoid: true
    });

    return Ticket;
};

//////////////////////////////
// Handles the Ticket model //
//////////////////////////////

'use strict';

module.exports = function (sequelize, DataTypes) {
    var Ticket = sequelize.define('Ticket', {
        id: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false,
            autoIncrement: true,
            primaryKey: true,
        },

        username: {
            type: DataTypes.INTEGER(10).UNSIGNED,
            allowNull: false
        },

        student: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        contributor: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        paid: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        paid_at: {
            type: DataTypes.DATE,
        },

        paid_with: {
            type: DataTypes.STRING(45)
        },

        temporarlyOut: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false
        },

        barcode: {
            type: DataTypes.STRING(13),
            allowNull: false,
            unique: true
        }

        // Association with Price
        // Association with Event
        // Association with Mean of Payment
    }, {
        underscored: true,
        paranoid: true
    });

    return Ticket;
};

Add barcode to Ticket db structure


/**
 * @copyright Copyright (c) 2012-2015 2amigOS! Consulting Group LLC
 * @link http://2amigos.us
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 */
if (typeof dosamigos == "undefined" || !dosamigos) {
    var dosamigos = {};
}

dosamigos.ckEditorWidget = (function ($) {

    var pub = {
        registerOnChangeHandler: function (id) {
            CKEDITOR && CKEDITOR.instances[id] && CKEDITOR.instances[id].on('change', function () {
                CKEDITOR.instances[id].updateElement();
                $('#' + id).trigger('change');
                return false;
            });
        },
        registerCsrfImageUploadHandler: function () {
            yii & $(document).off('click', '.cke_dialog_tabs a[title="Upload"]').on('click', '.cke_dialog_tabs a[title="Upload"]', function () {
                var $forms = $('.cke_dialog_ui_input_file iframe').contents().find('form');
                var csrfName = yii.getCsrfParam();
                $forms.each(function () {
                    if (!$(this).find('input[name=' + csrfName + ']').length) {
                        var csrfTokenInput = $('<input/>').attr({
                            'type': 'hidden',
                            'name': csrfName
                        }).val(yii.getCsrfToken());
                        $(this).append(csrfTokenInput);
                    }
                });
            });
        }
    };
    return pub;
})(jQuery);

/**
 * @copyright Copyright (c) 2012-2015 2amigOS! Consulting Group LLC
 * @link http://2amigos.us
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
 */
if (typeof dosamigos == "undefined" || !dosamigos) {
    var dosamigos = {};
}

dosamigos.ckEditorWidget = (function ($) {

    var pub = {
        registerOnChangeHandler: function (id) {
            CKEDITOR && CKEDITOR.instances[id] && CKEDITOR.instances[id].on('change', function () {
                CKEDITOR.instances[id].updateElement();
                $('#' + id).trigger('change');
                return false;
            });
        },
        registerCsrfImageUploadHandler: function () {
            yii & $(document).off('click', '.cke_dialog_tabs a[id^="cke_Upload_"]').on('click', '.cke_dialog_tabs a[id^="cke_Upload_"]', function () {
                var $forms = $('.cke_dialog_ui_input_file iframe').contents().find('form');
                var csrfName = yii.getCsrfParam();
                $forms.each(function () {
                    if (!$(this).find('input[name=' + csrfName + ']').length) {
                        var csrfTokenInput = $('<input/>').attr({
                            'type': 'hidden',
                            'name': csrfName
                        }).val(yii.getCsrfToken());
                        $(this).append(csrfTokenInput);
                    }
                });
            });
        }
    };
    return pub;
})(jQuery);

Fix bug when locale isn't English.


Package.describe({
  summary: "meteor sql bindings for server-side calls"
});

Npm.depends({ "newrelic": "1.0.1" });

Package.on_use(function(api) {
  if (api.export) api.export('newrelic', 'server');
  api.add_files('newrelic_npm.js', 'server');
});

Package.describe({
  summary: "meteor sql bindings for server-side calls"
});

Npm.depends({ "newrelic": "1.3.0" });

Package.on_use(function(api) {
  if (api.export) api.export('newrelic', 'server');
  api.add_files('newrelic_npm.js', 'server');
});

Update New Relic library to 1.3.0


this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('learn-memory').then(function(cache) {
      return cache.addAll([
        '/',
        '/stylesheets/styles.css',
        '/views/lessons-list.html',
        '/views/lessons-id.html',
        '/javascripts/scripts.js',
        '/langs/locale-en.json'
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  if (event.request.method !== 'GET' || /authenticated/.test(event.request.url)) {
    return;
  }
  var get = function () {
    return fetch(event.request).then(function(response) {
      return caches.open('learn-memory').then(function(cache) {
        cache.put(event.request, response.clone());
        return response;
      });
    });
  };

  event.respondWith(
    caches
      .match(event.request)
      .then(function(cached) {
        // get the latest updates from API
        if (/api/.test(event.request.url)) {
          return get().catch(function () {
            return cached;
          });
        }

        // the cached value could be undefined
        if (typeof cached == 'undefined') {
      	  return get();
      	}

        return cached;
      })
      .catch(get)
  );
});

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('learn-memory-1500879945180').then(function(cache) {
      return cache.addAll([
        '/',
        '/stylesheets/styles.css',
        '/views/lessons-list.html',
        '/views/lessons-id.html',
        '/javascripts/scripts.js',
        '/langs/locale-en.json'
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  if (event.request.method !== 'GET' || /authenticated/.test(event.request.url)) {
    return;
  }
  var get = function () {
    return fetch(event.request).then(function(response) {
      return caches.open('learn-memory').then(function(cache) {
        cache.put(event.request, response.clone());
        return response;
      });
    });
  };

  event.respondWith(
    caches
      .match(event.request)
      .then(function(cached) {
        // get the latest updates from API
        if (/api/.test(event.request.url)) {
          return get().catch(function () {
            return cached;
          });
        }

        // the cached value could be undefined
        if (typeof cached == 'undefined') {
      	  return get();
      	}

        return cached;
      })
      .catch(get)
  );
});

this.addEventListener('activate', function(event) {
  var cacheWhitelist = ['learn-memory-1500879945180'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

Add a system to update service worker


class Player {

  constructor({ socket }) {
    // The identifier for this player, unique on a per-room level
    this.id = Date.now();
    // The current socket assocuat
    this.socket = socket;
    // The player's total number of wins across all games
    this.score = 0;
  }

}

module.exports = Player;

class Player {

  constructor({ socket }) {
    // The identifier for this player, unique on a per-room level
    this.id = Date.now();
    // The current socket assocuat
    this.socket = socket;
    // The player's total number of wins across all games
    this.score = 0;
  }

  toJSON() {
    return {
      name: this.name,
      score: this.score
    };
  }

}

module.exports = Player;

Fix infinite recursion on server


function initSettings() {
  var globalSettings = getGlobalSettings();
  if (globalSettings['hw_accel']) {
    $('hw_accel').value = globalSettings['hw_accel'];
  }
}

function onForget() {
  resetSiteSchemes();
  loadSettingsDisplay();
  update();
}

// Open all links in new tabs.
function onLinkClick() {
  var links = document.getElementsByTagName("a");
  for (var i = 0; i < links.length; i++) {
    (function () {
      var ln = links[i];
      var location = ln.href;
      ln.onclick = function () {
          chrome.tabs.create({active: true, url: location});
      };
    })();
  }
}

function onHwAccel(evt) {
  setGlobalSetting('hw_accel', evt.target.value);
}

function loadSettingsDisplay() {
  var settings = {
    'version': 1,
    'schemes': JSON.parse(localStorage['siteschemes']),
    'modifiers': JSON.parse(localStorage['sitemodifiers'] || '{}')
  }
  $('settings').value = JSON.stringify(settings, null, 4);
}

function init() {
  initSettings();
  $('forget').addEventListener('click', onForget, false);
  $('hw_accel').addEventListener('change', onHwAccel, false);
  loadSettingsDisplay();
}

window.addEventListener('load', init, false);
document.addEventListener('DOMContentLoaded', onLinkClick);

function initSettings() {
  var globalSettings = getGlobalSettings();
  if (globalSettings['hw_accel']) {
    $('hw_accel').value = globalSettings['hw_accel'];
  }
}

function onForget() {
  resetSiteSchemes();
  loadSettingsDisplay();
}

// Open all links in new tabs.
function onLinkClick() {
  var links = document.getElementsByTagName("a");
  for (var i = 0; i < links.length; i++) {
    (function () {
      var ln = links[i];
      var location = ln.href;
      ln.onclick = function () {
          chrome.tabs.create({active: true, url: location});
      };
    })();
  }
}

function onHwAccel(evt) {
  setGlobalSetting('hw_accel', evt.target.value);
}

function loadSettingsDisplay() {
  var settings = {
    'version': 1,
    'schemes': JSON.parse(localStorage['siteschemes']),
    'modifiers': JSON.parse(localStorage['sitemodifiers'] || '{}')
  }
  $('settings').value = JSON.stringify(settings, null, 4);
}

function init() {
  initSettings();
  $('forget').addEventListener('click', onForget, false);
  $('hw_accel').addEventListener('change', onHwAccel, false);
  loadSettingsDisplay();
}

window.addEventListener('load', init, false);
document.addEventListener('DOMContentLoaded', onLinkClick);

Fix error clearing site customizations


import React from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'
import { compose, getContext, mapProps, withHandlers } from 'recompose'

export const connectIO = (handlers) => compose(
  getContext({
    runIO: PropTypes.func
  }),
  withHandlers(_.mapValues(handlers, (handler) => {
    return ({ runIO, ...props }) => (...args) => {
      return runIO(handler(props)(...args))
    }
  })),
  mapProps(({ runIO, ...props }) => props)
)

export default connectIO

Remove unused reference to React


Package.describe({
	summary: "Methods to create admin role and add first admin user.",
  version: "0.1.0",
	name: "brylie:first-admin",
  git: "https://github.com/brylie/meteor-first-user-admin"
});

Package.on_use(function (api, where) {
  api.versionsFrom("1.0.1");
	api.use('accounts-base', ['server']);
	api.use("alanning:roles@1.2.11", ['server']);
	api.use("underscore", ['server']);

	api.add_files('server/methods/roles.js', ['server']);
	api.add_files('server/methods/users.js', ['server']);
});

Package.describe({
  summary: "Methods to create admin role and add first admin user.",
  version: "0.1.1",
  name: "brylie:first-admin",
  git: "https://github.com/brylie/meteor-first-user-admin"
});

Package.on_use(function (api, where) {
  api.versionsFrom("1.0.1");
  api.use('accounts-base', ['server']);
  api.use("alanning:roles@1.2.11", ['server']);

  api.add_files('server/methods/roles.js', ['server']);
  api.add_files('server/methods/users.js', ['server']);
});

Remove underscore dependency; cleanup whitespace


Package.describe({
    name: 'nimblenotes:typed.js',
    version: '0.0.1',
    summary: 'Typed.js v1.1.1',
    git: 'https://github.com/NimbleNotes/nimblenotes-packages',
    documentation: 'README.md'
});

Package.onUse(function (api) {
    api.versionsFrom('1.1.0.2');

    api.use([
        'jquery',
        'nimblenotes:jquery-autosize@0.0.1'
    ], 'client');
    api.addFiles([
      'js/typed.js'
    ], 'client');
});

Package.describe({
    name: 'nimblenotes:typed.js',
    version: '0.0.1',
    summary: 'Typed.js v1.1.1',
    git: 'https://github.com/NimbleNotes/nimblenotes-packages',
    documentation: 'README.md'
});

Package.onUse(function (api) {
    api.versionsFrom('1.1.0.2');

    api.use([
        'jquery',
    ], 'client');
    api.addFiles([
      'js/typed.js'
    ], 'client');
});

Remove dependency on jquery autosize


'use strict';

var fs = require('fs'), files = fs.readdirSync('./lib/resources');
var resources = {}, resource, i = 0, l = files.length;

for(; i < l; i++) {
  resource = files[i];

  if(resource === 'index.js'        ||
     resource === 'resourceBase.js' ||
     resource.indexOf('.js') < 0) {
    continue;
  }

  resource = resource.replace('.js', '');
  resources[resource] = require('./' + resource);
}

module.exports = resources;

'use strict';

var fs = require('fs'), files = fs.readdirSync(__dirname);
var resources = {}, resource, i = 0, l = files.length;

for(; i < l; i++) {
  resource = files[i];

  if(resource === 'index.js'        ||
     resource === 'resourceBase.js' ||
     resource.indexOf('.js') < 0) {
    continue;
  }

  resource = resource.replace('.js', '');
  resources[resource] = require('./' + resource);
}

module.exports = resources;

Fix resource loader file path being relative to running script. Use current directory global instead.


Package.describe({
  summary: "Jade template language for Meteor"
});

Package._transitional_registerBuildPlugin({
  name: "compileJade",
  use: [
    "underscore",
    "html-tools",
    "spacebars-compiler",
  ],
  sources: [
    "plugin/lexer.js",
    "plugin/parser.js",
    "plugin/filters.js",
    "plugin/compiler.js",
    "plugin/handler.js",
  ],
  npmDependencies: {
    "jade": "1.1.5",
    "markdown": "0.5.0",
  }
});

Package.on_test(function (api) {
  api.use("jade");
  api.use("tinytest");
  api.add_files("tests/tests.jade");
  api.add_files("tests/client.js", "client");
  api.add_files("tests/server.js", "server");
});

Package.describe({
  summary: "Jade template language for Meteor"
});

Package._transitional_registerBuildPlugin({
  name: "compileJade",
  use: [
    "underscore",
    "html-tools",
    "spacebars-compiler",
  ],
  sources: [
    "plugin/lexer.js",
    "plugin/parser.js",
    "plugin/filters.js",
    "plugin/compiler.js",
    "plugin/handler.js",
  ],
  npmDependencies: {
    "jade": "1.2.0",
    "markdown": "0.5.0",
  }
});

Package.on_test(function (api) {
  api.use("jade");
  api.use("tinytest");
  api.add_files("tests/tests.jade");
  api.add_files("tests/client.js", "client");
  api.add_files("tests/server.js", "server");
});

Upgrade jade from 1.1.5 to 1.2.0


import React from "react";
import classes from "./ShortSGonksRow.module.css";

const ShortSGonksRow = (props) => {
  console.log(props);
  const getDiffIndicator = () => {
    const diff =
      props.datapoints[props.datapoints.length - 1] >
      props.datapoints[props.datapoints.length - 2]
        ? "+"
        : props.datapoints[props.datapoints.length - 1] <
          props.datapoints[props.datapoints.length - 2]
        ? "-"
        : "=";

    const style =
      diff == "+"
        ? { color: "green" }
        : diff == "-"
        ? { color: "red" }
        : { color: "orange" };
    return (

        {diff}

    );
  };
  return (

      {getDiffIndicator()}

{props.searchterm}

$t{props.currentprice}

  );
};

export default ShortSGonksRow;

import React from "react";
import classes from "./ShortSGonksRow.module.css";

const ShortSGonksRow = (props) => {
  console.log(props);
  const getDiffIndicator = () => {
    const priceDelta =
      props.datapoints[props.datapoints.length - 1] -
      props.datapoints[props.datapoints.length - 2];
    const diff = priceDelta > 0 ? "+" : priceDelta < 0 ? "-" : "=";

    const style =
      diff == "+"
        ? { color: "green" }
        : diff == "-"
        ? { color: "red" }
        : { color: "orange" };
    return (

        {diff}

    );
  };
  return (

      {getDiffIndicator()}

{props.searchterm}

$t{props.currentprice}

  );
};

export default ShortSGonksRow;

Update price change indicator code to be more readable


import localForage from 'localforage';
import { withClientState } from 'apollo-link-state';
import { DialerInfo } from './queries';
import { CONFERENCE_PHONE_NUMBER } from '../config';

let defaultPhoneNumber;
(async () => {
  defaultPhoneNumber =
    (await localForage.getItem('dialer/CONFERENCE_PHONE_NUMBER')) || CONFERENCE_PHONE_NUMBER;
})();

export default withClientState({
  Query: {
    dialer: () => ({
      __typename: 'Dialer',
      phoneNumber: defaultPhoneNumber,
    }),
  },
  Mutation: {
    updateDialer: (_, { input: { phoneNumber } = {} }, { cache }) => {
      const currentDialerQuery = cache.readQuery({ query: DialerInfo, variables: {} });
      const updatedDialer = {
        ...currentDialerQuery.dialer,
        phoneNumber,
      };
      cache.writeQuery({
        query: DialerInfo,
        data: {
          dialer: updatedDialer,
        },
      });
      localForage.setItem('dialer/CONFERENCE_PHONE_NUMBER', phoneNumber);
      return updatedDialer;
    },
  },
});

import localForage from 'localforage';
import { withClientState } from 'apollo-link-state';
import { DialerInfo } from './queries';
import { CONFERENCE_PHONE_NUMBER } from '../config';

// TODO refactor after https://github.com/apollographql/apollo-link-state/issues/119 is resolved
let persistedPhoneNumber;
(async () => {
  persistedPhoneNumber = await localForage.getItem('dialer/PHONE_NUMBER');
})();

export default withClientState({
  Query: {
    dialer: () => ({
      __typename: 'Dialer',
      phoneNumber: persistedPhoneNumber || CONFERENCE_PHONE_NUMBER,
    }),
  },
  Mutation: {
    updateDialer: (_, { input: { phoneNumber } = {} }, { cache }) => {
      const currentDialerQuery = cache.readQuery({ query: DialerInfo, variables: {} });
      const updatedDialer = {
        ...currentDialerQuery.dialer,
        phoneNumber,
      };
      cache.writeQuery({
        query: DialerInfo,
        data: {
          dialer: updatedDialer,
        },
      });
      localForage.setItem('dialer/PHONE_NUMBER', phoneNumber);
      return updatedDialer;
    },
  },
});

Fix persistence in private tabs


var mongoose = require('mongoose');
var Category = require('./category');

var productSchema = {
  name: {
    type: String,
    required: true
  },
  pictures: [{
    type: String,
    // pictures must start with http://
    match: /^http:\/\//i
  }],
  price: {
    amount: {
      type: Number,
      required: true
    },
    currency: {
      type: String,
      // only three currencies supported for now
      enum: ['USD', 'EUR', 'GPB'],
      required: true
    }
  },
  category: Category.categorySchema
};

module.exports = mongoose.Schema(productSchema);
exports.productSchema = productSchema;

var mongoose = require('mongoose');
var Category = require('./category');

var productSchema = {
  name: {
    type: String,
    required: true
  },
  pictures: [{
    type: String,
    // pictures must start with http://
    match: /^http:\/\//i
  }],
  price: {
    amount: {
      type: Number,
      required: true
    },
    currency: {
      type: String,
      // only three currencies supported for now
      enum: ['USD', 'EUR', 'GPB'],
      required: true
    }
  },
  category: Category.categorySchema
};

var schema = mongoose.Schema(productSchema);

var currencySymbols = {
  'USD': '$',
  'EUR': '€',
  'GBP': '£'
}

schema.virtual('displayPrice').get(function() {
  return currencySymbols[this.price.currency] + this.price.amount;
});

schema.set('toObject', { virtuals: true });
schema.set('toJSON', { virtuals: true });

module.exports = schema;
exports.productSchema = productSchema;

Add currency symbols and displayPrice virtual


// Note that this file is required before we install our Babel hooks in
// ../tool-env/install-babel.js, so we can't use ES2015+ syntax here.

var win32Extensions = {
  node: ".exe",
  npm: ".cmd"
};

// The dev_bundle/bin command has to come immediately after the meteor
// command, as in `meteor npm` or `meteor node`, because we don't want to
// require("./main.js") for these commands.
var devBundleBinCommand = process.argv[2];
var args = process.argv.slice(3);

function getChildProcess() {
  if (! win32Extensions.hasOwnProperty(devBundleBinCommand)) {
    return Promise.resolve(null);
  }

  var helpers = require("./dev-bundle-bin-helpers.js");

  if (process.platform === "win32") {
    devBundleBinCommand += win32Extensions[devBundleBinCommand];
  }

  return Promise.all([
    helpers.getCommandPath(devBundleBinCommand),
    helpers.getEnv()
  ]).then(function (cmdAndEnv) {
    var cmd = cmdAndEnv[0];
    var env = cmdAndEnv[1];
    var child = require("child_process").spawn(cmd, args, {
      stdio: "inherit",
      env: env
    });

    require("./flush-buffers-on-exit-in-windows.js");

    child.on("exit", function (exitCode) {
      process.exit(exitCode);
    });

    return child;
  });
}

module.exports = getChildProcess();

// Note that this file is required before we install our Babel hooks in
// ../tool-env/install-babel.js, so we can't use ES2015+ syntax here.

var win32Extensions = {
  node: ".exe",
  npm: ".cmd"
};

// The dev_bundle/bin command has to come immediately after the meteor
// command, as in `meteor npm` or `meteor node`, because we don't want to
// require("./main.js") for these commands.
var devBundleBinCommand = process.argv[2];
var args = process.argv.slice(3);

function getChildProcess() {
  if (! win32Extensions.hasOwnProperty(devBundleBinCommand)) {
    return Promise.resolve(null);
  }

  var helpers = require("./dev-bundle-bin-helpers.js");

  if (process.platform === "win32") {
    devBundleBinCommand += win32Extensions[devBundleBinCommand];
  }

  return Promise.all([
    helpers.getCommandPath(devBundleBinCommand),
    helpers.getEnv()
  ]).then(function (cmdAndEnv) {
    var cmd = cmdAndEnv[0];
    var env = cmdAndEnv[1];
    var child = require("child_process").spawn(cmd, args, {
      stdio: "inherit",
      env: env
    });

    require("./flush-buffers-on-exit-in-windows.js");

    child.on("error", function (error) {
      console.log(error.stack || error);
    });

    child.on("exit", function (exitCode) {
      process.exit(exitCode);
    });

    return child;
  });
}

module.exports = getChildProcess();

Print stack traces from failed `meteor {node,npm}` commands.


import * as t from 'babel-types'
import { useMinify } from '../utils/options'
import { isStyled, isHelper } from '../utils/detectors'

const minify = (linebreak) => {
  const regex = new RegExp(linebreak + '\\s*', 'g')
  return (code) => code.split(regex).filter(line => line.length > 0).map((line) => {
    return line.indexOf('//') === -1 ? line : line + '\n';
  }).join('')
}

const minifyRaw = minify('(?:\\\\r|\\\\n|\\r|\\n)')
const minifyCooked = minify('[\\r\\n]')

export default (path, state) => {
  if (useMinify(state) && (isStyled(path.node.tag, state) || isHelper(path.node.tag, state))) {
    const templateLiteral = path.node.quasi
    for (let element of templateLiteral.quasis) {
      element.value.raw = minifyRaw(element.value.raw)
      element.value.cooked = minifyCooked(element.value.cooked)
    }
  }
}

import * as t from 'babel-types'
import {
  useMinify,
  useCSSPreprocessor
} from '../utils/options'
import { isStyled, isHelper } from '../utils/detectors'

const minify = (linebreak) => {
  const regex = new RegExp(linebreak + '\\s*', 'g')
  return (code) => code.split(regex).filter(line => line.length > 0).map((line) => {
    return line.indexOf('//') === -1 ? line : line + '\n';
  }).join('')
}

const minifyRaw = minify('(?:\\\\r|\\\\n|\\r|\\n)')
const minifyCooked = minify('[\\r\\n]')

export default (path, state) => {
  if (
    useMinify(state) &&
    !useCSSPreprocessor(state) &&
    (
      isStyled(path.node.tag, state) ||
      isHelper(path.node.tag, state)
    )
  ) {
    const templateLiteral = path.node.quasi
    for (let element of templateLiteral.quasis) {
      element.value.raw = minifyRaw(element.value.raw)
      element.value.cooked = minifyCooked(element.value.cooked)
    }
  }
}

Disable minificiation when preprocessing is enabled


/* eslint-disable import/prefer-default-export */
export const getAdjustedStyle = (style, width, isLastCell) => {
  if (width && isLastCell) return { ...style, flex: width, borderRightWidth: 0 };
  if (width) return { ...style, flex: width };
  if (isLastCell) return { ...style, borderWidth: 0 };
  return style;
};

/* eslint-disable import/prefer-default-export */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Utility method to inject properties into a provided style object.
 * If width is passed, add a flex property equal to width.
 * If isLastCell is passed, add a borderRightWidth: 0 property to remove
 * the border for the last cell in a row.
 * If neither are used, do nothing.
 *
 * @param {Object} style      Style object to inject styles into.
 * @param {Number} width      Value for the flex property to inject.
 * @param {Bool}   isLastCell Indicator for the cell being the last in a row.
 */
export const getAdjustedStyle = (style, width, isLastCell) => {
  if (width && isLastCell) return { ...style, flex: width, borderRightWidth: 0 };
  if (width) return { ...style, flex: width };
  if (isLastCell) return { ...style, borderWidth: 0 };
  return style;
};

Add doc string to utility method getAdjustedStyle


var mysql  = require('mysql');
var connectionParams = require('../../config/connectionParams');
function getConnection() {
  var connection = mysql.createConnection(connectionParams);
  return connection;
}

function Calibration(databaseRow) {
    for(var propertyName in databaseRow) {
        this[propertyName] = databaseRow[propertyName];
    }
}

function Calibrations() {
  function query(queryString, queryParams, callback) {
    var connection = getConnection();
    connection.connect();
    connection.query(queryString, queryParams, callback);
    connection.end()
  }

  this.findById = function(calibration_id, callback) {
    // callback is (calibration, err)
    // query the calibrations by id
    var queryString = 'SELECT * FROM calibrations WHERE CalibrationID = ?';
    query(queryString, [calibration_id], function(err, results) {
        if(err) {
          callback(null,err);
        } else {
          var calibrationResult = new Calibration(results[0]);
          callback(calibrationResult);
        }
      });
    };
  this.findByFilter = function(params, callback) {
    var queryString = 'SELECT * FROM calibrations WHERE minAge > ? AND maxAge < ?';
    query(queryString, [params.min, params.max], function(err, results) {
      if(err) {
        callback(null,err);
      } else {
        var calibrationResults = results.map(function(result) { return new Calibration(result)});
        callback(calibrationResults);
      }
    });
  };
}

module.exports = new Calibrations();

var mysql  = require('mysql');
var connectionParams = require('../../config/connectionParams');
function getConnection() {
  var connection = mysql.createConnection(connectionParams);
  return connection;
}

function Calibration(databaseRow) {
    for(var propertyName in databaseRow) {
        this[propertyName] = databaseRow[propertyName];
    }
}

function Calibrations() {
  function query(queryString, queryParams, callback) {
    var connection = getConnection();
    connection.connect();
    connection.query(queryString, queryParams, callback);
    connection.end()
  }

  var TABLE_NAME = 'View_Calibrations';
  this.findById = function(calibration_id, callback) {
    // callback is (calibration, err)
    // query the calibrations by id
    var queryString = 'SELECT * FROM ' + TABLE_NAME + ' WHERE CalibrationID = ?';
    query(queryString, [calibration_id], function(err, results) {
        if(err) {
          callback(null,err);
        } else {
          var calibrationResult = new Calibration(results[0]);
          callback(calibrationResult);
        }
      });
    };
  this.findByFilter = function(params, callback) {
    var queryString = 'SELECT * FROM ' + TABLE_NAME + ' WHERE minAge > ? AND maxAge < ?';
    query(queryString, [params.min, params.max], function(err, results) {
      if(err) {
        callback(null,err);
      } else {
        var calibrationResults = results.map(function(result) { return new Calibration(result)});
        callback(calibrationResults);
      }
    });
  };
}

module.exports = new Calibrations();

Move table name literal out of query


import React, { Component } from 'react';
import './App.css';
import './index.css';
import Rowbox from './components/Rowbox';
import IconRow from './components/IconRow';

class App extends Component {
  render() {
    return (

Fay Ray

          <IconRow />

          <Rowbox text='Is a band' number='1'/>
          <Rowbox text='Has a new single' number='2'/>

          <Rowbox text='Plays shows' number='3'/>
          <Rowbox text='Takes photos' number='4'/>
          <Rowbox text='Uses email' number='5'/>

    );
  }
}

export default App;

import React, { Component } from 'react';
import './App.css';
import './index.css';
import Rowbox from './components/Rowbox';
import IconRow from './components/IconRow';

class App extends Component {
  render() {
    return (

FAY RAY

          <IconRow />

          <Rowbox text='Is a band' number='1'/>
          <Rowbox text='Has a new single' number='2'/>

          <Rowbox text='Plays shows' number='3'/>
          <Rowbox text='Takes photos' number='4'/>
          <Rowbox text='Uses email' number='5'/>

    );
  }
}

export default App;

Change band name to all caps


(function () {
	'use strict';

	Array.prototype.innerJoin = function (arr, outer, inner, result, comparer) {
		comparer = comparer || linq.EqualityComparer;
		var res = [];

		this.forEach(function (t) {
			arr.where(function (u) {
					return comparer(outer(t), inner(u));
				})
				.forEach(function (u) {
					res.push(result(t, u));
				});
		});

		return res;
	};

}());

(function () {
	'use strict';

	Array.prototype.innerJoin = function (inner, outerKeySelector, innerKeySelector, resultSelector, comparer) {
		if (!inner) { throw new TypeError("inner is undefined."); }
		if (!innerKeySelector) { throw new TypeError("innerKeySelector is undefined."); }
		if (!outerKeySelector) { throw new TypeError("outerKeySelector is undefined."); }

		resultSelector = resultSelector || function (o, i) {
			return [o, i];
		};
		comparer = comparer || linq.EqualityComparer;
		var innerKeyed = inner.select(function (e) {
			return {
				key: innerKeySelector(e),
				element: e
			};
		});
		return this.selectMany(function (o) {
			var key = outerKeySelector(o);
			return innerKeyed.where(function (i) {
					return comparer(key, i.key);
				})
				.select(function (i) {
					return i.element;
				});
		}, resultSelector.bind(null));
	};

}());

Validate arguments, make resultSelector optional, only apply key selectors once


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

exports.getInEverySystem = function (geom, callback) {

  var params = {
    geometry: geom,
  };

  $.ajax({
    url: '/api/geometry',
    method: 'POST', // get cannot have JSON body
    data: JSON.stringify(params), // request data
    contentType: 'application/json', // request data type
    processData: false, // already string
    dataType: 'json', // response data type
    headers: { 'Authorization': 'Bearer ' + account.getToken() },
    success: function (coordinates) {
      return callback(null, coordinates);
    },
    error: function (jqxhr) {
      var err = new Error(jqxhr.statusText);
      err.code = jqxhr.status;
      return callback(err);
    },
  });
};

var request = require('./lib/request');

exports.getInEverySystem = function (geom, callback) {
  return request.postJSON({
    url: '/api/geometry',
    data: {
      geometry: geom,
    },
  }, callback);
};

exports.parseCoordinates = function (params, callback) {
  // Parameters
  //   params, object with props
  //     coordinateSystem
  //     coordinatesText
  //   callback
  //     fn (err, latlng), where
  //       latlng
  //         { lat, lng } in WGS84
  //
  return callback(null, {
    lat: 0,
    lng: 0,
  });
};

Use request instead of raw jquery ajax


var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var piwik = require('./piwik');
var mongodb = require('./mongodb');

function dateDaysAgo(nb_days) {
    var date = new Date();
    date = new Date(date.toISOString().slice(0,10));
    date.setDate(date.getDate() - nb_days);
    return date;
}

var nineWeeksAgo = dateDaysAgo(7 * 9);
var yesterday = dateDaysAgo(1);
var today = dateDaysAgo(0);

Promise.all([
    mongodb.getDailySituationCount(nineWeeksAgo,today),
    piwik.getUsageData(nineWeeksAgo, yesterday)
])
.then(function(data) { return [].concat(data[0], data[1]); })
.then(function(data) { return fs.writeFileAsync('dist/documents/stats.json', JSON.stringify(data, null, 2), 'utf-8'); })
.catch(function(error) {
    console.error('error', error);
    process.exitCode = 1;
})
.finally(mongodb.closeDb);

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var piwik = require('./piwik');
var mongodb = require('./mongodb');

function dateDaysAgo(nb_days) {
    var date = new Date();
    date = new Date(date.toISOString().slice(0,10));
    date.setDate(date.getDate() - nb_days);
    return date;
}

var nineWeeksAgo = dateDaysAgo(7 * 9);
var yesterday = dateDaysAgo(1);
var today = dateDaysAgo(0);

var relative_path = __dirname + '/../../../dist/documents/stats.json';
Promise.all([
    mongodb.getDailySituationCount(nineWeeksAgo,today),
    piwik.getUsageData(nineWeeksAgo, yesterday)
])
.then(function(data) { return [].concat(data[0], data[1]); })
.then(function(data) { return fs.writeFileAsync(relative_path, JSON.stringify(data, null, 2), 'utf-8'); })
.catch(function(error) {
    console.error('error', error);
    process.exitCode = 1;
})
.finally(mongodb.closeDb);

Allow script run from any folder


var assert = require("assert");
var {defer, promiseList} = require("ringo/promise");

exports.testPromiseList = function() {
    var d1 = defer(), d2 = defer(), d3 = defer();
    var l = promiseList(d1.promise, d2.promise, d3); // promiseList should convert d3 to promise
    l.then(function(result) {
        assert.deepEqual(result, [{value: "ok"}, {value: 1}, {error: "error"}]);
    }, function(error) {
        assert.fail("promiseList called error callback");
    });
    d2.resolve(1);
    d3.resolve("error", true);
    d1.resolve("ok");
};

// start the test runner if we're called directly from command line
if (require.main == module.id) {
    system.exit(require('test').run(exports));
}

var assert = require("assert");
var {defer, promises} = require("ringo/promise");

exports.testPromiseList = function() {
    var d1 = defer(), d2 = defer(), d3 = defer();
    var l = promises(d1.promise, d2.promise, d3); // promiseList should convert d3 to promise
    l.then(function(result) {
        assert.deepEqual(result, [{value: "ok"}, {value: 1}, {error: "error"}]);
    }, function(error) {
        assert.fail("promiseList called error callback");
    });
    d2.resolve(1);
    d3.resolve("error", true);
    d1.resolve("ok");
};

// start the test runner if we're called directly from command line
if (require.main == module.id) {
    system.exit(require('test').run(exports));
}

Test promises instead of promiseList.


/*
 * Copyright (c) 2014-2020 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const expect = chai.expect

describe('webhook', () => {
  const webhook = require('../../lib/webhook')

  const challenge = {
    key: 'key'
  }

  describe('notify', () => {
    it('fails when no webhook URL is provided via environment variable', () => {
      expect(() => webhook.notify(challenge)).to.throw('options.uri is a required argument')
    })

    it('fails when supplied webhook is not a valid URL', () => {
      expect(() => webhook.notify(challenge, 'localhorst')).to.throw('Invalid URI "localhorst"')
    })

    it('submits POST with payload to existing URL', () => {
      expect(() => webhook.notify(challenge, 'http://localhost')).to.not.throw()
    })
  })
})

/*
 * Copyright (c) 2014-2020 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const expect = chai.expect

describe('webhook', () => {
  const webhook = require('../../lib/webhook')

  const challenge = {
    key: 'key'
  }

  describe('notify', () => {
    it('fails when no webhook URL is provided via environment variable', () => {
      expect(() => webhook.notify(challenge)).to.throw('options.uri is a required argument')
    })

    it('fails when supplied webhook is not a valid URL', () => {
      expect(() => webhook.notify(challenge, 'localhorst')).to.throw('Invalid URI "localhorst"')
    })

    it('submits POST with payload to existing URL', () => {
      expect(() => webhook.notify(challenge, 'https://webhook.site/f69013b6-c475-46ed-973f-aa07e5e573a3')).to.not.throw()
    })
  })
})

Use webhook URL that should be available from anywhere


var fs = require('fs');
var specs = JSON.parse(fs.readFileSync('tests/end2end/specs.json'));

var browser_capabilities = JSON.parse(process.env.SELENIUM_BROWSER_CAPABILITIES);
browser_capabilities['name'] = 'GlobaLeaks-E2E';
browser_capabilities['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
browser_capabilities['build'] = process.env.TRAVIS_BUILD_NUMBER;
browser_capabilities['tags'] = [process.env.TRAVIS_BRANCH];

exports.config = {
  framework: 'jasmine',

  baseUrl: 'http://localhost:9000/',

  troubleshoot: false,
  directConnect: false,

  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,
  capabilities: browser_capabilities,

  params: {
    'testFileDownload': false,
    'verifyFileDownload': false,
    'tmpDir': '/tmp/globaleaks-download',
  },

  specs: specs,

  jasmineNodeOpts: {
    isVerbose: true,
    includeStackTrace: true,
    defaultTimeoutInterval: 360000
  }
};

var fs = require('fs');
var specs = JSON.parse(fs.readFileSync('tests/end2end/specs.json'));

var browser_capabilities = JSON.parse(process.env.SELENIUM_BROWSER_CAPABILITIES);
browser_capabilities['name'] = 'GlobaLeaks-E2E';
browser_capabilities['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
browser_capabilities['build'] = process.env.TRAVIS_BUILD_NUMBER;
browser_capabilities['tags'] = [process.env.TRAVIS_BRANCH];

exports.config = {
  framework: 'jasmine',

  baseUrl: 'http://localhost:9000/',

  troubleshoot: false,
  directConnect: false,

  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,
  sauceBuild: process.env.TRAVIS_BUILD_NUMBER,
  capabilities: browser_capabilities,

  params: {
    'testFileDownload': false,
    'verifyFileDownload': false,
    'tmpDir': '/tmp/globaleaks-download',
  },

  specs: specs,

  jasmineNodeOpts: {
    isVerbose: true,
    includeStackTrace: true,
    defaultTimeoutInterval: 360000
  }
};

Use protractor sauceBuild parameter to mass the build number to Sauce


define(['underscore', 'backbone.marionette', './LessonView'],
  function(_, Marionette, LessonView) {
  'use strict';

  return Marionette.CollectionView.extend({
    el: $('#timetable'),
    childView: LessonView,
    childViewOptions: function () {
      return {
        parentView: this,
        timetable: this.collection
      };
    },

    events: {
      'mousemove': 'mouseMove',
      'mouseleave': 'mouseLeave'
    },

    initialize: function() {
      this.$colgroups = this.$('colgroup');
    },

    mouseMove: function(evt) {
      if (!this.colX) {
        this.colX = this.$('#mon > tr:last-child > td')
          .filter(':even')
          .map(function() { return $(this).offset().left; })
          .get();
      }

      var currCol = this.$colgroups.eq(_.sortedIndex(this.colX, evt.pageX));
      if (!currCol.is(this.prevCol)) {
        if (this.prevCol) {
          this.prevCol.removeAttr('class');
        }
        currCol.addClass('hover');
        this.prevCol = currCol;
      }
    },

    mouseLeave: function() {
      if (this.prevCol) {
        this.prevCol.removeAttr('class');
        this.prevCol = false;
      }
    },

    attachBuffer: function () {
    },

    attachHtml: function () {
    }
  });
});

define(['underscore', 'backbone.marionette', './LessonView'],
  function(_, Marionette, LessonView) {
  'use strict';

  return Marionette.CollectionView.extend({
    el: $('#timetable'),
    childView: LessonView,
    childViewOptions: function () {
      return {
        parentView: this,
        timetable: this.collection
      };
    },

    events: {
      'mousemove': 'mouseMove',
      'mouseleave': 'mouseLeave'
    },

    ui: {
      colgroups: 'colgroup'
    },

    initialize: function() {
    },

    mouseMove: function(evt) {
      if (!this.colX) {
        this.colX = this.$('#mon > tr:last-child > td')
          .filter(':even')
          .map(function() { return $(this).offset().left; })
          .get();
      }

      var currCol = this.ui.colgroups.eq(_.sortedIndex(this.colX, evt.pageX));
      if (!currCol.is(this.prevCol)) {
        if (this.prevCol) {
          this.prevCol.removeAttr('class');
        }
        currCol.addClass('hover');
        this.prevCol = currCol;
      }
    },

    mouseLeave: function() {
      if (this.prevCol) {
        this.prevCol.removeAttr('class');
        this.prevCol = false;
      }
    },

    attachBuffer: function () {
    },

    attachHtml: function () {
    }
  });
});

Store colgroup in ui hash


// scripts app
var app;
(function(){
	"use strict";
	app = new ElecionesApp();
	console.log(app);
	// load mapa
	$.get("img/mapaBA_SVG.txt", function(mapa){
		$("#mapa_cont").html(mapa);
	});

	$('select#opts').change(function(e){
		app.change_dropdown($(this).val());
	});

})();

// scripts app
var app;
$(function(){
	"use strict";
	app = new ElecionesApp();
	console.log(app);
	// load mapa
	$.get("img/mapaBA_SVG.txt", function(mapa){
		$("#mapa_cont").html(mapa);
	});

	$('select#opts').change(function(e){
		app.change_dropdown($(this).val());
	});

});

Fix problem with anonymous function in production mode


var api = require("./webpack.config.defaults")
  .entry("./src/server.js")
  // @TODO Auto-installed deps aren't in node_modules (yet),
  // so let's see if this is a problem or not
  .externals(/^@?\w[a-z\-0-9\./]+$/)
  // .externals(...require("fs").readdirSync("./node_modules"))
  .output("build/server")
  .target("node")
  .when("development", function(api) {
    return api
      .entry({
        server: [
          "webpack/hot/poll?1000",
          "./src/server.js",
        ],
      })
      .sourcemap("source-map")
      .plugin("start-server-webpack-plugin")
      .plugin("webpack.BannerPlugin", {
        banner: `require("source-map-support").install();`,
        raw: true,
      })
    ;
  })
;

module.exports = api.getConfig();

var api = require("./webpack.config.defaults")
  .entry("./src/server.js")
  .externals(/^@?\w[a-z\-0-9\./]+$/)
  .output("build/server")
  .target("node")
  .when("development", function(api) {
    return api
      .entry({
        server: [
          "webpack/hot/poll?1000",
          "./src/server.js",
        ],
      })
      .sourcemap("source-map")
      .plugin("start-server-webpack-plugin")
      .plugin("webpack.BannerPlugin", {
        banner: `require("source-map-support").install();`,
        raw: true,
      })
    ;
  })
;

module.exports = api.getConfig();

Add comment that externals RegExp does work


$(document).ready(function() {
  $("[data-toggle=tooldesc]").data('container', 'body').tooltip({placement: 'bottom', trigger: 'manual'});
  $("[data-toggle=tooltip], [data-toggle=tooltips] *").data('container', 'body').tooltip();
  setInterval(function () {
      $('.page-loader:visible').each(function() {
          var elem = this.parentElement;
          $(this).removeClass('page-loader');
          $(this).addClass('page-loading');
          var url = $(this).data('loader-url');
          if (!url) {
              $(this).remove();
              console.error("No url to go to for page loader!");
          }
          $.get(url, function(data) {
              $(elem).html(data);
              $(this).remove(); // Maybe not required
          });
      });
  }, 500);
});

$(document).ready(function() {
  setInterval(function () {
      $('.page-loader:visible').each(function() {
          var elem = this.parentElement;
          $(this).removeClass('page-loader');
          $(this).addClass('page-loading');
          var url = $(this).data('loader-url');
          if (!url) {
              $(this).remove();
              console.error("No url to go to for page loader!");
          }
          $.get(url, function(data) {
              $(elem).html(data);
              $(this).remove(); // Maybe not required
              setup_hover();
          });
      });
  }, 500);
    setup_hover();
});

function setup_hover() {
    $("[data-toggle=tooldesc]").data('container', 'body').tooltip({placement: 'bottom', trigger: 'manual'});
    $("[data-toggle=tooltip], [data-toggle=tooltips] *").data('container', 'body').tooltip();
}

Fix tooltips when lazy loading


'use strict'

module.exports = [
  {
    name: 'auckland',
    id: 1
  },
  {
    name: 'wellington',
    id: 15
  },
  {
    name: 'waikato',
    id: 14
  }
]

'use strict'

const auckland = {
  name: 'auckland',
  id: 1
}

const wellington = {
  name: 'wellington',
  id: 15
}

const waikato = {
  name: 'waikato',
  id: 14
}

module.exports = [
  auckland
]

Disable cities other than AKL for testing


angular.module('14all', ['ui.bootstrap','ngAnimate',
    'auth',
    /* Modules */
    'manga','movie','serie','anime','game','book',
    '14all.templates'])
    .config(['RestangularProvider',function(RestangularProvider){
        RestangularProvider.setRestangularFields({
            id: "_id"
        });
        RestangularProvider.setBaseUrl('/api');
    }])
    .controller('AppCtrl',['$scope','$location','authService','$store',function($scope,$location,authService,$store){

        // FIX SETTINGS
        var tempSettings = $store.get('settings');
        if(angular.isDefined(tempSettings.excludeFinished)){
            $store.remove('settings');
        }
        // FIX SETTINGS
        $scope.isLoggedIn = authService.isLoggedIn;
        $scope.logout = authService.logout;
        $scope.settings = $store.bind($scope,'settings',{
            filter:{
                stats:{
                    finished: false,
                    dropped: false
                },
                orderBy:{
                    predicate:'',
                    reverse:false
                },
                keyword:{}
            }
        });

        $scope.orderBy = $scope.settings.filter.orderBy;

        $scope.focus  = {search:true};

        $scope.$on("$routeChangeStart",function(event,next,current){
            $scope.focus.search = false;
        });

        $scope.$on("$routeChangeSuccess",function(event,next,current){
            $scope.focus.search = true;
        });
    }]);

angular.module('14all', ['ui.bootstrap','ngAnimate',
    'auth',
    /* Modules */
    'manga','movie','serie','anime','game','book',
    '14all.templates'])
    .config(['RestangularProvider',function(RestangularProvider){
        RestangularProvider.setRestangularFields({
            id: "_id"
        });
        RestangularProvider.setBaseUrl('/api');
    }])
    .controller('AppCtrl',['$scope','$location','authService','$store',function($scope,$location,authService,$store){

        // FIX SETTINGS
        var tempSettings = $store.get('settings');
        if(angular.isDefined(tempSettings) && angular.isDefined(tempSettings.excludeFinished)){
            $store.remove('settings');
        }
        // FIX SETTINGS
        $scope.isLoggedIn = authService.isLoggedIn;
        $scope.logout = authService.logout;
        $scope.settings = $store.bind($scope,'settings',{
            filter:{
                stats:{
                    finished: false,
                    dropped: false
                },
                orderBy:{
                    predicate:'',
                    reverse:false
                },
                keyword:{}
            }
        });

        $scope.orderBy = $scope.settings.filter.orderBy;

        $scope.focus  = {search:true};

        $scope.$on("$routeChangeStart",function(event,next,current){
            $scope.focus.search = false;
        });

        $scope.$on("$routeChangeSuccess",function(event,next,current){
            $scope.focus.search = true;
        });
    }]);

Fix for Cannot read property 'excludeFinished' of null


/* eslint global-require: off, import/no-mutable-exports: off */
import merge from 'lodash/merge';
import flow from 'lodash/flow';

const environment = process.env.NODE_ENV || 'test';

let start = () => {};

if (environment !== 'production') {
  const { default: Mirage, camelize } = require('@bigtest/mirage');
  const { default: coreModules } = require('./index');
  require('./force-fetch-polyfill');

  start = (scenarioNames, options = {}) => {
    const { coreScenarios = {}, baseConfig: coreConfig, ...coreOpts } = coreModules;
    const { scenarios = {}, baseConfig = () => {}, ...opts } = options;

    const server = new Mirage(merge({
      baseConfig: flow(coreConfig, baseConfig),
      environment
    }, coreOpts, opts));

    // mirage only loads a `default` scenario for us out of the box,
    // so instead of providing all scenarios we run specific scenarios
    // after the mirage server is initialized.
    [].concat(scenarioNames || 'default').filter(Boolean).forEach(name => {
      const key = camelize(name);
      const scenario = scenarios[key] || coreScenarios[key];
      if (scenario) scenario(server);
    });

    return server;
  };
}

export default start;

/* eslint global-require: off, import/no-mutable-exports: off */
import merge from 'lodash/merge';
import flow from 'lodash/flow';

const environment = process.env.NODE_ENV || 'test';

let start = () => {};

if (environment !== 'production') {
  const { default: Mirage, camelize } = require('@bigtest/mirage');
  const { default: coreModules } = require('./index');
  require('./force-fetch-polyfill');

  start = (scenarioNames, options = {}) => {
    const { coreScenarios = {}, baseConfig: coreConfig, ...coreOpts } = coreModules;
    const { scenarios = {}, baseConfig = () => {}, ...opts } = options;

    const server = new Mirage(merge({
      baseConfig: flow(coreConfig, baseConfig),
      environment
    }, coreOpts, opts));

    // the default scenario is only used when not in test mode
    let defaultScenario;
    if (!scenarioNames && environment !== 'test') {
      defaultScenario = 'default';
    }

    // mirage only loads a `default` scenario for us out of the box,
    // so instead of providing all scenarios we run specific scenarios
    // after the mirage server is initialized.
    [].concat(scenarioNames || defaultScenario).filter(Boolean).forEach(name => {
      const key = camelize(name);
      const scenario = scenarios[key] || coreScenarios[key];
      if (scenario) scenario(server);
    });

    return server;
  };
}

export default start;

Fix mirage to only use default scenario when not testing


function updateMeters()
{
	x_cpu_usage(updateCPU);
	x_mem_usage(updateMemory);
	x_get_uptime(updateUptime);
	x_get_pfstate(updateState);

	window.setTimeout('updateMeters()', 1500);
}

function updateMemory(x)
{
	document.getElementById("memusagemeter").value = x + '%';

	document.getElementById("memwidtha").style.width = x + 'px';
	document.getElementById("memwidthb").style.width = (100 - x) + 'px';
}

function updateCPU(x)
{
	document.getElementById("cpumeter").value = x + '%';

	document.getElementById("cpuwidtha").style.width = x + 'px';
	document.getElementById("cpuwidthb").style.width = (100 - x) + 'px';
}

function updateUptime(x)
{
	document.getElementById("uptime").value = x;
}

function updateState(x)
{
	document.getElementById("pfstate").value = x;
}

window.setTimeout('updateMeters()', 1500);

function updateMeters()
{
	x_cpu_usage(updateCPU);
	x_mem_usage(updateMemory);
	x_get_uptime(updateUptime);
	x_get_pfstate(updateState);

	window.setTimeout('updateMeters()', 10000);
}

function updateMemory(x)
{
	document.getElementById("memusagemeter").value = x + '%';

	document.getElementById("memwidtha").style.width = x + 'px';
	document.getElementById("memwidthb").style.width = (100 - x) + 'px';
}

function updateCPU(x)
{
	document.getElementById("cpumeter").value = x + '%';

	document.getElementById("cpuwidtha").style.width = x + 'px';
	document.getElementById("cpuwidthb").style.width = (100 - x) + 'px';
}

function updateUptime(x)
{
	document.getElementById("uptime").value = x;
}

function updateState(x)
{
	document.getElementById("pfstate").value = x;
}

window.setTimeout('updateMeters()', 10000);

Update time now 10 seconds.


import { createReducer } from 'redux-act';

import {
    queryEntities,
    queryEntitySetEntities,
    fetchEntity,
    createEntity,
    updateEntity,
    deleteEntity,
} from 'actions';
import {
    objectLoadStart, objectLoadError, objectLoadComplete, objectDelete, resultObjects,
} from 'reducers/util';

const initialState = {};

export default createReducer({

    [querySimilar.COMPLETE]: (state, { result }) => resultObjects(state, result),

}, initialState);

import { createReducer } from 'redux-act';

import { querySimilar } from 'actions';
import { resultObjects } from 'reducers/util';

const initialState = {};

export default createReducer({

    [querySimilar.COMPLETE]: (state, { result }) => resultObjects(state, result),

}, initialState);

Fix ESLint errors and warnings