import request from 'requisition'

/**
 * Description: Makes an asynchronous HTTP call to the parliament RSS feed xml and returns the content (XML).
 *
 * @return  {string} The string specifying the parliamentary schedule feed in xml format.
 */
const retrieveRSSFeedXml = async() => {
    try {
        const url = global.settings.dataSources.parliamentCalendarRSSFeed.mainUrl
        const response = await request(url)
        const parliamentCalendarRSSFeedXml = await response.text()

        return parliamentCalendarRSSFeedXml

    } catch (error) {
        console.error(`
        HTTP request to calendar RSS Failed with exception message:

        ${error}`)

        throw new Error(error)
    }
}

export default retrieveRSSFeedXml

import request from 'requisition'

/**
 * Description: Makes an asynchronous HTTP call to the parliament RSS feed xml and returns the content (XML).
 *
 * @return  {string} The string specifying the parliamentary schedule feed in xml format.
 */
const retrieveRSSFeedXml = async () => {
    try {
        const url = global.settings.dataSources.parliamentCalendarRSSFeed.mainUrl
        const response = await request(url)

        if (response.statusCode != 200) {
            throw new Error('Unable to retrieve response from parliament calendar RSS Feed')
        }

        const parliamentCalendarRSSFeedXml = await response.text()

        return parliamentCalendarRSSFeedXml

    } catch (error) {
        console.error(`
        HTTP request to calendar RSS Failed with exception message:

        ${error}`)

        throw new Error(error)
    }
}

export default retrieveRSSFeedXml

Add response status code check


angular.module('facette.ui.pane', [])

.directive('ngPane', function() {
    return {
        restrict: 'A',
        scope: {},
        controller: 'PaneController'
    };
})

.controller('PaneController', function($scope, $rootScope, $element) {
    $rootScope.steps = {};

    // Define scope functions
    $rootScope.switch = function(step) {
        var element = angular.element('[ng-pane-step="' + step + '"]').show();
        element.siblings('[ng-pane-step]').hide();

        $scope.$parent.step = step;
    };

    // Add class on pane element
    $element.addClass('pane');

    // Switch to first pane by default
    $scope.$evalAsync(function() { $rootScope.switch(1); });
})

.directive('ngPaneStep', function($rootScope) {
    return {
        require: '^ngPane',
        restrict: 'A',
        scope: {},
        link: function(scope, element, attrs) {
            $rootScope.steps[attrs.ngPaneStep] = element;
        }
    };
});

angular.module('facette.ui.pane', [])

.directive('ngPane', function() {
    return {
        restrict: 'A',
        scope: {},
        controller: 'PaneController'
    };
})

.controller('PaneController', function($scope, $rootScope, $element) {
    $rootScope.steps = {};

    // Define scope functions
    $rootScope.switch = function(step) {
        var element = angular.element('[ng-pane-step="' + step + '"]').show();
        element.siblings('[ng-pane-step]').hide();

        // Update step in both parent and root scopes (needed for main step change detection)
        $rootScope.step = $scope.$parent.step = step;
    };

    // Add class on pane element
    $element.addClass('pane');

    // Switch to first pane by default
    $scope.$evalAsync(function() { $rootScope.switch(1); });
})

.directive('ngPaneStep', function($rootScope) {
    return {
        require: '^ngPane',
        restrict: 'A',
        scope: {},
        link: function(scope, element, attrs) {
            $rootScope.steps[attrs.ngPaneStep] = element;
        }
    };
});

Fix main step change detection


define([], function () {

    return {
        'select': $('select').append('<option>Value</option><option>Value</option>');
    };
});

define([], function () {

    return {
        'select': $("<select><option>Value</option><option>Value</option></select>")
    };
});

Change expected value for Select component


export const URL = 'http://pth.mi-squared.com';
// export const URL = 'http://localhost:7771';

// export const URL = 'http://pth.mi-squared.com';
export const URL = 'http://localhost:7771';

Set ttl to 1hr, refresh_ttl to 5hrs, and ttl for forgot password to 24hr


Promise.all([
  fetch('cy-style.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    }),
  fetch('data.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    })
])
  .then(function(dataArray) {
    var cy = window.cy = cytoscape({
      container: document.getElementById('cy'),

      layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20
      },

      style: dataArray[0],

      elements: dataArray[1]

    });
  });

Promise.all([
  fetch('cy-style.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    }),
  fetch('data.json', {mode: 'no-cors'})
    .then(function(res) {
      return res.json()
    })
])
  .then(function(dataArray) {
    var cy = window.cy = cytoscape({
      container: document.getElementById('cy'),

      layout: {
        name: 'cose',
        idealEdgeLength: 100,
        nodeOverlap: 20,
        refresh: 20,
        fit: true,
        padding: 30,
        randomize: false,
        componentSpacing: 100,
        nodeRepulsion: 400000,
        edgeElasticity: 100,
        nestingFactor: 5,
        gravity: 80,
        numIter: 1000,
        initialTemp: 200,
        coolingFactor: 0.95,
        minTemp: 1.0
      },

      style: dataArray[0],

      elements: dataArray[1]

    });
  });

Fix cose options in demo


name: Dashboard - Highlight active projects (Compact View only)
description: See https://github.com/quincunx/testrail-ui-scripts
author: Christian Schuerer-Waldheim <csw@gmx.at>
version: 1.0
includes: ^dashboard
excludes:

js:
$(document).ready(
  function() {
    // Check if Compact View is being displayed
    if ($('span.content-header-icon:nth-child(1) > a:nth-child(1) > img:nth-child(1)').attr('src').indexOf("Inactive") >= 0) {
      console.log("TestRail UI Script - Dashboard - Highlight active projects: Only Compact View is supported");
    } else {
      $('#content-inner > table > tbody  > tr').each(function() {
	var $project = $(this);
	var run_count = 0;
	$project.children('td:nth-child(3)').each(function() { run_count = $(this).children('strong:nth-child(3)').html(); });
	if (run_count > 0) { $project.css("background-color", "#D1E6FD"); }
      });
    }
  }
);

name: Dashboard - Highlight active projects (Compact View only)
description: See https://github.com/quincunx/testrail-ui-scripts
author: Christian Schuerer-Waldheim <csw@gmx.at>
version: 1.0
includes: ^dashboard
excludes:

js:
$(document).ready(
  function() {
    // Check if Compact View is being displayed
    if ($('span.content-header-icon:nth-child(1) > a:nth-child(1) > img:nth-child(1)').attr('src').indexOf("Inactive") >= 0) {
      console.log("TestRail UI Script - Dashboard - Highlight active projects: Only Compact View is supported");
    } else {
      $('#content-inner > table > tbody  > tr').each(function() {
        var $project = $(this);
        var run_count = 0;
        $project.children('td:nth-child(3)').each(function() { run_count = $(this).children('strong:nth-child(3)').html(); });
        if (run_count > 0) { $project.addClass("project-active"); }
      });
    }
  }
);

css:
.project-active{
  background:#FFCC33
}

Use css class instead of direct formatting


// Copyright 2016 Endless Mobile, Inc.

/* exported Thematic */

const GObject = imports.gi.GObject;

const Filter = imports.app.interfaces.filter;
const HistoryStore = imports.app.historyStore;
const Module = imports.app.interfaces.module;

/**
 * Class: Filter.Thematic
 *
 * If the current item in our history model is featured, filter for non-featured
 * models. If the current item in our history is not featured, filter for
 * featured models.
 */
const Thematic = new Module.Class({
    Name: 'Filter.Thematic',
    Extends: GObject.Object,
    Implements: [Filter.Filter],

    _init: function (props={}) {
        this.parent(props);
        HistoryStore.get_default().connect('changed', () => {
            this.emit('filter-changed');
        });
    },

    // Filter implementation
    include_impl: function (model) {
        return (model.featured != HistoryStore.get_default().get_current_item().model.featured);
    },
});

// Copyright 2016 Endless Mobile, Inc.

/* exported Thematic */

const GObject = imports.gi.GObject;

const Filter = imports.app.interfaces.filter;
const HistoryStore = imports.app.historyStore;
const Module = imports.app.interfaces.module;

/**
 * Class: Filter.Thematic
 *
 * If the current item in our history model is featured, filter for non-featured
 * models. If the current item in our history is not featured, filter for
 * featured models.
 */
const Thematic = new Module.Class({
    Name: 'Filter.Thematic',
    Extends: GObject.Object,
    Implements: [Filter.Filter],

    _init: function (props={}) {
        this.parent(props);
        HistoryStore.get_default().connect('changed', () => {
            this.emit('filter-changed');
        });
    },

    // Filter implementation
    include_impl: function (model) {
        let item = HistoryStore.get_default().get_current_item();
        // Kind of silly but without this check the filter throws on app startup
        // because Selection.AllSets always loads its content right away on
        // init. It will get refiltered properly once a user clicks on a set
        // object.
        if (item && item.model)
            return (model.featured != item.model.featured);
        return true;
    },
});

Check that we have a current model in Filter.Thematic


module.exports = (req, res, next) => {
  const postcode = req.body.uk_postcode
  req.body.uk_postcode = postcode && postcode.split(',')
  next()
}

module.exports = (req, res, next) => {
  const postcode = req.body.uk_postcode
  req.body.uk_postcode =
    postcode && postcode.split(',').filter((x) => !x.match(/^\s*$/))
  next()
}

Fix a bug with trailing commas in postcode filter


module.exports = {
  ELASTICSEARCH_URL: 'http://localhost:62223', // Shared by api-server and CLI evaluation script
  ELASTIC_LOG_LEVEL: 'trace',
};

module.exports = {
  ELASTICSEARCH_URL: 'http://localhost:62223', // Shared by api-server and CLI evaluation script
  // ELASTIC_LOG_LEVEL: 'trace',
};

Make error logs less verbose


var Backbone = require('backbone');
var url = require('url');

"use strict";

exports.HistoryUpdate = Backbone.View.extend({

    initialize : function () {
        console.log('HistoryUpdate:initialize');
        this.listenTo(this.model, "change:asset", this.assetChanged);
        this.listenTo(this.model, "change:activeTemplate", this.assetChanged);
        // note that we don't listen for a change in the collection as
        // this could lead to an invalid URL (e.g. change the collection to
        // something else, URL immediately changes, user saves before asset
        // loads)
    },

    assetChanged: function () {
        var u = url.parse(window.location.href.replace('#', '?'), true);
        u.search = null;
        if (this.model.activeTemplate() == undefined ||
            this.model.activeCollection() == undefined ||
            this.model.assetIndex() == undefined) {
            // only want to set full valid states.
            return
        }
        u.query.t = this.model.activeTemplate();
        u.query.c = this.model.activeCollection();
        u.query.i = this.model.assetIndex() + 1;
        history.replaceState(null, null, url.format(u).replace('?', '#'));
    }
});

var Backbone = require('backbone');
var url = require('url');

"use strict";

exports.HistoryUpdate = Backbone.View.extend({

    initialize : function () {
        console.log('HistoryUpdate:initialize');
        this.listenTo(this.model, "change:asset", this.assetChanged);
        this.listenTo(this.model, "change:activeTemplate", this.assetChanged);
        // note that we don't listen for a change in the collection as
        // this could lead to an invalid URL (e.g. change the collection to
        // something else, URL immediately changes, user saves before asset
        // loads)
    },

    assetChanged: function () {
        var u = url.parse(window.location.href.replace('#', '?'), true);
        u.search = null;

        if (this.model.activeTemplate()) {
            u.query.t = this.model.activeTemplate();
        }

        if (this.model.activeCollection()) {
            u.query.c = this.model.activeCollection();
        }

        if (this.model.assetIndex() !== undefined) {
            u.query.i = this.model.assetIndex() + 1;
        }

        history.replaceState(null, null, url.format(u).replace('?', '#'));
    }
});

Allow for more flexible url pattern


import React from 'react/addons';
import Backbone from 'backbone';
import Router from 'react-router';
import stores from 'stores';
import Stats from './Stats.react';

export default React.createClass({
    displayName: "ProvidersList",

    render: function () {
    let providers = this.props.providers;

    let ProviderCards =  providers.map(function(item) {
        let provider = item.attributes;
        return (

                    <Router.Link to = "provider" params = {{id: provider.id}} >

 {provider.name}

 {provider.description}

                        <Stats provider={provider} />

                    </Router.Link>

        );
    });
        return (

                {ProviderCards}

        );
    }

});

import React from 'react/addons';
import Backbone from 'backbone';
import Router from 'react-router';
import stores from 'stores';
import Stats from './Stats.react';

export default React.createClass({
    displayName: "ProvidersList",

    render: function () {
        let providers = this.props.providers;

        let ProviderCards = providers.map(function(provider) {
            return (

                        <Router.Link
                            to="provider"
                            params={{id: provider.get('id')}}>

                                    {provider.get('name')}

                                    {provider.get('description')}

                                <Stats provider={provider} />

                        </Router.Link>

            );
        });
        return (

                {ProviderCards}

        );
    }

});

Remove referencing Backbone.Model's attributes internal structure


var events = require('events')
var util = require('util')

function Messenger () {
    this.parent = new events.EventEmitter
    events.EventEmitter.call(this)
}
util.inherits(Messenger, events.EventEmitter)

Messenger.prototype.send = function () {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    this.parent.emit.apply(this.parent, [ 'message' ].concat(vargs.slice(0, 2)))
}

module.exports = Messenger

var events = require('events')
var util = require('util')

function Messenger () {
    this.parent = new events.EventEmitter
    this.connected = true
    events.EventEmitter.call(this)
}
util.inherits(Messenger, events.EventEmitter)

Messenger.prototype.send = function () {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    this.parent.emit.apply(this.parent, [ 'message' ].concat(vargs.slice(0, 2)))
}

module.exports = Messenger

Set `connected` to `true` on `Messenger`.


import test from 'ava';
import jsdom from 'jsdom';
import fs from 'fs';

test.before(t => {
  // Angular dependencies
  const angularScript = fs.readFileSync('../node_modules/angular/angular.js', 'utf8');
  global.document = jsdom.jsdom(`<!doctype html><html><head><meta charset="utf-8"><script>${angularScript}</script></head><body ng-app="app"><div><div ng-if="notInScope">aabbcc</div></div></body></html>`);
  global.window = document.defaultView;
  global.Node = global.window.Node;

  require('angular/angular');

  // Make angular available for angular-mocks
  global.angular = global.window.angular;

  // Set some window properties required by angular-mocks
  global.window.mocha = {};
  global.window.beforeEach = test.beforeEach;
  global.window.afterEach = test.afterEach;

  require('angular-mocks');

  angular.module('app', ['NgModelOptionsOnInvalid']);
});

test('a', t => {
  angular.mock.inject(($rootScope, $compile) => {
    const scope = $rootScope.$new();
    const element = $compile('

aabbcc

')(scope);
    $rootScope.$digest();
    const abc = element.html() || '';
    console.log('abc', abc);
    t.false(abc.includes('aabbcc'));
  });
});

import test from 'ava';
import jsdom from 'jsdom';

test.before(t => {
  // Angular dependencies
  global.document = jsdom.jsdom('<!doctype html><meta charset="utf-8">');
  global.window = document.defaultView;
  global.Node = global.window.Node;

  require('angular/angular');

  // Make angular available for angular-mocks
  global.angular = global.window.angular;

  // Set some window properties required by angular-mocks
  global.window.mocha = {};
  global.window.beforeEach = test.beforeEach;
  global.window.afterEach = test.afterEach;

  require('angular-mocks');

  angular.module('app', ['NgModelOptionsOnInvalid']);
});

test('a', t => {
  angular.mock.inject(($rootScope, $compile) => {
    const scope = $rootScope.$new();
    const element = $compile('

aabbcc

')(scope);
    $rootScope.$digest();
    const abc = element.html() || '';
    console.log('abc', abc);
    t.false(abc.includes('aabbcc'));
  });
});

Revert "Add angular to document directly"


var app = angular.module("browseApp.mainctrls", []);

app.controller("header", function($scope){
//    $scope.header = "Welcome to Fey Anthology!";
});

/*
* Main controller for the category state.
*
*/
app.controller("controller_c", function($scope) {

$scope.$parent.header = "List of categories";

});

/*
* Main controller for the works list state.
*
*/app.controller("controller_l", function($scope) {

$scope.$parent.header = "List";

$scope.catID = $stateParams.catID;

});

/*
* Main controller for the new entry state.
*
*/
app.controller("controller_n", function($scope, $stateParams) {
$scope.$parent.header = "Submit something new";

$scope.catID = $stateParams.catID;

});

var app = angular.module("browseApp.mainctrls", []);

app.controller("header", function($scope){
//    $scope.header = "Welcome to Fey Anthology!";
});

/*
* Main controller for the category state.
*
*/
app.controller("controller_c", function($scope) {

$scope.$parent.header = "List of categories";

});

/*
* Main controller for the works list state.
*
*/app.controller("controller_l", function($scope, $stateParams) {

$scope.$parent.header = "List";

$scope.catID = $stateParams.catID;

});

/*
* Main controller for the new entry state.
*
*/
app.controller("controller_n", function($scope, $stateParams) {
$scope.$parent.header = "Submit something new";

$scope.catID = $stateParams.catID;

});

Add stateParams to controller functions


import actionTypes from '../constants/actionTypes';

export default (state = {}, action) => {
  switch (action.type) {
    case actionTypes.LOGIN_USER: {
      return {
        token: action.token,
        data: action.data,
      };
    }
    case actionTypes.LOGOUT_USER: {
      return {};
    }
    case actionTypes.SET_USER_TOKEN: {
      return {
        ...state,
        token: action.token,
      };
    }
    case actionTypes.SET_USER_DATA: {
      return {
        ...state,
        data: action.data,
      };
    }
    default: {
      return state;
    }
  }
};

import actionTypes from '../constants/actionTypes';

export default (state = { token: null, data: {} }, action) => {
  switch (action.type) {
    case actionTypes.LOGIN_USER: {
      return {
        token: action.token,
        data: action.data,
      };
    }
    case actionTypes.LOGOUT_USER: {
      return {};
    }
    case actionTypes.SET_USER_TOKEN: {
      return {
        ...state,
        token: action.token,
      };
    }
    case actionTypes.SET_USER_DATA: {
      return {
        ...state,
        data: action.data,
      };
    }
    default: {
      return state;
    }
  }
};

Adjust initial state of user


function isEnvironment(environment) {
  if (environment === 'client' && !Meteor.isClient) {
    return false;
  }
  else if (environment === 'server' && !Meteor.isServer) {
    return false;
  }
  return true;
};

export default isEnvironment;

let environment;
if (Meteor.isClient) {
  environment = 'client';
}
else if (Meteor.isServer) {
  environment = 'server';
}
else if (Meteor.isCordova) {
  environment = 'cordova';
}

function isEnvironment(environments) {
  // Make sure that argument is an array.
  if (Match.test(environments, String)) {
    environments = [environments];
  }

  return _.includes(environments, environment);
};

export default isEnvironment;

Allow save, remove, validate methods execution in multiple environments


import React from "react";
import {Box} from "adminlte";

import LinkedListGroup from "app/components/LinkedListGroup";
import Pagination from "app/components/Pagination";
import RefreshButton from "app/components/RefreshButton";
import SearchBox from "app/components/SearchBox";

class MasterBox extends React.Component {
    render() {
        const {collection, CreateForm, QueryForm} = this.props;

        return (
            <Box.Wrapper>
                <Box.Header>
                    <Box.Title>{collection.title}</Box.Title>
                    <Box.Tools>
                        <SearchBox {...this.props}/>
                    </Box.Tools>
                </Box.Header>
                <Box.Body>
                    <LinkedListGroup {...this.props}/>
                    <div className="text-center">
                        <CreateForm {...this.props}/>
                        <QueryForm {...this.props}/>
                        <RefreshButton {...this.props}/>
                    </div>
                    <Pagination {...this.props}/>
                </Box.Body>
            </Box.Wrapper>
        );
    }
}

export default MasterBox;

import React from "react";
import {Box} from "adminlte";

import LinkedListGroup from "app/components/LinkedListGroup";
import Pagination from "app/components/Pagination";
import RefreshButton from "app/components/RefreshButton";
import SearchBox from "app/components/SearchBox";

class MasterBox extends React.Component {
    render() {
        const {collection, CreateForm, QueryForm} = this.props;

        return (
            <Box.Wrapper>
                <Box.Header>
                    <Box.Title>{collection.title}</Box.Title>
                    <Box.Tools>
                        <SearchBox {...this.props}/>
                    </Box.Tools>
                </Box.Header>
                <Box.Body>
                    <LinkedListGroup {...this.props}/>
                    <div className="text-center">
                        <CreateForm {...this.props}/>
                        <RefreshButton {...this.props}/>
                        <QueryForm {...this.props}/>
                    </div>
                    <Pagination {...this.props}/>
                </Box.Body>
            </Box.Wrapper>
        );
    }
}

export default MasterBox;

Swap query/refresh button spots on list screen


wax = wax || {};
wax.mm = wax.mm || {};

wax.mm.interaction = function() {
    var dirty = false, _grid;

    function grid() {
        var zoomLayer = map.getLayerAt(0)
            .levels[Math.round(map.getZoom())];
        if (!dirty && _grid !== undefined && _grid.length) {
            return _grid;
        } else {
            _grid = (function(t) {
                var o = [];
                for (var key in t) {
                    if (t[key].parentNode === zoomLayer) {
                        var offset = wax.u.offset(t[key]);
                        o.push([
                            offset.top,
                            offset.left,
                            t[key]
                        ]);
                    }
                }
                return o;
            })(map.getLayerAt(0).tiles);
            return _grid;
        }
    }

    function attach(x) {
        if (!arguments.length) return map;
        map = x;
        function setdirty() { dirty = true; }
        var clearingEvents = ['zoomed', 'panned', 'centered',
            'extentset', 'resized', 'drawn'];
        for (var i = 0; i < clearingEvents.length; i++) {
            map.addCallback(clearingEvents[i], setdirty);
        }
    }

    return wax.interaction()
        .attach(attach)
        .grid(grid);
};

wax = wax || {};
wax.mm = wax.mm || {};

wax.mm.interaction = function() {
    var dirty = false, _grid;

    function grid() {
        var zoomLayer = map.getLayerAt(0)
            .levels[Math.round(map.getZoom())];
        if (!dirty && _grid !== undefined && _grid.length) {
            return _grid;
        } else {
            _grid = (function(t) {
                var o = [];
                for (var key in t) {
                    if (t[key].parentNode === zoomLayer) {
                        var offset = wax.u.offset(t[key]);
                        o.push([
                            offset.top,
                            offset.left,
                            t[key]
                        ]);
                    }
                }
                return o;
            })(map.getLayerAt(0).tiles);
            return _grid;
        }
    }

    function attach(x) {
        if (!arguments.length) return map;
        map = x;
        function setdirty() { dirty = true; }
        var clearingEvents = ['zoomed', 'panned', 'centered',
            'extentset', 'resized', 'drawn'];
        for (var i = 0; i < clearingEvents.length; i++) {
            map.addCallback(clearingEvents[i], setdirty);
        }
    }

    return wax.interaction()
        .attach(attach)
        .parent(function() {
          return map.parent;
        })
        .grid(grid);
};

Update mm control for new style


//
// moduleBundler
// Adds support for 'require' calls in Gistbooks
//

import * as bb from 'backbone';
import * as _ from 'underscore';
import * as detective from 'detective';

export default {
  getBundle: function(src) {
    var modules = detective(src);

    if (modules.length === 0) {
      return Promise.resolve(src);
    }

    var dependencies = {};

    _.each(modules, function(module) {
      dependencies[module] = 'latest';
    });

    var body = {
      options: { debug: true },
      dependencies: dependencies
    };

    return bb.$.post('http://wzrd.in/multi', JSON.stringify(body)).then(function(data) {
      var js = '';
      _.each(data, function(datum) {
        js += datum.bundle;
      });
      return js + src;
    });
  }
};

//
// moduleBundler
// Adds support for 'require' calls in Gistbooks
//

import * as bb from 'backbone';
import * as _ from 'underscore';
import * as detective from 'detective';

export default {
  getBundle: function(src) {
    var modules = detective(src);

    if (modules.length === 0) {
      return Promise.resolve(src);
    }

    var dependencies = {};

    _.each(modules, function(module) {
      dependencies[module] = 'latest';
    });

    var body = {
      options: { debug: true },
      dependencies: dependencies
    };

    return bb.$.post('http://wzrd.bocoup.com/multi', JSON.stringify(body)).then(function(data) {
      var js = '';
      _.each(data, function(datum) {
        js += datum.bundle;
      });
      return js + src;
    });
  }
};

Switch to Bocoup's hosted browserify-cdn.


import url from 'url';

export const isApiExternal = !!url.parse(__API_URL__).protocol;
export const apiUrl = !isApiExternal
  ? `${window.location.protocol}//${window.location.hostname}${
      __DEV__ ? ':8080' : ':' + window.location.port || ''
    }${__API_URL__}`
  : __API_URL__;

import url from 'url';

export const isApiExternal = !!url.parse(__API_URL__).protocol;
export const apiUrl = !isApiExternal
  ? `${window.location.protocol}//${window.location.hostname}:8080${__API_URL__}`
  : __API_URL__;

Fix production URL to GraphQL API is relative URL used


/**
 * Delete action with confirm dialog, triggers REST DELETE request
 *
 * @class   OroApp.DatagridActionDelete
 * @extends OroApp.DatagridAction
 */
OroApp.DatagridActionDelete = OroApp.DatagridAction.extend({

    /** @property Backbone.BootstrapModal */
    errorModal: undefined,

    /** @property Backbone.BootstrapModal */
    confirmModal: undefined,

    /**
     * Execute delete model
     */
    execute: function() {
        this.getConfirmDialog().open($.proxy(this.doDelete, this));
    },

    /**
     * Confirm delete item
     */
    doDelete: function() {
        var self = this;
        this.model.destroy({
            url: this.getLink(),
            wait: true,
            error: function() {
                self.getErrorDialog().open();
            }
        });
    },

    /**
     * Get view for confirm modal
     *
     * @return {Backbone.BootstrapModal}
     */
    getConfirmDialog: function() {
        if (!this.confirmModal) {
            this.confirmModal = new Backbone.BootstrapModal({
                title: 'Delete Confirmation',
                content: 'Are you sure you want to delete this item?'
            });
        }
        return this.confirmModal;
    },

    /**
     * Get view for error modal
     *
     * @return {Backbone.BootstrapModal}
     */
    getErrorDialog: function() {
        if (!this.errorModal) {
            this.confirmModal = new Backbone.BootstrapModal({
                title: 'Delete Error',
                content: 'Cannot delete item.',
                cancelText: false
            });
        }
        return this.confirmModal;
    }
});

/**
 * Delete action with confirm dialog, triggers REST DELETE request
 *
 * @class   OroApp.DatagridActionDelete
 * @extends OroApp.DatagridAction
 */
OroApp.DatagridActionDelete = OroApp.DatagridAction.extend({

    /** @property Backbone.BootstrapModal */
    errorModal: undefined,

    /** @property Backbone.BootstrapModal */
    confirmModal: undefined,

    /**
     * Execute delete model
     */
    execute: function() {
        this.getConfirmDialog().open();
    },

    /**
     * Confirm delete item
     */
    doDelete: function() {
        var self = this;
        this.model.destroy({
            url: this.getLink(),
            wait: true,
            error: function() {
                self.getErrorDialog().open();
            }
        });
    },

    /**
     * Get view for confirm modal
     *
     * @return {Backbone.BootstrapModal}
     */
    getConfirmDialog: function() {
        if (!this.confirmModal) {
            this.confirmModal = new Backbone.BootstrapModal({
                title: 'Delete Confirmation',
                content: 'Are you sure you want to delete this item?'
            });
            this.confirmModal.on('ok', _.bind(this.doDelete, this));
        }
        return this.confirmModal;
    },

    /**
     * Get view for error modal
     *
     * @return {Backbone.BootstrapModal}
     */
    getErrorDialog: function() {
        if (!this.errorModal) {
            this.confirmModal = new Backbone.BootstrapModal({
                title: 'Delete Error',
                content: 'Cannot delete item.',
                cancelText: false
            });
        }
        return this.confirmModal;
    }
});

Delete user error - fix bug


import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';

const EXCEPTIONS = [
  'table',
  'mitigation',
  'adaptation',
  'sectoral-information',
  'ndcs'
];

class ScrollToTop extends PureComponent {
  componentDidUpdate(prevProps) {
    const currentPath = this.props.location.pathname;
    const paths = currentPath.split('/');
    const lastPath = paths.slice(paths.length - 1)[0];
    if (
      currentPath !== prevProps.location.pathname &&
      EXCEPTIONS.indexOf(lastPath) === -1
    ) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    return this.props.children;
  }
}

ScrollToTop.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.node
};

export default withRouter(ScrollToTop);

import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';

const EXCEPTIONS = [
  'table',
  'mitigation',
  'adaptation',
  'sectoral-information',
  'ndcs',
  'models',
  'scenarios',
  'indicators'
];

class ScrollToTop extends PureComponent {
  componentDidUpdate(prevProps) {
    const currentPath = this.props.location.pathname;
    const paths = currentPath.split('/');
    const lastPath = paths.slice(paths.length - 1)[0];
    if (
      currentPath !== prevProps.location.pathname &&
      EXCEPTIONS.indexOf(lastPath) === -1
    ) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    return this.props.children;
  }
}

ScrollToTop.propTypes = {
  location: PropTypes.object.isRequired,
  children: PropTypes.node
};

export default withRouter(ScrollToTop);

Fix scroll to top when using the menu


var async = require( "async" );
var cta = require( "./chicago-cta" );

var main = function() {

    var tasks = [ cta.bus.getRoutes, cta.bus.getDirections, cta.bus.getStops ];

    async.waterfall( tasks, function ( err, result ) {

        // TODO: send this to mongo
        console.log( result );

    } );

};

if ( require.main === module ) {

    main();

}

var fs = require( "fs" );
var async = require( "async" );
var nomnom = require( "nomnom" );
var cta = require( "./chicago-cta" );

var writeJSONFile = function ( path, data, done ) {

    return fs.writeFile( path, JSON.stringify( data ), done );

};

var main = function() {

    var options = nomnom.option( "file", {
                abbr: "f",
                default: null,
                help: "Path to output file for JSON export"
            } )
            .parse();

    var tasks = [
        cta.bus.getRoutes,
        cta.bus.getDirections,
        cta.bus.getStops
    ];

    async.waterfall( tasks, function ( err, result ) {

        if ( options.file ) {
            writeJSONFile( options.file, result, function ( err ) {

                if ( err ) {
                    console.log( "Error generating JSON file" );
                } else {
                    console.log( "JSON file complete" );
                }

            } );
        } else {
            console.log( "TODO: send this to mongo" );
        }

    } );

};

if ( require.main === module ) {

    main();

}

Add option to generate JSON file


import test from 'ava'
import Onionoo from '../'

test('Onionoo is a function', t => {
  t.is(typeof Onionoo, 'function')
})

test('Onionoo cannot be invoked without \'new\'', t => {
  t.throws(() => Onionoo())
  t.notThrows(() => new Onionoo())
})

test('Onionoo instance is an object', t => {
  t.is(typeof new Onionoo(), 'object')
})

test('Onionoo instance contains expected endpoints', t => {
  const onionoo = new Onionoo()
  const expectedEndpoints = [
    'summary',
    'details',
    'bandwidth',
    'weights',
    'clients',
    'uptime'
  ]

  t.deepEqual(Object.keys(onionoo), expectedEndpoints)
})

import test from 'ava'
import Onionoo from '../'

test('Onionoo is a function', t => {
  t.is(typeof Onionoo, 'function')
})

test('Onionoo cannot be invoked without \'new\'', t => {
  t.throws(() => Onionoo())
  t.notThrows(() => new Onionoo())
})

test('Onionoo instance is an object', t => {
  const onionoo = new Onionoo()

  t.is(typeof onionoo, 'object')
})

test('Onionoo instance contains expected endpoints', t => {
  const onionoo = new Onionoo()
  const expectedEndpoints = [
    'summary',
    'details',
    'bandwidth',
    'weights',
    'clients',
    'uptime'
  ]

  t.deepEqual(Object.keys(onionoo), expectedEndpoints)
})

Tweak Onionoo instance object check to follow consistent pattern


import { Model as <%= className %>Mixin<%if (projections) {%>, defineProjections<%}%> } from '../mixins/regenerated/models/<%= name %>';
import <%if(parentModelName) {%><%= parentClassName %>Model from './<%= parentModelName %>';<%}else{%>{ Projection } from 'ember-flexberry-data';<%}%>
let Model = <%if(parentModelName) {%><%= parentClassName %>Model.extend<%}else{%>Projection.Model.extend<%}%>(<%= className %>Mixin, {

});
<%if(parentModelName) {%>Model.reopenClass({
  _parentModelName: '<%= parentModelName %>'
});<%}%>
<%if(projections) {%>defineProjections(Model);<%}%>
export default Model;

Fix JSCS of generated model files


'use strict'

const path = require('path')
const cp = require('child_process')
const distInfo = require('./dist-info')

const distPath = distInfo.getDistPath()
const productName = distInfo.getProductName()

let binaryPath = ''
if (process.platform === 'darwin') {
  binaryPath = path.join(distPath, `${productName}.app`, 'Contents', 'MacOS', `${productName}`)
} else if (process.platform === 'win32') {
  binaryPath = path.join(distPath, `${productName}`)
} else {
  console.error(`I dunno how to run on ${process.arch} :(`)
  process.exit(1)
}

module.exports = function () {
  const env = Object.assign({}, process.env, {NODE_ENV: 'development'})
  return cp.spawn(binaryPath, [], {env})
}

'use strict'

const path = require('path')
const cp = require('child_process')
const fs = require('fs')
const distInfo = require('./dist-info')

const distPath = distInfo.getDistPath()
const productName = distInfo.getProductName()

let binaryPath = ''
if (process.platform === 'darwin') {
  binaryPath = path.join(distPath, `${productName}.app`, 'Contents', 'MacOS', `${productName}`)
} else if (process.platform === 'win32') {
  binaryPath = path.join(distPath, `${productName}`)
} else {
  console.error(`I dunno how to run on ${process.arch} :(`)
  process.exit(1)
}

module.exports = function () {
  try {
    const stats = fs.statSync(binaryPath)
    if (!stats.isFile()) {
      return null
    }
  } catch (e) {
    return null
  }

  const env = Object.assign({}, process.env, {NODE_ENV: 'development'})
  return cp.spawn(binaryPath, [], {env})
}

Return null if the binary doesn't exist.


const InputNumber = require('domain_abstract/ui/InputNumber');

module.exports = require('./PropertyIntegerView').extend({

  events: {
    'change': 'inputValueChanged',
    'input': 'inputValueChangedSoft',
  },

  templateInput(model) {
    const ppfx = this.ppfx;
    return `
      <div class="${ppfx}field ${ppfx}field-range">
        <input type="range"
          min="${model.get('min')}"
          max="${model.get('max')}"
          step="${model.get('step')}"/>
      </div>
    `;
  },

  getSliderEl() {
    if (!this.slider) {
      this.slider = this.el.querySelector('input[type=range]');
    }

    return this.slider;
  },

  inputValueChanged() {
    const model = this.model;
    const step = model.get('step');
    this.getInputEl().value = this.getSliderEl().value;
    const value = this.getInputValue() - step;
    model.set('value', value, {avoidStore: 1}).set('value', value + step);
    this.elementUpdated();
  },

  inputValueChangedSoft() {
    this.getInputEl().value = this.getSliderEl().value;
    this.model.set('value', this.getInputValue(), {avoidStore: 1});
    this.elementUpdated();
  },

  setValue(value) {
    this.getSliderEl().value = value;
    this.inputInst.setValue(value, {silent: 1});
  },
});

const InputNumber = require('domain_abstract/ui/InputNumber');
const Property = require('./PropertyIntegerView');

module.exports = Property.extend({

  events: {
    'change': 'inputValueChanged',
    'input': 'inputValueChangedSoft',
  },

  templateInput(model) {
    const ppfx = this.ppfx;
    return `
      <div class="${ppfx}field ${ppfx}field-range">
        <input type="range"
          min="${model.get('min')}"
          max="${model.get('max')}"
          step="${model.get('step')}"/>
      </div>
    `;
  },

  getSliderEl() {
    if (!this.slider) {
      this.slider = this.el.querySelector('input[type=range]');
    }

    return this.slider;
  },

  inputValueChanged() {
    const model = this.model;
    const step = model.get('step');
    this.getInputEl().value = this.getSliderEl().value;
    const value = this.getInputValue() - step;
    model.set('value', value, {avoidStore: 1}).set('value', value + step);
    this.elementUpdated();
  },

  inputValueChangedSoft() {
    this.getInputEl().value = this.getSliderEl().value;
    this.model.set('value', this.getInputValue(), {avoidStore: 1});
    this.elementUpdated();
  },

  setValue(value) {
    this.getSliderEl().value = value;
    this.inputInst.setValue(value, {silent: 1});
  },

  onRender() {
    Property.prototype.onRender.apply(this, arguments);
    const model = this.model;

    if (!model.get('showInput')) {
      this.inputInst.el.style.display = 'none';
    }
  }
});

Make the slider input toggable


import React from 'react'

import GamesContainer from './containers/GamesContainer'

export default (props) => {
  return (

Mens

Womans

          <GamesContainer filter={{gender: 'male'}} />

          <GamesContainer filter={{gender: 'female'}} />

  )
}

import React from 'react'

import GamesContainer from './containers/GamesContainer'

export default (props) => {
  return (

Mens

          <GamesContainer filter={{gender: 'male'}} />

Womans

          <GamesContainer filter={{gender: 'female'}} />

  )
}

Fix overview page col headers


/**
 * wpscan module fpd-vulnerability.js
 * Scan a PHP file for Full Path Disclosure
 */

/**
 * Required modules
 */

const request = require( 'request' ).defaults( { followRedirect: false } )
const fs = require( '../fs' )
const log = require( '../log' )

/**
 * Initiator method
 *
 * @param   {Object}  data  Data object with request values
 * @return  void
 */

exports.fire = ( data ) => {

    const { wpURL, siteURL, userAgent, silentMode } = data

    const filterName = fs.fileName( __filename, '.js' )

    const logObj = { silentMode, filterName }

    const targetURL = `${wpURL}/wp-includes/rss.php`

    request( {
        'url': targetURL,
        'method': 'GET',
        'headers': { 'User-Agent': userAgent }
    }, ( error, response, body ) => {

        if ( error || response.statusCode === 404 ) {
            return log.info( `${targetURL} is not found`, logObj )
        }

        if ( body.includes( '.php' ) ) {
            return log.warn( `${siteURL} is affected by FPD vulnerability`, logObj )
        }

        return log.ok( `${siteURL} is not affected by FPD vulnerability`, logObj )

    } )
}

/**
 * wpscan module fpd-vulnerability.js
 * Scan a PHP file for Full Path Disclosure
 */

/**
 * Required modules
 */

const request = require( 'request' ).defaults( { followRedirect: false } )
const fs = require( '../fs' )
const log = require( '../log' )

/**
 * Initiator method
 *
 * @param   {Object}  data  Data object with request values
 * @return  void
 */

exports.fire = ( data ) => {

    const { wpURL, siteURL, userAgent, silentMode } = data

    const filterName = fs.fileName( __filename, '.js' )

    const logObj = { silentMode, filterName }

    const targetURL = `${wpURL}/wp-includes/rss.php`

    request( {
        'url': targetURL,
        'method': 'GET',
        'headers': { 'User-Agent': userAgent }
    }, ( error, response, body ) => {

        if ( error || response.statusCode === 404 ) {
            return log.info( `${targetURL} is not found`, logObj )
        }

        if ( body.includes( '_deprecated_file' ) ) {
            return log.warn( `${siteURL} is affected by FPD vulnerability`, logObj )
        }

        return log.ok( `${siteURL} is not affected by FPD vulnerability`, logObj )

    } )
}

Edit search pattern for FPD vulnerability scan


import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return this.store.createRecord('parseUser');
  },

  actions: {
    createUser: function(user) {
      var interestArray = new Array();
      $('input[name=interest]').each(function() {
        interestArray.push($(this).val());
      });

      user.set('email', user.get('username'));
      user.set('interests', interestArray);
      user.save().then(function() {
        this.get('session').authenticate('authenticator:parse-token', {
          sessionToken: user.get('sessionToken')
        });
        this.transitionTo('index');
      }.bind(this));
    }
  }
});

import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return this.store.createRecord('parseUser');
  },

  actions: {
    createUser: function(user) {
      var interestArray = [];
      $('input[name=interest]').each(function() {
        interestArray.push($(this).val());
      });

      user.set('email', user.get('username'));
      user.set('interests', interestArray);
      user.save().then(function() {
        this.get('session').authenticate('authenticator:parse-token', {
          sessionToken: user.get('sessionToken')
        });
        this.transitionTo('index');
      }.bind(this));
    }
  }
});

Update array to literal notation


export const selectNewSensor = ({ newSensor }) => {
  const { macAddress = '' } = newSensor || {};
  return macAddress;
};

export const selectHotConsecutiveTemperatureThreshold = ({ newSensor }) => {
  const { COLD_CONSECUTIVE: coldConfig } = newSensor;
  const { temperature: coldConfigTemp } = coldConfig;

  const hotConfigTempThreshold = coldConfigTemp + 0.1;

  return hotConfigTempThreshold;
};

export const selectHotCumulativeTemperatureThreshold = ({ newSensor }) => {
  const { COLD_CUMULATIVE: coldConfig } = newSensor;
  const { temperature: coldConfigTemp } = coldConfig;

  const hotConfigTempThreshold = coldConfigTemp + 0.1;

  return hotConfigTempThreshold;
};

export const selectColdConsecutiveTemperatureThreshold = ({ newSensor }) => {
  const { HOT_CONSECUTIVE: hotConfig } = newSensor;
  const { temperature: hotConfigTemp } = hotConfig;

  const coldConfigTempThreshold = hotConfigTemp - 0.1;

  return coldConfigTempThreshold;
};

export const selectColdCumulativeTemperatureThreshold = ({ newSensor }) => {
  const { HOT_CUMULATIVE: hotConfig } = newSensor;
  const { temperature: hotConfigTemp } = hotConfig;

  const coldConfigTempThreshold = hotConfigTemp - 0.1;

  return coldConfigTempThreshold;
};

export const selectConfigTemperatureThresholds = state => {
  const coldCumulativeConfigTempThreshold = selectColdCumulativeTemperatureThreshold(state);
  const hotCumulativeConfigTempThreshold = selectHotCumulativeTemperatureThreshold(state);

  const coldConsecutiveConfigTempThreshold = selectColdConsecutiveTemperatureThreshold(state);
  const hotConsecutiveConfigTempThreshold = selectHotConsecutiveTemperatureThreshold(state);

  return {
    COLD_CUMULATIVE: coldCumulativeConfigTempThreshold,
    HOT_CUMULATIVE: hotCumulativeConfigTempThreshold,
    COLD_CONSECUTIVE: coldConsecutiveConfigTempThreshold,

    HOT_CONSECUTIVE: hotConsecutiveConfigTempThreshold,
  };
};

Add selectors to select the threshold values for config temperatures


// @flow
import 'bootstrap/dist/css/bootstrap.css';
import createBrowserHistory from 'history/createBrowserHistory'
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createPersistor, getStoredState } from 'redux-persist';
import App from './App';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import configureStore from './store';

const history = createBrowserHistory();
const persistConfig = {};

async function bootstrap() {
  const restoredState = await getStoredState(persistConfig);
  const store = configureStore(history, restoredState);
  createPersistor(store, persistConfig);

  ReactDOM.render(<Provider store={store}>
    <ConnectedRouter history={history}>
      <App/>
    </ConnectedRouter>
  </Provider>, document.getElementById('root'));
  registerServiceWorker();
}

bootstrap();

// @flow
import 'bootstrap/dist/css/bootstrap.css';
import createHistory from 'history/createHashHistory'
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createPersistor, getStoredState } from 'redux-persist';
import App from './App';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import configureStore from './store';

const history = createHistory();
const persistConfig = {};

async function bootstrap() {
  const restoredState = await getStoredState(persistConfig);
  const store = configureStore(history, restoredState);
  createPersistor(store, persistConfig);

  ReactDOM.render(<Provider store={store}>
    <ConnectedRouter history={history}>
      <App/>
    </ConnectedRouter>
  </Provider>, document.getElementById('root'));
  registerServiceWorker();
}

bootstrap();

Move to hash history to allow refresh when on java server


/**
 * This is a manifest file that will be compiled into application.js, which will
 * include all the files listed below.
 *
 * Any JavaScript file within this directory can be referenced here using a
 * relative path.
 *
 * It's not advisable to add code directly here, but if you do, it will appear
 * at the bottom of the compiled file.
 */

//= require lib/jquery-2.1.0.min
//= require lib/moment-2.6.0.min
//= require lib/moment-langs.min
//= require lib/daterangepicker-1.3.5
//= require lib/bootstrap.min
//= require lib/underscore-1.6.0.min
//= require lib/angular-1.2.16
//= require lib/angular-route-1.2.16
//= require lib/angular-cookies-1.2.16
//= require lib/angular-file-upload-0.5.5
//= require lib/ng-bs-daterangepicker-0.0.1
//= require lib/angular-google-maps-1.0.18.min
//= require lib/angular-strap-2.0.1
//= require config
//= require_tree ng
//= require lib/ng-directives

/**
 * This is a manifest file that will be compiled into application.js, which will
 * include all the files listed below.
 *
 * Any JavaScript file within this directory can be referenced here using a
 * relative path.
 *
 * It's not advisable to add code directly here, but if you do, it will appear
 * at the bottom of the compiled file.
 */

//= require lib/jquery-2.1.0.min
//= require lib/moment-2.6.0.min
//= require lib/moment-langs.min
//= require lib/daterangepicker-1.3.5
//= require lib/bootstrap.min
//= require lib/underscore-1.6.0.min
//= require lib/angular-1.2.16
//= require lib/angular-locale_ru-ru
//= require lib/angular-route-1.2.16
//= require lib/angular-cookies-1.2.16
//= require lib/angular-file-upload-0.5.5
//= require lib/ng-bs-daterangepicker-0.0.1
//= require lib/angular-google-maps-1.0.18.min
//= require lib/angular-strap-2.0.1
//= require lib/angular-strap-tpl-2.0.1
//= require config
//= require_tree ng
//= require lib/ng-directives

Add russian i18n for angular.


BeTogether.Router.map(function(){
  this.resource('home', {path: '/'});
  this.resource('profile', function(){
    this.resource('trip', {path: ':trip_id'});
  });
});

BeTogether.Router.map(function(){
  this.resource('home', {path: '/'});
  this.resource('profile', function(){
    this.resource('trip', {path: 'trips/:name'});
  });
});

Change the url for trip template


'use strict';

angular.module('classBrowserUHApp.offeredclass', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/offeredclass', {
        templateUrl: 'views/offeredclass/offeredclass.html',
        controller: 'OfferedClassCtrl'
    });
}])

.controller('OfferedClassCtrl', ['$scope', '$http', '$q', '$rootScope', function ($scope, $http, $q, $rootScope) {

    $scope.format = {
        availableOptions: [
        {formatType: 'Online', isOrNot: 'Is'},
        {formatType: 'Not Online', isOrNot: 'Not'},
        {formatType: 'Not Face to Face', isOrNot: 'Not'},
        {formatType: 'Face to Face', isOrNot: 'Is'},
        {formatType: 'Not Hybrid', isOrNot: 'Not'},
        {formatType: 'Hybrid', isOrNot: 'Is'}
    ]
    };

    $rootScope.httpService.getData('resources/terms.json').then(function(result) {
        $scope.terms = result;
    });

}]);

'use strict';

angular.module('classBrowserUHApp.offeredclass', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/offeredclass', {
        templateUrl: 'views/offeredclass/offeredclass.html',
        controller: 'OfferedClassCtrl'
    });
}])

.controller('OfferedClassCtrl', ['$scope', '$http', '$q', '$rootScope', function ($scope, $http, $q, $rootScope) {

    $rootScope.httpService.getData('resources/format.json').then(function(result) {
        $scope.format = result;
    });

    $rootScope.httpService.getData('resources/terms.json').then(function(result) {
        $scope.terms = result;
    });

}]);

Set format options from JSON file, resources/format.json.


// Copyright (c) 2016-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

'use strict'

const express = require('express')
const app = express()
const auth = require('http-auth')

const port = process.env.PORT || 3000

// Basic auth
// Set USER and PASSWORD environment variables
const basic = auth.basic({
  realm: 'Salesforce Lightning Design System Prototype'
}, (username, password, next) => {
  next(username === process.env.USERNAME && password === process.env.PASSWORD)
})

if (process.env.USERNAME && process.env.PASSWORD) {
  app.use(auth.connect(basic))
}

app.use('/', express.static('./dist'))

app.listen(port, () =>
  console.log(`Listening on port ${port}!\n\nDeveloping locally? Run "npm run dev" instead.`))

// Copyright (c) 2016-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

'use strict'

const express = require('express')
const app = express()
const auth = require('http-auth')

const port = process.env.PORT || 3000

// Basic auth
// Set USERNAME and PASSWORD environment variables
const basic = auth.basic({
  realm: 'Salesforce Lightning Design System Prototype'
}, (username, password, next) => {
  next(username === process.env.USERNAME && password === process.env.PASSWORD)
})

if (process.env.USERNAME && process.env.PASSWORD) {
  app.use(auth.connect(basic))
}

app.use('/', express.static('./dist'))

app.listen(port, () =>
  console.log(`Listening on port ${port}!\n\nDeveloping locally? Run "npm run dev" instead.`))

Fix environment variable name in comment


module.exports = createApp

var express = require('express')
var PORT = process.env.port || 80

function createApp() {
  var app = express()
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade')
  app.use(express.static('app'))

  app.get('/', function (req, res) {
    res.render('index.jade')
  })

  return app
}

function startApp() {
  var app = createApp()
  app.listen(PORT)
}

if (module === require.main) {
  startApp()
  console.log('listening on port ' + PORT)
}

module.exports = createApp

var express = require('express')
var PORT = process.env.port || 80

function createApp() {
  var app = express()
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade')
  app.use(express.static(__dirname + '/app'))

  app.get('/', function (req, res) {
    res.render('index.jade')
  })

  return app
}

function startApp() {
  var app = createApp()
  app.listen(PORT)
}

if (module === require.main) {
  startApp()
  console.log('listening on port ' + PORT)
}

Use correct directory for static files


'use strict';

module.exports = {
  CATEGORY_NAME_MAXLENGTH: 30,
  NOTE_TITLE_MAXLENGTH: 30,
  NOTE_NOTE_MAXLENGTH: 500
};

'use strict';

module.exports = {
  CATEGORY_NAME_MAXLENGTH: 20,
  NOTE_TITLE_MAXLENGTH: 20,
  NOTE_NOTE_MAXLENGTH: 500
};

Reduce category name and note title to 20 characters


var measureCRP = function() {
	var span = document.getElementsByTagName('span')[0];
	span.innerText = 'interactive'; // change DOM text content
	span.style.display = 'inline';  // change CSSOM property
	// create a new element, style it, and append it to the DOM
	var loadTime = document.createElement('div');
	loadTime.innerText = 'You loaded this page on: ' + new Date();
	loadTime.style.color = 'blue';
	document.body.appendChild(loadTime);
}

function measureCRP() {
    var span = document.getElementsByTagName('span')[0];
    span.innerText = 'interactive'; // Change DOM text content.
    span.style.display = 'inline';  // Change CSSOM property.
    // Create a new element, style it, and append it to the DOM.
    var loadTime = document.createElement('div');
    loadTime.innerText = 'You loaded this page on: ' + new Date();
    loadTime.style.color = 'blue';
    document.body.appendChild(loadTime);
}

Fix code style issues in sample


var _ = require('lodash');

module.exports = function(config, reducerName, records) {
  // All given records must have a key
  var allKeys = _.all(records, config.key);
  if (!allKeys) throw new Error(reducerName + ': Expected to have all records with ' + config.key);
}

var _ = require('lodash');

module.exports = function(config, reducerName, records) {
  // All given records must have a key
  var allKeys = _.all(records, config.key);
  if (!allKeys) throw new Error(reducerName + ': Expected all records to have a value for the store\'s key `' + config.key + '`');
}

Make the "your config.key, or your data, hasn't got the right `key` field" error message a little clearer.


// @flow
module.exports = {
  env: {
    jest: true,
  },
  plugins: ['testing-library'],
  extends: ['plugin:jest/recommended', 'plugin:testing-library/react'],
  rules: {
    'react/jsx-no-bind': 0,
    // This rule isn't useful because use Flow.
    'jest/valid-title': 0,

    // Adding more errors now
    'testing-library/no-manual-cleanup': 'error',
  },
};

// @flow
module.exports = {
  env: {
    jest: true,
  },
  plugins: ['testing-library'],
  extends: ['plugin:jest/recommended', 'plugin:testing-library/react'],
  rules: {
    'react/jsx-no-bind': 0,
    // This rule isn't useful because use Flow.
    'jest/valid-title': 0,

    // Adding more errors now
    'testing-library/no-manual-cleanup': 'error',
    'testing-library/no-wait-for-empty-callback': 'error',
    'testing-library/no-wait-for-snapshot': 'error',
    'testing-library/prefer-wait-for': 'error',
  },
};

Add new rules that are not offended just yet


/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */
import React from 'react';
import { Wizard } from '../widgets';
import { PatientSelect } from '../widgets/Tabs/PatientSelect';
import { PatientEdit } from '../widgets/Tabs/PatientEdit';
import { PrescriptionConfirmation } from '../widgets/Tabs/PrescriptionConfirmation';

import { dispensingStrings } from '../localization';

const tabs = [
  {
    component: PatientSelect,
    name: 'patient',
    title: dispensingStrings.select_the_patient,
  },
  { component: PatientEdit, name: 'edit', title: dispensingStrings.edit_the_patient },
  { component: PrescriptionConfirmation, name: 'prescription', title: dispensingStrings.finalise },
];

export const VaccineDispensingPage = () => <Wizard tabs={tabs} />;

/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2021
 */
import React from 'react';
import { Wizard } from '../widgets';
import { PatientSelect } from '../widgets/Tabs/PatientSelect';
import { PatientEdit } from '../widgets/Tabs/PatientEdit';
import { PrescriptionConfirmation } from '../widgets/Tabs/PrescriptionConfirmation';

import { dispensingStrings } from '../localization';

const tabs = [
  {
    component: PatientSelect,
    name: 'patient',
    title: dispensingStrings.select_the_patient,
  },
  { component: PatientEdit, name: 'edit', title: dispensingStrings.edit_the_patient },
  { component: PrescriptionConfirmation, name: 'prescription', title: dispensingStrings.finalise },
];

export const VaccineDispensingPage = () => <Wizard captureUncaughtGestures={false} tabs={tabs} />;

Remove capturing of gestures from vaccine wizard


Tinytest.add('init', function (test) {
  var TestCollection = new Mongo.Collection()
, kriegslustigFilterTestInstance = Object.create(KriegslustigFilter)
  kriegslustigFilterTestInstance.collection = TestCollection
  kriegslustigFilterTestInstance.init()
  TestCollection.insert({
    something: true
  })
  test.equal(kriegslustigFilterTestInstance._items[0].something, true, 'Initiation failed');
});

Tinytest.add('getItems', function (test) {
  var TestCollection = new Mongo.Collection()
, kriegslustigFilterTestInstance = Object.create(KriegslustigFilter)
  kriegslustigFilterTestInstance.collection = TestCollection
  kriegslustigFilterTestInstance.init()
  TestCollection.insert({
    something: true
  })
  test.equal(kriegslustigFilterTestInstance.getItems()[0].something, true, 'Initiation failed');
});

Tinytest.add('init', function (test) {
  var TestCollection = new Mongo.Collection()
, kriegslustigFilterTestInstance = Object.create(KriegslustigFilter)
  kriegslustigFilterTestInstance.collection = TestCollection
  kriegslustigFilterTestInstance.init()
  TestCollection.insert({
    something: true
  })
  test.equal(kriegslustigFilterTestInstance._items[0].something, true, 'Initiation failed');
});

Tinytest.add('getItems', function (test) {
  var TestCollection = new Mongo.Collection()
, kriegslustigFilterTestInstance = Object.create(KriegslustigFilter)
  kriegslustigFilterTestInstance.collection = TestCollection
  kriegslustigFilterTestInstance.init()
  TestCollection.insert({
    something: true
  })
  test.equal(kriegslustigFilterTestInstance.getItems()[0].something, true, 'Initiation failed');
  TestCollection.insert({
    something: false
  })
  test.equal(kriegslustigFilterTestInstance._items[1].something, false, '_items didn\'nt reactively update')
});

Tinytest.add('Listen for changes in the collection', function (test) {
  var kriegslustigFilterTestInstance = Object.create(KriegslustigFilter)
  kriegslustigFilterTestInstance.collection = new Mongo.Collection()
  kriegslustigFilterTestInstance.init()
  kriegslustigFilterTestInstance.collection.insert({
    test: true
  })
  test.equal(kriegslustigFilterTestInstance._items[0].test, true, 'It should reactively update the _items array after the collection updates')
})

Add a test: 'Listen for changes in the collection'


import express from "express"
import graphqlHTTP from "express-graphql"
import { graphqlExpress, graphiqlExpress } from "graphql-server-express"
import bodyParser from "body-parser"
import mongoose, { Schema } from "mongoose"
import schema from "./graphql"
import Item from "./db/item"

// Constants
const CONFIG = require("./settings.yaml")

const app = express()

mongoose.Promise = global.Promise
mongoose.connect(
  `mongodb://${CONFIG.mongo.url}:${CONFIG.mongo.port}/${CONFIG.mongo.database}`,
  {
    useMongoClient: true
  }
)

app.use(
  "/graphql",
  bodyParser.json({
    limit: "15mb"
  }),
  graphqlExpress(() => ({
    schema
  }))
)

app.use(
  "/graphiql",
  graphiqlExpress({
    endpointURL: "/graphql"
  })
)

export { app, CONFIG }

import express from "express"
import graphqlHTTP from "express-graphql"
import { graphqlExpress, graphiqlExpress } from "graphql-server-express"
import bodyParser from "body-parser"
import mongoose, { Schema } from "mongoose"
import schema from "./graphql"
import Item from "./db/item"

// Constants
const CONFIG = require("./settings.yaml")

const app = express()

mongoose.Promise = global.Promise
mongoose.connect(
  `mongodb://${CONFIG.mongodb.url}:${CONFIG.mongodb.port}/${CONFIG.mongodb.database}`,
  {
    useMongoClient: true
  }
)

app.use(
  "/graphql",
  bodyParser.json({
    limit: "15mb"
  }),
  graphqlExpress(() => ({
    schema
  }))
)

app.use(
  "/graphiql",
  graphiqlExpress({
    endpointURL: "/graphql"
  })
)

export { app, CONFIG }

Change names of variables in settings file


'use strict';

var express = require('express');
var controller = require('./bounceEndpoint');

var router = express.Router();

router.get('/', controller.show);
router.get('/:token', controller.show);
router.post('/new', controller.create);

module.exports = router;

'use strict';

var express = require('express');
var controller = require('./bounceEndpoint');

var router = express.Router();

router.get('/:token', controller.show);
router.post('/new', controller.create);
router.put('/defer', controller.defer);

module.exports = router;

Add missing route for deferring


export default {
    getReadWidget:   () => '<ma-choices-column values="::entry.listValues[field.name()]"></ma-choices-column>',
    getLinkWidget:   () => '<ma-reference-many-link-column ids="::value" values="::entry.listValues[field.name()]" field="::field"></ma-reference-many-link-column>',
    getFilterWidget: () => '<ma-choices-field field="::field" value="value" datastore="::datastore"></ma-choices-field>',
    getWriteWidget:  () => '<ma-reference-many-field field="::field" value="value" datastore="::datastore"></ma-reference-many-field>'
};

export default {
    getReadWidget:   () => '<ma-choices-column values="::entry.listValues[field.name()]"></ma-choices-column>',
    getLinkWidget:   () => '<ma-reference-many-link-column ids="::value" values="::entry.listValues[field.name()]" field="::field"></ma-reference-many-link-column>',
    getFilterWidget: () => '<ma-reference-many-field field="::field" value="value" datastore="::datastore"></reference-many-field-field>',
    getWriteWidget:  () => '<ma-reference-many-field field="::field" value="value" datastore="::datastore"></ma-reference-many-field>'
};

Allow reference_many field to be used as filter


var h5s = ['abbr', 'aside', 'article', 'code', 'figcaption', 'figure',
		'section', 'time'];
for (var i = 0; i < h5s.length; i++)
	document.createElement(h5s[i]);
window.onload = function () { $('h1').after('
Google Chrome recommended.
'); };

var h5s = ['abbr', 'aside', 'article', 'code', 'figcaption', 'figure',
		'nav', 'section', 'time'];
for (var i = 0; i < h5s.length; i++)
	document.createElement(h5s[i]);
window.onload = function () { $('h1').after('
Google Chrome recommended.
'); };

Add nav element to IE fix


import {polyfill} from 'es6-promise';
polyfill();

export const data = {
  email: 'foo@bar.baz',
  emptyArray: [],
  error: 'foo',
  errorMsg: 'Set sail for fail!',
  found: 'bar',
  message: 'Wubba lubba dub dub!',
  notFound: 'baz',
  none: ' ',
  obj: {
    foo: 'bar'
  },
  objArray: [
    {foo: 'bar'}
  ]
};

export const loggerMock = {
  error: () => {
  },
  info: () => {
  },
  log: () => {
  },
  warn: () => {
  }
};

import {polyfill} from 'es6-promise';
if (global.Promise === undefined) {
  polyfill();
}

export const data = {
  email: 'foo@bar.baz',
  emptyArray: [],
  error: 'foo',
  errorMsg: 'Set sail for fail!',
  found: 'bar',
  message: 'Wubba lubba dub dub!',
  notFound: 'baz',
  none: ' ',
  obj: {
    foo: 'bar'
  },
  objArray: [
    {foo: 'bar'}
  ]
};

export const loggerMock = {
  error: () => {
  },
  info: () => {
  },
  log: () => {
  },
  warn: () => {
  }
};

Use native Promise in tests if available


module.exports = function(grunt) {
  return grunt.registerMultiTask('mkdir', 'Make directories.', function() {
    var options;
    options = this.options({
      mode: null,
      create: []
    });
    grunt.verbose.writeflags(options, 'Options');
    return options.create.forEach(function(filepath) {
      grunt.log.write('Creating "' + filepath + '"...');
      try {
        filepath = grunt.template.process( filepath );
        grunt.file.mkdir(filepath, options.mode);
        return grunt.log.ok();
      } catch (e) {
        grunt.log.error();
        grunt.verbose.error(e);
        return grunt.fail.warn('Mkdir operation failed.');
      }
    });
  });
};

module.exports = function(grunt) {
  return grunt.registerMultiTask('mkdir', 'Make directories.', function() {
    var options;
    options = this.options({
      mode: null,
      create: []
    });
    grunt.verbose.writeflags(options, 'Options');
    return options.create.forEach(function(filepath) {
      grunt.log.write('Creating "' + filepath + '"...');
      try {
        filepath = grunt.template.process(filepath);
        grunt.file.mkdir(filepath, options.mode);
        return grunt.log.ok();
      } catch (e) {
        grunt.log.error();
        grunt.verbose.error(e);
        return grunt.fail.warn('Mkdir operation failed.');
      }
    });
  });
};

Change to code style that reflects rest of project


'use strict';

const config = require('config');

const cache = require('gulp-cached');
const failure = require('../lib/helpers/failure');
const task = require('../lib/helpers/task');
const pages = require('../lib/tasks/pages');
const sync = require('browser-sync');

module.exports = gulp => {
  // ////////////////////////////
  // Compile all Pages
  // ////////////////////////////
  gulp.task('pages', 'Compiles markdown and HTML files using Nunjucks, making front matter available at compile time', () => {
    return gulp.src(config.watch.pages)
      .pipe(cache('pages'))
      .pipe(pages.compile())
        .on('error', failure('pages'))
      .pipe(gulp.dest(task.dest('')))
      .pipe(sync.stream({
        match: '**/*.html',
      }));
  });

  // ////////////////////////////
  // Watch for changes in all Pages and recompile them
  // ////////////////////////////
  task.watch('pages', config.watch.pages, 'pages', gulp);
};

'use strict';

const config = require('config');

const clone = require('lodash/cloneDeep');
const failure = require('../lib/helpers/failure');
const task = require('../lib/helpers/task');
const pages = require('../lib/tasks/pages');
const sync = require('browser-sync');

module.exports = gulp => {
  // ////////////////////////////
  // Compile all Pages
  // ////////////////////////////
  gulp.task('pages', 'Compiles markdown and HTML files using Nunjucks, making front matter available at compile time', () => {
    return gulp.src(config.watch.pages)
      .pipe(pages.compile())
        .on('error', failure('pages'))
      .pipe(gulp.dest(task.dest('')))
      .pipe(sync.stream({
        match: '**/*.html',
      }));
  });

  // ////////////////////////////
  // Watch for changes in all Pages and recompile them
  // ////////////////////////////
  const watch = clone(config.watch.pages);
  watch.push(`${config.folders.templates}/**/*`);

  task.watch('pages', watch, 'pages', gulp);
};

Fix Pages rendering on template change


require("@babel/register")({
  extensions: [".ts", ".js", ".tsx", ".jsx"],
})

require("coffeescript/register")
require("@babel/polyfill")
require("raf/polyfill")
require("should")
require("./src/lib/jade_hook")

// FIXME: Do we need this?
// NOTE: Once we do AOT compilation we probably want to re-enable this on the server in development mode only.
// require('source-map-support/register')

const path = require("path")
const Adapter = require("enzyme-adapter-react-16")
const Enzyme = require("enzyme")
const sd = require("sharify").data

// TODO: Look into why this bumps user off of other node command-line tab
require("dotenv").config({
  path: path.join(process.cwd(), ".env.test"),
})

Enzyme.configure({
  adapter: new Adapter(),
})

try {
  window.matchMedia =
    window.matchMedia ||
    function() {
      return {
        matches: false,
        addListener: function() {},
        removeListener: function() {},
      }
    }
} catch (error) {}

sd.AP = {
  loginPagePath: "/login",
  signupPagePath: "/signup",
  facebookPath: "/facebook",
  twitterPath: "/twitter",
}
sd.APP_URL = "http://artsy.net"

require("@babel/register")({
  extensions: [".ts", ".js", ".tsx", ".jsx"],
})

require("coffeescript/register")
require("@babel/polyfill")
require("raf/polyfill")
require("should")
require("./src/lib/jade_hook")

// FIXME: Do we need this?
// NOTE: Once we do AOT compilation we probably want to re-enable this on the server in development mode only.
// require('source-map-support/register')

const path = require("path")
const Adapter = require("enzyme-adapter-react-16")
const Enzyme = require("enzyme")
const sd = require("sharify").data

// TODO: Look into why this bumps user off of other node command-line tab
require("dotenv").config({
  path: path.join(process.cwd(), ".env.test"),
})

Enzyme.configure({
  adapter: new Adapter(),
})

try {
  global.matchMedia = window.matchMedia =
    window.matchMedia ||
    function() {
      return {
        matches: false,
        addListener: function() {},
        removeListener: function() {},
      }
    }
} catch (error) {}

sd.AP = {
  loginPagePath: "/login",
  signupPagePath: "/signup",
  facebookPath: "/facebook",
  twitterPath: "/twitter",
}
sd.APP_URL = "http://artsy.net"

Add matchMedia to the global scope


PromiseAllSync = {
  extend: function(PromiseClass) {
    PromiseClass.allSync = function(collection, fn, unfn) {
      var stack = [];
      return collection.reduce(function(promise, item) {
        return promise.then(function() {
          var nextPromise = fn ? fn(item) : item;
          return nextPromise.then(function() {
            if(unfn) stack.push(item);
          });
        });
      }, PromiseClass.resolve())
        .catch(function(e) {
          if(unfn) while(stack.length) unfn(stack.pop());
          return PromiseClass.reject(e);
        });
    }
  }
};

PromiseAllSync = {
  extend: function(PromiseClass) {
    PromiseClass.allSync = function(collection, fn, unfn) {
      var stack = [];
      return collection.reduce(function(promise, item) {
        return promise.then(function() {
          var nextPromise = fn ? fn(item) : item;
          return nextPromise.then(function() {
            if(unfn) stack.push(item);
          });
        });
      }, (PromiseClass.resolve || PromiseClass.when)())
        .catch(function(e) {
          if(unfn) while(stack.length) unfn(stack.pop());
          return PromiseClass.reject(e);
        });
    }
  }
};

Support Q, use when not static resolve


// @flow

import { longAbsoluteDate } from 'lib/utils/date-utils';
import { connect } from 'lib/utils/redux-utils';
import * as React from 'react';

import { SingleLine } from '../components/single-line.react';
import type { AppState } from '../redux/redux-setup';
import { styleSelector } from '../themes/colors';

export type DisplayType = 'lowContrast' | 'modal';

type Props = {|
  time: number,
  display: DisplayType,
  // Redux state
  styles: typeof styles,
|};
function Timestamp(props: Props) {
  const style = [props.styles.timestamp];
  if (props.display === 'modal') {
    style.push(props.styles.modal);
  }
  return (
    <SingleLine style={style}>
      {longAbsoluteDate(props.time).toUpperCase()}
    </SingleLine>
  );
}

const timestampHeight = 26;

const styles = {
  modal: {
    // high contrast framed against OverlayNavigator-dimmed background
    color: 'white',
  },
  timestamp: {
    alignSelf: 'center',
    bottom: 0,
    color: 'listBackgroundTernaryLabel',
    fontSize: 14,
    height: timestampHeight,
    paddingVertical: 3,
  },
};
const stylesSelector = styleSelector(styles);

const WrappedTimestamp = connect((state: AppState) => ({
  styles: stylesSelector(state),
}))(Timestamp);

export { WrappedTimestamp as Timestamp, timestampHeight };

// @flow

import { longAbsoluteDate } from 'lib/utils/date-utils';
import * as React from 'react';

import { SingleLine } from '../components/single-line.react';
import { useStyles } from '../themes/colors';

export type DisplayType = 'lowContrast' | 'modal';

type Props = {|
  +time: number,
  +display: DisplayType,
|};
function Timestamp(props: Props) {
  const styles = useStyles(unboundStyles);
  const style = [styles.timestamp];
  if (props.display === 'modal') {
    style.push(styles.modal);
  }
  return (
    <SingleLine style={style}>
      {longAbsoluteDate(props.time).toUpperCase()}
    </SingleLine>
  );
}

const timestampHeight = 26;

const unboundStyles = {
  modal: {
    // high contrast framed against OverlayNavigator-dimmed background
    color: 'white',
  },
  timestamp: {
    alignSelf: 'center',
    bottom: 0,
    color: 'listBackgroundTernaryLabel',
    fontSize: 14,
    height: timestampHeight,
    paddingVertical: 3,
  },
};

export { Timestamp, timestampHeight };

Use hook instead of connect functions and HOC in Timestamp


export default class FocusHandler {
  inputFocused = false;
  editorMouseDown = false;

  onEditorMouseDown = ():void => {
    this.editorFocused = true;
  }

  onInputMouseDown = ():void => {
    this.inputFocused = true;
  }

  isEditorBlur = (event): void => {
    if (
      (event.target.tagName === 'INPUT' || event.target.tagName === 'LABEL' || event.target.tagName === 'TEXTAREA') &&
      !this.editorFocused
    ) {
      this.inputFocused = false;
      return true;
    } else if (
      (event.target.tagName !== 'INPUT' || event.target.tagName !== 'LABEL' || event.target.tagName === 'TEXTAREA') &&
      !this.inputFocused
    ) {
      this.editorFocused = false;
      return true;
    }
    return false;
  };

  isEditorFocused = ():void => {
    if (!this.inputFocused) {
      return true;
    }
    this.inputFocused = false;
    return false;
  }

  isToolbarFocused = ():void => {
    if (!this.editorFocused) {
      return true;
    }
    this.editorFocused = false;
    return false;
  }

  isInputFocused = ():void => this.inputFocused;
}

export default class FocusHandler {
  inputFocused = false;
  editorMouseDown = false;

  onEditorMouseDown = ():void => {
    this.editorFocused = true;
  }

  onInputMouseDown = ():void => {
    this.inputFocused = true;
  }

  isEditorBlur = (event): void => {
    if (
      (event.target.tagName === 'INPUT' || event.target.tagName === 'LABEL' || event.target.tagName === 'TEXTAREA') &&
      !this.editorFocused
    ) {
      this.inputFocused = false;
      return true;
    } else if (
      (event.target.tagName !== 'INPUT' || event.target.tagName !== 'LABEL' || event.target.tagName !== 'TEXTAREA') &&
      !this.inputFocused
    ) {
      this.editorFocused = false;
      return true;
    }
    return false;
  };

  isEditorFocused = ():void => {
    if (!this.inputFocused) {
      return true;
    }
    this.inputFocused = false;
    return false;
  }

  isToolbarFocused = ():void => {
    if (!this.editorFocused) {
      return true;
    }
    this.editorFocused = false;
    return false;
  }

  isInputFocused = ():void => this.inputFocused;
}

Use correct comparison operator for TEXTAREA in isEditorBlur


import { expect } from 'chai'

import AtomicString from '../../src/atomic/string'

/** @test {AtomicString} */
describe('AtomicString', () => {
  const bitArrayHello = [104, 97, 108, 108, 111, 0, 0, 0]
  let atomic

  before(() => {
    atomic = new AtomicString('hallo')
  })

  /** @test {AtomicString#pack} */
  describe('unpack', () => {
    let returnValue

    before(() => {
      const data = new Uint8Array(bitArrayHello)
      const dataView = new DataView(data.buffer)

      returnValue = atomic.unpack(dataView, 0)
    })

    it('returns a number', () => {
      expect(returnValue).to.be.a('number')
    })

    it('sets the offset to a multiple of 4', () => {
      expect(atomic.offset % 4).to.equal(0)
    })

    it('sets the value to a human readable string', () => {
      expect(atomic.value).to.equal('hallo')
    })
  })

  /** @test {AtomicString#unpack} */
  describe('pack', () => {
    it('returns correct bits', () => {
      expect(JSON.stringify(atomic.pack())).to.equal(
        JSON.stringify(new Int8Array(bitArrayHello))
      )
    })
  })
})

import { expect } from 'chai'

import AtomicString from '../../src/atomic/string'

/** @test {AtomicString} */
describe('AtomicString', () => {
  const bitArrayHello = [104, 97, 108, 108, 111, 0, 0, 0]
  let atomic

  before(() => {
    atomic = new AtomicString('hallo')
  })

  /** @test {AtomicString#unpack} */
  describe('unpack', () => {
    let returnValue

    before(() => {
      const data = new Uint8Array(bitArrayHello)
      const dataView = new DataView(data.buffer)

      returnValue = atomic.unpack(dataView, 0)
    })

    it('returns a number', () => {
      expect(returnValue).to.be.a('number')
    })

    it('sets the offset to a multiple of 4', () => {
      expect(atomic.offset % 4).to.equal(0)
    })

    it('sets the value to a human readable string', () => {
      expect(atomic.value).to.equal('hallo')
    })
  })

  /** @test {AtomicString#pack} */
  describe('pack', () => {
    it('returns correct bits', () => {
      expect(JSON.stringify(atomic.pack())).to.equal(
        JSON.stringify(new Int8Array(bitArrayHello))
      )
    })
  })
})

Fix wrong test naming for docs


var lastField = null;
var currentFillColor = '';   // ???
var changeCounter = 0;       // ???

function setField(element) {
    // element contains the current html element
    if (element.style.backgroundColor !== currentFillColor) {
        element.style.backgroundColor = currentFillColor;
    } else {
        element.style.backgroundColor = '#eee';
    }
    if (++changeCounter === 10) {
        alert('You have clicked 10 times on the field!');
        // Should the counter be reset after the alert?
        // changeCounter = 0;
    }
}

function setFillColor(color) {
    // color should be a string
    var logEntry = document.createElement('p');
    logEntry.appendChild(document.createTextNode('Color changed'));
    logEntry.style.color = currentFillColor = color;
    document.getElementById('Log').appendChild(logEntry);
}

var lastField = null;
var currentFillColor = '';   // ???
var changeCounter = 0;       // ???

function setField(element) {
    // element contains the current html element
    if (element.style.backgroundColor !== currentFillColor) {
        element.style.backgroundColor = currentFillColor;
    } else {
        element.style.backgroundColor = '#eee';
    }
    if (lastField === element) {
        ++changeCounter;
    } else {
        changeCounter = 0;
    }
    if (changeCounter === 10) {
        alert('You have clicked 10 times on the field!');
        changeCounter = 0;
    }
    lastField = element;
}

function setFillColor(color) {
    // color should be a string
    var logEntry = document.createElement('p');
    logEntry.appendChild(document.createTextNode('Color changed'));
    logEntry.style.color = currentFillColor = color;
    document.getElementById('Log').appendChild(logEntry);
}

Make use of the lastField variable


module.exports = function (ctx, p) {
  if (!p.image.complete) {
    return
  }

  var w = p.z * p.image.width
  var h = p.z * p.image.height

  ctx.globalAlpha = p.a
  ctx.translate(p.x, p.y)
  ctx.rotate(p.r)
  ctx.drawImage(
    p.image,
    -Math.floor(w / 2), // gravity to image center
    -Math.floor(h / 2),
    w, h
  )

  ctx.setTransform(1, 0, 0, 1, 0, 0) // resetTransform
  ctx.globalAlpha = 1 // reset alpha
}

module.exports = function (ctx, p) {
  if (!p.image.complete) {
    return
  }

  var w = p.z * p.image.width
  var h = p.z * p.image.height

  ctx.globalAlpha = p.a
  ctx.translate(p.x, p.y)
  ctx.rotate(p.r)
  ctx.drawImage(
    p.image,
    -w / 2, // gravity to image center
    -h / 2,
    w, h
  )

  ctx.setTransform(1, 0, 0, 1, 0, 0) // resetTransform
  ctx.globalAlpha = 1 // reset alpha
}

Remove unnecessary flooring for image gravity point


//@flow

export const updateObject = (
  oldObj: { [string]: any },
  newVals: { [string]: any }
): { [string]: any } => Object.assign({}, oldObj, newVals);

export const updateObjectInArray = (
  array: mixed[],
  id: number,
  updateCallback: () => mixed
): mixed[] => [
  ...array.slice(0, id),
  updateCallback(array[id]),
  ...array.slice(id + 1)
];

export const createReducer = (
  initialState: any,
  handlers: { [string]: <T>(*) => T }
) => (state: any = initialState, action: { type: string }): mixed => {
  if (handlers.hasOwnProperty(action.type)) {
    return handlers[action.type](state, action);
  }
  return state;
};

//@flow

export const updateObject = (
  oldObj: { [string]: any },
  newVals: { [string]: any }
): { [string]: any } => Object.assign({}, oldObj, newVals);

export const updateObjectInArray = (
  array: mixed[],
  id: number,
  updateCallback: () => mixed
): mixed[] => [
  ...array.slice(0, id),
  updateCallback(array[id]),
  ...array.slice(id + 1)
];

export const createReducer = (
  initialState: any,
  handlers: { [string]: (*) => any }
) => (state: any = initialState, action: { type: string }): mixed => {
  if (handlers.hasOwnProperty(action.type)) {
    return handlers[action.type](state, action);
  }
  return state;
};

Adjust typing of creatReducer function


jQuery(document).ready(function(){

    jQuery(".stripe-error-message").hide();

    Stripe.setPublishableKey(Drupal.settings.stripe_js_pk);
    var form = jQuery('.uc-cart-checkout-form');

    stripeResponseHandler = function(status, response) {
        var token;
        if (response.error) {
            jQuery(".stripe-error-message .message").text(response.error.message);
            jQuery(".stripe-error-message").show();
            form.find("#edit-continue").attr("disabled", false);
        } else {
            token = response.id;
            jQuery("input[name=\"panes[payment][details][stripeToken]\"]").val(token);
            form.find("#edit-continue").attr("disabled", false);

            jQuery('#edit-continue').click();
        }
    };

    form.submit(function(event) {
        event.preventDefault();

        if(form.find('input[name="panes[payment][payment_method]"][value="stripe_js"]:checked').length !== 0){
            form.find("#edit-continue").attr("disabled", true);
            Stripe.card.createToken(form, stripeResponseHandler);
        } else {
            jQuery('#edit-continue').click();
        }
    });
});

jQuery(document).ready(function(){
    jQuery(".stripe-error-message").hide();

    Stripe.setPublishableKey(Drupal.settings.stripe_js_pk);

    var ucStripeAllow = false;
    var form = jQuery('.uc-cart-checkout-form');

    stripeResponseHandler = function(status, response) {
        var token;
        if (response.error) {
            jQuery(".stripe-error-message .message").text(response.error.message);
            jQuery(".stripe-error-message").show();
            form.find("#edit-continue").attr("disabled", false);
        } else {
            token = response.id;
            jQuery("input[name=\"panes[payment][details][stripeToken]\"]").val(token);
            form.find("#edit-continue").attr("disabled", false);

            ucStripeAllow = true;
            jQuery('#edit-continue').click();
        }
    };

    form.submit(function(event) {
        if(!ucStripeAllow && parseInt(form.find('input[name="panes[payment][payment_method]"][value="stripe_js"]:checked').length) === 1){
            event.preventDefault();
            form.find("#edit-continue").attr("disabled", true);
            Stripe.card.createToken(form, stripeResponseHandler);
        }
    });
});

Monitor the card status independantly


var rModsList = [];

/* start ui_mod_list */
var global_mod_list = [

];
var scene_mod_list = {'connect_to_game': [

],'game_over': [

],
'icon_atlas': [

],
'live_game': [
	//In game timer
	'../../mods/dTimer/dTimer.css',
	'../../mods/dTimer/dTimer.js',

	//Mex/Energy Count
	'../../mods/dMexCount/dMexCount.css',
	'../../mods/dMexCount/dMexCount.js',

	//Better System view (show system view at all times)
	'../../mods/dBetterSystemView/dBetterSystemView.css',
	'../../mods/dBetterSystemView/dBetterSystemView.js',

	//Reminders
	'../../mods/dReminderTimer/dReminderTimer.css',
	'../../mods/dReminderTimer/dReminderTimer.js',
],
'load_planet': [

],
'lobby': [

],
'matchmaking': [

],
'new_game': [

],
'server_browser': [

],
'settings': [

],
'special_icon_atlas': [

],
'start': [

],
'system_editor': [

],
'transit': [

]
}
/* end ui_mod_list */

var rModsList = [];

/* start ui_mod_list */
var global_mod_list = [

];
var scene_mod_list = {'connect_to_game': [

],'game_over': [

],
'icon_atlas': [

],
'live_game': [
	//In game timer
	'../../mods/dTimer/dTimer.css',
	'../../mods/dTimer/dTimer.js',

	//Mex/Energy Count
	'../../mods/dMexCount/dMexCount.css',
	'../../mods/dMexCount/dMexCount.js',

	//Better System view (show system view at all times)
	'../../mods/dBetterSystemView/dBetterSystemView.css',
	'../../mods/dBetterSystemView/dBetterSystemView.js',

	//Reminders
	'../../mods/dReminderTimer/dReminderTimer.css',
	'../../mods/dReminderTimer/dReminderTimer.js',

	//Hide for casting
	'../../mods/dHideCast/dHideCast.css',
	'../../mods/dHideCast/dHideCast.js',

],
'load_planet': [

],
'lobby': [

],
'matchmaking': [

],
'new_game': [

],
'server_browser': [

],
'settings': [

],
'special_icon_atlas': [

],
'start': [

],
'system_editor': [

],
'transit': [

]
}
/* end ui_mod_list */

Add hide for casting to the default mod list


var test = require('tape')

var CHR = require('../../src/index')

test("String arguments (Issue #20)", function (t) {
  var chr = new CHR()
  chr("a('p') ==> b")

  chr.a('q').then(function () {
    t.equal(chr.Store.length, 1)

    chr.a('p').then(function () {
      t.equal(chr.Store.length, 3)

      t.end()
    })
  })
})

var test = require('tape')

var CHR = require('../../src/index')

test('String arguments (Issue #20)', function (t) {
  var chr = new CHR()
  chr("a('p') ==> b")

  chr.a('q').then(function () {
    t.equal(chr.Store.length, 1)

    chr.a('p').then(function () {
      t.equal(chr.Store.length, 3)

      t.end()
    })
  })
})

test('String arguments (Issue #20), with Int value', function (t) {
  var chr = new CHR()
  chr("a('42') ==> b")

  chr.a(42).then(function () {
    t.equal(chr.Store.length, 1)

    chr.a('42').then(function () {
      t.equal(chr.Store.length, 3)

      t.end()
    })
  })
})

test('Int arguments', function (t) {
  var chr = new CHR()
  chr('a(42) ==> b')

  chr.a('q').then(function () {
    t.equal(chr.Store.length, 1)

    chr.a('42').then(function () {
      t.equal(chr.Store.length, 2)

      chr.a(42).then(function () {
        t.equal(chr.Store.length, 4)

        t.end()
      })
    })
  })
})

test('Check String argument in guard', function (t) {
  var chr = new CHR()
  chr("a(X) ==> X === 'p' | b")

  chr.a('q').then(function () {
    t.equal(chr.Store.length, 1)

    chr.a('p').then(function () {
      t.equal(chr.Store.length, 3)

      t.end()
    })
  })
})

test('Check String argument in guard, with Int value', function (t) {
  var chr = new CHR()
  chr("a(X) ==> X === '42' | b")

  chr.a('q').then(function () {
    t.equal(chr.Store.length, 1)

    chr.a(42).then(function () {
      t.equal(chr.Store.length, 2)

      chr.a('42').then(function () {
        t.equal(chr.Store.length, 4)

        t.end()
      })
    })
  })
})

Add more tests for string/int types as constraint arguments or in the guard


'use strict';

var ogr = require('../lib/gdal.js').ogr;
var path = require('path');
var assert = require('chai').assert;

describe('Open', function() {
	describe('GeoJSON', function() {
		var filename, ds;

		it('should not throw', function() {
			filename = path.join(__dirname,"data/park.geo.json");
			ds = ogr.open(filename);
		});
		it('should be able to read layer count', function() {
			assert.equal(ds.getLayerCount(), 1);
		});

		describe('layer', function() {
			var layer;
			before(function() { layer = ds.getLayer(0); });
			it('should exist', function() {
				assert.ok(layer);
				assert.instanceOf(layer, ogr.Layer);
			});
			it('should have definition', function() {
				assert.ok(layer.getLayerDefn());
			})
		});
	});
});

'use strict';

var ogr = require('../lib/gdal.js').ogr;
var path = require('path');
var assert = require('chai').assert;

describe('Open', function() {
	describe('GeoJSON', function() {
		var filename, ds;

		it('should not throw', function() {
			filename = path.join(__dirname,"data/park.geo.json");
			ds = ogr.open(filename);
		});
		it('should be able to read layer count', function() {
			assert.equal(ds.getLayerCount(), 1);
		});

		describe('layer', function() {
			var layer;
			before(function() { layer = ds.getLayer(0); });
			it('should exist', function() {
				assert.ok(layer);
				assert.instanceOf(layer, ogr.Layer);
			});
			describe('definition', function() {
				var defn;
				before(function() {
					defn = layer.getLayerDefn();
				});
				it('should exist', function() {
					assert.ok(defn);
					assert.instanceOf(defn, ogr.FeatureDefn);
				});
				it('should have all fields defined', function() {
					assert.equal(defn.getFieldCount(), 3);
					assert.equal(defn.getFieldDefn(0).getName(), 'kind');
					assert.equal(defn.getFieldDefn(1).getName(), 'name');
					assert.equal(defn.getFieldDefn(2).getName(), 'state');
				});
			});
			describe('features', function() {
				it('should have all fields', function() {
					assert.equal(layer.getFeatureCount(), 1);
					var feature = layer.getFeature(0);
					var fields = feature.getFields();

					assert.deepEqual(fields, {
						'kind': 'county',
						'state': 'WY',
						'name': 'Park'
					});
				});
			});
		});
	});
});

Test for field definitions / values.


var expect = require('chai').expect;
var supertest = require('supertest');
var api = supertest("http://localhost:3000");

describe('Checkout index page', function(){
  it("should respond with 200", function(done){
    api.get("/checkouts/new").expect(200, done);
  });

  it("should generate a client token", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/var token = \"[\w=]+\";/);
      done();
    });
  });

  it("should include the checkout form", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/<form id="checkout"/);
      done();
    });
  });

  it("should include the dropin payment-form div", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/<div id="payment-form"/);
      done();
    });
  });
});

var expect = require('chai').expect;
var supertest = require('supertest');
var api = supertest("http://localhost:3000");

describe('Checkout index page', function(){
  it("responds with 200", function(done){
    api.get("/checkouts/new").expect(200, done);
  });

  it("generates a client token", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/var token = \"[\w=]+\";/);
      done();
    });
  });

  it("includes the checkout form", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/<form id="checkout"/);
      done();
    });
  });

  it("includes the dropin payment-form div", function(done){
    api.get("/checkouts/new").end(function(err, res) {
      expect(res.text).to.match(/<div id="payment-form"/);
      done();
    });
  });
});

Remove should from test descriptions


const downloadIcon = require('./../lib/modules/download/downloadIcon');
const fs = require('fs');

const ICON_URL = 'https://web.whatsapp.com/favicon.ico';

describe('Download Icons', function() {
    this.timeout(10000);
    it('Can download Icons', function(done) {
        downloadIcon(ICON_URL)
            .then(icon => {
                if (!icon) {
                    throw 'Icon not found';
                }
                fs.writeFileSync(__dirname + 'testicon.ico', icon.data);
                done();
            })
            .catch(done);
    })
});

const downloadIcon = require('./../lib/modules/download/downloadIcon');
const fs = require('fs');
const path = require('path');

const ICON_URL = 'https://web.whatsapp.com/favicon.ico';

const ICON_PATH = path.join(__dirname, '..','out', 'test_icon.ico');

describe('Download Icons', function() {
    this.timeout(10000);
    it('Can download Icons', function(done) {
        downloadIcon(ICON_URL)
            .then(icon => {
                if (!icon) {
                    throw 'Icon not found';
                }
                fs.writeFileSync(ICON_PATH, icon.data);
                done();
            })
            .catch(done);
    })
});

Save `downloadIcon` test output to `out`


const options = {
  animationDuration: 0.5,
  callbacks: {
    onFilteringStart: function() { },
    onFilteringEnd: function() { },
    onShufflingStart: function() { },
    onShufflingEnd: function() { },
    onSortingStart: function() { },
    onSortingEnd: function() { }
  },
  controlsSelector: '',
  delay: 0,
  delayMode: 'progressive',
  easing: 'ease-out',
  filter: 'all',
  filterOutCss: {
    'opacity': 0,
    'transform': 'scale(0.5)'
  },
  filterInCss: {
    'opacity': 1,
    'transform': 'scale(1)'
  },
  layout: 'sameSize',
  multifilterLogicalOperator: 'or',
  setupControls: true,
}

export default options;

const options = {
  animationDuration: 0.5,
  callbacks: {
    onFilteringStart: () => { },
    onFilteringEnd: () => { },
    onShufflingStart: () => { },
    onShufflingEnd: () => { },
    onSortingStart: () => { },
    onSortingEnd: () => { },
  },
  controlsSelector: '',
  delay: 0,
  delayMode: 'progressive',
  easing: 'ease-out',
  filter: 'all',
  filterOutCss: {
    'opacity': 0,
    'transform': 'scale(0.5)'
  },
  filterInCss: {
    'opacity': 1,
    'transform': 'scale(1)'
  },
  layout: 'sameSize',
  multifilterLogicalOperator: 'or',
  setupControls: true,
}

export default options;

Replace functions for ES6 arrow functions


// Home Route
Router.route('/', function () {
  this.render('home');
  SEO.set({ title: 'Home -' + Meteor.App.NAME });
});

// Home Route
Router.route('/', function () {
  this.render('home');
  SEO.set({ title: 'Home - ' + Meteor.App.NAME });
});

Fix missing whitespace in title


(function() {
    if(typeof(this.PHP_JS) === "undefined"){
        // This references at top of namespace allows PHP_JS class to
        // be included directly inside an object (unusual use)
        var PHP_JS = function(cfgObj) {
            if(!(this instanceof PHP_JS)) {
                // Allow invokation without "new"
                return new PHP_JS(cfgObj);
            }
            this.window = cfgObj && cfgObj.window ? cfgObj.window : window;
            // Allow user to pass in window, e.g., if in context
            // without access to window but need to pass in, like
            // a Mozilla JavaScript module
        };
    }
    var php_js_shared = {};
    // Private static holder across all instances; we usually use
    // instance variables, but this is necessary for a very few
    // like require_once()/include_once()
    PHP_JS.prototype = {
//#FUNCTIONS_HERE#
    }; // end PHP_JS.prototype
    this.PHP_JS = PHP_JS;
    // User must instantiate (don't need "new" to do it, though slightly
    // faster if do use "new")
    // use in own code like:   var $P = PHP_JS();
    // If you place this namespace in a context like a JavaScript module
    // (e.g., for a Firefox extension) without access to the global
    // window object, you could instantiate in code which did have
    // access to "window" like this: var $P = PHP_JS({window:window});
}());
// Kevin, I moved invocation parentheses inside the other parentheses for
// the sake of jslint; otherwise, no difference

Use template for namespaced version