import React, { Component } from 'react';
import FormInput from '../formInput/formInput';

class FormEmail extends Component {
  render() {
    const validEmailRegex = /(^[^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})/;
    return (
      <FormInput
        {...this.props}
        validationRegex={validEmailRegex}
        validationErrorMessage="Must be a valid email"
        ref={(child) => { this.inputRef = child; }}
      />
    );
  }
}

export default FormEmail;

Add blank line for readability


Checklist.MainView = Ember.View.extend({
  tagName: 'section',
  templateName: 'main_view',

  classNames: ['main'],

  landingColorboxConfig: {
    inline: true,
    transition: 'fade',
    close: 'x',
    opacity: 0.8,
    fixed: true,
    maxWidth: 675,
    innerWidth: 675,
    open: true,
    className: 'landing',
    onClosed: function(event) {
      $.cookie("Checklist.CONFIG.visited", true);
    }
  },

  didInsertElement: function() {
    var target = document.getElementById('loading');
    var spinner = new Spinner(Checklist.CONFIG.spinner).spin(target);

    if (!$.cookie("Checklist.CONFIG.visited")) {
      $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
    }
  },

  closeLandingBox: function(event) {
    $.colorbox.close();
  },

  doLocaleEN: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'en'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  },

  doLocaleES: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'es'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  },

  doLocaleFR: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'fr'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  }

});

Checklist.MainView = Ember.View.extend({
  tagName: 'section',
  templateName: 'main_view',

  classNames: ['main'],

  landingColorboxConfig: {
    inline: true,
    transition: 'fade',
    close: 'x',
    opacity: 0.8,
    fixed: true,
    maxWidth: 675,
    innerWidth: 675,
    open: true,
    className: 'landing',
    onClosed: function(event) {
      $.cookie("Checklist.CONFIG.visited", true);
    }
  },

  didInsertElement: function() {
    var target = document.getElementById('loading');
    var spinner = new Spinner(Checklist.CONFIG.spinner).spin(target);

    var popup = location.href.split(/(&|\?)popup=/).pop()
    if(popup !== undefined && popup == "0") {
      $.cookie("Checklist.CONFIG.visited", true);
    }

    if (!$.cookie("Checklist.CONFIG.visited")) {
      $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
    }
  },

  closeLandingBox: function(event) {
    $.colorbox.close();
  },

  doLocaleEN: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'en'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  },

  doLocaleES: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'es'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  },

  doLocaleFR: function(event) {
    Checklist.router.transitionTo('home.index', {locale: 'fr'});
    $('#landing-btn').colorbox(this.get('landingColorboxConfig'));
  }

});

Hide initial popup if popup param is 0


/*!
 * Wef
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

/**
 * wef module
 */
(function(global) {
    var wef = function() {
        return new wef.prototype.init();
    };
    wef.prototype = {
        constructor:wef,
        version: "0.0.1",
        init: function() {
            return this;
        }
    };
    wef.fn = wef.prototype;
    wef.prototype.init.prototype = wef.prototype;

    wef.fn.extend = function (receiver, giver) {
        var tmp = receiver;
        //both must be objects
        if (typeof receiver === "object" && typeof giver === "object") {
            if (tmp == null) {
                tmp = {};
            }
            if (receiver == null) {
                return tmp;
            }
            for (var property in giver) {
                tmp[property] = giver[property];
            }
            return tmp
        }
        wef.f.error("InvalidArgumentException: incorrect argument type");
        return null;
    };

    wef.fn.isFunction = function (obj) {
        return typeof obj == "function";
    };

    wef.fn.isString = function (obj) {
        return typeof obj == "string";
    };

    wef.fn.error = function (message) {
        throw new Error(message);
    };

    //registering global variable
    if (global.wef) {
        throw new Error("wef has already been defined");
    } else {
        global.wef = wef();
    }
})(window);

/*!
 * Wef
 * Copyright (c) 2011 Pablo Escalada
 * MIT Licensed
 */

/**
 * wef module
 */
(function(global) {
    var wef = function() {
        return new wef.prototype.init();
    };
    wef.prototype = {
        constructor:wef,
        version: "0.0.1",
        init: function() {
            return this;
        }
    };
    wef.fn = wef.prototype;
    wef.prototype.init.prototype = wef.prototype;

    wef.fn.extend = function (receiver, giver) {
        var tmp = receiver;
        //both must be objects
        if (typeof receiver === "object" && typeof giver === "object") {
            if (tmp === null) {
                tmp = {};
            }
            if (receiver === null) {
                return tmp;
            }
            for (var property in giver) {
                tmp[property] = giver[property];
            }
            return tmp
        }
        wef.f.error("InvalidArgumentException: incorrect argument type");
        return null;
    };

    wef.fn.isFunction = function (obj) {
        return typeof obj == "function";
    };

    wef.fn.isString = function (obj) {
        return typeof obj == "string";
    };

    wef.fn.error = function (message) {
        throw new Error(message);
    };

    //registering global variable
    if (global.wef) {
        throw new Error("wef has already been defined");
    } else {
        global.wef = wef();
    }
})(window);

Fix jslint warning in null checks


/* LIB */

const radial = require('../../radial');
const authenticate = require('./authenticate');

/* MODULES */

const xmlConvert = require('xml-js');

/* CONSTRUCTOR */

(function () {

  var RiskOrderStatus = {};

  /* PRIVATE VARIABLES */

  /* PUBLIC VARIABLES */

  /* PUBLIC FUNCTIONS */

  RiskOrderStatus.parse = function (req, fn) {
    authenticate('riskOrderStatus', req, function (err) {
      if (err) {
        return fn({
          status: 401,
          type: 'Authentication Failed',
          message: err.message
        });
      }

      var body = req.body;
      var reply = body.RiskAssessmentReplyList;

      return fn(null, reply);
    });
  };

  /* NPM EXPORT */

  if (typeof module === 'object' && module.exports) {
    module.exports = RiskOrderStatus.parse;
  } else {
    throw new Error('This module only works with NPM in NodeJS environments.');
  }

}());

/* LIB */

const radial = require('../../radial');
const authenticate = require('./authenticate');

/* MODULES */

const xmlConvert = require('xml-js');

/* CONSTRUCTOR */

(function () {

  var RiskOrderStatus = {};

  /* PRIVATE VARIABLES */

  /* PUBLIC VARIABLES */

  /* PUBLIC FUNCTIONS */

  RiskOrderStatus.parse = function (req, fn) {
    authenticate('riskOrderStatus', req, function (err) {
      if (err) {
        return fn({
          status: 401,
          type: 'Authentication Failed',
          message: err.message
        });
      }

      var body = req.body;
      var reply = body.RiskAssessmentReplyList || body.riskassessmentreplylist;
      var list = reply.RiskAssessmentReply || reply.riskassessmentreply;

      var replyList = [];
      list.forEach(function (item) {
        replyList.push({
          orderId: item.OrderId || item.orderid,
          responseCode: item.ResponseCode || item.responsecode,
          storeId: item.StoreId || item.storeid,
          reasonCode: item.ReasonCode || item.reasoncode,
          reasonCodeDescription: item.ReasonCodeDescription || item.reasoncodedescription
        });
      });

      return fn(null, replyList);
    });
  };

  /* NPM EXPORT */

  if (typeof module === 'object' && module.exports) {
    module.exports = RiskOrderStatus.parse;
  } else {
    throw new Error('This module only works with NPM in NodeJS environments.');
  }

}());

Improve reply list response for risk status webhook.


angular.module('proxtop', ['ngMaterial', 'ngSanitize', 'ui.router', 'angular-progress-arc', 'pascalprecht.translate', 'debounce'])
    .config(['$stateProvider', '$urlRouterProvider', '$translateProvider', '$mdIconProvider', '$logProvider', function($stateProvider, $urlRouterProvider, $translateProvider, $mdIconProvider, $logProvider) {
        $urlRouterProvider.otherwise('/');
        $translateProvider.useStaticFilesLoader({
            prefix: 'locale/locale-',
            suffix: '.json'
        });

        $translateProvider.preferredLanguage('de');
        $translateProvider.useSanitizeValueStrategy('escape');

        $mdIconProvider.defaultIconSet('../bower_components/font-awesome/fonts/fontawesome-webfont.svg');

        $logProvider.debugEnabled(false);
    }]);

angular.module('proxtop', ['ngMaterial', 'ngSanitize', 'ui.router', 'angular-progress-arc', 'pascalprecht.translate', 'debounce'])
    .config(['$stateProvider', '$urlRouterProvider', '$translateProvider', '$mdIconProvider', '$logProvider', '$compileProvider', function($stateProvider, $urlRouterProvider, $translateProvider, $mdIconProvider, $logProvider, $compileProvider) {
        $urlRouterProvider.otherwise('/');
        $translateProvider.useStaticFilesLoader({
            prefix: 'locale/locale-',
            suffix: '.json'
        });

        $translateProvider.preferredLanguage('de');
        $translateProvider.useSanitizeValueStrategy('escape');

        $mdIconProvider.defaultIconSet('../bower_components/font-awesome/fonts/fontawesome-webfont.svg');

        $logProvider.debugEnabled(false);

        $compileProvider.debugInfoEnabled(false);
    }]);

Disable debug info for release.


const db = require('../db.js');
const Sequelize = require('sequelize');

const getUserIdsGivenSelfIdAndRelationshipType = (selfId, relationshipType) => {
  const queryStr = `SELECT users.id FROM users
                      INNER JOIN relationships
                        ON users.id = relationships.user1Id OR users.id = relationships.user2Id
                      WHERE users.id = ${selfId} AND relationships.type = ${relationshipType}
                    `;
  return db.query(queryStr, { type: Sequelize.QueryTypes.SELECT });
};

exports.getConnections = (selfId) => getUserIdsGivenSelfIdAndRelationshipType(selfId, 'connection');

exports.getRejects = (selfId) => getUserIdsGivenSelfIdAndRelationshipType(selfId, 'reject');

exports.getRequests = (selfId) => {
  const queryStr = `SELECT users.id FROM users
                      INNER JOIN relationships
                        ON users.id = relationships.user2Id
                      WHERE users.id = ${selfId} AND relationships.type = 'request'
                    `;
  return db.query(queryStr, { type: Sequelize.QueryTypes.SELECT });
};

const db = require('../db.js');
const Sequelize = require('sequelize');

const getUserIdsGivenSelfIdAndRelationshipType = (selfId, relationshipType) => {
  const queryStr = `SELECT users.id FROM users
                      INNER JOIN relationships
                        ON users.id = relationships.user1Id OR users.id = relationships.user2Id
                      WHERE users.id = ${selfId} AND relationships.type = ${relationshipType}
                    `;
  return db.query(queryStr, { type: Sequelize.QueryTypes.SELECT });
};

exports.getConnections = (selfId) => getUserIdsGivenSelfIdAndRelationshipType(selfId, 'connection');

exports.getRejects = (selfId) => getUserIdsGivenSelfIdAndRelationshipType(selfId, 'reject');

exports.getRequests = (selfId) => {
  const queryStr = `SELECT relationships.user1Id FROM users
                      INNER JOIN relationships
                        ON users.id = relationships.user2Id
                      WHERE users.id = ${selfId} AND relationships.type = 'request'
                    `;
  return db.query(queryStr, { type: Sequelize.QueryTypes.SELECT });
};

Return sender ids instead of recipient id


const applicationConfig = {
  host: 'localhost',
  port: '3000',
  privateUploadDirectory: 'uploads/photos',

  getFullHostname() {
    if (this.port !== '443') {
      return `${this.host}:${this.port}`;
    }
    return this.host;
  }
};

if (process.env.NODE_ENV === 'production') {
  applicationConfig.host = 'https://sheltered-plateau-86472.herokuapp.com/';
  applicationConfig.port = '443';
}

export default applicationConfig;

const applicationConfig = {
  host: 'localhost',
  port: '3000',
  privateUploadDirectory: 'uploads/photos',

  getFullHostname() {
    if (this.port !== '443') {
      return `${this.host}:${this.port}`;
    }
    return this.host;
  }
};

if (process.env.NODE_ENV === 'production') {
  applicationConfig.host = 'https://sheltered-plateau-86472.herokuapp.com/';
  applicationConfig.port = process.env.PORT;
}

export default applicationConfig;

Set port to Heroku variable


import express from 'express';

import * as userController from '../../controllers/users';
import * as recipeController from '../../controllers/recipes';
import * as favoriteController from '../../controllers/favorites';

import * as auth from '../../middleware/auth';

const user = express.Router();

user.post('/signup', userController.signUp);
user.post('/signin', userController.signIn);

user.get('/myRecipes', auth.default);
user.get('/myRecipes', recipeController.getUserRecipes);

user.post('/:userId/recipes/:recipeId', favoriteController.addToFavorite);
user.delete('/:userId/recipes/:recipeId',
  favoriteController.removeFromFavorites);
user.get('/:userId/recipes', favoriteController.getFavRecipes);

export default user;

import express from 'express';

import * as userController from '../../controllers/users';
import * as recipeController from '../../controllers/recipes';
import * as favoriteController from '../../controllers/favorites';

import * as auth from '../../middleware/auth';

const user = express.Router();

user.post('/signup', userController.signUp);
user.post('/signin', userController.signIn);

user.use('*', auth.default);
user.get('/myRecipes', recipeController.getUserRecipes);

user.post('/:userId/recipes/:recipeId', favoriteController.addToFavorite);
user.delete('/:userId/recipes/:recipeId',
  favoriteController.removeFromFavorites);
user.get('/:userId/recipes', favoriteController.getFavRecipes);

export default user;

Add auth middleware after signup/signin user routes


"use strict";

module.exports = React.createClass({displayName: 'CommentView',

    render: function() {
        var comment = this.props.comment;
        if (!comment) {
            return (

                    No comment.

            );
        }

        var htmlBody = comment.getHtmlBody();

        if (/<\/blockquote>$/.test(htmlBody)) {
            // TODO ends in a block quote, hide it behind a toggle button.
        }

        return (

                    {comment.getUser().getUserLinkJsx()} commented {
                        comment.getTimeAgo()
                    } ago

        );
    }
});

"use strict";

module.exports = React.createClass({displayName: 'CommentView',

    render: function() {
        var comment = this.props.comment;
        if (!comment) {
            return (

                    No comment.

            );
        }

        var htmlBody = comment.getHtmlBody();

        if (/<\/blockquote>$/.test(htmlBody)) {
            // TODO ends in a block quote, hide it behind a toggle button.
        }

        return (

                    {comment.getUser().getUserLinkJsx()} commented {
                        comment.getTimeAgo()
                    } ago (Source)

        );
    }
});

Add source links to all comments


var get = require('./get'),
    getIn = require('./getIn'),
    assoc = require('./assoc'),
    dissoc = require('./dissoc'),
    assocIn = require('./assocIn'),
    dissocIn = require('./dissocIn'),
    deepMerge = require('./deepMerge'),
    update = require('./update'),
    updateIn = require('./updateIn'),
    merge = require('./merge'),
    util = require('./util');

function multiGet(obj, path, orValue) {
  if (typeof path === 'string' || typeof path === 'number') return get(obj, path, orValue);
  return getIn(obj, path, orValue);
}

function multiAssoc(obj, path, value) {
  if (util.isArray(path)) return assocIn(obj, path, value);
  return assoc(obj, path, value);
}

function multiDissoc(obj, path) {
  if (util.isArray(path)) return dissocIn(obj, path);
  return dissoc(obj, path);
}

function multiUpdate(obj, path, fn) {
  if (typeof path === 'string' || typeof path === 'number') return update(obj, path, fn);
  return updateIn(obj, path, fn);
}

module.exports = {
  get: multiGet,
  assoc: multiAssoc,
  dissoc: multiDissoc,
  update: multiUpdate,
  merge: merge,
  deepMerge: deepMerge
};

var get = require('./get'),
    getIn = require('./getIn'),
    assoc = require('./assoc'),
    dissoc = require('./dissoc'),
    assocIn = require('./assocIn'),
    dissocIn = require('./dissocIn'),
    deepMerge = require('./deepMerge'),
    update = require('./update'),
    updateIn = require('./updateIn'),
    merge = require('./merge'),
    util = require('./util');

function multiGet(obj, path, orValue) {
  if (util.isArray(path)) return getIn(obj, path, orValue);
  return get(obj, path, orValue);
}

function multiAssoc(obj, path, value) {
  if (util.isArray(path)) return assocIn(obj, path, value);
  return assoc(obj, path, value);
}

function multiDissoc(obj, path) {
  if (util.isArray(path)) return dissocIn(obj, path);
  return dissoc(obj, path);
}

function multiUpdate(obj, path, fn) {
  if (util.isArray(path)) return updateIn(obj, path, fn);
  return update(obj, path, fn);
}

module.exports = {
  get: multiGet,
  assoc: multiAssoc,
  dissoc: multiDissoc,
  update: multiUpdate,
  merge: merge,
  deepMerge: deepMerge
};

Bring multis inline with each other


import React, { Component, PropTypes } from 'react'
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'

import { planTrip, profileTrip } from '../../actions/api'

class PlanTripButton extends Component {
  static propTypes = {
    routingType: PropTypes.string,
    text: PropTypes.string,
    onClick: PropTypes.func,
    planTrip: PropTypes.func,
    profileTrip: PropTypes.func
  }

  _onClick = () => {
    switch (this.props.routingType) {
      case 'ITINERARY':
        this.props.planTrip()
        break
      case 'PROFILE':
        this.props.profileTrip()
        break
    }
  }

  render () {
    return (
      <Button
        className='plan-trip-button'
        onClick={this._onClick || this.props.onClick}
      >{this.props.text || 'Plan Trip'}</Button>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    routingType: state.otp.currentQuery.routingType
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    planTrip: () => { dispatch(planTrip()) },
    profileTrip: () => { dispatch(profileTrip()) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(PlanTripButton)

import React, { Component, PropTypes } from 'react'
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'

import { routingQuery } from '../../actions/api'

class PlanTripButton extends Component {
  static propTypes = {
    routingType: PropTypes.string,
    text: PropTypes.string,
    onClick: PropTypes.func,
    planTrip: PropTypes.func,
    profileTrip: PropTypes.func
  }

  _onClick = () => {
    this.props.routingQuery()
  }

  render () {
    return (
      <Button
        className='plan-trip-button'
        onClick={this._onClick || this.props.onClick}
      >{this.props.text || 'Plan Trip'}</Button>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {}
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    routingQuery: () => { dispatch(routingQuery()) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(PlanTripButton)

Update PlanTripButton to work with recent API refactor


module.exports = [
  ['console.log();', 'красноглазое.чмо() нах'],
  /**
   * Вхождения внутри слова
   *
   * https://github.com/samgozman/YoptaScript/issues/30
   */
  ['checking', 'checking'],
  ['form', 'form'],
  ['settings', 'settings'],
  ['undefined', 'undefined'],
  ['upload', 'заебенить'],
];

module.exports = [
  ['console.log();', 'красноглазое.чмо() нах'],
  /**
   * Вхождения внутри слова
   *
   * https://github.com/samgozman/YoptaScript/issues/30
   */
  ['checking', 'checking'],
  ['form', 'form'],
  ['settings', 'settings'],
  ['undefined', 'undefined'],
  ['upload', 'заебенить'],

  ['!x === y || z && a <= b++ ', 'чобляx эквалиосука y иличо z ичо a поц bплюсуюНа '],
];

Add operator compilation test case


/* eslint-disable camelcase */
import styled from "styled-components";
import { colors } from "constants/theme";

const PropertyPane = styled.div`
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: ${colors.white};
  padding: 1em;
  /* stylelint-disable */
  box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px;
  /* stylelint-enable */
`;

export default PropertyPane;

/* eslint-disable camelcase */
import styled from "styled-components";
import { colors } from "constants/theme";

const PropertyPane = styled.div`
  height: 100%;
  display: flex;
  flex-direction: column;
  background-color: ${colors.white};
  padding: 1em;
  box-shadow: rgba(0, 0, 0, 0.14) 0 4px 5px 0;
`;

export default PropertyPane;

Remove stylelint ignore comment which was added by mistake.


'use strict';

var assert = require('assert');
var sinon = require('sinon');

var Api = require('../../lib/endpoints/api');
var Request = require('../../lib/request');

describe('endpoints/api', function () {
    describe('listFunctions', function () {
        it('should set the request URL', function () {
            var request = new Request();
            var api;
            var stub;

            stub = sinon.stub(request, 'get', function (url) {
                assert.strictEqual(url, '/api/listfunctions');
            });

            api = new Api(request);
            api.listFunctions();

            assert.ok(stub.called);
        });
    });

    describe('maintenance', function () {
        it('should set the request URL', function () {
            var request = new Request();
            var api;
            var stub;

            stub = sinon.stub(request, 'get', function (url) {
                assert.strictEqual(url, '/api/maintenance');
            });

            api = new Api(request);
            api.maintenance();

            assert.ok(stub.called);
        });
    });

    describe('serviceInfo', function () {
        it('should set the request URL', function () {
            var request = new Request();
            var api;
            var stub;

            stub = sinon.stub(request, 'get', function (url) {
                assert.strictEqual(url, '/api/serviceinfo');
            });

            api = new Api(request);
            api.serviceInfo();

            assert.ok(stub.called);
        });
    });
});

'use strict';

const assert = require('assert');
const sinon = require('sinon');

const Api = require('../../lib/endpoints/api');
const Request = require('../../lib/request');

describe('endpoints/api', () => {
    describe('listFunctions', () => {
        it('should set the request URL', () => {
            const request = new Request();
            const api = new Api(request);
            const stub = sinon.stub(request, 'get', (url) => {
                assert.strictEqual(url, '/api/listfunctions');
            });

            api.listFunctions();

            assert.ok(stub.called);
        });
    });

    describe('maintenance', () => {
        it('should set the request URL', () => {
            const request = new Request();
            const api = new Api(request);
            const stub = sinon.stub(request, 'get', (url) => {
                assert.strictEqual(url, '/api/maintenance');
            });

            api.maintenance();

            assert.ok(stub.called);
        });
    });

    describe('serviceInfo', () => {
        it('should set the request URL', () => {
            const request = new Request();
            const api = new Api(request);
            const stub = sinon.stub(request, 'get', (url) => {
                assert.strictEqual(url, '/api/serviceinfo');
            });

            api.serviceInfo();

            assert.ok(stub.called);
        });
    });
});

Rewrite Api tests to ES2015


'use strict';

let lastEval = undefined;
let ringBuffer = [];

function createAPI() {
  const api = {
    $_: lastEval,
  };

  [ 0, 1, 2, 3, 4 ].forEach(idx =>
    api[`$${idx}`] = ringBuffer[idx]);

  return api;
}

Object.defineProperty(global, '__buggerCLI', {
  get() {
    return createAPI();
  },
});

exports.addToRingBuffer = function addToRingBuffer(value) {
  ringBuffer.unshift(value);
  ringBuffer = ringBuffer.slice(0, 5);
  return value;
};

exports.setLastEval = function setLastEval(value) {
  lastEval = value;
  return value;
};

'use strict';

const vm = require('vm');

const Debug = vm.runInDebugContext('Debug');

let lastEval = undefined;
let ringBuffer = [];

const breakMap = new Map();

function addDebugBreak(target) {
  if (typeof target !== 'function') {
    throw new TypeError('debug(fn) expects a function');
  }
  if (breakMap.has(target)) {
    throw new Error('Function already has a breakpoint');
  }
  const breakId = Debug.setBreakPoint(target);
  breakMap.set(target, breakId);
}

function removeDebugBreak(target) {
  if (typeof target !== 'function') {
    throw new TypeError('debug(fn) expects a function');
  }
  const breakId = breakMap.get(target);
  if (!breakId) {
    throw new Error('Function has not breakpoint');
  }
  Debug.clearBreakPoint(breakId);
}

function createAPI() {
  const api = {
    $_: lastEval,
    debug: addDebugBreak,
    undebug: removeDebugBreak,
  };

  [ 0, 1, 2, 3, 4 ].forEach(idx =>
    api[`$${idx}`] = ringBuffer[idx]);

  return api;
}

Object.defineProperty(global, '__buggerCLI', {
  get() {
    return createAPI();
  },
});

exports.addToRingBuffer = function addToRingBuffer(value) {
  ringBuffer.unshift(value);
  ringBuffer = ringBuffer.slice(0, 5);
  return value;
};

exports.setLastEval = function setLastEval(value) {
  lastEval = value;
  return value;
};

Add [un]debug to cli api


function getTurnCount(state, robot, memory) {
    for (let turn = 0;; turn++) {
        if (state.parcels.length == 0) {
            return turn;
        }
        let action = robot(state, memory);
        state = state.move(action.direction);
        memory = action.memory;
    }
}

function compareRobots(robot1, memory1, robot2, memory2) {
    let total1 = 0, total2 = 0, reps = 100;
    for (let i = 0; i < reps; ++i) {
        let initState = VillageState.random();
        total1 += getTurnCount(initState, robot1, memory1);
        total2 += getTurnCount(initState, robot2, memory2);
    }
    console.log(`${robot1.name}: ${total1 / reps}\n` +
                `${robot2.name}: ${total2 / reps}`);
}

function compareRobots(robot1, memory1, robot2, memory2) {
	const rounds = 100;
	let total1 = 0, total2 = 0;

	for (let i = 0; i < rounds; ++i) {
		let state = VillageState.random();
		total1 += getTurnCount(state, robot1, memory1);
		total2 += getTurnCount(state, robot2, memory2);
	}

	console.log(`robot1: ${total1 / rounds}\nrobot2: ${total2 / rounds}`)
}

function getTurnCount(state, robot, memory) {
	for (let turn = 0;; ++turn) {
		if (state.parcels.length == 0) return turn;

		let action = robot(state, memory);
		state = state.move(action.direction);
		memory = action.memory;
	}
}

Add chapter 7, exercise 1


export default {

	props: [
		'type',
		'index',
		'fields',
		'other'
	],

	data() {
		return this.fields;
	},

	created() {
		this.watching = {};
		this.watchFields(this.fields);

		// should only be triggered when all fields are overwitten
		this.$watch('fields', () => {
			this.watchFields(this.fields);
		});

		// TODO: deal with fields that have nested data?
	},

	methods: {
		watchFields(fields) {
			Object.keys(fields).map((name) => {
				if(!this.watching[name]) {
					this.watching[name] = true;

					this.$watch(`fields.${name}`, (newVal) => {
						this[name] = newVal;
					}, {
						deep: true
					});
				}
			});
		}
	}

};

import inlineFieldMixin from 'mixins/inlineFieldMixin';

export default {

	props: [
		'type',
		'index',
		'fields',
		'other'
	],

	mixins: [inlineFieldMixin],

	data() {
		return { ...this.fields };
	},

	created() {
		this.fieldElements = {};
		this.watching = {};
		this.watchFields(this.fields);

		// should only be triggered when all fields are overwitten
		this.$watch('fields', () => {
			this.watchFields(this.fields);
		});
	},

	methods: {
		watchFields(fields) {
			Object.keys(fields).map((name) => {
				if(!this.watching[name]) {
					this.watching[name] = true;

					this.$watch(`fields.${name}`, (newVal) => {
						if(this.internalChange) {
							this.internalChange = false;
							return;
						}

						this[name] = newVal;
					}, {
						deep: true
					});
				}
			});
		}
	}

};

Add inline field mixin to base block class.


/*jslint browser*/
// This file is the shim layer to connect jslint to CodeMirror editor
import jslint from "./jslint.mjs";

const CodeMirror = window.CodeMirror;

function validator(text, options) {
    let data = jslint(text, options, options.globals);
    let warnings = data.warnings;
    let output = [];

    if (data.stop) {
        output.push({
            message: "JSLint was unable to finish.",
            severity: "warning",
            from: new CodeMirror.Pos(0, 0),
            to: new CodeMirror.Pos(0, 0)
        });
    }

    warnings.forEach(function (warning) {
        output.push({
            message: warning.message,
            severity: "error",
            from: new CodeMirror.Pos(warning.line, warning.column - 1),
            to: new CodeMirror.Pos(warning.line, warning.column + 1)
        });
    });

    return output;
}

CodeMirror.registerHelper("lint", "javascript", validator);

/*jslint browser*/
// This file is the shim layer to connect jslint to CodeMirror editor
import jslint from "./jslint.mjs";

const CodeMirror = window.CodeMirror;

function validator(text, options) {
    let data = jslint(text, options, options.globals);
    let warnings = data.warnings;
    let output = [];

    if (data.stop) {
        output.push({
            message: "JSLint was unable to finish.",
            severity: "warning",
            from: new CodeMirror.Pos(0, 0),
            to: new CodeMirror.Pos(0, 0)
        });
    }

    warnings.forEach(function (warning) {
        output.push({
            message: warning.message,
            severity: "error",
            from: new CodeMirror.Pos(warning.line - 1, warning.column - 1),
            to: new CodeMirror.Pos(warning.line - 1, warning.column + 1)
        });
    });

    return output;
}

CodeMirror.registerHelper("lint", "javascript", validator);

Add correction between CodeMirror lint plugin to display warning on expected line number


'use strict';

/*
 * This is intended to start necessary effort for sync access to data for server-side rendering.
 */

const TopData = require('../../storage/top-stories.js');
const items = require('../../storage/items.js');

function provideRenderData(req, properties) {
  req.log.info('provideRenderData called');

  const latestUUID = TopData.uuid();
  const latestTopItems = TopData.latest().slice(0,20);
  const latestEntities = latestTopItems.reduce(function(acc, cur, index) {
    const item = items.get(cur, req.log);
    acc[item.id] = item;
    return acc;
  }, {});

  return {
    uuid: latestUUID,
    from: 0,
    to: 20,
    items: latestTopItems,
    entities: latestEntities
  };
}

module.exports = provideRenderData;

'use strict';

/*
 * This is intended to start necessary effort for sync access to data for server-side rendering.
 */

const ListData = require('../../storage/lists.js');
const ItemsData = require('../../storage/items.js');

function provideRenderData(req, properties) {
  req.log.info('provideRenderData called');

  const latestUUID = ListData.uuid('top');
  const latestTopItems = ListData.latest('top').slice(0,20);
  const latestEntities = latestTopItems.reduce(function(acc, cur, index) {
    const item = ItemsData.get(cur, req.log);
    acc[item.id] = item;
    return acc;
  }, {});

  return {
    uuid: latestUUID,
    from: 0,
    to: 20,
    items: latestTopItems,
    entities: latestEntities
  };
}

module.exports = provideRenderData;

Rename imports and exports for experimental SSR data fetching.


define(['jquery', 'backbone', 'underscore', 'moxie.conf', 'hbs!places/templates/detail', 'hbs!places/templates/busrti', 'hbs!places/templates/trainrti'],
    function($, Backbone, _, conf, detailTemplate, busRTITemplate, trainRTITemplate){
    var RTI_REFRESH = 15000;    // 15 seconds
    var DetailView = Backbone.View.extend({

        attributes: {
            'class': 'detail-map'
        },

        serialize: function() {
            var poi = this.model.toJSON();
            return {
                poi: poi,
                multiRTI: poi.RTI.length > 1,
                alternateRTI: this.model.getAlternateRTI(),
                currentRTI: this.model.getCurrentRTI()
            };
        },
        template: detailTemplate,
        manage: true,

        afterRender: function() {
            Backbone.trigger('domchange:title', this.model.get('name'));
            if (this.model.get('RTI')) {
                this.refreshID = this.model.renderRTI(this.$('#poi-rti')[0], RTI_REFRESH);
            }
        },

        cleanup: function() {
            clearInterval(this.refreshID);
        }

    });
    return DetailView;
});

define(['jquery', 'backbone', 'underscore', 'moxie.conf', 'hbs!places/templates/detail', 'hbs!places/templates/busrti', 'hbs!places/templates/trainrti'],
    function($, Backbone, _, conf, detailTemplate, busRTITemplate, trainRTITemplate){
    var RTI_REFRESH = 15000;    // 15 seconds
    var DetailView = Backbone.View.extend({

        initialize: function() {
            Backbone.trigger('domchange:title', this.model.get('name'));
            Backbone.on('favourited', _.bind(this.favourited, this));
        },
        attributes: {
            'class': 'detail-map'
        },

        serialize: function() {
            var poi = this.model.toJSON();
            return {
                poi: poi,
                multiRTI: poi.RTI.length > 1,
                alternateRTI: this.model.getAlternateRTI(),
                currentRTI: this.model.getCurrentRTI()
            };
        },
        template: detailTemplate,
        manage: true,

        afterRender: function() {
            if (this.model.get('RTI')) {
                this.refreshID = this.model.renderRTI(this.$('#poi-rti')[0], RTI_REFRESH);
            }
        },

        favourited: function(fav) {
            fav.set('options', {model: this.model.toJSON()});
            fav.set('type', 'poi:'+this.model.get('type'));
            fav.save();
        },

        cleanup: function() {
            Backbone.off('favourited');
            clearInterval(this.refreshID);
        }

    });
    return DetailView;
});

Set the POI being saved on the Favourite model


global.process = { __proto__: process, pid: 123456 }
Date.now = function () { return 1459875739796 }
require('os').hostname = function () { return 'abcdefghijklmnopqr' }
var pino = require(require.resolve('./../../../'))
var log = pino()
log.info('h')

global.process = { __proto__: process, pid: 123456 }
Date.now = function () { return 1459875739796 }
require('os').hostname = function () { return 'abcdefghijklmnopqr' }
var pino = require(require.resolve('./../../../'))
var log = pino({extreme: true})
log.info('h')

Fix no event loop test (need to wait for fd)


var test = require('tape'),
	es2015 = require('babel-preset-es2015'),
	es2015Loose = require('..');

var LOOSE = { loose: true };

var PREFIX = 'transform-es2015-';

var SHOULD_BE_LOOSE = [
	PREFIX+'template-literals',
	PREFIX+'classes',
	PREFIX+'computed-properties',
	PREFIX+'for-of',
	PREFIX+'spread',
	PREFIX+'destructuring',
	PREFIX+'modules-commonjs'
];

function getPluginEntry(name) {
	var plugin = require('../node_modules/babel-preset-es2015/node_modules/babel-plugin-'+name);
	for (var i=es2015Loose.plugins.length; i--; ) {
		var p = es2015Loose.plugins[i];
		if (p===plugin || p[0]===plugin) {
			return p;
		}
	}
	return false;
}

SHOULD_BE_LOOSE.forEach(function(name) {
	test(name, function(t) {
		t.plan(3);
		var entry = getPluginEntry(name);

		t.ok(entry, 'Entry for '+name+' should exist');
		t.ok(Array.isArray(entry), 'Entry for '+name+' should be an Array');
		t.deepEqual(entry[1], LOOSE, 'Entry for '+name+' should specify {loose:true}');
	});
});

var test = require('tape')
var es2015 = require('babel-preset-es2015')
var es2015Loose = require('../index')

var LOOSE = {loose: true}

var PREFIX = 'transform-es2015-'

var SHOULD_BE_LOOSE = [
	PREFIX + 'template-literals',
	PREFIX + 'classes',
	PREFIX + 'computed-properties',
	PREFIX + 'for-of',
	PREFIX + 'spread',
	PREFIX + 'destructuring',
	PREFIX + 'modules-commonjs'
]

function getPluginEntry(name) {
	var plugin = require('babel-plugin-' + name)
	for (var loosePlugin of es2015Loose.plugins) {
		if (loosePlugin === plugin || loosePlugin[0] === plugin) {
			return loosePlugin
		}
	}
	return false
}

SHOULD_BE_LOOSE.forEach(function(name) {
	test(name, function(t) {
		t.plan(3)
		var entry = getPluginEntry(name)

		t.ok(entry, 'Entry for ' + name + ' should exist')
		t.ok(Array.isArray(entry), 'Entry for ' + name + ' should be an Array')
		t.deepEqual(entry[1], LOOSE, 'Entry for ' + name + ' should specify {loose:true}')
	})
});

Test fixes and style adjustments


import main from '../'
import test from 'ava'

test('extends files', t => {
  const expected = [
    'airbnb',
    'wyze/base',
    'wyze/rules/react',
    'wyze/rules/jsx-a11y',
  ]

  t.deepEqual(main.extends, expected)
})

import main from '../'
import test from 'ava'

test('extends files', t => {
  const expected = [
    'airbnb-base',
    'airbnb-base/rules/strict',
    'airbnb/rules/react',
    'wyze/base',
    'wyze/rules/react',
    'wyze/rules/jsx-a11y',
  ]

  t.deepEqual(main.extends, expected)
})

Fix test for default config


'use strict';

const Botkit = require('botkit');
const apiaibotkit = require('../api-ai-botkit');

const slackToken = process.env.SLACK_TOKEN;
const apiaiToken = process.env.APIAI_TOKEN;

const apiai = apiaibotkit(apiaiToken);
const controller = Botkit.slackbot();

controller.hears('.*', ['direct_message', 'direct_mention', 'mention'], function (bot, message) {
    apiai.process(message, bot);
});

controller.on('reaction_added', function (bot, message) {
   console.log(message);
});

apiai.all(function (message, resp, bot) {
    console.log(resp.result.action);
});

apiai
    .action('smalltalk.greetings', function (message, resp, bot) {
        var responseText = resp.result.fulfillment.speech;
        bot.reply(message, responseText);
    })
    .action('input.unknown', function (message, resp, bot) {
        bot.reply(message, "Sorry, I don't understand");
    });

controller.spawn({
    token: slackToken
}).startRTM();

'use strict';

const Botkit = require('botkit');
const apiaibotkit = require('../api-ai-botkit');

const slackToken = process.env.SLACK_TOKEN;
const apiaiToken = process.env.APIAI_TOKEN;

const apiai = apiaibotkit(apiaiToken);
const controller = Botkit.slackbot();

controller.hears('.*', ['direct_message', 'direct_mention', 'mention'], function (bot, message) {
    apiai.process(message, bot);
});

controller.on('reaction_added', function (bot, message) {
   console.log(message);
});

apiai.all(function (message, resp, bot) {
    console.log(resp.result.action);
});

apiai
    .action('smalltalk.greetings', function (message, resp, bot) {
        let responseText = resp.result.fulfillment.speech;
        bot.reply(message, responseText);
    })
    .action('input.unknown', function (message, resp, bot) {
        bot.reply(message, "Sorry, I don't understand");
    });

controller.spawn({
    token: slackToken
}).startRTM();

Use let instead of var


Accounts.onCreateUser(function(options, user) {
  if (Accounts.ui._options.onPostSignUpHook) {
    return Accounts.ui._options.onPostSignUpHook(options, user);
  }
  return user;
});

Accounts.onCreateUser(function(options, user) {
  if (Accounts.ui._options.onPostSignUpHook) {
    let _user = Accounts.ui._options.onPostSignUpHook(options, user);
    return _user || user;
  }
  return user;
});

Allow for no changes to the user object.


const test = require('tap').test;
const TextToSpeech = require('../../src/extensions/scratch3_text2speech/index.js');

const fakeStage = {
    textToSpeechLanguage: null
};

const fakeRuntime = {
    getTargetForStage: () => fakeStage,
    on: () => {} // Stub out listener methods used in constructor.
};

const ext = new TextToSpeech(fakeRuntime);

test('if no language is saved in the project, use default', t => {
    t.strictEqual(ext.getCurrentLanguage(), 'en');
    t.end();
});

test('if an unsupported language is dropped onto the set language block, use default', t => {
    ext.setLanguage({LANGUAGE: 'nope'});
    t.strictEqual(ext.getCurrentLanguage(), 'en');
    t.end();
});

test('if a supported language name is dropped onto the set language block, use it', t => {
    ext.setLanguage({LANGUAGE: 'español'});
    t.strictEqual(ext.getCurrentLanguage(), 'es');
    t.end();
});

test('get the extension locale for a supported locale that differs', t => {
    ext.setLanguage({LANGUAGE: 'ja-hira'});
    t.strictEqual(ext.getCurrentLanguage(), 'ja');
    t.end();
});

test('use localized spoken language name in place of localized written language name', t => {
    ext.getEditorLanguage = () => 'es';
    const languageMenu = ext.getLanguageMenu();
    const localizedNameForChineseInSpanish = languageMenu.find(el => el.value === 'zh-cn').text;
    t.strictEqual(localizedNameForChineseInSpanish, 'Chino (Mandarín)'); // i.e. should not be 'Chino (simplificado)'
    t.end();
});

Add test for localized spoken language name


import cli from 'cli';
import {Gpio} from 'chip-gpio';
import sensor from 'ds18x20';

var options = cli.parse();

var interval = 2000;
var threshold = 25;

var heater = new Gpio(0, 'out');

function setHeater(on) {
  console.log('Heater:', on ? 'on' : 'off');
  heater.write(on ? 0 : 1);
}
function setHeaterOn() {
  setHeater(true);
}
function setHeaterOff() {
  setHeater(false);
}

sensor.isDriverLoaded((err, isLoaded) => {
  console.log(isLoaded);
  sensor.list((err, listOfDeviceIds) => {
    console.log(listOfDeviceIds);
  });
  setInterval(() => {
    sensor.getAll((err, tempObj) => {
      var sum = 0;
      var len = 0;
      tempObj.forEach(obj => {
        console.log(obj);
        sum += obj;
        len++;
      });

      var average = sum / len;

      console.log(average);

      setHeater(average < threshold);
    });
  }, interval);
});

import cli from 'cli';
import {Gpio} from 'chip-gpio';
import sensor from 'ds18x20';

var options = cli.parse();

var interval = 2000;
var threshold = 25;

var heater = new Gpio(0, 'out');

function setHeater(on) {
  console.log('Heater:', on ? 'on' : 'off');
  heater.write(on ? 0 : 1);
}
function setHeaterOn() {
  setHeater(true);
}
function setHeaterOff() {
  setHeater(false);
}

sensor.isDriverLoaded((err, isLoaded) => {
  if (!isLoaded) {
    console.log('Driver not loaded');
    return;
  }
  sensor.list((err, listOfDeviceIds) => {
    console.log(listOfDeviceIds);
  });
  setInterval(() => {
    sensor.getAll((err, tempObj) => {
      var sum = 0;
      var len = 0;
      tempObj.forEach(obj => {
        console.log(obj);
        sum += obj;
        len++;
      });

      var average = sum / len;

      console.log(average);

      setHeater(average < threshold);
    });
  }, interval);
});

Handle driver not loaded cleaner


import * as ACTION_TYPES from '../constants/action_types'
import * as MAPPING_TYPES from '../constants/mapping_types'
import * as api from '../networking/api'
import * as StreamFilters from '../components/streams/StreamFilters'
import * as StreamRenderables from '../components/streams/StreamRenderables'

export function loadNotifications(params = {}) {
  return {
    type: ACTION_TYPES.LOAD_STREAM,
    payload: { endpoint: api.notifications(params), vo: {} },
    meta: {
      mappingType: MAPPING_TYPES.ACTIVITIES,
      renderStream: {
        asList: StreamRenderables.notificationList,
        asGrid: StreamRenderables.notificationList,
      },
      resultFilter: StreamFilters.notificationsFromActivities,
    },
  }
}

import React from 'react'
import * as ACTION_TYPES from '../constants/action_types'
import * as MAPPING_TYPES from '../constants/mapping_types'
import * as api from '../networking/api'
import * as StreamFilters from '../components/streams/StreamFilters'
import * as StreamRenderables from '../components/streams/StreamRenderables'
import { ZeroState } from '../components/zeros/Zeros'

export function loadNotifications(params = {}) {
  return {
    type: ACTION_TYPES.LOAD_STREAM,
    payload: { endpoint: api.notifications(params), vo: {} },
    meta: {
      mappingType: MAPPING_TYPES.ACTIVITIES,
      renderStream: {
        asList: StreamRenderables.notificationList,
        asGrid: StreamRenderables.notificationList,
        asZero: <ZeroState>Sorry, no notifications found.</ZeroState>,
      },
      resultFilter: StreamFilters.notificationsFromActivities,
    },
  }
}

Add a notification zero state


var _ = require('underscore');

module.exports = {

  line: function (browser, selector, index) {
    var lineInGroup = _.isNumber(index) ? '.group' + index : '';
    return function () {
      return browser
        .$(selector + ' svg .line' + lineInGroup)
          .should.eventually.exist;
    };
  },

  stack: function (browser, selector, index) {
    var lineInGroup = _.isNumber(index) ? '.group' + index : '';
    return function () {
      var linePath;
      return browser
        .$(selector + ' svg .line' + lineInGroup)
          .should.eventually.exist
          .getAttribute('d').then(function (d) {
            linePath = d.split('M')[1];
          })
        .$(selector + ' svg .stack' + lineInGroup)
          .should.eventually.exist
          .getAttribute('d').then(function (d) {
            // the line follows the top of the stack, so the line path should be a
            // substring of the stack path
            d.should.contain(linePath);
          });
    };
  }

};

var _ = require('underscore');

module.exports = {

  line: function (browser, selector, index) {
    var lineInGroup = _.isNumber(index) ? '.group' + index : '';
    return function () {
      return browser
        .$(selector + ' svg .line' + lineInGroup)
          .should.eventually.exist;
    };
  },

  stack: function (browser, selector, index) {
    var lineInGroup = _.isNumber(index) ? '.group' + index : '';
    return function () {
      var linePath;
      return browser
        .$(selector + ' svg .line' + lineInGroup)
          .should.eventually.exist
          .getAttribute('d').then(function (d) {
            if (d !== null) {
              linePath = d.split('M')[1];
            }
          })
        .$(selector + ' svg .stack' + lineInGroup)
          .should.eventually.exist
          .getAttribute('d').then(function (d) {
            // the line follows the top of the stack, so the line path should be a
            // substring of the stack path
            if (d !== null) {
              d.should.contain(linePath);
            }
          });
    };
  }

};

Add a guard clause for svg attribute presence


import './App.scss';
import Component from 'react-pure-render/component';
import Footer from './Footer.react';
import Header from './Header.react';
import Helmet from 'react-helmet';
import React, { PropTypes } from 'react';
import start from '../../common/app/start';

class App extends Component {

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

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

    return (
      <div className="page">
        <Helmet
          link={[
            { rel: 'shortcut icon', href: require('./favicon.ico') }
          ]}
          meta={[{
            name: 'description',
            content: 'Dev stack and starter kit for functional and universal React web apps'
          }]}
          titleTemplate="%s - Este.js"
        />
        {/* Pass location to ensure header active links are updated. */}
        <Header location={location} />
        {children}
        <Footer />
      </div>
    );
  }

}

export default start(App);

import './App.scss';
import Component from 'react-pure-render/component';
import Footer from './Footer.react';
import Header from './Header.react';
import Helmet from 'react-helmet';
import React, { PropTypes } from 'react';
import start from '../../common/app/start';

class App extends Component {

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

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

    return (
      <div className="page">
        <Helmet
          link={[
            { rel: 'shortcut icon', href: require('./favicon.ico') }
          ]}
          meta={[{
            content: 'Dev stack and starter kit for functional and universal React web apps',
            name: 'description'
          }]}
          titleTemplate="%s - Este.js"
        />
        {/* Pass location to ensure header active links are updated. */}
        <Header location={location} />
        {children}
        <Footer />
      </div>
    );
  }

}

export default start(App);

Sort lines alphabetically. Never gets tired.


import Ember from 'ember';
import moment from 'moment';

const { computed, Logger:logger } = Ember;

export default Ember.Service.extend({
  _timeZone: null,

  locale: null,
  defaultFormat: null,

  init() {
    this._super(...arguments);

    if (!this.get('defaultFormat')) {
      this.set('defaultFormat', 'LLLL');
    }
  },

  timeZone: computed('_timeZone', {
    get() {
      return this.get('_timeZone');
    },

    set(propertyKey, timeZone) {
      if (!moment.tz) {
        logger.warn('[ember-moment] attempted to set timezone, but moment-timezone unavailable.');
        return;
      }

      this.set('_timeZone', timeZone);

      return timeZone;
    }
  }),

  changeLocale(locale) {
    this.set('locale', locale);
  },

  changeTimeZone(timeZone) {
    this.set('timeZone', timeZone);
  },

  moment() {
    let time = moment(...arguments);
    const locale = this.get('locale');
    const timeZone = this.get('timeZone');

    if (locale) {
      time = time.locale(locale);
    }

    if (timeZone && time.tz) {
      time = time.tz(timeZone);
    }

    return time;
  }
});

import Ember from 'ember';
import moment from 'moment';

const { computed, Logger:logger } = Ember;

export default Ember.Service.extend({
  _timeZone: null,

  locale: null,
  defaultFormat: null,

  init() {
    this._super(...arguments);

    if (!this.get('defaultFormat')) {
      this.set('defaultFormat', 'LLLL');
    }
  },

  timeZone: computed('_timeZone', {
    get() {
      return this.get('_timeZone');
    },

    set(propertyKey, timeZone) {
      if (!moment.tz) {
        logger.warn('[ember-moment] attempted to set timezone, but moment-timezone unavailable.');
        return;
      }

      this.set('_timeZone', timeZone);

      return timeZone;
    }
  }),

  changeLocale(locale) {
    this.set('locale', locale);
  },

  changeTimeZone(timeZone) {
    this.set('timeZone', timeZone);
  },

  isMoment(obj) {
    return moment.isMoment(obj);
  },

  moment() {
    let time = moment(...arguments);
    const locale = this.get('locale');
    const timeZone = this.get('timeZone');

    if (locale) {
      time = time.locale(locale);
    }

    if (timeZone && time.tz) {
      time = time.tz(timeZone);
    }

    return time;
  }
});

Add a shortcut for isMoment


import React, { Component } from 'react';

class SearchBar extends Component{

    constructor(props){
        super(props);

        this.state = { term : ''}; //оголошує обєкт
        console.log(this.state);
    }

    render(){
        //this.setState({term: event.target.value})
        //те ж саме що, але так не правильно this.state.term = event.target.value
        return (
            <div>
                <input onChange = { event => this.setState({term: event.target.value})} />;
                //змінює значення обєкта і присвоює йому значення інпута
                value of input : {this.state.term}
                // виводить на екран обєкт стейт
            </div>
        );
    }

}

export default SearchBar;

import React, { Component } from 'react';

class SearchBar extends Component{

    constructor(props){
        super(props);

        this.state = { term : 'Starting Value'}; //оголошує обєкт
        console.log(this.state);
    }

    render(){
        //this.setState({term: event.target.value})
        //те ж саме що, але так не правильно this.state.term = event.target.value
        return (
            <div> {this.state.term}
                <input
                value={this.state.term}
                onChange = { event => this.setState({term: event.target.value})} />
            </div>
        );
    }

}

export default SearchBar;

Create example 2 with state and Starting Value in input


//= require jquery
//= require handlebars.runtime
//= require underscore/underscore
//= require backbone/backbone
//= require backbone/backbone.handlebars.adapter
//= require marionette/backbone.babysitter
//= require marionette/backbone.radio
//= require marionette/backbone.marionette
//= require marionette/backbone.radio.adapter
//= require_self

//= require jquery
//= require handlebars.runtime
//= require underscore/underscore
//= require backbone/backbone
//= require marionette/backbone.babysitter
//= require marionette/backbone.radio
//= require marionette/backbone.marionette
//= require marionette/backbone.radio.adapter
//= require marionette/backbone.handlebars.adapter
//= require_self

Fix handlebars adapter asset references


const { describe, it, before, after, beforeEach, expect, sinon } = require('../../../test-helper');
const Hapi = require('hapi');

const snapshotController = require('../../../../lib/application/snapshots/snapshot-controller');

describe('Unit | Router | snapshot-router', () => {

  let server;

  beforeEach(() => {
    server = new Hapi.Server();
    server.connection({ port: null });
    server.register({ register: require('../../../../lib/application/snapshots') });
  });

  describe('POST /api/snapshots', _ => {

    before(() => {
      sinon.stub(snapshotController, 'create').callsFake((request, reply) => reply('ok'));
    });

    after(() => {
      snapshotController.create.restore();
    });

    it('should exist', (done) => {
      return server.inject({ method: 'POST', url: '/api/snapshots' }, res => {
        expect(res.statusCode).to.equal(200);
        done();
      });
    });
  });

});

Add unit test to snapshot index route


// @flow

import { compose } from 'redux';
import { connect } from 'react-redux';
import { dispatched } from '@webkom/react-prepare';

import UserSettingsNotifications from './components/UserSettingsNotifications';
import {
  fetchNotificationAlternatives,
  fetchNotificationSettings,
  updateNotificationSetting
} from 'app/actions/NotificationSettingsActions';
import {
  selectNotificationSettingsAlternatives,
  selectNotificationSettings
} from 'app/reducers/notificationSettings';

const loadData = (props, dispatch) => {
  return Promise.all([
    dispatch(fetchNotificationAlternatives()),
    dispatch(fetchNotificationSettings())
  ]);
};

const mapStateToProps = state => {
  return {
    alternatives: selectNotificationSettingsAlternatives(state),
    settings: selectNotificationSettings(state)
  };
};

const mapDispatchToProps = { updateNotificationSetting };

export default compose(
  dispatched(loadData),
  connect(mapStateToProps, mapDispatchToProps)
)(UserSettingsNotifications);

// @flow

import { compose } from 'redux';
import { connect } from 'react-redux';
import { dispatched } from '@webkom/react-prepare';

import UserSettingsNotifications from './components/UserSettingsNotifications';
import {
  fetchNotificationAlternatives,
  fetchNotificationSettings,
  updateNotificationSetting
} from 'app/actions/NotificationSettingsActions';
import {
  selectNotificationSettingsAlternatives,
  selectNotificationSettings
} from 'app/reducers/notificationSettings';

const loadData = (props, dispatch) => {
  return Promise.all([
    dispatch(fetchNotificationAlternatives()),
    dispatch(fetchNotificationSettings())
  ]);
};

const mapStateToProps = state => {
  return {
    alternatives: selectNotificationSettingsAlternatives(state),
    settings: selectNotificationSettings(state)
  };
};

const mapDispatchToProps = { updateNotificationSetting };

export default compose(
  dispatched(loadData, {
    componentWillReceiveProps: false
  }),
  connect(mapStateToProps, mapDispatchToProps)
)(UserSettingsNotifications);

Fix user notification settings fetch


/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.core',
  name: 'AxiomCloner',
  documentation: 'An axiom that clones an axiom from another model.',
  properties: [
    {
      class: 'Class',
      name: 'from'
    },
    {
      class: 'String',
      name: 'axiom'
    },
    {
      class: 'String',
      name: 'name',
      transient: true,
      expression: function(axiom) {
        return axiom + '_cloner';
      }
    },
    {
      class: 'Map',
      name: 'config'
    }
  ],
  methods: [
    function installInClass(cls) {
      var axiom = this.from.getAxiomByName(this.axiom);
      if ( ! axiom ) {
        throw `Cannot find ${this.axiom} on ${this.from.id}`;
      }
      axiom = axiom.clone().copyFrom(this.config);
      cls.installAxiom(axiom);
    }
  ]
});

foam.SCRIPT({
  package: 'foam.core',
  name: 'AxiomClonerConvenienceMethod',
  code: function() {
    foam.axiomCloner = function(from, axiom, config) {
      return foam.core.AxiomCloner.create({
        from: from,
        axiom: axiom,
        config: config
      });
    };
  }
});

/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.core',
  name: 'AxiomCloner',
  documentation: 'An axiom that clones an axiom from another model.',
  properties: [
    {
      class: 'Class',
      name: 'from'
    },
    {
      class: 'String',
      name: 'axiom'
    },
    {
      class: 'String',
      name: 'name',
      transient: true,
      expression: function(axiom) {
        return axiom + '_cloner';
      }
    },
    {
      class: 'Map',
      name: 'config'
    }
  ],
  methods: [
    function installInClass(cls) {
      var axiom = this.from.getAxiomByName(this.axiom);
      if ( ! axiom ) {
        throw `Cannot find ${this.axiom} on ${this.from.id}`;
      }
      axiom = axiom.clone().copyFrom(this.config);
      cls.installAxiom(axiom);
    }
  ]
});

foam.LIB({
  name: 'foam',
  methods: [
    function axiomCloner(from, axiom, config) {
      return {
        class: 'foam.core.AxiomCloner',
        from: from,
        axiom: axiom,
        config: config
      };
    }
  ]
});

Use foam.LIB and be a bit lazier.


import React, { Component } from 'react';

class BoardContainer extends Component {
  constructor() {

  }
}

export default BoardContainer;

import React, { Component } from 'react';

class BoardContainer extends Component {
  constructor() {

  }
  randomArray = () => {
    array = [];
    for (let i = 0; i < 10; i++) {
      array[i] = [];
      for (let j = 0; j < 10; j++) {
        array[i][j] = Math.floor(Math.random() + .5);
      }
    }
    return array;
  }
}

export default BoardContainer;

Add method to generate random game of life board


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require angular
//= require angular-route
//= require_tree .

Add angular and angular-route requires


'use strict'

const Errors = require('../errors')
const Decal = require('../classes/Decal')
const User = require('../db').User
const Permission = require('../permission')

class HTTP {
  static check (request, response, next) {
    if (Object.keys(request.query).length > 0) {
      Permission.require('user.read', request.user).then(function () {
        User.findOne({
          where: request.query
        }).then(function (user) {
          if (!user) {
            let error = Error.template('not_found', 'user')
            response.model.errors.push(error)
            response.status(error.code)
            next()
          }

          Decal.checkEligble(user).then(function () {
            response.model.data = {
              eligble: true
            }
            response.status = 200
            next()
          }).catch(function (error) {
            response.model.errors.push(error)
            response.status(error.code)
            next()
          })
        })
      }).catch(function (error) {
        response.model.errors.push(error)
        response.status(error.error.code)
        next()
      })
    } else {
      Decal.checkEligble(request.user).then(function () {
        response.model.data = {
          eligble: true
        }
        response.status = 200
        next()
      }).catch(function (error) {
        response.model.errors.push(error)
        response.status(error.code)
        next()
      })
    }
  }

  static redeem (request, response, next) {
    Decal.getDecalForUser(request.user).then(function (decal) {
      response.model.data = decal
      response.status = 200
      next()
    }).catch(function (errorData) {
      let error = Errors.throw('server_error', errorData)
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }
}

module.exports = {
  HTTP
}

'use strict'

const Error = require('../errors')
const Decal = require('../classes/Decal')
const User = require('../db').User
const Permission = require('../permission')
const DecalsPresenter = require('../classes/Presenters').DecalsPresenter

class Decals {
  static async check (ctx) {
    if (Object.keys(ctx.query).length > 0) {
      if (Permission.require(['user.read'], ctx.state.user, ctx.state.scope)) {
        let user = await User.findOne({
          where: ctx.query
        })

        if (!user) {
          throw Error.template('not_found', 'user')
        }

        await Decal.checkEligble(user)
        return {
          eligible: true
        }
      }
    } else {
      await Decal.checkEligble(ctx.user)
      return {
        eligible: true
      }
    }
  }

  static async redeem (ctx) {
    let decal = await Decal.getDecalForUser(ctx.user)
    return DecalsPresenter.render(decal)
  }
}

module.exports = Decals

Rewrite Decals API for Koa


requirejs.config({
    baseUrl: 'assets/js',
    paths: {
        'jquery': '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min',
        'bootstrap': '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min',
        'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min',
        'backbone': '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min',
        'jquery-cookie': '//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min'
    },
    shim: {
        'jquery-cookie': {
            deps: ['jquery'],
            exports: 'jQuery.fn.cookie'
        }
    }
});
requirejs(['main']);

requirejs.config({
    baseUrl: 'assets/js',
    paths: {
        'jquery': '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min',
        'bootstrap': '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min',
        'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min',
        'backbone': '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min',
        'jquery-cookie': '//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min'
    }
});
requirejs(['main']);

Remove jquery cookie from shim config.


describe('Profile', function() {
  const owner = require('../fixtures/user.json').owner;
  const profile = require('../fixtures/profile.json');

  it('Create a new profile', function() {
    cy.login('/organizations', owner.username, owner.password);

    cy.get('a')
      .contains('new organization')
      .click();
    cy.location('pathname').should('contain', '/new');

    cy.get('#title').type(profile.title);
    cy.get('#name').type(profile.title);
    cy.get('#is_public_profile').click();
    cy.get('button')
      .contains('Create')
      .click();
  });
});

describe('Profile', function() {
  const owner = require('../fixtures/user.json').owner;
  const profile = require('../fixtures/profile.json');

  it('Create a new profile', function() {
    cy.login('/organizations', owner.username, owner.password);

    cy.get('a')
      .contains('new organization')
      .click();
    cy.location('pathname').should('contain', '/new');

    cy.get('#title').type(profile.title);
    cy.get('#name').type(profile.title);
    cy.get('#is_public_profile').click();
    cy.get('button')
      .contains('Next')
      .click();
  });
});

Change button to Next in add profile test


import {ApolloClient, HttpLink, InMemoryCache} from "@apollo/client"

import fetch from "isomorphic-fetch"

/**
 * @typedef {import("@apollo/client").NormalizedCacheObject} NormalizedCacheObject
 */

/**
 * @type {ApolloClient<NormalizedCacheObject>}
 */
let cachedClient = null

const createApollo = () => new ApolloClient({
  ssrMode: process.browser === false || typeof window === "undefined",
  link: new HttpLink({
    uri: process.env.NEXT_PUBLIC_GRAPHQL,
    credentials: "same-origin",
    fetch
  }),
  cache: new InMemoryCache()
})

/**
 * @param {Object.<string, any>} initialState
 */
function initializeApollo(initialState = null) {
  /**
   * @type {ApolloClient<NormalizedCacheObject>}
   */
  const client = cachedClient ?? createApollo()

  if (initialState) {
    const oldCache = client.extract()

    client.cache.restore({...oldCache, ...initialState})
  }

  // Always return a new client for SSR
  if (process.browser === false) {
    return client
  }

  if (cachedClient) {
    cachedClient = client
  }

  return client
}

export default initializeApollo

import {ApolloClient, HttpLink, InMemoryCache} from "@apollo/client"

import fetch from "isomorphic-fetch"

/**
 * @typedef {import("@apollo/client").NormalizedCacheObject} NormalizedCacheObject
 */

/**
 * @type {ApolloClient<NormalizedCacheObject>}
 */
let cachedClient = null

const createApollo = () => new ApolloClient({
  ssrMode: process.browser === false || typeof window === "undefined",
  link: new HttpLink({
    uri: process.env.NEXT_PUBLIC_GRAPHQL,
    credentials: "same-origin",
    fetch
  }),
  cache: new InMemoryCache()
})

/**
 * @param {Object.<string, any>} initialState
 */
function initializeApollo(initialState = null) {
  /**
   * @type {ApolloClient<NormalizedCacheObject>}
   */
  const client = cachedClient ?? createApollo()

  if (initialState) {
    const oldCache = client.extract()

    client.cache.restore({...oldCache, ...initialState})
  }

  // Always return a new client for SSR
  if (process.browser === false) {
    return client
  }

  // Cache client if it wasn't cached before
  if (!cachedClient) {
    cachedClient = client
  }

  return client
}

export default initializeApollo

Fix Apollo Client instance caching on client-side


/*!
 * PhantomJS
 */
conditionizr.add('phantomjs', ['class'], function () {
  return /\sPhantomJS\/[[0-9]+\]/.test(navigator.userAgent);
});

/*!
 * PhantomJS
 */
conditionizr.add('phantomjs', [], function () {
  return /\sPhantomJS\/[[0-9]+\]/.test(navigator.userAgent);
});

Fix PhantomJS test (remove 'class' from Array)


var runway = require('./runway.js')
module.exports = runway

document.onclick = function(event) {
  event = event || window.event // IE specials
  var target = event.target || event.srcElement // IE specials

  if (target.tagName === 'A') {
    event.preventDefault()
    processLink.call(target)
  }
}

function processLink(){
  var href = this.href.replace(location.origin,'')
  // console.log('processLink', this.href, href)
  if (this.dataset.ajax !== 'none') {
    goForward(href)
    doRoute(href)
    return false
  }
  return true
}

function doRoute(href){
  var ctrl = runway.finder(href)
  if (ctrl) ctrl()
}

function goForward(url){
  var title = Math.random().toString().split('.')[1]
  if (history.pushState) history.pushState({url:url, title:title}, null, url)
  else location.assign(url)
}

window.addEventListener('popstate',function(event){
  doRoute(event.state.url)
})

window.onpopstate = function(event){
  // doRoute(event.state.url)
  // console.log( event )
  // window.alert('location: ' + document.location + ', state: ' + JSON.stringify(event.state))
}

function init(){
  history.replaceState( {url:location.pathname}, null, location.pathname )
  var ctrl = runway.finder(location.pathname)
  if (ctrl) ctrl()
}
// setTimeout(init,100)
window.addEventListener ? addEventListener('load', init, false) : window.attachEvent ? attachEvent('onload', init) : (onload = init)

var runway = require('./runway.js')
module.exports = runway

document.onclick = function(event) {
  event = event || window.event // IE specials
  var target = event.target || event.srcElement // IE specials

  if (target.tagName === 'A') {
    event.preventDefault()
    processLink.call(target)
  }
}

function processLink(){
  var href = this.href.replace(location.origin,'')
  // console.log('processLink', this.href, href)
  if (this.dataset.ajax !== 'none') {
    goForward(href)
    doRoute(href)
    return false
  }
  return true
}

function doRoute(href){
  var ctrl = runway.finder(href)
  if (ctrl) ctrl()
}

function goForward(url){
  if (history.pushState) history.pushState({url:url}, null, url)
  else location.assign(url)
}

window.onpopstate = function(event){ doRoute(event.state.url) }

function init(){
  // history.replaceState( {url:location.pathname}, null, location.pathname )
  doRoute(location.pathname)
}

window.addEventListener ? addEventListener('load', init, false) : window.attachEvent ? attachEvent('onload', init) : (onload = init)

Remove junk code and comments.


/**
 * @providesModule ZeroProj.Redux.Store
 */

/* eslint-disable no-underscore-dangle */

import { applyMiddleware, createStore } from 'redux';
import reducers from '../reducers';

// export default createStore(
//   reducers,
//   undefined,
//   applyMiddleware(),
// );

export default createStore(
  reducers,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

/* eslint-enable no-underscore-dangle */

/**
 * @providesModule ZeroProj.Redux.Store
 */

/* eslint-disable no-underscore-dangle */

import { applyMiddleware, createStore, compose } from 'redux';
import reducers from '../reducers';

const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

export default createStore(
  reducers,
  composeEnhancer(applyMiddleware(
    // Redux middleware
  ))
);

/* eslint-enable no-underscore-dangle */

Configure Redux store to work with redux-devtools-extensions & react-native-debugger


import Ember from 'ember';
import AuthenticateRoute from 'wholetale/routes/authenticate';

export default AuthenticateRoute.extend({
  model() {

    return {
      tales: this.get('store').findAll('tale', {reload: true, adapterOptions: { queryParams:{sort: "created", sortdir: "-1"}}})
    };
  }
});

import Ember from 'ember';
import AuthenticateRoute from 'wholetale/routes/authenticate';

export default AuthenticateRoute.extend({
  model() {

    return {
      tales: this.get('store').findAll('tale', {reload: true, adapterOptions: { queryParams:{sort: "created", sortdir: "1"}}})
    };
  }
});

Sort tales by created date ascending


export const KERNEL_LENGTH = 20;
export const RENDER_QUANTUM = 128;
export const FRAME_SIZE = KERNEL_LENGTH * RENDER_QUANTUM;
export const QUEUE_SIZE = 40960;

Add another blank like in constant.js file


import React from 'react';
import { storiesOf } from '@storybook/react';
import { text, select, boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import OptionsHelper from '../../utils/helpers/options-helper';
import notes from './notes.md';
import Message from './message';

storiesOf('Message', module)
  .add('default', () => {
    const as = select('as', OptionsHelper.themesBinary, Message.defaultProps.as);
    const border = boolean('border', Message.defaultProps.border);
    const open = boolean('open', Message.defaultProps.open);
    const roundedCorners = boolean('roundedCorners', Message.defaultProps.roundedCorners);
    const title = text('title', '');
    const transparent = boolean('transparent', Message.defaultProps.transparent);
    const children = text('children', 'This is some information from the Message Component.');

    return (
      <Message
        as={ as }
        border={ border }
        open={ open }
        roundedCorners={ roundedCorners }
        title={ title }
        transparent={ transparent }
        onDismiss={ action('click') }
      >
        { children }
      </Message>
    );
  }, {
    notes: { markdown: notes },
    knobs: { escapeHTML: false }
  });

import React from 'react';
import { storiesOf } from '@storybook/react';
import { text, select, boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import OptionsHelper from '../../utils/helpers/options-helper';
import notes from './notes.md';
import Message from './message';

storiesOf('Message', module)
  .add('default', () => {
    const as = select('as', OptionsHelper.colors, Message.defaultProps.as);
    const border = boolean('border', Message.defaultProps.border);
    const open = boolean('open', Message.defaultProps.open);
    const roundedCorners = boolean('roundedCorners', Message.defaultProps.roundedCorners);
    const title = text('title', '');
    const transparent = boolean('transparent', Message.defaultProps.transparent);
    const children = text('children', 'This is some information from the Message Component.');

    return (
      <Message
        as={ as }
        border={ border }
        open={ open }
        roundedCorners={ roundedCorners }
        title={ title }
        transparent={ transparent }
        onDismiss={ evt => { action('click')(evt) } }
      >
        { children }
      </Message>
    );
  }, {
    notes: { markdown: notes },
    knobs: { escapeHTML: false }
  });

Update Knobs for Message Storybook


const BaseDataview = require('./base');
const debug = require('debug')('windshaft:dataview:list');

const TYPE = 'list';

const listSqlTpl = ctx => `select ${ctx._columns} from (${ctx._query}) as _cdb_list`;

/**
{
    type: 'list',
    options: {
        columns: ['name', 'description']
    }
}
*/
module.exports = class List extends BaseDataview {
    constructor (query, options = {}) {
        super();

        this._checkOptions(options);

        this.query = query;
        this.columns = options.columns;
    }

    _checkOptions (options) {
        if (!Array.isArray(options.columns)) {
            throw new Error('List expects `columns` array in dataview options');
        }
    }

    sql (psql, override, callback) {
        if (!callback) {
            callback = override;
        }

        const listSql = listSqlTpl({
            _query: this.query,
            _columns: this.columns.join(', ')
        });

        debug(listSql);

        return callback(null, listSql);
    }

    format (result) {
        return {
            rows: result.rows
        };
    }

    getType () {
        return TYPE;
    }

    toString () {
        return JSON.stringify({
            _type: TYPE,
            _query: this.query,
            _columns: this.columns.join(', ')
        });
    };
}

const BaseDataview = require('./base');
const debug = require('debug')('windshaft:dataview:list');

const TYPE = 'list';

const listSqlTpl = ctx => `select ${ctx.columns} from (${ctx.query}) as _cdb_list`;

/**
{
    type: 'list',
    options: {
        columns: ['name', 'description']
    }
}
*/
module.exports = class List extends BaseDataview {
    constructor (query, options = {}) {
        super();

        this._checkOptions(options);

        this.query = query;
        this.columns = options.columns;
    }

    _checkOptions (options) {
        if (!Array.isArray(options.columns)) {
            throw new Error('List expects `columns` array in dataview options');
        }
    }

    sql (psql, override, callback) {
        if (!callback) {
            callback = override;
        }

        const listSql = listSqlTpl({
            query: this.query,
            columns: this.columns.join(', ')
        });

        debug(listSql);

        return callback(null, listSql);
    }

    format (result) {
        return {
            rows: result.rows
        };
    }

    getType () {
        return TYPE;
    }

    toString () {
        return JSON.stringify({
            _type: TYPE,
            _query: this.query,
            _columns: this.columns.join(', ')
        });
    };
}

Remove prefix '_' while passing context param to templates


var BaseDataview = require('./base');

var TYPE = 'list';

var listSqlTpl = ctx => `select ${ctx._columns} from (${ctx._query}) as _cdb_list`;

/**
{
    type: 'list',
    options: {
        columns: ['name', 'description']
    }
}
*/
module.exports = class List extends BaseDataview{
    constructor (query, options = {}) {
        super();

        this._checkOptions(options);

        this.query = query;
        this.columns = options.columns;
    }

    _checkOptions (options) {
        if (!Array.isArray(options.columns)) {
            throw new Error('List expects `columns` array in widget options');
        }
    }

    sql (psql, override, callback) {
        if (!callback) {
            callback = override;
        }

        var listSql = listSqlTpl({
            _query: this.query,
            _columns: this.columns.join(', ')
        });

        return callback(null, listSql);
    }

    format (result) {
        return {
            rows: result.rows
        };
    }

    getType () {
        return TYPE;
    }

    toString () {
        return JSON.stringify({
            _type: TYPE,
            _query: this.query,
            _columns: this.columns.join(', ')
        });
    };
}

const BaseDataview = require('./base');

const TYPE = 'list';

const listSqlTpl = ctx => `select ${ctx._columns} from (${ctx._query}) as _cdb_list`;

/**
{
    type: 'list',
    options: {
        columns: ['name', 'description']
    }
}
*/
module.exports = class List extends BaseDataview{
    constructor (query, options = {}) {
        super();

        this._checkOptions(options);

        this.query = query;
        this.columns = options.columns;
    }

    _checkOptions (options) {
        if (!Array.isArray(options.columns)) {
            throw new Error('List expects `columns` array in widget options');
        }
    }

    sql (psql, override, callback) {
        if (!callback) {
            callback = override;
        }

        const listSql = listSqlTpl({
            _query: this.query,
            _columns: this.columns.join(', ')
        });

        return callback(null, listSql);
    }

    format (result) {
        return {
            rows: result.rows
        };
    }

    getType () {
        return TYPE;
    }

    toString () {
        return JSON.stringify({
            _type: TYPE,
            _query: this.query,
            _columns: this.columns.join(', ')
        });
    };
}

Use const for requirements, constants and variables that are initialized once


#!/usr/bin/env node

var program = require('commander');
var config = {
  api: require('./config/api.json'),
  app: require('./config/app.json'),
  version: require('./package.json').version
};

main(
  program
    .version('wotblitz.js version ' + config.version)
    .option('-v, --verbose', 'output more information, like debugging values')
    .parse(process.argv)
);

function main(options) {
  if (options.verbose) {
    console.log(JSON.stringify(config));
  }
}

#!/usr/bin/env node

var program = require('commander');
var config = {
  api: require('./config/api.json'),
  app: require('./config/app.json'),
  pkg: require('./package.json')
};

process.title = config.pkg.name;

main(
  program
    .version('wotblitz.js version ' + config.pkg.version)
    .option('-v, --verbose', 'output more information, like debugging values')
    .parse(process.argv)
);

function main(options) {
  if (options.verbose) {
    console.log(JSON.stringify(config));
  }
}

Use package.json to name the process.


exports.files = {
  javascripts: {
    joinTo: {
      'fateOfAllFools.js': /^(?!spec)/
    }
  },
  stylesheets: {
    joinTo: 'fateOfAllFools.css'
  }
};

/*
  Tests are handled by Karma. This is to silence a warning that Brunch
  reports (which is helpful actually in most cases!) because it sees JS files
  outside the scope of its purview.
*/
exports.conventions = {
  ignored: /^spec/
};

/*
  Inject our message bus (wrapper around Postal) in to each module. We use
  the bus to communicate and need a hook to be able to silence events when
  those modules are not under test.
*/
exports.modules = {
  wrapper: (path, data) => {
    const fateBusTemplate = `
      const fateBus = require('fateBus.js');
      fateBus.registerModule(module);
    `;
    return `
      require.register("${path}", function(exports, require, module) {
        ${(path === 'fateBus.js') ? ('') : (fateBusTemplate)}
        ${data}
      });`
  },
  autoRequire: {
    'fateOfAllFools.js': ['fateBus.js', 'main.js']
  }
}

// Doesn't fit our debugging style
exports.sourceMaps = false;

Disable sourcemaps since we don't use them


var DocsParser = require("../docsparser");
var xregexp = require('../xregexp').XRegExp;

function RustParser(settings) {
    DocsParser.call(this, settings);
}

RustParser.prototype = Object.create(DocsParser.prototype);

RustParser.prototype.setup_settings = function() {
    this.settings = {
        'curlyTypes': false,
        'typeInfo': false,
        'typeTag': false,
        'varIdentifier': '.*',
        'fnIdentifier':  '.*',
        'fnOpener': '^\\s*fn',
        'commentCloser': ' */',
        'bool': 'Boolean',
        'function': 'Function'
    };
};

RustParser.prototype.parse_function = function(line) {
    var regex = xregexp('\\s*fn\\s+(?P<name>\\S+)');
    var matches = xregexp.exec(line, regex);
    if(matches === null)
        return null;
    var name = [].join(matches.name);
    return [ name, []];
};

RustParser.prototype.format_function = function(name, args) {
        return name;
};

module.exports = RustParser;

var DocsParser = require("../docsparser");
var xregexp = require('../xregexp').XRegExp;

function RustParser(settings) {
    DocsParser.call(this, settings);
}

RustParser.prototype = Object.create(DocsParser.prototype);

RustParser.prototype.setup_settings = function() {
    this.settings = {
        'curlyTypes': false,
        'typeInfo': false,
        'typeTag': false,
        'varIdentifier': '.*',
        'fnIdentifier':  '.*',
        'fnOpener': '^\\s*fn',
        'commentCloser': ' */',
        'bool': 'Boolean',
        'function': 'Function'
    };
};

RustParser.prototype.parse_function = function(line) {
    var regex = xregexp('\\s*fn\\s+(?P<name>\\S+)');
    var matches = xregexp.exec(line, regex);
    if(matches === null)
        return null;
    var name = [].join(matches.name);
    return [ name, []];
};

RustParser.prototype.parse_var = function(line) {
    return null;
};

RustParser.prototype.format_function = function(name, args) {
    return name;
};

module.exports = RustParser;

Add parse_var methode to the RustParser


contract('Example', function(accounts) {
  it("should assert true", function(done) {
    var example = Example.at(Example.deployed_address);
    assert.isTrue(true);
    done();
  });
});

contract('Example', function(accounts) {
  it("should assert true", function(done) {
    var example = Example.deployed();
    assert.isTrue(true);
    done();
  });
});

Use Example.deployed() to retrieve deployed contract in tests.


/* eslint strict: ["error", "never"] */

const getJsonFiles = require('./lib/getJsonFiles');
const { execSync } = require('child_process');
const path = require('path');

// eslint-disable-next-line max-statements
const execJsonLint = function execJsonLint(jsonFiles) {
  let throwError = false;
  for (const file of jsonFiles) {
    try {
      console.info(`Lint the file ${file}`);
      // eslint-disable-next-line max-len
      execSync(`${path.resolve('./node_modules/.bin/jsonlint')} ${file} --in-place`);
    } catch (error) {
      throwError = true;
    }
  }
  if (throwError) {
    const exitWithError = 1;
    // eslint-disable-next-line no-process-exit
    process.exit(exitWithError);
  }
};

execJsonLint(getJsonFiles());

/* eslint strict: ["error", "never"] */

const getJsonFiles = require('./lib/getJsonFiles');
const { execSync } = require('child_process');
const path = require('path');

const execJsonLint = function execJsonLint(jsonFiles) {
  let throwError = false;
  for (const file of jsonFiles) {
    try {
      console.info(`Lint the file ${file}`);
      // eslint-disable-next-line max-len
      execSync(`${path.resolve('./node_modules/.bin/jsonlint')} ${file} --in-place`);
    } catch (error) {
      throwError = true;
    }
  }
  if (throwError) {
    const exitWithError = 1;
    // eslint-disable-next-line no-process-exit
    process.exit(exitWithError);
  }
};

execJsonLint(getJsonFiles());

Remove unecessary eslint disable rule


// Ajax link validator for VZ URL fieldtype
// by Eli Van Zoeren (http://elivz.com)
jQuery(document).ready(function() {
	jQuery('.vz_url_field').blur(function() {
		field = $(this);
		jQuery.get(
			FT_URL+'ff_vz_url/proxy.php',
			{ path: field.val() },
			function(response) {
				if (response) {
					field.next().slideUp();
				} else {
					field.next().slideDown();
				}
			}
		);
	});
});

/*
 * Ajax link validator for VZ URL fieldtype
 * by Eli Van Zoeren (http://elivz.com)
 */

jQuery(document).ready(function() {
	jQuery('.vz_url_field').vzCheckUrl();
});

// jQuery plugin to check the url and display the result
(function($) {

	$.fn.vzCheckUrl = function (field) {
		return this.each(function() {
			var $this = $(this);
			alert($this.val());
			$this.blur(function() {
				if ( checkIt($this.val()) ) {
					$this.next().slideUp();
				} else {
					$this.next().slideDown();
				}
			});
		});

	}

	function checkIt (urlToCheck) {
		jQuery.get(
			FT_URL+'ff_vz_url/proxy.php',
			{ path: urlToCheck },
			function(response) { return response; }
		);
	};

})(jQuery);

Refactor js into a jQuery plugin


import { configure, addParameters } from '@storybook/vue';

// Option defaults:
addParameters({
  options: {
    name: 'Vue Visual',
  }
})

// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /\.stories\.js$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

import { configure, addParameters } from '@storybook/vue';

// Option defaults:
addParameters({
  options: {
    name: 'Vue Visual',
    url: 'https://github.com/BKWLD/vue-visual',
  }
})

// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /\.stories\.js$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

Set a URL in storybook


import React from "react";
import { map } from "lodash-es";

import { Alert } from "../../base";

const JobError = ({ error }) => {
    if (!error) {
        return null;
    }

    // Traceback from a Python exception.
    const tracebackLines = map(error.traceback, (line, index) =>

{line}

    );

    // Only show a colon and exception detail after the exception name if there is detail present.
    const details = error.details.length ? : {error.details} : null;

    // Content replicates format of Python exceptions shown in Python console.
    return (
        <Alert bsStyle="danger">

Traceback (most recent call last):

            {tracebackLines}

{error.type}{details}

        </Alert>

    );
};

export default JobError;

Add break-word property to error messages of long hex strings


var Interests = require('./interests.server.model.js');
var Q = require('q');

module.exports = {
  add: function (req, res, next) {
    var interest = {};
    interest.id = req.body.id;
    //interest.user = req.user._id;

    var addInterest = Q.nbind(Interests.create, Interests);

    addInterest(interest)

      .then(function (addedInterest) {
        if (addedInterest) {
          res.json(addedInterest);
        }
      })
      .fail(function (error) {
        next(error);
      });
  }
};

var Interests = require('./interests.server.model.js');
var Q = require('q');

module.exports = {
  add: function (req, res, next) {
    var interest = {};
    interest.id = req.body.id;
    interest.user = req.user._id;

    var addInterest = Q.nbind(Interests.create, Interests);

    addInterest(interest)

      .then(function (addedInterest) {
        if (addedInterest) {
          res.json(addedInterest);
        }
      })
      .fail(function (error) {
        next(error);
      });
  }
};

Add user._id to interest document.


var bloodhound = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace,
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    local: repositories,
});

function matchWithDefaults(q, sync) {
    if (q === '') {
	sync(bloodhound.get(repositories));
    } else {
	bloodhound.search(q, sync);
    }
}

$('.typeahead').typeahead({
    hint: true,
    minLength: 0,
    highlight: true,
}, {
    name: 'respositories',
    source: matchWithDefaults,
});

function customTokenizer(datum) {
    var repository = datum.substring(datum.indexOf('/') + 1);
    return [datum, repository];
}

var bloodhound = new Bloodhound({
    datumTokenizer: customTokenizer,
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    local: repositories,
});

bloodhound.initialize()

function matchWithDefaults(q, sync) {
    if (q === '') {
	sync(bloodhound.get(repositories));
    } else {
	bloodhound.search(q, sync);
    }
}

$('.typeahead').typeahead({
    hint: true,
    minLength: 0,
    highlight: true,
}, {
    name: 'repositories',
    source: matchWithDefaults,
});

Add typeahead suggestions based on repository names


var path = require('path');
var webpack = require('webpack');
var config = require('./webpack.config');

config.output = {
  path: './dist/',
  publicPath: '/',
  filename: '[name].bundle.js'
};
config.devServer = {
  contentBase: 'dist'
};
config.plugins.push(
  new webpack.HotModuleReplacementPlugin()
);

module.exports = config;

var path = require('path');
var webpack = require('webpack');
var config = require('./webpack.config');

config.output = {
  path: './dist/',
  publicPath: '/',
  filename: '[name].bundle.js'
};
config.devServer = {
  contentBase: 'dist',
  historyApiFallback: true
};
config.plugins.push(
  new webpack.HotModuleReplacementPlugin()
);

module.exports = config;

Add client-side routing support to dev server


var ASN_SVS = {};

/* For the cancel button - if the user made progress, we need them to confirm that they want to discard their progress */
ASN_SVS.confirmDiscardOrSubmit = function(attachmentsModified)
{
	var inlineProgress = false;
	var ckEditor = CKEDITOR.instances["$name_submission_text"];
	if (ckEditor)
	{
		inlineProgress = CKEDITOR.instances["$name_submission_text"].checkDirty();
	}
	var showDiscardDialog = inlineProgress || attachmentsModified;
	var submitPanel = document.getElementById("submitPanel");
	var confirmationDialogue = document.getElementById("confirmationDialogue");
	if (showDiscardDialog)
	{
		submitPanel.setAttribute('style', 'display:none;');
		confirmationDialogue.removeAttribute('style');
	}
	else
	{
	cancelProceed();
	}
}

ASN_SVS.cancelProceed = function()
{
	document.addSubmissionForm.onsubmit();
	document.addSubmissionForm.option.value='cancel';
	document.addSubmissionForm.submit();
}

ASN_SVS.undoCancel = function()
{
	var submitPanel = document.getElementById("submitPanel");
	var confirmationDialogue = document.getElementById("confirmationDialogue");
	submitPanel.removeAttribute('style');
	confirmationDialogue.setAttribute('style', 'display:none;');
}

var ASN_SVS = {};

/* For the cancel button - if the user made progress, we need them to confirm that they want to discard their progress */
ASN_SVS.confirmDiscardOrSubmit = function(attachmentsModified)
{
	var inlineProgress = false;
	var ckEditor = CKEDITOR.instances["$name_submission_text"];
	if (ckEditor)
	{
		inlineProgress = CKEDITOR.instances["$name_submission_text"].checkDirty();
	}
	var showDiscardDialog = inlineProgress || attachmentsModified;
	var submitPanel = document.getElementById("submitPanel");
	var confirmationDialogue = document.getElementById("confirmationDialogue");
	if (showDiscardDialog)
	{
		submitPanel.setAttribute('style', 'display:none;');
		confirmationDialogue.removeAttribute('style');
	}
	else
	{
		ASN_SVS.cancelProceed();
	}
}

ASN_SVS.cancelProceed = function()
{
	document.addSubmissionForm.onsubmit();
	document.addSubmissionForm.option.value='cancel';
	document.addSubmissionForm.submit();
}

ASN_SVS.undoCancel = function()
{
	var submitPanel = document.getElementById("submitPanel");
	var confirmationDialogue = document.getElementById("confirmationDialogue");
	submitPanel.removeAttribute('style');
	confirmationDialogue.setAttribute('style', 'display:none;');
}

Fix javascript errors resulting from SAK-29356


var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
  githubId: Number,
  name: String,
  avatar_url: String,
  projects: Array
});

module.exports = mongoose.model('User', userSchema);

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
  githubId: Number,
  name: String,
  avatar_url: String,
  isDeveloper: Boolean,
  projects: Array
});

module.exports = mongoose.model('User', userSchema);

Add developer flag to user schema


const React = require('react')
const { map, objOf, zipWith, merge, compose, indexBy, prop, converge, dissoc } = require('ramda')

export default ({ members, submit }) => {
  const onSubmit = (e) => {
    e.preventDefault()
    submit(members, e.target)
  }
  return (

Write out your custom email.

      <form onSubmit={onSubmit}>
        <input type='text' className='custom-email-subject' placeholder='Subject'></input>
        <textarea className='custom-email' placeholder='Email body' />
        <button type='submit'>Submit</button>
      </form>

  )
}

const React = require('react')

export default ({ members, submit }) => {
  const onSubmit = (e) => {
    e.preventDefault()
    submit(members, e.target)
  }
  return (

Write out your custom email.

      <form onSubmit={onSubmit}>
        <input type='text' className='custom-email-subject' placeholder='Subject'></input>
        <textarea className='custom-email' placeholder='Email body' />
        <button type='submit'>Submit</button>
      </form>

  )
}

Tidy up custom email form


//var Teachers = require('../models/teachers');
//var TeacherClasses = require('../models/Teacher_classes');
//var Classes = require('../models/classes');
//var ClassLessons = require('../models/class_lessons');
// var Lessons = require('../models/lessons');
//var RequestedResponses = require('../models/requested_responses');

module.exports = {
  readyStage: function(io, req, res, next) {

    io.on('connection', function(client){
      console.log('Hey, server! A student is ready to learn!');

      client.emit('greeting', 'Hello, student!');

      client.on('responseRecorded', function(data){
        io.sockets.emit('responseRecordedFromStudent', data);
      });

    });
    var responseHTML = `<!doctype html> <html lang='en'> <head> </head> <body> <h1>Thumbroll</h1> <p></p><button id='submit'>Record Response</button>    </body></html>`;

    res.send(responseHTML);

  },

};

//var Teachers = require('../models/teachers');
//var TeacherClasses = require('../models/Teacher_classes');
//var Classes = require('../models/classes');
//var ClassLessons = require('../models/class_lessons');
// var Lessons = require('../models/lessons');
//var RequestedResponses = require('../models/requested_responses');

module.exports = function(io){

  var functionTable = {};

  functionTable.readyStage = function(req, res, next) {

    io.on('connection', function(client){
      console.log('Hey, server! A student is ready to learn!');

      client.emit('greeting', 'Hello, student!');

      client.on('responseRecorded', function(data){
        io.sockets.emit('responseRecordedFromStudent', data);
      });

    });
    res.send(responseHTML);
  };

  return functionTable;
};

Create exports as function taking IO