const series = {
        fps: {
            label: "FPS",
            maxValue: 65,
            color: "#F78900"
        },
        fpsMin: {
            label: "FPS Min",
            maxValue: 65,
            color: "#800000"
        },
        cps: {
            label: "CPS",
            maxValue: 65,
            color: "#E5E500"
        },
        playerCount: {
            label: "Players",
            maxValue: 400,
            color: "#3EB2F2"
        },
        localAiCount: {
            label: "Local AI",
            maxValue: 1000,
            color: "#8ECFFF"
        },
        remoteAiCount: {
            label: "Remote AI",
            maxValue: 1000,
            color: "#C9E3FF"
        },
        entityCount: {
            label: "Entities",
            maxValue: 1000,
            color: "#03CC20"
        }
    };
const seriesAxes = Object.getOwnPropertyNames(series);

module.exports = {
    seriesAxes: seriesAxes,
    series: series,
    graph: {
        margin: { top: 20, right: 20, bottom: 0, left: 20 },
        xLengthInSeconds: 20 * 60,
        width: 700,
        height: 260
    }
}

const series = {
        fps: {
            label: "FPS",
            maxValue: 125,
            color: "#F78900"
        },
        fpsMin: {
            label: "FPS Min",
            maxValue: 125,
            color: "#800000"
        },
        cps: {
            label: "CPS",
            maxValue: 125,
            color: "#E5E500"
        },
        playerCount: {
            label: "Players",
            maxValue: 400,
            color: "#3EB2F2"
        },
        localAiCount: {
            label: "Local AI",
            maxValue: 1000,
            color: "#8ECFFF"
        },
        remoteAiCount: {
            label: "Remote AI",
            maxValue: 1000,
            color: "#C9E3FF"
        },
        entityCount: {
            label: "Entities",
            maxValue: 1000,
            color: "#03CC20"
        }
    };
const seriesAxes = Object.getOwnPropertyNames(series);

module.exports = {
    seriesAxes: seriesAxes,
    series: series,
    graph: {
        margin: { top: 20, right: 20, bottom: 0, left: 20 },
        xLengthInSeconds: 20 * 60,
        width: 700,
        height: 260
    }
}

Update maxValues for fps anf cps


import React from 'react'
import {
  Flex,
  Box,
  Heading,
  Text,
  Avatar,
  Badge
} from '@hackclub/design-system'

const Base = Flex.extend`
  border-radius: ${props => props.theme.radii[2]};
  max-width: 36rem;
  img {
    flex-shrink: 0;
  }
`

const Bio = ({ bg, img, name, role, text, ...props }) => (
  <Base p={3} bg={`${bg}.0`} {...props}>
    <Avatar size="64px" src={img} mr={2} alt=""/>
    <Box>
      <Flex align="center" wrap style={{ lineHeight: '1.25' }}>
        <Heading.h3 f={3} m={0} mr={2} regular={false} children={name} />
        <Badge px={2} f={1} bg={`${bg}.5`} children={role} />
      </Flex>
      <Text f={2} mt={[1, 0]} mb={0} color="black" children={text} />
    </Box>
  </Base>
)

export default Bio

import React from 'react'
import {
  Flex,
  Box,
  Heading,
  Text,
  Avatar,
  Badge
} from '@hackclub/design-system'

const Base = Flex.extend`
  border-radius: ${props => props.theme.radii[2]};
  max-width: 36rem;
  img {
    flex-shrink: 0;
  }
`

const Bio = ({ bg, img, name, role, text, ...props }) => (
  <Base p={3} bg={`${bg}.0`} {...props}>
    <Avatar size="64px" src={img} mr={2} alt={name} />
    <Box>
      <Flex align="center" wrap style={{ lineHeight: '1.25' }}>
        <Heading.h3 f={3} m={0} mr={2} regular={false} children={name} />
        <Badge px={2} f={1} bg={`${bg}.5`} children={role} />
      </Flex>
      <Text f={2} mt={[1, 0]} mb={0} color="black" children={text} />
    </Box>
  </Base>
)

export default Bio

Set alt attr as team member's name


angular.module('materialscommons').directive('processSettings', processSettingsDirective);
function processSettingsDirective() {
    return {
        restrict: 'E',
        scope: {
            settings: '=',
            taskId: '=',
            templateId: '=',
            attribute: '='
        },
        controller: ProcessSettingsDirectiveController,
        controllerAs: 'ctrl',
        bindToController: true,
        templateUrl: 'app/project/processes/process/create/components/process-settings.html'
    }
}

/*@ngInject*/
function ProcessSettingsDirectiveController(experimentsService, toast, $stateParams) {
    var ctrl = this;
    ctrl.datePickerOptions = {
        formatYear: 'yy',
        startingDay: 1
    };
    ctrl.openDatePicker = openDatePicker;

    ctrl.updateSettingProperty = (property) => {
        if (!property.value) {
            return;
        }

        property.setup_attribute = ctrl.attribute;
        let propertyArgs = {
            template_id: ctrl.templateId,
            properties: [property]
        };
        experimentsService.updateTaskTemplateProperties($stateParams.project_id, $stateParams.experiment_id, ctrl.taskId, propertyArgs)
            .then(
                () => null,
                () => toast.error('Unable to update property')
            );
    };

    ///////////////////////////////////////

    function openDatePicker($event, prop) {
        $event.preventDefault();
        $event.stopPropagation();
        prop.opened = true;
    }
}

angular.module('materialscommons').directive('processSettings', processSettingsDirective);
function processSettingsDirective() {
    return {
        restrict: 'E',
        scope: {
            settings: '=',
            taskId: '=',
            templateId: '=',
            attribute: '='
        },
        controller: ProcessSettingsDirectiveController,
        controllerAs: 'ctrl',
        bindToController: true,
        templateUrl: 'app/project/processes/process/create/components/process-settings.html'
    }
}

/*@ngInject*/
function ProcessSettingsDirectiveController(experimentsService, toast, $stateParams) {
    var ctrl = this;
    ctrl.datePickerOptions = {
        formatYear: 'yy',
        startingDay: 1
    };
    ctrl.openDatePicker = openDatePicker;

    ctrl.updateSettingProperty = (property) => {
        if (!property.value) {
            return;
        }

        if (property._type === "date") {
            console.dir(property);
            return;
        }

        property.setup_attribute = ctrl.attribute;
        let propertyArgs = {
            template_id: ctrl.templateId,
            properties: [property]
        };
        experimentsService.updateTaskTemplateProperties($stateParams.project_id, $stateParams.experiment_id, ctrl.taskId, propertyArgs)
            .then(
                () => null,
                () => toast.error('Unable to update property')
            );
    };

    ///////////////////////////////////////

    function openDatePicker($event, prop) {
        $event.preventDefault();
        $event.stopPropagation();
        prop.opened = true;
    }
}

Add debug to handle date case (need to test).


/* global game, Phaser, ms */

var Player = function(targetTile) {
    Phaser.Sprite.call(this, game, 0, 0, 'pix');
    game.add.existing(this);
    ms.player = this;
    this.anchor.set(0.5);
    this.tint = 0x0000ff;
    this.height = 70;
    this.width = 40;
    this.x = targetTile.x;
    this.y = targetTile.y;

    if (targetTile.enemies.length > 0) targetTile.enemies.forEach(function(enemy) {
        enemy.kill();
        enemy.destroy();
    });
    targetTile.enemies = [];
    targetTile.setExplored();
    targetTile.events.onInputUp.dispatch();
    print(targetTile.getAdjacentTiles().length);
    targetTile.getAdjacentTiles().forEach(function(tile) {
        tile.setExplored();
        tile.getAdjacentTiles().forEach(function(inner) {
            if (inner.state != 'explored') inner.setRevealed();
        });
    });
};
Player.prototype = Object.create(Phaser.Sprite.prototype);
Player.prototype.constructor = Player;

module.exports = Player;

/* global game, Phaser, ms */

var Player = function(targetTile) {
    Phaser.Sprite.call(this, game, 0, 0, 'pix');
    game.add.existing(this);
    ms.player = this;
    this.anchor.set(0.5);
    this.tint = 0x0000ff;
    this.height = 70;
    this.width = 40;
    this.x = targetTile.x;
    this.y = targetTile.y;

    if (targetTile.enemies.length > 0) targetTile.enemies.forEach(function(enemy) {
        enemy.kill();
        enemy.destroy();
    });
    targetTile.enemies = [];
    targetTile.setExplored();
    print(targetTile.getAdjacentTiles().length);
    targetTile.getAdjacentTiles().forEach(function(tile) {
        tile.setExplored();
        tile.getAdjacentTiles().forEach(function(inner) {
            if (inner.enemies.length > 0) inner.enemies.forEach(function(enemy){
                enemy.kill();
                enemy.destroy();
            });
            inner.enemies = [];
            if (inner.state != 'explored') inner.setRevealed();
        });
    });
    targetTile.events.onInputUp.dispatch();
};
Player.prototype = Object.create(Phaser.Sprite.prototype);
Player.prototype.constructor = Player;

module.exports = Player;

Remove nearby enemies on game start.


import 'babel-polyfill'
import express from 'express'
import fallback from 'express-history-api-fallback'
import path from 'path'
import log from './log'
import proxy from 'http-proxy-middleware'

// Properly catch async exceptions, log them, and re-throw them
// on the main process to crash the program
const wrap = (fn) =>
  (...args) =>
    fn(...args)
      .catch((ex) => {
        log.error(ex)
        process.nextTick(() => { throw ex })
      })
const app = express()
const port = process.env.PORT
const publicPath = path.resolve(__dirname, '../../build/frontend')
app.enable('trust proxy')

app.listen(port, () => {
  log.info(`Node app is running on port ${port}`)
})

app.get('/', wrap((req, res) => {
  res.redirect('/home')
}))

app.use(express.static(publicPath, {
  maxAge: '180 days'
}))

app.use(['/home', '/teams', '/faq'],
  proxy({ target: 'http://brandnewcongress.nationbuilder.com/', changeOrigin: true }))

app.use(fallback('index.html', {
  root: publicPath,
  maxAge: 0
}))

import 'babel-polyfill'
import express from 'express'
import fallback from 'express-history-api-fallback'
import path from 'path'
import log from './log'
import proxy from 'http-proxy-middleware'

// Properly catch async exceptions, log them, and re-throw them
// on the main process to crash the program
const wrap = (fn) =>
  (...args) =>
    fn(...args)
      .catch((ex) => {
        log.error(ex)
        process.nextTick(() => { throw ex })
      })
const app = express()
const port = process.env.PORT
const publicPath = path.resolve(__dirname, '../../build/frontend')
app.enable('trust proxy')

app.listen(port, () => {
  log.info(`Node app is running on port ${port}`)
})

app.get('/', wrap((req, res) => {
  res.redirect('/home')
}))

app.use(express.static(publicPath, {
  maxAge: '180 days'
}))

app.use([
  '/home',
  '/teams',
  '/about'],
  proxy({
    target: 'http://brandnewcongress.nationbuilder.com/',
    changeOrigin: true
  }))

app.use([
  '/abteam',
  '/adteam',
  '/call',
  '/conferencecallteam',
  '/dataentryteam',
  '/faq',
  '/officeteam',
  '/researchteam',
  '/shareteam',
  '/spreadsheetteam',
  '/textingteam',
  '/travelteam'],
  proxy({
    target: 'http://brandnewcongress.github.io/',
    changeOrigin: true
  }))

app.use(fallback('index.html', {
  root: publicPath,
  maxAge: 0
}))

Set up all the URLs


"use strict";

var utils = require("../utils");
var log = require("npmlog");

module.exports = function(defaultFuncs, api, ctx) {
  return function changeNickname(nickname, threadID, participantID, callback) {

    var form = {
      'nickname': nickname,
      'participant_id': participantID,
      'thread_or_other_fbid': threadID
    };

    defaultFuncs
      .post("https://www.messenger.com/messaging/save_thread_nickname/?source=thread_settings&dpr=1", ctx.jar, form)
      .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
      .then(function(resData) {
        if (resData.error === 1545014) {
          throw {error: "Trying to change nickname of user isn't in thread"};
        }
        if (resData.error === 1357031) {
          throw {error: "Trying to change user nickname of a chat that doesn't exist. Have at least one message in the thread before trying to change the user nickname."};
        }
        if (resData.error) {
          throw resData;
        }

        return callback();
      })
      .catch(function(err) {
        log.error("Error in changeNickname", err);
        return callback(err);
      });
  };
};

"use strict";

var utils = require("../utils");
var log = require("npmlog");

module.exports = function(defaultFuncs, api, ctx) {
  return function changeNickname(nickname, threadID, participantID, callback) {

    var form = {
      'nickname': nickname,
      'participant_id': participantID,
      'thread_or_other_fbid': threadID
    };

    defaultFuncs
      .post("https://www.messenger.com/messaging/save_thread_nickname/?source=thread_settings&dpr=1", ctx.jar, form)
      .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
      .then(function(resData) {
        if (resData.error === 1545014) {
          throw {error: "Trying to change nickname of user isn't in thread"};
        }
        if (resData.error === 1357031) {
          throw {error: "Trying to change user nickname of a thread that doesn't exist. Have at least one message in the thread before trying to change the user nickname."};
        }
        if (resData.error) {
          throw resData;
        }

        return callback();
      })
      .catch(function(err) {
        log.error("Error in changeNickname", err);
        return callback(err);
      });
  };
};

Change of second error message


'use strict';
define(
    ['three', 'OrbitControls'],
    function(THREE) {
        return class Camera {

            // ##############################################
            // # Constructor ################################
            // ##############################################

            constructor(scene, renderer, fov, aspectRatio, near, far, cameraPosition) {
                this.scene = scene;

                this.near           = near;
                this.far            = far;
                this.cameraPosition = cameraPosition;

                this.camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);
                this.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
                this.camera.zoom = 0.5;
                this.camera.updateProjectionMatrix();

                scene.camera = this;

                this.controls = new THREE.OrbitControls(this.camera, renderer.domElement);
                this.controls.enableDamping = true;
                this.controls.dampingFactor = 0.125;
                this.controls.enableZoom    = true;
                this.controls.rotateSpeed   = 0.15;
                this.controls.enableKeys    = false;
            }
        }
    }
);

'use strict';
define(
    ['three', 'OrbitControls'],
    function(THREE) {
        return class Camera {

            // ##############################################
            // # Constructor ################################
            // ##############################################

            constructor(scene, renderer, fov, aspectRatio, near, far, cameraPosition) {
                this.scene = scene;

                this.near           = near;
                this.far            = far;
                this.cameraPosition = cameraPosition;

                this.camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);
                this.camera.position.set(cameraPosition.x, cameraPosition.y, cameraPosition.z);
                this.camera.zoom = 0.5;
                this.camera.updateProjectionMatrix();

                scene.camera = this;

                this.controls = new THREE.OrbitControls(this.camera, renderer.domElement);
                this.controls.enableDamping = true;
                this.controls.dampingFactor = 0.125;
                this.controls.enableZoom    = true;
                this.controls.rotateSpeed   = 0.15;
                this.controls.enableKeys    = false;
                this.controls.mouseButtons  = { ORBIT: THREE.MOUSE.RIGHT, PAN: THREE.MOUSE.LEFT };
            }
        }
    }
);

Switch left/right mouse buttons, remove zoom on middle


const AWS = require('aws-sdk');
const config = require('./config');
const helpers = require('./helpers');

if (process.argv.length < 2 || process.argv.length > 3) {
  console.error('Usage: node list_bucket_objects.js <BUCKET_NAME>');
  process.exit(1);
}

const bucketName = (process.argv.length === 2 ? config.DEFAULT_BUCKET : process.argv[2]);

const s3 = new AWS.S3();

// s3.listObjectsV2({ Bucket: bucketName }, (err, data) => {
//   if (err) {
//     console.error('Error retrieiving bucket list.');
//     console.error(err);
//   } else {
//     data.Contents.forEach((object) => {
//       console.log(object.Key);
//     });
//   }
// });

helpers.getBucketObjects(s3, { bucketName }, (err, objects) => {
  if (err) {
    console.error(err);
  } else {
    console.log(objects);
  }
});

const AWS = require('aws-sdk');
const config = require('./config');

if (process.argv.length < 2 || process.argv.length > 3) {
  console.error('Usage: node list_bucket_objects.js <BUCKET_NAME>');
  process.exit(1);
}

const bucketName = (process.argv.length === 2 ? config.DEFAULT_BUCKET : process.argv[2]);

const s3 = new AWS.S3();

s3.listObjectsV2({ Bucket: bucketName }, (err, data) => {
  if (err) {
    console.error('Error retrieiving bucket list.');
    console.error(err);
  } else {
    data.Contents.forEach((object) => {
      console.log(object.Key);
    });
  }
});

Move helper call to router.js


/*
 USAGE EXAMPLE:

 <jf-grid-filter
 filter-grid="gridOptions"  //the name of the grid (grid options)
 filter-field="fieldName"        //the name of the field that should be filtered
 filter-on-change>          //optional - don't use a button for filtering, filter on every change
 </jf-grid-filter>

 */

export function jfTooltipOnOverflow() {

    return {
        restrict: 'A',
        scope: {
        },
        link: ($scope, $element) => {
            $($element).on('mouseenter',(e)=>{
                let target = $(e.target);
                let targetContent = target.text().trim();

                if (target[0].scrollWidth > target.innerWidth()) {
                    if (!target.hasClass('tooltipstered')) {
                        target.tooltipster({
                            animation: 'fade',
                            trigger: 'hover',
                            onlyOne: 'true',
                            interactive: 'true',
                            position: 'bottom',
                            theme: 'tooltipster-default bottom',
                            content: targetContent
                        });
                        target.tooltipster('show');
                    }
                    else {
                        target.tooltipster('enable');

                        if (target.tooltipster('content') != targetContent)
                            target.tooltipster('content', targetContent);
                    }
                }
                else if (target.hasClass('tooltipstered'))
                    target.tooltipster('disable');
            });
            $scope.$on('$destroy', () => {
                $($element).off('mouseenter');
                $($element).off('mouseleave');
            });

        }
    };
}

/*
 USAGE EXAMPLE:

 <jf-grid-filter
 filter-grid="gridOptions"  //the name of the grid (grid options)
 filter-field="fieldName"        //the name of the field that should be filtered
 filter-on-change>          //optional - don't use a button for filtering, filter on every change
 </jf-grid-filter>

 */

export function jfTooltipOnOverflow() {

    return {
        restrict: 'A',
        scope: {
        },
        link: ($scope, $element) => {
            $($element).on('mouseenter',(e)=>{
                let target = $($element);
                let targetContent = target.text().trim();

                if (target[0].scrollWidth > target.innerWidth()) {
                    if (!target.hasClass('tooltipstered')) {
                        target.tooltipster({
                            animation: 'fade',
                            trigger: 'hover',
                            onlyOne: 'true',
                            interactive: 'true',
                            position: 'bottom',
                            theme: 'tooltipster-default bottom',
                            content: targetContent
                        });
                        target.tooltipster('show');
                    }
                    else {
                        target.tooltipster('enable');

                        if (target.tooltipster('content') != targetContent)
                            target.tooltipster('content', targetContent);
                    }
                }
                else if (target.hasClass('tooltipstered'))
                    target.tooltipster('disable');
            });
            $scope.$on('$destroy', () => {
                $($element).off('mouseenter');
                $($element).off('mouseleave');
            });

        }
    };
}

Use the root dir element as the target, not the actual event target.


export const getSelectedTime = (
    server_time,
    selected_time,
    market_open_time,
) => {
    const boundaries = selected_time.isBefore(market_open_time)
        ? market_open_time.isBefore(server_time)
            ? server_time.add(5, 'minute')
            : market_open_time.add(5, 'minute')
        : selected_time;

    return boundaries.format('HH:mm');
};

export const getBoundaries = (
    server_time,
    market_open_time,
    market_close_time,
) => {
    const boundaries = {
        start: server_time.isBefore(market_open_time)
            ? market_open_time
            : server_time.add(5, 'minute'),
        end: market_close_time,
    };
    return boundaries;
};

export const getSelectedTime = (
    server_time,
    selected_time,
    market_open_time,
) => {
    const boundaries = selected_time.isBefore(market_open_time)
        ? market_open_time.isBefore(server_time)
            ? server_time.add(5, 'minute')
            : market_open_time.add(5, 'minute')
        : selected_time;

    return boundaries.format('HH:mm');
};

export const getBoundaries = (
    server_time,
    market_open_time,
    market_close_time,
) => {
    const boundaries = {
        start: server_time.isBefore(market_open_time)
            ? market_open_time.add(5, 'minute')
            : server_time.add(5, 'minute'),
        end: market_close_time,
    };
    return boundaries;
};

Add 5 minutes to start boundary


const Tweet = require('../models/tweet')
const controllerUtils = require('./controllerUtils')

const specificTweetsController = function (req, res) {
  if (!req.params.name) {
    res.status(400).send('Invalid trend name')
    return
  }

  let query
  if (!req.query.max_id) {
    query = {trend: req.params.name}
  } else {
    // If max_id is provided, ensure that it is valid
    if(!controllerUtils.isNumeric(req.query.max_id)) {
      res.status(400).send('Invalid max_id parameter')
      return
    }
    options = {trend: req.params.name, _id: {$gt: res.query.max_id}}
  }

  Tweet.find(query, (err, tweets) => {
    if (err) {
      res.status(500).send('Internal error while retreiving tweets')
    } else {
      res.type('application/json')
      res.send(tweets)
    }
  })
}

module.exports = specificTweetsController

const Tweet = require('../models/tweet')
const controllerUtils = require('./controller-utils')

const specificTweetsController = function (req, res) {
  if (!req.params.name) {
    res.status(400).send('Invalid trend name')
    return
  }

  let query
  if (!req.query.max_id) {
    query = {trend: req.params.name}
  } else {
    // If max_id is provided, ensure that it is valid
    if(!controllerUtils.isNumeric(req.query.max_id)) {
      res.status(400).send('Invalid max_id parameter')
      return
    }
    options = {trend: req.params.name, _id: {$gt: res.query.max_id}}
  }

  Tweet.find(query, (err, tweets) => {
    if (err) {
      res.status(500).send('Internal error while retreiving tweets')
    } else {
      res.type('application/json')
      res.send(tweets)
    }
  })
}

module.exports = specificTweetsController

Fix spelling error in module


'use strict';

const { GraphQLInt, GraphQLString } = require('graphql');

// Pagination arguments
const paginationActionTypes = ['find'];
const getPaginationArgument = function ({ action: { actionType, multiple }, defaultPageSize, maxPageSize }) {
  // Only with safe operations that return an array, i.e. only with findMany
  if (!(paginationActionTypes.includes(actionType) && multiple)) { return; }

  return {
    page_size: {
      type: GraphQLInt,
      description: `Sets pagination size.
Using 0 disables pagination.
Maximum: ${maxPageSize}`,
      defaultValue: defaultPageSize,
    },

    after: {
      type: GraphQLString,
      description: `Retrieves next pagination batch, using the previous response's last model's 'token'.
Using '' means 'from the beginning'`,
      defaultValue: '',
    },

    before: {
      type: GraphQLString,
      description: `Retrieves previous pagination batch, using the previous response's first model's 'token'.
Using '' means 'from the end'`,
    },
  };
};

module.exports = {
  getPaginationArgument,
};

'use strict';

const { GraphQLInt, GraphQLString } = require('graphql');

// Pagination arguments
const paginationActionTypes = ['find'];
const getPaginationArgument = function ({ action: { actionType, multiple }, defaultPageSize, maxPageSize }) {
  // Only with safe operations that return an array, i.e. only with findMany
  if (!(paginationActionTypes.includes(actionType) && multiple)) { return; }

  return {
    page_size: {
      type: GraphQLInt,
      description: `Sets pagination size.
Using 0 disables pagination.
Maximum: ${maxPageSize}`,
      defaultValue: defaultPageSize,
    },

    after: {
      type: GraphQLString,
      description: `Retrieves next pagination batch, using the previous response's last model's 'token'.
Using '' means 'from the beginning'`,
      defaultValue: '',
    },

    before: {
      type: GraphQLString,
      description: `Retrieves previous pagination batch, using the previous response's first model's 'token'.
Using '' means 'from the end'`,
    },

    page: {
      type: GraphQLInt,
      description: `Page number, for pagination.
Starts at 1.
Cannot be used with 'before' or 'after'`,
    },
  };
};

module.exports = {
  getPaginationArgument,
};

Add GraphQL schema description for args.page


var exec = require('child_process').exec
var fs = require('fs')

var helper = require('../verify/helpers.js')
var userData = require('../data.json')

var addtoList = helper.addtoList
var markChallengeCompleted = helper.markChallengeCompleted
var writeData = helper.writeData

var currentChallenge = 'commit_to_it'

// check that they've commited changes

module.exports = function commitVerify (path) {
  exec('git status', {cwd: path}, function (err, stdout, stdrr) {
    if (err) return addtoList(err.message, false)
    var show = stdout.trim()

    if (show.match("Initial commit")) {
      addtoList("Hmm, can't find\ncommitted changes.", false)
    } else if (show.match("nothing to commit")) {
      addtoList("Changes have been committed!", true)
    } else {
      addtoList("Seems there are changes\nto commit still.", false)
    }
  })
}

var exec = require('child_process').exec
var fs = require('fs')

var helper = require('../verify/helpers.js')
var userData = require('../data.json')

var addtoList = helper.addtoList
var markChallengeCompleted = helper.markChallengeCompleted
var writeData = helper.writeData

var currentChallenge = 'commit_to_it'

// check that they've commited changes

module.exports = function commitVerify (path) {
  if (!fs.lstatSync(path).isDirectory()) return addtoList('Path is not a directory', false)
  exec('git status', {cwd: path}, function (err, stdout, stdrr) {
    if (err) return addtoList(err.message, false)
    var show = stdout.trim()

    if (show.match('Initial commit')) {
      addtoList("Hmm, can't find committed changes.", false)
    } else if (show.match('nothing to commit')) {
      addtoList('Changes have been committed!', true)
      markChallengeCompleted()
      writeData(userData, currentChallenge)
    } else {
      addtoList('Seems there are changes to commit still.', false)
    }
  })
}

Clean up Commit cha verify, add completed steps


// XXX This currently implements loading screens for mobile apps only,
// but in the future can be expanded to all apps.

var holdCount = 0;
var alreadyHidden = false;

LaunchScreen = {
  hold: function () {
    if (! Meteor.isCordova) {
      return {
        release: function () { /* noop */ }
      };
    }

    if (alreadyHidden) {
      throw new Error("Can't show launch screen once it's hidden");
    }

    holdCount++;

    var released = false;
    var release = function () {
      if (! Meteor.isCordova)
        return;

      if (! released) {
        holdCount--;
        if (holdCount === 0 &&
            typeof navigator !== 'undefined' && navigator.splashscreen) {
          alreadyHidden = true;
          navigator.splashscreen.hide();
        }
      }
    };

    // Returns a launch screen handle with a release method
    return {
      release: release
    };
  }
};

// XXX This currently implements loading screens for mobile apps only,
// but in the future can be expanded to all apps.

var holdCount = 0;
var alreadyHidden = false;

LaunchScreen = {
  hold: function () {
    if (! Meteor.isCordova) {
      return {
        release: function () { /* noop */ }
      };
    }

    if (alreadyHidden) {
      throw new Error("Can't show launch screen once it's hidden");
    }

    holdCount++;

    var released = false;
    var release = function () {
      if (! Meteor.isCordova)
        return;

      if (! released) {
        released = true;
        holdCount--;
        if (holdCount === 0 &&
            typeof navigator !== 'undefined' && navigator.splashscreen) {
          alreadyHidden = true;
          navigator.splashscreen.hide();
        }
      }
    };

    // Returns a launch screen handle with a release method
    return {
      release: release
    };
  }
};

Fix bug in launch-screen that disabled holding it beyond a 6s timeout


import React, { Component } from 'react';

import SearchActions from '../actions/SearchActions.js';

class Run extends Component {
  componentDidMount() {
    SearchActions.nsearch(this.props.namespace || 'default');
  }

  componentWillUnmount() {
    SearchActions.nsearch(this.props.namespace || 'default');
  }

  render() {
    return null;
  }
}

Run.propTypes = {
  namespace: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.arrayOf(React.PropTypes.string),
  ]),
};

export default Run;

import React, { Component } from 'react';

import SearchActions from '../actions/SearchActions.js';

class Run extends Component {
  componentDidMount() {
    SearchActions.nsearch(this.props.namespace);
  }

  componentWillUnmount() {
    if (this.props.runOnUnmount) {
      SearchActions.nsearch(this.props.namespace);
    }
  }

  render() {
    return null;
  }
}

Run.propTypes = {
  namespace: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.arrayOf(React.PropTypes.string),
  ]),
  runOnUnmount: React.PropTypes.boolean,
};

Run.defaultProps = {
  namespace: 'default',
  runOnUnmount: true,
}

export default Run;

Allow run to not run on unmount


import backbone from 'backbone';
import AllocationSource from 'models/AllocationSource';
import globals from 'globals';
import _ from 'underscore';

import allocationSources from 'mockdata/allocationSources.json';
import mockSync from 'utilities/mockSync';

export default backbone.Collection.extend({
    model: AllocationSource,

    url: globals.API_V2_ROOT + "/allocation_sources",

    parse: function (response) {

        // Ensure the api returns values for these fields
        let defaults = {
            compute_used: 100,
            compute_allowed: 1000,
            name: "dummy"
        };

        let results = response.results.map(source => {
            Object.keys(defaults).forEach(f => {
                if (!source[f]) {
                    console.warn("We are tampering with data "+f+" until the api settles");
                    source[f] = defaults[f];
                }
            });
            return source;
        });
        return results;
    },

    sync: globals.USE_MOCK_DATA
          ? mockSync(allocationSources)
          : Backbone.sync
});

import backbone from 'backbone';
import AllocationSource from 'models/AllocationSource';
import globals from 'globals';
import _ from 'underscore';

import allocationSources from 'mockdata/allocationSources.json';
import mockSync from 'utilities/mockSync';

export default backbone.Collection.extend({
    model: AllocationSource,

    url: globals.API_V2_ROOT + "/allocation_sources",

    parse: function (response) {
        console.warn("We are tampering with data until the api settles");

        // Ensure the api returns values for these fields
        let defaults = {
            compute_used: 100,
            compute_allowed: 1000,
            name: "dummy"
        };

        let results = response.results.map(source => {
            Object.keys(defaults).forEach(f => {
                if (!source[f]) {
                    source[f] = defaults[f];
                }
            });
            return source;
        });
        return results;
    },

    sync: globals.USE_MOCK_DATA
          ? mockSync(allocationSources)
          : Backbone.sync
});

Revert "Show tampering warning *only* when data has been tampered with :ninja:"


(function () {
    function initTwinklingStars() {
        const devicePixelRatio = Math.min(window.devicePixelRatio, 3) || 1;

        particlesJS('stars', {
            particles: {
                number: {
                    value: 180,
                    density: {
                        enable: true,
                        value_area: 600
                    }
                },
                color: {
                    value: '#ffffff'
                },
                shape: {
                    type: 'circle'
                },
                opacity: {
                    value: 1,
                    random: true,
                    anim: {
                        enable: true,
                        speed: 3,
                        opacity_min: 0
                    }
                },
                size: {
                    value: 1.5 / devicePixelRatio,
                    random: true,
                    anim: {
                        enable: true,
                        speed: 1,
                        size_min: 0.5 / devicePixelRatio,
                        sync: false
                    }
                },
                line_linked: {
                    enable: false
                },
                move: {
                    enable: true,
                    speed: 50,
                    direction: 'none',
                    random: true,
                    straight: true,
                    out_mode: 'bounce'
                }
            },
            retina_detect: true
        });
    }

    window.onload = () => {
        document.body.className = '';
        initTwinklingStars();
    }

    window.onresize = () => {
        clearTimeout(window.onResizeEnd);
        window.onResizeEnd = setTimeout(initTwinklingStars, 250);
    }

    window.ontouchmove = () => false;

    window.onorientationchange = () => {
        document.body.scrollTop = 0;
    };
}());

(function () {
    // Private auxiliary functions

    function initTwinklingStars() {
        const devicePixelRatio = Math.min(window.devicePixelRatio, 3) || 1;

        particlesJS('stars', {
            particles: {
                number: {
                    value: 180,
                    density: {
                        enable: true,
                        value_area: 600
                    }
                },
                color: {
                    value: '#ffffff'
                },
                shape: {
                    type: 'circle'
                },
                opacity: {
                    value: 1,
                    random: true,
                    anim: {
                        enable: true,
                        speed: 3,
                        opacity_min: 0
                    }
                },
                size: {
                    value: 1.5 / devicePixelRatio,
                    random: true,
                    anim: {
                        enable: true,
                        speed: 1,
                        size_min: 0.5 / devicePixelRatio,
                        sync: false
                    }
                },
                line_linked: {
                    enable: false
                },
                move: {
                    enable: true,
                    speed: 50,
                    direction: 'none',
                    random: true,
                    straight: true,
                    out_mode: 'bounce'
                }
            },
            retina_detect: true
        });
    }

    // Event handlers for varius Window events

    window.onload = () => {
        document.body.className = '';
        initTwinklingStars();
    }

    window.onresize = () => {
        clearTimeout(window.onResizeEnd);
        window.onResizeEnd = setTimeout(initTwinklingStars, 250);
    }

    window.ontouchmove = () => false;

    window.onorientationchange = () => {
        document.body.scrollTop = 0;
    };
}());

Add header comments to JS code


// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

function SpecError(message) {
    var error = new Error(message);
    error.context = [];
    error.annotate = annotate;
    return error;
}

function annotate(context) {
    this.context.push(context);
}

// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

module.exports = SpecError;

function SpecError(message) {
    var error = new Error(message);
    error.context = [];
    error.annotate = annotate;
    return error;
}

function annotate(context) {
    this.context.push(context);
}

Fix missing exports for SpecError


window.addEventListener("keypress", function (event) {
    if (window.document.activeElement !== window.document.body) {
        return;
    }

    const keyCode = event.code;
    if (keyCode.indexOf("Digit") !== 0) {
        return;
    }

    const digit = Number(keyCode[5]);
    if (digit < 0 || digit > 9) {
        return;
    }

    const index = (digit === 0) ? 8 : (digit - 1);
    const results = document.body.querySelectorAll("h3.r");
    const result = results[index];
    if (!result) {
        return;
    }

    const anchor = result.querySelector("a");
    const href = anchor.href;

    window.location.href = href;
});

const emojicationSuffix = "\u{FE0F}\u{20e3} ";

function emojifyResults () {
    const results = document.body.querySelectorAll("h3.r");
    const last = results.length - 1;

    console.log("results", results);

    results.forEach((result, index) => {
        if (index === last || index < 9) {
            const digit = (index === last) ? 0 : index + 1;
            const emojiDigit = String(digit) + emojicationSuffix;

            result.insertAdjacentText("afterbegin", emojiDigit);
        }
    });
};

if (window.document.readyState === "complete") {
    emojifyResults();
}

window.document.onreadystatechange = function () {
    if (document.readyState === "complete") {
        emojifyResults();
    }
}

window.addEventListener("keydown", function handleNavigation (event) {
    if (window.document.activeElement !== window.document.body) {
        return;
    }

    const keyCode = event.code;
    if (keyCode.indexOf("Digit") !== 0) {
        return;
    }

    const digit = Number(keyCode[5]);
    if (digit < 0 || digit > 9) {
        return;
    }

    const index = (digit === 0) ? 8 : (digit - 1);
    const results = document.body.querySelectorAll("h3.r");
    const result = results[index];
    if (!result) {
        return;
    }

    event.stopPropagation();

    const anchor = result.querySelector("a");
    const href = anchor.href;

    window.location.href = href;
}, true);

const emojicationSuffix = "\u{FE0F}\u{20e3} ";

function emojifyResults () {
    const results = document.body.querySelectorAll("h3.r");
    const last = results.length - 1;

    results.forEach((result, index) => {
        if (index === last || index < 9) {
            const digit = (index === last) ? 0 : index + 1;
            const emojiDigit = String(digit) + emojicationSuffix;

            result.insertAdjacentText("afterbegin", emojiDigit);
        }
    });
};

if (window.document.readyState === "complete") {
    emojifyResults();
}

window.document.onreadystatechange = function () {
    if (document.readyState === "complete") {
        emojifyResults();
    }
}

Handle navigation on keydown instead of keypress to work around automatic input focus acquisition.


import Ember from "ember";

export default Ember.ArrayController.extend({
  needs: ["offer"],
  sortProperties: ["createdAt"],
  sortAscending: true,
  isPrivate: false,
  offer: Ember.computed.alias("controllers.offer.model"),
  item: null,

  actions: {
    sendMessage: function() {
      var values = this.getProperties("body", "offer", "item", "isPrivate");
      values.createdAt = new Date();
      values.sender = this.store.getById("user", this.get("session.currentUser.id"));

      var message = this.store.createRecord("message", values);
      message.save();

      this.set("body", "");
      Ember.$("body").animate({ scrollTop: Ember.$(document).height() }, 1000);
    }
  }
});

import Ember from "ember";

export default Ember.ArrayController.extend({
  needs: ["offer"],
  sortProperties: ["createdAt"],
  sortAscending: true,
  isPrivate: false,
  offer: Ember.computed.alias("controllers.offer.model"),
  item: null,

  actions: {
    sendMessage: function() {
      var _this = this;
      var values = this.getProperties("body", "offer", "item", "isPrivate");
      values.createdAt = new Date();
      values.sender = this.store.getById("user", this.get("session.currentUser.id"));

      var message = this.store.createRecord("message", values);
      message.save().then(function() {
        _this.set("body", "");
      }, function() {
        _this.store.unloadRecord(message);
      });

      Ember.$("body").animate({ scrollTop: Ember.$(document).height() }, 1000);
    }
  }
});

Handle failure when attempting to send a message


import Ember from 'ember';
import computedMoment from 'ember-calendar/macros/computed-moment';
import computedDuration from 'ember-calendar/macros/computed-duration';
import Calendar from './calendar';
import OccurrenceProxy from './occurrence-proxy';

export default Calendar.extend({
  component: null,
  timeZone: Ember.computed.oneWay('component.timeZone'),
  startingTime: computedMoment('component.startingDate'),
  dayStartingTime: computedDuration('component.dayStartingTime'),
  dayEndingTime: computedDuration('component.dayEndingTime'),
  timeSlotDuration: computedDuration('component.timeSlotDuration'),

  defaultOccurrenceTitle: Ember.computed.oneWay(
    'component.defaultOccurrenceTitle'
  ),

  defaultOccurrenceDuration: computedDuration(
    'component.defaultOccurrenceDuration'
  ),

  defaultOccurrenceType: computedDuration(
    'component.defaultOccurrenceType'
  ),

  occurrences: Ember.computed('component.occurrences.[]', function() {
    return this.get('component.occurrences').map((occurrence) => {
      return OccurrenceProxy.create({ calendar: this, content: occurrence });
    });
  })
});

import Ember from 'ember';
import computedMoment from 'ember-calendar/macros/computed-moment';
import computedDuration from 'ember-calendar/macros/computed-duration';
import Calendar from './calendar';
import OccurrenceProxy from './occurrence-proxy';

export default Calendar.extend({
  component: null,
  timeZone: Ember.computed.oneWay('component.timeZone'),
  startingTime: computedMoment('component.startingDate'),
  dayStartingTime: computedDuration('component.dayStartingTime'),
  dayEndingTime: computedDuration('component.dayEndingTime'),
  timeSlotDuration: computedDuration('component.timeSlotDuration'),
  type: Ember.computed.oneWay('component.type'),

  defaultOccurrenceTitle: Ember.computed.oneWay(
    'component.defaultOccurrenceTitle'
  ),

  defaultOccurrenceDuration: computedDuration(
    'component.defaultOccurrenceDuration'
  ),

  defaultOccurrenceType: computedDuration(
    'component.defaultOccurrenceType'
  ),

  occurrences: Ember.computed('component.occurrences.[]', function() {
    return this.get('component.occurrences').map((occurrence) => {
      return OccurrenceProxy.create({ calendar: this, content: occurrence });
    });
  })
});

Add type computed to the component calendar model.


import React, { PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Count from 'countup.js';

class CountUp extends React.Component {
  componentDidMount() {
    const {
      start,
      end,
      duration,
      useEasing,
      useGrouping,
      separator,
      decimal,
      prefix,
      suffix,
      callback,
    } = this.props;

    const countup = new Count(findDOMNode(this), start, end, {
      duration,
      useEasing,
      useGrouping,
      separator,
      decimal,
      prefix,
      suffix,
    });

    countup.start(callback);
  }

  render() {
    return ;
  }
}

CountUp.defaultProps = {
  className: 'CountUp',
  start: 0,
  end: 100,
  duration: 2.5,
  useEasing: true,
  useGrouping: true,
  separator: ',',
  decimal: '.',
  prefix: '',
  suffix: '',
};

CountUp.propTypes = {
  start: PropTypes.number.isRequired,
  end: PropTypes.number.isRequired,
  duration: PropTypes.number,
  useEasing: PropTypes.bool,
  useGrouping: PropTypes.bool,
  separator: PropTypes.string,
  decimal: PropTypes.string,
  prefix: PropTypes.string,
  suffix: PropTypes.string,
  callback: PropTypes.func,
};

export default CountUp;

import React, { PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Count from 'countup.js';

class CountUp extends React.Component {
  componentDidMount() {
    const {
      start,
      end,
      duration,
      useEasing,
      useGrouping,
      separator,
      decimal,
      prefix,
      suffix,
      callback,
    } = this.props;

    const countup = new Count(findDOMNode(this), start, end, {
      duration,
      useEasing,
      useGrouping,
      separator,
      decimal,
      prefix,
      suffix,
    });

    countup.start(callback);
  }

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

    return ;
  }
}

CountUp.defaultProps = {
  start: 0,
  end: 100,
  duration: 2.5,
  useEasing: true,
  useGrouping: true,
  separator: ',',
  decimal: '.',
  prefix: '',
  suffix: '',
};

CountUp.propTypes = {
  className: PropTypes.node,
  style: PropTypes.node,
  start: PropTypes.number.isRequired,
  end: PropTypes.number.isRequired,
  duration: PropTypes.number,
  useEasing: PropTypes.bool,
  useGrouping: PropTypes.bool,
  separator: PropTypes.string,
  decimal: PropTypes.string,
  prefix: PropTypes.string,
  suffix: PropTypes.string,
  callback: PropTypes.func,
};

export default CountUp;

Add className and styles props


var fipsData;
google.load('visualization', '1', {packages:['table']});
$(function() {
    $.ajax({
        url: '/data/fipsData.json',
        dataType: 'json',
        success: function(data) {
            fipsData = data;
        }
    });
    $('#collapse-data-more').on('shown.bs.collapse', function () {
        generateChart();
    });
});

function generateChart() {
    if (!fipsData) {
        //Wait for fips data to load
        setTimeout(generateChart, 1000);
    } else {
        var data = new google.visualization.DataTable();

        data.addColumn('string', 'State');
        data.addColumn('string', 'County');
        data.addColumn('number', 'Count');
        var rows = [];
        var keys = vehicleCounts.keys();
        for (var i in keys) {
            var key = keys[i];
            var count = vehicleCounts.get(key.replace(/^0*/, ''));
            var fips = fipsData[''+key];
            if (fips) {
                rows.push([fips.State, fips.County, count]);
            } else {
                console.log('Unknown fips: ' + fips);
            }
        }

        data.addRows(rows);

        var table = new google.visualization.Table(document.getElementById('dataTable'));
        table.draw(data, {showRowNumber: false, sort: 'enable', sortColumn: 2, sortAscending: false});
    }
}

var fipsData;
google.load('visualization', '1', {packages:['table']});
$(function() {
    $.ajax({
        url: '/data/fipsData.json',
        dataType: 'json',
        success: function(data) {
            fipsData = data;
        }
    });
    $('#collapse-data-more').on('shown.bs.collapse', function () {
        generateChart();
        $('#tableToggle').html('Hide this data');
    });
    $('#collapse-data-more').on('hidden.bs.collapse', function () {
        $('#tableToggle').html('Learn More About This Data');
    });
});

function generateChart() {
    if (!fipsData) {
        //Wait for fips data to load
        setTimeout(generateChart, 1000);
    } else {
        var data = new google.visualization.DataTable();

        data.addColumn('string', 'State');
        data.addColumn('string', 'County');
        data.addColumn('number', 'Count');
        var rows = [];
        var keys = vehicleCounts.keys();
        for (var i in keys) {
            var key = keys[i];
            var count = vehicleCounts.get(key.replace(/^0*/, ''));
            var fips = fipsData[''+key];
            if (fips) {
                rows.push([fips.State, fips.County, count]);
            } else {
                console.log('Unknown fips: ' + fips);
            }
        }

        data.addRows(rows);

        var table = new google.visualization.Table(document.getElementById('dataTable'));
        table.draw(data, {showRowNumber: false, sort: 'enable', sortColumn: 2, sortAscending: false});
    }
}

Change button text and other stuff


import test from 'tape'
import {generate, validate} from '../src/index'

test('should generate reference with numbers', (assert) => {
  const expected = 'RF712348231'
  const actual = generate(2348231)

  assert.equal(actual, expected)
  assert.end()
})

test('should generate reference with characters', (assert) => {
  const expected = 'RF19AB2G59X56V543'
  const actual = generate('AB2G5 9X56 V543')

  assert.equal(actual, expected)
  assert.end()
})

test('should generate reference with zero padding', (assert) => {
  const expected = 'RF097'
  const actual = generate('7')

  assert.equal(actual, expected)
  assert.end()
})

test('should generate valid reference from timestamp', (assert) => {
  const expected = true
  const actual = validate(generate())

  assert.equal(actual, expected)
  assert.end()
})

import test from 'tape'
import {generate, validate} from '../src/index'

test('should generate reference with numbers', (assert) => {
  const expected = 'RF712348231'
  const actual = generate(2348231)

  assert.equal(actual, expected)
  assert.end()
})

test('should generate reference with characters', (assert) => {
  const expected = 'RF19AB2G59X56V543'
  const actual = generate('AB2G5 9X56 V543')

  assert.equal(actual, expected)
  assert.end()
})

test('should generate reference with zero padding', (assert) => {
  const expected = 'RF097'
  const actual = generate('7')

  assert.equal(actual, expected)
  assert.end()
})

test('should generate reference from zero padded number', (assert) => {
  const expected = 'RF7400001'
  const actual = generate('00001')

  assert.equal(actual, expected)
  assert.end()
})

test('should generate valid reference from timestamp', (assert) => {
  const expected = true
  const actual = validate(generate())

  assert.equal(actual, expected)
  assert.end()
})

Add test to cover generating creditor reference from zero padded number


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

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

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

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

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

        helpers.mockPrompt(this.app, {
            'name': 'Hello World',
            'version': '0.1.0',
            'repository': '',
            'projectType': 'isDrupal',
            'libs': [ 'hasModernizr', 'hasUniform', 'hasHandlebars' ]
        });
        this.app.options['skip-install'] = true;
        this.app.run({}, function () {
            helpers.assertFiles(expected);
            done();
        });
    });
});

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

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

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

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

    it('creates expected files', function (done) {
        var expected = [
            // add files you expect to exist here.
            '.jshintrc',
            '.editorconfig',
            'config.rb',
            'bower.json',
            'Gruntfile.js',
            '.gitignore',
            '.jshintrc',
            '.editorconfig',
            'css/sass/_base.scss',
            'css/sass/_buttons.scss',
            'css/sass/_forms.scss',
            'css/sass/_typography.scss',
            'css/sass/master.scss',
            'js/.js',
            'index.html'
        ];

        helpers.mockPrompt(this.app, {
            'name': 'Hello World',
            'version': '0.1.0',
            'repository': '',
            'projectType': 'isDrupal',
            'libs': [ 'hasModernizr', 'hasUniform', 'hasHandlebars' ]
        });
        this.app.options['skip-install'] = true;
        this.app.run({}, function () {
            helpers.assertFiles(expected);
            done();
        });
    });
});

Add more files to test for creation


/**
 * Copyright 2017-present, Callstack.
 * All rights reserved.
 *
 * @flow
 */
const program = require("commander");
const pjson = require("../../package.json");
const logger = require("../utils/logger")(false);

import type { Command, Context } from "../types";

const commands: Array<Command> = [require("./start")];

const ctx: Context = {
  console: console,
};

commands.forEach((command: Command) => {
  const options = command.options || [];

  const cmd = program
    .command(command.name)
    .description(command.description)
    .action(function run() {
      logger.clear();
      logger.printLogo();

      const options = this.opts();
      const argv: Array<string> = Array.from(arguments).slice(0, -1);

      command.action(ctx, argv, options);
    });

  options
    .forEach(opt => cmd.option(
      opt.name,
      opt.description,
      opt.parse || ((val) => val),
      typeof opt.default === 'function' ? opt.default(ctx) : opt.default,
    ));

});

program
  .command('*', null, { noHelp: true })
  .action((cmd) => {
    logger.clear();
    logger.printLogo();
    logger.error(`:x:  Command '${cmd}' not recognized`);
    program.help();
  });

program.version(pjson.version).parse(process.argv);

/**
 * Copyright 2017-present, Callstack.
 * All rights reserved.
 *
 * @flow
 */
const program = require("commander");
const pjson = require("../../package.json");
const logger = require("../utils/logger")(false);

import type { Command, Context } from "../types";

const commands: Array<Command> = [require("./start")];

const ctx: Context = {
  console: console,
};

commands.forEach((command: Command) => {
  const options = command.options || [];

  const cmd = program
    .command(command.name)
    .description(command.description)
    .action(function run() {
      logger.clear();
      logger.printLogo();

      const options = this.opts();
      const argv: Array<string> = Array.from(arguments).slice(0, -1);

      command.action(ctx, argv, options);
    });

  options
    .forEach(opt => cmd.option(
      opt.name,
      opt.description,
      opt.parse || ((val) => val),
      typeof opt.default === 'function' ? opt.default(ctx) : opt.default,
    ));

  cmd._helpInformation = cmd.helpInformation.bind(cmd);
  cmd.helpInformation = function() {
    logger.clear();
    logger.printLogo(2);
    return this._helpInformation();
  };
});

program
  .command('*', null, { noHelp: true })
  .action((cmd) => {
    logger.clear();
    logger.printLogo();
    logger.error(`:x:  Command '${cmd}' not recognized`);
    program.help();
  });

program.version(pjson.version).parse(process.argv);

Add logo to command help information


var fs = require("q-io/fs"); // https://github.com/kriskowal/q-io
var request = require('request');

module.exports = function () {

  this.Given(/^a set of specifications exists$/, function (callback) {
    /**
     * Copy this repo's features files to a public directory for serving.
     */

    // Remove old files.
    fs.removeTree('public/test-feature-files/')
      .then(function() {
        return fs.copyTree('features', 'public/test-feature-files/');
      })

      // We are done.
      .then(function(val) {
          callback();
      })

      // Pass unhandled errors to the test framework.
      .catch(function(err) {
        callback(err);
      })

      // End the promise chain.
      .done();
  });

  this.When(/^an interested party attempts to view them$/, function (callback) {
    var world = this;
    request
      .get('http://localhost:3000', function(error, response, body) {
        world.statusCode = response.statusCode;
        world.body = body;
        callback();
      });
  });

  this.Then(/^the specifications should be visible$/, function (callback) {
    if (this.statusCode === 200 && /feature/.test(this.body)) {
      callback();
    } else {
      callback("Got response: status code: " + this.statusCode + ". Body: " + this.body);
    }
  });
}

var fs = require("q-io/fs"); // https://github.com/kriskowal/q-io
var request = require('request');

module.exports = function () {

  this.Given(/^a set of specifications exists$/, function (callback) {
    /**
     * Copy this repo's features files to a public directory for serving.
     */

    // Remove old files.
    fs.removeTree('public/test-feature-files/')
      .catch(function(err) {
        // Ignore failure to unlink missing directory.
        if (err.code !== 'ENOENT') throw err;
      })
      .then(function() {
        return fs.makeTree('public/test-feature-files/');
      })
      .then(function() {
        return fs.copyTree('features', 'public/test-feature-files/');
      })

      // We are done.
      .then(function(val) {
          callback();
      })

      // Pass unhandled errors to the test framework.
      .catch(function(err) {
        callback(err);
      })

      // End the promise chain.
      .done();
  });

  this.When(/^an interested party attempts to view them$/, function (callback) {
    var world = this;
    request
      .get('http://localhost:3000', function(error, response, body) {
        world.statusCode = response.statusCode;
        world.body = body;
        callback();
      });
  });

  this.Then(/^the specifications should be visible$/, function (callback) {
    if (this.statusCode === 200 && /feature/.test(this.body)) {
      callback();
    } else {
      callback("Got response: status code: " + this.statusCode + ". Body: " + this.body);
    }
  });
}

Handle missing test data directory.


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

module.exports = {
  devtool: 'source-map',
  context: __dirname,
  entry: [
    './index.js'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },

  module: {
    loaders: [
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'url-loader?limit=10000&minetype=application/font-woff'
      },
      { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader'
      },
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      },
      {
        test: /\.styl$/,
        loader: 'style!css!stylus?paths=node_modules'
      },
      { test: /\.js$|\.jsx$/,
        exclude: [/node_modules/],
        loaders: [
          'react-hot',
          'babel?' + JSON.stringify({presets: ['stage-2', 'es2015', 'react']})
        ]
      }
    ]
  },

  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    })
  ]
};

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

module.exports = {
  devtool: 'source-map',
  context: __dirname,
  entry: [
    './index.js'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },

  module: {
    loaders: [
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'url-loader?limit=10000&minetype=application/font-woff'
      },
      { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader'
      },
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      },
      {
        test: /\.styl$/,
        loader: 'style!css!stylus?paths=node_modules'
      },
      { test: /\.js$|\.jsx$/,
        exclude: [/node_modules/],
        loaders: [
          'babel?' + JSON.stringify({presets: ['stage-2', 'es2015', 'react']})
        ]
      }
    ]
  },

  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    })
  ]
};

Remove hot loader from webpack config


'use strict';

const template = require('../../views/components/Logo.hbs');

class Logo {

	constructor() {
		this.container = document.createElement('div');

		this.container.classList.add('logo-container');
		this.render();
	}

	render() {
		this.container.innerHTML = template();
	}
}

module.exports = Logo;

'use strict';

const template = require('../../views/components/Logo.hbs');

class Logo {

	constructor() {
		this.container = document.createElement('div');
		this.container.classList.add('logo-container');
		this.render();
	}

	render() {
		this.container.innerHTML = template();
	}

	show() {
		this.container.classList.remove('hidden');
	}

	hidden() {
		this.container.classList.add('hidden');
	}

}

module.exports = Logo;

Create show and hidden methods


const Request = require('request');
const env2 = require('env2');
const HapiCookie = require('hapi-auth-cookie');

env2('./config.env');

module.exports = {
  path: '/welcome',
  method: 'GET',
  handler: (req, rep) => {
    const accessUrl = `https://github.com/login/oauth/access_token`;
    Request.post({
      headers: {
        accept: 'application/json'
      },
      url: accessUrl,
      form: {
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        code: req.query.code
      }
    }, (err, res, body) => {
      if (err) throw err;
      const parsed = JSON.parse(body);
      const userDetails = {
        access_token: parsed.access_token
      };
      req.cookieAuth.set({access_token: userDetails.access_token});
      rep.redirect('/');
    });
  }
};

const Request = require('request');
const env2 = require('env2');
const HapiCookie = require('hapi-auth-cookie');

env2('./config.env');

module.exports = {
  path: '/welcome',
  method: 'GET',
  handler: (req, rep) => {
    const accessUrl = `https://github.com/login/oauth/access_token`;
    Request.post({
      headers: {
        accept: 'application/json'
      },
      url: accessUrl,
      form: {
        client_id: process.env.CLIENT_ID,
        client_secret: process.env.CLIENT_SECRET,
        code: req.query.code
      }
    }, (err, res, body) => {
      if (err) throw err;
      let parsed = JSON.parse(body);
      const userToken = {
        access_token: parsed.access_token
      };
      Request.get({
        headers: {
          'User-Agent': 'GitPom',
          // as recommended by the API documentation
          Accept: `application/vnd.github.v3+json`,
          Authorization: `token ${userToken.access_token}`
        },
        url: `https://api.github.com/user`
      }, (err, res, body) => {
        if (err) throw err;
        parsed = JSON.parse(body);
        const userDetails = {
          userName: parsed.login,
          avatarUrl: parsed.avatar_url
        };
        // set the cookie containing the token, the username and the avatar url
        req.cookieAuth.set(Object.assign(userToken, userDetails));
        rep.redirect('/');
      });
    });
  }
};

Add user details in the cookie


var config = module.exports;

config['development'] = {
    env: 'browser'
    , rootPath: '../'
    , deps: [
        'components/jquery/jquery.js'
    ]
    , sources: ['src/js/aviso.js']
    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//    , extensions: [ require('buster-coverage') ]
//    , "buster-coverage": {
//        outputDirectory: "test/coverage"
//        , format: "lcov"
//        , combinedResultsOnly: true
//    }
};

// @TODO Add tests for the /dist files
//
//config['core'] = {
//    env: 'browser'
//    , rootPath: '../'
//    , deps: [
//        'components/jquery/jquery.js'
//    ]
//    , sources: ['dist/aviso.js']
//    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//};
//
//config['amd'] = {
//    env: 'browser'
//    , rootPath: '../'
//    , deps: [
//        'components/jquery/jquery.js'
//    ]
//    , sources: ['dist/amd/aviso.js']
//    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//};

var config = module.exports;

config['development'] = {
    env: 'browser'
    , rootPath: '../'
    , deps: [
        'bower_components/jquery/jquery.js'
    ]
    , sources: ['src/js/aviso.js']
    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//    , extensions: [ require('buster-coverage') ]
//    , "buster-coverage": {
//        outputDirectory: "test/coverage"
//        , format: "lcov"
//        , combinedResultsOnly: true
//    }
};

// @TODO Add tests for the /dist files
//
//config['core'] = {
//    env: 'browser'
//    , rootPath: '../'
//    , deps: [
//        'components/jquery/jquery.js'
//    ]
//    , sources: ['dist/aviso.js']
//    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//};
//
//config['amd'] = {
//    env: 'browser'
//    , rootPath: '../'
//    , deps: [
//        'components/jquery/jquery.js'
//    ]
//    , sources: ['dist/amd/aviso.js']
//    , specs: ['test/buster-jquery-assertions.js', 'test/spec/**/*.js']
//};

Fix travis? It was referencing the old /components folder which has since been renamed.


const assert = require('assert');
const { crypto, config } = require('./config');

describe("WebCrypto", () => {

    it("get random values", () => {
        var buf = new Uint8Array(16);
        var check = new Buffer(buf).toString("base64");
        assert.notEqual(new Buffer(crypto.getRandomValues(buf)).toString("base64"), check, "Has no random values");
    })

    it("get random values with large buffer", () => {
        var buf = new Uint8Array(65600);
        assert.throws(() => {
            crypto.getRandomValues(buf);
        }, Error);
    })

    it("reset", (done) => {
        const currentHandle = crypto.session.handle.toString("hex");
        crypto.reset()
            .then(() => {
                crypto.login(config.pin);
                const newHandle = crypto.session.handle.toString("hex");
                assert(currentHandle !== newHandle, true, "handle of session wasn't changed");
            })
            .then(done, done);
    })
})

const assert = require('assert');
const crypto = require('./config').crypto;
const config = require('./config').config;

describe("WebCrypto", () => {

    it("get random values", () => {
        var buf = new Uint8Array(16);
        var check = new Buffer(buf).toString("base64");
        assert.notEqual(new Buffer(crypto.getRandomValues(buf)).toString("base64"), check, "Has no random values");
    })

    it("get random values with large buffer", () => {
        var buf = new Uint8Array(65600);
        assert.throws(() => {
            crypto.getRandomValues(buf);
        }, Error);
    })

    it("reset", (done) => {
        const currentHandle = crypto.session.handle.toString("hex");
        crypto.reset()
            .then(() => {
                crypto.login(config.pin);
                const newHandle = crypto.session.handle.toString("hex");
                assert(currentHandle !== newHandle, true, "handle of session wasn't changed");
            })
            .then(done, done);
    })
})

Fix require for node v5


$(function(){
  var
    $btns = $('.btn'),
    colors = ['#1ABC9C','#2ECC71','#3498DB','#34495E','#9B59B6','#F1C40F','#E67E22','#E74C3C','#7F8C8D','#2980B9','#95A5A6','#C0392B','#D35400','#F39C12','#666'],
    selected = null,
    rnd = function(min,max){
      return  Math.floor(Math.random()*(max-min+1)+min);
    },
    setColors = function(){
      $btns.each(function(){
        $(this).css({
          'background-color': colors[rnd(0,14)]
        });
      });
    },
    exploid = function(){
      $btns.each(function(){
        $(this).css({
          left : rnd(0,90) + '%',
          top : rnd(0,90) + '%'
        });
      });
    },
    choose1 = function(){

    },
    setEvents = function(){
      $btns.click(choose1);
    },
    init = function(){
      setColors();
      exploid();
      setEvents();
    };

  init();
});

$(function(){
  var
  $start = $('#start'),
  $btns = $('.btns .btn'),
  colors = ['#1ABC9C','#2ECC71','#3498DB','#34495E','#9B59B6','#F1C40F','#E67E22','#E74C3C','#7F8C8D','#2980B9','#95A5A6','#C0392B','#D35400','#F39C12','#666'],
  selected = null,
  rnd = function(min,max){
    return  Math.floor(Math.random()*(max-min+1)+min);
  },
  randColor = function(){
    return colors[rnd(0,colors.length-1)];
  },
  setColors = function(){
    $btns.each(function(){
      $(this).css({
        'background-color': randColor()
      });
    });
  },
  exploid = function(){
    $btns.each(function(){
      $(this).animate({
        left : rnd(0,90) + '%',
        top : rnd(0,90) + '%'
      },400);
    });
  },
  choose1 = function(){

  },
  start = function(){
    exploid();
    $btns.click(choose1);
  },
  reset = function(){
    window.location.reload(); // cheap as posible !
  },
  init = function(){
    $start.click(start);
    setColors();
  };

  init();

});

Add start and reset fn


const jsonServer = require('json-server')
const server = jsonServer.create()
const path = require('path')
const router = jsonServer.router(path.join(__dirname, 'db.json'))
const middlewares = jsonServer.defaults()

server.use(middlewares)
server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
  // non-get requests just send back the same data sent
  if (['POST', 'DELETE', 'PUT', 'PATCH'].includes(req.method)) {
    res.jsonp(req.body)
  } else {
    next()
  }
})

router.render = (req, res) => {
  var result = res.locals.data
  var keyPath = req.originalUrl.split('=')[1]
  if (keyPath === 'keyPath') {
    result = {}
    result[keyPath] = res.locals.data
  }
  res.jsonp(result)
}

server.use(jsonServer.rewriter({
  '/api/': '/'
}))

server.use(router)
server.listen(3000, () => {
  console.log('JSON Server is running')
})

const jsonServer = require('json-server')
const server = jsonServer.create()
const path = require('path')
const router = jsonServer.router(path.join(__dirname, 'db.json'))
const middlewares = jsonServer.defaults()

server.use(middlewares)
server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
  // non-get requests just send back the same data sent
  if (['POST', 'DELETE', 'PUT', 'PATCH'].includes(req.method)) {
    res.jsonp(req.body)
  } else {
    next()
  }
})

router.render = (req, res) => {
  var result = res.locals.data
  var query = req.originalUrl.split('?')[1]
  if (query !== undefined) {
    var tuple = query.split('=')
    var key = tuple[0]
    var value = tuple[1]
    if (key === 'keyPath') {
      var node = {}
      var lastNode = null
      var lastPath = null
      result = node
      value.split('.').forEach((path) => {
        lastNode = node
        lastPath = path
        node[path] = {}
        node = node[path]
      })
      lastNode[lastPath] = res.locals.data
    }
  }
  res.jsonp(result)
}

server.use(jsonServer.rewriter({
  '/api/': '/'
}))

server.use(router)
server.listen(3000, () => {
  console.log('JSON Server is running')
})

Support nested key path for result, fix bugs


const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
const gifFolder = './public/gifs';

app.use(express.static('public'));

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname + '/public/index.html'));
});

fs.readdir(gifFolder, (err, files) => {
  fs.writeFileSync('public/fileNames.json', JSON.stringify(files));
});

app.listen(port, (err) => {
  if (err) console.log(`Error: ${err}`);

  console.log(`Server is running on port ${port}`);
});

const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
const gifFolder = './public/gifs';

app.use(express.static('public'));

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname + '/public/index.html'));
});

fs.readdir(gifFolder, (err, files) => {
  if (err) {
    console.log("Can't find gif directory. Make sure you have a folder of gifs in this location => '/public/gifs' ... Then restart server.");
  }

  fs.writeFileSync('public/fileNames.json', JSON.stringify(files));
});

app.listen(port, (err) => {
  if (err) console.log(`Error: ${err}`);

  console.log(`Server is running on port ${port}`);
});

Add console.log error message in case gif folder is not found


class BrowseController {
  constructor($scope, $uibModalInstance, ImageFactory, text) {
    var imageCount = 60;

    $scope.text = text;
    $scope.photos = [];
    $scope.canShowPhotos = false;

    var getImages = function(query) {

      ImageFactory.getList(query, imageCount).then(function(result) {
        var row = 0;
        var photos = [];
        var data = result.data;
        while (data.length) {
          photos[row] = data.splice(0, 12);
          row += 1;
        }
        $scope.photos = photos;
        $scope.canShowPhotos = true;
      });
    };

    $scope.select = function(photo) {
      $uibModalInstance.close(photo);
    };

    $scope.cancel = function() {
      $uibModalInstance.dismiss('cancel');
    };

    $scope.search = function() {
      $scope.canShowPhotos = false;
      getImages($scope.text);
    };

    $scope.search();
  }
}

export default BrowseController;

class BrowseController {
  constructor($scope, $uibModalInstance, ImageFactory, text) {
    'ngInject';

    var imageCount = 60;

    $scope.text = text;
    $scope.photos = [];
    $scope.canShowPhotos = false;

    var getImages = function(query) {

      ImageFactory.getList(query, imageCount).then(function(result) {
        var row = 0;
        var photos = [];
        var data = result.data;
        while (data.length) {
          photos[row] = data.splice(0, 12);
          row += 1;
        }
        $scope.photos = photos;
        $scope.canShowPhotos = true;
      });
    };

    $scope.select = function(photo) {
      $uibModalInstance.close(photo);
    };

    $scope.cancel = function() {
      $uibModalInstance.dismiss('cancel');
    };

    $scope.search = function() {
      $scope.canShowPhotos = false;
      getImages($scope.text);
    };

    $scope.search();
  }
}

export default BrowseController;

Fix modal cannot be opened after minification


const { get } = require('lodash')

// eslint-disable-next-line camelcase
const transformAccountManager = ({ one_list_group_global_account_manager }) => ({
  name: get(one_list_group_global_account_manager, 'name'),
  email: get(one_list_group_global_account_manager, 'contact_email'),
  team: get(one_list_group_global_account_manager, '["dit_team"].name'),
})

module.exports = transformAccountManager

const { get } = require('lodash')

// eslint-disable-next-line camelcase
const transformAccountManager = ({ one_list_group_global_account_manager }) => ({
  name: get(one_list_group_global_account_manager, 'name'),
  email: get(one_list_group_global_account_manager, 'contact_email'),
  team: get(one_list_group_global_account_manager, 'dit_team.name'),
})

module.exports = transformAccountManager

Update usage of lodash get


import CitableContentManager from './CitableContentManager'

export default class SupplementaryManager extends CitableContentManager {
  constructor (documentSession, labelGenerator) {
    super(documentSession, 'file', ['supplementary-file'], labelGenerator)
    this._updateLabels('initial')
  }
}

import CitableContentManager from './CitableContentManager'

export default class SupplementaryManager extends CitableContentManager {
  constructor (documentSession, labelGenerator) {
    super(documentSession, 'file', ['supplementary-file'], labelGenerator)
    this._updateLabels('initial')
  }

  // ATTENTION: for now we consider only supplementary files that are direct children of the body
  // TODO: we need to specify how this should be extended to supplementary files in figure panels
  getCitables () {
    return this._getContentElement().resolve('content').filter(child => child.type === 'supplementary-file')
  }
}

Manage SupplementaryFile labels only in body.


import lazyRequire from '@sanity/util/lib/lazyRequire'

const helpText = `
Options
  N/A

Examples
  sanity graphql list
`

export default {
  name: 'list',
  signature: '',
  group: 'graphql',
  description: 'Lists all the GraphQL endpoints deployed for this project',
  action: lazyRequire(require.resolve('../../actions/graphql/listApisAction')),
  helpText
}

import lazyRequire from '@sanity/util/lib/lazyRequire'

const helpText = `
Examples
  sanity graphql list
`

export default {
  name: 'list',
  signature: '',
  group: 'graphql',
  description: 'Lists all the GraphQL endpoints deployed for this project',
  action: lazyRequire(require.resolve('../../actions/graphql/listApisAction')),
  helpText
}

Align help text of graphql list command with other optionless commands


var vm = require('vm');

exports.javascript = function(element, code, filename) {
  var document = element.ownerDocument,
      window = document.parentWindow;

  if (window) {
    var ctx = window.__scriptContext;
    if (!ctx) {
      window.__scriptContext = ctx = vm.createContext({});
      ctx.__proto__ = window;
    }
    var tracelimitbak = Error.stackTraceLimit;
    Error.stackTraceLimit = 100;
    try {
      vm.Script.runInContext(code, ctx, filename);
    }
    catch(e) {
      element.trigger(
        'error', 'Running ' + filename + ' failed.',
        {error: e, filename: filename}
      );
    }
    Error.stackTraceLimit = tracelimitbak;
  }
};

var vm = require('vm');

exports.javascript = function(element, code, filename) {
  var document = element.ownerDocument,
      window = document.parentWindow;

  if (window) {
    var tracelimitbak = Error.stackTraceLimit;
    Error.stackTraceLimit = 100;
    try {
      vm.Script.runInNewContext(code, window, filename);
    }
    catch(e) {
      element.trigger(
        'error', 'Running ' + filename + ' failed.',
        {error: e, filename: filename}
      );
    }
    Error.stackTraceLimit = tracelimitbak;
  }
};

Patch to allow Ender packages to work with jsdom.env


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

function authenticate(username, password) {
    console.log('Authenticating as ' + username);
    return true;
}
container.sasl_server_mechanisms.enable_plain(authenticate);
var server = container.listen({'port':process.env.LISTENPORT, 'require_sasl': true});
console.log('Listening on port ' + process.env.LISTENPORT);
container.on('connection_open', function (context) {
    var authenticatedIdentity = { 'sub' : context.connection.sasl_transport.username };
    var properties = context.connection.local.open.properties || {};
    properties["authenticated-identity"] = authenticatedIdentity;
    context.connection.local.open.properties = properties;
    context.connection.close();
});
container.on('disconnected', function (context) {
});

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

function authenticate(username, password) {
    console.log('Authenticating as ' + username);
    return true;
}
container.sasl_server_mechanisms.enable_plain(authenticate);
container.sasl_server_mechanisms.enable_anonymous();
var server = container.listen({'port':process.env.LISTENPORT, 'require_sasl': true});
console.log('Listening on port ' + process.env.LISTENPORT);
container.on('connection_open', function (context) {
    var authenticatedIdentity = { 'sub' : context.connection.sasl_transport.username || 'anonymous' };
    var properties = context.connection.local.open.properties || {};
    properties["authenticated-identity"] = authenticatedIdentity;
    context.connection.local.open.properties = properties;
    context.connection.close();
});
container.on('disconnected', function (context) {
});

Allow non-authservice to offer anonymous


var AppDispatcher = require('../dispatcher/AppDispatcher');
var ProjectConstants = require('../constants/ProjectConstants');
var defaults = require('../config').defaults;

var ProjectActions = {
  create: function() {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_CREATED,
      project: defaults
    });
  },

  updateSource: function(projectKey, language, source) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_SOURCE_EDITED,
      projectKey: projectKey,
      language: language,
      source: source
    });
  },

  toggleLibrary: function(projectKey, libraryKey) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_LIBRARY_TOGGLED,
      projectKey: projectKey,
      libraryKey: libraryKey
    });
  },

  loadFromStorage: function(project) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_LOADED_FROM_STORAGE,
      projectKey: project.projectKey,
      project: project
    });
  }
};

module.exports = ProjectActions;

var _ = require('lodash');

var AppDispatcher = require('../dispatcher/AppDispatcher');
var ProjectConstants = require('../constants/ProjectConstants');
var defaults = require('../config').defaults;

var ProjectActions = {
  create: function() {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_CREATED,
      project: _.cloneDeep(defaults)
    });
  },

  updateSource: function(projectKey, language, source) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_SOURCE_EDITED,
      projectKey: projectKey,
      language: language,
      source: source
    });
  },

  toggleLibrary: function(projectKey, libraryKey) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_LIBRARY_TOGGLED,
      projectKey: projectKey,
      libraryKey: libraryKey
    });
  },

  loadFromStorage: function(project) {
    AppDispatcher.dispatch({
      actionType: ProjectConstants.PROJECT_LOADED_FROM_STORAGE,
      projectKey: project.projectKey,
      project: project
    });
  }
};

module.exports = ProjectActions;

Clone defaults when creating new project


import React from 'react';

export default class List extends React.Component {
    static propTypes = {
        items: React.PropTypes.array.isRequired,
    }

    render() {
        const items = this.props.items;
        const and = 'og';
        if (!items || items.length === 0) {
            return ;
        }
        if (items.length === 1) {
            return {items[0]};
        }
        if (items.length === 2) {
            return {items[0]} {and} {items[1]};
        }
        return {items.slice(0, -1).join(', ')} {and} {items[-1]};
    }
}

import React from 'react';

export default class List extends React.Component {
    static propTypes = {
        items: React.PropTypes.array.isRequired,
    }

    render() {
        const items = this.props.items;
        const and = 'og';
        if (!items || items.length === 0) {
            return ;
        }
        if (items.length === 1) {
            return {items[0]};
        }
        if (items.length === 2) {
            return {items[0]} {and} {items[1]};
        }
        return {items.slice(0, -1).join(', ')} {and} {items[items.length - 1]};
    }
}

Fix show last element from list


/* eslint-env node */
module.exports = {
  "test_page": "tests/index.html?hidepassed",
  "disable_watching": true,
  "launch_in_ci": [
    "Chrome"
  ],
  "launch_in_dev": [
    "PhantomJS",
    "Chrome"
  ]
};

/* eslint-env node */
module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: [
    'Chrome'
  ],
  launch_in_dev: [
    'Chrome'
  ],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        '--disable-gpu',
        '--headless',
        '--remote-debugging-port=0',
        '--window-size=1440,900'
      ]
    }
  }
};

Drop PhantomJS and use Chrome for ember try tests.


'use strict';

/* eslint-disable no-var, prefer-arrow-callback */
var HttpBackend = require('httpbackend');

var CustomWorld = (function () {
    var chai = require('chai');
    var chaiAsPromised = require('chai-as-promised');
    var CustomWorld = function CustomWorld () {
        global.By = global.protractor.By;
        chai.use(chaiAsPromised);
        global.expect = chai.expect;
        global.Promise = require('bluebird');
    };

    return CustomWorld;
})();

module.exports = function () {
    this.World = function (callback) {
        var w = new CustomWorld();
        return callback(w);
    };

    /* eslint-disable new-cap */
    this.Before(function (callback) {
    /* eslint-enable new-cap */
        global.httpBackend = new HttpBackend(global.browser);
        callback();
    });

    /* eslint-disable new-cap */
    this.After(function (callback) {
    /* eslint-enable new-cap */
        global.httpBackend.clear();
        callback();
    });

    return this.World;
};

'use strict';

/* eslint-disable no-var, prefer-arrow-callback */
var HttpBackend = require('httpbackend');

var CustomWorld = (function () {
    var chai = require('chai');
    var chaiAsPromised = require('chai-as-promised');
    var CustomWorld = function CustomWorld () {
        global.By = global.protractor.By;
        chai.use(chaiAsPromised);
        global.expect = chai.expect;
        global.Promise = require('bluebird');
    };

    return CustomWorld;
})();

module.exports = function () {
    this.World = function (callback) {
        var w = new CustomWorld();
        return callback(w);
    };

    /* eslint-disable new-cap */
    this.Before(function (callback) {
    /* eslint-enable new-cap */
        global.httpBackend = new HttpBackend(global.browser);
        callback();
    });

    /* eslint-disable new-cap */
    this.After(function (callback) {
    /* eslint-enable new-cap */
        global.httpBackend.clear();
		global.browser.manage.deleteAllCookies();
		global.browser.executeScript('window.sessionStorage.clear();');
		global.browser.executeScript('window.localStorage.clear();');
        callback();
    });

    return this.World;
};

Add after step to clear cookies and local/session storage.


import * as path from 'path';
import { transform } from 'babel-core';
import formatError from '../formatError';

const getError = (fn) => {
    try {
        fn();
    } catch (e) {
        return e;
    }
};

const file = path.join(process.cwd(), 'foo/bar.js');

it('formats Babel errors', () => {
    const error = getError(() => transform('cons invalid = true;', { filename: file }));

    expect(formatError(error)).toMatchSnapshot();
});

it('returns other errors as strings', () => {
    const error = getError(() => { throw new Error('other error type'); });

    expect(formatError(error)).toMatchSnapshot();
});

import * as path from 'path';
import { transform } from 'babel-core';
import formatError from '../formatError';

const getError = (fn) => {
    try {
        fn();
    } catch (e) {
        return e;
    }
};

const file = path.join(process.cwd(), 'foo/bar.js');

it('formats Babel errors', () => {
    const error = getError(() => transform('cons invalid = true;', { filename: file }));

    console.log(error.message);

    expect(formatError(error)).toMatchSnapshot();
});

it('returns other errors as strings', () => {
    const error = getError(() => { throw new Error('other error type'); });

    expect(formatError(error)).toMatchSnapshot();
});

Add logging to see what's up with appveyor.


// @flow

import React from 'react'
import {Column, Row} from '../../components/layout'
import {ListRow, Title} from '../../components/list'
import type {ThinJobType} from './types'

export class JobRow extends React.PureComponent {
  props: {
    onPress: ThinJobType => any,
    job: ThinJobType,
  }

  _onPress = () => this.props.onPress(this.props.job)

  render() {
    const {job} = this.props

    return (
      <ListRow arrowPosition="top" onPress={this._onPress}>
        <Row alignItems="center">
          <Column flex={1}>
            <Title lines={1}>{job.title}</Title>
          </Column>
        </Row>
      </ListRow>
    )
  }
}

// @flow

import React from 'react'
import {Column, Row} from '../../components/layout'
import {ListRow, Title} from '../../components/list'
import type {ThinJobType} from './types'

export class JobRow extends React.PureComponent {
  props: {
    onPress: ThinJobType => any,
    job: ThinJobType,
  }

  _onPress = () => this.props.onPress(this.props.job)

  render() {
    const {job} = this.props

    return (
      <ListRow arrowPosition="center" onPress={this._onPress}>
        <Row minHeight={36} alignItems="center">
          <Column flex={1}>
            <Title lines={1}>{job.title}</Title>
          </Column>
        </Row>
      </ListRow>
    )
  }
}

Increase height of job row


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

import {join, relative, sep} from 'path'
import {getProjectDir} from '../../../paths'

const PROJECT_DIR = getProjectDir()

function fileName(file) {
  const RELATIVE = relative(PROJECT_DIR, file)
  let nodes = RELATIVE.split(sep)

  if (nodes[0] === 'src')
    nodes.shift()

  return `${nodes.join(sep)}?[sha512:hash:base64:8]`
}

export default function () {
  return {
    exclude: /\.(css|jsx?|mjs)$/,
    use: [{
      loader: 'url-loader',
      options: {
        fallback: 'file-loader',
        limit: 1024,
        name: fileName,
      },
    }],
  }
}

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

import {join, relative, sep} from 'path'
import {getProjectDir} from '../../../paths'

const PROJECT_DIR = getProjectDir()

function fileName(file) {
  const RELATIVE = relative(PROJECT_DIR, file)
  let nodes = RELATIVE.split(sep)

  if (nodes[0] === 'src')
    nodes.shift()

  return `${nodes.join('/')}?[sha512:hash:base64:8]`
}

export default function () {
  return {
    exclude: /\.(css|jsx?|mjs)$/,
    use: [{
      loader: 'url-loader',
      options: {
        fallback: 'file-loader',
        limit: 1024,
        name: fileName,
      },
    }],
  }
}

Use forward slash for file paths


import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackMixpanelEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

import React from 'react';
import { connect } from 'react-redux';

import { trackAnalyticsEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackAnalyticsEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

Fix in-flight collision of two related PRs


describe('<%= name %>', function () {

});

import version from '../src/version';

describe('<%= name %>', function () {
  it('version', function () {
    expect(version).to.be.a('string');
  });
});

Add initial test for version string.